moby/daemon/containerd/store.go
Paweł Gronowski a75354c045
c8d: Implement push
This implements `docker push` under containerd image store.  When
pushing manifest lists that reference a content which is not present in
the local content store, it will attempt to perform the cross-repo mount
the content if possible.

Considering this scenario:

```bash
$ docker pull docker.io/library/busybox
```
This will download manifest list and only host platform-specific
manifest and blobs.

Note, tagging to a different repository (but still the same registry) and pushing:
```bash
$ docker tag docker.io/library/busybox docker.io/private-repo/mybusybox
$ docker push docker.io/private-repo/mybusybox
```

will result in error, because the neither we nor the target repository
doesn't have the manifests that the busybox manifest list references
(because manifests can't be cross-repo mounted).

If for some reason the manifests and configs for all other platforms
would be present in the content store, but only layer blobs were
missing, then the push would work, because the blobs can be cross-repo
mounted (only if we push to the same registry).

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2023-03-30 19:53:02 +02:00

84 lines
2.5 KiB
Go

package containerd
import (
"context"
"github.com/containerd/containerd/content"
cerrdefs "github.com/containerd/containerd/errdefs"
"github.com/docker/distribution/reference"
"github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
// fakeStoreWithSources fakes the existence of the specified content.
// Only existence is faked - Info function will include the distribution source label
// which makes it possible to perform cross-repo mount.
// ReaderAt will still fail with ErrNotFound.
type fakeStoreWithSources struct {
s content.Store
sources map[digest.Digest]distributionSource
}
// wrapWithFakeMountableBlobs wraps the provided content store.
func wrapWithFakeMountableBlobs(s content.Store, sources map[digest.Digest]distributionSource) fakeStoreWithSources {
return fakeStoreWithSources{
s: s,
sources: sources,
}
}
func (p fakeStoreWithSources) Delete(ctx context.Context, dgst digest.Digest) error {
return p.s.Delete(ctx, dgst)
}
func (p fakeStoreWithSources) Info(ctx context.Context, dgst digest.Digest) (content.Info, error) {
info, err := p.s.Info(ctx, dgst)
if err != nil {
if !cerrdefs.IsNotFound(err) {
return info, err
}
source, ok := p.sources[dgst]
if !ok {
return info, err
}
key := labelDistributionSource + reference.Domain(source.registryRef)
value := reference.Path(source.registryRef)
return content.Info{
Digest: dgst,
Labels: map[string]string{
key: value,
},
}, nil
}
return info, nil
}
func (p fakeStoreWithSources) Update(ctx context.Context, info content.Info, fieldpaths ...string) (content.Info, error) {
return p.s.Update(ctx, info, fieldpaths...)
}
func (p fakeStoreWithSources) Walk(ctx context.Context, fn content.WalkFunc, filters ...string) error {
return p.s.Walk(ctx, fn, filters...)
}
func (p fakeStoreWithSources) ReaderAt(ctx context.Context, desc ocispec.Descriptor) (content.ReaderAt, error) {
return p.s.ReaderAt(ctx, desc)
}
func (p fakeStoreWithSources) Abort(ctx context.Context, ref string) error {
return p.s.Abort(ctx, ref)
}
func (p fakeStoreWithSources) ListStatuses(ctx context.Context, filters ...string) ([]content.Status, error) {
return p.s.ListStatuses(ctx, filters...)
}
func (p fakeStoreWithSources) Status(ctx context.Context, ref string) (content.Status, error) {
return p.s.Status(ctx, ref)
}
func (p fakeStoreWithSources) Writer(ctx context.Context, opts ...content.WriterOpt) (content.Writer, error) {
return p.s.Writer(ctx, opts...)
}