1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package mutate
16
17 import (
18 "encoding/json"
19 "errors"
20 "fmt"
21 "sync"
22
23 "github.com/google/go-containerregistry/pkg/logs"
24 v1 "github.com/google/go-containerregistry/pkg/v1"
25 "github.com/google/go-containerregistry/pkg/v1/match"
26 "github.com/google/go-containerregistry/pkg/v1/partial"
27 "github.com/google/go-containerregistry/pkg/v1/stream"
28 "github.com/google/go-containerregistry/pkg/v1/types"
29 )
30
31 func computeDescriptor(ia IndexAddendum) (*v1.Descriptor, error) {
32 desc, err := partial.Descriptor(ia.Add)
33 if err != nil {
34 return nil, err
35 }
36
37
38 if ia.Descriptor.Size != 0 {
39 desc.Size = ia.Descriptor.Size
40 }
41 if string(ia.Descriptor.MediaType) != "" {
42 desc.MediaType = ia.Descriptor.MediaType
43 }
44 if ia.Descriptor.Digest != (v1.Hash{}) {
45 desc.Digest = ia.Descriptor.Digest
46 }
47 if ia.Descriptor.Platform != nil {
48 desc.Platform = ia.Descriptor.Platform
49 }
50 if len(ia.Descriptor.URLs) != 0 {
51 desc.URLs = ia.Descriptor.URLs
52 }
53 if len(ia.Descriptor.Annotations) != 0 {
54 desc.Annotations = ia.Descriptor.Annotations
55 }
56 if ia.Descriptor.Data != nil {
57 desc.Data = ia.Descriptor.Data
58 }
59
60 return desc, nil
61 }
62
63 type index struct {
64 base v1.ImageIndex
65 adds []IndexAddendum
66
67 remove match.Matcher
68
69 computed bool
70 manifest *v1.IndexManifest
71 annotations map[string]string
72 mediaType *types.MediaType
73 imageMap map[v1.Hash]v1.Image
74 indexMap map[v1.Hash]v1.ImageIndex
75 layerMap map[v1.Hash]v1.Layer
76 subject *v1.Descriptor
77
78 sync.Mutex
79 }
80
81 var _ v1.ImageIndex = (*index)(nil)
82
83 func (i *index) MediaType() (types.MediaType, error) {
84 if i.mediaType != nil {
85 return *i.mediaType, nil
86 }
87 return i.base.MediaType()
88 }
89
90 func (i *index) Size() (int64, error) { return partial.Size(i) }
91
92 func (i *index) compute() error {
93 i.Lock()
94 defer i.Unlock()
95
96
97 if i.computed {
98 return nil
99 }
100
101 i.imageMap = make(map[v1.Hash]v1.Image)
102 i.indexMap = make(map[v1.Hash]v1.ImageIndex)
103 i.layerMap = make(map[v1.Hash]v1.Layer)
104
105 m, err := i.base.IndexManifest()
106 if err != nil {
107 return err
108 }
109 manifest := m.DeepCopy()
110 manifests := manifest.Manifests
111
112 if i.remove != nil {
113 var cleanedManifests []v1.Descriptor
114 for _, m := range manifests {
115 if !i.remove(m) {
116 cleanedManifests = append(cleanedManifests, m)
117 }
118 }
119 manifests = cleanedManifests
120 }
121
122 for _, add := range i.adds {
123 desc, err := computeDescriptor(add)
124 if err != nil {
125 return err
126 }
127
128 manifests = append(manifests, *desc)
129 if idx, ok := add.Add.(v1.ImageIndex); ok {
130 i.indexMap[desc.Digest] = idx
131 } else if img, ok := add.Add.(v1.Image); ok {
132 i.imageMap[desc.Digest] = img
133 } else if l, ok := add.Add.(v1.Layer); ok {
134 i.layerMap[desc.Digest] = l
135 } else {
136 logs.Warn.Printf("Unexpected index addendum: %T", add.Add)
137 }
138 }
139
140 manifest.Manifests = manifests
141
142 if i.mediaType != nil {
143 manifest.MediaType = *i.mediaType
144 }
145
146 if i.annotations != nil {
147 if manifest.Annotations == nil {
148 manifest.Annotations = map[string]string{}
149 }
150 for k, v := range i.annotations {
151 manifest.Annotations[k] = v
152 }
153 }
154 manifest.Subject = i.subject
155
156 i.manifest = manifest
157 i.computed = true
158 return nil
159 }
160
161 func (i *index) Image(h v1.Hash) (v1.Image, error) {
162 if img, ok := i.imageMap[h]; ok {
163 return img, nil
164 }
165 return i.base.Image(h)
166 }
167
168 func (i *index) ImageIndex(h v1.Hash) (v1.ImageIndex, error) {
169 if idx, ok := i.indexMap[h]; ok {
170 return idx, nil
171 }
172 return i.base.ImageIndex(h)
173 }
174
175 type withLayer interface {
176 Layer(v1.Hash) (v1.Layer, error)
177 }
178
179
180 func (i *index) Layer(h v1.Hash) (v1.Layer, error) {
181 if layer, ok := i.layerMap[h]; ok {
182 return layer, nil
183 }
184 if wl, ok := i.base.(withLayer); ok {
185 return wl.Layer(h)
186 }
187 return nil, fmt.Errorf("layer not found: %s", h)
188 }
189
190
191 func (i *index) Digest() (v1.Hash, error) {
192 if err := i.compute(); err != nil {
193 return v1.Hash{}, err
194 }
195 return partial.Digest(i)
196 }
197
198
199 func (i *index) IndexManifest() (*v1.IndexManifest, error) {
200 if err := i.compute(); err != nil {
201 return nil, err
202 }
203 return i.manifest.DeepCopy(), nil
204 }
205
206
207 func (i *index) RawManifest() ([]byte, error) {
208 if err := i.compute(); err != nil {
209 return nil, err
210 }
211 return json.Marshal(i.manifest)
212 }
213
214 func (i *index) Manifests() ([]partial.Describable, error) {
215 if err := i.compute(); errors.Is(err, stream.ErrNotComputed) {
216
217
218
219 manifests, err := partial.Manifests(i.base)
220 if err != nil {
221 return nil, err
222 }
223 for _, add := range i.adds {
224 manifests = append(manifests, add.Add)
225 }
226 return manifests, nil
227 } else if err != nil {
228 return nil, err
229 }
230
231 return partial.ComputeManifests(i)
232 }
233
View as plain text