github.com/banzaicloud/operator-tools@v0.28.10/pkg/types/base_types.go (about) 1 // Copyright © 2020 Banzai Cloud 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package types 16 17 import ( 18 appsv1 "k8s.io/api/apps/v1" 19 corev1 "k8s.io/api/core/v1" 20 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 22 "github.com/banzaicloud/operator-tools/pkg/utils" 23 ) 24 25 const ( 26 NameLabel = "app.kubernetes.io/name" 27 InstanceLabel = "app.kubernetes.io/instance" 28 VersionLabel = "app.kubernetes.io/version" 29 ComponentLabel = "app.kubernetes.io/component" 30 ManagedByLabel = "app.kubernetes.io/managed-by" 31 32 BanzaiCloudManagedComponent = "banzaicloud.io/managed-component" 33 BanzaiCloudOwnedBy = "banzaicloud.io/owned-by" 34 BanzaiCloudRelatedTo = "banzaicloud.io/related-to" 35 BanzaiCloudDesiredStateCreated = "banzaicloud.io/desired-state-created" 36 ) 37 38 type ObjectKey struct { 39 Name string `json:"name,omitempty"` 40 Namespace string `json:"namespace,omitempty"` 41 } 42 43 type ReconcileStatus string 44 45 const ( 46 // Used for components and for aggregated status 47 ReconcileStatusFailed ReconcileStatus = "Failed" 48 49 // Used for components and for aggregated status 50 ReconcileStatusReconciling ReconcileStatus = "Reconciling" 51 52 // Used for components 53 ReconcileStatusAvailable ReconcileStatus = "Available" 54 ReconcileStatusUnmanaged ReconcileStatus = "Unmanaged" 55 ReconcileStatusRemoved ReconcileStatus = "Removed" 56 57 // Used for aggregated status if all the components are stableized (Available, Unmanaged or Removed) 58 ReconcileStatusSucceeded ReconcileStatus = "Succeeded" 59 60 // Used to trigger reconciliation for a resource that otherwise ignores status changes, but listens to the Pending state 61 // See PendingStatusPredicate in pkg/reconciler 62 ReconcileStatusPending ReconcileStatus = "Pending" 63 ) 64 65 func (s ReconcileStatus) Stable() bool { 66 return s == ReconcileStatusUnmanaged || s == ReconcileStatusRemoved || s == ReconcileStatusAvailable 67 } 68 69 func (s ReconcileStatus) Available() bool { 70 return s == ReconcileStatusAvailable || s == ReconcileStatusSucceeded 71 } 72 73 func (s ReconcileStatus) Failed() bool { 74 return s == ReconcileStatusFailed 75 } 76 77 func (s ReconcileStatus) Pending() bool { 78 return s == ReconcileStatusReconciling || s == ReconcileStatusPending 79 } 80 81 // Computes an aggregated state based on component statuses 82 func AggregatedState(componentStatuses []ReconcileStatus) ReconcileStatus { 83 overallStatus := ReconcileStatusReconciling 84 statusMap := make(map[ReconcileStatus]bool) 85 hasUnstable := false 86 for _, cs := range componentStatuses { 87 if cs != "" { 88 statusMap[cs] = true 89 } 90 if !(cs == "" || cs.Stable()) { 91 hasUnstable = true 92 } 93 } 94 95 if statusMap[ReconcileStatusFailed] { 96 overallStatus = ReconcileStatusFailed 97 } else if statusMap[ReconcileStatusReconciling] { 98 overallStatus = ReconcileStatusReconciling 99 } 100 101 if !hasUnstable { 102 overallStatus = ReconcileStatusSucceeded 103 } 104 return overallStatus 105 } 106 107 // +kubebuilder:object:generate=true 108 109 // EnabledComponent implements the "enabled component" pattern 110 // Embed this type into other component types to avoid unnecessary code duplication 111 // NOTE: Don't forget to annotate the embedded field with `json:",inline"` tag for controller-gen 112 type EnabledComponent struct { 113 Enabled *bool `json:"enabled,omitempty"` 114 } 115 116 // IsDisabled returns true iff the component is explicitly disabled 117 func (ec EnabledComponent) IsDisabled() bool { 118 return ec.Enabled != nil && !*ec.Enabled 119 } 120 121 // IsEnabled returns true iff the component is explicitly enabled 122 func (ec EnabledComponent) IsEnabled() bool { 123 return utils.PointerToBool(ec.Enabled) 124 } 125 126 // IsSkipped returns true iff the component is neither enabled nor disabled explicitly 127 func (ec EnabledComponent) IsSkipped() bool { 128 return ec.Enabled == nil 129 } 130 131 // +kubebuilder:object:generate=true 132 133 // Deprecated 134 // Consider using ObjectMeta in the typeoverrides package combined with the merge package 135 type MetaBase struct { 136 Annotations map[string]string `json:"annotations,omitempty"` 137 Labels map[string]string `json:"labels,omitempty"` 138 } 139 140 func (base *MetaBase) Merge(meta metav1.ObjectMeta) metav1.ObjectMeta { 141 if base == nil { 142 return meta 143 } 144 if len(base.Annotations) > 0 { 145 if meta.Annotations == nil { 146 meta.Annotations = make(map[string]string) 147 } 148 for key, val := range base.Annotations { 149 meta.Annotations[key] = val 150 } 151 } 152 if len(base.Labels) > 0 { 153 if meta.Labels == nil { 154 meta.Labels = make(map[string]string) 155 } 156 for key, val := range base.Labels { 157 meta.Labels[key] = val 158 } 159 } 160 return meta 161 } 162 163 // +kubebuilder:object:generate=true 164 165 // Deprecated 166 // Consider using PodTemplateSpec in the typeoverrides package combined with the merge package 167 type PodTemplateBase struct { 168 Metadata *MetaBase `json:"metadata,omitempty"` 169 PodSpec *PodSpecBase `json:"spec,omitempty"` 170 } 171 172 func (base *PodTemplateBase) Override(template corev1.PodTemplateSpec) corev1.PodTemplateSpec { 173 if base == nil { 174 return template 175 } 176 if base.Metadata != nil { 177 template.ObjectMeta = base.Metadata.Merge(template.ObjectMeta) 178 } 179 if base.PodSpec != nil { 180 template.Spec = base.PodSpec.Override(template.Spec) 181 } 182 return template 183 } 184 185 // +kubebuilder:object:generate=true 186 187 // Deprecated 188 // Consider using Container in the typeoverrides package combined with the merge package 189 type ContainerBase struct { 190 Name string `json:"name,omitempty"` 191 Resources *corev1.ResourceRequirements `json:"resources,omitempty"` 192 Image string `json:"image,omitempty"` 193 PullPolicy corev1.PullPolicy `json:"pullPolicy,omitempty"` 194 Command []string `json:"command,omitempty"` 195 VolumeMounts []corev1.VolumeMount `json:"volumeMounts,omitempty" patchStrategy:"merge" patchMergeKey:"mountPath" ` 196 SecurityContext *corev1.SecurityContext `json:"securityContext,omitempty"` 197 LivenessProbe *corev1.Probe `json:"livenessProbe,omitempty"` 198 ReadinessProbe *corev1.Probe `json:"readinessProbe,omitempty"` 199 } 200 201 func (base *ContainerBase) Override(container corev1.Container) corev1.Container { 202 if base == nil { 203 return container 204 } 205 if base.Resources != nil { 206 container.Resources = *base.Resources 207 } 208 if base.Image != "" { 209 container.Image = base.Image 210 } 211 if base.PullPolicy != "" { 212 container.ImagePullPolicy = base.PullPolicy 213 } 214 if len(base.Command) > 0 { 215 container.Command = base.Command 216 } 217 if len(base.VolumeMounts) > 0 { 218 container.VolumeMounts = base.VolumeMounts 219 } 220 if base.SecurityContext != nil { 221 container.SecurityContext = base.SecurityContext 222 } 223 if base.LivenessProbe != nil { 224 container.LivenessProbe = base.LivenessProbe 225 } 226 if base.ReadinessProbe != nil { 227 container.LivenessProbe = base.LivenessProbe 228 } 229 return container 230 } 231 232 // +kubebuilder:object:generate=true 233 234 // Deprecated 235 // Consider using PodSpec in the typeoverrides package combined with the merge package 236 type PodSpecBase struct { 237 Tolerations []corev1.Toleration `json:"tolerations,omitempty"` 238 NodeSelector map[string]string `json:"nodeSelector,omitempty"` 239 ServiceAccountName string `json:"serviceAccountName,omitempty"` 240 Affinity *corev1.Affinity `json:"affinity,omitempty"` 241 SecurityContext *corev1.PodSecurityContext `json:"securityContext,omitempty"` 242 Volumes []corev1.Volume `json:"volumes,omitempty" patchStrategy:"merge" patchMergeKey:"name"` 243 PriorityClassName string `json:"priorityClassName,omitempty"` 244 Containers []ContainerBase `json:"containers,omitempty" patchStrategy:"merge" patchMergeKey:"name"` 245 InitContainers []ContainerBase `json:"initContainers,omitempty" patchStrategy:"merge" patchMergeKey:"name"` 246 ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` 247 } 248 249 func (base *PodSpecBase) Override(spec corev1.PodSpec) corev1.PodSpec { 250 if base == nil { 251 return spec 252 } 253 if base.SecurityContext != nil { 254 spec.SecurityContext = base.SecurityContext 255 } 256 if base.Tolerations != nil { 257 spec.Tolerations = base.Tolerations 258 } 259 if base.NodeSelector != nil { 260 spec.NodeSelector = base.NodeSelector 261 } 262 if base.ServiceAccountName != "" { 263 spec.ServiceAccountName = base.ServiceAccountName 264 } 265 if base.Affinity != nil { 266 spec.Affinity = base.Affinity 267 } 268 if len(base.Volumes) > 0 { 269 spec.Volumes = base.Volumes 270 } 271 if base.PriorityClassName != "" { 272 spec.PriorityClassName = base.PriorityClassName 273 } 274 if len(base.Containers) > 0 { 275 for _, baseContainer := range base.Containers { 276 for o, originalContainer := range spec.Containers { 277 if baseContainer.Name == originalContainer.Name { 278 spec.Containers[o] = baseContainer.Override(originalContainer) 279 break 280 } 281 } 282 } 283 } 284 if len(base.InitContainers) > 0 { 285 for _, baseContainer := range base.InitContainers { 286 for o, originalContainer := range spec.InitContainers { 287 if baseContainer.Name == originalContainer.Name { 288 spec.InitContainers[o] = baseContainer.Override(originalContainer) 289 break 290 } 291 } 292 } 293 } 294 295 spec.ImagePullSecrets = append(([]corev1.LocalObjectReference)(nil), spec.ImagePullSecrets...) 296 spec.ImagePullSecrets = append(spec.ImagePullSecrets, base.ImagePullSecrets...) 297 298 return spec 299 } 300 301 // +kubebuilder:object:generate=true 302 303 // Deprecated 304 // Consider using Deployment in the typeoverrides package combined with the merge package 305 type DeploymentBase struct { 306 *MetaBase `json:",inline"` 307 Spec *DeploymentSpecBase `json:"spec,omitempty"` 308 } 309 310 func (base *DeploymentBase) Override(deployment appsv1.Deployment) appsv1.Deployment { 311 if base == nil { 312 return deployment 313 } 314 if base.MetaBase != nil { 315 deployment.ObjectMeta = base.MetaBase.Merge(deployment.ObjectMeta) 316 } 317 if base.Spec != nil { 318 deployment.Spec = base.Spec.Override(deployment.Spec) 319 } 320 return deployment 321 } 322 323 // +kubebuilder:object:generate=true 324 325 // Deprecated 326 // Consider using DeploymentSpec in the typeoverrides package combined with the merge package 327 type DeploymentSpecBase struct { 328 Replicas *int32 `json:"replicas,omitempty"` 329 Selector *metav1.LabelSelector `json:"selector,omitempty"` 330 Strategy *appsv1.DeploymentStrategy `json:"strategy,omitempty"` 331 Template *PodTemplateBase `json:"template,omitempty"` 332 } 333 334 func (base *DeploymentSpecBase) Override(spec appsv1.DeploymentSpec) appsv1.DeploymentSpec { 335 if base == nil { 336 return spec 337 } 338 if base.Replicas != nil { 339 spec.Replicas = base.Replicas 340 } 341 spec.Selector = mergeSelectors(base.Selector, spec.Selector) 342 if base.Strategy != nil { 343 spec.Strategy = *base.Strategy 344 } 345 if base.Template != nil { 346 spec.Template = base.Template.Override(spec.Template) 347 } 348 return spec 349 } 350 351 // +kubebuilder:object:generate=true 352 353 // Deprecated 354 // Consider using StatefulSet in the typeoverrides package combined with the merge package 355 type StatefulSetBase struct { 356 *MetaBase `json:",inline"` 357 Spec *StatefulsetSpecBase `json:"spec,omitempty"` 358 } 359 360 func (base *StatefulSetBase) Override(statefulSet appsv1.StatefulSet) appsv1.StatefulSet { 361 if base == nil { 362 return statefulSet 363 } 364 if base.MetaBase != nil { 365 statefulSet.ObjectMeta = base.MetaBase.Merge(statefulSet.ObjectMeta) 366 } 367 if base.Spec != nil { 368 statefulSet.Spec = base.Spec.Override(statefulSet.Spec) 369 } 370 return statefulSet 371 } 372 373 // +kubebuilder:object:generate=true 374 375 // Deprecated 376 // Consider using StatefulSetSpec in the typeoverrides package combined with the merge package 377 type StatefulsetSpecBase struct { 378 Replicas *int32 `json:"replicas,omitempty"` 379 Selector *metav1.LabelSelector `json:"selector,omitempty"` 380 PodManagementPolicy appsv1.PodManagementPolicyType `json:"podManagementPolicy,omitempty"` 381 UpdateStrategy *appsv1.StatefulSetUpdateStrategy `json:"updateStrategy,omitempty"` 382 Template *PodTemplateBase `json:"template,omitempty"` 383 } 384 385 func (base *StatefulsetSpecBase) Override(spec appsv1.StatefulSetSpec) appsv1.StatefulSetSpec { 386 if base == nil { 387 return spec 388 } 389 if base.Replicas != nil { 390 spec.Replicas = base.Replicas 391 } 392 spec.Selector = mergeSelectors(base.Selector, spec.Selector) 393 if base.PodManagementPolicy != "" { 394 spec.PodManagementPolicy = base.PodManagementPolicy 395 } 396 if base.UpdateStrategy != nil { 397 spec.UpdateStrategy = *base.UpdateStrategy 398 } 399 if base.Template != nil { 400 spec.Template = base.Template.Override(spec.Template) 401 } 402 return spec 403 } 404 405 // +kubebuilder:object:generate=true 406 407 // Deprecated 408 // Consider using DaemonSet in the typeoverrides package combined with the merge package 409 type DaemonSetBase struct { 410 *MetaBase `json:",inline"` 411 Spec *DaemonSetSpecBase `json:"spec,omitempty"` 412 } 413 414 func (base *DaemonSetBase) Override(daemonset appsv1.DaemonSet) appsv1.DaemonSet { 415 if base == nil { 416 return daemonset 417 } 418 if base.MetaBase != nil { 419 daemonset.ObjectMeta = base.MetaBase.Merge(daemonset.ObjectMeta) 420 } 421 if base.Spec != nil { 422 daemonset.Spec = base.Spec.Override(daemonset.Spec) 423 } 424 return daemonset 425 } 426 427 // +kubebuilder:object:generate=true 428 429 // Deprecated 430 // Consider using DaemonSetSpec in the typeoverrides package combined with the merge package 431 type DaemonSetSpecBase struct { 432 Selector *metav1.LabelSelector `json:"selector,omitempty"` 433 UpdateStrategy *appsv1.DaemonSetUpdateStrategy `json:"updateStrategy,omitempty"` 434 MinReadySeconds int32 `json:"minReadySeconds,omitempty"` 435 RevisionHistoryLimit *int32 `json:"revisionHistoryLimit,omitempty"` 436 Template *PodTemplateBase `json:"template,omitempty"` 437 } 438 439 func (base *DaemonSetSpecBase) Override(spec appsv1.DaemonSetSpec) appsv1.DaemonSetSpec { 440 if base == nil { 441 return spec 442 } 443 spec.Selector = mergeSelectors(base.Selector, spec.Selector) 444 if base.UpdateStrategy != nil { 445 spec.UpdateStrategy = *base.UpdateStrategy 446 } 447 if base.MinReadySeconds != 0 { 448 spec.MinReadySeconds = base.MinReadySeconds 449 } 450 if base.RevisionHistoryLimit != nil { 451 spec.RevisionHistoryLimit = base.RevisionHistoryLimit 452 } 453 if base.Template != nil { 454 spec.Template = base.Template.Override(spec.Template) 455 } 456 return spec 457 } 458 459 func mergeSelectors(base, spec *metav1.LabelSelector) *metav1.LabelSelector { 460 if base == nil { 461 return spec 462 } 463 464 if base.MatchLabels != nil { 465 if spec == nil { 466 spec = &metav1.LabelSelector{} 467 } 468 if spec.MatchLabels == nil { 469 spec.MatchLabels = make(map[string]string) 470 } 471 for k, v := range base.MatchLabels { 472 spec.MatchLabels[k] = v 473 } 474 } 475 if base.MatchExpressions != nil { 476 spec.MatchExpressions = append(spec.MatchExpressions, base.MatchExpressions...) 477 } 478 479 return spec 480 }