From fa3a64e3523a13dd07d2b11f5c0445b8dcdaeafb Mon Sep 17 00:00:00 2001 From: Jingfang Liu Date: Thu, 19 Jul 2018 16:31:59 -0700 Subject: [PATCH 1/2] Add imageTagTransformer --- pkg/transformers/imagetag.go | 116 ++++++++++++++++++++ pkg/transformers/imagetag_test.go | 170 ++++++++++++++++++++++++++++++ 2 files changed, 286 insertions(+) create mode 100644 pkg/transformers/imagetag.go create mode 100644 pkg/transformers/imagetag_test.go diff --git a/pkg/transformers/imagetag.go b/pkg/transformers/imagetag.go new file mode 100644 index 000000000..d78ce2f5d --- /dev/null +++ b/pkg/transformers/imagetag.go @@ -0,0 +1,116 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package transformers + +import ( + "strings" + + "github.com/kubernetes-sigs/kustomize/pkg/resmap" + "github.com/kubernetes-sigs/kustomize/pkg/types" +) + +// imageTagTransformer replace image tags +type imageTagTransformer struct { + imageTags []types.ImageTag +} + +var _ Transformer = &imageTagTransformer{} + +// NewImageTagTransformer constructs a imageTagTransformer. +func NewImageTagTransformer(slice []types.ImageTag) (Transformer, error) { + return &imageTagTransformer{slice}, nil +} + +// Transform finds the matching images and replace the tag +func (pt *imageTagTransformer) Transform(resources resmap.ResMap) error { + if len(pt.imageTags) == 0 { + return nil + } + for _, res := range resources { + err := pt.findAndReplaceTag(res.UnstructuredContent()) + if err != nil { + return err + } + } + return nil +} + +/* + findAndReplaceTag replaces the image tags inside one object + It searches the object for container session + then loops though all images inside containers session, finds matched ones and update the tag name +*/ +func (pt *imageTagTransformer) findAndReplaceTag(obj map[string]interface{}) error { + _, found := obj["containers"] + if found { + return pt.updateContainers(obj) + } + return pt.findContainers(obj) +} + +func (pt *imageTagTransformer) updateContainers(obj map[string]interface{}) error { + containers := obj["containers"].([]interface{}) + for i := range containers { + container := containers[i].(map[string]interface{}) + image, found := container["image"] + if !found { + continue + } + for _, imagetag := range pt.imageTags { + if isImageMatched(image.(string), imagetag.Name) { + container["image"] = strings.Join([]string{imagetag.Name, imagetag.NewTag}, ":") + break + } + } + containers[i] = container + } + obj["containers"] = containers + return nil +} + +func (pt *imageTagTransformer) findContainers(obj map[string]interface{}) error { + for key := range obj { + switch typedV := obj[key].(type) { + case map[string]interface{}: + err := pt.findAndReplaceTag(typedV) + if err != nil { + return err + } + case []interface{}: + for i := range typedV { + item := typedV[i] + typedItem, ok := item.(map[string]interface{}) + if ok { + err := pt.findAndReplaceTag(typedItem) + if err != nil { + return err + } + } + typedV[i] = typedItem + } + } + } + return nil +} + +func isImageMatched(s, t string) bool { + imagetag := strings.Split(s, ":") + if len(imagetag) >= 1 { + return imagetag[0] == t + } + return false +} diff --git a/pkg/transformers/imagetag_test.go b/pkg/transformers/imagetag_test.go new file mode 100644 index 000000000..f9723839a --- /dev/null +++ b/pkg/transformers/imagetag_test.go @@ -0,0 +1,170 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package transformers + +import ( + "reflect" + "testing" + + "github.com/kubernetes-sigs/kustomize/pkg/resmap" + "github.com/kubernetes-sigs/kustomize/pkg/resource" + "github.com/kubernetes-sigs/kustomize/pkg/types" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +func TestImageTagTransformer(t *testing.T) { + m := resmap.ResMap{ + resource.NewResId(deploy, "deploy1"): resource.NewResourceFromMap( + map[string]interface{}{ + "group": "apps", + "apiVersion": "v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "deploy1", + }, + "spec": map[string]interface{}{ + "template": map[string]interface{}{ + "spec": map[string]interface{}{ + "containers": []interface{}{ + map[string]interface{}{ + "name": "nginx", + "image": "nginx:1.7.9", + }, + map[string]interface{}{ + "name": "nginx2", + "image": "my-nginx:1.8.0", + }, + }, + }, + }, + }, + }), + resource.NewResId(schema.GroupVersionKind{Kind: "randomeKind"}, "random"): resource.NewResourceFromMap( + map[string]interface{}{ + "spec": map[string]interface{}{ + "template": map[string]interface{}{ + "spec": map[string]interface{}{ + "containers": []interface{}{ + map[string]interface{}{ + "name": "nginx1", + "image": "nginx", + }, + map[string]interface{}{ + "name": "nginx2", + "image": "my-nginx:random", + }, + }, + }, + }, + }, + "spec2": map[string]interface{}{ + "template": map[string]interface{}{ + "spec": map[string]interface{}{ + "containers": []interface{}{ + map[string]interface{}{ + "name": "ngin3", + "image": "nginx:v1", + }, + map[string]interface{}{ + "name": "nginx4", + "image": "my-nginx:latest", + }, + }, + }, + }, + }, + }), + } + expected := resmap.ResMap{ + resource.NewResId(deploy, "deploy1"): resource.NewResourceFromMap( + map[string]interface{}{ + "group": "apps", + "apiVersion": "v1", + "kind": "Deployment", + "metadata": map[string]interface{}{ + "name": "deploy1", + }, + "spec": map[string]interface{}{ + "template": map[string]interface{}{ + "spec": map[string]interface{}{ + "containers": []interface{}{ + map[string]interface{}{ + "name": "nginx", + "image": "nginx:v2", + }, + map[string]interface{}{ + "name": "nginx2", + "image": "my-nginx:previous", + }, + }, + }, + }, + }, + }), + resource.NewResId(schema.GroupVersionKind{Kind: "randomeKind"}, "random"): resource.NewResourceFromMap( + map[string]interface{}{ + "spec": map[string]interface{}{ + "template": map[string]interface{}{ + "spec": map[string]interface{}{ + "containers": []interface{}{ + map[string]interface{}{ + "name": "nginx1", + "image": "nginx:v2", + }, + map[string]interface{}{ + "name": "nginx2", + "image": "my-nginx:previous", + }, + }, + }, + }, + }, + "spec2": map[string]interface{}{ + "template": map[string]interface{}{ + "spec": map[string]interface{}{ + "containers": []interface{}{ + map[string]interface{}{ + "name": "ngin3", + "image": "nginx:v2", + }, + map[string]interface{}{ + "name": "nginx4", + "image": "my-nginx:previous", + }, + }, + }, + }, + }, + }), + } + + it, err := NewImageTagTransformer([]types.ImageTag{ + {Name: "nginx", NewTag: "v2"}, + {Name: "my-nginx", NewTag: "previous"}, + }) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + err = it.Transform(m) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !reflect.DeepEqual(m, expected) { + err = expected.ErrorIfNotEqual(m) + t.Fatalf("actual doesn't match expected: %v", err) + } +} From 4b6f180d0c4251bff91c7ae8541cc2befaa7351e Mon Sep 17 00:00:00 2001 From: Jingfang Liu Date: Fri, 20 Jul 2018 10:45:17 -0700 Subject: [PATCH 2/2] address comments --- pkg/transformers/imagetag.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/pkg/transformers/imagetag.go b/pkg/transformers/imagetag.go index d78ce2f5d..fcea93a95 100644 --- a/pkg/transformers/imagetag.go +++ b/pkg/transformers/imagetag.go @@ -76,9 +76,7 @@ func (pt *imageTagTransformer) updateContainers(obj map[string]interface{}) erro break } } - containers[i] = container } - obj["containers"] = containers return nil } @@ -100,7 +98,6 @@ func (pt *imageTagTransformer) findContainers(obj map[string]interface{}) error return err } } - typedV[i] = typedItem } } } @@ -109,8 +106,5 @@ func (pt *imageTagTransformer) findContainers(obj map[string]interface{}) error func isImageMatched(s, t string) bool { imagetag := strings.Split(s, ":") - if len(imagetag) >= 1 { - return imagetag[0] == t - } - return false + return len(imagetag) >= 1 && imagetag[0] == t }