package testsuite import ( "context" "fmt" "io/ioutil" "math/rand" "os" "github.com/containerd/containerd/fs/fstest" "github.com/containerd/containerd/mount" "github.com/containerd/containerd/snapshots" "github.com/pkg/errors" ) func applyToMounts(m []mount.Mount, work string, a fstest.Applier) (err error) { td, err := ioutil.TempDir(work, "prepare") if err != nil { return errors.Wrap(err, "failed to create temp dir") } defer os.RemoveAll(td) if err := mount.All(m, td); err != nil { return errors.Wrap(err, "failed to mount") } defer func() { if err1 := mount.UnmountAll(td, umountflags); err == nil { err = errors.Wrap(err1, "failed to unmount") } }() return a.Apply(td) } // createSnapshot creates a new snapshot in the snapshotter // given an applier to run on top of the given parent. func createSnapshot(ctx context.Context, sn snapshots.Snapshotter, parent, work string, a fstest.Applier) (string, error) { n := fmt.Sprintf("%p-%d", a, rand.Int()) prepare := fmt.Sprintf("%s-prepare", n) m, err := sn.Prepare(ctx, prepare, parent, opt) if err != nil { return "", errors.Wrap(err, "failed to prepare snapshot") } if err := applyToMounts(m, work, a); err != nil { return "", errors.Wrap(err, "failed to apply") } if err := sn.Commit(ctx, n, prepare, opt); err != nil { return "", errors.Wrap(err, "failed to commit") } return n, nil } func checkSnapshot(ctx context.Context, sn snapshots.Snapshotter, work, name, check string) (err error) { td, err := ioutil.TempDir(work, "check") if err != nil { return errors.Wrap(err, "failed to create temp dir") } defer func() { if err1 := os.RemoveAll(td); err == nil { err = errors.Wrapf(err1, "failed to remove temporary directory %s", td) } }() view := fmt.Sprintf("%s-view", name) m, err := sn.View(ctx, view, name, opt) if err != nil { return errors.Wrap(err, "failed to create view") } defer func() { if err1 := sn.Remove(ctx, view); err == nil { err = errors.Wrap(err1, "failed to remove view") } }() if err := mount.All(m, td); err != nil { return errors.Wrap(err, "failed to mount") } defer func() { if err1 := mount.UnmountAll(td, umountflags); err == nil { err = errors.Wrap(err1, "failed to unmount view") } }() if err := fstest.CheckDirectoryEqual(check, td); err != nil { return errors.Wrap(err, "check directory failed") } return nil } // checkSnapshots creates a new chain of snapshots in the given snapshotter // using the provided appliers, checking each snapshot created in a view // against the changes applied to a single directory. func checkSnapshots(ctx context.Context, sn snapshots.Snapshotter, work string, as ...fstest.Applier) error { td, err := ioutil.TempDir(work, "flat") if err != nil { return errors.Wrap(err, "failed to create temp dir") } defer os.RemoveAll(td) var parentID string for i, a := range as { s, err := createSnapshot(ctx, sn, parentID, work, a) if err != nil { return errors.Wrapf(err, "failed to create snapshot %d", i+1) } if err := a.Apply(td); err != nil { return errors.Wrapf(err, "failed to apply to check directory on %d", i+1) } if err := checkSnapshot(ctx, sn, work, s, td); err != nil { return errors.Wrapf(err, "snapshot check failed on snapshot %d", i+1) } parentID = s } return nil } // checkInfo checks that the infos are the same func checkInfo(si1, si2 snapshots.Info) error { if si1.Kind != si2.Kind { return errors.Errorf("Expected kind %v, got %v", si1.Kind, si2.Kind) } if si1.Name != si2.Name { return errors.Errorf("Expected name %v, got %v", si1.Name, si2.Name) } if si1.Parent != si2.Parent { return errors.Errorf("Expected Parent %v, got %v", si1.Parent, si2.Parent) } if len(si1.Labels) != len(si2.Labels) { return errors.Errorf("Expected %d labels, got %d", len(si1.Labels), len(si2.Labels)) } for k, l1 := range si1.Labels { l2 := si2.Labels[k] if l1 != l2 { return errors.Errorf("Expected label %v, got %v", l1, l2) } } if si1.Created != si2.Created { return errors.Errorf("Expected Created %v, got %v", si1.Created, si2.Created) } if si1.Updated != si2.Updated { return errors.Errorf("Expected Updated %v, got %v", si1.Updated, si2.Updated) } return nil }