1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package mutate
16
17 import (
18 "bytes"
19 "encoding/json"
20 "errors"
21 "sync"
22
23 v1 "github.com/google/go-containerregistry/pkg/v1"
24 "github.com/google/go-containerregistry/pkg/v1/partial"
25 "github.com/google/go-containerregistry/pkg/v1/stream"
26 "github.com/google/go-containerregistry/pkg/v1/types"
27 )
28
29 type image struct {
30 base v1.Image
31 adds []Addendum
32
33 computed bool
34 configFile *v1.ConfigFile
35 manifest *v1.Manifest
36 annotations map[string]string
37 mediaType *types.MediaType
38 configMediaType *types.MediaType
39 diffIDMap map[v1.Hash]v1.Layer
40 digestMap map[v1.Hash]v1.Layer
41 subject *v1.Descriptor
42
43 sync.Mutex
44 }
45
46 var _ v1.Image = (*image)(nil)
47
48 func (i *image) MediaType() (types.MediaType, error) {
49 if i.mediaType != nil {
50 return *i.mediaType, nil
51 }
52 return i.base.MediaType()
53 }
54
55 func (i *image) compute() error {
56 i.Lock()
57 defer i.Unlock()
58
59
60 if i.computed {
61 return nil
62 }
63 var configFile *v1.ConfigFile
64 if i.configFile != nil {
65 configFile = i.configFile
66 } else {
67 cf, err := i.base.ConfigFile()
68 if err != nil {
69 return err
70 }
71 configFile = cf.DeepCopy()
72 }
73 diffIDs := configFile.RootFS.DiffIDs
74 history := configFile.History
75
76 diffIDMap := make(map[v1.Hash]v1.Layer)
77 digestMap := make(map[v1.Hash]v1.Layer)
78
79 for _, add := range i.adds {
80 history = append(history, add.History)
81 if add.Layer != nil {
82 diffID, err := add.Layer.DiffID()
83 if err != nil {
84 return err
85 }
86 diffIDs = append(diffIDs, diffID)
87 diffIDMap[diffID] = add.Layer
88 }
89 }
90
91 m, err := i.base.Manifest()
92 if err != nil {
93 return err
94 }
95 manifest := m.DeepCopy()
96 manifestLayers := manifest.Layers
97 for _, add := range i.adds {
98 if add.Layer == nil {
99
100 continue
101 }
102
103 desc, err := partial.Descriptor(add.Layer)
104 if err != nil {
105 return err
106 }
107
108
109 if len(add.Annotations) != 0 {
110 desc.Annotations = add.Annotations
111 }
112 if len(add.URLs) != 0 {
113 desc.URLs = add.URLs
114 }
115
116 if add.MediaType != "" {
117 desc.MediaType = add.MediaType
118 }
119
120 manifestLayers = append(manifestLayers, *desc)
121 digestMap[desc.Digest] = add.Layer
122 }
123
124 configFile.RootFS.DiffIDs = diffIDs
125 configFile.History = history
126
127 manifest.Layers = manifestLayers
128
129 rcfg, err := json.Marshal(configFile)
130 if err != nil {
131 return err
132 }
133 d, sz, err := v1.SHA256(bytes.NewBuffer(rcfg))
134 if err != nil {
135 return err
136 }
137 manifest.Config.Digest = d
138 manifest.Config.Size = sz
139
140
141 if m.Config.Data != nil {
142 manifest.Config.Data = rcfg
143 }
144
145
146 if i.configMediaType != nil {
147 manifest.Config.MediaType = *i.configMediaType
148 }
149
150 if i.mediaType != nil {
151 manifest.MediaType = *i.mediaType
152 }
153
154 if i.annotations != nil {
155 if manifest.Annotations == nil {
156 manifest.Annotations = map[string]string{}
157 }
158
159 for k, v := range i.annotations {
160 manifest.Annotations[k] = v
161 }
162 }
163 manifest.Subject = i.subject
164
165 i.configFile = configFile
166 i.manifest = manifest
167 i.diffIDMap = diffIDMap
168 i.digestMap = digestMap
169 i.computed = true
170 return nil
171 }
172
173
174
175 func (i *image) Layers() ([]v1.Layer, error) {
176 if err := i.compute(); errors.Is(err, stream.ErrNotComputed) {
177
178
179
180 layers, err := i.base.Layers()
181 if err != nil {
182 return nil, err
183 }
184 for _, add := range i.adds {
185 layers = append(layers, add.Layer)
186 }
187 return layers, nil
188 } else if err != nil {
189 return nil, err
190 }
191
192 diffIDs, err := partial.DiffIDs(i)
193 if err != nil {
194 return nil, err
195 }
196 ls := make([]v1.Layer, 0, len(diffIDs))
197 for _, h := range diffIDs {
198 l, err := i.LayerByDiffID(h)
199 if err != nil {
200 return nil, err
201 }
202 ls = append(ls, l)
203 }
204 return ls, nil
205 }
206
207
208 func (i *image) ConfigName() (v1.Hash, error) {
209 if err := i.compute(); err != nil {
210 return v1.Hash{}, err
211 }
212 return partial.ConfigName(i)
213 }
214
215
216 func (i *image) ConfigFile() (*v1.ConfigFile, error) {
217 if err := i.compute(); err != nil {
218 return nil, err
219 }
220 return i.configFile.DeepCopy(), nil
221 }
222
223
224 func (i *image) RawConfigFile() ([]byte, error) {
225 if err := i.compute(); err != nil {
226 return nil, err
227 }
228 return json.Marshal(i.configFile)
229 }
230
231
232 func (i *image) Digest() (v1.Hash, error) {
233 if err := i.compute(); err != nil {
234 return v1.Hash{}, err
235 }
236 return partial.Digest(i)
237 }
238
239
240 func (i *image) Size() (int64, error) {
241 if err := i.compute(); err != nil {
242 return -1, err
243 }
244 return partial.Size(i)
245 }
246
247
248 func (i *image) Manifest() (*v1.Manifest, error) {
249 if err := i.compute(); err != nil {
250 return nil, err
251 }
252 return i.manifest.DeepCopy(), nil
253 }
254
255
256 func (i *image) RawManifest() ([]byte, error) {
257 if err := i.compute(); err != nil {
258 return nil, err
259 }
260 return json.Marshal(i.manifest)
261 }
262
263
264
265 func (i *image) LayerByDigest(h v1.Hash) (v1.Layer, error) {
266 if cn, err := i.ConfigName(); err != nil {
267 return nil, err
268 } else if h == cn {
269 return partial.ConfigLayer(i)
270 }
271 if layer, ok := i.digestMap[h]; ok {
272 return layer, nil
273 }
274 return i.base.LayerByDigest(h)
275 }
276
277
278
279 func (i *image) LayerByDiffID(h v1.Hash) (v1.Layer, error) {
280 if layer, ok := i.diffIDMap[h]; ok {
281 return layer, nil
282 }
283 return i.base.LayerByDiffID(h)
284 }
285
286 func validate(adds []Addendum) error {
287 for _, add := range adds {
288 if add.Layer == nil && !add.History.EmptyLayer {
289 return errors.New("unable to add a nil layer to the image")
290 }
291 }
292 return nil
293 }
294
View as plain text