github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/apis/apps/v1alpha1/clusterdefinition_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  	"strings"
    21  
    22  	appsv1 "k8s.io/api/apps/v1"
    23  	corev1 "k8s.io/api/core/v1"
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  	"k8s.io/apimachinery/pkg/util/intstr"
    26  
    27  	workloads "github.com/1aal/kubeblocks/apis/workloads/v1alpha1"
    28  )
    29  
    30  // ClusterDefinitionSpec defines the desired state of ClusterDefinition
    31  type ClusterDefinitionSpec struct {
    32  
    33  	// Cluster definition type defines well known application cluster type, e.g. mysql/redis/mongodb
    34  	// +kubebuilder:validation:MaxLength=24
    35  	// +kubebuilder:validation:Pattern:=`^[a-z0-9]([a-z0-9\-]*[a-z0-9])?$`
    36  	// +optional
    37  	Type string `json:"type,omitempty"`
    38  
    39  	// componentDefs provides cluster components definitions.
    40  	// +kubebuilder:validation:Required
    41  	// +kubebuilder:validation:MinItems=1
    42  	// +patchMergeKey=name
    43  	// +patchStrategy=merge,retainKeys
    44  	// +listType=map
    45  	// +listMapKey=name
    46  	ComponentDefs []ClusterComponentDefinition `json:"componentDefs" patchStrategy:"merge,retainKeys" patchMergeKey:"name"`
    47  
    48  	// Connection credential template used for creating a connection credential
    49  	// secret for cluster.apps.kubeblocks.io object.
    50  	//
    51  	// Built-in objects are:
    52  	// - `$(RANDOM_PASSWD)` - random 8 characters.
    53  	// - `$(UUID)` - generate a random UUID v4 string.
    54  	// - `$(UUID_B64)` - generate a random UUID v4 BASE64 encoded string.
    55  	// - `$(UUID_STR_B64)` - generate a random UUID v4 string then BASE64 encoded.
    56  	// - `$(UUID_HEX)` - generate a random UUID v4 HEX representation.
    57  	// - `$(HEADLESS_SVC_FQDN)` - headless service FQDN placeholder, value pattern - $(CLUSTER_NAME)-$(1ST_COMP_NAME)-headless.$(NAMESPACE).svc,
    58  	//    where 1ST_COMP_NAME is the 1st component that provide `ClusterDefinition.spec.componentDefs[].service` attribute;
    59  	// - `$(SVC_FQDN)` - service FQDN  placeholder, value pattern - $(CLUSTER_NAME)-$(1ST_COMP_NAME).$(NAMESPACE).svc,
    60  	//    where 1ST_COMP_NAME is the 1st component that provide `ClusterDefinition.spec.componentDefs[].service` attribute;
    61  	// - `$(SVC_PORT_{PORT-NAME})` - a ServicePort's port value with specified port name, i.e, a servicePort JSON struct:
    62  	//    `{"name": "mysql", "targetPort": "mysqlContainerPort", "port": 3306}`, and "$(SVC_PORT_mysql)" in the
    63  	//    connection credential value is 3306.
    64  	// +optional
    65  	ConnectionCredential map[string]string `json:"connectionCredential,omitempty"`
    66  }
    67  
    68  // SystemAccountSpec specifies information to create system accounts.
    69  type SystemAccountSpec struct {
    70  	// cmdExecutorConfig configs how to get client SDK and perform statements.
    71  	// +kubebuilder:validation:Required
    72  	CmdExecutorConfig *CmdExecutorConfig `json:"cmdExecutorConfig"`
    73  	// passwordConfig defines the pattern to generate password.
    74  	// +kubebuilder:validation:Required
    75  	PasswordConfig PasswordConfig `json:"passwordConfig"`
    76  	// accounts defines system account config settings.
    77  	// +kubebuilder:validation:Required
    78  	// +kubebuilder:validation:MinItems=1
    79  	// +patchMergeKey=name
    80  	// +patchStrategy=merge,retainKeys
    81  	// +listType=map
    82  	// +listMapKey=name
    83  	Accounts []SystemAccountConfig `json:"accounts" patchStrategy:"merge,retainKeys" patchMergeKey:"name"`
    84  }
    85  
    86  // CmdExecutorConfig specifies how to perform creation and deletion statements.
    87  type CmdExecutorConfig struct {
    88  	CommandExecutorEnvItem `json:",inline"`
    89  	CommandExecutorItem    `json:",inline"`
    90  }
    91  
    92  // PasswordConfig helps provide to customize complexity of password generation pattern.
    93  type PasswordConfig struct {
    94  	// length defines the length of password.
    95  	// +kubebuilder:validation:Maximum=32
    96  	// +kubebuilder:validation:Minimum=8
    97  	// +kubebuilder:default=10
    98  	// +optional
    99  	Length int32 `json:"length,omitempty"`
   100  	//  numDigits defines number of digits.
   101  	// +kubebuilder:validation:Maximum=20
   102  	// +kubebuilder:validation:Minimum=0
   103  	// +kubebuilder:default=2
   104  	// +optional
   105  	NumDigits int32 `json:"numDigits,omitempty"`
   106  	// numSymbols defines number of symbols.
   107  	// +kubebuilder:validation:Maximum=20
   108  	// +kubebuilder:validation:Minimum=0
   109  	// +kubebuilder:default=0
   110  	// +optional
   111  	NumSymbols int32 `json:"numSymbols,omitempty"`
   112  	// letterCase defines to use lower-cases, upper-cases or mixed-cases of letters.
   113  	// +kubebuilder:default=MixedCases
   114  	// +optional
   115  	LetterCase LetterCase `json:"letterCase,omitempty"`
   116  }
   117  
   118  // SystemAccountConfig specifies how to create and delete system accounts.
   119  type SystemAccountConfig struct {
   120  	// name is the name of a system account.
   121  	// +kubebuilder:validation:Required
   122  	Name AccountName `json:"name"`
   123  	// provisionPolicy defines how to create account.
   124  	// +kubebuilder:validation:Required
   125  	ProvisionPolicy ProvisionPolicy `json:"provisionPolicy"`
   126  }
   127  
   128  // ProvisionPolicy defines the policy details for creating accounts.
   129  type ProvisionPolicy struct {
   130  	// type defines the way to provision an account, either `CreateByStmt` or `ReferToExisting`.
   131  	// +kubebuilder:validation:Required
   132  	Type ProvisionPolicyType `json:"type"`
   133  	// scope is the scope to provision account, and the scope could be `AnyPods` or `AllPods`.
   134  	// +kubebuilder:default=AnyPods
   135  	Scope ProvisionScope `json:"scope"`
   136  	// statements will be used when Type is CreateByStmt.
   137  	// +optional
   138  	Statements *ProvisionStatements `json:"statements,omitempty"`
   139  	// secretRef will be used when Type is ReferToExisting.
   140  	// +optional
   141  	SecretRef *ProvisionSecretRef `json:"secretRef,omitempty"`
   142  }
   143  
   144  // ProvisionSecretRef defines the information of secret referred to.
   145  type ProvisionSecretRef struct {
   146  	// name refers to the name of the secret.
   147  	// +kubebuilder:validation:Required
   148  	Name string `json:"name"`
   149  	// namespace refers to the namespace of the secret.
   150  	// +kubebuilder:validation:Required
   151  	Namespace string `json:"namespace"`
   152  }
   153  
   154  // ProvisionStatements defines the statements used to create accounts.
   155  type ProvisionStatements struct {
   156  	// creation specifies statement how to create this account with required privileges.
   157  	// +kubebuilder:validation:Required
   158  	CreationStatement string `json:"creation"`
   159  	// update specifies statement how to update account's password.
   160  	// +optional
   161  	UpdateStatement string `json:"update,omitempty"`
   162  	// deletion specifies statement how to delete this account.
   163  	// Used in combination with `CreateionStatement` to delete the account before create it.
   164  	// For instance, one usually uses `drop user if exists` statement followed by `create user` statement to create an account.
   165  	// Deprecated: this field is deprecated, use `update` instead.
   166  	// +optional
   167  	DeletionStatement string `json:"deletion,omitempty"`
   168  }
   169  
   170  // ClusterDefinitionStatus defines the observed state of ClusterDefinition
   171  type ClusterDefinitionStatus struct {
   172  	// ClusterDefinition phase, valid values are `empty`, `Available`, 'Unavailable`.
   173  	// Available is ClusterDefinition become available, and can be referenced for co-related objects.
   174  	Phase Phase `json:"phase,omitempty"`
   175  
   176  	// Extra message in current phase
   177  	// +optional
   178  	Message string `json:"message,omitempty"`
   179  
   180  	// observedGeneration is the most recent generation observed for this
   181  	// ClusterDefinition. It corresponds to the ClusterDefinition's generation, which is
   182  	// updated on mutation by the API Server.
   183  	// +optional
   184  	ObservedGeneration int64 `json:"observedGeneration,omitempty"`
   185  }
   186  
   187  func (r ClusterDefinitionStatus) GetTerminalPhases() []Phase {
   188  	return []Phase{AvailablePhase}
   189  }
   190  
   191  type ExporterConfig struct {
   192  	// scrapePort is exporter port for Time Series Database to scrape metrics.
   193  	// +kubebuilder:validation:Required
   194  	// +kubebuilder:validation:XIntOrString
   195  	ScrapePort intstr.IntOrString `json:"scrapePort"`
   196  
   197  	// scrapePath is exporter url path for Time Series Database to scrape metrics.
   198  	// +kubebuilder:validation:MaxLength=128
   199  	// +kubebuilder:default="/metrics"
   200  	// +optional
   201  	ScrapePath string `json:"scrapePath,omitempty"`
   202  }
   203  
   204  type MonitorConfig struct {
   205  	// builtIn is a switch to enable KubeBlocks builtIn monitoring.
   206  	// If BuiltIn is set to true, monitor metrics will be scraped automatically.
   207  	// If BuiltIn is set to false, the provider should set ExporterConfig and Sidecar container own.
   208  	// +kubebuilder:default=false
   209  	// +optional
   210  	BuiltIn bool `json:"builtIn,omitempty"`
   211  
   212  	// exporterConfig provided by provider, which specify necessary information to Time Series Database.
   213  	// exporterConfig is valid when builtIn is false.
   214  	// +optional
   215  	Exporter *ExporterConfig `json:"exporterConfig,omitempty"`
   216  }
   217  
   218  type LogConfig struct {
   219  	// name log type name, such as slow for MySQL slow log file.
   220  	// +kubebuilder:validation:Required
   221  	// +kubebuilder:validation:MaxLength=128
   222  	Name string `json:"name"`
   223  
   224  	// filePathPattern log file path pattern which indicate how to find this file
   225  	// corresponding to variable (log path) in database kernel. please don't set this casually.
   226  	// +kubebuilder:validation:Required
   227  	// +kubebuilder:validation:MaxLength=4096
   228  	FilePathPattern string `json:"filePathPattern"`
   229  }
   230  
   231  type VolumeTypeSpec struct {
   232  	// name definition is the same as the name of the VolumeMounts field in PodSpec.Container,
   233  	// similar to the relations of Volumes[*].name and VolumesMounts[*].name in Pod.Spec.
   234  	// +kubebuilder:validation:Required
   235  	// +kubebuilder:validation:Pattern:=`^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$`
   236  	Name string `json:"name"`
   237  
   238  	// type is in enum of {data, log}.
   239  	// VolumeTypeData: the volume is for the persistent data storage.
   240  	// VolumeTypeLog: the volume is for the persistent log storage.
   241  	// +optional
   242  	Type VolumeType `json:"type,omitempty"`
   243  }
   244  
   245  type VolumeProtectionSpec struct {
   246  	// The high watermark threshold for volume space usage.
   247  	// If there is any specified volumes who's space usage is over the threshold, the pre-defined "LOCK" action
   248  	// will be triggered to degrade the service to protect volume from space exhaustion, such as to set the instance
   249  	// as read-only. And after that, if all volumes' space usage drops under the threshold later, the pre-defined
   250  	// "UNLOCK" action will be performed to recover the service normally.
   251  	// +kubebuilder:validation:Maximum=100
   252  	// +kubebuilder:validation:Minimum=0
   253  	// +kubebuilder:default=90
   254  	// +optional
   255  	HighWatermark int `json:"highWatermark,omitempty"`
   256  
   257  	// Volumes to protect.
   258  	// +optional
   259  	Volumes []ProtectedVolume `json:"volumes,omitempty"`
   260  }
   261  
   262  type ProtectedVolume struct {
   263  	// Name of volume to protect.
   264  	// +optional
   265  	Name string `json:"name,omitempty"`
   266  
   267  	// Volume specified high watermark threshold, it will override the component level threshold.
   268  	// If the value is invalid, it will be ignored and the component level threshold will be used.
   269  	// +kubebuilder:validation:Maximum=100
   270  	// +kubebuilder:validation:Minimum=0
   271  	// +optional
   272  	HighWatermark *int `json:"highWatermark,omitempty"`
   273  }
   274  
   275  type ServiceRefDeclaration struct {
   276  	// The name of the service reference declaration.
   277  	// The service reference can come from an external service that is not part of KubeBlocks, or services provided by other KubeBlocks Cluster objects.
   278  	// The specific type of service reference depends on the binding declaration when creates a Cluster.
   279  	// +kubebuilder:validation:Required
   280  	Name string `json:"name"`
   281  
   282  	// serviceRefDeclarationSpecs is a collection of service descriptions for a service reference declaration.
   283  	// Each ServiceRefDeclarationSpec defines a service Kind and Version. When multiple ServiceRefDeclarationSpecs are defined,
   284  	// it indicates that the ServiceRefDeclaration can be any one of the specified ServiceRefDeclarationSpecs.
   285  	// For example, when the ServiceRefDeclaration is declared to require an OLTP database, which can be either MySQL or PostgreSQL,
   286  	// you can define a ServiceRefDeclarationSpec for MySQL and another ServiceRefDeclarationSpec for PostgreSQL,
   287  	// when referencing the service within the cluster, as long as the serviceKind and serviceVersion match either MySQL or PostgreSQL, it can be used.
   288  	// +kubebuilder:validation:Required
   289  	ServiceRefDeclarationSpecs []ServiceRefDeclarationSpec `json:"serviceRefDeclarationSpecs"`
   290  }
   291  
   292  type ServiceRefDeclarationSpec struct {
   293  	// service kind, indicating the type or nature of the service. It should be well-known application cluster type, e.g. {mysql, redis, mongodb}.
   294  	// The serviceKind is case-insensitive and supports abbreviations for some well-known databases.
   295  	// For example, both 'zk' and 'zookeeper' will be considered as a ZooKeeper cluster, and 'pg', 'postgres', 'postgresql' will all be considered as a PostgreSQL cluster.
   296  	// +kubebuilder:validation:Required
   297  	ServiceKind string `json:"serviceKind"`
   298  
   299  	// The service version of the service reference. It is a regular expression that matches a version number pattern.
   300  	// For example, `^8.0.8$`, `8.0.\d{1,2}$`, `^[v\-]*?(\d{1,2}\.){0,3}\d{1,2}$`
   301  	// +kubebuilder:validation:Required
   302  	ServiceVersion string `json:"serviceVersion"`
   303  }
   304  
   305  // ClusterComponentDefinition provides a workload component specification template,
   306  // with attributes that strongly work with stateful workloads and day-2 operations
   307  // behaviors.
   308  // +kubebuilder:validation:XValidation:rule="has(self.workloadType) && self.workloadType == 'Consensus' ? (has(self.consensusSpec) || has(self.rsmSpec)) : !has(self.consensusSpec)",message="componentDefs.consensusSpec(deprecated) or componentDefs.rsmSpec(recommended) is required when componentDefs.workloadType is Consensus, and forbidden otherwise"
   309  type ClusterComponentDefinition struct {
   310  	// A component definition name, this name could be used as default name of `Cluster.spec.componentSpecs.name`,
   311  	// and so this name is need to conform with same validation rules as `Cluster.spec.componentSpecs.name`, that
   312  	// is currently comply with IANA Service Naming rule. This name will apply to "apps.kubeblocks.io/component-name"
   313  	// object label value.
   314  	// +kubebuilder:validation:Required
   315  	// +kubebuilder:validation:MaxLength=22
   316  	// +kubebuilder:validation:Pattern:=`^[a-z]([a-z0-9\-]*[a-z0-9])?$`
   317  	Name string `json:"name"`
   318  
   319  	// The description of component definition.
   320  	// +optional
   321  	Description string `json:"description,omitempty"`
   322  
   323  	// workloadType defines type of the workload.
   324  	// Stateless is a stateless workload type used to describe stateless applications.
   325  	// Stateful is a stateful workload type used to describe common stateful applications.
   326  	// Consensus is a stateful workload type used to describe applications based on consensus protocols, common consensus protocols such as raft and paxos.
   327  	// Replication is a stateful workload type used to describe applications based on the primary-secondary data replication protocol.
   328  	// +kubebuilder:validation:Required
   329  	WorkloadType WorkloadType `json:"workloadType"`
   330  
   331  	// characterType defines well-known database component name, such as mongos(mongodb), proxy(redis), mariadb(mysql)
   332  	// KubeBlocks will generate proper monitor configs for well-known characterType when builtIn is true.
   333  	//
   334  	// CharacterType will also be used in role probe to decide which probe engine to use.
   335  	// current available candidates are: mysql, postgres, mongodb, redis, etcd, kafka.
   336  	// +optional
   337  	CharacterType string `json:"characterType,omitempty"`
   338  
   339  	// The configSpec field provided by provider, and
   340  	// finally this configTemplateRefs will be rendered into the user's own configuration file according to the user's cluster.
   341  	// +optional
   342  	// +patchMergeKey=name
   343  	// +patchStrategy=merge,retainKeys
   344  	// +listType=map
   345  	// +listMapKey=name
   346  	ConfigSpecs []ComponentConfigSpec `json:"configSpecs,omitempty"`
   347  
   348  	// The scriptSpec field provided by provider, and
   349  	// finally this configTemplateRefs will be rendered into the user's own configuration file according to the user's cluster.
   350  	// +optional
   351  	// +patchMergeKey=name
   352  	// +patchStrategy=merge,retainKeys
   353  	// +listType=map
   354  	// +listMapKey=name
   355  	// +optional
   356  	ScriptSpecs []ComponentTemplateSpec `json:"scriptSpecs,omitempty"`
   357  
   358  	// probes setting for healthy checks.
   359  	// +optional
   360  	Probes *ClusterDefinitionProbes `json:"probes,omitempty"`
   361  
   362  	// monitor is monitoring config which provided by provider.
   363  	// +optional
   364  	Monitor *MonitorConfig `json:"monitor,omitempty"`
   365  
   366  	// logConfigs is detail log file config which provided by provider.
   367  	// +optional
   368  	// +patchMergeKey=name
   369  	// +patchStrategy=merge,retainKeys
   370  	// +listType=map
   371  	// +listMapKey=name
   372  	LogConfigs []LogConfig `json:"logConfigs,omitempty" patchStrategy:"merge,retainKeys" patchMergeKey:"name"`
   373  
   374  	// podSpec define pod spec template of the cluster component.
   375  	// +kubebuilder:pruning:PreserveUnknownFields
   376  	// +optional
   377  	PodSpec *corev1.PodSpec `json:"podSpec,omitempty"`
   378  
   379  	// service defines the behavior of a service spec.
   380  	// provide read-write service when WorkloadType is Consensus.
   381  	// +optional
   382  	Service *ServiceSpec `json:"service,omitempty"`
   383  
   384  	// statelessSpec defines stateless related spec if workloadType is Stateless.
   385  	// +optional
   386  	//+kubebuilder:deprecatedversion:warning="This field is deprecated from KB 0.7.0, use RSMSpec instead."
   387  	StatelessSpec *StatelessSetSpec `json:"statelessSpec,omitempty"`
   388  
   389  	// statefulSpec defines stateful related spec if workloadType is Stateful.
   390  	// +optional
   391  	//+kubebuilder:deprecatedversion:warning="This field is deprecated from KB 0.7.0, use RSMSpec instead."
   392  	StatefulSpec *StatefulSetSpec `json:"statefulSpec,omitempty"`
   393  
   394  	// consensusSpec defines consensus related spec if workloadType is Consensus, required if workloadType is Consensus.
   395  	// +optional
   396  	//+kubebuilder:deprecatedversion:warning="This field is deprecated from KB 0.7.0, use RSMSpec instead."
   397  	ConsensusSpec *ConsensusSetSpec `json:"consensusSpec,omitempty"`
   398  
   399  	// replicationSpec defines replication related spec if workloadType is Replication.
   400  	// +optional
   401  	//+kubebuilder:deprecatedversion:warning="This field is deprecated from KB 0.7.0, use RSMSpec instead."
   402  	ReplicationSpec *ReplicationSetSpec `json:"replicationSpec,omitempty"`
   403  
   404  	// RSMSpec defines workload related spec of this component.
   405  	// start from KB 0.7.0, RSM(ReplicatedStateMachineSpec) will be the underlying CR which powers all kinds of workload in KB.
   406  	// RSM is an enhanced stateful workload extension dedicated for heavy-state workloads like databases.
   407  	// +optional
   408  	RSMSpec *RSMSpec `json:"rsmSpec,omitempty"`
   409  
   410  	// horizontalScalePolicy controls the behavior of horizontal scale.
   411  	// +optional
   412  	HorizontalScalePolicy *HorizontalScalePolicy `json:"horizontalScalePolicy,omitempty"`
   413  
   414  	// Statement to create system account.
   415  	// +optional
   416  	SystemAccounts *SystemAccountSpec `json:"systemAccounts,omitempty"`
   417  
   418  	// volumeTypes is used to describe the purpose of the volumes
   419  	// mapping the name of the VolumeMounts in the PodSpec.Container field,
   420  	// such as data volume, log volume, etc.
   421  	// When backing up the volume, the volume can be correctly backed up
   422  	// according to the volumeType.
   423  	//
   424  	// For example:
   425  	//  `name: data, type: data` means that the volume named `data` is used to store `data`.
   426  	//  `name: binlog, type: log` means that the volume named `binlog` is used to store `log`.
   427  	//
   428  	// NOTE:
   429  	//   When volumeTypes is not defined, the backup function will not be supported,
   430  	// even if a persistent volume has been specified.
   431  	// +listType=map
   432  	// +listMapKey=name
   433  	// +optional
   434  	VolumeTypes []VolumeTypeSpec `json:"volumeTypes,omitempty"`
   435  
   436  	// customLabelSpecs is used for custom label tags which you want to add to the component resources.
   437  	// +listType=map
   438  	// +listMapKey=key
   439  	// +optional
   440  	CustomLabelSpecs []CustomLabelSpec `json:"customLabelSpecs,omitempty"`
   441  
   442  	// switchoverSpec defines command to do switchover.
   443  	// in particular, when workloadType=Replication, the command defined in switchoverSpec will only be executed under the condition of cluster.componentSpecs[x].SwitchPolicy.type=Noop.
   444  	// +optional
   445  	SwitchoverSpec *SwitchoverSpec `json:"switchoverSpec,omitempty"`
   446  
   447  	// +optional
   448  	VolumeProtectionSpec *VolumeProtectionSpec `json:"volumeProtectionSpec,omitempty"`
   449  
   450  	// componentDefRef is used to inject values from other components into the current component.
   451  	// values will be saved and updated in a configmap and mounted to the current component.
   452  	// +patchMergeKey=componentDefName
   453  	// +patchStrategy=merge,retainKeys
   454  	// +listType=map
   455  	// +listMapKey=componentDefName
   456  	// +optional
   457  	ComponentDefRef []ComponentDefRef `json:"componentDefRef,omitempty" patchStrategy:"merge" patchMergeKey:"componentDefName"`
   458  
   459  	// serviceRefDeclarations is used to declare the service reference of the current component.
   460  	// +optional
   461  	ServiceRefDeclarations []ServiceRefDeclaration `json:"serviceRefDeclarations,omitempty"`
   462  }
   463  
   464  func (r *ClusterComponentDefinition) GetStatefulSetWorkload() StatefulSetWorkload {
   465  	switch r.WorkloadType {
   466  	case Stateless:
   467  		return nil
   468  	case Stateful:
   469  		return r.StatefulSpec
   470  	case Consensus:
   471  		return r.ConsensusSpec
   472  	case Replication:
   473  		return r.ReplicationSpec
   474  	}
   475  	panic("unreachable")
   476  }
   477  
   478  // GetMinAvailable get workload's minAvailable settings, return 51% for workloadType=Consensus,
   479  // value 1 pod for workloadType=[Stateless|Stateful|Replication].
   480  func (r *ClusterComponentDefinition) GetMinAvailable() *intstr.IntOrString {
   481  	if r == nil {
   482  		return nil
   483  	}
   484  	switch r.WorkloadType {
   485  	case Consensus:
   486  		// Consensus workload have min pods of >50%.
   487  		v := intstr.FromString("51%")
   488  		return &v
   489  	case Replication, Stateful, Stateless:
   490  		// Stateful & Replication workload have min. pod being 1.
   491  		v := intstr.FromInt(1)
   492  		return &v
   493  	}
   494  	return nil
   495  }
   496  
   497  // GetMaxUnavailable get workload's maxUnavailable settings, this value is not suitable for PDB.spec.maxUnavailable
   498  // usage, as a PDB with maxUnavailable=49% and if workload's replicaCount=3 and allowed disruption pod count is 2,
   499  // check following setup:
   500  //
   501  // #cmd: kubectl get sts,po,pdb  -l app.kubernetes.io/instance=consul
   502  // NAME                      READY   AGE
   503  // statefulset.apps/consul   3/3     3h23m
   504  //
   505  // NAME           READY   STATUS    RESTARTS   AGE
   506  // pod/consul-0   1/1     Running   0          3h
   507  // pod/consul-2   1/1     Running   0          16s
   508  // pod/consul-1   1/1     Running   0          16s
   509  //
   510  // NAME                                MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
   511  // poddisruptionbudget.policy/consul   N/A             49%               2                     3h23m
   512  //
   513  // VS. using minAvailable=51% will result allowed disruption pod count is 1
   514  //
   515  // NAME                      READY   AGE
   516  // statefulset.apps/consul   3/3     3h26m
   517  //
   518  // NAME           READY   STATUS    RESTARTS   AGE
   519  // pod/consul-0   1/1     Running   0          3h3m
   520  // pod/consul-2   1/1     Running   0          3m35s
   521  // pod/consul-1   1/1     Running   0          3m35s
   522  //
   523  // NAME                                MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
   524  // poddisruptionbudget.policy/consul   51%             N/A               1                     3h26m
   525  func (r *ClusterComponentDefinition) GetMaxUnavailable() *intstr.IntOrString {
   526  	if r == nil {
   527  		return nil
   528  	}
   529  
   530  	getMaxUnavailable := func(ssus appsv1.StatefulSetUpdateStrategy) *intstr.IntOrString {
   531  		if ssus.RollingUpdate == nil {
   532  			return nil
   533  		}
   534  		return ssus.RollingUpdate.MaxUnavailable
   535  	}
   536  
   537  	switch r.WorkloadType {
   538  	case Stateless:
   539  		if r.StatelessSpec == nil || r.StatelessSpec.UpdateStrategy.RollingUpdate == nil {
   540  			return nil
   541  		}
   542  		return r.StatelessSpec.UpdateStrategy.RollingUpdate.MaxUnavailable
   543  	case Stateful, Consensus, Replication:
   544  		_, s := r.GetStatefulSetWorkload().FinalStsUpdateStrategy()
   545  		return getMaxUnavailable(s)
   546  	}
   547  	panic("unreachable")
   548  }
   549  
   550  func (r *ClusterComponentDefinition) IsStatelessWorkload() bool {
   551  	return r.WorkloadType == Stateless
   552  }
   553  
   554  func (r *ClusterComponentDefinition) GetCommonStatefulSpec() (*StatefulSetSpec, error) {
   555  	if r.IsStatelessWorkload() {
   556  		return nil, ErrWorkloadTypeIsStateless
   557  	}
   558  	switch r.WorkloadType {
   559  	case Stateful:
   560  		return r.StatefulSpec, nil
   561  	case Consensus:
   562  		if r.ConsensusSpec != nil {
   563  			return &r.ConsensusSpec.StatefulSetSpec, nil
   564  		}
   565  	case Replication:
   566  		if r.ReplicationSpec != nil {
   567  			return &r.ReplicationSpec.StatefulSetSpec, nil
   568  		}
   569  	default:
   570  		panic("unreachable")
   571  		// return nil, ErrWorkloadTypeIsUnknown
   572  	}
   573  	return nil, nil
   574  }
   575  
   576  type ServiceSpec struct {
   577  	// The list of ports that are exposed by this service.
   578  	// More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies
   579  	// +patchMergeKey=port
   580  	// +patchStrategy=merge
   581  	// +listType=map
   582  	// +listMapKey=port
   583  	// +listMapKey=protocol
   584  	// +optional
   585  	Ports []ServicePort `json:"ports,omitempty" patchStrategy:"merge" patchMergeKey:"port" protobuf:"bytes,1,rep,name=ports"`
   586  
   587  	// NOTES: name also need to be key
   588  }
   589  
   590  func (r *ServiceSpec) toSVCPorts() []corev1.ServicePort {
   591  	ports := make([]corev1.ServicePort, 0, len(r.Ports))
   592  	for _, p := range r.Ports {
   593  		ports = append(ports, p.toSVCPort())
   594  	}
   595  	return ports
   596  }
   597  
   598  func (r ServiceSpec) ToSVCSpec() corev1.ServiceSpec {
   599  	return corev1.ServiceSpec{
   600  		Ports: r.toSVCPorts(),
   601  	}
   602  }
   603  
   604  type ServicePort struct {
   605  	// The name of this port within the service. This must be a DNS_LABEL.
   606  	// All ports within a ServiceSpec must have unique names. When considering
   607  	// the endpoints for a Service, this must match the 'name' field in the
   608  	// EndpointPort.
   609  	// +kubebuilder:validation:Required
   610  	Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"`
   611  
   612  	// The IP protocol for this port. Supports "TCP", "UDP", and "SCTP".
   613  	// Default is TCP.
   614  	// +kubebuilder:validation:Enum={TCP,UDP,SCTP}
   615  	// +default="TCP"
   616  	// +optional
   617  	Protocol corev1.Protocol `json:"protocol,omitempty" protobuf:"bytes,2,opt,name=protocol,casttype=Protocol"`
   618  
   619  	// The application protocol for this port.
   620  	// This field follows standard Kubernetes label syntax.
   621  	// Un-prefixed names are reserved for IANA standard service names (as per
   622  	// RFC-6335 and https://www.iana.org/assignments/service-names).
   623  	// Non-standard protocols should use prefixed names such as
   624  	// mycompany.com/my-custom-protocol.
   625  	// +optional
   626  	AppProtocol *string `json:"appProtocol,omitempty" protobuf:"bytes,6,opt,name=appProtocol"`
   627  
   628  	// The port that will be exposed by this service.
   629  	Port int32 `json:"port" protobuf:"varint,3,opt,name=port"`
   630  
   631  	// Number or name of the port to access on the pods targeted by the service.
   632  	// Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
   633  	// If this is a string, it will be looked up as a named port in the
   634  	// target Pod's container ports. If this is not specified, the value
   635  	// of the 'port' field is used (an identity map).
   636  	// This field is ignored for services with clusterIP=None, and should be
   637  	// omitted or set equal to the 'port' field.
   638  	// More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service
   639  	// +kubebuilder:validation:XIntOrString
   640  	// +optional
   641  	TargetPort intstr.IntOrString `json:"targetPort,omitempty" protobuf:"bytes,4,opt,name=targetPort"`
   642  }
   643  
   644  func (r *ServicePort) toSVCPort() corev1.ServicePort {
   645  	return corev1.ServicePort{
   646  		Name:        r.Name,
   647  		Protocol:    r.Protocol,
   648  		AppProtocol: r.AppProtocol,
   649  		Port:        r.Port,
   650  		TargetPort:  r.TargetPort,
   651  	}
   652  }
   653  
   654  type HorizontalScalePolicy struct {
   655  	// type controls what kind of data synchronization do when component scale out.
   656  	// Policy is in enum of {None, CloneVolume}. The default policy is `None`.
   657  	// None: Default policy, create empty volume and no data clone.
   658  	// CloneVolume: Do data clone to newly scaled pods. Prefer to use volume snapshot first,
   659  	//         and will try backup tool if volume snapshot is not enabled, finally
   660  	// 	       report error if both above cannot work.
   661  	// Snapshot: Deprecated, alias for CloneVolume.
   662  	// +kubebuilder:default=None
   663  	// +optional
   664  	Type HScaleDataClonePolicyType `json:"type,omitempty"`
   665  
   666  	// BackupPolicyTemplateName reference the backup policy template.
   667  	// +optional
   668  	BackupPolicyTemplateName string `json:"backupPolicyTemplateName,omitempty"`
   669  
   670  	// volumeMountsName defines which volumeMount of the container to do backup,
   671  	// only work if Type is not None
   672  	// if not specified, the 1st volumeMount will be chosen
   673  	// +optional
   674  	VolumeMountsName string `json:"volumeMountsName,omitempty"`
   675  }
   676  
   677  type ClusterDefinitionProbeCMDs struct {
   678  	// Write check executed on probe sidecar, used to check workload's allow write access.
   679  	// +optional
   680  	Writes []string `json:"writes,omitempty"`
   681  
   682  	// Read check executed on probe sidecar, used to check workload's readonly access.
   683  	// +optional
   684  	Queries []string `json:"queries,omitempty"`
   685  }
   686  
   687  type ClusterDefinitionProbe struct {
   688  	// How often (in seconds) to perform the probe.
   689  	// +kubebuilder:default=1
   690  	// +kubebuilder:validation:Minimum=1
   691  	PeriodSeconds int32 `json:"periodSeconds,omitempty"`
   692  
   693  	// Number of seconds after which the probe times out. Defaults to 1 second.
   694  	// +kubebuilder:default=1
   695  	// +kubebuilder:validation:Minimum=1
   696  	TimeoutSeconds int32 `json:"timeoutSeconds,omitempty"`
   697  
   698  	// Minimum consecutive failures for the probe to be considered failed after having succeeded.
   699  	// +kubebuilder:default=3
   700  	// +kubebuilder:validation:Minimum=2
   701  	FailureThreshold int32 `json:"failureThreshold,omitempty"`
   702  
   703  	// commands used to execute for probe.
   704  	// +optional
   705  	Commands *ClusterDefinitionProbeCMDs `json:"commands,omitempty"`
   706  }
   707  
   708  type ClusterDefinitionProbes struct {
   709  	// Probe for DB running check.
   710  	// +optional
   711  	RunningProbe *ClusterDefinitionProbe `json:"runningProbe,omitempty"`
   712  
   713  	// Probe for DB status check.
   714  	// +optional
   715  	StatusProbe *ClusterDefinitionProbe `json:"statusProbe,omitempty"`
   716  
   717  	// Probe for DB role changed check.
   718  	// +optional
   719  	//+kubebuilder:deprecatedversion:warning="This field is deprecated from KB 0.7.0, use RSMSpec instead."
   720  	RoleProbe *ClusterDefinitionProbe `json:"roleProbe,omitempty"`
   721  
   722  	// roleProbeTimeoutAfterPodsReady(in seconds), when all pods of the component are ready,
   723  	// it will detect whether the application is available in the pod.
   724  	// if pods exceed the InitializationTimeoutSeconds time without a role label,
   725  	// this component will enter the Failed/Abnormal phase.
   726  	// Note that this configuration will only take effect if the component supports RoleProbe
   727  	// and will not affect the life cycle of the pod. default values are 60 seconds.
   728  	// +optional
   729  	// +kubebuilder:validation:Minimum=30
   730  	RoleProbeTimeoutAfterPodsReady int32 `json:"roleProbeTimeoutAfterPodsReady,omitempty"`
   731  }
   732  
   733  type StatelessSetSpec struct {
   734  	// updateStrategy defines the underlying deployment strategy to use to replace existing pods with new ones.
   735  	// +optional
   736  	// +patchStrategy=retainKeys
   737  	UpdateStrategy appsv1.DeploymentStrategy `json:"updateStrategy,omitempty"`
   738  }
   739  
   740  type StatefulSetSpec struct {
   741  	// updateStrategy, Pods update strategy.
   742  	// In case of workloadType=Consensus the update strategy will be following:
   743  	//
   744  	// serial: update Pods one by one that guarantee minimum component unavailable time.
   745  	// 		Learner -> Follower(with AccessMode=none) -> Follower(with AccessMode=readonly) -> Follower(with AccessMode=readWrite) -> Leader
   746  	// bestEffortParallel: update Pods in parallel that guarantee minimum component un-writable time.
   747  	//		Learner, Follower(minority) in parallel -> Follower(majority) -> Leader, keep majority online all the time.
   748  	// parallel: force parallel
   749  	// +kubebuilder:default=Serial
   750  	// +optional
   751  	UpdateStrategy UpdateStrategy `json:"updateStrategy,omitempty"`
   752  
   753  	// llPodManagementPolicy is the low-level controls how pods are created during initial scale up,
   754  	// when replacing pods on nodes, or when scaling down.
   755  	// `OrderedReady` policy specify where pods are created in increasing order (pod-0, then
   756  	// pod-1, etc) and the controller will wait until each pod is ready before
   757  	// continuing. When scaling down, the pods are removed in the opposite order.
   758  	// `Parallel` policy specify create pods in parallel
   759  	// to match the desired scale without waiting, and on scale down will delete
   760  	// all pods at once.
   761  	// +optional
   762  	LLPodManagementPolicy appsv1.PodManagementPolicyType `json:"llPodManagementPolicy,omitempty"`
   763  
   764  	// llUpdateStrategy indicates the low-level StatefulSetUpdateStrategy that will be
   765  	// employed to update Pods in the StatefulSet when a revision is made to
   766  	// Template. Will ignore `updateStrategy` attribute if provided.
   767  	// +optional
   768  	LLUpdateStrategy *appsv1.StatefulSetUpdateStrategy `json:"llUpdateStrategy,omitempty"`
   769  }
   770  
   771  var _ StatefulSetWorkload = &StatefulSetSpec{}
   772  
   773  func (r *StatefulSetSpec) GetUpdateStrategy() UpdateStrategy {
   774  	if r == nil {
   775  		return SerialStrategy
   776  	}
   777  	return r.UpdateStrategy
   778  }
   779  
   780  func (r *StatefulSetSpec) FinalStsUpdateStrategy() (appsv1.PodManagementPolicyType, appsv1.StatefulSetUpdateStrategy) {
   781  	if r == nil {
   782  		r = &StatefulSetSpec{
   783  			UpdateStrategy: SerialStrategy,
   784  		}
   785  	}
   786  	return r.finalStsUpdateStrategy()
   787  }
   788  
   789  func (r *StatefulSetSpec) finalStsUpdateStrategy() (appsv1.PodManagementPolicyType, appsv1.StatefulSetUpdateStrategy) {
   790  	if r.LLUpdateStrategy != nil {
   791  		return r.LLPodManagementPolicy, *r.LLUpdateStrategy
   792  	}
   793  
   794  	zeroPartition := int32(0)
   795  	switch r.UpdateStrategy {
   796  	case BestEffortParallelStrategy:
   797  		m := intstr.FromString("49%")
   798  		return appsv1.ParallelPodManagement, appsv1.StatefulSetUpdateStrategy{
   799  			Type: appsv1.RollingUpdateStatefulSetStrategyType,
   800  			RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
   801  				// explicitly set the partition as 0 to avoid update workload unexpectedly.
   802  				Partition: &zeroPartition,
   803  				// alpha feature since v1.24
   804  				// ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#maximum-unavailable-pods
   805  				MaxUnavailable: &m,
   806  			},
   807  		}
   808  	case ParallelStrategy:
   809  		return appsv1.ParallelPodManagement, appsv1.StatefulSetUpdateStrategy{
   810  			Type: appsv1.RollingUpdateStatefulSetStrategyType,
   811  		}
   812  	case SerialStrategy:
   813  		fallthrough
   814  	default:
   815  		m := intstr.FromInt(1)
   816  		return appsv1.OrderedReadyPodManagement, appsv1.StatefulSetUpdateStrategy{
   817  			Type: appsv1.RollingUpdateStatefulSetStrategyType,
   818  			RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{
   819  				// explicitly set the partition as 0 to avoid update workload unexpectedly.
   820  				Partition: &zeroPartition,
   821  				// alpha feature since v1.24
   822  				// ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#maximum-unavailable-pods
   823  				MaxUnavailable: &m,
   824  			},
   825  		}
   826  	}
   827  }
   828  
   829  type ConsensusSetSpec struct {
   830  	StatefulSetSpec `json:",inline"`
   831  
   832  	// leader, one single leader.
   833  	// +kubebuilder:validation:Required
   834  	Leader ConsensusMember `json:"leader"`
   835  
   836  	// followers, has voting right but not Leader.
   837  	// +optional
   838  	Followers []ConsensusMember `json:"followers,omitempty"`
   839  
   840  	// learner, no voting right.
   841  	// +optional
   842  	Learner *ConsensusMember `json:"learner,omitempty"`
   843  }
   844  
   845  var _ StatefulSetWorkload = &ConsensusSetSpec{}
   846  
   847  func (r *ConsensusSetSpec) GetUpdateStrategy() UpdateStrategy {
   848  	if r == nil {
   849  		return SerialStrategy
   850  	}
   851  	return r.UpdateStrategy
   852  }
   853  
   854  func (r *ConsensusSetSpec) FinalStsUpdateStrategy() (appsv1.PodManagementPolicyType, appsv1.StatefulSetUpdateStrategy) {
   855  	if r == nil {
   856  		r = NewConsensusSetSpec()
   857  	}
   858  	if r.LLUpdateStrategy != nil {
   859  		return r.LLPodManagementPolicy, *r.LLUpdateStrategy
   860  	}
   861  	_, s := r.StatefulSetSpec.finalStsUpdateStrategy()
   862  	// switch r.UpdateStrategy {
   863  	// case SerialStrategy, BestEffortParallelStrategy:
   864  	s.Type = appsv1.OnDeleteStatefulSetStrategyType
   865  	s.RollingUpdate = nil
   866  	// }
   867  	return appsv1.ParallelPodManagement, s
   868  }
   869  
   870  func NewConsensusSetSpec() *ConsensusSetSpec {
   871  	return &ConsensusSetSpec{
   872  		Leader: DefaultLeader,
   873  		StatefulSetSpec: StatefulSetSpec{
   874  			UpdateStrategy: SerialStrategy,
   875  		},
   876  	}
   877  }
   878  
   879  type ConsensusMember struct {
   880  	// name, role name.
   881  	// +kubebuilder:validation:Required
   882  	// +kubebuilder:default=leader
   883  	Name string `json:"name"`
   884  
   885  	// accessMode, what service this member capable.
   886  	// +kubebuilder:validation:Required
   887  	// +kubebuilder:default=ReadWrite
   888  	AccessMode AccessMode `json:"accessMode"`
   889  
   890  	// replicas, number of Pods of this role.
   891  	// default 1 for Leader
   892  	// default 0 for Learner
   893  	// default Cluster.spec.componentSpec[*].Replicas - Leader.Replicas - Learner.Replicas for Followers
   894  	// +kubebuilder:default=0
   895  	// +kubebuilder:validation:Minimum=0
   896  	// +optional
   897  	Replicas *int32 `json:"replicas,omitempty"`
   898  }
   899  
   900  type RSMSpec struct {
   901  	// Roles, a list of roles defined in the system.
   902  	// +optional
   903  	Roles []workloads.ReplicaRole `json:"roles,omitempty"`
   904  
   905  	// RoleProbe provides method to probe role.
   906  	// +optional
   907  	RoleProbe *workloads.RoleProbe `json:"roleProbe,omitempty"`
   908  
   909  	// MembershipReconfiguration provides actions to do membership dynamic reconfiguration.
   910  	// +optional
   911  	MembershipReconfiguration *workloads.MembershipReconfiguration `json:"membershipReconfiguration,omitempty"`
   912  
   913  	// MemberUpdateStrategy, Members(Pods) update strategy.
   914  	// serial: update Members one by one that guarantee minimum component unavailable time.
   915  	// 		Learner -> Follower(with AccessMode=none) -> Follower(with AccessMode=readonly) -> Follower(with AccessMode=readWrite) -> Leader
   916  	// bestEffortParallel: update Members in parallel that guarantee minimum component un-writable time.
   917  	//		Learner, Follower(minority) in parallel -> Follower(majority) -> Leader, keep majority online all the time.
   918  	// parallel: force parallel
   919  	// +kubebuilder:validation:Enum={Serial,BestEffortParallel,Parallel}
   920  	// +optional
   921  	MemberUpdateStrategy *workloads.MemberUpdateStrategy `json:"memberUpdateStrategy,omitempty"`
   922  }
   923  
   924  type ReplicationSetSpec struct {
   925  	StatefulSetSpec `json:",inline"`
   926  }
   927  
   928  var _ StatefulSetWorkload = &ReplicationSetSpec{}
   929  
   930  func (r *ReplicationSetSpec) GetUpdateStrategy() UpdateStrategy {
   931  	if r == nil {
   932  		return SerialStrategy
   933  	}
   934  	return r.UpdateStrategy
   935  }
   936  
   937  func (r *ReplicationSetSpec) FinalStsUpdateStrategy() (appsv1.PodManagementPolicyType, appsv1.StatefulSetUpdateStrategy) {
   938  	if r == nil {
   939  		r = &ReplicationSetSpec{}
   940  	}
   941  	if r.LLUpdateStrategy != nil {
   942  		return r.LLPodManagementPolicy, *r.LLUpdateStrategy
   943  	}
   944  	_, s := r.StatefulSetSpec.finalStsUpdateStrategy()
   945  	s.Type = appsv1.OnDeleteStatefulSetStrategyType
   946  	s.RollingUpdate = nil
   947  	return appsv1.ParallelPodManagement, s
   948  }
   949  
   950  type SwitchoverSpec struct {
   951  	// withCandidate corresponds to the switchover of the specified candidate primary or leader instance.
   952  	// +optional
   953  	WithCandidate *SwitchoverAction `json:"withCandidate,omitempty"`
   954  
   955  	// withoutCandidate corresponds to a switchover that does not specify a candidate primary or leader instance.
   956  	// +optional
   957  	WithoutCandidate *SwitchoverAction `json:"withoutCandidate,omitempty"`
   958  }
   959  
   960  type SwitchoverAction struct {
   961  	// cmdExecutorConfig is the executor configuration of the switchover command.
   962  	// +kubebuilder:validation:Required
   963  	CmdExecutorConfig *CmdExecutorConfig `json:"cmdExecutorConfig"`
   964  
   965  	// scriptSpecSelectors defines the selector of the scriptSpecs that need to be referenced.
   966  	// Once ScriptSpecSelectors is defined, the scripts defined in scriptSpecs can be referenced in the SwitchoverAction.CmdExecutorConfig.
   967  	// +optional
   968  	ScriptSpecSelectors []ScriptSpecSelector `json:"scriptSpecSelectors,omitempty"`
   969  }
   970  
   971  type ScriptSpecSelector struct {
   972  	// ScriptSpec name of the referent, refer to componentDefs[x].scriptSpecs[y].Name.
   973  	// +kubebuilder:validation:Required
   974  	// +kubebuilder:validation:MaxLength=63
   975  	// +kubebuilder:validation:Pattern:=`^[a-z0-9]([a-z0-9\.\-]*[a-z0-9])?$`
   976  	Name string `json:"name"`
   977  }
   978  
   979  type CommandExecutorEnvItem struct {
   980  	// image for Connector when executing the command.
   981  	// +kubebuilder:validation:Required
   982  	Image string `json:"image"`
   983  	// envs is a list of environment variables.
   984  	// +kubebuilder:pruning:PreserveUnknownFields
   985  	// +patchMergeKey=name
   986  	// +patchStrategy=merge,retainKeys
   987  	// +optional
   988  	Env []corev1.EnvVar `json:"env,omitempty" patchStrategy:"merge" patchMergeKey:"name"`
   989  }
   990  
   991  type CommandExecutorItem struct {
   992  	// command to perform statements.
   993  	// +kubebuilder:validation:Required
   994  	// +kubebuilder:validation:MinItems=1
   995  	Command []string `json:"command"`
   996  	// args is used to perform statements.
   997  	// +optional
   998  	Args []string `json:"args,omitempty"`
   999  }
  1000  
  1001  type CustomLabelSpec struct {
  1002  	// key name of label
  1003  	// +kubebuilder:validation:Required
  1004  	Key string `json:"key"`
  1005  
  1006  	// value of label
  1007  	// +kubebuilder:validation:Required
  1008  	Value string `json:"value"`
  1009  
  1010  	// resources defines the resources to be labeled.
  1011  	// +kubebuilder:validation:Required
  1012  	Resources []GVKResource `json:"resources,omitempty"`
  1013  }
  1014  
  1015  type GVKResource struct {
  1016  	// gvk is Group/Version/Kind, for example "v1/Pod", "apps/v1/StatefulSet", etc.
  1017  	// when the gvk resource filtered by the selector already exists, if there is no corresponding custom label, it will be added, and if label already exists, it will be updated.
  1018  	// +kubebuilder:validation:Required
  1019  	GVK string `json:"gvk"`
  1020  
  1021  	// selector is a label query over a set of resources.
  1022  	// +optional
  1023  	Selector map[string]string `json:"selector,omitempty"`
  1024  }
  1025  
  1026  // +genclient
  1027  // +genclient:nonNamespaced
  1028  // +k8s:openapi-gen=true
  1029  // +kubebuilder:object:root=true
  1030  // +kubebuilder:subresource:status
  1031  // +kubebuilder:resource:categories={kubeblocks},scope=Cluster,shortName=cd
  1032  // +kubebuilder:printcolumn:name="MAIN-COMPONENT-NAME",type="string",JSONPath=".spec.componentDefs[0].name",description="main component names"
  1033  // +kubebuilder:printcolumn:name="STATUS",type="string",JSONPath=".status.phase",description="status phase"
  1034  // +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp"
  1035  
  1036  // ClusterDefinition is the Schema for the clusterdefinitions API
  1037  type ClusterDefinition struct {
  1038  	metav1.TypeMeta   `json:",inline"`
  1039  	metav1.ObjectMeta `json:"metadata,omitempty"`
  1040  
  1041  	Spec   ClusterDefinitionSpec   `json:"spec,omitempty"`
  1042  	Status ClusterDefinitionStatus `json:"status,omitempty"`
  1043  }
  1044  
  1045  // +kubebuilder:object:root=true
  1046  
  1047  // ClusterDefinitionList contains a list of ClusterDefinition
  1048  type ClusterDefinitionList struct {
  1049  	metav1.TypeMeta `json:",inline"`
  1050  	metav1.ListMeta `json:"metadata,omitempty"`
  1051  	Items           []ClusterDefinition `json:"items"`
  1052  }
  1053  
  1054  func init() {
  1055  	SchemeBuilder.Register(&ClusterDefinition{}, &ClusterDefinitionList{})
  1056  }
  1057  
  1058  // ValidateEnabledLogConfigs validates enabledLogs against component compDefName, and returns the invalid logNames undefined in ClusterDefinition.
  1059  func (r *ClusterDefinition) ValidateEnabledLogConfigs(compDefName string, enabledLogs []string) []string {
  1060  	invalidLogNames := make([]string, 0, len(enabledLogs))
  1061  	logTypes := make(map[string]struct{})
  1062  	for _, comp := range r.Spec.ComponentDefs {
  1063  		if !strings.EqualFold(compDefName, comp.Name) {
  1064  			continue
  1065  		}
  1066  		for _, logConfig := range comp.LogConfigs {
  1067  			logTypes[logConfig.Name] = struct{}{}
  1068  		}
  1069  	}
  1070  	// imply that all values in enabledLogs config are invalid.
  1071  	if len(logTypes) == 0 {
  1072  		return enabledLogs
  1073  	}
  1074  	for _, name := range enabledLogs {
  1075  		if _, ok := logTypes[name]; !ok {
  1076  			invalidLogNames = append(invalidLogNames, name)
  1077  		}
  1078  	}
  1079  	return invalidLogNames
  1080  }
  1081  
  1082  // GetComponentDefByName gets component definition from ClusterDefinition with compDefName
  1083  func (r *ClusterDefinition) GetComponentDefByName(compDefName string) *ClusterComponentDefinition {
  1084  	for _, component := range r.Spec.ComponentDefs {
  1085  		if component.Name == compDefName {
  1086  			return &component
  1087  		}
  1088  	}
  1089  	return nil
  1090  }
  1091  
  1092  // FailurePolicyType specifies the type of failure policy
  1093  // +enum
  1094  // +kubebuilder:validation:Enum={Ignore,Fail}
  1095  type FailurePolicyType string
  1096  
  1097  const (
  1098  	// Ignore means that an error will be ignored but logged.
  1099  	FailurePolicyIgnore FailurePolicyType = "Ignore"
  1100  	// ReportError means that an error will be reported.
  1101  	FailurePolicyFail FailurePolicyType = "Fail"
  1102  )
  1103  
  1104  // ComponentValueFromType specifies the type of component value from.
  1105  // +enum
  1106  // +kubebuilder:validation:Enum={FieldRef,ServiceRef,HeadlessServiceRef}
  1107  type ComponentValueFromType string
  1108  
  1109  const (
  1110  	FromFieldRef           ComponentValueFromType = "FieldRef"
  1111  	FromServiceRef         ComponentValueFromType = "ServiceRef"
  1112  	FromHeadlessServiceRef ComponentValueFromType = "HeadlessServiceRef"
  1113  )
  1114  
  1115  // ComponentDefRef is used to select the component and its fields to be referenced.
  1116  type ComponentDefRef struct {
  1117  	// componentDefName is the name of the componentDef to select.
  1118  	// +kubebuilder:validation:Required
  1119  	ComponentDefName string `json:"componentDefName"`
  1120  	// failurePolicy is the failure policy of the component.
  1121  	// If failed to find the component, the failure policy will be used.
  1122  	// +kubebuilder:validation:Enum={Ignore,Fail}
  1123  	// +default="Ignore"
  1124  	// +optional
  1125  	FailurePolicy FailurePolicyType `json:"failurePolicy,omitempty"`
  1126  	// componentRefEnv specifies a list of values to be injected as env variables to each component.
  1127  	// +kbubebuilder:validation:Required
  1128  	// +patchMergeKey=name
  1129  	// +patchStrategy=merge,retainKeys
  1130  	// +listType=map
  1131  	// +listMapKey=name
  1132  	// +optional
  1133  	ComponentRefEnvs []ComponentRefEnv `json:"componentRefEnv" patchStrategy:"merge" patchMergeKey:"name"`
  1134  }
  1135  
  1136  // ComponentRefEnv specifies name and value of an env.
  1137  type ComponentRefEnv struct {
  1138  	// name is the name of the env to be injected, and it must be a C identifier.
  1139  	// +kubebuilder:validation:Required
  1140  	// +kubebuilder:validation:Pattern=`^[A-Za-z_][A-Za-z0-9_]*$`
  1141  	Name string `json:"name"`
  1142  	// value is the value of the env to be injected.
  1143  	// +optional
  1144  	Value string `json:"value,omitempty"`
  1145  	// valueFrom specifies the source of the env to be injected.
  1146  	// +optional
  1147  	ValueFrom *ComponentValueFrom `json:"valueFrom,omitempty"`
  1148  }
  1149  
  1150  type ComponentValueFrom struct {
  1151  	// type is the type of the source to select. There are three types: `FieldRef`, `ServiceRef`, `HeadlessServiceRef`.
  1152  	// +kubebuilder:validation:Enum={FieldRef,ServiceRef,HeadlessServiceRef}
  1153  	// +kubebuilder:validation:Required
  1154  	Type ComponentValueFromType `json:"type"`
  1155  	// fieldRef is the jsonpath of the source to select when type is `FieldRef`.
  1156  	// there are two objects registered in the jsonpath: `componentDef` and `components`.
  1157  	// componentDef is the component definition object specified in `componentRef.componentDefName`.
  1158  	// components is the component list objects referring to the component definition object.
  1159  	// +optional
  1160  	FieldPath string `json:"fieldPath,omitempty"`
  1161  	// format is the format of each headless service address.
  1162  	// there are three builtin variables can be used as placeholder: $POD_ORDINAL, $POD_FQDN, $POD_NAME
  1163  	// $POD_ORDINAL is the ordinal of the pod.
  1164  	// $POD_FQDN is the fully qualified domain name of the pod.
  1165  	// $POD_NAME is the name of the pod
  1166  	// +optional
  1167  	// +kubebuilder:default=="$POD_FQDN"
  1168  	Format string `json:"format,omitempty"`
  1169  	// joinWith is the string to join the values of headless service addresses.
  1170  	// +optional
  1171  	// +kubebuilder:default=","
  1172  	JoinWith string `json:"joinWith,omitempty"`
  1173  }