diff --git a/docs/api-references/docs.md b/docs/api-references/docs.md index 7c06701e76..5f559ceaa0 100644 --- a/docs/api-references/docs.md +++ b/docs/api-references/docs.md @@ -734,6 +734,20 @@ TiFlashSpec +ticdc
+ + +TiCDCSpec + + + + +(Optional) +

TiCDC cluster spec

+ + + + pump
@@ -2624,6 +2638,7 @@ and component-level overrides

(Appears on:
PDSpec, PumpSpec, +TiCDCSpec, TiDBSpec, TiFlashSpec, TiKVSpec) @@ -8056,6 +8071,88 @@ Same for other components.

+

TiCDCSpec

+

+(Appears on: +TidbClusterSpec) +

+

+

TiCDCSpec contains details of TiCDC members

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+ComponentSpec
+ + +ComponentSpec + + +
+

+(Members of ComponentSpec are embedded into this type.) +

+
+ResourceRequirements
+ + +Kubernetes core/v1.ResourceRequirements + + +
+

+(Members of ResourceRequirements are embedded into this type.) +

+
+serviceAccount
+ +string + +
+

Specify a Service Account for TiCDC

+
+replicas
+ +int32 + +
+

The desired ready replicas

+
+baseImage
+ +string + +
+(Optional) +

Base image of the component, image tag is now allowed during validation

+

TiDBAccessConfig

