github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/apis/apps/v1alpha1/cluster_types.go (about)

     1  /*
     2  Copyright (C) 2022-2023 ApeCloud Co., Ltd
     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 v1alpha1
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"strings"
    23  
    24  	"github.com/pkg/errors"
    25  	corev1 "k8s.io/api/core/v1"
    26  	"k8s.io/apimachinery/pkg/api/resource"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/util/intstr"
    29  	"sigs.k8s.io/controller-runtime/pkg/client"
    30  
    31  	dpv1alpha1 "github.com/1aal/kubeblocks/apis/dataprotection/v1alpha1"
    32  	workloads "github.com/1aal/kubeblocks/apis/workloads/v1alpha1"
    33  	"github.com/1aal/kubeblocks/pkg/constant"
    34  	viper "github.com/1aal/kubeblocks/pkg/viperx"
    35  )
    36  
    37  // ClusterSpec defines the desired state of Cluster.
    38  type ClusterSpec struct {
    39  	// Cluster referencing ClusterDefinition name. This is an immutable attribute.
    40  	// +kubebuilder:validation:Required
    41  	// +kubebuilder:validation:MaxLength=63
    42  	// +kubebuilder:validation:Pattern:=`^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$`
    43  	// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="clusterDefinitionRef is immutable"
    44  	ClusterDefRef string `json:"clusterDefinitionRef"`
    45  
    46  	// Cluster referencing ClusterVersion name.
    47  	// +kubebuilder:validation:MaxLength=63
    48  	// +kubebuilder:validation:Pattern:=`^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$`
    49  	// +optional
    50  	ClusterVersionRef string `json:"clusterVersionRef,omitempty"`
    51  
    52  	// Cluster termination policy. Valid values are DoNotTerminate, Halt, Delete, WipeOut.
    53  	// DoNotTerminate will block delete operation.
    54  	// Halt will delete workload resources such as statefulset, deployment workloads but keep PVCs.
    55  	// Delete is based on Halt and deletes PVCs.
    56  	// WipeOut is based on Delete and wipe out all volume snapshots and snapshot data from backup storage location.
    57  	// +kubebuilder:validation:Required
    58  	TerminationPolicy TerminationPolicyType `json:"terminationPolicy"`
    59  
    60  	// List of componentSpecs you want to replace in ClusterDefinition and ClusterVersion. It will replace the field in ClusterDefinition's and ClusterVersion's component if type is matching.
    61  	// +patchMergeKey=name
    62  	// +patchStrategy=merge,retainKeys
    63  	// +listType=map
    64  	// +listMapKey=name
    65  	// +kubebuilder:validation:Required
    66  	// +kubebuilder:validation:MinItems=1
    67  	// +kubebuilder:validation:MaxItems=128
    68  	// +kubebuilder:validation:XValidation:rule="self.all(x, size(self.filter(c, c.name == x.name)) == 1)",message="duplicated component"
    69  	// +kubebuilder:validation:XValidation:rule="self.all(x, oldSelf.exists(y, y.name == x.name))",message="component can not be added dynamically"
    70  	// +kubebuilder:validation:XValidation:rule="oldSelf.all(x, self.exists(y, y.name == x.name))",message="component can not be removed dynamically"
    71  	ComponentSpecs []ClusterComponentSpec `json:"componentSpecs,omitempty" patchStrategy:"merge,retainKeys" patchMergeKey:"name"`
    72  
    73  	// tenancy describes how pods are distributed across node.
    74  	// SharedNode means multiple pods may share the same node.
    75  	// DedicatedNode means each pod runs on their own dedicated node.
    76  	// +optional
    77  	Tenancy TenancyType `json:"tenancy,omitempty"`
    78  
    79  	// availabilityPolicy describes the availability policy, including zone, node, and none.
    80  	// +optional
    81  	AvailabilityPolicy AvailabilityPolicyType `json:"availabilityPolicy,omitempty"`
    82  
    83  	// affinity is a group of affinity scheduling rules.
    84  	// +optional
    85  	Affinity *Affinity `json:"affinity,omitempty"`
    86  
    87  	// tolerations are attached to tolerate any taint that matches the triple `key,value,effect` using the matching operator `operator`.
    88  	// +kubebuilder:pruning:PreserveUnknownFields
    89  	// +optional
    90  	Tolerations []corev1.Toleration `json:"tolerations,omitempty"`
    91  
    92  	// replicas specifies the replicas of the first componentSpec, if the replicas of the first componentSpec is specified, this value will be ignored.
    93  	// +optional
    94  	Replicas *int32 `json:"replicas,omitempty"`
    95  
    96  	// resources specifies the resources of the first componentSpec, if the resources of the first componentSpec is specified, this value will be ignored.
    97  	// +optional
    98  	Resources ClusterResources `json:"resources,omitempty"`
    99  
   100  	// storage specifies the storage of the first componentSpec, if the storage of the first componentSpec is specified, this value will be ignored.
   101  	// +optional
   102  	Storage ClusterStorage `json:"storage,omitempty"`
   103  
   104  	// monitor specifies the configuration of monitor
   105  	// +optional
   106  	Monitor ClusterMonitor `json:"monitor,omitempty"`
   107  
   108  	// network specifies the configuration of network
   109  	// +optional
   110  	Network *ClusterNetwork `json:"network,omitempty"`
   111  
   112  	// cluster backup configuration.
   113  	// +optional
   114  	Backup *ClusterBackup `json:"backup,omitempty"`
   115  }
   116  
   117  type ClusterBackup struct {
   118  	// enabled defines whether to enable automated backup.
   119  	// +kubebuilder:default=false
   120  	// +optional
   121  	Enabled *bool `json:"enabled,omitempty"`
   122  
   123  	// retentionPeriod determines a duration up to which the backup should be kept.
   124  	// controller will remove all backups that are older than the RetentionPeriod.
   125  	// For example, RetentionPeriod of `30d` will keep only the backups of last 30 days.
   126  	// Sample duration format:
   127  	// - years: 	2y
   128  	// - months: 	6mo
   129  	// - days: 		30d
   130  	// - hours: 	12h
   131  	// - minutes: 	30m
   132  	// You can also combine the above durations. For example: 30d12h30m
   133  	// +kubebuilder:default="7d"
   134  	// +optional
   135  	RetentionPeriod dpv1alpha1.RetentionPeriod `json:"retentionPeriod,omitempty"`
   136  
   137  	// backup method name to use, that is defined in backupPolicy.
   138  	// +optional
   139  	Method string `json:"method"`
   140  
   141  	// the cron expression for schedule, the timezone is in UTC. see https://en.wikipedia.org/wiki/Cron.
   142  	// +optional
   143  	CronExpression string `json:"cronExpression,omitempty"`
   144  
   145  	// startingDeadlineMinutes defines the deadline in minutes for starting the backup job
   146  	// if it misses scheduled time for any reason.
   147  	// +kubebuilder:validation:Minimum=0
   148  	// +kubebuilder:validation:Maximum=1440
   149  	// +optional
   150  	StartingDeadlineMinutes *int64 `json:"startingDeadlineMinutes,omitempty"`
   151  
   152  	// repoName is the name of the backupRepo, if not set, will use the default backupRepo.
   153  	// +optional
   154  	RepoName string `json:"repoName,omitempty"`
   155  
   156  	// pitrEnabled defines whether to enable point-in-time recovery.
   157  	// +kubebuilder:default=false
   158  	// +optional
   159  	PITREnabled *bool `json:"pitrEnabled,omitempty"`
   160  }
   161  
   162  type ClusterResources struct {
   163  
   164  	// cpu resource needed, more info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
   165  	// +optional
   166  	CPU resource.Quantity `json:"cpu,omitempty"`
   167  
   168  	// memory resource needed, more info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
   169  	// +optional
   170  	Memory resource.Quantity `json:"memory,omitempty"`
   171  }
   172  
   173  type ClusterStorage struct {
   174  
   175  	// storage size needed, more info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
   176  	// +optional
   177  	Size resource.Quantity `json:"size,omitempty"`
   178  }
   179  
   180  type ResourceMeta struct {
   181  	// name is the name of the referenced the Configmap/Secret object.
   182  	// +kubebuilder:validation:Required
   183  	// +kubebuilder:validation:MaxLength=63
   184  	// +kubebuilder:validation:Pattern:=`^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$`
   185  	Name string `json:"name"`
   186  
   187  	// mountPath is the path at which to mount the volume.
   188  	// +kubebuilder:validation:Required
   189  	// +kubebuilder:validation:MaxLength=256
   190  	// +kubebuilder:validation:Pattern:=`^/[a-z]([a-z0-9\-]*[a-z0-9])?$`
   191  	MountPoint string `json:"mountPoint"`
   192  
   193  	// subPath is a relative file path within the volume to mount.
   194  	// +optional
   195  	SubPath string `json:"subPath,omitempty"`
   196  
   197  	// asVolumeFrom defines the list of containers where volumeMounts will be injected into.
   198  	// +listType=set
   199  	// +optional
   200  	AsVolumeFrom []string `json:"asVolumeFrom,omitempty"`
   201  }
   202  
   203  type SecretRef struct {
   204  	ResourceMeta `json:",inline"`
   205  
   206  	// secret defines the secret volume source.
   207  	// +kubebuilder:validation:Required
   208  	Secret corev1.SecretVolumeSource `json:"secret"`
   209  }
   210  
   211  type ConfigMapRef struct {
   212  	ResourceMeta `json:",inline"`
   213  
   214  	// configMap defines the configmap volume source.
   215  	// +kubebuilder:validation:Required
   216  	ConfigMap corev1.ConfigMapVolumeSource `json:"configMap"`
   217  }
   218  
   219  type UserResourceRefs struct {
   220  	// secretRefs defines the user-defined secrets.
   221  	// +patchMergeKey=name
   222  	// +patchStrategy=merge,retainKeys
   223  	// +listType=map
   224  	// +listMapKey=name
   225  	// +optional
   226  	SecretRefs []SecretRef `json:"secretRefs,omitempty"`
   227  
   228  	// configMapRefs defines the user-defined configmaps.
   229  	// +patchMergeKey=name
   230  	// +patchStrategy=merge,retainKeys
   231  	// +listType=map
   232  	// +listMapKey=name
   233  	// +optional
   234  	ConfigMapRefs []ConfigMapRef `json:"configMapRefs,omitempty"`
   235  }
   236  
   237  // ClusterStatus defines the observed state of Cluster.
   238  type ClusterStatus struct {
   239  	// observedGeneration is the most recent generation observed for this
   240  	// Cluster. It corresponds to the Cluster's generation, which is
   241  	// updated on mutation by the API Server.
   242  	// +optional
   243  	ObservedGeneration int64 `json:"observedGeneration,omitempty"`
   244  
   245  	// phase describes the phase of the Cluster, the detail information of the phases are as following:
   246  	// Creating: all components are in `Creating` phase.
   247  	// Running: all components are in `Running` phase, means the cluster is working well.
   248  	// Updating: all components are in `Creating`, `Running` or `Updating` phase,
   249  	// and at least one component is in `Creating` or `Updating` phase, means the cluster is doing an update.
   250  	// Stopping: at least one component is in `Stopping` phase, means the cluster is in a stop progress.
   251  	// Stopped: all components are in 'Stopped` phase, means the cluster has stopped and didn't provide any function anymore.
   252  	// Failed: all components are in `Failed` phase, means the cluster is unavailable.
   253  	// Abnormal: some components are in `Failed` or `Abnormal` phase, means the cluster in a fragile state. troubleshoot need to be done.
   254  	// Deleting: the cluster is being deleted.
   255  	// +optional
   256  	Phase ClusterPhase `json:"phase,omitempty"`
   257  
   258  	// message describes cluster details message in current phase.
   259  	// +optional
   260  	Message string `json:"message,omitempty"`
   261  
   262  	// components record the current status information of all components of the cluster.
   263  	// +optional
   264  	Components map[string]ClusterComponentStatus `json:"components,omitempty"`
   265  
   266  	// clusterDefGeneration represents the generation number of ClusterDefinition referenced.
   267  	// +optional
   268  	ClusterDefGeneration int64 `json:"clusterDefGeneration,omitempty"`
   269  
   270  	// Describe current state of cluster API Resource, like warning.
   271  	// +optional
   272  	Conditions []metav1.Condition `json:"conditions,omitempty"`
   273  }
   274  
   275  // ClusterComponentSpec defines the cluster component spec.
   276  type ClusterComponentSpec struct {
   277  	// name defines cluster's component name, this name is also part of Service DNS name, so this name will
   278  	// comply with IANA Service Naming rule.
   279  	// +kubebuilder:validation:Required
   280  	// +kubebuilder:validation:MaxLength=22
   281  	// +kubebuilder:validation:Pattern:=`^[a-z]([a-z0-9\-]*[a-z0-9])?$`
   282  	// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="name is immutable"
   283  	Name string `json:"name"`
   284  
   285  	// componentDefRef references componentDef defined in ClusterDefinition spec. Need to
   286  	// comply with IANA Service Naming rule.
   287  	// +kubebuilder:validation:Required
   288  	// +kubebuilder:validation:MaxLength=22
   289  	// +kubebuilder:validation:Pattern:=`^[a-z]([a-z0-9\-]*[a-z0-9])?$`
   290  	// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="componentDefRef is immutable"
   291  	ComponentDefRef string `json:"componentDefRef"`
   292  
   293  	// classDefRef references the class defined in ComponentClassDefinition.
   294  	// +optional
   295  	ClassDefRef *ClassDefRef `json:"classDefRef,omitempty"`
   296  
   297  	// serviceRefs define service references for the current component. Based on the referenced services, they can be categorized into two types:
   298  	// Service provided by external sources: These services are provided by external sources and are not managed by KubeBlocks. They can be Kubernetes-based or non-Kubernetes services. For external services, you need to provide an additional ServiceDescriptor object to establish the service binding.
   299  	// Service provided by other KubeBlocks clusters: These services are provided by other KubeBlocks clusters. You can bind to these services by specifying the name of the hosting cluster.
   300  	// Each type of service reference requires specific configurations and bindings to establish the connection and interaction with the respective services.
   301  	// It should be noted that the ServiceRef has cluster-level semantic consistency, meaning that within the same Cluster, service references with the same ServiceRef.Name are considered to be the same service. It is only allowed to bind to the same Cluster or ServiceDescriptor.
   302  	// +optional
   303  	ServiceRefs []ServiceRef `json:"serviceRefs,omitempty"`
   304  
   305  	// monitor is a switch to enable monitoring and is set as false by default.
   306  	// KubeBlocks provides an extension mechanism to support component level monitoring,
   307  	// which will scrape metrics auto or manually from servers in component and export
   308  	// metrics to Time Series Database.
   309  	// +kubebuilder:default=false
   310  	// +optional
   311  	Monitor bool `json:"monitor,omitempty"`
   312  
   313  	// enabledLogs indicates which log file takes effect in the database cluster.
   314  	// element is the log type which is defined in cluster definition logConfig.name,
   315  	// and will set relative variables about this log type in database kernel.
   316  	// +listType=set
   317  	// +optional
   318  	EnabledLogs []string `json:"enabledLogs,omitempty"`
   319  
   320  	// Component replicas. The default value is used in ClusterDefinition spec if not specified.
   321  	// +kubebuilder:validation:Required
   322  	// +kubebuilder:validation:Minimum=0
   323  	// +kubebuilder:default=1
   324  	Replicas int32 `json:"replicas"`
   325  
   326  	// affinity describes affinities specified by users.
   327  	// +optional
   328  	Affinity *Affinity `json:"affinity,omitempty"`
   329  
   330  	// Component tolerations will override ClusterSpec.Tolerations if specified.
   331  	// +kubebuilder:pruning:PreserveUnknownFields
   332  	// +optional
   333  	Tolerations []corev1.Toleration `json:"tolerations,omitempty"`
   334  
   335  	// Resources requests and limits of workload.
   336  	// +kubebuilder:pruning:PreserveUnknownFields
   337  	// +optional
   338  	Resources corev1.ResourceRequirements `json:"resources,omitempty"`
   339  
   340  	// volumeClaimTemplates information for statefulset.spec.volumeClaimTemplates.
   341  	// +optional
   342  	// +patchMergeKey=name
   343  	// +patchStrategy=merge,retainKeys
   344  	VolumeClaimTemplates []ClusterComponentVolumeClaimTemplate `json:"volumeClaimTemplates,omitempty" patchStrategy:"merge,retainKeys" patchMergeKey:"name"`
   345  
   346  	// Services expose endpoints that can be accessed by clients.
   347  	// +optional
   348  	Services []ClusterComponentService `json:"services,omitempty"`
   349  
   350  	// switchPolicy defines the strategy for switchover and failover when workloadType is Replication.
   351  	// +optional
   352  	SwitchPolicy *ClusterSwitchPolicy `json:"switchPolicy,omitempty"`
   353  
   354  	// Enables or disables TLS certs.
   355  	// +optional
   356  	TLS bool `json:"tls,omitempty"`
   357  
   358  	// issuer defines provider context for TLS certs.
   359  	// required when TLS enabled
   360  	// +optional
   361  	Issuer *Issuer `json:"issuer,omitempty"`
   362  
   363  	// serviceAccountName is the name of the ServiceAccount that running component depends on.
   364  	// +optional
   365  	ServiceAccountName string `json:"serviceAccountName,omitempty"`
   366  
   367  	// noCreatePDB defines the PodDisruptionBudget creation behavior and is set to true if creation of PodDisruptionBudget
   368  	// for this component is not needed. It defaults to false.
   369  	// +kubebuilder:default=false
   370  	// +optional
   371  	NoCreatePDB bool `json:"noCreatePDB,omitempty"`
   372  
   373  	// userResourceRefs defines the user-defined volumes.
   374  	// +optional
   375  	UserResourceRefs *UserResourceRefs `json:"userResourceRefs,omitempty"`
   376  }
   377  
   378  // GetMinAvailable wraps the 'prefer' value return. As for component replicaCount <= 1, it will return 0,
   379  // and as for replicaCount=2 it will return 1.
   380  func (r *ClusterComponentSpec) GetMinAvailable(prefer *intstr.IntOrString) *intstr.IntOrString {
   381  	if r == nil || r.NoCreatePDB || prefer == nil {
   382  		return nil
   383  	}
   384  	if r.Replicas <= 1 {
   385  		m := intstr.FromInt(0)
   386  		return &m
   387  	} else if r.Replicas == 2 {
   388  		m := intstr.FromInt(1)
   389  		return &m
   390  	}
   391  	return prefer
   392  }
   393  
   394  type ComponentMessageMap map[string]string
   395  
   396  // ClusterComponentStatus records components status.
   397  type ClusterComponentStatus struct {
   398  	// phase describes the phase of the component and the detail information of the phases are as following:
   399  	// Creating: `Creating` is a special `Updating` with previous phase `empty`(means "") or `Creating`.
   400  	// Running: component replicas > 0 and all pod specs are latest with a Running state.
   401  	// Updating: component replicas > 0 and has no failed pods. the component is being updated.
   402  	// Abnormal: component replicas > 0 but having some failed pods. the component basically works but in a fragile state.
   403  	// Failed: component replicas > 0 but having some failed pods. the component doesn't work anymore.
   404  	// Stopping: component replicas = 0 and has terminating pods.
   405  	// Stopped: component replicas = 0 and all pods have been deleted.
   406  	// Deleting: the component is being deleted.
   407  	Phase ClusterComponentPhase `json:"phase,omitempty"`
   408  
   409  	// message records the component details message in current phase.
   410  	// Keys are podName or deployName or statefulSetName. The format is `ObjectKind/Name`.
   411  	// +optional
   412  	Message ComponentMessageMap `json:"message,omitempty"`
   413  
   414  	// podsReady checks if all pods of the component are ready.
   415  	// +optional
   416  	PodsReady *bool `json:"podsReady,omitempty"`
   417  
   418  	// podsReadyTime what time point of all component pods are ready,
   419  	// this time is the ready time of the last component pod.
   420  	// +optional
   421  	PodsReadyTime *metav1.Time `json:"podsReadyTime,omitempty"`
   422  
   423  	// consensusSetStatus specifies the mapping of role and pod name.
   424  	// +optional
   425  	// +kubebuilder:deprecatedversion:warning="This field is deprecated from KB 0.7.0, use MembersStatus instead."
   426  	ConsensusSetStatus *ConsensusSetStatus `json:"consensusSetStatus,omitempty"`
   427  
   428  	// replicationSetStatus specifies the mapping of role and pod name.
   429  	// +optional
   430  	// +kubebuilder:deprecatedversion:warning="This field is deprecated from KB 0.7.0, use MembersStatus instead."
   431  	ReplicationSetStatus *ReplicationSetStatus `json:"replicationSetStatus,omitempty"`
   432  
   433  	// members' status.
   434  	// +optional
   435  	MembersStatus []workloads.MemberStatus `json:"membersStatus,omitempty"`
   436  }
   437  
   438  type ConsensusSetStatus struct {
   439  	// Leader status.
   440  	// +kubebuilder:validation:Required
   441  	Leader ConsensusMemberStatus `json:"leader"`
   442  
   443  	// Followers status.
   444  	// +optional
   445  	Followers []ConsensusMemberStatus `json:"followers,omitempty"`
   446  
   447  	// Learner status.
   448  	// +optional
   449  	Learner *ConsensusMemberStatus `json:"learner,omitempty"`
   450  }
   451  
   452  type ConsensusMemberStatus struct {
   453  	// Defines the role name.
   454  	// +kubebuilder:validation:Required
   455  	// +kubebuilder:default=leader
   456  	Name string `json:"name"`
   457  
   458  	// accessMode defines what service this pod provides.
   459  	// +kubebuilder:validation:Required
   460  	// +kubebuilder:default=ReadWrite
   461  	AccessMode AccessMode `json:"accessMode"`
   462  
   463  	// Pod name.
   464  	// +kubebuilder:validation:Required
   465  	// +kubebuilder:default=Unknown
   466  	Pod string `json:"pod"`
   467  }
   468  
   469  type ReplicationSetStatus struct {
   470  	// Primary status.
   471  	// +kubebuilder:validation:Required
   472  	Primary ReplicationMemberStatus `json:"primary"`
   473  
   474  	// Secondaries status.
   475  	// +optional
   476  	Secondaries []ReplicationMemberStatus `json:"secondaries,omitempty"`
   477  }
   478  
   479  type ReplicationMemberStatus struct {
   480  	// Pod name.
   481  	// +kubebuilder:validation:Required
   482  	// +kubebuilder:default=Unknown
   483  	Pod string `json:"pod"`
   484  }
   485  
   486  type ClusterSwitchPolicy struct {
   487  	// TODO other attribute extensions
   488  
   489  	// clusterSwitchPolicy defines type of the switchPolicy when workloadType is Replication.
   490  	// MaximumAvailability: [WIP] when the primary is active, do switch if the synchronization delay = 0 in the user-defined lagProbe data delay detection logic, otherwise do not switch. The primary is down, switch immediately. It will be available in future versions.
   491  	// MaximumDataProtection: [WIP] when the primary is active, do switch if synchronization delay = 0 in the user-defined lagProbe data lag detection logic, otherwise do not switch. If the primary is down, if it can be judged that the primary and secondary data are consistent, then do the switch, otherwise do not switch. It will be available in future versions.
   492  	// Noop: KubeBlocks will not perform high-availability switching on components. Users need to implement HA by themselves or integrate open source HA solution.
   493  	// +kubebuilder:validation:Required
   494  	// +kubebuilder:default=Noop
   495  	// +optional
   496  	Type SwitchPolicyType `json:"type"`
   497  }
   498  
   499  type ClusterComponentVolumeClaimTemplate struct {
   500  	// Reference `ClusterDefinition.spec.componentDefs.containers.volumeMounts.name`.
   501  	// +kubebuilder:validation:Required
   502  	Name string `json:"name"`
   503  	// spec defines the desired characteristics of a volume requested by a pod author.
   504  	// +optional
   505  	Spec PersistentVolumeClaimSpec `json:"spec,omitempty"`
   506  }
   507  
   508  func (r *ClusterComponentVolumeClaimTemplate) toVolumeClaimTemplate() corev1.PersistentVolumeClaimTemplate {
   509  	return corev1.PersistentVolumeClaimTemplate{
   510  		ObjectMeta: metav1.ObjectMeta{
   511  			Name: r.Name,
   512  		},
   513  		Spec: r.Spec.ToV1PersistentVolumeClaimSpec(),
   514  	}
   515  }
   516  
   517  type PersistentVolumeClaimSpec struct {
   518  	// accessModes contains the desired access modes the volume should have.
   519  	// More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1.
   520  	// +kubebuilder:pruning:PreserveUnknownFields
   521  	// +optional
   522  	AccessModes []corev1.PersistentVolumeAccessMode `json:"accessModes,omitempty" protobuf:"bytes,1,rep,name=accessModes,casttype=PersistentVolumeAccessMode"`
   523  	// resources represents the minimum resources the volume should have.
   524  	// If RecoverVolumeExpansionFailure feature is enabled users are allowed to specify resource requirements
   525  	// that are lower than previous value but must still be higher than capacity recorded in the
   526  	// status field of the claim.
   527  	// More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources.
   528  	// +kubebuilder:pruning:PreserveUnknownFields
   529  	// +optional
   530  	Resources corev1.ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,2,opt,name=resources"`
   531  	// storageClassName is the name of the StorageClass required by the claim.
   532  	// More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1.
   533  	// +optional
   534  	StorageClassName *string `json:"storageClassName,omitempty" protobuf:"bytes,5,opt,name=storageClassName"`
   535  	// volumeMode defines what type of volume is required by the claim.
   536  	// +optional
   537  	VolumeMode *corev1.PersistentVolumeMode `json:"volumeMode,omitempty" protobuf:"bytes,6,opt,name=volumeMode,casttype=PersistentVolumeMode"`
   538  }
   539  
   540  // ToV1PersistentVolumeClaimSpec converts to corev1.PersistentVolumeClaimSpec.
   541  func (r *PersistentVolumeClaimSpec) ToV1PersistentVolumeClaimSpec() corev1.PersistentVolumeClaimSpec {
   542  	return corev1.PersistentVolumeClaimSpec{
   543  		AccessModes:      r.AccessModes,
   544  		Resources:        r.Resources,
   545  		StorageClassName: r.getStorageClassName(viper.GetString(constant.CfgKeyDefaultStorageClass)),
   546  		VolumeMode:       r.VolumeMode,
   547  	}
   548  }
   549  
   550  // getStorageClassName returns PersistentVolumeClaimSpec.StorageClassName if a value is assigned; otherwise,
   551  // it returns preferSC argument.
   552  func (r *PersistentVolumeClaimSpec) getStorageClassName(preferSC string) *string {
   553  	if r.StorageClassName != nil && *r.StorageClassName != "" {
   554  		return r.StorageClassName
   555  	}
   556  	if preferSC != "" {
   557  		return &preferSC
   558  	}
   559  	return nil
   560  }
   561  
   562  type Affinity struct {
   563  	// podAntiAffinity describes the anti-affinity level of pods within a component.
   564  	// Preferred means try spread pods by `TopologyKeys`.
   565  	// Required means must spread pods by `TopologyKeys`.
   566  	// +kubebuilder:default=Preferred
   567  	// +optional
   568  	PodAntiAffinity PodAntiAffinity `json:"podAntiAffinity,omitempty"`
   569  
   570  	// topologyKey is the key of node labels.
   571  	// Nodes that have a label with this key and identical values are considered to be in the same topology.
   572  	// It's used as the topology domain for pod anti-affinity and pod spread constraint.
   573  	// Some well-known label keys, such as "kubernetes.io/hostname" and "topology.kubernetes.io/zone"
   574  	// are often used as TopologyKey, as well as any other custom label key.
   575  	// +listType=set
   576  	// +optional
   577  	TopologyKeys []string `json:"topologyKeys,omitempty"`
   578  
   579  	// nodeLabels describes that pods must be scheduled to the nodes with the specified node labels.
   580  	// +optional
   581  	NodeLabels map[string]string `json:"nodeLabels,omitempty"`
   582  
   583  	// tenancy describes how pods are distributed across node.
   584  	// SharedNode means multiple pods may share the same node.
   585  	// DedicatedNode means each pod runs on their own dedicated node.
   586  	// +kubebuilder:default=SharedNode
   587  	// +optional
   588  	Tenancy TenancyType `json:"tenancy,omitempty"`
   589  }
   590  
   591  // Issuer defines Tls certs issuer
   592  type Issuer struct {
   593  	// Name of issuer.
   594  	// Options supported:
   595  	// - KubeBlocks - Certificates signed by KubeBlocks Operator.
   596  	// - UserProvided - User provided own CA-signed certificates.
   597  	// +kubebuilder:validation:Enum={KubeBlocks, UserProvided}
   598  	// +kubebuilder:default=KubeBlocks
   599  	// +kubebuilder:validation:Required
   600  	Name IssuerName `json:"name"`
   601  
   602  	// secretRef. TLS certs Secret reference
   603  	// required when from is UserProvided
   604  	// +optional
   605  	SecretRef *TLSSecretRef `json:"secretRef,omitempty"`
   606  }
   607  
   608  // TLSSecretRef defines Secret contains Tls certs
   609  type TLSSecretRef struct {
   610  	// Name of the Secret
   611  	// +kubebuilder:validation:Required
   612  	Name string `json:"name"`
   613  
   614  	// CA cert key in Secret
   615  	// +kubebuilder:validation:Required
   616  	CA string `json:"ca"`
   617  
   618  	// Cert key in Secret
   619  	// +kubebuilder:validation:Required
   620  	Cert string `json:"cert"`
   621  
   622  	// Key of TLS private key in Secret
   623  	// +kubebuilder:validation:Required
   624  	Key string `json:"key"`
   625  }
   626  
   627  type ClusterComponentService struct {
   628  	// Service name
   629  	// +kubebuilder:validation:Required
   630  	// +kubebuilder:validation:MaxLength=15
   631  	Name string `json:"name"`
   632  
   633  	// serviceType determines how the Service is exposed. Valid
   634  	// options are ClusterIP, NodePort, and LoadBalancer.
   635  	// "ClusterIP" allocates a cluster-internal IP address for load-balancing
   636  	// to endpoints. Endpoints are determined by the selector or if that is not
   637  	// specified, they are determined by manual construction of an Endpoints object or
   638  	// EndpointSlice objects. If clusterIP is "None", no virtual IP is
   639  	// allocated and the endpoints are published as a set of endpoints rather
   640  	// than a virtual IP.
   641  	// "NodePort" builds on ClusterIP and allocates a port on every node which
   642  	// routes to the same endpoints as the clusterIP.
   643  	// "LoadBalancer" builds on NodePort and creates an external load-balancer
   644  	// (if supported in the current cloud) which routes to the same endpoints
   645  	// as the clusterIP.
   646  	// More info: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types.
   647  	// +kubebuilder:default=ClusterIP
   648  	// +kubebuilder:validation:Enum={ClusterIP,NodePort,LoadBalancer}
   649  	// +kubebuilder:pruning:PreserveUnknownFields
   650  	// +optional
   651  	ServiceType corev1.ServiceType `json:"serviceType,omitempty"`
   652  
   653  	// If ServiceType is LoadBalancer, cloud provider related parameters can be put here
   654  	// More info: https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer.
   655  	// +optional
   656  	Annotations map[string]string `json:"annotations,omitempty"`
   657  }
   658  
   659  type ClassDefRef struct {
   660  	// Name refers to the name of the ComponentClassDefinition.
   661  	// +kubebuilder:validation:MaxLength=63
   662  	// +kubebuilder:validation:Pattern:=`^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$`
   663  	// +optional
   664  	Name string `json:"name,omitempty"`
   665  
   666  	// Class refers to the name of the class that is defined in the ComponentClassDefinition.
   667  	// +kubebuilder:validation:Required
   668  	Class string `json:"class"`
   669  }
   670  
   671  type ClusterMonitor struct {
   672  
   673  	// monitoringInterval specifies interval of monitoring, no monitor if set to 0
   674  	// +kubebuilder:validation:XIntOrString
   675  	// +optional
   676  	MonitoringInterval *intstr.IntOrString `json:"monitoringInterval,omitempty"`
   677  }
   678  
   679  type ClusterNetwork struct {
   680  
   681  	// hostNetworkAccessible specifies whether host network is accessible. It defaults to false
   682  	// +kubebuilder:default=false
   683  	// +optional
   684  	HostNetworkAccessible bool `json:"hostNetworkAccessible,omitempty"`
   685  
   686  	// publiclyAccessible specifies whether it is publicly accessible. It defaults to false
   687  	// +kubebuilder:default=false
   688  	// +optional
   689  	PubliclyAccessible bool `json:"publiclyAccessible,omitempty"`
   690  }
   691  
   692  type ServiceRef struct {
   693  	// name of the service reference declaration. references the serviceRefDeclaration name defined in clusterDefinition.componentDefs[*].serviceRefDeclarations[*].name
   694  	// +kubebuilder:validation:Required
   695  	Name string `json:"name"`
   696  
   697  	// namespace defines the namespace of the referenced Cluster or the namespace of the referenced ServiceDescriptor object.
   698  	// If not set, the referenced Cluster and ServiceDescriptor will be searched in the namespace of the current cluster by default.
   699  	// +optional
   700  	Namespace string `json:"namespace,omitempty" protobuf:"bytes,3,opt,name=namespace"`
   701  
   702  	// When referencing a service provided by other KubeBlocks cluster, you need to provide the name of the Cluster being referenced.
   703  	// By default, when other KubeBlocks Cluster are referenced, the ClusterDefinition.spec.connectionCredential secret corresponding to the referenced Cluster will be used to bind to the current component.
   704  	// Currently, if a KubeBlocks cluster is to be referenced, the connection credential secret should include and correspond to the following fields: endpoint, port, username, and password.
   705  	// Under this referencing approach, the ServiceKind and ServiceVersion of service reference declaration defined in the ClusterDefinition will not be validated.
   706  	// If both Cluster and ServiceDescriptor are specified, the Cluster takes precedence.
   707  	// +optional
   708  	Cluster string `json:"cluster,omitempty"`
   709  
   710  	// serviceDescriptor defines the service descriptor of the service provided by external sources.
   711  	// When referencing a service provided by external sources, you need to provide the ServiceDescriptor object name to establish the service binding.
   712  	// And serviceDescriptor is the name of the ServiceDescriptor object, furthermore, the ServiceDescriptor.spec.serviceKind and ServiceDescriptor.spec.serviceVersion
   713  	// should match clusterDefinition.componentDefs[*].serviceRefDeclarations[*].serviceRefDeclarationSpecs[*].serviceKind
   714  	// and the regular expression defines in clusterDefinition.componentDefs[*].serviceRefDeclarations[*].serviceRefDeclarationSpecs[*].serviceVersion.
   715  	// If both Cluster and ServiceDescriptor are specified, the Cluster takes precedence.
   716  	// +optional
   717  	ServiceDescriptor string `json:"serviceDescriptor,omitempty"`
   718  }
   719  
   720  // +genclient
   721  // +k8s:openapi-gen=true
   722  // +kubebuilder:object:root=true
   723  // +kubebuilder:subresource:status
   724  // +kubebuilder:resource:categories={kubeblocks,all}
   725  // +kubebuilder:printcolumn:name="CLUSTER-DEFINITION",type="string",JSONPath=".spec.clusterDefinitionRef",description="ClusterDefinition referenced by cluster."
   726  // +kubebuilder:printcolumn:name="VERSION",type="string",JSONPath=".spec.clusterVersionRef",description="Cluster Application Version."
   727  // +kubebuilder:printcolumn:name="TERMINATION-POLICY",type="string",JSONPath=".spec.terminationPolicy",description="Cluster termination policy."
   728  // +kubebuilder:printcolumn:name="STATUS",type="string",JSONPath=".status.phase",description="Cluster Status."
   729  // +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp"
   730  
   731  // Cluster is the Schema for the clusters API.
   732  type Cluster struct {
   733  	metav1.TypeMeta   `json:",inline"`
   734  	metav1.ObjectMeta `json:"metadata,omitempty"`
   735  
   736  	Spec   ClusterSpec   `json:"spec,omitempty"`
   737  	Status ClusterStatus `json:"status,omitempty"`
   738  }
   739  
   740  // +kubebuilder:object:root=true
   741  
   742  // ClusterList contains a list of Cluster.
   743  type ClusterList struct {
   744  	metav1.TypeMeta `json:",inline"`
   745  	metav1.ListMeta `json:"metadata,omitempty"`
   746  	Items           []Cluster `json:"items"`
   747  }
   748  
   749  func init() {
   750  	SchemeBuilder.Register(&Cluster{}, &ClusterList{})
   751  }
   752  
   753  func (r Cluster) IsDeleting() bool {
   754  	if r.GetDeletionTimestamp().IsZero() {
   755  		return false
   756  	}
   757  	return r.Spec.TerminationPolicy != DoNotTerminate
   758  }
   759  
   760  func (r Cluster) IsUpdating() bool {
   761  	return r.Status.ObservedGeneration != r.Generation
   762  }
   763  
   764  func (r Cluster) IsStatusUpdating() bool {
   765  	return !r.IsDeleting() && !r.IsUpdating()
   766  }
   767  
   768  // GetVolumeClaimNames gets all PVC names of component compName.
   769  //
   770  // r.Spec.GetComponentByName(compName).VolumeClaimTemplates[*].Name will be used if no claimNames provided
   771  //
   772  // nil return if:
   773  // 1. component compName not found or
   774  // 2. len(VolumeClaimTemplates)==0 or
   775  // 3. any claimNames not found
   776  func (r *Cluster) GetVolumeClaimNames(compName string, claimNames ...string) []string {
   777  	if r == nil {
   778  		return nil
   779  	}
   780  	comp := r.Spec.GetComponentByName(compName)
   781  	if comp == nil {
   782  		return nil
   783  	}
   784  	if len(comp.VolumeClaimTemplates) == 0 {
   785  		return nil
   786  	}
   787  	if len(claimNames) == 0 {
   788  		for _, template := range comp.VolumeClaimTemplates {
   789  			claimNames = append(claimNames, template.Name)
   790  		}
   791  	}
   792  	allExist := true
   793  	for _, name := range claimNames {
   794  		found := false
   795  		for _, template := range comp.VolumeClaimTemplates {
   796  			if template.Name == name {
   797  				found = true
   798  				break
   799  			}
   800  		}
   801  		if !found {
   802  			allExist = false
   803  			break
   804  		}
   805  	}
   806  	if !allExist {
   807  		return nil
   808  	}
   809  
   810  	pvcNames := make([]string, 0)
   811  	for _, claimName := range claimNames {
   812  		for i := 0; i < int(comp.Replicas); i++ {
   813  			pvcName := fmt.Sprintf("%s-%s-%s-%d", claimName, r.Name, compName, i)
   814  			pvcNames = append(pvcNames, pvcName)
   815  		}
   816  	}
   817  	return pvcNames
   818  }
   819  
   820  // GetComponentByName gets component by name.
   821  func (r ClusterSpec) GetComponentByName(componentName string) *ClusterComponentSpec {
   822  	for _, v := range r.ComponentSpecs {
   823  		if v.Name == componentName {
   824  			return &v
   825  		}
   826  	}
   827  	return nil
   828  }
   829  
   830  // GetComponentDefRefName gets the name of referenced component definition.
   831  func (r ClusterSpec) GetComponentDefRefName(componentName string) string {
   832  	for _, component := range r.ComponentSpecs {
   833  		if componentName == component.Name {
   834  			return component.ComponentDefRef
   835  		}
   836  	}
   837  	return ""
   838  }
   839  
   840  // ValidateEnabledLogs validates enabledLogs config in cluster.yaml, and returns metav1.Condition when detecting invalid values.
   841  func (r ClusterSpec) ValidateEnabledLogs(cd *ClusterDefinition) error {
   842  	message := make([]string, 0)
   843  	for _, comp := range r.ComponentSpecs {
   844  		invalidLogNames := cd.ValidateEnabledLogConfigs(comp.ComponentDefRef, comp.EnabledLogs)
   845  		if len(invalidLogNames) == 0 {
   846  			continue
   847  		}
   848  		message = append(message, fmt.Sprintf("EnabledLogs: %s are not defined in Component: %s of the clusterDefinition", invalidLogNames, comp.Name))
   849  	}
   850  	if len(message) > 0 {
   851  		return errors.New(strings.Join(message, ";"))
   852  	}
   853  	return nil
   854  }
   855  
   856  // GetDefNameMappingComponents returns ComponentDefRef name mapping ClusterComponentSpec.
   857  func (r ClusterSpec) GetDefNameMappingComponents() map[string][]ClusterComponentSpec {
   858  	m := map[string][]ClusterComponentSpec{}
   859  	for _, c := range r.ComponentSpecs {
   860  		v := m[c.ComponentDefRef]
   861  		v = append(v, c)
   862  		m[c.ComponentDefRef] = v
   863  	}
   864  	return m
   865  }
   866  
   867  // GetMessage gets message map deep copy object.
   868  func (r ClusterComponentStatus) GetMessage() ComponentMessageMap {
   869  	messageMap := map[string]string{}
   870  	for k, v := range r.Message {
   871  		messageMap[k] = v
   872  	}
   873  	return messageMap
   874  }
   875  
   876  // SetMessage overrides message map object.
   877  func (r *ClusterComponentStatus) SetMessage(messageMap ComponentMessageMap) {
   878  	if r == nil {
   879  		return
   880  	}
   881  	r.Message = messageMap
   882  }
   883  
   884  // SetObjectMessage sets K8s workload message to component status message map.
   885  func (r *ClusterComponentStatus) SetObjectMessage(objectKind, objectName, message string) {
   886  	if r == nil {
   887  		return
   888  	}
   889  	if r.Message == nil {
   890  		r.Message = map[string]string{}
   891  	}
   892  	messageKey := fmt.Sprintf("%s/%s", objectKind, objectName)
   893  	r.Message[messageKey] = message
   894  }
   895  
   896  // GetObjectMessage gets the k8s workload message in component status message map
   897  func (r ClusterComponentStatus) GetObjectMessage(objectKind, objectName string) string {
   898  	messageKey := fmt.Sprintf("%s/%s", objectKind, objectName)
   899  	return r.Message[messageKey]
   900  }
   901  
   902  // SetObjectMessage sets k8s workload message to component status message map
   903  func (r ComponentMessageMap) SetObjectMessage(objectKind, objectName, message string) {
   904  	if r == nil {
   905  		return
   906  	}
   907  	messageKey := fmt.Sprintf("%s/%s", objectKind, objectName)
   908  	r[messageKey] = message
   909  }
   910  
   911  // SetComponentStatus does safe operation on ClusterStatus.Components map object update.
   912  func (r *ClusterStatus) SetComponentStatus(name string, status ClusterComponentStatus) {
   913  	r.checkedInitComponentsMap()
   914  	r.Components[name] = status
   915  }
   916  
   917  func (r *ClusterStatus) checkedInitComponentsMap() {
   918  	if r.Components == nil {
   919  		r.Components = map[string]ClusterComponentStatus{}
   920  	}
   921  }
   922  
   923  // ToVolumeClaimTemplates convert r.VolumeClaimTemplates to []corev1.PersistentVolumeClaimTemplate.
   924  func (r *ClusterComponentSpec) ToVolumeClaimTemplates() []corev1.PersistentVolumeClaimTemplate {
   925  	if r == nil {
   926  		return nil
   927  	}
   928  	var ts []corev1.PersistentVolumeClaimTemplate
   929  	for _, t := range r.VolumeClaimTemplates {
   930  		ts = append(ts, t.toVolumeClaimTemplate())
   931  	}
   932  	return ts
   933  }
   934  
   935  // GetClusterUpRunningPhases returns Cluster running or partially running phases.
   936  func GetClusterUpRunningPhases() []ClusterPhase {
   937  	return []ClusterPhase{
   938  		RunningClusterPhase,
   939  		AbnormalClusterPhase,
   940  		FailedClusterPhase, // REVIEW/TODO: single component with single pod component are handled as FailedClusterPhase, ought to remove this.
   941  	}
   942  }
   943  
   944  // GetReconfiguringRunningPhases return Cluster running or partially running phases.
   945  func GetReconfiguringRunningPhases() []ClusterPhase {
   946  	return []ClusterPhase{
   947  		RunningClusterPhase,
   948  		UpdatingClusterPhase, // enable partial running for reconfiguring
   949  		AbnormalClusterPhase,
   950  		FailedClusterPhase,
   951  	}
   952  }
   953  
   954  // GetComponentTerminalPhases return Cluster's component terminal phases.
   955  func GetComponentTerminalPhases() []ClusterComponentPhase {
   956  	return []ClusterComponentPhase{
   957  		RunningClusterCompPhase,
   958  		StoppedClusterCompPhase,
   959  		FailedClusterCompPhase,
   960  		AbnormalClusterCompPhase,
   961  	}
   962  }
   963  
   964  // GetComponentUpRunningPhase returns component running or partially running phases.
   965  func GetComponentUpRunningPhase() []ClusterComponentPhase {
   966  	return []ClusterComponentPhase{
   967  		RunningClusterCompPhase,
   968  		AbnormalClusterCompPhase,
   969  		FailedClusterCompPhase,
   970  	}
   971  }
   972  
   973  // ComponentPodsAreReady checks if the pods of component are ready.
   974  func ComponentPodsAreReady(podsAreReady *bool) bool {
   975  	return podsAreReady != nil && *podsAreReady
   976  }
   977  
   978  // GetComponentDefByCluster gets component from ClusterDefinition with compDefName
   979  func GetComponentDefByCluster(ctx context.Context, cli client.Client, cluster Cluster,
   980  	compDefName string) (*ClusterComponentDefinition, error) {
   981  	clusterDef := &ClusterDefinition{}
   982  	if err := cli.Get(ctx, client.ObjectKey{Name: cluster.Spec.ClusterDefRef}, clusterDef); err != nil {
   983  		return nil, err
   984  	}
   985  	for _, component := range clusterDef.Spec.ComponentDefs {
   986  		if component.Name == compDefName {
   987  			return &component, nil
   988  		}
   989  	}
   990  	return nil, ErrNotMatchingCompDef
   991  }