Skip to content

Commit

Permalink
OCI media types; annotation support; oci index
Browse files Browse the repository at this point in the history
Signed-off-by: Mike Brown <brownwm@us.ibm.com>
  • Loading branch information
mikebrow committed Jun 18, 2018
1 parent 6fcea22 commit c94f288
Show file tree
Hide file tree
Showing 11 changed files with 102 additions and 90 deletions.
8 changes: 8 additions & 0 deletions blobs.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/docker/distribution/reference"
"github.com/opencontainers/go-digest"
"github.com/opencontainers/image-spec/specs-go/v1"
)

var (
Expand Down Expand Up @@ -72,6 +73,13 @@ type Descriptor struct {
// URLs contains the source URLs of this content.
URLs []string `json:"urls,omitempty"`

// Annotations contains arbitrary metadata relating to the targeted content.
Annotations map[string]string `json:"annotations,omitempty"`

// Platform describes the platform which the image in the manifest runs on.
// This should only be used when referring to a manifest.
Platform *v1.Platform `json:"platform,omitempty"`

// NOTE: Before adding a field here, please ensure that all
// other options have been exhausted. Much of the type relationships
// depend on the simplicity of this type.
Expand Down
12 changes: 6 additions & 6 deletions manifest/manifestlist/manifestlist.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,13 @@ import (

"github.com/docker/distribution"
"github.com/docker/distribution/manifest"
"github.com/docker/distribution/manifest/ocischema"
"github.com/opencontainers/go-digest"
"github.com/opencontainers/image-spec/specs-go/v1"
)

const (
// MediaTypeManifestList specifies the mediaType for manifest lists.
MediaTypeManifestList = "application/vnd.docker.distribution.manifest.list.v2+json"
// MediaTypeOCIManifestList specifies the mediaType for OCI compliant manifest
// lists.
MediaTypeOCIManifestList = "application/vnd.oci.image.manifest.list.v1+json"
)

// SchemaVersion provides a pre-initialized version structure for this
Expand All @@ -30,7 +27,7 @@ var SchemaVersion = manifest.Versioned{
// packages OCIschema version of the manifest.
var OCISchemaVersion = manifest.Versioned{
SchemaVersion: 2,
MediaType: MediaTypeOCIManifestList,
MediaType: v1.MediaTypeImageIndex,
}

func init() {
Expand Down Expand Up @@ -92,6 +89,9 @@ type ManifestList struct {

// Config references the image configuration as a blob.
Manifests []ManifestDescriptor `json:"manifests"`

// Annotations contains arbitrary metadata for the image index.
Annotations map[string]string `json:"annotations,omitempty"`
}

// References returns the distribution descriptors for the referenced image
Expand Down Expand Up @@ -119,7 +119,7 @@ type DeserializedManifestList struct {
// and its JSON representation.
func FromDescriptors(descriptors []ManifestDescriptor) (*DeserializedManifestList, error) {
var m ManifestList
if len(descriptors) > 0 && descriptors[0].Descriptor.MediaType == ocischema.MediaTypeManifest {
if len(descriptors) > 0 && descriptors[0].Descriptor.MediaType == v1.MediaTypeImageManifest {
m = ManifestList{
Versioned: OCISchemaVersion,
}
Expand Down
13 changes: 7 additions & 6 deletions manifest/manifestlist/manifestlist_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"testing"

"github.com/docker/distribution"
"github.com/opencontainers/image-spec/specs-go/v1"
)

var expectedManifestListSerialization = []byte(`{
Expand Down Expand Up @@ -110,9 +111,9 @@ func TestManifestList(t *testing.T) {
}
}

var expectedOCIManifestListSerialization = []byte(`{
var expectedOCIImageIndexSerialization = []byte(`{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.list.v1+json",
"mediaType": "application/vnd.oci.image.index.v1+json",
"manifests": [
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
Expand All @@ -138,7 +139,7 @@ var expectedOCIManifestListSerialization = []byte(`{
]
}`)

func TestOCIManifestList(t *testing.T) {
func TestOCIImageIndex(t *testing.T) {
manifestDescriptors := []ManifestDescriptor{
{
Descriptor: distribution.Descriptor{
Expand Down Expand Up @@ -172,7 +173,7 @@ func TestOCIManifestList(t *testing.T) {

mediaType, canonical, _ := deserialized.Payload()

if mediaType != MediaTypeOCIManifestList {
if mediaType != v1.MediaTypeImageIndex {
t.Fatalf("unexpected media type: %s", mediaType)
}

Expand All @@ -187,8 +188,8 @@ func TestOCIManifestList(t *testing.T) {
}

// Check that the canonical field has the expected value.
if !bytes.Equal(expectedOCIManifestListSerialization, canonical) {
t.Fatalf("manifest bytes not equal: %q != %q", string(canonical), string(expectedOCIManifestListSerialization))
if !bytes.Equal(expectedOCIImageIndexSerialization, canonical) {
t.Fatalf("manifest bytes not equal: %q != %q", string(canonical), string(expectedOCIImageIndexSerialization))
}

var unmarshalled DeserializedManifestList
Expand Down
7 changes: 4 additions & 3 deletions manifest/ocischema/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/docker/distribution"
"github.com/docker/distribution/context"
"github.com/opencontainers/go-digest"
"github.com/opencontainers/image-spec/specs-go/v1"
)

// builder is a type for constructing manifests.
Expand Down Expand Up @@ -48,7 +49,7 @@ func (mb *builder) Build(ctx context.Context) (distribution.Manifest, error) {
case nil:
// Override MediaType, since Put always replaces the specified media
// type with application/octet-stream in the descriptor it returns.
m.Config.MediaType = MediaTypeConfig
m.Config.MediaType = v1.MediaTypeImageConfig
return FromStruct(m)
case distribution.ErrBlobUnknown:
// nop
Expand All @@ -57,10 +58,10 @@ func (mb *builder) Build(ctx context.Context) (distribution.Manifest, error) {
}

// Add config to the blob store
m.Config, err = mb.bs.Put(ctx, MediaTypeConfig, mb.configJSON)
m.Config, err = mb.bs.Put(ctx, v1.MediaTypeImageConfig, mb.configJSON)
// Override MediaType, since Put always replaces the specified media
// type with application/octet-stream in the descriptor it returns.
m.Config.MediaType = MediaTypeConfig
m.Config.MediaType = v1.MediaTypeImageConfig
if err != nil {
return nil, err
}
Expand Down
9 changes: 5 additions & 4 deletions manifest/ocischema/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/docker/distribution"
"github.com/docker/distribution/context"
"github.com/opencontainers/go-digest"
"github.com/opencontainers/image-spec/specs-go/v1"
)

type mockBlobService struct {
Expand Down Expand Up @@ -151,17 +152,17 @@ func TestBuilder(t *testing.T) {
{
Digest: digest.Digest("sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"),
Size: 5312,
MediaType: MediaTypeLayer,
MediaType: v1.MediaTypeImageLayerGzip,
},
{
Digest: digest.Digest("sha256:86e0e091d0da6bde2456dbb48306f3956bbeb2eae1b5b9a43045843f69fe4aaa"),
Size: 235231,
MediaType: MediaTypeLayer,
MediaType: v1.MediaTypeImageLayerGzip,
},
{
Digest: digest.Digest("sha256:b4ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"),
Size: 639152,
MediaType: MediaTypeLayer,
MediaType: v1.MediaTypeImageLayerGzip,
},
}

Expand Down Expand Up @@ -195,7 +196,7 @@ func TestBuilder(t *testing.T) {
if target.Digest != configDigest {
t.Fatalf("unexpected digest in target: %s", target.Digest.String())
}
if target.MediaType != MediaTypeConfig {
if target.MediaType != v1.MediaTypeImageConfig {
t.Fatalf("unexpected media type in target: %s", target.MediaType)
}
if target.Size != 3153 {
Expand Down
33 changes: 10 additions & 23 deletions manifest/ocischema/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,32 +8,15 @@ import (
"github.com/docker/distribution"
"github.com/docker/distribution/manifest"
"github.com/opencontainers/go-digest"
)

const (
// MediaTypeManifest specifies the mediaType for the current version.
MediaTypeManifest = "application/vnd.oci.image.manifest.v1+json"

// MediaTypeConfig specifies the mediaType for the image configuration.
MediaTypeConfig = "application/vnd.oci.image.config.v1+json"

// MediaTypePluginConfig specifies the mediaType for plugin configuration.
MediaTypePluginConfig = "application/vnd.docker.plugin.v1+json"

// MediaTypeLayer is the mediaType used for layers referenced by the manifest.
MediaTypeLayer = "application/vnd.oci.image.layer.v1.tar+gzip"

// MediaTypeForeignLayer is the mediaType used for layers that must be
// downloaded from foreign URLs.
MediaTypeForeignLayer = "application/vnd.docker.image.rootfs.foreign.diff.tar.gzip"
"github.com/opencontainers/image-spec/specs-go/v1"
)

var (
// SchemaVersion provides a pre-initialized version structure for this
// packages version of the manifest.
SchemaVersion = manifest.Versioned{
SchemaVersion: 2, // Mike: todo this could confusing cause oci version 1 is closer to docker 2 than 1
MediaType: MediaTypeManifest,
SchemaVersion: 2, // TODO: (mikebrow/stevvooe) this could be confusing cause oci version 1 is closer to docker 2 than 1
MediaType: v1.MediaTypeImageManifest,
}
)

Expand All @@ -46,15 +29,15 @@ func init() {
}

dgst := digest.FromBytes(b)
return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: MediaTypeManifest}, err
return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: v1.MediaTypeImageManifest}, err
}
err := distribution.RegisterManifestSchema(MediaTypeManifest, ocischemaFunc)
err := distribution.RegisterManifestSchema(v1.MediaTypeImageManifest, ocischemaFunc)
if err != nil {
panic(fmt.Sprintf("Unable to register manifest: %s", err))
}
}

// Manifest defines a schema2 manifest.
// Manifest defines a ocischema manifest.
type Manifest struct {
manifest.Versioned

Expand All @@ -64,13 +47,17 @@ type Manifest struct {
// Layers lists descriptors for the layers referenced by the
// configuration.
Layers []distribution.Descriptor `json:"layers"`

// Annotations contains arbitrary metadata for the image manifest.
Annotations map[string]string `json:"annotations,omitempty"`
}

// References returnes the descriptors of this manifests references.
func (m Manifest) References() []distribution.Descriptor {
references := make([]distribution.Descriptor, 0, 1+len(m.Layers))
references = append(references, m.Config)
references = append(references, m.Layers...)
// TODO: (mikebrow/stevvooe) should we return annotations as references
return references
}

Expand Down
13 changes: 7 additions & 6 deletions manifest/ocischema/manifest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"testing"

"github.com/docker/distribution"
"github.com/opencontainers/image-spec/specs-go/v1"
)

var expectedManifestSerialization = []byte(`{
Expand All @@ -32,13 +33,13 @@ func TestManifest(t *testing.T) {
Config: distribution.Descriptor{
Digest: "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
Size: 985,
MediaType: MediaTypeConfig,
MediaType: v1.MediaTypeImageConfig,
},
Layers: []distribution.Descriptor{
{
Digest: "sha256:62d8908bee94c202b2d35224a221aaa2058318bfa9879fa541efaecba272331b",
Size: 153263,
MediaType: MediaTypeLayer,
MediaType: v1.MediaTypeImageLayerGzip,
},
},
}
Expand All @@ -48,9 +49,9 @@ func TestManifest(t *testing.T) {
t.Fatalf("error creating DeserializedManifest: %v", err)
}

mediaType, canonical, err := deserialized.Payload()
mediaType, canonical, _ := deserialized.Payload()

if mediaType != MediaTypeManifest {
if mediaType != v1.MediaTypeImageManifest {
t.Fatalf("unexpected media type: %s", mediaType)
}

Expand Down Expand Up @@ -82,7 +83,7 @@ func TestManifest(t *testing.T) {
if target.Digest != "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b" {
t.Fatalf("unexpected digest in target: %s", target.Digest.String())
}
if target.MediaType != MediaTypeConfig {
if target.MediaType != v1.MediaTypeImageConfig {
t.Fatalf("unexpected media type in target: %s", target.MediaType)
}
if target.Size != 985 {
Expand All @@ -102,7 +103,7 @@ func TestManifest(t *testing.T) {
if references[1].Digest != "sha256:62d8908bee94c202b2d35224a221aaa2058318bfa9879fa541efaecba272331b" {
t.Fatalf("unexpected digest in reference: %s", references[0].Digest.String())
}
if references[1].MediaType != MediaTypeLayer {
if references[1].MediaType != v1.MediaTypeImageLayerGzip {
t.Fatalf("unexpected media type in reference: %s", references[0].MediaType)
}
if references[1].Size != 153263 {
Expand Down
Loading

0 comments on commit c94f288

Please sign in to comment.