Skip to content

Commit fc729a5

Browse files
committed
Add layout write tests
1 parent 34770b4 commit fc729a5

File tree

6 files changed

+245
-39
lines changed

6 files changed

+245
-39
lines changed

pkg/v1/layout/image.go

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -93,34 +93,23 @@ func (li *layoutImage) LayerByDigest(h v1.Hash) (partial.CompressedLayer, error)
9393

9494
for _, desc := range manifest.Layers {
9595
if h == desc.Digest {
96-
// We assume that all these layers are compressed, which is probably not
97-
// safe to assume. It will take some restructuring to make that work, so
98-
// just return an error for now if we encounter unexpected layers.
99-
if err := checkCompressedLayer(desc); err != nil {
100-
return nil, err
96+
switch desc.MediaType {
97+
case types.OCILayer, types.DockerLayer:
98+
return partial.CompressedToLayer(&compressedBlob{
99+
path: li.path,
100+
desc: desc,
101+
})
102+
default:
103+
// TODO: We assume everything is a compressed blob, but that might not be true.
104+
// TODO: Handle foreign layers.
105+
return nil, fmt.Errorf("unexpected media type: %v for layer: %v", desc.MediaType, desc.Digest)
101106
}
102-
103-
return partial.CompressedLayer(&compressedBlob{
104-
path: li.path,
105-
desc: desc,
106-
}), nil
107107
}
108108
}
109109

110110
return nil, fmt.Errorf("could not find layer in image: %s", h)
111111
}
112112

