1 package ocischema
2
3 import (
4 "context"
5 "reflect"
6 "testing"
7
8 "github.com/docker/distribution"
9 "github.com/opencontainers/go-digest"
10 v1 "github.com/opencontainers/image-spec/specs-go/v1"
11 )
12
13 type mockBlobService struct {
14 descriptors map[digest.Digest]distribution.Descriptor
15 }
16
17 func (bs *mockBlobService) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) {
18 if descriptor, ok := bs.descriptors[dgst]; ok {
19 return descriptor, nil
20 }
21 return distribution.Descriptor{}, distribution.ErrBlobUnknown
22 }
23
24 func (bs *mockBlobService) Get(ctx context.Context, dgst digest.Digest) ([]byte, error) {
25 panic("not implemented")
26 }
27
28 func (bs *mockBlobService) Open(ctx context.Context, dgst digest.Digest) (distribution.ReadSeekCloser, error) {
29 panic("not implemented")
30 }
31
32 func (bs *mockBlobService) Put(ctx context.Context, mediaType string, p []byte) (distribution.Descriptor, error) {
33 d := distribution.Descriptor{
34 Digest: digest.FromBytes(p),
35 Size: int64(len(p)),
36 MediaType: "application/octet-stream",
37 }
38 bs.descriptors[d.Digest] = d
39 return d, nil
40 }
41
42 func (bs *mockBlobService) Create(ctx context.Context, options ...distribution.BlobCreateOption) (distribution.BlobWriter, error) {
43 panic("not implemented")
44 }
45
46 func (bs *mockBlobService) Resume(ctx context.Context, id string) (distribution.BlobWriter, error) {
47 panic("not implemented")
48 }
49
50 func TestBuilder(t *testing.T) {
51 imgJSON := []byte(`{
52 "created": "2015-10-31T22:22:56.015925234Z",
53 "author": "Alyssa P. Hacker <alyspdev@example.com>",
54 "architecture": "amd64",
55 "os": "linux",
56 "config": {
57 "User": "alice",
58 "ExposedPorts": {
59 "8080/tcp": {}
60 },
61 "Env": [
62 "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
63 "FOO=oci_is_a",
64 "BAR=well_written_spec"
65 ],
66 "Entrypoint": [
67 "/bin/my-app-binary"
68 ],
69 "Cmd": [
70 "--foreground",
71 "--config",
72 "/etc/my-app.d/default.cfg"
73 ],
74 "Volumes": {
75 "/var/job-result-data": {},
76 "/var/log/my-app-logs": {}
77 },
78 "WorkingDir": "/home/alice",
79 "Labels": {
80 "com.example.project.git.url": "https://example.com/project.git",
81 "com.example.project.git.commit": "45a939b2999782a3f005621a8d0f29aa387e1d6b"
82 }
83 },
84 "rootfs": {
85 "diff_ids": [
86 "sha256:c6f988f4874bb0add23a778f753c65efe992244e148a1d2ec2a8b664fb66bbd1",
87 "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
88 ],
89 "type": "layers"
90 },
91 "annotations": {
92 "hot": "potato"
93 }
94 "history": [
95 {
96 "created": "2015-10-31T22:22:54.690851953Z",
97 "created_by": "/bin/sh -c #(nop) ADD file:a3bc1e842b69636f9df5256c49c5374fb4eef1e281fe3f282c65fb853ee171c5 in /"
98 },
99 {
100 "created": "2015-10-31T22:22:55.613815829Z",
101 "created_by": "/bin/sh -c #(nop) CMD [\"sh\"]",
102 "empty_layer": true
103 }
104 ]
105 }`)
106 configDigest := digest.FromBytes(imgJSON)
107
108 descriptors := []distribution.Descriptor{
109 {
110 Digest: digest.Digest("sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"),
111 Size: 5312,
112 MediaType: v1.MediaTypeImageLayerGzip,
113 Annotations: map[string]string{"apple": "orange", "lettuce": "wrap"},
114 },
115 {
116 Digest: digest.Digest("sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa"),
117 Size: 235231,
118 MediaType: v1.MediaTypeImageLayerGzip,
119 },
120 {
121 Digest: digest.Digest("sha256:b4ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"),
122 Size: 639152,
123 MediaType: v1.MediaTypeImageLayerGzip,
124 },
125 }
126 annotations := map[string]string{"hot": "potato"}
127
128 bs := &mockBlobService{descriptors: make(map[digest.Digest]distribution.Descriptor)}
129 builder := NewManifestBuilder(bs, imgJSON, annotations)
130
131 for _, d := range descriptors {
132 if err := builder.AppendReference(d); err != nil {
133 t.Fatalf("AppendReference returned error: %v", err)
134 }
135 }
136
137 built, err := builder.Build(context.Background())
138 if err != nil {
139 t.Fatalf("Build returned error: %v", err)
140 }
141
142
143 _, err = bs.Stat(context.Background(), configDigest)
144 if err != nil {
145 t.Fatal("config was not put in the blob store")
146 }
147
148 manifest := built.(*DeserializedManifest).Manifest
149 if manifest.Annotations["hot"] != "potato" {
150 t.Fatalf("unexpected annotation in manifest: %s", manifest.Annotations["hot"])
151 }
152
153 if manifest.Versioned.SchemaVersion != 2 {
154 t.Fatal("SchemaVersion != 2")
155 }
156
157 target := manifest.Target()
158 if target.Digest != configDigest {
159 t.Fatalf("unexpected digest in target: %s", target.Digest.String())
160 }
161 if target.MediaType != v1.MediaTypeImageConfig {
162 t.Fatalf("unexpected media type in target: %s", target.MediaType)
163 }
164 if target.Size != 1632 {
165 t.Fatalf("unexpected size in target: %d", target.Size)
166 }
167
168 references := manifest.References()
169 expected := append([]distribution.Descriptor{manifest.Target()}, descriptors...)
170 if !reflect.DeepEqual(references, expected) {
171 t.Fatal("References() does not match the descriptors added")
172 }
173 }
174
View as plain text