-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix manifest lists to always use correct size #1156
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ import ( | |
"github.com/docker/cli/cli/command" | ||
"github.com/docker/cli/cli/manifest/types" | ||
registryclient "github.com/docker/cli/cli/registry/client" | ||
"github.com/docker/distribution" | ||
"github.com/docker/distribution/manifest/manifestlist" | ||
"github.com/docker/distribution/manifest/schema2" | ||
"github.com/docker/distribution/reference" | ||
|
@@ -141,7 +142,9 @@ func buildManifestList(manifests []types.ImageManifest, targetRef reference.Name | |
|
||
descriptors := []manifestlist.ManifestDescriptor{} | ||
for _, imageManifest := range manifests { | ||
if imageManifest.Platform.Architecture == "" || imageManifest.Platform.OS == "" { | ||
if imageManifest.Descriptor.Platform == nil || | ||
imageManifest.Descriptor.Platform.Architecture == "" || | ||
imageManifest.Descriptor.Platform.OS == "" { | ||
return nil, errors.Errorf( | ||
"manifest %s must have an OS and Architecture to be pushed to a registry", imageManifest.Ref) | ||
} | ||
|
@@ -167,17 +170,18 @@ func buildManifestDescriptor(targetRepo *registry.RepositoryInfo, imageManifest | |
return manifestlist.ManifestDescriptor{}, errors.Errorf("cannot use source images from a different registry than the target image: %s != %s", manifestRepoHostname, targetRepoHostname) | ||
} | ||
|
||
mediaType, raw, err := imageManifest.Payload() | ||
if err != nil { | ||
return manifestlist.ManifestDescriptor{}, err | ||
manifest := manifestlist.ManifestDescriptor{ | ||
Descriptor: distribution.Descriptor{ | ||
Digest: imageManifest.Descriptor.Digest, | ||
Size: imageManifest.Descriptor.Size, | ||
MediaType: imageManifest.Descriptor.MediaType, | ||
}, | ||
} | ||
|
||
manifest := manifestlist.ManifestDescriptor{ | ||
Platform: imageManifest.Platform, | ||
platform := types.PlatformSpecFromOCI(imageManifest.Descriptor.Platform) | ||
if platform != nil { | ||
manifest.Platform = *platform | ||
} | ||
manifest.Descriptor.Digest = imageManifest.Digest | ||
manifest.Size = int64(len(raw)) | ||
manifest.MediaType = mediaType | ||
|
||
if err = manifest.Descriptor.Digest.Validate(); err != nil { | ||
return manifestlist.ManifestDescriptor{}, errors.Wrapf(err, | ||
|
@@ -195,7 +199,11 @@ func buildBlobRequestList(imageManifest types.ImageManifest, repoName reference. | |
if err != nil { | ||
return nil, err | ||
} | ||
blobReqs = append(blobReqs, manifestBlob{canonical: canonical, os: imageManifest.Platform.OS}) | ||
var os string | ||
if imageManifest.Descriptor.Platform != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. since this is called after buildManifestList, this should never be nil |
||
os = imageManifest.Descriptor.Platform.OS | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: this would be better as |
||
} | ||
blobReqs = append(blobReqs, manifestBlob{canonical: canonical, os: os}) | ||
} | ||
return blobReqs, nil | ||
} | ||
|
@@ -206,7 +214,7 @@ func buildPutManifestRequest(imageManifest types.ImageManifest, targetRef refere | |
if err != nil { | ||
return mountRequest{}, err | ||
} | ||
mountRef, err := reference.WithDigest(refWithoutTag, imageManifest.Digest) | ||
mountRef, err := reference.WithDigest(refWithoutTag, imageManifest.Descriptor.Digest) | ||
if err != nil { | ||
return mountRequest{}, err | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,11 @@ import ( | |
"strings" | ||
|
||
"github.com/docker/cli/cli/manifest/types" | ||
"github.com/docker/distribution/manifest/manifestlist" | ||
"github.com/docker/distribution/reference" | ||
digest "github.com/opencontainers/go-digest" | ||
ocispec "github.com/opencontainers/image-spec/specs-go/v1" | ||
"github.com/pkg/errors" | ||
) | ||
|
||
// Store manages local storage of image distribution manifests | ||
|
@@ -50,8 +54,37 @@ func (s *fsStore) getFromFilename(ref reference.Reference, filename string) (typ | |
case err != nil: | ||
return types.ImageManifest{}, err | ||
} | ||
var manifestInfo types.ImageManifest | ||
return manifestInfo, json.Unmarshal(bytes, &manifestInfo) | ||
var manifestInfo struct { | ||
types.ImageManifest | ||
|
||
// Deprecated Fields, replaced by Descriptor | ||
Digest digest.Digest | ||
Platform *manifestlist.PlatformSpec | ||
} | ||
|
||
if err := json.Unmarshal(bytes, &manifestInfo); err != nil { | ||
return types.ImageManifest{}, err | ||
} | ||
|
||
// Compatibility with image manifests created before | ||
// descriptor, newer versions omit Digest and Platform | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Compatibility here is only for the manifests created by earlier versions of If so, we should probably add a TODO to remove this code at some point |
||
if manifestInfo.Digest != "" { | ||
mediaType, raw, err := manifestInfo.Payload() | ||
if err != nil { | ||
return types.ImageManifest{}, err | ||
} | ||
if dgst := digest.FromBytes(raw); dgst != manifestInfo.Digest { | ||
return types.ImageManifest{}, errors.Errorf("invalid manifest file %v: image manifest digest mismatch (%v != %v)", filename, manifestInfo.Digest, dgst) | ||
} | ||
manifestInfo.ImageManifest.Descriptor = ocispec.Descriptor{ | ||
Digest: manifestInfo.Digest, | ||
Size: int64(len(raw)), | ||
MediaType: mediaType, | ||
Platform: types.OCIPlatform(manifestInfo.Platform), | ||
} | ||
} | ||
|
||
return manifestInfo.ImageManifest, nil | ||
} | ||
|
||
// GetList returns all the local manifests for a transaction | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,15 +8,46 @@ import ( | |
"github.com/docker/distribution/manifest/schema2" | ||
"github.com/docker/distribution/reference" | ||
"github.com/opencontainers/go-digest" | ||
ocispec "github.com/opencontainers/image-spec/specs-go/v1" | ||
"github.com/pkg/errors" | ||
) | ||
|
||
// ImageManifest contains info to output for a manifest object. | ||
type ImageManifest struct { | ||
Ref *SerializableNamed | ||
Digest digest.Digest | ||
Ref *SerializableNamed | ||
Descriptor ocispec.Descriptor | ||
|
||
// SchemaV2Manifest is used for inspection | ||
// TODO: Deprecate this and store manifest blobs | ||
SchemaV2Manifest *schema2.DeserializedManifest `json:",omitempty"` | ||
Platform manifestlist.PlatformSpec | ||
} | ||
|
||
// OCIPlatform creates an OCI platform from a manifest list platform spec | ||
func OCIPlatform(ps *manifestlist.PlatformSpec) *ocispec.Platform { | ||
if ps == nil { | ||
return nil | ||
} | ||
return &ocispec.Platform{ | ||
Architecture: ps.Architecture, | ||
OS: ps.OS, | ||
OSVersion: ps.OSVersion, | ||
OSFeatures: ps.OSFeatures, | ||
Variant: ps.Variant, | ||
} | ||
} | ||
|
||
// PlatformSpecFromOCI creates a platform spec from OCI platform | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wondering if we somehow can get rid of having both types (given that they're effectively the same) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using the containerd code instead of the distribution code would effectively get rid of this. |
||
func PlatformSpecFromOCI(p *ocispec.Platform) *manifestlist.PlatformSpec { | ||
if p == nil { | ||
return nil | ||
} | ||
return &manifestlist.PlatformSpec{ | ||
Architecture: p.Architecture, | ||
OS: p.OS, | ||
OSVersion: p.OSVersion, | ||
OSFeatures: p.OSFeatures, | ||
Variant: p.Variant, | ||
} | ||
} | ||
|
||
// Blobs returns the digests for all the blobs referenced by this manifest | ||
|
@@ -30,6 +61,7 @@ func (i ImageManifest) Blobs() []digest.Digest { | |
|
||
// Payload returns the media type and bytes for the manifest | ||
func (i ImageManifest) Payload() (string, []byte, error) { | ||
// TODO: If available, read content from a content store by digest | ||
switch { | ||
case i.SchemaV2Manifest != nil: | ||
return i.SchemaV2Manifest.Payload() | ||
|
@@ -51,18 +83,11 @@ func (i ImageManifest) References() []distribution.Descriptor { | |
|
||
// NewImageManifest returns a new ImageManifest object. The values for Platform | ||
// are initialized from those in the image | ||
func NewImageManifest(ref reference.Named, digest digest.Digest, img Image, manifest *schema2.DeserializedManifest) ImageManifest { | ||
platform := manifestlist.PlatformSpec{ | ||
OS: img.OS, | ||
Architecture: img.Architecture, | ||
OSVersion: img.OSVersion, | ||
OSFeatures: img.OSFeatures, | ||
} | ||
func NewImageManifest(ref reference.Named, desc ocispec.Descriptor, manifest *schema2.DeserializedManifest) ImageManifest { | ||
return ImageManifest{ | ||
Ref: &SerializableNamed{Named: ref}, | ||
Digest: digest, | ||
Descriptor: desc, | ||
SchemaV2Manifest: manifest, | ||
Platform: platform, | ||
} | ||
} | ||
|
||
|
@@ -87,21 +112,3 @@ func (s *SerializableNamed) UnmarshalJSON(b []byte) error { | |
func (s *SerializableNamed) MarshalJSON() ([]byte, error) { | ||
return json.Marshal(s.String()) | ||
} | ||
|
||
// Image is the minimal set of fields required to set default platform settings | ||
// on a manifest. | ||
type Image struct { | ||
Architecture string `json:"architecture,omitempty"` | ||
OS string `json:"os,omitempty"` | ||
OSVersion string `json:"os.version,omitempty"` | ||
OSFeatures []string `json:"os.features,omitempty"` | ||
} | ||
|
||
// NewImageFromJSON creates an Image configuration from json. | ||
func NewImageFromJSON(src []byte) (*Image, error) { | ||
img := &Image{} | ||
if err := json.Unmarshal(src, img); err != nil { | ||
return nil, err | ||
} | ||
return img, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wonder if this validation should be inside
buildManifestDescriptor()
, but I assume we can't move it there because that's also used bymanifest inspect
?