blob: c78e261af6ecf1f858574f80d89b164b21d6133f [file] [edit]
package exporter
import (
"context"
"strings"
"github.com/containerd/log"
"github.com/distribution/reference"
"github.com/docker/docker/builder/builder-next/exporter/overrides"
"github.com/moby/buildkit/exporter"
"github.com/moby/buildkit/exporter/containerimage/exptypes"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
type BuildkitCallbacks struct {
// Exported is a Called when an image is exported by buildkit.
Exported func(ctx context.Context, id string, desc ocispec.Descriptor)
// Named is a callback that is called when an image is created in the
// containerd image store by buildkit.
Named func(ctx context.Context, ref reference.NamedTagged, desc ocispec.Descriptor)
}
// Wraps the containerimage exporter's Resolve method to apply moby-specific
// overrides to the exporter attributes.
type imageExporterMobyWrapper struct {
exp exporter.Exporter
callbacks BuildkitCallbacks
}
// NewWrapper returns an exporter wrapper that applies moby specific attributes
// and hooks the export process.
func NewWrapper(exp exporter.Exporter, callbacks BuildkitCallbacks) (exporter.Exporter, error) {
return &imageExporterMobyWrapper{
exp: exp,
callbacks: callbacks,
}, nil
}
// Resolve applies moby specific attributes to the request.
func (e *imageExporterMobyWrapper) Resolve(ctx context.Context, id int, exporterAttrs map[string]string) (exporter.ExporterInstance, error) {
if exporterAttrs == nil {
exporterAttrs = make(map[string]string)
}
reposAndTags, err := overrides.SanitizeRepoAndTags(strings.Split(exporterAttrs[string(exptypes.OptKeyName)], ","))
if err != nil {
return nil, err
}
exporterAttrs[string(exptypes.OptKeyName)] = strings.Join(reposAndTags, ",")
exporterAttrs[string(exptypes.OptKeyUnpack)] = "true"
if _, has := exporterAttrs[string(exptypes.OptKeyDanglingPrefix)]; !has {
exporterAttrs[string(exptypes.OptKeyDanglingPrefix)] = "moby-dangling"
}
inst, err := e.exp.Resolve(ctx, id, exporterAttrs)
if err != nil {
return nil, err
}
return &imageExporterInstanceWrapper{
ExporterInstance: inst,
callbacks: e.callbacks,
}, nil
}
type imageExporterInstanceWrapper struct {
exporter.ExporterInstance
callbacks BuildkitCallbacks
}
func (i *imageExporterInstanceWrapper) Export(ctx context.Context, src *exporter.Source, inlineCache exptypes.InlineCache, sessionID string) (map[string]string, exporter.DescriptorReference, error) {
out, ref, err := i.ExporterInstance.Export(ctx, src, inlineCache, sessionID)
if err != nil {
return out, ref, err
}
desc := ref.Descriptor()
imageID := out[exptypes.ExporterImageDigestKey]
if i.callbacks.Exported != nil {
i.callbacks.Exported(ctx, imageID, desc)
}
if i.callbacks.Named != nil {
for _, name := range strings.Split(out[string(exptypes.OptKeyName)], ",") {
ref, err := reference.ParseNormalizedNamed(name)
if err != nil {
// Shouldn't happen, but log if it does and continue.
log.G(ctx).WithFields(log.Fields{
"name": name,
"error": err,
}).Warn("image named with invalid reference produced by buildkit")
continue
}
namedTagged := reference.TagNameOnly(ref).(reference.NamedTagged)
i.callbacks.Named(ctx, namedTagged, desc)
}
}
return out, ref, nil
}