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