package graphtest import ( "bytes" "fmt" "math/rand" "os" "sort" "github.com/containerd/continuity/driver" "github.com/docker/docker/daemon/graphdriver" "github.com/docker/docker/pkg/archive" "github.com/docker/docker/pkg/stringid" ) func randomContent(size int, seed int64) []byte { s := rand.NewSource(seed) content := make([]byte, size) for i := 0; i < len(content); i += 7 { val := s.Int63() for j := 0; i+j < len(content) && j < 7; j++ { content[i+j] = byte(val) val >>= 8 } } return content } func addFiles(drv graphdriver.Driver, layer string, seed int64) error { root, err := drv.Get(layer, "") if err != nil { return err } defer drv.Put(layer) if err := driver.WriteFile(root, root.Join(root.Path(), "file-a"), randomContent(64, seed), 0755); err != nil { return err } if err := root.MkdirAll(root.Join(root.Path(), "dir-b"), 0755); err != nil { return err } if err := driver.WriteFile(root, root.Join(root.Path(), "dir-b", "file-b"), randomContent(128, seed+1), 0755); err != nil { return err } return driver.WriteFile(root, root.Join(root.Path(), "file-c"), randomContent(128*128, seed+2), 0755) } func checkFile(drv graphdriver.Driver, layer, filename string, content []byte) error { root, err := drv.Get(layer, "") if err != nil { return err } defer drv.Put(layer) fileContent, err := driver.ReadFile(root, root.Join(root.Path(), filename)) if err != nil { return err } if !bytes.Equal(fileContent, content) { return fmt.Errorf("mismatched file content %v, expecting %v", fileContent, content) } return nil } func addFile(drv graphdriver.Driver, layer, filename string, content []byte) error { root, err := drv.Get(layer, "") if err != nil { return err } defer drv.Put(layer) return driver.WriteFile(root, root.Join(root.Path(), filename), content, 0755) } func addDirectory(drv graphdriver.Driver, layer, dir string) error { root, err := drv.Get(layer, "") if err != nil { return err } defer drv.Put(layer) return root.MkdirAll(root.Join(root.Path(), dir), 0755) } func removeAll(drv graphdriver.Driver, layer string, names ...string) error { root, err := drv.Get(layer, "") if err != nil { return err } defer drv.Put(layer) for _, filename := range names { if err := root.RemoveAll(root.Join(root.Path(), filename)); err != nil { return err } } return nil } func checkFileRemoved(drv graphdriver.Driver, layer, filename string) error { root, err := drv.Get(layer, "") if err != nil { return err } defer drv.Put(layer) if _, err := root.Stat(root.Join(root.Path(), filename)); err == nil { return fmt.Errorf("file still exists: %s", root.Join(root.Path(), filename)) } else if !os.IsNotExist(err) { return err } return nil } func addManyFiles(drv graphdriver.Driver, layer string, count int, seed int64) error { root, err := drv.Get(layer, "") if err != nil { return err } defer drv.Put(layer) for i := 0; i < count; i += 100 { dir := root.Join(root.Path(), fmt.Sprintf("directory-%d", i)) if err := root.MkdirAll(dir, 0755); err != nil { return err } for j := 0; i+j < count && j < 100; j++ { file := root.Join(dir, fmt.Sprintf("file-%d", i+j)) if err := driver.WriteFile(root, file, randomContent(64, seed+int64(i+j)), 0755); err != nil { return err } } } return nil } func changeManyFiles(drv graphdriver.Driver, layer string, count int, seed int64) ([]archive.Change, error) { root, err := drv.Get(layer, "") if err != nil { return nil, err } defer drv.Put(layer) changes := []archive.Change{} for i := 0; i < count; i += 100 { archiveRoot := fmt.Sprintf("/directory-%d", i) if err := root.MkdirAll(root.Join(root.Path(), archiveRoot), 0755); err != nil { return nil, err } for j := 0; i+j < count && j < 100; j++ { if j == 0 { changes = append(changes, archive.Change{ Path: archiveRoot, Kind: archive.ChangeModify, }) } var change archive.Change switch j % 3 { // Update file case 0: change.Path = root.Join(archiveRoot, fmt.Sprintf("file-%d", i+j)) change.Kind = archive.ChangeModify if err := driver.WriteFile(root, root.Join(root.Path(), change.Path), randomContent(64, seed+int64(i+j)), 0755); err != nil { return nil, err } // Add file case 1: change.Path = root.Join(archiveRoot, fmt.Sprintf("file-%d-%d", seed, i+j)) change.Kind = archive.ChangeAdd if err := driver.WriteFile(root, root.Join(root.Path(), change.Path), randomContent(64, seed+int64(i+j)), 0755); err != nil { return nil, err } // Remove file case 2: change.Path = root.Join(archiveRoot, fmt.Sprintf("file-%d", i+j)) change.Kind = archive.ChangeDelete if err := root.Remove(root.Join(root.Path(), change.Path)); err != nil { return nil, err } } changes = append(changes, change) } } return changes, nil } func checkManyFiles(drv graphdriver.Driver, layer string, count int, seed int64) error { root, err := drv.Get(layer, "") if err != nil { return err } defer drv.Put(layer) for i := 0; i < count; i += 100 { dir := root.Join(root.Path(), fmt.Sprintf("directory-%d", i)) for j := 0; i+j < count && j < 100; j++ { file := root.Join(dir, fmt.Sprintf("file-%d", i+j)) fileContent, err := driver.ReadFile(root, file) if err != nil { return err } content := randomContent(64, seed+int64(i+j)) if !bytes.Equal(fileContent, content) { return fmt.Errorf("mismatched file content %v, expecting %v", fileContent, content) } } } return nil } type changeList []archive.Change func (c changeList) Less(i, j int) bool { if c[i].Path == c[j].Path { return c[i].Kind < c[j].Kind } return c[i].Path < c[j].Path } func (c changeList) Len() int { return len(c) } func (c changeList) Swap(i, j int) { c[j], c[i] = c[i], c[j] } func checkChanges(expected, actual []archive.Change) error { if len(expected) != len(actual) { return fmt.Errorf("unexpected number of changes, expected %d, got %d", len(expected), len(actual)) } sort.Sort(changeList(expected)) sort.Sort(changeList(actual)) for i := range expected { if expected[i] != actual[i] { return fmt.Errorf("unexpected change, expecting %v, got %v", expected[i], actual[i]) } } return nil } func addLayerFiles(drv graphdriver.Driver, layer, parent string, i int) error { root, err := drv.Get(layer, "") if err != nil { return err } defer drv.Put(layer) if err := driver.WriteFile(root, root.Join(root.Path(), "top-id"), []byte(layer), 0755); err != nil { return err } layerDir := root.Join(root.Path(), fmt.Sprintf("layer-%d", i)) if err := root.MkdirAll(layerDir, 0755); err != nil { return err } if err := driver.WriteFile(root, root.Join(layerDir, "layer-id"), []byte(layer), 0755); err != nil { return err } if err := driver.WriteFile(root, root.Join(layerDir, "parent-id"), []byte(parent), 0755); err != nil { return err } return nil } func addManyLayers(drv graphdriver.Driver, baseLayer string, count int) (string, error) { lastLayer := baseLayer for i := 1; i <= count; i++ { nextLayer := stringid.GenerateRandomID() if err := drv.Create(nextLayer, lastLayer, nil); err != nil { return "", err } if err := addLayerFiles(drv, nextLayer, lastLayer, i); err != nil { return "", err } lastLayer = nextLayer } return lastLayer, nil } func checkManyLayers(drv graphdriver.Driver, layer string, count int) error { root, err := drv.Get(layer, "") if err != nil { return err } defer drv.Put(layer) layerIDBytes, err := driver.ReadFile(root, root.Join(root.Path(), "top-id")) if err != nil { return err } if !bytes.Equal(layerIDBytes, []byte(layer)) { return fmt.Errorf("mismatched file content %v, expecting %v", layerIDBytes, []byte(layer)) } for i := count; i > 0; i-- { layerDir := root.Join(root.Path(), fmt.Sprintf("layer-%d", i)) thisLayerIDBytes, err := driver.ReadFile(root, root.Join(layerDir, "layer-id")) if err != nil { return err } if !bytes.Equal(thisLayerIDBytes, layerIDBytes) { return fmt.Errorf("mismatched file content %v, expecting %v", thisLayerIDBytes, layerIDBytes) } layerIDBytes, err = driver.ReadFile(root, root.Join(layerDir, "parent-id")) if err != nil { return err } } return nil } // readDir reads a directory just like driver.ReadDir() // then hides specific files (currently "lost+found") // so the tests don't "see" it func readDir(r driver.Driver, dir string) ([]os.FileInfo, error) { a, err := driver.ReadDir(r, dir) if err != nil { return nil, err } b := a[:0] for _, x := range a { if x.Name() != "lost+found" { // ext4 always have this dir b = append(b, x) } } return b, nil }