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 }