hack: Load special images on demand
Rewrite `.build-empty-images` shell script that produced special images (emptyfs with no layers, and empty danglign image) to a Go functions that construct the same archives in a temporary directory. Use them to load these images on demand only in the tests that need them. Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
parent
2a38569337
commit
bc94dfc7d2
14 changed files with 234 additions and 89 deletions
|
@ -1,55 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
function imageNotPresent {
|
|
||||||
local img="$1"
|
|
||||||
! docker image inspect "$img" > /dev/null 2> /dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
if imageNotPresent "emptyfs"; then
|
|
||||||
# build a "docker save" tarball for "emptyfs"
|
|
||||||
# see https://github.com/docker/docker/pull/5262
|
|
||||||
# and also https://github.com/docker/docker/issues/4242
|
|
||||||
dir="$DEST/emptyfs"
|
|
||||||
uuid=511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158
|
|
||||||
mkdir -p "$dir/$uuid"
|
|
||||||
(
|
|
||||||
echo '[{"Config":"11f64303f0f7ffdc71f001788132bca5346831939a956e3e975c93267d89a16d.json","RepoTags":["emptyfs:latest"],"Layers":["511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158/layer.tar"]}]' > "$dir/manifest.json"
|
|
||||||
echo -n '{"architecture":"x86_64","comment":"Imported from -","container_config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":null,"Image":"","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"created":"2013-06-13T14:03:50.821769-07:00","docker_version":"0.4.0","history":[{"created":"2013-06-13T14:03:50.821769-07:00","comment":"Imported from -"}],"rootfs":{"type":"layers","diff_ids":["sha256:84ff92691f909a05b224e1c56abb4864f01b4f8e3c854e4bb4c7baf1d3f6d652"]}}' > "$dir/11f64303f0f7ffdc71f001788132bca5346831939a956e3e975c93267d89a16d.json"
|
|
||||||
|
|
||||||
echo '{"emptyfs":{"latest":"511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158"}}' > "$dir/repositories"
|
|
||||||
cd "$dir/$uuid"
|
|
||||||
echo '{"id":"511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158","comment":"Imported from -","created":"2013-06-13T14:03:50.821769-07:00","container_config":{"Hostname":"","Domainname":"","User":"","Memory":0,"MemorySwap":0,"CpuShares":0,"AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"PortSpecs":null,"ExposedPorts":null,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":null,"Image":"","Volumes":null,"WorkingDir":"","Entrypoint":null,"NetworkDisabled":false,"OnBuild":null},"docker_version":"0.4.0","architecture":"x86_64","Size":0}' > json
|
|
||||||
echo '1.0' > VERSION
|
|
||||||
tar -cf layer.tar --files-from /dev/null
|
|
||||||
)
|
|
||||||
(
|
|
||||||
[ -n "$TESTDEBUG" ] && set -x
|
|
||||||
tar -cC "$dir" . | docker load
|
|
||||||
)
|
|
||||||
rm -rf "$dir"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# without c8d image store, image id is the config's id
|
|
||||||
dangling_cfg=0df1207206e5288f4a989a2f13d1f5b3c4e70467702c1d5d21dfc9f002b7bd43
|
|
||||||
# with c8d image store, image id is the id of manifest/manifest list.
|
|
||||||
dangling_mfst=16d365089e5c10e1673ee82ab5bba38ade9b763296ad918bd24b42a1156c5456
|
|
||||||
if imageNotPresent "$dangling_cfg" && imageNotPresent "$dangling_mfst"; then
|
|
||||||
dir="$DEST/dangling"
|
|
||||||
mkdir -p "$dir"
|
|
||||||
(
|
|
||||||
cd "$dir"
|
|
||||||
printf '{"schemaVersion":2,"manifests":[{"mediaType":"application/vnd.docker.distribution.manifest.v2+json","digest":"sha256:16d365089e5c10e1673ee82ab5bba38ade9b763296ad918bd24b42a1156c5456","size":264,"annotations":{"org.opencontainers.image.created":"2023-05-19T08:00:44Z"},"platform":{"architecture":"amd64","os":"linux"}}]}' > index.json
|
|
||||||
printf '[{"Config":"blobs/sha256/0df1207206e5288f4a989a2f13d1f5b3c4e70467702c1d5d21dfc9f002b7bd43","RepoTags":null,"Layers":null}]' > manifest.json
|
|
||||||
mkdir -p blobs/sha256
|
|
||||||
printf '{"schemaVersion":2,"mediaType":"application/vnd.docker.distribution.manifest.v2+json","config":{"mediaType":"application/vnd.docker.container.image.v1+json","digest":"sha256:0df1207206e5288f4a989a2f13d1f5b3c4e70467702c1d5d21dfc9f002b7bd43","size":390},"layers":[]}' > blobs/sha256/$dangling_mfst
|
|
||||||
printf '{"architecture":"amd64","config":{"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"WorkingDir":"/","Labels":{"org.mobyproject.test.specialimage":"1"},"OnBuild":null},"created":null,"history":[{"created_by":"LABEL org.mobyproject.test.specialimage=1","comment":"buildkit.dockerfile.v0","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":null}}' > blobs/sha256/$dangling_cfg
|
|
||||||
tar -cf layer.tar --files-from /dev/null
|
|
||||||
)
|
|
||||||
(
|
|
||||||
[ -n "$TESTDEBUG" ] && set -x
|
|
||||||
tar -cC "$dir" . | docker load
|
|
||||||
)
|
|
||||||
rm -rf "$dir"
|
|
||||||
|
|
||||||
fi
|
|
|
@ -162,7 +162,3 @@ while ! ${TEST_CLIENT_BINARY} version &> /dev/null; do
|
||||||
sleep 2
|
sleep 2
|
||||||
done
|
done
|
||||||
printf "\n"
|
printf "\n"
|
||||||
|
|
||||||
if [ "$(docker version --format '{{ .Server.Os }}')" != 'windows' ]; then
|
|
||||||
bundle .build-empty-images
|
|
||||||
fi
|
|
||||||
|
|
|
@ -60,5 +60,4 @@ test_env() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
sh /scripts/build-empty-images.sh
|
|
||||||
run_test_integration
|
run_test_integration
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/docker/integration-cli/cli"
|
"github.com/docker/docker/integration-cli/cli"
|
||||||
|
"github.com/docker/docker/internal/testutils/specialimage"
|
||||||
"gotest.tools/v3/assert"
|
"gotest.tools/v3/assert"
|
||||||
"gotest.tools/v3/icmd"
|
"gotest.tools/v3/icmd"
|
||||||
)
|
)
|
||||||
|
@ -31,7 +32,7 @@ func (s *DockerCLIInspectSuite) OnTimeout(c *testing.T) {
|
||||||
|
|
||||||
func (s *DockerCLIInspectSuite) TestInspectImage(c *testing.T) {
|
func (s *DockerCLIInspectSuite) TestInspectImage(c *testing.T) {
|
||||||
testRequires(c, DaemonIsLinux)
|
testRequires(c, DaemonIsLinux)
|
||||||
imageTest := "emptyfs"
|
imageTest := loadSpecialImage(c, specialimage.EmptyFS)
|
||||||
// It is important that this ID remain stable. If a code change causes
|
// It is important that this ID remain stable. If a code change causes
|
||||||
// it to be different, this is equivalent to a cache bust when pulling
|
// it to be different, this is equivalent to a cache bust when pulling
|
||||||
// a legacy-format manifest. If the check at the end of this function
|
// a legacy-format manifest. If the check at the end of this function
|
||||||
|
@ -136,7 +137,8 @@ func (s *DockerCLIInspectSuite) TestInspectTypeFlagWithInvalidValue(c *testing.T
|
||||||
|
|
||||||
func (s *DockerCLIInspectSuite) TestInspectImageFilterInt(c *testing.T) {
|
func (s *DockerCLIInspectSuite) TestInspectImageFilterInt(c *testing.T) {
|
||||||
testRequires(c, DaemonIsLinux)
|
testRequires(c, DaemonIsLinux)
|
||||||
imageTest := "emptyfs"
|
imageTest := loadSpecialImage(c, specialimage.EmptyFS)
|
||||||
|
|
||||||
out := inspectField(c, imageTest, "Size")
|
out := inspectField(c, imageTest, "Size")
|
||||||
|
|
||||||
size, err := strconv.Atoi(out)
|
size, err := strconv.Atoi(out)
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"github.com/docker/docker/integration-cli/cli"
|
"github.com/docker/docker/integration-cli/cli"
|
||||||
"github.com/docker/docker/integration-cli/cli/build"
|
"github.com/docker/docker/integration-cli/cli/build"
|
||||||
"github.com/docker/docker/integration-cli/daemon"
|
"github.com/docker/docker/integration-cli/daemon"
|
||||||
|
"github.com/docker/docker/internal/testutils/specialimage"
|
||||||
"github.com/docker/docker/libnetwork/resolvconf"
|
"github.com/docker/docker/libnetwork/resolvconf"
|
||||||
"github.com/docker/docker/pkg/stringid"
|
"github.com/docker/docker/pkg/stringid"
|
||||||
"github.com/docker/docker/runconfig"
|
"github.com/docker/docker/runconfig"
|
||||||
|
@ -1933,7 +1934,8 @@ func (s *DockerCLIRunSuite) TestRunCidFileCleanupIfEmpty(c *testing.T) {
|
||||||
tmpCidFile := path.Join(tmpDir, "cid")
|
tmpCidFile := path.Join(tmpDir, "cid")
|
||||||
|
|
||||||
// This must be an image that has no CMD or ENTRYPOINT set
|
// This must be an image that has no CMD or ENTRYPOINT set
|
||||||
image := "emptyfs"
|
image := loadSpecialImage(c, specialimage.EmptyFS)
|
||||||
|
|
||||||
out, _, err := dockerCmdWithError("run", "--cidfile", tmpCidFile, image)
|
out, _, err := dockerCmdWithError("run", "--cidfile", tmpCidFile, image)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
c.Fatalf("Run without command must fail. out=%s", out)
|
c.Fatalf("Run without command must fail. out=%s", out)
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/integration-cli/cli"
|
"github.com/docker/docker/integration-cli/cli"
|
||||||
"github.com/docker/docker/integration-cli/cli/build"
|
"github.com/docker/docker/integration-cli/cli/build"
|
||||||
|
"github.com/docker/docker/internal/testutils/specialimage"
|
||||||
"gotest.tools/v3/assert"
|
"gotest.tools/v3/assert"
|
||||||
is "gotest.tools/v3/assert/cmp"
|
is "gotest.tools/v3/assert/cmp"
|
||||||
"gotest.tools/v3/icmd"
|
"gotest.tools/v3/icmd"
|
||||||
|
@ -111,8 +112,11 @@ func (s *DockerCLISaveLoadSuite) TestSaveSingleTag(c *testing.T) {
|
||||||
|
|
||||||
func (s *DockerCLISaveLoadSuite) TestSaveImageId(c *testing.T) {
|
func (s *DockerCLISaveLoadSuite) TestSaveImageId(c *testing.T) {
|
||||||
testRequires(c, DaemonIsLinux)
|
testRequires(c, DaemonIsLinux)
|
||||||
|
|
||||||
|
emptyFSImage := loadSpecialImage(c, specialimage.EmptyFS)
|
||||||
|
|
||||||
imgRepoName := "foobar-save-image-id-test"
|
imgRepoName := "foobar-save-image-id-test"
|
||||||
cli.DockerCmd(c, "tag", "emptyfs:latest", fmt.Sprintf("%v:latest", imgRepoName))
|
cli.DockerCmd(c, "tag", emptyFSImage, fmt.Sprintf("%v:latest", imgRepoName))
|
||||||
|
|
||||||
out := cli.DockerCmd(c, "images", "-q", "--no-trunc", imgRepoName).Stdout()
|
out := cli.DockerCmd(c, "images", "-q", "--no-trunc", imgRepoName).Stdout()
|
||||||
cleanedLongImageID := strings.TrimPrefix(strings.TrimSpace(out), "sha256:")
|
cleanedLongImageID := strings.TrimPrefix(strings.TrimSpace(out), "sha256:")
|
||||||
|
@ -203,13 +207,16 @@ func (s *DockerCLISaveLoadSuite) TestSaveWithNoExistImage(c *testing.T) {
|
||||||
|
|
||||||
func (s *DockerCLISaveLoadSuite) TestSaveMultipleNames(c *testing.T) {
|
func (s *DockerCLISaveLoadSuite) TestSaveMultipleNames(c *testing.T) {
|
||||||
testRequires(c, DaemonIsLinux)
|
testRequires(c, DaemonIsLinux)
|
||||||
|
|
||||||
|
emptyFSImage := loadSpecialImage(c, specialimage.EmptyFS)
|
||||||
|
|
||||||
const imgRepoName = "foobar-save-multi-name-test"
|
const imgRepoName = "foobar-save-multi-name-test"
|
||||||
|
|
||||||
oneTag := fmt.Sprintf("%v-one:latest", imgRepoName)
|
oneTag := fmt.Sprintf("%v-one:latest", imgRepoName)
|
||||||
twoTag := fmt.Sprintf("%v-two:latest", imgRepoName)
|
twoTag := fmt.Sprintf("%v-two:latest", imgRepoName)
|
||||||
|
|
||||||
cli.DockerCmd(c, "tag", "emptyfs:latest", oneTag)
|
cli.DockerCmd(c, "tag", emptyFSImage, oneTag)
|
||||||
cli.DockerCmd(c, "tag", "emptyfs:latest", twoTag)
|
cli.DockerCmd(c, "tag", emptyFSImage, twoTag)
|
||||||
|
|
||||||
out, err := RunCommandPipelineWithOutput(
|
out, err := RunCommandPipelineWithOutput(
|
||||||
exec.Command(dockerBinary, "save", strings.TrimSuffix(oneTag, ":latest"), twoTag),
|
exec.Command(dockerBinary, "save", strings.TrimSuffix(oneTag, ":latest"), twoTag),
|
||||||
|
|
|
@ -18,6 +18,8 @@ import (
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
"github.com/docker/docker/integration-cli/cli"
|
"github.com/docker/docker/integration-cli/cli"
|
||||||
"github.com/docker/docker/integration-cli/daemon"
|
"github.com/docker/docker/integration-cli/daemon"
|
||||||
|
"github.com/docker/docker/internal/testutils/specialimage"
|
||||||
|
"github.com/docker/docker/pkg/archive"
|
||||||
"github.com/docker/docker/testutil"
|
"github.com/docker/docker/testutil"
|
||||||
"gotest.tools/v3/assert"
|
"gotest.tools/v3/assert"
|
||||||
"gotest.tools/v3/assert/cmp"
|
"gotest.tools/v3/assert/cmp"
|
||||||
|
@ -464,3 +466,43 @@ func sumAsIntegers(vals ...interface{}) interface{} {
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadSpecialImage(c *testing.T, imageFunc specialimage.SpecialImageFunc) string {
|
||||||
|
tmpDir := c.TempDir()
|
||||||
|
|
||||||
|
imgDir := filepath.Join(tmpDir, "image")
|
||||||
|
assert.NilError(c, os.Mkdir(imgDir, 0o755))
|
||||||
|
|
||||||
|
assert.NilError(c, imageFunc(imgDir))
|
||||||
|
|
||||||
|
rc, err := archive.TarWithOptions(imgDir, &archive.TarOptions{})
|
||||||
|
assert.NilError(c, err)
|
||||||
|
defer rc.Close()
|
||||||
|
|
||||||
|
imgTar := filepath.Join(tmpDir, "image.tar")
|
||||||
|
tarFile, err := os.OpenFile(imgTar, os.O_CREATE|os.O_WRONLY, 0o644)
|
||||||
|
assert.NilError(c, err)
|
||||||
|
|
||||||
|
defer tarFile.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(tarFile, rc)
|
||||||
|
assert.NilError(c, err)
|
||||||
|
|
||||||
|
tarFile.Close()
|
||||||
|
|
||||||
|
out := cli.DockerCmd(c, "load", "-i", imgTar).Stdout()
|
||||||
|
|
||||||
|
for _, line := range strings.Split(out, "\n") {
|
||||||
|
line := strings.TrimSpace(line)
|
||||||
|
|
||||||
|
if _, imageID, hasID := strings.Cut(line, "Loaded image ID: "); hasID {
|
||||||
|
return imageID
|
||||||
|
}
|
||||||
|
if _, imageRef, hasRef := strings.Cut(line, "Loaded image: "); hasRef {
|
||||||
|
return imageRef
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Fatalf("failed to extract image ref from %q", out)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/docker/testutil/environment"
|
"github.com/docker/docker/internal/testutils/specialimage"
|
||||||
"gotest.tools/v3/assert"
|
"gotest.tools/v3/assert"
|
||||||
is "gotest.tools/v3/assert/cmp"
|
is "gotest.tools/v3/assert/cmp"
|
||||||
"gotest.tools/v3/skip"
|
"gotest.tools/v3/skip"
|
||||||
|
@ -17,7 +17,7 @@ func TestImageInspectEmptyTagsAndDigests(t *testing.T) {
|
||||||
|
|
||||||
client := testEnv.APIClient()
|
client := testEnv.APIClient()
|
||||||
|
|
||||||
danglingID := environment.GetTestDanglingImageId(testEnv)
|
danglingID := specialimage.Load(ctx, t, client, specialimage.Dangling)
|
||||||
|
|
||||||
inspect, raw, err := client.ImageInspectWithRaw(ctx, danglingID)
|
inspect, raw, err := client.ImageInspectWithRaw(ctx, danglingID)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
"github.com/docker/docker/integration/internal/container"
|
"github.com/docker/docker/integration/internal/container"
|
||||||
"github.com/docker/docker/testutil/environment"
|
"github.com/docker/docker/internal/testutils/specialimage"
|
||||||
"gotest.tools/v3/assert"
|
"gotest.tools/v3/assert"
|
||||||
is "gotest.tools/v3/assert/cmp"
|
is "gotest.tools/v3/assert/cmp"
|
||||||
"gotest.tools/v3/skip"
|
"gotest.tools/v3/skip"
|
||||||
|
@ -18,7 +18,7 @@ func TestPruneDontDeleteUsedDangling(t *testing.T) {
|
||||||
ctx := setupTest(t)
|
ctx := setupTest(t)
|
||||||
client := testEnv.APIClient()
|
client := testEnv.APIClient()
|
||||||
|
|
||||||
danglingID := environment.GetTestDanglingImageId(testEnv)
|
danglingID := specialimage.Load(ctx, t, client, specialimage.Dangling)
|
||||||
|
|
||||||
container.Create(ctx, t, client,
|
container.Create(ctx, t, client,
|
||||||
container.WithImage(danglingID),
|
container.WithImage(danglingID),
|
||||||
|
|
41
internal/testutils/specialimage/dangling.go
Normal file
41
internal/testutils/specialimage/dangling.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package specialimage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const danglingImageManifestDigest = "sha256:16d365089e5c10e1673ee82ab5bba38ade9b763296ad918bd24b42a1156c5456" // #nosec G101 -- ignoring: Potential hardcoded credentials (gosec)
|
||||||
|
const danglingImageConfigDigest = "sha256:0df1207206e5288f4a989a2f13d1f5b3c4e70467702c1d5d21dfc9f002b7bd43" // #nosec G101 -- ignoring: Potential hardcoded credentials (gosec)
|
||||||
|
|
||||||
|
// Dangling creates an image with no layers and no tag.
|
||||||
|
// It also has an extra org.mobyproject.test.specialimage=1 label set.
|
||||||
|
// Layout: OCI.
|
||||||
|
func Dangling(dir string) error {
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, "index.json"), []byte(`{"schemaVersion":2,"manifests":[{"mediaType":"application/vnd.docker.distribution.manifest.v2+json","digest":"sha256:16d365089e5c10e1673ee82ab5bba38ade9b763296ad918bd24b42a1156c5456","size":264,"annotations":{"org.opencontainers.image.created":"2023-05-19T08:00:44Z"},"platform":{"architecture":"amd64","os":"linux"}}]}`), 0o644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, "manifest.json"), []byte(`[{"Config":"blobs/sha256/0df1207206e5288f4a989a2f13d1f5b3c4e70467702c1d5d21dfc9f002b7bd43","RepoTags":null,"Layers":null}]`), 0o644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Mkdir(filepath.Join(dir, "blobs"), 0o755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
blobsDir := filepath.Join(dir, "blobs", "sha256")
|
||||||
|
if err := os.Mkdir(blobsDir, 0o755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.WriteFile(filepath.Join(blobsDir, strings.TrimPrefix(danglingImageManifestDigest, "sha256:")), []byte(`{"schemaVersion":2,"mediaType":"application/vnd.docker.distribution.manifest.v2+json","config":{"mediaType":"application/vnd.docker.container.image.v1+json","digest":"sha256:0df1207206e5288f4a989a2f13d1f5b3c4e70467702c1d5d21dfc9f002b7bd43","size":390},"layers":[]}`), 0o644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filepath.Join(blobsDir, strings.TrimPrefix(danglingImageConfigDigest, "sha256:")), []byte(`{"architecture":"amd64","config":{"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"WorkingDir":"/","Labels":{"org.mobyproject.test.specialimage":"1"},"OnBuild":null},"created":null,"history":[{"created_by":"LABEL org.mobyproject.test.specialimage=1","comment":"buildkit.dockerfile.v0","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":null}}`), 0o644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
64
internal/testutils/specialimage/emptyfs.go
Normal file
64
internal/testutils/specialimage/emptyfs.go
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
package specialimage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EmptyFS builds an image with an empty rootfs.
|
||||||
|
// Layout: Legacy Docker Archive
|
||||||
|
// See https://github.com/docker/docker/pull/5262
|
||||||
|
// and also https://github.com/docker/docker/issues/4242
|
||||||
|
func EmptyFS(dir string) error {
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, "manifest.json"), []byte(`[{"Config":"11f64303f0f7ffdc71f001788132bca5346831939a956e3e975c93267d89a16d.json","RepoTags":["emptyfs:latest"],"Layers":["511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158/layer.tar"]}]`), 0o644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Mkdir(filepath.Join(dir, "blobs"), 0o755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
blobsDir := filepath.Join(dir, "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158")
|
||||||
|
if err := os.Mkdir(blobsDir, 0o755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, "VERSION"), []byte(`1.0`), 0o644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, "repositories"), []byte(`{"emptyfs":{"latest":"511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158"}}`), 0o644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, "11f64303f0f7ffdc71f001788132bca5346831939a956e3e975c93267d89a16d.json"), []byte(`{"architecture":"x86_64","comment":"Imported from -","container_config":{"Hostname":"","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":null,"Image":"","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"created":"2013-06-13T14:03:50.821769-07:00","docker_version":"0.4.0","history":[{"created":"2013-06-13T14:03:50.821769-07:00","comment":"Imported from -"}],"rootfs":{"type":"layers","diff_ids":["sha256:84ff92691f909a05b224e1c56abb4864f01b4f8e3c854e4bb4c7baf1d3f6d652"]}}`), 0o644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.WriteFile(filepath.Join(blobsDir, "json"), []byte(`{"id":"511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158","comment":"Imported from -","created":"2013-06-13T14:03:50.821769-07:00","container_config":{"Hostname":"","Domainname":"","User":"","Memory":0,"MemorySwap":0,"CpuShares":0,"AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"PortSpecs":null,"ExposedPorts":null,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":null,"Image":"","Volumes":null,"WorkingDir":"","Entrypoint":null,"NetworkDisabled":false,"OnBuild":null},"docker_version":"0.4.0","architecture":"x86_64","Size":0}`+"\n"), 0o644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
layerFile, err := os.OpenFile(filepath.Join(blobsDir, "layer.tar"), os.O_CREATE|os.O_WRONLY, 0o644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer layerFile.Close()
|
||||||
|
|
||||||
|
// 10240 NUL bytes is a valid empty tar archive.
|
||||||
|
_, err = io.Copy(layerFile, io.LimitReader(zeroReader{}, 10240))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type zeroReader struct{}
|
||||||
|
|
||||||
|
func (_ zeroReader) Read(p []byte) (n int, err error) {
|
||||||
|
l := len(p)
|
||||||
|
for idx := 0; idx < l; idx++ {
|
||||||
|
p[idx] = 0
|
||||||
|
}
|
||||||
|
return l, nil
|
||||||
|
}
|
66
internal/testutils/specialimage/load.go
Normal file
66
internal/testutils/specialimage/load.go
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
package specialimage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/docker/client"
|
||||||
|
"github.com/docker/docker/pkg/archive"
|
||||||
|
"github.com/docker/docker/pkg/jsonmessage"
|
||||||
|
"gotest.tools/v3/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SpecialImageFunc func(string) error
|
||||||
|
|
||||||
|
func Load(ctx context.Context, t *testing.T, apiClient client.APIClient, imageFunc SpecialImageFunc) string {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
|
||||||
|
err := imageFunc(tempDir)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
|
rc, err := archive.TarWithOptions(tempDir, &archive.TarOptions{})
|
||||||
|
assert.NilError(t, err)
|
||||||
|
|
||||||
|
defer rc.Close()
|
||||||
|
|
||||||
|
resp, err := apiClient.ImageLoad(ctx, rc, true)
|
||||||
|
assert.NilError(t, err, "Failed to load dangling image")
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if !assert.Check(t, err) {
|
||||||
|
respBody, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to read response body: %v", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
t.Fatalf("Failed load: %s", string(respBody))
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder := json.NewDecoder(resp.Body)
|
||||||
|
for {
|
||||||
|
var msg jsonmessage.JSONMessage
|
||||||
|
err := decoder.Decode(&msg)
|
||||||
|
if errors.Is(err, io.EOF) {
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
assert.NilError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.Stream = strings.TrimSpace(msg.Stream)
|
||||||
|
|
||||||
|
if _, imageID, hasID := strings.Cut(msg.Stream, "Loaded image ID: "); hasID {
|
||||||
|
return imageID
|
||||||
|
}
|
||||||
|
if _, imageRef, hasRef := strings.Cut(msg.Stream, "Loaded image: "); hasRef {
|
||||||
|
return imageRef
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Fatal("failed to read image ID")
|
||||||
|
return ""
|
||||||
|
}
|
|
@ -101,7 +101,6 @@ func ProtectImages(ctx context.Context, t testing.TB, testEnv *Execution) {
|
||||||
images = append(images, frozenImages...)
|
images = append(images, frozenImages...)
|
||||||
}
|
}
|
||||||
testEnv.ProtectImage(t, images...)
|
testEnv.ProtectImage(t, images...)
|
||||||
testEnv.ProtectImage(t, DanglingImageIdGraphDriver, DanglingImageIdSnapshotter)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getExistingImages(ctx context.Context, t testing.TB, testEnv *Execution) []string {
|
func getExistingImages(ctx context.Context, t testing.TB, testEnv *Execution) []string {
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
package environment
|
|
||||||
|
|
||||||
// DanglingImageIdGraphDriver is the digest for dangling images used
|
|
||||||
// in tests when the graph driver is used. The graph driver image store
|
|
||||||
// identifies images by the ID of their config.
|
|
||||||
const DanglingImageIdGraphDriver = "sha256:0df1207206e5288f4a989a2f13d1f5b3c4e70467702c1d5d21dfc9f002b7bd43" // #nosec G101 -- ignoring: Potential hardcoded credentials (gosec)
|
|
||||||
|
|
||||||
// DanglingImageIdSnapshotter is the digest for dangling images used in
|
|
||||||
// tests when the containerd image store is used. The container image
|
|
||||||
// store identifies images by the ID of their manifest/manifest list..
|
|
||||||
const DanglingImageIdSnapshotter = "sha256:16d365089e5c10e1673ee82ab5bba38ade9b763296ad918bd24b42a1156c5456" // #nosec G101 -- ignoring: Potential hardcoded credentials (gosec)
|
|
||||||
|
|
||||||
func GetTestDanglingImageId(testEnv *Execution) string {
|
|
||||||
if testEnv.UsingSnapshotter() {
|
|
||||||
return DanglingImageIdSnapshotter
|
|
||||||
}
|
|
||||||
return DanglingImageIdGraphDriver
|
|
||||||
}
|
|
Loading…
Reference in a new issue