github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/nomad/state/events_test.go (about) 1 package state 2 3 import ( 4 "testing" 5 "time" 6 7 memdb "github.com/hashicorp/go-memdb" 8 "github.com/hashicorp/nomad/ci" 9 "github.com/hashicorp/nomad/helper/pointer" 10 "github.com/hashicorp/nomad/helper/uuid" 11 "github.com/hashicorp/nomad/nomad/mock" 12 "github.com/hashicorp/nomad/nomad/structs" 13 "github.com/shoenig/test/must" 14 "github.com/stretchr/testify/require" 15 ) 16 17 // TestEventFromChange_SingleEventPerTable ensures that only a single event is 18 // created per table per memdb.Change 19 func TestEventFromChange_SingleEventPerTable(t *testing.T) { 20 ci.Parallel(t) 21 s := TestStateStoreCfg(t, TestStateStorePublisher(t)) 22 defer s.StopEventBroker() 23 24 changes := Changes{ 25 Index: 100, 26 MsgType: structs.JobRegisterRequestType, 27 Changes: memdb.Changes{ 28 { 29 Table: "job_version", 30 Before: mock.Job(), 31 After: mock.Job(), 32 }, 33 { 34 Table: "jobs", 35 Before: mock.Job(), 36 After: mock.Job(), 37 }, 38 }, 39 } 40 41 out := eventsFromChanges(s.db.ReadTxn(), changes) 42 require.Len(t, out.Events, 1) 43 require.Equal(t, out.Events[0].Type, structs.TypeJobRegistered) 44 } 45 46 func TestEventFromChange_ACLTokenSecretID(t *testing.T) { 47 ci.Parallel(t) 48 s := TestStateStoreCfg(t, TestStateStorePublisher(t)) 49 defer s.StopEventBroker() 50 51 token := mock.ACLToken() 52 require.NotEmpty(t, token.SecretID) 53 54 // Create 55 changes := Changes{ 56 Index: 100, 57 MsgType: structs.NodeRegisterRequestType, 58 Changes: memdb.Changes{ 59 { 60 Table: "acl_token", 61 Before: nil, 62 After: token, 63 }, 64 }, 65 } 66 67 out := eventsFromChanges(s.db.ReadTxn(), changes) 68 require.Len(t, out.Events, 1) 69 // Ensure original value not altered 70 require.NotEmpty(t, token.SecretID) 71 72 aclTokenEvent, ok := out.Events[0].Payload.(*structs.ACLTokenEvent) 73 require.True(t, ok) 74 require.Empty(t, aclTokenEvent.ACLToken.SecretID) 75 76 require.Equal(t, token.SecretID, aclTokenEvent.SecretID()) 77 78 // Delete 79 changes = Changes{ 80 Index: 100, 81 MsgType: structs.NodeDeregisterRequestType, 82 Changes: memdb.Changes{ 83 { 84 Table: "acl_token", 85 Before: token, 86 After: nil, 87 }, 88 }, 89 } 90 91 out2 := eventsFromChanges(s.db.ReadTxn(), changes) 92 require.Len(t, out2.Events, 1) 93 94 tokenEvent2, ok := out2.Events[0].Payload.(*structs.ACLTokenEvent) 95 require.True(t, ok) 96 require.Empty(t, tokenEvent2.ACLToken.SecretID) 97 } 98 99 func TestEventsFromChanges_DeploymentUpdate(t *testing.T) { 100 ci.Parallel(t) 101 s := TestStateStoreCfg(t, TestStateStorePublisher(t)) 102 defer s.StopEventBroker() 103 104 // setup 105 setupTx := s.db.WriteTxn(10) 106 107 j := mock.Job() 108 e := mock.Eval() 109 e.JobID = j.ID 110 111 d := mock.Deployment() 112 d.JobID = j.ID 113 114 require.NoError(t, s.upsertJobImpl(10, j, false, setupTx)) 115 require.NoError(t, s.upsertDeploymentImpl(10, d, setupTx)) 116 117 setupTx.Txn.Commit() 118 119 msgType := structs.DeploymentStatusUpdateRequestType 120 121 req := &structs.DeploymentStatusUpdateRequest{ 122 DeploymentUpdate: &structs.DeploymentStatusUpdate{ 123 DeploymentID: d.ID, 124 Status: structs.DeploymentStatusPaused, 125 StatusDescription: structs.DeploymentStatusDescriptionPaused, 126 }, 127 Eval: e, 128 // Exlude Job and assert its added 129 } 130 131 require.NoError(t, s.UpdateDeploymentStatus(msgType, 100, req)) 132 133 events := WaitForEvents(t, s, 100, 1, 1*time.Second) 134 require.Len(t, events, 2) 135 136 got := events[0] 137 require.Equal(t, uint64(100), got.Index) 138 require.Equal(t, d.ID, got.Key) 139 140 de := got.Payload.(*structs.DeploymentEvent) 141 require.Equal(t, structs.DeploymentStatusPaused, de.Deployment.Status) 142 require.Contains(t, got.FilterKeys, j.ID) 143 } 144 145 func TestEventsFromChanges_DeploymentPromotion(t *testing.T) { 146 ci.Parallel(t) 147 s := TestStateStoreCfg(t, TestStateStorePublisher(t)) 148 defer s.StopEventBroker() 149 150 // setup 151 setupTx := s.db.WriteTxn(10) 152 153 j := mock.Job() 154 tg1 := j.TaskGroups[0] 155 tg2 := tg1.Copy() 156 tg2.Name = "foo" 157 j.TaskGroups = append(j.TaskGroups, tg2) 158 require.NoError(t, s.upsertJobImpl(10, j, false, setupTx)) 159 160 d := mock.Deployment() 161 d.StatusDescription = structs.DeploymentStatusDescriptionRunningNeedsPromotion 162 d.JobID = j.ID 163 d.TaskGroups = map[string]*structs.DeploymentState{ 164 "web": { 165 DesiredTotal: 10, 166 DesiredCanaries: 1, 167 }, 168 "foo": { 169 DesiredTotal: 10, 170 DesiredCanaries: 1, 171 }, 172 } 173 require.NoError(t, s.upsertDeploymentImpl(10, d, setupTx)) 174 175 // create set of allocs 176 c1 := mock.Alloc() 177 c1.JobID = j.ID 178 c1.DeploymentID = d.ID 179 d.TaskGroups[c1.TaskGroup].PlacedCanaries = append(d.TaskGroups[c1.TaskGroup].PlacedCanaries, c1.ID) 180 c1.DeploymentStatus = &structs.AllocDeploymentStatus{ 181 Healthy: pointer.Of(true), 182 } 183 c2 := mock.Alloc() 184 c2.JobID = j.ID 185 c2.DeploymentID = d.ID 186 d.TaskGroups[c2.TaskGroup].PlacedCanaries = append(d.TaskGroups[c2.TaskGroup].PlacedCanaries, c2.ID) 187 c2.TaskGroup = tg2.Name 188 c2.DeploymentStatus = &structs.AllocDeploymentStatus{ 189 Healthy: pointer.Of(true), 190 } 191 192 require.NoError(t, s.upsertAllocsImpl(10, []*structs.Allocation{c1, c2}, setupTx)) 193 194 // commit setup transaction 195 setupTx.Txn.Commit() 196 197 e := mock.Eval() 198 // Request to promote canaries 199 msgType := structs.DeploymentPromoteRequestType 200 req := &structs.ApplyDeploymentPromoteRequest{ 201 DeploymentPromoteRequest: structs.DeploymentPromoteRequest{ 202 DeploymentID: d.ID, 203 All: true, 204 }, 205 Eval: e, 206 } 207 208 require.NoError(t, s.UpdateDeploymentPromotion(msgType, 100, req)) 209 210 events := WaitForEvents(t, s, 100, 1, 1*time.Second) 211 require.Len(t, events, 4) 212 213 got := events[0] 214 require.Equal(t, uint64(100), got.Index) 215 require.Equal(t, d.ID, got.Key) 216 217 de := got.Payload.(*structs.DeploymentEvent) 218 require.Equal(t, structs.DeploymentStatusRunning, de.Deployment.Status) 219 require.Equal(t, structs.TypeDeploymentPromotion, got.Type) 220 } 221 222 func TestEventsFromChanges_DeploymentAllocHealthRequestType(t *testing.T) { 223 ci.Parallel(t) 224 s := TestStateStoreCfg(t, TestStateStorePublisher(t)) 225 defer s.StopEventBroker() 226 227 // setup 228 setupTx := s.db.WriteTxn(10) 229 230 j := mock.Job() 231 tg1 := j.TaskGroups[0] 232 tg2 := tg1.Copy() 233 tg2.Name = "foo" 234 j.TaskGroups = append(j.TaskGroups, tg2) 235 require.NoError(t, s.upsertJobImpl(10, j, false, setupTx)) 236 237 d := mock.Deployment() 238 d.StatusDescription = structs.DeploymentStatusDescriptionRunningNeedsPromotion 239 d.JobID = j.ID 240 d.TaskGroups = map[string]*structs.DeploymentState{ 241 "web": { 242 DesiredTotal: 10, 243 DesiredCanaries: 1, 244 }, 245 "foo": { 246 DesiredTotal: 10, 247 DesiredCanaries: 1, 248 }, 249 } 250 require.NoError(t, s.upsertDeploymentImpl(10, d, setupTx)) 251 252 // create set of allocs 253 c1 := mock.Alloc() 254 c1.JobID = j.ID 255 c1.DeploymentID = d.ID 256 d.TaskGroups[c1.TaskGroup].PlacedCanaries = append(d.TaskGroups[c1.TaskGroup].PlacedCanaries, c1.ID) 257 c1.DeploymentStatus = &structs.AllocDeploymentStatus{ 258 Healthy: pointer.Of(true), 259 } 260 c2 := mock.Alloc() 261 c2.JobID = j.ID 262 c2.DeploymentID = d.ID 263 d.TaskGroups[c2.TaskGroup].PlacedCanaries = append(d.TaskGroups[c2.TaskGroup].PlacedCanaries, c2.ID) 264 c2.TaskGroup = tg2.Name 265 c2.DeploymentStatus = &structs.AllocDeploymentStatus{ 266 Healthy: pointer.Of(true), 267 } 268 269 require.NoError(t, s.upsertAllocsImpl(10, []*structs.Allocation{c1, c2}, setupTx)) 270 271 // Commit setup 272 setupTx.Commit() 273 274 msgType := structs.DeploymentAllocHealthRequestType 275 276 req := &structs.ApplyDeploymentAllocHealthRequest{ 277 DeploymentAllocHealthRequest: structs.DeploymentAllocHealthRequest{ 278 DeploymentID: d.ID, 279 HealthyAllocationIDs: []string{c1.ID}, 280 UnhealthyAllocationIDs: []string{c2.ID}, 281 }, 282 DeploymentUpdate: &structs.DeploymentStatusUpdate{ 283 DeploymentID: d.ID, 284 }, 285 } 286 287 require.NoError(t, s.UpdateDeploymentAllocHealth(msgType, 100, req)) 288 289 events := WaitForEvents(t, s, 100, 1, 1*time.Second) 290 require.Len(t, events, 3) 291 292 var allocEvents []structs.Event 293 var deploymentEvent []structs.Event 294 for _, e := range events { 295 if e.Topic == structs.TopicAllocation { 296 allocEvents = append(allocEvents, e) 297 } else if e.Topic == structs.TopicDeployment { 298 deploymentEvent = append(deploymentEvent, e) 299 } 300 } 301 302 require.Len(t, allocEvents, 2) 303 for _, e := range allocEvents { 304 require.Equal(t, 100, int(e.Index)) 305 require.Equal(t, structs.TypeDeploymentAllocHealth, e.Type) 306 require.Equal(t, structs.TopicAllocation, e.Topic) 307 } 308 309 require.Len(t, deploymentEvent, 1) 310 for _, e := range deploymentEvent { 311 require.Equal(t, 100, int(e.Index)) 312 require.Equal(t, structs.TypeDeploymentAllocHealth, e.Type) 313 require.Equal(t, structs.TopicDeployment, e.Topic) 314 require.Equal(t, d.ID, e.Key) 315 } 316 } 317 318 func TestEventsFromChanges_UpsertNodeEventsType(t *testing.T) { 319 ci.Parallel(t) 320 s := TestStateStoreCfg(t, TestStateStorePublisher(t)) 321 defer s.StopEventBroker() 322 323 // setup 324 n1 := mock.Node() 325 n2 := mock.Node() 326 327 require.NoError(t, s.UpsertNode(structs.MsgTypeTestSetup, 10, n1)) 328 require.NoError(t, s.UpsertNode(structs.MsgTypeTestSetup, 12, n2)) 329 330 msgType := structs.UpsertNodeEventsType 331 req := &structs.EmitNodeEventsRequest{ 332 NodeEvents: map[string][]*structs.NodeEvent{ 333 n1.ID: { 334 { 335 Message: "update", 336 }, 337 }, 338 n2.ID: { 339 { 340 Message: "update", 341 }, 342 }, 343 }, 344 } 345 346 require.NoError(t, s.UpsertNodeEvents(msgType, 100, req.NodeEvents)) 347 events := WaitForEvents(t, s, 100, 1, 1*time.Second) 348 require.Len(t, events, 2) 349 350 for _, e := range events { 351 require.Equal(t, structs.TopicNode, e.Topic) 352 require.Equal(t, structs.TypeNodeEvent, e.Type) 353 event := e.Payload.(*structs.NodeStreamEvent) 354 require.Equal(t, "update", event.Node.Events[len(event.Node.Events)-1].Message) 355 } 356 357 } 358 359 func TestEventsFromChanges_NodeUpdateStatusRequest(t *testing.T) { 360 ci.Parallel(t) 361 s := TestStateStoreCfg(t, TestStateStorePublisher(t)) 362 defer s.StopEventBroker() 363 364 // setup 365 n1 := mock.Node() 366 367 require.NoError(t, s.UpsertNode(structs.MsgTypeTestSetup, 10, n1)) 368 369 updated := time.Now() 370 msgType := structs.NodeUpdateStatusRequestType 371 req := &structs.NodeUpdateStatusRequest{ 372 NodeID: n1.ID, 373 Status: structs.NodeStatusDown, 374 UpdatedAt: updated.UnixNano(), 375 NodeEvent: &structs.NodeEvent{Message: "down"}, 376 } 377 378 require.NoError(t, s.UpdateNodeStatus(msgType, 100, req.NodeID, req.Status, req.UpdatedAt, req.NodeEvent)) 379 events := WaitForEvents(t, s, 100, 1, 1*time.Second) 380 require.Len(t, events, 1) 381 382 e := events[0] 383 require.Equal(t, structs.TopicNode, e.Topic) 384 require.Equal(t, structs.TypeNodeEvent, e.Type) 385 event := e.Payload.(*structs.NodeStreamEvent) 386 require.Equal(t, "down", event.Node.Events[len(event.Node.Events)-1].Message) 387 require.Equal(t, structs.NodeStatusDown, event.Node.Status) 388 } 389 390 func TestEventsFromChanges_EvalUpdateRequestType(t *testing.T) { 391 ci.Parallel(t) 392 s := TestStateStoreCfg(t, TestStateStorePublisher(t)) 393 defer s.StopEventBroker() 394 395 // setup 396 e1 := mock.Eval() 397 398 require.NoError(t, s.UpsertEvals(structs.MsgTypeTestSetup, 10, []*structs.Evaluation{e1})) 399 400 e2 := mock.Eval() 401 e2.ID = e1.ID 402 e2.JobID = e1.JobID 403 e2.Status = structs.EvalStatusBlocked 404 405 msgType := structs.EvalUpdateRequestType 406 req := &structs.EvalUpdateRequest{ 407 Evals: []*structs.Evaluation{e2}, 408 } 409 410 require.NoError(t, s.UpsertEvals(msgType, 100, req.Evals)) 411 412 events := WaitForEvents(t, s, 100, 1, 1*time.Second) 413 require.Len(t, events, 1) 414 415 e := events[0] 416 require.Equal(t, structs.TopicEvaluation, e.Topic) 417 require.Equal(t, structs.TypeEvalUpdated, e.Type) 418 require.Contains(t, e.FilterKeys, e2.JobID) 419 require.Contains(t, e.FilterKeys, e2.DeploymentID) 420 event := e.Payload.(*structs.EvaluationEvent) 421 require.Equal(t, "blocked", event.Evaluation.Status) 422 } 423 424 func TestEventsFromChanges_ApplyPlanResultsRequestType(t *testing.T) { 425 ci.Parallel(t) 426 s := TestStateStoreCfg(t, TestStateStorePublisher(t)) 427 defer s.StopEventBroker() 428 429 // setup 430 alloc := mock.Alloc() 431 alloc2 := mock.Alloc() 432 job := alloc.Job 433 alloc.Job = nil 434 alloc2.Job = nil 435 436 d := mock.Deployment() 437 alloc.DeploymentID = d.ID 438 alloc2.DeploymentID = d.ID 439 440 require.NoError(t, s.UpsertJob(structs.MsgTypeTestSetup, 9, job)) 441 442 eval := mock.Eval() 443 eval.JobID = job.ID 444 445 // Create an eval 446 require.NoError(t, s.UpsertEvals(structs.MsgTypeTestSetup, 10, []*structs.Evaluation{eval})) 447 448 msgType := structs.ApplyPlanResultsRequestType 449 req := &structs.ApplyPlanResultsRequest{ 450 AllocUpdateRequest: structs.AllocUpdateRequest{ 451 Alloc: []*structs.Allocation{alloc, alloc2}, 452 Job: job, 453 }, 454 Deployment: d, 455 EvalID: eval.ID, 456 } 457 458 require.NoError(t, s.UpsertPlanResults(msgType, 100, req)) 459 460 events := WaitForEvents(t, s, 100, 1, 1*time.Second) 461 require.Len(t, events, 5) 462 463 var allocs []structs.Event 464 var evals []structs.Event 465 var jobs []structs.Event 466 var deploys []structs.Event 467 for _, e := range events { 468 if e.Topic == structs.TopicAllocation { 469 allocs = append(allocs, e) 470 } else if e.Topic == structs.TopicEvaluation { 471 evals = append(evals, e) 472 } else if e.Topic == structs.TopicJob { 473 jobs = append(jobs, e) 474 } else if e.Topic == structs.TopicDeployment { 475 deploys = append(deploys, e) 476 } 477 require.Equal(t, structs.TypePlanResult, e.Type) 478 } 479 require.Len(t, allocs, 2) 480 require.Len(t, evals, 1) 481 require.Len(t, jobs, 1) 482 require.Len(t, deploys, 1) 483 } 484 485 func TestEventsFromChanges_BatchNodeUpdateDrainRequestType(t *testing.T) { 486 ci.Parallel(t) 487 s := TestStateStoreCfg(t, TestStateStorePublisher(t)) 488 defer s.StopEventBroker() 489 490 // setup 491 n1 := mock.Node() 492 n2 := mock.Node() 493 494 require.NoError(t, s.UpsertNode(structs.MsgTypeTestSetup, 10, n1)) 495 require.NoError(t, s.UpsertNode(structs.MsgTypeTestSetup, 11, n2)) 496 497 updated := time.Now() 498 msgType := structs.BatchNodeUpdateDrainRequestType 499 500 expectedDrain := &structs.DrainStrategy{ 501 DrainSpec: structs.DrainSpec{ 502 Deadline: -1 * time.Second, 503 }, 504 } 505 event := &structs.NodeEvent{ 506 Message: "Drain strategy enabled", 507 Subsystem: structs.NodeEventSubsystemDrain, 508 Timestamp: time.Now(), 509 } 510 req := structs.BatchNodeUpdateDrainRequest{ 511 Updates: map[string]*structs.DrainUpdate{ 512 n1.ID: { 513 DrainStrategy: expectedDrain, 514 }, 515 n2.ID: { 516 DrainStrategy: expectedDrain, 517 }, 518 }, 519 NodeEvents: map[string]*structs.NodeEvent{ 520 n1.ID: event, 521 n2.ID: event, 522 }, 523 UpdatedAt: updated.UnixNano(), 524 } 525 526 require.NoError(t, s.BatchUpdateNodeDrain(msgType, 100, req.UpdatedAt, req.Updates, req.NodeEvents)) 527 528 events := WaitForEvents(t, s, 100, 1, 1*time.Second) 529 require.Len(t, events, 2) 530 531 for _, e := range events { 532 require.Equal(t, 100, int(e.Index)) 533 require.Equal(t, structs.TypeNodeDrain, e.Type) 534 require.Equal(t, structs.TopicNode, e.Topic) 535 ne := e.Payload.(*structs.NodeStreamEvent) 536 require.Equal(t, event.Message, ne.Node.Events[len(ne.Node.Events)-1].Message) 537 } 538 } 539 540 func TestEventsFromChanges_NodeUpdateEligibilityRequestType(t *testing.T) { 541 ci.Parallel(t) 542 s := TestStateStoreCfg(t, TestStateStorePublisher(t)) 543 defer s.StopEventBroker() 544 545 // setup 546 n1 := mock.Node() 547 548 require.NoError(t, s.UpsertNode(structs.MsgTypeTestSetup, 10, n1)) 549 550 msgType := structs.NodeUpdateEligibilityRequestType 551 552 event := &structs.NodeEvent{ 553 Message: "Node marked as ineligible", 554 Subsystem: structs.NodeEventSubsystemCluster, 555 Timestamp: time.Now(), 556 } 557 558 req := structs.NodeUpdateEligibilityRequest{ 559 NodeID: n1.ID, 560 NodeEvent: event, 561 Eligibility: structs.NodeSchedulingIneligible, 562 UpdatedAt: time.Now().UnixNano(), 563 } 564 565 require.NoError(t, s.UpdateNodeEligibility(msgType, 100, req.NodeID, req.Eligibility, req.UpdatedAt, req.NodeEvent)) 566 567 events := WaitForEvents(t, s, 100, 1, 1*time.Second) 568 require.Len(t, events, 1) 569 570 for _, e := range events { 571 require.Equal(t, 100, int(e.Index)) 572 require.Equal(t, structs.TypeNodeDrain, e.Type) 573 require.Equal(t, structs.TopicNode, e.Topic) 574 ne := e.Payload.(*structs.NodeStreamEvent) 575 require.Equal(t, event.Message, ne.Node.Events[len(ne.Node.Events)-1].Message) 576 require.Equal(t, structs.NodeSchedulingIneligible, ne.Node.SchedulingEligibility) 577 } 578 } 579 580 func TestEventsFromChanges_AllocUpdateDesiredTransitionRequestType(t *testing.T) { 581 ci.Parallel(t) 582 s := TestStateStoreCfg(t, TestStateStorePublisher(t)) 583 defer s.StopEventBroker() 584 585 alloc := mock.Alloc() 586 587 require.Nil(t, s.UpsertJob(structs.MsgTypeTestSetup, 10, alloc.Job)) 588 require.Nil(t, s.UpsertAllocs(structs.MsgTypeTestSetup, 11, []*structs.Allocation{alloc})) 589 590 msgType := structs.AllocUpdateDesiredTransitionRequestType 591 592 eval := &structs.Evaluation{ 593 ID: uuid.Generate(), 594 Namespace: alloc.Namespace, 595 Priority: alloc.Job.Priority, 596 Type: alloc.Job.Type, 597 TriggeredBy: structs.EvalTriggerNodeDrain, 598 JobID: alloc.Job.ID, 599 JobModifyIndex: alloc.Job.ModifyIndex, 600 Status: structs.EvalStatusPending, 601 } 602 evals := []*structs.Evaluation{eval} 603 604 req := &structs.AllocUpdateDesiredTransitionRequest{ 605 Allocs: map[string]*structs.DesiredTransition{ 606 alloc.ID: {Migrate: pointer.Of(true)}, 607 }, 608 Evals: evals, 609 } 610 611 require.NoError(t, s.UpdateAllocsDesiredTransitions(msgType, 100, req.Allocs, req.Evals)) 612 613 events := WaitForEvents(t, s, 100, 1, 1*time.Second) 614 require.Len(t, events, 2) 615 616 var allocs []structs.Event 617 var evalEvents []structs.Event 618 for _, e := range events { 619 if e.Topic == structs.TopicEvaluation { 620 evalEvents = append(evalEvents, e) 621 } else if e.Topic == structs.TopicAllocation { 622 allocs = append(allocs, e) 623 } else { 624 require.Fail(t, "unexpected event type") 625 } 626 627 require.Equal(t, structs.TypeAllocationUpdateDesiredStatus, e.Type) 628 } 629 630 require.Len(t, allocs, 1) 631 require.Len(t, evalEvents, 1) 632 } 633 634 func TestEventsFromChanges_JobBatchDeregisterRequestType(t *testing.T) { 635 // TODO Job batch deregister logic mostly occurs in the FSM 636 t.SkipNow() 637 638 } 639 func TestEventsFromChanges_AllocClientUpdateRequestType(t *testing.T) { 640 t.SkipNow() 641 } 642 643 func TestEventsFromChanges_JobDeregisterRequestType(t *testing.T) { 644 t.SkipNow() 645 } 646 647 func TestEventsFromChanges_WithDeletion(t *testing.T) { 648 ci.Parallel(t) 649 650 changes := Changes{ 651 Index: uint64(1), 652 Changes: memdb.Changes{ 653 { 654 Table: "jobs", 655 Before: &structs.Job{}, 656 After: &structs.Job{}, 657 }, 658 { 659 Table: "jobs", 660 Before: &structs.Job{}, 661 After: nil, // deleted 662 }, 663 }, 664 MsgType: structs.JobDeregisterRequestType, 665 } 666 667 event := eventsFromChanges(nil, changes) 668 require.NotNil(t, event) 669 670 require.Len(t, event.Events, 1) 671 } 672 673 func TestEventsFromChanges_WithNodeDeregistration(t *testing.T) { 674 ci.Parallel(t) 675 676 before := &structs.Node{ 677 ID: "some-id", 678 Datacenter: "some-datacenter", 679 } 680 681 changes := Changes{ 682 Index: uint64(1), 683 Changes: memdb.Changes{ 684 { 685 Table: "nodes", 686 Before: before, 687 After: nil, // deleted 688 }, 689 }, 690 MsgType: structs.NodeDeregisterRequestType, 691 } 692 693 actual := eventsFromChanges(nil, changes) 694 require.NotNil(t, actual) 695 696 require.Len(t, actual.Events, 1) 697 698 event := actual.Events[0] 699 700 require.Equal(t, structs.TypeNodeDeregistration, event.Type) 701 require.Equal(t, uint64(1), event.Index) 702 require.Equal(t, structs.TopicNode, event.Topic) 703 require.Equal(t, "some-id", event.Key) 704 705 require.Len(t, event.FilterKeys, 0) 706 707 nodeEvent, ok := event.Payload.(*structs.NodeStreamEvent) 708 require.True(t, ok) 709 require.Equal(t, *before, *nodeEvent.Node) 710 } 711 712 func TestNodeEventsFromChanges(t *testing.T) { 713 ci.Parallel(t) 714 715 cases := []struct { 716 Name string 717 MsgType structs.MessageType 718 Setup func(s *StateStore, tx *txn) error 719 Mutate func(s *StateStore, tx *txn) error 720 WantEvents []structs.Event 721 WantTopic structs.Topic 722 }{ 723 { 724 MsgType: structs.NodeRegisterRequestType, 725 WantTopic: structs.TopicNode, 726 Name: "node registered", 727 Mutate: func(s *StateStore, tx *txn) error { 728 return upsertNodeTxn(tx, tx.Index, testNode()) 729 }, 730 WantEvents: []structs.Event{{ 731 Topic: structs.TopicNode, 732 Type: structs.TypeNodeRegistration, 733 Key: testNodeID(), 734 Index: 100, 735 Payload: &structs.NodeStreamEvent{ 736 Node: testNode(), 737 }, 738 }}, 739 }, 740 { 741 MsgType: structs.NodeRegisterRequestType, 742 WantTopic: structs.TopicNode, 743 Name: "node registered initializing", 744 Mutate: func(s *StateStore, tx *txn) error { 745 return upsertNodeTxn(tx, tx.Index, testNode(nodeNotReady)) 746 }, 747 WantEvents: []structs.Event{{ 748 Topic: structs.TopicNode, 749 Type: structs.TypeNodeRegistration, 750 Key: testNodeID(), 751 Index: 100, 752 Payload: &structs.NodeStreamEvent{ 753 Node: testNode(nodeNotReady), 754 }, 755 }}, 756 }, 757 { 758 MsgType: structs.NodeDeregisterRequestType, 759 WantTopic: structs.TopicNode, 760 Name: "node deregistered", 761 Setup: func(s *StateStore, tx *txn) error { 762 return upsertNodeTxn(tx, tx.Index, testNode()) 763 }, 764 Mutate: func(s *StateStore, tx *txn) error { 765 return deleteNodeTxn(tx, tx.Index, []string{testNodeID()}) 766 }, 767 WantEvents: []structs.Event{{ 768 Topic: structs.TopicNode, 769 Type: structs.TypeNodeDeregistration, 770 Key: testNodeID(), 771 Index: 100, 772 Payload: &structs.NodeStreamEvent{ 773 Node: testNode(), 774 }, 775 }}, 776 }, 777 { 778 MsgType: structs.NodeDeregisterRequestType, 779 WantTopic: structs.TopicNode, 780 Name: "batch node deregistered", 781 Setup: func(s *StateStore, tx *txn) error { 782 require.NoError(t, upsertNodeTxn(tx, tx.Index, testNode())) 783 return upsertNodeTxn(tx, tx.Index, testNode(nodeIDTwo)) 784 }, 785 Mutate: func(s *StateStore, tx *txn) error { 786 return deleteNodeTxn(tx, tx.Index, []string{testNodeID(), testNodeIDTwo()}) 787 }, 788 WantEvents: []structs.Event{ 789 { 790 Topic: structs.TopicNode, 791 Type: structs.TypeNodeDeregistration, 792 Key: testNodeID(), 793 Index: 100, 794 Payload: &structs.NodeStreamEvent{ 795 Node: testNode(), 796 }, 797 }, 798 { 799 Topic: structs.TopicNode, 800 Type: structs.TypeNodeDeregistration, 801 Key: testNodeIDTwo(), 802 Index: 100, 803 Payload: &structs.NodeStreamEvent{ 804 Node: testNode(nodeIDTwo), 805 }, 806 }, 807 }, 808 }, 809 { 810 MsgType: structs.UpsertNodeEventsType, 811 WantTopic: structs.TopicNode, 812 Name: "batch node events upserted", 813 Setup: func(s *StateStore, tx *txn) error { 814 require.NoError(t, upsertNodeTxn(tx, tx.Index, testNode())) 815 return upsertNodeTxn(tx, tx.Index, testNode(nodeIDTwo)) 816 }, 817 Mutate: func(s *StateStore, tx *txn) error { 818 eventFn := func(id string) []*structs.NodeEvent { 819 return []*structs.NodeEvent{ 820 { 821 Message: "test event one", 822 Subsystem: "Cluster", 823 Details: map[string]string{ 824 "NodeID": id, 825 }, 826 }, 827 { 828 Message: "test event two", 829 Subsystem: "Cluster", 830 Details: map[string]string{ 831 "NodeID": id, 832 }, 833 }, 834 } 835 } 836 require.NoError(t, s.upsertNodeEvents(tx.Index, testNodeID(), eventFn(testNodeID()), tx)) 837 return s.upsertNodeEvents(tx.Index, testNodeIDTwo(), eventFn(testNodeIDTwo()), tx) 838 }, 839 WantEvents: []structs.Event{ 840 { 841 Topic: structs.TopicNode, 842 Type: structs.TypeNodeEvent, 843 Key: testNodeID(), 844 Index: 100, 845 Payload: &structs.NodeStreamEvent{ 846 Node: testNode(), 847 }, 848 }, 849 { 850 Topic: structs.TopicNode, 851 Type: structs.TypeNodeEvent, 852 Key: testNodeIDTwo(), 853 Index: 100, 854 Payload: &structs.NodeStreamEvent{ 855 Node: testNode(nodeIDTwo), 856 }, 857 }, 858 }, 859 }, 860 } 861 862 for _, tc := range cases { 863 t.Run(tc.Name, func(t *testing.T) { 864 s := TestStateStoreCfg(t, TestStateStorePublisher(t)) 865 defer s.StopEventBroker() 866 867 if tc.Setup != nil { 868 // Bypass publish mechanism for setup 869 setupTx := s.db.WriteTxn(10) 870 require.NoError(t, tc.Setup(s, setupTx)) 871 setupTx.Txn.Commit() 872 } 873 874 tx := s.db.WriteTxnMsgT(tc.MsgType, 100) 875 require.NoError(t, tc.Mutate(s, tx)) 876 877 changes := Changes{Changes: tx.Changes(), Index: 100, MsgType: tc.MsgType} 878 got := eventsFromChanges(tx, changes) 879 880 require.NotNil(t, got) 881 882 require.Equal(t, len(tc.WantEvents), len(got.Events)) 883 for idx, g := range got.Events { 884 // assert equality of shared fields 885 886 want := tc.WantEvents[idx] 887 require.Equal(t, want.Index, g.Index) 888 require.Equal(t, want.Key, g.Key) 889 require.Equal(t, want.Topic, g.Topic) 890 891 switch tc.MsgType { 892 case structs.NodeRegisterRequestType: 893 requireNodeRegistrationEventEqual(t, tc.WantEvents[idx], g) 894 case structs.NodeDeregisterRequestType: 895 requireNodeDeregistrationEventEqual(t, tc.WantEvents[idx], g) 896 case structs.UpsertNodeEventsType: 897 requireNodeEventEqual(t, tc.WantEvents[idx], g) 898 default: 899 require.Fail(t, "unhandled message type") 900 } 901 } 902 }) 903 } 904 } 905 906 func TestNodeDrainEventFromChanges(t *testing.T) { 907 ci.Parallel(t) 908 s := TestStateStoreCfg(t, TestStateStorePublisher(t)) 909 defer s.StopEventBroker() 910 911 // setup 912 setupTx := s.db.WriteTxn(10) 913 914 node := mock.Node() 915 alloc1 := mock.Alloc() 916 alloc2 := mock.Alloc() 917 alloc1.NodeID = node.ID 918 alloc2.NodeID = node.ID 919 920 require.NoError(t, upsertNodeTxn(setupTx, 10, node)) 921 require.NoError(t, s.upsertAllocsImpl(100, []*structs.Allocation{alloc1, alloc2}, setupTx)) 922 setupTx.Txn.Commit() 923 924 // changes 925 tx := s.db.WriteTxn(100) 926 927 strat := &structs.DrainStrategy{ 928 DrainSpec: structs.DrainSpec{ 929 Deadline: 10 * time.Minute, 930 IgnoreSystemJobs: false, 931 }, 932 StartedAt: time.Now(), 933 } 934 markEligible := false 935 updatedAt := time.Now() 936 event := &structs.NodeEvent{} 937 938 require.NoError(t, s.updateNodeDrainImpl(tx, 100, node.ID, strat, markEligible, updatedAt.UnixNano(), event, nil, "", false)) 939 changes := Changes{Changes: tx.Changes(), Index: 100, MsgType: structs.NodeUpdateDrainRequestType} 940 got := eventsFromChanges(tx, changes) 941 942 require.Len(t, got.Events, 1) 943 944 require.Equal(t, structs.TopicNode, got.Events[0].Topic) 945 require.Equal(t, structs.TypeNodeDrain, got.Events[0].Type) 946 require.Equal(t, uint64(100), got.Events[0].Index) 947 948 nodeEvent, ok := got.Events[0].Payload.(*structs.NodeStreamEvent) 949 require.True(t, ok) 950 951 require.Equal(t, structs.NodeSchedulingIneligible, nodeEvent.Node.SchedulingEligibility) 952 require.Equal(t, strat, nodeEvent.Node.DrainStrategy) 953 } 954 955 func Test_eventsFromChanges_ServiceRegistration(t *testing.T) { 956 ci.Parallel(t) 957 testState := TestStateStoreCfg(t, TestStateStorePublisher(t)) 958 defer testState.StopEventBroker() 959 960 // Generate test service registration. 961 service := mock.ServiceRegistrations()[0] 962 963 // Upsert a service registration. 964 writeTxn := testState.db.WriteTxn(10) 965 updated, err := testState.upsertServiceRegistrationTxn(10, writeTxn, service) 966 require.True(t, updated) 967 require.NoError(t, err) 968 writeTxn.Txn.Commit() 969 970 // Pull the events from the stream. 971 registerChange := Changes{Changes: writeTxn.Changes(), Index: 10, MsgType: structs.ServiceRegistrationUpsertRequestType} 972 receivedChange := eventsFromChanges(writeTxn, registerChange) 973 974 // Check the event, and it's payload are what we are expecting. 975 require.Len(t, receivedChange.Events, 1) 976 require.Equal(t, structs.TopicService, receivedChange.Events[0].Topic) 977 require.Equal(t, structs.TypeServiceRegistration, receivedChange.Events[0].Type) 978 require.Equal(t, uint64(10), receivedChange.Events[0].Index) 979 980 eventPayload := receivedChange.Events[0].Payload.(*structs.ServiceRegistrationStreamEvent) 981 require.Equal(t, service, eventPayload.Service) 982 983 // Delete the previously upserted service registration. 984 deleteTxn := testState.db.WriteTxn(20) 985 require.NoError(t, testState.deleteServiceRegistrationByIDTxn(uint64(20), deleteTxn, service.Namespace, service.ID)) 986 writeTxn.Txn.Commit() 987 988 // Pull the events from the stream. 989 deregisterChange := Changes{Changes: deleteTxn.Changes(), Index: 20, MsgType: structs.ServiceRegistrationDeleteByIDRequestType} 990 receivedDeleteChange := eventsFromChanges(deleteTxn, deregisterChange) 991 992 // Check the event, and it's payload are what we are expecting. 993 require.Len(t, receivedDeleteChange.Events, 1) 994 require.Equal(t, structs.TopicService, receivedDeleteChange.Events[0].Topic) 995 require.Equal(t, structs.TypeServiceDeregistration, receivedDeleteChange.Events[0].Type) 996 require.Equal(t, uint64(20), receivedDeleteChange.Events[0].Index) 997 998 eventPayload = receivedChange.Events[0].Payload.(*structs.ServiceRegistrationStreamEvent) 999 require.Equal(t, service, eventPayload.Service) 1000 } 1001 1002 func Test_eventsFromChanges_ACLRole(t *testing.T) { 1003 ci.Parallel(t) 1004 testState := TestStateStoreCfg(t, TestStateStorePublisher(t)) 1005 defer testState.StopEventBroker() 1006 1007 // Generate a test ACL role. 1008 aclRole := mock.ACLRole() 1009 1010 // Upsert the role into state, skipping the checks perform to ensure the 1011 // linked policies exist. 1012 writeTxn := testState.db.WriteTxn(10) 1013 updated, err := testState.upsertACLRoleTxn(10, writeTxn, aclRole, true) 1014 require.True(t, updated) 1015 require.NoError(t, err) 1016 writeTxn.Txn.Commit() 1017 1018 // Pull the events from the stream. 1019 upsertChange := Changes{Changes: writeTxn.Changes(), Index: 10, MsgType: structs.ACLRolesUpsertRequestType} 1020 receivedChange := eventsFromChanges(writeTxn, upsertChange) 1021 1022 // Check the event, and it's payload are what we are expecting. 1023 require.Len(t, receivedChange.Events, 1) 1024 require.Equal(t, structs.TopicACLRole, receivedChange.Events[0].Topic) 1025 require.Equal(t, aclRole.ID, receivedChange.Events[0].Key) 1026 require.Equal(t, aclRole.Name, receivedChange.Events[0].FilterKeys[0]) 1027 require.Equal(t, structs.TypeACLRoleUpserted, receivedChange.Events[0].Type) 1028 require.Equal(t, uint64(10), receivedChange.Events[0].Index) 1029 1030 eventPayload := receivedChange.Events[0].Payload.(*structs.ACLRoleStreamEvent) 1031 require.Equal(t, aclRole, eventPayload.ACLRole) 1032 1033 // Delete the previously upserted ACL role. 1034 deleteTxn := testState.db.WriteTxn(20) 1035 require.NoError(t, testState.deleteACLRoleByIDTxn(deleteTxn, aclRole.ID)) 1036 require.NoError(t, deleteTxn.Insert(tableIndex, &IndexEntry{TableACLRoles, 20})) 1037 deleteTxn.Txn.Commit() 1038 1039 // Pull the events from the stream. 1040 deleteChange := Changes{Changes: deleteTxn.Changes(), Index: 20, MsgType: structs.ACLRolesDeleteByIDRequestType} 1041 receivedDeleteChange := eventsFromChanges(deleteTxn, deleteChange) 1042 1043 // Check the event, and it's payload are what we are expecting. 1044 require.Len(t, receivedDeleteChange.Events, 1) 1045 require.Equal(t, structs.TopicACLRole, receivedDeleteChange.Events[0].Topic) 1046 require.Equal(t, aclRole.ID, receivedDeleteChange.Events[0].Key) 1047 require.Equal(t, aclRole.Name, receivedDeleteChange.Events[0].FilterKeys[0]) 1048 require.Equal(t, structs.TypeACLRoleDeleted, receivedDeleteChange.Events[0].Type) 1049 require.Equal(t, uint64(20), receivedDeleteChange.Events[0].Index) 1050 1051 eventPayload = receivedChange.Events[0].Payload.(*structs.ACLRoleStreamEvent) 1052 require.Equal(t, aclRole, eventPayload.ACLRole) 1053 } 1054 1055 func Test_eventsFromChanges_ACLAuthMethod(t *testing.T) { 1056 ci.Parallel(t) 1057 testState := TestStateStoreCfg(t, TestStateStorePublisher(t)) 1058 defer testState.StopEventBroker() 1059 1060 // Generate a test ACL auth method 1061 authMethod := mock.ACLAuthMethod() 1062 1063 // Upsert the auth method straight into state 1064 writeTxn := testState.db.WriteTxn(10) 1065 updated, err := testState.upsertACLAuthMethodTxn(10, writeTxn, authMethod) 1066 must.True(t, updated) 1067 must.NoError(t, err) 1068 writeTxn.Txn.Commit() 1069 1070 // Pull the events from the stream. 1071 upsertChange := Changes{Changes: writeTxn.Changes(), Index: 10, MsgType: structs.ACLAuthMethodsUpsertRequestType} 1072 receivedChange := eventsFromChanges(writeTxn, upsertChange) 1073 must.NotNil(t, receivedChange) 1074 1075 // Check the event, and its payload are what we are expecting. 1076 must.Len(t, 1, receivedChange.Events) 1077 must.Eq(t, structs.TopicACLAuthMethod, receivedChange.Events[0].Topic) 1078 must.Eq(t, authMethod.Name, receivedChange.Events[0].Key) 1079 must.Eq(t, structs.TypeACLAuthMethodUpserted, receivedChange.Events[0].Type) 1080 must.Eq(t, uint64(10), receivedChange.Events[0].Index) 1081 1082 eventPayload := receivedChange.Events[0].Payload.(*structs.ACLAuthMethodEvent) 1083 must.Eq(t, authMethod, eventPayload.AuthMethod) 1084 1085 // Delete the previously upserted auth method 1086 deleteTxn := testState.db.WriteTxn(20) 1087 must.NoError(t, testState.deleteACLAuthMethodTxn(deleteTxn, authMethod.Name)) 1088 must.NoError(t, deleteTxn.Insert(tableIndex, &IndexEntry{TableACLAuthMethods, 20})) 1089 deleteTxn.Txn.Commit() 1090 1091 // Pull the events from the stream. 1092 deleteChange := Changes{Changes: deleteTxn.Changes(), Index: 20, MsgType: structs.ACLAuthMethodsDeleteRequestType} 1093 receivedDeleteChange := eventsFromChanges(deleteTxn, deleteChange) 1094 must.NotNil(t, receivedDeleteChange) 1095 1096 // Check the event, and its payload are what we are expecting. 1097 must.Len(t, 1, receivedDeleteChange.Events) 1098 must.Eq(t, structs.TopicACLAuthMethod, receivedDeleteChange.Events[0].Topic) 1099 must.Eq(t, authMethod.Name, receivedDeleteChange.Events[0].Key) 1100 must.Eq(t, structs.TypeACLAuthMethodDeleted, receivedDeleteChange.Events[0].Type) 1101 must.Eq(t, uint64(20), receivedDeleteChange.Events[0].Index) 1102 1103 eventPayload = receivedChange.Events[0].Payload.(*structs.ACLAuthMethodEvent) 1104 must.Eq(t, authMethod, eventPayload.AuthMethod) 1105 } 1106 1107 func Test_eventsFromChanges_ACLBindingRule(t *testing.T) { 1108 ci.Parallel(t) 1109 testState := TestStateStoreCfg(t, TestStateStorePublisher(t)) 1110 defer testState.StopEventBroker() 1111 1112 // Generate a test ACL binding rule. 1113 bindingRule := mock.ACLBindingRule() 1114 1115 // Upsert the binding rule straight into state. 1116 writeTxn := testState.db.WriteTxn(10) 1117 updated, err := testState.upsertACLBindingRuleTxn(10, writeTxn, bindingRule, true) 1118 must.True(t, updated) 1119 must.NoError(t, err) 1120 writeTxn.Txn.Commit() 1121 1122 // Pull the events from the stream. 1123 upsertChange := Changes{Changes: writeTxn.Changes(), Index: 10, MsgType: structs.ACLBindingRulesUpsertRequestType} 1124 receivedChange := eventsFromChanges(writeTxn, upsertChange) 1125 must.NotNil(t, receivedChange) 1126 1127 // Check the event, and its payload are what we are expecting. 1128 must.Len(t, 1, receivedChange.Events) 1129 must.Eq(t, structs.TopicACLBindingRule, receivedChange.Events[0].Topic) 1130 must.Eq(t, bindingRule.ID, receivedChange.Events[0].Key) 1131 must.SliceContainsAll(t, []string{bindingRule.AuthMethod}, receivedChange.Events[0].FilterKeys) 1132 must.Eq(t, structs.TypeACLBindingRuleUpserted, receivedChange.Events[0].Type) 1133 must.Eq(t, 10, receivedChange.Events[0].Index) 1134 1135 must.Eq(t, bindingRule, receivedChange.Events[0].Payload.(*structs.ACLBindingRuleEvent).ACLBindingRule) 1136 1137 // Delete the previously upserted binding rule. 1138 deleteTxn := testState.db.WriteTxn(20) 1139 must.NoError(t, testState.deleteACLBindingRuleTxn(deleteTxn, bindingRule.ID)) 1140 must.NoError(t, deleteTxn.Insert(tableIndex, &IndexEntry{TableACLBindingRules, 20})) 1141 deleteTxn.Txn.Commit() 1142 1143 // Pull the events from the stream. 1144 deleteChange := Changes{Changes: deleteTxn.Changes(), Index: 20, MsgType: structs.ACLBindingRulesDeleteRequestType} 1145 receivedDeleteChange := eventsFromChanges(deleteTxn, deleteChange) 1146 must.NotNil(t, receivedDeleteChange) 1147 1148 // Check the event, and its payload are what we are expecting. 1149 must.Len(t, 1, receivedDeleteChange.Events) 1150 must.Eq(t, structs.TopicACLBindingRule, receivedDeleteChange.Events[0].Topic) 1151 must.Eq(t, bindingRule.ID, receivedDeleteChange.Events[0].Key) 1152 must.SliceContainsAll(t, []string{bindingRule.AuthMethod}, receivedDeleteChange.Events[0].FilterKeys) 1153 must.Eq(t, structs.TypeACLBindingRuleDeleted, receivedDeleteChange.Events[0].Type) 1154 must.Eq(t, uint64(20), receivedDeleteChange.Events[0].Index) 1155 1156 must.Eq(t, bindingRule, receivedDeleteChange.Events[0].Payload.(*structs.ACLBindingRuleEvent).ACLBindingRule) 1157 } 1158 1159 func requireNodeRegistrationEventEqual(t *testing.T, want, got structs.Event) { 1160 t.Helper() 1161 1162 wantPayload := want.Payload.(*structs.NodeStreamEvent) 1163 gotPayload := got.Payload.(*structs.NodeStreamEvent) 1164 1165 // Check payload equality for the fields that we can easily control 1166 require.Equal(t, wantPayload.Node.Status, gotPayload.Node.Status) 1167 require.Equal(t, wantPayload.Node.ID, gotPayload.Node.ID) 1168 require.NotEqual(t, wantPayload.Node.Events, gotPayload.Node.Events) 1169 } 1170 1171 func requireNodeDeregistrationEventEqual(t *testing.T, want, got structs.Event) { 1172 t.Helper() 1173 1174 wantPayload := want.Payload.(*structs.NodeStreamEvent) 1175 gotPayload := got.Payload.(*structs.NodeStreamEvent) 1176 1177 require.Equal(t, wantPayload.Node.ID, gotPayload.Node.ID) 1178 require.NotEqual(t, wantPayload.Node.Events, gotPayload.Node.Events) 1179 } 1180 1181 func requireNodeEventEqual(t *testing.T, want, got structs.Event) { 1182 gotPayload := got.Payload.(*structs.NodeStreamEvent) 1183 1184 require.Len(t, gotPayload.Node.Events, 3) 1185 } 1186 1187 type nodeOpts func(n *structs.Node) 1188 1189 func nodeNotReady(n *structs.Node) { 1190 n.Status = structs.NodeStatusInit 1191 } 1192 1193 func nodeIDTwo(n *structs.Node) { 1194 n.ID = testNodeIDTwo() 1195 } 1196 1197 func testNode(opts ...nodeOpts) *structs.Node { 1198 n := mock.Node() 1199 n.ID = testNodeID() 1200 1201 n.SecretID = "ab9812d3-6a21-40d3-973d-d9d2174a23ee" 1202 1203 for _, opt := range opts { 1204 opt(n) 1205 } 1206 return n 1207 } 1208 1209 func testNodeID() string { 1210 return "9d5741c1-3899-498a-98dd-eb3c05665863" 1211 } 1212 1213 func testNodeIDTwo() string { 1214 return "694ff31d-8c59-4030-ac83-e15692560c8d" 1215 }