github.com/outbrain/consul@v1.4.5/agent/health_endpoint_test.go (about) 1 package agent 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "net/http" 8 "net/http/httptest" 9 "reflect" 10 "testing" 11 12 "github.com/hashicorp/consul/agent/structs" 13 "github.com/hashicorp/consul/api" 14 "github.com/hashicorp/consul/testrpc" 15 "github.com/hashicorp/consul/testutil/retry" 16 "github.com/hashicorp/serf/coordinate" 17 "github.com/stretchr/testify/assert" 18 "github.com/stretchr/testify/require" 19 ) 20 21 func TestHealthChecksInState(t *testing.T) { 22 t.Parallel() 23 t.Run("warning", func(t *testing.T) { 24 a := NewTestAgent(t, t.Name(), "") 25 defer a.Shutdown() 26 27 req, _ := http.NewRequest("GET", "/v1/health/state/warning?dc=dc1", nil) 28 retry.Run(t, func(r *retry.R) { 29 resp := httptest.NewRecorder() 30 obj, err := a.srv.HealthChecksInState(resp, req) 31 if err != nil { 32 r.Fatal(err) 33 } 34 if err := checkIndex(resp); err != nil { 35 r.Fatal(err) 36 } 37 38 // Should be a non-nil empty list 39 nodes := obj.(structs.HealthChecks) 40 if nodes == nil || len(nodes) != 0 { 41 r.Fatalf("bad: %v", obj) 42 } 43 }) 44 }) 45 46 t.Run("passing", func(t *testing.T) { 47 a := NewTestAgent(t, t.Name(), "") 48 defer a.Shutdown() 49 50 req, _ := http.NewRequest("GET", "/v1/health/state/passing?dc=dc1", nil) 51 retry.Run(t, func(r *retry.R) { 52 resp := httptest.NewRecorder() 53 obj, err := a.srv.HealthChecksInState(resp, req) 54 if err != nil { 55 r.Fatal(err) 56 } 57 if err := checkIndex(resp); err != nil { 58 r.Fatal(err) 59 } 60 61 // Should be 1 health check for the server 62 nodes := obj.(structs.HealthChecks) 63 if len(nodes) != 1 { 64 r.Fatalf("bad: %v", obj) 65 } 66 }) 67 }) 68 } 69 70 func TestHealthChecksInState_NodeMetaFilter(t *testing.T) { 71 t.Parallel() 72 a := NewTestAgent(t, t.Name(), "") 73 defer a.Shutdown() 74 75 args := &structs.RegisterRequest{ 76 Datacenter: "dc1", 77 Node: "bar", 78 Address: "127.0.0.1", 79 NodeMeta: map[string]string{"somekey": "somevalue"}, 80 Check: &structs.HealthCheck{ 81 Node: "bar", 82 Name: "node check", 83 Status: api.HealthCritical, 84 }, 85 } 86 var out struct{} 87 if err := a.RPC("Catalog.Register", args, &out); err != nil { 88 t.Fatalf("err: %v", err) 89 } 90 91 req, _ := http.NewRequest("GET", "/v1/health/state/critical?node-meta=somekey:somevalue", nil) 92 retry.Run(t, func(r *retry.R) { 93 resp := httptest.NewRecorder() 94 obj, err := a.srv.HealthChecksInState(resp, req) 95 if err != nil { 96 r.Fatal(err) 97 } 98 if err := checkIndex(resp); err != nil { 99 r.Fatal(err) 100 } 101 102 // Should be 1 health check for the server 103 nodes := obj.(structs.HealthChecks) 104 if len(nodes) != 1 { 105 r.Fatalf("bad: %v", obj) 106 } 107 }) 108 } 109 110 func TestHealthChecksInState_DistanceSort(t *testing.T) { 111 t.Parallel() 112 a := NewTestAgent(t, t.Name(), "") 113 defer a.Shutdown() 114 115 args := &structs.RegisterRequest{ 116 Datacenter: "dc1", 117 Node: "bar", 118 Address: "127.0.0.1", 119 Check: &structs.HealthCheck{ 120 Node: "bar", 121 Name: "node check", 122 Status: api.HealthCritical, 123 }, 124 } 125 126 var out struct{} 127 if err := a.RPC("Catalog.Register", args, &out); err != nil { 128 t.Fatalf("err: %v", err) 129 } 130 131 args.Node, args.Check.Node = "foo", "foo" 132 if err := a.RPC("Catalog.Register", args, &out); err != nil { 133 t.Fatalf("err: %v", err) 134 } 135 136 req, _ := http.NewRequest("GET", "/v1/health/state/critical?dc=dc1&near=foo", nil) 137 resp := httptest.NewRecorder() 138 obj, err := a.srv.HealthChecksInState(resp, req) 139 if err != nil { 140 t.Fatalf("err: %v", err) 141 } 142 assertIndex(t, resp) 143 nodes := obj.(structs.HealthChecks) 144 if len(nodes) != 2 { 145 t.Fatalf("bad: %v", nodes) 146 } 147 if nodes[0].Node != "bar" { 148 t.Fatalf("bad: %v", nodes) 149 } 150 if nodes[1].Node != "foo" { 151 t.Fatalf("bad: %v", nodes) 152 } 153 154 // Send an update for the node and wait for it to get applied. 155 arg := structs.CoordinateUpdateRequest{ 156 Datacenter: "dc1", 157 Node: "foo", 158 Coord: coordinate.NewCoordinate(coordinate.DefaultConfig()), 159 } 160 if err := a.RPC("Coordinate.Update", &arg, &out); err != nil { 161 t.Fatalf("err: %v", err) 162 } 163 // Retry until foo moves to the front of the line. 164 retry.Run(t, func(r *retry.R) { 165 resp = httptest.NewRecorder() 166 obj, err = a.srv.HealthChecksInState(resp, req) 167 if err != nil { 168 r.Fatalf("err: %v", err) 169 } 170 assertIndex(t, resp) 171 nodes = obj.(structs.HealthChecks) 172 if len(nodes) != 2 { 173 r.Fatalf("bad: %v", nodes) 174 } 175 if nodes[0].Node != "foo" { 176 r.Fatalf("bad: %v", nodes) 177 } 178 if nodes[1].Node != "bar" { 179 r.Fatalf("bad: %v", nodes) 180 } 181 }) 182 } 183 184 func TestHealthNodeChecks(t *testing.T) { 185 t.Parallel() 186 a := NewTestAgent(t, t.Name(), "") 187 defer a.Shutdown() 188 testrpc.WaitForTestAgent(t, a.RPC, "dc1") 189 190 req, _ := http.NewRequest("GET", "/v1/health/node/nope?dc=dc1", nil) 191 resp := httptest.NewRecorder() 192 obj, err := a.srv.HealthNodeChecks(resp, req) 193 if err != nil { 194 t.Fatalf("err: %v", err) 195 } 196 assertIndex(t, resp) 197 198 // Should be a non-nil empty list 199 nodes := obj.(structs.HealthChecks) 200 if nodes == nil || len(nodes) != 0 { 201 t.Fatalf("bad: %v", obj) 202 } 203 204 req, _ = http.NewRequest("GET", fmt.Sprintf("/v1/health/node/%s?dc=dc1", a.Config.NodeName), nil) 205 resp = httptest.NewRecorder() 206 obj, err = a.srv.HealthNodeChecks(resp, req) 207 if err != nil { 208 t.Fatalf("err: %v", err) 209 } 210 assertIndex(t, resp) 211 212 // Should be 1 health check for the server 213 nodes = obj.(structs.HealthChecks) 214 if len(nodes) != 1 { 215 t.Fatalf("bad: %v", obj) 216 } 217 } 218 219 func TestHealthServiceChecks(t *testing.T) { 220 t.Parallel() 221 a := NewTestAgent(t, t.Name(), "") 222 defer a.Shutdown() 223 testrpc.WaitForTestAgent(t, a.RPC, "dc1") 224 225 req, _ := http.NewRequest("GET", "/v1/health/checks/consul?dc=dc1", nil) 226 resp := httptest.NewRecorder() 227 obj, err := a.srv.HealthServiceChecks(resp, req) 228 if err != nil { 229 t.Fatalf("err: %v", err) 230 } 231 assertIndex(t, resp) 232 233 // Should be a non-nil empty list 234 nodes := obj.(structs.HealthChecks) 235 if nodes == nil || len(nodes) != 0 { 236 t.Fatalf("bad: %v", obj) 237 } 238 239 // Create a service check 240 args := &structs.RegisterRequest{ 241 Datacenter: "dc1", 242 Node: a.Config.NodeName, 243 Address: "127.0.0.1", 244 Check: &structs.HealthCheck{ 245 Node: a.Config.NodeName, 246 Name: "consul check", 247 ServiceID: "consul", 248 }, 249 } 250 251 var out struct{} 252 if err = a.RPC("Catalog.Register", args, &out); err != nil { 253 t.Fatalf("err: %v", err) 254 } 255 256 req, _ = http.NewRequest("GET", "/v1/health/checks/consul?dc=dc1", nil) 257 resp = httptest.NewRecorder() 258 obj, err = a.srv.HealthServiceChecks(resp, req) 259 if err != nil { 260 t.Fatalf("err: %v", err) 261 } 262 assertIndex(t, resp) 263 264 // Should be 1 health check for consul 265 nodes = obj.(structs.HealthChecks) 266 if len(nodes) != 1 { 267 t.Fatalf("bad: %v", obj) 268 } 269 } 270 271 func TestHealthServiceChecks_NodeMetaFilter(t *testing.T) { 272 t.Parallel() 273 a := NewTestAgent(t, t.Name(), "") 274 defer a.Shutdown() 275 testrpc.WaitForTestAgent(t, a.RPC, "dc1") 276 277 req, _ := http.NewRequest("GET", "/v1/health/checks/consul?dc=dc1&node-meta=somekey:somevalue", nil) 278 resp := httptest.NewRecorder() 279 obj, err := a.srv.HealthServiceChecks(resp, req) 280 if err != nil { 281 t.Fatalf("err: %v", err) 282 } 283 assertIndex(t, resp) 284 285 // Should be a non-nil empty list 286 nodes := obj.(structs.HealthChecks) 287 if nodes == nil || len(nodes) != 0 { 288 t.Fatalf("bad: %v", obj) 289 } 290 291 // Create a service check 292 args := &structs.RegisterRequest{ 293 Datacenter: "dc1", 294 Node: a.Config.NodeName, 295 Address: "127.0.0.1", 296 NodeMeta: map[string]string{"somekey": "somevalue"}, 297 Check: &structs.HealthCheck{ 298 Node: a.Config.NodeName, 299 Name: "consul check", 300 ServiceID: "consul", 301 }, 302 } 303 304 var out struct{} 305 if err = a.RPC("Catalog.Register", args, &out); err != nil { 306 t.Fatalf("err: %v", err) 307 } 308 309 req, _ = http.NewRequest("GET", "/v1/health/checks/consul?dc=dc1&node-meta=somekey:somevalue", nil) 310 resp = httptest.NewRecorder() 311 obj, err = a.srv.HealthServiceChecks(resp, req) 312 if err != nil { 313 t.Fatalf("err: %v", err) 314 } 315 assertIndex(t, resp) 316 317 // Should be 1 health check for consul 318 nodes = obj.(structs.HealthChecks) 319 if len(nodes) != 1 { 320 t.Fatalf("bad: %v", obj) 321 } 322 } 323 324 func TestHealthServiceChecks_DistanceSort(t *testing.T) { 325 t.Parallel() 326 a := NewTestAgent(t, t.Name(), "") 327 defer a.Shutdown() 328 testrpc.WaitForTestAgent(t, a.RPC, "dc1") 329 330 // Create a service check 331 args := &structs.RegisterRequest{ 332 Datacenter: "dc1", 333 Node: "bar", 334 Address: "127.0.0.1", 335 Service: &structs.NodeService{ 336 ID: "test", 337 Service: "test", 338 }, 339 Check: &structs.HealthCheck{ 340 Node: "bar", 341 Name: "test check", 342 ServiceID: "test", 343 }, 344 } 345 346 var out struct{} 347 if err := a.RPC("Catalog.Register", args, &out); err != nil { 348 t.Fatalf("err: %v", err) 349 } 350 351 args.Node, args.Check.Node = "foo", "foo" 352 if err := a.RPC("Catalog.Register", args, &out); err != nil { 353 t.Fatalf("err: %v", err) 354 } 355 356 req, _ := http.NewRequest("GET", "/v1/health/checks/test?dc=dc1&near=foo", nil) 357 resp := httptest.NewRecorder() 358 obj, err := a.srv.HealthServiceChecks(resp, req) 359 if err != nil { 360 t.Fatalf("err: %v", err) 361 } 362 assertIndex(t, resp) 363 nodes := obj.(structs.HealthChecks) 364 if len(nodes) != 2 { 365 t.Fatalf("bad: %v", obj) 366 } 367 if nodes[0].Node != "bar" { 368 t.Fatalf("bad: %v", nodes) 369 } 370 if nodes[1].Node != "foo" { 371 t.Fatalf("bad: %v", nodes) 372 } 373 374 // Send an update for the node and wait for it to get applied. 375 arg := structs.CoordinateUpdateRequest{ 376 Datacenter: "dc1", 377 Node: "foo", 378 Coord: coordinate.NewCoordinate(coordinate.DefaultConfig()), 379 } 380 if err := a.RPC("Coordinate.Update", &arg, &out); err != nil { 381 t.Fatalf("err: %v", err) 382 } 383 // Retry until foo has moved to the front of the line. 384 retry.Run(t, func(r *retry.R) { 385 resp = httptest.NewRecorder() 386 obj, err = a.srv.HealthServiceChecks(resp, req) 387 if err != nil { 388 r.Fatalf("err: %v", err) 389 } 390 assertIndex(t, resp) 391 nodes = obj.(structs.HealthChecks) 392 if len(nodes) != 2 { 393 r.Fatalf("bad: %v", obj) 394 } 395 if nodes[0].Node != "foo" { 396 r.Fatalf("bad: %v", nodes) 397 } 398 if nodes[1].Node != "bar" { 399 r.Fatalf("bad: %v", nodes) 400 } 401 }) 402 } 403 404 func TestHealthServiceNodes(t *testing.T) { 405 t.Parallel() 406 a := NewTestAgent(t, t.Name(), "") 407 defer a.Shutdown() 408 testrpc.WaitForTestAgent(t, a.RPC, "dc1") 409 410 assert := assert.New(t) 411 require := require.New(t) 412 413 req, _ := http.NewRequest("GET", "/v1/health/service/consul?dc=dc1", nil) 414 resp := httptest.NewRecorder() 415 obj, err := a.srv.HealthServiceNodes(resp, req) 416 if err != nil { 417 t.Fatalf("err: %v", err) 418 } 419 420 assertIndex(t, resp) 421 422 // Should be 1 health check for consul 423 nodes := obj.(structs.CheckServiceNodes) 424 if len(nodes) != 1 { 425 t.Fatalf("bad: %v", obj) 426 } 427 428 req, _ = http.NewRequest("GET", "/v1/health/service/nope?dc=dc1", nil) 429 resp = httptest.NewRecorder() 430 obj, err = a.srv.HealthServiceNodes(resp, req) 431 if err != nil { 432 t.Fatalf("err: %v", err) 433 } 434 435 assertIndex(t, resp) 436 437 // Should be a non-nil empty list 438 nodes = obj.(structs.CheckServiceNodes) 439 if nodes == nil || len(nodes) != 0 { 440 t.Fatalf("bad: %v", obj) 441 } 442 443 args := &structs.RegisterRequest{ 444 Datacenter: "dc1", 445 Node: "bar", 446 Address: "127.0.0.1", 447 Service: &structs.NodeService{ 448 ID: "test", 449 Service: "test", 450 }, 451 } 452 453 var out struct{} 454 if err := a.RPC("Catalog.Register", args, &out); err != nil { 455 t.Fatalf("err: %v", err) 456 } 457 458 req, _ = http.NewRequest("GET", "/v1/health/service/test?dc=dc1", nil) 459 resp = httptest.NewRecorder() 460 obj, err = a.srv.HealthServiceNodes(resp, req) 461 if err != nil { 462 t.Fatalf("err: %v", err) 463 } 464 465 assertIndex(t, resp) 466 467 // Should be a non-nil empty list for checks 468 nodes = obj.(structs.CheckServiceNodes) 469 if len(nodes) != 1 || nodes[0].Checks == nil || len(nodes[0].Checks) != 0 { 470 t.Fatalf("bad: %v", obj) 471 } 472 473 // Test caching 474 { 475 // List instances with cache enabled 476 req, _ := http.NewRequest("GET", "/v1/health/service/test?cached", nil) 477 resp := httptest.NewRecorder() 478 obj, err := a.srv.HealthServiceNodes(resp, req) 479 require.NoError(err) 480 nodes := obj.(structs.CheckServiceNodes) 481 assert.Len(nodes, 1) 482 483 // Should be a cache miss 484 assert.Equal("MISS", resp.Header().Get("X-Cache")) 485 } 486 487 { 488 // List instances with cache enabled 489 req, _ := http.NewRequest("GET", "/v1/health/service/test?cached", nil) 490 resp := httptest.NewRecorder() 491 obj, err := a.srv.HealthServiceNodes(resp, req) 492 require.NoError(err) 493 nodes := obj.(structs.CheckServiceNodes) 494 assert.Len(nodes, 1) 495 496 // Should be a cache HIT now! 497 assert.Equal("HIT", resp.Header().Get("X-Cache")) 498 } 499 500 // Ensure background refresh works 501 { 502 // Register a new instance of the service 503 args2 := args 504 args2.Node = "baz" 505 args2.Address = "127.0.0.2" 506 require.NoError(a.RPC("Catalog.Register", args, &out)) 507 508 retry.Run(t, func(r *retry.R) { 509 // List it again 510 req, _ := http.NewRequest("GET", "/v1/health/service/test?cached", nil) 511 resp := httptest.NewRecorder() 512 obj, err := a.srv.HealthServiceNodes(resp, req) 513 r.Check(err) 514 515 nodes := obj.(structs.CheckServiceNodes) 516 if len(nodes) != 2 { 517 r.Fatalf("Want 2 nodes") 518 } 519 520 // Should be a cache hit! The data should've updated in the cache 521 // in the background so this should've been fetched directly from 522 // the cache. 523 if resp.Header().Get("X-Cache") != "HIT" { 524 r.Fatalf("should be a cache hit") 525 } 526 }) 527 } 528 } 529 530 func TestHealthServiceNodes_NodeMetaFilter(t *testing.T) { 531 t.Parallel() 532 a := NewTestAgent(t, t.Name(), "") 533 defer a.Shutdown() 534 testrpc.WaitForLeader(t, a.RPC, "dc1") 535 536 req, _ := http.NewRequest("GET", "/v1/health/service/consul?dc=dc1&node-meta=somekey:somevalue", nil) 537 resp := httptest.NewRecorder() 538 obj, err := a.srv.HealthServiceNodes(resp, req) 539 if err != nil { 540 t.Fatalf("err: %v", err) 541 } 542 543 assertIndex(t, resp) 544 545 // Should be a non-nil empty list 546 nodes := obj.(structs.CheckServiceNodes) 547 if nodes == nil || len(nodes) != 0 { 548 t.Fatalf("bad: %v", obj) 549 } 550 551 args := &structs.RegisterRequest{ 552 Datacenter: "dc1", 553 Node: "bar", 554 Address: "127.0.0.1", 555 NodeMeta: map[string]string{"somekey": "somevalue"}, 556 Service: &structs.NodeService{ 557 ID: "test", 558 Service: "test", 559 }, 560 } 561 562 var out struct{} 563 if err := a.RPC("Catalog.Register", args, &out); err != nil { 564 t.Fatalf("err: %v", err) 565 } 566 567 req, _ = http.NewRequest("GET", "/v1/health/service/test?dc=dc1&node-meta=somekey:somevalue", nil) 568 resp = httptest.NewRecorder() 569 obj, err = a.srv.HealthServiceNodes(resp, req) 570 if err != nil { 571 t.Fatalf("err: %v", err) 572 } 573 574 assertIndex(t, resp) 575 576 // Should be a non-nil empty list for checks 577 nodes = obj.(structs.CheckServiceNodes) 578 if len(nodes) != 1 || nodes[0].Checks == nil || len(nodes[0].Checks) != 0 { 579 t.Fatalf("bad: %v", obj) 580 } 581 } 582 583 func TestHealthServiceNodes_DistanceSort(t *testing.T) { 584 t.Parallel() 585 a := NewTestAgent(t, t.Name(), "") 586 defer a.Shutdown() 587 dc := "dc1" 588 // Create a service check 589 args := &structs.RegisterRequest{ 590 Datacenter: dc, 591 Node: "bar", 592 Address: "127.0.0.1", 593 Service: &structs.NodeService{ 594 ID: "test", 595 Service: "test", 596 }, 597 Check: &structs.HealthCheck{ 598 Node: "bar", 599 Name: "test check", 600 ServiceID: "test", 601 }, 602 } 603 testrpc.WaitForLeader(t, a.RPC, dc) 604 var out struct{} 605 if err := a.RPC("Catalog.Register", args, &out); err != nil { 606 t.Fatalf("err: %v", err) 607 } 608 609 args.Node, args.Check.Node = "foo", "foo" 610 if err := a.RPC("Catalog.Register", args, &out); err != nil { 611 t.Fatalf("err: %v", err) 612 } 613 614 req, _ := http.NewRequest("GET", "/v1/health/service/test?dc=dc1&near=foo", nil) 615 resp := httptest.NewRecorder() 616 obj, err := a.srv.HealthServiceNodes(resp, req) 617 if err != nil { 618 t.Fatalf("err: %v", err) 619 } 620 assertIndex(t, resp) 621 nodes := obj.(structs.CheckServiceNodes) 622 if len(nodes) != 2 { 623 t.Fatalf("bad: %v", obj) 624 } 625 if nodes[0].Node.Node != "bar" { 626 t.Fatalf("bad: %v", nodes) 627 } 628 if nodes[1].Node.Node != "foo" { 629 t.Fatalf("bad: %v", nodes) 630 } 631 632 // Send an update for the node and wait for it to get applied. 633 arg := structs.CoordinateUpdateRequest{ 634 Datacenter: "dc1", 635 Node: "foo", 636 Coord: coordinate.NewCoordinate(coordinate.DefaultConfig()), 637 } 638 if err := a.RPC("Coordinate.Update", &arg, &out); err != nil { 639 t.Fatalf("err: %v", err) 640 } 641 // Retry until foo has moved to the front of the line. 642 retry.Run(t, func(r *retry.R) { 643 resp = httptest.NewRecorder() 644 obj, err = a.srv.HealthServiceNodes(resp, req) 645 if err != nil { 646 r.Fatalf("err: %v", err) 647 } 648 assertIndex(t, resp) 649 nodes = obj.(structs.CheckServiceNodes) 650 if len(nodes) != 2 { 651 r.Fatalf("bad: %v", obj) 652 } 653 if nodes[0].Node.Node != "foo" { 654 r.Fatalf("bad: %v", nodes) 655 } 656 if nodes[1].Node.Node != "bar" { 657 r.Fatalf("bad: %v", nodes) 658 } 659 }) 660 } 661 662 func TestHealthServiceNodes_PassingFilter(t *testing.T) { 663 t.Parallel() 664 a := NewTestAgent(t, t.Name(), "") 665 defer a.Shutdown() 666 667 dc := "dc1" 668 // Create a failing service check 669 args := &structs.RegisterRequest{ 670 Datacenter: dc, 671 Node: a.Config.NodeName, 672 Address: "127.0.0.1", 673 Check: &structs.HealthCheck{ 674 Node: a.Config.NodeName, 675 Name: "consul check", 676 ServiceID: "consul", 677 Status: api.HealthCritical, 678 }, 679 } 680 681 testrpc.WaitForLeader(t, a.RPC, dc) 682 var out struct{} 683 if err := a.RPC("Catalog.Register", args, &out); err != nil { 684 t.Fatalf("err: %v", err) 685 } 686 687 t.Run("bc_no_query_value", func(t *testing.T) { 688 req, _ := http.NewRequest("GET", "/v1/health/service/consul?passing", nil) 689 resp := httptest.NewRecorder() 690 obj, err := a.srv.HealthServiceNodes(resp, req) 691 if err != nil { 692 t.Fatalf("err: %v", err) 693 } 694 695 assertIndex(t, resp) 696 697 // Should be 0 health check for consul 698 nodes := obj.(structs.CheckServiceNodes) 699 if len(nodes) != 0 { 700 t.Fatalf("bad: %v", obj) 701 } 702 }) 703 704 t.Run("passing_true", func(t *testing.T) { 705 req, _ := http.NewRequest("GET", "/v1/health/service/consul?passing=true", nil) 706 resp := httptest.NewRecorder() 707 obj, err := a.srv.HealthServiceNodes(resp, req) 708 if err != nil { 709 t.Fatalf("err: %v", err) 710 } 711 712 assertIndex(t, resp) 713 714 // Should be 0 health check for consul 715 nodes := obj.(structs.CheckServiceNodes) 716 if len(nodes) != 0 { 717 t.Fatalf("bad: %v", obj) 718 } 719 }) 720 721 t.Run("passing_false", func(t *testing.T) { 722 req, _ := http.NewRequest("GET", "/v1/health/service/consul?passing=false", nil) 723 resp := httptest.NewRecorder() 724 obj, err := a.srv.HealthServiceNodes(resp, req) 725 if err != nil { 726 t.Fatalf("err: %v", err) 727 } 728 729 assertIndex(t, resp) 730 731 // Should be 1 consul, it's unhealthy, but we specifically asked for 732 // everything. 733 nodes := obj.(structs.CheckServiceNodes) 734 if len(nodes) != 1 { 735 t.Fatalf("bad: %v", obj) 736 } 737 }) 738 739 t.Run("passing_bad", func(t *testing.T) { 740 req, _ := http.NewRequest("GET", "/v1/health/service/consul?passing=nope-nope-nope", nil) 741 resp := httptest.NewRecorder() 742 a.srv.HealthServiceNodes(resp, req) 743 744 if code := resp.Code; code != 400 { 745 t.Errorf("bad response code %d, expected %d", code, 400) 746 } 747 748 body, err := ioutil.ReadAll(resp.Body) 749 if err != nil { 750 t.Fatal(err) 751 } 752 if !bytes.Contains(body, []byte("Invalid value for ?passing")) { 753 t.Errorf("bad %s", body) 754 } 755 }) 756 } 757 758 func TestHealthServiceNodes_WanTranslation(t *testing.T) { 759 t.Parallel() 760 a1 := NewTestAgent(t, t.Name(), ` 761 datacenter = "dc1" 762 translate_wan_addrs = true 763 acl_datacenter = "" 764 `) 765 defer a1.Shutdown() 766 testrpc.WaitForLeader(t, a1.RPC, "dc1") 767 768 a2 := NewTestAgent(t, t.Name(), ` 769 datacenter = "dc2" 770 translate_wan_addrs = true 771 acl_datacenter = "" 772 `) 773 defer a2.Shutdown() 774 testrpc.WaitForLeader(t, a2.RPC, "dc2") 775 776 // Wait for the WAN join. 777 addr := fmt.Sprintf("127.0.0.1:%d", a1.Config.SerfPortWAN) 778 if _, err := a2.JoinWAN([]string{addr}); err != nil { 779 t.Fatalf("err: %v", err) 780 } 781 retry.Run(t, func(r *retry.R) { 782 if got, want := len(a1.WANMembers()), 2; got < want { 783 r.Fatalf("got %d WAN members want at least %d", got, want) 784 } 785 }) 786 787 // Register a node with DC2. 788 { 789 args := &structs.RegisterRequest{ 790 Datacenter: "dc2", 791 Node: "foo", 792 Address: "127.0.0.1", 793 TaggedAddresses: map[string]string{ 794 "wan": "127.0.0.2", 795 }, 796 Service: &structs.NodeService{ 797 Service: "http_wan_translation_test", 798 }, 799 } 800 801 var out struct{} 802 if err := a2.RPC("Catalog.Register", args, &out); err != nil { 803 t.Fatalf("err: %v", err) 804 } 805 } 806 807 // Query for a service in DC2 from DC1. 808 req, _ := http.NewRequest("GET", "/v1/health/service/http_wan_translation_test?dc=dc2", nil) 809 resp1 := httptest.NewRecorder() 810 obj1, err1 := a1.srv.HealthServiceNodes(resp1, req) 811 if err1 != nil { 812 t.Fatalf("err: %v", err1) 813 } 814 assertIndex(t, resp1) 815 816 // Expect that DC1 gives us a WAN address (since the node is in DC2). 817 nodes1 := obj1.(structs.CheckServiceNodes) 818 if len(nodes1) != 1 { 819 t.Fatalf("bad: %v", obj1) 820 } 821 node1 := nodes1[0].Node 822 if node1.Address != "127.0.0.2" { 823 t.Fatalf("bad: %v", node1) 824 } 825 826 // Query DC2 from DC2. 827 resp2 := httptest.NewRecorder() 828 obj2, err2 := a2.srv.HealthServiceNodes(resp2, req) 829 if err2 != nil { 830 t.Fatalf("err: %v", err2) 831 } 832 assertIndex(t, resp2) 833 834 // Expect that DC2 gives us a private address (since the node is in DC2). 835 nodes2 := obj2.(structs.CheckServiceNodes) 836 if len(nodes2) != 1 { 837 t.Fatalf("bad: %v", obj2) 838 } 839 node2 := nodes2[0].Node 840 if node2.Address != "127.0.0.1" { 841 t.Fatalf("bad: %v", node2) 842 } 843 } 844 845 func TestHealthConnectServiceNodes(t *testing.T) { 846 t.Parallel() 847 848 assert := assert.New(t) 849 a := NewTestAgent(t, t.Name(), "") 850 defer a.Shutdown() 851 852 // Register 853 args := structs.TestRegisterRequestProxy(t) 854 var out struct{} 855 assert.Nil(a.RPC("Catalog.Register", args, &out)) 856 857 // Request 858 req, _ := http.NewRequest("GET", fmt.Sprintf( 859 "/v1/health/connect/%s?dc=dc1", args.Service.Proxy.DestinationServiceName), nil) 860 resp := httptest.NewRecorder() 861 obj, err := a.srv.HealthConnectServiceNodes(resp, req) 862 assert.Nil(err) 863 assertIndex(t, resp) 864 865 // Should be a non-nil empty list for checks 866 nodes := obj.(structs.CheckServiceNodes) 867 assert.Len(nodes, 1) 868 assert.Len(nodes[0].Checks, 0) 869 } 870 871 func TestHealthConnectServiceNodes_PassingFilter(t *testing.T) { 872 t.Parallel() 873 874 a := NewTestAgent(t, t.Name(), "") 875 defer a.Shutdown() 876 877 // Register 878 args := structs.TestRegisterRequestProxy(t) 879 args.Check = &structs.HealthCheck{ 880 Node: args.Node, 881 Name: "check", 882 ServiceID: args.Service.Service, 883 Status: api.HealthCritical, 884 } 885 var out struct{} 886 assert.Nil(t, a.RPC("Catalog.Register", args, &out)) 887 888 t.Run("bc_no_query_value", func(t *testing.T) { 889 assert := assert.New(t) 890 req, _ := http.NewRequest("GET", fmt.Sprintf( 891 "/v1/health/connect/%s?passing", args.Service.Proxy.DestinationServiceName), nil) 892 resp := httptest.NewRecorder() 893 obj, err := a.srv.HealthConnectServiceNodes(resp, req) 894 assert.Nil(err) 895 assertIndex(t, resp) 896 897 // Should be 0 health check for consul 898 nodes := obj.(structs.CheckServiceNodes) 899 assert.Len(nodes, 0) 900 }) 901 902 t.Run("passing_true", func(t *testing.T) { 903 assert := assert.New(t) 904 req, _ := http.NewRequest("GET", fmt.Sprintf( 905 "/v1/health/connect/%s?passing=true", args.Service.Proxy.DestinationServiceName), nil) 906 resp := httptest.NewRecorder() 907 obj, err := a.srv.HealthConnectServiceNodes(resp, req) 908 assert.Nil(err) 909 assertIndex(t, resp) 910 911 // Should be 0 health check for consul 912 nodes := obj.(structs.CheckServiceNodes) 913 assert.Len(nodes, 0) 914 }) 915 916 t.Run("passing_false", func(t *testing.T) { 917 assert := assert.New(t) 918 req, _ := http.NewRequest("GET", fmt.Sprintf( 919 "/v1/health/connect/%s?passing=false", args.Service.Proxy.DestinationServiceName), nil) 920 resp := httptest.NewRecorder() 921 obj, err := a.srv.HealthConnectServiceNodes(resp, req) 922 assert.Nil(err) 923 assertIndex(t, resp) 924 925 // Should be 1 926 nodes := obj.(structs.CheckServiceNodes) 927 assert.Len(nodes, 1) 928 }) 929 930 t.Run("passing_bad", func(t *testing.T) { 931 assert := assert.New(t) 932 req, _ := http.NewRequest("GET", fmt.Sprintf( 933 "/v1/health/connect/%s?passing=nope-nope", args.Service.Proxy.DestinationServiceName), nil) 934 resp := httptest.NewRecorder() 935 a.srv.HealthConnectServiceNodes(resp, req) 936 assert.Equal(400, resp.Code) 937 938 body, err := ioutil.ReadAll(resp.Body) 939 assert.Nil(err) 940 assert.True(bytes.Contains(body, []byte("Invalid value for ?passing"))) 941 }) 942 } 943 944 func TestFilterNonPassing(t *testing.T) { 945 t.Parallel() 946 nodes := structs.CheckServiceNodes{ 947 structs.CheckServiceNode{ 948 Checks: structs.HealthChecks{ 949 &structs.HealthCheck{ 950 Status: api.HealthCritical, 951 }, 952 &structs.HealthCheck{ 953 Status: api.HealthCritical, 954 }, 955 }, 956 }, 957 structs.CheckServiceNode{ 958 Checks: structs.HealthChecks{ 959 &structs.HealthCheck{ 960 Status: api.HealthCritical, 961 }, 962 &structs.HealthCheck{ 963 Status: api.HealthCritical, 964 }, 965 }, 966 }, 967 structs.CheckServiceNode{ 968 Checks: structs.HealthChecks{ 969 &structs.HealthCheck{ 970 Status: api.HealthPassing, 971 }, 972 }, 973 }, 974 } 975 out := filterNonPassing(nodes) 976 if len(out) != 1 && reflect.DeepEqual(out[0], nodes[2]) { 977 t.Fatalf("bad: %v", out) 978 } 979 }