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