Skip to content

Commit

Permalink
c8d/convert: Accept multiple tags as destination
Browse files Browse the repository at this point in the history
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
  • Loading branch information
vvoland committed May 16, 2024
1 parent 2d4184e commit 67c33f8
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 41 deletions.
2 changes: 1 addition & 1 deletion api/server/router/image/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type imageBackend interface {
GetImage(ctx context.Context, refOrID string, options backend.GetImageOpts) (*dockerimage.Image, error)
TagImage(ctx context.Context, id dockerimage.ID, newRef reference.Named) error
ImagesPrune(ctx context.Context, pruneFilters filters.Args) (*types.ImagesPruneReport, error)
ImageConvert(ctx context.Context, src string, dst reference.NamedTagged, options image.ConvertOptions) error
ImageConvert(ctx context.Context, src string, dsts []reference.NamedTagged, options image.ConvertOptions) error
}

type importExportBackend interface {
Expand Down
15 changes: 10 additions & 5 deletions api/server/router/image/image_routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -535,11 +535,16 @@ func (ir *imageRouter) postImagesConvert(ctx context.Context, w http.ResponseWri
}

src := r.Form.Get("from")
dst, err := reference.ParseNamed(r.Form.Get("to"))
if err != nil {
return errdefs.InvalidParameter(fmt.Errorf("invalid 'to' parameter: %w", err))

var dstRefs []reference.Named
for _, t := range r.Form["to"] {
dst, err := reference.ParseNamed(r.Form.Get("to"))
if err != nil {
return errdefs.InvalidParameter(fmt.Errorf("invalid 'to' parameter: %w", err))
}

dstRefs = append(dstRefs, dst)
}
dstRef := dst.(reference.NamedTagged)

opts := imagetypes.ConvertOptions{
OnlyAvailablePlatforms: httputils.BoolValue(r, "only-available-platforms"),
Expand All @@ -554,7 +559,7 @@ func (ir *imageRouter) postImagesConvert(ctx context.Context, w http.ResponseWri
opts.Platforms = append(opts.Platforms, sp)
}

if err := ir.backend.ImageConvert(ctx, src, dstRef, opts); err != nil {
if err := ir.backend.ImageConvert(ctx, src, dstRefs, opts); err != nil {
w.WriteHeader(http.StatusInternalServerError)
return err
}
Expand Down
6 changes: 4 additions & 2 deletions client/image_convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
)

// ImageList converts image.
func (cli *Client) ImageConvert(ctx context.Context, src string, dst reference.NamedTagged, options image.ConvertOptions) error {
func (cli *Client) ImageConvert(ctx context.Context, src string, dsts []reference.NamedTagged, options image.ConvertOptions) error {
query := url.Values{}

if options.OnlyAvailablePlatforms && len(options.Platforms) > 0 {
Expand All @@ -35,7 +35,9 @@ func (cli *Client) ImageConvert(ctx context.Context, src string, dst reference.N
}

query.Set("from", src)
query.Set("to", dst.String())
for _, dst := range dsts {
query.Add("to", dst.String())
}

serverResp, err := cli.post(ctx, "/images/convert", query, nil, nil)
ensureReaderClosed(serverResp)
Expand Down
2 changes: 1 addition & 1 deletion client/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ type ImageAPIClient interface {
ImageSave(ctx context.Context, images []string) (io.ReadCloser, error)
ImageTag(ctx context.Context, image, ref string) error
ImagesPrune(ctx context.Context, pruneFilter filters.Args) (types.ImagesPruneReport, error)
ImageConvert(ctx context.Context, src string, dst reference.NamedTagged, options image.ConvertOptions) error
ImageConvert(ctx context.Context, src string, dst []reference.NamedTagged, options image.ConvertOptions) error
}

// NetworkAPIClient defines API client methods for the networks
Expand Down
65 changes: 36 additions & 29 deletions daemon/containerd/image_convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,14 @@ import (
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)

func (i *ImageService) ImageConvert(ctx context.Context, src string, dst reference.NamedTagged, opts imagetypes.ConvertOptions) error {
func (i *ImageService) ImageConvert(ctx context.Context, src string, dsts []reference.NamedTagged, opts imagetypes.ConvertOptions) error {
log.G(ctx).Debugf("converting: %+v", opts)

srcImg, err := i.resolveImage(ctx, src)
if err != nil {
return err
}

if _, err := i.images.Get(ctx, dst.String()); !cerrdefs.IsNotFound(err) {
err := i.images.Delete(ctx, dst.String(), images.SynchronousDelete())
if err != nil {
return errdefs.System(fmt.Errorf("failed to delete existing image: %w", err))
}
}

if opts.OnlyAvailablePlatforms && len(opts.Platforms) > 0 {
return errdefs.InvalidParameter(errors.New("specifying both explicit platform list and only-available-platforms is not allowed"))
}
Expand All @@ -49,7 +42,6 @@ func (i *ImageService) ImageConvert(ctx context.Context, src string, dst referen
}

newImg := srcImg
newImg.Name = dst.String()

n, err := i.convertManifests(ctx, srcImg, opts)
if err != nil {
Expand All @@ -68,8 +60,21 @@ func (i *ImageService) ImageConvert(ctx context.Context, src string, dst referen
newImg.UpdatedAt = newImg.CreatedAt
}

if _, err := i.images.Create(ctx, newImg); err != nil {
return errdefs.System(fmt.Errorf("failed to create image: %w", err))
for _, dst := range dsts {
newImg.Name = dst.String()

if _, err := i.images.Create(ctx, newImg); err != nil {
if cerrdefs.IsAlreadyExists(err) {
_, err := i.ImageDelete(ctx, dst.String(), true, false)
if err != nil {
return errdefs.System(fmt.Errorf("failed to delete existing image: %w", err))
}
_, err = i.images.Create(ctx, newImg)
}
if err != nil {
return errdefs.System(fmt.Errorf("failed to create image: %w", err))
}
}
}
return nil
}
Expand All @@ -89,26 +94,28 @@ func (i *ImageService) convertManifests(ctx context.Context, srcImg images.Image
}

err := walker(ctx, srcImg, func(m *ImageManifest) error {
if opts.NoAttestations && m.IsAttestation() {
return nil
if m.IsAttestation() {
if opts.NoAttestations {
return nil
}
} else {
mtarget := m.Target()
mplatform, err := m.ImagePlatform(ctx)
if err != nil {
return err
}
if !pm.Match(mplatform) {
changed = true
log.G(ctx).WithFields(log.Fields{
"platform": mplatform,
"digest": mtarget.Digest,
}).Debugf("skipping manifest %s due to platform mismatch", mtarget.Digest)
return images.ErrSkipDesc
}

newManifests = append(newManifests, mtarget)
}

mtarget := m.Target()
mplatform, err := m.ImagePlatform(ctx)
if err != nil {
return err
}
if !pm.Match(mplatform) {
changed = true
log.G(ctx).WithFields(log.Fields{
"platform": mplatform,
"digest": mtarget.Digest,
}).Debugf("skipping manifest %s due to platform mismatch", mtarget.Digest)
return images.ErrSkipDesc
}

newManifests = append(newManifests, mtarget)

return nil
})
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion daemon/containerd/image_convert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func TestImageConvert(t *testing.T) {
assert.NilError(t, err)

dst := dstRef(tc.name)
err = service.ImageConvert(ctx, srcImg.Name, dst, tc.opts)
err = service.ImageConvert(ctx, srcImg.Name, []reference.NamedTagged{dst}, tc.opts)

if tc.err != nil {
assert.ErrorIs(t, err, tc.err)
Expand Down
2 changes: 1 addition & 1 deletion daemon/image_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ type ImageService interface {
ImageHistory(ctx context.Context, name string) ([]*imagetype.HistoryResponseItem, error)
CommitImage(ctx context.Context, c backend.CommitConfig) (image.ID, error)
SquashImage(id, parent string) (string, error)
ImageConvert(ctx context.Context, src string, dst reference.NamedTagged, options imagetype.ConvertOptions) error
ImageConvert(ctx context.Context, src string, dsts []reference.NamedTagged, options imagetype.ConvertOptions) error

// Containerd related methods

Expand Down
2 changes: 1 addition & 1 deletion daemon/images/image_convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ import (
"github.com/docker/docker/errdefs"
)

func (i *ImageService) ImageConvert(ctx context.Context, src string, dst reference.NamedTagged, opts imagetypes.ConvertOptions) error {
func (i *ImageService) ImageConvert(ctx context.Context, src string, dsts []reference.NamedTagged, opts imagetypes.ConvertOptions) error {
return errdefs.NotImplemented(errors.New("not supported in graphdriver backed image store"))
}

0 comments on commit 67c33f8

Please sign in to comment.