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 }