diff --git a/daemon/containerd/service.go b/daemon/containerd/service.go index 620fd18ea872d..ba510241fb97e 100644 --- a/daemon/containerd/service.go +++ b/daemon/containerd/service.go @@ -11,6 +11,7 @@ import ( "github.com/containerd/containerd/content" cerrdefs "github.com/containerd/containerd/errdefs" containerdimages "github.com/containerd/containerd/images" + "github.com/containerd/containerd/images/converter" "github.com/containerd/containerd/platforms" "github.com/containerd/containerd/remotes" "github.com/containerd/containerd/remotes/docker" @@ -357,7 +358,59 @@ func (cs *containerdStore) LookupImage(ctx context.Context, name string) (*types } func (cs *containerdStore) PushImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error { - panic("not implemented") + // TODO: Pass this from user? + platformMatcher := platforms.DefaultStrict() + + ref, err := reference.ParseNormalizedNamed(image) + if err != nil { + return err + } + if tag != "" { + // Push by digest is not supported, so only tags are supported. + ref, err = reference.WithTag(ref, tag) + if err != nil { + return err + } + } + + is := cs.client.ImageService() + + img, err := is.Get(ctx, ref.String()) + if err != nil { + return errors.Wrap(err, "Failed to get image") + } + + target := img.Target + + // Create a temporary image which is stripped from content that references other platforms. + // We or the remote may not have them and referencing them will end with an error. + if platformMatcher != platforms.All { + tmpRef := ref.String() + "-tmp-platformspecific" + platformImg, err := converter.Convert(ctx, cs.client, tmpRef, ref.String(), converter.WithPlatform(platformMatcher)) + if err != nil { + return errors.Wrap(err, "Failed to convert image to platform specific") + } + + target = platformImg.Target + defer cs.client.ImageService().Delete(ctx, platformImg.Name, containerdimages.SynchronousDelete()) + } + + imageHandler := containerdimages.HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) (subdescs []ocispec.Descriptor, err error) { + logrus.WithField("desc", desc).Debug("Pushing") + return nil, nil + }) + imageHandler = remotes.SkipNonDistributableBlobs(imageHandler) + + resolver := newResolverFromAuthConfig(authConfig) + + logrus.WithField("desc", target).WithField("ref", ref.String()).Info("Pushing desc to remote ref") + err = cs.client.Push(ctx, ref.String(), target, + containerd.WithResolver(resolver), + containerd.WithPlatformMatcher(platformMatcher), + containerd.WithImageHandler(imageHandler), + ) + + return err } func (cs *containerdStore) SearchRegistryForImages(ctx context.Context, searchFilters filters.Args, term string, limit int, authConfig *types.AuthConfig, metaHeaders map[string][]string) (*registrytypes.SearchResults, error) {