1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package mutate
17
18 import (
19 "context"
20 "errors"
21 "fmt"
22
23 v1 "github.com/google/go-containerregistry/pkg/v1"
24 "github.com/google/go-containerregistry/pkg/v1/empty"
25 "github.com/google/go-containerregistry/pkg/v1/mutate"
26 "github.com/google/go-containerregistry/pkg/v1/types"
27 "github.com/sigstore/cosign/v2/pkg/oci"
28 )
29
30
31
32
33
34
35
36
37
38 type Fn func(context.Context, oci.SignedEntity) (oci.SignedEntity, error)
39
40
41
42 var ErrSkipChildren = errors.New("skip child entities")
43
44
45
46
47 func Map(ctx context.Context, parent oci.SignedEntity, fn Fn) (oci.SignedEntity, error) {
48 parent, err := fn(before(ctx), parent)
49 switch {
50 case errors.Is(err, ErrSkipChildren):
51 return parent, nil
52 case err != nil:
53 return nil, err
54 case parent == nil:
55
56 return nil, nil
57 }
58
59 sii, ok := parent.(oci.SignedImageIndex)
60 if !ok {
61 return parent, nil
62 }
63 im, err := sii.IndexManifest()
64 if err != nil {
65 return nil, err
66 }
67
68
69 changed := false
70
71 adds := []IndexAddendum{}
72 for _, desc := range im.Manifests {
73 switch desc.MediaType {
74 case types.OCIImageIndex, types.DockerManifestList:
75 x, err := sii.SignedImageIndex(desc.Digest)
76 if err != nil {
77 return nil, err
78 }
79
80 se, err := Map(ctx, x, fn)
81 if err != nil {
82 return nil, err
83 } else if se == nil {
84
85 changed = true
86 continue
87 }
88
89 changed = changed || (x != se)
90 adds = append(adds, IndexAddendum{
91 Add: se.(oci.SignedImageIndex),
92 Descriptor: v1.Descriptor{
93 URLs: desc.URLs,
94 MediaType: desc.MediaType,
95 Annotations: desc.Annotations,
96 Platform: desc.Platform,
97 },
98 })
99
100 case types.OCIManifestSchema1, types.DockerManifestSchema2:
101 x, err := sii.SignedImage(desc.Digest)
102 if err != nil {
103 return nil, err
104 }
105
106 se, err := fn(ctx, x)
107 if err != nil {
108 return nil, err
109 } else if se == nil {
110
111 changed = true
112 continue
113 }
114
115 changed = changed || (x != se)
116 adds = append(adds, IndexAddendum{
117 Add: se.(oci.SignedImage),
118 Descriptor: v1.Descriptor{
119 URLs: desc.URLs,
120 MediaType: desc.MediaType,
121 Annotations: desc.Annotations,
122 Platform: desc.Platform,
123 },
124 })
125
126 default:
127 return nil, fmt.Errorf("unknown mime type: %v", desc.MediaType)
128 }
129 }
130
131 if !changed {
132 return parent, nil
133 }
134
135
136 e := mutate.IndexMediaType(empty.Index, im.MediaType)
137 e = mutate.Annotations(e, im.Annotations).(v1.ImageIndex)
138
139
140 result := AppendManifests(e, adds...)
141
142
143 return fn(after(ctx), result)
144 }
145
146
147
148 type mapPassKey struct{}
149
150
151 func before(ctx context.Context) context.Context {
152 return context.WithValue(ctx, mapPassKey{}, "before")
153 }
154
155
156 func after(ctx context.Context) context.Context {
157 return context.WithValue(ctx, mapPassKey{}, "after")
158 }
159
160
161
162 func IsBeforeChildren(ctx context.Context) bool {
163 return ctx.Value(mapPassKey{}) == "before"
164 }
165
166
167
168
169 func IsAfterChildren(ctx context.Context) bool {
170 return ctx.Value(mapPassKey{}) == "after"
171 }
172
View as plain text