sigs.k8s.io/cluster-api@v1.7.1/cmd/clusterctl/internal/util/objs.go (about) 1 /* 2 Copyright 2020 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 util 18 19 import ( 20 "github.com/pkg/errors" 21 appsv1 "k8s.io/api/apps/v1" 22 corev1 "k8s.io/api/core/v1" 23 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 24 "k8s.io/apimachinery/pkg/runtime" 25 26 "sigs.k8s.io/cluster-api/cmd/clusterctl/internal/scheme" 27 ) 28 29 const ( 30 deploymentKind = "Deployment" 31 daemonSetKind = "DaemonSet" 32 controllerContainerName = "manager" 33 ) 34 35 // InspectImages identifies the container images required to install the objects defined in the objs. 36 // NB. The implemented approach is specific for the provider components YAML & for the cert-manager manifest; it is not 37 // intended to cover all the possible objects used to deploy containers existing in Kubernetes. 38 func InspectImages(objs []unstructured.Unstructured) ([]string, error) { 39 images := []string{} 40 41 for i := range objs { 42 o := objs[i] 43 44 var podSpec corev1.PodSpec 45 46 switch o.GetKind() { 47 case deploymentKind: 48 d := &appsv1.Deployment{} 49 if err := scheme.Scheme.Convert(&o, d, nil); err != nil { 50 return nil, err 51 } 52 podSpec = d.Spec.Template.Spec 53 case daemonSetKind: 54 d := &appsv1.DaemonSet{} 55 if err := scheme.Scheme.Convert(&o, d, nil); err != nil { 56 return nil, err 57 } 58 podSpec = d.Spec.Template.Spec 59 default: 60 continue 61 } 62 63 for _, c := range podSpec.Containers { 64 images = append(images, c.Image) 65 } 66 67 for _, c := range podSpec.InitContainers { 68 images = append(images, c.Image) 69 } 70 } 71 72 return images, nil 73 } 74 75 // IsClusterResource returns true if the resource kind is cluster wide (not namespaced). 76 func IsClusterResource(kind string) bool { 77 return !IsResourceNamespaced(kind) 78 } 79 80 // IsResourceNamespaced returns true if the resource kind is namespaced. 81 func IsResourceNamespaced(kind string) bool { 82 switch kind { 83 case "Namespace", 84 "Node", 85 "PersistentVolume", 86 "PodSecurityPolicy", 87 "CertificateSigningRequest", 88 "ClusterRoleBinding", 89 "ClusterRole", 90 "VolumeAttachment", 91 "StorageClass", 92 "CSIDriver", 93 "CSINode", 94 "ValidatingWebhookConfiguration", 95 "MutatingWebhookConfiguration", 96 "CustomResourceDefinition", 97 "PriorityClass", 98 "RuntimeClass": 99 return false 100 default: 101 return true 102 } 103 } 104 105 // FixImages alters images using the give alter func 106 // NB. The implemented approach is specific for the provider components YAML & for the cert-manager manifest; it is not 107 // intended to cover all the possible objects used to deploy containers existing in Kubernetes. 108 func FixImages(objs []unstructured.Unstructured, alterImageFunc func(image string) (string, error)) ([]unstructured.Unstructured, error) { 109 for i := range objs { 110 if err := fixDeploymentImages(&objs[i], alterImageFunc); err != nil { 111 return nil, err 112 } 113 if err := fixDaemonSetImages(&objs[i], alterImageFunc); err != nil { 114 return nil, err 115 } 116 } 117 return objs, nil 118 } 119 120 func fixDeploymentImages(o *unstructured.Unstructured, alterImageFunc func(image string) (string, error)) error { 121 if o.GetKind() != deploymentKind { 122 return nil 123 } 124 125 // Convert Unstructured into a typed object 126 d := &appsv1.Deployment{} 127 if err := scheme.Scheme.Convert(o, d, nil); err != nil { 128 return err 129 } 130 131 if err := fixPodSpecImages(&d.Spec.Template.Spec, alterImageFunc); err != nil { 132 return errors.Wrapf(err, "failed to fix containers in deployment %s", d.Name) 133 } 134 135 // Convert typed object back to Unstructured 136 return scheme.Scheme.Convert(d, o, nil) 137 } 138 139 func fixDaemonSetImages(o *unstructured.Unstructured, alterImageFunc func(image string) (string, error)) error { 140 if o.GetKind() != daemonSetKind { 141 return nil 142 } 143 144 // Convert Unstructured into a typed object 145 d := &appsv1.DaemonSet{} 146 if err := scheme.Scheme.Convert(o, d, nil); err != nil { 147 return err 148 } 149 150 if err := fixPodSpecImages(&d.Spec.Template.Spec, alterImageFunc); err != nil { 151 return errors.Wrapf(err, "failed to fix containers in deamonSet %s", d.Name) 152 } 153 // Convert typed object back to Unstructured 154 return scheme.Scheme.Convert(d, o, nil) 155 } 156 157 func fixPodSpecImages(podSpec *corev1.PodSpec, alterImageFunc func(image string) (string, error)) error { 158 if err := fixContainersImage(podSpec.Containers, alterImageFunc); err != nil { 159 return errors.Wrapf(err, "failed to fix containers") 160 } 161 if err := fixContainersImage(podSpec.InitContainers, alterImageFunc); err != nil { 162 return errors.Wrapf(err, "failed to fix init containers") 163 } 164 return nil 165 } 166 167 func fixContainersImage(containers []corev1.Container, alterImageFunc func(image string) (string, error)) error { 168 for j := range containers { 169 container := &containers[j] 170 image, err := alterImageFunc(container.Image) 171 if err != nil { 172 return errors.Wrapf(err, "failed to fix image for container %s", container.Name) 173 } 174 container.Image = image 175 } 176 return nil 177 } 178 179 // IsDeploymentWithManager return true if obj is a deployment containing a pod with at least one container named 'manager', 180 // that according to the clusterctl contract, identifies the provider's controller. 181 func IsDeploymentWithManager(obj unstructured.Unstructured) bool { 182 if obj.GroupVersionKind().Kind == deploymentKind { 183 var dep appsv1.Deployment 184 if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), &dep); err != nil { 185 return false 186 } 187 for _, c := range dep.Spec.Template.Spec.Containers { 188 if c.Name == controllerContainerName { 189 return true 190 } 191 } 192 } 193 return false 194 }