moby/integration/image/save_test.go
Sebastiaan van Stijn 76d8bfdff4
testutil/environment: remove Execution.OSType field
This field was added in f0e5b3d7d8 to
account for older versions of the engine (Docker EE LTS versions), which
did not yet provide the OSType field in Docker info, and had to be manually
set using the TEST_OSTYPE env-var.

This patch removes the field in favor of the equivalent in DaemonInfo. It's
more verbose, but also less ambiguous what information we're using (i.e.,
the platform the daemon is running on, not the local platform).

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2023-06-26 11:18:09 +02:00

242 lines
6.1 KiB
Go

package image
import (
"archive/tar"
"context"
"encoding/json"
"io"
"io/fs"
"os"
"path/filepath"
"reflect"
"sort"
"strings"
"testing"
"time"
"github.com/cpuguy83/tar2go"
"github.com/docker/docker/api/types"
containerapi "github.com/docker/docker/api/types/container"
"github.com/docker/docker/integration/internal/build"
"github.com/docker/docker/integration/internal/container"
"github.com/docker/docker/pkg/archive"
"github.com/docker/docker/testutil/fakecontext"
"github.com/opencontainers/go-digest"
"gotest.tools/v3/assert"
"gotest.tools/v3/assert/cmp"
"gotest.tools/v3/skip"
)
type imageSaveManifestEntry struct {
Config string
RepoTags []string
Layers []string
}
func tarIndexFS(t *testing.T, rdr io.Reader) fs.FS {
t.Helper()
dir := t.TempDir()
f, err := os.Create(filepath.Join(dir, "image.tar"))
assert.NilError(t, err)
// Do not close at the end of this function otherwise the indexer won't work
t.Cleanup(func() { f.Close() })
_, err = io.Copy(f, rdr)
assert.NilError(t, err)
return tar2go.NewIndex(f).FS()
}
func TestSaveCheckTimes(t *testing.T) {
t.Parallel()
defer setupTest(t)()
client := testEnv.APIClient()
ctx := context.Background()
const repoName = "busybox:latest"
img, _, err := client.ImageInspectWithRaw(ctx, repoName)
assert.NilError(t, err)
rdr, err := client.ImageSave(ctx, []string{repoName})
assert.NilError(t, err)
tarfs := tarIndexFS(t, rdr)
dt, err := fs.ReadFile(tarfs, "manifest.json")
assert.NilError(t, err)
var ls []imageSaveManifestEntry
assert.NilError(t, json.Unmarshal(dt, &ls))
assert.Assert(t, cmp.Len(ls, 1))
info, err := fs.Stat(tarfs, ls[0].Config)
assert.NilError(t, err)
created, err := time.Parse(time.RFC3339, img.Created)
assert.NilError(t, err)
assert.Equal(t, created.Format(time.RFC3339), info.ModTime().Format(time.RFC3339), "expected: %s, actual: %s", created, info.ModTime())
}
func TestSaveRepoWithMultipleImages(t *testing.T) {
defer setupTest(t)()
ctx := context.Background()
client := testEnv.APIClient()
makeImage := func(from string, tag string) string {
id := container.Run(ctx, t, client, func(cfg *container.TestContainerConfig) {
cfg.Config.Image = from
cfg.Config.Cmd = []string{"true"}
})
chW, chErr := client.ContainerWait(ctx, id, containerapi.WaitConditionNotRunning)
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
select {
case <-chW:
case err := <-chErr:
assert.NilError(t, err)
case <-ctx.Done():
t.Fatal("timeout waiting for container to exit")
}
res, err := client.ContainerCommit(ctx, id, types.ContainerCommitOptions{Reference: tag})
assert.NilError(t, err)
err = client.ContainerRemove(ctx, id, types.ContainerRemoveOptions{Force: true})
assert.NilError(t, err)
return res.ID
}
repoName := "foobar-save-multi-images-test"
tagFoo := repoName + ":foo"
tagBar := repoName + ":bar"
idFoo := makeImage("busybox:latest", tagFoo)
idBar := makeImage("busybox:latest", tagBar)
client.ImageRemove(ctx, repoName, types.ImageRemoveOptions{Force: true})
rdr, err := client.ImageSave(ctx, []string{repoName, "busybox:latest"})
assert.NilError(t, err)
defer rdr.Close()
tarfs := tarIndexFS(t, rdr)
dt, err := fs.ReadFile(tarfs, "manifest.json")
assert.NilError(t, err)
var mfstLs []imageSaveManifestEntry
assert.NilError(t, json.Unmarshal(dt, &mfstLs))
actual := make([]string, 0, len(mfstLs))
for _, m := range mfstLs {
actual = append(actual, strings.TrimPrefix(m.Config, "blobs/sha256/"))
// make sure the blob actually exists
_, err := fs.Stat(tarfs, m.Config)
assert.Check(t, cmp.Nil(err))
}
// make the list of expected layers
img, _, err := client.ImageInspectWithRaw(ctx, "busybox:latest")
assert.NilError(t, err)
expected := []string{img.ID, idFoo, idBar}
// prefixes are not in tar
for i := range expected {
expected[i] = digest.Digest(expected[i]).Encoded()
}
sort.Strings(actual)
sort.Strings(expected)
assert.Assert(t, cmp.DeepEqual(actual, expected), "archive does not contains the right layers: got %v, expected %v", actual, expected)
}
func TestSaveDirectoryPermissions(t *testing.T) {
skip.If(t, testEnv.DaemonInfo.OSType == "windows", "Test is looking at linux specific details")
defer setupTest(t)()
ctx := context.Background()
client := testEnv.APIClient()
layerEntries := []string{"opt/", "opt/a/", "opt/a/b/", "opt/a/b/c"}
layerEntriesAUFS := []string{"./", ".wh..wh.aufs", ".wh..wh.orph/", ".wh..wh.plnk/", "opt/", "opt/a/", "opt/a/b/", "opt/a/b/c"}
dockerfile := `FROM busybox
RUN adduser -D user && mkdir -p /opt/a/b && chown -R user:user /opt/a
RUN touch /opt/a/b/c && chown user:user /opt/a/b/c`
imgID := build.Do(ctx, t, client, fakecontext.New(t, t.TempDir(), fakecontext.WithDockerfile(dockerfile)))
rdr, err := client.ImageSave(ctx, []string{imgID})
assert.NilError(t, err)
defer rdr.Close()
tarfs := tarIndexFS(t, rdr)
dt, err := fs.ReadFile(tarfs, "manifest.json")
assert.NilError(t, err)
var mfstLs []imageSaveManifestEntry
assert.NilError(t, json.Unmarshal(dt, &mfstLs))
var found bool
for _, p := range mfstLs[0].Layers {
var entriesSansDev []string
f, err := tarfs.Open(p)
assert.NilError(t, err)
entries, err := listTar(f)
f.Close()
assert.NilError(t, err)
for _, e := range entries {
if !strings.Contains(e, "dev/") {
entriesSansDev = append(entriesSansDev, e)
}
}
assert.NilError(t, err, "encountered error while listing tar entries: %s", err)
if reflect.DeepEqual(entriesSansDev, layerEntries) || reflect.DeepEqual(entriesSansDev, layerEntriesAUFS) {
found = true
break
}
}
assert.Assert(t, found, "failed to find the layer with the right content listing")
}
func listTar(f io.Reader) ([]string, error) {
// If using the containerd snapshotter, the tar file may be compressed
dec, err := archive.DecompressStream(f)
if err != nil {
return nil, err
}
defer dec.Close()
tr := tar.NewReader(dec)
var entries []string
for {
th, err := tr.Next()
if err == io.EOF {
// end of tar archive
return entries, nil
}
if err != nil {
return entries, err
}
entries = append(entries, th.Name)
}
}