github.com/demonoid81/containerd@v1.3.4/snapshots/testsuite/testsuite.go (about) 1 /* 2 Copyright The containerd 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 testsuite 18 19 import ( 20 "context" 21 "fmt" 22 "io/ioutil" 23 "math/rand" 24 "os" 25 "path/filepath" 26 "testing" 27 "time" 28 29 "github.com/containerd/containerd/errdefs" 30 "github.com/containerd/containerd/log/logtest" 31 "github.com/containerd/containerd/mount" 32 "github.com/containerd/containerd/namespaces" 33 "github.com/containerd/containerd/pkg/testutil" 34 "github.com/containerd/containerd/snapshots" 35 "github.com/containerd/continuity/fs/fstest" 36 "gotest.tools/assert" 37 is "gotest.tools/assert/cmp" 38 ) 39 40 // SnapshotterSuite runs a test suite on the snapshotter given a factory function. 41 func SnapshotterSuite(t *testing.T, name string, snapshotterFn func(ctx context.Context, root string) (snapshots.Snapshotter, func() error, error)) { 42 restoreMask := clearMask() 43 defer restoreMask() 44 45 t.Run("Basic", makeTest(name, snapshotterFn, checkSnapshotterBasic)) 46 t.Run("StatActive", makeTest(name, snapshotterFn, checkSnapshotterStatActive)) 47 t.Run("StatComitted", makeTest(name, snapshotterFn, checkSnapshotterStatCommitted)) 48 t.Run("TransitivityTest", makeTest(name, snapshotterFn, checkSnapshotterTransitivity)) 49 t.Run("PreareViewFailingtest", makeTest(name, snapshotterFn, checkSnapshotterPrepareView)) 50 t.Run("Update", makeTest(name, snapshotterFn, checkUpdate)) 51 t.Run("Remove", makeTest(name, snapshotterFn, checkRemove)) 52 53 t.Run("LayerFileupdate", makeTest(name, snapshotterFn, checkLayerFileUpdate)) 54 t.Run("RemoveDirectoryInLowerLayer", makeTest(name, snapshotterFn, checkRemoveDirectoryInLowerLayer)) 55 t.Run("Chown", makeTest(name, snapshotterFn, checkChown)) 56 t.Run("DirectoryPermissionOnCommit", makeTest(name, snapshotterFn, checkDirectoryPermissionOnCommit)) 57 t.Run("RemoveIntermediateSnapshot", makeTest(name, snapshotterFn, checkRemoveIntermediateSnapshot)) 58 t.Run("DeletedFilesInChildSnapshot", makeTest(name, snapshotterFn, checkDeletedFilesInChildSnapshot)) 59 t.Run("MoveFileFromLowerLayer", makeTest(name, snapshotterFn, checkFileFromLowerLayer)) 60 t.Run("Rename", makeTest(name, snapshotterFn, checkRename)) 61 62 t.Run("ViewReadonly", makeTest(name, snapshotterFn, checkSnapshotterViewReadonly)) 63 64 t.Run("StatInWalk", makeTest(name, snapshotterFn, checkStatInWalk)) 65 t.Run("CloseTwice", makeTest(name, snapshotterFn, closeTwice)) 66 t.Run("RootPermission", makeTest(name, snapshotterFn, checkRootPermission)) 67 68 t.Run("128LayersMount", makeTest(name, snapshotterFn, check128LayersMount(name))) 69 } 70 71 func makeTest(name string, snapshotterFn func(ctx context.Context, root string) (snapshots.Snapshotter, func() error, error), fn func(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string)) func(t *testing.T) { 72 return func(t *testing.T) { 73 t.Parallel() 74 75 ctx := logtest.WithT(context.Background(), t) 76 ctx = namespaces.WithNamespace(ctx, "testsuite") 77 // Make two directories: a snapshotter root and a play area for the tests: 78 // 79 // /tmp 80 // work/ -> passed to test functions 81 // root/ -> passed to snapshotter 82 // 83 tmpDir, err := ioutil.TempDir("", "snapshot-suite-"+name+"-") 84 if err != nil { 85 t.Fatal(err) 86 } 87 defer os.RemoveAll(tmpDir) 88 89 root := filepath.Join(tmpDir, "root") 90 if err := os.MkdirAll(root, 0777); err != nil { 91 t.Fatal(err) 92 } 93 94 snapshotter, cleanup, err := snapshotterFn(ctx, root) 95 if err != nil { 96 t.Fatalf("Failed to initialize snapshotter: %+v", err) 97 } 98 defer func() { 99 if cleanup != nil { 100 if err := cleanup(); err != nil { 101 t.Errorf("Cleanup failed: %v", err) 102 } 103 } 104 }() 105 106 work := filepath.Join(tmpDir, "work") 107 if err := os.MkdirAll(work, 0777); err != nil { 108 t.Fatal(err) 109 } 110 111 defer testutil.DumpDirOnFailure(t, tmpDir) 112 fn(ctx, t, snapshotter, work) 113 } 114 } 115 116 var opt = snapshots.WithLabels(map[string]string{ 117 "containerd.io/gc.root": time.Now().UTC().Format(time.RFC3339), 118 }) 119 120 // checkSnapshotterBasic tests the basic workflow of a snapshot snapshotter. 121 func checkSnapshotterBasic(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) { 122 initialApplier := fstest.Apply( 123 fstest.CreateFile("/foo", []byte("foo\n"), 0777), 124 fstest.CreateDir("/a", 0755), 125 fstest.CreateDir("/a/b", 0755), 126 fstest.CreateDir("/a/b/c", 0755), 127 ) 128 129 diffApplier := fstest.Apply( 130 fstest.CreateFile("/bar", []byte("bar\n"), 0777), 131 // also, change content of foo to bar 132 fstest.CreateFile("/foo", []byte("bar\n"), 0777), 133 fstest.RemoveAll("/a/b"), 134 ) 135 136 preparing := filepath.Join(work, "preparing") 137 if err := os.MkdirAll(preparing, 0777); err != nil { 138 t.Fatalf("failure reason: %+v", err) 139 } 140 141 mounts, err := snapshotter.Prepare(ctx, preparing, "", opt) 142 if err != nil { 143 t.Fatalf("failure reason: %+v", err) 144 } 145 146 if len(mounts) < 1 { 147 t.Fatal("expected mounts to have entries") 148 } 149 150 if err := mount.All(mounts, preparing); err != nil { 151 t.Fatalf("failure reason: %+v", err) 152 } 153 154 if err := initialApplier.Apply(preparing); err != nil { 155 t.Fatalf("failure reason: %+v", err) 156 } 157 // unmount before commit 158 testutil.Unmount(t, preparing) 159 160 committed := filepath.Join(work, "committed") 161 if err := snapshotter.Commit(ctx, committed, preparing, opt); err != nil { 162 t.Fatalf("failure reason: %+v", err) 163 } 164 165 si, err := snapshotter.Stat(ctx, committed) 166 if err != nil { 167 t.Fatalf("failure reason: %+v", err) 168 } 169 170 assert.Check(t, is.Equal("", si.Parent)) 171 assert.Check(t, is.Equal(snapshots.KindCommitted, si.Kind)) 172 173 _, err = snapshotter.Stat(ctx, preparing) 174 if err == nil { 175 t.Fatalf("%s should no longer be available after Commit", preparing) 176 } 177 178 next := filepath.Join(work, "nextlayer") 179 if err := os.MkdirAll(next, 0777); err != nil { 180 t.Fatalf("failure reason: %+v", err) 181 } 182 183 mounts, err = snapshotter.Prepare(ctx, next, committed, opt) 184 if err != nil { 185 t.Fatalf("failure reason: %+v", err) 186 } 187 if err := mount.All(mounts, next); err != nil { 188 t.Fatalf("failure reason: %+v", err) 189 } 190 191 if err := fstest.CheckDirectoryEqualWithApplier(next, initialApplier); err != nil { 192 t.Fatalf("failure reason: %+v", err) 193 } 194 195 if err := diffApplier.Apply(next); err != nil { 196 t.Fatalf("failure reason: %+v", err) 197 } 198 // unmount before commit 199 testutil.Unmount(t, next) 200 201 ni, err := snapshotter.Stat(ctx, next) 202 if err != nil { 203 t.Fatal(err) 204 } 205 206 assert.Check(t, is.Equal(committed, ni.Parent)) 207 assert.Check(t, is.Equal(snapshots.KindActive, ni.Kind)) 208 209 nextCommitted := filepath.Join(work, "committed-next") 210 if err := snapshotter.Commit(ctx, nextCommitted, next, opt); err != nil { 211 t.Fatalf("failure reason: %+v", err) 212 } 213 214 si2, err := snapshotter.Stat(ctx, nextCommitted) 215 if err != nil { 216 t.Fatalf("failure reason: %+v", err) 217 } 218 219 assert.Check(t, is.Equal(committed, si2.Parent)) 220 assert.Check(t, is.Equal(snapshots.KindCommitted, si2.Kind)) 221 222 _, err = snapshotter.Stat(ctx, next) 223 if err == nil { 224 t.Fatalf("%s should no longer be available after Commit", next) 225 } 226 227 expected := map[string]snapshots.Info{ 228 si.Name: si, 229 si2.Name: si2, 230 } 231 walked := map[string]snapshots.Info{} // walk is not ordered 232 assert.NilError(t, snapshotter.Walk(ctx, func(ctx context.Context, si snapshots.Info) error { 233 walked[si.Name] = si 234 return nil 235 })) 236 237 for ek, ev := range expected { 238 av, ok := walked[ek] 239 if !ok { 240 t.Errorf("Missing stat for %v", ek) 241 continue 242 } 243 assert.Check(t, is.DeepEqual(ev, av)) 244 } 245 246 nextnext := filepath.Join(work, "nextnextlayer") 247 if err := os.MkdirAll(nextnext, 0777); err != nil { 248 t.Fatalf("failure reason: %+v", err) 249 } 250 251 mounts, err = snapshotter.View(ctx, nextnext, nextCommitted, opt) 252 if err != nil { 253 t.Fatalf("failure reason: %+v", err) 254 } 255 if err := mount.All(mounts, nextnext); err != nil { 256 t.Fatalf("failure reason: %+v", err) 257 } 258 259 if err := fstest.CheckDirectoryEqualWithApplier(nextnext, 260 fstest.Apply(initialApplier, diffApplier)); err != nil { 261 testutil.Unmount(t, nextnext) 262 t.Fatalf("failure reason: %+v", err) 263 } 264 265 testutil.Unmount(t, nextnext) 266 assert.NilError(t, snapshotter.Remove(ctx, nextnext)) 267 assert.Assert(t, is.ErrorContains(snapshotter.Remove(ctx, committed), "remove")) 268 assert.NilError(t, snapshotter.Remove(ctx, nextCommitted)) 269 assert.NilError(t, snapshotter.Remove(ctx, committed)) 270 } 271 272 // Create a New Layer on top of base layer with Prepare, Stat on new layer, should return Active layer. 273 func checkSnapshotterStatActive(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) { 274 preparing := filepath.Join(work, "preparing") 275 if err := os.MkdirAll(preparing, 0777); err != nil { 276 t.Fatal(err) 277 } 278 279 mounts, err := snapshotter.Prepare(ctx, preparing, "", opt) 280 if err != nil { 281 t.Fatal(err) 282 } 283 284 if len(mounts) < 1 { 285 t.Fatal("expected mounts to have entries") 286 } 287 288 if err = mount.All(mounts, preparing); err != nil { 289 t.Fatal(err) 290 } 291 defer testutil.Unmount(t, preparing) 292 293 if err = ioutil.WriteFile(filepath.Join(preparing, "foo"), []byte("foo\n"), 0777); err != nil { 294 t.Fatal(err) 295 } 296 297 si, err := snapshotter.Stat(ctx, preparing) 298 if err != nil { 299 t.Fatal(err) 300 } 301 assert.Check(t, is.Equal(si.Name, preparing)) 302 assert.Check(t, is.Equal(snapshots.KindActive, si.Kind)) 303 assert.Check(t, is.Equal("", si.Parent)) 304 } 305 306 // Commit a New Layer on top of base layer with Prepare & Commit , Stat on new layer, should return Committed layer. 307 func checkSnapshotterStatCommitted(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) { 308 preparing := filepath.Join(work, "preparing") 309 if err := os.MkdirAll(preparing, 0777); err != nil { 310 t.Fatal(err) 311 } 312 313 mounts, err := snapshotter.Prepare(ctx, preparing, "", opt) 314 if err != nil { 315 t.Fatal(err) 316 } 317 318 if len(mounts) < 1 { 319 t.Fatal("expected mounts to have entries") 320 } 321 322 if err = mount.All(mounts, preparing); err != nil { 323 t.Fatal(err) 324 } 325 defer testutil.Unmount(t, preparing) 326 327 if err = ioutil.WriteFile(filepath.Join(preparing, "foo"), []byte("foo\n"), 0777); err != nil { 328 t.Fatal(err) 329 } 330 331 committed := filepath.Join(work, "committed") 332 if err = snapshotter.Commit(ctx, committed, preparing, opt); err != nil { 333 t.Fatal(err) 334 } 335 336 si, err := snapshotter.Stat(ctx, committed) 337 if err != nil { 338 t.Fatal(err) 339 } 340 assert.Check(t, is.Equal(si.Name, committed)) 341 assert.Check(t, is.Equal(snapshots.KindCommitted, si.Kind)) 342 assert.Check(t, is.Equal("", si.Parent)) 343 344 } 345 346 func snapshotterPrepareMount(ctx context.Context, snapshotter snapshots.Snapshotter, diffPathName string, parent string, work string) (string, error) { 347 preparing := filepath.Join(work, diffPathName) 348 if err := os.MkdirAll(preparing, 0777); err != nil { 349 return "", err 350 } 351 352 mounts, err := snapshotter.Prepare(ctx, preparing, parent, opt) 353 if err != nil { 354 return "", err 355 } 356 357 if len(mounts) < 1 { 358 return "", fmt.Errorf("expected mounts to have entries") 359 } 360 361 if err = mount.All(mounts, preparing); err != nil { 362 return "", err 363 } 364 return preparing, nil 365 } 366 367 // Given A <- B <- C, B is the parent of C and A is a transitive parent of C (in this case, a "grandparent") 368 func checkSnapshotterTransitivity(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) { 369 preparing, err := snapshotterPrepareMount(ctx, snapshotter, "preparing", "", work) 370 if err != nil { 371 t.Fatal(err) 372 } 373 defer testutil.Unmount(t, preparing) 374 375 if err = ioutil.WriteFile(filepath.Join(preparing, "foo"), []byte("foo\n"), 0777); err != nil { 376 t.Fatal(err) 377 } 378 379 snapA := filepath.Join(work, "snapA") 380 if err = snapshotter.Commit(ctx, snapA, preparing, opt); err != nil { 381 t.Fatal(err) 382 } 383 384 next, err := snapshotterPrepareMount(ctx, snapshotter, "next", snapA, work) 385 if err != nil { 386 t.Fatal(err) 387 } 388 defer testutil.Unmount(t, next) 389 390 if err = ioutil.WriteFile(filepath.Join(next, "foo"), []byte("foo bar\n"), 0777); err != nil { 391 t.Fatal(err) 392 } 393 394 snapB := filepath.Join(work, "snapB") 395 if err = snapshotter.Commit(ctx, snapB, next, opt); err != nil { 396 t.Fatal(err) 397 } 398 399 siA, err := snapshotter.Stat(ctx, snapA) 400 if err != nil { 401 t.Fatal(err) 402 } 403 404 siB, err := snapshotter.Stat(ctx, snapB) 405 if err != nil { 406 t.Fatal(err) 407 } 408 409 siParentB, err := snapshotter.Stat(ctx, siB.Parent) 410 if err != nil { 411 t.Fatal(err) 412 } 413 414 // Test the transivity 415 assert.Check(t, is.Equal("", siA.Parent)) 416 assert.Check(t, is.Equal(snapA, siB.Parent)) 417 assert.Check(t, is.Equal("", siParentB.Parent)) 418 419 } 420 421 // Creating two layers with Prepare or View with same key must fail. 422 func checkSnapshotterPrepareView(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) { 423 preparing, err := snapshotterPrepareMount(ctx, snapshotter, "preparing", "", work) 424 if err != nil { 425 t.Fatal(err) 426 } 427 defer testutil.Unmount(t, preparing) 428 429 snapA := filepath.Join(work, "snapA") 430 if err = snapshotter.Commit(ctx, snapA, preparing, opt); err != nil { 431 t.Fatal(err) 432 } 433 434 // Prepare & View with same key 435 newLayer := filepath.Join(work, "newlayer") 436 if err = os.MkdirAll(preparing, 0777); err != nil { 437 t.Fatal(err) 438 } 439 440 // Prepare & View with same key 441 _, err = snapshotter.Prepare(ctx, newLayer, snapA, opt) 442 if err != nil { 443 t.Fatal(err) 444 } 445 446 _, err = snapshotter.View(ctx, newLayer, snapA, opt) 447 assert.Check(t, err != nil) 448 449 // Two Prepare with same key 450 prepLayer := filepath.Join(work, "prepLayer") 451 if err = os.MkdirAll(preparing, 0777); err != nil { 452 t.Fatal(err) 453 } 454 455 _, err = snapshotter.Prepare(ctx, prepLayer, snapA, opt) 456 if err != nil { 457 t.Fatal(err) 458 } 459 460 _, err = snapshotter.Prepare(ctx, prepLayer, snapA, opt) 461 assert.Check(t, err != nil) 462 463 // Two View with same key 464 viewLayer := filepath.Join(work, "viewLayer") 465 if err = os.MkdirAll(preparing, 0777); err != nil { 466 t.Fatal(err) 467 } 468 469 _, err = snapshotter.View(ctx, viewLayer, snapA, opt) 470 if err != nil { 471 t.Fatal(err) 472 } 473 474 _, err = snapshotter.View(ctx, viewLayer, snapA, opt) 475 assert.Check(t, err != nil) 476 477 } 478 479 // Deletion of files/folder of base layer in new layer, On Commit, those files should not be visible. 480 func checkDeletedFilesInChildSnapshot(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) { 481 482 l1Init := fstest.Apply( 483 fstest.CreateFile("/foo", []byte("foo\n"), 0777), 484 fstest.CreateFile("/foobar", []byte("foobar\n"), 0777), 485 ) 486 l2Init := fstest.Apply( 487 fstest.RemoveAll("/foobar"), 488 ) 489 l3Init := fstest.Apply() 490 491 if err := checkSnapshots(ctx, snapshotter, work, l1Init, l2Init, l3Init); err != nil { 492 t.Fatalf("Check snapshots failed: %+v", err) 493 } 494 495 } 496 497 //Create three layers. Deleting intermediate layer must fail. 498 func checkRemoveIntermediateSnapshot(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) { 499 500 base, err := snapshotterPrepareMount(ctx, snapshotter, "base", "", work) 501 if err != nil { 502 t.Fatal(err) 503 } 504 505 committedBase := filepath.Join(work, "committed-base") 506 if err = snapshotter.Commit(ctx, committedBase, base, opt); err != nil { 507 t.Fatal(err) 508 } 509 510 // Create intermediate layer 511 intermediate := filepath.Join(work, "intermediate") 512 if _, err = snapshotter.Prepare(ctx, intermediate, committedBase, opt); err != nil { 513 t.Fatal(err) 514 } 515 516 committedInter := filepath.Join(work, "committed-inter") 517 if err = snapshotter.Commit(ctx, committedInter, intermediate, opt); err != nil { 518 t.Fatal(err) 519 } 520 521 // Create top layer 522 topLayer := filepath.Join(work, "toplayer") 523 if _, err = snapshotter.Prepare(ctx, topLayer, committedInter, opt); err != nil { 524 t.Fatal(err) 525 } 526 527 // Deletion of intermediate layer must fail. 528 err = snapshotter.Remove(ctx, committedInter) 529 if err == nil { 530 t.Fatal("intermediate layer removal should fail.") 531 } 532 533 //Removal from toplayer to base should not fail. 534 err = snapshotter.Remove(ctx, topLayer) 535 if err != nil { 536 t.Fatal(err) 537 } 538 err = snapshotter.Remove(ctx, committedInter) 539 if err != nil { 540 t.Fatal(err) 541 } 542 testutil.Unmount(t, base) 543 err = snapshotter.Remove(ctx, committedBase) 544 if err != nil { 545 t.Fatal(err) 546 } 547 } 548 549 // baseTestSnapshots creates a base set of snapshots for tests, each snapshot is empty 550 // Tests snapshots: 551 // c1 - committed snapshot, no parent 552 // c2 - committed snapshot, c1 is parent 553 // a1 - active snapshot, c2 is parent 554 // a1 - active snapshot, no parent 555 // v1 - view snapshot, v1 is parent 556 // v2 - view snapshot, no parent 557 func baseTestSnapshots(ctx context.Context, snapshotter snapshots.Snapshotter) error { 558 if _, err := snapshotter.Prepare(ctx, "c1-a", "", opt); err != nil { 559 return err 560 } 561 if err := snapshotter.Commit(ctx, "c1", "c1-a", opt); err != nil { 562 return err 563 } 564 if _, err := snapshotter.Prepare(ctx, "c2-a", "c1", opt); err != nil { 565 return err 566 } 567 if err := snapshotter.Commit(ctx, "c2", "c2-a", opt); err != nil { 568 return err 569 } 570 if _, err := snapshotter.Prepare(ctx, "a1", "c2", opt); err != nil { 571 return err 572 } 573 if _, err := snapshotter.Prepare(ctx, "a2", "", opt); err != nil { 574 return err 575 } 576 if _, err := snapshotter.View(ctx, "v1", "c2", opt); err != nil { 577 return err 578 } 579 if _, err := snapshotter.View(ctx, "v2", "", opt); err != nil { 580 return err 581 } 582 return nil 583 } 584 585 func checkUpdate(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) { 586 t1 := time.Now().UTC() 587 if err := baseTestSnapshots(ctx, snapshotter); err != nil { 588 t.Fatalf("Failed to create base snapshots: %v", err) 589 } 590 t2 := time.Now().UTC() 591 testcases := []struct { 592 name string 593 kind snapshots.Kind 594 parent string 595 }{ 596 { 597 name: "c1", 598 kind: snapshots.KindCommitted, 599 }, 600 { 601 name: "c2", 602 kind: snapshots.KindCommitted, 603 parent: "c1", 604 }, 605 { 606 name: "a1", 607 kind: snapshots.KindActive, 608 parent: "c2", 609 }, 610 { 611 name: "a2", 612 kind: snapshots.KindActive, 613 }, 614 { 615 name: "v1", 616 kind: snapshots.KindView, 617 parent: "c2", 618 }, 619 { 620 name: "v2", 621 kind: snapshots.KindView, 622 }, 623 } 624 for _, tc := range testcases { 625 st, err := snapshotter.Stat(ctx, tc.name) 626 if err != nil { 627 t.Fatalf("Failed to stat %s: %v", tc.name, err) 628 } 629 if st.Created.Before(t1) || st.Created.After(t2) { 630 t.Errorf("(%s) wrong created time %s: expected between %s and %s", tc.name, st.Created, t1, t2) 631 continue 632 } 633 if st.Created != st.Updated { 634 t.Errorf("(%s) unexpected updated time %s: expected %s", tc.name, st.Updated, st.Created) 635 continue 636 } 637 if st.Kind != tc.kind { 638 t.Errorf("(%s) unexpected kind %s, expected %s", tc.name, st.Kind, tc.kind) 639 continue 640 } 641 if st.Parent != tc.parent { 642 t.Errorf("(%s) unexpected parent %q, expected %q", tc.name, st.Parent, tc.parent) 643 continue 644 } 645 if st.Name != tc.name { 646 t.Errorf("(%s) unexpected name %q, expected %q", tc.name, st.Name, tc.name) 647 continue 648 } 649 650 createdAt := st.Created 651 rootTime := time.Now().UTC().Format(time.RFC3339) 652 expected := map[string]string{ 653 "l1": "v1", 654 "l2": "v2", 655 "l3": "v3", 656 // Keep root label 657 "containerd.io/gc.root": rootTime, 658 } 659 st.Parent = "doesnotexist" 660 st.Labels = expected 661 u1 := time.Now().UTC() 662 st, err = snapshotter.Update(ctx, st) 663 if err != nil { 664 t.Fatalf("Failed to update %s: %v", tc.name, err) 665 } 666 u2 := time.Now().UTC() 667 668 if st.Created != createdAt { 669 t.Errorf("(%s) wrong created time %s: expected %s", tc.name, st.Created, createdAt) 670 continue 671 } 672 if st.Updated.Before(u1) || st.Updated.After(u2) { 673 t.Errorf("(%s) wrong updated time %s: expected between %s and %s", tc.name, st.Updated, u1, u2) 674 continue 675 } 676 if st.Kind != tc.kind { 677 t.Errorf("(%s) unexpected kind %s, expected %s", tc.name, st.Kind, tc.kind) 678 continue 679 } 680 if st.Parent != tc.parent { 681 t.Errorf("(%s) unexpected parent %q, expected %q", tc.name, st.Parent, tc.parent) 682 continue 683 } 684 if st.Name != tc.name { 685 t.Errorf("(%s) unexpected name %q, expected %q", tc.name, st.Name, tc.name) 686 continue 687 } 688 assertLabels(t, st.Labels, expected) 689 690 expected = map[string]string{ 691 "l1": "updated", 692 "l3": "v3", 693 694 "containerd.io/gc.root": rootTime, 695 } 696 st.Labels = map[string]string{ 697 "l1": "updated", 698 "l4": "v4", 699 } 700 st, err = snapshotter.Update(ctx, st, "labels.l1", "labels.l2") 701 if err != nil { 702 t.Fatalf("Failed to update %s: %v", tc.name, err) 703 } 704 assertLabels(t, st.Labels, expected) 705 706 expected = map[string]string{ 707 "l4": "v4", 708 709 "containerd.io/gc.root": rootTime, 710 } 711 st.Labels = expected 712 st, err = snapshotter.Update(ctx, st, "labels") 713 if err != nil { 714 t.Fatalf("Failed to update %s: %v", tc.name, err) 715 } 716 assertLabels(t, st.Labels, expected) 717 718 // Test failure received when providing immutable field path 719 st.Parent = "doesnotexist" 720 st, err = snapshotter.Update(ctx, st, "parent") 721 if err == nil { 722 t.Errorf("Expected error updating with immutable field path") 723 } else if !errdefs.IsInvalidArgument(err) { 724 t.Fatalf("Unexpected error updating %s: %+v", tc.name, err) 725 } 726 } 727 } 728 729 func assertLabels(t *testing.T, actual, expected map[string]string) { 730 if len(actual) != len(expected) { 731 t.Fatalf("Label size mismatch: %d vs %d\n\tActual: %#v\n\tExpected: %#v", len(actual), len(expected), actual, expected) 732 } 733 for k, v := range expected { 734 if a := actual[k]; v != a { 735 t.Errorf("Wrong label value for %s, got %q, expected %q", k, a, v) 736 } 737 } 738 if t.Failed() { 739 t.FailNow() 740 } 741 } 742 743 func checkRemove(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) { 744 if _, err := snapshotter.Prepare(ctx, "committed-a", "", opt); err != nil { 745 t.Fatal(err) 746 } 747 if err := snapshotter.Commit(ctx, "committed-1", "committed-a", opt); err != nil { 748 t.Fatal(err) 749 } 750 if _, err := snapshotter.Prepare(ctx, "reuse-1", "committed-1", opt); err != nil { 751 t.Fatal(err) 752 } 753 if err := snapshotter.Remove(ctx, "reuse-1"); err != nil { 754 t.Fatal(err) 755 } 756 if _, err := snapshotter.View(ctx, "reuse-1", "committed-1", opt); err != nil { 757 t.Fatal(err) 758 } 759 if err := snapshotter.Remove(ctx, "reuse-1"); err != nil { 760 t.Fatal(err) 761 } 762 if _, err := snapshotter.Prepare(ctx, "reuse-1", "", opt); err != nil { 763 t.Fatal(err) 764 } 765 if err := snapshotter.Remove(ctx, "committed-1"); err != nil { 766 t.Fatal(err) 767 } 768 if err := snapshotter.Commit(ctx, "committed-1", "reuse-1", opt); err != nil { 769 t.Fatal(err) 770 } 771 } 772 773 // checkSnapshotterViewReadonly ensures a KindView snapshot to be mounted as a read-only filesystem. 774 // This function is called only when WithTestViewReadonly is true. 775 func checkSnapshotterViewReadonly(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) { 776 preparing := filepath.Join(work, "preparing") 777 if _, err := snapshotter.Prepare(ctx, preparing, "", opt); err != nil { 778 t.Fatal(err) 779 } 780 committed := filepath.Join(work, "committed") 781 if err := snapshotter.Commit(ctx, committed, preparing, opt); err != nil { 782 t.Fatal(err) 783 } 784 view := filepath.Join(work, "view") 785 m, err := snapshotter.View(ctx, view, committed, opt) 786 if err != nil { 787 t.Fatal(err) 788 } 789 viewMountPoint := filepath.Join(work, "view-mount") 790 if err := os.MkdirAll(viewMountPoint, 0777); err != nil { 791 t.Fatal(err) 792 } 793 794 // Just checking the option string of m is not enough, we need to test real mount. (#1368) 795 if err := mount.All(m, viewMountPoint); err != nil { 796 t.Fatal(err) 797 } 798 799 testfile := filepath.Join(viewMountPoint, "testfile") 800 if err := ioutil.WriteFile(testfile, []byte("testcontent"), 0777); err != nil { 801 t.Logf("write to %q failed with %v (EROFS is expected but can be other error code)", testfile, err) 802 } else { 803 t.Fatalf("write to %q should fail (EROFS) but did not fail", testfile) 804 } 805 testutil.Unmount(t, viewMountPoint) 806 assert.NilError(t, snapshotter.Remove(ctx, view)) 807 assert.NilError(t, snapshotter.Remove(ctx, committed)) 808 } 809 810 // Move files from base layer to new location in intermediate layer. 811 // Verify if the file at source is deleted and copied to new location. 812 func checkFileFromLowerLayer(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) { 813 l1Init := fstest.Apply( 814 fstest.CreateDir("/dir1", 0700), 815 fstest.CreateFile("/dir1/f1", []byte("Hello"), 0644), 816 fstest.CreateDir("dir2", 0700), 817 fstest.CreateFile("dir2/f2", []byte("..."), 0644), 818 ) 819 l2Init := fstest.Apply( 820 fstest.CreateDir("/dir3", 0700), 821 fstest.CreateFile("/dir3/f1", []byte("Hello"), 0644), 822 fstest.RemoveAll("/dir1"), 823 fstest.Link("dir2/f2", "dir3/f2"), 824 fstest.RemoveAll("dir2/f2"), 825 ) 826 827 if err := checkSnapshots(ctx, snapshotter, work, l1Init, l2Init); err != nil { 828 t.Fatalf("Check snapshots failed: %+v", err) 829 } 830 } 831 832 func closeTwice(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) { 833 n := fmt.Sprintf("closeTwice-%d", rand.Int()) 834 prepare := fmt.Sprintf("%s-prepare", n) 835 836 // do some dummy ops to modify the snapshotter internal state 837 if _, err := snapshotter.Prepare(ctx, prepare, "", opt); err != nil { 838 t.Fatal(err) 839 } 840 if err := snapshotter.Commit(ctx, n, prepare, opt); err != nil { 841 t.Fatal(err) 842 } 843 if err := snapshotter.Remove(ctx, n); err != nil { 844 t.Fatal(err) 845 } 846 if err := snapshotter.Close(); err != nil { 847 t.Fatalf("The first close failed: %+v", err) 848 } 849 if err := snapshotter.Close(); err != nil { 850 t.Fatalf("The second close failed: %+v", err) 851 } 852 } 853 854 func checkRootPermission(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) { 855 preparing, err := snapshotterPrepareMount(ctx, snapshotter, "preparing", "", work) 856 if err != nil { 857 t.Fatal(err) 858 } 859 defer testutil.Unmount(t, preparing) 860 st, err := os.Stat(preparing) 861 if err != nil { 862 t.Fatal(err) 863 } 864 if mode := st.Mode() & 0777; mode != 0755 { 865 t.Fatalf("expected 0755, got 0%o", mode) 866 } 867 } 868 869 func check128LayersMount(name string) func(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) { 870 return func(ctx context.Context, t *testing.T, snapshotter snapshots.Snapshotter, work string) { 871 if name == "Aufs" { 872 t.Skip("aufs tests have issues with whiteouts here on some CI kernels") 873 } 874 lowestApply := fstest.Apply( 875 fstest.CreateFile("/bottom", []byte("way at the bottom\n"), 0777), 876 fstest.CreateFile("/overwriteme", []byte("FIRST!\n"), 0777), 877 fstest.CreateDir("/ADDHERE", 0755), 878 fstest.CreateDir("/ONLYME", 0755), 879 fstest.CreateFile("/ONLYME/bottom", []byte("bye!\n"), 0777), 880 ) 881 882 appliers := []fstest.Applier{lowestApply} 883 for i := 1; i <= 127; i++ { 884 appliers = append(appliers, fstest.Apply( 885 fstest.CreateFile("/overwriteme", []byte(fmt.Sprintf("%d WAS HERE!\n", i)), 0777), 886 fstest.CreateFile(fmt.Sprintf("/ADDHERE/file-%d", i), []byte("same\n"), 0755), 887 fstest.RemoveAll("/ONLYME"), 888 fstest.CreateDir("/ONLYME", 0755), 889 fstest.CreateFile(fmt.Sprintf("/ONLYME/file-%d", i), []byte("only me!\n"), 0777), 890 )) 891 } 892 893 flat := filepath.Join(work, "flat") 894 if err := os.MkdirAll(flat, 0777); err != nil { 895 t.Fatalf("failed to create flat dir(%s): %+v", flat, err) 896 } 897 898 // NOTE: add gc labels to avoid snapshots get removed by gc... 899 parent := "" 900 for i, applier := range appliers { 901 preparing := filepath.Join(work, fmt.Sprintf("prepare-layer-%d", i)) 902 if err := os.MkdirAll(preparing, 0777); err != nil { 903 t.Fatalf("[layer %d] failed to create preparing dir(%s): %+v", i, preparing, err) 904 } 905 906 mounts, err := snapshotter.Prepare(ctx, preparing, parent, opt) 907 if err != nil { 908 t.Fatalf("[layer %d] failed to get mount info: %+v", i, err) 909 } 910 911 if err := mount.All(mounts, preparing); err != nil { 912 t.Fatalf("[layer %d] failed to mount on the target(%s): %+v", i, preparing, err) 913 } 914 915 if err := fstest.CheckDirectoryEqual(preparing, flat); err != nil { 916 testutil.Unmount(t, preparing) 917 t.Fatalf("[layer %d] preparing doesn't equal to flat before apply: %+v", i, err) 918 } 919 920 if err := applier.Apply(flat); err != nil { 921 testutil.Unmount(t, preparing) 922 t.Fatalf("[layer %d] failed to apply on flat dir: %+v", i, err) 923 } 924 925 if err = applier.Apply(preparing); err != nil { 926 testutil.Unmount(t, preparing) 927 t.Fatalf("[layer %d] failed to apply on preparing dir: %+v", i, err) 928 } 929 930 if err := fstest.CheckDirectoryEqual(preparing, flat); err != nil { 931 testutil.Unmount(t, preparing) 932 t.Fatalf("[layer %d] preparing doesn't equal to flat after apply: %+v", i, err) 933 } 934 935 testutil.Unmount(t, preparing) 936 937 parent = filepath.Join(work, fmt.Sprintf("committed-%d", i)) 938 if err := snapshotter.Commit(ctx, parent, preparing, opt); err != nil { 939 t.Fatalf("[layer %d] failed to commit the preparing: %+v", i, err) 940 } 941 942 } 943 944 view := filepath.Join(work, "fullview") 945 if err := os.MkdirAll(view, 0777); err != nil { 946 t.Fatalf("failed to create fullview dir(%s): %+v", view, err) 947 } 948 949 mounts, err := snapshotter.View(ctx, view, parent, opt) 950 if err != nil { 951 t.Fatalf("failed to get view's mount info: %+v", err) 952 } 953 954 if err := mount.All(mounts, view); err != nil { 955 t.Fatalf("failed to mount on the target(%s): %+v", view, err) 956 } 957 defer testutil.Unmount(t, view) 958 959 if err := fstest.CheckDirectoryEqual(view, flat); err != nil { 960 t.Fatalf("fullview should equal to flat: %+v", err) 961 } 962 } 963 }