Repository: moby/moby
File: builder/builder-next/exporter/wrapper.go (Go)

Code before:
	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) {
	var exported ExportedCallback
	if i.callbacks.Exported != nil {
		exported = i.callbacks.Exported(ctx, src, i.Attrs())
	}

	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 exported != nil {
		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)

Review comment (suggestion):
I'm thinking whether we could avoid leaking buildkit stuff into the daemon and having to extend the `ImageService`.

I think we could also make it work similar to how it works with pull:

https://github.com/moby/moby/blob/0451e4fa185033906c80275767bceed15376fadb/daemon/containerd/image_pull.go#L96-L112

Basically:
1. Before build, lease all content targetted by the image that can possibly be overriden (so it doesn't get GC'd)
2. Invoke build
3. Check if the images changed, if they did, tag dangling images and then remove leases

Code after:
		exporterAttrs = make(map[string]string)
	}
	reposAndTags, err := overrides.SanitizeRepoAndTags(strings.Split(exporterAttrs[string(exptypes.OptKeyName)], ","))
	if err != nil {
		return nil, err
	}

	// Force the exporter to not use a name so it always creates a dangling image.
	exporterAttrs[string(exptypes.OptKeyName)] = ""
	exporterAttrs[string(exptypes.OptKeyUnpack)] = "true"
	if _, has := exporterAttrs[string(exptypes.OptKeyDanglingPrefix)]; !has {
		exporterAttrs[string(exptypes.OptKeyDanglingPrefix)] = "moby-dangling"
	}
	exporterAttrs[string(exptypes.OptKeyDanglingEmptyOnly)] = "true"

	inst, err := e.exp.Resolve(ctx, id, exporterAttrs)
	if err != nil {
		return nil, err
	}

	return &imageExporterInstanceWrapper{
		ExporterInstance: inst,
		reposAndTags:     reposAndTags,
		opt:              e.opt,
	}, nil
}

type imageExporterInstanceWrapper struct {
	exporter.ExporterInstance
	reposAndTags []string
	opt          Opt
}

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.opt.Callbacks.Exported != nil {
		i.opt.Callbacks.Exported(ctx, imageID, desc)
	}

	err = i.processNamed(ctx, image.ID(imageID), out, desc)
	return out, ref, err
}

func (i *imageExporterInstanceWrapper) processNamed(ctx context.Context, imageID image.ID, out map[string]string, desc ocispec.Descriptor) error {
	if len(i.reposAndTags) == 0 {