From 8481b9fda3298d6eddb15e504ae9383ddc86ebd6 Mon Sep 17 00:00:00 2001 From: Ludovico Russo Date: Fri, 9 Apr 2021 22:17:18 +0200 Subject: [PATCH] handle deamonSet in InspectImages and FixImages --- cmd/clusterctl/internal/util/objs.go | 120 +++++++++++++++------- cmd/clusterctl/internal/util/objs_test.go | 67 ++++++++++++ 2 files changed, 148 insertions(+), 39 deletions(-) diff --git a/cmd/clusterctl/internal/util/objs.go b/cmd/clusterctl/internal/util/objs.go index ebcd464cc4c8..331244ae5b48 100644 --- a/cmd/clusterctl/internal/util/objs.go +++ b/cmd/clusterctl/internal/util/objs.go @@ -19,6 +19,7 @@ package util import ( "github.com/pkg/errors" appsv1 "k8s.io/api/apps/v1" + coreV1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" clusterctlv1 "sigs.k8s.io/cluster-api/cmd/clusterctl/api/v1alpha3" "sigs.k8s.io/cluster-api/cmd/clusterctl/internal/scheme" @@ -26,6 +27,7 @@ import ( const ( deploymentKind = "Deployment" + daemonSetKind = "DaemonSet" controllerContainerName = "manager" ) @@ -37,19 +39,32 @@ func InspectImages(objs []unstructured.Unstructured) ([]string, error) { for i := range objs { o := objs[i] - if o.GetKind() == deploymentKind { + + var podSpec coreV1.PodSpec + + switch o.GetKind() { + case deploymentKind: d := &appsv1.Deployment{} if err := scheme.Scheme.Convert(&o, d, nil); err != nil { return nil, err } - - for _, c := range d.Spec.Template.Spec.Containers { - images = append(images, c.Image) + podSpec = d.Spec.Template.Spec + case daemonSetKind: + d := &appsv1.DaemonSet{} + if err := scheme.Scheme.Convert(&o, d, nil); err != nil { + return nil, err } + podSpec = d.Spec.Template.Spec + default: + continue + } - for _, c := range d.Spec.Template.Spec.InitContainers { - images = append(images, c.Image) - } + for _, c := range podSpec.Containers { + images = append(images, c.Image) + } + + for _, c := range podSpec.InitContainers { + images = append(images, c.Image) } } @@ -102,45 +117,72 @@ func IsSharedResource(o unstructured.Unstructured) bool { // NB. The implemented approach is specific for the provider components YAML & for the cert-manager manifest; it is not // intended to cover all the possible objects used to deploy containers existing in Kubernetes. func FixImages(objs []unstructured.Unstructured, alterImageFunc func(image string) (string, error)) ([]unstructured.Unstructured, error) { - // look for resources of kind Deployment and alter the image for i := range objs { - o := &objs[i] - if o.GetKind() != deploymentKind { - continue + if err := fixDeploymentImages(&objs[i], alterImageFunc); err != nil { + return nil, err } - - // Convert Unstructured into a typed object - d := &appsv1.Deployment{} - if err := scheme.Scheme.Convert(o, d, nil); err != nil { + if err := fixDaemonSetImages(&objs[i], alterImageFunc); err != nil { return nil, err } + } + return objs, nil +} - // Alter the image - for j := range d.Spec.Template.Spec.Containers { - container := d.Spec.Template.Spec.Containers[j] - image, err := alterImageFunc(container.Image) - if err != nil { - return nil, errors.Wrapf(err, "failed to fix image for container %s in deployment %s", container.Name, d.Name) - } - container.Image = image - d.Spec.Template.Spec.Containers[j] = container - } +func fixDeploymentImages(o *unstructured.Unstructured, alterImageFunc func(image string) (string, error)) error { + if o.GetKind() != deploymentKind { + return nil + } - for j := range d.Spec.Template.Spec.InitContainers { - container := d.Spec.Template.Spec.InitContainers[j] - image, err := alterImageFunc(container.Image) - if err != nil { - return nil, errors.Wrapf(err, "failed to fix image for init container %s in deployment %s", container.Name, d.Name) - } - container.Image = image - d.Spec.Template.Spec.InitContainers[j] = container - } + // Convert Unstructured into a typed object + d := &appsv1.Deployment{} + if err := scheme.Scheme.Convert(o, d, nil); err != nil { + return err + } - // Convert typed object back to Unstructured - if err := scheme.Scheme.Convert(d, o, nil); err != nil { - return nil, err + if err := fixPodSpecImages(&d.Spec.Template.Spec, alterImageFunc); err != nil { + return errors.Wrapf(err, "failed to fix containers in deployment %s", d.Name) + } + + // Convert typed object back to Unstructured + return scheme.Scheme.Convert(d, o, nil) +} + +func fixDaemonSetImages(o *unstructured.Unstructured, alterImageFunc func(image string) (string, error)) error { + if o.GetKind() != daemonSetKind { + return nil + } + + // Convert Unstructured into a typed object + d := &appsv1.DaemonSet{} + if err := scheme.Scheme.Convert(o, d, nil); err != nil { + return err + } + + if err := fixPodSpecImages(&d.Spec.Template.Spec, alterImageFunc); err != nil { + return errors.Wrapf(err, "failed to fix containers in deamonSet %s", d.Name) + } + // Convert typed object back to Unstructured + return scheme.Scheme.Convert(d, o, nil) +} + +func fixPodSpecImages(podSpec *coreV1.PodSpec, alterImageFunc func(image string) (string, error)) error { + if err := fixContainersImage(podSpec.Containers, alterImageFunc); err != nil { + return errors.Wrapf(err, "failed to fix containers") + } + if err := fixContainersImage(podSpec.InitContainers, alterImageFunc); err != nil { + return errors.Wrapf(err, "failed to fix init containers") + } + return nil +} + +func fixContainersImage(containers []coreV1.Container, alterImageFunc func(image string) (string, error)) error { + for j := range containers { + container := &containers[j] + image, err := alterImageFunc(container.Image) + if err != nil { + return errors.Wrapf(err, "failed to fix image for container %s", container.Name) } - objs[i] = *o + container.Image = image } - return objs, nil + return nil } diff --git a/cmd/clusterctl/internal/util/objs_test.go b/cmd/clusterctl/internal/util/objs_test.go index 8e582c638562..2352f446418e 100644 --- a/cmd/clusterctl/internal/util/objs_test.go +++ b/cmd/clusterctl/internal/util/objs_test.go @@ -126,6 +126,39 @@ func Test_inspectImages(t *testing.T) { want: []string{"gcr.io/k8s-staging-cluster-api/cluster-api-controller:master", "gcr.io/k8s-staging-cluster-api/cluster-api-controller:init"}, wantErr: false, }, + { + name: "controller with deamonSet", + args: args{ + objs: []unstructured.Unstructured{ + { + Object: map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": daemonSetKind, + "spec": map[string]interface{}{ + "template": map[string]interface{}{ + "spec": map[string]interface{}{ + "containers": []map[string]interface{}{ + { + "name": controllerContainerName, + "image": "gcr.io/k8s-staging-cluster-api/cluster-api-controller:master", + }, + }, + "initContainers": []map[string]interface{}{ + { + "name": controllerContainerName, + "image": "gcr.io/k8s-staging-cluster-api/cluster-api-controller:init", + }, + }, + }, + }, + }, + }, + }, + }, + }, + want: []string{"gcr.io/k8s-staging-cluster-api/cluster-api-controller:master", "gcr.io/k8s-staging-cluster-api/cluster-api-controller:init"}, + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -188,6 +221,40 @@ func TestFixImages(t *testing.T) { want: []string{"foo-container-image", "foo-init-container-image"}, wantErr: false, }, + { + name: "fix daemonSet containers images", + args: args{ + objs: []unstructured.Unstructured{ + { + Object: map[string]interface{}{ + "apiVersion": "apps/v1", + "kind": daemonSetKind, + "spec": map[string]interface{}{ + "template": map[string]interface{}{ + "spec": map[string]interface{}{ + "containers": []map[string]interface{}{ + { + "image": "container-image", + }, + }, + "initContainers": []map[string]interface{}{ + { + "image": "init-container-image", + }, + }, + }, + }, + }, + }, + }, + }, + alterImageFunc: func(image string) (string, error) { + return fmt.Sprintf("foo-%s", image), nil + }, + }, + want: []string{"foo-container-image", "foo-init-container-image"}, + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {