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:
parent
1d47ef7b80
commit
60d9c5dfad
5 changed files with 27 additions and 8 deletions
|
@ -60,8 +60,8 @@ func init() {
|
||||||
return nil, distribution.Descriptor{}, err
|
return nil, distribution.Descriptor{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.MediaType != v1.MediaTypeImageIndex {
|
if m.MediaType != "" && m.MediaType != v1.MediaTypeImageIndex {
|
||||||
err = fmt.Errorf("mediaType in image index should be '%s' not '%s'",
|
err = fmt.Errorf("if present, mediaType in image index should be '%s' not '%s'",
|
||||||
v1.MediaTypeImageIndex, m.MediaType)
|
v1.MediaTypeImageIndex, m.MediaType)
|
||||||
|
|
||||||
return nil, distribution.Descriptor{}, err
|
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
|
// Payload returns the raw content of the manifest list. The contents can be
|
||||||
// used to calculate the content identifier.
|
// used to calculate the content identifier.
|
||||||
func (m DeserializedManifestList) Payload() (string, []byte, error) {
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -299,7 +299,7 @@ func TestMediaTypes(t *testing.T) {
|
||||||
mediaTypeTest(t, MediaTypeManifestList, "", true)
|
mediaTypeTest(t, MediaTypeManifestList, "", true)
|
||||||
mediaTypeTest(t, MediaTypeManifestList, MediaTypeManifestList, false)
|
mediaTypeTest(t, MediaTypeManifestList, MediaTypeManifestList, false)
|
||||||
mediaTypeTest(t, MediaTypeManifestList, MediaTypeManifestList+"XXX", true)
|
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, false)
|
||||||
mediaTypeTest(t, v1.MediaTypeImageIndex, v1.MediaTypeImageIndex+"XXX", true)
|
mediaTypeTest(t, v1.MediaTypeImageIndex, v1.MediaTypeImageIndex+"XXX", true)
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,8 +97,8 @@ func (m *DeserializedManifest) UnmarshalJSON(b []byte) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if manifest.MediaType != v1.MediaTypeImageManifest {
|
if manifest.MediaType != "" && manifest.MediaType != v1.MediaTypeImageManifest {
|
||||||
return fmt.Errorf("mediaType in manifest should be '%s' not '%s'",
|
return fmt.Errorf("if present, mediaType in manifest should be '%s' not '%s'",
|
||||||
v1.MediaTypeImageManifest, manifest.MediaType)
|
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
|
// Payload returns the raw content of the manifest. The contents can be used to
|
||||||
// calculate the content identifier.
|
// calculate the content identifier.
|
||||||
func (m DeserializedManifest) Payload() (string, []byte, error) {
|
func (m DeserializedManifest) Payload() (string, []byte, error) {
|
||||||
return m.MediaType, m.canonical, nil
|
return v1.MediaTypeImageManifest, m.canonical, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -178,7 +178,7 @@ func mediaTypeTest(t *testing.T, mediaType string, shouldError bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMediaTypes(t *testing.T) {
|
func TestMediaTypes(t *testing.T) {
|
||||||
mediaTypeTest(t, "", true)
|
mediaTypeTest(t, "", false)
|
||||||
mediaTypeTest(t, v1.MediaTypeImageManifest, false)
|
mediaTypeTest(t, v1.MediaTypeImageManifest, false)
|
||||||
mediaTypeTest(t, v1.MediaTypeImageManifest+"XXX", true)
|
mediaTypeTest(t, v1.MediaTypeImageManifest+"XXX", true)
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,6 +106,18 @@ func (ms *manifestStore) Get(ctx context.Context, dgst digest.Digest, options ..
|
||||||
return ms.ocischemaHandler.Unmarshal(ctx, dgst, content)
|
return ms.ocischemaHandler.Unmarshal(ctx, dgst, content)
|
||||||
case manifestlist.MediaTypeManifestList, v1.MediaTypeImageIndex:
|
case manifestlist.MediaTypeManifestList, v1.MediaTypeImageIndex:
|
||||||
return ms.manifestListHandler.Unmarshal(ctx, dgst, content)
|
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:
|
default:
|
||||||
return nil, distribution.ErrManifestVerification{fmt.Errorf("unrecognized manifest content type %s", versioned.MediaType)}
|
return nil, distribution.ErrManifestVerification{fmt.Errorf("unrecognized manifest content type %s", versioned.MediaType)}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue