| package layer // import "github.com/docker/docker/layer" | |
| import ( | |
| "io" | |
| "os" | |
| "path/filepath" | |
| "runtime" | |
| "sort" | |
| "testing" | |
| "github.com/containerd/continuity/driver" | |
| "github.com/docker/docker/pkg/archive" | |
| ) | |
| func TestMountInit(t *testing.T) { | |
| // TODO Windows: Figure out why this is failing | |
| if runtime.GOOS == "windows" { | |
| t.Skip("Failing on Windows") | |
| } | |
| ls, _, cleanup := newTestStore(t) | |
| defer cleanup() | |
| basefile := newTestFile("testfile.txt", []byte("base data!"), 0o644) | |
| initfile := newTestFile("testfile.txt", []byte("init data!"), 0o777) | |
| li := initWithFiles(basefile) | |
| layer, err := createLayer(ls, "", li) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| mountInit := func(root string) error { | |
| return initfile.ApplyFile(root) | |
| } | |
| rwLayerOpts := &CreateRWLayerOpts{ | |
| InitFunc: mountInit, | |
| } | |
| m, err := ls.CreateRWLayer("fun-mount", layer.ChainID(), rwLayerOpts) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| pathFS, err := m.Mount("") | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| fi, err := os.Stat(filepath.Join(pathFS, "testfile.txt")) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| f, err := os.Open(filepath.Join(pathFS, "testfile.txt")) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| defer f.Close() | |
| b, err := io.ReadAll(f) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| if expected := "init data!"; string(b) != expected { | |
| t.Fatalf("Unexpected test file contents %q, expected %q", string(b), expected) | |
| } | |
| if fi.Mode().Perm() != 0o777 { | |
| t.Fatalf("Unexpected filemode %o, expecting %o", fi.Mode().Perm(), 0o777) | |
| } | |
| } | |
| func TestMountSize(t *testing.T) { | |
| // TODO Windows: Figure out why this is failing | |
| if runtime.GOOS == "windows" { | |
| t.Skip("Failing on Windows") | |
| } | |
| ls, _, cleanup := newTestStore(t) | |
| defer cleanup() | |
| content1 := []byte("Base contents") | |
| content2 := []byte("Mutable contents") | |
| contentInit := []byte("why am I excluded from the size ☹") | |
| li := initWithFiles(newTestFile("file1", content1, 0o644)) | |
| layer, err := createLayer(ls, "", li) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| mountInit := func(root string) error { | |
| return newTestFile("file-init", contentInit, 0o777).ApplyFile(root) | |
| } | |
| rwLayerOpts := &CreateRWLayerOpts{ | |
| InitFunc: mountInit, | |
| } | |
| m, err := ls.CreateRWLayer("mount-size", layer.ChainID(), rwLayerOpts) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| pathFS, err := m.Mount("") | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| if err := os.WriteFile(filepath.Join(pathFS, "file2"), content2, 0o755); err != nil { | |
| t.Fatal(err) | |
| } | |
| mountSize, err := m.Size() | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| if expected := len(content2); int(mountSize) != expected { | |
| t.Fatalf("Unexpected mount size %d, expected %d", int(mountSize), expected) | |
| } | |
| } | |
| func TestMountChanges(t *testing.T) { | |
| // TODO Windows: Figure out why this is failing | |
| if runtime.GOOS == "windows" { | |
| t.Skip("Failing on Windows") | |
| } | |
| ls, _, cleanup := newTestStore(t) | |
| defer cleanup() | |
| basefiles := []FileApplier{ | |
| newTestFile("testfile1.txt", []byte("base data!"), 0o644), | |
| newTestFile("testfile2.txt", []byte("base data!"), 0o644), | |
| newTestFile("testfile3.txt", []byte("base data!"), 0o644), | |
| } | |
| initfile := newTestFile("testfile1.txt", []byte("init data!"), 0o777) | |
| li := initWithFiles(basefiles...) | |
| layer, err := createLayer(ls, "", li) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| mountInit := func(root string) error { | |
| return initfile.ApplyFile(root) | |
| } | |
| rwLayerOpts := &CreateRWLayerOpts{ | |
| InitFunc: mountInit, | |
| } | |
| m, err := ls.CreateRWLayer("mount-changes", layer.ChainID(), rwLayerOpts) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| pathFS, err := m.Mount("") | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| if err := driver.LocalDriver.Lchmod(filepath.Join(pathFS, "testfile1.txt"), 0o755); err != nil { | |
| t.Fatal(err) | |
| } | |
| if err := os.WriteFile(filepath.Join(pathFS, "testfile1.txt"), []byte("mount data!"), 0o755); err != nil { | |
| t.Fatal(err) | |
| } | |
| if err := os.Remove(filepath.Join(pathFS, "testfile2.txt")); err != nil { | |
| t.Fatal(err) | |
| } | |
| if err := driver.LocalDriver.Lchmod(filepath.Join(pathFS, "testfile3.txt"), 0o755); err != nil { | |
| t.Fatal(err) | |
| } | |
| if err := os.WriteFile(filepath.Join(pathFS, "testfile4.txt"), []byte("mount data!"), 0o644); err != nil { | |
| t.Fatal(err) | |
| } | |
| changes, err := m.Changes() | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| if expected := 4; len(changes) != expected { | |
| t.Fatalf("Wrong number of changes %d, expected %d", len(changes), expected) | |
| } | |
| sortChanges(changes) | |
| assertChange(t, changes[0], archive.Change{ | |
| Path: "/testfile1.txt", | |
| Kind: archive.ChangeModify, | |
| }) | |
| assertChange(t, changes[1], archive.Change{ | |
| Path: "/testfile2.txt", | |
| Kind: archive.ChangeDelete, | |
| }) | |
| assertChange(t, changes[2], archive.Change{ | |
| Path: "/testfile3.txt", | |
| Kind: archive.ChangeModify, | |
| }) | |
| assertChange(t, changes[3], archive.Change{ | |
| Path: "/testfile4.txt", | |
| Kind: archive.ChangeAdd, | |
| }) | |
| } | |
| func TestMountApply(t *testing.T) { | |
| // TODO Windows: Figure out why this is failing | |
| if runtime.GOOS == "windows" { | |
| t.Skip("Failing on Windows") | |
| } | |
| ls, _, cleanup := newTestStore(t) | |
| defer cleanup() | |
| basefile := newTestFile("testfile.txt", []byte("base data!"), 0o644) | |
| newfile := newTestFile("newfile.txt", []byte("new data!"), 0o755) | |
| li := initWithFiles(basefile) | |
| layer, err := createLayer(ls, "", li) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| di := initWithFiles(newfile) | |
| diffLayer, err := createLayer(ls, "", di) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| m, err := ls.CreateRWLayer("fun-mount", layer.ChainID(), nil) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| r, err := diffLayer.TarStream() | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| if _, err := m.ApplyDiff(r); err != nil { | |
| t.Fatal(err) | |
| } | |
| pathFS, err := m.Mount("") | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| f, err := os.Open(filepath.Join(pathFS, "newfile.txt")) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| defer f.Close() | |
| b, err := io.ReadAll(f) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| if expected := "new data!"; string(b) != expected { | |
| t.Fatalf("Unexpected test file contents %q, expected %q", string(b), expected) | |
| } | |
| } | |
| func assertChange(t *testing.T, actual, expected archive.Change) { | |
| if actual.Path != expected.Path { | |
| t.Fatalf("Unexpected change path %s, expected %s", actual.Path, expected.Path) | |
| } | |
| if actual.Kind != expected.Kind { | |
| t.Fatalf("Unexpected change type %s, expected %s", actual.Kind, expected.Kind) | |
| } | |
| } | |
| func sortChanges(changes []archive.Change) { | |
| cs := &changeSorter{ | |
| changes: changes, | |
| } | |
| sort.Sort(cs) | |
| } | |
| type changeSorter struct { | |
| changes []archive.Change | |
| } | |
| func (cs *changeSorter) Len() int { | |
| return len(cs.changes) | |
| } | |
| func (cs *changeSorter) Swap(i, j int) { | |
| cs.changes[i], cs.changes[j] = cs.changes[j], cs.changes[i] | |
| } | |
| func (cs *changeSorter) Less(i, j int) bool { | |
| return cs.changes[i].Path < cs.changes[j].Path | |
| } |