1package schema2
2
3import (
4	"context"
5	"reflect"
6	"testing"
7
8	"github.com/docker/distribution"
9	"github.com/opencontainers/go-digest"
10)
11
12type mockBlobService struct {
13	descriptors map[digest.Digest]distribution.Descriptor
14}
15
16func (bs *mockBlobService) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) {
17	if descriptor, ok := bs.descriptors[dgst]; ok {
18		return descriptor, nil
19	}
20	return distribution.Descriptor{}, distribution.ErrBlobUnknown
21}
22
23func (bs *mockBlobService) Get(ctx context.Context, dgst digest.Digest) ([]byte, error) {
24	panic("not implemented")
25}
26
27func (bs *mockBlobService) Open(ctx context.Context, dgst digest.Digest) (distribution.ReadSeekCloser, error) {
28	panic("not implemented")
29}
30
31func (bs *mockBlobService) Put(ctx context.Context, mediaType string, p []byte) (distribution.Descriptor, error) {
32	d := distribution.Descriptor{
33		Digest:    digest.FromBytes(p),
34		Size:      int64(len(p)),
35		MediaType: "application/octet-stream",
36	}
37	bs.descriptors[d.Digest] = d
38	return d, nil
39}
40
41func (bs *mockBlobService) Create(ctx context.Context, options ...distribution.BlobCreateOption) (distribution.BlobWriter, error) {
42	panic("not implemented")
43}
44
45func (bs *mockBlobService) Resume(ctx context.Context, id string) (distribution.BlobWriter, error) {
46	panic("not implemented")
47}
48
49func TestBuilder(t *testing.T) {
50	imgJSON := []byte(`{
51    "architecture": "amd64",
52    "config": {
53        "AttachStderr": false,
54        "AttachStdin": false,
55        "AttachStdout": false,
56        "Cmd": [
57            "/bin/sh",
58            "-c",
59            "echo hi"
60        ],
61        "Domainname": "",
62        "Entrypoint": null,
63        "Env": [
64            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
65            "derived=true",
66            "asdf=true"
67        ],
68        "Hostname": "23304fc829f9",
69        "Image": "sha256:4ab15c48b859c2920dd5224f92aabcd39a52794c5b3cf088fb3bbb438756c246",
70        "Labels": {},
71        "OnBuild": [],
72        "OpenStdin": false,
73        "StdinOnce": false,
74        "Tty": false,
75        "User": "",
76        "Volumes": null,
77        "WorkingDir": ""
78    },
79    "container": "e91032eb0403a61bfe085ff5a5a48e3659e5a6deae9f4d678daa2ae399d5a001",
80    "container_config": {
81        "AttachStderr": false,
82        "AttachStdin": false,
83        "AttachStdout": false,
84        "Cmd": [
85            "/bin/sh",
86            "-c",
87            "#(nop) CMD [\"/bin/sh\" \"-c\" \"echo hi\"]"
88        ],
89        "Domainname": "",
90        "Entrypoint": null,
91        "Env": [
92            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
93            "derived=true",
94            "asdf=true"
95        ],
96        "Hostname": "23304fc829f9",
97        "Image": "sha256:4ab15c48b859c2920dd5224f92aabcd39a52794c5b3cf088fb3bbb438756c246",
98        "Labels": {},
99        "OnBuild": [],
100        "OpenStdin": false,
101        "StdinOnce": false,
102        "Tty": false,
103        "User": "",
104        "Volumes": null,
105        "WorkingDir": ""
106    },
107    "created": "2015-11-04T23:06:32.365666163Z",
108    "docker_version": "1.9.0-dev",
109    "history": [
110        {
111            "created": "2015-10-31T22:22:54.690851953Z",
112            "created_by": "/bin/sh -c #(nop) ADD file:a3bc1e842b69636f9df5256c49c5374fb4eef1e281fe3f282c65fb853ee171c5 in /"
113        },
114        {
115            "created": "2015-10-31T22:22:55.613815829Z",
116            "created_by": "/bin/sh -c #(nop) CMD [\"sh\"]"
117        },
118        {
119            "created": "2015-11-04T23:06:30.934316144Z",
120            "created_by": "/bin/sh -c #(nop) ENV derived=true",
121            "empty_layer": true
122        },
123        {
124            "created": "2015-11-04T23:06:31.192097572Z",
125            "created_by": "/bin/sh -c #(nop) ENV asdf=true",
126            "empty_layer": true
127        },
128        {
129            "created": "2015-11-04T23:06:32.083868454Z",
130            "created_by": "/bin/sh -c dd if=/dev/zero of=/file bs=1024 count=1024"
131        },
132        {
133            "created": "2015-11-04T23:06:32.365666163Z",
134            "created_by": "/bin/sh -c #(nop) CMD [\"/bin/sh\" \"-c\" \"echo hi\"]",
135            "empty_layer": true
136        }
137    ],
138    "os": "linux",
139    "rootfs": {
140        "diff_ids": [
141            "sha256:c6f988f4874bb0add23a778f753c65efe992244e148a1d2ec2a8b664fb66bbd1",
142            "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
143            "sha256:13f53e08df5a220ab6d13c58b2bf83a59cbdc2e04d0a3f041ddf4b0ba4112d49"
144        ],
145        "type": "layers"
146    }
147}`)
148	configDigest := digest.FromBytes(imgJSON)
149
150	descriptors := []distribution.Descriptor{
151		{
152			Digest:    digest.Digest("sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"),
153			Size:      5312,
154			MediaType: MediaTypeLayer,
155		},
156		{
157			Digest:    digest.Digest("sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa"),
158			Size:      235231,
159			MediaType: MediaTypeLayer,
160		},
161		{
162			Digest:    digest.Digest("sha256:b4ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"),
163			Size:      639152,
164			MediaType: MediaTypeLayer,
165		},
166	}
167
168	bs := &mockBlobService{descriptors: make(map[digest.Digest]distribution.Descriptor)}
169	builder := NewManifestBuilder(bs, MediaTypeImageConfig, imgJSON)
170
171	for _, d := range descriptors {
172		if err := builder.AppendReference(d); err != nil {
173			t.Fatalf("AppendReference returned error: %v", err)
174		}
175	}
176
177	built, err := builder.Build(context.Background())
178	if err != nil {
179		t.Fatalf("Build returned error: %v", err)
180	}
181
182	// Check that the config was put in the blob store
183	_, err = bs.Stat(context.Background(), configDigest)
184	if err != nil {
185		t.Fatal("config was not put in the blob store")
186	}
187
188	manifest := built.(*DeserializedManifest).Manifest
189
190	if manifest.Versioned.SchemaVersion != 2 {
191		t.Fatal("SchemaVersion != 2")
192	}
193
194	target := manifest.Target()
195	if target.Digest != configDigest {
196		t.Fatalf("unexpected digest in target: %s", target.Digest.String())
197	}
198	if target.MediaType != MediaTypeImageConfig {
199		t.Fatalf("unexpected media type in target: %s", target.MediaType)
200	}
201	if target.Size != 3153 {
202		t.Fatalf("unexpected size in target: %d", target.Size)
203	}
204
205	references := manifest.References()
206	expected := append([]distribution.Descriptor{manifest.Target()}, descriptors...)
207	if !reflect.DeepEqual(references, expected) {
208		t.Fatal("References() does not match the descriptors added")
209	}
210}
211