2016-11-17 18:28:05 +00:00
|
|
|
package ocischema
|
|
|
|
|
|
|
|
import (
|
2017-09-01 02:14:40 +00:00
|
|
|
"context"
|
2016-11-17 18:28:05 +00:00
|
|
|
"reflect"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/docker/distribution"
|
|
|
|
"github.com/opencontainers/go-digest"
|
2019-10-09 12:02:21 +00:00
|
|
|
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
2016-11-17 18:28:05 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type mockBlobService struct {
|
|
|
|
descriptors map[digest.Digest]distribution.Descriptor
|
|
|
|
}
|
|
|
|
|
|
|
|
func (bs *mockBlobService) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) {
|
|
|
|
if descriptor, ok := bs.descriptors[dgst]; ok {
|
|
|
|
return descriptor, nil
|
|
|
|
}
|
|
|
|
return distribution.Descriptor{}, distribution.ErrBlobUnknown
|
|
|
|
}
|
|
|
|
|
|
|
|
func (bs *mockBlobService) Get(ctx context.Context, dgst digest.Digest) ([]byte, error) {
|
|
|
|
panic("not implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (bs *mockBlobService) Open(ctx context.Context, dgst digest.Digest) (distribution.ReadSeekCloser, error) {
|
|
|
|
panic("not implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (bs *mockBlobService) Put(ctx context.Context, mediaType string, p []byte) (distribution.Descriptor, error) {
|
|
|
|
d := distribution.Descriptor{
|
|
|
|
Digest: digest.FromBytes(p),
|
|
|
|
Size: int64(len(p)),
|
|
|
|
MediaType: "application/octet-stream",
|
|
|
|
}
|
|
|
|
bs.descriptors[d.Digest] = d
|
|
|
|
return d, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (bs *mockBlobService) Create(ctx context.Context, options ...distribution.BlobCreateOption) (distribution.BlobWriter, error) {
|
|
|
|
panic("not implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (bs *mockBlobService) Resume(ctx context.Context, id string) (distribution.BlobWriter, error) {
|
|
|
|
panic("not implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBuilder(t *testing.T) {
|
|
|
|
imgJSON := []byte(`{
|
2017-07-21 01:44:02 +00:00
|
|
|
"created": "2015-10-31T22:22:56.015925234Z",
|
|
|
|
"author": "Alyssa P. Hacker <alyspdev@example.com>",
|
2016-11-17 18:28:05 +00:00
|
|
|
"architecture": "amd64",
|
2017-07-21 01:44:02 +00:00
|
|
|
"os": "linux",
|
2016-11-17 18:28:05 +00:00
|
|
|
"config": {
|
2017-07-21 01:44:02 +00:00
|
|
|
"User": "alice",
|
|
|
|
"ExposedPorts": {
|
|
|
|
"8080/tcp": {}
|
|
|
|
},
|
2016-11-17 18:28:05 +00:00
|
|
|
"Env": [
|
|
|
|
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
2017-07-21 01:44:02 +00:00
|
|
|
"FOO=oci_is_a",
|
|
|
|
"BAR=well_written_spec"
|
2016-11-17 18:28:05 +00:00
|
|
|
],
|
2017-07-21 01:44:02 +00:00
|
|
|
"Entrypoint": [
|
|
|
|
"/bin/my-app-binary"
|
2016-11-17 18:28:05 +00:00
|
|
|
],
|
2017-07-21 01:44:02 +00:00
|
|
|
"Cmd": [
|
|
|
|
"--foreground",
|
|
|
|
"--config",
|
|
|
|
"/etc/my-app.d/default.cfg"
|
2016-11-17 18:28:05 +00:00
|
|
|
],
|
2017-07-21 01:44:02 +00:00
|
|
|
"Volumes": {
|
|
|
|
"/var/job-result-data": {},
|
|
|
|
"/var/log/my-app-logs": {}
|
2016-11-17 18:28:05 +00:00
|
|
|
},
|
2017-07-21 01:44:02 +00:00
|
|
|
"WorkingDir": "/home/alice",
|
|
|
|
"Labels": {
|
|
|
|
"com.example.project.git.url": "https://example.com/project.git",
|
|
|
|
"com.example.project.git.commit": "45a939b2999782a3f005621a8d0f29aa387e1d6b"
|
2016-11-17 18:28:05 +00:00
|
|
|
}
|
2017-07-21 01:44:02 +00:00
|
|
|
},
|
2016-11-17 18:28:05 +00:00
|
|
|
"rootfs": {
|
2017-07-21 01:44:02 +00:00
|
|
|
"diff_ids": [
|
|
|
|
"sha256:c6f988f4874bb0add23a778f753c65efe992244e148a1d2ec2a8b664fb66bbd1",
|
|
|
|
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
|
|
|
|
],
|
|
|
|
"type": "layers"
|
|
|
|
},
|
2017-07-31 23:34:11 +00:00
|
|
|
"annotations": {
|
|
|
|
"hot": "potato"
|
|
|
|
}
|
2017-07-21 01:44:02 +00:00
|
|
|
"history": [
|
|
|
|
{
|
|
|
|
"created": "2015-10-31T22:22:54.690851953Z",
|
|
|
|
"created_by": "/bin/sh -c #(nop) ADD file:a3bc1e842b69636f9df5256c49c5374fb4eef1e281fe3f282c65fb853ee171c5 in /"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"created": "2015-10-31T22:22:55.613815829Z",
|
|
|
|
"created_by": "/bin/sh -c #(nop) CMD [\"sh\"]",
|
|
|
|
"empty_layer": true
|
|
|
|
}
|
|
|
|
]
|
2016-11-17 18:28:05 +00:00
|
|
|
}`)
|
|
|
|
configDigest := digest.FromBytes(imgJSON)
|
|
|
|
|
|
|
|
descriptors := []distribution.Descriptor{
|
|
|
|
{
|
2017-07-21 01:44:02 +00:00
|
|
|
Digest: digest.Digest("sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"),
|
|
|
|
Size: 5312,
|
|
|
|
MediaType: v1.MediaTypeImageLayerGzip,
|
|
|
|
Annotations: map[string]string{"apple": "orange", "lettuce": "wrap"},
|
2016-11-17 18:28:05 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Digest: digest.Digest("sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa"),
|
|
|
|
Size: 235231,
|
2017-07-11 19:19:47 +00:00
|
|
|
MediaType: v1.MediaTypeImageLayerGzip,
|
2016-11-17 18:28:05 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
Digest: digest.Digest("sha256:b4ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"),
|
|
|
|
Size: 639152,
|
2017-07-11 19:19:47 +00:00
|
|
|
MediaType: v1.MediaTypeImageLayerGzip,
|
2016-11-17 18:28:05 +00:00
|
|
|
},
|
|
|
|
}
|
2017-07-31 23:34:11 +00:00
|
|
|
annotations := map[string]string{"hot": "potato"}
|
2016-11-17 18:28:05 +00:00
|
|
|
|
|
|
|
bs := &mockBlobService{descriptors: make(map[digest.Digest]distribution.Descriptor)}
|
2017-07-31 23:34:11 +00:00
|
|
|
builder := NewManifestBuilder(bs, imgJSON, annotations)
|
2016-11-17 18:28:05 +00:00
|
|
|
|
|
|
|
for _, d := range descriptors {
|
|
|
|
if err := builder.AppendReference(d); err != nil {
|
|
|
|
t.Fatalf("AppendReference returned error: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
built, err := builder.Build(context.Background())
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Build returned error: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that the config was put in the blob store
|
|
|
|
_, err = bs.Stat(context.Background(), configDigest)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal("config was not put in the blob store")
|
|
|
|
}
|
|
|
|
|
|
|
|
manifest := built.(*DeserializedManifest).Manifest
|
2017-07-31 23:34:11 +00:00
|
|
|
if manifest.Annotations["hot"] != "potato" {
|
|
|
|
t.Fatalf("unexpected annotation in manifest: %s", manifest.Annotations["hot"])
|
|
|
|
}
|
2016-11-17 18:28:05 +00:00
|
|
|
|
|
|
|
if manifest.Versioned.SchemaVersion != 2 {
|
|
|
|
t.Fatal("SchemaVersion != 2")
|
|
|
|
}
|
|
|
|
|
|
|
|
target := manifest.Target()
|
|
|
|
if target.Digest != configDigest {
|
|
|
|
t.Fatalf("unexpected digest in target: %s", target.Digest.String())
|
|
|
|
}
|
2017-07-11 19:19:47 +00:00
|
|
|
if target.MediaType != v1.MediaTypeImageConfig {
|
2016-11-17 18:28:05 +00:00
|
|
|
t.Fatalf("unexpected media type in target: %s", target.MediaType)
|
|
|
|
}
|
2017-07-31 23:34:11 +00:00
|
|
|
if target.Size != 1632 {
|
2016-11-17 18:28:05 +00:00
|
|
|
t.Fatalf("unexpected size in target: %d", target.Size)
|
|
|
|
}
|
|
|
|
|
|
|
|
references := manifest.References()
|
|
|
|
expected := append([]distribution.Descriptor{manifest.Target()}, descriptors...)
|
|
|
|
if !reflect.DeepEqual(references, expected) {
|
|
|
|
t.Fatal("References() does not match the descriptors added")
|
|
|
|
}
|
|
|
|
}
|