@@ -17,6 +17,7 @@ package cmd
17
17
import (
18
18
"errors"
19
19
"fmt"
20
+ "github.com/google/go-containerregistry/pkg/v1/layout"
20
21
"strings"
21
22
22
23
"github.com/google/go-containerregistry/pkg/crane"
@@ -37,33 +38,78 @@ func NewCmdMutate(options *[]crane.Option) *cobra.Command {
37
38
var newRef string
38
39
var newRepo string
39
40
var user string
41
+ var ociImageLayoutPath string
40
42
41
43
mutateCmd := & cobra.Command {
42
44
Use : "mutate" ,
43
45
Short : "Modify image labels and annotations. The container must be pushed to a registry, and the manifest is updated there." ,
44
- Args : cobra .ExactArgs (1 ),
45
46
RunE : func (_ * cobra.Command , args []string ) error {
47
+ var img v1.Image
48
+ var err error
46
49
// Pull image and get config.
47
- ref := args [0 ]
50
+ var ref string
51
+ if len (args ) > 0 {
52
+ ref = args [0 ]
53
+ }
54
+
55
+ if ref != "" && ociImageLayoutPath != "" {
56
+ return errors .New ("cannot specify both image and oci image layout" )
57
+ }
58
+
59
+ if ref != "" {
60
+ if len (annotations ) != 0 {
61
+ desc , err := crane .Head (ref , * options ... )
62
+ if err != nil {
63
+ return err
64
+ }
65
+ if desc .MediaType .IsIndex () {
66
+ return errors .New ("mutating annotations on an index is not yet supported" )
67
+ }
68
+ }
48
69
49
- if len (annotations ) != 0 {
50
- desc , err := crane .Head (ref , * options ... )
70
+ i , err := crane .Pull (ref , * options ... )
71
+ if err != nil {
72
+ return fmt .Errorf ("pulling %s: %w" , ref , err )
73
+ }
74
+ img = i
75
+ } else if ociImageLayoutPath != "" {
76
+ oil , err := layout .FromPath (ociImageLayoutPath )
51
77
if err != nil {
52
78
return err
53
79
}
54
- if desc .MediaType .IsIndex () {
55
- return errors .New ("mutating annotations on an index is not yet supported" )
80
+ ii , err := oil .ImageIndex ()
81
+ if err != nil {
82
+ return err
56
83
}
57
- }
58
84
59
- if newRepo != "" && newRef != "" {
60
- return errors .New ("repository can't be set when a tag is specified" )
61
- }
85
+ m , err := ii .IndexManifest ()
86
+ if err != nil {
87
+ return err
88
+ }
62
89
63
- img , err := crane .Pull (ref , * options ... )
64
- if err != nil {
65
- return fmt .Errorf ("pulling %s: %w" , ref , err )
90
+ desc := m .Manifests [0 ]
91
+
92
+ if len (annotations ) != 0 {
93
+ if err != nil {
94
+ return err
95
+ }
96
+ if desc .MediaType .IsIndex () {
97
+ return errors .New ("mutating annotations on an index is not yet supported" )
98
+ }
99
+ }
100
+
101
+ var i v1.Image
102
+ if desc .MediaType .IsImage () {
103
+ i , err = oil .Image (desc .Digest )
104
+ if err != nil {
105
+ return err
106
+ }
107
+ } else if desc .MediaType .IsIndex () {
108
+ return errors .New ("mutating layers on an index is not yet supported" )
109
+ }
110
+ img = i
66
111
}
112
+
67
113
if len (newLayers ) != 0 {
68
114
img , err = crane .Append (img , newLayers ... )
69
115
if err != nil {
@@ -122,35 +168,52 @@ func NewCmdMutate(options *[]crane.Option) *cobra.Command {
122
168
123
169
img = mutate .Annotations (img , annotations ).(v1.Image )
124
170
125
- // If the new ref isn't provided, write over the original image.
126
- // If that ref was provided by digest (e.g., output from
127
- // another crane command), then strip that and push the
128
- // mutated image by digest instead.
129
- if newRepo != "" {
130
- newRef = newRepo
131
- } else if newRef == "" {
132
- newRef = ref
133
- }
134
171
digest , err := img .Digest ()
135
172
if err != nil {
136
173
return fmt .Errorf ("digesting new image: %w" , err )
137
174
}
138
- if outFile != "" {
139
- if err := crane .Save (img , newRef , outFile ); err != nil {
140
- return fmt .Errorf ("writing output %q: %w" , outFile , err )
175
+ if ref != "" {
176
+ if newRepo != "" && newRef != "" {
177
+ return errors .New ("repository can't be set when a tag is specified" )
178
+ }
179
+
180
+ // If the new ref isn't provided, write over the original image.
181
+ // If that ref was provided by digest (e.g., output from
182
+ // another crane command), then strip that and push the
183
+ // mutated image by digest instead.
184
+ if newRepo != "" {
185
+ newRef = newRepo
186
+ } else if newRef == "" {
187
+ newRef = ref
188
+ }
189
+
190
+ if outFile != "" {
191
+ if err := crane .Save (img , newRef , outFile ); err != nil {
192
+ return fmt .Errorf ("writing output %q: %w" , outFile , err )
193
+ }
194
+ } else {
195
+ r , err := name .ParseReference (newRef )
196
+ if err != nil {
197
+ return fmt .Errorf ("parsing %s: %w" , newRef , err )
198
+ }
199
+ if _ , ok := r .(name.Digest ); ok || newRepo != "" {
200
+ newRef = r .Context ().Digest (digest .String ()).String ()
201
+ }
202
+ if err := crane .Push (img , newRef , * options ... ); err != nil {
203
+ return fmt .Errorf ("pushing %s: %w" , newRef , err )
204
+ }
205
+ fmt .Println (r .Context ().Digest (digest .String ()))
141
206
}
142
207
} else {
143
- r , err := name .ParseReference (newRef )
208
+ // TODO: this adds the new manifest to the index, but doesn't remove the old one
209
+ p , err := layout .FromPath (ociImageLayoutPath )
144
210
if err != nil {
145
- return fmt .Errorf ("parsing %s: %w" , newRef , err )
146
- }
147
- if _ , ok := r .(name.Digest ); ok || newRepo != "" {
148
- newRef = r .Context ().Digest (digest .String ()).String ()
211
+ return err
149
212
}
150
- if err := crane .Push (img , newRef , * options ... ); err != nil {
151
- return fmt .Errorf ("pushing %s: %w" , newRef , err )
213
+ err = p .WriteImage (img )
214
+ if err != nil {
215
+ return err
152
216
}
153
- fmt .Println (r .Context ().Digest (digest .String ()))
154
217
}
155
218
return nil
156
219
},
@@ -165,6 +228,7 @@ func NewCmdMutate(options *[]crane.Option) *cobra.Command {
165
228
mutateCmd .Flags ().StringVarP (& outFile , "output" , "o" , "" , "Path to new tarball of resulting image" )
166
229
mutateCmd .Flags ().StringSliceVar (& newLayers , "append" , []string {}, "Path to tarball to append to image" )
167
230
mutateCmd .Flags ().StringVarP (& user , "user" , "u" , "" , "New user to set" )
231
+ mutateCmd .Flags ().StringVarP (& ociImageLayoutPath , "oci-image-layout" , "" , "" , "A path to OCI Image Layout directory" )
168
232
return mutateCmd
169
233
}
170
234
0 commit comments