Skip to content

Commit 2c1be17

Browse files
authored
Merge pull request #704 from narg95/feature/add-image-transformer
Add image transformer
2 parents 56ce6b8 + 6616b25 commit 2c1be17

File tree

9 files changed

+362
-162
lines changed

9 files changed

+362
-162
lines changed

pkg/commands/edit/set/setimagetag.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ import (
2525
"github.com/spf13/cobra"
2626
"sigs.k8s.io/kustomize/pkg/commands/kustfile"
2727
"sigs.k8s.io/kustomize/pkg/fs"
28-
"sigs.k8s.io/kustomize/pkg/types"
28+
"sigs.k8s.io/kustomize/pkg/image"
2929
)
3030

3131
type setImageTagOptions struct {
32-
imageTagMap map[string]types.ImageTag
32+
imageTagMap map[string]image.ImageTag
3333
}
3434

3535
var pattern = regexp.MustCompile("^(.*):([a-zA-Z0-9._-]*)$")
@@ -74,11 +74,11 @@ func (o *setImageTagOptions) Validate(args []string) error {
7474
return errors.New("no image specified")
7575
}
7676

77-
o.imageTagMap = make(map[string]types.ImageTag)
77+
o.imageTagMap = make(map[string]image.ImageTag)
7878

7979
for _, arg := range args {
8080
if s := strings.Split(arg, "@"); len(s) > 1 {
81-
o.imageTagMap[s[0]] = types.ImageTag{
81+
o.imageTagMap[s[0]] = image.ImageTag{
8282
Name: s[0],
8383
Digest: s[1],
8484
}
@@ -89,7 +89,7 @@ func (o *setImageTagOptions) Validate(args []string) error {
8989
if len(s) != 3 {
9090
return errors.New("invalid format of imagetag, must specify it as <image>:<newtag> or <image>@<digest>")
9191
}
92-
o.imageTagMap[s[1]] = types.ImageTag{
92+
o.imageTagMap[s[1]] = image.ImageTag{
9393
Name: s[1],
9494
NewTag: s[2],
9595
}
@@ -116,7 +116,7 @@ func (o *setImageTagOptions) RunSetImageTags(fSys fs.FileSystem) error {
116116
o.imageTagMap[it.Name] = it
117117
}
118118

119-
var imageTags []types.ImageTag
119+
var imageTags []image.ImageTag
120120
for _, v := range o.imageTagMap {
121121
imageTags = append(imageTags, v)
122122
}

pkg/image/append.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
Copyright 2019 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package image
18+
19+
// Append appends a slice of type Tag to slice of type Image
20+
func Append(images []Image, tags ...ImageTag) []Image {
21+
for _, tag := range tags {
22+
images = append(images, toImage(tag))
23+
}
24+
return images
25+
}
26+
27+
func toImage(tag ImageTag) Image {
28+
return Image{
29+
Name: tag.Name,
30+
NewTag: tag.NewTag,
31+
Digest: tag.Digest,
32+
}
33+
}

pkg/image/image.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
Copyright 2019 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// Package image provides struct definitions and libraries
18+
// for image overwriting of names, tags and digest.
19+
package image
20+
21+
// Image contains an image name, a new name, a new tag or digest,
22+
// which will replace the original name and tag.
23+
type Image struct {
24+
// Name is a tag-less image name.
25+
Name string `json:"name,omitempty" yaml:"name,omitempty"`
26+
27+
// NewName is the value used to replace the original name.
28+
NewName string `json:"newName,omitempty" yaml:"newName,omitempty"`
29+
30+
// NewTag is the value used to replace the original tag.
31+
NewTag string `json:"newTag,omitempty" yaml:"newTag,omitempty"`
32+
33+
// Digest is the value used to replace the original image tag.
34+
// If digest is present NewTag value is ignored.
35+
Digest string `json:"digest,omitempty" yaml:"digest,omitempty"`
36+
}

pkg/image/imagetag.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
Copyright 2019 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package image
18+
19+
// ImageTag contains an image and a new tag, which will replace the original tag.
20+
// Deprecated, instead use Image.
21+
type ImageTag struct {
22+
// Name is a tag-less image name.
23+
Name string `json:"name,omitempty" yaml:"name,omitempty"`
24+
25+
// NewTag is the value to use in replacing the original tag.
26+
NewTag string `json:"newTag,omitempty" yaml:"newTag,omitempty"`
27+
28+
// Digest is the value used to replace the original image tag.
29+
// If digest is present NewTag value is ignored.
30+
Digest string `json:"digest,omitempty" yaml:"digest,omitempty"`
31+
}

pkg/target/kusttarget.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ func (kt *KustTarget) newTransformer(
302302
return nil, err
303303
}
304304
r = append(r, t)
305-
t, err = transformers.NewImageTagTransformer(kt.kustomization.ImageTags)
305+
t, err = transformers.NewImageTransformer(kt.kustomization.Images)
306306
if err != nil {
307307
return nil, err
308308
}

pkg/transformers/image.go

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/*
2+
Copyright 2019 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package transformers
18+
19+
import (
20+
"regexp"
21+
"strings"
22+
23+
"sigs.k8s.io/kustomize/pkg/image"
24+
"sigs.k8s.io/kustomize/pkg/resmap"
25+
)
26+
27+
// imageTransformer replace image names and tags
28+
type imageTransformer struct {
29+
images []image.Image
30+
}
31+
32+
var _ Transformer = &imageTransformer{}
33+
34+
// NewImageTransformer constructs an imageTransformer.
35+
func NewImageTransformer(slice []image.Image) (Transformer, error) {
36+
return &imageTransformer{slice}, nil
37+
}
38+
39+
// Transform finds the matching images and replaces name, tag and/or digest
40+
func (pt *imageTransformer) Transform(resources resmap.ResMap) error {
41+
if len(pt.images) == 0 {
42+
return nil
43+
}
44+
for _, res := range resources {
45+
err := pt.findAndReplaceImage(res.Map())
46+
if err != nil {
47+
return err
48+
}
49+
}
50+
return nil
51+
}
52+
53+
/*
54+
findAndReplaceImage replaces the image name and tags inside one object
55+
It searches the object for container session
56+
then loops though all images inside containers session,
57+
finds matched ones and update the image name and tag name
58+
*/
59+
func (pt *imageTransformer) findAndReplaceImage(obj map[string]interface{}) error {
60+
paths := []string{"containers", "initContainers"}
61+
found := false
62+
for _, path := range paths {
63+
_, found = obj[path]
64+
if found {
65+
err := pt.updateContainers(obj, path)
66+
if err != nil {
67+
return err
68+
}
69+
}
70+
}
71+
if !found {
72+
return pt.findContainers(obj)
73+
}
74+
return nil
75+
}
76+
77+
func (pt *imageTransformer) updateContainers(obj map[string]interface{}, path string) error {
78+
containers := obj[path].([]interface{})
79+
for i := range containers {
80+
container := containers[i].(map[string]interface{})
81+
containerImage, found := container["image"]
82+
if !found {
83+
continue
84+
}
85+
86+
imageName := containerImage.(string)
87+
for _, image := range pt.images {
88+
if isImageMatched(imageName, image.Name) {
89+
name, tag := split(imageName)
90+
91+
if image.NewName != "" {
92+
name = image.NewName
93+
}
94+
95+
if image.NewTag != "" {
96+
tag = ":" + image.NewTag
97+
}
98+
99+
if image.Digest != "" {
100+
tag = "@" + image.Digest
101+
}
102+
103+
container["image"] = name + tag
104+
break
105+
}
106+
}
107+
}
108+
return nil
109+
}
110+
111+
func (pt *imageTransformer) findContainers(obj map[string]interface{}) error {
112+
for key := range obj {
113+
switch typedV := obj[key].(type) {
114+
case map[string]interface{}:
115+
err := pt.findAndReplaceImage(typedV)
116+
if err != nil {
117+
return err
118+
}
119+
case []interface{}:
120+
for i := range typedV {
121+
item := typedV[i]
122+
typedItem, ok := item.(map[string]interface{})
123+
if ok {
124+
err := pt.findAndReplaceImage(typedItem)
125+
if err != nil {
126+
return err
127+
}
128+
}
129+
}
130+
}
131+
}
132+
return nil
133+
}
134+
135+
func isImageMatched(s, t string) bool {
136+
// Tag values are limited to [a-zA-Z0-9_.-].
137+
pattern, _ := regexp.Compile("^" + t + "(:[a-zA-Z0-9_.-]*)?$")
138+
return pattern.MatchString(s)
139+
}
140+
141+
// split separates and returns the name and tag parts
142+
// from the image string using either colon `:` or at `@` separators.
143+
// Note that the returned tag keeps its separator.
144+
func split(imageName string) (name string, tag string) {
145+
ic := strings.LastIndex(imageName, ":")
146+
ia := strings.LastIndex(imageName, "@")
147+
if ic < 0 && ia < 0 {
148+
return imageName, ""
149+
}
150+
151+
i := ic
152+
if ic < 0 {
153+
i = ia
154+
}
155+
156+
name = imageName[:i]
157+
tag = imageName[i:]
158+
return
159+
}

0 commit comments

Comments
 (0)