package ocischema import ( "context" "reflect" "testing" "github.com/docker/distribution" "github.com/opencontainers/go-digest" "github.com/opencontainers/image-spec/specs-go/v1" ) 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(`{ "created": "2015-10-31T22:22:56.015925234Z", "author": "Alyssa P. Hacker ", "architecture": "amd64", "os": "linux", "config": { "User": "alice", "ExposedPorts": { "8080/tcp": {} }, "Env": [ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "FOO=oci_is_a", "BAR=well_written_spec" ], "Entrypoint": [ "/bin/my-app-binary" ], "Cmd": [ "--foreground", "--config", "/etc/my-app.d/default.cfg" ], "Volumes": { "/var/job-result-data": {}, "/var/log/my-app-logs": {} }, "WorkingDir": "/home/alice", "Labels": { "com.example.project.git.url": "https://example.com/project.git", "com.example.project.git.commit": "45a939b2999782a3f005621a8d0f29aa387e1d6b" } }, "rootfs": { "diff_ids": [ "sha256:c6f988f4874bb0add23a778f753c65efe992244e148a1d2ec2a8b664fb66bbd1", "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" ], "type": "layers" }, "annotations": { "hot": "potato" } "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 } ] }`) configDigest := digest.FromBytes(imgJSON) descriptors := []distribution.Descriptor{ { Digest: digest.Digest("sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"), Size: 5312, MediaType: v1.MediaTypeImageLayerGzip, Annotations: map[string]string{"apple": "orange", "lettuce": "wrap"}, }, { Digest: digest.Digest("sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa"), Size: 235231, MediaType: v1.MediaTypeImageLayerGzip, }, { Digest: digest.Digest("sha256:b4ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"), Size: 639152, MediaType: v1.MediaTypeImageLayerGzip, }, } annotations := map[string]string{"hot": "potato"} bs := &mockBlobService{descriptors: make(map[digest.Digest]distribution.Descriptor)} builder := NewManifestBuilder(bs, imgJSON, annotations) 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 if manifest.Annotations["hot"] != "potato" { t.Fatalf("unexpected annotation in manifest: %s", manifest.Annotations["hot"]) } 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()) } if target.MediaType != v1.MediaTypeImageConfig { t.Fatalf("unexpected media type in target: %s", target.MediaType) } if target.Size != 1632 { 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") } }