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  }