(Appears on: @@ -13846,6 +13943,20 @@ TiFlashSpec +ticdc
+ + +TiCDCSpec + + + + +(Optional) +

TiCDC cluster spec

+ + + + pump
diff --git a/manifests/crd.yaml b/manifests/crd.yaml index 7c4ab444b1..6a8cc0449c 100644 --- a/manifests/crd.yaml +++ b/manifests/crd.yaml @@ -1379,6 +1379,414 @@ spec: type: string schedulerName: type: string + ticdc: + properties: + affinity: + properties: + nodeAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + preference: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + format: int32 + type: integer + required: + - weight + - preference + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + properties: + nodeSelectorTerms: + items: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - weight + - podAffinityTerm + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + properties: + preferredDuringSchedulingIgnoredDuringExecution: + items: + properties: + podAffinityTerm: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - weight + - podAffinityTerm + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + items: + properties: + labelSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + type: object + type: object + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + annotations: + type: object + baseImage: + type: string + configUpdateStrategy: + type: string + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + resourceFieldRef: + properties: + containerName: + type: string + divisor: {} + resource: + type: string + required: + - resource + type: object + secretKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + hostNetwork: + type: boolean + imagePullPolicy: + type: string + limits: + type: object + nodeSelector: + type: object + podSecurityContext: + properties: + fsGroup: + format: int64 + type: integer + runAsGroup: + format: int64 + type: integer + runAsNonRoot: + type: boolean + runAsUser: + format: int64 + type: integer + seLinuxOptions: + properties: + level: + type: string + role: + type: string + type: + type: string + user: + type: string + type: object + supplementalGroups: + items: + format: int64 + type: integer + type: array + sysctls: + items: + properties: + name: + type: string + value: + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + properties: + gmsaCredentialSpec: + type: string + gmsaCredentialSpecName: + type: string + runAsUserName: + type: string + type: object + type: object + priorityClassName: + type: string + replicas: + format: int32 + type: integer + requests: + type: object + schedulerName: + type: string + serviceAccount: + type: string + tolerations: + items: + properties: + effect: + type: string + key: + type: string + operator: + type: string + tolerationSeconds: + format: int64 + type: integer + value: + type: string + type: object + type: array + version: + type: string + required: + - replicas + type: object tidb: properties: affinity: diff --git a/pkg/apis/pingcap/v1alpha1/defaulting/tidbcluster.go b/pkg/apis/pingcap/v1alpha1/defaulting/tidbcluster.go index a9b76529ef..99551f1884 100644 --- a/pkg/apis/pingcap/v1alpha1/defaulting/tidbcluster.go +++ b/pkg/apis/pingcap/v1alpha1/defaulting/tidbcluster.go @@ -25,6 +25,7 @@ const ( defaultPDImage = "pingcap/pd" defaultBinlogImage = "pingcap/tidb-binlog" defaultTiFlashImage = "pingcap/tiflash" + defaultTiCDCImage = "pingcap/ticdc" ) var ( @@ -42,6 +43,9 @@ func SetTidbClusterDefault(tc *v1alpha1.TidbCluster) { if tc.Spec.TiFlash != nil { setTiFlashSpecDefault(tc) } + if tc.Spec.TiCDC != nil { + setTiCDCSpecDefault(tc) + } } // setTidbClusterSpecDefault is only managed the property under Spec @@ -130,3 +134,11 @@ func setTiFlashSpecDefault(tc *v1alpha1.TidbCluster) { tc.Spec.TiFlash.MaxFailoverCount = pointer.Int32Ptr(3) } } + +func setTiCDCSpecDefault(tc *v1alpha1.TidbCluster) { + if len(tc.Spec.Version) > 0 || tc.Spec.TiCDC.Version != nil { + if tc.Spec.TiCDC.BaseImage == "" { + tc.Spec.TiCDC.BaseImage = defaultTiCDCImage + } + } +} diff --git a/pkg/apis/pingcap/v1alpha1/openapi_generated.go b/pkg/apis/pingcap/v1alpha1/openapi_generated.go index dc481648ba..442eabe8b0 100644 --- a/pkg/apis/pingcap/v1alpha1/openapi_generated.go +++ b/pkg/apis/pingcap/v1alpha1/openapi_generated.go @@ -88,6 +88,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.StmtSummary": schema_pkg_apis_pingcap_v1alpha1_StmtSummary(ref), "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.StorageClaim": schema_pkg_apis_pingcap_v1alpha1_StorageClaim(ref), "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.StorageProvider": schema_pkg_apis_pingcap_v1alpha1_StorageProvider(ref), + "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiCDCSpec": schema_pkg_apis_pingcap_v1alpha1_TiCDCSpec(ref), "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiDBAccessConfig": schema_pkg_apis_pingcap_v1alpha1_TiDBAccessConfig(ref), "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiDBConfig": schema_pkg_apis_pingcap_v1alpha1_TiDBConfig(ref), "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiDBServiceSpec": schema_pkg_apis_pingcap_v1alpha1_TiDBServiceSpec(ref), @@ -3956,6 +3957,181 @@ func schema_pkg_apis_pingcap_v1alpha1_StorageProvider(ref common.ReferenceCallba } } +func schema_pkg_apis_pingcap_v1alpha1_TiCDCSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "TiCDCSpec contains details of TiCDC members", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "version": { + SchemaProps: spec.SchemaProps{ + Description: "Version of the component. Override the cluster-level version if non-empty Optional: Defaults to cluster-level setting", + Type: []string{"string"}, + Format: "", + }, + }, + "imagePullPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "ImagePullPolicy of the component. Override the cluster-level imagePullPolicy if present Optional: Defaults to cluster-level setting", + Type: []string{"string"}, + Format: "", + }, + }, + "hostNetwork": { + SchemaProps: spec.SchemaProps{ + Description: "Whether Hostnetwork of the component is enabled. Override the cluster-level setting if present Optional: Defaults to cluster-level setting", + Type: []string{"boolean"}, + Format: "", + }, + }, + "affinity": { + SchemaProps: spec.SchemaProps{ + Description: "Affinity of the component. Override the cluster-level one if present Optional: Defaults to cluster-level setting", + Ref: ref("k8s.io/api/core/v1.Affinity"), + }, + }, + "priorityClassName": { + SchemaProps: spec.SchemaProps{ + Description: "PriorityClassName of the component. Override the cluster-level one if present Optional: Defaults to cluster-level setting", + Type: []string{"string"}, + Format: "", + }, + }, + "schedulerName": { + SchemaProps: spec.SchemaProps{ + Description: "SchedulerName of the component. Override the cluster-level one if present Optional: Defaults to cluster-level setting", + Type: []string{"string"}, + Format: "", + }, + }, + "nodeSelector": { + SchemaProps: spec.SchemaProps{ + Description: "NodeSelector of the component. Merged into the cluster-level nodeSelector if non-empty Optional: Defaults to cluster-level setting", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "annotations": { + SchemaProps: spec.SchemaProps{ + Description: "Annotations of the component. Merged into the cluster-level annotations if non-empty Optional: Defaults to cluster-level setting", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "tolerations": { + SchemaProps: spec.SchemaProps{ + Description: "Tolerations of the component. Override the cluster-level tolerations if non-empty Optional: Defaults to cluster-level setting", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.Toleration"), + }, + }, + }, + }, + }, + "podSecurityContext": { + SchemaProps: spec.SchemaProps{ + Description: "PodSecurityContext of the component", + Ref: ref("k8s.io/api/core/v1.PodSecurityContext"), + }, + }, + "configUpdateStrategy": { + SchemaProps: spec.SchemaProps{ + Description: "ConfigUpdateStrategy of the component. Override the cluster-level updateStrategy if present Optional: Defaults to cluster-level setting", + Type: []string{"string"}, + Format: "", + }, + }, + "env": { + SchemaProps: spec.SchemaProps{ + Description: "List of environment variables to set in the container, like v1.Container.Env. Note that following env names cannot be used and may be overrided by tidb-operator built envs. - NAMESPACE - TZ - SERVICE_NAME - PEER_SERVICE_NAME - HEADLESS_SERVICE_NAME - SET_NAME - HOSTNAME - CLUSTER_NAME - POD_NAME - BINLOG_ENABLED - SLOW_LOG_FILE", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/api/core/v1.EnvVar"), + }, + }, + }, + }, + }, + "limits": { + SchemaProps: spec.SchemaProps{ + Description: "Limits describes the maximum amount of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "requests": { + SchemaProps: spec.SchemaProps{ + Description: "Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/", + Type: []string{"object"}, + AdditionalProperties: &spec.SchemaOrBool{ + Allows: true, + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Ref: ref("k8s.io/apimachinery/pkg/api/resource.Quantity"), + }, + }, + }, + }, + }, + "serviceAccount": { + SchemaProps: spec.SchemaProps{ + Description: "Specify a Service Account for TiCDC", + Type: []string{"string"}, + Format: "", + }, + }, + "replicas": { + SchemaProps: spec.SchemaProps{ + Description: "The desired ready replicas", + Type: []string{"integer"}, + Format: "int32", + }, + }, + "baseImage": { + SchemaProps: spec.SchemaProps{ + Description: "Base image of the component, image tag is now allowed during validation", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"replicas"}, + }, + }, + Dependencies: []string{ + "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.Toleration", "k8s.io/apimachinery/pkg/api/resource.Quantity"}, + } +} + func schema_pkg_apis_pingcap_v1alpha1_TiDBAccessConfig(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -7535,6 +7711,12 @@ func schema_pkg_apis_pingcap_v1alpha1_TidbClusterSpec(ref common.ReferenceCallba Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiFlashSpec"), }, }, + "ticdc": { + SchemaProps: spec.SchemaProps{ + Description: "TiCDC cluster spec", + Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiCDCSpec"), + }, + }, "pump": { SchemaProps: spec.SchemaProps{ Description: "Pump cluster spec", @@ -7677,7 +7859,7 @@ func schema_pkg_apis_pingcap_v1alpha1_TidbClusterSpec(ref common.ReferenceCallba }, }, Dependencies: []string{ - "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.HelperSpec", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.PDSpec", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.PumpSpec", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TLSCluster", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiDBSpec", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiFlashSpec", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiKVSpec", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Toleration"}, + "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.HelperSpec", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.PDSpec", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.PumpSpec", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TLSCluster", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiCDCSpec", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiDBSpec", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiFlashSpec", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiKVSpec", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.Toleration"}, } } diff --git a/pkg/apis/pingcap/v1alpha1/types.go b/pkg/apis/pingcap/v1alpha1/types.go index a006073c90..b62ad4af0d 100644 --- a/pkg/apis/pingcap/v1alpha1/types.go +++ b/pkg/apis/pingcap/v1alpha1/types.go @@ -118,6 +118,10 @@ type TidbClusterSpec struct { // +optional TiFlash *TiFlashSpec `json:"tiflash,omitempty"` + // TiCDC cluster spec + // +optional + TiCDC *TiCDCSpec `json:"ticdc,omitempty"` + // Pump cluster spec // +optional Pump *PumpSpec `json:"pump,omitempty"` @@ -376,6 +380,25 @@ type TiFlashSpec struct { LogTailer *LogTailerSpec `json:"logTailer,omitempty"` } +// TiCDCSpec contains details of TiCDC members +// +k8s:openapi-gen=true +type TiCDCSpec struct { + ComponentSpec `json:",inline"` + corev1.ResourceRequirements `json:",inline"` + + // Specify a Service Account for TiCDC + ServiceAccount string `json:"serviceAccount,omitempty"` + + // The desired ready replicas + // +kubebuilder:validation:Minimum=1 + Replicas int32 `json:"replicas"` + + // Base image of the component, image tag is now allowed during validation + // +kubebuilder:default=pingcap/ticdc + // +optional + BaseImage string `json:"baseImage"` +} + // +k8s:openapi-gen=true // LogTailerSpec represents an optional log tailer sidecar container type LogTailerSpec struct { diff --git a/pkg/apis/pingcap/v1alpha1/validation/validation.go b/pkg/apis/pingcap/v1alpha1/validation/validation.go index c0f4b2ce62..ac28e36774 100644 --- a/pkg/apis/pingcap/v1alpha1/validation/validation.go +++ b/pkg/apis/pingcap/v1alpha1/validation/validation.go @@ -62,6 +62,9 @@ func validateTiDBClusterSpec(spec *v1alpha1.TidbClusterSpec, fldPath *field.Path if spec.TiFlash != nil { allErrs = append(allErrs, validateTiFlashSpec(spec.TiFlash, fldPath.Child("tiflash"))...) } + if spec.TiCDC != nil { + allErrs = append(allErrs, validateTiCDCSpec(spec.TiCDC, fldPath.Child("ticdc"))...) + } return allErrs } @@ -90,6 +93,12 @@ func validateTiFlashSpec(spec *v1alpha1.TiFlashSpec, fldPath *field.Path) field. return allErrs } +func validateTiCDCSpec(spec *v1alpha1.TiCDCSpec, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + allErrs = append(allErrs, validateComponentSpec(&spec.ComponentSpec, fldPath)...) + return allErrs +} + func validateTiFlashConfig(config *v1alpha1.TiFlashConfig, path *field.Path) field.ErrorList { allErrs := field.ErrorList{} if config == nil { diff --git a/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go index b39b67b52a..f65934675d 100644 --- a/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go @@ -3211,6 +3211,24 @@ func (in *TLSCluster) DeepCopy() *TLSCluster { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TiCDCSpec) DeepCopyInto(out *TiCDCSpec) { + *out = *in + in.ComponentSpec.DeepCopyInto(&out.ComponentSpec) + in.ResourceRequirements.DeepCopyInto(&out.ResourceRequirements) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TiCDCSpec. +func (in *TiCDCSpec) DeepCopy() *TiCDCSpec { + if in == nil { + return nil + } + out := new(TiCDCSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TiDBAccessConfig) DeepCopyInto(out *TiDBAccessConfig) { *out = *in @@ -5299,6 +5317,11 @@ func (in *TidbClusterSpec) DeepCopyInto(out *TidbClusterSpec) { *out = new(TiFlashSpec) (*in).DeepCopyInto(*out) } + if in.TiCDC != nil { + in, out := &in.TiCDC, &out.TiCDC + *out = new(TiCDCSpec) + (*in).DeepCopyInto(*out) + } if in.Pump != nil { in, out := &in.Pump, &out.Pump *out = new(PumpSpec)