github.com/blixtra/nomad@v0.7.2-0.20171221000451-da9a1d7bb050/api/api_test.go (about) 1 package api 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "net/http/httptest" 8 "os" 9 "strings" 10 "testing" 11 "time" 12 13 "github.com/hashicorp/nomad/helper/uuid" 14 "github.com/hashicorp/nomad/testutil" 15 "github.com/stretchr/testify/assert" 16 ) 17 18 type configCallback func(c *Config) 19 20 // seen is used to track which tests we have already marked as parallel 21 var seen map[*testing.T]struct{} 22 23 func init() { 24 seen = make(map[*testing.T]struct{}) 25 } 26 27 func makeACLClient(t *testing.T, cb1 configCallback, 28 cb2 testutil.ServerConfigCallback) (*Client, *testutil.TestServer, *ACLToken) { 29 client, server := makeClient(t, cb1, func(c *testutil.TestServerConfig) { 30 c.ACL.Enabled = true 31 if cb2 != nil { 32 cb2(c) 33 } 34 }) 35 36 // Get the root token 37 root, _, err := client.ACLTokens().Bootstrap(nil) 38 if err != nil { 39 t.Fatalf("failed to bootstrap ACLs: %v", err) 40 } 41 client.SetSecretID(root.SecretID) 42 return client, server, root 43 } 44 45 func makeClient(t *testing.T, cb1 configCallback, 46 cb2 testutil.ServerConfigCallback) (*Client, *testutil.TestServer) { 47 // Make client config 48 conf := DefaultConfig() 49 if cb1 != nil { 50 cb1(conf) 51 } 52 53 // Create server 54 server := testutil.NewTestServer(t, cb2) 55 conf.Address = "http://" + server.HTTPAddr 56 57 // Create client 58 client, err := NewClient(conf) 59 if err != nil { 60 t.Fatalf("err: %v", err) 61 } 62 63 return client, server 64 } 65 66 func TestRequestTime(t *testing.T) { 67 t.Parallel() 68 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 69 time.Sleep(100 * time.Millisecond) 70 d, err := json.Marshal(struct{ Done bool }{true}) 71 if err != nil { 72 http.Error(w, err.Error(), http.StatusInternalServerError) 73 return 74 } 75 w.Write(d) 76 })) 77 defer srv.Close() 78 79 conf := DefaultConfig() 80 conf.Address = srv.URL 81 82 client, err := NewClient(conf) 83 if err != nil { 84 t.Fatalf("err: %v", err) 85 } 86 87 var out interface{} 88 89 qm, err := client.query("/", &out, nil) 90 if err != nil { 91 t.Fatalf("query err: %v", err) 92 } 93 if qm.RequestTime == 0 { 94 t.Errorf("bad request time: %d", qm.RequestTime) 95 } 96 97 wm, err := client.write("/", struct{ S string }{"input"}, &out, nil) 98 if err != nil { 99 t.Fatalf("write err: %v", err) 100 } 101 if wm.RequestTime == 0 { 102 t.Errorf("bad request time: %d", wm.RequestTime) 103 } 104 105 wm, err = client.delete("/", &out, nil) 106 if err != nil { 107 t.Fatalf("delete err: %v", err) 108 } 109 if wm.RequestTime == 0 { 110 t.Errorf("bad request time: %d", wm.RequestTime) 111 } 112 } 113 114 func TestDefaultConfig_env(t *testing.T) { 115 t.Parallel() 116 url := "http://1.2.3.4:5678" 117 auth := []string{"nomaduser", "12345"} 118 region := "test" 119 namespace := "dev" 120 token := "foobar" 121 122 os.Setenv("NOMAD_ADDR", url) 123 defer os.Setenv("NOMAD_ADDR", "") 124 125 os.Setenv("NOMAD_REGION", region) 126 defer os.Setenv("NOMAD_REGION", "") 127 128 os.Setenv("NOMAD_NAMESPACE", namespace) 129 defer os.Setenv("NOMAD_NAMESPACE", "") 130 131 os.Setenv("NOMAD_HTTP_AUTH", strings.Join(auth, ":")) 132 defer os.Setenv("NOMAD_HTTP_AUTH", "") 133 134 os.Setenv("NOMAD_TOKEN", token) 135 defer os.Setenv("NOMAD_TOKEN", "") 136 137 config := DefaultConfig() 138 139 if config.Address != url { 140 t.Errorf("expected %q to be %q", config.Address, url) 141 } 142 143 if config.Region != region { 144 t.Errorf("expected %q to be %q", config.Region, region) 145 } 146 147 if config.Namespace != namespace { 148 t.Errorf("expected %q to be %q", config.Namespace, namespace) 149 } 150 151 if config.HttpAuth.Username != auth[0] { 152 t.Errorf("expected %q to be %q", config.HttpAuth.Username, auth[0]) 153 } 154 155 if config.HttpAuth.Password != auth[1] { 156 t.Errorf("expected %q to be %q", config.HttpAuth.Password, auth[1]) 157 } 158 159 if config.SecretID != token { 160 t.Errorf("Expected %q to be %q", config.SecretID, token) 161 } 162 } 163 164 func TestSetQueryOptions(t *testing.T) { 165 t.Parallel() 166 c, s := makeClient(t, nil, nil) 167 defer s.Stop() 168 169 r, _ := c.newRequest("GET", "/v1/jobs") 170 q := &QueryOptions{ 171 Region: "foo", 172 Namespace: "bar", 173 AllowStale: true, 174 WaitIndex: 1000, 175 WaitTime: 100 * time.Second, 176 AuthToken: "foobar", 177 } 178 r.setQueryOptions(q) 179 180 if r.params.Get("region") != "foo" { 181 t.Fatalf("bad: %v", r.params) 182 } 183 if r.params.Get("namespace") != "bar" { 184 t.Fatalf("bad: %v", r.params) 185 } 186 if _, ok := r.params["stale"]; !ok { 187 t.Fatalf("bad: %v", r.params) 188 } 189 if r.params.Get("index") != "1000" { 190 t.Fatalf("bad: %v", r.params) 191 } 192 if r.params.Get("wait") != "100000ms" { 193 t.Fatalf("bad: %v", r.params) 194 } 195 if r.token != "foobar" { 196 t.Fatalf("bad: %v", r.token) 197 } 198 } 199 200 func TestSetWriteOptions(t *testing.T) { 201 t.Parallel() 202 c, s := makeClient(t, nil, nil) 203 defer s.Stop() 204 205 r, _ := c.newRequest("GET", "/v1/jobs") 206 q := &WriteOptions{ 207 Region: "foo", 208 Namespace: "bar", 209 AuthToken: "foobar", 210 } 211 r.setWriteOptions(q) 212 213 if r.params.Get("region") != "foo" { 214 t.Fatalf("bad: %v", r.params) 215 } 216 if r.params.Get("namespace") != "bar" { 217 t.Fatalf("bad: %v", r.params) 218 } 219 if r.token != "foobar" { 220 t.Fatalf("bad: %v", r.token) 221 } 222 } 223 224 func TestRequestToHTTP(t *testing.T) { 225 t.Parallel() 226 c, s := makeClient(t, nil, nil) 227 defer s.Stop() 228 229 r, _ := c.newRequest("DELETE", "/v1/jobs/foo") 230 q := &QueryOptions{ 231 Region: "foo", 232 Namespace: "bar", 233 AuthToken: "foobar", 234 } 235 r.setQueryOptions(q) 236 req, err := r.toHTTP() 237 if err != nil { 238 t.Fatalf("err: %v", err) 239 } 240 241 if req.Method != "DELETE" { 242 t.Fatalf("bad: %v", req) 243 } 244 if req.URL.RequestURI() != "/v1/jobs/foo?namespace=bar®ion=foo" { 245 t.Fatalf("bad: %v", req) 246 } 247 if req.Header.Get("X-Nomad-Token") != "foobar" { 248 t.Fatalf("bad: %v", req) 249 } 250 } 251 252 func TestParseQueryMeta(t *testing.T) { 253 t.Parallel() 254 resp := &http.Response{ 255 Header: make(map[string][]string), 256 } 257 resp.Header.Set("X-Nomad-Index", "12345") 258 resp.Header.Set("X-Nomad-LastContact", "80") 259 resp.Header.Set("X-Nomad-KnownLeader", "true") 260 261 qm := &QueryMeta{} 262 if err := parseQueryMeta(resp, qm); err != nil { 263 t.Fatalf("err: %v", err) 264 } 265 266 if qm.LastIndex != 12345 { 267 t.Fatalf("Bad: %v", qm) 268 } 269 if qm.LastContact != 80*time.Millisecond { 270 t.Fatalf("Bad: %v", qm) 271 } 272 if !qm.KnownLeader { 273 t.Fatalf("Bad: %v", qm) 274 } 275 } 276 277 func TestParseWriteMeta(t *testing.T) { 278 t.Parallel() 279 resp := &http.Response{ 280 Header: make(map[string][]string), 281 } 282 resp.Header.Set("X-Nomad-Index", "12345") 283 284 wm := &WriteMeta{} 285 if err := parseWriteMeta(resp, wm); err != nil { 286 t.Fatalf("err: %v", err) 287 } 288 289 if wm.LastIndex != 12345 { 290 t.Fatalf("Bad: %v", wm) 291 } 292 } 293 294 func TestQueryString(t *testing.T) { 295 t.Parallel() 296 c, s := makeClient(t, nil, nil) 297 defer s.Stop() 298 299 r, _ := c.newRequest("PUT", "/v1/abc?foo=bar&baz=zip") 300 q := &WriteOptions{ 301 Region: "foo", 302 Namespace: "bar", 303 } 304 r.setWriteOptions(q) 305 306 req, err := r.toHTTP() 307 if err != nil { 308 t.Fatalf("err: %s", err) 309 } 310 311 if uri := req.URL.RequestURI(); uri != "/v1/abc?baz=zip&foo=bar&namespace=bar®ion=foo" { 312 t.Fatalf("bad uri: %q", uri) 313 } 314 } 315 316 func TestClient_NodeClient(t *testing.T) { 317 http := "testdomain:4646" 318 tlsNode := func(string, *QueryOptions) (*Node, *QueryMeta, error) { 319 return &Node{ 320 ID: uuid.Generate(), 321 Status: "ready", 322 HTTPAddr: http, 323 TLSEnabled: true, 324 }, nil, nil 325 } 326 noTlsNode := func(string, *QueryOptions) (*Node, *QueryMeta, error) { 327 return &Node{ 328 ID: uuid.Generate(), 329 Status: "ready", 330 HTTPAddr: http, 331 TLSEnabled: false, 332 }, nil, nil 333 } 334 335 optionNoRegion := &QueryOptions{} 336 optionRegion := &QueryOptions{ 337 Region: "foo", 338 } 339 340 clientNoRegion, err := NewClient(DefaultConfig()) 341 assert.Nil(t, err) 342 343 regionConfig := DefaultConfig() 344 regionConfig.Region = "bar" 345 clientRegion, err := NewClient(regionConfig) 346 assert.Nil(t, err) 347 348 expectedTLSAddr := fmt.Sprintf("https://%s", http) 349 expectedNoTLSAddr := fmt.Sprintf("http://%s", http) 350 351 cases := []struct { 352 Node nodeLookup 353 QueryOptions *QueryOptions 354 Client *Client 355 ExpectedAddr string 356 ExpectedRegion string 357 ExpectedTLSServerName string 358 }{ 359 { 360 Node: tlsNode, 361 QueryOptions: optionNoRegion, 362 Client: clientNoRegion, 363 ExpectedAddr: expectedTLSAddr, 364 ExpectedRegion: "global", 365 ExpectedTLSServerName: "client.global.nomad", 366 }, 367 { 368 Node: tlsNode, 369 QueryOptions: optionRegion, 370 Client: clientNoRegion, 371 ExpectedAddr: expectedTLSAddr, 372 ExpectedRegion: "foo", 373 ExpectedTLSServerName: "client.foo.nomad", 374 }, 375 { 376 Node: tlsNode, 377 QueryOptions: optionRegion, 378 Client: clientRegion, 379 ExpectedAddr: expectedTLSAddr, 380 ExpectedRegion: "foo", 381 ExpectedTLSServerName: "client.foo.nomad", 382 }, 383 { 384 Node: tlsNode, 385 QueryOptions: optionNoRegion, 386 Client: clientRegion, 387 ExpectedAddr: expectedTLSAddr, 388 ExpectedRegion: "bar", 389 ExpectedTLSServerName: "client.bar.nomad", 390 }, 391 { 392 Node: noTlsNode, 393 QueryOptions: optionNoRegion, 394 Client: clientNoRegion, 395 ExpectedAddr: expectedNoTLSAddr, 396 ExpectedRegion: "global", 397 ExpectedTLSServerName: "", 398 }, 399 { 400 Node: noTlsNode, 401 QueryOptions: optionRegion, 402 Client: clientNoRegion, 403 ExpectedAddr: expectedNoTLSAddr, 404 ExpectedRegion: "foo", 405 ExpectedTLSServerName: "", 406 }, 407 { 408 Node: noTlsNode, 409 QueryOptions: optionRegion, 410 Client: clientRegion, 411 ExpectedAddr: expectedNoTLSAddr, 412 ExpectedRegion: "foo", 413 ExpectedTLSServerName: "", 414 }, 415 { 416 Node: noTlsNode, 417 QueryOptions: optionNoRegion, 418 Client: clientRegion, 419 ExpectedAddr: expectedNoTLSAddr, 420 ExpectedRegion: "bar", 421 ExpectedTLSServerName: "", 422 }, 423 } 424 425 for _, c := range cases { 426 name := fmt.Sprintf("%s__%s__%s", c.ExpectedAddr, c.ExpectedRegion, c.ExpectedTLSServerName) 427 t.Run(name, func(t *testing.T) { 428 assert := assert.New(t) 429 nodeClient, err := c.Client.getNodeClientImpl("testID", c.QueryOptions, c.Node) 430 assert.Nil(err) 431 assert.Equal(c.ExpectedRegion, nodeClient.config.Region) 432 assert.Equal(c.ExpectedAddr, nodeClient.config.Address) 433 assert.NotNil(nodeClient.config.TLSConfig) 434 assert.Equal(c.ExpectedTLSServerName, nodeClient.config.TLSConfig.TLSServerName) 435 }) 436 } 437 }