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>
| 1 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,55 +0,0 @@ |
| 1 |
-#!/usr/bin/env bash |
|
| 2 |
-set -e |
|
| 3 |
- |
|
| 4 |
-function imageNotPresent {
|
|
| 5 |
- local img="$1" |
|
| 6 |
- ! docker image inspect "$img" > /dev/null 2> /dev/null |
|
| 7 |
-} |
|
| 8 |
- |
|
| 9 |
-if imageNotPresent "emptyfs"; then |
|
| 10 |
- # build a "docker save" tarball for "emptyfs" |
|
| 11 |
- # see https://github.com/docker/docker/pull/5262 |
|
| 12 |
- # and also https://github.com/docker/docker/issues/4242 |
|
| 13 |
- dir="$DEST/emptyfs" |
|
| 14 |
- uuid=511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158 |
|
| 15 |
- mkdir -p "$dir/$uuid" |
|
| 16 |
- ( |
|
| 17 |
- echo '[{"Config":"11f64303f0f7ffdc71f001788132bca5346831939a956e3e975c93267d89a16d.json","RepoTags":["emptyfs:latest"],"Layers":["511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158/layer.tar"]}]' > "$dir/manifest.json"
|
|
| 18 |
- 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"
|
|
| 19 |
- |
|
| 20 |
- echo '{"emptyfs":{"latest":"511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158"}}' > "$dir/repositories"
|
|
| 21 |
- cd "$dir/$uuid" |
|
| 22 |
- 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
|
|
| 23 |
- echo '1.0' > VERSION |
|
| 24 |
- tar -cf layer.tar --files-from /dev/null |
|
| 25 |
- ) |
|
| 26 |
- ( |
|
| 27 |
- [ -n "$TESTDEBUG" ] && set -x |
|
| 28 |
- tar -cC "$dir" . | docker load |
|
| 29 |
- ) |
|
| 30 |
- rm -rf "$dir" |
|
| 31 |
-fi |
|
| 32 |
- |
|
| 33 |
-# without c8d image store, image id is the config's id |
|
| 34 |
-dangling_cfg=0df1207206e5288f4a989a2f13d1f5b3c4e70467702c1d5d21dfc9f002b7bd43 |
|
| 35 |
-# with c8d image store, image id is the id of manifest/manifest list. |
|
| 36 |
-dangling_mfst=16d365089e5c10e1673ee82ab5bba38ade9b763296ad918bd24b42a1156c5456 |
|
| 37 |
-if imageNotPresent "$dangling_cfg" && imageNotPresent "$dangling_mfst"; then |
|
| 38 |
- dir="$DEST/dangling" |
|
| 39 |
- mkdir -p "$dir" |
|
| 40 |
- ( |
|
| 41 |
- cd "$dir" |
|
| 42 |
- 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
|
|
| 43 |
- printf '[{"Config":"blobs/sha256/0df1207206e5288f4a989a2f13d1f5b3c4e70467702c1d5d21dfc9f002b7bd43","RepoTags":null,"Layers":null}]' > manifest.json
|
|
| 44 |
- mkdir -p blobs/sha256 |
|
| 45 |
- 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
|
|
| 46 |
- 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
|
|
| 47 |
- tar -cf layer.tar --files-from /dev/null |
|
| 48 |
- ) |
|
| 49 |
- ( |
|
| 50 |
- [ -n "$TESTDEBUG" ] && set -x |
|
| 51 |
- tar -cC "$dir" . | docker load |
|
| 52 |
- ) |
|
| 53 |
- rm -rf "$dir" |
|
| 54 |
- |
|
| 55 |
-fi |
| ... | ... |
@@ -13,6 +13,7 @@ import ( |
| 13 | 13 |
"github.com/docker/docker/api/types" |
| 14 | 14 |
"github.com/docker/docker/api/types/container" |
| 15 | 15 |
"github.com/docker/docker/integration-cli/cli" |
| 16 |
+ "github.com/docker/docker/internal/testutils/specialimage" |
|
| 16 | 17 |
"gotest.tools/v3/assert" |
| 17 | 18 |
"gotest.tools/v3/icmd" |
| 18 | 19 |
) |
| ... | ... |
@@ -31,7 +32,7 @@ func (s *DockerCLIInspectSuite) OnTimeout(c *testing.T) {
|
| 31 | 31 |
|
| 32 | 32 |
func (s *DockerCLIInspectSuite) TestInspectImage(c *testing.T) {
|
| 33 | 33 |
testRequires(c, DaemonIsLinux) |
| 34 |
- imageTest := "emptyfs" |
|
| 34 |
+ imageTest := loadSpecialImage(c, specialimage.EmptyFS) |
|
| 35 | 35 |
// It is important that this ID remain stable. If a code change causes |
| 36 | 36 |
// it to be different, this is equivalent to a cache bust when pulling |
| 37 | 37 |
// a legacy-format manifest. If the check at the end of this function |
| ... | ... |
@@ -136,7 +137,8 @@ func (s *DockerCLIInspectSuite) TestInspectTypeFlagWithInvalidValue(c *testing.T |
| 136 | 136 |
|
| 137 | 137 |
func (s *DockerCLIInspectSuite) TestInspectImageFilterInt(c *testing.T) {
|
| 138 | 138 |
testRequires(c, DaemonIsLinux) |
| 139 |
- imageTest := "emptyfs" |
|
| 139 |
+ imageTest := loadSpecialImage(c, specialimage.EmptyFS) |
|
| 140 |
+ |
|
| 140 | 141 |
out := inspectField(c, imageTest, "Size") |
| 141 | 142 |
|
| 142 | 143 |
size, err := strconv.Atoi(out) |
| ... | ... |
@@ -26,6 +26,7 @@ import ( |
| 26 | 26 |
"github.com/docker/docker/integration-cli/cli" |
| 27 | 27 |
"github.com/docker/docker/integration-cli/cli/build" |
| 28 | 28 |
"github.com/docker/docker/integration-cli/daemon" |
| 29 |
+ "github.com/docker/docker/internal/testutils/specialimage" |
|
| 29 | 30 |
"github.com/docker/docker/libnetwork/resolvconf" |
| 30 | 31 |
"github.com/docker/docker/pkg/stringid" |
| 31 | 32 |
"github.com/docker/docker/runconfig" |
| ... | ... |
@@ -1933,7 +1934,8 @@ func (s *DockerCLIRunSuite) TestRunCidFileCleanupIfEmpty(c *testing.T) {
|
| 1933 | 1933 |
tmpCidFile := path.Join(tmpDir, "cid") |
| 1934 | 1934 |
|
| 1935 | 1935 |
// This must be an image that has no CMD or ENTRYPOINT set |
| 1936 |
- image := "emptyfs" |
|
| 1936 |
+ image := loadSpecialImage(c, specialimage.EmptyFS) |
|
| 1937 |
+ |
|
| 1937 | 1938 |
out, _, err := dockerCmdWithError("run", "--cidfile", tmpCidFile, image)
|
| 1938 | 1939 |
if err == nil {
|
| 1939 | 1940 |
c.Fatalf("Run without command must fail. out=%s", out)
|
| ... | ... |
@@ -13,6 +13,7 @@ import ( |
| 13 | 13 |
"github.com/docker/docker/api/types" |
| 14 | 14 |
"github.com/docker/docker/integration-cli/cli" |
| 15 | 15 |
"github.com/docker/docker/integration-cli/cli/build" |
| 16 |
+ "github.com/docker/docker/internal/testutils/specialimage" |
|
| 16 | 17 |
"gotest.tools/v3/assert" |
| 17 | 18 |
is "gotest.tools/v3/assert/cmp" |
| 18 | 19 |
"gotest.tools/v3/icmd" |
| ... | ... |
@@ -111,8 +112,11 @@ func (s *DockerCLISaveLoadSuite) TestSaveSingleTag(c *testing.T) {
|
| 111 | 111 |
|
| 112 | 112 |
func (s *DockerCLISaveLoadSuite) TestSaveImageId(c *testing.T) {
|
| 113 | 113 |
testRequires(c, DaemonIsLinux) |
| 114 |
+ |
|
| 115 |
+ emptyFSImage := loadSpecialImage(c, specialimage.EmptyFS) |
|
| 116 |
+ |
|
| 114 | 117 |
imgRepoName := "foobar-save-image-id-test" |
| 115 |
- cli.DockerCmd(c, "tag", "emptyfs:latest", fmt.Sprintf("%v:latest", imgRepoName))
|
|
| 118 |
+ cli.DockerCmd(c, "tag", emptyFSImage, fmt.Sprintf("%v:latest", imgRepoName))
|
|
| 116 | 119 |
|
| 117 | 120 |
out := cli.DockerCmd(c, "images", "-q", "--no-trunc", imgRepoName).Stdout() |
| 118 | 121 |
cleanedLongImageID := strings.TrimPrefix(strings.TrimSpace(out), "sha256:") |
| ... | ... |
@@ -203,13 +207,16 @@ func (s *DockerCLISaveLoadSuite) TestSaveWithNoExistImage(c *testing.T) {
|
| 203 | 203 |
|
| 204 | 204 |
func (s *DockerCLISaveLoadSuite) TestSaveMultipleNames(c *testing.T) {
|
| 205 | 205 |
testRequires(c, DaemonIsLinux) |
| 206 |
+ |
|
| 207 |
+ emptyFSImage := loadSpecialImage(c, specialimage.EmptyFS) |
|
| 208 |
+ |
|
| 206 | 209 |
const imgRepoName = "foobar-save-multi-name-test" |
| 207 | 210 |
|
| 208 | 211 |
oneTag := fmt.Sprintf("%v-one:latest", imgRepoName)
|
| 209 | 212 |
twoTag := fmt.Sprintf("%v-two:latest", imgRepoName)
|
| 210 | 213 |
|
| 211 |
- cli.DockerCmd(c, "tag", "emptyfs:latest", oneTag) |
|
| 212 |
- cli.DockerCmd(c, "tag", "emptyfs:latest", twoTag) |
|
| 214 |
+ cli.DockerCmd(c, "tag", emptyFSImage, oneTag) |
|
| 215 |
+ cli.DockerCmd(c, "tag", emptyFSImage, twoTag) |
|
| 213 | 216 |
|
| 214 | 217 |
out, err := RunCommandPipelineWithOutput( |
| 215 | 218 |
exec.Command(dockerBinary, "save", strings.TrimSuffix(oneTag, ":latest"), twoTag), |
| ... | ... |
@@ -18,6 +18,8 @@ import ( |
| 18 | 18 |
"github.com/docker/docker/client" |
| 19 | 19 |
"github.com/docker/docker/integration-cli/cli" |
| 20 | 20 |
"github.com/docker/docker/integration-cli/daemon" |
| 21 |
+ "github.com/docker/docker/internal/testutils/specialimage" |
|
| 22 |
+ "github.com/docker/docker/pkg/archive" |
|
| 21 | 23 |
"github.com/docker/docker/testutil" |
| 22 | 24 |
"gotest.tools/v3/assert" |
| 23 | 25 |
"gotest.tools/v3/assert/cmp" |
| ... | ... |
@@ -464,3 +466,43 @@ func sumAsIntegers(vals ...interface{}) interface{} {
|
| 464 | 464 |
} |
| 465 | 465 |
return s |
| 466 | 466 |
} |
| 467 |
+ |
|
| 468 |
+func loadSpecialImage(c *testing.T, imageFunc specialimage.SpecialImageFunc) string {
|
|
| 469 |
+ tmpDir := c.TempDir() |
|
| 470 |
+ |
|
| 471 |
+ imgDir := filepath.Join(tmpDir, "image") |
|
| 472 |
+ assert.NilError(c, os.Mkdir(imgDir, 0o755)) |
|
| 473 |
+ |
|
| 474 |
+ assert.NilError(c, imageFunc(imgDir)) |
|
| 475 |
+ |
|
| 476 |
+ rc, err := archive.TarWithOptions(imgDir, &archive.TarOptions{})
|
|
| 477 |
+ assert.NilError(c, err) |
|
| 478 |
+ defer rc.Close() |
|
| 479 |
+ |
|
| 480 |
+ imgTar := filepath.Join(tmpDir, "image.tar") |
|
| 481 |
+ tarFile, err := os.OpenFile(imgTar, os.O_CREATE|os.O_WRONLY, 0o644) |
|
| 482 |
+ assert.NilError(c, err) |
|
| 483 |
+ |
|
| 484 |
+ defer tarFile.Close() |
|
| 485 |
+ |
|
| 486 |
+ _, err = io.Copy(tarFile, rc) |
|
| 487 |
+ assert.NilError(c, err) |
|
| 488 |
+ |
|
| 489 |
+ tarFile.Close() |
|
| 490 |
+ |
|
| 491 |
+ out := cli.DockerCmd(c, "load", "-i", imgTar).Stdout() |
|
| 492 |
+ |
|
| 493 |
+ for _, line := range strings.Split(out, "\n") {
|
|
| 494 |
+ line := strings.TrimSpace(line) |
|
| 495 |
+ |
|
| 496 |
+ if _, imageID, hasID := strings.Cut(line, "Loaded image ID: "); hasID {
|
|
| 497 |
+ return imageID |
|
| 498 |
+ } |
|
| 499 |
+ if _, imageRef, hasRef := strings.Cut(line, "Loaded image: "); hasRef {
|
|
| 500 |
+ return imageRef |
|
| 501 |
+ } |
|
| 502 |
+ } |
|
| 503 |
+ |
|
| 504 |
+ c.Fatalf("failed to extract image ref from %q", out)
|
|
| 505 |
+ return "" |
|
| 506 |
+} |
| ... | ... |
@@ -4,7 +4,7 @@ import ( |
| 4 | 4 |
"encoding/json" |
| 5 | 5 |
"testing" |
| 6 | 6 |
|
| 7 |
- "github.com/docker/docker/testutil/environment" |
|
| 7 |
+ "github.com/docker/docker/internal/testutils/specialimage" |
|
| 8 | 8 |
"gotest.tools/v3/assert" |
| 9 | 9 |
is "gotest.tools/v3/assert/cmp" |
| 10 | 10 |
"gotest.tools/v3/skip" |
| ... | ... |
@@ -17,7 +17,7 @@ func TestImageInspectEmptyTagsAndDigests(t *testing.T) {
|
| 17 | 17 |
|
| 18 | 18 |
client := testEnv.APIClient() |
| 19 | 19 |
|
| 20 |
- danglingID := environment.GetTestDanglingImageId(testEnv) |
|
| 20 |
+ danglingID := specialimage.Load(ctx, t, client, specialimage.Dangling) |
|
| 21 | 21 |
|
| 22 | 22 |
inspect, raw, err := client.ImageInspectWithRaw(ctx, danglingID) |
| 23 | 23 |
assert.NilError(t, err) |
| ... | ... |
@@ -5,7 +5,7 @@ import ( |
| 5 | 5 |
|
| 6 | 6 |
"github.com/docker/docker/api/types/filters" |
| 7 | 7 |
"github.com/docker/docker/integration/internal/container" |
| 8 |
- "github.com/docker/docker/testutil/environment" |
|
| 8 |
+ "github.com/docker/docker/internal/testutils/specialimage" |
|
| 9 | 9 |
"gotest.tools/v3/assert" |
| 10 | 10 |
is "gotest.tools/v3/assert/cmp" |
| 11 | 11 |
"gotest.tools/v3/skip" |
| ... | ... |
@@ -18,7 +18,7 @@ func TestPruneDontDeleteUsedDangling(t *testing.T) {
|
| 18 | 18 |
ctx := setupTest(t) |
| 19 | 19 |
client := testEnv.APIClient() |
| 20 | 20 |
|
| 21 |
- danglingID := environment.GetTestDanglingImageId(testEnv) |
|
| 21 |
+ danglingID := specialimage.Load(ctx, t, client, specialimage.Dangling) |
|
| 22 | 22 |
|
| 23 | 23 |
container.Create(ctx, t, client, |
| 24 | 24 |
container.WithImage(danglingID), |
| 25 | 25 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,41 @@ |
| 0 |
+package specialimage |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "os" |
|
| 4 |
+ "path/filepath" |
|
| 5 |
+ "strings" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+const danglingImageManifestDigest = "sha256:16d365089e5c10e1673ee82ab5bba38ade9b763296ad918bd24b42a1156c5456" // #nosec G101 -- ignoring: Potential hardcoded credentials (gosec) |
|
| 9 |
+const danglingImageConfigDigest = "sha256:0df1207206e5288f4a989a2f13d1f5b3c4e70467702c1d5d21dfc9f002b7bd43" // #nosec G101 -- ignoring: Potential hardcoded credentials (gosec) |
|
| 10 |
+ |
|
| 11 |
+// Dangling creates an image with no layers and no tag. |
|
| 12 |
+// It also has an extra org.mobyproject.test.specialimage=1 label set. |
|
| 13 |
+// Layout: OCI. |
|
| 14 |
+func Dangling(dir string) error {
|
|
| 15 |
+ 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 {
|
|
| 16 |
+ return err |
|
| 17 |
+ } |
|
| 18 |
+ |
|
| 19 |
+ if err := os.WriteFile(filepath.Join(dir, "manifest.json"), []byte(`[{"Config":"blobs/sha256/0df1207206e5288f4a989a2f13d1f5b3c4e70467702c1d5d21dfc9f002b7bd43","RepoTags":null,"Layers":null}]`), 0o644); err != nil {
|
|
| 20 |
+ return err |
|
| 21 |
+ } |
|
| 22 |
+ |
|
| 23 |
+ if err := os.Mkdir(filepath.Join(dir, "blobs"), 0o755); err != nil {
|
|
| 24 |
+ return err |
|
| 25 |
+ } |
|
| 26 |
+ |
|
| 27 |
+ blobsDir := filepath.Join(dir, "blobs", "sha256") |
|
| 28 |
+ if err := os.Mkdir(blobsDir, 0o755); err != nil {
|
|
| 29 |
+ return err |
|
| 30 |
+ } |
|
| 31 |
+ |
|
| 32 |
+ 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 {
|
|
| 33 |
+ return err |
|
| 34 |
+ } |
|
| 35 |
+ 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 {
|
|
| 36 |
+ return err |
|
| 37 |
+ } |
|
| 38 |
+ |
|
| 39 |
+ return nil |
|
| 40 |
+} |
| 0 | 41 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,64 @@ |
| 0 |
+package specialimage |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "io" |
|
| 4 |
+ "os" |
|
| 5 |
+ "path/filepath" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+// EmptyFS builds an image with an empty rootfs. |
|
| 9 |
+// Layout: Legacy Docker Archive |
|
| 10 |
+// See https://github.com/docker/docker/pull/5262 |
|
| 11 |
+// and also https://github.com/docker/docker/issues/4242 |
|
| 12 |
+func EmptyFS(dir string) error {
|
|
| 13 |
+ if err := os.WriteFile(filepath.Join(dir, "manifest.json"), []byte(`[{"Config":"11f64303f0f7ffdc71f001788132bca5346831939a956e3e975c93267d89a16d.json","RepoTags":["emptyfs:latest"],"Layers":["511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158/layer.tar"]}]`), 0o644); err != nil {
|
|
| 14 |
+ return err |
|
| 15 |
+ } |
|
| 16 |
+ |
|
| 17 |
+ if err := os.Mkdir(filepath.Join(dir, "blobs"), 0o755); err != nil {
|
|
| 18 |
+ return err |
|
| 19 |
+ } |
|
| 20 |
+ |
|
| 21 |
+ blobsDir := filepath.Join(dir, "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158") |
|
| 22 |
+ if err := os.Mkdir(blobsDir, 0o755); err != nil {
|
|
| 23 |
+ return err |
|
| 24 |
+ } |
|
| 25 |
+ |
|
| 26 |
+ if err := os.WriteFile(filepath.Join(dir, "VERSION"), []byte(`1.0`), 0o644); err != nil {
|
|
| 27 |
+ return err |
|
| 28 |
+ } |
|
| 29 |
+ if err := os.WriteFile(filepath.Join(dir, "repositories"), []byte(`{"emptyfs":{"latest":"511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158"}}`), 0o644); err != nil {
|
|
| 30 |
+ return err |
|
| 31 |
+ } |
|
| 32 |
+ 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 {
|
|
| 33 |
+ return err |
|
| 34 |
+ } |
|
| 35 |
+ |
|
| 36 |
+ 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 {
|
|
| 37 |
+ return err |
|
| 38 |
+ } |
|
| 39 |
+ |
|
| 40 |
+ layerFile, err := os.OpenFile(filepath.Join(blobsDir, "layer.tar"), os.O_CREATE|os.O_WRONLY, 0o644) |
|
| 41 |
+ if err != nil {
|
|
| 42 |
+ return err |
|
| 43 |
+ } |
|
| 44 |
+ defer layerFile.Close() |
|
| 45 |
+ |
|
| 46 |
+ // 10240 NUL bytes is a valid empty tar archive. |
|
| 47 |
+ _, err = io.Copy(layerFile, io.LimitReader(zeroReader{}, 10240))
|
|
| 48 |
+ if err != nil {
|
|
| 49 |
+ return err |
|
| 50 |
+ } |
|
| 51 |
+ |
|
| 52 |
+ return nil |
|
| 53 |
+} |
|
| 54 |
+ |
|
| 55 |
+type zeroReader struct{}
|
|
| 56 |
+ |
|
| 57 |
+func (_ zeroReader) Read(p []byte) (n int, err error) {
|
|
| 58 |
+ l := len(p) |
|
| 59 |
+ for idx := 0; idx < l; idx++ {
|
|
| 60 |
+ p[idx] = 0 |
|
| 61 |
+ } |
|
| 62 |
+ return l, nil |
|
| 63 |
+} |
| 0 | 64 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,66 @@ |
| 0 |
+package specialimage |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "context" |
|
| 4 |
+ "encoding/json" |
|
| 5 |
+ "errors" |
|
| 6 |
+ "io" |
|
| 7 |
+ "strings" |
|
| 8 |
+ "testing" |
|
| 9 |
+ |
|
| 10 |
+ "github.com/docker/docker/client" |
|
| 11 |
+ "github.com/docker/docker/pkg/archive" |
|
| 12 |
+ "github.com/docker/docker/pkg/jsonmessage" |
|
| 13 |
+ "gotest.tools/v3/assert" |
|
| 14 |
+) |
|
| 15 |
+ |
|
| 16 |
+type SpecialImageFunc func(string) error |
|
| 17 |
+ |
|
| 18 |
+func Load(ctx context.Context, t *testing.T, apiClient client.APIClient, imageFunc SpecialImageFunc) string {
|
|
| 19 |
+ tempDir := t.TempDir() |
|
| 20 |
+ |
|
| 21 |
+ err := imageFunc(tempDir) |
|
| 22 |
+ assert.NilError(t, err) |
|
| 23 |
+ |
|
| 24 |
+ rc, err := archive.TarWithOptions(tempDir, &archive.TarOptions{})
|
|
| 25 |
+ assert.NilError(t, err) |
|
| 26 |
+ |
|
| 27 |
+ defer rc.Close() |
|
| 28 |
+ |
|
| 29 |
+ resp, err := apiClient.ImageLoad(ctx, rc, true) |
|
| 30 |
+ assert.NilError(t, err, "Failed to load dangling image") |
|
| 31 |
+ |
|
| 32 |
+ defer resp.Body.Close() |
|
| 33 |
+ |
|
| 34 |
+ if !assert.Check(t, err) {
|
|
| 35 |
+ respBody, err := io.ReadAll(resp.Body) |
|
| 36 |
+ if err != nil {
|
|
| 37 |
+ t.Fatalf("Failed to read response body: %v", err)
|
|
| 38 |
+ return "" |
|
| 39 |
+ } |
|
| 40 |
+ t.Fatalf("Failed load: %s", string(respBody))
|
|
| 41 |
+ } |
|
| 42 |
+ |
|
| 43 |
+ decoder := json.NewDecoder(resp.Body) |
|
| 44 |
+ for {
|
|
| 45 |
+ var msg jsonmessage.JSONMessage |
|
| 46 |
+ err := decoder.Decode(&msg) |
|
| 47 |
+ if errors.Is(err, io.EOF) {
|
|
| 48 |
+ break |
|
| 49 |
+ } else {
|
|
| 50 |
+ assert.NilError(t, err) |
|
| 51 |
+ } |
|
| 52 |
+ |
|
| 53 |
+ msg.Stream = strings.TrimSpace(msg.Stream) |
|
| 54 |
+ |
|
| 55 |
+ if _, imageID, hasID := strings.Cut(msg.Stream, "Loaded image ID: "); hasID {
|
|
| 56 |
+ return imageID |
|
| 57 |
+ } |
|
| 58 |
+ if _, imageRef, hasRef := strings.Cut(msg.Stream, "Loaded image: "); hasRef {
|
|
| 59 |
+ return imageRef |
|
| 60 |
+ } |
|
| 61 |
+ } |
|
| 62 |
+ |
|
| 63 |
+ t.Fatal("failed to read image ID")
|
|
| 64 |
+ return "" |
|
| 65 |
+} |
| ... | ... |
@@ -101,7 +101,6 @@ func ProtectImages(ctx context.Context, t testing.TB, testEnv *Execution) {
|
| 101 | 101 |
images = append(images, frozenImages...) |
| 102 | 102 |
} |
| 103 | 103 |
testEnv.ProtectImage(t, images...) |
| 104 |
- testEnv.ProtectImage(t, DanglingImageIdGraphDriver, DanglingImageIdSnapshotter) |
|
| 105 | 104 |
} |
| 106 | 105 |
|
| 107 | 106 |
func getExistingImages(ctx context.Context, t testing.TB, testEnv *Execution) []string {
|
| 108 | 107 |
deleted file mode 100644 |
| ... | ... |
@@ -1,18 +0,0 @@ |
| 1 |
-package environment |
|
| 2 |
- |
|
| 3 |
-// DanglingImageIdGraphDriver is the digest for dangling images used |
|
| 4 |
-// in tests when the graph driver is used. The graph driver image store |
|
| 5 |
-// identifies images by the ID of their config. |
|
| 6 |
-const DanglingImageIdGraphDriver = "sha256:0df1207206e5288f4a989a2f13d1f5b3c4e70467702c1d5d21dfc9f002b7bd43" // #nosec G101 -- ignoring: Potential hardcoded credentials (gosec) |
|
| 7 |
- |
|
| 8 |
-// DanglingImageIdSnapshotter is the digest for dangling images used in |
|
| 9 |
-// tests when the containerd image store is used. The container image |
|
| 10 |
-// store identifies images by the ID of their manifest/manifest list.. |
|
| 11 |
-const DanglingImageIdSnapshotter = "sha256:16d365089e5c10e1673ee82ab5bba38ade9b763296ad918bd24b42a1156c5456" // #nosec G101 -- ignoring: Potential hardcoded credentials (gosec) |
|
| 12 |
- |
|
| 13 |
-func GetTestDanglingImageId(testEnv *Execution) string {
|
|
| 14 |
- if testEnv.UsingSnapshotter() {
|
|
| 15 |
- return DanglingImageIdSnapshotter |
|
| 16 |
- } |
|
| 17 |
- return DanglingImageIdGraphDriver |
|
| 18 |
-} |