github.com/stefanmcshane/helm@v0.0.0-20221213002717-88a4a2c6e77d/pkg/storage/storage_test.go (about) 1 /* 2 Copyright The Helm Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package storage // import "github.com/stefanmcshane/helm/pkg/storage" 18 19 import ( 20 "fmt" 21 "reflect" 22 "testing" 23 24 "github.com/pkg/errors" 25 26 rspb "github.com/stefanmcshane/helm/pkg/release" 27 "github.com/stefanmcshane/helm/pkg/storage/driver" 28 ) 29 30 func TestStorageCreate(t *testing.T) { 31 // initialize storage 32 storage := Init(driver.NewMemory()) 33 34 // create fake release 35 rls := ReleaseTestData{ 36 Name: "angry-beaver", 37 Version: 1, 38 }.ToRelease() 39 40 assertErrNil(t.Fatal, storage.Create(rls), "StoreRelease") 41 42 // fetch the release 43 res, err := storage.Get(rls.Name, rls.Version) 44 assertErrNil(t.Fatal, err, "QueryRelease") 45 46 // verify the fetched and created release are the same 47 if !reflect.DeepEqual(rls, res) { 48 t.Fatalf("Expected %v, got %v", rls, res) 49 } 50 } 51 52 func TestStorageUpdate(t *testing.T) { 53 // initialize storage 54 storage := Init(driver.NewMemory()) 55 56 // create fake release 57 rls := ReleaseTestData{ 58 Name: "angry-beaver", 59 Version: 1, 60 Status: rspb.StatusDeployed, 61 }.ToRelease() 62 63 assertErrNil(t.Fatal, storage.Create(rls), "StoreRelease") 64 65 // modify the release 66 rls.Info.Status = rspb.StatusUninstalled 67 assertErrNil(t.Fatal, storage.Update(rls), "UpdateRelease") 68 69 // retrieve the updated release 70 res, err := storage.Get(rls.Name, rls.Version) 71 assertErrNil(t.Fatal, err, "QueryRelease") 72 73 // verify updated and fetched releases are the same. 74 if !reflect.DeepEqual(rls, res) { 75 t.Fatalf("Expected %v, got %v", rls, res) 76 } 77 } 78 79 func TestStorageDelete(t *testing.T) { 80 // initialize storage 81 storage := Init(driver.NewMemory()) 82 83 // create fake release 84 rls := ReleaseTestData{ 85 Name: "angry-beaver", 86 Version: 1, 87 }.ToRelease() 88 rls2 := ReleaseTestData{ 89 Name: "angry-beaver", 90 Version: 2, 91 }.ToRelease() 92 93 assertErrNil(t.Fatal, storage.Create(rls), "StoreRelease") 94 assertErrNil(t.Fatal, storage.Create(rls2), "StoreRelease") 95 96 // delete the release 97 res, err := storage.Delete(rls.Name, rls.Version) 98 assertErrNil(t.Fatal, err, "DeleteRelease") 99 100 // verify updated and fetched releases are the same. 101 if !reflect.DeepEqual(rls, res) { 102 t.Fatalf("Expected %v, got %v", rls, res) 103 } 104 105 hist, err := storage.History(rls.Name) 106 if err != nil { 107 t.Errorf("unexpected error: %s", err) 108 } 109 110 // We have now deleted one of the two records. 111 if len(hist) != 1 { 112 t.Errorf("expected 1 record for deleted release version, got %d", len(hist)) 113 } 114 115 if hist[0].Version != 2 { 116 t.Errorf("Expected version to be 2, got %d", hist[0].Version) 117 } 118 } 119 120 func TestStorageList(t *testing.T) { 121 // initialize storage 122 storage := Init(driver.NewMemory()) 123 124 // setup storage with test releases 125 setup := func() { 126 // release records 127 rls0 := ReleaseTestData{Name: "happy-catdog", Status: rspb.StatusSuperseded}.ToRelease() 128 rls1 := ReleaseTestData{Name: "livid-human", Status: rspb.StatusSuperseded}.ToRelease() 129 rls2 := ReleaseTestData{Name: "relaxed-cat", Status: rspb.StatusSuperseded}.ToRelease() 130 rls3 := ReleaseTestData{Name: "hungry-hippo", Status: rspb.StatusDeployed}.ToRelease() 131 rls4 := ReleaseTestData{Name: "angry-beaver", Status: rspb.StatusDeployed}.ToRelease() 132 rls5 := ReleaseTestData{Name: "opulent-frog", Status: rspb.StatusUninstalled}.ToRelease() 133 rls6 := ReleaseTestData{Name: "happy-liger", Status: rspb.StatusUninstalled}.ToRelease() 134 135 // create the release records in the storage 136 assertErrNil(t.Fatal, storage.Create(rls0), "Storing release 'rls0'") 137 assertErrNil(t.Fatal, storage.Create(rls1), "Storing release 'rls1'") 138 assertErrNil(t.Fatal, storage.Create(rls2), "Storing release 'rls2'") 139 assertErrNil(t.Fatal, storage.Create(rls3), "Storing release 'rls3'") 140 assertErrNil(t.Fatal, storage.Create(rls4), "Storing release 'rls4'") 141 assertErrNil(t.Fatal, storage.Create(rls5), "Storing release 'rls5'") 142 assertErrNil(t.Fatal, storage.Create(rls6), "Storing release 'rls6'") 143 } 144 145 var listTests = []struct { 146 Description string 147 NumExpected int 148 ListFunc func() ([]*rspb.Release, error) 149 }{ 150 {"ListDeployed", 2, storage.ListDeployed}, 151 {"ListReleases", 7, storage.ListReleases}, 152 {"ListUninstalled", 2, storage.ListUninstalled}, 153 } 154 155 setup() 156 157 for _, tt := range listTests { 158 list, err := tt.ListFunc() 159 assertErrNil(t.Fatal, err, tt.Description) 160 // verify the count of releases returned 161 if len(list) != tt.NumExpected { 162 t.Errorf("ListReleases(%s): expected %d, actual %d", 163 tt.Description, 164 tt.NumExpected, 165 len(list)) 166 } 167 } 168 } 169 170 func TestStorageDeployed(t *testing.T) { 171 storage := Init(driver.NewMemory()) 172 173 const name = "angry-bird" 174 const vers = 4 175 176 // setup storage with test releases 177 setup := func() { 178 // release records 179 rls0 := ReleaseTestData{Name: name, Version: 1, Status: rspb.StatusSuperseded}.ToRelease() 180 rls1 := ReleaseTestData{Name: name, Version: 2, Status: rspb.StatusSuperseded}.ToRelease() 181 rls2 := ReleaseTestData{Name: name, Version: 3, Status: rspb.StatusSuperseded}.ToRelease() 182 rls3 := ReleaseTestData{Name: name, Version: 4, Status: rspb.StatusDeployed}.ToRelease() 183 184 // create the release records in the storage 185 assertErrNil(t.Fatal, storage.Create(rls0), "Storing release 'angry-bird' (v1)") 186 assertErrNil(t.Fatal, storage.Create(rls1), "Storing release 'angry-bird' (v2)") 187 assertErrNil(t.Fatal, storage.Create(rls2), "Storing release 'angry-bird' (v3)") 188 assertErrNil(t.Fatal, storage.Create(rls3), "Storing release 'angry-bird' (v4)") 189 } 190 191 setup() 192 193 rls, err := storage.Last(name) 194 if err != nil { 195 t.Fatalf("Failed to query for deployed release: %s\n", err) 196 } 197 198 switch { 199 case rls == nil: 200 t.Fatalf("Release is nil") 201 case rls.Name != name: 202 t.Fatalf("Expected release name %q, actual %q\n", name, rls.Name) 203 case rls.Version != vers: 204 t.Fatalf("Expected release version %d, actual %d\n", vers, rls.Version) 205 case rls.Info.Status != rspb.StatusDeployed: 206 t.Fatalf("Expected release status 'DEPLOYED', actual %s\n", rls.Info.Status.String()) 207 } 208 } 209 210 func TestStorageDeployedWithCorruption(t *testing.T) { 211 storage := Init(driver.NewMemory()) 212 213 const name = "angry-bird" 214 const vers = int(4) 215 216 // setup storage with test releases 217 setup := func() { 218 // release records (notice odd order and corruption) 219 rls0 := ReleaseTestData{Name: name, Version: 1, Status: rspb.StatusSuperseded}.ToRelease() 220 rls1 := ReleaseTestData{Name: name, Version: 4, Status: rspb.StatusDeployed}.ToRelease() 221 rls2 := ReleaseTestData{Name: name, Version: 3, Status: rspb.StatusSuperseded}.ToRelease() 222 rls3 := ReleaseTestData{Name: name, Version: 2, Status: rspb.StatusDeployed}.ToRelease() 223 224 // create the release records in the storage 225 assertErrNil(t.Fatal, storage.Create(rls0), "Storing release 'angry-bird' (v1)") 226 assertErrNil(t.Fatal, storage.Create(rls1), "Storing release 'angry-bird' (v2)") 227 assertErrNil(t.Fatal, storage.Create(rls2), "Storing release 'angry-bird' (v3)") 228 assertErrNil(t.Fatal, storage.Create(rls3), "Storing release 'angry-bird' (v4)") 229 } 230 231 setup() 232 233 rls, err := storage.Deployed(name) 234 if err != nil { 235 t.Fatalf("Failed to query for deployed release: %s\n", err) 236 } 237 238 switch { 239 case rls == nil: 240 t.Fatalf("Release is nil") 241 case rls.Name != name: 242 t.Fatalf("Expected release name %q, actual %q\n", name, rls.Name) 243 case rls.Version != vers: 244 t.Fatalf("Expected release version %d, actual %d\n", vers, rls.Version) 245 case rls.Info.Status != rspb.StatusDeployed: 246 t.Fatalf("Expected release status 'DEPLOYED', actual %s\n", rls.Info.Status.String()) 247 } 248 } 249 250 func TestStorageHistory(t *testing.T) { 251 storage := Init(driver.NewMemory()) 252 253 const name = "angry-bird" 254 255 // setup storage with test releases 256 setup := func() { 257 // release records 258 rls0 := ReleaseTestData{Name: name, Version: 1, Status: rspb.StatusSuperseded}.ToRelease() 259 rls1 := ReleaseTestData{Name: name, Version: 2, Status: rspb.StatusSuperseded}.ToRelease() 260 rls2 := ReleaseTestData{Name: name, Version: 3, Status: rspb.StatusSuperseded}.ToRelease() 261 rls3 := ReleaseTestData{Name: name, Version: 4, Status: rspb.StatusDeployed}.ToRelease() 262 263 // create the release records in the storage 264 assertErrNil(t.Fatal, storage.Create(rls0), "Storing release 'angry-bird' (v1)") 265 assertErrNil(t.Fatal, storage.Create(rls1), "Storing release 'angry-bird' (v2)") 266 assertErrNil(t.Fatal, storage.Create(rls2), "Storing release 'angry-bird' (v3)") 267 assertErrNil(t.Fatal, storage.Create(rls3), "Storing release 'angry-bird' (v4)") 268 } 269 270 setup() 271 272 h, err := storage.History(name) 273 if err != nil { 274 t.Fatalf("Failed to query for release history (%q): %s\n", name, err) 275 } 276 if len(h) != 4 { 277 t.Fatalf("Release history (%q) is empty\n", name) 278 } 279 } 280 281 var errMaxHistoryMockDriverSomethingHappened = errors.New("something happened") 282 283 type MaxHistoryMockDriver struct { 284 Driver driver.Driver 285 } 286 287 func NewMaxHistoryMockDriver(d driver.Driver) *MaxHistoryMockDriver { 288 return &MaxHistoryMockDriver{Driver: d} 289 } 290 func (d *MaxHistoryMockDriver) Create(key string, rls *rspb.Release) error { 291 return d.Driver.Create(key, rls) 292 } 293 func (d *MaxHistoryMockDriver) Update(key string, rls *rspb.Release) error { 294 return d.Driver.Update(key, rls) 295 } 296 func (d *MaxHistoryMockDriver) Delete(key string) (*rspb.Release, error) { 297 return nil, errMaxHistoryMockDriverSomethingHappened 298 } 299 func (d *MaxHistoryMockDriver) Get(key string) (*rspb.Release, error) { 300 return d.Driver.Get(key) 301 } 302 func (d *MaxHistoryMockDriver) List(filter func(*rspb.Release) bool) ([]*rspb.Release, error) { 303 return d.Driver.List(filter) 304 } 305 func (d *MaxHistoryMockDriver) Query(labels map[string]string) ([]*rspb.Release, error) { 306 return d.Driver.Query(labels) 307 } 308 func (d *MaxHistoryMockDriver) Name() string { 309 return d.Driver.Name() 310 } 311 312 func TestMaxHistoryErrorHandling(t *testing.T) { 313 //func TestStorageRemoveLeastRecentWithError(t *testing.T) { 314 storage := Init(NewMaxHistoryMockDriver(driver.NewMemory())) 315 storage.Log = t.Logf 316 317 storage.MaxHistory = 1 318 319 const name = "angry-bird" 320 321 // setup storage with test releases 322 setup := func() { 323 // release records 324 rls1 := ReleaseTestData{Name: name, Version: 1, Status: rspb.StatusSuperseded}.ToRelease() 325 326 // create the release records in the storage 327 assertErrNil(t.Fatal, storage.Driver.Create(makeKey(rls1.Name, rls1.Version), rls1), "Storing release 'angry-bird' (v1)") 328 } 329 setup() 330 331 rls2 := ReleaseTestData{Name: name, Version: 2, Status: rspb.StatusSuperseded}.ToRelease() 332 wantErr := errMaxHistoryMockDriverSomethingHappened 333 gotErr := storage.Create(rls2) 334 if !errors.Is(gotErr, wantErr) { 335 t.Fatalf("Storing release 'angry-bird' (v2) should return the error %#v, but returned %#v", wantErr, gotErr) 336 } 337 } 338 339 func TestStorageRemoveLeastRecent(t *testing.T) { 340 storage := Init(driver.NewMemory()) 341 storage.Log = t.Logf 342 343 // Make sure that specifying this at the outset doesn't cause any bugs. 344 storage.MaxHistory = 10 345 346 const name = "angry-bird" 347 348 // setup storage with test releases 349 setup := func() { 350 // release records 351 rls0 := ReleaseTestData{Name: name, Version: 1, Status: rspb.StatusSuperseded}.ToRelease() 352 rls1 := ReleaseTestData{Name: name, Version: 2, Status: rspb.StatusSuperseded}.ToRelease() 353 rls2 := ReleaseTestData{Name: name, Version: 3, Status: rspb.StatusSuperseded}.ToRelease() 354 rls3 := ReleaseTestData{Name: name, Version: 4, Status: rspb.StatusDeployed}.ToRelease() 355 356 // create the release records in the storage 357 assertErrNil(t.Fatal, storage.Create(rls0), "Storing release 'angry-bird' (v1)") 358 assertErrNil(t.Fatal, storage.Create(rls1), "Storing release 'angry-bird' (v2)") 359 assertErrNil(t.Fatal, storage.Create(rls2), "Storing release 'angry-bird' (v3)") 360 assertErrNil(t.Fatal, storage.Create(rls3), "Storing release 'angry-bird' (v4)") 361 } 362 setup() 363 364 // Because we have not set a limit, we expect 4. 365 expect := 4 366 if hist, err := storage.History(name); err != nil { 367 t.Fatal(err) 368 } else if len(hist) != expect { 369 t.Fatalf("expected %d items in history, got %d", expect, len(hist)) 370 } 371 372 storage.MaxHistory = 3 373 rls5 := ReleaseTestData{Name: name, Version: 5, Status: rspb.StatusDeployed}.ToRelease() 374 assertErrNil(t.Fatal, storage.Create(rls5), "Storing release 'angry-bird' (v5)") 375 376 // On inserting the 5th record, we expect two records to be pruned from history. 377 hist, err := storage.History(name) 378 if err != nil { 379 t.Fatal(err) 380 } else if len(hist) != storage.MaxHistory { 381 for _, item := range hist { 382 t.Logf("%s %v", item.Name, item.Version) 383 } 384 t.Fatalf("expected %d items in history, got %d", storage.MaxHistory, len(hist)) 385 } 386 387 // We expect the existing records to be 3, 4, and 5. 388 for i, item := range hist { 389 v := item.Version 390 if expect := i + 3; v != expect { 391 t.Errorf("Expected release %d, got %d", expect, v) 392 } 393 } 394 } 395 396 func TestStorageDoNotDeleteDeployed(t *testing.T) { 397 storage := Init(driver.NewMemory()) 398 storage.Log = t.Logf 399 storage.MaxHistory = 3 400 401 const name = "angry-bird" 402 403 // setup storage with test releases 404 setup := func() { 405 // release records 406 rls0 := ReleaseTestData{Name: name, Version: 1, Status: rspb.StatusSuperseded}.ToRelease() 407 rls1 := ReleaseTestData{Name: name, Version: 2, Status: rspb.StatusDeployed}.ToRelease() 408 rls2 := ReleaseTestData{Name: name, Version: 3, Status: rspb.StatusFailed}.ToRelease() 409 rls3 := ReleaseTestData{Name: name, Version: 4, Status: rspb.StatusFailed}.ToRelease() 410 411 // create the release records in the storage 412 assertErrNil(t.Fatal, storage.Create(rls0), "Storing release 'angry-bird' (v1)") 413 assertErrNil(t.Fatal, storage.Create(rls1), "Storing release 'angry-bird' (v2)") 414 assertErrNil(t.Fatal, storage.Create(rls2), "Storing release 'angry-bird' (v3)") 415 assertErrNil(t.Fatal, storage.Create(rls3), "Storing release 'angry-bird' (v4)") 416 } 417 setup() 418 419 rls5 := ReleaseTestData{Name: name, Version: 5, Status: rspb.StatusFailed}.ToRelease() 420 assertErrNil(t.Fatal, storage.Create(rls5), "Storing release 'angry-bird' (v5)") 421 422 // On inserting the 5th record, we expect a total of 3 releases, but we expect version 2 423 // (the only deployed release), to still exist 424 hist, err := storage.History(name) 425 if err != nil { 426 t.Fatal(err) 427 } else if len(hist) != storage.MaxHistory { 428 for _, item := range hist { 429 t.Logf("%s %v", item.Name, item.Version) 430 } 431 t.Fatalf("expected %d items in history, got %d", storage.MaxHistory, len(hist)) 432 } 433 434 expectedVersions := map[int]bool{ 435 2: true, 436 4: true, 437 5: true, 438 } 439 440 for _, item := range hist { 441 if !expectedVersions[item.Version] { 442 t.Errorf("Release version %d, found when not expected", item.Version) 443 } 444 } 445 } 446 447 func TestStorageLast(t *testing.T) { 448 storage := Init(driver.NewMemory()) 449 450 const name = "angry-bird" 451 452 // Set up storage with test releases. 453 setup := func() { 454 // release records 455 rls0 := ReleaseTestData{Name: name, Version: 1, Status: rspb.StatusSuperseded}.ToRelease() 456 rls1 := ReleaseTestData{Name: name, Version: 2, Status: rspb.StatusSuperseded}.ToRelease() 457 rls2 := ReleaseTestData{Name: name, Version: 3, Status: rspb.StatusSuperseded}.ToRelease() 458 rls3 := ReleaseTestData{Name: name, Version: 4, Status: rspb.StatusFailed}.ToRelease() 459 460 // create the release records in the storage 461 assertErrNil(t.Fatal, storage.Create(rls0), "Storing release 'angry-bird' (v1)") 462 assertErrNil(t.Fatal, storage.Create(rls1), "Storing release 'angry-bird' (v2)") 463 assertErrNil(t.Fatal, storage.Create(rls2), "Storing release 'angry-bird' (v3)") 464 assertErrNil(t.Fatal, storage.Create(rls3), "Storing release 'angry-bird' (v4)") 465 } 466 467 setup() 468 469 h, err := storage.Last(name) 470 if err != nil { 471 t.Fatalf("Failed to query for release history (%q): %s\n", name, err) 472 } 473 474 if h.Version != 4 { 475 t.Errorf("Expected revision 4, got %d", h.Version) 476 } 477 } 478 479 // TestUpgradeInitiallyFailedRelease tests a case when there are no deployed release yet, but history limit has been 480 // reached: the has-no-deployed-releases error should not occur in such case. 481 func TestUpgradeInitiallyFailedReleaseWithHistoryLimit(t *testing.T) { 482 storage := Init(driver.NewMemory()) 483 storage.MaxHistory = 4 484 485 const name = "angry-bird" 486 487 // setup storage with test releases 488 setup := func() { 489 // release records 490 rls0 := ReleaseTestData{Name: name, Version: 1, Status: rspb.StatusFailed}.ToRelease() 491 rls1 := ReleaseTestData{Name: name, Version: 2, Status: rspb.StatusFailed}.ToRelease() 492 rls2 := ReleaseTestData{Name: name, Version: 3, Status: rspb.StatusFailed}.ToRelease() 493 rls3 := ReleaseTestData{Name: name, Version: 4, Status: rspb.StatusFailed}.ToRelease() 494 495 // create the release records in the storage 496 assertErrNil(t.Fatal, storage.Create(rls0), "Storing release 'angry-bird' (v1)") 497 assertErrNil(t.Fatal, storage.Create(rls1), "Storing release 'angry-bird' (v2)") 498 assertErrNil(t.Fatal, storage.Create(rls2), "Storing release 'angry-bird' (v3)") 499 assertErrNil(t.Fatal, storage.Create(rls3), "Storing release 'angry-bird' (v4)") 500 501 hist, err := storage.History(name) 502 if err != nil { 503 t.Fatalf("unexpected error: %s", err) 504 } 505 506 wantHistoryLen := 4 507 if len(hist) != wantHistoryLen { 508 t.Fatalf("expected history of release %q to contain %d releases, got %d", name, wantHistoryLen, len(hist)) 509 } 510 } 511 512 setup() 513 514 rls5 := ReleaseTestData{Name: name, Version: 5, Status: rspb.StatusFailed}.ToRelease() 515 err := storage.Create(rls5) 516 if err != nil { 517 t.Fatalf("Failed to create a new release version: %s", err) 518 } 519 520 hist, err := storage.History(name) 521 if err != nil { 522 t.Fatalf("unexpected error: %s", err) 523 } 524 525 for i, rel := range hist { 526 wantVersion := i + 2 527 if rel.Version != wantVersion { 528 t.Fatalf("Expected history release %d version to equal %d, got %d", i+1, wantVersion, rel.Version) 529 } 530 531 wantStatus := rspb.StatusFailed 532 if rel.Info.Status != wantStatus { 533 t.Fatalf("Expected history release %d status to equal %q, got %q", i+1, wantStatus, rel.Info.Status) 534 } 535 } 536 } 537 538 type ReleaseTestData struct { 539 Name string 540 Version int 541 Manifest string 542 Namespace string 543 Status rspb.Status 544 } 545 546 func (test ReleaseTestData) ToRelease() *rspb.Release { 547 return &rspb.Release{ 548 Name: test.Name, 549 Version: test.Version, 550 Manifest: test.Manifest, 551 Namespace: test.Namespace, 552 Info: &rspb.Info{Status: test.Status}, 553 } 554 } 555 556 func assertErrNil(eh func(args ...interface{}), err error, message string) { 557 if err != nil { 558 eh(fmt.Sprintf("%s: %q", message, err)) 559 } 560 }