Browse code

specialimage: Return optional ocispec.Index

To ease accessing image descriptors in tests.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>

Paweł Gronowski authored on 2024/02/27 01:38:46
Showing 5 changed files
... ...
@@ -473,7 +473,8 @@ func loadSpecialImage(c *testing.T, imageFunc specialimage.SpecialImageFunc) str
473 473
 	imgDir := filepath.Join(tmpDir, "image")
474 474
 	assert.NilError(c, os.Mkdir(imgDir, 0o755))
475 475
 
476
-	assert.NilError(c, imageFunc(imgDir))
476
+	_, err := imageFunc(imgDir)
477
+	assert.NilError(c, err)
477 478
 
478 479
 	rc, err := archive.TarWithOptions(imgDir, &archive.TarOptions{})
479 480
 	assert.NilError(c, err)
... ...
@@ -4,6 +4,8 @@ import (
4 4
 	"os"
5 5
 	"path/filepath"
6 6
 	"strings"
7
+
8
+	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
7 9
 )
8 10
 
9 11
 const danglingImageManifestDigest = "sha256:16d365089e5c10e1673ee82ab5bba38ade9b763296ad918bd24b42a1156c5456" // #nosec G101 -- ignoring: Potential hardcoded credentials (gosec)
... ...
@@ -12,30 +14,30 @@ const danglingImageConfigDigest = "sha256:0df1207206e5288f4a989a2f13d1f5b3c4e704
12 12
 // Dangling creates an image with no layers and no tag.
13 13
 // It also has an extra org.mobyproject.test.specialimage=1 label set.
14 14
 // Layout: OCI.
