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