Skip to content

Commit 7596331

Browse files
committed
feat: add oci image layout mutation
Signed-off-by: Batuhan Apaydın <[email protected]>
1 parent e2d575c commit 7596331

File tree

1 file changed

+97
-33
lines changed

1 file changed

+97
-33
lines changed

cmd/crane/cmd/mutate.go

+97-33
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package cmd
1717
import (
1818
"errors"
1919
"fmt"
20+
"github.com/google/go-containerregistry/pkg/v1/layout"
2021
"strings"
2122

2223
"github.com/google/go-containerregistry/pkg/crane"
@@ -37,33 +38,78 @@ func NewCmdMutate(options *[]crane.Option) *cobra.Command {
3738
var newRef string
3839
var newRepo string
3940
var user string
41+
var ociImageLayoutPath string
4042

4143
mutateCmd := &cobra.Command{
4244
Use: "mutate",
4345
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),
4546
RunE: func(_ *cobra.Command, args []string) error {
47+
var img v1.Image
48+
var err error
4649
// 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+
}
4869

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)
5177
if err != nil {
5278
return err
5379
}
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
5683
}
57-
}
5884

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+
}
6289

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
66111
}
112+
67113
if len(newLayers) != 0 {
68114
img, err = crane.Append(img, newLayers...)
69115
if err != nil {
@@ -122,35 +168,52 @@ func NewCmdMutate(options *[]crane.Option) *cobra.Command {
122168

123169
img = mutate.Annotations(img, annotations).(v1.Image)
124170

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-
}
134171
digest, err := img.Digest()
135172
if err != nil {
136173
return fmt.Errorf("digesting new image: %w", err)
137174
}
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()))
141206
}
142207
} 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)
144210
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
149212
}
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
152216
}
153-
fmt.Println(r.Context().Digest(digest.String()))
154217
}
155218
return nil
156219
},
@@ -165,6 +228,7 @@ func NewCmdMutate(options *[]crane.Option) *cobra.Command {
165228
mutateCmd.Flags().StringVarP(&outFile, "output", "o", "", "Path to new tarball of resulting image")
166229
mutateCmd.Flags().StringSliceVar(&newLayers, "append", []string{}, "Path to tarball to append to image")
167230
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")
168232
return mutateCmd
169233
}
170234

0 commit comments

Comments
 (0)