diff --git a/cache/remotecache/export.go b/cache/remotecache/export.go index d9d5bab7..542aa760 100644 --- a/cache/remotecache/export.go +++ b/cache/remotecache/export.go @@ -12,6 +12,7 @@ import ( v1 "github.com/moby/buildkit/cache/remotecache/v1" "github.com/moby/buildkit/session" "github.com/moby/buildkit/solver" + "github.com/moby/buildkit/util/compression" "github.com/moby/buildkit/util/contentutil" "github.com/moby/buildkit/util/progress" digest "github.com/opencontainers/go-digest" @@ -55,20 +56,17 @@ type contentCacheExporter struct { solver.CacheExporterTarget chains *v1.CacheChains ingester content.Ingester + oci bool } -func NewExporter(ingester content.Ingester) Exporter { +func NewExporter(ingester content.Ingester, oci bool) Exporter { cc := v1.NewCacheChains() - return &contentCacheExporter{CacheExporterTarget: cc, chains: cc, ingester: ingester} + return &contentCacheExporter{CacheExporterTarget: cc, chains: cc, ingester: ingester, oci: oci} } func (ce *contentCacheExporter) Finalize(ctx context.Context) (map[string]string, error) { - return export(ctx, ce.ingester, ce.chains) -} - -func export(ctx context.Context, ingester content.Ingester, cc *v1.CacheChains) (map[string]string, error) { res := make(map[string]string) - config, descs, err := cc.Marshal() + config, descs, err := ce.chains.Marshal() if err != nil { return nil, err } @@ -86,6 +84,9 @@ func export(ctx context.Context, ingester content.Ingester, cc *v1.CacheChains) var mfst manifestList mfst.SchemaVersion = 2 mfst.MediaType = images.MediaTypeDockerSchema2ManifestList + if ce.oci { + mfst.MediaType = ocispec.MediaTypeImageIndex + } for _, l := range config.Layers { dgstPair, ok := descs[l.Blob] @@ -93,13 +94,15 @@ func export(ctx context.Context, ingester content.Ingester, cc *v1.CacheChains) return nil, errors.Errorf("missing blob %s", l.Blob) } layerDone := oneOffProgress(ctx, fmt.Sprintf("writing layer %s", l.Blob)) - if err := contentutil.Copy(ctx, ingester, dgstPair.Provider, dgstPair.Descriptor); err != nil { + if err := contentutil.Copy(ctx, ce.ingester, dgstPair.Provider, dgstPair.Descriptor); err != nil { return nil, layerDone(errors.Wrap(err, "error writing layer blob")) } layerDone(nil) mfst.Manifests = append(mfst.Manifests, dgstPair.Descriptor) } + mfst.Manifests = compression.ConvertAllLayerMediaTypes(ce.oci, mfst.Manifests...) + dt, err := json.Marshal(config) if err != nil { return nil, err @@ -111,7 +114,7 @@ func export(ctx context.Context, ingester content.Ingester, cc *v1.CacheChains) MediaType: v1.CacheConfigMediaTypeV0, } configDone := oneOffProgress(ctx, fmt.Sprintf("writing config %s", dgst)) - if err := content.WriteBlob(ctx, ingester, dgst.String(), bytes.NewReader(dt), desc); err != nil { + if err := content.WriteBlob(ctx, ce.ingester, dgst.String(), bytes.NewReader(dt), desc); err != nil { return nil, configDone(errors.Wrap(err, "error writing config blob")) } configDone(nil) @@ -130,7 +133,7 @@ func export(ctx context.Context, ingester content.Ingester, cc *v1.CacheChains) MediaType: mfst.MediaType, } mfstDone := oneOffProgress(ctx, fmt.Sprintf("writing manifest %s", dgst)) - if err := content.WriteBlob(ctx, ingester, dgst.String(), bytes.NewReader(dt), desc); err != nil { + if err := content.WriteBlob(ctx, ce.ingester, dgst.String(), bytes.NewReader(dt), desc); err != nil { return nil, mfstDone(errors.Wrap(err, "error writing manifest blob")) } descJSON, err := json.Marshal(desc) diff --git a/cache/remotecache/local/local.go b/cache/remotecache/local/local.go index 1e99ebbc..2ee194af 100644 --- a/cache/remotecache/local/local.go +++ b/cache/remotecache/local/local.go @@ -2,6 +2,7 @@ package local import ( "context" + "strconv" "time" "github.com/containerd/containerd/content" @@ -17,6 +18,7 @@ const ( attrDigest = "digest" attrSrc = "src" attrDest = "dest" + attrOCIMediatypes = "oci-mediatypes" contentStoreIDPrefix = "local:" ) @@ -27,12 +29,20 @@ func ResolveCacheExporterFunc(sm *session.Manager) remotecache.ResolveCacheExpor if store == "" { return nil, errors.New("local cache exporter requires dest") } + ociMediatypes := true + if v, ok := attrs[attrOCIMediatypes]; ok { + b, err := strconv.ParseBool(v) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse %s", attrOCIMediatypes) + } + ociMediatypes = b + } csID := contentStoreIDPrefix + store cs, err := getContentStore(ctx, sm, g, csID) if err != nil { return nil, err } - return remotecache.NewExporter(cs), nil + return remotecache.NewExporter(cs, ociMediatypes), nil } } diff --git a/cache/remotecache/registry/registry.go b/cache/remotecache/registry/registry.go index f93d703f..281d9fa4 100644 --- a/cache/remotecache/registry/registry.go +++ b/cache/remotecache/registry/registry.go @@ -2,6 +2,7 @@ package registry import ( "context" + "strconv" "github.com/containerd/containerd/content" "github.com/containerd/containerd/remotes/docker" @@ -28,7 +29,8 @@ func canonicalizeRef(rawRef string) (string, error) { } const ( - attrRef = "ref" + attrRef = "ref" + attrOCIMediatypes = "oci-mediatypes" ) func ResolveCacheExporterFunc(sm *session.Manager, hosts docker.RegistryHosts) remotecache.ResolveCacheExporterFunc { @@ -37,12 +39,20 @@ func ResolveCacheExporterFunc(sm *session.Manager, hosts docker.RegistryHosts) r if err != nil { return nil, err } + ociMediatypes := true + if v, ok := attrs[attrOCIMediatypes]; ok { + b, err := strconv.ParseBool(v) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse %s", attrOCIMediatypes) + } + ociMediatypes = b + } remote := resolver.DefaultPool.GetResolver(hosts, ref, "push", sm, g) pusher, err := remote.Pusher(ctx, ref) if err != nil { return nil, err } - return remotecache.NewExporter(contentutil.FromPusher(pusher)), nil + return remotecache.NewExporter(contentutil.FromPusher(pusher), ociMediatypes), nil } }