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 }