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