Handle OCI manifests and image indexes without a media type

In the OCI image specification, the MediaType field is reserved
and otherwise undefined; assume that manifests without a media
in storage are OCI images or image indexes, and determine which
by looking at what fields are in the JSON. We do keep a check
that when unmarshalling an OCI image or image index, if it has
a MediaType field, it must match that media type of the upload.

Signed-off-by: Owen W. Taylor <otaylor@fishsoup.net>
This commit is contained in:
Owen W. Taylor 2018-03-14 17:22:36 -04:00
parent 1d47ef7b80
commit 60d9c5dfad
5 changed files with 27 additions and 8 deletions

View File

@ -60,8 +60,8 @@ func init() {
return nil, distribution.Descriptor{}, err
}
if m.MediaType != v1.MediaTypeImageIndex {
err = fmt.Errorf("mediaType in image index should be '%s' not '%s'",
if m.MediaType != "" && m.MediaType != v1.MediaTypeImageIndex {
err = fmt.Errorf("if present, mediaType in image index should be '%s' not '%s'",
v1.MediaTypeImageIndex, m.MediaType)
return nil, distribution.Descriptor{}, err
@ -205,5 +205,12 @@ func (m *DeserializedManifestList) MarshalJSON() ([]byte, error) {
// Payload returns the raw content of the manifest list. The contents can be
// used to calculate the content identifier.
func (m DeserializedManifestList) Payload() (string, []byte, error) {
return m.MediaType, m.canonical, nil
var mediaType string
if m.MediaType == "" {
mediaType = v1.MediaTypeImageIndex
} else {
mediaType = m.MediaType
}
return mediaType, m.canonical, nil
}

View File

@ -299,7 +299,7 @@ func TestMediaTypes(t *testing.T) {
mediaTypeTest(t, MediaTypeManifestList, "", true)
mediaTypeTest(t, MediaTypeManifestList, MediaTypeManifestList, false)
mediaTypeTest(t, MediaTypeManifestList, MediaTypeManifestList+"XXX", true)
mediaTypeTest(t, v1.MediaTypeImageIndex, "", true)
mediaTypeTest(t, v1.MediaTypeImageIndex, "", false)
mediaTypeTest(t, v1.MediaTypeImageIndex, v1.MediaTypeImageIndex, false)
mediaTypeTest(t, v1.MediaTypeImageIndex, v1.MediaTypeImageIndex+"XXX", true)
}

View File

@ -97,8 +97,8 @@ func (m *DeserializedManifest) UnmarshalJSON(b []byte) error {
return err
}
if manifest.MediaType != v1.MediaTypeImageManifest {
return fmt.Errorf("mediaType in manifest should be '%s' not '%s'",
if manifest.MediaType != "" && manifest.MediaType != v1.MediaTypeImageManifest {
return fmt.Errorf("if present, mediaType in manifest should be '%s' not '%s'",
v1.MediaTypeImageManifest, manifest.MediaType)
}
@ -120,5 +120,5 @@ func (m *DeserializedManifest) MarshalJSON() ([]byte, error) {
// Payload returns the raw content of the manifest. The contents can be used to
// calculate the content identifier.
func (m DeserializedManifest) Payload() (string, []byte, error) {
return m.MediaType, m.canonical, nil
return v1.MediaTypeImageManifest, m.canonical, nil
}

View File

@ -178,7 +178,7 @@ func mediaTypeTest(t *testing.T, mediaType string, shouldError bool) {
}
func TestMediaTypes(t *testing.T) {
mediaTypeTest(t, "", true)
mediaTypeTest(t, "", false)
mediaTypeTest(t, v1.MediaTypeImageManifest, false)
mediaTypeTest(t, v1.MediaTypeImageManifest+"XXX", true)
}

View File

@ -106,6 +106,18 @@ func (ms *manifestStore) Get(ctx context.Context, dgst digest.Digest, options ..
return ms.ocischemaHandler.Unmarshal(ctx, dgst, content)
case manifestlist.MediaTypeManifestList, v1.MediaTypeImageIndex:
return ms.manifestListHandler.Unmarshal(ctx, dgst, content)
case "":
// OCI image or image index - no media type in the content
// First see if it looks like an image index
res, err := ms.manifestListHandler.Unmarshal(ctx, dgst, content)
resIndex := res.(*manifestlist.DeserializedManifestList)
if err == nil && resIndex.Manifests != nil {
return resIndex, nil
}
// Otherwise, assume it must be an image manifest
return ms.ocischemaHandler.Unmarshal(ctx, dgst, content)
default:
return nil, distribution.ErrManifestVerification{fmt.Errorf("unrecognized manifest content type %s", versioned.MediaType)}
}