github.com/hernad/nomad@v1.6.112/nomad/state/state_store_variables_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package state 5 6 import ( 7 "encoding/json" 8 "sort" 9 "strings" 10 "testing" 11 12 memdb "github.com/hashicorp/go-memdb" 13 "github.com/shoenig/test/must" 14 15 "github.com/hernad/nomad/ci" 16 "github.com/hernad/nomad/helper/uuid" 17 "github.com/hernad/nomad/nomad/mock" 18 "github.com/hernad/nomad/nomad/structs" 19 ) 20 21 func TestStateStore_GetVariable(t *testing.T) { 22 ci.Parallel(t) 23 testState := testStateStore(t) 24 ws := memdb.NewWatchSet() 25 sve, err := testState.GetVariable(ws, "default", "not/a/path") 26 must.NoError(t, err) 27 must.Nil(t, sve) 28 } 29 30 func TestStateStore_UpsertVariables(t *testing.T) { 31 ci.Parallel(t) 32 testState := testStateStore(t) 33 ws := memdb.NewWatchSet() 34 35 svs := []*structs.VariableEncrypted{ 36 mock.VariableEncrypted(), 37 mock.VariableEncrypted(), 38 } 39 svs[0].Path = "aaaaa" 40 svs[1].Path = "bbbbb" 41 42 insertIndex := uint64(20) 43 44 var expectedQuotaSize int64 45 for _, v := range svs { 46 expectedQuotaSize += int64(len(v.Data)) 47 } 48 49 // Ensure new variables are inserted as expected with their 50 // correct indexes, along with an update to the index table. 51 t.Run("1 create new variables", func(t *testing.T) { 52 // Perform the initial upsert of variables. 53 for _, sv := range svs { 54 insertIndex++ 55 resp := testState.VarSet(insertIndex, &structs.VarApplyStateRequest{ 56 Op: structs.VarOpSet, 57 Var: sv, 58 }) 59 must.NoError(t, resp.Error) 60 } 61 62 // Check that the index for the table was modified as expected. 63 initialIndex, err := testState.Index(TableVariables) 64 must.NoError(t, err) 65 must.Eq(t, insertIndex, initialIndex) 66 67 // List all the variables in the table 68 iter, err := testState.Variables(ws) 69 must.NoError(t, err) 70 71 got := []*structs.VariableEncrypted{} 72 for raw := iter.Next(); raw != nil; raw = iter.Next() { 73 sv := raw.(*structs.VariableEncrypted) 74 var svCopy structs.VariableEncrypted 75 svCopy = sv.Copy() 76 got = append(got, &svCopy) 77 } 78 must.Len(t, 2, got, must.Sprintf("incorrect number of variables found")) 79 80 // Ensure the create and modify indexes are populated correctly. 81 must.Eq(t, 21, got[0].CreateIndex, must.Sprintf("%s: incorrect create index", got[0].Path)) 82 must.Eq(t, 21, got[0].ModifyIndex, must.Sprintf("%s: incorrect modify index", got[0].Path)) 83 must.Eq(t, 22, got[1].CreateIndex, must.Sprintf("%s: incorrect create index", got[1].Path)) 84 must.Eq(t, 22, got[1].ModifyIndex, must.Sprintf("%s: incorrect modify index", got[1].Path)) 85 86 quotaUsed, err := testState.VariablesQuotaByNamespace(ws, structs.DefaultNamespace) 87 must.NoError(t, err) 88 must.Eq(t, expectedQuotaSize, quotaUsed.Size) 89 90 // update the mocks so the test element has the correct create/modify 91 // indexes and times now that we have validated them 92 svs = got 93 }) 94 95 t.Run("1a fetch variable", func(t *testing.T) { 96 sve, err := testState.GetVariable(ws, svs[0].Namespace, svs[0].Path) 97 must.NoError(t, err) 98 must.NotNil(t, sve) 99 }) 100 101 // Upsert the exact same variables without any modification. In this 102 // case, the index table should not be updated, indicating no write actually 103 // happened due to equality checking. 104 t.Run("2 upsert same", func(t *testing.T) { 105 reInsertIndex := uint64(30) 106 107 for _, sv := range svs { 108 svReq := &structs.VarApplyStateRequest{ 109 Op: structs.VarOpSet, 110 Var: sv, 111 } 112 reInsertIndex++ 113 resp := testState.VarSet(reInsertIndex, svReq) 114 must.NoError(t, resp.Error) 115 } 116 117 reInsertActualIndex, err := testState.Index(TableVariables) 118 must.NoError(t, err) 119 must.Eq(t, insertIndex, reInsertActualIndex, must.Sprintf("index should not have changed")) 120 121 quotaUsed, err := testState.VariablesQuotaByNamespace(ws, structs.DefaultNamespace) 122 must.NoError(t, err) 123 must.Eq(t, expectedQuotaSize, quotaUsed.Size) 124 }) 125 126 // Modify a single one of the previously inserted variables and 127 // performs an upsert. This ensures the index table is modified correctly 128 // and that each variable is updated, or not, as expected. 129 t.Run("3 modify one", func(t *testing.T) { 130 sv1Update := svs[0].Copy() 131 sv1Update.KeyID = "sv1-update" 132 133 buf := make([]byte, 1+len(sv1Update.Data)) 134 copy(buf, sv1Update.Data) 135 buf[len(buf)-1] = 'x' 136 sv1Update.Data = buf 137 138 update1Index := uint64(40) 139 140 resp := testState.VarSet(update1Index, &structs.VarApplyStateRequest{ 141 Op: structs.VarOpSet, 142 Var: &sv1Update, 143 }) 144 must.NoError(t, resp.Error) 145 146 // Check that the index for the table was modified as expected. 147 updateActualIndex, err := testState.Index(TableVariables) 148 must.NoError(t, err) 149 must.Eq(t, update1Index, updateActualIndex, must.Sprintf("index should have changed")) 150 151 // Get the variables from the table. 152 iter, err := testState.Variables(ws) 153 must.NoError(t, err) 154 155 got := []*structs.VariableEncrypted{} 156 157 // Iterate all the stored variables and assert indexes have been updated as expected 158 for raw := iter.Next(); raw != nil; raw = iter.Next() { 159 sv := raw.(*structs.VariableEncrypted) 160 var svCopy structs.VariableEncrypted 161 svCopy = sv.Copy() 162 got = append(got, &svCopy) 163 } 164 must.Len(t, 2, got) 165 must.Eq(t, update1Index, got[0].ModifyIndex) 166 must.Eq(t, insertIndex, got[1].ModifyIndex) 167 168 // update the mocks so the test element has the correct create/modify 169 // indexes and times now that we have validated them 170 svs = got 171 172 quotaUsed, err := testState.VariablesQuotaByNamespace(ws, structs.DefaultNamespace) 173 must.NoError(t, err) 174 must.Eq(t, expectedQuotaSize+1, quotaUsed.Size) 175 }) 176 177 // Modify the second variable but send an upsert request that 178 // includes this and the already modified variable. 179 t.Run("4 upsert other", func(t *testing.T) { 180 update2Index := uint64(50) 181 sv2 := svs[1].Copy() 182 sv2.KeyID = "sv2-update" 183 sv2.ModifyIndex = update2Index 184 185 resp := testState.VarSet(update2Index, &structs.VarApplyStateRequest{ 186 Op: structs.VarOpSet, 187 Var: &sv2, 188 }) 189 must.NoError(t, resp.Error) 190 191 // Check that the index for the table was modified as expected. 192 update2ActualIndex, err := testState.Index(TableVariables) 193 must.NoError(t, err) 194 must.Eq(t, update2Index, update2ActualIndex, must.Sprintf("index should have changed")) 195 196 // Get the variables from the table. 197 iter, err := testState.Variables(ws) 198 must.NoError(t, err) 199 200 got := []structs.VariableEncrypted{} 201 202 // Iterate all the stored variables and assert indexes have been updated as expected 203 for raw := iter.Next(); raw != nil; raw = iter.Next() { 204 sv := raw.(*structs.VariableEncrypted) 205 got = append(got, sv.Copy()) 206 } 207 must.Len(t, 2, got) 208 must.Eq(t, svs[0].ModifyIndex, got[0].ModifyIndex) 209 must.Eq(t, update2Index, got[1].ModifyIndex) 210 211 must.True(t, svs[0].Equal(got[0])) 212 must.True(t, sv2.Equal(got[1])) 213 214 quotaUsed, err := testState.VariablesQuotaByNamespace(ws, structs.DefaultNamespace) 215 must.NoError(t, err) 216 must.Eq(t, expectedQuotaSize+1, quotaUsed.Size) 217 218 }) 219 } 220 221 func TestStateStore_DeleteVariable(t *testing.T) { 222 ci.Parallel(t) 223 testState := testStateStore(t) 224 225 // Generate some test variables that we will use and modify throughout. 226 svs := []*structs.VariableEncrypted{ 227 mock.VariableEncrypted(), 228 mock.VariableEncrypted(), 229 } 230 svs[0].Path = "aaaaa" 231 svs[1].Path = "bbbbb" 232 233 initialIndex := uint64(10) 234 235 t.Run("1 delete a variable that does not exist", func(t *testing.T) { 236 237 resp := testState.VarDelete(initialIndex, &structs.VarApplyStateRequest{ 238 Op: structs.VarOpDelete, 239 Var: svs[0], 240 }) 241 must.NoError(t, resp.Error, must.Sprintf("deleting non-existing var is not an error")) 242 243 actualInitialIndex, err := testState.Index(TableVariables) 244 must.NoError(t, err) 245 must.Eq(t, 0, actualInitialIndex, must.Sprintf("index should not have changed")) 246 247 quotaUsed, err := testState.VariablesQuotaByNamespace(nil, structs.DefaultNamespace) 248 must.NoError(t, err) 249 must.Nil(t, quotaUsed) 250 }) 251 252 // Upsert two variables, deletes one, then ensure the 253 // remaining is left as expected. 254 t.Run("2 upsert variable and delete", func(t *testing.T) { 255 256 ns := mock.Namespace() 257 ns.Name = svs[0].Namespace 258 must.NoError(t, testState.UpsertNamespaces(initialIndex, []*structs.Namespace{ns})) 259 260 for _, sv := range svs { 261 svReq := &structs.VarApplyStateRequest{ 262 Op: structs.VarOpSet, 263 Var: sv, 264 } 265 initialIndex++ 266 resp := testState.VarSet(initialIndex, svReq) 267 must.NoError(t, resp.Error) 268 } 269 270 // Perform the delete. 271 delete1Index := uint64(20) 272 273 resp := testState.VarDelete(delete1Index, &structs.VarApplyStateRequest{ 274 Op: structs.VarOpDelete, 275 Var: svs[0], 276 }) 277 must.NoError(t, resp.Error) 278 279 // Check that the index for the table was modified as expected. 280 actualDelete1Index, err := testState.Index(TableVariables) 281 must.NoError(t, err) 282 must.Eq(t, delete1Index, actualDelete1Index, must.Sprintf("index should have changed")) 283 284 ws := memdb.NewWatchSet() 285 286 // Get the variables from the table. 287 iter, err := testState.Variables(ws) 288 must.NoError(t, err) 289 290 var delete1Count int 291 var expectedQuotaSize int64 292 293 // Iterate all the stored variables and assert we have the expected 294 // number. 295 for raw := iter.Next(); raw != nil; raw = iter.Next() { 296 delete1Count++ 297 v := raw.(*structs.VariableEncrypted) 298 expectedQuotaSize += int64(len(v.Data)) 299 } 300 must.Eq(t, 1, delete1Count, must.Sprintf("unexpected number of variables in table")) 301 quotaUsed, err := testState.VariablesQuotaByNamespace(ws, structs.DefaultNamespace) 302 must.NoError(t, err) 303 must.Eq(t, expectedQuotaSize, quotaUsed.Size) 304 }) 305 306 t.Run("3 delete remaining variable", func(t *testing.T) { 307 delete2Index := uint64(30) 308 309 resp := testState.VarDelete(delete2Index, &structs.VarApplyStateRequest{ 310 Op: structs.VarOpDelete, 311 Var: svs[1], 312 }) 313 must.NoError(t, resp.Error) 314 315 // Check that the index for the table was modified as expected. 316 actualDelete2Index, err := testState.Index(TableVariables) 317 must.NoError(t, err) 318 must.Eq(t, delete2Index, actualDelete2Index, must.Sprintf("index should have changed")) 319 320 // Get the variables from the table. 321 ws := memdb.NewWatchSet() 322 iter, err := testState.Variables(ws) 323 must.NoError(t, err) 324 325 var delete2Count int 326 327 // Ensure the table is empty. 328 for raw := iter.Next(); raw != nil; raw = iter.Next() { 329 delete2Count++ 330 } 331 must.Eq(t, 0, delete2Count, must.Sprintf("unexpected number of variables in table")) 332 333 quotaUsed, err := testState.VariablesQuotaByNamespace(ws, structs.DefaultNamespace) 334 must.NoError(t, err) 335 must.Eq(t, 0, quotaUsed.Size) 336 }) 337 } 338 339 func TestStateStore_GetVariables(t *testing.T) { 340 ci.Parallel(t) 341 testState := testStateStore(t) 342 343 ns := mock.Namespace() 344 ns.Name = "~*magical*~" 345 initialIndex := uint64(10) 346 must.NoError(t, testState.UpsertNamespaces(initialIndex, []*structs.Namespace{ns})) 347 348 // Generate some test variables in different namespaces and upsert them. 349 svs := []*structs.VariableEncrypted{ 350 mock.VariableEncrypted(), 351 mock.VariableEncrypted(), 352 } 353 svs[0].Path = "aaaaa" 354 svs[0].Namespace = "~*magical*~" 355 svs[1].Path = "bbbbb" 356 357 for _, sv := range svs { 358 svReq := &structs.VarApplyStateRequest{ 359 Op: structs.VarOpSet, 360 Var: sv, 361 } 362 initialIndex++ 363 resp := testState.VarSet(initialIndex, svReq) 364 must.NoError(t, resp.Error) 365 } 366 367 // Look up variables using the namespace of the first mock variable. 368 ws := memdb.NewWatchSet() 369 iter, err := testState.GetVariablesByNamespace(ws, svs[0].Namespace) 370 must.NoError(t, err) 371 372 var count1 int 373 374 for raw := iter.Next(); raw != nil; raw = iter.Next() { 375 sv := raw.(*structs.VariableEncrypted) 376 must.Eq(t, svs[0].Namespace, sv.Namespace) 377 must.Eq(t, 11, sv.CreateIndex, must.Sprintf("%s incorrect create index", sv.Path)) 378 must.Eq(t, 11, sv.ModifyIndex, must.Sprintf("%s incorrect modify index", sv.Path)) 379 count1++ 380 } 381 382 must.Eq(t, 1, count1) 383 384 // Look up variables using the namespace of the second mock variable. 385 iter, err = testState.GetVariablesByNamespace(ws, svs[1].Namespace) 386 must.NoError(t, err) 387 388 var count2 int 389 390 for raw := iter.Next(); raw != nil; raw = iter.Next() { 391 count2++ 392 sv := raw.(*structs.VariableEncrypted) 393 must.Eq(t, initialIndex, sv.CreateIndex, must.Sprintf("%s incorrect create index", sv.Path)) 394 must.Eq(t, initialIndex, sv.ModifyIndex, must.Sprintf("%s incorrect modify index", sv.Path)) 395 must.Eq(t, svs[1].Namespace, sv.Namespace) 396 } 397 must.Eq(t, 1, count2) 398 399 // Look up variables using a namespace that shouldn't contain any 400 // variables. 401 iter, err = testState.GetVariablesByNamespace(ws, "pony-club") 402 must.NoError(t, err) 403 404 var count3 int 405 406 for raw := iter.Next(); raw != nil; raw = iter.Next() { 407 count3++ 408 } 409 must.Eq(t, 0, count3) 410 } 411 412 func TestStateStore_ListVariablesByNamespaceAndPrefix(t *testing.T) { 413 ci.Parallel(t) 414 testState := testStateStore(t) 415 416 // Generate some test variables and upsert them. 417 svs := []*structs.VariableEncrypted{} 418 for i := 0; i < 6; i++ { 419 sv := mock.VariableEncrypted() 420 svs = append(svs, sv) 421 } 422 423 svs[0].Path = "a/b" 424 svs[1].Path = "a/b/c" 425 svs[2].Path = "unrelated/b/c" 426 svs[3].Namespace = "other" 427 svs[3].Path = "a/b/c" 428 svs[4].Namespace = "other" 429 svs[4].Path = "a/q/z" 430 svs[5].Namespace = "other" 431 svs[5].Path = "a/z/z" 432 433 ns := mock.Namespace() 434 ns.Name = "other" 435 initialIndex := uint64(10) 436 must.NoError(t, testState.UpsertNamespaces(initialIndex, []*structs.Namespace{ns})) 437 438 for _, sv := range svs { 439 svReq := &structs.VarApplyStateRequest{ 440 Op: structs.VarOpSet, 441 Var: sv, 442 } 443 initialIndex++ 444 resp := testState.VarSet(initialIndex, svReq) 445 must.NoError(t, resp.Error) 446 } 447 448 t.Run("ByNamespace", func(t *testing.T) { 449 testCases := []struct { 450 desc string 451 namespace string 452 expectedCount int 453 }{ 454 { 455 desc: "default", 456 namespace: "default", 457 expectedCount: 2, 458 }, 459 { 460 desc: "other", 461 namespace: "other", 462 expectedCount: 3, 463 }, 464 { 465 desc: "nonexistent", 466 namespace: "BAD", 467 expectedCount: 0, 468 }, 469 } 470 471 ws := memdb.NewWatchSet() 472 for _, tC := range testCases { 473 t.Run(tC.desc, func(t *testing.T) { 474 iter, err := testState.GetVariablesByNamespace(ws, tC.namespace) 475 must.NoError(t, err) 476 477 var count int = 0 478 for raw := iter.Next(); raw != nil; raw = iter.Next() { 479 count++ 480 sv := raw.(*structs.VariableEncrypted) 481 must.Eq(t, tC.namespace, sv.Namespace) 482 } 483 }) 484 } 485 }) 486 487 t.Run("ByNamespaceAndPrefix", func(t *testing.T) { 488 testCases := []struct { 489 desc string 490 namespace string 491 prefix string 492 expectedCount int 493 }{ 494 { 495 desc: "ns1 with good path", 496 namespace: "default", 497 prefix: "a", 498 expectedCount: 2, 499 }, 500 { 501 desc: "ns2 with good path", 502 namespace: "other", 503 prefix: "a", 504 expectedCount: 3, 505 }, 506 { 507 desc: "ns1 path valid for ns2", 508 namespace: "default", 509 prefix: "a/b/c", 510 expectedCount: 1, 511 }, 512 { 513 desc: "ns2 empty prefix", 514 namespace: "other", 515 prefix: "", 516 expectedCount: 3, 517 }, 518 { 519 desc: "nonexistent ns", 520 namespace: "BAD", 521 prefix: "", 522 expectedCount: 0, 523 }, 524 } 525 526 ws := memdb.NewWatchSet() 527 for _, tC := range testCases { 528 t.Run(tC.desc, func(t *testing.T) { 529 iter, err := testState.GetVariablesByNamespaceAndPrefix(ws, tC.namespace, tC.prefix) 530 must.NoError(t, err) 531 532 var count int = 0 533 for raw := iter.Next(); raw != nil; raw = iter.Next() { 534 count++ 535 sv := raw.(*structs.VariableEncrypted) 536 must.Eq(t, tC.namespace, sv.Namespace) 537 must.True(t, strings.HasPrefix(sv.Path, tC.prefix)) 538 } 539 must.Eq(t, tC.expectedCount, count) 540 }) 541 } 542 }) 543 544 t.Run("ByPrefix", func(t *testing.T) { 545 testCases := []struct { 546 desc string 547 prefix string 548 expectedCount int 549 }{ 550 { 551 desc: "bad prefix", 552 prefix: "bad", 553 expectedCount: 0, 554 }, 555 { 556 desc: "multiple ns", 557 prefix: "a/b/c", 558 expectedCount: 2, 559 }, 560 { 561 desc: "all", 562 prefix: "", 563 expectedCount: 6, 564 }, 565 } 566 567 ws := memdb.NewWatchSet() 568 for _, tC := range testCases { 569 t.Run(tC.desc, func(t *testing.T) { 570 iter, err := testState.GetVariablesByPrefix(ws, tC.prefix) 571 must.NoError(t, err) 572 573 var count int = 0 574 for raw := iter.Next(); raw != nil; raw = iter.Next() { 575 count++ 576 sv := raw.(*structs.VariableEncrypted) 577 must.True(t, strings.HasPrefix(sv.Path, tC.prefix)) 578 } 579 must.Eq(t, tC.expectedCount, count) 580 }) 581 } 582 }) 583 } 584 func TestStateStore_ListVariablesByKeyID(t *testing.T) { 585 ci.Parallel(t) 586 testState := testStateStore(t) 587 588 // Generate some test variables and upsert them. 589 svs := []*structs.VariableEncrypted{} 590 for i := 0; i < 7; i++ { 591 sv := mock.VariableEncrypted() 592 sv.Path = uuid.Generate() 593 svs = append(svs, sv) 594 } 595 596 keyID := uuid.Generate() 597 598 expectedForKey := []string{} 599 for i := 0; i < 5; i++ { 600 svs[i].KeyID = keyID 601 expectedForKey = append(expectedForKey, svs[i].Path) 602 sort.Strings(expectedForKey) 603 } 604 605 expectedOrphaned := []string{svs[5].Path, svs[6].Path} 606 607 initialIndex := uint64(10) 608 609 for _, sv := range svs { 610 svReq := &structs.VarApplyStateRequest{ 611 Op: structs.VarOpSet, 612 Var: sv, 613 } 614 initialIndex++ 615 resp := testState.VarSet(initialIndex, svReq) 616 must.NoError(t, resp.Error) 617 } 618 619 ws := memdb.NewWatchSet() 620 iter, err := testState.GetVariablesByKeyID(ws, keyID) 621 must.NoError(t, err) 622 623 var count int 624 for raw := iter.Next(); raw != nil; raw = iter.Next() { 625 sv := raw.(*structs.VariableEncrypted) 626 must.Eq(t, keyID, sv.KeyID) 627 must.Eq(t, expectedForKey[count], sv.Path) 628 must.SliceNotContains(t, expectedOrphaned, sv.Path) 629 count++ 630 } 631 must.Eq(t, 5, count) 632 } 633 634 func printVariable(tsv *structs.VariableEncrypted) string { 635 b, _ := json.Marshal(tsv) 636 return string(b) 637 } 638 639 func printVariables(tsvs []*structs.VariableEncrypted) string { 640 if len(tsvs) == 0 { 641 return "" 642 } 643 var out strings.Builder 644 for _, tsv := range tsvs { 645 out.WriteString(printVariable(tsv) + "\n") 646 } 647 return out.String() 648 } 649 650 // TestStateStore_Variables_DeleteCAS 651 func TestStateStore_Variables_DeleteCAS(t *testing.T) { 652 ci.Parallel(t) 653 ts := testStateStore(t) 654 655 varNotExist := structs.VariableEncrypted{ 656 VariableMetadata: structs.VariableMetadata{ 657 Namespace: "default", 658 Path: "does/not/exist", 659 ModifyIndex: 0, 660 }, 661 } 662 663 t.Run("missing_var-cas_0", func(t *testing.T) { 664 ci.Parallel(t) 665 varNotExist := varNotExist 666 // A CAS delete with index 0 should succeed when the variable does not 667 // exist in the state store. 668 resp := ts.VarDeleteCAS(10, &structs.VarApplyStateRequest{ 669 Op: structs.VarOpDelete, 670 Var: &varNotExist, 671 }) 672 must.True(t, resp.IsOk()) 673 }) 674 t.Run("missing_var-cas_1", func(t *testing.T) { 675 ci.Parallel(t) 676 varZero := varNotExist 677 varNotExist := varNotExist 678 // A CAS delete with a non-zero index should return a conflict when the 679 // variable does not exist in the state store. The conflict value should 680 // be a zero value having the same namespace and path. 681 varNotExist.ModifyIndex = 1 682 req := &structs.VarApplyStateRequest{ 683 Op: structs.VarOpDelete, 684 Var: &varNotExist, 685 } 686 resp := ts.VarDeleteCAS(10, req) 687 must.True(t, resp.IsConflict()) 688 must.NotNil(t, resp.Conflict) 689 must.Eq(t, varZero.VariableMetadata, resp.Conflict.VariableMetadata) 690 }) 691 t.Run("real_var-cas_0", func(t *testing.T) { 692 ci.Parallel(t) 693 sv := mock.VariableEncrypted() 694 sv.CreateIndex = 0 695 sv.ModifyIndex = 0 696 sv.Path = "real_var/cas_0" 697 // Need to make a copy because VarSet mutates Var. 698 svZero := sv.Copy() 699 resp := ts.VarSet(10, &structs.VarApplyStateRequest{ 700 Op: structs.VarOpSet, 701 Var: sv, 702 }) 703 must.True(t, resp.IsOk(), must.Sprintf("resp: %+v", resp)) 704 705 // A CAS delete with a zero index should return a conflict when the 706 // variable exists in the state store. The conflict value should 707 // be the current state of the variable at the path. 708 req := &structs.VarApplyStateRequest{ 709 Op: structs.VarOpDelete, 710 Var: &svZero, 711 } 712 resp = ts.VarDeleteCAS(0, req) 713 must.True(t, resp.IsConflict(), must.Sprintf("resp: %+v", resp)) 714 must.NotNil(t, resp.Conflict) 715 must.Eq(t, sv.VariableMetadata, resp.Conflict.VariableMetadata) 716 }) 717 t.Run("real_var-cas_ok", func(t *testing.T) { 718 ci.Parallel(t) 719 sv := mock.VariableEncrypted() 720 sv.Path = "real_var/cas_ok" 721 resp := ts.VarSet(10, &structs.VarApplyStateRequest{ 722 Op: structs.VarOpSet, 723 Var: sv, 724 }) 725 must.True(t, resp.IsOk()) 726 727 // A CAS delete with a correct index should succeed. 728 req := &structs.VarApplyStateRequest{ 729 Op: structs.VarOpDelete, 730 Var: sv, 731 } 732 resp = ts.VarDeleteCAS(0, req) 733 must.True(t, resp.IsOk()) 734 }) 735 }