github.imxd.top/hashicorp/consul@v1.4.5/agent/consul/state/txn_test.go (about) 1 package state 2 3 import ( 4 "fmt" 5 "reflect" 6 "strings" 7 "testing" 8 9 "github.com/hashicorp/consul/agent/structs" 10 "github.com/hashicorp/consul/api" 11 "github.com/hashicorp/consul/types" 12 "github.com/pascaldekloe/goe/verify" 13 "github.com/stretchr/testify/require" 14 ) 15 16 func TestStateStore_Txn_Intention(t *testing.T) { 17 require := require.New(t) 18 s := testStateStore(t) 19 20 // Create some intentions. 21 ixn1 := &structs.Intention{ 22 ID: testUUID(), 23 SourceNS: "default", 24 SourceName: "web", 25 DestinationNS: "default", 26 DestinationName: "db", 27 Meta: map[string]string{}, 28 } 29 ixn2 := &structs.Intention{ 30 ID: testUUID(), 31 SourceNS: "default", 32 SourceName: "db", 33 DestinationNS: "default", 34 DestinationName: "*", 35 Action: structs.IntentionActionDeny, 36 Meta: map[string]string{}, 37 } 38 ixn3 := &structs.Intention{ 39 ID: testUUID(), 40 SourceNS: "default", 41 SourceName: "foo", 42 DestinationNS: "default", 43 DestinationName: "*", 44 Meta: map[string]string{}, 45 } 46 47 // Write the first two to the state store, leave the third 48 // to be created by the transaction operation. 49 require.NoError(s.IntentionSet(1, ixn1)) 50 require.NoError(s.IntentionSet(2, ixn2)) 51 52 // Set up a transaction that hits every operation. 53 ops := structs.TxnOps{ 54 &structs.TxnOp{ 55 Intention: &structs.TxnIntentionOp{ 56 Op: structs.IntentionOpUpdate, 57 Intention: ixn1, 58 }, 59 }, 60 &structs.TxnOp{ 61 Intention: &structs.TxnIntentionOp{ 62 Op: structs.IntentionOpDelete, 63 Intention: ixn2, 64 }, 65 }, 66 &structs.TxnOp{ 67 Intention: &structs.TxnIntentionOp{ 68 Op: structs.IntentionOpCreate, 69 Intention: ixn3, 70 }, 71 }, 72 } 73 results, errors := s.TxnRW(3, ops) 74 if len(errors) > 0 { 75 t.Fatalf("err: %v", errors) 76 } 77 78 // Make sure the response looks as expected. 79 expected := structs.TxnResults{} 80 verify.Values(t, "", results, expected) 81 82 // Pull the resulting state store contents. 83 idx, actual, err := s.Intentions(nil) 84 require.NoError(err) 85 if idx != 3 { 86 t.Fatalf("bad index: %d", idx) 87 } 88 89 // Make sure it looks as expected. 90 intentions := structs.Intentions{ 91 &structs.Intention{ 92 ID: ixn1.ID, 93 SourceNS: "default", 94 SourceName: "web", 95 DestinationNS: "default", 96 DestinationName: "db", 97 Meta: map[string]string{}, 98 Precedence: 9, 99 RaftIndex: structs.RaftIndex{ 100 CreateIndex: 1, 101 ModifyIndex: 3, 102 }, 103 }, 104 &structs.Intention{ 105 ID: ixn3.ID, 106 SourceNS: "default", 107 SourceName: "foo", 108 DestinationNS: "default", 109 DestinationName: "*", 110 Meta: map[string]string{}, 111 Precedence: 6, 112 RaftIndex: structs.RaftIndex{ 113 CreateIndex: 3, 114 ModifyIndex: 3, 115 }, 116 }, 117 } 118 verify.Values(t, "", actual, intentions) 119 } 120 121 func TestStateStore_Txn_Node(t *testing.T) { 122 require := require.New(t) 123 s := testStateStore(t) 124 125 // Create some nodes. 126 var nodes [5]structs.Node 127 for i := 0; i < len(nodes); i++ { 128 nodes[i] = structs.Node{ 129 Node: fmt.Sprintf("node%d", i+1), 130 ID: types.NodeID(testUUID()), 131 } 132 133 // Leave node5 to be created by an operation. 134 if i < 5 { 135 s.EnsureNode(uint64(i+1), &nodes[i]) 136 } 137 } 138 139 // Set up a transaction that hits every operation. 140 ops := structs.TxnOps{ 141 &structs.TxnOp{ 142 Node: &structs.TxnNodeOp{ 143 Verb: api.NodeGet, 144 Node: nodes[0], 145 }, 146 }, 147 &structs.TxnOp{ 148 Node: &structs.TxnNodeOp{ 149 Verb: api.NodeSet, 150 Node: nodes[4], 151 }, 152 }, 153 &structs.TxnOp{ 154 Node: &structs.TxnNodeOp{ 155 Verb: api.NodeCAS, 156 Node: structs.Node{ 157 Node: "node2", 158 ID: nodes[1].ID, 159 Datacenter: "dc2", 160 RaftIndex: structs.RaftIndex{ModifyIndex: 2}, 161 }, 162 }, 163 }, 164 &structs.TxnOp{ 165 Node: &structs.TxnNodeOp{ 166 Verb: api.NodeDelete, 167 Node: structs.Node{Node: "node3"}, 168 }, 169 }, 170 &structs.TxnOp{ 171 Node: &structs.TxnNodeOp{ 172 Verb: api.NodeDeleteCAS, 173 Node: structs.Node{ 174 Node: "node4", 175 RaftIndex: structs.RaftIndex{ModifyIndex: 4}, 176 }, 177 }, 178 }, 179 } 180 results, errors := s.TxnRW(8, ops) 181 if len(errors) > 0 { 182 t.Fatalf("err: %v", errors) 183 } 184 185 // Make sure the response looks as expected. 186 nodes[1].Datacenter = "dc2" 187 nodes[1].ModifyIndex = 8 188 expected := structs.TxnResults{ 189 &structs.TxnResult{ 190 Node: &nodes[0], 191 }, 192 &structs.TxnResult{ 193 Node: &nodes[4], 194 }, 195 &structs.TxnResult{ 196 Node: &nodes[1], 197 }, 198 } 199 verify.Values(t, "", results, expected) 200 201 // Pull the resulting state store contents. 202 idx, actual, err := s.Nodes(nil) 203 require.NoError(err) 204 if idx != 8 { 205 t.Fatalf("bad index: %d", idx) 206 } 207 208 // Make sure it looks as expected. 209 expectedNodes := structs.Nodes{&nodes[0], &nodes[1], &nodes[4]} 210 verify.Values(t, "", actual, expectedNodes) 211 } 212 213 func TestStateStore_Txn_Service(t *testing.T) { 214 require := require.New(t) 215 s := testStateStore(t) 216 217 testRegisterNode(t, s, 1, "node1") 218 219 // Create some services. 220 for i := 1; i <= 4; i++ { 221 testRegisterService(t, s, uint64(i+1), "node1", fmt.Sprintf("svc%d", i)) 222 } 223 224 // Set up a transaction that hits every operation. 225 ops := structs.TxnOps{ 226 &structs.TxnOp{ 227 Service: &structs.TxnServiceOp{ 228 Verb: api.ServiceGet, 229 Node: "node1", 230 Service: structs.NodeService{ID: "svc1"}, 231 }, 232 }, 233 &structs.TxnOp{ 234 Service: &structs.TxnServiceOp{ 235 Verb: api.ServiceSet, 236 Node: "node1", 237 Service: structs.NodeService{ID: "svc5"}, 238 }, 239 }, 240 &structs.TxnOp{ 241 Service: &structs.TxnServiceOp{ 242 Verb: api.ServiceCAS, 243 Node: "node1", 244 Service: structs.NodeService{ 245 ID: "svc2", 246 Tags: []string{"modified"}, 247 RaftIndex: structs.RaftIndex{ModifyIndex: 3}, 248 }, 249 }, 250 }, 251 &structs.TxnOp{ 252 Service: &structs.TxnServiceOp{ 253 Verb: api.ServiceDelete, 254 Node: "node1", 255 Service: structs.NodeService{ID: "svc3"}, 256 }, 257 }, 258 &structs.TxnOp{ 259 Service: &structs.TxnServiceOp{ 260 Verb: api.ServiceDeleteCAS, 261 Node: "node1", 262 Service: structs.NodeService{ 263 ID: "svc4", 264 RaftIndex: structs.RaftIndex{ModifyIndex: 5}, 265 }, 266 }, 267 }, 268 } 269 results, errors := s.TxnRW(6, ops) 270 if len(errors) > 0 { 271 t.Fatalf("err: %v", errors) 272 } 273 274 // Make sure the response looks as expected. 275 expected := structs.TxnResults{ 276 &structs.TxnResult{ 277 Service: &structs.NodeService{ 278 ID: "svc1", 279 Service: "svc1", 280 Address: "1.1.1.1", 281 Port: 1111, 282 Weights: &structs.Weights{Passing: 1, Warning: 1}, 283 RaftIndex: structs.RaftIndex{ 284 CreateIndex: 2, 285 ModifyIndex: 2, 286 }, 287 }, 288 }, 289 &structs.TxnResult{ 290 Service: &structs.NodeService{ 291 ID: "svc5", 292 Weights: &structs.Weights{Passing: 1, Warning: 1}, 293 RaftIndex: structs.RaftIndex{ 294 CreateIndex: 6, 295 ModifyIndex: 6, 296 }, 297 }, 298 }, 299 &structs.TxnResult{ 300 Service: &structs.NodeService{ 301 ID: "svc2", 302 Tags: []string{"modified"}, 303 Weights: &structs.Weights{Passing: 1, Warning: 1}, 304 RaftIndex: structs.RaftIndex{ 305 CreateIndex: 3, 306 ModifyIndex: 6, 307 }, 308 }, 309 }, 310 } 311 verify.Values(t, "", results, expected) 312 313 // Pull the resulting state store contents. 314 idx, actual, err := s.NodeServices(nil, "node1") 315 require.NoError(err) 316 if idx != 6 { 317 t.Fatalf("bad index: %d", idx) 318 } 319 320 // Make sure it looks as expected. 321 expectedServices := &structs.NodeServices{ 322 Node: &structs.Node{ 323 Node: "node1", 324 RaftIndex: structs.RaftIndex{ 325 CreateIndex: 1, 326 ModifyIndex: 1, 327 }, 328 }, 329 Services: map[string]*structs.NodeService{ 330 "svc1": &structs.NodeService{ 331 ID: "svc1", 332 Service: "svc1", 333 Address: "1.1.1.1", 334 Port: 1111, 335 RaftIndex: structs.RaftIndex{ 336 CreateIndex: 2, 337 ModifyIndex: 2, 338 }, 339 Weights: &structs.Weights{Passing: 1, Warning: 1}, 340 }, 341 "svc5": &structs.NodeService{ 342 ID: "svc5", 343 RaftIndex: structs.RaftIndex{ 344 CreateIndex: 6, 345 ModifyIndex: 6, 346 }, 347 Weights: &structs.Weights{Passing: 1, Warning: 1}, 348 }, 349 "svc2": &structs.NodeService{ 350 ID: "svc2", 351 Tags: []string{"modified"}, 352 RaftIndex: structs.RaftIndex{ 353 CreateIndex: 3, 354 ModifyIndex: 6, 355 }, 356 Weights: &structs.Weights{Passing: 1, Warning: 1}, 357 }, 358 }, 359 } 360 verify.Values(t, "", actual, expectedServices) 361 } 362 363 func TestStateStore_Txn_Checks(t *testing.T) { 364 require := require.New(t) 365 s := testStateStore(t) 366 367 testRegisterNode(t, s, 1, "node1") 368 369 // Create some checks. 370 for i := 1; i <= 4; i++ { 371 testRegisterCheck(t, s, uint64(i+1), "node1", "", types.CheckID(fmt.Sprintf("check%d", i)), "failing") 372 } 373 374 // Set up a transaction that hits every operation. 375 ops := structs.TxnOps{ 376 &structs.TxnOp{ 377 Check: &structs.TxnCheckOp{ 378 Verb: api.CheckGet, 379 Check: structs.HealthCheck{Node: "node1", CheckID: "check1"}, 380 }, 381 }, 382 &structs.TxnOp{ 383 Check: &structs.TxnCheckOp{ 384 Verb: api.CheckSet, 385 Check: structs.HealthCheck{Node: "node1", CheckID: "check5", Status: "passing"}, 386 }, 387 }, 388 &structs.TxnOp{ 389 Check: &structs.TxnCheckOp{ 390 Verb: api.CheckCAS, 391 Check: structs.HealthCheck{ 392 Node: "node1", 393 CheckID: "check2", 394 Status: "warning", 395 RaftIndex: structs.RaftIndex{ModifyIndex: 3}, 396 }, 397 }, 398 }, 399 &structs.TxnOp{ 400 Check: &structs.TxnCheckOp{ 401 Verb: api.CheckDelete, 402 Check: structs.HealthCheck{Node: "node1", CheckID: "check3"}, 403 }, 404 }, 405 &structs.TxnOp{ 406 Check: &structs.TxnCheckOp{ 407 Verb: api.CheckDeleteCAS, 408 Check: structs.HealthCheck{ 409 Node: "node1", 410 CheckID: "check4", 411 RaftIndex: structs.RaftIndex{ModifyIndex: 5}, 412 }, 413 }, 414 }, 415 } 416 results, errors := s.TxnRW(6, ops) 417 if len(errors) > 0 { 418 t.Fatalf("err: %v", errors) 419 } 420 421 // Make sure the response looks as expected. 422 expected := structs.TxnResults{ 423 &structs.TxnResult{ 424 Check: &structs.HealthCheck{ 425 Node: "node1", 426 CheckID: "check1", 427 Status: "failing", 428 RaftIndex: structs.RaftIndex{ 429 CreateIndex: 2, 430 ModifyIndex: 2, 431 }, 432 }, 433 }, 434 &structs.TxnResult{ 435 Check: &structs.HealthCheck{ 436 Node: "node1", 437 CheckID: "check5", 438 Status: "passing", 439 RaftIndex: structs.RaftIndex{ 440 CreateIndex: 6, 441 ModifyIndex: 6, 442 }, 443 }, 444 }, 445 &structs.TxnResult{ 446 Check: &structs.HealthCheck{ 447 Node: "node1", 448 CheckID: "check2", 449 Status: "warning", 450 RaftIndex: structs.RaftIndex{ 451 CreateIndex: 3, 452 ModifyIndex: 6, 453 }, 454 }, 455 }, 456 } 457 verify.Values(t, "", results, expected) 458 459 // Pull the resulting state store contents. 460 idx, actual, err := s.NodeChecks(nil, "node1") 461 require.NoError(err) 462 if idx != 6 { 463 t.Fatalf("bad index: %d", idx) 464 } 465 466 // Make sure it looks as expected. 467 expectedChecks := structs.HealthChecks{ 468 &structs.HealthCheck{ 469 Node: "node1", 470 CheckID: "check1", 471 Status: "failing", 472 RaftIndex: structs.RaftIndex{ 473 CreateIndex: 2, 474 ModifyIndex: 2, 475 }, 476 }, 477 &structs.HealthCheck{ 478 Node: "node1", 479 CheckID: "check2", 480 Status: "warning", 481 RaftIndex: structs.RaftIndex{ 482 CreateIndex: 3, 483 ModifyIndex: 6, 484 }, 485 }, 486 &structs.HealthCheck{ 487 Node: "node1", 488 CheckID: "check5", 489 Status: "passing", 490 RaftIndex: structs.RaftIndex{ 491 CreateIndex: 6, 492 ModifyIndex: 6, 493 }, 494 }, 495 } 496 verify.Values(t, "", actual, expectedChecks) 497 } 498 499 func TestStateStore_Txn_KVS(t *testing.T) { 500 s := testStateStore(t) 501 502 // Create KV entries in the state store. 503 testSetKey(t, s, 1, "foo/delete", "bar") 504 testSetKey(t, s, 2, "foo/bar/baz", "baz") 505 testSetKey(t, s, 3, "foo/bar/zip", "zip") 506 testSetKey(t, s, 4, "foo/zorp", "zorp") 507 testSetKey(t, s, 5, "foo/update", "stale") 508 509 // Make a real session. 510 testRegisterNode(t, s, 6, "node1") 511 session := testUUID() 512 if err := s.SessionCreate(7, &structs.Session{ID: session, Node: "node1"}); err != nil { 513 t.Fatalf("err: %s", err) 514 } 515 516 // Set up a transaction that hits every operation. 517 ops := structs.TxnOps{ 518 &structs.TxnOp{ 519 KV: &structs.TxnKVOp{ 520 Verb: api.KVGetTree, 521 DirEnt: structs.DirEntry{ 522 Key: "foo/bar", 523 }, 524 }, 525 }, 526 &structs.TxnOp{ 527 KV: &structs.TxnKVOp{ 528 Verb: api.KVSet, 529 DirEnt: structs.DirEntry{ 530 Key: "foo/new", 531 Value: []byte("one"), 532 }, 533 }, 534 }, 535 &structs.TxnOp{ 536 KV: &structs.TxnKVOp{ 537 Verb: api.KVDelete, 538 DirEnt: structs.DirEntry{ 539 Key: "foo/zorp", 540 }, 541 }, 542 }, 543 &structs.TxnOp{ 544 KV: &structs.TxnKVOp{ 545 Verb: api.KVDeleteCAS, 546 DirEnt: structs.DirEntry{ 547 Key: "foo/delete", 548 RaftIndex: structs.RaftIndex{ 549 ModifyIndex: 1, 550 }, 551 }, 552 }, 553 }, 554 &structs.TxnOp{ 555 KV: &structs.TxnKVOp{ 556 Verb: api.KVDeleteTree, 557 DirEnt: structs.DirEntry{ 558 Key: "foo/bar", 559 }, 560 }, 561 }, 562 &structs.TxnOp{ 563 KV: &structs.TxnKVOp{ 564 Verb: api.KVGet, 565 DirEnt: structs.DirEntry{ 566 Key: "foo/update", 567 }, 568 }, 569 }, 570 &structs.TxnOp{ 571 KV: &structs.TxnKVOp{ 572 Verb: api.KVCheckIndex, 573 DirEnt: structs.DirEntry{ 574 Key: "foo/update", 575 RaftIndex: structs.RaftIndex{ 576 ModifyIndex: 5, 577 }, 578 }, 579 }, 580 }, 581 &structs.TxnOp{ 582 KV: &structs.TxnKVOp{ 583 Verb: api.KVCAS, 584 DirEnt: structs.DirEntry{ 585 Key: "foo/update", 586 Value: []byte("new"), 587 RaftIndex: structs.RaftIndex{ 588 ModifyIndex: 5, 589 }, 590 }, 591 }, 592 }, 593 &structs.TxnOp{ 594 KV: &structs.TxnKVOp{ 595 Verb: api.KVGet, 596 DirEnt: structs.DirEntry{ 597 Key: "foo/update", 598 }, 599 }, 600 }, 601 &structs.TxnOp{ 602 KV: &structs.TxnKVOp{ 603 Verb: api.KVCheckIndex, 604 DirEnt: structs.DirEntry{ 605 Key: "foo/update", 606 RaftIndex: structs.RaftIndex{ 607 ModifyIndex: 8, 608 }, 609 }, 610 }, 611 }, 612 &structs.TxnOp{ 613 KV: &structs.TxnKVOp{ 614 Verb: api.KVLock, 615 DirEnt: structs.DirEntry{ 616 Key: "foo/lock", 617 Session: session, 618 }, 619 }, 620 }, 621 &structs.TxnOp{ 622 KV: &structs.TxnKVOp{ 623 Verb: api.KVCheckSession, 624 DirEnt: structs.DirEntry{ 625 Key: "foo/lock", 626 Session: session, 627 }, 628 }, 629 }, 630 &structs.TxnOp{ 631 KV: &structs.TxnKVOp{ 632 Verb: api.KVUnlock, 633 DirEnt: structs.DirEntry{ 634 Key: "foo/lock", 635 Session: session, 636 }, 637 }, 638 }, 639 &structs.TxnOp{ 640 KV: &structs.TxnKVOp{ 641 Verb: api.KVCheckSession, 642 DirEnt: structs.DirEntry{ 643 Key: "foo/lock", 644 Session: "", 645 }, 646 }, 647 }, 648 } 649 results, errors := s.TxnRW(8, ops) 650 if len(errors) > 0 { 651 t.Fatalf("err: %v", errors) 652 } 653 654 // Make sure the response looks as expected. 655 expected := structs.TxnResults{ 656 &structs.TxnResult{ 657 KV: &structs.DirEntry{ 658 Key: "foo/bar/baz", 659 Value: []byte("baz"), 660 RaftIndex: structs.RaftIndex{ 661 CreateIndex: 2, 662 ModifyIndex: 2, 663 }, 664 }, 665 }, 666 &structs.TxnResult{ 667 KV: &structs.DirEntry{ 668 Key: "foo/bar/zip", 669 Value: []byte("zip"), 670 RaftIndex: structs.RaftIndex{ 671 CreateIndex: 3, 672 ModifyIndex: 3, 673 }, 674 }, 675 }, 676 &structs.TxnResult{ 677 KV: &structs.DirEntry{ 678 Key: "foo/new", 679 RaftIndex: structs.RaftIndex{ 680 CreateIndex: 8, 681 ModifyIndex: 8, 682 }, 683 }, 684 }, 685 &structs.TxnResult{ 686 KV: &structs.DirEntry{ 687 Key: "foo/update", 688 Value: []byte("stale"), 689 RaftIndex: structs.RaftIndex{ 690 CreateIndex: 5, 691 ModifyIndex: 5, 692 }, 693 }, 694 }, 695 &structs.TxnResult{ 696 KV: &structs.DirEntry{ 697 698 Key: "foo/update", 699 RaftIndex: structs.RaftIndex{ 700 CreateIndex: 5, 701 ModifyIndex: 5, 702 }, 703 }, 704 }, 705 &structs.TxnResult{ 706 KV: &structs.DirEntry{ 707 Key: "foo/update", 708 RaftIndex: structs.RaftIndex{ 709 CreateIndex: 5, 710 ModifyIndex: 8, 711 }, 712 }, 713 }, 714 &structs.TxnResult{ 715 KV: &structs.DirEntry{ 716 Key: "foo/update", 717 Value: []byte("new"), 718 RaftIndex: structs.RaftIndex{ 719 CreateIndex: 5, 720 ModifyIndex: 8, 721 }, 722 }, 723 }, 724 &structs.TxnResult{ 725 KV: &structs.DirEntry{ 726 Key: "foo/update", 727 RaftIndex: structs.RaftIndex{ 728 CreateIndex: 5, 729 ModifyIndex: 8, 730 }, 731 }, 732 }, 733 &structs.TxnResult{ 734 KV: &structs.DirEntry{ 735 Key: "foo/lock", 736 Session: session, 737 LockIndex: 1, 738 RaftIndex: structs.RaftIndex{ 739 CreateIndex: 8, 740 ModifyIndex: 8, 741 }, 742 }, 743 }, 744 &structs.TxnResult{ 745 KV: &structs.DirEntry{ 746 Key: "foo/lock", 747 Session: session, 748 LockIndex: 1, 749 RaftIndex: structs.RaftIndex{ 750 CreateIndex: 8, 751 ModifyIndex: 8, 752 }, 753 }, 754 }, 755 &structs.TxnResult{ 756 KV: &structs.DirEntry{ 757 Key: "foo/lock", 758 LockIndex: 1, 759 RaftIndex: structs.RaftIndex{ 760 CreateIndex: 8, 761 ModifyIndex: 8, 762 }, 763 }, 764 }, 765 &structs.TxnResult{ 766 KV: &structs.DirEntry{ 767 Key: "foo/lock", 768 LockIndex: 1, 769 RaftIndex: structs.RaftIndex{ 770 CreateIndex: 8, 771 ModifyIndex: 8, 772 }, 773 }, 774 }, 775 } 776 if len(results) != len(expected) { 777 t.Fatalf("bad: %v", results) 778 } 779 for i := range results { 780 if !reflect.DeepEqual(results[i], expected[i]) { 781 t.Fatalf("bad %d", i) 782 } 783 } 784 785 // Pull the resulting state store contents. 786 idx, actual, err := s.KVSList(nil, "") 787 if err != nil { 788 t.Fatalf("err: %s", err) 789 } 790 if idx != 8 { 791 t.Fatalf("bad index: %d", idx) 792 } 793 794 // Make sure it looks as expected. 795 entries := structs.DirEntries{ 796 &structs.DirEntry{ 797 Key: "foo/lock", 798 LockIndex: 1, 799 RaftIndex: structs.RaftIndex{ 800 CreateIndex: 8, 801 ModifyIndex: 8, 802 }, 803 }, 804 &structs.DirEntry{ 805 Key: "foo/new", 806 Value: []byte("one"), 807 RaftIndex: structs.RaftIndex{ 808 CreateIndex: 8, 809 ModifyIndex: 8, 810 }, 811 }, 812 &structs.DirEntry{ 813 Key: "foo/update", 814 Value: []byte("new"), 815 RaftIndex: structs.RaftIndex{ 816 CreateIndex: 5, 817 ModifyIndex: 8, 818 }, 819 }, 820 } 821 if len(actual) != len(entries) { 822 t.Fatalf("bad len: %d != %d", len(actual), len(entries)) 823 } 824 for i := range actual { 825 if !reflect.DeepEqual(actual[i], entries[i]) { 826 t.Fatalf("bad %d", i) 827 } 828 } 829 } 830 831 func TestStateStore_Txn_KVS_Rollback(t *testing.T) { 832 s := testStateStore(t) 833 834 // Create KV entries in the state store. 835 testSetKey(t, s, 1, "foo/delete", "bar") 836 testSetKey(t, s, 2, "foo/update", "stale") 837 838 testRegisterNode(t, s, 3, "node1") 839 session := testUUID() 840 if err := s.SessionCreate(4, &structs.Session{ID: session, Node: "node1"}); err != nil { 841 t.Fatalf("err: %s", err) 842 } 843 ok, err := s.KVSLock(5, &structs.DirEntry{Key: "foo/lock", Value: []byte("foo"), Session: session}) 844 if !ok || err != nil { 845 t.Fatalf("didn't get the lock: %v %s", ok, err) 846 } 847 848 bogus := testUUID() 849 if err := s.SessionCreate(6, &structs.Session{ID: bogus, Node: "node1"}); err != nil { 850 t.Fatalf("err: %s", err) 851 } 852 853 // This function verifies that the state store wasn't changed. 854 verifyStateStore := func(desc string) { 855 idx, actual, err := s.KVSList(nil, "") 856 if err != nil { 857 t.Fatalf("err (%s): %s", desc, err) 858 } 859 if idx != 5 { 860 t.Fatalf("bad index (%s): %d", desc, idx) 861 } 862 863 // Make sure it looks as expected. 864 entries := structs.DirEntries{ 865 &structs.DirEntry{ 866 Key: "foo/delete", 867 Value: []byte("bar"), 868 RaftIndex: structs.RaftIndex{ 869 CreateIndex: 1, 870 ModifyIndex: 1, 871 }, 872 }, 873 &structs.DirEntry{ 874 Key: "foo/lock", 875 Value: []byte("foo"), 876 LockIndex: 1, 877 Session: session, 878 RaftIndex: structs.RaftIndex{ 879 CreateIndex: 5, 880 ModifyIndex: 5, 881 }, 882 }, 883 &structs.DirEntry{ 884 Key: "foo/update", 885 Value: []byte("stale"), 886 RaftIndex: structs.RaftIndex{ 887 CreateIndex: 2, 888 ModifyIndex: 2, 889 }, 890 }, 891 } 892 if len(actual) != len(entries) { 893 t.Fatalf("bad len (%s): %d != %d", desc, len(actual), len(entries)) 894 } 895 for i := range actual { 896 if !reflect.DeepEqual(actual[i], entries[i]) { 897 t.Fatalf("bad (%s): op %d: %v != %v", desc, i, *(actual[i]), *(entries[i])) 898 } 899 } 900 } 901 verifyStateStore("initial") 902 903 // Set up a transaction that fails every operation. 904 ops := structs.TxnOps{ 905 &structs.TxnOp{ 906 KV: &structs.TxnKVOp{ 907 Verb: api.KVCAS, 908 DirEnt: structs.DirEntry{ 909 Key: "foo/update", 910 Value: []byte("new"), 911 RaftIndex: structs.RaftIndex{ 912 ModifyIndex: 1, 913 }, 914 }, 915 }, 916 }, 917 &structs.TxnOp{ 918 KV: &structs.TxnKVOp{ 919 Verb: api.KVLock, 920 DirEnt: structs.DirEntry{ 921 Key: "foo/lock", 922 Session: bogus, 923 }, 924 }, 925 }, 926 &structs.TxnOp{ 927 KV: &structs.TxnKVOp{ 928 Verb: api.KVUnlock, 929 DirEnt: structs.DirEntry{ 930 Key: "foo/lock", 931 Session: bogus, 932 }, 933 }, 934 }, 935 &structs.TxnOp{ 936 KV: &structs.TxnKVOp{ 937 Verb: api.KVCheckSession, 938 DirEnt: structs.DirEntry{ 939 Key: "foo/lock", 940 Session: bogus, 941 }, 942 }, 943 }, 944 &structs.TxnOp{ 945 KV: &structs.TxnKVOp{ 946 Verb: api.KVGet, 947 DirEnt: structs.DirEntry{ 948 Key: "nope", 949 }, 950 }, 951 }, 952 &structs.TxnOp{ 953 KV: &structs.TxnKVOp{ 954 Verb: api.KVCheckSession, 955 DirEnt: structs.DirEntry{ 956 Key: "nope", 957 Session: bogus, 958 }, 959 }, 960 }, 961 &structs.TxnOp{ 962 KV: &structs.TxnKVOp{ 963 Verb: api.KVCheckIndex, 964 DirEnt: structs.DirEntry{ 965 Key: "foo/lock", 966 RaftIndex: structs.RaftIndex{ 967 ModifyIndex: 6, 968 }, 969 }, 970 }, 971 }, 972 &structs.TxnOp{ 973 KV: &structs.TxnKVOp{ 974 Verb: api.KVCheckIndex, 975 DirEnt: structs.DirEntry{ 976 Key: "nope", 977 RaftIndex: structs.RaftIndex{ 978 ModifyIndex: 6, 979 }, 980 }, 981 }, 982 }, 983 &structs.TxnOp{ 984 KV: &structs.TxnKVOp{ 985 Verb: "nope", 986 DirEnt: structs.DirEntry{ 987 Key: "foo/delete", 988 }, 989 }, 990 }, 991 } 992 results, errors := s.TxnRW(7, ops) 993 if len(errors) != len(ops) { 994 t.Fatalf("bad len: %d != %d", len(errors), len(ops)) 995 } 996 if len(results) != 0 { 997 t.Fatalf("bad len: %d != 0", len(results)) 998 } 999 verifyStateStore("after") 1000 1001 // Make sure the errors look reasonable. 1002 expected := []string{ 1003 "index is stale", 1004 "lock is already held", 1005 "lock isn't held, or is held by another session", 1006 "current session", 1007 `key "nope" doesn't exist`, 1008 `key "nope" doesn't exist`, 1009 "current modify index", 1010 `key "nope" doesn't exist`, 1011 "unknown KV verb", 1012 } 1013 if len(errors) != len(expected) { 1014 t.Fatalf("bad len: %d != %d", len(errors), len(expected)) 1015 } 1016 for i, msg := range expected { 1017 if errors[i].OpIndex != i { 1018 t.Fatalf("bad index: %d != %d", i, errors[i].OpIndex) 1019 } 1020 if !strings.Contains(errors[i].Error(), msg) { 1021 t.Fatalf("bad %d: %v", i, errors[i].Error()) 1022 } 1023 } 1024 } 1025 1026 func TestStateStore_Txn_KVS_RO(t *testing.T) { 1027 s := testStateStore(t) 1028 1029 // Create KV entries in the state store. 1030 testSetKey(t, s, 1, "foo", "bar") 1031 testSetKey(t, s, 2, "foo/bar/baz", "baz") 1032 testSetKey(t, s, 3, "foo/bar/zip", "zip") 1033 1034 // Set up a transaction that hits all the read-only operations. 1035 ops := structs.TxnOps{ 1036 &structs.TxnOp{ 1037 KV: &structs.TxnKVOp{ 1038 Verb: api.KVGetTree, 1039 DirEnt: structs.DirEntry{ 1040 Key: "foo/bar", 1041 }, 1042 }, 1043 }, 1044 &structs.TxnOp{ 1045 KV: &structs.TxnKVOp{ 1046 Verb: api.KVGet, 1047 DirEnt: structs.DirEntry{ 1048 Key: "foo", 1049 }, 1050 }, 1051 }, 1052 &structs.TxnOp{ 1053 KV: &structs.TxnKVOp{ 1054 Verb: api.KVCheckSession, 1055 DirEnt: structs.DirEntry{ 1056 Key: "foo/bar/baz", 1057 Session: "", 1058 }, 1059 }, 1060 }, 1061 &structs.TxnOp{ 1062 KV: &structs.TxnKVOp{ 1063 Verb: api.KVCheckSession, 1064 DirEnt: structs.DirEntry{ 1065 Key: "foo/bar/zip", 1066 RaftIndex: structs.RaftIndex{ 1067 ModifyIndex: 3, 1068 }, 1069 }, 1070 }, 1071 }, 1072 } 1073 results, errors := s.TxnRO(ops) 1074 if len(errors) > 0 { 1075 t.Fatalf("err: %v", errors) 1076 } 1077 1078 // Make sure the response looks as expected. 1079 expected := structs.TxnResults{ 1080 &structs.TxnResult{ 1081 KV: &structs.DirEntry{ 1082 Key: "foo/bar/baz", 1083 Value: []byte("baz"), 1084 RaftIndex: structs.RaftIndex{ 1085 CreateIndex: 2, 1086 ModifyIndex: 2, 1087 }, 1088 }, 1089 }, 1090 &structs.TxnResult{ 1091 KV: &structs.DirEntry{ 1092 Key: "foo/bar/zip", 1093 Value: []byte("zip"), 1094 RaftIndex: structs.RaftIndex{ 1095 CreateIndex: 3, 1096 ModifyIndex: 3, 1097 }, 1098 }, 1099 }, 1100 &structs.TxnResult{ 1101 KV: &structs.DirEntry{ 1102 Key: "foo", 1103 Value: []byte("bar"), 1104 RaftIndex: structs.RaftIndex{ 1105 CreateIndex: 1, 1106 ModifyIndex: 1, 1107 }, 1108 }, 1109 }, 1110 &structs.TxnResult{ 1111 KV: &structs.DirEntry{ 1112 Key: "foo/bar/baz", 1113 RaftIndex: structs.RaftIndex{ 1114 CreateIndex: 2, 1115 ModifyIndex: 2, 1116 }, 1117 }, 1118 }, 1119 &structs.TxnResult{ 1120 KV: &structs.DirEntry{ 1121 Key: "foo/bar/zip", 1122 RaftIndex: structs.RaftIndex{ 1123 CreateIndex: 3, 1124 ModifyIndex: 3, 1125 }, 1126 }, 1127 }, 1128 } 1129 if len(results) != len(expected) { 1130 t.Fatalf("bad: %v", results) 1131 } 1132 for i := range results { 1133 if !reflect.DeepEqual(results[i], expected[i]) { 1134 t.Fatalf("bad %d", i) 1135 } 1136 } 1137 } 1138 1139 func TestStateStore_Txn_KVS_RO_Safety(t *testing.T) { 1140 s := testStateStore(t) 1141 1142 // Create KV entries in the state store. 1143 testSetKey(t, s, 1, "foo", "bar") 1144 testSetKey(t, s, 2, "foo/bar/baz", "baz") 1145 testSetKey(t, s, 3, "foo/bar/zip", "zip") 1146 1147 // Set up a transaction that hits all the read-only operations. 1148 ops := structs.TxnOps{ 1149 &structs.TxnOp{ 1150 KV: &structs.TxnKVOp{ 1151 Verb: api.KVSet, 1152 DirEnt: structs.DirEntry{ 1153 Key: "foo", 1154 Value: []byte("nope"), 1155 }, 1156 }, 1157 }, 1158 &structs.TxnOp{ 1159 KV: &structs.TxnKVOp{ 1160 Verb: api.KVDelete, 1161 DirEnt: structs.DirEntry{ 1162 Key: "foo/bar/baz", 1163 }, 1164 }, 1165 }, 1166 &structs.TxnOp{ 1167 KV: &structs.TxnKVOp{ 1168 Verb: api.KVDeleteTree, 1169 DirEnt: structs.DirEntry{ 1170 Key: "foo/bar", 1171 }, 1172 }, 1173 }, 1174 } 1175 results, errors := s.TxnRO(ops) 1176 if len(results) > 0 { 1177 t.Fatalf("bad: %v", results) 1178 } 1179 if len(errors) != len(ops) { 1180 t.Fatalf("bad len: %d != %d", len(errors), len(ops)) 1181 } 1182 1183 // Make sure the errors look reasonable (tombstone inserts cause the 1184 // insert errors during the delete operations). 1185 expected := []string{ 1186 "cannot insert in read-only transaction", 1187 "cannot insert in read-only transaction", 1188 "failed recursive deleting kvs entry", 1189 } 1190 if len(errors) != len(expected) { 1191 t.Fatalf("bad len: %d != %d", len(errors), len(expected)) 1192 } 1193 for i, msg := range expected { 1194 if errors[i].OpIndex != i { 1195 t.Fatalf("bad index: %d != %d", i, errors[i].OpIndex) 1196 } 1197 if !strings.Contains(errors[i].Error(), msg) { 1198 t.Fatalf("bad %d: %v", i, errors[i].Error()) 1199 } 1200 } 1201 }