1package api 2 3import ( 4 "reflect" 5 "testing" 6 "time" 7 8 "github.com/hashicorp/consul/sdk/testutil" 9 "github.com/hashicorp/consul/sdk/testutil/retry" 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12) 13 14func TestAPI_CatalogDatacenters(t *testing.T) { 15 t.Parallel() 16 c, s := makeClient(t) 17 defer s.Stop() 18 19 catalog := c.Catalog() 20 retry.Run(t, func(r *retry.R) { 21 datacenters, err := catalog.Datacenters() 22 if err != nil { 23 r.Fatal(err) 24 } 25 if len(datacenters) < 1 { 26 r.Fatal("got 0 datacenters want at least one") 27 } 28 }) 29} 30 31func TestAPI_CatalogNodes(t *testing.T) { 32 t.Parallel() 33 c, s := makeClient(t) 34 defer s.Stop() 35 36 s.WaitForSerfCheck(t) 37 catalog := c.Catalog() 38 retry.Run(t, func(r *retry.R) { 39 nodes, meta, err := catalog.Nodes(nil) 40 // We're not concerned about the createIndex of an agent 41 // Hence we're setting it to the default value 42 nodes[0].CreateIndex = 0 43 if err != nil { 44 r.Fatal(err) 45 } 46 if meta.LastIndex < 2 { 47 r.Fatal("Last index must be greater than 1") 48 } 49 want := []*Node{ 50 { 51 ID: s.Config.NodeID, 52 Node: s.Config.NodeName, 53 Address: "127.0.0.1", 54 Datacenter: "dc1", 55 TaggedAddresses: map[string]string{ 56 "lan": "127.0.0.1", 57 "lan_ipv4": "127.0.0.1", 58 "wan": "127.0.0.1", 59 "wan_ipv4": "127.0.0.1", 60 }, 61 Meta: map[string]string{ 62 "consul-network-segment": "", 63 }, 64 // CreateIndex will never always be meta.LastIndex - 1 65 // The purpose of this test is not to test CreateIndex value of an agent 66 // rather to check if the client agent can get the correct number 67 // of agents with a particular service, KV pair, etc... 68 // Hence reverting this to the default value here. 69 CreateIndex: 0, 70 ModifyIndex: meta.LastIndex, 71 }, 72 } 73 require.Equal(r, want, nodes) 74 }) 75} 76 77func TestAPI_CatalogNodes_MetaFilter(t *testing.T) { 78 t.Parallel() 79 meta := map[string]string{"somekey": "somevalue"} 80 c, s := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) { 81 conf.NodeMeta = meta 82 }) 83 defer s.Stop() 84 85 catalog := c.Catalog() 86 // Make sure we get the node back when filtering by its metadata 87 retry.Run(t, func(r *retry.R) { 88 nodes, meta, err := catalog.Nodes(&QueryOptions{NodeMeta: meta}) 89 if err != nil { 90 r.Fatal(err) 91 } 92 93 if meta.LastIndex == 0 { 94 r.Fatalf("Bad: %v", meta) 95 } 96 97 if len(nodes) == 0 { 98 r.Fatalf("Bad: %v", nodes) 99 } 100 101 if _, ok := nodes[0].TaggedAddresses["wan"]; !ok { 102 r.Fatalf("Bad: %v", nodes[0]) 103 } 104 105 if v, ok := nodes[0].Meta["somekey"]; !ok || v != "somevalue" { 106 r.Fatalf("Bad: %v", nodes[0].Meta) 107 } 108 109 if nodes[0].Datacenter != "dc1" { 110 r.Fatalf("Bad datacenter: %v", nodes[0]) 111 } 112 }) 113 114 retry.Run(t, func(r *retry.R) { 115 // Get nothing back when we use an invalid filter 116 nodes, meta, err := catalog.Nodes(&QueryOptions{NodeMeta: map[string]string{"nope": "nope"}}) 117 if err != nil { 118 r.Fatal(err) 119 } 120 121 if meta.LastIndex == 0 { 122 r.Fatalf("Bad: %v", meta) 123 } 124 125 if len(nodes) != 0 { 126 r.Fatalf("Bad: %v", nodes) 127 } 128 }) 129} 130 131func TestAPI_CatalogNodes_Filter(t *testing.T) { 132 t.Parallel() 133 c, s := makeClient(t) 134 defer s.Stop() 135 136 // this sets up the catalog entries with things we can filter on 137 testNodeServiceCheckRegistrations(t, c, "dc1") 138 139 catalog := c.Catalog() 140 nodes, _, err := catalog.Nodes(nil) 141 require.NoError(t, err) 142 // 3 nodes inserted by the setup func above plus the agent itself 143 require.Len(t, nodes, 4) 144 145 // now filter down to just a couple nodes with a specific meta entry 146 nodes, _, err = catalog.Nodes(&QueryOptions{Filter: "Meta.env == production"}) 147 require.NoError(t, err) 148 require.Len(t, nodes, 2) 149 150 // filter out everything that isn't bar or baz 151 nodes, _, err = catalog.Nodes(&QueryOptions{Filter: "Node == bar or Node == baz"}) 152 require.NoError(t, err) 153 require.Len(t, nodes, 2) 154 155 // check for non-existent ip for the node addr 156 nodes, _, err = catalog.Nodes(&QueryOptions{Filter: "Address == `10.0.0.1`"}) 157 require.NoError(t, err) 158 require.Empty(t, nodes) 159} 160 161func TestAPI_CatalogServices(t *testing.T) { 162 t.Parallel() 163 c, s := makeClient(t) 164 defer s.Stop() 165 166 catalog := c.Catalog() 167 retry.Run(t, func(r *retry.R) { 168 services, meta, err := catalog.Services(nil) 169 if err != nil { 170 r.Fatal(err) 171 } 172 173 if meta.LastIndex == 0 { 174 r.Fatalf("Bad: %v", meta) 175 } 176 177 if len(services) == 0 { 178 r.Fatalf("Bad: %v", services) 179 } 180 }) 181} 182 183func TestAPI_CatalogServices_NodeMetaFilter(t *testing.T) { 184 t.Parallel() 185 meta := map[string]string{"somekey": "somevalue"} 186 c, s := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) { 187 conf.NodeMeta = meta 188 }) 189 defer s.Stop() 190 191 catalog := c.Catalog() 192 // Make sure we get the service back when filtering by the node's metadata 193 retry.Run(t, func(r *retry.R) { 194 services, meta, err := catalog.Services(&QueryOptions{NodeMeta: meta}) 195 if err != nil { 196 r.Fatal(err) 197 } 198 199 if meta.LastIndex == 0 { 200 r.Fatalf("Bad: %v", meta) 201 } 202 203 if len(services) == 0 { 204 r.Fatalf("Bad: %v", services) 205 } 206 }) 207 208 retry.Run(t, func(r *retry.R) { 209 // Get nothing back when using an invalid filter 210 services, meta, err := catalog.Services(&QueryOptions{NodeMeta: map[string]string{"nope": "nope"}}) 211 if err != nil { 212 r.Fatal(err) 213 } 214 215 if meta.LastIndex == 0 { 216 r.Fatalf("Bad: %v", meta) 217 } 218 219 if len(services) != 0 { 220 r.Fatalf("Bad: %v", services) 221 } 222 }) 223} 224 225func TestAPI_CatalogService(t *testing.T) { 226 t.Parallel() 227 c, s := makeClient(t) 228 defer s.Stop() 229 230 catalog := c.Catalog() 231 232 retry.Run(t, func(r *retry.R) { 233 services, meta, err := catalog.Service("consul", "", nil) 234 if err != nil { 235 r.Fatal(err) 236 } 237 238 if meta.LastIndex == 0 { 239 r.Fatalf("Bad: %v", meta) 240 } 241 242 if len(services) == 0 { 243 r.Fatalf("Bad: %v", services) 244 } 245 246 if services[0].Datacenter != "dc1" { 247 r.Fatalf("Bad datacenter: %v", services[0]) 248 } 249 }) 250} 251 252func TestAPI_CatalogServiceUnmanagedProxy(t *testing.T) { 253 t.Parallel() 254 c, s := makeClient(t) 255 defer s.Stop() 256 257 catalog := c.Catalog() 258 259 proxyReg := testUnmanagedProxyRegistration(t) 260 261 retry.Run(t, func(r *retry.R) { 262 _, err := catalog.Register(proxyReg, nil) 263 r.Check(err) 264 265 services, meta, err := catalog.Service("web-proxy", "", nil) 266 if err != nil { 267 r.Fatal(err) 268 } 269 270 if meta.LastIndex == 0 { 271 r.Fatalf("Bad: %v", meta) 272 } 273 274 if len(services) == 0 { 275 r.Fatalf("Bad: %v", services) 276 } 277 278 if services[0].Datacenter != "dc1" { 279 r.Fatalf("Bad datacenter: %v", services[0]) 280 } 281 282 if !reflect.DeepEqual(services[0].ServiceProxy, proxyReg.Service.Proxy) { 283 r.Fatalf("bad proxy.\nwant: %v\n got: %v", proxyReg.Service.Proxy, 284 services[0].ServiceProxy) 285 } 286 }) 287} 288 289func TestAPI_CatalogServiceCached(t *testing.T) { 290 t.Parallel() 291 c, s := makeClient(t) 292 defer s.Stop() 293 294 catalog := c.Catalog() 295 296 q := &QueryOptions{ 297 UseCache: true, 298 } 299 300 retry.Run(t, func(r *retry.R) { 301 services, meta, err := catalog.Service("consul", "", q) 302 if err != nil { 303 r.Fatal(err) 304 } 305 306 if meta.LastIndex == 0 { 307 r.Fatalf("Bad: %v", meta) 308 } 309 310 if len(services) == 0 { 311 r.Fatalf("Bad: %v", services) 312 } 313 314 if services[0].Datacenter != "dc1" { 315 r.Fatalf("Bad datacenter: %v", services[0]) 316 } 317 }) 318 319 require := require.New(t) 320 321 // Got success, next hit must be cache hit 322 _, meta, err := catalog.Service("consul", "", q) 323 require.NoError(err) 324 require.True(meta.CacheHit) 325 require.Equal(time.Duration(0), meta.CacheAge) 326} 327 328func TestAPI_CatalogService_SingleTag(t *testing.T) { 329 t.Parallel() 330 c, s := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) { 331 conf.NodeName = "node123" 332 }) 333 defer s.Stop() 334 335 agent := c.Agent() 336 catalog := c.Catalog() 337 338 reg := &AgentServiceRegistration{ 339 Name: "foo", 340 ID: "foo1", 341 Tags: []string{"bar"}, 342 } 343 require.NoError(t, agent.ServiceRegister(reg)) 344 defer agent.ServiceDeregister("foo1") 345 346 retry.Run(t, func(r *retry.R) { 347 services, meta, err := catalog.Service("foo", "bar", nil) 348 require.NoError(r, err) 349 require.NotEqual(r, meta.LastIndex, 0) 350 require.Len(r, services, 1) 351 require.Equal(r, services[0].ServiceID, "foo1") 352 }) 353} 354 355func TestAPI_CatalogService_MultipleTags(t *testing.T) { 356 t.Parallel() 357 c, s := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) { 358 conf.NodeName = "node123" 359 }) 360 defer s.Stop() 361 362 agent := c.Agent() 363 catalog := c.Catalog() 364 365 // Make two services with a check 366 reg := &AgentServiceRegistration{ 367 Name: "foo", 368 ID: "foo1", 369 Tags: []string{"bar"}, 370 } 371 require.NoError(t, agent.ServiceRegister(reg)) 372 defer agent.ServiceDeregister("foo1") 373 374 reg2 := &AgentServiceRegistration{ 375 Name: "foo", 376 ID: "foo2", 377 Tags: []string{"bar", "v2"}, 378 } 379 require.NoError(t, agent.ServiceRegister(reg2)) 380 defer agent.ServiceDeregister("foo2") 381 382 // Test searching with one tag (two results) 383 retry.Run(t, func(r *retry.R) { 384 services, meta, err := catalog.ServiceMultipleTags("foo", []string{"bar"}, nil) 385 386 require.NoError(r, err) 387 require.NotEqual(r, meta.LastIndex, 0) 388 389 // Should be 2 services with the `bar` tag 390 require.Len(r, services, 2) 391 }) 392 393 // Test searching with two tags (one result) 394 retry.Run(t, func(r *retry.R) { 395 services, meta, err := catalog.ServiceMultipleTags("foo", []string{"bar", "v2"}, nil) 396 397 require.NoError(r, err) 398 require.NotEqual(r, meta.LastIndex, 0) 399 400 // Should be exactly 1 service, named "foo2" 401 require.Len(r, services, 1) 402 require.Equal(r, services[0].ServiceID, "foo2") 403 }) 404} 405 406func TestAPI_CatalogService_NodeMetaFilter(t *testing.T) { 407 t.Parallel() 408 meta := map[string]string{"somekey": "somevalue"} 409 c, s := makeClientWithConfig(t, nil, func(conf *testutil.TestServerConfig) { 410 conf.NodeMeta = meta 411 }) 412 defer s.Stop() 413 414 catalog := c.Catalog() 415 retry.Run(t, func(r *retry.R) { 416 services, meta, err := catalog.Service("consul", "", &QueryOptions{NodeMeta: meta}) 417 if err != nil { 418 r.Fatal(err) 419 } 420 421 if meta.LastIndex == 0 { 422 r.Fatalf("Bad: %v", meta) 423 } 424 425 if len(services) == 0 { 426 r.Fatalf("Bad: %v", services) 427 } 428 429 if services[0].Datacenter != "dc1" { 430 r.Fatalf("Bad datacenter: %v", services[0]) 431 } 432 }) 433} 434 435func TestAPI_CatalogService_Filter(t *testing.T) { 436 t.Parallel() 437 c, s := makeClient(t) 438 defer s.Stop() 439 440 // this sets up the catalog entries with things we can filter on 441 testNodeServiceCheckRegistrations(t, c, "dc1") 442 443 catalog := c.Catalog() 444 445 services, _, err := catalog.Service("redis", "", &QueryOptions{Filter: "ServiceMeta.version == 1"}) 446 require.NoError(t, err) 447 // finds it on both foo and bar nodes 448 require.Len(t, services, 2) 449 450 require.Condition(t, func() bool { 451 return (services[0].Node == "foo" && services[1].Node == "bar") || 452 (services[0].Node == "bar" && services[1].Node == "foo") 453 }) 454 455 services, _, err = catalog.Service("redis", "", &QueryOptions{Filter: "NodeMeta.os != windows"}) 456 require.NoError(t, err) 457 // finds both service instances on foo 458 require.Len(t, services, 2) 459 require.Equal(t, "foo", services[0].Node) 460 require.Equal(t, "foo", services[1].Node) 461 462 services, _, err = catalog.Service("redis", "", &QueryOptions{Filter: "Address == `10.0.0.1`"}) 463 require.NoError(t, err) 464 require.Empty(t, services) 465 466} 467 468func testUpstreams(t *testing.T) []Upstream { 469 return []Upstream{ 470 { 471 DestinationName: "db", 472 LocalBindPort: 9191, 473 Config: map[string]interface{}{ 474 "connect_timeout_ms": float64(1000), 475 }, 476 }, 477 { 478 DestinationType: UpstreamDestTypePreparedQuery, 479 DestinationName: "geo-cache", 480 LocalBindPort: 8181, 481 }, 482 } 483} 484 485func testExpectUpstreamsWithDefaults(t *testing.T, upstreams []Upstream) []Upstream { 486 ups := make([]Upstream, len(upstreams)) 487 for i := range upstreams { 488 ups[i] = upstreams[i] 489 // Fill in default fields we expect to have back explicitly in a response 490 if ups[i].DestinationType == "" { 491 ups[i].DestinationType = UpstreamDestTypeService 492 } 493 } 494 return ups 495} 496 497// testUnmanagedProxy returns a fully configured external proxy service suitable 498// for checking that all the config fields make it back in a response intact. 499func testUnmanagedProxy(t *testing.T) *AgentService { 500 return &AgentService{ 501 Kind: ServiceKindConnectProxy, 502 Proxy: &AgentServiceConnectProxyConfig{ 503 DestinationServiceName: "web", 504 DestinationServiceID: "web1", 505 LocalServiceAddress: "127.0.0.2", 506 LocalServicePort: 8080, 507 Upstreams: testUpstreams(t), 508 Mode: ProxyModeTransparent, 509 TransparentProxy: &TransparentProxyConfig{ 510 OutboundListenerPort: 808, 511 }, 512 }, 513 ID: "web-proxy1", 514 Service: "web-proxy", 515 Port: 8001, 516 } 517} 518 519// testUnmanagedProxyRegistration returns a *CatalogRegistration for a fully 520// configured external proxy. 521func testUnmanagedProxyRegistration(t *testing.T) *CatalogRegistration { 522 return &CatalogRegistration{ 523 Datacenter: "dc1", 524 Node: "foobar", 525 Address: "192.168.10.10", 526 Service: testUnmanagedProxy(t), 527 } 528} 529 530func TestAPI_CatalogConnect(t *testing.T) { 531 t.Parallel() 532 c, s := makeClient(t) 533 defer s.Stop() 534 535 catalog := c.Catalog() 536 537 // Register service and proxy instances to test against. 538 proxyReg := testUnmanagedProxyRegistration(t) 539 540 proxy := proxyReg.Service 541 542 service := &AgentService{ 543 ID: proxyReg.Service.Proxy.DestinationServiceID, 544 Service: proxyReg.Service.Proxy.DestinationServiceName, 545 Port: 8000, 546 } 547 check := &AgentCheck{ 548 Node: "foobar", 549 CheckID: "service:" + service.ID, 550 Name: "Redis health check", 551 Notes: "Script based health check", 552 Status: HealthPassing, 553 ServiceID: service.ID, 554 } 555 556 reg := &CatalogRegistration{ 557 Datacenter: "dc1", 558 Node: "foobar", 559 Address: "192.168.10.10", 560 Service: service, 561 Check: check, 562 } 563 564 retry.Run(t, func(r *retry.R) { 565 if _, err := catalog.Register(reg, nil); err != nil { 566 r.Fatal(err) 567 } 568 if _, err := catalog.Register(proxyReg, nil); err != nil { 569 r.Fatal(err) 570 } 571 572 services, meta, err := catalog.Connect(proxyReg.Service.Proxy.DestinationServiceName, "", nil) 573 if err != nil { 574 r.Fatal(err) 575 } 576 577 if meta.LastIndex == 0 { 578 r.Fatalf("Bad: %v", meta) 579 } 580 581 if len(services) == 0 { 582 r.Fatalf("Bad: %v", services) 583 } 584 585 if services[0].Datacenter != "dc1" { 586 r.Fatalf("Bad datacenter: %v", services[0]) 587 } 588 589 if services[0].ServicePort != proxy.Port { 590 r.Fatalf("Returned port should be for proxy: %v", services[0]) 591 } 592 593 if !reflect.DeepEqual(services[0].ServiceProxy, proxy.Proxy) { 594 r.Fatalf("Returned proxy config should match:\nWant: %v\n Got: %v", 595 proxy.Proxy, services[0].ServiceProxy) 596 } 597 }) 598} 599 600func TestAPI_CatalogConnectNative(t *testing.T) { 601 t.Parallel() 602 c, s := makeClient(t) 603 defer s.Stop() 604 605 catalog := c.Catalog() 606 607 // Register service and proxy instances to test against. 608 service := &AgentService{ 609 ID: "redis1", 610 Service: "redis", 611 Port: 8000, 612 Connect: &AgentServiceConnect{Native: true}, 613 } 614 check := &AgentCheck{ 615 Node: "foobar", 616 CheckID: "service:redis1", 617 Name: "Redis health check", 618 Notes: "Script based health check", 619 Status: HealthPassing, 620 ServiceID: "redis1", 621 } 622 623 reg := &CatalogRegistration{ 624 Datacenter: "dc1", 625 Node: "foobar", 626 Address: "192.168.10.10", 627 Service: service, 628 Check: check, 629 } 630 631 retry.Run(t, func(r *retry.R) { 632 if _, err := catalog.Register(reg, nil); err != nil { 633 r.Fatal(err) 634 } 635 636 services, meta, err := catalog.Connect("redis", "", nil) 637 if err != nil { 638 r.Fatal(err) 639 } 640 641 if meta.LastIndex == 0 { 642 r.Fatalf("Bad: %v", meta) 643 } 644 645 if len(services) == 0 { 646 r.Fatalf("Bad: %v", services) 647 } 648 649 if services[0].Datacenter != "dc1" { 650 r.Fatalf("Bad datacenter: %v", services[0]) 651 } 652 653 if services[0].ServicePort != service.Port { 654 r.Fatalf("Returned port should be for proxy: %v", services[0]) 655 } 656 }) 657} 658 659func TestAPI_CatalogConnect_Filter(t *testing.T) { 660 t.Parallel() 661 c, s := makeClient(t) 662 defer s.Stop() 663 664 // this sets up the catalog entries with things we can filter on 665 testNodeServiceCheckRegistrations(t, c, "dc1") 666 667 catalog := c.Catalog() 668 669 services, _, err := catalog.Connect("web", "", &QueryOptions{Filter: "ServicePort == 443"}) 670 require.NoError(t, err) 671 require.Len(t, services, 2) 672 require.Condition(t, func() bool { 673 return (services[0].Node == "bar" && services[1].Node == "baz") || 674 (services[0].Node == "baz" && services[1].Node == "bar") 675 }) 676 677 // All the web-connect services are native 678 services, _, err = catalog.Connect("web", "", &QueryOptions{Filter: "ServiceConnect.Native != true"}) 679 require.NoError(t, err) 680 require.Empty(t, services) 681} 682 683func TestAPI_CatalogNode(t *testing.T) { 684 t.Parallel() 685 c, s := makeClient(t) 686 defer s.Stop() 687 688 catalog := c.Catalog() 689 690 name, err := c.Agent().NodeName() 691 require.NoError(t, err) 692 693 proxyReg := testUnmanagedProxyRegistration(t) 694 proxyReg.Node = name 695 proxyReg.SkipNodeUpdate = true 696 697 retry.Run(t, func(r *retry.R) { 698 // Register a connect proxy to ensure all it's config fields are returned 699 _, err := catalog.Register(proxyReg, nil) 700 r.Check(err) 701 702 info, meta, err := catalog.Node(name, nil) 703 if err != nil { 704 r.Fatal(err) 705 } 706 707 if meta.LastIndex == 0 { 708 r.Fatalf("Bad: %v", meta) 709 } 710 711 if len(info.Services) != 2 { 712 r.Fatalf("Bad: %v (len %d)", info, len(info.Services)) 713 } 714 715 if _, ok := info.Node.TaggedAddresses["wan"]; !ok { 716 r.Fatalf("Bad: %v", info.Node.TaggedAddresses) 717 } 718 719 if info.Node.Datacenter != "dc1" { 720 r.Fatalf("Bad datacenter: %v", info) 721 } 722 723 if _, ok := info.Services["web-proxy1"]; !ok { 724 r.Fatalf("Missing proxy service: %v", info.Services) 725 } 726 727 if !reflect.DeepEqual(proxyReg.Service.Proxy, info.Services["web-proxy1"].Proxy) { 728 r.Fatalf("Bad proxy config:\nwant %v\n got: %v", proxyReg.Service.Proxy, 729 info.Services["web-proxy"].Proxy) 730 } 731 }) 732} 733 734func TestAPI_CatalogNodeServiceList(t *testing.T) { 735 t.Parallel() 736 c, s := makeClient(t) 737 defer s.Stop() 738 739 catalog := c.Catalog() 740 741 name, err := c.Agent().NodeName() 742 require.NoError(t, err) 743 744 proxyReg := testUnmanagedProxyRegistration(t) 745 proxyReg.Node = name 746 proxyReg.SkipNodeUpdate = true 747 748 retry.Run(t, func(r *retry.R) { 749 // Register a connect proxy to ensure all it's config fields are returned 750 _, err := catalog.Register(proxyReg, nil) 751 r.Check(err) 752 753 info, meta, err := catalog.NodeServiceList(name, nil) 754 if err != nil { 755 r.Fatal(err) 756 } 757 758 if meta.LastIndex == 0 { 759 r.Fatalf("Bad: %v", meta) 760 } 761 762 if len(info.Services) != 2 { 763 r.Fatalf("Bad: %v (len %d)", info, len(info.Services)) 764 } 765 766 if _, ok := info.Node.TaggedAddresses["wan"]; !ok { 767 r.Fatalf("Bad: %v", info.Node.TaggedAddresses) 768 } 769 770 if info.Node.Datacenter != "dc1" { 771 r.Fatalf("Bad datacenter: %v", info) 772 } 773 774 var proxySvc *AgentService 775 for _, svc := range info.Services { 776 if svc.ID == "web-proxy1" { 777 proxySvc = svc 778 break 779 } 780 } 781 782 if proxySvc == nil { 783 r.Fatalf("Missing proxy service: %v", info.Services) 784 } 785 786 if !reflect.DeepEqual(proxyReg.Service.Proxy, proxySvc.Proxy) { 787 r.Fatalf("Bad proxy config:\nwant %v\n got: %v", proxyReg.Service.Proxy, 788 proxySvc.Proxy) 789 } 790 }) 791} 792 793func TestAPI_CatalogNode_Filter(t *testing.T) { 794 t.Parallel() 795 c, s := makeClient(t) 796 defer s.Stop() 797 798 // this sets up the catalog entries with things we can filter on 799 testNodeServiceCheckRegistrations(t, c, "dc1") 800 801 catalog := c.Catalog() 802 803 // should have only 1 matching service 804 info, _, err := catalog.Node("bar", &QueryOptions{Filter: "connect in Tags"}) 805 require.NoError(t, err) 806 require.Len(t, info.Services, 1) 807 require.Contains(t, info.Services, "webV1") 808 require.Equal(t, "web", info.Services["webV1"].Service) 809 810 // should get two services for the node 811 info, _, err = catalog.Node("baz", &QueryOptions{Filter: "connect in Tags"}) 812 require.NoError(t, err) 813 require.Len(t, info.Services, 2) 814} 815 816func TestAPI_CatalogRegistration(t *testing.T) { 817 t.Parallel() 818 c, s := makeClient(t) 819 defer s.Stop() 820 821 catalog := c.Catalog() 822 823 service := &AgentService{ 824 ID: "redis1", 825 Service: "redis", 826 Tags: []string{"master", "v1"}, 827 Port: 8000, 828 } 829 830 check := &AgentCheck{ 831 Node: "foobar", 832 CheckID: "service:redis1-a", 833 Name: "Redis health check", 834 Notes: "Script based health check", 835 Status: HealthPassing, 836 ServiceID: "redis1", 837 } 838 839 checks := HealthChecks{ 840 &HealthCheck{ 841 Node: "foobar", 842 CheckID: "service:redis1-b", 843 Name: "Redis health check", 844 Notes: "Script based health check", 845 Status: HealthPassing, 846 ServiceID: "redis1", 847 }, 848 } 849 850 reg := &CatalogRegistration{ 851 Datacenter: "dc1", 852 Node: "foobar", 853 Address: "192.168.10.10", 854 NodeMeta: map[string]string{"somekey": "somevalue"}, 855 Service: service, 856 // Specifying both Check and Checks is accepted by Consul 857 Check: check, 858 Checks: checks, 859 } 860 // Register a connect proxy for that service too 861 proxy := &AgentService{ 862 ID: "redis-proxy1", 863 Service: "redis-proxy", 864 Port: 8001, 865 Kind: ServiceKindConnectProxy, 866 Proxy: &AgentServiceConnectProxyConfig{ 867 DestinationServiceName: service.Service, 868 }, 869 } 870 proxyReg := &CatalogRegistration{ 871 Datacenter: "dc1", 872 Node: "foobar", 873 Address: "192.168.10.10", 874 NodeMeta: map[string]string{"somekey": "somevalue"}, 875 Service: proxy, 876 } 877 retry.Run(t, func(r *retry.R) { 878 if _, err := catalog.Register(reg, nil); err != nil { 879 r.Fatal(err) 880 } 881 if _, err := catalog.Register(proxyReg, nil); err != nil { 882 r.Fatal(err) 883 } 884 885 node, _, err := catalog.Node("foobar", nil) 886 if err != nil { 887 r.Fatal(err) 888 } 889 890 if _, ok := node.Services["redis1"]; !ok { 891 r.Fatal("missing service: redis1") 892 } 893 894 if _, ok := node.Services["redis-proxy1"]; !ok { 895 r.Fatal("missing service: redis-proxy1") 896 } 897 898 health, _, err := c.Health().Node("foobar", nil) 899 if err != nil { 900 r.Fatal(err) 901 } 902 903 if health[0].CheckID != "service:redis1-a" { 904 r.Fatal("missing checkid service:redis1-a") 905 } 906 907 if health[1].CheckID != "service:redis1-b" { 908 r.Fatal("missing checkid service:redis1-b") 909 } 910 911 if v, ok := node.Node.Meta["somekey"]; !ok || v != "somevalue" { 912 r.Fatal("missing node meta pair somekey:somevalue") 913 } 914 }) 915 916 // Test catalog deregistration of the previously registered service 917 dereg := &CatalogDeregistration{ 918 Datacenter: "dc1", 919 Node: "foobar", 920 Address: "192.168.10.10", 921 ServiceID: "redis1", 922 } 923 924 // ... and proxy 925 deregProxy := &CatalogDeregistration{ 926 Datacenter: "dc1", 927 Node: "foobar", 928 Address: "192.168.10.10", 929 ServiceID: "redis-proxy1", 930 } 931 932 if _, err := catalog.Deregister(dereg, nil); err != nil { 933 t.Fatalf("err: %v", err) 934 } 935 936 if _, err := catalog.Deregister(deregProxy, nil); err != nil { 937 t.Fatalf("err: %v", err) 938 } 939 940 retry.Run(t, func(r *retry.R) { 941 node, _, err := catalog.Node("foobar", nil) 942 if err != nil { 943 r.Fatal(err) 944 } 945 946 if _, ok := node.Services["redis1"]; ok { 947 r.Fatal("ServiceID:redis1 is not deregistered") 948 } 949 950 if _, ok := node.Services["redis-proxy1"]; ok { 951 r.Fatal("ServiceID:redis-proxy1 is not deregistered") 952 } 953 }) 954 955 // Test deregistration of the previously registered check 956 dereg = &CatalogDeregistration{ 957 Datacenter: "dc1", 958 Node: "foobar", 959 Address: "192.168.10.10", 960 CheckID: "service:redis1-a", 961 } 962 963 if _, err := catalog.Deregister(dereg, nil); err != nil { 964 t.Fatalf("err: %v", err) 965 } 966 967 dereg = &CatalogDeregistration{ 968 Datacenter: "dc1", 969 Node: "foobar", 970 Address: "192.168.10.10", 971 CheckID: "service:redis1-b", 972 } 973 974 if _, err := catalog.Deregister(dereg, nil); err != nil { 975 t.Fatalf("err: %v", err) 976 } 977 978 retry.Run(t, func(r *retry.R) { 979 health, _, err := c.Health().Node("foobar", nil) 980 if err != nil { 981 r.Fatal(err) 982 } 983 984 if len(health) != 0 { 985 r.Fatal("CheckID:service:redis1-a or CheckID:service:redis1-a is not deregistered") 986 } 987 }) 988 989 // Test node deregistration of the previously registered node 990 dereg = &CatalogDeregistration{ 991 Datacenter: "dc1", 992 Node: "foobar", 993 Address: "192.168.10.10", 994 } 995 996 if _, err := catalog.Deregister(dereg, nil); err != nil { 997 t.Fatalf("err: %v", err) 998 } 999 1000 retry.Run(t, func(r *retry.R) { 1001 node, _, err := catalog.Node("foobar", nil) 1002 if err != nil { 1003 r.Fatal(err) 1004 } 1005 1006 if node != nil { 1007 r.Fatalf("node is not deregistered: %v", node) 1008 } 1009 }) 1010} 1011 1012func TestAPI_CatalogEnableTagOverride(t *testing.T) { 1013 t.Parallel() 1014 c, s := makeClient(t) 1015 defer s.Stop() 1016 s.WaitForSerfCheck(t) 1017 1018 catalog := c.Catalog() 1019 1020 service := &AgentService{ 1021 ID: "redis1", 1022 Service: "redis", 1023 Tags: []string{"master", "v1"}, 1024 Port: 8000, 1025 } 1026 1027 reg := &CatalogRegistration{ 1028 Datacenter: "dc1", 1029 Node: "foobar", 1030 Address: "192.168.10.10", 1031 Service: service, 1032 } 1033 1034 retry.Run(t, func(r *retry.R) { 1035 if _, err := catalog.Register(reg, nil); err != nil { 1036 r.Fatal(err) 1037 } 1038 1039 node, _, err := catalog.Node("foobar", nil) 1040 if err != nil { 1041 r.Fatal(err) 1042 } 1043 1044 if _, ok := node.Services["redis1"]; !ok { 1045 r.Fatal("missing service: redis1") 1046 } 1047 if node.Services["redis1"].EnableTagOverride != false { 1048 r.Fatal("tag override set") 1049 } 1050 1051 services, _, err := catalog.Service("redis", "", nil) 1052 if err != nil { 1053 r.Fatal(err) 1054 } 1055 1056 if len(services) < 1 || services[0].ServiceName != "redis" { 1057 r.Fatal("missing service: redis") 1058 } 1059 if services[0].ServiceEnableTagOverride != false { 1060 r.Fatal("tag override set") 1061 } 1062 }) 1063 1064 service.EnableTagOverride = true 1065 1066 retry.Run(t, func(r *retry.R) { 1067 if _, err := catalog.Register(reg, nil); err != nil { 1068 r.Fatal(err) 1069 } 1070 1071 node, _, err := catalog.Node("foobar", nil) 1072 if err != nil { 1073 r.Fatal(err) 1074 } 1075 1076 if _, ok := node.Services["redis1"]; !ok { 1077 r.Fatal("missing service: redis1") 1078 } 1079 if node.Services["redis1"].EnableTagOverride != true { 1080 r.Fatal("tag override not set") 1081 } 1082 1083 services, _, err := catalog.Service("redis", "", nil) 1084 if err != nil { 1085 r.Fatal(err) 1086 } 1087 1088 if len(services) < 1 || services[0].ServiceName != "redis" { 1089 r.Fatal("missing service: redis") 1090 } 1091 if services[0].ServiceEnableTagOverride != true { 1092 r.Fatal("tag override not set") 1093 } 1094 }) 1095} 1096 1097func TestAPI_CatalogGatewayServices_Terminating(t *testing.T) { 1098 t.Parallel() 1099 c, s := makeClient(t) 1100 defer s.Stop() 1101 s.WaitForSerfCheck(t) 1102 1103 catalog := c.Catalog() 1104 1105 // Register a service to be covered by a wildcard in the config entry 1106 svc := &AgentService{ 1107 ID: "redis", 1108 Service: "redis", 1109 Port: 6379, 1110 } 1111 reg := &CatalogRegistration{ 1112 Datacenter: "dc1", 1113 Node: "bar", 1114 Address: "192.168.10.11", 1115 Service: svc, 1116 } 1117 retry.Run(t, func(r *retry.R) { 1118 if _, err := catalog.Register(reg, nil); err != nil { 1119 r.Fatal(err) 1120 } 1121 }) 1122 1123 entries := c.ConfigEntries() 1124 1125 // Associate the gateway and api/redis services 1126 gwEntry := TerminatingGatewayConfigEntry{ 1127 Kind: TerminatingGateway, 1128 Name: "terminating", 1129 Services: []LinkedService{ 1130 { 1131 Name: "api", 1132 CAFile: "api/ca.crt", 1133 CertFile: "api/client.crt", 1134 KeyFile: "api/client.key", 1135 SNI: "my-domain", 1136 }, 1137 { 1138 Name: "*", 1139 CAFile: "ca.crt", 1140 CertFile: "client.crt", 1141 KeyFile: "client.key", 1142 SNI: "my-alt-domain", 1143 }, 1144 }, 1145 } 1146 retry.Run(t, func(r *retry.R) { 1147 if success, _, err := entries.Set(&gwEntry, nil); err != nil || !success { 1148 r.Fatal(err) 1149 } 1150 }) 1151 1152 expect := []*GatewayService{ 1153 { 1154 Service: CompoundServiceName{"api", defaultNamespace}, 1155 Gateway: CompoundServiceName{"terminating", defaultNamespace}, 1156 GatewayKind: ServiceKindTerminatingGateway, 1157 CAFile: "api/ca.crt", 1158 CertFile: "api/client.crt", 1159 KeyFile: "api/client.key", 1160 SNI: "my-domain", 1161 }, 1162 { 1163 Service: CompoundServiceName{"redis", defaultNamespace}, 1164 Gateway: CompoundServiceName{"terminating", defaultNamespace}, 1165 GatewayKind: ServiceKindTerminatingGateway, 1166 CAFile: "ca.crt", 1167 CertFile: "client.crt", 1168 KeyFile: "client.key", 1169 SNI: "my-alt-domain", 1170 FromWildcard: true, 1171 }, 1172 } 1173 retry.Run(t, func(r *retry.R) { 1174 resp, _, err := catalog.GatewayServices("terminating", nil) 1175 assert.NoError(r, err) 1176 assert.Equal(r, expect, resp) 1177 }) 1178} 1179 1180func TestAPI_CatalogGatewayServices_Ingress(t *testing.T) { 1181 t.Parallel() 1182 c, s := makeClient(t) 1183 defer s.Stop() 1184 1185 s.WaitForSerfCheck(t) 1186 1187 entries := c.ConfigEntries() 1188 1189 // Associate the gateway and api/redis services 1190 gwEntry := IngressGatewayConfigEntry{ 1191 Kind: "ingress-gateway", 1192 Name: "ingress", 1193 Listeners: []IngressListener{ 1194 { 1195 Port: 8888, 1196 Services: []IngressService{ 1197 { 1198 Name: "api", 1199 }, 1200 }, 1201 }, 1202 { 1203 Port: 9999, 1204 Services: []IngressService{ 1205 { 1206 Name: "redis", 1207 }, 1208 }, 1209 }, 1210 }, 1211 } 1212 retry.Run(t, func(r *retry.R) { 1213 if success, _, err := entries.Set(&gwEntry, nil); err != nil || !success { 1214 r.Fatal(err) 1215 } 1216 }) 1217 1218 catalog := c.Catalog() 1219 1220 expect := []*GatewayService{ 1221 { 1222 Service: CompoundServiceName{"api", defaultNamespace}, 1223 Gateway: CompoundServiceName{"ingress", defaultNamespace}, 1224 GatewayKind: ServiceKindIngressGateway, 1225 Protocol: "tcp", 1226 Port: 8888, 1227 }, 1228 { 1229 Service: CompoundServiceName{"redis", defaultNamespace}, 1230 Gateway: CompoundServiceName{"ingress", defaultNamespace}, 1231 GatewayKind: ServiceKindIngressGateway, 1232 Protocol: "tcp", 1233 Port: 9999, 1234 }, 1235 } 1236 retry.Run(t, func(r *retry.R) { 1237 resp, _, err := catalog.GatewayServices("ingress", nil) 1238 assert.NoError(r, err) 1239 assert.Equal(r, expect, resp) 1240 }) 1241} 1242