Browse code

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>

Paweł Gronowski authored on 2023/12/05 19:59:19
Showing 14 changed files
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
... ...
@@ -162,7 +162,3 @@ while ! ${TEST_CLIENT_BINARY} version &> /dev/null; do
162 162
 	sleep 2
163 163
 done
164 164
 printf "\n"
165
-
166
-if [ "$(docker version --format '{{ .Server.Os }}')" != 'windows' ]; then
167
-	bundle .build-empty-images
168
-fi
... ...
@@ -60,5 +60,4 @@ test_env() {
60 60
 	)
61 61
 }
62 62
 
63
-sh /scripts/build-empty-images.sh
64 63
 run_test_integration
... ...
@@ -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
-}