github.com/rawahars/moby@v24.0.4+incompatible/builder/builder-next/exporter/mobyexporter/export.go (about)

     1  package mobyexporter
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"strings"
     8  
     9  	distref "github.com/docker/distribution/reference"
    10  	"github.com/docker/docker/image"
    11  	"github.com/docker/docker/layer"
    12  	"github.com/moby/buildkit/exporter"
    13  	"github.com/moby/buildkit/exporter/containerimage/exptypes"
    14  	"github.com/opencontainers/go-digest"
    15  	"github.com/pkg/errors"
    16  )
    17  
    18  const (
    19  	keyImageName = "name"
    20  )
    21  
    22  // Differ can make a moby layer from a snapshot
    23  type Differ interface {
    24  	EnsureLayer(ctx context.Context, key string) ([]layer.DiffID, error)
    25  }
    26  
    27  type ImageTagger interface {
    28  	TagImage(ctx context.Context, imageID image.ID, newTag distref.Named) error
    29  }
    30  
    31  // Opt defines a struct for creating new exporter
    32  type Opt struct {
    33  	ImageStore  image.Store
    34  	Differ      Differ
    35  	ImageTagger ImageTagger
    36  }
    37  
    38  type imageExporter struct {
    39  	opt Opt
    40  }
    41  
    42  // New creates a new moby imagestore exporter
    43  func New(opt Opt) (exporter.Exporter, error) {
    44  	im := &imageExporter{opt: opt}
    45  	return im, nil
    46  }
    47  
    48  func (e *imageExporter) Resolve(ctx context.Context, opt map[string]string) (exporter.ExporterInstance, error) {
    49  	i := &imageExporterInstance{
    50  		imageExporter: e,
    51  	}
    52  	for k, v := range opt {
    53  		switch k {
    54  		case keyImageName:
    55  			for _, v := range strings.Split(v, ",") {
    56  				ref, err := distref.ParseNormalizedNamed(v)
    57  				if err != nil {
    58  					return nil, err
    59  				}
    60  				i.targetNames = append(i.targetNames, ref)
    61  			}
    62  		default:
    63  			if i.meta == nil {
    64  				i.meta = make(map[string][]byte)
    65  			}
    66  			i.meta[k] = []byte(v)
    67  		}
    68  	}
    69  	return i, nil
    70  }
    71  
    72  type imageExporterInstance struct {
    73  	*imageExporter
    74  	targetNames []distref.Named
    75  	meta        map[string][]byte
    76  }
    77  
    78  func (e *imageExporterInstance) Name() string {
    79  	return "exporting to image"
    80  }
    81  
    82  func (e *imageExporterInstance) Config() *exporter.Config {
    83  	return exporter.NewConfig()
    84  }
    85  
    86  func (e *imageExporterInstance) Export(ctx context.Context, inp *exporter.Source, sessionID string) (map[string]string, exporter.DescriptorReference, error) {
    87  	if len(inp.Refs) > 1 {
    88  		return nil, nil, fmt.Errorf("exporting multiple references to image store is currently unsupported")
    89  	}
    90  
    91  	ref := inp.Ref
    92  	if ref != nil && len(inp.Refs) == 1 {
    93  		return nil, nil, fmt.Errorf("invalid exporter input: Ref and Refs are mutually exclusive")
    94  	}
    95  
    96  	// only one loop
    97  	for _, v := range inp.Refs {
    98  		ref = v
    99  	}
   100  
   101  	var config []byte
   102  	switch len(inp.Refs) {
   103  	case 0:
   104  		config = inp.Metadata[exptypes.ExporterImageConfigKey]
   105  	case 1:
   106  		platformsBytes, ok := inp.Metadata[exptypes.ExporterPlatformsKey]
   107  		if !ok {
   108  			return nil, nil, fmt.Errorf("cannot export image, missing platforms mapping")
   109  		}
   110  		var p exptypes.Platforms
   111  		if err := json.Unmarshal(platformsBytes, &p); err != nil {
   112  			return nil, nil, errors.Wrapf(err, "failed to parse platforms passed to exporter")
   113  		}
   114  		if len(p.Platforms) != len(inp.Refs) {
   115  			return nil, nil, errors.Errorf("number of platforms does not match references %d %d", len(p.Platforms), len(inp.Refs))
   116  		}
   117  		config = inp.Metadata[fmt.Sprintf("%s/%s", exptypes.ExporterImageConfigKey, p.Platforms[0].ID)]
   118  	}
   119  
   120  	var diffs []digest.Digest
   121  	if ref != nil {
   122  		layersDone := oneOffProgress(ctx, "exporting layers")
   123  
   124  		if err := ref.Finalize(ctx); err != nil {
   125  			return nil, nil, layersDone(err)
   126  		}
   127  
   128  		if err := ref.Extract(ctx, nil); err != nil {
   129  			return nil, nil, err
   130  		}
   131  
   132  		diffIDs, err := e.opt.Differ.EnsureLayer(ctx, ref.ID())
   133  		if err != nil {
   134  			return nil, nil, layersDone(err)
   135  		}
   136  
   137  		diffs = make([]digest.Digest, len(diffIDs))
   138  		for i := range diffIDs {
   139  			diffs[i] = digest.Digest(diffIDs[i])
   140  		}
   141  
   142  		_ = layersDone(nil)
   143  	}
   144  
   145  	if len(config) == 0 {
   146  		var err error
   147  		config, err = emptyImageConfig()
   148  		if err != nil {
   149  			return nil, nil, err
   150  		}
   151  	}
   152  
   153  	history, err := parseHistoryFromConfig(config)
   154  	if err != nil {
   155  		return nil, nil, err
   156  	}
   157  
   158  	diffs, history = normalizeLayersAndHistory(diffs, history, ref)
   159  
   160  	config, err = patchImageConfig(config, diffs, history, inp.Metadata[exptypes.ExporterInlineCache])
   161  	if err != nil {
   162  		return nil, nil, err
   163  	}
   164  
   165  	configDigest := digest.FromBytes(config)
   166  
   167  	configDone := oneOffProgress(ctx, fmt.Sprintf("writing image %s", configDigest))
   168  	id, err := e.opt.ImageStore.Create(config)
   169  	if err != nil {
   170  		return nil, nil, configDone(err)
   171  	}
   172  	_ = configDone(nil)
   173  
   174  	if e.opt.ImageTagger != nil {
   175  		for _, targetName := range e.targetNames {
   176  			tagDone := oneOffProgress(ctx, "naming to "+targetName.String())
   177  			if err := e.opt.ImageTagger.TagImage(ctx, image.ID(digest.Digest(id)), targetName); err != nil {
   178  				return nil, nil, tagDone(err)
   179  			}
   180  			_ = tagDone(nil)
   181  		}
   182  	}
   183  
   184  	return map[string]string{
   185  		exptypes.ExporterImageConfigDigestKey: configDigest.String(),
   186  		exptypes.ExporterImageDigestKey:       id.String(),
   187  	}, nil, nil
   188  }