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  }