github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/command/agent/consul/unit_test.go (about) 1 package consul 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "strings" 8 "sync/atomic" 9 "testing" 10 "time" 11 12 "github.com/hashicorp/consul/api" 13 "github.com/hashicorp/nomad/ci" 14 "github.com/hashicorp/nomad/client/serviceregistration" 15 "github.com/hashicorp/nomad/helper/testlog" 16 "github.com/hashicorp/nomad/helper/uuid" 17 "github.com/hashicorp/nomad/nomad/structs" 18 "github.com/hashicorp/nomad/plugins/drivers" 19 "github.com/kr/pretty" 20 "github.com/shoenig/test/must" 21 "github.com/stretchr/testify/require" 22 ) 23 24 const ( 25 // Ports used in testWorkload 26 xPort = 1234 27 yPort = 1235 28 ) 29 30 func testWorkload() *serviceregistration.WorkloadServices { 31 return &serviceregistration.WorkloadServices{ 32 AllocInfo: structs.AllocInfo{ 33 AllocID: uuid.Generate(), 34 Task: "taskname", 35 }, 36 Restarter: &restartRecorder{}, 37 Services: []*structs.Service{ 38 { 39 Name: "taskname-service", 40 PortLabel: "x", 41 Tags: []string{"tag1", "tag2"}, 42 Meta: map[string]string{"meta1": "foo"}, 43 }, 44 }, 45 Networks: []*structs.NetworkResource{ 46 { 47 DynamicPorts: []structs.Port{ 48 {Label: "x", Value: xPort}, 49 {Label: "y", Value: yPort}, 50 }, 51 }, 52 }, 53 } 54 } 55 56 // restartRecorder is a minimal WorkloadRestarter implementation that simply 57 // counts how many restarts were triggered. 58 type restartRecorder struct { 59 restarts int64 60 } 61 62 func (r *restartRecorder) Restart(ctx context.Context, event *structs.TaskEvent, failure bool) error { 63 atomic.AddInt64(&r.restarts, 1) 64 return nil 65 } 66 67 // testFakeCtx contains a fake Consul AgentAPI 68 type testFakeCtx struct { 69 ServiceClient *ServiceClient 70 FakeConsul *MockAgent 71 Workload *serviceregistration.WorkloadServices 72 } 73 74 var errNoOps = fmt.Errorf("testing error: no pending operations") 75 76 // syncOps simulates one iteration of the ServiceClient.Run loop and returns 77 // any errors returned by sync() or errNoOps if no pending operations. 78 func (t *testFakeCtx) syncOnce(reason syncReason) error { 79 switch reason { 80 81 case syncPeriodic: 82 err := t.ServiceClient.sync(syncPeriodic) 83 if err == nil { 84 t.ServiceClient.clearExplicitlyDeregistered() 85 } 86 return err 87 88 case syncNewOps: 89 select { 90 case ops := <-t.ServiceClient.opCh: 91 t.ServiceClient.merge(ops) 92 err := t.ServiceClient.sync(syncNewOps) 93 if err == nil { 94 t.ServiceClient.clearExplicitlyDeregistered() 95 } 96 return err 97 default: 98 return errNoOps 99 } 100 101 case syncShutdown: 102 return errors.New("no test for sync due to shutdown") 103 } 104 105 return errors.New("bad sync reason") 106 } 107 108 // setupFake creates a testFakeCtx with a ServiceClient backed by a fakeConsul. 109 // A test Workload is also provided. 110 func setupFake(t *testing.T) *testFakeCtx { 111 agentClient := NewMockAgent(ossFeatures) 112 nsClient := NewNamespacesClient(NewMockNamespaces(nil), agentClient) 113 workload := testWorkload() 114 115 // by default start fake client being out of probation 116 serviceClient := NewServiceClient(agentClient, nsClient, testlog.HCLogger(t), true) 117 serviceClient.deregisterProbationExpiry = time.Now().Add(-1 * time.Minute) 118 119 return &testFakeCtx{ 120 ServiceClient: serviceClient, 121 FakeConsul: agentClient, 122 Workload: workload, 123 } 124 } 125 126 func TestConsul_ChangeTags(t *testing.T) { 127 ci.Parallel(t) 128 129 ctx := setupFake(t) 130 r := require.New(t) 131 132 r.NoError(ctx.ServiceClient.RegisterWorkload(ctx.Workload)) 133 r.NoError(ctx.syncOnce(syncNewOps)) 134 r.Equal(1, len(ctx.FakeConsul.services), "Expected 1 service to be registered with Consul") 135 136 // Validate the alloc registration 137 reg1, err := ctx.ServiceClient.AllocRegistrations(ctx.Workload.AllocInfo.AllocID) 138 r.NoError(err) 139 r.NotNil(reg1, "Unexpected nil alloc registration") 140 r.Equal(1, reg1.NumServices()) 141 r.Equal(0, reg1.NumChecks()) 142 143 serviceBefore := ctx.FakeConsul.lookupService("default", "taskname-service")[0] 144 r.Equal(serviceBefore.Name, ctx.Workload.Services[0].Name) 145 r.Equal(serviceBefore.Tags, ctx.Workload.Services[0].Tags) 146 147 // Update the task definition 148 origWorkload := ctx.Workload.Copy() 149 ctx.Workload.Services[0].Tags[0] = "new-tag" 150 151 // Register and sync the update 152 r.NoError(ctx.ServiceClient.UpdateWorkload(origWorkload, ctx.Workload)) 153 r.NoError(ctx.syncOnce(syncNewOps)) 154 r.Equal(1, len(ctx.FakeConsul.services), "Expected 1 service to be registered with Consul") 155 156 // Validate the consul service definition changed 157 serviceAfter := ctx.FakeConsul.lookupService("default", "taskname-service")[0] 158 r.Equal(serviceAfter.Name, ctx.Workload.Services[0].Name) 159 r.Equal(serviceAfter.Tags, ctx.Workload.Services[0].Tags) 160 r.Equal("new-tag", serviceAfter.Tags[0]) 161 } 162 163 func TestConsul_EnableTagOverride_Syncs(t *testing.T) { 164 ci.Parallel(t) 165 166 ctx := setupFake(t) 167 r := require.New(t) 168 169 // Configure our test service to set EnableTagOverride = true 170 ctx.Workload.Services[0].EnableTagOverride = true 171 172 r.NoError(ctx.ServiceClient.RegisterWorkload(ctx.Workload)) 173 r.NoError(ctx.syncOnce(syncNewOps)) 174 r.Equal(1, len(ctx.FakeConsul.services)) 175 176 // Validate the alloc registration 177 reg1, err := ctx.ServiceClient.AllocRegistrations(ctx.Workload.AllocInfo.AllocID) 178 r.NoError(err) 179 r.NotNil(reg1) 180 r.Equal(1, reg1.NumServices()) 181 r.Equal(0, reg1.NumChecks()) 182 183 const service = "taskname-service" 184 185 // check things are what we expect 186 consulServiceDefBefore := ctx.FakeConsul.lookupService("default", service)[0] 187 r.Equal(ctx.Workload.Services[0].Name, consulServiceDefBefore.Name) 188 r.Equal([]string{"tag1", "tag2"}, consulServiceDefBefore.Tags) 189 r.True(consulServiceDefBefore.EnableTagOverride) 190 191 // manually set the tags in consul 192 ctx.FakeConsul.lookupService("default", service)[0].Tags = []string{"new", "tags"} 193 194 // do a periodic sync (which will respect EnableTagOverride) 195 r.NoError(ctx.syncOnce(syncPeriodic)) 196 r.Equal(1, len(ctx.FakeConsul.services)) 197 consulServiceDefAfter := ctx.FakeConsul.lookupService("default", service)[0] 198 r.Equal([]string{"new", "tags"}, consulServiceDefAfter.Tags) // manually set tags should still be there 199 200 // now do a new-ops sync (which will override EnableTagOverride) 201 r.NoError(ctx.ServiceClient.RegisterWorkload(ctx.Workload)) 202 r.NoError(ctx.syncOnce(syncNewOps)) 203 r.Equal(1, len(ctx.FakeConsul.services)) 204 consulServiceDefUpdated := ctx.FakeConsul.lookupService("default", service)[0] 205 r.Equal([]string{"tag1", "tag2"}, consulServiceDefUpdated.Tags) // jobspec tags should be set now 206 } 207 208 // TestConsul_ChangePorts asserts that changing the ports on a service updates 209 // it in Consul. Pre-0.7.1 ports were not part of the service ID and this was a 210 // slightly different code path than changing tags. 211 func TestConsul_ChangePorts(t *testing.T) { 212 ci.Parallel(t) 213 214 ctx := setupFake(t) 215 216 ctx.Workload.Services[0].Checks = []*structs.ServiceCheck{ 217 { 218 Name: "c1", 219 Type: "tcp", 220 Interval: time.Second, 221 Timeout: time.Second, 222 PortLabel: "x", 223 }, 224 { 225 Name: "c2", 226 Type: "script", 227 Interval: 9000 * time.Hour, 228 Timeout: time.Second, 229 }, 230 { 231 Name: "c3", 232 Type: "http", 233 Protocol: "http", 234 Path: "/", 235 Interval: time.Second, 236 Timeout: time.Second, 237 PortLabel: "y", 238 }, 239 } 240 241 must.NoError(t, ctx.ServiceClient.RegisterWorkload(ctx.Workload)) 242 must.NoError(t, ctx.syncOnce(syncNewOps)) 243 must.MapLen(t, 1, ctx.FakeConsul.services["default"]) 244 245 for _, v := range ctx.FakeConsul.services["default"] { 246 must.Eq(t, ctx.Workload.Services[0].Name, v.Name) 247 must.Eq(t, ctx.Workload.Services[0].Tags, v.Tags) 248 must.Eq(t, xPort, v.Port) 249 } 250 251 must.MapLen(t, 3, ctx.FakeConsul.checks["default"], must.Sprintf("checks %#v", ctx.FakeConsul.checks)) 252 253 origTCPKey := "" 254 origScriptKey := "" 255 origHTTPKey := "" 256 for k, v := range ctx.FakeConsul.checks["default"] { 257 switch v.Name { 258 case "c1": 259 origTCPKey = k 260 must.Eq(t, fmt.Sprintf(":%d", xPort), v.TCP) 261 case "c2": 262 origScriptKey = k 263 case "c3": 264 origHTTPKey = k 265 must.Eq(t, fmt.Sprintf("http://:%d/", yPort), v.HTTP) 266 default: 267 t.Fatalf("unexpected check: %q", v.Name) 268 } 269 } 270 271 must.StrHasPrefix(t, "_nomad-check-", origTCPKey) 272 must.StrHasPrefix(t, "_nomad-check-", origScriptKey) 273 must.StrHasPrefix(t, "_nomad-check-", origHTTPKey) 274 275 // Now update the PortLabel on the Service and Check c3 276 origWorkload := ctx.Workload.Copy() 277 ctx.Workload.Services[0].PortLabel = "y" 278 ctx.Workload.Services[0].Checks = []*structs.ServiceCheck{ 279 { 280 Name: "c1", 281 Type: "tcp", 282 Interval: time.Second, 283 Timeout: time.Second, 284 PortLabel: "x", 285 }, 286 { 287 Name: "c2", 288 Type: "script", 289 Interval: 9000 * time.Hour, 290 Timeout: time.Second, 291 }, 292 { 293 Name: "c3", 294 Type: "http", 295 Protocol: "http", 296 Path: "/", 297 Interval: time.Second, 298 Timeout: time.Second, 299 // Removed PortLabel; should default to service's (y) 300 }, 301 } 302 303 must.NoError(t, ctx.ServiceClient.UpdateWorkload(origWorkload, ctx.Workload)) 304 must.NoError(t, ctx.syncOnce(syncNewOps)) 305 must.MapLen(t, 1, ctx.FakeConsul.services["default"]) 306 307 for _, v := range ctx.FakeConsul.services["default"] { 308 must.Eq(t, ctx.Workload.Services[0].Name, v.Name) 309 must.Eq(t, ctx.Workload.Services[0].Tags, v.Tags) 310 must.Eq(t, yPort, v.Port) 311 } 312 must.MapLen(t, 3, ctx.FakeConsul.checks["default"]) 313 314 for k, v := range ctx.FakeConsul.checks["default"] { 315 switch v.Name { 316 case "c1": 317 // C1 is changed because the service was re-registered 318 must.NotEq(t, origTCPKey, k) 319 must.Eq(t, fmt.Sprintf(":%d", xPort), v.TCP) 320 case "c2": 321 // C2 is changed because the service was re-registered 322 must.NotEq(t, origScriptKey, k) 323 case "c3": 324 must.NotEq(t, origHTTPKey, k) 325 must.Eq(t, fmt.Sprintf("http://:%d/", yPort), v.HTTP) 326 default: 327 must.Unreachable(t, must.Sprintf("unknown check: %q", k)) 328 } 329 } 330 } 331 332 // TestConsul_ShutdownOK tests the ok path for the shutdown logic in 333 // ServiceClient. 334 func TestConsul_ShutdownOK(t *testing.T) { 335 ci.Parallel(t) 336 337 require := require.New(t) 338 ctx := setupFake(t) 339 go ctx.ServiceClient.Run() 340 341 // register the Nomad agent service and check 342 agentServices := []*structs.Service{ 343 { 344 Name: "http", 345 Tags: []string{"nomad"}, 346 PortLabel: "localhost:2345", 347 Checks: []*structs.ServiceCheck{ 348 { 349 Name: "nomad-tcp", 350 Type: "tcp", 351 Interval: 9000 * time.Hour, // make check block 352 Timeout: 10 * time.Second, 353 InitialStatus: "warning", 354 }, 355 }, 356 }, 357 } 358 require.NoError(ctx.ServiceClient.RegisterAgent("client", agentServices)) 359 require.Eventually(ctx.ServiceClient.hasSeen, time.Second, 10*time.Millisecond) 360 361 // assert successful registration 362 require.Len(ctx.FakeConsul.services["default"], 1, "expected agent service to be registered") 363 require.Len(ctx.FakeConsul.checks["default"], 1, "expected agent check to be registered") 364 require.Contains(ctx.FakeConsul.services["default"], makeAgentServiceID("client", agentServices[0])) 365 366 // Shutdown() should block until Nomad agent service/check is deregistered 367 require.NoError(ctx.ServiceClient.Shutdown()) 368 require.Len(ctx.FakeConsul.services["default"], 0, "expected agent service to be deregistered") 369 require.Len(ctx.FakeConsul.checks["default"], 0, "expected agent check to be deregistered") 370 } 371 372 // TestConsul_ShutdownBlocked tests the blocked past deadline path for the 373 // shutdown logic in ServiceClient. 374 func TestConsul_ShutdownBlocked(t *testing.T) { 375 ci.Parallel(t) 376 377 require := require.New(t) 378 ctx := setupFake(t) 379 // can be short because we're intentionally blocking, but needs to 380 // be longer than the time we'll block Consul so we can be sure 381 // we're not delayed either. 382 ctx.ServiceClient.shutdownWait = time.Second 383 go ctx.ServiceClient.Run() 384 385 // register the Nomad agent service and check 386 agentServices := []*structs.Service{ 387 { 388 Name: "http", 389 Tags: []string{"nomad"}, 390 PortLabel: "localhost:2345", 391 Checks: []*structs.ServiceCheck{ 392 { 393 Name: "nomad-tcp", 394 Type: "tcp", 395 Interval: 9000 * time.Hour, // make check block 396 Timeout: 10 * time.Second, 397 InitialStatus: "warning", 398 }, 399 }, 400 }, 401 } 402 require.NoError(ctx.ServiceClient.RegisterAgent("client", agentServices)) 403 require.Eventually(ctx.ServiceClient.hasSeen, time.Second, 10*time.Millisecond) 404 require.Len(ctx.FakeConsul.services["default"], 1, "expected agent service to be registered") 405 require.Len(ctx.FakeConsul.checks["default"], 1, "expected agent check to be registered") 406 407 // prevent normal shutdown by blocking Consul. the shutdown should wait 408 // until agent deregistration has finished 409 waiter := make(chan struct{}) 410 result := make(chan error) 411 go func() { 412 ctx.FakeConsul.mu.Lock() 413 close(waiter) 414 result <- ctx.ServiceClient.Shutdown() 415 }() 416 417 <-waiter // wait for lock to be hit 418 419 // Shutdown should block until all enqueued operations finish. 420 preShutdown := time.Now() 421 select { 422 case <-time.After(200 * time.Millisecond): 423 ctx.FakeConsul.mu.Unlock() 424 require.NoError(<-result) 425 case <-result: 426 t.Fatal("should not have received result until Consul unblocked") 427 } 428 shutdownTime := time.Now().Sub(preShutdown).Seconds() 429 require.Less(shutdownTime, time.Second.Seconds(), 430 "expected shutdown to take >200ms and <1s") 431 require.Greater(shutdownTime, 200*time.Millisecond.Seconds(), 432 "expected shutdown to take >200ms and <1s") 433 require.Len(ctx.FakeConsul.services["default"], 0, 434 "expected agent service to be deregistered") 435 require.Len(ctx.FakeConsul.checks["default"], 0, 436 "expected agent check to be deregistered") 437 } 438 439 // TestConsul_DriverNetwork_AutoUse asserts that if a driver network has 440 // auto-use set then services should advertise it unless explicitly set to 441 // host. Checks should always use host. 442 func TestConsul_DriverNetwork_AutoUse(t *testing.T) { 443 ci.Parallel(t) 444 445 ctx := setupFake(t) 446 447 ctx.Workload.Services = []*structs.Service{ 448 { 449 Name: "auto-advertise-x", 450 PortLabel: "x", 451 AddressMode: structs.AddressModeAuto, 452 Checks: []*structs.ServiceCheck{ 453 { 454 Name: "default-check-x", 455 Type: "tcp", 456 Interval: time.Second, 457 Timeout: time.Second, 458 }, 459 { 460 Name: "weird-y-check", 461 Type: "http", 462 Interval: time.Second, 463 Timeout: time.Second, 464 PortLabel: "y", 465 }, 466 }, 467 }, 468 { 469 Name: "driver-advertise-y", 470 PortLabel: "y", 471 AddressMode: structs.AddressModeDriver, 472 Checks: []*structs.ServiceCheck{ 473 { 474 Name: "default-check-y", 475 Type: "tcp", 476 Interval: time.Second, 477 Timeout: time.Second, 478 }, 479 }, 480 }, 481 { 482 Name: "host-advertise-y", 483 PortLabel: "y", 484 AddressMode: structs.AddressModeHost, 485 }, 486 } 487 488 ctx.Workload.DriverNetwork = &drivers.DriverNetwork{ 489 PortMap: map[string]int{ 490 "x": 8888, 491 "y": 9999, 492 }, 493 IP: "172.18.0.2", 494 AutoAdvertise: true, 495 } 496 497 if err := ctx.ServiceClient.RegisterWorkload(ctx.Workload); err != nil { 498 t.Fatalf("unexpected error registering task: %v", err) 499 } 500 501 if err := ctx.syncOnce(syncNewOps); err != nil { 502 t.Fatalf("unexpected error syncing task: %v", err) 503 } 504 505 if n := len(ctx.FakeConsul.services["default"]); n != 3 { 506 t.Fatalf("expected 2 services but found: %d", n) 507 } 508 509 for _, v := range ctx.FakeConsul.services["default"] { 510 switch v.Name { 511 case ctx.Workload.Services[0].Name: // x 512 // Since DriverNetwork.AutoAdvertise=true, driver ports should be used 513 if v.Port != ctx.Workload.DriverNetwork.PortMap["x"] { 514 t.Errorf("expected service %s's port to be %d but found %d", 515 v.Name, ctx.Workload.DriverNetwork.PortMap["x"], v.Port) 516 } 517 // The order of checks in Consul is not guaranteed to 518 // be the same as their order in the Workload definition, 519 // so check in a loop 520 if expected := 2; len(v.Checks) != expected { 521 t.Errorf("expected %d checks but found %d", expected, len(v.Checks)) 522 } 523 for _, c := range v.Checks { 524 // No name on AgentServiceChecks, use type 525 switch { 526 case c.TCP != "": 527 // Checks should always use host port though 528 if c.TCP != ":1234" { // xPort 529 t.Errorf("expected service %s check 1's port to be %d but found %q", 530 v.Name, xPort, c.TCP) 531 } 532 case c.HTTP != "": 533 if c.HTTP != "http://:1235" { // yPort 534 t.Errorf("expected service %s check 2's port to be %d but found %q", 535 v.Name, yPort, c.HTTP) 536 } 537 default: 538 t.Errorf("unexpected check %#v on service %q", c, v.Name) 539 } 540 } 541 case ctx.Workload.Services[1].Name: // y 542 // Service should be container ip:port 543 if v.Address != ctx.Workload.DriverNetwork.IP { 544 t.Errorf("expected service %s's address to be %s but found %s", 545 v.Name, ctx.Workload.DriverNetwork.IP, v.Address) 546 } 547 if v.Port != ctx.Workload.DriverNetwork.PortMap["y"] { 548 t.Errorf("expected service %s's port to be %d but found %d", 549 v.Name, ctx.Workload.DriverNetwork.PortMap["x"], v.Port) 550 } 551 // Check should be host ip:port 552 if v.Checks[0].TCP != ":1235" { // yPort 553 t.Errorf("expected service %s check's port to be %d but found %s", 554 v.Name, yPort, v.Checks[0].TCP) 555 } 556 case ctx.Workload.Services[2].Name: // y + host mode 557 if v.Port != yPort { 558 t.Errorf("expected service %s's port to be %d but found %d", 559 v.Name, yPort, v.Port) 560 } 561 default: 562 t.Errorf("unexpected service name: %q", v.Name) 563 } 564 } 565 } 566 567 // TestConsul_DriverNetwork_NoAutoUse asserts that if a driver network doesn't 568 // set auto-use only services which request the driver's network should 569 // advertise it. 570 func TestConsul_DriverNetwork_NoAutoUse(t *testing.T) { 571 ci.Parallel(t) 572 573 ctx := setupFake(t) 574 575 ctx.Workload.Services = []*structs.Service{ 576 { 577 Name: "auto-advertise-x", 578 PortLabel: "x", 579 AddressMode: structs.AddressModeAuto, 580 }, 581 { 582 Name: "driver-advertise-y", 583 PortLabel: "y", 584 AddressMode: structs.AddressModeDriver, 585 }, 586 { 587 Name: "host-advertise-y", 588 PortLabel: "y", 589 AddressMode: structs.AddressModeHost, 590 }, 591 } 592 593 ctx.Workload.DriverNetwork = &drivers.DriverNetwork{ 594 PortMap: map[string]int{ 595 "x": 8888, 596 "y": 9999, 597 }, 598 IP: "172.18.0.2", 599 AutoAdvertise: false, 600 } 601 602 if err := ctx.ServiceClient.RegisterWorkload(ctx.Workload); err != nil { 603 t.Fatalf("unexpected error registering task: %v", err) 604 } 605 606 if err := ctx.syncOnce(syncNewOps); err != nil { 607 t.Fatalf("unexpected error syncing task: %v", err) 608 } 609 610 if n := len(ctx.FakeConsul.services["default"]); n != 3 { 611 t.Fatalf("expected 3 services but found: %d", n) 612 } 613 614 for _, v := range ctx.FakeConsul.services["default"] { 615 switch v.Name { 616 case ctx.Workload.Services[0].Name: // x + auto 617 // Since DriverNetwork.AutoAdvertise=false, host ports should be used 618 if v.Port != xPort { 619 t.Errorf("expected service %s's port to be %d but found %d", 620 v.Name, xPort, v.Port) 621 } 622 case ctx.Workload.Services[1].Name: // y + driver mode 623 // Service should be container ip:port 624 if v.Address != ctx.Workload.DriverNetwork.IP { 625 t.Errorf("expected service %s's address to be %s but found %s", 626 v.Name, ctx.Workload.DriverNetwork.IP, v.Address) 627 } 628 if v.Port != ctx.Workload.DriverNetwork.PortMap["y"] { 629 t.Errorf("expected service %s's port to be %d but found %d", 630 v.Name, ctx.Workload.DriverNetwork.PortMap["x"], v.Port) 631 } 632 case ctx.Workload.Services[2].Name: // y + host mode 633 if v.Port != yPort { 634 t.Errorf("expected service %s's port to be %d but found %d", 635 v.Name, yPort, v.Port) 636 } 637 default: 638 t.Errorf("unexpected service name: %q", v.Name) 639 } 640 } 641 } 642 643 // TestConsul_DriverNetwork_Change asserts that if a driver network is 644 // specified and a service updates its use its properly updated in Consul. 645 func TestConsul_DriverNetwork_Change(t *testing.T) { 646 ci.Parallel(t) 647 648 ctx := setupFake(t) 649 650 ctx.Workload.Services = []*structs.Service{ 651 { 652 Name: "service-foo", 653 PortLabel: "x", 654 AddressMode: structs.AddressModeAuto, 655 }, 656 } 657 658 ctx.Workload.DriverNetwork = &drivers.DriverNetwork{ 659 PortMap: map[string]int{ 660 "x": 8888, 661 "y": 9999, 662 }, 663 IP: "172.18.0.2", 664 AutoAdvertise: false, 665 } 666 667 syncAndAssertPort := func(port int) { 668 if err := ctx.syncOnce(syncNewOps); err != nil { 669 t.Fatalf("unexpected error syncing task: %v", err) 670 } 671 672 if n := len(ctx.FakeConsul.services["default"]); n != 1 { 673 t.Fatalf("expected 1 service but found: %d", n) 674 } 675 676 for _, v := range ctx.FakeConsul.services["default"] { 677 switch v.Name { 678 case ctx.Workload.Services[0].Name: 679 if v.Port != port { 680 t.Errorf("expected service %s's port to be %d but found %d", 681 v.Name, port, v.Port) 682 } 683 default: 684 t.Errorf("unexpected service name: %q", v.Name) 685 } 686 } 687 } 688 689 // Initial service should advertise host port x 690 if err := ctx.ServiceClient.RegisterWorkload(ctx.Workload); err != nil { 691 t.Fatalf("unexpected error registering task: %v", err) 692 } 693 694 syncAndAssertPort(xPort) 695 696 // UpdateWorkload to use Host (shouldn't change anything) 697 origWorkload := ctx.Workload.Copy() 698 ctx.Workload.Services[0].AddressMode = structs.AddressModeHost 699 700 if err := ctx.ServiceClient.UpdateWorkload(origWorkload, ctx.Workload); err != nil { 701 t.Fatalf("unexpected error updating task: %v", err) 702 } 703 704 syncAndAssertPort(xPort) 705 706 // UpdateWorkload to use Driver (*should* change IP and port) 707 origWorkload = ctx.Workload.Copy() 708 ctx.Workload.Services[0].AddressMode = structs.AddressModeDriver 709 710 if err := ctx.ServiceClient.UpdateWorkload(origWorkload, ctx.Workload); err != nil { 711 t.Fatalf("unexpected error updating task: %v", err) 712 } 713 714 syncAndAssertPort(ctx.Workload.DriverNetwork.PortMap["x"]) 715 } 716 717 // TestConsul_CanaryTags asserts CanaryTags are used when Canary=true 718 func TestConsul_CanaryTags(t *testing.T) { 719 ci.Parallel(t) 720 721 require := require.New(t) 722 ctx := setupFake(t) 723 724 canaryTags := []string{"tag1", "canary"} 725 ctx.Workload.Canary = true 726 ctx.Workload.Services[0].CanaryTags = canaryTags 727 728 require.NoError(ctx.ServiceClient.RegisterWorkload(ctx.Workload)) 729 require.NoError(ctx.syncOnce(syncNewOps)) 730 require.Len(ctx.FakeConsul.services["default"], 1) 731 for _, service := range ctx.FakeConsul.services["default"] { 732 require.Equal(canaryTags, service.Tags) 733 } 734 735 // Disable canary and assert tags are not the canary tags 736 origWorkload := ctx.Workload.Copy() 737 ctx.Workload.Canary = false 738 require.NoError(ctx.ServiceClient.UpdateWorkload(origWorkload, ctx.Workload)) 739 require.NoError(ctx.syncOnce(syncNewOps)) 740 require.Len(ctx.FakeConsul.services["default"], 1) 741 for _, service := range ctx.FakeConsul.services["default"] { 742 require.NotEqual(canaryTags, service.Tags) 743 } 744 745 ctx.ServiceClient.RemoveWorkload(ctx.Workload) 746 require.NoError(ctx.syncOnce(syncNewOps)) 747 require.Len(ctx.FakeConsul.services["default"], 0) 748 } 749 750 // TestConsul_CanaryTags_NoTags asserts Tags are used when Canary=true and there 751 // are no specified canary tags 752 func TestConsul_CanaryTags_NoTags(t *testing.T) { 753 ci.Parallel(t) 754 755 require := require.New(t) 756 ctx := setupFake(t) 757 758 tags := []string{"tag1", "foo"} 759 ctx.Workload.Canary = true 760 ctx.Workload.Services[0].Tags = tags 761 762 require.NoError(ctx.ServiceClient.RegisterWorkload(ctx.Workload)) 763 require.NoError(ctx.syncOnce(syncNewOps)) 764 require.Len(ctx.FakeConsul.services["default"], 1) 765 for _, service := range ctx.FakeConsul.services["default"] { 766 require.Equal(tags, service.Tags) 767 } 768 769 // Disable canary and assert tags dont change 770 origWorkload := ctx.Workload.Copy() 771 ctx.Workload.Canary = false 772 require.NoError(ctx.ServiceClient.UpdateWorkload(origWorkload, ctx.Workload)) 773 require.NoError(ctx.syncOnce(syncNewOps)) 774 require.Len(ctx.FakeConsul.services["default"], 1) 775 for _, service := range ctx.FakeConsul.services["default"] { 776 require.Equal(tags, service.Tags) 777 } 778 779 ctx.ServiceClient.RemoveWorkload(ctx.Workload) 780 require.NoError(ctx.syncOnce(syncNewOps)) 781 require.Len(ctx.FakeConsul.services["default"], 0) 782 } 783 784 // TestConsul_CanaryMeta asserts CanaryMeta are used when Canary=true 785 func TestConsul_CanaryMeta(t *testing.T) { 786 ci.Parallel(t) 787 788 require := require.New(t) 789 ctx := setupFake(t) 790 791 canaryMeta := map[string]string{"meta1": "canary"} 792 canaryMeta["external-source"] = "nomad" 793 ctx.Workload.Canary = true 794 ctx.Workload.Services[0].CanaryMeta = canaryMeta 795 796 require.NoError(ctx.ServiceClient.RegisterWorkload(ctx.Workload)) 797 require.NoError(ctx.syncOnce(syncNewOps)) 798 require.Len(ctx.FakeConsul.services["default"], 1) 799 for _, service := range ctx.FakeConsul.services["default"] { 800 require.Equal(canaryMeta, service.Meta) 801 } 802 803 // Disable canary and assert meta are not the canary meta 804 origWorkload := ctx.Workload.Copy() 805 ctx.Workload.Canary = false 806 require.NoError(ctx.ServiceClient.UpdateWorkload(origWorkload, ctx.Workload)) 807 require.NoError(ctx.syncOnce(syncNewOps)) 808 require.Len(ctx.FakeConsul.services["default"], 1) 809 for _, service := range ctx.FakeConsul.services["default"] { 810 require.NotEqual(canaryMeta, service.Meta) 811 } 812 813 ctx.ServiceClient.RemoveWorkload(ctx.Workload) 814 require.NoError(ctx.syncOnce(syncNewOps)) 815 require.Len(ctx.FakeConsul.services["default"], 0) 816 } 817 818 // TestConsul_CanaryMeta_NoMeta asserts Meta are used when Canary=true and there 819 // are no specified canary meta 820 func TestConsul_CanaryMeta_NoMeta(t *testing.T) { 821 ci.Parallel(t) 822 823 require := require.New(t) 824 ctx := setupFake(t) 825 826 meta := map[string]string{"meta1": "foo"} 827 meta["external-source"] = "nomad" 828 ctx.Workload.Canary = true 829 ctx.Workload.Services[0].Meta = meta 830 831 require.NoError(ctx.ServiceClient.RegisterWorkload(ctx.Workload)) 832 require.NoError(ctx.syncOnce(syncNewOps)) 833 require.Len(ctx.FakeConsul.services["default"], 1) 834 for _, service := range ctx.FakeConsul.services["default"] { 835 require.Equal(meta, service.Meta) 836 } 837 838 // Disable canary and assert meta dont change 839 origWorkload := ctx.Workload.Copy() 840 ctx.Workload.Canary = false 841 require.NoError(ctx.ServiceClient.UpdateWorkload(origWorkload, ctx.Workload)) 842 require.NoError(ctx.syncOnce(syncNewOps)) 843 require.Len(ctx.FakeConsul.services["default"], 1) 844 for _, service := range ctx.FakeConsul.services["default"] { 845 require.Equal(meta, service.Meta) 846 } 847 848 ctx.ServiceClient.RemoveWorkload(ctx.Workload) 849 require.NoError(ctx.syncOnce(syncNewOps)) 850 require.Len(ctx.FakeConsul.services["default"], 0) 851 } 852 853 // TestConsul_PeriodicSync asserts that Nomad periodically reconciles with 854 // Consul. 855 func TestConsul_PeriodicSync(t *testing.T) { 856 ci.Parallel(t) 857 858 ctx := setupFake(t) 859 defer ctx.ServiceClient.Shutdown() 860 861 // Lower periodic sync interval to speed up test 862 ctx.ServiceClient.periodicInterval = 1 * time.Millisecond 863 864 // Run for 20ms and assert hits >= 5 because each sync() calls multiple 865 // Consul APIs 866 go ctx.ServiceClient.Run() 867 868 select { 869 case <-ctx.ServiceClient.exitCh: 870 t.Fatalf("exited unexpectedly") 871 case <-time.After(20 * time.Millisecond): 872 } 873 874 minHits := 5 875 if hits := ctx.FakeConsul.getHits(); hits < minHits { 876 t.Fatalf("expected at least %d hits but found %d", minHits, hits) 877 } 878 } 879 880 // TestIsNomadService asserts the isNomadService helper returns true for Nomad 881 // task IDs and false for unknown IDs and Nomad agent IDs (see #2827). 882 func TestIsNomadService(t *testing.T) { 883 ci.Parallel(t) 884 885 tests := []struct { 886 id string 887 result bool 888 }{ 889 {"_nomad-client-nomad-client-http", false}, 890 {"_nomad-server-nomad-serf", false}, 891 892 // Pre-0.7.1 style IDs still match 893 {"_nomad-executor-abc", true}, 894 {"_nomad-executor", true}, 895 896 // Post-0.7.1 style IDs match 897 {"_nomad-task-FBBK265QN4TMT25ND4EP42TJVMYJ3HR4", true}, 898 899 {"not-nomad", false}, 900 {"_nomad", false}, 901 } 902 903 for _, test := range tests { 904 t.Run(test.id, func(t *testing.T) { 905 actual := isNomadService(test.id) 906 if actual != test.result { 907 t.Errorf("%q should be %t but found %t", test.id, test.result, actual) 908 } 909 }) 910 } 911 } 912 913 // TestCreateCheckReg_HTTP asserts Nomad ServiceCheck structs are properly 914 // converted to Consul API AgentCheckRegistrations for HTTP checks. 915 func TestCreateCheckReg_HTTP(t *testing.T) { 916 ci.Parallel(t) 917 918 check := &structs.ServiceCheck{ 919 Name: "name", 920 Type: "http", 921 Path: "/path", 922 PortLabel: "label", 923 Method: "POST", 924 Header: map[string][]string{ 925 "Foo": {"bar"}, 926 }, 927 } 928 929 serviceID := "testService" 930 checkID := check.Hash(serviceID) 931 host := "localhost" 932 port := 41111 933 namespace := "" 934 935 expected := &api.AgentCheckRegistration{ 936 Namespace: namespace, 937 ID: checkID, 938 Name: "name", 939 ServiceID: serviceID, 940 AgentServiceCheck: api.AgentServiceCheck{ 941 Timeout: "0s", 942 Interval: "0s", 943 HTTP: fmt.Sprintf("http://%s:%d/path", host, port), 944 Method: "POST", 945 Header: map[string][]string{ 946 "Foo": {"bar"}, 947 }, 948 }, 949 } 950 951 actual, err := createCheckReg(serviceID, checkID, check, host, port, namespace) 952 if err != nil { 953 t.Fatalf("err: %v", err) 954 } 955 956 if diff := pretty.Diff(actual, expected); len(diff) > 0 { 957 t.Fatalf("diff:\n%s\n", strings.Join(diff, "\n")) 958 } 959 } 960 961 // TestCreateCheckReg_GRPC asserts Nomad ServiceCheck structs are properly 962 // converted to Consul API AgentCheckRegistrations for GRPC checks. 963 func TestCreateCheckReg_GRPC(t *testing.T) { 964 ci.Parallel(t) 965 966 check := &structs.ServiceCheck{ 967 Name: "name", 968 Type: "grpc", 969 PortLabel: "label", 970 GRPCService: "foo.Bar", 971 GRPCUseTLS: true, 972 TLSSkipVerify: true, 973 Timeout: time.Second, 974 Interval: time.Minute, 975 } 976 977 serviceID := "testService" 978 checkID := check.Hash(serviceID) 979 980 expected := &api.AgentCheckRegistration{ 981 Namespace: "", 982 ID: checkID, 983 Name: check.Name, 984 ServiceID: serviceID, 985 AgentServiceCheck: api.AgentServiceCheck{ 986 Timeout: "1s", 987 Interval: "1m0s", 988 GRPC: "localhost:8080/foo.Bar", 989 GRPCUseTLS: true, 990 TLSSkipVerify: true, 991 }, 992 } 993 994 actual, err := createCheckReg(serviceID, checkID, check, "localhost", 8080, "default") 995 must.NoError(t, err) 996 must.Eq(t, expected, actual) 997 } 998 999 func TestConsul_ServiceName_Duplicates(t *testing.T) { 1000 ci.Parallel(t) 1001 ctx := setupFake(t) 1002 1003 ctx.Workload.Services = []*structs.Service{ 1004 { 1005 Name: "best-service", 1006 PortLabel: "x", 1007 Tags: []string{"foo"}, 1008 Checks: []*structs.ServiceCheck{ 1009 { 1010 Name: "check-a", 1011 Type: "tcp", 1012 Interval: time.Second, 1013 Timeout: time.Second, 1014 }, 1015 }, 1016 }, 1017 { 1018 Name: "best-service", 1019 PortLabel: "y", 1020 Tags: []string{"bar"}, 1021 Checks: []*structs.ServiceCheck{ 1022 { 1023 Name: "check-b", 1024 Type: "tcp", 1025 Interval: time.Second, 1026 Timeout: time.Second, 1027 }, 1028 }, 1029 }, 1030 { 1031 Name: "worst-service", 1032 PortLabel: "y", 1033 }, 1034 } 1035 1036 must.NoError(t, ctx.ServiceClient.RegisterWorkload(ctx.Workload)) 1037 must.NoError(t, ctx.syncOnce(syncNewOps)) 1038 must.MapLen(t, 3, ctx.FakeConsul.services["default"]) 1039 1040 for _, s := range ctx.FakeConsul.services["default"] { 1041 switch { 1042 case s.Name == "best-service" && s.Port == xPort: 1043 must.SliceContainsAll(t, s.Tags, ctx.Workload.Services[0].Tags) 1044 must.SliceLen(t, 1, s.Checks) 1045 case s.Name == "best-service" && s.Port == yPort: 1046 must.SliceContainsAll(t, s.Tags, ctx.Workload.Services[1].Tags) 1047 must.SliceLen(t, 1, s.Checks) 1048 case s.Name == "worst-service": 1049 must.SliceEmpty(t, s.Checks) 1050 } 1051 } 1052 } 1053 1054 // TestConsul_ServiceDeregistration_OutOfProbation asserts that during in steady 1055 // state we remove any services we don't reconize locally 1056 func TestConsul_ServiceDeregistration_OutProbation(t *testing.T) { 1057 ci.Parallel(t) 1058 1059 ctx := setupFake(t) 1060 require := require.New(t) 1061 1062 ctx.ServiceClient.deregisterProbationExpiry = time.Now().Add(-1 * time.Hour) 1063 1064 remainingWorkload := testWorkload() 1065 remainingWorkload.Services = []*structs.Service{ 1066 { 1067 Name: "remaining-service", 1068 PortLabel: "x", 1069 Checks: []*structs.ServiceCheck{ 1070 { 1071 Name: "check", 1072 Type: "tcp", 1073 Interval: time.Second, 1074 Timeout: time.Second, 1075 }, 1076 }, 1077 }, 1078 } 1079 remainingWorkloadServiceID := serviceregistration.MakeAllocServiceID(remainingWorkload.AllocInfo.AllocID, 1080 remainingWorkload.Name(), remainingWorkload.Services[0]) 1081 1082 require.NoError(ctx.ServiceClient.RegisterWorkload(remainingWorkload)) 1083 require.NoError(ctx.syncOnce(syncNewOps)) 1084 require.Len(ctx.FakeConsul.services, 1) 1085 require.Len(ctx.FakeConsul.checks, 1) 1086 1087 explicitlyRemovedWorkload := testWorkload() 1088 explicitlyRemovedWorkload.Services = []*structs.Service{ 1089 { 1090 Name: "explicitly-removed-service", 1091 PortLabel: "y", 1092 Checks: []*structs.ServiceCheck{ 1093 { 1094 Name: "check", 1095 Type: "tcp", 1096 Interval: time.Second, 1097 Timeout: time.Second, 1098 }, 1099 }, 1100 }, 1101 } 1102 explicitlyRemovedWorkloadServiceID := serviceregistration.MakeAllocServiceID(explicitlyRemovedWorkload.AllocInfo.AllocID, 1103 explicitlyRemovedWorkload.Name(), explicitlyRemovedWorkload.Services[0]) 1104 1105 require.NoError(ctx.ServiceClient.RegisterWorkload(explicitlyRemovedWorkload)) 1106 1107 require.NoError(ctx.syncOnce(syncNewOps)) 1108 require.Len(ctx.FakeConsul.services["default"], 2) 1109 require.Len(ctx.FakeConsul.checks["default"], 2) 1110 1111 // we register a task through nomad API then remove it out of band 1112 outofbandWorkload := testWorkload() 1113 outofbandWorkload.Services = []*structs.Service{ 1114 { 1115 Name: "unknown-service", 1116 PortLabel: "x", 1117 Checks: []*structs.ServiceCheck{ 1118 { 1119 Name: "check", 1120 Type: "tcp", 1121 Interval: time.Second, 1122 Timeout: time.Second, 1123 }, 1124 }, 1125 }, 1126 } 1127 outofbandWorkloadServiceID := serviceregistration.MakeAllocServiceID(outofbandWorkload.AllocInfo.AllocID, 1128 outofbandWorkload.Name(), outofbandWorkload.Services[0]) 1129 1130 require.NoError(ctx.ServiceClient.RegisterWorkload(outofbandWorkload)) 1131 require.NoError(ctx.syncOnce(syncNewOps)) 1132 1133 require.Len(ctx.FakeConsul.services["default"], 3) 1134 1135 // remove outofbandWorkload from local services so it appears unknown to client 1136 require.Len(ctx.ServiceClient.services, 3) 1137 require.Len(ctx.ServiceClient.checks, 3) 1138 1139 delete(ctx.ServiceClient.services, outofbandWorkloadServiceID) 1140 delete(ctx.ServiceClient.checks, MakeCheckID(outofbandWorkloadServiceID, outofbandWorkload.Services[0].Checks[0])) 1141 1142 require.Len(ctx.ServiceClient.services, 2) 1143 require.Len(ctx.ServiceClient.checks, 2) 1144 1145 // Sync and ensure that explicitly removed service as well as outofbandWorkload were removed 1146 1147 ctx.ServiceClient.RemoveWorkload(explicitlyRemovedWorkload) 1148 require.NoError(ctx.syncOnce(syncNewOps)) 1149 require.NoError(ctx.ServiceClient.sync(syncNewOps)) 1150 require.Len(ctx.FakeConsul.services["default"], 1) 1151 require.Len(ctx.FakeConsul.checks["default"], 1) 1152 1153 require.Contains(ctx.FakeConsul.services["default"], remainingWorkloadServiceID) 1154 require.NotContains(ctx.FakeConsul.services["default"], outofbandWorkloadServiceID) 1155 require.NotContains(ctx.FakeConsul.services["default"], explicitlyRemovedWorkloadServiceID) 1156 1157 require.Contains(ctx.FakeConsul.checks["default"], MakeCheckID(remainingWorkloadServiceID, remainingWorkload.Services[0].Checks[0])) 1158 require.NotContains(ctx.FakeConsul.checks["default"], MakeCheckID(outofbandWorkloadServiceID, outofbandWorkload.Services[0].Checks[0])) 1159 require.NotContains(ctx.FakeConsul.checks["default"], MakeCheckID(explicitlyRemovedWorkloadServiceID, explicitlyRemovedWorkload.Services[0].Checks[0])) 1160 } 1161 1162 // TestConsul_ServiceDeregistration_InProbation asserts that during initialization 1163 // we only deregister services that were explicitly removed and leave unknown 1164 // services untouched. This adds a grace period for restoring recovered tasks 1165 // before deregistering them 1166 func TestConsul_ServiceDeregistration_InProbation(t *testing.T) { 1167 ci.Parallel(t) 1168 1169 ctx := setupFake(t) 1170 require := require.New(t) 1171 1172 ctx.ServiceClient.deregisterProbationExpiry = time.Now().Add(1 * time.Hour) 1173 1174 remainingWorkload := testWorkload() 1175 remainingWorkload.Services = []*structs.Service{ 1176 { 1177 Name: "remaining-service", 1178 PortLabel: "x", 1179 Checks: []*structs.ServiceCheck{ 1180 { 1181 Name: "check", 1182 Type: "tcp", 1183 Interval: time.Second, 1184 Timeout: time.Second, 1185 }, 1186 }, 1187 }, 1188 } 1189 remainingWorkloadServiceID := serviceregistration.MakeAllocServiceID(remainingWorkload.AllocInfo.AllocID, 1190 remainingWorkload.Name(), remainingWorkload.Services[0]) 1191 1192 require.NoError(ctx.ServiceClient.RegisterWorkload(remainingWorkload)) 1193 require.NoError(ctx.syncOnce(syncNewOps)) 1194 require.Len(ctx.FakeConsul.services, 1) 1195 require.Len(ctx.FakeConsul.checks, 1) 1196 1197 explicitlyRemovedWorkload := testWorkload() 1198 explicitlyRemovedWorkload.Services = []*structs.Service{ 1199 { 1200 Name: "explicitly-removed-service", 1201 PortLabel: "y", 1202 Checks: []*structs.ServiceCheck{ 1203 { 1204 Name: "check", 1205 Type: "tcp", 1206 Interval: time.Second, 1207 Timeout: time.Second, 1208 }, 1209 }, 1210 }, 1211 } 1212 explicitlyRemovedWorkloadServiceID := serviceregistration.MakeAllocServiceID(explicitlyRemovedWorkload.AllocInfo.AllocID, 1213 explicitlyRemovedWorkload.Name(), explicitlyRemovedWorkload.Services[0]) 1214 1215 require.NoError(ctx.ServiceClient.RegisterWorkload(explicitlyRemovedWorkload)) 1216 1217 require.NoError(ctx.syncOnce(syncNewOps)) 1218 require.Len(ctx.FakeConsul.services["default"], 2) 1219 require.Len(ctx.FakeConsul.checks["default"], 2) 1220 1221 // we register a task through nomad API then remove it out of band 1222 outofbandWorkload := testWorkload() 1223 outofbandWorkload.Services = []*structs.Service{ 1224 { 1225 Name: "unknown-service", 1226 PortLabel: "x", 1227 Checks: []*structs.ServiceCheck{ 1228 { 1229 Name: "check", 1230 Type: "tcp", 1231 Interval: time.Second, 1232 Timeout: time.Second, 1233 }, 1234 }, 1235 }, 1236 } 1237 outofbandWorkloadServiceID := serviceregistration.MakeAllocServiceID(outofbandWorkload.AllocInfo.AllocID, 1238 outofbandWorkload.Name(), outofbandWorkload.Services[0]) 1239 1240 require.NoError(ctx.ServiceClient.RegisterWorkload(outofbandWorkload)) 1241 require.NoError(ctx.syncOnce(syncNewOps)) 1242 1243 require.Len(ctx.FakeConsul.services["default"], 3) 1244 1245 // remove outofbandWorkload from local services so it appears unknown to client 1246 require.Len(ctx.ServiceClient.services, 3) 1247 require.Len(ctx.ServiceClient.checks, 3) 1248 1249 delete(ctx.ServiceClient.services, outofbandWorkloadServiceID) 1250 delete(ctx.ServiceClient.checks, MakeCheckID(outofbandWorkloadServiceID, outofbandWorkload.Services[0].Checks[0])) 1251 1252 require.Len(ctx.ServiceClient.services, 2) 1253 require.Len(ctx.ServiceClient.checks, 2) 1254 1255 // Sync and ensure that explicitly removed service was removed, but outofbandWorkload remains 1256 1257 ctx.ServiceClient.RemoveWorkload(explicitlyRemovedWorkload) 1258 require.NoError(ctx.syncOnce(syncNewOps)) 1259 require.NoError(ctx.ServiceClient.sync(syncNewOps)) 1260 require.Len(ctx.FakeConsul.services["default"], 2) 1261 require.Len(ctx.FakeConsul.checks["default"], 2) 1262 1263 require.Contains(ctx.FakeConsul.services["default"], remainingWorkloadServiceID) 1264 require.Contains(ctx.FakeConsul.services["default"], outofbandWorkloadServiceID) 1265 require.NotContains(ctx.FakeConsul.services["default"], explicitlyRemovedWorkloadServiceID) 1266 1267 require.Contains(ctx.FakeConsul.checks["default"], MakeCheckID(remainingWorkloadServiceID, remainingWorkload.Services[0].Checks[0])) 1268 require.Contains(ctx.FakeConsul.checks["default"], MakeCheckID(outofbandWorkloadServiceID, outofbandWorkload.Services[0].Checks[0])) 1269 require.NotContains(ctx.FakeConsul.checks["default"], MakeCheckID(explicitlyRemovedWorkloadServiceID, explicitlyRemovedWorkload.Services[0].Checks[0])) 1270 1271 // after probation, outofband services and checks are removed 1272 ctx.ServiceClient.deregisterProbationExpiry = time.Now().Add(-1 * time.Hour) 1273 1274 require.NoError(ctx.ServiceClient.sync(syncNewOps)) 1275 require.Len(ctx.FakeConsul.services["default"], 1) 1276 require.Len(ctx.FakeConsul.checks["default"], 1) 1277 1278 require.Contains(ctx.FakeConsul.services["default"], remainingWorkloadServiceID) 1279 require.NotContains(ctx.FakeConsul.services["default"], outofbandWorkloadServiceID) 1280 require.NotContains(ctx.FakeConsul.services["default"], explicitlyRemovedWorkloadServiceID) 1281 1282 require.Contains(ctx.FakeConsul.checks["default"], MakeCheckID(remainingWorkloadServiceID, remainingWorkload.Services[0].Checks[0])) 1283 require.NotContains(ctx.FakeConsul.checks["default"], MakeCheckID(outofbandWorkloadServiceID, outofbandWorkload.Services[0].Checks[0])) 1284 require.NotContains(ctx.FakeConsul.checks["default"], MakeCheckID(explicitlyRemovedWorkloadServiceID, explicitlyRemovedWorkload.Services[0].Checks[0])) 1285 }