From 328441cdc6484fe08e4f1f47cc3a16f2cd9edb4f Mon Sep 17 00:00:00 2001 From: Tonis Tiigi Date: Fri, 9 Apr 2021 10:49:15 -0700 Subject: [PATCH] imagetools: fix merging JSON descriptor with old one Signed-off-by: Tonis Tiigi --- commands/imagetools/create.go | 26 ++++++++++++++++++- docs/reference/buildx_imagetools_create.md | 13 ++++++++++ util/imagetools/create.go | 30 ++++++++++++++++------ 3 files changed, 60 insertions(+), 9 deletions(-) diff --git a/commands/imagetools/create.go b/commands/imagetools/create.go index 0246d615..a2078078 100644 --- a/commands/imagetools/create.go +++ b/commands/imagetools/create.go @@ -118,7 +118,15 @@ func runCreate(dockerCli command.Cli, in createOptions, args []string) error { return err } srcs[i].Ref = nil - srcs[i].Desc = desc + if srcs[i].Desc.Digest == "" { + srcs[i].Desc = desc + } else { + var err error + srcs[i].Desc, err = mergeDesc(desc, srcs[i].Desc) + if err != nil { + return err + } + } return nil }) }(i) @@ -238,3 +246,19 @@ func createCmd(dockerCli command.Cli) *cobra.Command { return cmd } + +func mergeDesc(d1, d2 ocispec.Descriptor) (ocispec.Descriptor, error) { + if d2.Size != 0 && d1.Size != d2.Size { + return ocispec.Descriptor{}, errors.Errorf("invalid size mismatch for %s, %d != %d", d1.Digest, d2.Size, d1.Size) + } + if d2.MediaType != "" { + d1.MediaType = d2.MediaType + } + if len(d2.Annotations) != 0 { + d1.Annotations = d2.Annotations // no merge so support removes + } + if d2.Platform != nil { + d1.Platform = d2.Platform // missing items filled in later from image config + } + return d1, nil +} diff --git a/docs/reference/buildx_imagetools_create.md b/docs/reference/buildx_imagetools_create.md index 8eb414e0..eee671a4 100644 --- a/docs/reference/buildx_imagetools_create.md +++ b/docs/reference/buildx_imagetools_create.md @@ -50,6 +50,19 @@ Use the `--dry-run` flag to not push the image, just show it. Reads source from files. A source can be a manifest digest, manifest reference, or a JSON of OCI descriptor object. +In order to define annotations or additional platform properties like `os.version` and +`os.features` you need to add them in the OCI descriptor object encoded in JSON. + +``` +docker buildx imagetools inspect --raw alpine | jq '.manifests[0] | .platform."os.version"="10.1"' > descr.json +docker buildx imagetools create -f descr.json myuser/image +``` + +The descriptor in the file is merged with existing descriptor in the registry if it exists. + +The supported fields for the descriptor are defined in [OCI spec](https://github.com/opencontainers/image-spec/blob/master/descriptor.md#properties) . + + ### Set reference for new image (-t, --tag) ``` diff --git a/util/imagetools/create.go b/util/imagetools/create.go index 96f5a05f..2913813c 100644 --- a/util/imagetools/create.go +++ b/util/imagetools/create.go @@ -47,13 +47,16 @@ func (r *Resolver) Combine(ctx context.Context, in string, descs []ocispec.Descr switch mt { case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest: + p := descs[i].Platform if descs[i].Platform == nil { - p, err := r.loadPlatform(ctx, in, dt) - if err != nil { + p = &ocispec.Platform{} + } + if p.OS == "" || p.Architecture == "" { + if err := r.loadPlatform(ctx, p, in, dt); err != nil { return err } - descs[i].Platform = p } + descs[i].Platform = p case images.MediaTypeDockerSchema1Manifest: return errors.Errorf("schema1 manifests are not allowed in manifest lists") } @@ -166,24 +169,35 @@ func (r *Resolver) Push(ctx context.Context, ref reference.Named, desc ocispec.D return err } -func (r *Resolver) loadPlatform(ctx context.Context, in string, dt []byte) (*ocispec.Platform, error) { +func (r *Resolver) loadPlatform(ctx context.Context, p2 *ocispec.Platform, in string, dt []byte) error { var manifest ocispec.Manifest if err := json.Unmarshal(dt, &manifest); err != nil { - return nil, errors.WithStack(err) + return errors.WithStack(err) } dt, err := r.GetDescriptor(ctx, in, manifest.Config) if err != nil { - return nil, err + return err } var p ocispec.Platform if err := json.Unmarshal(dt, &p); err != nil { - return nil, errors.WithStack(err) + return errors.WithStack(err) } p = platforms.Normalize(p) - return &p, nil + + if p2.Architecture == "" { + p2.Architecture = p.Architecture + if p2.Variant == "" { + p2.Variant = p.Variant + } + } + if p2.OS == "" { + p2.OS = p.OS + } + + return nil } func detectMediaType(dt []byte) (string, error) {