dotcloud/docker

View on GitHub
daemon/graphdriver/graphtest/testutil.go

Summary

Maintainability
A
0 mins
Test Coverage
package graphtest // import "github.com/docker/docker/daemon/graphdriver/graphtest"

import (
    "bytes"
    "fmt"
    "io/fs"
    "math/rand"
    "os"
    "path/filepath"
    "sort"

    "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 := os.WriteFile(filepath.Join(root, "file-a"), randomContent(64, seed), 0o755); err != nil {
        return err
    }
    if err := os.MkdirAll(filepath.Join(root, "dir-b"), 0o755); err != nil {
        return err
    }
    if err := os.WriteFile(filepath.Join(root, "dir-b", "file-b"), randomContent(128, seed+1), 0o755); err != nil {
        return err
    }

    return os.WriteFile(filepath.Join(root, "file-c"), randomContent(128*128, seed+2), 0o755)
}

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 := os.ReadFile(filepath.Join(root, 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 os.WriteFile(filepath.Join(root, filename), content, 0o755)
}

func addDirectory(drv graphdriver.Driver, layer, dir string) error {
    root, err := drv.Get(layer, "")
    if err != nil {
        return err
    }
    defer drv.Put(layer)

    return os.MkdirAll(filepath.Join(root, dir), 0o755)
}

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 := os.RemoveAll(filepath.Join(root, 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 := os.Stat(filepath.Join(root, filename)); err == nil {
        return fmt.Errorf("file still exists: %s", filepath.Join(root, 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 := filepath.Join(root, fmt.Sprintf("directory-%d", i))
        if err := os.MkdirAll(dir, 0o755); err != nil {
            return err
        }
        for j := 0; i+j < count && j < 100; j++ {
            file := filepath.Join(dir, fmt.Sprintf("file-%d", i+j))
            if err := os.WriteFile(file, randomContent(64, seed+int64(i+j)), 0o755); 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)

    var changes []archive.Change
    for i := 0; i < count; i += 100 {
        archiveRoot := fmt.Sprintf("/directory-%d", i)
        if err := os.MkdirAll(filepath.Join(root, archiveRoot), 0o755); 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 = filepath.Join(archiveRoot, fmt.Sprintf("file-%d", i+j))
                change.Kind = archive.ChangeModify
                if err := os.WriteFile(filepath.Join(root, change.Path), randomContent(64, seed+int64(i+j)), 0o755); err != nil {
                    return nil, err
                }
            // Add file
            case 1:
                change.Path = filepath.Join(archiveRoot, fmt.Sprintf("file-%d-%d", seed, i+j))
                change.Kind = archive.ChangeAdd
                if err := os.WriteFile(filepath.Join(root, change.Path), randomContent(64, seed+int64(i+j)), 0o755); err != nil {
                    return nil, err
                }
            // Remove file
            case 2:
                change.Path = filepath.Join(archiveRoot, fmt.Sprintf("file-%d", i+j))
                change.Kind = archive.ChangeDelete
                if err := os.Remove(filepath.Join(root, 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 := filepath.Join(root, fmt.Sprintf("directory-%d", i))
        for j := 0; i+j < count && j < 100; j++ {
            file := filepath.Join(dir, fmt.Sprintf("file-%d", i+j))
            fileContent, err := os.ReadFile(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 := os.WriteFile(filepath.Join(root, "top-id"), []byte(layer), 0o755); err != nil {
        return err
    }
    layerDir := filepath.Join(root, fmt.Sprintf("layer-%d", i))
    if err := os.MkdirAll(layerDir, 0o755); err != nil {
        return err
    }
    if err := os.WriteFile(filepath.Join(layerDir, "layer-id"), []byte(layer), 0o755); err != nil {
        return err
    }
    return os.WriteFile(filepath.Join(layerDir, "parent-id"), []byte(parent), 0o755)
}

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 := os.ReadFile(filepath.Join(root, "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 := filepath.Join(root, fmt.Sprintf("layer-%d", i))

        thisLayerIDBytes, err := os.ReadFile(filepath.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 = os.ReadFile(filepath.Join(layerDir, "parent-id"))
        if err != nil {
            return err
        }
    }
    return nil
}

// readDir reads a directory just like os.ReadDir()
// then hides specific files (currently "lost+found")
// so the tests don't "see" it
func readDir(dir string) ([]fs.DirEntry, error) {
    a, err := os.ReadDir(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
}