Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

remotecache: allow oci mediatypes on exporting to registry #1746

Merged
merged 2 commits into from
Oct 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@ The directory layout conforms to OCI Image Spec v1.0.
- `mode=max`: export all the layers of all intermediate steps. Not supported for `inline` cache exporter.
- `ref=docker.io/user/image:tag`: reference for `registry` cache exporter
- `dest=path/to/output-dir`: directory for `local` cache exporter
- `oci-mediatypes=true|false`: whether to use OCI mediatypes in exported manifests for `local` and `registry` exporter. Since BuildKit `v0.8` defaults to true.

#### `--import-cache` options
- `type`: `registry` or `local`. Use `registry` to import `inline` cache.
Expand Down
23 changes: 13 additions & 10 deletions cache/remotecache/export.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}
Expand All @@ -86,20 +84,25 @@ 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]
if !ok {
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
Expand All @@ -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)
Expand All @@ -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)
Expand Down
12 changes: 11 additions & 1 deletion cache/remotecache/local/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package local

import (
"context"
"strconv"
"time"

"github.com/containerd/containerd/content"
Expand All @@ -17,6 +18,7 @@ const (
attrDigest = "digest"
attrSrc = "src"
attrDest = "dest"
attrOCIMediatypes = "oci-mediatypes"
contentStoreIDPrefix = "local:"
)

Expand All @@ -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
}
}

Expand Down
14 changes: 12 additions & 2 deletions cache/remotecache/registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package registry

import (
"context"
"strconv"

"github.com/containerd/containerd/content"
"github.com/containerd/containerd/remotes/docker"
Expand All @@ -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 {
Expand All @@ -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
}
}

Expand Down