mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-07-17 17:52:12 +00:00
Implement support for digests in imageTags
This commit adds a new field to imageTags transformer so that digests can be used instead of image tags if needed. Closes https://github.com/kubernetes-sigs/kustomize/issues/326
This commit is contained in:
@@ -247,8 +247,12 @@ vars:
|
|||||||
# image: nginx:1.7.9
|
# image: nginx:1.7.9
|
||||||
#```
|
#```
|
||||||
# one can change the tag of myimage to v1 and the tag of nginx to 1.8.0 with the following:
|
# one can change the tag of myimage to v1 and the tag of nginx to 1.8.0 with the following:
|
||||||
|
#
|
||||||
|
# It also supports digests. If digest is present newTag is ignored.
|
||||||
imageTags:
|
imageTags:
|
||||||
- name: mycontainerregistry/myimage
|
- name: mycontainerregistry/myimage
|
||||||
newTag: v1
|
newTag: v1
|
||||||
- name: nginx
|
- name: nginx
|
||||||
newTag: 1.8.0
|
newTag: 1.8.0
|
||||||
|
- name: alpine
|
||||||
|
digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
@@ -29,7 +30,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type setImageTagOptions struct {
|
type setImageTagOptions struct {
|
||||||
imageTagMap map[string]string
|
imageTagMap map[string]types.ImageTag
|
||||||
}
|
}
|
||||||
|
|
||||||
var pattern = regexp.MustCompile("^(.*):([a-zA-Z0-9._-]*)$")
|
var pattern = regexp.MustCompile("^(.*):([a-zA-Z0-9._-]*)$")
|
||||||
@@ -40,10 +41,10 @@ func newCmdSetImageTag(fsys fs.FileSystem) *cobra.Command {
|
|||||||
|
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "imagetag",
|
Use: "imagetag",
|
||||||
Short: "Sets images and their new tags in the kustomization file",
|
Short: "Sets images and their new tags or digests in the kustomization file",
|
||||||
Example: `
|
Example: `
|
||||||
The command
|
The command
|
||||||
set imagetag nginx:1.8.0 my-app:latest
|
set imagetag nginx:1.8.0 my-app:latest alpine@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3
|
||||||
will add
|
will add
|
||||||
|
|
||||||
imageTags:
|
imageTags:
|
||||||
@@ -51,9 +52,11 @@ imageTags:
|
|||||||
newTag: 1.8.0
|
newTag: 1.8.0
|
||||||
- name: my-app
|
- name: my-app
|
||||||
newTag: latest
|
newTag: latest
|
||||||
|
- name: alpine
|
||||||
|
digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3
|
||||||
|
|
||||||
to the kustomization file if it doesn't exist,
|
to the kustomization file if it doesn't exist,
|
||||||
and overwrite the previous newTag if the image name exists.
|
and overwrite the previous ones if the image tag exists.
|
||||||
`,
|
`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
err := o.Validate(args)
|
err := o.Validate(args)
|
||||||
@@ -71,13 +74,27 @@ func (o *setImageTagOptions) Validate(args []string) error {
|
|||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
return errors.New("no image and newTag specified")
|
return errors.New("no image and newTag specified")
|
||||||
}
|
}
|
||||||
o.imageTagMap = make(map[string]string)
|
|
||||||
|
o.imageTagMap = make(map[string]types.ImageTag)
|
||||||
|
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
|
if strings.Contains(arg, "@") {
|
||||||
|
img := strings.Split(arg, "@")
|
||||||
|
o.imageTagMap[img[0]] = types.ImageTag{
|
||||||
|
Name: img[0],
|
||||||
|
Digest: img[1],
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
imagetag := pattern.FindStringSubmatch(arg)
|
imagetag := pattern.FindStringSubmatch(arg)
|
||||||
if len(imagetag) != 3 {
|
if len(imagetag) != 3 {
|
||||||
return errors.New("invalid format of imagetag, must specify it as <image>:<newtag>")
|
return errors.New("invalid format of imagetag, must specify it as <image>:<newtag> or <image>@<digest>")
|
||||||
|
}
|
||||||
|
o.imageTagMap[imagetag[1]] = types.ImageTag{
|
||||||
|
Name: imagetag[1],
|
||||||
|
NewTag: imagetag[2],
|
||||||
}
|
}
|
||||||
o.imageTagMap[imagetag[1]] = imagetag[2]
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -92,16 +109,18 @@ func (o *setImageTagOptions) RunSetImageTags(fsys fs.FileSystem) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
imageTagMap := map[string]string{}
|
|
||||||
for _, it := range m.ImageTags {
|
for _, it := range m.ImageTags {
|
||||||
imageTagMap[it.Name] = it.NewTag
|
if _, ok := o.imageTagMap[it.Name]; ok {
|
||||||
}
|
continue
|
||||||
for key, value := range o.imageTagMap {
|
}
|
||||||
imageTagMap[key] = value
|
|
||||||
|
o.imageTagMap[it.Name] = it
|
||||||
}
|
}
|
||||||
|
|
||||||
var imageTags []types.ImageTag
|
var imageTags []types.ImageTag
|
||||||
for key, value := range imageTagMap {
|
for _, v := range o.imageTagMap {
|
||||||
imageTags = append(imageTags, types.ImageTag{Name: key, NewTag: value})
|
imageTags = append(imageTags, v)
|
||||||
}
|
}
|
||||||
sort.Slice(imageTags, func(i, j int) bool {
|
sort.Slice(imageTags, func(i, j int) bool {
|
||||||
return imageTags[i].Name < imageTags[j].Name
|
return imageTags[i].Name < imageTags[j].Name
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ func TestSetImageTagsHappyPath(t *testing.T) {
|
|||||||
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
fakeFS.WriteFile(constants.KustomizationFileName, []byte(kustomizationContent))
|
||||||
|
|
||||||
cmd := newCmdSetImageTag(fakeFS)
|
cmd := newCmdSetImageTag(fakeFS)
|
||||||
args := []string{"image1:tag1", "image2:tag2", "localhost:5000/operator:1.0.0"}
|
args := []string{"image1:tag1", "image2:tag2", "localhost:5000/operator:1.0.0",
|
||||||
|
"foo.bar.baz:5000/one/two@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3"}
|
||||||
err := cmd.RunE(cmd, args)
|
err := cmd.RunE(cmd, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected cmd error: %v", err)
|
t.Errorf("unexpected cmd error: %v", err)
|
||||||
@@ -40,6 +41,8 @@ func TestSetImageTagsHappyPath(t *testing.T) {
|
|||||||
}
|
}
|
||||||
expected := []byte(`
|
expected := []byte(`
|
||||||
imageTags:
|
imageTags:
|
||||||
|
- digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3
|
||||||
|
name: foo.bar.baz:5000/one/two
|
||||||
- name: image1
|
- name: image1
|
||||||
newTag: tag1
|
newTag: tag1
|
||||||
- name: image2
|
- name: image2
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ package transformers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
"github.com/kubernetes-sigs/kustomize/pkg/resmap"
|
||||||
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
"github.com/kubernetes-sigs/kustomize/pkg/types"
|
||||||
@@ -83,7 +82,12 @@ func (pt *imageTagTransformer) updateContainers(obj map[string]interface{}, path
|
|||||||
}
|
}
|
||||||
for _, imagetag := range pt.imageTags {
|
for _, imagetag := range pt.imageTags {
|
||||||
if isImageMatched(image.(string), imagetag.Name) {
|
if isImageMatched(image.(string), imagetag.Name) {
|
||||||
container["image"] = strings.Join([]string{imagetag.Name, imagetag.NewTag}, ":")
|
container["image"] = imagetag.Name + ":" + imagetag.NewTag
|
||||||
|
|
||||||
|
if imagetag.Digest != "" {
|
||||||
|
container["image"] = imagetag.Name + "@" + imagetag.Digest
|
||||||
|
}
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,6 +50,10 @@ func TestImageTagTransformer(t *testing.T) {
|
|||||||
"name": "nginx",
|
"name": "nginx",
|
||||||
"image": "nginx:1.7.9",
|
"image": "nginx:1.7.9",
|
||||||
},
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"name": "replaced-with-digest",
|
||||||
|
"image": "foobar:1",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -114,6 +118,10 @@ func TestImageTagTransformer(t *testing.T) {
|
|||||||
"name": "nginx",
|
"name": "nginx",
|
||||||
"image": "nginx:v2",
|
"image": "nginx:v2",
|
||||||
},
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"name": "replaced-with-digest",
|
||||||
|
"image": "foobar@sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -160,6 +168,7 @@ func TestImageTagTransformer(t *testing.T) {
|
|||||||
{Name: "nginx", NewTag: "v2"},
|
{Name: "nginx", NewTag: "v2"},
|
||||||
{Name: "my-nginx", NewTag: "previous"},
|
{Name: "my-nginx", NewTag: "previous"},
|
||||||
{Name: "myprivaterepohostname:1234/my/image", NewTag: "v1.0.1"},
|
{Name: "myprivaterepohostname:1234/my/image", NewTag: "v1.0.1"},
|
||||||
|
{Name: "foobar", Digest: "sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3"},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
|||||||
@@ -176,4 +176,8 @@ type ImageTag struct {
|
|||||||
|
|
||||||
// NewTag is the value to use in replacing the original tag.
|
// NewTag is the value to use in replacing the original tag.
|
||||||
NewTag string `json:"newTag,omitempty" yaml:"newTag,omitempty"`
|
NewTag string `json:"newTag,omitempty" yaml:"newTag,omitempty"`
|
||||||
|
|
||||||
|
// Digest is the value used to replace the original image tag.
|
||||||
|
// If digest is present NewTag value is ignored.
|
||||||
|
Digest string `json:"digest,omitempty" yaml:"digest,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user