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