github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/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/demonoid81/moby/image" 11 "github.com/demonoid81/moby/layer" 12 "github.com/demonoid81/moby/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) (map[string]string, error) { 81 82 if len(inp.Refs) > 1 { 83 return nil, fmt.Errorf("exporting multiple references to image store is currently unsupported") 84 } 85 86 ref := inp.Ref 87 if ref != nil && len(inp.Refs) == 1 { 88 return nil, fmt.Errorf("invalid exporter input: Ref and Refs are mutually exclusive") 89 } 90 91 // only one loop 92 for _, v := range inp.Refs { 93 ref = v 94 } 95 96 var config []byte 97 switch len(inp.Refs) { 98 case 0: 99 config = inp.Metadata[exptypes.ExporterImageConfigKey] 100 case 1: 101 platformsBytes, ok := inp.Metadata[exptypes.ExporterPlatformsKey] 102 if !ok { 103 return nil, fmt.Errorf("cannot export image, missing platforms mapping") 104 } 105 var p exptypes.Platforms 106 if err := json.Unmarshal(platformsBytes, &p); err != nil { 107 return nil, errors.Wrapf(err, "failed to parse platforms passed to exporter") 108 } 109 if len(p.Platforms) != len(inp.Refs) { 110 return nil, errors.Errorf("number of platforms does not match references %d %d", len(p.Platforms), len(inp.Refs)) 111 } 112 config = inp.Metadata[fmt.Sprintf("%s/%s", exptypes.ExporterImageConfigKey, p.Platforms[0].ID)] 113 } 114 115 var diffs []digest.Digest 116 if ref != nil { 117 layersDone := oneOffProgress(ctx, "exporting layers") 118 119 if err := ref.Finalize(ctx, true); err != nil { 120 return nil, layersDone(err) 121 } 122 123 diffIDs, err := e.opt.Differ.EnsureLayer(ctx, ref.ID()) 124 if err != nil { 125 return nil, layersDone(err) 126 } 127 128 diffs = make([]digest.Digest, len(diffIDs)) 129 for i := range diffIDs { 130 diffs[i] = digest.Digest(diffIDs[i]) 131 } 132 133 _ = layersDone(nil) 134 } 135 136 if len(config) == 0 { 137 var err error 138 config, err = emptyImageConfig() 139 if err != nil { 140 return nil, err 141 } 142 } 143 144 history, err := parseHistoryFromConfig(config) 145 if err != nil { 146 return nil, err 147 } 148 149 diffs, history = normalizeLayersAndHistory(diffs, history, ref) 150 151 config, err = patchImageConfig(config, diffs, history, inp.Metadata[exptypes.ExporterInlineCache]) 152 if err != nil { 153 return nil, err 154 } 155 156 configDigest := digest.FromBytes(config) 157 158 configDone := oneOffProgress(ctx, fmt.Sprintf("writing image %s", configDigest)) 159 id, err := e.opt.ImageStore.Create(config) 160 if err != nil { 161 return nil, configDone(err) 162 } 163 _ = configDone(nil) 164 165 if e.opt.ReferenceStore != nil { 166 for _, targetName := range e.targetNames { 167 tagDone := oneOffProgress(ctx, "naming to "+targetName.String()) 168 169 if err := e.opt.ReferenceStore.AddTag(targetName, digest.Digest(id), true); err != nil { 170 return nil, tagDone(err) 171 } 172 _ = tagDone(nil) 173 } 174 } 175 176 return map[string]string{ 177 "containerimage.digest": id.String(), 178 }, nil 179 }