113-
func checkCompressedLayer(desc v1.Descriptor) error {
114-
switch desc.MediaType {
115-
case types.OCILayer:
116-
case types.DockerLayer:
117-
default:
118-
return fmt.Errorf("unexpected layer media type: %s for layer: %s", desc.MediaType, desc.Digest)
119-
}
120-
121-
return nil
122-
}
123-
124113
type compressedBlob struct {
125114
path string
126115
desc v1.Descriptor

pkg/v1/layout/image_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,17 @@ import (
2323
)
2424

2525
var (
26+
indexDigest = v1.Hash{
27+
Algorithm: "sha256",
28+
Hex: "05f95b26ed10668b7183c1e2da98610e91372fa9f510046d4ce5812addad86b5",
29+
}
2630
manifestDigest = v1.Hash{
2731
Algorithm: "sha256",
2832
Hex: "eebff607b1628d67459b0596643fc07de70d702eccf030f0bc7bb6fc2b278650",
2933
}
30-
indexDigest = v1.Hash{
34+
configDigest = v1.Hash{
3135
Algorithm: "sha256",
32-
Hex: "05f95b26ed10668b7183c1e2da98610e91372fa9f510046d4ce5812addad86b5",
36+
Hex: "6e0b05049ed9c17d02e1a55e80d6599dbfcce7f4f4b022e3c673e685789c470e",
3337
}
3438
bogusDigest = v1.Hash{
3539
Algorithm: "sha256",
@@ -40,10 +44,6 @@ var (
4044
)
4145

4246
func TestImage(t *testing.T) {
43-
configDigest := v1.Hash{
44-
Algorithm: "sha256",
45-
Hex: "6e0b05049ed9c17d02e1a55e80d6599dbfcce7f4f4b022e3c673e685789c470e",
46-
}
4747
img, err := Image(testPath, manifestDigest)
4848
if err != nil {
4949
t.Fatalf("Image() = %v", err)

pkg/v1/layout/index.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,12 @@ func Index(path string) (v1.ImageIndex, error) {
4040
return nil, err
4141
}
4242

43-
img := &layoutIndex{
43+
idx := &layoutIndex{
4444
path: path,
4545
rawIndex: rawIndex,
4646
}
4747

48-
return img, nil
48+
return idx, nil
4949
}
5050

5151
func (i *layoutIndex) MediaType() (types.MediaType, error) {

pkg/v1/layout/index_test.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func TestIndex(t *testing.T) {
2828
}
2929

3030
if err := validate.Index(idx); err != nil {
31-
t.Fatalf("validate.Index() = %v", err)
31+
t.Errorf("validate.Index() = %v", err)
3232
}
3333

3434
mt, err := idx.MediaType()
@@ -37,7 +37,11 @@ func TestIndex(t *testing.T) {
3737
}
3838

3939
if got, want := mt, types.OCIImageIndex; got != want {
40-
t.Fatalf("MediaType(); want: %v got: %v", want, got)
40+
t.Errorf("MediaType(); want: %v got: %v", want, got)
41+
}
42+
43+
if _, err := idx.Blob(configDigest); err != nil {
44+
t.Errorf("validate.Index() = %v", err)
4145
}
4246
}
4347

@@ -48,22 +52,22 @@ func TestIndexErrors(t *testing.T) {
4852
}
4953

5054
if _, err := idx.Image(bogusDigest); err == nil {
51-
t.Fatalf("idx.Image(%s) = nil, expected err", bogusDigest)
55+
t.Errorf("idx.Image(%s) = nil, expected err", bogusDigest)
5256
}
5357

5458
if _, err := idx.Image(indexDigest); err == nil {
55-
t.Fatalf("idx.Image(%s) = nil, expected err", bogusDigest)
59+
t.Errorf("idx.Image(%s) = nil, expected err", bogusDigest)
5660
}
5761

5862
if _, err := idx.ImageIndex(bogusDigest); err == nil {
59-
t.Fatalf("idx.ImageIndex(%s) = nil, expected err", bogusDigest)
63+
t.Errorf("idx.ImageIndex(%s) = nil, expected err", bogusDigest)
6064
}
6165

6266
if _, err := idx.ImageIndex(manifestDigest); err == nil {
63-
t.Fatalf("idx.ImageIndex(%s) = nil, expected err", bogusDigest)
67+
t.Errorf("idx.ImageIndex(%s) = nil, expected err", bogusDigest)
6468
}
6569

6670
if _, err := Index(bogusPath); err == nil {
67-
t.Fatalf("ImageIndex(%s) = nil, expected err", bogusPath)
71+
t.Errorf("Index(%s) = nil, expected err", bogusPath)
6872
}
6973
}

pkg/v1/layout/write.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,16 @@ func AppendDescriptor(path string, desc v1.Descriptor) (v1.ImageIndex, error) {
112112
// Create an empty image index if it doesn't exist.
113113
var ii v1.ImageIndex
114114
ii, err := Index(path)
115-
if os.IsNotExist(err) {
116-
if err := writeFile(path, "oci-layout", []byte(layoutFile)); err != nil {
115+
if err != nil {
116+
if os.IsNotExist(err) {
117+
// If it's not there, initialize the index.
118+
if err := writeFile(path, "oci-layout", []byte(layoutFile)); err != nil {
119+
return nil, err
120+
}
121+
ii = empty.Index
122+
} else {
117123
return nil, err
118124
}
119-
ii = empty.Index
120125
}
121126

122127
index, err := ii.IndexManifest()

pkg/v1/layout/write_test.go

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
package layout
2+
3+
import (
4+
"io/ioutil"
5+
"os"
6+
"strconv"
7+
"testing"
8+
9+
"github.com/google/go-cmp/cmp"
10+
"github.com/google/go-containerregistry/pkg/v1"
11+
"github.com/google/go-containerregistry/pkg/v1/random"
12+
"github.com/google/go-containerregistry/pkg/v1/types"
13+
"github.com/google/go-containerregistry/pkg/v1/validate"
14+
)
15+
16+
func TestWrite(t *testing.T) {
17+
tmp, err := ioutil.TempDir("", "write-index-test")
18+
if err != nil {
19+
t.Fatal(err)
20+
}
21+
22+
defer os.RemoveAll(tmp)
23+
24+
original, err := Index(testPath)
25+
if err != nil {
26+
t.Fatal(err)
27+
}
28+
29+
if err := Write(tmp, original); err != nil {
30+
t.Fatalf("Write(%s) = %v", tmp, err)
31+
}
32+
33+
written, err := Index(tmp)
34+
if err != nil {
35+
t.Fatal(err)
36+
}
37+
38+
if err := validate.Index(written); err != nil {
39+
t.Fatalf("validate.Index() = %v", err)
40+
}
41+
}
42+
43+
func TestWriteErrors(t *testing.T) {
44+
idx, err := Index(testPath)
45+
if err != nil {
46+
t.Fatalf("Index() = %v", err)
47+
}
48+
img, err := Image(testPath, manifestDigest)
49+
if err != nil {
50+
t.Fatalf("Image() = %v", err)
51+
}
52+
53+
// Found this here:
54+
// https://github.com/golang/go/issues/24195
55+
invalidPath := "double-null-padded-string\x00\x00"
56+
if err := Write(invalidPath, idx); err == nil {
57+
t.Fatalf("Write(%s) = nil, expected err", invalidPath)
58+
}
59+
if err := WriteIndex(invalidPath, idx); err == nil {
60+
t.Fatalf("WriteIndex(%s) = nil, expected err", invalidPath)
61+
}
62+
if err := WriteImage(invalidPath, img); err == nil {
63+
t.Fatalf("WriteIndex(%s) = nil, expected err", invalidPath)
64+
}
65+
if _, err := AppendIndex(invalidPath, idx); err == nil {
66+
t.Fatalf("WriteIndex(%s) = nil, expected err", invalidPath)
67+
}
68+
if _, err := AppendImage(invalidPath, img); err == nil {
69+
t.Fatalf("WriteIndex(%s) = nil, expected err", invalidPath)
70+
}
71+
if _, err := AppendDescriptor(invalidPath, v1.Descriptor{}); err == nil {
72+
t.Fatalf("WriteIndex(%s) = nil, expected err", invalidPath)
73+
}
74+
}
75+
76+
func TestAppendDescriptorInitializesIndex(t *testing.T) {
77+
tmp, err := ioutil.TempDir("", "write-index-test")
78+
if err != nil {
79+
t.Fatal(err)
80+
}
81+
82+
defer os.RemoveAll(tmp)
83+
84+
// Append a descriptor to a non-existent layout.
85+
desc := v1.Descriptor{
86+
Digest: bogusDigest,
87+
Size: 1337,
88+
MediaType: types.MediaType("not real"),
89+
}
90+
if _, err := AppendDescriptor(tmp, desc); err != nil {
91+
t.Fatalf("AppendDescriptor(%s) = %v", tmp, err)
92+
}
93+
94+
// Read that layout from disk and make sure the descriptor is there.
95+
idx, err := Index(tmp)
96+
if err != nil {
97+
t.Fatalf("Index() = %v", err)
98+
}
99+
manifest, err := idx.IndexManifest()
100+
if err != nil {
101+
t.Fatalf("IndexManifest() = %v", err)
102+
}
103+
if diff := cmp.Diff(manifest.Manifests[0], desc); diff != "" {
104+
t.Fatalf("bad descriptor: (-got +want) %s", diff)
105+
}
106+
}
107+
108+
func TestAppendArtifacts(t *testing.T) {
109+
tmp, err := ioutil.TempDir("", "write-index-test")
110+
if err != nil {
111+
t.Fatal(err)
112+
}
113+
114+
defer os.RemoveAll(tmp)
115+
116+
original, err := Index(testPath)
117+
if err != nil {
118+
t.Fatal(err)
119+
}
120+
originalManifest, err := original.IndexManifest()
121+
if err != nil {
122+
t.Fatal(err)
123+
}
124+
125+
// Let's reconstruct the original.
126+
for i, desc := range originalManifest.Manifests {
127+
// Each descriptor is annotated with its position.
128+
annotations := map[string]string{
129+
"org.opencontainers.image.ref.name": strconv.Itoa(i + 1),
130+
}
131+
switch desc.MediaType {
132+
case types.OCIImageIndex, types.DockerManifestList:
133+
ii, err := original.ImageIndex(desc.Digest)
134+
if err != nil {
135+
t.Fatal(err)
136+
}
137+
if _, err := AppendIndex(tmp, ii, WithAnnotations(annotations)); err != nil {
138+
t.Fatal(err)
139+
}
140+
case types.OCIManifestSchema1, types.DockerManifestSchema2:
141+
img, err := original.Image(desc.Digest)
142+
if err != nil {
143+
t.Fatal(err)
144+
}
145+
if _, err := AppendImage(tmp, img, WithAnnotations(annotations)); err != nil {
146+
t.Fatal(err)
147+
}
148+
}
149+
}
150+
151+
reconstructed, err := Index(tmp)
152+
if err != nil {
153+
t.Fatalf("Index() = %v", err)
154+
}
155+
reconstructedManifest, err := reconstructed.IndexManifest()
156+
if err != nil {
157+
t.Fatal(err)
158+
}
159+
if diff := cmp.Diff(originalManifest, reconstructedManifest); diff != "" {
160+
t.Fatalf("bad manifest: (-got +want) %s", diff)
161+
}
162+
}
163+
164+
func TestOptions(t *testing.T) {
165+
tmp, err := ioutil.TempDir("", "write-index-test")
166+
if err != nil {
167+
t.Fatal(err)
168+
}
169+
annotations := map[string]string{
170+
"foo": "bar",
171+
}
172+
urls := []string{"https://example.com"}
173+
platform := v1.Platform{
174+
Architecture: "mill",
175+
OS: "haiku",
176+
}
177+
img, err := random.Image(5, 5)
178+
if err != nil {
179+
t.Fatal(err)
180+
}
181+
options := []LayoutOption{
182+
WithAnnotations(annotations),
183+
WithURLs(urls),
184+
WithPlatform(platform),
185+
}
186+
idx, err := AppendImage(tmp, img, options...)
187+
if err != nil {
188+
t.Fatal(err)
189+
}
190+
indexManifest, err := idx.IndexManifest()
191+
if err != nil {
192+
t.Fatal(err)
193+
}
194+
195+
desc := indexManifest.Manifests[0]
196+
if got, want := desc.Annotations["foo"], "bar"; got != want {
197+
t.Fatalf("wrong annotation; got: %v, want: %v", got, want)
198+
}
199+
if got, want := desc.URLs[0], "https://example.com"; got != want {
200+
t.Fatalf("wrong urls; got: %v, want: %v", got, want)
201+
}
202+
if got, want := desc.Platform.Architecture, "mill"; got != want {
203+
t.Fatalf("wrong Architecture; got: %v, want: %v", got, want)
204+
}
205+
if got, want := desc.Platform.OS, "haiku"; got != want {
206+
t.Fatalf("wrong OS; got: %v, want: %v", got, want)
207+
}
208+
}

0 commit comments

Comments
 (0)