15
-func Dangling(dir string) error {
15
+func Dangling(dir string) (*ocispec.Index, error) {
16 16
 	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 {
17
-		return err
17
+		return nil, err
18 18
 	}
19 19
 
20 20
 	if err := os.WriteFile(filepath.Join(dir, "manifest.json"), []byte(`[{"Config":"blobs/sha256/0df1207206e5288f4a989a2f13d1f5b3c4e70467702c1d5d21dfc9f002b7bd43","RepoTags":null,"Layers":null}]`), 0o644); err != nil {
21
-		return err
21
+		return nil, err
22 22
 	}
23 23
 
24 24
 	if err := os.Mkdir(filepath.Join(dir, "blobs"), 0o755); err != nil {
25
-		return err
25
+		return nil, err
26 26
 	}
27 27
 
28 28
 	blobsDir := filepath.Join(dir, "blobs", "sha256")
29 29
 	if err := os.Mkdir(blobsDir, 0o755); err != nil {
30
-		return err
30
+		return nil, err
31 31
 	}
32 32
 
33 33
 	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 {
34
-		return err
34
+		return nil, err
35 35
 	}
36 36
 	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 {
37
-		return err
37
+		return nil, err
38 38
 	}
39 39
 
40
-	return nil
40
+	return nil, nil
41 41
 }
... ...
@@ -4,53 +4,55 @@ import (
4 4
 	"io"
5 5
 	"os"
6 6
 	"path/filepath"
7
+
8
+	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
7 9
 )
8 10
 
9 11
 // EmptyFS builds an image with an empty rootfs.
10 12
 // Layout: Legacy Docker Archive
11 13
 // See https://github.com/docker/docker/pull/5262
12 14
 // and also https://github.com/docker/docker/issues/4242
13
-func EmptyFS(dir string) error {
15
+func EmptyFS(dir string) (*ocispec.Index, error) {
14 16
 	if err := os.WriteFile(filepath.Join(dir, "manifest.json"), []byte(`[{"Config":"11f64303f0f7ffdc71f001788132bca5346831939a956e3e975c93267d89a16d.json","RepoTags":["emptyfs:latest"],"Layers":["511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158/layer.tar"]}]`), 0o644); err != nil {
15
-		return err
17
+		return nil, err
16 18
 	}
17 19
 
18 20
 	if err := os.Mkdir(filepath.Join(dir, "blobs"), 0o755); err != nil {
19
-		return err
21
+		return nil, err
20 22
 	}
21 23
 
22 24
 	blobsDir := filepath.Join(dir, "511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158")
23 25
 	if err := os.Mkdir(blobsDir, 0o755); err != nil {
24
-		return err
26
+		return nil, err
25 27
 	}
26 28
 
27 29
 	if err := os.WriteFile(filepath.Join(dir, "VERSION"), []byte(`1.0`), 0o644); err != nil {
28
-		return err
30
+		return nil, err
29 31
 	}
30 32
 	if err := os.WriteFile(filepath.Join(dir, "repositories"), []byte(`{"emptyfs":{"latest":"511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158"}}`), 0o644); err != nil {
31
-		return err
33
+		return nil, err
32 34
 	}
33 35
 	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 {
34
-		return err
36
+		return nil, err
35 37
 	}
36 38
 
37 39
 	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 {
38
-		return err
40
+		return nil, err
39 41
 	}
40 42
 
41 43
 	layerFile, err := os.OpenFile(filepath.Join(blobsDir, "layer.tar"), os.O_CREATE|os.O_WRONLY, 0o644)
42 44
 	if err != nil {
43
-		return err
45
+		return nil, err
44 46
 	}
45 47
 	defer layerFile.Close()
46 48
 
47 49
 	// 10240 NUL bytes is a valid empty tar archive.
48 50
 	_, err = io.Copy(layerFile, io.LimitReader(zeroReader{}, 10240))
49 51
 	if err != nil {
50
-		return err
52
+		return nil, err
51 53
 	}
52 54
 
53
-	return nil
55
+	return nil, nil
54 56
 }
55 57
 
56 58
 type zeroReader struct{}
... ...
@@ -12,15 +12,16 @@ import (
12 12
 	"github.com/docker/docker/client"
13 13
 	"github.com/docker/docker/pkg/archive"
14 14
 	"github.com/docker/docker/pkg/jsonmessage"
15
+	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
15 16
 	"gotest.tools/v3/assert"
16 17
 )
17 18
 
18
-type SpecialImageFunc func(string) error
19
+type SpecialImageFunc func(string) (*ocispec.Index, error)
19 20
 
20 21
 func Load(ctx context.Context, t *testing.T, apiClient client.APIClient, imageFunc SpecialImageFunc) string {
21 22
 	tempDir := t.TempDir()
22 23
 
23
-	err := imageFunc(tempDir)
24
+	_, err := imageFunc(tempDir)
24 25
 	assert.NilError(t, err)
25 26
 
26 27
 	rc, err := archive.TarWithOptions(tempDir, &archive.TarOptions{})
... ...
@@ -16,20 +16,20 @@ import (
16 16
 	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
17 17
 )
18 18
 
19
-func MultiLayer(dir string) error {
19
+func MultiLayer(dir string) (*ocispec.Index, error) {
20 20
 	const imageRef = "multilayer:latest"
21 21
 
22 22
 	layer1Desc, err := writeLayerWithOneFile(dir, "foo", []byte("1"))
23 23
 	if err != nil {
24
-		return err
24
+		return nil, err
25 25
 	}
26 26
 	layer2Desc, err := writeLayerWithOneFile(dir, "bar", []byte("2"))
27 27
 	if err != nil {
28
-		return err
28
+		return nil, err
29 29
 	}
30 30
 	layer3Desc, err := writeLayerWithOneFile(dir, "hello", []byte("world"))
31 31
 	if err != nil {
32
-		return err
32
+		return nil, err
33 33
 	}
34 34
 
35 35
 	configDesc, err := writeJsonBlob(dir, ocispec.MediaTypeImageConfig, ocispec.Image{
... ...
@@ -43,7 +43,7 @@ func MultiLayer(dir string) error {
43 43
 		},
44 44
 	})
45 45
 	if err != nil {
46
-		return err
46
+		return nil, err
47 47
 	}
48 48
 
49 49
 	manifest := ocispec.Manifest{
... ...
@@ -53,7 +53,7 @@ func MultiLayer(dir string) error {
53 53
 	}
54 54
 
55 55
 	legacyManifests := []manifestItem{
56
-		manifestItem{
56
+		{
57 57
 			Config:   blobPath(configDesc),
58 58
 			RepoTags: []string{imageRef},
59 59
 			Layers:   []string{blobPath(layer1Desc), blobPath(layer2Desc), blobPath(layer3Desc)},
... ...
@@ -62,7 +62,7 @@ func MultiLayer(dir string) error {
62 62
 
63 63
 	ref, err := reference.ParseNormalizedNamed(imageRef)
64 64
 	if err != nil {
65
-		return err
65
+		return nil, err
66 66
 	}
67 67
 	return singlePlatformImage(dir, ref, manifest, legacyManifests)
68 68
 }
... ...
@@ -74,10 +74,10 @@ type manifestItem struct {
74 74
 	Layers   []string
75 75
 }
76 76
 
77
-func singlePlatformImage(dir string, ref reference.Named, manifest ocispec.Manifest, legacyManifests []manifestItem) error {
77
+func singlePlatformImage(dir string, ref reference.Named, manifest ocispec.Manifest, legacyManifests []manifestItem) (*ocispec.Index, error) {
78 78
 	manifestDesc, err := writeJsonBlob(dir, ocispec.MediaTypeImageManifest, manifest)
79 79
 	if err != nil {
80
-		return err
80
+		return nil, err
81 81
 	}
82 82
 
83 83
 	if ref != nil {
... ...
@@ -90,25 +90,24 @@ func singlePlatformImage(dir string, ref reference.Named, manifest ocispec.Manif
90 90
 		}
91 91
 	}
92 92
 
93
-	if err := writeJson(ocispec.Index{
93
+	idx := ocispec.Index{
94 94
 		Versioned: specs.Versioned{SchemaVersion: 2},
95 95
 		MediaType: ocispec.MediaTypeImageIndex,
96 96
 		Manifests: []ocispec.Descriptor{manifestDesc},
97
-	}, filepath.Join(dir, "index.json")); err != nil {
98
-		return err
99 97
 	}
100
-	if err != nil {
101
-		return err
98
+	if err := writeJson(idx, filepath.Join(dir, "index.json")); err != nil {
99
+		return nil, err
102 100
 	}
103
-
104 101
 	if err := writeJson(legacyManifests, filepath.Join(dir, "manifest.json")); err != nil {
105
-		return err
102
+		return nil, err
106 103
 	}
104
+
105
+	err = os.WriteFile(filepath.Join(dir, "oci-layout"), []byte(`{"imageLayoutVersion": "1.0.0"}`), 0o644)
107 106
 	if err != nil {
108
-		return err
107
+		return nil, err
109 108
 	}
110 109
 
111
-	return os.WriteFile(filepath.Join(dir, "oci-layout"), []byte(`{"imageLayoutVersion": "1.0.0"}`), 0o644)
110
+	return &idx, nil
112 111
 }
113 112
 
114 113
 func fileArchive(dir string, name string, content []byte) (io.ReadCloser, error) {