@@ -18,7 +18,7 @@ import (
1818//go:generate mockery --name Nomad --filename nomad.go
1919type Nomad interface {
2020 // SetConfig for the client, path is a valid Nomad JSON config file
21- SetConfig (address string , port , nodes int ) error
21+ SetConfig (address string , port , nodes int , acl_token string ) error
2222 // Create jobs in the provided files
2323 Create (files []string ) error
2424 // Stop jobs in the provided files
@@ -30,9 +30,11 @@ type Nomad interface {
3030 // HealthCheckAPI uses the Nomad API to check that all servers and nodes
3131 // are ready. The function will block until either all nodes are healthy or the
3232 // timeout period elapses.
33- HealthCheckAPI (time.Duration ) error
33+ HealthCheckAPI (time.Duration , bool ) error
3434 // Endpoints returns a list of endpoints for a cluster
3535 Endpoints (job , group , task string ) ([]map [string ]string , error )
36+ // Bootstrap ACLs
37+ BootstrapACLs () error
3638}
3739
3840// NomadImpl is an implementation of the Nomad interface
@@ -43,6 +45,7 @@ type NomadImpl struct {
4345 address string
4446 port int
4547 clientNodes int
48+ aclToken string
4649}
4750
4851// NewNomad creates a new Nomad client
@@ -59,17 +62,52 @@ type createRequest struct {
5962 Job string
6063}
6164
65+ func (n * NomadImpl ) setAuthHeaders (rq * http.Request ) {
66+ if n .aclToken != "" {
67+ rq .Header .Set ("X-Nomad-Token" , n .aclToken )
68+ }
69+ }
70+
6271// SetConfig loads the Nomad config from a file
63- func (n * NomadImpl ) SetConfig (address string , port , nodes int ) error {
72+ func (n * NomadImpl ) SetConfig (address string , port , nodes int , acl_token string ) error {
6473 n .address = address
6574 n .port = port
6675 n .clientNodes = nodes
76+ n .aclToken = acl_token
6777
6878 return nil
6979}
7080
71- // HealthCheckAPI executes a HTTP heath check for a Nomad cluster
72- func (n * NomadImpl ) HealthCheckAPI (timeout time.Duration ) error {
81+ func (n * NomadImpl ) BootstrapACLs () error {
82+ if n .aclToken != "" {
83+ n .l .Debug ("Bootstrapping ACLs" , "address" , n .address )
84+
85+ jsonBody := []byte (fmt .Sprintf (`{"BootstrapSecret":"%s"}` , n .aclToken ))
86+ bodyReader := bytes .NewReader (jsonBody )
87+
88+ rq , err := http .NewRequest (http .MethodPost , fmt .Sprintf ("%s:%d/v1/acl/bootstrap" , n .address , n .port ), bodyReader )
89+ if err != nil {
90+ return err
91+ }
92+
93+ resp , err := n .httpClient .Do (rq )
94+ if err != nil {
95+ return xerrors .Errorf ("Unable to bootstrap ACLs: %w" , err )
96+ }
97+ defer resp .Body .Close ()
98+
99+ if resp .StatusCode != http .StatusOK {
100+ // try to read the body for the error
101+ d , _ := ioutil .ReadAll (resp .Body )
102+ return xerrors .Errorf ("Error bootstrapping ACLs, got status code %d, error: %s" , resp .StatusCode , string (d ))
103+ }
104+
105+ }
106+ return nil
107+ }
108+
109+ // HealthCheckAPI executes a HTTP health check for a Nomad cluster
110+ func (n * NomadImpl ) HealthCheckAPI (timeout time.Duration , simple bool ) error {
73111 n .l .Debug ("Performing Nomad health check" , "address" , n .address )
74112 st := time .Now ()
75113 for {
@@ -79,75 +117,87 @@ func (n *NomadImpl) HealthCheckAPI(timeout time.Duration) error {
79117 return fmt .Errorf ("Timeout waiting for Nomad healthcheck %s" , n .address )
80118 }
81119
82- rq , err := http .NewRequest (http .MethodGet , fmt .Sprintf ("%s:%d/v1/nodes" , n .address , n .port ), nil )
83- if err != nil {
84- return err
85- }
86-
87- resp , err := n .httpClient .Do (rq )
88- if err == nil && resp .StatusCode == 200 {
89- nodes := []map [string ]interface {}{}
90- // check number of nodes
91- json .NewDecoder (resp .Body ).Decode (& nodes )
92-
93- // loop nodes and check ready
94- readyCount := 0
95- for _ , node := range nodes {
96- // get the node status
97- nodeStatus := node ["Status" ].(string )
98- nodeName := node ["Name" ].(string )
99- nodeEligable := node ["SchedulingEligibility" ].(string )
100-
101- n .l .Debug ("Node status" , "node" , nodeName , "status" , nodeStatus , "eligible" , nodeEligable )
102- // get the driver status
103- drivers , ok := node ["Drivers" ].(map [string ]interface {})
104- if ! ok {
105- continue
106- }
107-
108- var driversHealthy = true
109- var dockerDetected = false
110- for k , v := range drivers {
111- driver , ok := v .(map [string ]interface {})
112- if ! ok {
113- continue
114- }
120+ if simple {
121+ rq , err := http .NewRequest (http .MethodGet , fmt .Sprintf ("%s:%d/v1/status/leader" , n .address , n .port ), nil )
122+ if err != nil {
123+ return err
124+ }
125+ resp , err := n .httpClient .Do (rq )
126+ if err == nil && resp .StatusCode == http .StatusOK {
127+ return nil
128+ }
129+ } else {
130+ rq , err := http .NewRequest (http .MethodGet , fmt .Sprintf ("%s:%d/v1/nodes" , n .address , n .port ), nil )
131+ n .setAuthHeaders (rq )
132+ if err != nil {
133+ return err
134+ }
115135
116- healthy , ok := driver ["Healthy" ].(bool )
136+ resp , err := n .httpClient .Do (rq )
137+ if err == nil && resp .StatusCode == http .StatusOK {
138+ nodes := []map [string ]interface {}{}
139+ // check number of nodes
140+ json .NewDecoder (resp .Body ).Decode (& nodes )
141+
142+ // loop nodes and check ready
143+ readyCount := 0
144+ for _ , node := range nodes {
145+ // get the node status
146+ nodeStatus := node ["Status" ].(string )
147+ nodeName := node ["Name" ].(string )
148+ nodeEligable := node ["SchedulingEligibility" ].(string )
149+
150+ n .l .Debug ("Node status" , "node" , nodeName , "status" , nodeStatus , "eligible" , nodeEligable )
151+ // get the driver status
152+ drivers , ok := node ["Drivers" ].(map [string ]interface {})
117153 if ! ok {
118154 continue
119155 }
120156
121- detected , ok := driver ["Detected" ].(bool )
122- if ! ok || ! detected {
123- continue
124- }
157+ var driversHealthy = true
158+ var dockerDetected = false
159+ for k , v := range drivers {
160+ driver , ok := v .(map [string ]interface {})
161+ if ! ok {
162+ continue
163+ }
164+
165+ healthy , ok := driver ["Healthy" ].(bool )
166+ if ! ok {
167+ continue
168+ }
169+
170+ detected , ok := driver ["Detected" ].(bool )
171+ if ! ok || ! detected {
172+ continue
173+ }
174+
175+ // we need to make a special case to check the docker driver is
176+ // present as if the nomad server starts before docker then the
177+ // presence of docker will not be detected
178+ if k == "docker" {
179+ dockerDetected = true
180+ }
181+
182+ n .l .Debug ("Driver status" , "node" , nodeName , "driver" , k , "healthy" , healthy )
183+ if ! healthy {
184+ driversHealthy = false
185+ }
125186
126- // we need to make a special case to check the docker driver is
127- // present as if the nomad server starts before docker then the
128- // presence of docker will not be detected
129- if k == "docker" {
130- dockerDetected = true
131187 }
132188
133- n .l .Debug ("Driver status" , "node" , nodeName , "driver" , k , "healthy" , healthy )
134- if ! healthy {
135- driversHealthy = false
189+ if nodeStatus == "ready" && nodeEligable == "eligible" && driversHealthy && dockerDetected {
190+ readyCount ++
136191 }
137-
138192 }
139193
140- if nodeStatus == "ready" && nodeEligable == "eligible" && driversHealthy && dockerDetected {
141- readyCount ++
194+ if readyCount == n .clientNodes {
195+ n .l .Debug ("Nomad check complete" , "address" , n .address )
196+ return nil
142197 }
143- }
144198
145- if readyCount == n .clientNodes {
146- n .l .Debug ("Nomad check complete" , "address" , n .address )
147- return nil
199+ n .l .Debug ("Nodes not ready" , "ready" , readyCount , "nodes" , n .clientNodes )
148200 }
149-
150- n .l .Debug ("Nodes not ready" , "ready" , readyCount , "nodes" , n .clientNodes )
151201 }
152202
153203 // backoff
@@ -171,6 +221,7 @@ func (n *NomadImpl) Create(files []string) error {
171221 cr := fmt .Sprintf (`{"Job": %s}` , string (jsonJob ))
172222
173223 r , err := http .NewRequest (http .MethodPost , addr , bytes .NewReader ([]byte (cr )))
224+ n .setAuthHeaders (r )
174225 if err != nil {
175226 return xerrors .Errorf ("Unable to create http request: %w" , err )
176227 }
@@ -201,6 +252,7 @@ func (n *NomadImpl) Stop(files []string) error {
201252
202253 // stop the job
203254 r , err := http .NewRequest (http .MethodDelete , fmt .Sprintf ("%s:%d/v1/job/%s" , n .address , n .port , id ), nil )
255+ n .setAuthHeaders (r )
204256 if err != nil {
205257 return xerrors .Errorf ("Unable to create http request: %w" , err )
206258 }
@@ -235,6 +287,7 @@ func (n *NomadImpl) ParseJob(file string) ([]byte, error) {
235287
236288 // validate the config with the Nomad API
237289 r , err := http .NewRequest (http .MethodPost , fmt .Sprintf ("%s:%d/v1/jobs/parse" , n .address , n .port ), bytes .NewReader (jobData ))
290+ n .setAuthHeaders (r )
238291 if err != nil {
239292 return nil , xerrors .Errorf ("Unable to create http request: %w" , err )
240293 }
@@ -313,6 +366,7 @@ func (n *NomadImpl) Endpoints(job, group, task string) ([]map[string]string, err
313366 }
314367
315368 r , err := http .NewRequest (http .MethodGet , fmt .Sprintf ("%s:%d/v1/allocation/%s" , n .address , n .port , j ["ID" ]), nil )
369+ n .setAuthHeaders (r )
316370 if err != nil {
317371 return nil , xerrors .Errorf ("Unable to create http request: %w" , err )
318372 }
@@ -393,6 +447,7 @@ func (n *NomadImpl) Endpoints(job, group, task string) ([]map[string]string, err
393447func (n * NomadImpl ) getJobAllocations (job string ) ([]map [string ]interface {}, error ) {
394448 // get the allocations for the job
395449 r , err := http .NewRequest (http .MethodGet , fmt .Sprintf ("%s:%d/v1/job/%s/allocations" , n .address , n .port , job ), nil )
450+ n .setAuthHeaders (r )
396451 if err != nil {
397452 return nil , xerrors .Errorf ("Unable to create http request: %w" , err )
398453 }
0 commit comments