github.com/clly/consul@v1.4.5/agent/consul/health_endpoint_test.go (about) 1 package consul 2 3 import ( 4 "os" 5 "testing" 6 "time" 7 8 "github.com/hashicorp/consul/agent/structs" 9 "github.com/hashicorp/consul/api" 10 "github.com/hashicorp/consul/lib" 11 "github.com/hashicorp/consul/testrpc" 12 "github.com/hashicorp/net-rpc-msgpackrpc" 13 "github.com/stretchr/testify/assert" 14 "github.com/stretchr/testify/require" 15 ) 16 17 func TestHealth_ChecksInState(t *testing.T) { 18 t.Parallel() 19 dir1, s1 := testServer(t) 20 defer os.RemoveAll(dir1) 21 defer s1.Shutdown() 22 codec := rpcClient(t, s1) 23 defer codec.Close() 24 25 testrpc.WaitForLeader(t, s1.RPC, "dc1") 26 27 arg := structs.RegisterRequest{ 28 Datacenter: "dc1", 29 Node: "foo", 30 Address: "127.0.0.1", 31 Check: &structs.HealthCheck{ 32 Name: "memory utilization", 33 Status: api.HealthPassing, 34 }, 35 } 36 var out struct{} 37 if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil { 38 t.Fatalf("err: %v", err) 39 } 40 41 var out2 structs.IndexedHealthChecks 42 inState := structs.ChecksInStateRequest{ 43 Datacenter: "dc1", 44 State: api.HealthPassing, 45 } 46 if err := msgpackrpc.CallWithCodec(codec, "Health.ChecksInState", &inState, &out2); err != nil { 47 t.Fatalf("err: %v", err) 48 } 49 50 checks := out2.HealthChecks 51 if len(checks) != 2 { 52 t.Fatalf("Bad: %v", checks) 53 } 54 55 // Serf check is automatically added 56 if checks[0].Name != "memory utilization" { 57 t.Fatalf("Bad: %v", checks[0]) 58 } 59 if checks[1].CheckID != structs.SerfCheckID { 60 t.Fatalf("Bad: %v", checks[1]) 61 } 62 } 63 64 func TestHealth_ChecksInState_NodeMetaFilter(t *testing.T) { 65 t.Parallel() 66 dir1, s1 := testServer(t) 67 defer os.RemoveAll(dir1) 68 defer s1.Shutdown() 69 codec := rpcClient(t, s1) 70 defer codec.Close() 71 72 testrpc.WaitForLeader(t, s1.RPC, "dc1") 73 74 arg := structs.RegisterRequest{ 75 Datacenter: "dc1", 76 Node: "foo", 77 Address: "127.0.0.1", 78 NodeMeta: map[string]string{ 79 "somekey": "somevalue", 80 "common": "1", 81 }, 82 Check: &structs.HealthCheck{ 83 Name: "memory utilization", 84 Status: api.HealthPassing, 85 }, 86 } 87 var out struct{} 88 if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil { 89 t.Fatalf("err: %v", err) 90 } 91 arg = structs.RegisterRequest{ 92 Datacenter: "dc1", 93 Node: "bar", 94 Address: "127.0.0.2", 95 NodeMeta: map[string]string{ 96 "common": "1", 97 }, 98 Check: &structs.HealthCheck{ 99 Name: "disk space", 100 Status: api.HealthPassing, 101 }, 102 } 103 if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil { 104 t.Fatalf("err: %v", err) 105 } 106 107 cases := []struct { 108 filters map[string]string 109 checkNames []string 110 }{ 111 // Get foo's check by its unique meta value 112 { 113 filters: map[string]string{"somekey": "somevalue"}, 114 checkNames: []string{"memory utilization"}, 115 }, 116 // Get both foo/bar's checks by their common meta value 117 { 118 filters: map[string]string{"common": "1"}, 119 checkNames: []string{"disk space", "memory utilization"}, 120 }, 121 // Use an invalid meta value, should get empty result 122 { 123 filters: map[string]string{"invalid": "nope"}, 124 checkNames: []string{}, 125 }, 126 // Use multiple filters to get foo's check 127 { 128 filters: map[string]string{ 129 "somekey": "somevalue", 130 "common": "1", 131 }, 132 checkNames: []string{"memory utilization"}, 133 }, 134 } 135 136 for _, tc := range cases { 137 var out structs.IndexedHealthChecks 138 inState := structs.ChecksInStateRequest{ 139 Datacenter: "dc1", 140 NodeMetaFilters: tc.filters, 141 State: api.HealthPassing, 142 } 143 if err := msgpackrpc.CallWithCodec(codec, "Health.ChecksInState", &inState, &out); err != nil { 144 t.Fatalf("err: %v", err) 145 } 146 147 checks := out.HealthChecks 148 if len(checks) != len(tc.checkNames) { 149 t.Fatalf("Bad: %v, %v", checks, tc.checkNames) 150 } 151 152 for i, check := range checks { 153 if tc.checkNames[i] != check.Name { 154 t.Fatalf("Bad: %v %v", checks, tc.checkNames) 155 } 156 } 157 } 158 } 159 160 func TestHealth_ChecksInState_DistanceSort(t *testing.T) { 161 t.Parallel() 162 dir1, s1 := testServer(t) 163 defer os.RemoveAll(dir1) 164 defer s1.Shutdown() 165 codec := rpcClient(t, s1) 166 defer codec.Close() 167 168 testrpc.WaitForLeader(t, s1.RPC, "dc1") 169 if err := s1.fsm.State().EnsureNode(1, &structs.Node{Node: "foo", Address: "127.0.0.2"}); err != nil { 170 t.Fatalf("err: %v", err) 171 } 172 if err := s1.fsm.State().EnsureNode(2, &structs.Node{Node: "bar", Address: "127.0.0.3"}); err != nil { 173 t.Fatalf("err: %v", err) 174 } 175 updates := structs.Coordinates{ 176 {Node: "foo", Coord: lib.GenerateCoordinate(1 * time.Millisecond)}, 177 {Node: "bar", Coord: lib.GenerateCoordinate(2 * time.Millisecond)}, 178 } 179 if err := s1.fsm.State().CoordinateBatchUpdate(3, updates); err != nil { 180 t.Fatalf("err: %v", err) 181 } 182 183 arg := structs.RegisterRequest{ 184 Datacenter: "dc1", 185 Node: "foo", 186 Address: "127.0.0.1", 187 Check: &structs.HealthCheck{ 188 Name: "memory utilization", 189 Status: api.HealthPassing, 190 }, 191 } 192 193 var out struct{} 194 if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil { 195 t.Fatalf("err: %v", err) 196 } 197 198 arg.Node = "bar" 199 if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil { 200 t.Fatalf("err: %v", err) 201 } 202 203 // Query relative to foo to make sure it shows up first in the list. 204 var out2 structs.IndexedHealthChecks 205 inState := structs.ChecksInStateRequest{ 206 Datacenter: "dc1", 207 State: api.HealthPassing, 208 Source: structs.QuerySource{ 209 Datacenter: "dc1", 210 Node: "foo", 211 }, 212 } 213 if err := msgpackrpc.CallWithCodec(codec, "Health.ChecksInState", &inState, &out2); err != nil { 214 t.Fatalf("err: %v", err) 215 } 216 checks := out2.HealthChecks 217 if len(checks) != 3 { 218 t.Fatalf("Bad: %v", checks) 219 } 220 if checks[0].Node != "foo" { 221 t.Fatalf("Bad: %v", checks[1]) 222 } 223 224 // Now query relative to bar to make sure it shows up first. 225 inState.Source.Node = "bar" 226 if err := msgpackrpc.CallWithCodec(codec, "Health.ChecksInState", &inState, &out2); err != nil { 227 t.Fatalf("err: %v", err) 228 } 229 checks = out2.HealthChecks 230 if len(checks) != 3 { 231 t.Fatalf("Bad: %v", checks) 232 } 233 if checks[0].Node != "bar" { 234 t.Fatalf("Bad: %v", checks[1]) 235 } 236 } 237 238 func TestHealth_NodeChecks(t *testing.T) { 239 t.Parallel() 240 dir1, s1 := testServer(t) 241 defer os.RemoveAll(dir1) 242 defer s1.Shutdown() 243 codec := rpcClient(t, s1) 244 defer codec.Close() 245 246 testrpc.WaitForLeader(t, s1.RPC, "dc1") 247 248 arg := structs.RegisterRequest{ 249 Datacenter: "dc1", 250 Node: "foo", 251 Address: "127.0.0.1", 252 Check: &structs.HealthCheck{ 253 Name: "memory utilization", 254 Status: api.HealthPassing, 255 }, 256 } 257 var out struct{} 258 if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil { 259 t.Fatalf("err: %v", err) 260 } 261 262 var out2 structs.IndexedHealthChecks 263 node := structs.NodeSpecificRequest{ 264 Datacenter: "dc1", 265 Node: "foo", 266 } 267 if err := msgpackrpc.CallWithCodec(codec, "Health.NodeChecks", &node, &out2); err != nil { 268 t.Fatalf("err: %v", err) 269 } 270 271 checks := out2.HealthChecks 272 if len(checks) != 1 { 273 t.Fatalf("Bad: %v", checks) 274 } 275 if checks[0].Name != "memory utilization" { 276 t.Fatalf("Bad: %v", checks) 277 } 278 } 279 280 func TestHealth_ServiceChecks(t *testing.T) { 281 t.Parallel() 282 dir1, s1 := testServer(t) 283 defer os.RemoveAll(dir1) 284 defer s1.Shutdown() 285 codec := rpcClient(t, s1) 286 defer codec.Close() 287 288 testrpc.WaitForLeader(t, s1.RPC, "dc1") 289 290 arg := structs.RegisterRequest{ 291 Datacenter: "dc1", 292 Node: "foo", 293 Address: "127.0.0.1", 294 Service: &structs.NodeService{ 295 ID: "db", 296 Service: "db", 297 }, 298 Check: &structs.HealthCheck{ 299 Name: "db connect", 300 Status: api.HealthPassing, 301 ServiceID: "db", 302 }, 303 } 304 var out struct{} 305 if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil { 306 t.Fatalf("err: %v", err) 307 } 308 309 var out2 structs.IndexedHealthChecks 310 node := structs.ServiceSpecificRequest{ 311 Datacenter: "dc1", 312 ServiceName: "db", 313 } 314 if err := msgpackrpc.CallWithCodec(codec, "Health.ServiceChecks", &node, &out2); err != nil { 315 t.Fatalf("err: %v", err) 316 } 317 318 checks := out2.HealthChecks 319 if len(checks) != 1 { 320 t.Fatalf("Bad: %v", checks) 321 } 322 if checks[0].Name != "db connect" { 323 t.Fatalf("Bad: %v", checks) 324 } 325 } 326 327 func TestHealth_ServiceChecks_NodeMetaFilter(t *testing.T) { 328 t.Parallel() 329 dir1, s1 := testServer(t) 330 defer os.RemoveAll(dir1) 331 defer s1.Shutdown() 332 codec := rpcClient(t, s1) 333 defer codec.Close() 334 335 testrpc.WaitForLeader(t, s1.RPC, "dc1") 336 337 arg := structs.RegisterRequest{ 338 Datacenter: "dc1", 339 Node: "foo", 340 Address: "127.0.0.1", 341 NodeMeta: map[string]string{ 342 "somekey": "somevalue", 343 "common": "1", 344 }, 345 Service: &structs.NodeService{ 346 ID: "db", 347 Service: "db", 348 }, 349 Check: &structs.HealthCheck{ 350 Name: "memory utilization", 351 Status: api.HealthPassing, 352 ServiceID: "db", 353 }, 354 } 355 var out struct{} 356 if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil { 357 t.Fatalf("err: %v", err) 358 } 359 arg = structs.RegisterRequest{ 360 Datacenter: "dc1", 361 Node: "bar", 362 Address: "127.0.0.2", 363 NodeMeta: map[string]string{ 364 "common": "1", 365 }, 366 Service: &structs.NodeService{ 367 ID: "db", 368 Service: "db", 369 }, 370 Check: &structs.HealthCheck{ 371 Name: "disk space", 372 Status: api.HealthPassing, 373 ServiceID: "db", 374 }, 375 } 376 if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil { 377 t.Fatalf("err: %v", err) 378 } 379 380 cases := []struct { 381 filters map[string]string 382 checkNames []string 383 }{ 384 // Get foo's check by its unique meta value 385 { 386 filters: map[string]string{"somekey": "somevalue"}, 387 checkNames: []string{"memory utilization"}, 388 }, 389 // Get both foo/bar's checks by their common meta value 390 { 391 filters: map[string]string{"common": "1"}, 392 checkNames: []string{"disk space", "memory utilization"}, 393 }, 394 // Use an invalid meta value, should get empty result 395 { 396 filters: map[string]string{"invalid": "nope"}, 397 checkNames: []string{}, 398 }, 399 // Use multiple filters to get foo's check 400 { 401 filters: map[string]string{ 402 "somekey": "somevalue", 403 "common": "1", 404 }, 405 checkNames: []string{"memory utilization"}, 406 }, 407 } 408 409 for _, tc := range cases { 410 var out structs.IndexedHealthChecks 411 args := structs.ServiceSpecificRequest{ 412 Datacenter: "dc1", 413 NodeMetaFilters: tc.filters, 414 ServiceName: "db", 415 } 416 if err := msgpackrpc.CallWithCodec(codec, "Health.ServiceChecks", &args, &out); err != nil { 417 t.Fatalf("err: %v", err) 418 } 419 420 checks := out.HealthChecks 421 if len(checks) != len(tc.checkNames) { 422 t.Fatalf("Bad: %v, %v", checks, tc.checkNames) 423 } 424 425 for i, check := range checks { 426 if tc.checkNames[i] != check.Name { 427 t.Fatalf("Bad: %v %v", checks, tc.checkNames) 428 } 429 } 430 } 431 } 432 433 func TestHealth_ServiceChecks_DistanceSort(t *testing.T) { 434 t.Parallel() 435 dir1, s1 := testServer(t) 436 defer os.RemoveAll(dir1) 437 defer s1.Shutdown() 438 codec := rpcClient(t, s1) 439 defer codec.Close() 440 441 testrpc.WaitForLeader(t, s1.RPC, "dc1") 442 if err := s1.fsm.State().EnsureNode(1, &structs.Node{Node: "foo", Address: "127.0.0.2"}); err != nil { 443 t.Fatalf("err: %v", err) 444 } 445 if err := s1.fsm.State().EnsureNode(2, &structs.Node{Node: "bar", Address: "127.0.0.3"}); err != nil { 446 t.Fatalf("err: %v", err) 447 } 448 updates := structs.Coordinates{ 449 {Node: "foo", Coord: lib.GenerateCoordinate(1 * time.Millisecond)}, 450 {Node: "bar", Coord: lib.GenerateCoordinate(2 * time.Millisecond)}, 451 } 452 if err := s1.fsm.State().CoordinateBatchUpdate(3, updates); err != nil { 453 t.Fatalf("err: %v", err) 454 } 455 456 arg := structs.RegisterRequest{ 457 Datacenter: "dc1", 458 Node: "foo", 459 Address: "127.0.0.1", 460 Service: &structs.NodeService{ 461 ID: "db", 462 Service: "db", 463 }, 464 Check: &structs.HealthCheck{ 465 Name: "db connect", 466 Status: api.HealthPassing, 467 ServiceID: "db", 468 }, 469 } 470 471 var out struct{} 472 if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil { 473 t.Fatalf("err: %v", err) 474 } 475 476 arg.Node = "bar" 477 if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil { 478 t.Fatalf("err: %v", err) 479 } 480 481 // Query relative to foo to make sure it shows up first in the list. 482 var out2 structs.IndexedHealthChecks 483 node := structs.ServiceSpecificRequest{ 484 Datacenter: "dc1", 485 ServiceName: "db", 486 Source: structs.QuerySource{ 487 Datacenter: "dc1", 488 Node: "foo", 489 }, 490 } 491 if err := msgpackrpc.CallWithCodec(codec, "Health.ServiceChecks", &node, &out2); err != nil { 492 t.Fatalf("err: %v", err) 493 } 494 checks := out2.HealthChecks 495 if len(checks) != 2 { 496 t.Fatalf("Bad: %v", checks) 497 } 498 if checks[0].Node != "foo" { 499 t.Fatalf("Bad: %v", checks) 500 } 501 if checks[1].Node != "bar" { 502 t.Fatalf("Bad: %v", checks) 503 } 504 505 // Now query relative to bar to make sure it shows up first. 506 node.Source.Node = "bar" 507 if err := msgpackrpc.CallWithCodec(codec, "Health.ServiceChecks", &node, &out2); err != nil { 508 t.Fatalf("err: %v", err) 509 } 510 checks = out2.HealthChecks 511 if len(checks) != 2 { 512 t.Fatalf("Bad: %v", checks) 513 } 514 if checks[0].Node != "bar" { 515 t.Fatalf("Bad: %v", checks) 516 } 517 if checks[1].Node != "foo" { 518 t.Fatalf("Bad: %v", checks) 519 } 520 } 521 522 func TestHealth_ServiceNodes(t *testing.T) { 523 t.Parallel() 524 dir1, s1 := testServer(t) 525 defer os.RemoveAll(dir1) 526 defer s1.Shutdown() 527 codec := rpcClient(t, s1) 528 defer codec.Close() 529 530 testrpc.WaitForLeader(t, s1.RPC, "dc1") 531 532 arg := structs.RegisterRequest{ 533 Datacenter: "dc1", 534 Node: "foo", 535 Address: "127.0.0.1", 536 Service: &structs.NodeService{ 537 ID: "db", 538 Service: "db", 539 Tags: []string{"master"}, 540 }, 541 Check: &structs.HealthCheck{ 542 Name: "db connect", 543 Status: api.HealthPassing, 544 ServiceID: "db", 545 }, 546 } 547 var out struct{} 548 if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil { 549 t.Fatalf("err: %v", err) 550 } 551 552 arg = structs.RegisterRequest{ 553 Datacenter: "dc1", 554 Node: "bar", 555 Address: "127.0.0.2", 556 Service: &structs.NodeService{ 557 ID: "db", 558 Service: "db", 559 Tags: []string{"slave"}, 560 }, 561 Check: &structs.HealthCheck{ 562 Name: "db connect", 563 Status: api.HealthWarning, 564 ServiceID: "db", 565 }, 566 } 567 if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil { 568 t.Fatalf("err: %v", err) 569 } 570 571 var out2 structs.IndexedCheckServiceNodes 572 req := structs.ServiceSpecificRequest{ 573 Datacenter: "dc1", 574 ServiceName: "db", 575 ServiceTags: []string{"master"}, 576 TagFilter: false, 577 } 578 if err := msgpackrpc.CallWithCodec(codec, "Health.ServiceNodes", &req, &out2); err != nil { 579 t.Fatalf("err: %v", err) 580 } 581 582 nodes := out2.Nodes 583 if len(nodes) != 2 { 584 t.Fatalf("Bad: %v", nodes) 585 } 586 if nodes[0].Node.Node != "bar" { 587 t.Fatalf("Bad: %v", nodes[0]) 588 } 589 if nodes[1].Node.Node != "foo" { 590 t.Fatalf("Bad: %v", nodes[1]) 591 } 592 if !lib.StrContains(nodes[0].Service.Tags, "slave") { 593 t.Fatalf("Bad: %v", nodes[0]) 594 } 595 if !lib.StrContains(nodes[1].Service.Tags, "master") { 596 t.Fatalf("Bad: %v", nodes[1]) 597 } 598 if nodes[0].Checks[0].Status != api.HealthWarning { 599 t.Fatalf("Bad: %v", nodes[0]) 600 } 601 if nodes[1].Checks[0].Status != api.HealthPassing { 602 t.Fatalf("Bad: %v", nodes[1]) 603 } 604 605 // Same should still work for <1.3 RPCs with singular tags 606 // DEPRECATED (singular-service-tag) - remove this when backwards RPC compat 607 // with 1.2.x is not required. 608 { 609 var out2 structs.IndexedCheckServiceNodes 610 req := structs.ServiceSpecificRequest{ 611 Datacenter: "dc1", 612 ServiceName: "db", 613 ServiceTag: "master", 614 TagFilter: false, 615 } 616 if err := msgpackrpc.CallWithCodec(codec, "Health.ServiceNodes", &req, &out2); err != nil { 617 t.Fatalf("err: %v", err) 618 } 619 620 nodes := out2.Nodes 621 if len(nodes) != 2 { 622 t.Fatalf("Bad: %v", nodes) 623 } 624 if nodes[0].Node.Node != "bar" { 625 t.Fatalf("Bad: %v", nodes[0]) 626 } 627 if nodes[1].Node.Node != "foo" { 628 t.Fatalf("Bad: %v", nodes[1]) 629 } 630 if !lib.StrContains(nodes[0].Service.Tags, "slave") { 631 t.Fatalf("Bad: %v", nodes[0]) 632 } 633 if !lib.StrContains(nodes[1].Service.Tags, "master") { 634 t.Fatalf("Bad: %v", nodes[1]) 635 } 636 if nodes[0].Checks[0].Status != api.HealthWarning { 637 t.Fatalf("Bad: %v", nodes[0]) 638 } 639 if nodes[1].Checks[0].Status != api.HealthPassing { 640 t.Fatalf("Bad: %v", nodes[1]) 641 } 642 } 643 } 644 645 func TestHealth_ServiceNodes_MultipleServiceTags(t *testing.T) { 646 t.Parallel() 647 dir1, s1 := testServer(t) 648 defer os.RemoveAll(dir1) 649 defer s1.Shutdown() 650 codec := rpcClient(t, s1) 651 defer codec.Close() 652 653 testrpc.WaitForLeader(t, s1.RPC, "dc1") 654 655 arg := structs.RegisterRequest{ 656 Datacenter: "dc1", 657 Node: "foo", 658 Address: "127.0.0.1", 659 Service: &structs.NodeService{ 660 ID: "db", 661 Service: "db", 662 Tags: []string{"master", "v2"}, 663 }, 664 Check: &structs.HealthCheck{ 665 Name: "db connect", 666 Status: api.HealthPassing, 667 ServiceID: "db", 668 }, 669 } 670 var out struct{} 671 require.NoError(t, msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out)) 672 673 arg = structs.RegisterRequest{ 674 Datacenter: "dc1", 675 Node: "bar", 676 Address: "127.0.0.2", 677 Service: &structs.NodeService{ 678 ID: "db", 679 Service: "db", 680 Tags: []string{"slave", "v2"}, 681 }, 682 Check: &structs.HealthCheck{ 683 Name: "db connect", 684 Status: api.HealthWarning, 685 ServiceID: "db", 686 }, 687 } 688 require.NoError(t, msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out)) 689 690 var out2 structs.IndexedCheckServiceNodes 691 req := structs.ServiceSpecificRequest{ 692 Datacenter: "dc1", 693 ServiceName: "db", 694 ServiceTags: []string{"master", "v2"}, 695 TagFilter: true, 696 } 697 require.NoError(t, msgpackrpc.CallWithCodec(codec, "Health.ServiceNodes", &req, &out2)) 698 699 nodes := out2.Nodes 700 require.Len(t, nodes, 1) 701 require.Equal(t, nodes[0].Node.Node, "foo") 702 require.Contains(t, nodes[0].Service.Tags, "v2") 703 require.Contains(t, nodes[0].Service.Tags, "master") 704 require.Equal(t, nodes[0].Checks[0].Status, api.HealthPassing) 705 } 706 707 func TestHealth_ServiceNodes_NodeMetaFilter(t *testing.T) { 708 t.Parallel() 709 dir1, s1 := testServer(t) 710 defer os.RemoveAll(dir1) 711 defer s1.Shutdown() 712 codec := rpcClient(t, s1) 713 defer codec.Close() 714 715 testrpc.WaitForLeader(t, s1.RPC, "dc1") 716 717 arg := structs.RegisterRequest{ 718 Datacenter: "dc1", 719 Node: "foo", 720 Address: "127.0.0.1", 721 NodeMeta: map[string]string{ 722 "somekey": "somevalue", 723 "common": "1", 724 }, 725 Service: &structs.NodeService{ 726 ID: "db", 727 Service: "db", 728 }, 729 Check: &structs.HealthCheck{ 730 Name: "memory utilization", 731 Status: api.HealthPassing, 732 ServiceID: "db", 733 }, 734 } 735 var out struct{} 736 if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil { 737 t.Fatalf("err: %v", err) 738 } 739 740 arg = structs.RegisterRequest{ 741 Datacenter: "dc1", 742 Node: "bar", 743 Address: "127.0.0.2", 744 NodeMeta: map[string]string{ 745 "common": "1", 746 }, 747 Service: &structs.NodeService{ 748 ID: "db", 749 Service: "db", 750 }, 751 Check: &structs.HealthCheck{ 752 Name: "disk space", 753 Status: api.HealthWarning, 754 ServiceID: "db", 755 }, 756 } 757 if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil { 758 t.Fatalf("err: %v", err) 759 } 760 761 cases := []struct { 762 filters map[string]string 763 nodes structs.CheckServiceNodes 764 }{ 765 // Get foo's check by its unique meta value 766 { 767 filters: map[string]string{"somekey": "somevalue"}, 768 nodes: structs.CheckServiceNodes{ 769 structs.CheckServiceNode{ 770 Node: &structs.Node{Node: "foo"}, 771 Checks: structs.HealthChecks{&structs.HealthCheck{Name: "memory utilization"}}, 772 }, 773 }, 774 }, 775 // Get both foo/bar's checks by their common meta value 776 { 777 filters: map[string]string{"common": "1"}, 778 nodes: structs.CheckServiceNodes{ 779 structs.CheckServiceNode{ 780 Node: &structs.Node{Node: "bar"}, 781 Checks: structs.HealthChecks{&structs.HealthCheck{Name: "disk space"}}, 782 }, 783 structs.CheckServiceNode{ 784 Node: &structs.Node{Node: "foo"}, 785 Checks: structs.HealthChecks{&structs.HealthCheck{Name: "memory utilization"}}, 786 }, 787 }, 788 }, 789 // Use an invalid meta value, should get empty result 790 { 791 filters: map[string]string{"invalid": "nope"}, 792 nodes: structs.CheckServiceNodes{}, 793 }, 794 // Use multiple filters to get foo's check 795 { 796 filters: map[string]string{ 797 "somekey": "somevalue", 798 "common": "1", 799 }, 800 nodes: structs.CheckServiceNodes{ 801 structs.CheckServiceNode{ 802 Node: &structs.Node{Node: "foo"}, 803 Checks: structs.HealthChecks{&structs.HealthCheck{Name: "memory utilization"}}, 804 }, 805 }, 806 }, 807 } 808 809 for _, tc := range cases { 810 var out structs.IndexedCheckServiceNodes 811 req := structs.ServiceSpecificRequest{ 812 Datacenter: "dc1", 813 NodeMetaFilters: tc.filters, 814 ServiceName: "db", 815 } 816 if err := msgpackrpc.CallWithCodec(codec, "Health.ServiceNodes", &req, &out); err != nil { 817 t.Fatalf("err: %v", err) 818 } 819 820 if len(out.Nodes) != len(tc.nodes) { 821 t.Fatalf("bad: %v, %v, filters: %v", out.Nodes, tc.nodes, tc.filters) 822 } 823 824 for i, node := range out.Nodes { 825 checks := tc.nodes[i].Checks 826 if len(node.Checks) != len(checks) { 827 t.Fatalf("bad: %v, %v, filters: %v", node.Checks, checks, tc.filters) 828 } 829 for j, check := range node.Checks { 830 if check.Name != checks[j].Name { 831 t.Fatalf("bad: %v, %v, filters: %v", check, checks[j], tc.filters) 832 } 833 } 834 } 835 } 836 } 837 838 func TestHealth_ServiceNodes_DistanceSort(t *testing.T) { 839 t.Parallel() 840 dir1, s1 := testServer(t) 841 defer os.RemoveAll(dir1) 842 defer s1.Shutdown() 843 codec := rpcClient(t, s1) 844 defer codec.Close() 845 846 testrpc.WaitForLeader(t, s1.RPC, "dc1") 847 if err := s1.fsm.State().EnsureNode(1, &structs.Node{Node: "foo", Address: "127.0.0.2"}); err != nil { 848 t.Fatalf("err: %v", err) 849 } 850 if err := s1.fsm.State().EnsureNode(2, &structs.Node{Node: "bar", Address: "127.0.0.3"}); err != nil { 851 t.Fatalf("err: %v", err) 852 } 853 updates := structs.Coordinates{ 854 {Node: "foo", Coord: lib.GenerateCoordinate(1 * time.Millisecond)}, 855 {Node: "bar", Coord: lib.GenerateCoordinate(2 * time.Millisecond)}, 856 } 857 if err := s1.fsm.State().CoordinateBatchUpdate(3, updates); err != nil { 858 t.Fatalf("err: %v", err) 859 } 860 861 arg := structs.RegisterRequest{ 862 Datacenter: "dc1", 863 Node: "foo", 864 Address: "127.0.0.1", 865 Service: &structs.NodeService{ 866 ID: "db", 867 Service: "db", 868 }, 869 Check: &structs.HealthCheck{ 870 Name: "db connect", 871 Status: api.HealthPassing, 872 ServiceID: "db", 873 }, 874 } 875 876 var out struct{} 877 if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil { 878 t.Fatalf("err: %v", err) 879 } 880 881 arg.Node = "bar" 882 if err := msgpackrpc.CallWithCodec(codec, "Catalog.Register", &arg, &out); err != nil { 883 t.Fatalf("err: %v", err) 884 } 885 886 // Query relative to foo to make sure it shows up first in the list. 887 var out2 structs.IndexedCheckServiceNodes 888 req := structs.ServiceSpecificRequest{ 889 Datacenter: "dc1", 890 ServiceName: "db", 891 Source: structs.QuerySource{ 892 Datacenter: "dc1", 893 Node: "foo", 894 }, 895 } 896 if err := msgpackrpc.CallWithCodec(codec, "Health.ServiceNodes", &req, &out2); err != nil { 897 t.Fatalf("err: %v", err) 898 } 899 nodes := out2.Nodes 900 if len(nodes) != 2 { 901 t.Fatalf("Bad: %v", nodes) 902 } 903 if nodes[0].Node.Node != "foo" { 904 t.Fatalf("Bad: %v", nodes[0]) 905 } 906 if nodes[1].Node.Node != "bar" { 907 t.Fatalf("Bad: %v", nodes[1]) 908 } 909 910 // Now query relative to bar to make sure it shows up first. 911 req.Source.Node = "bar" 912 if err := msgpackrpc.CallWithCodec(codec, "Health.ServiceNodes", &req, &out2); err != nil { 913 t.Fatalf("err: %v", err) 914 } 915 nodes = out2.Nodes 916 if len(nodes) != 2 { 917 t.Fatalf("Bad: %v", nodes) 918 } 919 if nodes[0].Node.Node != "bar" { 920 t.Fatalf("Bad: %v", nodes[0]) 921 } 922 if nodes[1].Node.Node != "foo" { 923 t.Fatalf("Bad: %v", nodes[1]) 924 } 925 } 926 927 func TestHealth_ServiceNodes_ConnectProxy_ACL(t *testing.T) { 928 t.Parallel() 929 930 assert := assert.New(t) 931 dir1, s1 := testServerWithConfig(t, func(c *Config) { 932 c.ACLDatacenter = "dc1" 933 c.ACLsEnabled = true 934 c.ACLMasterToken = "root" 935 c.ACLDefaultPolicy = "deny" 936 c.ACLEnforceVersion8 = false 937 }) 938 defer os.RemoveAll(dir1) 939 defer s1.Shutdown() 940 codec := rpcClient(t, s1) 941 defer codec.Close() 942 943 testrpc.WaitForLeader(t, s1.RPC, "dc1") 944 945 // Create the ACL. 946 arg := structs.ACLRequest{ 947 Datacenter: "dc1", 948 Op: structs.ACLSet, 949 ACL: structs.ACL{ 950 Name: "User token", 951 Type: structs.ACLTokenTypeClient, 952 Rules: ` 953 service "foo" { 954 policy = "write" 955 } 956 `, 957 }, 958 WriteRequest: structs.WriteRequest{Token: "root"}, 959 } 960 var token string 961 assert.Nil(msgpackrpc.CallWithCodec(codec, "ACL.Apply", arg, &token)) 962 963 { 964 var out struct{} 965 966 // Register a service 967 args := structs.TestRegisterRequestProxy(t) 968 args.WriteRequest.Token = "root" 969 args.Service.ID = "foo-proxy-0" 970 args.Service.Service = "foo-proxy" 971 args.Service.Proxy.DestinationServiceName = "bar" 972 args.Check = &structs.HealthCheck{ 973 Name: "proxy", 974 Status: api.HealthPassing, 975 ServiceID: args.Service.ID, 976 } 977 assert.Nil(msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out)) 978 979 // Register a service 980 args = structs.TestRegisterRequestProxy(t) 981 args.WriteRequest.Token = "root" 982 args.Service.Service = "foo-proxy" 983 args.Service.Proxy.DestinationServiceName = "foo" 984 args.Check = &structs.HealthCheck{ 985 Name: "proxy", 986 Status: api.HealthPassing, 987 ServiceID: args.Service.Service, 988 } 989 assert.Nil(msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out)) 990 991 // Register a service 992 args = structs.TestRegisterRequestProxy(t) 993 args.WriteRequest.Token = "root" 994 args.Service.Service = "another-proxy" 995 args.Service.Proxy.DestinationServiceName = "foo" 996 args.Check = &structs.HealthCheck{ 997 Name: "proxy", 998 Status: api.HealthPassing, 999 ServiceID: args.Service.Service, 1000 } 1001 assert.Nil(msgpackrpc.CallWithCodec(codec, "Catalog.Register", &args, &out)) 1002 } 1003 1004 // List w/ token. This should disallow because we don't have permission 1005 // to read "bar" 1006 req := structs.ServiceSpecificRequest{ 1007 Connect: true, 1008 Datacenter: "dc1", 1009 ServiceName: "bar", 1010 QueryOptions: structs.QueryOptions{Token: token}, 1011 } 1012 var resp structs.IndexedCheckServiceNodes 1013 assert.Nil(msgpackrpc.CallWithCodec(codec, "Health.ServiceNodes", &req, &resp)) 1014 assert.Len(resp.Nodes, 0) 1015 1016 // List w/ token. This should work since we're requesting "foo", but should 1017 // also only contain the proxies with names that adhere to our ACL. 1018 req = structs.ServiceSpecificRequest{ 1019 Connect: true, 1020 Datacenter: "dc1", 1021 ServiceName: "foo", 1022 QueryOptions: structs.QueryOptions{Token: token}, 1023 } 1024 assert.Nil(msgpackrpc.CallWithCodec(codec, "Health.ServiceNodes", &req, &resp)) 1025 assert.Len(resp.Nodes, 1) 1026 } 1027 1028 func TestHealth_NodeChecks_FilterACL(t *testing.T) { 1029 t.Parallel() 1030 dir, token, srv, codec := testACLFilterServer(t) 1031 defer os.RemoveAll(dir) 1032 defer srv.Shutdown() 1033 defer codec.Close() 1034 1035 opt := structs.NodeSpecificRequest{ 1036 Datacenter: "dc1", 1037 Node: srv.config.NodeName, 1038 QueryOptions: structs.QueryOptions{Token: token}, 1039 } 1040 reply := structs.IndexedHealthChecks{} 1041 if err := msgpackrpc.CallWithCodec(codec, "Health.NodeChecks", &opt, &reply); err != nil { 1042 t.Fatalf("err: %s", err) 1043 } 1044 found := false 1045 for _, chk := range reply.HealthChecks { 1046 switch chk.ServiceName { 1047 case "foo": 1048 found = true 1049 case "bar": 1050 t.Fatalf("bad: %#v", reply.HealthChecks) 1051 } 1052 } 1053 if !found { 1054 t.Fatalf("bad: %#v", reply.HealthChecks) 1055 } 1056 1057 // We've already proven that we call the ACL filtering function so we 1058 // test node filtering down in acl.go for node cases. This also proves 1059 // that we respect the version 8 ACL flag, since the test server sets 1060 // that to false (the regression value of *not* changing this is better 1061 // for now until we change the sense of the version 8 ACL flag). 1062 } 1063 1064 func TestHealth_ServiceChecks_FilterACL(t *testing.T) { 1065 t.Parallel() 1066 dir, token, srv, codec := testACLFilterServer(t) 1067 defer os.RemoveAll(dir) 1068 defer srv.Shutdown() 1069 defer codec.Close() 1070 1071 opt := structs.ServiceSpecificRequest{ 1072 Datacenter: "dc1", 1073 ServiceName: "foo", 1074 QueryOptions: structs.QueryOptions{Token: token}, 1075 } 1076 reply := structs.IndexedHealthChecks{} 1077 if err := msgpackrpc.CallWithCodec(codec, "Health.ServiceChecks", &opt, &reply); err != nil { 1078 t.Fatalf("err: %s", err) 1079 } 1080 found := false 1081 for _, chk := range reply.HealthChecks { 1082 if chk.ServiceName == "foo" { 1083 found = true 1084 break 1085 } 1086 } 1087 if !found { 1088 t.Fatalf("bad: %#v", reply.HealthChecks) 1089 } 1090 1091 opt.ServiceName = "bar" 1092 reply = structs.IndexedHealthChecks{} 1093 if err := msgpackrpc.CallWithCodec(codec, "Health.ServiceChecks", &opt, &reply); err != nil { 1094 t.Fatalf("err: %s", err) 1095 } 1096 if len(reply.HealthChecks) != 0 { 1097 t.Fatalf("bad: %#v", reply.HealthChecks) 1098 } 1099 1100 // We've already proven that we call the ACL filtering function so we 1101 // test node filtering down in acl.go for node cases. This also proves 1102 // that we respect the version 8 ACL flag, since the test server sets 1103 // that to false (the regression value of *not* changing this is better 1104 // for now until we change the sense of the version 8 ACL flag). 1105 } 1106 1107 func TestHealth_ServiceNodes_FilterACL(t *testing.T) { 1108 t.Parallel() 1109 dir, token, srv, codec := testACLFilterServer(t) 1110 defer os.RemoveAll(dir) 1111 defer srv.Shutdown() 1112 defer codec.Close() 1113 1114 opt := structs.ServiceSpecificRequest{ 1115 Datacenter: "dc1", 1116 ServiceName: "foo", 1117 QueryOptions: structs.QueryOptions{Token: token}, 1118 } 1119 reply := structs.IndexedCheckServiceNodes{} 1120 if err := msgpackrpc.CallWithCodec(codec, "Health.ServiceNodes", &opt, &reply); err != nil { 1121 t.Fatalf("err: %s", err) 1122 } 1123 if len(reply.Nodes) != 1 { 1124 t.Fatalf("bad: %#v", reply.Nodes) 1125 } 1126 1127 opt.ServiceName = "bar" 1128 reply = structs.IndexedCheckServiceNodes{} 1129 if err := msgpackrpc.CallWithCodec(codec, "Health.ServiceNodes", &opt, &reply); err != nil { 1130 t.Fatalf("err: %s", err) 1131 } 1132 if len(reply.Nodes) != 0 { 1133 t.Fatalf("bad: %#v", reply.Nodes) 1134 } 1135 1136 // We've already proven that we call the ACL filtering function so we 1137 // test node filtering down in acl.go for node cases. This also proves 1138 // that we respect the version 8 ACL flag, since the test server sets 1139 // that to false (the regression value of *not* changing this is better 1140 // for now until we change the sense of the version 8 ACL flag). 1141 } 1142 1143 func TestHealth_ChecksInState_FilterACL(t *testing.T) { 1144 t.Parallel() 1145 dir, token, srv, codec := testACLFilterServer(t) 1146 defer os.RemoveAll(dir) 1147 defer srv.Shutdown() 1148 defer codec.Close() 1149 1150 opt := structs.ChecksInStateRequest{ 1151 Datacenter: "dc1", 1152 State: api.HealthPassing, 1153 QueryOptions: structs.QueryOptions{Token: token}, 1154 } 1155 reply := structs.IndexedHealthChecks{} 1156 if err := msgpackrpc.CallWithCodec(codec, "Health.ChecksInState", &opt, &reply); err != nil { 1157 t.Fatalf("err: %s", err) 1158 } 1159 1160 found := false 1161 for _, chk := range reply.HealthChecks { 1162 switch chk.ServiceName { 1163 case "foo": 1164 found = true 1165 case "bar": 1166 t.Fatalf("bad service 'bar': %#v", reply.HealthChecks) 1167 } 1168 } 1169 if !found { 1170 t.Fatalf("missing service 'foo': %#v", reply.HealthChecks) 1171 } 1172 1173 // We've already proven that we call the ACL filtering function so we 1174 // test node filtering down in acl.go for node cases. This also proves 1175 // that we respect the version 8 ACL flag, since the test server sets 1176 // that to false (the regression value of *not* changing this is better 1177 // for now until we change the sense of the version 8 ACL flag). 1178 }