From b80ec2a0d90d0b2d0c8e4d2289fe0abe4e53c690 Mon Sep 17 00:00:00 2001 From: Mikhail Volkov Date: Wed, 29 May 2024 11:06:30 +0300 Subject: [PATCH] nomad provider --- examples/read-nomad/httpecho.hcl | 25 +++ examples/read-nomad/main.go | 282 +++++++++++++++++++++++++++++++ go.mod | 20 ++- go.sum | 31 ++++ go.work.sum | 8 +- providers/nomad/nomad.go | 179 ++++++++++++++++++++ 6 files changed, 536 insertions(+), 9 deletions(-) create mode 100644 examples/read-nomad/httpecho.hcl create mode 100644 examples/read-nomad/main.go create mode 100644 providers/nomad/nomad.go diff --git a/examples/read-nomad/httpecho.hcl b/examples/read-nomad/httpecho.hcl new file mode 100644 index 00000000..ad909a8a --- /dev/null +++ b/examples/read-nomad/httpecho.hcl @@ -0,0 +1,25 @@ +job "httpecho" { + datacenters = ["dc1"] + + group "echo" { + count = 5 + network { + port "http" {} + } + + task "server" { + driver = "docker" + + config { + image = "hashicorp/http-echo:latest" + + ports = ["http"] + + args = [ + "-listen", ":${NOMAD_PORT_http}", + "-text", "Hello and welcome to ${NOMAD_IP_http} running on port ${NOMAD_PORT_http}", + ] + } + } + } +} \ No newline at end of file diff --git a/examples/read-nomad/main.go b/examples/read-nomad/main.go new file mode 100644 index 00000000..c1101398 --- /dev/null +++ b/examples/read-nomad/main.go @@ -0,0 +1,282 @@ +package main + +import ( + "encoding/json" + "fmt" + "log" + "os" + "os/exec" + "strconv" + + "github.com/hashicorp/nomad/api" + "github.com/knadh/koanf/v2" + "github.com/knadh/koanf/v2/providers/nomad" +) + +// Example and test +// Requirements: +// +// - Docker installed +// - Docker daemon running +// - Nomad installed +// - Nomad running +// (sudo nomad agent -dev) +// - Nomad echo project running +// (nomad run httpecho.hcl) +// - curl +func main() { + fmt.Printf("Checking...\n") + var k = koanf.New(".") + var k2 = koanf.New(".") + var k3 = koanf.New(".") + + // allocs + out, err := exec.Command("curl", "http://localhost:4646/v1/allocations").Output() + if err != nil { + log.Fatalf("error retreiving allocs from curl: %v\n", err) + } + + var aListStub = []api.AllocationListStub{} + if err = json.Unmarshal(out, &aListStub); err != nil { + log.Fatalf("error unmarshalling alloc list: %s\n", err) + } + + nAllocs := len(aListStub) + allocsHTTPData := make([]api.Allocation, nAllocs) + + for i := 0; i < nAllocs; i++ { + cmdPath := fmt.Sprintf("http://localhost:4646/v1/allocation/%s", aListStub[i].ID) + out, err := exec.Command("curl", cmdPath).Output() + if err != nil { + log.Fatalf("error retrieving alloc with ID = %s: %s\n", aListStub[i].ID, err) + } + + json.Unmarshal(out, &allocsHTTPData[i]) + } + + nmdAllocs, err := nomad.Provider(nil, "allocs") + if err != nil { + log.Fatalf("error creating provider: %v\n", err) + } + + if err = k.Load(nmdAllocs, nil); err != nil { + log.Fatalf("error loading allocs: %v\n", err) + } + + // allocs: checking + for i1 := 0; i1 < nAllocs; i1++ { + ID := allocsHTTPData[i1].ID + + var key string + var flagFound bool = false + + // finding alloc with this ID + for i2 := 0; i2 < nAllocs; i2++ { + key = "echo" + strconv.Itoa(i2) + if k.String(key+".ID") == ID { + flagFound = true + break + } + } + + if !flagFound { + log.Fatalf("Alloc ID not found: test failed\n") + } + + // comparing all data + if k.String(key+".Namespace") != allocsHTTPData[i1].Namespace { + fmt.Printf("Alloc Namespace: test failed\n") + os.Exit(1) + } + + if k.String(key+".EvalID") != allocsHTTPData[i1].EvalID { + fmt.Printf("Alloc EvalID: test failed\n") + os.Exit(1) + } + + if k.String(key+".Name") != allocsHTTPData[i1].Name { + fmt.Printf("Alloc Name: test failed\n") + os.Exit(1) + } + + if k.String(key+".NodeID") != allocsHTTPData[i1].NodeID { + fmt.Printf("Alloc NodeID: test failed\n") + os.Exit(1) + } + + if k.String(key+".NodeName") != allocsHTTPData[i1].NodeName { + fmt.Printf("Alloc NodeName: test failed\n") + os.Exit(1) + } + + if k.String(key+".JobID") != allocsHTTPData[i1].JobID { + fmt.Printf("Alloc JobID: test failed\n") + os.Exit(1) + } + + if k.String(key+".TaskGroup") != allocsHTTPData[i1].TaskGroup { + fmt.Printf("Alloc TaskGroup: test failed\n") + os.Exit(1) + } + + if k.String(key+".DesiredStatus") != allocsHTTPData[i1].DesiredStatus { + fmt.Printf("Alloc DesiredStatus: test failed\n") + os.Exit(1) + } + + if k.String(key+".ClientStatus") != allocsHTTPData[i1].ClientStatus { + fmt.Printf("Alloc ClientStatus: test failed\n") + os.Exit(1) + } + + if k.String(key+".DeploymentID") != allocsHTTPData[i1].DeploymentID { + fmt.Printf("Alloc DeploymentID: test failed\n") + os.Exit(1) + } + + if k.String(key+".DeploymentID") == allocsHTTPData[i1].ID { + fmt.Printf("Alloc DeploymentID = ID: test failed\n") + os.Exit(1) + } + + // network comparison + for k1 := 0; k1 < len(allocsHTTPData[i1].Resources.Networks); k1++ { + ipSearch := allocsHTTPData[i1].Resources.Networks[k1].IP + + // ipSearch in koanf + // koanf key + var keyNetwork string + for k2 := 0; k2 < len(allocsHTTPData[i1].Resources.Networks); k2++ { + keyNetwork = key + ".Network" + strconv.Itoa(k1) + ".IP" + if ipSearch == k.String(keyNetwork) { + break + } + } + + // port search + for k2 := 0; k2 < len(allocsHTTPData[i1].Resources.Networks[k1].DynamicPorts); k2++ { + flagPort := false + for k3 := 0; k3 <= k2; k3++ { + keyPort := key + ".Network" + strconv.Itoa(k1) + ".Port" + strconv.Itoa(k3) + if k.Int(keyPort) == allocsHTTPData[i1].Resources.Networks[k1].DynamicPorts[k2].Value { + flagPort = true + break + } + } + + if flagPort { + flagPort = false + } else { + fmt.Printf( + "Alloc ports: test failed, port %d isn't found\n", + allocsHTTPData[i1].Resources.Networks[k1].DynamicPorts[k2].Value) + os.Exit(1) + } + } + } + } + + fmt.Printf("Allocations: test passed\n") + + // raft + out, err = exec.Command("curl", "http://localhost:4646/v1/operator/raft/configuration").Output() + if err != nil { + log.Fatalf("error retreiving raft configuration from curl: %v\n", err) + } + + raftCfg := api.RaftConfiguration{} + if err = json.Unmarshal(out, &raftCfg); err != nil { + log.Fatalf("error unmarshalling raft configuration: %s\n", err) + } + + nmdRaft, err := nomad.Provider(nil, "raft") + if err != nil { + log.Fatalf("error creating provider: %v\n", err) + } + + if err = k2.Load(nmdRaft, nil); err != nil { + log.Fatalf("error loading allocs: %v\n", err) + } + + nServers := len(raftCfg.Servers) + + // raft: checking + for i1 := 0; i1 < nServers; i1++ { + ID := raftCfg.Servers[i1].ID + + var key string + var flagFound bool = false + + // finding alloc with this ID + for i2 := 0; i2 < nServers; i2++ { + key = "server" + strconv.Itoa(i2) + if k2.String(key+".ID") == ID { + flagFound = true + break + } + } + + if !flagFound { + log.Fatalf("Raft server ID not found: test failed") + } + + if k2.String(key+".Node") != raftCfg.Servers[i1].Node { + log.Fatalf("Raft server node: test failed") + } + + if k2.String(key+".Address") != raftCfg.Servers[i1].Address { + log.Fatalf("Raft server address: test failed") + } + + if k2.Bool(key+".Leader") != raftCfg.Servers[i1].Leader { + log.Fatalf("Raft server leader: test failed") + } + + if k2.Bool(key+".Voter") != raftCfg.Servers[i1].Voter { + log.Fatalf("Raft server voter: test failed") + } + + if k2.String(key+".RaftProtocol") != raftCfg.Servers[i1].RaftProtocol { + log.Fatalf("Raft server protocol: test failed") + } + } + + fmt.Printf("Raft configuration: test passed\n") + + // vars + _, err = exec.Command( + "curl", + "--header", + "Content-Type: application/json", + "--request", + "POST", + "--data", + `{"Path": "databases/sql", "Items": {"mysql": "75cp21", "postgresql": "52pg24"}}`, + "http://localhost:4646/v1/var/databases/sql", + ).Output() + if err != nil { + log.Fatalf("Couldn't create variables: %v\n", err) + } + + nmdVars, err := nomad.Provider(nil, "vars") + if err != nil { + log.Fatalf("error creating provider: %v\n", err) + } + + if err = k3.Load(nmdVars, nil); err != nil { + log.Fatalf("error loading vars: %v\n", err) + } + + // vars: checking + if k3.String("databases/sql.mysql") != "75cp21" { + log.Fatalf("Vars: test failed\n") + } + + if k3.String("databases/sql.postgresql") != "52pg24" { + log.Fatalf("Vars: test failed\n") + } + + fmt.Printf("Vars: test passed\n") + + fmt.Printf("ALL TESTS PASSED\n") +} diff --git a/go.mod b/go.mod index cdd023fb..796d4ae3 100644 --- a/go.mod +++ b/go.mod @@ -4,12 +4,24 @@ go 1.18 require ( github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 + github.com/hashicorp/nomad/api v0.0.0-20240528130403-9fb2b10ab63d github.com/knadh/koanf/maps v0.1.1 github.com/mitchellh/copystructure v1.2.0 ) -require github.com/mitchellh/reflectwalk v1.0.2 // indirect +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/hashicorp/cronexpr v1.1.2 // indirect + github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) -retract ( - v2.0.2 // Tagged as minor version, but contains breaking changes. -) \ No newline at end of file +retract v2.0.2 // Tagged as minor version, but contains breaking changes. diff --git a/go.sum b/go.sum index a35d2f97..1dbd8052 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,39 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 h1:TQcrn6Wq+sKGkpyPvppOz99zsMBaUOKXq6HSv655U1c= github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= +github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/nomad/api v0.0.0-20240528130403-9fb2b10ab63d h1:7JpW+CLlTdBLFejoKNx8Tu7VY8eiFkMmOLPnkYbLVrw= +github.com/hashicorp/nomad/api v0.0.0-20240528130403-9fb2b10ab63d/go.mod h1:svtxn6QnrQ69P23VvIWMR34tg3vmwLz4UdUzm1dSCgE= github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs= github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/shoenig/test v1.7.1 h1:UJcjSAI3aUKx52kfcfhblgyhZceouhvvs3OYdWgn+PY= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/go.work.sum b/go.work.sum index b879b3bf..c26546fe 100644 --- a/go.work.sum +++ b/go.work.sum @@ -125,30 +125,28 @@ github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91 github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q= github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/sdk v0.13.1/go.mod h1:SW/mM4LbKfqmMvcFu8v+eiQQ7oitXEFeiBe9StxERb0= github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/providers/nomad/nomad.go b/providers/nomad/nomad.go new file mode 100644 index 00000000..00ed6246 --- /dev/null +++ b/providers/nomad/nomad.go @@ -0,0 +1,179 @@ +package nomad + +import ( + "errors" + "strconv" + + "github.com/hashicorp/nomad/api" +) + +var errUnprovided = errors.New("Nomad provider does not support this method") + +// Nomad implements Nomad provider. +// datatype can be "allocs", "raft" or "vars" +type Nomad struct { + client *api.Client + dtype string +} + +// Provider returns an instance of Nomad provider. +func Provider(cfg *api.Config, datatype string) (*Nomad, error) { + if cfg == nil { + cfg = api.DefaultConfig() + } + + c, err := api.NewClient(cfg) + if err != nil { + return nil, err + } + + return &Nomad{client: c, dtype: datatype}, nil +} + +// ReadBytes is not supported by the Nomad provider. +func (n *Nomad) ReadBytes() ([]byte, error) { + return nil, errUnprovided +} + +// TODO: read with configuration + +func (n *Nomad) Read() (map[string]interface{}, error) { + var mp map[string]interface{} + var err error + + switch n.dtype { + case "allocs": + mp, err = n.ReadAllocs() + if err != nil { + return nil, err + } + case "raft": + mp, err = n.ReadRaft() + if err != nil { + return nil, err + } + case "vars": + mp, err = n.ReadVars() + if err != nil { + return nil, err + } + } + + return mp, nil +} + +func (n *Nomad) ReadAllocs() (map[string]interface{}, error) { + mp := make(map[string]interface{}) + mpKeys := make(map[string]int) + + allocs := n.client.Allocations() + + allocsInfo, _, err := allocs.List(nil) + if err != nil { + return nil, err + } + + nAllocs := len(allocsInfo) + allocsData := make([]*api.Allocation, nAllocs) + + for i := 0; i < nAllocs; i++ { + allocsData[i], _, err = allocs.Info(allocsInfo[i].ID, nil) + if err != nil { + return nil, err + } + } + + for i := 0; i < nAllocs; i++ { + mpAlloc := make(map[string]interface{}) + + mpAlloc["ID"] = allocsData[i].ID + mpAlloc["Namespace"] = allocsData[i].Namespace + mpAlloc["EvalID"] = allocsData[i].EvalID + mpAlloc["Name"] = allocsData[i].Name + mpAlloc["NodeID"] = allocsData[i].NodeID + mpAlloc["NodeName"] = allocsData[i].NodeName + mpAlloc["JobID"] = allocsData[i].JobID + mpAlloc["TaskGroup"] = allocsData[i].TaskGroup + mpAlloc["DesiredStatus"] = allocsData[i].DesiredStatus + mpAlloc["ClientStatus"] = allocsData[i].ClientStatus + mpAlloc["DeploymentID"] = allocsData[i].DeploymentID + + for n := 0; n < len(allocsData[i].Resources.Networks); n++ { + mpNetwork := make(map[string]interface{}) + + mpNetwork["IP"] = allocsData[i].Resources.Networks[n].IP + + for p := 0; p < len(allocsData[i].Resources.Networks[n].DynamicPorts); p++ { + keyPort := "Port" + strconv.Itoa(p) + mpNetwork[keyPort] = allocsData[i].Resources.Networks[n].DynamicPorts[p].Value + } + + keyNetwork := "Network" + strconv.Itoa(n) + mpAlloc[keyNetwork] = mpNetwork + } + + keyAlloc := allocsData[i].TaskGroup + if _, ok := mpKeys[keyAlloc]; ok { + mpKeys[keyAlloc]++ + } else { + mpKeys[keyAlloc] = 0 + } + + keyAlloc += strconv.Itoa(mpKeys[keyAlloc]) + mp[keyAlloc] = mpAlloc + } + + return mp, nil +} + +func (n *Nomad) ReadRaft() (map[string]interface{}, error) { + mp := make(map[string]interface{}) + + operator := n.client.Operator() + raftCfg, err := operator.RaftGetConfiguration(nil) + if err != nil { + return nil, err + } + + for i := 0; i < len(raftCfg.Servers); i++ { + mpServer := make(map[string]interface{}) + + mpServer["ID"] = raftCfg.Servers[i].ID + mpServer["Node"] = raftCfg.Servers[i].Node + mpServer["Address"] = raftCfg.Servers[i].Address + mpServer["Leader"] = raftCfg.Servers[i].Leader + mpServer["Voter"] = raftCfg.Servers[i].Voter + mpServer["RaftProtocol"] = raftCfg.Servers[i].RaftProtocol + + keyServer := "server" + strconv.Itoa(i) + mp[keyServer] = mpServer + } + + return mp, nil +} + +func (n *Nomad) ReadVars() (map[string]interface{}, error) { + mp := make(map[string]interface{}) + + varsObj := n.client.Variables() + varsInfo, _, err := varsObj.List(nil) + if err != nil { + return nil, err + } + + for i := 0; i < len(varsInfo); i++ { + mpPathvars := make(map[string]interface{}) + vItems, _, err := varsObj.GetVariableItems(varsInfo[i].Path, nil) + if err != nil { + return nil, err + } + + for k := range vItems { + mpPathvars[k] = vItems[k] + } + + mp[varsInfo[i].Path] = mpPathvars + } + + return mp, nil +}