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 }