github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/command/agent/consul/unit_test.go (about) 1 package consul 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "reflect" 8 "strings" 9 "sync/atomic" 10 "testing" 11 "time" 12 13 "github.com/hashicorp/consul/api" 14 "github.com/hashicorp/nomad/helper/testlog" 15 "github.com/hashicorp/nomad/helper/uuid" 16 "github.com/hashicorp/nomad/nomad/structs" 17 "github.com/hashicorp/nomad/plugins/drivers" 18 "github.com/kr/pretty" 19 "github.com/stretchr/testify/assert" 20 "github.com/stretchr/testify/require" 21 ) 22 23 const ( 24 // Ports used in testWorkload 25 xPort = 1234 26 yPort = 1235 27 ) 28 29 func testWorkload() *WorkloadServices { 30 return &WorkloadServices{ 31 AllocID: uuid.Generate(), 32 Task: "taskname", 33 Restarter: &restartRecorder{}, 34 Services: []*structs.Service{ 35 { 36 Name: "taskname-service", 37 PortLabel: "x", 38 Tags: []string{"tag1", "tag2"}, 39 Meta: map[string]string{"meta1": "foo"}, 40 }, 41 }, 42 Networks: []*structs.NetworkResource{ 43 { 44 DynamicPorts: []structs.Port{ 45 {Label: "x", Value: xPort}, 46 {Label: "y", Value: yPort}, 47 }, 48 }, 49 }, 50 } 51 } 52 53 // restartRecorder is a minimal WorkloadRestarter implementation that simply 54 // counts how many restarts were triggered. 55 type restartRecorder struct { 56 restarts int64 57 } 58 59 func (r *restartRecorder) Restart(ctx context.Context, event *structs.TaskEvent, failure bool) error { 60 atomic.AddInt64(&r.restarts, 1) 61 return nil 62 } 63 64 // testFakeCtx contains a fake Consul AgentAPI 65 type testFakeCtx struct { 66 ServiceClient *ServiceClient 67 FakeConsul *MockAgent 68 Workload *WorkloadServices 69 } 70 71 var errNoOps = fmt.Errorf("testing error: no pending operations") 72 73 // syncOps simulates one iteration of the ServiceClient.Run loop and returns 74 // any errors returned by sync() or errNoOps if no pending operations. 75 func (t *testFakeCtx) syncOnce(reason syncReason) error { 76 switch reason { 77 78 case syncPeriodic: 79 err := t.ServiceClient.sync(syncPeriodic) 80 if err == nil { 81 t.ServiceClient.clearExplicitlyDeregistered() 82 } 83 return err 84 85 case syncNewOps: 86 select { 87 case ops := <-t.ServiceClient.opCh: 88 t.ServiceClient.merge(ops) 89 err := t.ServiceClient.sync(syncNewOps) 90 if err == nil { 91 t.ServiceClient.clearExplicitlyDeregistered() 92 } 93 return err 94 default: 95 return errNoOps 96 } 97 98 case syncShutdown: 99 return errors.New("no test for sync due to shutdown") 100 } 101 102 return errors.New("bad sync reason") 103 } 104 105 // setupFake creates a testFakeCtx with a ServiceClient backed by a fakeConsul. 106 // A test Workload is also provided. 107 func setupFake(t *testing.T) *testFakeCtx { 108 fc := NewMockAgent() 109 tw := testWorkload() 110 111 // by default start fake client being out of probation 112 sc := NewServiceClient(fc, testlog.HCLogger(t), true) 113 sc.deregisterProbationExpiry = time.Now().Add(-1 * time.Minute) 114 115 return &testFakeCtx{ 116 ServiceClient: sc, 117 FakeConsul: fc, 118 Workload: tw, 119 } 120 } 121 122 func TestConsul_ChangeTags(t *testing.T) { 123 t.Parallel() 124 ctx := setupFake(t) 125 r := require.New(t) 126 127 r.NoError(ctx.ServiceClient.RegisterWorkload(ctx.Workload)) 128 r.NoError(ctx.syncOnce(syncNewOps)) 129 r.Equal(1, len(ctx.FakeConsul.services), "Expected 1 service to be registered with Consul") 130 131 // Validate the alloc registration 132 reg1, err := ctx.ServiceClient.AllocRegistrations(ctx.Workload.AllocID) 133 r.NoError(err) 134 r.NotNil(reg1, "Unexpected nil alloc registration") 135 r.Equal(1, reg1.NumServices()) 136 r.Equal(0, reg1.NumChecks()) 137 138 serviceBefore := ctx.FakeConsul.lookupService("taskname-service")[0] 139 r.Equal(serviceBefore.Name, ctx.Workload.Services[0].Name) 140 r.Equal(serviceBefore.Tags, ctx.Workload.Services[0].Tags) 141 142 // Update the task definition 143 origWorkload := ctx.Workload.Copy() 144 ctx.Workload.Services[0].Tags[0] = "new-tag" 145 146 // Register and sync the update 147 r.NoError(ctx.ServiceClient.UpdateWorkload(origWorkload, ctx.Workload)) 148 r.NoError(ctx.syncOnce(syncNewOps)) 149 r.Equal(1, len(ctx.FakeConsul.services), "Expected 1 service to be registered with Consul") 150 151 // Validate the consul service definition changed 152 serviceAfter := ctx.FakeConsul.lookupService("taskname-service")[0] 153 r.Equal(serviceAfter.Name, ctx.Workload.Services[0].Name) 154 r.Equal(serviceAfter.Tags, ctx.Workload.Services[0].Tags) 155 r.Equal("new-tag", serviceAfter.Tags[0]) 156 } 157 158 func TestConsul_EnableTagOverride_Syncs(t *testing.T) { 159 t.Parallel() 160 ctx := setupFake(t) 161 r := require.New(t) 162 163 // Configure our test service to set EnableTagOverride = true 164 ctx.Workload.Services[0].EnableTagOverride = true 165 166 r.NoError(ctx.ServiceClient.RegisterWorkload(ctx.Workload)) 167 r.NoError(ctx.syncOnce(syncNewOps)) 168 r.Equal(1, len(ctx.FakeConsul.services)) 169 170 // Validate the alloc registration 171 reg1, err := ctx.ServiceClient.AllocRegistrations(ctx.Workload.AllocID) 172 r.NoError(err) 173 r.NotNil(reg1) 174 r.Equal(1, reg1.NumServices()) 175 r.Equal(0, reg1.NumChecks()) 176 177 const service = "taskname-service" 178 179 // sanity check things are what we expect 180 consulServiceDefBefore := ctx.FakeConsul.lookupService(service)[0] 181 r.Equal(ctx.Workload.Services[0].Name, consulServiceDefBefore.Name) 182 r.Equal([]string{"tag1", "tag2"}, consulServiceDefBefore.Tags) 183 r.True(consulServiceDefBefore.EnableTagOverride) 184 185 // manually set the tags in consul 186 ctx.FakeConsul.lookupService(service)[0].Tags = []string{"new", "tags"} 187 188 // do a periodic sync (which will respect EnableTagOverride) 189 r.NoError(ctx.syncOnce(syncPeriodic)) 190 r.Equal(1, len(ctx.FakeConsul.services)) 191 consulServiceDefAfter := ctx.FakeConsul.lookupService(service)[0] 192 r.Equal([]string{"new", "tags"}, consulServiceDefAfter.Tags) // manually set tags should still be there 193 194 // now do a new-ops sync (which will override EnableTagOverride) 195 r.NoError(ctx.ServiceClient.RegisterWorkload(ctx.Workload)) 196 r.NoError(ctx.syncOnce(syncNewOps)) 197 r.Equal(1, len(ctx.FakeConsul.services)) 198 consulServiceDefUpdated := ctx.FakeConsul.lookupService(service)[0] 199 r.Equal([]string{"tag1", "tag2"}, consulServiceDefUpdated.Tags) // jobspec tags should be set now 200 } 201 202 // TestConsul_ChangePorts asserts that changing the ports on a service updates 203 // it in Consul. Pre-0.7.1 ports were not part of the service ID and this was a 204 // slightly different code path than changing tags. 205 func TestConsul_ChangePorts(t *testing.T) { 206 ctx := setupFake(t) 207 require := require.New(t) 208 209 ctx.Workload.Services[0].Checks = []*structs.ServiceCheck{ 210 { 211 Name: "c1", 212 Type: "tcp", 213 Interval: time.Second, 214 Timeout: time.Second, 215 PortLabel: "x", 216 }, 217 { 218 Name: "c2", 219 Type: "script", 220 Interval: 9000 * time.Hour, 221 Timeout: time.Second, 222 }, 223 { 224 Name: "c3", 225 Type: "http", 226 Protocol: "http", 227 Path: "/", 228 Interval: time.Second, 229 Timeout: time.Second, 230 PortLabel: "y", 231 }, 232 } 233 234 require.NoError(ctx.ServiceClient.RegisterWorkload(ctx.Workload)) 235 require.NoError(ctx.syncOnce(syncNewOps)) 236 require.Equal(1, len(ctx.FakeConsul.services), "Expected 1 service to be registered with Consul") 237 238 for _, v := range ctx.FakeConsul.services { 239 require.Equal(ctx.Workload.Services[0].Name, v.Name) 240 require.Equal(ctx.Workload.Services[0].Tags, v.Tags) 241 require.Equal(xPort, v.Port) 242 } 243 244 require.Len(ctx.FakeConsul.checks, 3) 245 246 origTCPKey := "" 247 origScriptKey := "" 248 origHTTPKey := "" 249 for k, v := range ctx.FakeConsul.checks { 250 switch v.Name { 251 case "c1": 252 origTCPKey = k 253 require.Equal(fmt.Sprintf(":%d", xPort), v.TCP) 254 case "c2": 255 origScriptKey = k 256 case "c3": 257 origHTTPKey = k 258 require.Equal(fmt.Sprintf("http://:%d/", yPort), v.HTTP) 259 default: 260 t.Fatalf("unexpected check: %q", v.Name) 261 } 262 } 263 264 require.NotEmpty(origTCPKey) 265 require.NotEmpty(origScriptKey) 266 require.NotEmpty(origHTTPKey) 267 268 // Now update the PortLabel on the Service and Check c3 269 origWorkload := ctx.Workload.Copy() 270 ctx.Workload.Services[0].PortLabel = "y" 271 ctx.Workload.Services[0].Checks = []*structs.ServiceCheck{ 272 { 273 Name: "c1", 274 Type: "tcp", 275 Interval: time.Second, 276 Timeout: time.Second, 277 PortLabel: "x", 278 }, 279 { 280 Name: "c2", 281 Type: "script", 282 Interval: 9000 * time.Hour, 283 Timeout: time.Second, 284 }, 285 { 286 Name: "c3", 287 Type: "http", 288 Protocol: "http", 289 Path: "/", 290 Interval: time.Second, 291 Timeout: time.Second, 292 // Removed PortLabel; should default to service's (y) 293 }, 294 } 295 296 require.NoError(ctx.ServiceClient.UpdateWorkload(origWorkload, ctx.Workload)) 297 require.NoError(ctx.syncOnce(syncNewOps)) 298 require.Equal(1, len(ctx.FakeConsul.services), "Expected 1 service to be registered with Consul") 299 300 for _, v := range ctx.FakeConsul.services { 301 require.Equal(ctx.Workload.Services[0].Name, v.Name) 302 require.Equal(ctx.Workload.Services[0].Tags, v.Tags) 303 require.Equal(yPort, v.Port) 304 } 305 306 require.Equal(3, len(ctx.FakeConsul.checks)) 307 308 for k, v := range ctx.FakeConsul.checks { 309 switch v.Name { 310 case "c1": 311 // C1 is changed because the service was re-registered 312 require.NotEqual(origTCPKey, k) 313 require.Equal(fmt.Sprintf(":%d", xPort), v.TCP) 314 case "c2": 315 // C2 is changed because the service was re-registered 316 require.NotEqual(origScriptKey, k) 317 case "c3": 318 require.NotEqual(origHTTPKey, k) 319 require.Equal(fmt.Sprintf("http://:%d/", yPort), v.HTTP) 320 default: 321 t.Errorf("Unknown check: %q", k) 322 } 323 } 324 } 325 326 // TestConsul_ChangeChecks asserts that updating only the checks on a service 327 // properly syncs with Consul. 328 func TestConsul_ChangeChecks(t *testing.T) { 329 ctx := setupFake(t) 330 ctx.Workload.Services[0].Checks = []*structs.ServiceCheck{ 331 { 332 Name: "c1", 333 Type: "tcp", 334 Interval: time.Second, 335 Timeout: time.Second, 336 PortLabel: "x", 337 CheckRestart: &structs.CheckRestart{ 338 Limit: 3, 339 }, 340 }, 341 } 342 343 if err := ctx.ServiceClient.RegisterWorkload(ctx.Workload); err != nil { 344 t.Fatalf("unexpected error registering task: %v", err) 345 } 346 347 if err := ctx.syncOnce(syncNewOps); err != nil { 348 t.Fatalf("unexpected error syncing task: %v", err) 349 } 350 351 if n := len(ctx.FakeConsul.services); n != 1 { 352 t.Fatalf("expected 1 service but found %d:\n%#v", n, ctx.FakeConsul.services) 353 } 354 355 // Assert a check restart watch update was enqueued and clear it 356 if n := len(ctx.ServiceClient.checkWatcher.checkUpdateCh); n != 1 { 357 t.Fatalf("expected 1 check restart update but found %d", n) 358 } 359 upd := <-ctx.ServiceClient.checkWatcher.checkUpdateCh 360 c1ID := upd.checkID 361 362 // Query the allocs registrations and then again when we update. The IDs 363 // should change 364 reg1, err := ctx.ServiceClient.AllocRegistrations(ctx.Workload.AllocID) 365 if err != nil { 366 t.Fatalf("Looking up alloc registration failed: %v", err) 367 } 368 if reg1 == nil { 369 t.Fatalf("Nil alloc registrations: %v", err) 370 } 371 if num := reg1.NumServices(); num != 1 { 372 t.Fatalf("Wrong number of services: got %d; want 1", num) 373 } 374 if num := reg1.NumChecks(); num != 1 { 375 t.Fatalf("Wrong number of checks: got %d; want 1", num) 376 } 377 378 origServiceKey := "" 379 for k, v := range ctx.FakeConsul.services { 380 origServiceKey = k 381 if v.Name != ctx.Workload.Services[0].Name { 382 t.Errorf("expected Name=%q != %q", ctx.Workload.Services[0].Name, v.Name) 383 } 384 if v.Port != xPort { 385 t.Errorf("expected Port x=%v but found: %v", xPort, v.Port) 386 } 387 } 388 389 if n := len(ctx.FakeConsul.checks); n != 1 { 390 t.Fatalf("expected 1 check but found %d:\n%#v", n, ctx.FakeConsul.checks) 391 } 392 for _, v := range ctx.FakeConsul.checks { 393 if v.Name != "c1" { 394 t.Fatalf("expected check c1 but found %q", v.Name) 395 } 396 } 397 398 // Now add a check and modify the original 399 origWorkload := ctx.Workload.Copy() 400 ctx.Workload.Services[0].Checks = []*structs.ServiceCheck{ 401 { 402 Name: "c1", 403 Type: "tcp", 404 Interval: 2 * time.Second, 405 Timeout: time.Second, 406 PortLabel: "x", 407 CheckRestart: &structs.CheckRestart{ 408 Limit: 3, 409 }, 410 }, 411 { 412 Name: "c2", 413 Type: "http", 414 Path: "/", 415 Interval: time.Second, 416 Timeout: time.Second, 417 PortLabel: "x", 418 }, 419 } 420 if err := ctx.ServiceClient.UpdateWorkload(origWorkload, ctx.Workload); err != nil { 421 t.Fatalf("unexpected error registering task: %v", err) 422 } 423 424 // Assert 2 check restart watch updates was enqueued 425 if n := len(ctx.ServiceClient.checkWatcher.checkUpdateCh); n != 2 { 426 t.Fatalf("expected 2 check restart updates but found %d", n) 427 } 428 429 // First the new watch 430 upd = <-ctx.ServiceClient.checkWatcher.checkUpdateCh 431 if upd.checkID == c1ID || upd.remove { 432 t.Fatalf("expected check watch update to be an add of checkID=%q but found remove=%t checkID=%q", 433 c1ID, upd.remove, upd.checkID) 434 } 435 436 // Then remove the old watch 437 upd = <-ctx.ServiceClient.checkWatcher.checkUpdateCh 438 if upd.checkID != c1ID || !upd.remove { 439 t.Fatalf("expected check watch update to be a removal of checkID=%q but found remove=%t checkID=%q", 440 c1ID, upd.remove, upd.checkID) 441 } 442 443 if err := ctx.syncOnce(syncNewOps); err != nil { 444 t.Fatalf("unexpected error syncing task: %v", err) 445 } 446 447 if n := len(ctx.FakeConsul.services); n != 1 { 448 t.Fatalf("expected 1 service but found %d:\n%#v", n, ctx.FakeConsul.services) 449 } 450 451 if _, ok := ctx.FakeConsul.services[origServiceKey]; !ok { 452 t.Errorf("unexpected key change; was: %q -- but found %#v", origServiceKey, ctx.FakeConsul.services) 453 } 454 455 if n := len(ctx.FakeConsul.checks); n != 2 { 456 t.Fatalf("expected 2 check but found %d:\n%#v", n, ctx.FakeConsul.checks) 457 } 458 459 for k, v := range ctx.FakeConsul.checks { 460 switch v.Name { 461 case "c1": 462 if expected := fmt.Sprintf(":%d", xPort); v.TCP != expected { 463 t.Errorf("expected Port x=%v but found: %v", expected, v.TCP) 464 } 465 466 // update id 467 c1ID = k 468 case "c2": 469 if expected := fmt.Sprintf("http://:%d/", xPort); v.HTTP != expected { 470 t.Errorf("expected Port x=%v but found: %v", expected, v.HTTP) 471 } 472 default: 473 t.Errorf("Unknown check: %q", k) 474 } 475 } 476 477 // Check again and ensure the IDs changed 478 reg2, err := ctx.ServiceClient.AllocRegistrations(ctx.Workload.AllocID) 479 if err != nil { 480 t.Fatalf("Looking up alloc registration failed: %v", err) 481 } 482 if reg2 == nil { 483 t.Fatalf("Nil alloc registrations: %v", err) 484 } 485 if num := reg2.NumServices(); num != 1 { 486 t.Fatalf("Wrong number of services: got %d; want 1", num) 487 } 488 if num := reg2.NumChecks(); num != 2 { 489 t.Fatalf("Wrong number of checks: got %d; want 2", num) 490 } 491 492 for task, treg := range reg1.Tasks { 493 otherTaskReg, ok := reg2.Tasks[task] 494 if !ok { 495 t.Fatalf("Task %q not in second reg", task) 496 } 497 498 for sID, sreg := range treg.Services { 499 otherServiceReg, ok := otherTaskReg.Services[sID] 500 if !ok { 501 t.Fatalf("service ID changed") 502 } 503 504 for newID := range sreg.checkIDs { 505 if _, ok := otherServiceReg.checkIDs[newID]; ok { 506 t.Fatalf("check IDs should change") 507 } 508 } 509 } 510 } 511 512 // Alter a CheckRestart and make sure the watcher is updated but nothing else 513 origWorkload = ctx.Workload.Copy() 514 ctx.Workload.Services[0].Checks = []*structs.ServiceCheck{ 515 { 516 Name: "c1", 517 Type: "tcp", 518 Interval: 2 * time.Second, 519 Timeout: time.Second, 520 PortLabel: "x", 521 CheckRestart: &structs.CheckRestart{ 522 Limit: 11, 523 }, 524 }, 525 { 526 Name: "c2", 527 Type: "http", 528 Path: "/", 529 Interval: time.Second, 530 Timeout: time.Second, 531 PortLabel: "x", 532 }, 533 } 534 if err := ctx.ServiceClient.UpdateWorkload(origWorkload, ctx.Workload); err != nil { 535 t.Fatalf("unexpected error registering task: %v", err) 536 } 537 if err := ctx.syncOnce(syncNewOps); err != nil { 538 t.Fatalf("unexpected error syncing task: %v", err) 539 } 540 541 if n := len(ctx.FakeConsul.checks); n != 2 { 542 t.Fatalf("expected 2 check but found %d:\n%#v", n, ctx.FakeConsul.checks) 543 } 544 545 for k, v := range ctx.FakeConsul.checks { 546 if v.Name == "c1" { 547 if k != c1ID { 548 t.Errorf("expected c1 to still have id %q but found %q", c1ID, k) 549 } 550 break 551 } 552 } 553 554 // Assert a check restart watch update was enqueued for a removal and an add 555 if n := len(ctx.ServiceClient.checkWatcher.checkUpdateCh); n != 1 { 556 t.Fatalf("expected 1 check restart update but found %d", n) 557 } 558 <-ctx.ServiceClient.checkWatcher.checkUpdateCh 559 } 560 561 // TestConsul_RegServices tests basic service registration. 562 func TestConsul_RegServices(t *testing.T) { 563 ctx := setupFake(t) 564 565 // Add a check w/restarting 566 ctx.Workload.Services[0].Checks = []*structs.ServiceCheck{ 567 { 568 Name: "testcheck", 569 Type: "tcp", 570 Interval: 100 * time.Millisecond, 571 CheckRestart: &structs.CheckRestart{ 572 Limit: 3, 573 }, 574 }, 575 } 576 577 if err := ctx.ServiceClient.RegisterWorkload(ctx.Workload); err != nil { 578 t.Fatalf("unexpected error registering task: %v", err) 579 } 580 581 if err := ctx.syncOnce(syncNewOps); err != nil { 582 t.Fatalf("unexpected error syncing task: %v", err) 583 } 584 585 if n := len(ctx.FakeConsul.services); n != 1 { 586 t.Fatalf("expected 1 service but found %d:\n%#v", n, ctx.FakeConsul.services) 587 } 588 589 for _, v := range ctx.FakeConsul.services { 590 if v.Name != ctx.Workload.Services[0].Name { 591 t.Errorf("expected Name=%q != %q", ctx.Workload.Services[0].Name, v.Name) 592 } 593 if !reflect.DeepEqual(v.Tags, ctx.Workload.Services[0].Tags) { 594 t.Errorf("expected Tags=%v != %v", ctx.Workload.Services[0].Tags, v.Tags) 595 } 596 if v.Port != xPort { 597 t.Errorf("expected Port=%d != %d", xPort, v.Port) 598 } 599 } 600 601 // Assert the check update is pending 602 if n := len(ctx.ServiceClient.checkWatcher.checkUpdateCh); n != 1 { 603 t.Fatalf("expected 1 check restart update but found %d", n) 604 } 605 606 // Assert the check update is properly formed 607 checkUpd := <-ctx.ServiceClient.checkWatcher.checkUpdateCh 608 if checkUpd.checkRestart.allocID != ctx.Workload.AllocID { 609 t.Fatalf("expected check's allocid to be %q but found %q", "allocid", checkUpd.checkRestart.allocID) 610 } 611 if expected := 200 * time.Millisecond; checkUpd.checkRestart.timeLimit != expected { 612 t.Fatalf("expected check's time limit to be %v but found %v", expected, checkUpd.checkRestart.timeLimit) 613 } 614 615 // Make a change which will register a new service 616 ctx.Workload.Services[0].Name = "taskname-service2" 617 ctx.Workload.Services[0].Tags[0] = "tag3" 618 if err := ctx.ServiceClient.RegisterWorkload(ctx.Workload); err != nil { 619 t.Fatalf("unexpected error registering task: %v", err) 620 } 621 622 // Assert check update is pending 623 if n := len(ctx.ServiceClient.checkWatcher.checkUpdateCh); n != 1 { 624 t.Fatalf("expected 1 check restart update but found %d", n) 625 } 626 627 // Assert the check update's id has changed 628 checkUpd2 := <-ctx.ServiceClient.checkWatcher.checkUpdateCh 629 if checkUpd.checkID == checkUpd2.checkID { 630 t.Fatalf("expected new check update to have a new ID both both have: %q", checkUpd.checkID) 631 } 632 633 // Make sure changes don't take affect until sync() is called (since 634 // Run() isn't running) 635 if n := len(ctx.FakeConsul.services); n != 1 { 636 t.Fatalf("expected 1 service but found %d:\n%#v", n, ctx.FakeConsul.services) 637 } 638 for _, v := range ctx.FakeConsul.services { 639 if reflect.DeepEqual(v.Tags, ctx.Workload.Services[0].Tags) { 640 t.Errorf("expected Tags to differ, changes applied before sync()") 641 } 642 } 643 644 // Now sync() and re-check for the applied updates 645 if err := ctx.syncOnce(syncNewOps); err != nil { 646 t.Fatalf("unexpected error syncing task: %v", err) 647 } 648 if n := len(ctx.FakeConsul.services); n != 2 { 649 t.Fatalf("expected 2 services but found %d:\n%#v", n, ctx.FakeConsul.services) 650 } 651 found := false 652 for _, v := range ctx.FakeConsul.services { 653 if v.Name == ctx.Workload.Services[0].Name { 654 if found { 655 t.Fatalf("found new service name %q twice", v.Name) 656 } 657 found = true 658 if !reflect.DeepEqual(v.Tags, ctx.Workload.Services[0].Tags) { 659 t.Errorf("expected Tags=%v != %v", ctx.Workload.Services[0].Tags, v.Tags) 660 } 661 } 662 } 663 if !found { 664 t.Fatalf("did not find new service %q", ctx.Workload.Services[0].Name) 665 } 666 667 // Remove the new task 668 ctx.ServiceClient.RemoveWorkload(ctx.Workload) 669 if err := ctx.syncOnce(syncNewOps); err != nil { 670 t.Fatalf("unexpected error syncing task: %v", err) 671 } 672 if n := len(ctx.FakeConsul.services); n != 1 { 673 t.Fatalf("expected 1 service but found %d:\n%#v", n, ctx.FakeConsul.services) 674 } 675 for _, v := range ctx.FakeConsul.services { 676 if v.Name != "taskname-service" { 677 t.Errorf("expected original task to survive not %q", v.Name) 678 } 679 } 680 681 // Assert check update is pending 682 if n := len(ctx.ServiceClient.checkWatcher.checkUpdateCh); n != 1 { 683 t.Fatalf("expected 1 check restart update but found %d", n) 684 } 685 686 // Assert the check update's id is correct and that it's a removal 687 checkUpd3 := <-ctx.ServiceClient.checkWatcher.checkUpdateCh 688 if checkUpd2.checkID != checkUpd3.checkID { 689 t.Fatalf("expected checkid %q but found %q", checkUpd2.checkID, checkUpd3.checkID) 690 } 691 if !checkUpd3.remove { 692 t.Fatalf("expected check watch removal update but found: %#v", checkUpd3) 693 } 694 } 695 696 // TestConsul_ShutdownOK tests the ok path for the shutdown logic in 697 // ServiceClient. 698 func TestConsul_ShutdownOK(t *testing.T) { 699 require := require.New(t) 700 ctx := setupFake(t) 701 go ctx.ServiceClient.Run() 702 703 // register the Nomad agent service and check 704 agentServices := []*structs.Service{ 705 { 706 Name: "http", 707 Tags: []string{"nomad"}, 708 PortLabel: "localhost:2345", 709 Checks: []*structs.ServiceCheck{ 710 { 711 Name: "nomad-tcp", 712 Type: "tcp", 713 Interval: 9000 * time.Hour, // make check block 714 Timeout: 10 * time.Second, 715 InitialStatus: "warning", 716 }, 717 }, 718 }, 719 } 720 require.NoError(ctx.ServiceClient.RegisterAgent("client", agentServices)) 721 require.Eventually(ctx.ServiceClient.hasSeen, time.Second, 10*time.Millisecond) 722 723 // assert successful registration 724 require.Len(ctx.FakeConsul.services, 1, "expected agent service to be registered") 725 require.Len(ctx.FakeConsul.checks, 1, "expected agent check to be registered") 726 require.Contains(ctx.FakeConsul.services, 727 makeAgentServiceID("client", agentServices[0])) 728 729 // Shutdown() should block until Nomad agent service/check is deregistered 730 require.NoError(ctx.ServiceClient.Shutdown()) 731 require.Len(ctx.FakeConsul.services, 0, "expected agent service to be deregistered") 732 require.Len(ctx.FakeConsul.checks, 0, "expected agent check to be deregistered") 733 } 734 735 // TestConsul_ShutdownBlocked tests the blocked past deadline path for the 736 // shutdown logic in ServiceClient. 737 func TestConsul_ShutdownBlocked(t *testing.T) { 738 require := require.New(t) 739 t.Parallel() 740 ctx := setupFake(t) 741 // can be short because we're intentionally blocking, but needs to 742 // be longer than the time we'll block Consul so we can be sure 743 // we're not delayed either. 744 ctx.ServiceClient.shutdownWait = time.Second 745 go ctx.ServiceClient.Run() 746 747 // register the Nomad agent service and check 748 agentServices := []*structs.Service{ 749 { 750 Name: "http", 751 Tags: []string{"nomad"}, 752 PortLabel: "localhost:2345", 753 Checks: []*structs.ServiceCheck{ 754 { 755 Name: "nomad-tcp", 756 Type: "tcp", 757 Interval: 9000 * time.Hour, // make check block 758 Timeout: 10 * time.Second, 759 InitialStatus: "warning", 760 }, 761 }, 762 }, 763 } 764 require.NoError(ctx.ServiceClient.RegisterAgent("client", agentServices)) 765 require.Eventually(ctx.ServiceClient.hasSeen, time.Second, 10*time.Millisecond) 766 require.Len(ctx.FakeConsul.services, 1, "expected agent service to be registered") 767 require.Len(ctx.FakeConsul.checks, 1, "expected agent check to be registered") 768 769 // prevent normal shutdown by blocking Consul. the shutdown should wait 770 // until agent deregistration has finished 771 waiter := make(chan struct{}) 772 result := make(chan error) 773 go func() { 774 ctx.FakeConsul.mu.Lock() 775 close(waiter) 776 result <- ctx.ServiceClient.Shutdown() 777 }() 778 779 <-waiter // wait for lock to be hit 780 781 // Shutdown should block until all enqueued operations finish. 782 preShutdown := time.Now() 783 select { 784 case <-time.After(200 * time.Millisecond): 785 ctx.FakeConsul.mu.Unlock() 786 require.NoError(<-result) 787 case <-result: 788 t.Fatal("should not have received result until Consul unblocked") 789 } 790 shutdownTime := time.Now().Sub(preShutdown).Seconds() 791 require.Less(shutdownTime, time.Second.Seconds(), 792 "expected shutdown to take >200ms and <1s") 793 require.Greater(shutdownTime, 200*time.Millisecond.Seconds(), 794 "expected shutdown to take >200ms and <1s") 795 require.Len(ctx.FakeConsul.services, 0, 796 "expected agent service to be deregistered") 797 require.Len(ctx.FakeConsul.checks, 0, 798 "expected agent check to be deregistered") 799 } 800 801 // TestConsul_DriverNetwork_AutoUse asserts that if a driver network has 802 // auto-use set then services should advertise it unless explicitly set to 803 // host. Checks should always use host. 804 func TestConsul_DriverNetwork_AutoUse(t *testing.T) { 805 t.Parallel() 806 ctx := setupFake(t) 807 808 ctx.Workload.Services = []*structs.Service{ 809 { 810 Name: "auto-advertise-x", 811 PortLabel: "x", 812 AddressMode: structs.AddressModeAuto, 813 Checks: []*structs.ServiceCheck{ 814 { 815 Name: "default-check-x", 816 Type: "tcp", 817 Interval: time.Second, 818 Timeout: time.Second, 819 }, 820 { 821 Name: "weird-y-check", 822 Type: "http", 823 Interval: time.Second, 824 Timeout: time.Second, 825 PortLabel: "y", 826 }, 827 }, 828 }, 829 { 830 Name: "driver-advertise-y", 831 PortLabel: "y", 832 AddressMode: structs.AddressModeDriver, 833 Checks: []*structs.ServiceCheck{ 834 { 835 Name: "default-check-y", 836 Type: "tcp", 837 Interval: time.Second, 838 Timeout: time.Second, 839 }, 840 }, 841 }, 842 { 843 Name: "host-advertise-y", 844 PortLabel: "y", 845 AddressMode: structs.AddressModeHost, 846 }, 847 } 848 849 ctx.Workload.DriverNetwork = &drivers.DriverNetwork{ 850 PortMap: map[string]int{ 851 "x": 8888, 852 "y": 9999, 853 }, 854 IP: "172.18.0.2", 855 AutoAdvertise: true, 856 } 857 858 if err := ctx.ServiceClient.RegisterWorkload(ctx.Workload); err != nil { 859 t.Fatalf("unexpected error registering task: %v", err) 860 } 861 862 if err := ctx.syncOnce(syncNewOps); err != nil { 863 t.Fatalf("unexpected error syncing task: %v", err) 864 } 865 866 if n := len(ctx.FakeConsul.services); n != 3 { 867 t.Fatalf("expected 2 services but found: %d", n) 868 } 869 870 for _, v := range ctx.FakeConsul.services { 871 switch v.Name { 872 case ctx.Workload.Services[0].Name: // x 873 // Since DriverNetwork.AutoAdvertise=true, driver ports should be used 874 if v.Port != ctx.Workload.DriverNetwork.PortMap["x"] { 875 t.Errorf("expected service %s's port to be %d but found %d", 876 v.Name, ctx.Workload.DriverNetwork.PortMap["x"], v.Port) 877 } 878 // The order of checks in Consul is not guaranteed to 879 // be the same as their order in the Workload definition, 880 // so check in a loop 881 if expected := 2; len(v.Checks) != expected { 882 t.Errorf("expected %d checks but found %d", expected, len(v.Checks)) 883 } 884 for _, c := range v.Checks { 885 // No name on AgentServiceChecks, use type 886 switch { 887 case c.TCP != "": 888 // Checks should always use host port though 889 if c.TCP != ":1234" { // xPort 890 t.Errorf("expected service %s check 1's port to be %d but found %q", 891 v.Name, xPort, c.TCP) 892 } 893 case c.HTTP != "": 894 if c.HTTP != "http://:1235" { // yPort 895 t.Errorf("expected service %s check 2's port to be %d but found %q", 896 v.Name, yPort, c.HTTP) 897 } 898 default: 899 t.Errorf("unexpected check %#v on service %q", c, v.Name) 900 } 901 } 902 case ctx.Workload.Services[1].Name: // y 903 // Service should be container ip:port 904 if v.Address != ctx.Workload.DriverNetwork.IP { 905 t.Errorf("expected service %s's address to be %s but found %s", 906 v.Name, ctx.Workload.DriverNetwork.IP, v.Address) 907 } 908 if v.Port != ctx.Workload.DriverNetwork.PortMap["y"] { 909 t.Errorf("expected service %s's port to be %d but found %d", 910 v.Name, ctx.Workload.DriverNetwork.PortMap["x"], v.Port) 911 } 912 // Check should be host ip:port 913 if v.Checks[0].TCP != ":1235" { // yPort 914 t.Errorf("expected service %s check's port to be %d but found %s", 915 v.Name, yPort, v.Checks[0].TCP) 916 } 917 case ctx.Workload.Services[2].Name: // y + host mode 918 if v.Port != yPort { 919 t.Errorf("expected service %s's port to be %d but found %d", 920 v.Name, yPort, v.Port) 921 } 922 default: 923 t.Errorf("unexpected service name: %q", v.Name) 924 } 925 } 926 } 927 928 // TestConsul_DriverNetwork_NoAutoUse asserts that if a driver network doesn't 929 // set auto-use only services which request the driver's network should 930 // advertise it. 931 func TestConsul_DriverNetwork_NoAutoUse(t *testing.T) { 932 t.Parallel() 933 ctx := setupFake(t) 934 935 ctx.Workload.Services = []*structs.Service{ 936 { 937 Name: "auto-advertise-x", 938 PortLabel: "x", 939 AddressMode: structs.AddressModeAuto, 940 }, 941 { 942 Name: "driver-advertise-y", 943 PortLabel: "y", 944 AddressMode: structs.AddressModeDriver, 945 }, 946 { 947 Name: "host-advertise-y", 948 PortLabel: "y", 949 AddressMode: structs.AddressModeHost, 950 }, 951 } 952 953 ctx.Workload.DriverNetwork = &drivers.DriverNetwork{ 954 PortMap: map[string]int{ 955 "x": 8888, 956 "y": 9999, 957 }, 958 IP: "172.18.0.2", 959 AutoAdvertise: false, 960 } 961 962 if err := ctx.ServiceClient.RegisterWorkload(ctx.Workload); err != nil { 963 t.Fatalf("unexpected error registering task: %v", err) 964 } 965 966 if err := ctx.syncOnce(syncNewOps); err != nil { 967 t.Fatalf("unexpected error syncing task: %v", err) 968 } 969 970 if n := len(ctx.FakeConsul.services); n != 3 { 971 t.Fatalf("expected 3 services but found: %d", n) 972 } 973 974 for _, v := range ctx.FakeConsul.services { 975 switch v.Name { 976 case ctx.Workload.Services[0].Name: // x + auto 977 // Since DriverNetwork.AutoAdvertise=false, host ports should be used 978 if v.Port != xPort { 979 t.Errorf("expected service %s's port to be %d but found %d", 980 v.Name, xPort, v.Port) 981 } 982 case ctx.Workload.Services[1].Name: // y + driver mode 983 // Service should be container ip:port 984 if v.Address != ctx.Workload.DriverNetwork.IP { 985 t.Errorf("expected service %s's address to be %s but found %s", 986 v.Name, ctx.Workload.DriverNetwork.IP, v.Address) 987 } 988 if v.Port != ctx.Workload.DriverNetwork.PortMap["y"] { 989 t.Errorf("expected service %s's port to be %d but found %d", 990 v.Name, ctx.Workload.DriverNetwork.PortMap["x"], v.Port) 991 } 992 case ctx.Workload.Services[2].Name: // y + host mode 993 if v.Port != yPort { 994 t.Errorf("expected service %s's port to be %d but found %d", 995 v.Name, yPort, v.Port) 996 } 997 default: 998 t.Errorf("unexpected service name: %q", v.Name) 999 } 1000 } 1001 } 1002 1003 // TestConsul_DriverNetwork_Change asserts that if a driver network is 1004 // specified and a service updates its use its properly updated in Consul. 1005 func TestConsul_DriverNetwork_Change(t *testing.T) { 1006 t.Parallel() 1007 ctx := setupFake(t) 1008 1009 ctx.Workload.Services = []*structs.Service{ 1010 { 1011 Name: "service-foo", 1012 PortLabel: "x", 1013 AddressMode: structs.AddressModeAuto, 1014 }, 1015 } 1016 1017 ctx.Workload.DriverNetwork = &drivers.DriverNetwork{ 1018 PortMap: map[string]int{ 1019 "x": 8888, 1020 "y": 9999, 1021 }, 1022 IP: "172.18.0.2", 1023 AutoAdvertise: false, 1024 } 1025 1026 syncAndAssertPort := func(port int) { 1027 if err := ctx.syncOnce(syncNewOps); err != nil { 1028 t.Fatalf("unexpected error syncing task: %v", err) 1029 } 1030 1031 if n := len(ctx.FakeConsul.services); n != 1 { 1032 t.Fatalf("expected 1 service but found: %d", n) 1033 } 1034 1035 for _, v := range ctx.FakeConsul.services { 1036 switch v.Name { 1037 case ctx.Workload.Services[0].Name: 1038 if v.Port != port { 1039 t.Errorf("expected service %s's port to be %d but found %d", 1040 v.Name, port, v.Port) 1041 } 1042 default: 1043 t.Errorf("unexpected service name: %q", v.Name) 1044 } 1045 } 1046 } 1047 1048 // Initial service should advertise host port x 1049 if err := ctx.ServiceClient.RegisterWorkload(ctx.Workload); err != nil { 1050 t.Fatalf("unexpected error registering task: %v", err) 1051 } 1052 1053 syncAndAssertPort(xPort) 1054 1055 // UpdateWorkload to use Host (shouldn't change anything) 1056 origWorkload := ctx.Workload.Copy() 1057 ctx.Workload.Services[0].AddressMode = structs.AddressModeHost 1058 1059 if err := ctx.ServiceClient.UpdateWorkload(origWorkload, ctx.Workload); err != nil { 1060 t.Fatalf("unexpected error updating task: %v", err) 1061 } 1062 1063 syncAndAssertPort(xPort) 1064 1065 // UpdateWorkload to use Driver (*should* change IP and port) 1066 origWorkload = ctx.Workload.Copy() 1067 ctx.Workload.Services[0].AddressMode = structs.AddressModeDriver 1068 1069 if err := ctx.ServiceClient.UpdateWorkload(origWorkload, ctx.Workload); err != nil { 1070 t.Fatalf("unexpected error updating task: %v", err) 1071 } 1072 1073 syncAndAssertPort(ctx.Workload.DriverNetwork.PortMap["x"]) 1074 } 1075 1076 // TestConsul_CanaryTags asserts CanaryTags are used when Canary=true 1077 func TestConsul_CanaryTags(t *testing.T) { 1078 t.Parallel() 1079 require := require.New(t) 1080 ctx := setupFake(t) 1081 1082 canaryTags := []string{"tag1", "canary"} 1083 ctx.Workload.Canary = true 1084 ctx.Workload.Services[0].CanaryTags = canaryTags 1085 1086 require.NoError(ctx.ServiceClient.RegisterWorkload(ctx.Workload)) 1087 require.NoError(ctx.syncOnce(syncNewOps)) 1088 require.Len(ctx.FakeConsul.services, 1) 1089 for _, service := range ctx.FakeConsul.services { 1090 require.Equal(canaryTags, service.Tags) 1091 } 1092 1093 // Disable canary and assert tags are not the canary tags 1094 origWorkload := ctx.Workload.Copy() 1095 ctx.Workload.Canary = false 1096 require.NoError(ctx.ServiceClient.UpdateWorkload(origWorkload, ctx.Workload)) 1097 require.NoError(ctx.syncOnce(syncNewOps)) 1098 require.Len(ctx.FakeConsul.services, 1) 1099 for _, service := range ctx.FakeConsul.services { 1100 require.NotEqual(canaryTags, service.Tags) 1101 } 1102 1103 ctx.ServiceClient.RemoveWorkload(ctx.Workload) 1104 require.NoError(ctx.syncOnce(syncNewOps)) 1105 require.Len(ctx.FakeConsul.services, 0) 1106 } 1107 1108 // TestConsul_CanaryTags_NoTags asserts Tags are used when Canary=true and there 1109 // are no specified canary tags 1110 func TestConsul_CanaryTags_NoTags(t *testing.T) { 1111 t.Parallel() 1112 require := require.New(t) 1113 ctx := setupFake(t) 1114 1115 tags := []string{"tag1", "foo"} 1116 ctx.Workload.Canary = true 1117 ctx.Workload.Services[0].Tags = tags 1118 1119 require.NoError(ctx.ServiceClient.RegisterWorkload(ctx.Workload)) 1120 require.NoError(ctx.syncOnce(syncNewOps)) 1121 require.Len(ctx.FakeConsul.services, 1) 1122 for _, service := range ctx.FakeConsul.services { 1123 require.Equal(tags, service.Tags) 1124 } 1125 1126 // Disable canary and assert tags dont change 1127 origWorkload := ctx.Workload.Copy() 1128 ctx.Workload.Canary = false 1129 require.NoError(ctx.ServiceClient.UpdateWorkload(origWorkload, ctx.Workload)) 1130 require.NoError(ctx.syncOnce(syncNewOps)) 1131 require.Len(ctx.FakeConsul.services, 1) 1132 for _, service := range ctx.FakeConsul.services { 1133 require.Equal(tags, service.Tags) 1134 } 1135 1136 ctx.ServiceClient.RemoveWorkload(ctx.Workload) 1137 require.NoError(ctx.syncOnce(syncNewOps)) 1138 require.Len(ctx.FakeConsul.services, 0) 1139 } 1140 1141 // TestConsul_CanaryMeta asserts CanaryMeta are used when Canary=true 1142 func TestConsul_CanaryMeta(t *testing.T) { 1143 t.Parallel() 1144 require := require.New(t) 1145 ctx := setupFake(t) 1146 1147 canaryMeta := map[string]string{"meta1": "canary"} 1148 canaryMeta["external-source"] = "nomad" 1149 ctx.Workload.Canary = true 1150 ctx.Workload.Services[0].CanaryMeta = canaryMeta 1151 1152 require.NoError(ctx.ServiceClient.RegisterWorkload(ctx.Workload)) 1153 require.NoError(ctx.syncOnce(syncNewOps)) 1154 require.Len(ctx.FakeConsul.services, 1) 1155 for _, service := range ctx.FakeConsul.services { 1156 require.Equal(canaryMeta, service.Meta) 1157 } 1158 1159 // Disable canary and assert meta are not the canary meta 1160 origWorkload := ctx.Workload.Copy() 1161 ctx.Workload.Canary = false 1162 require.NoError(ctx.ServiceClient.UpdateWorkload(origWorkload, ctx.Workload)) 1163 require.NoError(ctx.syncOnce(syncNewOps)) 1164 require.Len(ctx.FakeConsul.services, 1) 1165 for _, service := range ctx.FakeConsul.services { 1166 require.NotEqual(canaryMeta, service.Meta) 1167 } 1168 1169 ctx.ServiceClient.RemoveWorkload(ctx.Workload) 1170 require.NoError(ctx.syncOnce(syncNewOps)) 1171 require.Len(ctx.FakeConsul.services, 0) 1172 } 1173 1174 // TestConsul_CanaryMeta_NoMeta asserts Meta are used when Canary=true and there 1175 // are no specified canary meta 1176 func TestConsul_CanaryMeta_NoMeta(t *testing.T) { 1177 t.Parallel() 1178 require := require.New(t) 1179 ctx := setupFake(t) 1180 1181 meta := map[string]string{"meta1": "foo"} 1182 meta["external-source"] = "nomad" 1183 ctx.Workload.Canary = true 1184 ctx.Workload.Services[0].Meta = meta 1185 1186 require.NoError(ctx.ServiceClient.RegisterWorkload(ctx.Workload)) 1187 require.NoError(ctx.syncOnce(syncNewOps)) 1188 require.Len(ctx.FakeConsul.services, 1) 1189 for _, service := range ctx.FakeConsul.services { 1190 require.Equal(meta, service.Meta) 1191 } 1192 1193 // Disable canary and assert meta dont change 1194 origWorkload := ctx.Workload.Copy() 1195 ctx.Workload.Canary = false 1196 require.NoError(ctx.ServiceClient.UpdateWorkload(origWorkload, ctx.Workload)) 1197 require.NoError(ctx.syncOnce(syncNewOps)) 1198 require.Len(ctx.FakeConsul.services, 1) 1199 for _, service := range ctx.FakeConsul.services { 1200 require.Equal(meta, service.Meta) 1201 } 1202 1203 ctx.ServiceClient.RemoveWorkload(ctx.Workload) 1204 require.NoError(ctx.syncOnce(syncNewOps)) 1205 require.Len(ctx.FakeConsul.services, 0) 1206 } 1207 1208 // TestConsul_PeriodicSync asserts that Nomad periodically reconciles with 1209 // Consul. 1210 func TestConsul_PeriodicSync(t *testing.T) { 1211 t.Parallel() 1212 1213 ctx := setupFake(t) 1214 defer ctx.ServiceClient.Shutdown() 1215 1216 // Lower periodic sync interval to speed up test 1217 ctx.ServiceClient.periodicInterval = 1 * time.Millisecond 1218 1219 // Run for 20ms and assert hits >= 5 because each sync() calls multiple 1220 // Consul APIs 1221 go ctx.ServiceClient.Run() 1222 1223 select { 1224 case <-ctx.ServiceClient.exitCh: 1225 t.Fatalf("exited unexpectedly") 1226 case <-time.After(20 * time.Millisecond): 1227 } 1228 1229 minHits := 5 1230 if hits := ctx.FakeConsul.getHits(); hits < minHits { 1231 t.Fatalf("expected at least %d hits but found %d", minHits, hits) 1232 } 1233 } 1234 1235 // TestIsNomadService asserts the isNomadService helper returns true for Nomad 1236 // task IDs and false for unknown IDs and Nomad agent IDs (see #2827). 1237 func TestIsNomadService(t *testing.T) { 1238 t.Parallel() 1239 1240 tests := []struct { 1241 id string 1242 result bool 1243 }{ 1244 {"_nomad-client-nomad-client-http", false}, 1245 {"_nomad-server-nomad-serf", false}, 1246 1247 // Pre-0.7.1 style IDs still match 1248 {"_nomad-executor-abc", true}, 1249 {"_nomad-executor", true}, 1250 1251 // Post-0.7.1 style IDs match 1252 {"_nomad-task-FBBK265QN4TMT25ND4EP42TJVMYJ3HR4", true}, 1253 1254 {"not-nomad", false}, 1255 {"_nomad", false}, 1256 } 1257 1258 for _, test := range tests { 1259 t.Run(test.id, func(t *testing.T) { 1260 actual := isNomadService(test.id) 1261 if actual != test.result { 1262 t.Errorf("%q should be %t but found %t", test.id, test.result, actual) 1263 } 1264 }) 1265 } 1266 } 1267 1268 // TestCreateCheckReg_HTTP asserts Nomad ServiceCheck structs are properly 1269 // converted to Consul API AgentCheckRegistrations for HTTP checks. 1270 func TestCreateCheckReg_HTTP(t *testing.T) { 1271 t.Parallel() 1272 check := &structs.ServiceCheck{ 1273 Name: "name", 1274 Type: "http", 1275 Path: "/path", 1276 PortLabel: "label", 1277 Method: "POST", 1278 Header: map[string][]string{ 1279 "Foo": {"bar"}, 1280 }, 1281 } 1282 1283 serviceID := "testService" 1284 checkID := check.Hash(serviceID) 1285 host := "localhost" 1286 port := 41111 1287 1288 expected := &api.AgentCheckRegistration{ 1289 ID: checkID, 1290 Name: "name", 1291 ServiceID: serviceID, 1292 AgentServiceCheck: api.AgentServiceCheck{ 1293 Timeout: "0s", 1294 Interval: "0s", 1295 HTTP: fmt.Sprintf("http://%s:%d/path", host, port), 1296 Method: "POST", 1297 Header: map[string][]string{ 1298 "Foo": {"bar"}, 1299 }, 1300 }, 1301 } 1302 1303 actual, err := createCheckReg(serviceID, checkID, check, host, port) 1304 if err != nil { 1305 t.Fatalf("err: %v", err) 1306 } 1307 1308 if diff := pretty.Diff(actual, expected); len(diff) > 0 { 1309 t.Fatalf("diff:\n%s\n", strings.Join(diff, "\n")) 1310 } 1311 } 1312 1313 // TestCreateCheckReg_GRPC asserts Nomad ServiceCheck structs are properly 1314 // converted to Consul API AgentCheckRegistrations for GRPC checks. 1315 func TestCreateCheckReg_GRPC(t *testing.T) { 1316 t.Parallel() 1317 check := &structs.ServiceCheck{ 1318 Name: "name", 1319 Type: "grpc", 1320 PortLabel: "label", 1321 GRPCService: "foo.Bar", 1322 GRPCUseTLS: true, 1323 TLSSkipVerify: true, 1324 Timeout: time.Second, 1325 Interval: time.Minute, 1326 } 1327 1328 serviceID := "testService" 1329 checkID := check.Hash(serviceID) 1330 1331 expected := &api.AgentCheckRegistration{ 1332 ID: checkID, 1333 Name: "name", 1334 ServiceID: serviceID, 1335 AgentServiceCheck: api.AgentServiceCheck{ 1336 Timeout: "1s", 1337 Interval: "1m0s", 1338 GRPC: "localhost:8080/foo.Bar", 1339 GRPCUseTLS: true, 1340 TLSSkipVerify: true, 1341 }, 1342 } 1343 1344 actual, err := createCheckReg(serviceID, checkID, check, "localhost", 8080) 1345 require.NoError(t, err) 1346 require.Equal(t, expected, actual) 1347 } 1348 1349 // TestGetAddress asserts Nomad uses the correct ip and port for services and 1350 // checks depending on port labels, driver networks, and address mode. 1351 func TestGetAddress(t *testing.T) { 1352 const HostIP = "127.0.0.1" 1353 1354 cases := []struct { 1355 Name string 1356 1357 // Parameters 1358 Mode string 1359 PortLabel string 1360 Host map[string]int // will be converted to structs.Networks 1361 Driver *drivers.DriverNetwork 1362 Ports structs.AllocatedPorts 1363 Status *structs.AllocNetworkStatus 1364 1365 // Results 1366 ExpectedIP string 1367 ExpectedPort int 1368 ExpectedErr string 1369 }{ 1370 // Valid Configurations 1371 { 1372 Name: "ExampleService", 1373 Mode: structs.AddressModeAuto, 1374 PortLabel: "db", 1375 Host: map[string]int{"db": 12435}, 1376 Driver: &drivers.DriverNetwork{ 1377 PortMap: map[string]int{"db": 6379}, 1378 IP: "10.1.2.3", 1379 }, 1380 ExpectedIP: HostIP, 1381 ExpectedPort: 12435, 1382 }, 1383 { 1384 Name: "Host", 1385 Mode: structs.AddressModeHost, 1386 PortLabel: "db", 1387 Host: map[string]int{"db": 12345}, 1388 Driver: &drivers.DriverNetwork{ 1389 PortMap: map[string]int{"db": 6379}, 1390 IP: "10.1.2.3", 1391 }, 1392 ExpectedIP: HostIP, 1393 ExpectedPort: 12345, 1394 }, 1395 { 1396 Name: "Driver", 1397 Mode: structs.AddressModeDriver, 1398 PortLabel: "db", 1399 Host: map[string]int{"db": 12345}, 1400 Driver: &drivers.DriverNetwork{ 1401 PortMap: map[string]int{"db": 6379}, 1402 IP: "10.1.2.3", 1403 }, 1404 ExpectedIP: "10.1.2.3", 1405 ExpectedPort: 6379, 1406 }, 1407 { 1408 Name: "AutoDriver", 1409 Mode: structs.AddressModeAuto, 1410 PortLabel: "db", 1411 Host: map[string]int{"db": 12345}, 1412 Driver: &drivers.DriverNetwork{ 1413 PortMap: map[string]int{"db": 6379}, 1414 IP: "10.1.2.3", 1415 AutoAdvertise: true, 1416 }, 1417 ExpectedIP: "10.1.2.3", 1418 ExpectedPort: 6379, 1419 }, 1420 { 1421 Name: "DriverCustomPort", 1422 Mode: structs.AddressModeDriver, 1423 PortLabel: "7890", 1424 Host: map[string]int{"db": 12345}, 1425 Driver: &drivers.DriverNetwork{ 1426 PortMap: map[string]int{"db": 6379}, 1427 IP: "10.1.2.3", 1428 }, 1429 ExpectedIP: "10.1.2.3", 1430 ExpectedPort: 7890, 1431 }, 1432 1433 // Invalid Configurations 1434 { 1435 Name: "DriverWithoutNetwork", 1436 Mode: structs.AddressModeDriver, 1437 PortLabel: "db", 1438 Host: map[string]int{"db": 12345}, 1439 Driver: nil, 1440 ExpectedErr: "no driver network exists", 1441 }, 1442 { 1443 Name: "DriverBadPort", 1444 Mode: structs.AddressModeDriver, 1445 PortLabel: "bad-port-label", 1446 Host: map[string]int{"db": 12345}, 1447 Driver: &drivers.DriverNetwork{ 1448 PortMap: map[string]int{"db": 6379}, 1449 IP: "10.1.2.3", 1450 }, 1451 ExpectedErr: "invalid port", 1452 }, 1453 { 1454 Name: "DriverZeroPort", 1455 Mode: structs.AddressModeDriver, 1456 PortLabel: "0", 1457 Driver: &drivers.DriverNetwork{ 1458 IP: "10.1.2.3", 1459 }, 1460 ExpectedErr: "invalid port", 1461 }, 1462 { 1463 Name: "HostBadPort", 1464 Mode: structs.AddressModeHost, 1465 PortLabel: "bad-port-label", 1466 ExpectedErr: "invalid port", 1467 }, 1468 { 1469 Name: "InvalidMode", 1470 Mode: "invalid-mode", 1471 PortLabel: "80", 1472 ExpectedErr: "invalid address mode", 1473 }, 1474 { 1475 Name: "NoPort_AutoMode", 1476 Mode: structs.AddressModeAuto, 1477 ExpectedIP: HostIP, 1478 }, 1479 { 1480 Name: "NoPort_HostMode", 1481 Mode: structs.AddressModeHost, 1482 ExpectedIP: HostIP, 1483 }, 1484 { 1485 Name: "NoPort_DriverMode", 1486 Mode: structs.AddressModeDriver, 1487 Driver: &drivers.DriverNetwork{ 1488 IP: "10.1.2.3", 1489 }, 1490 ExpectedIP: "10.1.2.3", 1491 }, 1492 1493 // Scenarios using port 0.12 networking fields (NetworkStatus, AllocatedPortMapping) 1494 { 1495 Name: "ExampleServer_withAllocatedPorts", 1496 Mode: structs.AddressModeAuto, 1497 PortLabel: "db", 1498 Ports: []structs.AllocatedPortMapping{ 1499 { 1500 Label: "db", 1501 Value: 12435, 1502 To: 6379, 1503 HostIP: HostIP, 1504 }, 1505 }, 1506 Status: &structs.AllocNetworkStatus{ 1507 InterfaceName: "eth0", 1508 Address: "172.26.0.1", 1509 }, 1510 ExpectedIP: HostIP, 1511 ExpectedPort: 12435, 1512 }, 1513 { 1514 Name: "Host_withAllocatedPorts", 1515 Mode: structs.AddressModeHost, 1516 PortLabel: "db", 1517 Ports: []structs.AllocatedPortMapping{ 1518 { 1519 Label: "db", 1520 Value: 12345, 1521 To: 6379, 1522 HostIP: HostIP, 1523 }, 1524 }, 1525 Status: &structs.AllocNetworkStatus{ 1526 InterfaceName: "eth0", 1527 Address: "172.26.0.1", 1528 }, 1529 ExpectedIP: HostIP, 1530 ExpectedPort: 12345, 1531 }, 1532 { 1533 Name: "Driver_withAllocatedPorts", 1534 Mode: structs.AddressModeDriver, 1535 PortLabel: "db", 1536 Ports: []structs.AllocatedPortMapping{ 1537 { 1538 Label: "db", 1539 Value: 12345, 1540 To: 6379, 1541 HostIP: HostIP, 1542 }, 1543 }, 1544 Driver: &drivers.DriverNetwork{ 1545 IP: "10.1.2.3", 1546 }, 1547 Status: &structs.AllocNetworkStatus{ 1548 InterfaceName: "eth0", 1549 Address: "172.26.0.1", 1550 }, 1551 ExpectedIP: "10.1.2.3", 1552 ExpectedPort: 6379, 1553 }, 1554 { 1555 Name: "AutoDriver_withAllocatedPorts", 1556 Mode: structs.AddressModeAuto, 1557 PortLabel: "db", 1558 Ports: []structs.AllocatedPortMapping{ 1559 { 1560 Label: "db", 1561 Value: 12345, 1562 To: 6379, 1563 HostIP: HostIP, 1564 }, 1565 }, 1566 Driver: &drivers.DriverNetwork{ 1567 IP: "10.1.2.3", 1568 AutoAdvertise: true, 1569 }, 1570 Status: &structs.AllocNetworkStatus{ 1571 InterfaceName: "eth0", 1572 Address: "172.26.0.1", 1573 }, 1574 ExpectedIP: "10.1.2.3", 1575 ExpectedPort: 6379, 1576 }, 1577 { 1578 Name: "DriverCustomPort_withAllocatedPorts", 1579 Mode: structs.AddressModeDriver, 1580 PortLabel: "7890", 1581 Ports: []structs.AllocatedPortMapping{ 1582 { 1583 Label: "db", 1584 Value: 12345, 1585 To: 6379, 1586 HostIP: HostIP, 1587 }, 1588 }, 1589 Driver: &drivers.DriverNetwork{ 1590 IP: "10.1.2.3", 1591 }, 1592 Status: &structs.AllocNetworkStatus{ 1593 InterfaceName: "eth0", 1594 Address: "172.26.0.1", 1595 }, 1596 ExpectedIP: "10.1.2.3", 1597 ExpectedPort: 7890, 1598 }, 1599 { 1600 Name: "Host_MultiHostInterface", 1601 Mode: structs.AddressModeAuto, 1602 PortLabel: "db", 1603 Ports: []structs.AllocatedPortMapping{ 1604 { 1605 Label: "db", 1606 Value: 12345, 1607 To: 6379, 1608 HostIP: "127.0.0.100", 1609 }, 1610 }, 1611 Status: &structs.AllocNetworkStatus{ 1612 InterfaceName: "eth0", 1613 Address: "172.26.0.1", 1614 }, 1615 ExpectedIP: "127.0.0.100", 1616 ExpectedPort: 12345, 1617 }, 1618 { 1619 Name: "Alloc", 1620 Mode: structs.AddressModeAlloc, 1621 PortLabel: "db", 1622 Ports: []structs.AllocatedPortMapping{ 1623 { 1624 Label: "db", 1625 Value: 12345, 1626 To: 6379, 1627 HostIP: HostIP, 1628 }, 1629 }, 1630 Status: &structs.AllocNetworkStatus{ 1631 InterfaceName: "eth0", 1632 Address: "172.26.0.1", 1633 }, 1634 ExpectedIP: "172.26.0.1", 1635 ExpectedPort: 6379, 1636 }, 1637 { 1638 Name: "Alloc no to value", 1639 Mode: structs.AddressModeAlloc, 1640 PortLabel: "db", 1641 Ports: []structs.AllocatedPortMapping{ 1642 { 1643 Label: "db", 1644 Value: 12345, 1645 HostIP: HostIP, 1646 }, 1647 }, 1648 Status: &structs.AllocNetworkStatus{ 1649 InterfaceName: "eth0", 1650 Address: "172.26.0.1", 1651 }, 1652 ExpectedIP: "172.26.0.1", 1653 ExpectedPort: 12345, 1654 }, 1655 { 1656 Name: "AllocCustomPort", 1657 Mode: structs.AddressModeAlloc, 1658 PortLabel: "6379", 1659 Status: &structs.AllocNetworkStatus{ 1660 InterfaceName: "eth0", 1661 Address: "172.26.0.1", 1662 }, 1663 ExpectedIP: "172.26.0.1", 1664 ExpectedPort: 6379, 1665 }, 1666 } 1667 1668 for _, tc := range cases { 1669 t.Run(tc.Name, func(t *testing.T) { 1670 // convert host port map into a structs.Networks 1671 networks := []*structs.NetworkResource{ 1672 { 1673 IP: HostIP, 1674 ReservedPorts: make([]structs.Port, len(tc.Host)), 1675 }, 1676 } 1677 1678 i := 0 1679 for label, port := range tc.Host { 1680 networks[0].ReservedPorts[i].Label = label 1681 networks[0].ReservedPorts[i].Value = port 1682 i++ 1683 } 1684 1685 // Run getAddress 1686 ip, port, err := getAddress(tc.Mode, tc.PortLabel, networks, tc.Driver, tc.Ports, tc.Status) 1687 1688 // Assert the results 1689 assert.Equal(t, tc.ExpectedIP, ip, "IP mismatch") 1690 assert.Equal(t, tc.ExpectedPort, port, "Port mismatch") 1691 if tc.ExpectedErr == "" { 1692 assert.Nil(t, err) 1693 } else { 1694 if err == nil { 1695 t.Fatalf("expected error containing %q but err=nil", tc.ExpectedErr) 1696 } else { 1697 assert.Contains(t, err.Error(), tc.ExpectedErr) 1698 } 1699 } 1700 }) 1701 } 1702 } 1703 1704 func TestConsul_ServiceName_Duplicates(t *testing.T) { 1705 t.Parallel() 1706 ctx := setupFake(t) 1707 require := require.New(t) 1708 1709 ctx.Workload.Services = []*structs.Service{ 1710 { 1711 Name: "best-service", 1712 PortLabel: "x", 1713 Tags: []string{ 1714 "foo", 1715 }, 1716 Checks: []*structs.ServiceCheck{ 1717 { 1718 Name: "check-a", 1719 Type: "tcp", 1720 Interval: time.Second, 1721 Timeout: time.Second, 1722 }, 1723 }, 1724 }, 1725 { 1726 Name: "best-service", 1727 PortLabel: "y", 1728 Tags: []string{ 1729 "bar", 1730 }, 1731 Checks: []*structs.ServiceCheck{ 1732 { 1733 Name: "checky-mccheckface", 1734 Type: "tcp", 1735 Interval: time.Second, 1736 Timeout: time.Second, 1737 }, 1738 }, 1739 }, 1740 { 1741 Name: "worst-service", 1742 PortLabel: "y", 1743 }, 1744 } 1745 1746 require.NoError(ctx.ServiceClient.RegisterWorkload(ctx.Workload)) 1747 1748 require.NoError(ctx.syncOnce(syncNewOps)) 1749 1750 require.Len(ctx.FakeConsul.services, 3) 1751 1752 for _, v := range ctx.FakeConsul.services { 1753 if v.Name == ctx.Workload.Services[0].Name && v.Port == xPort { 1754 require.ElementsMatch(v.Tags, ctx.Workload.Services[0].Tags) 1755 require.Len(v.Checks, 1) 1756 } else if v.Name == ctx.Workload.Services[1].Name && v.Port == yPort { 1757 require.ElementsMatch(v.Tags, ctx.Workload.Services[1].Tags) 1758 require.Len(v.Checks, 1) 1759 } else if v.Name == ctx.Workload.Services[2].Name { 1760 require.Len(v.Checks, 0) 1761 } 1762 } 1763 } 1764 1765 // TestConsul_ServiceDeregistration_OutOfProbation asserts that during in steady 1766 // state we remove any services we don't reconize locally 1767 func TestConsul_ServiceDeregistration_OutProbation(t *testing.T) { 1768 t.Parallel() 1769 ctx := setupFake(t) 1770 require := require.New(t) 1771 1772 ctx.ServiceClient.deregisterProbationExpiry = time.Now().Add(-1 * time.Hour) 1773 1774 remainingWorkload := testWorkload() 1775 remainingWorkload.Services = []*structs.Service{ 1776 { 1777 Name: "remaining-service", 1778 PortLabel: "x", 1779 Checks: []*structs.ServiceCheck{ 1780 { 1781 Name: "check", 1782 Type: "tcp", 1783 Interval: time.Second, 1784 Timeout: time.Second, 1785 }, 1786 }, 1787 }, 1788 } 1789 remainingWorkloadServiceID := MakeAllocServiceID(remainingWorkload.AllocID, 1790 remainingWorkload.Name(), remainingWorkload.Services[0]) 1791 1792 require.NoError(ctx.ServiceClient.RegisterWorkload(remainingWorkload)) 1793 require.NoError(ctx.syncOnce(syncNewOps)) 1794 require.Len(ctx.FakeConsul.services, 1) 1795 require.Len(ctx.FakeConsul.checks, 1) 1796 1797 explicitlyRemovedWorkload := testWorkload() 1798 explicitlyRemovedWorkload.Services = []*structs.Service{ 1799 { 1800 Name: "explicitly-removed-service", 1801 PortLabel: "y", 1802 Checks: []*structs.ServiceCheck{ 1803 { 1804 Name: "check", 1805 Type: "tcp", 1806 Interval: time.Second, 1807 Timeout: time.Second, 1808 }, 1809 }, 1810 }, 1811 } 1812 explicitlyRemovedWorkloadServiceID := MakeAllocServiceID(explicitlyRemovedWorkload.AllocID, 1813 explicitlyRemovedWorkload.Name(), explicitlyRemovedWorkload.Services[0]) 1814 1815 require.NoError(ctx.ServiceClient.RegisterWorkload(explicitlyRemovedWorkload)) 1816 1817 require.NoError(ctx.syncOnce(syncNewOps)) 1818 require.Len(ctx.FakeConsul.services, 2) 1819 require.Len(ctx.FakeConsul.checks, 2) 1820 1821 // we register a task through nomad API then remove it out of band 1822 outofbandWorkload := testWorkload() 1823 outofbandWorkload.Services = []*structs.Service{ 1824 { 1825 Name: "unknown-service", 1826 PortLabel: "x", 1827 Checks: []*structs.ServiceCheck{ 1828 { 1829 Name: "check", 1830 Type: "tcp", 1831 Interval: time.Second, 1832 Timeout: time.Second, 1833 }, 1834 }, 1835 }, 1836 } 1837 outofbandWorkloadServiceID := MakeAllocServiceID(outofbandWorkload.AllocID, 1838 outofbandWorkload.Name(), outofbandWorkload.Services[0]) 1839 1840 require.NoError(ctx.ServiceClient.RegisterWorkload(outofbandWorkload)) 1841 require.NoError(ctx.syncOnce(syncNewOps)) 1842 1843 require.Len(ctx.FakeConsul.services, 3) 1844 1845 // remove outofbandWorkload from local services so it appears unknown to client 1846 require.Len(ctx.ServiceClient.services, 3) 1847 require.Len(ctx.ServiceClient.checks, 3) 1848 1849 delete(ctx.ServiceClient.services, outofbandWorkloadServiceID) 1850 delete(ctx.ServiceClient.checks, MakeCheckID(outofbandWorkloadServiceID, outofbandWorkload.Services[0].Checks[0])) 1851 1852 require.Len(ctx.ServiceClient.services, 2) 1853 require.Len(ctx.ServiceClient.checks, 2) 1854 1855 // Sync and ensure that explicitly removed service as well as outofbandWorkload were removed 1856 1857 ctx.ServiceClient.RemoveWorkload(explicitlyRemovedWorkload) 1858 require.NoError(ctx.syncOnce(syncNewOps)) 1859 require.NoError(ctx.ServiceClient.sync(syncNewOps)) 1860 require.Len(ctx.FakeConsul.services, 1) 1861 require.Len(ctx.FakeConsul.checks, 1) 1862 1863 require.Contains(ctx.FakeConsul.services, remainingWorkloadServiceID) 1864 require.NotContains(ctx.FakeConsul.services, outofbandWorkloadServiceID) 1865 require.NotContains(ctx.FakeConsul.services, explicitlyRemovedWorkloadServiceID) 1866 1867 require.Contains(ctx.FakeConsul.checks, MakeCheckID(remainingWorkloadServiceID, remainingWorkload.Services[0].Checks[0])) 1868 require.NotContains(ctx.FakeConsul.checks, MakeCheckID(outofbandWorkloadServiceID, outofbandWorkload.Services[0].Checks[0])) 1869 require.NotContains(ctx.FakeConsul.checks, MakeCheckID(explicitlyRemovedWorkloadServiceID, explicitlyRemovedWorkload.Services[0].Checks[0])) 1870 } 1871 1872 // TestConsul_ServiceDeregistration_InProbation asserts that during initialization 1873 // we only deregister services that were explicitly removed and leave unknown 1874 // services untouched. This adds a grace period for restoring recovered tasks 1875 // before deregistering them 1876 func TestConsul_ServiceDeregistration_InProbation(t *testing.T) { 1877 t.Parallel() 1878 ctx := setupFake(t) 1879 require := require.New(t) 1880 1881 ctx.ServiceClient.deregisterProbationExpiry = time.Now().Add(1 * time.Hour) 1882 1883 remainingWorkload := testWorkload() 1884 remainingWorkload.Services = []*structs.Service{ 1885 { 1886 Name: "remaining-service", 1887 PortLabel: "x", 1888 Checks: []*structs.ServiceCheck{ 1889 { 1890 Name: "check", 1891 Type: "tcp", 1892 Interval: time.Second, 1893 Timeout: time.Second, 1894 }, 1895 }, 1896 }, 1897 } 1898 remainingWorkloadServiceID := MakeAllocServiceID(remainingWorkload.AllocID, 1899 remainingWorkload.Name(), remainingWorkload.Services[0]) 1900 1901 require.NoError(ctx.ServiceClient.RegisterWorkload(remainingWorkload)) 1902 require.NoError(ctx.syncOnce(syncNewOps)) 1903 require.Len(ctx.FakeConsul.services, 1) 1904 require.Len(ctx.FakeConsul.checks, 1) 1905 1906 explicitlyRemovedWorkload := testWorkload() 1907 explicitlyRemovedWorkload.Services = []*structs.Service{ 1908 { 1909 Name: "explicitly-removed-service", 1910 PortLabel: "y", 1911 Checks: []*structs.ServiceCheck{ 1912 { 1913 Name: "check", 1914 Type: "tcp", 1915 Interval: time.Second, 1916 Timeout: time.Second, 1917 }, 1918 }, 1919 }, 1920 } 1921 explicitlyRemovedWorkloadServiceID := MakeAllocServiceID(explicitlyRemovedWorkload.AllocID, 1922 explicitlyRemovedWorkload.Name(), explicitlyRemovedWorkload.Services[0]) 1923 1924 require.NoError(ctx.ServiceClient.RegisterWorkload(explicitlyRemovedWorkload)) 1925 1926 require.NoError(ctx.syncOnce(syncNewOps)) 1927 require.Len(ctx.FakeConsul.services, 2) 1928 require.Len(ctx.FakeConsul.checks, 2) 1929 1930 // we register a task through nomad API then remove it out of band 1931 outofbandWorkload := testWorkload() 1932 outofbandWorkload.Services = []*structs.Service{ 1933 { 1934 Name: "unknown-service", 1935 PortLabel: "x", 1936 Checks: []*structs.ServiceCheck{ 1937 { 1938 Name: "check", 1939 Type: "tcp", 1940 Interval: time.Second, 1941 Timeout: time.Second, 1942 }, 1943 }, 1944 }, 1945 } 1946 outofbandWorkloadServiceID := MakeAllocServiceID(outofbandWorkload.AllocID, 1947 outofbandWorkload.Name(), outofbandWorkload.Services[0]) 1948 1949 require.NoError(ctx.ServiceClient.RegisterWorkload(outofbandWorkload)) 1950 require.NoError(ctx.syncOnce(syncNewOps)) 1951 1952 require.Len(ctx.FakeConsul.services, 3) 1953 1954 // remove outofbandWorkload from local services so it appears unknown to client 1955 require.Len(ctx.ServiceClient.services, 3) 1956 require.Len(ctx.ServiceClient.checks, 3) 1957 1958 delete(ctx.ServiceClient.services, outofbandWorkloadServiceID) 1959 delete(ctx.ServiceClient.checks, MakeCheckID(outofbandWorkloadServiceID, outofbandWorkload.Services[0].Checks[0])) 1960 1961 require.Len(ctx.ServiceClient.services, 2) 1962 require.Len(ctx.ServiceClient.checks, 2) 1963 1964 // Sync and ensure that explicitly removed service was removed, but outofbandWorkload remains 1965 1966 ctx.ServiceClient.RemoveWorkload(explicitlyRemovedWorkload) 1967 require.NoError(ctx.syncOnce(syncNewOps)) 1968 require.NoError(ctx.ServiceClient.sync(syncNewOps)) 1969 require.Len(ctx.FakeConsul.services, 2) 1970 require.Len(ctx.FakeConsul.checks, 2) 1971 1972 require.Contains(ctx.FakeConsul.services, remainingWorkloadServiceID) 1973 require.Contains(ctx.FakeConsul.services, outofbandWorkloadServiceID) 1974 require.NotContains(ctx.FakeConsul.services, explicitlyRemovedWorkloadServiceID) 1975 1976 require.Contains(ctx.FakeConsul.checks, MakeCheckID(remainingWorkloadServiceID, remainingWorkload.Services[0].Checks[0])) 1977 require.Contains(ctx.FakeConsul.checks, MakeCheckID(outofbandWorkloadServiceID, outofbandWorkload.Services[0].Checks[0])) 1978 require.NotContains(ctx.FakeConsul.checks, MakeCheckID(explicitlyRemovedWorkloadServiceID, explicitlyRemovedWorkload.Services[0].Checks[0])) 1979 1980 // after probation, outofband services and checks are removed 1981 ctx.ServiceClient.deregisterProbationExpiry = time.Now().Add(-1 * time.Hour) 1982 1983 require.NoError(ctx.ServiceClient.sync(syncNewOps)) 1984 require.Len(ctx.FakeConsul.services, 1) 1985 require.Len(ctx.FakeConsul.checks, 1) 1986 1987 require.Contains(ctx.FakeConsul.services, remainingWorkloadServiceID) 1988 require.NotContains(ctx.FakeConsul.services, outofbandWorkloadServiceID) 1989 require.NotContains(ctx.FakeConsul.services, explicitlyRemovedWorkloadServiceID) 1990 1991 require.Contains(ctx.FakeConsul.checks, MakeCheckID(remainingWorkloadServiceID, remainingWorkload.Services[0].Checks[0])) 1992 require.NotContains(ctx.FakeConsul.checks, MakeCheckID(outofbandWorkloadServiceID, outofbandWorkload.Services[0].Checks[0])) 1993 require.NotContains(ctx.FakeConsul.checks, MakeCheckID(explicitlyRemovedWorkloadServiceID, explicitlyRemovedWorkload.Services[0].Checks[0])) 1994 1995 }