github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/controller/operators/olm/overrides/inject/inject.go (about)

     1  package inject
     2  
     3  import (
     4  	"errors"
     5  	"reflect"
     6  
     7  	appsv1 "k8s.io/api/apps/v1"
     8  	corev1 "k8s.io/api/core/v1"
     9  )
    10  
    11  // InjectEnvIntoDeployment injects the proxy env variables specified in
    12  // proxyEnvVar into the container(s) of the given PodSpec.
    13  //
    14  // If any Container in PodSpec already defines an env variable of the same name
    15  // as any of the proxy env variables then it will be overwritten.
    16  func InjectEnvIntoDeployment(podSpec *corev1.PodSpec, envVars []corev1.EnvVar) error {
    17  	if podSpec == nil {
    18  		return errors.New("no pod spec provided")
    19  	}
    20  
    21  	for i := range podSpec.Containers {
    22  		container := &podSpec.Containers[i]
    23  		container.Env = mergeEnvVars(container.Env, envVars)
    24  	}
    25  
    26  	return nil
    27  }
    28  
    29  func mergeEnvVars(containerEnvVars []corev1.EnvVar, newEnvVars []corev1.EnvVar) []corev1.EnvVar {
    30  	// Build a map of environment variables.
    31  	// newEnvVars always overrides containerEnvVars.
    32  	mergedMap := map[string]corev1.EnvVar{}
    33  	for _, envVar := range containerEnvVars {
    34  		mergedMap[envVar.Name] = envVar
    35  	}
    36  	for _, envVar := range newEnvVars {
    37  		mergedMap[envVar.Name] = envVar
    38  	}
    39  
    40  	// To keep things in the expected order, always put the
    41  	// original environment variable names into the merged
    42  	// output in place first.
    43  	merged := make([]corev1.EnvVar, 0, len(mergedMap))
    44  	for _, e := range containerEnvVars {
    45  		envVar := mergedMap[e.Name]
    46  		merged = append(merged, envVar)
    47  		delete(mergedMap, e.Name)
    48  	}
    49  
    50  	// Then for any remaining newEnvVars (i.e. env vars
    51  	// that weren't present in the containerEnvVars), add
    52  	// them at the end in the order they were provided in
    53  	// the subscription.
    54  	for _, e := range newEnvVars {
    55  		envVar, ok := mergedMap[e.Name]
    56  		if !ok {
    57  			continue
    58  		}
    59  		merged = append(merged, envVar)
    60  	}
    61  
    62  	return merged
    63  }
    64  
    65  // InjectEnvFromIntoDeployment injects the envFrom variables
    66  // into the container(s) of the given PodSpec.
    67  //
    68  // If any Container in PodSpec already defines an envFrom variable
    69  // as any of the provided envFrom then it will be overwritten.
    70  func InjectEnvFromIntoDeployment(podSpec *corev1.PodSpec, envFromVars []corev1.EnvFromSource) error {
    71  	if podSpec == nil {
    72  		return errors.New("no pod spec provided")
    73  	}
    74  
    75  	for i := range podSpec.Containers {
    76  		container := &podSpec.Containers[i]
    77  		container.EnvFrom = mergeEnvFromVars(container.EnvFrom, envFromVars)
    78  	}
    79  
    80  	return nil
    81  }
    82  
    83  func mergeEnvFromVars(containerEnvFromVars []corev1.EnvFromSource, newEnvFromVars []corev1.EnvFromSource) []corev1.EnvFromSource {
    84  	merged := containerEnvFromVars
    85  
    86  	for _, newEnvFromVar := range newEnvFromVars {
    87  		if !findEnvFromVar(containerEnvFromVars, newEnvFromVar) {
    88  			merged = append(merged, newEnvFromVar)
    89  		}
    90  	}
    91  
    92  	return merged
    93  }
    94  
    95  func findEnvFromVar(envFromVar []corev1.EnvFromSource, newEnvFromVar corev1.EnvFromSource) bool {
    96  	for i := range envFromVar {
    97  		if reflect.DeepEqual(envFromVar[i], newEnvFromVar) {
    98  			return true
    99  		}
   100  	}
   101  	return false
   102  }
   103  
   104  // InjectVolumesIntoDeployment injects the provided Volumes
   105  // into the container(s) of the given PodSpec.
   106  //
   107  // If any Container in PodSpec already defines a Volume of the same name
   108  // as any of the provided Volumes then it will be overwritten.
   109  func InjectVolumesIntoDeployment(podSpec *corev1.PodSpec, volumes []corev1.Volume) error {
   110  	if podSpec == nil {
   111  		return errors.New("no pod spec provided")
   112  	}
   113  
   114  	podSpec.Volumes = mergeVolumes(podSpec.Volumes, volumes)
   115  
   116  	return nil
   117  }
   118  
   119  func mergeVolumes(podSpecVolumes []corev1.Volume, newVolumes []corev1.Volume) (merged []corev1.Volume) {
   120  	merged = podSpecVolumes
   121  
   122  	for _, newVolume := range newVolumes {
   123  		existing, found := findVolume(podSpecVolumes, newVolume.Name)
   124  		if found {
   125  			*existing = newVolume
   126  			continue
   127  		}
   128  
   129  		merged = append(merged, newVolume)
   130  	}
   131  
   132  	return
   133  }
   134  
   135  func findVolume(volumes []corev1.Volume, name string) (foundVolume *corev1.Volume, found bool) {
   136  	for i := range volumes {
   137  		if name == volumes[i].Name {
   138  			// Environment variable names are case sensitive.
   139  			found = true
   140  			foundVolume = &volumes[i]
   141  
   142  			break
   143  		}
   144  	}
   145  
   146  	return
   147  }
   148  
   149  // InjectVolumeMountsIntoDeployment injects the provided VolumeMounts
   150  // into the given PodSpec.
   151  //
   152  // If the PodSpec already defines a VolumeMount of the same name
   153  // as any of the provided VolumeMounts then it will be overwritten.
   154  func InjectVolumeMountsIntoDeployment(podSpec *corev1.PodSpec, volumeMounts []corev1.VolumeMount) error {
   155  	if podSpec == nil {
   156  		return errors.New("no pod spec provided")
   157  	}
   158  
   159  	for i := range podSpec.Containers {
   160  		container := &podSpec.Containers[i]
   161  		container.VolumeMounts = mergeVolumeMounts(container.VolumeMounts, volumeMounts)
   162  	}
   163  
   164  	return nil
   165  }
   166  
   167  func mergeVolumeMounts(containerVolumeMounts []corev1.VolumeMount, newVolumeMounts []corev1.VolumeMount) (merged []corev1.VolumeMount) {
   168  	merged = containerVolumeMounts
   169  
   170  	for _, newVolumeMount := range newVolumeMounts {
   171  		existing, found := findVolumeMount(containerVolumeMounts, newVolumeMount.Name)
   172  		if found {
   173  			*existing = newVolumeMount
   174  			continue
   175  		}
   176  
   177  		merged = append(merged, newVolumeMount)
   178  	}
   179  
   180  	return
   181  }
   182  
   183  func findVolumeMount(volumeMounts []corev1.VolumeMount, name string) (foundVolumeMount *corev1.VolumeMount, found bool) {
   184  	for i := range volumeMounts {
   185  		if name == volumeMounts[i].Name {
   186  			// Environment variable names are case sensitive.
   187  			found = true
   188  			foundVolumeMount = &volumeMounts[i]
   189  
   190  			break
   191  		}
   192  	}
   193  
   194  	return
   195  }
   196  
   197  // InjectTolerationsIntoDeployment injects provided Tolerations
   198  // into the given Pod Spec
   199  //
   200  // Tolerations will be appended to the existing once if it
   201  // does not already exist
   202  func InjectTolerationsIntoDeployment(podSpec *corev1.PodSpec, tolerations []corev1.Toleration) error {
   203  	if podSpec == nil {
   204  		return errors.New("no pod spec provided")
   205  	}
   206  
   207  	podSpec.Tolerations = mergeTolerations(podSpec.Tolerations, tolerations)
   208  	return nil
   209  }
   210  
   211  func mergeTolerations(podTolerations []corev1.Toleration, newTolerations []corev1.Toleration) (mergedTolerations []corev1.Toleration) {
   212  	mergedTolerations = podTolerations
   213  	for _, newToleration := range newTolerations {
   214  		_, found := findToleration(podTolerations, newToleration)
   215  		if !found {
   216  			mergedTolerations = append(mergedTolerations, newToleration)
   217  		}
   218  	}
   219  
   220  	return
   221  }
   222  
   223  func findToleration(tolerations []corev1.Toleration, toleration corev1.Toleration) (foundToleration *corev1.Toleration, found bool) {
   224  	for i := range tolerations {
   225  		if reflect.DeepEqual(toleration, tolerations[i]) {
   226  			found = true
   227  			foundToleration = &toleration
   228  
   229  			break
   230  		}
   231  	}
   232  
   233  	return
   234  }
   235  
   236  // InjectResourcesIntoDeployment will inject provided Resources
   237  // into given podSpec
   238  //
   239  // If podSpec already defines Resources, it will be overwritten
   240  func InjectResourcesIntoDeployment(podSpec *corev1.PodSpec, resources *corev1.ResourceRequirements) error {
   241  	if podSpec == nil {
   242  		return errors.New("no pod spec provided")
   243  	}
   244  
   245  	if resources == nil {
   246  		return nil
   247  	}
   248  
   249  	for i := range podSpec.Containers {
   250  		container := &podSpec.Containers[i]
   251  		container.Resources = *resources
   252  	}
   253  
   254  	return nil
   255  }
   256  
   257  // InjectNodeSelectorIntoDeployment injects the provided NodeSelector
   258  // into the container(s) of the given PodSpec.
   259  //
   260  // If the PodSpec already defines a NodeSelector it will be overwritten.
   261  func InjectNodeSelectorIntoDeployment(podSpec *corev1.PodSpec, nodeSelector map[string]string) error {
   262  	if podSpec == nil {
   263  		return errors.New("no pod spec provided")
   264  	}
   265  
   266  	if nodeSelector != nil {
   267  		podSpec.NodeSelector = nodeSelector
   268  	}
   269  
   270  	return nil
   271  }
   272  
   273  // OverrideDeploymentAffinity will override the corev1.Affinity defined in the Deployment
   274  // with the given corev1.Affinity. Any nil top-level sub-attributes (e.g. NodeAffinity, PodAffinity, and PodAntiAffinity)
   275  // will be ignored. Hint: to overwrite those top-level attributes, empty them out. I.e. use the empty/default object ({})
   276  // e.g. NodeAffinity{}. In yaml:
   277  //
   278  //	affinity:
   279  //	  nodeAffinity: {}
   280  //	  podAffinity: {}
   281  //	  podAntiAffinity: {}
   282  //
   283  // will completely remove the deployment podSpec.affinity and is equivalent to
   284  // affinity: {}
   285  func OverrideDeploymentAffinity(podSpec *corev1.PodSpec, affinity *corev1.Affinity) error {
   286  	if podSpec == nil {
   287  		return errors.New("no pod spec provided")
   288  	}
   289  
   290  	if affinity == nil {
   291  		return nil
   292  	}
   293  
   294  	// if podSpec.Affinity is nil or empty/default then completely override podSpec.Affinity with overrides
   295  	if podSpec.Affinity == nil || reflect.DeepEqual(podSpec.Affinity, &corev1.Affinity{}) {
   296  		if reflect.DeepEqual(affinity, &corev1.Affinity{}) {
   297  			podSpec.Affinity = nil
   298  		} else {
   299  			podSpec.Affinity = affinity
   300  		}
   301  		return nil
   302  	}
   303  
   304  	// if overriding affinity is empty/default then nil out podSpec.Affinity
   305  	if reflect.DeepEqual(affinity, &corev1.Affinity{}) {
   306  		podSpec.Affinity = nil
   307  		return nil
   308  	}
   309  
   310  	// override podSpec.Affinity each attribute as necessary nilling out any default/empty overrides on the podSpec
   311  	if affinity.NodeAffinity != nil {
   312  		if reflect.DeepEqual(affinity.NodeAffinity, &corev1.NodeAffinity{}) {
   313  			podSpec.Affinity.NodeAffinity = nil
   314  		} else {
   315  			podSpec.Affinity.NodeAffinity = affinity.NodeAffinity
   316  		}
   317  	}
   318  
   319  	if affinity.PodAffinity != nil {
   320  		if reflect.DeepEqual(affinity.PodAffinity, &corev1.PodAffinity{}) {
   321  			podSpec.Affinity.PodAffinity = nil
   322  		} else {
   323  			podSpec.Affinity.PodAffinity = affinity.PodAffinity
   324  		}
   325  	}
   326  
   327  	if affinity.PodAntiAffinity != nil {
   328  		if reflect.DeepEqual(affinity.PodAntiAffinity, &corev1.PodAntiAffinity{}) {
   329  			podSpec.Affinity = nil
   330  		} else {
   331  			podSpec.Affinity.PodAntiAffinity = affinity.PodAntiAffinity
   332  		}
   333  	}
   334  
   335  	// special case: if after being overridden, podSpec is the same as default/empty then nil it out
   336  	if reflect.DeepEqual(&corev1.Affinity{}, podSpec.Affinity) {
   337  		podSpec.Affinity = nil
   338  	}
   339  
   340  	return nil
   341  }
   342  
   343  // InjectAnnotationsIntoDeployment injects the provided Annotations
   344  // into the container(s) of the given PodSpec.
   345  //
   346  // If the Deployment already defines any Annotations they will NOT be overwritten.
   347  func InjectAnnotationsIntoDeployment(deployment *appsv1.Deployment, newAnnotations map[string]string) error {
   348  	if deployment == nil {
   349  		return errors.New("no deployment provided")
   350  	}
   351  
   352  	// do not override existing annotations
   353  	if newAnnotations != nil {
   354  		mergedDeploymentAnnotations := map[string]string{}
   355  		mergedPodAnnotations := map[string]string{}
   356  
   357  		// add newAnnotations first to prevent them from overwriting the defaults
   358  		for k, v := range newAnnotations {
   359  			mergedDeploymentAnnotations[k] = v
   360  			mergedPodAnnotations[k] = v
   361  		}
   362  
   363  		// then replace any duplicate annotations with the default annotation
   364  		for k, v := range deployment.Annotations {
   365  			mergedDeploymentAnnotations[k] = v
   366  		}
   367  		for k, v := range deployment.Spec.Template.GetAnnotations() {
   368  			mergedPodAnnotations[k] = v
   369  		}
   370  
   371  		// Inject Into Deployment
   372  		deployment.SetAnnotations(mergedDeploymentAnnotations)
   373  		// Inject Into Pod Spec
   374  		deployment.Spec.Template.SetAnnotations(mergedPodAnnotations)
   375  	}
   376  
   377  	return nil
   378  }