From 9240101c7ea41828496108dbe9a07e9812a8729f Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Mon, 28 Oct 2024 04:35:01 +0100 Subject: [PATCH 01/49] new types --- docs/api-references/docs.md | 225 +++- manifests/crd.yaml | 992 ++++++++++++++++++ .../crd/v1/pingcap.com_compactbackups.yaml | 992 ++++++++++++++++++ .../pingcap/v1alpha1/openapi_generated.go | 109 ++ pkg/apis/pingcap/v1alpha1/types.go | 28 + .../pingcap/v1alpha1/zz_generated.deepcopy.go | 62 ++ .../typed/pingcap/v1alpha1/compactbackup.go | 175 +++ .../v1alpha1/fake/fake_compactbackup.go | 126 +++ .../v1alpha1/fake/fake_pingcap_client.go | 4 + .../pingcap/v1alpha1/generated_expansion.go | 2 + .../typed/pingcap/v1alpha1/pingcap_client.go | 5 + .../informers/externalversions/generic.go | 2 + .../pingcap/v1alpha1/compactbackup.go | 87 ++ .../pingcap/v1alpha1/interface.go | 7 + .../listers/pingcap/v1alpha1/compactbackup.go | 96 ++ .../pingcap/v1alpha1/expansion_generated.go | 8 + 16 files changed, 2919 insertions(+), 1 deletion(-) create mode 100644 manifests/crd/v1/pingcap.com_compactbackups.yaml create mode 100644 pkg/client/clientset/versioned/typed/pingcap/v1alpha1/compactbackup.go create mode 100644 pkg/client/clientset/versioned/typed/pingcap/v1alpha1/fake/fake_compactbackup.go create mode 100644 pkg/client/informers/externalversions/pingcap/v1alpha1/compactbackup.go create mode 100644 pkg/client/listers/pingcap/v1alpha1/compactbackup.go diff --git a/docs/api-references/docs.md b/docs/api-references/docs.md index 8367f02c8c..45249bf48a 100644 --- a/docs/api-references/docs.md +++ b/docs/api-references/docs.md @@ -3447,6 +3447,7 @@ string

(Appears on: BackupSpec, +CompactSpec, RestoreSpec)

@@ -3867,7 +3868,8 @@ string

BackupMode

(Appears on: -BackupSpec) +BackupSpec, +CompactSpec)

BackupType represents the backup mode, such as snapshot backup or log backup.

@@ -5335,6 +5337,225 @@ FlashSecurity +

CompactBackup

+

+

+ + + + + + + + + + + + + + + + + +
FieldDescription
+metadata
+ + +Kubernetes meta/v1.ObjectMeta + + +
+Refer to the Kubernetes API documentation for the fields of the +metadata field. +
+spec
+ + +CompactSpec + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+resources
+ + +Kubernetes core/v1.ResourceRequirements + + +
+
+env
+ + +[]Kubernetes core/v1.EnvVar + + +
+(Optional) +
+from
+ + +TiDBAccessConfig + + +
+

From is the tidb cluster that needs to backup.

+
+backupMode
+ + +BackupMode + + +
+
+StorageProvider
+ + +StorageProvider + + +
+

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

+

StorageProvider configures where and how backups should be stored.

+
+br
+ + +BRConfig + + +
+

BRConfig is the configs for BR

+
+
+

CompactSpec

+

+(Appears on: +CompactBackup) +

+

+

BackupSpec contains the backup specification for a tidb cluster.

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+resources
+ + +Kubernetes core/v1.ResourceRequirements + + +
+
+env
+ + +[]Kubernetes core/v1.EnvVar + + +
+(Optional) +
+from
+ + +TiDBAccessConfig + + +
+

From is the tidb cluster that needs to backup.

+
+backupMode
+ + +BackupMode + + +
+
+StorageProvider
+ + +StorageProvider + + +
+

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

+

StorageProvider configures where and how backups should be stored.

+
+br
+ + +BRConfig + + +
+

BRConfig is the configs for BR

+

ComponentAccessor

ComponentAccessor is the interface to access component details, which respects the cluster-level properties @@ -15861,6 +16082,7 @@ More info: BackupSpec, +CompactSpec, RestoreSpec)

@@ -16750,6 +16972,7 @@ map[github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.StorageVolumeName

(Appears on: BackupSpec, +CompactSpec, RestoreSpec)

diff --git a/manifests/crd.yaml b/manifests/crd.yaml index 063308006f..4d0c3000d0 100644 --- a/manifests/crd.yaml +++ b/manifests/crd.yaml @@ -7051,6 +7051,998 @@ spec: --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + name: compactbackups.pingcap.com +spec: + group: pingcap.com + names: + kind: CompactBackup + listKind: CompactBackupList + plural: compactbackups + shortNames: + - bk + singular: compactbackup + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + azblob: + properties: + accessTier: + type: string + container: + type: string + path: + type: string + prefix: + type: string + sasToken: + type: string + secretName: + type: string + storageAccount: + type: string + type: object + backupMode: + default: snapshot + type: string + br: + properties: + checkRequirements: + type: boolean + checksum: + type: boolean + cluster: + type: string + clusterNamespace: + type: string + concurrency: + format: int32 + type: integer + db: + type: string + logLevel: + type: string + onLine: + type: boolean + options: + items: + type: string + type: array + rateLimit: + type: integer + sendCredToTikv: + type: boolean + statusAddr: + type: string + table: + type: string + timeAgo: + type: string + required: + - cluster + type: object + 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 + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + from: + properties: + host: + type: string + port: + format: int32 + type: integer + secretName: + type: string + tlsClientSecretName: + type: string + user: + type: string + required: + - host + - secretName + type: object + gcs: + properties: + bucket: + type: string + bucketAcl: + type: string + location: + type: string + objectAcl: + type: string + path: + type: string + prefix: + type: string + projectId: + type: string + secretName: + type: string + storageClass: + type: string + required: + - projectId + type: object + local: + properties: + prefix: + type: string + volume: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + type: string + kind: + type: string + readOnly: + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + wwids: + items: + type: string + type: array + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + volumeMount: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + required: + - volume + - volumeMount + type: object + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + s3: + properties: + acl: + type: string + bucket: + type: string + endpoint: + type: string + options: + items: + type: string + type: array + path: + type: string + prefix: + type: string + provider: + type: string + region: + type: string + secretName: + type: string + sse: + type: string + storageClass: + type: string + required: + - provider + type: object + type: object + required: + - metadata + - spec + type: object + served: true + storage: true +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition metadata: annotations: controller-gen.kubebuilder.io/version: v0.15.0 diff --git a/manifests/crd/v1/pingcap.com_compactbackups.yaml b/manifests/crd/v1/pingcap.com_compactbackups.yaml new file mode 100644 index 0000000000..1421cffcdb --- /dev/null +++ b/manifests/crd/v1/pingcap.com_compactbackups.yaml @@ -0,0 +1,992 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.15.0 + name: compactbackups.pingcap.com +spec: + group: pingcap.com + names: + kind: CompactBackup + listKind: CompactBackupList + plural: compactbackups + shortNames: + - bk + singular: compactbackup + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + properties: + azblob: + properties: + accessTier: + type: string + container: + type: string + path: + type: string + prefix: + type: string + sasToken: + type: string + secretName: + type: string + storageAccount: + type: string + type: object + backupMode: + default: snapshot + type: string + br: + properties: + checkRequirements: + type: boolean + checksum: + type: boolean + cluster: + type: string + clusterNamespace: + type: string + concurrency: + format: int32 + type: integer + db: + type: string + logLevel: + type: string + onLine: + type: boolean + options: + items: + type: string + type: array + rateLimit: + type: integer + sendCredToTikv: + type: boolean + statusAddr: + type: string + table: + type: string + timeAgo: + type: string + required: + - cluster + type: object + 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 + x-kubernetes-map-type: atomic + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + properties: + key: + type: string + name: + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + from: + properties: + host: + type: string + port: + format: int32 + type: integer + secretName: + type: string + tlsClientSecretName: + type: string + user: + type: string + required: + - host + - secretName + type: object + gcs: + properties: + bucket: + type: string + bucketAcl: + type: string + location: + type: string + objectAcl: + type: string + path: + type: string + prefix: + type: string + projectId: + type: string + secretName: + type: string + storageClass: + type: string + required: + - projectId + type: object + local: + properties: + prefix: + type: string + volume: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + type: string + kind: + type: string + readOnly: + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + wwids: + items: + type: string + type: array + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + volumeMount: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + required: + - volume + - volumeMount + type: object + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + s3: + properties: + acl: + type: string + bucket: + type: string + endpoint: + type: string + options: + items: + type: string + type: array + path: + type: string + prefix: + type: string + provider: + type: string + region: + type: string + secretName: + type: string + sse: + type: string + storageClass: + type: string + required: + - provider + type: object + type: object + required: + - metadata + - spec + type: object + served: true + storage: true diff --git a/pkg/apis/pingcap/v1alpha1/openapi_generated.go b/pkg/apis/pingcap/v1alpha1/openapi_generated.go index 4f789a1a7e..ecb4d3e23b 100644 --- a/pkg/apis/pingcap/v1alpha1/openapi_generated.go +++ b/pkg/apis/pingcap/v1alpha1/openapi_generated.go @@ -46,6 +46,8 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.CleanOption": schema_pkg_apis_pingcap_v1alpha1_CleanOption(ref), "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.ClusterRef": schema_pkg_apis_pingcap_v1alpha1_ClusterRef(ref), "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.CommonConfig": schema_pkg_apis_pingcap_v1alpha1_CommonConfig(ref), + "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.CompactBackup": schema_pkg_apis_pingcap_v1alpha1_CompactBackup(ref), + "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.CompactSpec": schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref), "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.ComponentSpec": schema_pkg_apis_pingcap_v1alpha1_ComponentSpec(ref), "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.ConfigMapRef": schema_pkg_apis_pingcap_v1alpha1_ConfigMapRef(ref), "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.DMCluster": schema_pkg_apis_pingcap_v1alpha1_DMCluster(ref), @@ -1621,6 +1623,113 @@ func schema_pkg_apis_pingcap_v1alpha1_CommonConfig(ref common.ReferenceCallback) } } +func schema_pkg_apis_pingcap_v1alpha1_CompactBackup(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "spec": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.CompactSpec"), + }, + }, + }, + Required: []string{"spec"}, + }, + }, + Dependencies: []string{ + "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.CompactSpec"}, + } +} + +func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "BackupSpec contains the backup specification for a tidb cluster.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "resources": { + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.ResourceRequirements"), + }, + }, + "env": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.EnvVar"), + }, + }, + }, + }, + }, + "from": { + SchemaProps: spec.SchemaProps{ + Description: "From is the tidb cluster that needs to backup.", + Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiDBAccessConfig"), + }, + }, + "backupMode": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + Format: "", + }, + }, + "s3": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.S3StorageProvider"), + }, + }, + "gcs": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.GcsStorageProvider"), + }, + }, + "azblob": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.AzblobStorageProvider"), + }, + }, + "local": { + SchemaProps: spec.SchemaProps{ + Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.LocalStorageProvider"), + }, + }, + "br": { + SchemaProps: spec.SchemaProps{ + Description: "BRConfig is the configs for BR", + Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BRConfig"), + }, + }, + }, + }, + }, + Dependencies: []string{ + "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.AzblobStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BRConfig", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.GcsStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.LocalStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.S3StorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiDBAccessConfig", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.ResourceRequirements"}, + } +} + func schema_pkg_apis_pingcap_v1alpha1_ComponentSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/pkg/apis/pingcap/v1alpha1/types.go b/pkg/apis/pingcap/v1alpha1/types.go index 5532fc5660..b52008fb7a 100644 --- a/pkg/apis/pingcap/v1alpha1/types.go +++ b/pkg/apis/pingcap/v1alpha1/types.go @@ -3431,3 +3431,31 @@ type ScalePolicy struct { // +optional ScaleOutParallelism *int32 `json:"scaleOutParallelism,omitempty"` } + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +k8s:openapi-gen=true +// +kubebuilder:resource:shortName="bk" +type CompactBackup struct { + metav1.TypeMeta `json:",inline"` + // +k8s:openapi-gen=false + metav1.ObjectMeta `json:"metadata"` + + Spec CompactSpec `json:"spec"` +} + +// BackupSpec contains the backup specification for a tidb cluster. +// +k8s:openapi-gen=true +type CompactSpec struct { + corev1.ResourceRequirements `json:"resources,omitempty"` + // +optional + Env []corev1.EnvVar `json:"env,omitempty"` + // From is the tidb cluster that needs to backup. + From *TiDBAccessConfig `json:"from,omitempty"` + // +kubebuilder:default=snapshot + Mode BackupMode `json:"backupMode,omitempty"` + // StorageProvider configures where and how backups should be stored. + StorageProvider `json:",inline"` + // BRConfig is the configs for BR + BR *BRConfig `json:"br,omitempty"` +} diff --git a/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go index b65b9dca1d..e9e5f29518 100644 --- a/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go @@ -865,6 +865,68 @@ func (in *CommonConfig) DeepCopy() *CommonConfig { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CompactBackup) DeepCopyInto(out *CompactBackup) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompactBackup. +func (in *CompactBackup) DeepCopy() *CompactBackup { + if in == nil { + return nil + } + out := new(CompactBackup) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CompactBackup) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CompactSpec) DeepCopyInto(out *CompactSpec) { + *out = *in + in.ResourceRequirements.DeepCopyInto(&out.ResourceRequirements) + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.From != nil { + in, out := &in.From, &out.From + *out = new(TiDBAccessConfig) + (*in).DeepCopyInto(*out) + } + in.StorageProvider.DeepCopyInto(&out.StorageProvider) + if in.BR != nil { + in, out := &in.BR, &out.BR + *out = new(BRConfig) + (*in).DeepCopyInto(*out) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompactSpec. +func (in *CompactSpec) DeepCopy() *CompactSpec { + if in == nil { + return nil + } + out := new(CompactSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ComponentSpec) DeepCopyInto(out *ComponentSpec) { *out = *in diff --git a/pkg/client/clientset/versioned/typed/pingcap/v1alpha1/compactbackup.go b/pkg/client/clientset/versioned/typed/pingcap/v1alpha1/compactbackup.go new file mode 100644 index 0000000000..de71c963d9 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/pingcap/v1alpha1/compactbackup.go @@ -0,0 +1,175 @@ +// Copyright PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + "time" + + v1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1" + scheme "github.com/pingcap/tidb-operator/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// CompactBackupsGetter has a method to return a CompactBackupInterface. +// A group's client should implement this interface. +type CompactBackupsGetter interface { + CompactBackups(namespace string) CompactBackupInterface +} + +// CompactBackupInterface has methods to work with CompactBackup resources. +type CompactBackupInterface interface { + Create(ctx context.Context, compactBackup *v1alpha1.CompactBackup, opts v1.CreateOptions) (*v1alpha1.CompactBackup, error) + Update(ctx context.Context, compactBackup *v1alpha1.CompactBackup, opts v1.UpdateOptions) (*v1alpha1.CompactBackup, error) + Delete(ctx context.Context, name string, opts v1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error + Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.CompactBackup, error) + List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.CompactBackupList, error) + Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.CompactBackup, err error) + CompactBackupExpansion +} + +// compactBackups implements CompactBackupInterface +type compactBackups struct { + client rest.Interface + ns string +} + +// newCompactBackups returns a CompactBackups +func newCompactBackups(c *PingcapV1alpha1Client, namespace string) *compactBackups { + return &compactBackups{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the compactBackup, and returns the corresponding compactBackup object, and an error if there is any. +func (c *compactBackups) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.CompactBackup, err error) { + result = &v1alpha1.CompactBackup{} + err = c.client.Get(). + Namespace(c.ns). + Resource("compactbackups"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of CompactBackups that match those selectors. +func (c *compactBackups) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.CompactBackupList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1alpha1.CompactBackupList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("compactbackups"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested compactBackups. +func (c *compactBackups) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("compactbackups"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a compactBackup and creates it. Returns the server's representation of the compactBackup, and an error, if there is any. +func (c *compactBackups) Create(ctx context.Context, compactBackup *v1alpha1.CompactBackup, opts v1.CreateOptions) (result *v1alpha1.CompactBackup, err error) { + result = &v1alpha1.CompactBackup{} + err = c.client.Post(). + Namespace(c.ns). + Resource("compactbackups"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(compactBackup). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a compactBackup and updates it. Returns the server's representation of the compactBackup, and an error, if there is any. +func (c *compactBackups) Update(ctx context.Context, compactBackup *v1alpha1.CompactBackup, opts v1.UpdateOptions) (result *v1alpha1.CompactBackup, err error) { + result = &v1alpha1.CompactBackup{} + err = c.client.Put(). + Namespace(c.ns). + Resource("compactbackups"). + Name(compactBackup.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(compactBackup). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the compactBackup and deletes it. Returns an error if one occurs. +func (c *compactBackups) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("compactbackups"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *compactBackups) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("compactbackups"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched compactBackup. +func (c *compactBackups) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.CompactBackup, err error) { + result = &v1alpha1.CompactBackup{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("compactbackups"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/pingcap/v1alpha1/fake/fake_compactbackup.go b/pkg/client/clientset/versioned/typed/pingcap/v1alpha1/fake/fake_compactbackup.go new file mode 100644 index 0000000000..725757a8ae --- /dev/null +++ b/pkg/client/clientset/versioned/typed/pingcap/v1alpha1/fake/fake_compactbackup.go @@ -0,0 +1,126 @@ +// Copyright PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + v1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeCompactBackups implements CompactBackupInterface +type FakeCompactBackups struct { + Fake *FakePingcapV1alpha1 + ns string +} + +var compactbackupsResource = v1alpha1.SchemeGroupVersion.WithResource("compactbackups") + +var compactbackupsKind = v1alpha1.SchemeGroupVersion.WithKind("CompactBackup") + +// Get takes name of the compactBackup, and returns the corresponding compactBackup object, and an error if there is any. +func (c *FakeCompactBackups) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.CompactBackup, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(compactbackupsResource, c.ns, name), &v1alpha1.CompactBackup{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.CompactBackup), err +} + +// List takes label and field selectors, and returns the list of CompactBackups that match those selectors. +func (c *FakeCompactBackups) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.CompactBackupList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(compactbackupsResource, compactbackupsKind, c.ns, opts), &v1alpha1.CompactBackupList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.CompactBackupList{ListMeta: obj.(*v1alpha1.CompactBackupList).ListMeta} + for _, item := range obj.(*v1alpha1.CompactBackupList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested compactBackups. +func (c *FakeCompactBackups) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(compactbackupsResource, c.ns, opts)) + +} + +// Create takes the representation of a compactBackup and creates it. Returns the server's representation of the compactBackup, and an error, if there is any. +func (c *FakeCompactBackups) Create(ctx context.Context, compactBackup *v1alpha1.CompactBackup, opts v1.CreateOptions) (result *v1alpha1.CompactBackup, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(compactbackupsResource, c.ns, compactBackup), &v1alpha1.CompactBackup{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.CompactBackup), err +} + +// Update takes the representation of a compactBackup and updates it. Returns the server's representation of the compactBackup, and an error, if there is any. +func (c *FakeCompactBackups) Update(ctx context.Context, compactBackup *v1alpha1.CompactBackup, opts v1.UpdateOptions) (result *v1alpha1.CompactBackup, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(compactbackupsResource, c.ns, compactBackup), &v1alpha1.CompactBackup{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.CompactBackup), err +} + +// Delete takes name of the compactBackup and deletes it. Returns an error if one occurs. +func (c *FakeCompactBackups) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteActionWithOptions(compactbackupsResource, c.ns, name, opts), &v1alpha1.CompactBackup{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeCompactBackups) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(compactbackupsResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &v1alpha1.CompactBackupList{}) + return err +} + +// Patch applies the patch and returns the patched compactBackup. +func (c *FakeCompactBackups) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.CompactBackup, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(compactbackupsResource, c.ns, name, pt, data, subresources...), &v1alpha1.CompactBackup{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.CompactBackup), err +} diff --git a/pkg/client/clientset/versioned/typed/pingcap/v1alpha1/fake/fake_pingcap_client.go b/pkg/client/clientset/versioned/typed/pingcap/v1alpha1/fake/fake_pingcap_client.go index 80dcf44250..94f1896e3e 100644 --- a/pkg/client/clientset/versioned/typed/pingcap/v1alpha1/fake/fake_pingcap_client.go +++ b/pkg/client/clientset/versioned/typed/pingcap/v1alpha1/fake/fake_pingcap_client.go @@ -33,6 +33,10 @@ func (c *FakePingcapV1alpha1) BackupSchedules(namespace string) v1alpha1.BackupS return &FakeBackupSchedules{c, namespace} } +func (c *FakePingcapV1alpha1) CompactBackups(namespace string) v1alpha1.CompactBackupInterface { + return &FakeCompactBackups{c, namespace} +} + func (c *FakePingcapV1alpha1) DMClusters(namespace string) v1alpha1.DMClusterInterface { return &FakeDMClusters{c, namespace} } diff --git a/pkg/client/clientset/versioned/typed/pingcap/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/pingcap/v1alpha1/generated_expansion.go index 6799967ce4..d5d8f8681e 100644 --- a/pkg/client/clientset/versioned/typed/pingcap/v1alpha1/generated_expansion.go +++ b/pkg/client/clientset/versioned/typed/pingcap/v1alpha1/generated_expansion.go @@ -19,6 +19,8 @@ type BackupExpansion interface{} type BackupScheduleExpansion interface{} +type CompactBackupExpansion interface{} + type DMClusterExpansion interface{} type DataResourceExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/pingcap/v1alpha1/pingcap_client.go b/pkg/client/clientset/versioned/typed/pingcap/v1alpha1/pingcap_client.go index 126a8b75a6..cb570a8b87 100644 --- a/pkg/client/clientset/versioned/typed/pingcap/v1alpha1/pingcap_client.go +++ b/pkg/client/clientset/versioned/typed/pingcap/v1alpha1/pingcap_client.go @@ -27,6 +27,7 @@ type PingcapV1alpha1Interface interface { RESTClient() rest.Interface BackupsGetter BackupSchedulesGetter + CompactBackupsGetter DMClustersGetter DataResourcesGetter RestoresGetter @@ -51,6 +52,10 @@ func (c *PingcapV1alpha1Client) BackupSchedules(namespace string) BackupSchedule return newBackupSchedules(c, namespace) } +func (c *PingcapV1alpha1Client) CompactBackups(namespace string) CompactBackupInterface { + return newCompactBackups(c, namespace) +} + func (c *PingcapV1alpha1Client) DMClusters(namespace string) DMClusterInterface { return newDMClusters(c, namespace) } diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index a5ccab2deb..6b07e7efe1 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -54,6 +54,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Pingcap().V1alpha1().Backups().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("backupschedules"): return &genericInformer{resource: resource.GroupResource(), informer: f.Pingcap().V1alpha1().BackupSchedules().Informer()}, nil + case v1alpha1.SchemeGroupVersion.WithResource("compactbackups"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Pingcap().V1alpha1().CompactBackups().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("dmclusters"): return &genericInformer{resource: resource.GroupResource(), informer: f.Pingcap().V1alpha1().DMClusters().Informer()}, nil case v1alpha1.SchemeGroupVersion.WithResource("dataresources"): diff --git a/pkg/client/informers/externalversions/pingcap/v1alpha1/compactbackup.go b/pkg/client/informers/externalversions/pingcap/v1alpha1/compactbackup.go new file mode 100644 index 0000000000..47083cb76a --- /dev/null +++ b/pkg/client/informers/externalversions/pingcap/v1alpha1/compactbackup.go @@ -0,0 +1,87 @@ +// Copyright PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by informer-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "context" + time "time" + + pingcapv1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1" + versioned "github.com/pingcap/tidb-operator/pkg/client/clientset/versioned" + internalinterfaces "github.com/pingcap/tidb-operator/pkg/client/informers/externalversions/internalinterfaces" + v1alpha1 "github.com/pingcap/tidb-operator/pkg/client/listers/pingcap/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// CompactBackupInformer provides access to a shared informer and lister for +// CompactBackups. +type CompactBackupInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1alpha1.CompactBackupLister +} + +type compactBackupInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewCompactBackupInformer constructs a new informer for CompactBackup type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewCompactBackupInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredCompactBackupInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredCompactBackupInformer constructs a new informer for CompactBackup type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredCompactBackupInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.PingcapV1alpha1().CompactBackups(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.PingcapV1alpha1().CompactBackups(namespace).Watch(context.TODO(), options) + }, + }, + &pingcapv1alpha1.CompactBackup{}, + resyncPeriod, + indexers, + ) +} + +func (f *compactBackupInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredCompactBackupInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *compactBackupInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&pingcapv1alpha1.CompactBackup{}, f.defaultInformer) +} + +func (f *compactBackupInformer) Lister() v1alpha1.CompactBackupLister { + return v1alpha1.NewCompactBackupLister(f.Informer().GetIndexer()) +} diff --git a/pkg/client/informers/externalversions/pingcap/v1alpha1/interface.go b/pkg/client/informers/externalversions/pingcap/v1alpha1/interface.go index 7ea63ea74a..2e5e23b7a6 100644 --- a/pkg/client/informers/externalversions/pingcap/v1alpha1/interface.go +++ b/pkg/client/informers/externalversions/pingcap/v1alpha1/interface.go @@ -25,6 +25,8 @@ type Interface interface { Backups() BackupInformer // BackupSchedules returns a BackupScheduleInformer. BackupSchedules() BackupScheduleInformer + // CompactBackups returns a CompactBackupInformer. + CompactBackups() CompactBackupInformer // DMClusters returns a DMClusterInformer. DMClusters() DMClusterInformer // DataResources returns a DataResourceInformer. @@ -66,6 +68,11 @@ func (v *version) BackupSchedules() BackupScheduleInformer { return &backupScheduleInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } +// CompactBackups returns a CompactBackupInformer. +func (v *version) CompactBackups() CompactBackupInformer { + return &compactBackupInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} + // DMClusters returns a DMClusterInformer. func (v *version) DMClusters() DMClusterInformer { return &dMClusterInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} diff --git a/pkg/client/listers/pingcap/v1alpha1/compactbackup.go b/pkg/client/listers/pingcap/v1alpha1/compactbackup.go new file mode 100644 index 0000000000..0e4f26d86f --- /dev/null +++ b/pkg/client/listers/pingcap/v1alpha1/compactbackup.go @@ -0,0 +1,96 @@ +// Copyright PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// CompactBackupLister helps list CompactBackups. +// All objects returned here must be treated as read-only. +type CompactBackupLister interface { + // List lists all CompactBackups in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.CompactBackup, err error) + // CompactBackups returns an object that can list and get CompactBackups. + CompactBackups(namespace string) CompactBackupNamespaceLister + CompactBackupListerExpansion +} + +// compactBackupLister implements the CompactBackupLister interface. +type compactBackupLister struct { + indexer cache.Indexer +} + +// NewCompactBackupLister returns a new CompactBackupLister. +func NewCompactBackupLister(indexer cache.Indexer) CompactBackupLister { + return &compactBackupLister{indexer: indexer} +} + +// List lists all CompactBackups in the indexer. +func (s *compactBackupLister) List(selector labels.Selector) (ret []*v1alpha1.CompactBackup, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.CompactBackup)) + }) + return ret, err +} + +// CompactBackups returns an object that can list and get CompactBackups. +func (s *compactBackupLister) CompactBackups(namespace string) CompactBackupNamespaceLister { + return compactBackupNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// CompactBackupNamespaceLister helps list and get CompactBackups. +// All objects returned here must be treated as read-only. +type CompactBackupNamespaceLister interface { + // List lists all CompactBackups in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1alpha1.CompactBackup, err error) + // Get retrieves the CompactBackup from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1alpha1.CompactBackup, error) + CompactBackupNamespaceListerExpansion +} + +// compactBackupNamespaceLister implements the CompactBackupNamespaceLister +// interface. +type compactBackupNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all CompactBackups in the indexer for a given namespace. +func (s compactBackupNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.CompactBackup, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.CompactBackup)) + }) + return ret, err +} + +// Get retrieves the CompactBackup from the indexer for a given namespace and name. +func (s compactBackupNamespaceLister) Get(name string) (*v1alpha1.CompactBackup, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("compactbackup"), name) + } + return obj.(*v1alpha1.CompactBackup), nil +} diff --git a/pkg/client/listers/pingcap/v1alpha1/expansion_generated.go b/pkg/client/listers/pingcap/v1alpha1/expansion_generated.go index 5b0bb67412..f2cb82902b 100644 --- a/pkg/client/listers/pingcap/v1alpha1/expansion_generated.go +++ b/pkg/client/listers/pingcap/v1alpha1/expansion_generated.go @@ -31,6 +31,14 @@ type BackupScheduleListerExpansion interface{} // BackupScheduleNamespaceLister. type BackupScheduleNamespaceListerExpansion interface{} +// CompactBackupListerExpansion allows custom methods to be added to +// CompactBackupLister. +type CompactBackupListerExpansion interface{} + +// CompactBackupNamespaceListerExpansion allows custom methods to be added to +// CompactBackupNamespaceLister. +type CompactBackupNamespaceListerExpansion interface{} + // DMClusterListerExpansion allows custom methods to be added to // DMClusterLister. type DMClusterListerExpansion interface{} From df3c1917f0a41c27e0ffb0cf9fa8dda6a204b7a1 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Mon, 28 Oct 2024 06:53:49 +0100 Subject: [PATCH 02/49] add new crd --- cmd/controller-manager/main.go | 2 + docs/api-references/docs.md | 39 ++++++ go.mod | 1 + go.sum | 2 + manifests/crd.yaml | 15 ++- .../crd/v1/pingcap.com_compactbackups.yaml | 15 ++- .../pingcap/v1alpha1/openapi_generated.go | 44 ++++++ pkg/apis/pingcap/v1alpha1/types.go | 21 ++- .../pingcap/v1alpha1/zz_generated.deepcopy.go | 50 +++++++ .../typed/pingcap/v1alpha1/compactbackup.go | 17 +++ .../v1alpha1/fake/fake_compactbackup.go | 12 ++ .../compact_backup_controller.go | 126 ++++++++++++++++++ 12 files changed, 339 insertions(+), 5 deletions(-) create mode 100644 pkg/controller/compactbackup/compact_backup_controller.go diff --git a/cmd/controller-manager/main.go b/cmd/controller-manager/main.go index 991de3e2eb..283de5583c 100644 --- a/cmd/controller-manager/main.go +++ b/cmd/controller-manager/main.go @@ -31,6 +31,7 @@ import ( "github.com/pingcap/tidb-operator/pkg/controller/autoscaler" "github.com/pingcap/tidb-operator/pkg/controller/backup" "github.com/pingcap/tidb-operator/pkg/controller/backupschedule" + compact "github.com/pingcap/tidb-operator/pkg/controller/compactbackup" "github.com/pingcap/tidb-operator/pkg/controller/dmcluster" "github.com/pingcap/tidb-operator/pkg/controller/restore" "github.com/pingcap/tidb-operator/pkg/controller/tidbcluster" @@ -182,6 +183,7 @@ func main() { tidbcluster.NewPodController(deps), dmcluster.NewController(deps), backup.NewController(deps), + compact.NewController(deps), restore.NewController(deps), backupschedule.NewController(deps), tidbinitializer.NewController(deps), diff --git a/docs/api-references/docs.md b/docs/api-references/docs.md index 45249bf48a..9273c83cc9 100644 --- a/docs/api-references/docs.md +++ b/docs/api-references/docs.md @@ -5457,6 +5457,18 @@ BRConfig + + +status
+ + +CompactStatus + + + + + +

CompactSpec

@@ -5556,6 +5568,33 @@ BRConfig +

CompactStatus

+

+(Appears on: +CompactBackup) +

+

+

+ + + + + + + + + + + + + +
FieldDescription
+state
+ +string + +
+

ComponentAccessor

ComponentAccessor is the interface to access component details, which respects the cluster-level properties diff --git a/go.mod b/go.mod index c9ad495768..dcd301c2a8 100644 --- a/go.mod +++ b/go.mod @@ -214,6 +214,7 @@ require ( gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/klog v1.0.0 k8s.io/kms v0.28.14 // indirect k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.1.2 // indirect diff --git a/go.sum b/go.sum index 317846741e..0ce9d0a1c7 100644 --- a/go.sum +++ b/go.sum @@ -1155,6 +1155,8 @@ k8s.io/component-base v0.28.14 h1:sJowHyRY166hBfBQ4cOKjkSvUo4bUdeuePtEOQfSNRY= k8s.io/component-base v0.28.14/go.mod h1:DgYlfHNvP1yeBb4L+UIzMsWNtOl0yqTk+4dGGc79H0w= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= +k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= diff --git a/manifests/crd.yaml b/manifests/crd.yaml index 4d0c3000d0..f774305743 100644 --- a/manifests/crd.yaml +++ b/manifests/crd.yaml @@ -7062,11 +7062,16 @@ spec: listKind: CompactBackupList plural: compactbackups shortNames: - - bk + - cpbk singular: compactbackup scope: Namespaced versions: - - name: v1alpha1 + - additionalPrinterColumns: + - description: The current status of the compact backup + jsonPath: .status.state + name: Status + type: string + name: v1alpha1 schema: openAPIV3Schema: properties: @@ -8034,12 +8039,18 @@ spec: - provider type: object type: object + status: + properties: + state: + type: string + type: object required: - metadata - spec type: object served: true storage: true + subresources: {} --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition diff --git a/manifests/crd/v1/pingcap.com_compactbackups.yaml b/manifests/crd/v1/pingcap.com_compactbackups.yaml index 1421cffcdb..e9f4194e1f 100644 --- a/manifests/crd/v1/pingcap.com_compactbackups.yaml +++ b/manifests/crd/v1/pingcap.com_compactbackups.yaml @@ -12,11 +12,16 @@ spec: listKind: CompactBackupList plural: compactbackups shortNames: - - bk + - cpbk singular: compactbackup scope: Namespaced versions: - - name: v1alpha1 + - additionalPrinterColumns: + - description: The current status of the compact backup + jsonPath: .status.state + name: Status + type: string + name: v1alpha1 schema: openAPIV3Schema: properties: @@ -984,9 +989,15 @@ spec: - provider type: object type: object + status: + properties: + state: + type: string + type: object required: - metadata - spec type: object served: true storage: true + subresources: {} diff --git a/pkg/apis/pingcap/v1alpha1/openapi_generated.go b/pkg/apis/pingcap/v1alpha1/openapi_generated.go index ecb4d3e23b..7a8e44f238 100644 --- a/pkg/apis/pingcap/v1alpha1/openapi_generated.go +++ b/pkg/apis/pingcap/v1alpha1/openapi_generated.go @@ -47,6 +47,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.ClusterRef": schema_pkg_apis_pingcap_v1alpha1_ClusterRef(ref), "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.CommonConfig": schema_pkg_apis_pingcap_v1alpha1_CommonConfig(ref), "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.CompactBackup": schema_pkg_apis_pingcap_v1alpha1_CompactBackup(ref), + "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.CompactBackupList": schema_pkg_apis_pingcap_v1alpha1_CompactBackupList(ref), "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.CompactSpec": schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref), "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.ComponentSpec": schema_pkg_apis_pingcap_v1alpha1_ComponentSpec(ref), "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.ConfigMapRef": schema_pkg_apis_pingcap_v1alpha1_ConfigMapRef(ref), @@ -1658,6 +1659,49 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactBackup(ref common.ReferenceCallback } } +func schema_pkg_apis_pingcap_v1alpha1_CompactBackupList(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Description: "CompactList contains a list of Compact Backup.", + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds", + Type: []string{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources", + Type: []string{"string"}, + Format: "", + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.CompactBackup"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.CompactBackup"}, + } +} + func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ diff --git a/pkg/apis/pingcap/v1alpha1/types.go b/pkg/apis/pingcap/v1alpha1/types.go index b52008fb7a..0a8fe7b8ce 100644 --- a/pkg/apis/pingcap/v1alpha1/types.go +++ b/pkg/apis/pingcap/v1alpha1/types.go @@ -3435,13 +3435,16 @@ type ScalePolicy struct { // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +k8s:openapi-gen=true -// +kubebuilder:resource:shortName="bk" +// +kubebuilder:resource:shortName="cpbk" +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.state`,description="The current status of the compact backup" type CompactBackup struct { metav1.TypeMeta `json:",inline"` // +k8s:openapi-gen=false metav1.ObjectMeta `json:"metadata"` Spec CompactSpec `json:"spec"` + // +k8s:openapi-gen=false + Status CompactStatus `json:"status,omitempty"` } // BackupSpec contains the backup specification for a tidb cluster. @@ -3459,3 +3462,19 @@ type CompactSpec struct { // BRConfig is the configs for BR BR *BRConfig `json:"br,omitempty"` } + +type CompactStatus struct { + State string `json:"state,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// +k8s:openapi-gen=true +// CompactList contains a list of Compact Backup. +type CompactBackupList struct { + metav1.TypeMeta `json:",inline"` + // +k8s:openapi-gen=false + metav1.ListMeta `json:"metadata"` + + Items []CompactBackup `json:"items"` +} diff --git a/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go index e9e5f29518..fc0b823152 100644 --- a/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go @@ -871,6 +871,7 @@ func (in *CompactBackup) DeepCopyInto(out *CompactBackup) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status return } @@ -892,6 +893,39 @@ func (in *CompactBackup) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CompactBackupList) DeepCopyInto(out *CompactBackupList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]CompactBackup, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompactBackupList. +func (in *CompactBackupList) DeepCopy() *CompactBackupList { + if in == nil { + return nil + } + out := new(CompactBackupList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *CompactBackupList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CompactSpec) DeepCopyInto(out *CompactSpec) { *out = *in @@ -927,6 +961,22 @@ func (in *CompactSpec) DeepCopy() *CompactSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CompactStatus) DeepCopyInto(out *CompactStatus) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompactStatus. +func (in *CompactStatus) DeepCopy() *CompactStatus { + if in == nil { + return nil + } + out := new(CompactStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ComponentSpec) DeepCopyInto(out *ComponentSpec) { *out = *in diff --git a/pkg/client/clientset/versioned/typed/pingcap/v1alpha1/compactbackup.go b/pkg/client/clientset/versioned/typed/pingcap/v1alpha1/compactbackup.go index de71c963d9..a42c9972de 100644 --- a/pkg/client/clientset/versioned/typed/pingcap/v1alpha1/compactbackup.go +++ b/pkg/client/clientset/versioned/typed/pingcap/v1alpha1/compactbackup.go @@ -37,6 +37,7 @@ type CompactBackupsGetter interface { type CompactBackupInterface interface { Create(ctx context.Context, compactBackup *v1alpha1.CompactBackup, opts v1.CreateOptions) (*v1alpha1.CompactBackup, error) Update(ctx context.Context, compactBackup *v1alpha1.CompactBackup, opts v1.UpdateOptions) (*v1alpha1.CompactBackup, error) + UpdateStatus(ctx context.Context, compactBackup *v1alpha1.CompactBackup, opts v1.UpdateOptions) (*v1alpha1.CompactBackup, error) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.CompactBackup, error) @@ -132,6 +133,22 @@ func (c *compactBackups) Update(ctx context.Context, compactBackup *v1alpha1.Com return } +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *compactBackups) UpdateStatus(ctx context.Context, compactBackup *v1alpha1.CompactBackup, opts v1.UpdateOptions) (result *v1alpha1.CompactBackup, err error) { + result = &v1alpha1.CompactBackup{} + err = c.client.Put(). + Namespace(c.ns). + Resource("compactbackups"). + Name(compactBackup.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(compactBackup). + Do(ctx). + Into(result) + return +} + // Delete takes name of the compactBackup and deletes it. Returns an error if one occurs. func (c *compactBackups) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { return c.client.Delete(). diff --git a/pkg/client/clientset/versioned/typed/pingcap/v1alpha1/fake/fake_compactbackup.go b/pkg/client/clientset/versioned/typed/pingcap/v1alpha1/fake/fake_compactbackup.go index 725757a8ae..2a7e2d387c 100644 --- a/pkg/client/clientset/versioned/typed/pingcap/v1alpha1/fake/fake_compactbackup.go +++ b/pkg/client/clientset/versioned/typed/pingcap/v1alpha1/fake/fake_compactbackup.go @@ -98,6 +98,18 @@ func (c *FakeCompactBackups) Update(ctx context.Context, compactBackup *v1alpha1 return obj.(*v1alpha1.CompactBackup), err } +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeCompactBackups) UpdateStatus(ctx context.Context, compactBackup *v1alpha1.CompactBackup, opts v1.UpdateOptions) (*v1alpha1.CompactBackup, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(compactbackupsResource, "status", c.ns, compactBackup), &v1alpha1.CompactBackup{}) + + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.CompactBackup), err +} + // Delete takes name of the compactBackup and deletes it. Returns an error if one occurs. func (c *FakeCompactBackups) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { _, err := c.Fake. diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go new file mode 100644 index 0000000000..83bed8c0ef --- /dev/null +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -0,0 +1,126 @@ +package compact + +import ( + "fmt" + "time" + + perrors "github.com/pingcap/errors" + "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1" + "github.com/pingcap/tidb-operator/pkg/controller" + "github.com/pingcap/tidb-operator/pkg/metrics" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/util/workqueue" + "k8s.io/klog" +) + +// Controller controls backup. +type Controller struct { + deps *controller.Dependencies + // backups that need to be synced. + queue workqueue.RateLimitingInterface +} + +// NewController creates a backup controller. +func NewController(deps *controller.Dependencies) *Controller { + c := &Controller{ + deps: deps, + queue: workqueue.NewNamedRateLimitingQueue( + controller.NewControllerRateLimiter(1*time.Second, 100*time.Second), + "backup", + ), + } + + backupInformer := deps.InformerFactory.Pingcap().V1alpha1().Backups() + jobInformer := deps.KubeInformerFactory.Batch().V1().Jobs() + backupInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: c.updateBackup, + UpdateFunc: func(old, cur interface{}) { + c.updateBackup(cur) + }, + DeleteFunc: c.updateBackup, + }) + jobInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + DeleteFunc: c.updateBackup, + }) + + return c +} + +// Name returns backup controller name. +func (c *Controller) Name() string { + return "compactBackup" +} + +// Run runs the backup controller. +func (c *Controller) Run(workers int, stopCh <-chan struct{}) { + defer utilruntime.HandleCrash() + defer c.queue.ShutDown() + + klog.Info("Starting backup controller") + defer klog.Info("Shutting down backup controller") + + for i := 0; i < workers; i++ { + go wait.Until(c.worker, time.Second, stopCh) + } + + <-stopCh +} + +// worker runs a worker goroutine that invokes processNextWorkItem until the the controller's queue is closed +func (c *Controller) worker() { + for c.processNextWorkItem() { + } +} + +func (c *Controller) updateBackup(cur interface{}) { + newBackup := cur.(*v1alpha1.CompactBackup) + ns := newBackup.GetNamespace() + name := newBackup.GetName() + + klog.Infof("backup object %s/%s enqueue", ns, name) + c.enqueueBackup(newBackup) +} + +// enqueueBackup enqueues the given backup in the work queue. +func (c *Controller) enqueueBackup(obj interface{}) { + key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) + if err != nil { + utilruntime.HandleError(fmt.Errorf("cound't get key for object %+v: %v", obj, err)) + return + } + c.queue.Add(key) +} + +// processNextWorkItem dequeues items, processes them, and marks them done. It enforces that the syncHandler is never +// invoked concurrently with the same key. +func (c *Controller) processNextWorkItem() bool { + metrics.ActiveWorkers.WithLabelValues(c.Name()).Add(1) + defer metrics.ActiveWorkers.WithLabelValues(c.Name()).Add(-1) + + key, quit := c.queue.Get() + if quit { + return false + } + defer c.queue.Done(key) + if err := c.sync(key.(string)); err != nil { + if perrors.Find(err, controller.IsRequeueError) != nil { + klog.Infof("Backup: %v, still need sync: %v, requeuing", key.(string), err) + c.queue.AddRateLimited(key) + } else if perrors.Find(err, controller.IsIgnoreError) != nil { + klog.Infof("Backup: %v, ignore err: %v", key.(string), err) + } else { + utilruntime.HandleError(fmt.Errorf("Backup: %v, sync failed, err: %v, requeuing", key.(string), err)) + c.queue.AddRateLimited(key) + } + } else { + c.queue.Forget(key) + } + return true +} + +func (c *Controller) sync(key string) (err error) { + klog.Infof("do nothing, skip") + return nil +} From 92c981990481a44ffaac2fa32820cdf009dd67b3 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Mon, 28 Oct 2024 08:53:55 +0100 Subject: [PATCH 03/49] trry --- pkg/apis/pingcap/v1alpha1/types.go | 104 +++++++++ .../compact_backup_controller.go | 206 +++++++++++++++++- pkg/controller/controller_utils.go | 17 ++ pkg/controller/dependences.go | 1 + 4 files changed, 327 insertions(+), 1 deletion(-) diff --git a/pkg/apis/pingcap/v1alpha1/types.go b/pkg/apis/pingcap/v1alpha1/types.go index 0a8fe7b8ce..667bc960f0 100644 --- a/pkg/apis/pingcap/v1alpha1/types.go +++ b/pkg/apis/pingcap/v1alpha1/types.go @@ -3451,16 +3451,120 @@ type CompactBackup struct { // +k8s:openapi-gen=true type CompactSpec struct { corev1.ResourceRequirements `json:"resources,omitempty"` + // List of environment variables to set in the container, like v1.Container.Env. + // Note that the following builtin env vars will be overwritten by values set here + // - S3_PROVIDER + // - S3_ENDPOINT + // - AWS_REGION + // - AWS_ACL + // - AWS_STORAGE_CLASS + // - AWS_DEFAULT_REGION + // - AWS_ACCESS_KEY_ID + // - AWS_SECRET_ACCESS_KEY + // - GCS_PROJECT_ID + // - GCS_OBJECT_ACL + // - GCS_BUCKET_ACL + // - GCS_LOCATION + // - GCS_STORAGE_CLASS + // - GCS_SERVICE_ACCOUNT_JSON_KEY + // - BR_LOG_TO_TERM // +optional Env []corev1.EnvVar `json:"env,omitempty"` // From is the tidb cluster that needs to backup. From *TiDBAccessConfig `json:"from,omitempty"` + // Type is the backup type for tidb cluster and only used when Mode = snapshot, such as full, db, table. + Type BackupType `json:"backupType,omitempty"` + // Mode is the backup mode, such as snapshot backup or log backup. // +kubebuilder:default=snapshot Mode BackupMode `json:"backupMode,omitempty"` + // TikvGCLifeTime is to specify the safe gc life time for backup. + // The time limit during which data is retained for each GC, in the format of Go Duration. + // When a GC happens, the current time minus this value is the safe point. + TikvGCLifeTime *string `json:"tikvGCLifeTime,omitempty"` // StorageProvider configures where and how backups should be stored. StorageProvider `json:",inline"` + // The storageClassName of the persistent volume for Backup data storage. + // Defaults to Kubernetes default storage class. + // +optional + StorageClassName *string `json:"storageClassName,omitempty"` + // StorageSize is the request storage size for backup job + StorageSize string `json:"storageSize,omitempty"` // BRConfig is the configs for BR BR *BRConfig `json:"br,omitempty"` + // CommitTs is the commit ts of the backup, snapshot ts for full backup or start ts for log backup. + // Format supports TSO or datetime, e.g. '400036290571534337', '2018-05-11 01:42:23'. + // Default is current timestamp. + // +optional + CommitTs string `json:"commitTs,omitempty"` + // Subcommand is the subcommand for BR, such as start, stop, pause etc. + // +optional + // +kubebuilder:validation:Enum:="log-start";"log-stop";"log-pause" + LogSubcommand LogSubCommandType `json:"logSubcommand,omitempty"` + // LogTruncateUntil is log backup truncate until timestamp. + // Format supports TSO or datetime, e.g. '400036290571534337', '2018-05-11 01:42:23'. + // +optional + LogTruncateUntil string `json:"logTruncateUntil,omitempty"` + // LogStop indicates that will stop the log backup. + // +optional + LogStop bool `json:"logStop,omitempty"` + // CalcSizeLevel determines how to size calculation of snapshots for EBS volume snapshot backup + // +optional + // +kubebuilder:default="all" + CalcSizeLevel string `json:"calcSizeLevel,omitempty"` + // FederalVolumeBackupPhase indicates which phase to execute in federal volume backup + // +optional + FederalVolumeBackupPhase FederalVolumeBackupPhase `json:"federalVolumeBackupPhase,omitempty"` + // ResumeGcSchedule indicates whether resume gc and pd scheduler for EBS volume snapshot backup + // +optional + ResumeGcSchedule bool `json:"resumeGcSchedule,omitempty"` + // DumplingConfig is the configs for dumpling + Dumpling *DumplingConfig `json:"dumpling,omitempty"` + // Base tolerations of backup Pods, components may add more tolerations upon this respectively + // +optional + Tolerations []corev1.Toleration `json:"tolerations,omitempty"` + // ToolImage specifies the tool image used in `Backup`, which supports BR and Dumpling images. + // For examples `spec.toolImage: pingcap/br:v4.0.8` or `spec.toolImage: pingcap/dumpling:v4.0.8` + // For BR image, if it does not contain tag, Pod will use image 'ToolImage:${TiKV_Version}'. + // +optional + ToolImage string `json:"toolImage,omitempty"` + // ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images. + // +optional + ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` + // TableFilter means Table filter expression for 'db.table' matching. BR supports this from v4.0.3. + TableFilter []string `json:"tableFilter,omitempty"` + // Affinity of backup Pods + // +optional + Affinity *corev1.Affinity `json:"affinity,omitempty"` + // Use KMS to decrypt the secrets + UseKMS bool `json:"useKMS,omitempty"` + // Specify service account of backup + ServiceAccount string `json:"serviceAccount,omitempty"` + // CleanPolicy denotes whether to clean backup data when the object is deleted from the cluster, if not set, the backup data will be retained + // +kubebuilder:validation:Enum:=Retain;OnFailure;Delete + // +kubebuilder:default=Retain + CleanPolicy CleanPolicyType `json:"cleanPolicy,omitempty"` + // CleanOption controls the behavior of clean. + CleanOption *CleanOption `json:"cleanOption,omitempty"` + + // PodSecurityContext of the component + // +optional + PodSecurityContext *corev1.PodSecurityContext `json:"podSecurityContext,omitempty"` + + // PriorityClassName of Backup Job Pods + PriorityClassName string `json:"priorityClassName,omitempty"` + + // BackoffRetryPolicy the backoff retry policy, currently only valid for snapshot backup + BackoffRetryPolicy BackoffRetryPolicy `json:"backoffRetryPolicy,omitempty"` + + // Additional volumes of component pod. + // +optional + AdditionalVolumes []corev1.Volume `json:"additionalVolumes,omitempty"` + // Additional volume mounts of component pod. + // +optional + AdditionalVolumeMounts []corev1.VolumeMount `json:"additionalVolumeMounts,omitempty"` + // VolumeBackupInitJobMaxActiveSeconds represents the deadline (in seconds) of the vbk init job + // +kubebuilder:default=600 + VolumeBackupInitJobMaxActiveSeconds int `json:"volumeBackupInitJobMaxActiveSeconds,omitempty"` } type CompactStatus struct { diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index 83bed8c0ef..8b604996f6 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -4,15 +4,24 @@ import ( "fmt" "time" + "github.com/pingcap/errors" perrors "github.com/pingcap/errors" + "github.com/pingcap/tidb-operator/pkg/apis/label" "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1" + "github.com/pingcap/tidb-operator/pkg/backup/constants" + backuputil "github.com/pingcap/tidb-operator/pkg/backup/util" "github.com/pingcap/tidb-operator/pkg/controller" "github.com/pingcap/tidb-operator/pkg/metrics" + "github.com/pingcap/tidb-operator/pkg/util" + batchv1 "k8s.io/api/batch/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/tools/cache" "k8s.io/client-go/util/workqueue" "k8s.io/klog" + "k8s.io/utils/pointer" ) // Controller controls backup. @@ -80,6 +89,7 @@ func (c *Controller) updateBackup(cur interface{}) { name := newBackup.GetName() klog.Infof("backup object %s/%s enqueue", ns, name) + newBackup.Status.State = "Prepare" c.enqueueBackup(newBackup) } @@ -121,6 +131,200 @@ func (c *Controller) processNextWorkItem() bool { } func (c *Controller) sync(key string) (err error) { - klog.Infof("do nothing, skip") + startTime := time.Now() + defer func() { + duration := time.Since(startTime) + metrics.ReconcileTime.WithLabelValues(c.Name()).Observe(duration.Seconds()) + + if err == nil { + metrics.ReconcileTotal.WithLabelValues(c.Name(), metrics.LabelSuccess).Inc() + } else if perrors.Find(err, controller.IsRequeueError) != nil { + metrics.ReconcileTotal.WithLabelValues(c.Name(), metrics.LabelRequeue).Inc() + } else { + metrics.ReconcileTotal.WithLabelValues(c.Name(), metrics.LabelError).Inc() + metrics.ReconcileErrors.WithLabelValues(c.Name()).Inc() + } + + klog.V(4).Infof("Finished syncing Backup %q (%v)", key, duration) + }() + + ns, name, err := cache.SplitMetaNamespaceKey(key) + if err != nil { + return err + } + backup, err := c.deps.CompactBackupLister.CompactBackups(ns).Get(name) + if errors.IsNotFound(err) { + klog.Infof("Backup has been deleted %v", key) + return nil + } + if err != nil { + return err + } + + return c.syncCompact(backup.DeepCopy()) +} + +func (c *Controller) syncCompact(backup *v1alpha1.CompactBackup) error { + ns := backup.GetNamespace() + name := backup.GetName() + backupJobName := backup.GetName() + + // make backup job + var err error + var job *batchv1.Job + var reason string + if job, reason, err = c.makeBackupJob(backup); err != nil { + klog.Errorf("backup %s/%s create job %s failed, reason is %s, error %v.", ns, name, backupJobName, reason, err) + return err + } + + // create k8s job + klog.Infof("backup %s/%s creating job %s.", ns, name, backupJobName) + if err := c.deps.JobControl.CreateJob(backup, job); err != nil { + errMsg := fmt.Errorf("create backup %s/%s job %s failed, err: %v", ns, name, backupJobName, err) + return errMsg + } return nil } + +func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job, string, error) { + ns := backup.GetNamespace() + name := backup.GetName() + jobName := backup.GetName() + backupNamespace := ns + if backup.Spec.BR.ClusterNamespace != "" { + backupNamespace = backup.Spec.BR.ClusterNamespace + } + + tc, err := c.deps.TiDBClusterLister.TidbClusters(backupNamespace).Get(backup.Spec.BR.Cluster) + if err != nil { + return nil, fmt.Sprintf("failed to fetch tidbcluster %s/%s", backupNamespace, backup.Spec.BR.Cluster), err + } + + var ( + envVars []corev1.EnvVar + reason string + ) + if backup.Spec.From != nil { + envVars, reason, err = backuputil.GenerateTidbPasswordEnv(ns, name, backup.Spec.From.SecretName, backup.Spec.UseKMS, c.deps.SecretLister) + if err != nil { + return nil, reason, err + } + } + + storageEnv, reason, err := backuputil.GenerateStorageCertEnv(ns, backup.Spec.UseKMS, backup.Spec.StorageProvider, c.deps.SecretLister) + if err != nil { + return nil, reason, fmt.Errorf("backup %s/%s, %v", ns, name, err) + } + + envVars = append(envVars, storageEnv...) + envVars = append(envVars, corev1.EnvVar{ + Name: "BR_LOG_TO_TERM", + Value: string(rune(1)), + }) + + // set env vars specified in backup.Spec.Env + envVars = util.AppendOverwriteEnv(envVars, backup.Spec.Env) + + args := []string{ + "backup", + fmt.Sprintf("--namespace=%s", ns), + fmt.Sprintf("--backupName=%s", name), + } + tikvImage := tc.TiKVImage() + _, tikvVersion := backuputil.ParseImage(tikvImage) + if tikvVersion != "" { + args = append(args, fmt.Sprintf("--tikvVersion=%s", tikvVersion)) + } + + volumeMounts := []corev1.VolumeMount{} + volumes := []corev1.Volume{} + + brVolumeMount := corev1.VolumeMount{ + Name: "br-bin", + ReadOnly: false, + MountPath: util.BRBinPath, + } + volumeMounts = append(volumeMounts, brVolumeMount) + + volumes = append(volumes, corev1.Volume{ + Name: "br-bin", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }) + + // mount volumes if specified + if backup.Spec.Local != nil { + volumes = append(volumes, backup.Spec.Local.Volume) + volumeMounts = append(volumeMounts, backup.Spec.Local.VolumeMount) + } + + serviceAccount := constants.DefaultServiceAccountName + if backup.Spec.ServiceAccount != "" { + serviceAccount = backup.Spec.ServiceAccount + } + + brImage := "pingcap/br:" + tikvVersion + jobLabels := util.CombineStringMap(label.NewBackup().Instance("Compact-test").BackupJob().Backup(name), backup.Labels) + podLabels := jobLabels + jobAnnotations := backup.Annotations + podAnnotations := backup.Annotations + + podSpec := &corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: podLabels, + Annotations: podAnnotations, + }, + Spec: corev1.PodSpec{ + SecurityContext: backup.Spec.PodSecurityContext, + ServiceAccountName: serviceAccount, + InitContainers: []corev1.Container{ + { + Name: "br", + Image: brImage, + Command: []string{"/bin/sh", "-c"}, + Args: []string{fmt.Sprintf("cp /br %s/br; echo 'BR copy finished'", util.BRBinPath)}, + ImagePullPolicy: corev1.PullIfNotPresent, + VolumeMounts: []corev1.VolumeMount{brVolumeMount}, + Resources: backup.Spec.ResourceRequirements, + }, + }, + Containers: []corev1.Container{ + { + Name: label.BackupJobLabelVal, + Image: c.deps.CLIConfig.TiDBBackupManagerImage, + Args: args, + ImagePullPolicy: corev1.PullIfNotPresent, + VolumeMounts: volumeMounts, + Env: util.AppendEnvIfPresent(envVars, "TZ"), + Resources: backup.Spec.ResourceRequirements, + }, + }, + RestartPolicy: corev1.RestartPolicyNever, + Tolerations: backup.Spec.Tolerations, + ImagePullSecrets: backup.Spec.ImagePullSecrets, + Affinity: backup.Spec.Affinity, + Volumes: volumes, + PriorityClassName: backup.Spec.PriorityClassName, + }, + } + + job := &batchv1.Job{ + ObjectMeta: metav1.ObjectMeta{ + Name: jobName, + Namespace: ns, + Labels: jobLabels, + Annotations: jobAnnotations, + OwnerReferences: []metav1.OwnerReference{ + controller.GetCompactBackupOwnerRef(backup), + }, + }, + Spec: batchv1.JobSpec{ + BackoffLimit: pointer.Int32Ptr(0), + Template: *podSpec, + }, + } + + return job, "", nil +} diff --git a/pkg/controller/controller_utils.go b/pkg/controller/controller_utils.go index e432802be8..c321d0fc67 100644 --- a/pkg/controller/controller_utils.go +++ b/pkg/controller/controller_utils.go @@ -49,6 +49,9 @@ var ( // BackupControllerKind contains the schema.GroupVersionKind for backup controller type. BackupControllerKind = v1alpha1.SchemeGroupVersion.WithKind("Backup") + // CompactBackupControllerKind contains the schema.GroupVersionKind for backup controller type. + CompactBackupControllerKind = v1alpha1.SchemeGroupVersion.WithKind("CompactBackup") + // RestoreControllerKind contains the schema.GroupVersionKind for restore controller type. RestoreControllerKind = v1alpha1.SchemeGroupVersion.WithKind("Restore") @@ -159,6 +162,20 @@ func GetBackupOwnerRef(backup *v1alpha1.Backup) metav1.OwnerReference { } } +// GetCompactBackupOwnerRef returns Backup's OwnerReference +func GetCompactBackupOwnerRef(backup *v1alpha1.CompactBackup) metav1.OwnerReference { + controller := true + blockOwnerDeletion := true + return metav1.OwnerReference{ + APIVersion: CompactBackupControllerKind.GroupVersion().String(), + Kind: CompactBackupControllerKind.Kind, + Name: backup.GetName(), + UID: backup.GetUID(), + Controller: &controller, + BlockOwnerDeletion: &blockOwnerDeletion, + } +} + // GetRestoreOwnerRef returns Restore's OwnerReference func GetRestoreOwnerRef(restore *v1alpha1.Restore) metav1.OwnerReference { controller := true diff --git a/pkg/controller/dependences.go b/pkg/controller/dependences.go index 6c22b8e658..3ed7c4d6ee 100644 --- a/pkg/controller/dependences.go +++ b/pkg/controller/dependences.go @@ -233,6 +233,7 @@ type Dependencies struct { TiDBClusterAutoScalerLister listers.TidbClusterAutoScalerLister DMClusterLister listers.DMClusterLister BackupLister listers.BackupLister + CompactBackupLister listers.CompactBackupLister RestoreLister listers.RestoreLister BackupScheduleLister listers.BackupScheduleLister TiDBInitializerLister listers.TidbInitializerLister From a420fdbbca897f0d3c0f8af915ecf9d67ec40494 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Mon, 28 Oct 2024 10:35:03 +0100 Subject: [PATCH 04/49] adapt controller --- docs/api-references/docs.md | 1045 ++++++++++++-- manifests/crd.yaml | 1278 +++++++++++++++++ .../crd/v1/pingcap.com_compactbackups.yaml | 1278 +++++++++++++++++ .../pingcap/v1alpha1/openapi_generated.go | 231 ++- .../pingcap/v1alpha1/zz_generated.deepcopy.go | 62 + .../compact_backup_controller.go | 82 +- pkg/controller/dependences.go | 1 + 7 files changed, 3777 insertions(+), 200 deletions(-) diff --git a/docs/api-references/docs.md b/docs/api-references/docs.md index 9273c83cc9..f48ec8c628 100644 --- a/docs/api-references/docs.md +++ b/docs/api-references/docs.md @@ -3620,7 +3620,8 @@ bool

BackoffRetryPolicy

(Appears on: -BackupSpec) +BackupSpec, +CompactSpec)

BackoffRetryPolicy is the backoff retry policy, currently only valid for snapshot backup. @@ -4751,6 +4752,7 @@ map[github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.LogSubCommandType

(Appears on: BackupSpec, +CompactSpec, RestoreSpec)

@@ -5091,7 +5093,8 @@ github.com/pingcap/tidb-operator/pkg/apis/util/config.GenericConfig

CleanOption

(Appears on: -BackupSpec) +BackupSpec, +CompactSpec)

CleanOption defines the configuration for cleanup backup

@@ -5170,7 +5173,8 @@ float64

CleanPolicyType

(Appears on: -BackupSpec) +BackupSpec, +CompactSpec)

CleanPolicyType represents the clean policy of backup data in remote storage

@@ -5209,361 +5213,1089 @@ default to the same namespace as TidbMonitor/TidbCluster/TidbNGMonitoring/TidbDa -name
+name
+ +string + + + +

Name is the name of TidbCluster object

+ + + + +clusterDomain
+ +string + + + +(Optional) +

ClusterDomain is the domain of TidbCluster object

+ + + + +

CommonConfig

+

+(Appears on: +TiFlashConfig) +

+

+

CommonConfig is the configuration of TiFlash process.

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+tmp_path
+ +string + +
+(Optional) +

Optional: Defaults to “/data0/tmp”

+
+path_realtime_mode
+ +bool + +
+(Optional) +

Optional: Defaults to false

+
+mark_cache_size
+ +int64 + +
+(Optional) +

Optional: Defaults to 5368709120

+
+minmax_index_cache_size
+ +int64 + +
+(Optional) +

Optional: Defaults to 5368709120

+
+flash
+ + +Flash + + +
+(Optional) +
+logger
+ + +FlashLogger + + +
+(Optional) +
+security
+ + +FlashSecurity + + +
+(Optional) +
+

CompactBackup

+

+

+ + + + + + + + + + + + + + + + + + + + + +
FieldDescription
+metadata
+ + +Kubernetes meta/v1.ObjectMeta + + +
+Refer to the Kubernetes API documentation for the fields of the +metadata field. +
+spec
+ + +CompactSpec + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+resources
+ + +Kubernetes core/v1.ResourceRequirements + + +
+
+env
+ + +[]Kubernetes core/v1.EnvVar + + +
+(Optional) +

List of environment variables to set in the container, like v1.Container.Env. +Note that the following builtin env vars will be overwritten by values set here +- S3_PROVIDER +- S3_ENDPOINT +- AWS_REGION +- AWS_ACL +- AWS_STORAGE_CLASS +- AWS_DEFAULT_REGION +- AWS_ACCESS_KEY_ID +- AWS_SECRET_ACCESS_KEY +- GCS_PROJECT_ID +- GCS_OBJECT_ACL +- GCS_BUCKET_ACL +- GCS_LOCATION +- GCS_STORAGE_CLASS +- GCS_SERVICE_ACCOUNT_JSON_KEY +- BR_LOG_TO_TERM

+
+from
+ + +TiDBAccessConfig + + +
+

From is the tidb cluster that needs to backup.

+
+backupType
+ + +BackupType + + +
+

Type is the backup type for tidb cluster and only used when Mode = snapshot, such as full, db, table.

+
+backupMode
+ + +BackupMode + + +
+

Mode is the backup mode, such as snapshot backup or log backup.

+
+tikvGCLifeTime
+ +string + +
+

TikvGCLifeTime is to specify the safe gc life time for backup. +The time limit during which data is retained for each GC, in the format of Go Duration. +When a GC happens, the current time minus this value is the safe point.

+
+StorageProvider
+ + +StorageProvider + + +
+

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

+

StorageProvider configures where and how backups should be stored.

+
+storageClassName
+ +string + +
+(Optional) +

The storageClassName of the persistent volume for Backup data storage. +Defaults to Kubernetes default storage class.

+
+storageSize
+ +string + +
+

StorageSize is the request storage size for backup job

+
+br
+ + +BRConfig + + +
+

BRConfig is the configs for BR

+
+commitTs
+ +string + +
+(Optional) +

CommitTs is the commit ts of the backup, snapshot ts for full backup or start ts for log backup. +Format supports TSO or datetime, e.g. ‘400036290571534337’, ‘2018-05-11 01:42:23’. +Default is current timestamp.

+
+logSubcommand
+ + +LogSubCommandType + + +
+(Optional) +

Subcommand is the subcommand for BR, such as start, stop, pause etc.

+
+logTruncateUntil
+ +string + +
+(Optional) +

LogTruncateUntil is log backup truncate until timestamp. +Format supports TSO or datetime, e.g. ‘400036290571534337’, ‘2018-05-11 01:42:23’.

+
+logStop
+ +bool + +
+(Optional) +

LogStop indicates that will stop the log backup.

+
+calcSizeLevel
+ +string + +
+(Optional) +

CalcSizeLevel determines how to size calculation of snapshots for EBS volume snapshot backup

+
+federalVolumeBackupPhase
+ + +FederalVolumeBackupPhase + + +
+(Optional) +

FederalVolumeBackupPhase indicates which phase to execute in federal volume backup

+
+resumeGcSchedule
+ +bool + +
+(Optional) +

ResumeGcSchedule indicates whether resume gc and pd scheduler for EBS volume snapshot backup

+
+dumpling
+ + +DumplingConfig + + +
+

DumplingConfig is the configs for dumpling

+
+tolerations
+ + +[]Kubernetes core/v1.Toleration + + +
+(Optional) +

Base tolerations of backup Pods, components may add more tolerations upon this respectively

+
+toolImage
+ +string + +
+(Optional) +

ToolImage specifies the tool image used in Backup, which supports BR and Dumpling images. +For examples spec.toolImage: pingcap/br:v4.0.8 or spec.toolImage: pingcap/dumpling:v4.0.8 +For BR image, if it does not contain tag, Pod will use image ‘ToolImage:${TiKV_Version}’.

+
+imagePullSecrets
+ + +[]Kubernetes core/v1.LocalObjectReference + + +
+(Optional) +

ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images.

+
+tableFilter
+ +[]string + +
+

TableFilter means Table filter expression for ‘db.table’ matching. BR supports this from v4.0.3.

+
+affinity
+ + +Kubernetes core/v1.Affinity + + +
+(Optional) +

Affinity of backup Pods

+
+useKMS
+ +bool + +
+

Use KMS to decrypt the secrets

+
+serviceAccount
+ +string + +
+

Specify service account of backup

+
+cleanPolicy
+ + +CleanPolicyType + + +
+

CleanPolicy denotes whether to clean backup data when the object is deleted from the cluster, if not set, the backup data will be retained

+
+cleanOption
+ + +CleanOption + + +
+

CleanOption controls the behavior of clean.

+
+podSecurityContext
+ + +Kubernetes core/v1.PodSecurityContext + + +
+(Optional) +

PodSecurityContext of the component

+
+priorityClassName
+ +string + +
+

PriorityClassName of Backup Job Pods

+
+backoffRetryPolicy
+ + +BackoffRetryPolicy + + +
+

BackoffRetryPolicy the backoff retry policy, currently only valid for snapshot backup

+
+additionalVolumes
+ + +[]Kubernetes core/v1.Volume + + +
+(Optional) +

Additional volumes of component pod.

+
+additionalVolumeMounts
+ + +[]Kubernetes core/v1.VolumeMount + + +
+(Optional) +

Additional volume mounts of component pod.

+
+volumeBackupInitJobMaxActiveSeconds
+ +int + +
+

VolumeBackupInitJobMaxActiveSeconds represents the deadline (in seconds) of the vbk init job

+
+
+status
+ + +CompactStatus + + +
+
+

CompactSpec

+

+(Appears on: +CompactBackup) +

+

+

BackupSpec contains the backup specification for a tidb cluster.

+

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - -
FieldDescription
+resources
+ + +Kubernetes core/v1.ResourceRequirements + + +
+
+env
+ + +[]Kubernetes core/v1.EnvVar + + +
+(Optional) +

List of environment variables to set in the container, like v1.Container.Env. +Note that the following builtin env vars will be overwritten by values set here +- S3_PROVIDER +- S3_ENDPOINT +- AWS_REGION +- AWS_ACL +- AWS_STORAGE_CLASS +- AWS_DEFAULT_REGION +- AWS_ACCESS_KEY_ID +- AWS_SECRET_ACCESS_KEY +- GCS_PROJECT_ID +- GCS_OBJECT_ACL +- GCS_BUCKET_ACL +- GCS_LOCATION +- GCS_STORAGE_CLASS +- GCS_SERVICE_ACCOUNT_JSON_KEY +- BR_LOG_TO_TERM

+
+from
+ + +TiDBAccessConfig + + +
+

From is the tidb cluster that needs to backup.

+
+backupType
+ + +BackupType + + +
+

Type is the backup type for tidb cluster and only used when Mode = snapshot, such as full, db, table.

+
+backupMode
+ + +BackupMode + + +
+

Mode is the backup mode, such as snapshot backup or log backup.

+
+tikvGCLifeTime
+ +string + +
+

TikvGCLifeTime is to specify the safe gc life time for backup. +The time limit during which data is retained for each GC, in the format of Go Duration. +When a GC happens, the current time minus this value is the safe point.

+
+StorageProvider
+ + +StorageProvider + + +
+

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

+

StorageProvider configures where and how backups should be stored.

+
+storageClassName
+ +string + +
+(Optional) +

The storageClassName of the persistent volume for Backup data storage. +Defaults to Kubernetes default storage class.

+
+storageSize
+ +string + +
+

StorageSize is the request storage size for backup job

+
+br
-string + +BRConfig +
-

Name is the name of TidbCluster object

+

BRConfig is the configs for BR

-clusterDomain
+commitTs
string
(Optional) -

ClusterDomain is the domain of TidbCluster object

+

CommitTs is the commit ts of the backup, snapshot ts for full backup or start ts for log backup. +Format supports TSO or datetime, e.g. ‘400036290571534337’, ‘2018-05-11 01:42:23’. +Default is current timestamp.

-

CommonConfig

-

-(Appears on: -TiFlashConfig) -

-

-

CommonConfig is the configuration of TiFlash process.

-

- - - - - - - - - -
FieldDescription
-tmp_path
+logSubcommand
-string + +LogSubCommandType +
(Optional) -

Optional: Defaults to “/data0/tmp”

+

Subcommand is the subcommand for BR, such as start, stop, pause etc.

-path_realtime_mode
+logTruncateUntil
-bool +string
(Optional) -

Optional: Defaults to false

+

LogTruncateUntil is log backup truncate until timestamp. +Format supports TSO or datetime, e.g. ‘400036290571534337’, ‘2018-05-11 01:42:23’.

-mark_cache_size
+logStop
-int64 +bool
(Optional) -

Optional: Defaults to 5368709120

+

LogStop indicates that will stop the log backup.

-minmax_index_cache_size
+calcSizeLevel
-int64 +string
(Optional) -

Optional: Defaults to 5368709120

+

CalcSizeLevel determines how to size calculation of snapshots for EBS volume snapshot backup

-flash
+federalVolumeBackupPhase
- -Flash + +FederalVolumeBackupPhase
(Optional) +

FederalVolumeBackupPhase indicates which phase to execute in federal volume backup

-logger
+resumeGcSchedule
- -FlashLogger - +bool
(Optional) +

ResumeGcSchedule indicates whether resume gc and pd scheduler for EBS volume snapshot backup

-security
+dumpling
- -FlashSecurity + +DumplingConfig
-(Optional) +

DumplingConfig is the configs for dumpling

-

CompactBackup

-

-

- - - - - - - - - -
FieldDescription
-metadata
+tolerations
- -Kubernetes meta/v1.ObjectMeta + +[]Kubernetes core/v1.Toleration
-Refer to the Kubernetes API documentation for the fields of the -metadata field. +(Optional) +

Base tolerations of backup Pods, components may add more tolerations upon this respectively

-spec
+toolImage
- -CompactSpec - +string
-
-
- +(Optional) +

ToolImage specifies the tool image used in Backup, which supports BR and Dumpling images. +For examples spec.toolImage: pingcap/br:v4.0.8 or spec.toolImage: pingcap/dumpling:v4.0.8 +For BR image, if it does not contain tag, Pod will use image ‘ToolImage:${TiKV_Version}’.

+ + - -
-resources
+imagePullSecrets
- -Kubernetes core/v1.ResourceRequirements + +[]Kubernetes core/v1.LocalObjectReference
+(Optional) +

ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images.

-env
+tableFilter
- -[]Kubernetes core/v1.EnvVar - +[]string
-(Optional) +

TableFilter means Table filter expression for ‘db.table’ matching. BR supports this from v4.0.3.

-from
+affinity
- -TiDBAccessConfig + +Kubernetes core/v1.Affinity
-

From is the tidb cluster that needs to backup.

+(Optional) +

Affinity of backup Pods

-backupMode
+useKMS
- -BackupMode - +bool
+

Use KMS to decrypt the secrets

-StorageProvider
+serviceAccount
- -StorageProvider - +string
-

-(Members of StorageProvider are embedded into this type.) -

-

StorageProvider configures where and how backups should be stored.

+

Specify service account of backup

-br
+cleanPolicy
- -BRConfig + +CleanPolicyType
-

BRConfig is the configs for BR

-
+

CleanPolicy denotes whether to clean backup data when the object is deleted from the cluster, if not set, the backup data will be retained

-status
+cleanOption
- -CompactStatus + +CleanOption
+

CleanOption controls the behavior of clean.

-

CompactSpec

-

-(Appears on: -CompactBackup) -

-

-

BackupSpec contains the backup specification for a tidb cluster.

-

- - - - - - - - @@ -7476,7 +8208,8 @@ for other components, the auto failover feature may be used instead.

DumplingConfig

(Appears on: -BackupSpec) +BackupSpec, +CompactSpec)

DumplingConfig contains config for dumpling

@@ -7767,7 +8500,8 @@ it takes effect only when set spec.recoverFailover=false

FederalVolumeBackupPhase

(Appears on: -BackupSpec) +BackupSpec, +CompactSpec)

FederalVolumeBackupPhase represents a phase to execute in federal volume backup

@@ -9179,6 +9913,7 @@ BackupConditionType (Appears on:BackupCondition, BackupSpec, +CompactSpec, LogSubCommandStatus)

diff --git a/manifests/crd.yaml b/manifests/crd.yaml index f774305743..21e5419639 100644 --- a/manifests/crd.yaml +++ b/manifests/crd.yaml @@ -7083,6 +7083,1102 @@ spec: type: object spec: properties: + additionalVolumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + additionalVolumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + type: string + kind: + type: string + readOnly: + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + wwids: + items: + type: string + type: array + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + 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 + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + 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 + x-kubernetes-map-type: atomic + type: array + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + 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: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + 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: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + 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: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + 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: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object azblob: properties: accessTier: @@ -7100,9 +8196,23 @@ spec: storageAccount: type: string type: object + backoffRetryPolicy: + properties: + maxRetryTimes: + default: 2 + type: integer + minRetryDuration: + default: 300s + type: string + retryTimeout: + default: 30m + type: string + type: object backupMode: default: snapshot type: string + backupType: + type: string br: properties: checkRequirements: @@ -7139,6 +8249,51 @@ spec: required: - cluster type: object + calcSizeLevel: + default: all + type: string + cleanOption: + properties: + backoffEnabled: + type: boolean + batchConcurrency: + format: int32 + type: integer + disableBatchConcurrency: + type: boolean + pageSize: + format: int64 + type: integer + retryCount: + default: 5 + type: integer + routineConcurrency: + format: int32 + type: integer + snapshotsDeleteRatio: + default: 1 + type: number + type: object + cleanPolicy: + default: Retain + enum: + - Retain + - OnFailure + - Delete + type: string + commitTs: + type: string + dumpling: + properties: + options: + items: + type: string + type: array + tableFilter: + items: + type: string + type: array + type: object env: items: properties: @@ -7203,6 +8358,8 @@ spec: - name type: object type: array + federalVolumeBackupPhase: + type: string from: properties: host: @@ -7243,6 +8400,14 @@ spec: required: - projectId type: object + imagePullSecrets: + items: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + type: array local: properties: prefix: @@ -7978,6 +9143,82 @@ spec: - volume - volumeMount type: object + logStop: + type: boolean + logSubcommand: + enum: + - log-start + - log-stop + - log-pause + type: string + logTruncateUntil: + type: string + podSecurityContext: + properties: + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + 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 + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + 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 + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + priorityClassName: + type: string resources: properties: claims: @@ -8009,6 +9250,8 @@ spec: x-kubernetes-int-or-string: true type: object type: object + resumeGcSchedule: + type: boolean s3: properties: acl: @@ -8038,6 +9281,41 @@ spec: required: - provider type: object + serviceAccount: + type: string + storageClassName: + type: string + storageSize: + type: string + tableFilter: + items: + type: string + type: array + tikvGCLifeTime: + 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 + toolImage: + type: string + useKMS: + type: boolean + volumeBackupInitJobMaxActiveSeconds: + default: 600 + type: integer type: object status: properties: diff --git a/manifests/crd/v1/pingcap.com_compactbackups.yaml b/manifests/crd/v1/pingcap.com_compactbackups.yaml index e9f4194e1f..dee3552d38 100644 --- a/manifests/crd/v1/pingcap.com_compactbackups.yaml +++ b/manifests/crd/v1/pingcap.com_compactbackups.yaml @@ -33,6 +33,1102 @@ spec: type: object spec: properties: + additionalVolumeMounts: + items: + properties: + mountPath: + type: string + mountPropagation: + type: string + name: + type: string + readOnly: + type: boolean + subPath: + type: string + subPathExpr: + type: string + required: + - mountPath + - name + type: object + type: array + additionalVolumes: + items: + properties: + awsElasticBlockStore: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + azureDisk: + properties: + cachingMode: + type: string + diskName: + type: string + diskURI: + type: string + fsType: + type: string + kind: + type: string + readOnly: + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + properties: + readOnly: + type: boolean + secretName: + type: string + shareName: + type: string + required: + - secretName + - shareName + type: object + cephfs: + properties: + monitors: + items: + type: string + type: array + path: + type: string + readOnly: + type: boolean + secretFile: + type: string + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - monitors + type: object + cinder: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + volumeID: + type: string + required: + - volumeID + type: object + configMap: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + properties: + driver: + type: string + fsType: + type: string + nodePublishSecretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + type: boolean + volumeAttributes: + additionalProperties: + type: string + type: object + required: + - driver + type: object + downwardAPI: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + emptyDir: + properties: + medium: + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + properties: + volumeClaimTemplate: + properties: + metadata: + type: object + spec: + properties: + accessModes: + items: + type: string + type: array + dataSource: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + dataSourceRef: + properties: + apiGroup: + type: string + kind: + type: string + name: + type: string + namespace: + type: string + required: + - kind + - name + type: object + resources: + properties: + claims: + items: + properties: + name: + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: object + selector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + storageClassName: + type: string + volumeMode: + type: string + volumeName: + type: string + type: object + required: + - spec + type: object + type: object + fc: + properties: + fsType: + type: string + lun: + format: int32 + type: integer + readOnly: + type: boolean + targetWWNs: + items: + type: string + type: array + wwids: + items: + type: string + type: array + type: object + flexVolume: + properties: + driver: + type: string + fsType: + type: string + options: + additionalProperties: + type: string + type: object + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + required: + - driver + type: object + flocker: + properties: + datasetName: + type: string + datasetUUID: + type: string + type: object + gcePersistentDisk: + properties: + fsType: + type: string + partition: + format: int32 + type: integer + pdName: + type: string + readOnly: + type: boolean + required: + - pdName + type: object + gitRepo: + properties: + directory: + type: string + repository: + type: string + revision: + type: string + required: + - repository + type: object + glusterfs: + properties: + endpoints: + type: string + path: + type: string + readOnly: + type: boolean + required: + - endpoints + - path + type: object + hostPath: + properties: + path: + type: string + type: + type: string + required: + - path + type: object + iscsi: + properties: + chapAuthDiscovery: + type: boolean + chapAuthSession: + type: boolean + fsType: + type: string + initiatorName: + type: string + iqn: + type: string + iscsiInterface: + type: string + lun: + format: int32 + type: integer + portals: + items: + type: string + type: array + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + targetPortal: + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + type: string + nfs: + properties: + path: + type: string + readOnly: + type: boolean + server: + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + properties: + claimName: + type: string + readOnly: + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + properties: + fsType: + type: string + pdID: + type: string + required: + - pdID + type: object + portworxVolume: + properties: + fsType: + type: string + readOnly: + type: boolean + volumeID: + type: string + required: + - volumeID + type: object + projected: + properties: + defaultMode: + format: int32 + type: integer + sources: + items: + properties: + configMap: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + properties: + items: + items: + properties: + fieldRef: + properties: + apiVersion: + type: string + fieldPath: + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + format: int32 + type: integer + path: + type: string + resourceFieldRef: + properties: + containerName: + type: string + divisor: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + type: object + secret: + properties: + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + name: + type: string + optional: + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + properties: + audience: + type: string + expirationSeconds: + format: int64 + type: integer + path: + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + properties: + group: + type: string + readOnly: + type: boolean + registry: + type: string + tenant: + type: string + user: + type: string + volume: + type: string + required: + - registry + - volume + type: object + rbd: + properties: + fsType: + type: string + image: + type: string + keyring: + type: string + monitors: + items: + type: string + type: array + pool: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + user: + type: string + required: + - image + - monitors + type: object + scaleIO: + properties: + fsType: + type: string + gateway: + type: string + protectionDomain: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + sslEnabled: + type: boolean + storageMode: + type: string + storagePool: + type: string + system: + type: string + volumeName: + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + properties: + defaultMode: + format: int32 + type: integer + items: + items: + properties: + key: + type: string + mode: + format: int32 + type: integer + path: + type: string + required: + - key + - path + type: object + type: array + optional: + type: boolean + secretName: + type: string + type: object + storageos: + properties: + fsType: + type: string + readOnly: + type: boolean + secretRef: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + volumeName: + type: string + volumeNamespace: + type: string + type: object + vsphereVolume: + properties: + fsType: + type: string + storagePolicyID: + type: string + storagePolicyName: + type: string + volumePath: + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + 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 + x-kubernetes-map-type: atomic + weight: + format: int32 + type: integer + required: + - preference + - weight + 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 + x-kubernetes-map-type: atomic + type: array + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + 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: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + 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: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + 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: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + weight: + format: int32 + type: integer + required: + - podAffinityTerm + - weight + 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: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaceSelector: + properties: + matchExpressions: + items: + properties: + key: + type: string + operator: + type: string + values: + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + items: + type: string + type: array + topologyKey: + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object azblob: properties: accessTier: @@ -50,9 +1146,23 @@ spec: storageAccount: type: string type: object + backoffRetryPolicy: + properties: + maxRetryTimes: + default: 2 + type: integer + minRetryDuration: + default: 300s + type: string + retryTimeout: + default: 30m + type: string + type: object backupMode: default: snapshot type: string + backupType: + type: string br: properties: checkRequirements: @@ -89,6 +1199,51 @@ spec: required: - cluster type: object + calcSizeLevel: + default: all + type: string + cleanOption: + properties: + backoffEnabled: + type: boolean + batchConcurrency: + format: int32 + type: integer + disableBatchConcurrency: + type: boolean + pageSize: + format: int64 + type: integer + retryCount: + default: 5 + type: integer + routineConcurrency: + format: int32 + type: integer + snapshotsDeleteRatio: + default: 1 + type: number + type: object + cleanPolicy: + default: Retain + enum: + - Retain + - OnFailure + - Delete + type: string + commitTs: + type: string + dumpling: + properties: + options: + items: + type: string + type: array + tableFilter: + items: + type: string + type: array + type: object env: items: properties: @@ -153,6 +1308,8 @@ spec: - name type: object type: array + federalVolumeBackupPhase: + type: string from: properties: host: @@ -193,6 +1350,14 @@ spec: required: - projectId type: object + imagePullSecrets: + items: + properties: + name: + type: string + type: object + x-kubernetes-map-type: atomic + type: array local: properties: prefix: @@ -928,6 +2093,82 @@ spec: - volume - volumeMount type: object + logStop: + type: boolean + logSubcommand: + enum: + - log-start + - log-stop + - log-pause + type: string + logTruncateUntil: + type: string + podSecurityContext: + properties: + fsGroup: + format: int64 + type: integer + fsGroupChangePolicy: + type: string + 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 + seccompProfile: + properties: + localhostProfile: + type: string + type: + type: string + required: + - type + 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 + hostProcess: + type: boolean + runAsUserName: + type: string + type: object + type: object + priorityClassName: + type: string resources: properties: claims: @@ -959,6 +2200,8 @@ spec: x-kubernetes-int-or-string: true type: object type: object + resumeGcSchedule: + type: boolean s3: properties: acl: @@ -988,6 +2231,41 @@ spec: required: - provider type: object + serviceAccount: + type: string + storageClassName: + type: string + storageSize: + type: string + tableFilter: + items: + type: string + type: array + tikvGCLifeTime: + 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 + toolImage: + type: string + useKMS: + type: boolean + volumeBackupInitJobMaxActiveSeconds: + default: 600 + type: integer type: object status: properties: diff --git a/pkg/apis/pingcap/v1alpha1/openapi_generated.go b/pkg/apis/pingcap/v1alpha1/openapi_generated.go index 7a8e44f238..6d510d42f8 100644 --- a/pkg/apis/pingcap/v1alpha1/openapi_generated.go +++ b/pkg/apis/pingcap/v1alpha1/openapi_generated.go @@ -1717,7 +1717,8 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) }, "env": { SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, + Description: "List of environment variables to set in the container, like v1.Container.Env. Note that the following builtin env vars will be overwritten by values set here - S3_PROVIDER - S3_ENDPOINT - AWS_REGION - AWS_ACL - AWS_STORAGE_CLASS - AWS_DEFAULT_REGION - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY - GCS_PROJECT_ID - GCS_OBJECT_ACL - GCS_BUCKET_ACL - GCS_LOCATION - GCS_STORAGE_CLASS - GCS_SERVICE_ACCOUNT_JSON_KEY - BR_LOG_TO_TERM", + Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -1734,10 +1735,25 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiDBAccessConfig"), }, }, + "backupType": { + SchemaProps: spec.SchemaProps{ + Description: "Type is the backup type for tidb cluster and only used when Mode = snapshot, such as full, db, table.", + Type: []string{"string"}, + Format: "", + }, + }, "backupMode": { SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", + Description: "Mode is the backup mode, such as snapshot backup or log backup.", + Type: []string{"string"}, + Format: "", + }, + }, + "tikvGCLifeTime": { + SchemaProps: spec.SchemaProps{ + Description: "TikvGCLifeTime is to specify the safe gc life time for backup. The time limit during which data is retained for each GC, in the format of Go Duration. When a GC happens, the current time minus this value is the safe point.", + Type: []string{"string"}, + Format: "", }, }, "s3": { @@ -1760,17 +1776,224 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.LocalStorageProvider"), }, }, + "storageClassName": { + SchemaProps: spec.SchemaProps{ + Description: "The storageClassName of the persistent volume for Backup data storage. Defaults to Kubernetes default storage class.", + Type: []string{"string"}, + Format: "", + }, + }, + "storageSize": { + SchemaProps: spec.SchemaProps{ + Description: "StorageSize is the request storage size for backup job", + Type: []string{"string"}, + Format: "", + }, + }, "br": { SchemaProps: spec.SchemaProps{ Description: "BRConfig is the configs for BR", Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BRConfig"), }, }, + "commitTs": { + SchemaProps: spec.SchemaProps{ + Description: "CommitTs is the commit ts of the backup, snapshot ts for full backup or start ts for log backup. Format supports TSO or datetime, e.g. '400036290571534337', '2018-05-11 01:42:23'. Default is current timestamp.", + Type: []string{"string"}, + Format: "", + }, + }, + "logSubcommand": { + SchemaProps: spec.SchemaProps{ + Description: "Subcommand is the subcommand for BR, such as start, stop, pause etc.", + Type: []string{"string"}, + Format: "", + }, + }, + "logTruncateUntil": { + SchemaProps: spec.SchemaProps{ + Description: "LogTruncateUntil is log backup truncate until timestamp. Format supports TSO or datetime, e.g. '400036290571534337', '2018-05-11 01:42:23'.", + Type: []string{"string"}, + Format: "", + }, + }, + "logStop": { + SchemaProps: spec.SchemaProps{ + Description: "LogStop indicates that will stop the log backup.", + Type: []string{"boolean"}, + Format: "", + }, + }, + "calcSizeLevel": { + SchemaProps: spec.SchemaProps{ + Description: "CalcSizeLevel determines how to size calculation of snapshots for EBS volume snapshot backup", + Type: []string{"string"}, + Format: "", + }, + }, + "federalVolumeBackupPhase": { + SchemaProps: spec.SchemaProps{ + Description: "FederalVolumeBackupPhase indicates which phase to execute in federal volume backup", + Type: []string{"string"}, + Format: "", + }, + }, + "resumeGcSchedule": { + SchemaProps: spec.SchemaProps{ + Description: "ResumeGcSchedule indicates whether resume gc and pd scheduler for EBS volume snapshot backup", + Type: []string{"boolean"}, + Format: "", + }, + }, + "dumpling": { + SchemaProps: spec.SchemaProps{ + Description: "DumplingConfig is the configs for dumpling", + Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.DumplingConfig"), + }, + }, + "tolerations": { + SchemaProps: spec.SchemaProps{ + Description: "Base tolerations of backup Pods, components may add more tolerations upon this respectively", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.Toleration"), + }, + }, + }, + }, + }, + "toolImage": { + SchemaProps: spec.SchemaProps{ + Description: "ToolImage specifies the tool image used in `Backup`, which supports BR and Dumpling images. For examples `spec.toolImage: pingcap/br:v4.0.8` or `spec.toolImage: pingcap/dumpling:v4.0.8` For BR image, if it does not contain tag, Pod will use image 'ToolImage:${TiKV_Version}'.", + Type: []string{"string"}, + Format: "", + }, + }, + "imagePullSecrets": { + SchemaProps: spec.SchemaProps{ + Description: "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.LocalObjectReference"), + }, + }, + }, + }, + }, + "tableFilter": { + SchemaProps: spec.SchemaProps{ + Description: "TableFilter means Table filter expression for 'db.table' matching. BR supports this from v4.0.3.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + }, + }, + "affinity": { + SchemaProps: spec.SchemaProps{ + Description: "Affinity of backup Pods", + Ref: ref("k8s.io/api/core/v1.Affinity"), + }, + }, + "useKMS": { + SchemaProps: spec.SchemaProps{ + Description: "Use KMS to decrypt the secrets", + Type: []string{"boolean"}, + Format: "", + }, + }, + "serviceAccount": { + SchemaProps: spec.SchemaProps{ + Description: "Specify service account of backup", + Type: []string{"string"}, + Format: "", + }, + }, + "cleanPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "CleanPolicy denotes whether to clean backup data when the object is deleted from the cluster, if not set, the backup data will be retained", + Type: []string{"string"}, + Format: "", + }, + }, + "cleanOption": { + SchemaProps: spec.SchemaProps{ + Description: "CleanOption controls the behavior of clean.", + Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.CleanOption"), + }, + }, + "podSecurityContext": { + SchemaProps: spec.SchemaProps{ + Description: "PodSecurityContext of the component", + Ref: ref("k8s.io/api/core/v1.PodSecurityContext"), + }, + }, + "priorityClassName": { + SchemaProps: spec.SchemaProps{ + Description: "PriorityClassName of Backup Job Pods", + Type: []string{"string"}, + Format: "", + }, + }, + "backoffRetryPolicy": { + SchemaProps: spec.SchemaProps{ + Description: "BackoffRetryPolicy the backoff retry policy, currently only valid for snapshot backup", + Default: map[string]interface{}{}, + Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BackoffRetryPolicy"), + }, + }, + "additionalVolumes": { + SchemaProps: spec.SchemaProps{ + Description: "Additional volumes of component pod.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.Volume"), + }, + }, + }, + }, + }, + "additionalVolumeMounts": { + SchemaProps: spec.SchemaProps{ + Description: "Additional volume mounts of component pod.", + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("k8s.io/api/core/v1.VolumeMount"), + }, + }, + }, + }, + }, + "volumeBackupInitJobMaxActiveSeconds": { + SchemaProps: spec.SchemaProps{ + Description: "VolumeBackupInitJobMaxActiveSeconds represents the deadline (in seconds) of the vbk init job", + Type: []string{"integer"}, + Format: "int32", + }, + }, }, }, }, Dependencies: []string{ - "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.AzblobStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BRConfig", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.GcsStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.LocalStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.S3StorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiDBAccessConfig", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.ResourceRequirements"}, + "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.AzblobStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BRConfig", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BackoffRetryPolicy", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.CleanOption", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.DumplingConfig", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.GcsStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.LocalStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.S3StorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiDBAccessConfig", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume", "k8s.io/api/core/v1.VolumeMount"}, } } diff --git a/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go index fc0b823152..c75e387d4f 100644 --- a/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go @@ -942,12 +942,74 @@ func (in *CompactSpec) DeepCopyInto(out *CompactSpec) { *out = new(TiDBAccessConfig) (*in).DeepCopyInto(*out) } + if in.TikvGCLifeTime != nil { + in, out := &in.TikvGCLifeTime, &out.TikvGCLifeTime + *out = new(string) + **out = **in + } in.StorageProvider.DeepCopyInto(&out.StorageProvider) + if in.StorageClassName != nil { + in, out := &in.StorageClassName, &out.StorageClassName + *out = new(string) + **out = **in + } if in.BR != nil { in, out := &in.BR, &out.BR *out = new(BRConfig) (*in).DeepCopyInto(*out) } + if in.Dumpling != nil { + in, out := &in.Dumpling, &out.Dumpling + *out = new(DumplingConfig) + (*in).DeepCopyInto(*out) + } + if in.Tolerations != nil { + in, out := &in.Tolerations, &out.Tolerations + *out = make([]v1.Toleration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.ImagePullSecrets != nil { + in, out := &in.ImagePullSecrets, &out.ImagePullSecrets + *out = make([]v1.LocalObjectReference, len(*in)) + copy(*out, *in) + } + if in.TableFilter != nil { + in, out := &in.TableFilter, &out.TableFilter + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Affinity != nil { + in, out := &in.Affinity, &out.Affinity + *out = new(v1.Affinity) + (*in).DeepCopyInto(*out) + } + if in.CleanOption != nil { + in, out := &in.CleanOption, &out.CleanOption + *out = new(CleanOption) + **out = **in + } + if in.PodSecurityContext != nil { + in, out := &in.PodSecurityContext, &out.PodSecurityContext + *out = new(v1.PodSecurityContext) + (*in).DeepCopyInto(*out) + } + out.BackoffRetryPolicy = in.BackoffRetryPolicy + if in.AdditionalVolumes != nil { + in, out := &in.AdditionalVolumes, &out.AdditionalVolumes + *out = make([]v1.Volume, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.AdditionalVolumeMounts != nil { + in, out := &in.AdditionalVolumeMounts, &out.AdditionalVolumeMounts + *out = make([]v1.VolumeMount, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } return } diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index 8b604996f6..c22d26c778 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -1,6 +1,7 @@ package compact import ( + "context" "fmt" "time" @@ -8,8 +9,8 @@ import ( perrors "github.com/pingcap/errors" "github.com/pingcap/tidb-operator/pkg/apis/label" "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1" - "github.com/pingcap/tidb-operator/pkg/backup/constants" backuputil "github.com/pingcap/tidb-operator/pkg/backup/util" + "github.com/pingcap/tidb-operator/pkg/client/clientset/versioned" "github.com/pingcap/tidb-operator/pkg/controller" "github.com/pingcap/tidb-operator/pkg/metrics" "github.com/pingcap/tidb-operator/pkg/util" @@ -29,6 +30,7 @@ type Controller struct { deps *controller.Dependencies // backups that need to be synced. queue workqueue.RateLimitingInterface + cli versioned.Interface } // NewController creates a backup controller. @@ -37,11 +39,12 @@ func NewController(deps *controller.Dependencies) *Controller { deps: deps, queue: workqueue.NewNamedRateLimitingQueue( controller.NewControllerRateLimiter(1*time.Second, 100*time.Second), - "backup", + "compactBackup", ), + cli: deps.Clientset, } - backupInformer := deps.InformerFactory.Pingcap().V1alpha1().Backups() + backupInformer := deps.InformerFactory.Pingcap().V1alpha1().CompactBackups() jobInformer := deps.KubeInformerFactory.Batch().V1().Jobs() backupInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: c.updateBackup, @@ -67,8 +70,8 @@ func (c *Controller) Run(workers int, stopCh <-chan struct{}) { defer utilruntime.HandleCrash() defer c.queue.ShutDown() - klog.Info("Starting backup controller") - defer klog.Info("Shutting down backup controller") + klog.Info("Starting compact backup controller") + defer klog.Info("Shutting down compact backup controller") for i := 0; i < workers; i++ { go wait.Until(c.worker, time.Second, stopCh) @@ -90,6 +93,12 @@ func (c *Controller) updateBackup(cur interface{}) { klog.Infof("backup object %s/%s enqueue", ns, name) newBackup.Status.State = "Prepare" + _, updateErr := c.cli.PingcapV1alpha1().CompactBackups(ns).Update(context.TODO(), newBackup, metav1.UpdateOptions{}) + if updateErr == nil { + klog.Infof("Backup: [%s/%s] updated successfully", ns, newBackup.GetName()) + } else { + klog.Errorf("Backup: [%s/%s] updated failed", ns, newBackup.GetName()) + } c.enqueueBackup(newBackup) } @@ -152,6 +161,7 @@ func (c *Controller) sync(key string) (err error) { if err != nil { return err } + klog.Infof("Backup: [%s/%s] start to sync", ns, name) backup, err := c.deps.CompactBackupLister.CompactBackups(ns).Get(name) if errors.IsNotFound(err) { klog.Infof("Backup has been deleted %v", key) @@ -161,7 +171,15 @@ func (c *Controller) sync(key string) (err error) { return err } - return c.syncCompact(backup.DeepCopy()) + err = c.syncCompact(backup.DeepCopy()) + backup.Status.State = "Complete" + _, updateErr := c.cli.PingcapV1alpha1().CompactBackups(ns).Update(context.TODO(), backup, metav1.UpdateOptions{}) + if updateErr == nil { + klog.Infof("Backup: [%s/%s] updated successfully", ns, backup.GetName()) + } else { + klog.Errorf("Backup: [%s/%s] updated failed", ns, backup.GetName()) + } + return err } func (c *Controller) syncCompact(backup *v1alpha1.CompactBackup) error { @@ -260,53 +278,35 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job volumeMounts = append(volumeMounts, backup.Spec.Local.VolumeMount) } - serviceAccount := constants.DefaultServiceAccountName - if backup.Spec.ServiceAccount != "" { - serviceAccount = backup.Spec.ServiceAccount - } + // serviceAccount := constants.DefaultServiceAccountName + // if backup.Spec.ServiceAccount != "" { + // serviceAccount = backup.Spec.ServiceAccount + // } - brImage := "pingcap/br:" + tikvVersion jobLabels := util.CombineStringMap(label.NewBackup().Instance("Compact-test").BackupJob().Backup(name), backup.Labels) - podLabels := jobLabels + // podLabels := jobLabels jobAnnotations := backup.Annotations - podAnnotations := backup.Annotations + // Create a basic PodSpec with a single container that just prints "Hello World" podSpec := &corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: podLabels, - Annotations: podAnnotations, + Labels: map[string]string{"app": "backup"}, + Annotations: map[string]string{"description": "A simple backup job"}, }, Spec: corev1.PodSpec{ - SecurityContext: backup.Spec.PodSecurityContext, - ServiceAccountName: serviceAccount, - InitContainers: []corev1.Container{ - { - Name: "br", - Image: brImage, - Command: []string{"/bin/sh", "-c"}, - Args: []string{fmt.Sprintf("cp /br %s/br; echo 'BR copy finished'", util.BRBinPath)}, - ImagePullPolicy: corev1.PullIfNotPresent, - VolumeMounts: []corev1.VolumeMount{brVolumeMount}, - Resources: backup.Spec.ResourceRequirements, - }, - }, Containers: []corev1.Container{ { - Name: label.BackupJobLabelVal, - Image: c.deps.CLIConfig.TiDBBackupManagerImage, - Args: args, - ImagePullPolicy: corev1.PullIfNotPresent, - VolumeMounts: volumeMounts, - Env: util.AppendEnvIfPresent(envVars, "TZ"), - Resources: backup.Spec.ResourceRequirements, + Name: "simple-backup", + Image: "busybox", // Using a simple image for demonstration + Command: []string{ + "/bin/sh", "-c", + }, + Args: []string{ + "echo 'Backup job running successfully'; sleep 30", + }, }, }, - RestartPolicy: corev1.RestartPolicyNever, - Tolerations: backup.Spec.Tolerations, - ImagePullSecrets: backup.Spec.ImagePullSecrets, - Affinity: backup.Spec.Affinity, - Volumes: volumes, - PriorityClassName: backup.Spec.PriorityClassName, + RestartPolicy: corev1.RestartPolicyNever, }, } diff --git a/pkg/controller/dependences.go b/pkg/controller/dependences.go index 3ed7c4d6ee..34878c0c44 100644 --- a/pkg/controller/dependences.go +++ b/pkg/controller/dependences.go @@ -379,6 +379,7 @@ func newDependencies( TiDBClusterAutoScalerLister: informerFactory.Pingcap().V1alpha1().TidbClusterAutoScalers().Lister(), DMClusterLister: informerFactory.Pingcap().V1alpha1().DMClusters().Lister(), BackupLister: informerFactory.Pingcap().V1alpha1().Backups().Lister(), + CompactBackupLister: informerFactory.Pingcap().V1alpha1().CompactBackups().Lister(), RestoreLister: informerFactory.Pingcap().V1alpha1().Restores().Lister(), BackupScheduleLister: informerFactory.Pingcap().V1alpha1().BackupSchedules().Lister(), TiDBInitializerLister: informerFactory.Pingcap().V1alpha1().TidbInitializers().Lister(), From 69b6c7be052e1d07f8a5cfe4b07282726c73d703 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Mon, 28 Oct 2024 11:24:43 +0100 Subject: [PATCH 05/49] register kind --- pkg/apis/pingcap/v1alpha1/register.go | 2 ++ .../compactbackup/compact_backup_controller.go | 14 ++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/pkg/apis/pingcap/v1alpha1/register.go b/pkg/apis/pingcap/v1alpha1/register.go index 2b98a786e6..ac7de27a0a 100644 --- a/pkg/apis/pingcap/v1alpha1/register.go +++ b/pkg/apis/pingcap/v1alpha1/register.go @@ -53,6 +53,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { &TidbClusterList{}, &Backup{}, &BackupList{}, + &CompactBackup{}, + &CompactBackupList{}, &BackupSchedule{}, &BackupScheduleList{}, &Restore{}, diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index c22d26c778..27a6efe1d5 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -163,14 +163,20 @@ func (c *Controller) sync(key string) (err error) { } klog.Infof("Backup: [%s/%s] start to sync", ns, name) backup, err := c.deps.CompactBackupLister.CompactBackups(ns).Get(name) - if errors.IsNotFound(err) { - klog.Infof("Backup has been deleted %v", key) - return nil - } if err != nil { + if errors.IsNotFound(err) { + klog.Infof("Backup has been deleted %v", key) + return nil + } + klog.Infof("Backup get failed %v", err) return err } + //Skip + if backup.Status.State == "Complete" { + return nil + } + err = c.syncCompact(backup.DeepCopy()) backup.Status.State = "Complete" _, updateErr := c.cli.PingcapV1alpha1().CompactBackups(ns).Update(context.TODO(), backup, metav1.UpdateOptions{}) From 4923c61203ff213706f000f915c2a38ce0d07373 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Tue, 29 Oct 2024 05:32:13 +0100 Subject: [PATCH 06/49] adjust param --- docs/api-references/docs.md | 311 ++---------------- manifests/crd.yaml | 70 +--- .../crd/v1/pingcap.com_compactbackups.yaml | 70 +--- .../pingcap/v1alpha1/openapi_generated.go | 100 +----- pkg/apis/pingcap/v1alpha1/types.go | 58 +--- .../pingcap/v1alpha1/zz_generated.deepcopy.go | 15 - .../compact_backup_controller.go | 42 ++- pkg/util/util.go | 1 + 8 files changed, 105 insertions(+), 562 deletions(-) diff --git a/docs/api-references/docs.md b/docs/api-references/docs.md index f48ec8c628..394222e8dd 100644 --- a/docs/api-references/docs.md +++ b/docs/api-references/docs.md @@ -3869,8 +3869,7 @@ string

BackupMode

(Appears on: -BackupSpec, -CompactSpec) +BackupSpec)

BackupType represents the backup mode, such as snapshot backup or log backup.

@@ -4752,7 +4751,6 @@ map[github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.LogSubCommandType

(Appears on: BackupSpec, -CompactSpec, RestoreSpec)

@@ -5093,8 +5091,7 @@ github.com/pingcap/tidb-operator/pkg/apis/util/config.GenericConfig

CleanOption

(Appears on: -BackupSpec, -CompactSpec) +BackupSpec)

CleanOption defines the configuration for cleanup backup

@@ -5173,8 +5170,7 @@ float64

CleanPolicyType

(Appears on: -BackupSpec, -CompactSpec) +BackupSpec)

CleanPolicyType represents the clean policy of backup data in remote storage

@@ -5436,41 +5432,12 @@ TiDBAccessConfig - - - - - - - - @@ -5534,75 +5501,35 @@ string - - - - - - - - - - - - @@ -5613,21 +5540,6 @@ bool - - - - @@ -5646,16 +5558,16 @@ DumplingConfig @@ -5674,17 +5586,6 @@ For BR image, if it does not contain tag, Pod will use image ‘ToolImage:${ - - - - - - - - - - - - - - - - - - - - @@ -6010,75 +5856,35 @@ string - - - - - - - - - - - - @@ -6089,21 +5895,6 @@ bool - - - - @@ -6122,16 +5913,16 @@ DumplingConfig @@ -6150,17 +5941,6 @@ For BR image, if it does not contain tag, Pod will use image ‘ToolImage:${ - - - - - - - - - - - - - - - - + + + + + + + + - - - - + + + + + + + + + + + + + + + + - - - - - - - - - - - - @@ -5730,7 +5720,7 @@ CompactStatus CompactBackup)

-

BackupSpec contains the backup specification for a tidb cluster.

+

CompactSpec contains the backup specification for a tidb cluster.

FieldDescription
-resources
+podSecurityContext
- -Kubernetes core/v1.ResourceRequirements + +Kubernetes core/v1.PodSecurityContext
+(Optional) +

PodSecurityContext of the component

-env
+priorityClassName
- -[]Kubernetes core/v1.EnvVar - +string
-(Optional) +

PriorityClassName of Backup Job Pods

-from
+backoffRetryPolicy
- -TiDBAccessConfig + +BackoffRetryPolicy
-

From is the tidb cluster that needs to backup.

+

BackoffRetryPolicy the backoff retry policy, currently only valid for snapshot backup

-backupMode
+additionalVolumes
- -BackupMode + +[]Kubernetes core/v1.Volume
+(Optional) +

Additional volumes of component pod.

-StorageProvider
+additionalVolumeMounts
- -StorageProvider + +[]Kubernetes core/v1.VolumeMount
-

-(Members of StorageProvider are embedded into this type.) -

-

StorageProvider configures where and how backups should be stored.

+(Optional) +

Additional volume mounts of component pod.

-br
+volumeBackupInitJobMaxActiveSeconds
- -BRConfig - +int
-

BRConfig is the configs for BR

+

VolumeBackupInitJobMaxActiveSeconds represents the deadline (in seconds) of the vbk init job

-backupType
- - -BackupType - - -
-

Type is the backup type for tidb cluster and only used when Mode = snapshot, such as full, db, table.

-
-backupMode
- - -BackupMode - - -
-

Mode is the backup mode, such as snapshot backup or log backup.

-
tikvGCLifeTime
string
-

TikvGCLifeTime is to specify the safe gc life time for backup. -The time limit during which data is retained for each GC, in the format of Go Duration. -When a GC happens, the current time minus this value is the safe point.

-(Optional) -

CommitTs is the commit ts of the backup, snapshot ts for full backup or start ts for log backup. -Format supports TSO or datetime, e.g. ‘400036290571534337’, ‘2018-05-11 01:42:23’. -Default is current timestamp.

-
-logSubcommand
- - -LogSubCommandType - - -
-(Optional) -

Subcommand is the subcommand for BR, such as start, stop, pause etc.

-
-logTruncateUntil
- -string - -
-(Optional) -

LogTruncateUntil is log backup truncate until timestamp. +

StartTs is the start ts of the compact backup. Format supports TSO or datetime, e.g. ‘400036290571534337’, ‘2018-05-11 01:42:23’.

-logStop
- -bool - -
-(Optional) -

LogStop indicates that will stop the log backup.

-
-calcSizeLevel
+endTs
string
(Optional) -

CalcSizeLevel determines how to size calculation of snapshots for EBS volume snapshot backup

+

EndTs is the end ts of the compact backup. +Format supports TSO or datetime, e.g. ‘400036290571534337’, ‘2018-05-11 01:42:23’. +Default is current timestamp.

-federalVolumeBackupPhase
+concurrency
- -FederalVolumeBackupPhase - +int
(Optional) -

FederalVolumeBackupPhase indicates which phase to execute in federal volume backup

+

ResumeGcSchedule indicates whether resume gc and pd scheduler for EBS volume snapshot backup +Concurrency is the concurrency of compact backup job

-(Optional) -

ResumeGcSchedule indicates whether resume gc and pd scheduler for EBS volume snapshot backup

-
-dumpling
- - -DumplingConfig - - -
-

DumplingConfig is the configs for dumpling

-toolImage
+brImage
string
(Optional) -

ToolImage specifies the tool image used in Backup, which supports BR and Dumpling images. -For examples spec.toolImage: pingcap/br:v4.0.8 or spec.toolImage: pingcap/dumpling:v4.0.8 -For BR image, if it does not contain tag, Pod will use image ‘ToolImage:${TiKV_Version}’.

+

BrImage specifies the br image used in compact Backup. +For examples spec.brImage: pingcap/br:v4.0.8 +For BR image, if it does not contain tag, Pod will use image ‘BrImage:${TiKV_Version}’.

-tableFilter
- -[]string - -
-

TableFilter means Table filter expression for ‘db.table’ matching. BR supports this from v4.0.3.

-
affinity
@@ -5721,32 +5622,6 @@ string
-cleanPolicy
- - -CleanPolicyType - - -
-

CleanPolicy denotes whether to clean backup data when the object is deleted from the cluster, if not set, the backup data will be retained

-
-cleanOption
- - -CleanOption - - -
-

CleanOption controls the behavior of clean.

-
podSecurityContext
@@ -5912,41 +5787,12 @@ TiDBAccessConfig
-backupType
- - -BackupType - - -
-

Type is the backup type for tidb cluster and only used when Mode = snapshot, such as full, db, table.

-
-backupMode
- - -BackupMode - - -
-

Mode is the backup mode, such as snapshot backup or log backup.

-
tikvGCLifeTime
string
-

TikvGCLifeTime is to specify the safe gc life time for backup. -The time limit during which data is retained for each GC, in the format of Go Duration. -When a GC happens, the current time minus this value is the safe point.

-(Optional) -

CommitTs is the commit ts of the backup, snapshot ts for full backup or start ts for log backup. -Format supports TSO or datetime, e.g. ‘400036290571534337’, ‘2018-05-11 01:42:23’. -Default is current timestamp.

-
-logSubcommand
- - -LogSubCommandType - - -
-(Optional) -

Subcommand is the subcommand for BR, such as start, stop, pause etc.

-
-logTruncateUntil
- -string - -
-(Optional) -

LogTruncateUntil is log backup truncate until timestamp. +

StartTs is the start ts of the compact backup. Format supports TSO or datetime, e.g. ‘400036290571534337’, ‘2018-05-11 01:42:23’.

-logStop
- -bool - -
-(Optional) -

LogStop indicates that will stop the log backup.

-
-calcSizeLevel
+endTs
string
(Optional) -

CalcSizeLevel determines how to size calculation of snapshots for EBS volume snapshot backup

+

EndTs is the end ts of the compact backup. +Format supports TSO or datetime, e.g. ‘400036290571534337’, ‘2018-05-11 01:42:23’. +Default is current timestamp.

-federalVolumeBackupPhase
+concurrency
- -FederalVolumeBackupPhase - +int
(Optional) -

FederalVolumeBackupPhase indicates which phase to execute in federal volume backup

+

ResumeGcSchedule indicates whether resume gc and pd scheduler for EBS volume snapshot backup +Concurrency is the concurrency of compact backup job

-(Optional) -

ResumeGcSchedule indicates whether resume gc and pd scheduler for EBS volume snapshot backup

-
-dumpling
- - -DumplingConfig - - -
-

DumplingConfig is the configs for dumpling

-toolImage
+brImage
string
(Optional) -

ToolImage specifies the tool image used in Backup, which supports BR and Dumpling images. -For examples spec.toolImage: pingcap/br:v4.0.8 or spec.toolImage: pingcap/dumpling:v4.0.8 -For BR image, if it does not contain tag, Pod will use image ‘ToolImage:${TiKV_Version}’.

+

BrImage specifies the br image used in compact Backup. +For examples spec.brImage: pingcap/br:v4.0.8 +For BR image, if it does not contain tag, Pod will use image ‘BrImage:${TiKV_Version}’.

-tableFilter
- -[]string - -
-

TableFilter means Table filter expression for ‘db.table’ matching. BR supports this from v4.0.3.

-
affinity
@@ -6197,32 +5977,6 @@ string
-cleanPolicy
- - -CleanPolicyType - - -
-

CleanPolicy denotes whether to clean backup data when the object is deleted from the cluster, if not set, the backup data will be retained

-
-cleanOption
- - -CleanOption - - -
-

CleanOption controls the behavior of clean.

-
podSecurityContext
@@ -8208,8 +7962,7 @@ for other components, the auto failover feature may be used instead.

DumplingConfig

(Appears on: -BackupSpec, -CompactSpec) +BackupSpec)

DumplingConfig contains config for dumpling

@@ -8500,8 +8253,7 @@ it takes effect only when set spec.recoverFailover=false

FederalVolumeBackupPhase

(Appears on: -BackupSpec, -CompactSpec) +BackupSpec)

FederalVolumeBackupPhase represents a phase to execute in federal volume backup

@@ -9913,7 +9665,6 @@ BackupConditionType (Appears on: BackupCondition, BackupSpec, -CompactSpec, LogSubCommandStatus)

diff --git a/manifests/crd.yaml b/manifests/crd.yaml index 21e5419639..fe1f2320e3 100644 --- a/manifests/crd.yaml +++ b/manifests/crd.yaml @@ -8208,11 +8208,6 @@ spec: default: 30m type: string type: object - backupMode: - default: snapshot - type: string - backupType: - type: string br: properties: checkRequirements: @@ -8249,51 +8244,14 @@ spec: required: - cluster type: object - calcSizeLevel: - default: all - type: string - cleanOption: - properties: - backoffEnabled: - type: boolean - batchConcurrency: - format: int32 - type: integer - disableBatchConcurrency: - type: boolean - pageSize: - format: int64 - type: integer - retryCount: - default: 5 - type: integer - routineConcurrency: - format: int32 - type: integer - snapshotsDeleteRatio: - default: 1 - type: number - type: object - cleanPolicy: - default: Retain - enum: - - Retain - - OnFailure - - Delete + brImage: type: string commitTs: type: string - dumpling: - properties: - options: - items: - type: string - type: array - tableFilter: - items: - type: string - type: array - type: object + concurrency: + type: integer + endTs: + type: string env: items: properties: @@ -8358,8 +8316,6 @@ spec: - name type: object type: array - federalVolumeBackupPhase: - type: string from: properties: host: @@ -9143,16 +9099,6 @@ spec: - volume - volumeMount type: object - logStop: - type: boolean - logSubcommand: - enum: - - log-start - - log-stop - - log-pause - type: string - logTruncateUntil: - type: string podSecurityContext: properties: fsGroup: @@ -9287,10 +9233,6 @@ spec: type: string storageSize: type: string - tableFilter: - items: - type: string - type: array tikvGCLifeTime: type: string tolerations: @@ -9309,8 +9251,6 @@ spec: type: string type: object type: array - toolImage: - type: string useKMS: type: boolean volumeBackupInitJobMaxActiveSeconds: diff --git a/manifests/crd/v1/pingcap.com_compactbackups.yaml b/manifests/crd/v1/pingcap.com_compactbackups.yaml index dee3552d38..feb7984243 100644 --- a/manifests/crd/v1/pingcap.com_compactbackups.yaml +++ b/manifests/crd/v1/pingcap.com_compactbackups.yaml @@ -1158,11 +1158,6 @@ spec: default: 30m type: string type: object - backupMode: - default: snapshot - type: string - backupType: - type: string br: properties: checkRequirements: @@ -1199,51 +1194,14 @@ spec: required: - cluster type: object - calcSizeLevel: - default: all - type: string - cleanOption: - properties: - backoffEnabled: - type: boolean - batchConcurrency: - format: int32 - type: integer - disableBatchConcurrency: - type: boolean - pageSize: - format: int64 - type: integer - retryCount: - default: 5 - type: integer - routineConcurrency: - format: int32 - type: integer - snapshotsDeleteRatio: - default: 1 - type: number - type: object - cleanPolicy: - default: Retain - enum: - - Retain - - OnFailure - - Delete + brImage: type: string commitTs: type: string - dumpling: - properties: - options: - items: - type: string - type: array - tableFilter: - items: - type: string - type: array - type: object + concurrency: + type: integer + endTs: + type: string env: items: properties: @@ -1308,8 +1266,6 @@ spec: - name type: object type: array - federalVolumeBackupPhase: - type: string from: properties: host: @@ -2093,16 +2049,6 @@ spec: - volume - volumeMount type: object - logStop: - type: boolean - logSubcommand: - enum: - - log-start - - log-stop - - log-pause - type: string - logTruncateUntil: - type: string podSecurityContext: properties: fsGroup: @@ -2237,10 +2183,6 @@ spec: type: string storageSize: type: string - tableFilter: - items: - type: string - type: array tikvGCLifeTime: type: string tolerations: @@ -2259,8 +2201,6 @@ spec: type: string type: object type: array - toolImage: - type: string useKMS: type: boolean volumeBackupInitJobMaxActiveSeconds: diff --git a/pkg/apis/pingcap/v1alpha1/openapi_generated.go b/pkg/apis/pingcap/v1alpha1/openapi_generated.go index 6d510d42f8..8716911d4d 100644 --- a/pkg/apis/pingcap/v1alpha1/openapi_generated.go +++ b/pkg/apis/pingcap/v1alpha1/openapi_generated.go @@ -1735,25 +1735,10 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiDBAccessConfig"), }, }, - "backupType": { - SchemaProps: spec.SchemaProps{ - Description: "Type is the backup type for tidb cluster and only used when Mode = snapshot, such as full, db, table.", - Type: []string{"string"}, - Format: "", - }, - }, - "backupMode": { - SchemaProps: spec.SchemaProps{ - Description: "Mode is the backup mode, such as snapshot backup or log backup.", - Type: []string{"string"}, - Format: "", - }, - }, "tikvGCLifeTime": { SchemaProps: spec.SchemaProps{ - Description: "TikvGCLifeTime is to specify the safe gc life time for backup. The time limit during which data is retained for each GC, in the format of Go Duration. When a GC happens, the current time minus this value is the safe point.", - Type: []string{"string"}, - Format: "", + Type: []string{"string"}, + Format: "", }, }, "s3": { @@ -1798,57 +1783,30 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) }, "commitTs": { SchemaProps: spec.SchemaProps{ - Description: "CommitTs is the commit ts of the backup, snapshot ts for full backup or start ts for log backup. Format supports TSO or datetime, e.g. '400036290571534337', '2018-05-11 01:42:23'. Default is current timestamp.", - Type: []string{"string"}, - Format: "", - }, - }, - "logSubcommand": { - SchemaProps: spec.SchemaProps{ - Description: "Subcommand is the subcommand for BR, such as start, stop, pause etc.", + Description: "StartTs is the start ts of the compact backup. Format supports TSO or datetime, e.g. '400036290571534337', '2018-05-11 01:42:23'.", Type: []string{"string"}, Format: "", }, }, - "logTruncateUntil": { + "endTs": { SchemaProps: spec.SchemaProps{ - Description: "LogTruncateUntil is log backup truncate until timestamp. Format supports TSO or datetime, e.g. '400036290571534337', '2018-05-11 01:42:23'.", + Description: "EndTs is the end ts of the compact backup. Format supports TSO or datetime, e.g. '400036290571534337', '2018-05-11 01:42:23'. Default is current timestamp.", Type: []string{"string"}, Format: "", }, }, - "logStop": { - SchemaProps: spec.SchemaProps{ - Description: "LogStop indicates that will stop the log backup.", - Type: []string{"boolean"}, - Format: "", - }, - }, - "calcSizeLevel": { - SchemaProps: spec.SchemaProps{ - Description: "CalcSizeLevel determines how to size calculation of snapshots for EBS volume snapshot backup", - Type: []string{"string"}, - Format: "", - }, - }, - "federalVolumeBackupPhase": { + "concurrency": { SchemaProps: spec.SchemaProps{ - Description: "FederalVolumeBackupPhase indicates which phase to execute in federal volume backup", - Type: []string{"string"}, - Format: "", + Description: "ResumeGcSchedule indicates whether resume gc and pd scheduler for EBS volume snapshot backup Concurrency is the concurrency of compact backup job", + Default: 4, + Type: []string{"integer"}, + Format: "int32", }, }, "resumeGcSchedule": { SchemaProps: spec.SchemaProps{ - Description: "ResumeGcSchedule indicates whether resume gc and pd scheduler for EBS volume snapshot backup", - Type: []string{"boolean"}, - Format: "", - }, - }, - "dumpling": { - SchemaProps: spec.SchemaProps{ - Description: "DumplingConfig is the configs for dumpling", - Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.DumplingConfig"), + Type: []string{"boolean"}, + Format: "", }, }, "tolerations": { @@ -1865,9 +1823,9 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) }, }, }, - "toolImage": { + "brImage": { SchemaProps: spec.SchemaProps{ - Description: "ToolImage specifies the tool image used in `Backup`, which supports BR and Dumpling images. For examples `spec.toolImage: pingcap/br:v4.0.8` or `spec.toolImage: pingcap/dumpling:v4.0.8` For BR image, if it does not contain tag, Pod will use image 'ToolImage:${TiKV_Version}'.", + Description: "BrImage specifies the br image used in compact `Backup`. For examples `spec.brImage: pingcap/br:v4.0.8` For BR image, if it does not contain tag, Pod will use image 'BrImage:${TiKV_Version}'.", Type: []string{"string"}, Format: "", }, @@ -1886,21 +1844,6 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) }, }, }, - "tableFilter": { - SchemaProps: spec.SchemaProps{ - Description: "TableFilter means Table filter expression for 'db.table' matching. BR supports this from v4.0.3.", - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", - }, - }, - }, - }, - }, "affinity": { SchemaProps: spec.SchemaProps{ Description: "Affinity of backup Pods", @@ -1921,19 +1864,6 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) Format: "", }, }, - "cleanPolicy": { - SchemaProps: spec.SchemaProps{ - Description: "CleanPolicy denotes whether to clean backup data when the object is deleted from the cluster, if not set, the backup data will be retained", - Type: []string{"string"}, - Format: "", - }, - }, - "cleanOption": { - SchemaProps: spec.SchemaProps{ - Description: "CleanOption controls the behavior of clean.", - Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.CleanOption"), - }, - }, "podSecurityContext": { SchemaProps: spec.SchemaProps{ Description: "PodSecurityContext of the component", @@ -1993,7 +1923,7 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) }, }, Dependencies: []string{ - "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.AzblobStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BRConfig", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BackoffRetryPolicy", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.CleanOption", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.DumplingConfig", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.GcsStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.LocalStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.S3StorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiDBAccessConfig", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume", "k8s.io/api/core/v1.VolumeMount"}, + "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.AzblobStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BRConfig", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BackoffRetryPolicy", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.GcsStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.LocalStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.S3StorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiDBAccessConfig", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume", "k8s.io/api/core/v1.VolumeMount"}, } } diff --git a/pkg/apis/pingcap/v1alpha1/types.go b/pkg/apis/pingcap/v1alpha1/types.go index 667bc960f0..bd3a913121 100644 --- a/pkg/apis/pingcap/v1alpha1/types.go +++ b/pkg/apis/pingcap/v1alpha1/types.go @@ -3471,16 +3471,8 @@ type CompactSpec struct { // +optional Env []corev1.EnvVar `json:"env,omitempty"` // From is the tidb cluster that needs to backup. - From *TiDBAccessConfig `json:"from,omitempty"` - // Type is the backup type for tidb cluster and only used when Mode = snapshot, such as full, db, table. - Type BackupType `json:"backupType,omitempty"` - // Mode is the backup mode, such as snapshot backup or log backup. - // +kubebuilder:default=snapshot - Mode BackupMode `json:"backupMode,omitempty"` - // TikvGCLifeTime is to specify the safe gc life time for backup. - // The time limit during which data is retained for each GC, in the format of Go Duration. - // When a GC happens, the current time minus this value is the safe point. - TikvGCLifeTime *string `json:"tikvGCLifeTime,omitempty"` + From *TiDBAccessConfig `json:"from,omitempty"` + TikvGCLifeTime *string `json:"tikvGCLifeTime,omitempty"` // StorageProvider configures where and how backups should be stored. StorageProvider `json:",inline"` // The storageClassName of the persistent volume for Backup data storage. @@ -3491,47 +3483,31 @@ type CompactSpec struct { StorageSize string `json:"storageSize,omitempty"` // BRConfig is the configs for BR BR *BRConfig `json:"br,omitempty"` - // CommitTs is the commit ts of the backup, snapshot ts for full backup or start ts for log backup. + // StartTs is the start ts of the compact backup. // Format supports TSO or datetime, e.g. '400036290571534337', '2018-05-11 01:42:23'. - // Default is current timestamp. - // +optional - CommitTs string `json:"commitTs,omitempty"` - // Subcommand is the subcommand for BR, such as start, stop, pause etc. - // +optional - // +kubebuilder:validation:Enum:="log-start";"log-stop";"log-pause" - LogSubcommand LogSubCommandType `json:"logSubcommand,omitempty"` - // LogTruncateUntil is log backup truncate until timestamp. + StartTs string `json:"commitTs,omitempty"` + // EndTs is the end ts of the compact backup. // Format supports TSO or datetime, e.g. '400036290571534337', '2018-05-11 01:42:23'. + // Default is current timestamp. // +optional - LogTruncateUntil string `json:"logTruncateUntil,omitempty"` - // LogStop indicates that will stop the log backup. - // +optional - LogStop bool `json:"logStop,omitempty"` - // CalcSizeLevel determines how to size calculation of snapshots for EBS volume snapshot backup - // +optional - // +kubebuilder:default="all" - CalcSizeLevel string `json:"calcSizeLevel,omitempty"` - // FederalVolumeBackupPhase indicates which phase to execute in federal volume backup - // +optional - FederalVolumeBackupPhase FederalVolumeBackupPhase `json:"federalVolumeBackupPhase,omitempty"` + EndTs string `json:"endTs,omitempty"` // ResumeGcSchedule indicates whether resume gc and pd scheduler for EBS volume snapshot backup // +optional + // Concurrency is the concurrency of compact backup job + // +default=4 + Concurrency int `json:"concurrency,omitempty"` ResumeGcSchedule bool `json:"resumeGcSchedule,omitempty"` - // DumplingConfig is the configs for dumpling - Dumpling *DumplingConfig `json:"dumpling,omitempty"` // Base tolerations of backup Pods, components may add more tolerations upon this respectively // +optional Tolerations []corev1.Toleration `json:"tolerations,omitempty"` - // ToolImage specifies the tool image used in `Backup`, which supports BR and Dumpling images. - // For examples `spec.toolImage: pingcap/br:v4.0.8` or `spec.toolImage: pingcap/dumpling:v4.0.8` - // For BR image, if it does not contain tag, Pod will use image 'ToolImage:${TiKV_Version}'. + // BrImage specifies the br image used in compact `Backup`. + // For examples `spec.brImage: pingcap/br:v4.0.8` + // For BR image, if it does not contain tag, Pod will use image 'BrImage:${TiKV_Version}'. // +optional - ToolImage string `json:"toolImage,omitempty"` + BrImage string `json:"brImage,omitempty"` // ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images. // +optional ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` - // TableFilter means Table filter expression for 'db.table' matching. BR supports this from v4.0.3. - TableFilter []string `json:"tableFilter,omitempty"` // Affinity of backup Pods // +optional Affinity *corev1.Affinity `json:"affinity,omitempty"` @@ -3539,12 +3515,6 @@ type CompactSpec struct { UseKMS bool `json:"useKMS,omitempty"` // Specify service account of backup ServiceAccount string `json:"serviceAccount,omitempty"` - // CleanPolicy denotes whether to clean backup data when the object is deleted from the cluster, if not set, the backup data will be retained - // +kubebuilder:validation:Enum:=Retain;OnFailure;Delete - // +kubebuilder:default=Retain - CleanPolicy CleanPolicyType `json:"cleanPolicy,omitempty"` - // CleanOption controls the behavior of clean. - CleanOption *CleanOption `json:"cleanOption,omitempty"` // PodSecurityContext of the component // +optional diff --git a/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go index c75e387d4f..9da323d29c 100644 --- a/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go @@ -958,11 +958,6 @@ func (in *CompactSpec) DeepCopyInto(out *CompactSpec) { *out = new(BRConfig) (*in).DeepCopyInto(*out) } - if in.Dumpling != nil { - in, out := &in.Dumpling, &out.Dumpling - *out = new(DumplingConfig) - (*in).DeepCopyInto(*out) - } if in.Tolerations != nil { in, out := &in.Tolerations, &out.Tolerations *out = make([]v1.Toleration, len(*in)) @@ -975,21 +970,11 @@ func (in *CompactSpec) DeepCopyInto(out *CompactSpec) { *out = make([]v1.LocalObjectReference, len(*in)) copy(*out, *in) } - if in.TableFilter != nil { - in, out := &in.TableFilter, &out.TableFilter - *out = make([]string, len(*in)) - copy(*out, *in) - } if in.Affinity != nil { in, out := &in.Affinity, &out.Affinity *out = new(v1.Affinity) (*in).DeepCopyInto(*out) } - if in.CleanOption != nil { - in, out := &in.CleanOption, &out.CleanOption - *out = new(CleanOption) - **out = **in - } if in.PodSecurityContext != nil { in, out := &in.PodSecurityContext, &out.PodSecurityContext *out = new(v1.PodSecurityContext) diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index 27a6efe1d5..fd9fa1a44b 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -3,6 +3,7 @@ package compact import ( "context" "fmt" + "strings" "time" "github.com/pingcap/errors" @@ -261,6 +262,16 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job args = append(args, fmt.Sprintf("--tikvVersion=%s", tikvVersion)) } + brImage := "pingcap/br:" + tikvVersion + if backup.Spec.BrImage != "" { + image := backup.Spec.BrImage + if !strings.ContainsRune(backup.Spec.BrImage, ':') { + image = fmt.Sprintf("%s:%s", image, tikvVersion) + } + + brImage = image + } + volumeMounts := []corev1.VolumeMount{} volumes := []corev1.Volume{} @@ -284,22 +295,37 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job volumeMounts = append(volumeMounts, backup.Spec.Local.VolumeMount) } - // serviceAccount := constants.DefaultServiceAccountName - // if backup.Spec.ServiceAccount != "" { - // serviceAccount = backup.Spec.ServiceAccount - // } - jobLabels := util.CombineStringMap(label.NewBackup().Instance("Compact-test").BackupJob().Backup(name), backup.Labels) - // podLabels := jobLabels + podLabels := jobLabels jobAnnotations := backup.Annotations // Create a basic PodSpec with a single container that just prints "Hello World" podSpec := &corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{"app": "backup"}, - Annotations: map[string]string{"description": "A simple backup job"}, + Labels: podLabels, + Annotations: jobAnnotations, }, Spec: corev1.PodSpec{ + InitContainers: []corev1.Container{ + { + Name: "br", + Image: brImage, + Command: []string{"/bin/sh", "-c"}, + Args: []string{fmt.Sprintf("cp /br %s/br; echo 'BR copy finished'", util.BRBinPath)}, + ImagePullPolicy: corev1.PullIfNotPresent, + VolumeMounts: []corev1.VolumeMount{brVolumeMount}, + Resources: backup.Spec.ResourceRequirements, + }, + { + Name: "tikv-ctl", + Image: tikvImage, + Command: []string{"/bin/sh", "-c"}, + Args: []string{fmt.Sprintf("cp /tikv-ctl %s/tikv-ctl; echo 'KVCTL copy finished'", util.KVCTLBinPath)}, + ImagePullPolicy: corev1.PullIfNotPresent, + VolumeMounts: []corev1.VolumeMount{brVolumeMount}, + Resources: backup.Spec.ResourceRequirements, + }, + }, Containers: []corev1.Container{ { Name: "simple-backup", diff --git a/pkg/util/util.go b/pkg/util/util.go index 3df326f809..909224a7be 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -48,6 +48,7 @@ var ( ClusterAssetsTLSPath = "/var/lib/cluster-assets-tls" TiDBClientTLSPath = "/var/lib/tidb-client-tls" BRBinPath = "/var/lib/br-bin" + KVCTLBinPath = "/var/lib/kvctl-bin" DumplingBinPath = "/var/lib/dumpling-bin" LightningBinPath = "/var/lib/lightning-bin" ClusterClientVolName = "cluster-client-tls" From 24b88b88d028bf844fe1decfd84bf0e3d15d98f8 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Tue, 29 Oct 2024 09:42:55 +0100 Subject: [PATCH 07/49] upload state --- .../compact_backup_controller.go | 180 ++++++++++++++---- 1 file changed, 142 insertions(+), 38 deletions(-) diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index fd9fa1a44b..0cf5d788a1 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -10,6 +10,7 @@ import ( perrors "github.com/pingcap/errors" "github.com/pingcap/tidb-operator/pkg/apis/label" "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1" + "github.com/pingcap/tidb-operator/pkg/backup/constants" backuputil "github.com/pingcap/tidb-operator/pkg/backup/util" "github.com/pingcap/tidb-operator/pkg/client/clientset/versioned" "github.com/pingcap/tidb-operator/pkg/controller" @@ -21,6 +22,7 @@ import ( utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/tools/cache" + "k8s.io/client-go/util/retry" "k8s.io/client-go/util/workqueue" "k8s.io/klog" "k8s.io/utils/pointer" @@ -87,19 +89,40 @@ func (c *Controller) worker() { } } +func (c *Controller) UpdateStatus(backup *v1alpha1.CompactBackup, newState string) error { + ns := backup.GetNamespace() + backupName := backup.GetName() + // try best effort to guarantee backup is updated. + err := retry.OnError(retry.DefaultRetry, func(e error) bool { return e != nil }, func() error { + // Always get the latest backup before update. + if updated, err := c.deps.CompactBackupLister.CompactBackups(ns).Get(backupName); err == nil { + // make a copy so we don't mutate the shared cache + *backup = *(updated.DeepCopy()) + } else { + utilruntime.HandleError(fmt.Errorf("error getting updated backup %s/%s from lister: %v", ns, backupName, err)) + return err + } + if backup.Status.State != newState { + backup.Status.State = newState + _, updateErr := c.cli.PingcapV1alpha1().CompactBackups(ns).Update(context.TODO(), backup, metav1.UpdateOptions{}) + if updateErr == nil { + klog.Infof("Backup: [%s/%s] updated successfully", ns, backupName) + return nil + } + klog.Errorf("Failed to update backup [%s/%s], error: %v", ns, backupName, updateErr) + return updateErr + } + return nil + }) + return err +} + func (c *Controller) updateBackup(cur interface{}) { newBackup := cur.(*v1alpha1.CompactBackup) ns := newBackup.GetNamespace() name := newBackup.GetName() klog.Infof("backup object %s/%s enqueue", ns, name) - newBackup.Status.State = "Prepare" - _, updateErr := c.cli.PingcapV1alpha1().CompactBackups(ns).Update(context.TODO(), newBackup, metav1.UpdateOptions{}) - if updateErr == nil { - klog.Infof("Backup: [%s/%s] updated successfully", ns, newBackup.GetName()) - } else { - klog.Errorf("Backup: [%s/%s] updated failed", ns, newBackup.GetName()) - } c.enqueueBackup(newBackup) } @@ -174,22 +197,25 @@ func (c *Controller) sync(key string) (err error) { } //Skip - if backup.Status.State == "Complete" { + if backup.Status.State != "" { return nil } - err = c.syncCompact(backup.DeepCopy()) - backup.Status.State = "Complete" - _, updateErr := c.cli.PingcapV1alpha1().CompactBackups(ns).Update(context.TODO(), backup, metav1.UpdateOptions{}) - if updateErr == nil { - klog.Infof("Backup: [%s/%s] updated successfully", ns, backup.GetName()) + c.UpdateStatus(backup, "Preparing") + + err = c.doCompact(backup.DeepCopy()) + + var newState string + if err != nil { + newState = "Failed" } else { - klog.Errorf("Backup: [%s/%s] updated failed", ns, backup.GetName()) + newState = "Running" } + c.UpdateStatus(backup, newState) return err } -func (c *Controller) syncCompact(backup *v1alpha1.CompactBackup) error { +func (c *Controller) doCompact(backup *v1alpha1.CompactBackup) error { ns := backup.GetNamespace() name := backup.GetName() backupJobName := backup.GetName() @@ -215,7 +241,7 @@ func (c *Controller) syncCompact(backup *v1alpha1.CompactBackup) error { func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job, string, error) { ns := backup.GetNamespace() name := backup.GetName() - jobName := backup.GetName() + jobName := backup.GetName() + "-compact-backup" backupNamespace := ns if backup.Spec.BR.ClusterNamespace != "" { backupNamespace = backup.Spec.BR.ClusterNamespace @@ -262,50 +288,103 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job args = append(args, fmt.Sprintf("--tikvVersion=%s", tikvVersion)) } - brImage := "pingcap/br:" + tikvVersion - if backup.Spec.BrImage != "" { - image := backup.Spec.BrImage - if !strings.ContainsRune(backup.Spec.BrImage, ':') { - image = fmt.Sprintf("%s:%s", image, tikvVersion) - } - - brImage = image - } + //TODO: (Ris)What is the instance here? + jobLabels := util.CombineStringMap(label.NewBackup().Instance("Compact-test").BackupJob().Backup(name), backup.Labels) + podLabels := jobLabels + jobAnnotations := backup.Annotations + podAnnotations := jobAnnotations volumeMounts := []corev1.VolumeMount{} volumes := []corev1.Volume{} + if tc.IsTLSClusterEnabled() { + args = append(args, "--cluster-tls=true") + volumeMounts = append(volumeMounts, corev1.VolumeMount{ + Name: util.ClusterClientVolName, + ReadOnly: true, + MountPath: util.ClusterClientTLSPath, + }) + volumes = append(volumes, corev1.Volume{ + Name: util.ClusterClientVolName, + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: util.ClusterClientTLSSecretName(backup.Spec.BR.Cluster), + }, + }, + }) + } + + if backup.Spec.From != nil && tc.Spec.TiDB != nil && tc.Spec.TiDB.TLSClient != nil && tc.Spec.TiDB.TLSClient.Enabled && !tc.SkipTLSWhenConnectTiDB() { + args = append(args, "--client-tls=true") + if tc.Spec.TiDB.TLSClient.SkipInternalClientCA { + args = append(args, "--skipClientCA=true") + } + + volumeMounts = append(volumeMounts, corev1.VolumeMount{ + Name: "tidb-client-tls", + ReadOnly: true, + MountPath: util.TiDBClientTLSPath, + }) + volumes = append(volumes, corev1.Volume{ + Name: "tidb-client-tls", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: util.TiDBClientTLSSecretName(backup.Spec.BR.Cluster, backup.Spec.From.TLSClientSecretName), + }, + }, + }) + } + brVolumeMount := corev1.VolumeMount{ - Name: "br-bin", + Name: "tool-bin", ReadOnly: false, MountPath: util.BRBinPath, } volumeMounts = append(volumeMounts, brVolumeMount) volumes = append(volumes, corev1.Volume{ - Name: "br-bin", + Name: "tool-bin", VolumeSource: corev1.VolumeSource{ EmptyDir: &corev1.EmptyDirVolumeSource{}, }, }) + if len(backup.Spec.AdditionalVolumes) > 0 { + volumes = append(volumes, backup.Spec.AdditionalVolumes...) + } + if len(backup.Spec.AdditionalVolumeMounts) > 0 { + volumeMounts = append(volumeMounts, backup.Spec.AdditionalVolumeMounts...) + } + // mount volumes if specified if backup.Spec.Local != nil { volumes = append(volumes, backup.Spec.Local.Volume) volumeMounts = append(volumeMounts, backup.Spec.Local.VolumeMount) } - jobLabels := util.CombineStringMap(label.NewBackup().Instance("Compact-test").BackupJob().Backup(name), backup.Labels) - podLabels := jobLabels - jobAnnotations := backup.Annotations + serviceAccount := constants.DefaultServiceAccountName + if backup.Spec.ServiceAccount != "" { + serviceAccount = backup.Spec.ServiceAccount + } + + brImage := "pingcap/br:" + tikvVersion + if backup.Spec.BrImage != "" { + image := backup.Spec.BrImage + if !strings.ContainsRune(backup.Spec.BrImage, ':') { + image = fmt.Sprintf("%s:%s", image, tikvVersion) + } + + brImage = image + } - // Create a basic PodSpec with a single container that just prints "Hello World" podSpec := &corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: podLabels, - Annotations: jobAnnotations, + Annotations: podAnnotations, }, Spec: corev1.PodSpec{ + SecurityContext: backup.Spec.PodSecurityContext, + ServiceAccountName: serviceAccount, InitContainers: []corev1.Container{ { Name: "br", @@ -313,17 +392,27 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job Command: []string{"/bin/sh", "-c"}, Args: []string{fmt.Sprintf("cp /br %s/br; echo 'BR copy finished'", util.BRBinPath)}, ImagePullPolicy: corev1.PullIfNotPresent, - VolumeMounts: []corev1.VolumeMount{brVolumeMount}, - Resources: backup.Spec.ResourceRequirements, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "tool-bin", + MountPath: util.BRBinPath, + }, + }, + Resources: backup.Spec.ResourceRequirements, }, { Name: "tikv-ctl", Image: tikvImage, Command: []string{"/bin/sh", "-c"}, - Args: []string{fmt.Sprintf("cp /tikv-ctl %s/tikv-ctl; echo 'KVCTL copy finished'", util.KVCTLBinPath)}, + Args: []string{fmt.Sprintf("cp /tikv-ctl %s/tikv-ctl; echo 'tikv-ctl copy finished'", util.KVCTLBinPath)}, ImagePullPolicy: corev1.PullIfNotPresent, - VolumeMounts: []corev1.VolumeMount{brVolumeMount}, - Resources: backup.Spec.ResourceRequirements, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "tool-bin", + MountPath: util.KVCTLBinPath, + }, + }, + Resources: backup.Spec.ResourceRequirements, }, }, Containers: []corev1.Container{ @@ -336,9 +425,24 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job Args: []string{ "echo 'Backup job running successfully'; sleep 30", }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "tool-bin", + MountPath: util.BRBinPath, + }, + { + Name: "tool-bin", + MountPath: util.KVCTLBinPath, + }, + }, }, }, - RestartPolicy: corev1.RestartPolicyNever, + RestartPolicy: corev1.RestartPolicyNever, + Tolerations: backup.Spec.Tolerations, + ImagePullSecrets: backup.Spec.ImagePullSecrets, + Affinity: backup.Spec.Affinity, + Volumes: volumes, + PriorityClassName: backup.Spec.PriorityClassName, }, } From 50b0bb2ca8c398e7440aa73bd7007d6493984697 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Tue, 29 Oct 2024 09:49:08 +0100 Subject: [PATCH 08/49] fix --- pkg/controller/compactbackup/compact_backup_controller.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index 0cf5d788a1..1ca367011e 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -335,13 +335,6 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job }) } - brVolumeMount := corev1.VolumeMount{ - Name: "tool-bin", - ReadOnly: false, - MountPath: util.BRBinPath, - } - volumeMounts = append(volumeMounts, brVolumeMount) - volumes = append(volumes, corev1.Volume{ Name: "tool-bin", VolumeSource: corev1.VolumeSource{ From 28a38e4c1b1754fc6df59507e8489b2c25c6f576 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Wed, 30 Oct 2024 08:30:31 +0100 Subject: [PATCH 09/49] remove cluster dependency --- docs/api-references/docs.md | 75 +++++++++------ manifests/crd.yaml | 40 +------- .../crd/v1/pingcap.com_compactbackups.yaml | 40 +------- .../pingcap/v1alpha1/openapi_generated.go | 22 +++-- pkg/apis/pingcap/v1alpha1/types.go | 8 +- .../pingcap/v1alpha1/zz_generated.deepcopy.go | 5 - .../compact_backup_controller.go | 93 ++++++------------- 7 files changed, 104 insertions(+), 179 deletions(-) diff --git a/docs/api-references/docs.md b/docs/api-references/docs.md index 394222e8dd..c83464c988 100644 --- a/docs/api-references/docs.md +++ b/docs/api-references/docs.md @@ -3447,7 +3447,6 @@ string

(Appears on: BackupSpec, -CompactSpec, RestoreSpec)

@@ -5482,19 +5481,6 @@ string

-br
- - -BRConfig - - -
-

BRConfig is the configs for BR

-
commitTs
string @@ -5558,6 +5544,17 @@ bool
+version
+ +string + +
+

Version specifies the tool image version used in compact Backup.

+
brImage
string @@ -5572,6 +5569,19 @@ For BR image, if it does not contain tag, Pod will use image ‘BrImage:${Ti
+tikvImage
+ +string + +
+(Optional) +

TiKVImage specifies the tikv image used in compact Backup. +For examples spec.tikvImage: pingcap/tikv:v4.0.8

+
imagePullSecrets
@@ -5837,19 +5847,6 @@ string
-br
- - -BRConfig - - -
-

BRConfig is the configs for BR

-
commitTs
string @@ -5913,6 +5910,17 @@ bool
+version
+ +string + +
+

Version specifies the tool image version used in compact Backup.

+
brImage
string @@ -5927,6 +5935,19 @@ For BR image, if it does not contain tag, Pod will use image ‘BrImage:${Ti
+tikvImage
+ +string + +
+(Optional) +

TiKVImage specifies the tikv image used in compact Backup. +For examples spec.tikvImage: pingcap/tikv:v4.0.8

+
imagePullSecrets
diff --git a/manifests/crd.yaml b/manifests/crd.yaml index fe1f2320e3..336e5e1171 100644 --- a/manifests/crd.yaml +++ b/manifests/crd.yaml @@ -8208,42 +8208,6 @@ spec: default: 30m type: string type: object - br: - properties: - checkRequirements: - type: boolean - checksum: - type: boolean - cluster: - type: string - clusterNamespace: - type: string - concurrency: - format: int32 - type: integer - db: - type: string - logLevel: - type: string - onLine: - type: boolean - options: - items: - type: string - type: array - rateLimit: - type: integer - sendCredToTikv: - type: boolean - statusAddr: - type: string - table: - type: string - timeAgo: - type: string - required: - - cluster - type: object brImage: type: string commitTs: @@ -9235,6 +9199,8 @@ spec: type: string tikvGCLifeTime: type: string + tikvImage: + type: string tolerations: items: properties: @@ -9253,6 +9219,8 @@ spec: type: array useKMS: type: boolean + version: + type: string volumeBackupInitJobMaxActiveSeconds: default: 600 type: integer diff --git a/manifests/crd/v1/pingcap.com_compactbackups.yaml b/manifests/crd/v1/pingcap.com_compactbackups.yaml index feb7984243..c58dbfe4ae 100644 --- a/manifests/crd/v1/pingcap.com_compactbackups.yaml +++ b/manifests/crd/v1/pingcap.com_compactbackups.yaml @@ -1158,42 +1158,6 @@ spec: default: 30m type: string type: object - br: - properties: - checkRequirements: - type: boolean - checksum: - type: boolean - cluster: - type: string - clusterNamespace: - type: string - concurrency: - format: int32 - type: integer - db: - type: string - logLevel: - type: string - onLine: - type: boolean - options: - items: - type: string - type: array - rateLimit: - type: integer - sendCredToTikv: - type: boolean - statusAddr: - type: string - table: - type: string - timeAgo: - type: string - required: - - cluster - type: object brImage: type: string commitTs: @@ -2185,6 +2149,8 @@ spec: type: string tikvGCLifeTime: type: string + tikvImage: + type: string tolerations: items: properties: @@ -2203,6 +2169,8 @@ spec: type: array useKMS: type: boolean + version: + type: string volumeBackupInitJobMaxActiveSeconds: default: 600 type: integer diff --git a/pkg/apis/pingcap/v1alpha1/openapi_generated.go b/pkg/apis/pingcap/v1alpha1/openapi_generated.go index 8716911d4d..bb6adb867a 100644 --- a/pkg/apis/pingcap/v1alpha1/openapi_generated.go +++ b/pkg/apis/pingcap/v1alpha1/openapi_generated.go @@ -1775,12 +1775,6 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) Format: "", }, }, - "br": { - SchemaProps: spec.SchemaProps{ - Description: "BRConfig is the configs for BR", - Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BRConfig"), - }, - }, "commitTs": { SchemaProps: spec.SchemaProps{ Description: "StartTs is the start ts of the compact backup. Format supports TSO or datetime, e.g. '400036290571534337', '2018-05-11 01:42:23'.", @@ -1823,6 +1817,13 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) }, }, }, + "version": { + SchemaProps: spec.SchemaProps{ + Description: "Version specifies the tool image version used in compact `Backup`.", + Type: []string{"string"}, + Format: "", + }, + }, "brImage": { SchemaProps: spec.SchemaProps{ Description: "BrImage specifies the br image used in compact `Backup`. For examples `spec.brImage: pingcap/br:v4.0.8` For BR image, if it does not contain tag, Pod will use image 'BrImage:${TiKV_Version}'.", @@ -1830,6 +1831,13 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) Format: "", }, }, + "tikvImage": { + SchemaProps: spec.SchemaProps{ + Description: "TiKVImage specifies the tikv image used in compact `Backup`. For examples `spec.tikvImage: pingcap/tikv:v4.0.8`", + Type: []string{"string"}, + Format: "", + }, + }, "imagePullSecrets": { SchemaProps: spec.SchemaProps{ Description: "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images.", @@ -1923,7 +1931,7 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) }, }, Dependencies: []string{ - "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.AzblobStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BRConfig", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BackoffRetryPolicy", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.GcsStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.LocalStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.S3StorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiDBAccessConfig", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume", "k8s.io/api/core/v1.VolumeMount"}, + "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.AzblobStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BackoffRetryPolicy", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.GcsStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.LocalStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.S3StorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiDBAccessConfig", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume", "k8s.io/api/core/v1.VolumeMount"}, } } diff --git a/pkg/apis/pingcap/v1alpha1/types.go b/pkg/apis/pingcap/v1alpha1/types.go index bd3a913121..c5f536be1d 100644 --- a/pkg/apis/pingcap/v1alpha1/types.go +++ b/pkg/apis/pingcap/v1alpha1/types.go @@ -3481,8 +3481,6 @@ type CompactSpec struct { StorageClassName *string `json:"storageClassName,omitempty"` // StorageSize is the request storage size for backup job StorageSize string `json:"storageSize,omitempty"` - // BRConfig is the configs for BR - BR *BRConfig `json:"br,omitempty"` // StartTs is the start ts of the compact backup. // Format supports TSO or datetime, e.g. '400036290571534337', '2018-05-11 01:42:23'. StartTs string `json:"commitTs,omitempty"` @@ -3500,11 +3498,17 @@ type CompactSpec struct { // Base tolerations of backup Pods, components may add more tolerations upon this respectively // +optional Tolerations []corev1.Toleration `json:"tolerations,omitempty"` + // Version specifies the tool image version used in compact `Backup`. + Version string `json:"version,omitempty"` // BrImage specifies the br image used in compact `Backup`. // For examples `spec.brImage: pingcap/br:v4.0.8` // For BR image, if it does not contain tag, Pod will use image 'BrImage:${TiKV_Version}'. // +optional BrImage string `json:"brImage,omitempty"` + // TiKVImage specifies the tikv image used in compact `Backup`. + // For examples `spec.tikvImage: pingcap/tikv:v4.0.8` + // +optional + TiKVImage string `json:"tikvImage,omitempty"` // ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images. // +optional ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` diff --git a/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go index 9da323d29c..51b722ada7 100644 --- a/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go @@ -953,11 +953,6 @@ func (in *CompactSpec) DeepCopyInto(out *CompactSpec) { *out = new(string) **out = **in } - if in.BR != nil { - in, out := &in.BR, &out.BR - *out = new(BRConfig) - (*in).DeepCopyInto(*out) - } if in.Tolerations != nil { in, out := &in.Tolerations, &out.Tolerations *out = make([]v1.Toleration, len(*in)) diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index 1ca367011e..2592bfc057 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -242,26 +242,12 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job ns := backup.GetNamespace() name := backup.GetName() jobName := backup.GetName() + "-compact-backup" - backupNamespace := ns - if backup.Spec.BR.ClusterNamespace != "" { - backupNamespace = backup.Spec.BR.ClusterNamespace - } - - tc, err := c.deps.TiDBClusterLister.TidbClusters(backupNamespace).Get(backup.Spec.BR.Cluster) - if err != nil { - return nil, fmt.Sprintf("failed to fetch tidbcluster %s/%s", backupNamespace, backup.Spec.BR.Cluster), err - } var ( envVars []corev1.EnvVar reason string + err error ) - if backup.Spec.From != nil { - envVars, reason, err = backuputil.GenerateTidbPasswordEnv(ns, name, backup.Spec.From.SecretName, backup.Spec.UseKMS, c.deps.SecretLister) - if err != nil { - return nil, reason, err - } - } storageEnv, reason, err := backuputil.GenerateStorageCertEnv(ns, backup.Spec.UseKMS, backup.Spec.StorageProvider, c.deps.SecretLister) if err != nil { @@ -282,10 +268,30 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job fmt.Sprintf("--namespace=%s", ns), fmt.Sprintf("--backupName=%s", name), } - tikvImage := tc.TiKVImage() - _, tikvVersion := backuputil.ParseImage(tikvImage) + + tikvImage := "pingcap/tikv" + if backup.Spec.TiKVImage != "" { + tikvImage = backup.Spec.TiKVImage + } + + tikvVersion := backup.Spec.Version + _, imageVersion := backuputil.ParseImage(tikvImage) + if imageVersion != "" { + tikvVersion = imageVersion + } + if tikvVersion != "" { args = append(args, fmt.Sprintf("--tikvVersion=%s", tikvVersion)) + } else { + return nil, "tikv version is empty", fmt.Errorf("tikv version is empty") + } + + brImage := fmt.Sprintf("pingcap/br:%s", tikvVersion) + if backup.Spec.BrImage != "" { + brImage = backup.Spec.BrImage + if !strings.ContainsRune(brImage, ':') { + brImage = fmt.Sprintf("%s:%s", brImage, tikvVersion) + } } //TODO: (Ris)What is the instance here? @@ -297,44 +303,6 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job volumeMounts := []corev1.VolumeMount{} volumes := []corev1.Volume{} - if tc.IsTLSClusterEnabled() { - args = append(args, "--cluster-tls=true") - volumeMounts = append(volumeMounts, corev1.VolumeMount{ - Name: util.ClusterClientVolName, - ReadOnly: true, - MountPath: util.ClusterClientTLSPath, - }) - volumes = append(volumes, corev1.Volume{ - Name: util.ClusterClientVolName, - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: util.ClusterClientTLSSecretName(backup.Spec.BR.Cluster), - }, - }, - }) - } - - if backup.Spec.From != nil && tc.Spec.TiDB != nil && tc.Spec.TiDB.TLSClient != nil && tc.Spec.TiDB.TLSClient.Enabled && !tc.SkipTLSWhenConnectTiDB() { - args = append(args, "--client-tls=true") - if tc.Spec.TiDB.TLSClient.SkipInternalClientCA { - args = append(args, "--skipClientCA=true") - } - - volumeMounts = append(volumeMounts, corev1.VolumeMount{ - Name: "tidb-client-tls", - ReadOnly: true, - MountPath: util.TiDBClientTLSPath, - }) - volumes = append(volumes, corev1.Volume{ - Name: "tidb-client-tls", - VolumeSource: corev1.VolumeSource{ - Secret: &corev1.SecretVolumeSource{ - SecretName: util.TiDBClientTLSSecretName(backup.Spec.BR.Cluster, backup.Spec.From.TLSClientSecretName), - }, - }, - }) - } - volumes = append(volumes, corev1.Volume{ Name: "tool-bin", VolumeSource: corev1.VolumeSource{ @@ -360,16 +328,6 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job serviceAccount = backup.Spec.ServiceAccount } - brImage := "pingcap/br:" + tikvVersion - if backup.Spec.BrImage != "" { - image := backup.Spec.BrImage - if !strings.ContainsRune(backup.Spec.BrImage, ':') { - image = fmt.Sprintf("%s:%s", image, tikvVersion) - } - - brImage = image - } - podSpec := &corev1.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: podLabels, @@ -416,7 +374,10 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job "/bin/sh", "-c", }, Args: []string{ - "echo 'Backup job running successfully'; sleep 30", + // Check if the binaries exist; if both checks pass, proceed with the backup job + "if [ -x " + util.BRBinPath + " ] && [ -x " + util.KVCTLBinPath + " ]; then " + + "echo 'Both binaries exist. Backup job running successfully'; " + + "else echo 'Required binaries missing! Exiting...'; exit 1; fi; sleep 30", }, VolumeMounts: []corev1.VolumeMount{ { From f54633324ecf84147823a771424f14d6b064428e Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Wed, 30 Oct 2024 09:35:07 +0100 Subject: [PATCH 10/49] refactor getStoragePath --- cmd/backup-manager/app/backup/manager.go | 4 ++-- cmd/backup-manager/app/util/util.go | 20 ++++++++++---------- cmd/backup-manager/app/util/util_test.go | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cmd/backup-manager/app/backup/manager.go b/cmd/backup-manager/app/backup/manager.go index 3678f3a0bd..8a49ca1b25 100644 --- a/cmd/backup-manager/app/backup/manager.go +++ b/cmd/backup-manager/app/backup/manager.go @@ -198,7 +198,7 @@ func (bm *Manager) performBackup(ctx context.Context, backup *v1alpha1.Backup, d var errs []error - backupFullPath, err := util.GetStoragePath(backup) + backupFullPath, err := util.GetStoragePath(&backup.Spec.StorageProvider) if err != nil { errs = append(errs, err) uerr := bm.StatusUpdater.Update(backup, &v1alpha1.BackupCondition{ @@ -506,7 +506,7 @@ func (bm *Manager) performLogBackup(ctx context.Context, backup *v1alpha1.Backup // startLogBackup starts log backup. func (bm *Manager) startLogBackup(ctx context.Context, backup *v1alpha1.Backup) (*controller.BackupUpdateStatus, string, error) { started := time.Now() - backupFullPath, err := util.GetStoragePath(backup) + backupFullPath, err := util.GetStoragePath(&backup.Spec.StorageProvider) if err != nil { klog.Errorf("Get backup full path of cluster %s failed, err: %s", bm, err) return nil, "GetBackupRemotePathFailed", err diff --git a/cmd/backup-manager/app/util/util.go b/cmd/backup-manager/app/util/util.go index b00b1eaa0c..877972fb27 100644 --- a/cmd/backup-manager/app/util/util.go +++ b/cmd/backup-manager/app/util/util.go @@ -103,28 +103,28 @@ func EnsureDirectoryExist(dirName string) error { } // GetStoragePath generate the path of a specific storage -func GetStoragePath(backup *v1alpha1.Backup) (string, error) { +func GetStoragePath(StorageProvider *v1alpha1.StorageProvider) (string, error) { var url, bucket, prefix string - st := util.GetStorageType(backup.Spec.StorageProvider) + st := util.GetStorageType(*StorageProvider) switch st { case v1alpha1.BackupStorageTypeS3: - prefix = backup.Spec.StorageProvider.S3.Prefix - bucket = backup.Spec.StorageProvider.S3.Bucket + prefix = StorageProvider.S3.Prefix + bucket = StorageProvider.S3.Bucket url = fmt.Sprintf("s3://%s", path.Join(bucket, prefix)) return url, nil case v1alpha1.BackupStorageTypeGcs: - prefix = backup.Spec.StorageProvider.Gcs.Prefix - bucket = backup.Spec.StorageProvider.Gcs.Bucket + prefix = StorageProvider.Gcs.Prefix + bucket = StorageProvider.Gcs.Bucket url = fmt.Sprintf("gcs://%s/", path.Join(bucket, prefix)) return url, nil case v1alpha1.BackupStorageTypeAzblob: - prefix = backup.Spec.StorageProvider.Azblob.Prefix - bucket = backup.Spec.StorageProvider.Azblob.Container + prefix = StorageProvider.Azblob.Prefix + bucket = StorageProvider.Azblob.Container url = fmt.Sprintf("azure://%s/", path.Join(bucket, prefix)) return url, nil case v1alpha1.BackupStorageTypeLocal: - prefix = backup.Spec.StorageProvider.Local.Prefix - mountPath := backup.Spec.StorageProvider.Local.VolumeMount.MountPath + prefix = StorageProvider.Local.Prefix + mountPath := StorageProvider.Local.VolumeMount.MountPath url = fmt.Sprintf("local://%s", path.Join(mountPath, prefix)) return url, nil default: diff --git a/cmd/backup-manager/app/util/util_test.go b/cmd/backup-manager/app/util/util_test.go index 0a25c93876..0848efaf20 100644 --- a/cmd/backup-manager/app/util/util_test.go +++ b/cmd/backup-manager/app/util/util_test.go @@ -260,7 +260,7 @@ func TestGetRemotePath(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - p, err := GetStoragePath(tt.backup) + p, err := GetStoragePath(&tt.backup.Spec.StorageProvider) if tt.err { g.Expect(err).To(HaveOccurred()) return From de989f86137a7c3a993ec580d795741702a29132 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Thu, 31 Oct 2024 06:20:12 +0100 Subject: [PATCH 11/49] add env vars --- .../compact_backup_controller.go | 76 +++++++++++++------ 1 file changed, 52 insertions(+), 24 deletions(-) diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index 2592bfc057..2dd20325c9 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -57,7 +57,7 @@ func NewController(deps *controller.Dependencies) *Controller { DeleteFunc: c.updateBackup, }) jobInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ - DeleteFunc: c.updateBackup, + DeleteFunc: c.deleteJob, }) return c @@ -117,6 +117,42 @@ func (c *Controller) UpdateStatus(backup *v1alpha1.CompactBackup, newState strin return err } +func (c *Controller) resolveCompactBackupFromJob(namespace string, job *batchv1.Job) *v1alpha1.CompactBackup { + owner := metav1.GetControllerOf(job) + if owner == nil { + return nil + } + + if owner.Kind != controller.CompactBackupControllerKind.Kind { + return nil + } + + backup, err := c.deps.CompactBackupLister.CompactBackups(namespace).Get(owner.Name) + if err != nil { + return nil + } + if owner.UID != backup.UID { + return nil + } + return backup +} + +func (c *Controller) deleteJob(obj interface{}) { + job, ok := obj.(*batchv1.Job) + if !ok { + return + } + + ns := job.GetNamespace() + jobName := job.GetName() + backup := c.resolveCompactBackupFromJob(ns, job) + if backup == nil { + return + } + klog.V(4).Infof("Job %s/%s deleted through %v.", ns, jobName, utilruntime.GetCaller()) + c.updateBackup(backup) +} + func (c *Controller) updateBackup(cur interface{}) { newBackup := cur.(*v1alpha1.CompactBackup) ns := newBackup.GetNamespace() @@ -241,7 +277,7 @@ func (c *Controller) doCompact(backup *v1alpha1.CompactBackup) error { func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job, string, error) { ns := backup.GetNamespace() name := backup.GetName() - jobName := backup.GetName() + "-compact-backup" + jobName := backup.GetName() var ( envVars []corev1.EnvVar @@ -310,6 +346,17 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job }, }) + volumeMounts = append(volumeMounts, + corev1.VolumeMount{ + Name: "tool-bin", + MountPath: util.BRBinPath, + }, + corev1.VolumeMount{ + Name: "tool-bin", + MountPath: util.KVCTLBinPath, + }, + ) + if len(backup.Spec.AdditionalVolumes) > 0 { volumes = append(volumes, backup.Spec.AdditionalVolumes...) } @@ -343,12 +390,7 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job Command: []string{"/bin/sh", "-c"}, Args: []string{fmt.Sprintf("cp /br %s/br; echo 'BR copy finished'", util.BRBinPath)}, ImagePullPolicy: corev1.PullIfNotPresent, - VolumeMounts: []corev1.VolumeMount{ - { - Name: "tool-bin", - MountPath: util.BRBinPath, - }, - }, + VolumeMounts: volumeMounts, Resources: backup.Spec.ResourceRequirements, }, { @@ -357,12 +399,7 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job Command: []string{"/bin/sh", "-c"}, Args: []string{fmt.Sprintf("cp /tikv-ctl %s/tikv-ctl; echo 'tikv-ctl copy finished'", util.KVCTLBinPath)}, ImagePullPolicy: corev1.PullIfNotPresent, - VolumeMounts: []corev1.VolumeMount{ - { - Name: "tool-bin", - MountPath: util.KVCTLBinPath, - }, - }, + VolumeMounts: volumeMounts, Resources: backup.Spec.ResourceRequirements, }, }, @@ -379,16 +416,7 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job "echo 'Both binaries exist. Backup job running successfully'; " + "else echo 'Required binaries missing! Exiting...'; exit 1; fi; sleep 30", }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: "tool-bin", - MountPath: util.BRBinPath, - }, - { - Name: "tool-bin", - MountPath: util.KVCTLBinPath, - }, - }, + VolumeMounts: volumeMounts, }, }, RestartPolicy: corev1.RestartPolicyNever, From a744d6b9c31344a8c305ec1452bd81a129b74df9 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Thu, 31 Oct 2024 07:31:10 +0100 Subject: [PATCH 12/49] use backup-manager --- .../compact_backup_controller.go | 44 ++++++++++++------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index 2dd20325c9..10710f539b 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -3,6 +3,7 @@ package compact import ( "context" "fmt" + "strconv" "strings" "time" @@ -10,6 +11,7 @@ import ( perrors "github.com/pingcap/errors" "github.com/pingcap/tidb-operator/pkg/apis/label" "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1" + "github.com/pingcap/tidb-operator/pkg/apis/util/config" "github.com/pingcap/tidb-operator/pkg/backup/constants" backuputil "github.com/pingcap/tidb-operator/pkg/backup/util" "github.com/pingcap/tidb-operator/pkg/client/clientset/versioned" @@ -291,31 +293,43 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job } envVars = append(envVars, storageEnv...) - envVars = append(envVars, corev1.EnvVar{ - Name: "BR_LOG_TO_TERM", - Value: string(rune(1)), - }) // set env vars specified in backup.Spec.Env envVars = util.AppendOverwriteEnv(envVars, backup.Spec.Env) args := []string{ - "backup", + "compact", fmt.Sprintf("--namespace=%s", ns), - fmt.Sprintf("--backupName=%s", name), + fmt.Sprintf("--resourceName=%s", name), + } + startTS, err := config.ParseTSString(backup.Spec.StartTs) + if err != nil { + return nil, fmt.Sprintf("failed to parse startTs(%v)", backup.Spec.StartTs), err + } + args = append(args, "--from-ts", strconv.FormatUint(startTS, 10)) + endTS, err := config.ParseTSString(backup.Spec.EndTs) + if err != nil { + return nil, fmt.Sprintf("failed to parse endTs(%v)", backup.Spec.EndTs), err } + args = append(args, "--until-ts", strconv.FormatUint(endTS, 10)) + args = append(args, "--concurrency", backup.Spec.EndTs) + args = append(args, "--name", backup.GetObjectMeta().GetName()) + strg, err := backuputil.GetStoragePath(backup.Spec.StorageProvider) + if err != nil { + return nil, fmt.Sprintf("failed to get storage path: %v", err), err + } + args = append(args, "--storage-string", strg) tikvImage := "pingcap/tikv" if backup.Spec.TiKVImage != "" { tikvImage = backup.Spec.TiKVImage } - + tikvVersion := backup.Spec.Version _, imageVersion := backuputil.ParseImage(tikvImage) if imageVersion != "" { tikvVersion = imageVersion } - if tikvVersion != "" { args = append(args, fmt.Sprintf("--tikvVersion=%s", tikvVersion)) } else { @@ -331,7 +345,7 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job } //TODO: (Ris)What is the instance here? - jobLabels := util.CombineStringMap(label.NewBackup().Instance("Compact-test").BackupJob().Backup(name), backup.Labels) + jobLabels := util.CombineStringMap(label.NewBackup().Instance("Compact-Backup").BackupJob().Backup(name), backup.Labels) podLabels := jobLabels jobAnnotations := backup.Annotations podAnnotations := jobAnnotations @@ -405,17 +419,13 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job }, Containers: []corev1.Container{ { - Name: "simple-backup", - Image: "busybox", // Using a simple image for demonstration + Name: "backup-manager", + Image: c.deps.CLIConfig.TiDBBackupManagerImage, Command: []string{ "/bin/sh", "-c", }, - Args: []string{ - // Check if the binaries exist; if both checks pass, proceed with the backup job - "if [ -x " + util.BRBinPath + " ] && [ -x " + util.KVCTLBinPath + " ]; then " + - "echo 'Both binaries exist. Backup job running successfully'; " + - "else echo 'Required binaries missing! Exiting...'; exit 1; fi; sleep 30", - }, + Args: args, + Env: envVars, VolumeMounts: volumeMounts, }, }, From e042927159b86100e7ae6115c81616f7e14e8a46 Mon Sep 17 00:00:00 2001 From: ideascf Date: Tue, 29 Oct 2024 11:19:46 +0800 Subject: [PATCH 13/49] opt(ticdc): support to overwrite the default cluster TLS cert secret name (#5778) --- docs/api-references/docs.md | 28 +++++++++++++++++++ .../crd/v1/pingcap.com_tidbclusters.yaml | 4 +++ .../pingcap/v1alpha1/openapi_generated.go | 14 ++++++++++ pkg/apis/pingcap/v1alpha1/types.go | 12 ++++++++ pkg/manager/member/ticdc_member_manager.go | 22 +++++++++++++-- 5 files changed, 78 insertions(+), 2 deletions(-) diff --git a/docs/api-references/docs.md b/docs/api-references/docs.md index c83464c988..e80e88a5e7 100644 --- a/docs/api-references/docs.md +++ b/docs/api-references/docs.md @@ -17354,6 +17354,34 @@ client certificates for the downstream.

+clusterTLSSecretName
+ +string + +
+(Optional) +

ClusterTLSSecretName is used for overwriting the default mTLS cert secret name (see also: pkg/util/util.go:ClusterTLSSecretName) +This field is useful for sharing the same mTLS cert secret for multiple ticdc clusters connecting to the same upstream tidb cluster.

+
+clusterClientTLSSecretName
+ +string + +
+(Optional) +

ClusterTLSSecretName is used for overwriting the default cluster client cert secret name (see also: pkg/util/util.go:ClusterClientTLSSecretName) +This field is useful for sharing the same cluster client cert secret for multiple ticdc clusters connecting to the same upstream tidb cluster. +The ClusterClientTLSSecret is actually not directly used by ticdc, but it is useful for executing some commands via ticdc-ctl +by kubectl exec -it ticdc-0 -- /cdc cli --ca /var/lib/cluster-client-tls/ca.crt --cert /var/lib/cluster-client-tls/tls.crt --key /var/lib/cluster-client-tls/tls.key ....

+
baseImage
string diff --git a/manifests/crd/v1/pingcap.com_tidbclusters.yaml b/manifests/crd/v1/pingcap.com_tidbclusters.yaml index bd231fd88b..b658c82317 100644 --- a/manifests/crd/v1/pingcap.com_tidbclusters.yaml +++ b/manifests/crd/v1/pingcap.com_tidbclusters.yaml @@ -13152,6 +13152,10 @@ spec: x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map + clusterClientTLSSecretName: + type: string + clusterTLSSecretName: + type: string config: x-kubernetes-preserve-unknown-fields: true configUpdateStrategy: diff --git a/pkg/apis/pingcap/v1alpha1/openapi_generated.go b/pkg/apis/pingcap/v1alpha1/openapi_generated.go index bb6adb867a..22ceb9820c 100644 --- a/pkg/apis/pingcap/v1alpha1/openapi_generated.go +++ b/pkg/apis/pingcap/v1alpha1/openapi_generated.go @@ -9723,6 +9723,20 @@ func schema_pkg_apis_pingcap_v1alpha1_TiCDCSpec(ref common.ReferenceCallback) co }, }, }, + "clusterTLSSecretName": { + SchemaProps: spec.SchemaProps{ + Description: "ClusterTLSSecretName is used for overwriting the default mTLS cert secret name (see also: pkg/util/util.go:ClusterTLSSecretName) This field is useful for sharing the same mTLS cert secret for multiple ticdc clusters connecting to the same upstream tidb cluster.", + Type: []string{"string"}, + Format: "", + }, + }, + "clusterClientTLSSecretName": { + SchemaProps: spec.SchemaProps{ + Description: "ClusterTLSSecretName is used for overwriting the default **cluster client** cert secret name (see also: pkg/util/util.go:ClusterClientTLSSecretName) This field is useful for sharing the same cluster client cert secret for multiple ticdc clusters connecting to the same upstream tidb cluster. The ClusterClientTLSSecret is actually not directly used by ticdc, but it is useful for executing some commands via `ticdc-ctl`\n by `kubectl exec -it ticdc-0 -- /cdc cli --ca /var/lib/cluster-client-tls/ca.crt --cert /var/lib/cluster-client-tls/tls.crt --key /var/lib/cluster-client-tls/tls.key ...`.", + Type: []string{"string"}, + Format: "", + }, + }, "baseImage": { SchemaProps: spec.SchemaProps{ Description: "Base image of the component, image tag is now allowed during validation", diff --git a/pkg/apis/pingcap/v1alpha1/types.go b/pkg/apis/pingcap/v1alpha1/types.go index c5f536be1d..f2944a08ea 100644 --- a/pkg/apis/pingcap/v1alpha1/types.go +++ b/pkg/apis/pingcap/v1alpha1/types.go @@ -859,6 +859,18 @@ type TiCDCSpec struct { // +optional TLSClientSecretNames []string `json:"tlsClientSecretNames,omitempty"` + // ClusterTLSSecretName is used for overwriting the default mTLS cert secret name (see also: pkg/util/util.go:ClusterTLSSecretName) + // This field is useful for sharing the same mTLS cert secret for multiple ticdc clusters connecting to the same upstream tidb cluster. + // +optional + ClusterTLSSecretName string `json:"clusterTLSSecretName,omitempty"` + + // ClusterTLSSecretName is used for overwriting the default **cluster client** cert secret name (see also: pkg/util/util.go:ClusterClientTLSSecretName) + // This field is useful for sharing the same cluster client cert secret for multiple ticdc clusters connecting to the same upstream tidb cluster. + // The ClusterClientTLSSecret is actually not directly used by ticdc, but it is useful for executing some commands via `ticdc-ctl` + // by `kubectl exec -it ticdc-0 -- /cdc cli --ca /var/lib/cluster-client-tls/ca.crt --cert /var/lib/cluster-client-tls/tls.crt --key /var/lib/cluster-client-tls/tls.key ...`. + // +optional + ClusterClientTLSSecretName string `json:"clusterClientTLSSecretName,omitempty"` + // Base image of the component, image tag is now allowed during validation // +kubebuilder:default=pingcap/ticdc // +optional diff --git a/pkg/manager/member/ticdc_member_manager.go b/pkg/manager/member/ticdc_member_manager.go index 3c29259fed..5d66cf5d84 100644 --- a/pkg/manager/member/ticdc_member_manager.go +++ b/pkg/manager/member/ticdc_member_manager.go @@ -405,13 +405,13 @@ func getNewTiCDCStatefulSet(tc *v1alpha1.TidbCluster, cm *corev1.ConfigMap) (*ap vols = append(vols, corev1.Volume{ Name: ticdcCertVolumeMount, VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ - SecretName: util.ClusterTLSSecretName(tc.Name, label.TiCDCLabelVal), + SecretName: getTiCDCClusterTLSCertSecretName(tc), }, }, }, corev1.Volume{ Name: util.ClusterClientVolName, VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ - SecretName: util.ClusterClientTLSSecretName(tc.Name), + SecretName: getTiCDCClusterClientTLSCertSecretName(tc), }, }, }) @@ -566,6 +566,24 @@ func getNewTiCDCStatefulSet(tc *v1alpha1.TidbCluster, cm *corev1.ConfigMap) (*ap return ticdcSts, nil } +func getTiCDCClusterTLSCertSecretName(tc *v1alpha1.TidbCluster) string { + clusterTLSSecretName := util.ClusterTLSSecretName(tc.Name, label.TiCDCLabelVal) + if tc.Spec.TiCDC.ClusterTLSSecretName != "" { + clusterTLSSecretName = tc.Spec.TiCDC.ClusterTLSSecretName + } + + return clusterTLSSecretName +} + +func getTiCDCClusterClientTLSCertSecretName(tc *v1alpha1.TidbCluster) string { + clusterClientTLSSecretName := util.ClusterClientTLSSecretName(tc.Name) + if tc.Spec.TiCDC.ClusterClientTLSSecretName != "" { + clusterClientTLSSecretName = tc.Spec.TiCDC.ClusterClientTLSSecretName + } + + return clusterClientTLSSecretName +} + func labelTiCDC(tc *v1alpha1.TidbCluster) label.Label { instanceName := tc.GetInstanceName() return label.New().Instance(instanceName).TiCDC() From ced6412e66ef2757696990e39a8599bb51368526 Mon Sep 17 00:00:00 2001 From: ideascf Date: Wed, 30 Oct 2024 14:59:41 +0800 Subject: [PATCH 14/49] =?UTF-8?q?Revert=20"opt(ticdc):=20support=20to=20ov?= =?UTF-8?q?erwrite=20the=20default=20cluster=20TLS=20cert=20secret=20?= =?UTF-8?q?=E2=80=A6"=20(#5823)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 8cb0848c353918d92fd3df66a46a9647bc17ba4f. --- docs/api-references/docs.md | 28 ------------------- .../crd/v1/pingcap.com_tidbclusters.yaml | 4 --- .../pingcap/v1alpha1/openapi_generated.go | 14 ---------- pkg/apis/pingcap/v1alpha1/types.go | 12 -------- pkg/manager/member/ticdc_member_manager.go | 22 ++------------- 5 files changed, 2 insertions(+), 78 deletions(-) diff --git a/docs/api-references/docs.md b/docs/api-references/docs.md index e80e88a5e7..c83464c988 100644 --- a/docs/api-references/docs.md +++ b/docs/api-references/docs.md @@ -17354,34 +17354,6 @@ client certificates for the downstream.

-clusterTLSSecretName
- -string - -
-(Optional) -

ClusterTLSSecretName is used for overwriting the default mTLS cert secret name (see also: pkg/util/util.go:ClusterTLSSecretName) -This field is useful for sharing the same mTLS cert secret for multiple ticdc clusters connecting to the same upstream tidb cluster.

-
-clusterClientTLSSecretName
- -string - -
-(Optional) -

ClusterTLSSecretName is used for overwriting the default cluster client cert secret name (see also: pkg/util/util.go:ClusterClientTLSSecretName) -This field is useful for sharing the same cluster client cert secret for multiple ticdc clusters connecting to the same upstream tidb cluster. -The ClusterClientTLSSecret is actually not directly used by ticdc, but it is useful for executing some commands via ticdc-ctl -by kubectl exec -it ticdc-0 -- /cdc cli --ca /var/lib/cluster-client-tls/ca.crt --cert /var/lib/cluster-client-tls/tls.crt --key /var/lib/cluster-client-tls/tls.key ....

-
baseImage
string diff --git a/manifests/crd/v1/pingcap.com_tidbclusters.yaml b/manifests/crd/v1/pingcap.com_tidbclusters.yaml index b658c82317..bd231fd88b 100644 --- a/manifests/crd/v1/pingcap.com_tidbclusters.yaml +++ b/manifests/crd/v1/pingcap.com_tidbclusters.yaml @@ -13152,10 +13152,6 @@ spec: x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map - clusterClientTLSSecretName: - type: string - clusterTLSSecretName: - type: string config: x-kubernetes-preserve-unknown-fields: true configUpdateStrategy: diff --git a/pkg/apis/pingcap/v1alpha1/openapi_generated.go b/pkg/apis/pingcap/v1alpha1/openapi_generated.go index 22ceb9820c..bb6adb867a 100644 --- a/pkg/apis/pingcap/v1alpha1/openapi_generated.go +++ b/pkg/apis/pingcap/v1alpha1/openapi_generated.go @@ -9723,20 +9723,6 @@ func schema_pkg_apis_pingcap_v1alpha1_TiCDCSpec(ref common.ReferenceCallback) co }, }, }, - "clusterTLSSecretName": { - SchemaProps: spec.SchemaProps{ - Description: "ClusterTLSSecretName is used for overwriting the default mTLS cert secret name (see also: pkg/util/util.go:ClusterTLSSecretName) This field is useful for sharing the same mTLS cert secret for multiple ticdc clusters connecting to the same upstream tidb cluster.", - Type: []string{"string"}, - Format: "", - }, - }, - "clusterClientTLSSecretName": { - SchemaProps: spec.SchemaProps{ - Description: "ClusterTLSSecretName is used for overwriting the default **cluster client** cert secret name (see also: pkg/util/util.go:ClusterClientTLSSecretName) This field is useful for sharing the same cluster client cert secret for multiple ticdc clusters connecting to the same upstream tidb cluster. The ClusterClientTLSSecret is actually not directly used by ticdc, but it is useful for executing some commands via `ticdc-ctl`\n by `kubectl exec -it ticdc-0 -- /cdc cli --ca /var/lib/cluster-client-tls/ca.crt --cert /var/lib/cluster-client-tls/tls.crt --key /var/lib/cluster-client-tls/tls.key ...`.", - Type: []string{"string"}, - Format: "", - }, - }, "baseImage": { SchemaProps: spec.SchemaProps{ Description: "Base image of the component, image tag is now allowed during validation", diff --git a/pkg/apis/pingcap/v1alpha1/types.go b/pkg/apis/pingcap/v1alpha1/types.go index f2944a08ea..c5f536be1d 100644 --- a/pkg/apis/pingcap/v1alpha1/types.go +++ b/pkg/apis/pingcap/v1alpha1/types.go @@ -859,18 +859,6 @@ type TiCDCSpec struct { // +optional TLSClientSecretNames []string `json:"tlsClientSecretNames,omitempty"` - // ClusterTLSSecretName is used for overwriting the default mTLS cert secret name (see also: pkg/util/util.go:ClusterTLSSecretName) - // This field is useful for sharing the same mTLS cert secret for multiple ticdc clusters connecting to the same upstream tidb cluster. - // +optional - ClusterTLSSecretName string `json:"clusterTLSSecretName,omitempty"` - - // ClusterTLSSecretName is used for overwriting the default **cluster client** cert secret name (see also: pkg/util/util.go:ClusterClientTLSSecretName) - // This field is useful for sharing the same cluster client cert secret for multiple ticdc clusters connecting to the same upstream tidb cluster. - // The ClusterClientTLSSecret is actually not directly used by ticdc, but it is useful for executing some commands via `ticdc-ctl` - // by `kubectl exec -it ticdc-0 -- /cdc cli --ca /var/lib/cluster-client-tls/ca.crt --cert /var/lib/cluster-client-tls/tls.crt --key /var/lib/cluster-client-tls/tls.key ...`. - // +optional - ClusterClientTLSSecretName string `json:"clusterClientTLSSecretName,omitempty"` - // Base image of the component, image tag is now allowed during validation // +kubebuilder:default=pingcap/ticdc // +optional diff --git a/pkg/manager/member/ticdc_member_manager.go b/pkg/manager/member/ticdc_member_manager.go index 5d66cf5d84..3c29259fed 100644 --- a/pkg/manager/member/ticdc_member_manager.go +++ b/pkg/manager/member/ticdc_member_manager.go @@ -405,13 +405,13 @@ func getNewTiCDCStatefulSet(tc *v1alpha1.TidbCluster, cm *corev1.ConfigMap) (*ap vols = append(vols, corev1.Volume{ Name: ticdcCertVolumeMount, VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ - SecretName: getTiCDCClusterTLSCertSecretName(tc), + SecretName: util.ClusterTLSSecretName(tc.Name, label.TiCDCLabelVal), }, }, }, corev1.Volume{ Name: util.ClusterClientVolName, VolumeSource: corev1.VolumeSource{ Secret: &corev1.SecretVolumeSource{ - SecretName: getTiCDCClusterClientTLSCertSecretName(tc), + SecretName: util.ClusterClientTLSSecretName(tc.Name), }, }, }) @@ -566,24 +566,6 @@ func getNewTiCDCStatefulSet(tc *v1alpha1.TidbCluster, cm *corev1.ConfigMap) (*ap return ticdcSts, nil } -func getTiCDCClusterTLSCertSecretName(tc *v1alpha1.TidbCluster) string { - clusterTLSSecretName := util.ClusterTLSSecretName(tc.Name, label.TiCDCLabelVal) - if tc.Spec.TiCDC.ClusterTLSSecretName != "" { - clusterTLSSecretName = tc.Spec.TiCDC.ClusterTLSSecretName - } - - return clusterTLSSecretName -} - -func getTiCDCClusterClientTLSCertSecretName(tc *v1alpha1.TidbCluster) string { - clusterClientTLSSecretName := util.ClusterClientTLSSecretName(tc.Name) - if tc.Spec.TiCDC.ClusterClientTLSSecretName != "" { - clusterClientTLSSecretName = tc.Spec.TiCDC.ClusterClientTLSSecretName - } - - return clusterClientTLSSecretName -} - func labelTiCDC(tc *v1alpha1.TidbCluster) label.Label { instanceName := tc.GetInstanceName() return label.New().Instance(instanceName).TiCDC() From b6faebdacf6c3ba32e74cf4423ec89f0415125c1 Mon Sep 17 00:00:00 2001 From: hillium Date: Tue, 29 Oct 2024 16:44:30 +0800 Subject: [PATCH 15/49] add compact command to backup manager Signed-off-by: hillium --- cmd/backup-manager/app/cmd/cmd.go | 1 + cmd/backup-manager/app/cmd/compact.go | 47 ++++ cmd/backup-manager/app/compact/kubelink.go | 147 ++++++++++ .../app/compact/options/options.go | 79 ++++++ cmd/backup-manager/app/compact/run.go | 254 ++++++++++++++++++ cmd/backup-manager/app/compact/run_test.go | 175 ++++++++++++ cmd/backup-manager/app/util/util.go | 4 +- images/tidb-backup-manager/entrypoint.sh | 7 +- 8 files changed, 712 insertions(+), 2 deletions(-) create mode 100644 cmd/backup-manager/app/cmd/compact.go create mode 100644 cmd/backup-manager/app/compact/kubelink.go create mode 100644 cmd/backup-manager/app/compact/options/options.go create mode 100644 cmd/backup-manager/app/compact/run.go create mode 100644 cmd/backup-manager/app/compact/run_test.go diff --git a/cmd/backup-manager/app/cmd/cmd.go b/cmd/backup-manager/app/cmd/cmd.go index c2a1b7a457..24c20e10d1 100644 --- a/cmd/backup-manager/app/cmd/cmd.go +++ b/cmd/backup-manager/app/cmd/cmd.go @@ -34,6 +34,7 @@ func NewBackupMgrCommand() *cobra.Command { cmds.AddCommand(NewRestoreCommand()) cmds.AddCommand(NewImportCommand()) cmds.AddCommand(NewCleanCommand()) + cmds.AddCommand(NewCompactCommand()) return cmds } diff --git a/cmd/backup-manager/app/cmd/compact.go b/cmd/backup-manager/app/cmd/compact.go new file mode 100644 index 0000000000..774335e440 --- /dev/null +++ b/cmd/backup-manager/app/cmd/compact.go @@ -0,0 +1,47 @@ +// Copyright 2024 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "context" + + "github.com/pingcap/tidb-operator/cmd/backup-manager/app/compact" + coptions "github.com/pingcap/tidb-operator/cmd/backup-manager/app/compact/options" + "github.com/spf13/cobra" +) + +func NewCompactCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "compact", + Short: "Compact log backup.", + RunE: func(cmd *cobra.Command, args []string) error { + opts := coptions.KubeOpts{} + if err := opts.ParseFromFlags(cmd.Flags()); err != nil { + return err + } + opts.Kubeconfig = kubecfg + + ctx := context.Background() + link, err := compact.NewKubelink(opts.Kubeconfig) + if err != nil { + return err + } + cx := compact.New(opts, link) + return cx.Run(ctx) + }, + } + + coptions.DefineFlags(cmd.Flags()) + return cmd +} diff --git a/cmd/backup-manager/app/compact/kubelink.go b/cmd/backup-manager/app/compact/kubelink.go new file mode 100644 index 0000000000..e2efbfd0a4 --- /dev/null +++ b/cmd/backup-manager/app/compact/kubelink.go @@ -0,0 +1,147 @@ +package compact + +import ( + "context" + "fmt" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb-operator/cmd/backup-manager/app/compact/options" + "github.com/pingcap/tidb-operator/cmd/backup-manager/app/util" + "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1" + "github.com/pingcap/tidb-operator/pkg/apis/util/config" + pkgutil "github.com/pingcap/tidb-operator/pkg/backup/util" + "github.com/pingcap/tidb-operator/pkg/client/clientset/versioned" + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/record" + "k8s.io/klog/v2" +) + +type Kubelink struct { + kube kubernetes.Interface + cr versioned.Interface + + ref *CompactionRef + recorder record.EventRecorder +} + +func NewKubelink(kubeconfig string) (*Kubelink, error) { + kube, cr, err := util.NewKubeAndCRCli(kubeconfig) + if err != nil { + return nil, err + } + return &Kubelink{ + kube: kube, + cr: cr, + }, nil +} + +func (k *Kubelink) GetCompaction(ctx context.Context, opts CompactionRef) (options.CompactOpts, error) { + if k.ref != nil { + return options.CompactOpts{}, errors.New("GetCompaction called twice") + } + k.ref = &opts + k.recorder = util.NewEventRecorder(k.kube, "compact.Kubelink") + + cb, err := k.cr.PingcapV1alpha1(). + CompactBackups(opts.Namespace). + Get(ctx, opts.Name, v1.GetOptions{}) + if err != nil { + return options.CompactOpts{}, err + } + + out := options.CompactOpts{} + args, err := pkgutil.GenStorageArgsForFlag(cb.Spec.StorageProvider, "") + if err != nil { + return options.CompactOpts{}, err + } + out.StorageOpts = args + + startTs, err := config.ParseTSString(cb.Spec.StartTs) + if err != nil { + return options.CompactOpts{}, errors.Annotatef(err, "failed to parse startTs %s", cb.Spec.StartTs) + } + endTs, err := config.ParseTSString(cb.Spec.EndTs) + if err != nil { + return options.CompactOpts{}, errors.Annotatef(err, "failed to parse endTs %s", cb.Spec.EndTs) + } + out.FromTS = startTs + out.UntilTS = endTs + + out.Name = cb.ObjectMeta.Name + out.Concurrency = uint64(cb.Spec.Concurrency) + + if err := out.Verify(); err != nil { + return options.CompactOpts{}, err + } + + return out, nil +} + +type cOP func(*v1alpha1.CompactBackup) error + +func (k *Kubelink) setState(newState string) cOP { + return func(cb *v1alpha1.CompactBackup) error { + cb.Status.State = newState + return nil + } +} + +func (k *Kubelink) event(ty, reason, msg string) cOP { + return func(cb *v1alpha1.CompactBackup) error { + k.recorder.Event(cb, ty, reason, msg) + return nil + } +} + +func (k *Kubelink) edit(ctx context.Context, extraOps ...cOP) error { + lister := k.cr.PingcapV1alpha1(). + CompactBackups(k.ref.Namespace) + cb, err := lister. + Get(ctx, k.ref.Name, v1.GetOptions{}) + if err != nil { + return err + } + + for _, op := range extraOps { + if err := op(cb); err != nil { + return err + } + } + + _, err = lister.Update(ctx, cb, v1.UpdateOptions{}) + if err != nil { + return err + } + return nil +} + +func (k *Kubelink) editOrWarn(ctx context.Context, extraOps ...cOP) { + if err := k.edit(ctx, extraOps...); err != nil { + klog.Warningf( + "failed to edit state for %s/%s: %v", + k.ref.Namespace, + k.ref.Name, + err, + ) + } +} + +func (k *Kubelink) OnStart(ctx context.Context) { + k.editOrWarn(ctx, k.setState("STARTED"), k.event(corev1.EventTypeNormal, "Started", "CompactionStarted")) +} + +func (k *Kubelink) OnProgress(ctx context.Context, p Progress) { + message := fmt.Sprintf("RUNNING[READ_META(%d/%d),COMPACT_WORK(%d/%d)]", + p.MetaCompleted, p.MetaTotal, p.BytesCompacted, p.BytesToCompact) + k.editOrWarn(ctx, k.setState(message)) +} + +func (k *Kubelink) OnFinish(ctx context.Context, err error) { + if err != nil { + k.editOrWarn(ctx, k.setState(fmt.Sprintf("ERR[%s]", err)), k.event(corev1.EventTypeWarning, "Failed", err.Error())) + } else { + k.editOrWarn(ctx, k.setState("DONE"), k.event(corev1.EventTypeNormal, "Succeeded", "CompactionDone")) + } +} diff --git a/cmd/backup-manager/app/compact/options/options.go b/cmd/backup-manager/app/compact/options/options.go new file mode 100644 index 0000000000..280a9ea122 --- /dev/null +++ b/cmd/backup-manager/app/compact/options/options.go @@ -0,0 +1,79 @@ +package options + +import ( + "math" + + "github.com/pingcap/errors" + + "github.com/spf13/pflag" +) + +const ( + fromTSUnset = math.MaxUint64 + untilTSUnset = 0 + + namespaceFlag = "namespace" + resourceNameFlag = "resourceName" + tiKVVersionFlag = "tikvVersion" + storageStringFlag = "storage-string" + fromTSFlag = "from-ts" + untilTSFlag = "until-ts" + nameFlag = "name" + concurrencyFlag = "concurrency" +) + +type KubeOpts struct { + // This should be fill by the caller. + Kubeconfig string `json:"-"` + Namespace string `json:"namespace"` + ResourceName string `json:"resourceName"` + TiKVVersion string `json:"tikvVersion"` +} + +type CompactOpts struct { + FromTS uint64 + UntilTS uint64 + Name string + Concurrency uint64 + StorageOpts []string +} + +func DefineFlags(fs *pflag.FlagSet) { + fs.String(tiKVVersionFlag, "", "TiKV version of the resource") + fs.String(namespaceFlag, "", "Namespace of the resource") + fs.String(resourceNameFlag, "", "Name of the resource") +} + +func (k *KubeOpts) ParseFromFlags(fs *pflag.FlagSet) error { + var err error + k.Namespace, err = fs.GetString(namespaceFlag) + if err != nil { + return errors.Trace(err) + } + k.ResourceName, err = fs.GetString(resourceNameFlag) + if err != nil { + return errors.Trace(err) + } + k.TiKVVersion, err = fs.GetString(tiKVVersionFlag) + if err != nil { + return errors.Trace(err) + } + + return nil +} + +func (c *CompactOpts) Verify() error { + if c.UntilTS < c.FromTS { + if c.UntilTS == untilTSUnset { + return errors.New("until-ts must be set") + } + if c.FromTS == fromTSUnset { + return errors.New("from-ts must be set") + } + return errors.Errorf("until-ts %d must be greater than from-ts %d", c.UntilTS, c.FromTS) + } + if c.Concurrency <= 0 { + return errors.Errorf("concurrency %d must be greater than 0", c.Concurrency) + } + return nil +} diff --git a/cmd/backup-manager/app/compact/run.go b/cmd/backup-manager/app/compact/run.go new file mode 100644 index 0000000000..8693ae5f9f --- /dev/null +++ b/cmd/backup-manager/app/compact/run.go @@ -0,0 +1,254 @@ +package compact + +import ( + "bytes" + "context" + "encoding/json" + "io" + "os" + "os/exec" + "path/filepath" + "strconv" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb-operator/cmd/backup-manager/app/compact/options" + "github.com/pingcap/tidb-operator/pkg/util" + "k8s.io/klog/v2" +) + +// CompactCtx is the context for *a* compaction. +type CompactCtx struct { + kopts options.KubeOpts + opts options.CompactOpts + + // Delegate defines some hooks that allow you to listen over events and + // controls what compactions should be done. + Delegate Delegate + + /* Below fields are used for testing. */ + + // OverrideBase64ify overrides the shell command used for generate + // the storage base64. + OverrideBase64ify func(ctx context.Context, args []string) *exec.Cmd + // OverrideCompact overrides the shell command used for compacting. + OverrideCompact func(ctx context.Context, args []string) *exec.Cmd +} + +type Progress struct { + MetaCompleted uint64 `json:"meta_completed"` + MetaTotal uint64 `json:"meta_total"` + BytesToCompact uint64 `json:"bytes_to_compact"` + BytesCompacted uint64 `json:"bytes_compacted"` +} + +type CompactionRef struct { + Namespace string + Name string +} + +// Delegate describes the "environment" that a compaction runs. +type Delegate interface { + // GetCompaction will be called at the very beginning. + // This should return a set of parameter of a compaction, then a compaction + // defined by these parameters will be executed. + // + // The argument `ref` refs to a `CompactBackup` resource in the cluster, + // and the compaction should be generated according to it. + GetCompaction(ctx context.Context, ref CompactionRef) (options.CompactOpts, error) + + // OnStart will be called when the `tikv-ctl compact-log-backup` process is about to be spawned. + OnStart(ctx context.Context) + // OnPrgress will be called when the progress of compaction updated. + OnProgress(ctx context.Context, p Progress) + // OnFinish will be called when the `tikv-ctl` process exits. + OnFinish(ctx context.Context, err error) +} + +// BaseDelegate is an empty delegate. +// +// You shouldn't directly use this delegate, instead you may embed it into +// your delegate implementation and override the methods you need. +type BaseDelegate struct{} + +func (BaseDelegate) GetCompaction(ctx context.Context, ref CompactionRef) (options.CompactOpts, error) { + return options.CompactOpts{}, errors.New("override `GetCompaction` to provide a meanful execution") +} +func (BaseDelegate) OnStart(ctx context.Context) {} +func (BaseDelegate) OnProgress(ctx context.Context, p Progress) {} +func (BaseDelegate) OnFinish(ctx context.Context, err error) {} + +func (r *CompactCtx) brBin() string { + return filepath.Join(util.BRBinPath, "br") +} + +func (r *CompactCtx) kvCtlBin() string { + return filepath.Join(util.KVCTLBinPath, "tikv-ctl") +} + +func (r *CompactCtx) base64ifyCmd(ctx context.Context, extraArgs []string) *exec.Cmd { + br := r.brBin() + args := []string{ + "operator", + "base64ify", + } + args = append(args, extraArgs...) + + if r.OverrideBase64ify != nil { + return r.OverrideBase64ify(ctx, args) + } + return exec.CommandContext(ctx, br, args...) +} + +func (r *CompactCtx) base64ifyStorage(ctx context.Context) (string, error) { + brCmd := r.base64ifyCmd(ctx, r.opts.StorageOpts) + out, err := brCmd.Output() + if err != nil { + eerr := err.(*exec.ExitError) + klog.Warningf("Failed to execute base64ify; stderr = %s", string(eerr.Stderr)) + return "", errors.Annotatef(err, "failed to execute BR with args %v", brCmd.Args) + } + out = bytes.Trim(out, "\r\n \t") + return string(out), nil +} + +func (r *CompactCtx) compactCmd(ctx context.Context, base64Storage string) *exec.Cmd { + ctl := r.kvCtlBin() + args := []string{ + "--log-level", + "INFO", + "--log-format", + "json", + "compact-log-backup", + "--storage-base64", + base64Storage, + "--from", + strconv.FormatUint(r.opts.FromTS, 10), + "--until", + strconv.FormatUint(r.opts.UntilTS, 10), + "-N", + strconv.FormatUint(r.opts.Concurrency, 10), + } + + if r.OverrideCompact != nil { + return r.OverrideCompact(ctx, args) + } + return exec.CommandContext(ctx, ctl, args...) +} + +func (r *CompactCtx) runCompaction(ctx context.Context, base64Storage string) (err error) { + cmd := r.compactCmd(ctx, base64Storage) + defer func() { r.Delegate.OnFinish(ctx, err) }() + + logs, err := cmd.StderrPipe() + if err != nil { + return errors.Annotate(err, "failed to create stderr pipe for compact") + } + if err := cmd.Start(); err != nil { + return errors.Annotate(err, "failed to start compact") + } + + r.Delegate.OnStart(ctx) + err = r.processCompactionLogs(ctx, io.TeeReader(logs, os.Stdout)) + if err != nil { + return err + } + + return cmd.Wait() +} + +// logLine is line of JSON log. +// It just extracted the message from the JSON and keeps the origin json bytes. +// So you may extract fields from it by `json.Unmarshal(l.Raw, ...)`. +type logLine struct { + Message string + + // Raw is the original log JSON. + Raw []byte +} + +var _ json.Unmarshaler = &logLine{} + +func (l *logLine) UnmarshalJSON(bytes []byte) error { + item := struct { + Message string `json:"message"` + }{} + if err := json.Unmarshal(bytes, &item); err != nil { + return err + } + l.Message = item.Message + l.Raw = bytes + + return nil +} + +func (r *CompactCtx) processLogLine(ctx context.Context, l logLine) error { + const ( + messageCompactionDone = "Finishing compaction." + messageCompactAborted = "Compaction aborted." + ) + + switch l.Message { + case messageCompactionDone: + var prog Progress + if err := json.Unmarshal(l.Raw, &prog); err != nil { + return errors.Annotate(err, "failed to decode progress") + } + r.Delegate.OnProgress(ctx, prog) + return nil + case messageCompactAborted: + errContainer := struct { + Err string `json:"err"` + }{} + if err := json.Unmarshal(l.Raw, &errContainer); err != nil { + return errors.Annotate(err, "failed to decode error message") + } + return errors.Errorf("compaction aborted: %s", errContainer.Err) + default: + return nil + } +} + +func (r *CompactCtx) processCompactionLogs(ctx context.Context, logStream io.Reader) error { + dec := json.NewDecoder(logStream) + + var line logLine + for dec.More() { + if ctx.Err() != nil { + return ctx.Err() + } + if err := dec.Decode(&line); err != nil { + return errors.Annotate(err, "failed to decode the line of log") + } + if err := r.processLogLine(ctx, line); err != nil { + return errors.Annotate(err, "error during processing log line") + } + } + + return nil +} + +func (r *CompactCtx) Run(ctx context.Context) error { + ref := CompactionRef{ + Namespace: r.kopts.Namespace, + Name: r.kopts.ResourceName, + } + opts, err := r.Delegate.GetCompaction(ctx, ref) + if err != nil { + return errors.Annotate(err, "failed to get storage string") + } + r.opts = opts + + b64, err := r.base64ifyStorage(ctx) + if err != nil { + return errors.Annotate(err, "failed to base64ify storage") + } + return r.runCompaction(ctx, b64) +} + +// New creates a new compaction context. +func New(opts options.KubeOpts, dele Delegate) *CompactCtx { + return &CompactCtx{ + kopts: opts, + Delegate: dele, + } +} diff --git a/cmd/backup-manager/app/compact/run_test.go b/cmd/backup-manager/app/compact/run_test.go new file mode 100644 index 0000000000..99d0d63739 --- /dev/null +++ b/cmd/backup-manager/app/compact/run_test.go @@ -0,0 +1,175 @@ +package compact_test + +import ( + "bytes" + "context" + "encoding/json" + "io" + "os/exec" + "testing" + "testing/iotest" + + "github.com/pingcap/tidb-operator/cmd/backup-manager/app/compact" + "github.com/pingcap/tidb-operator/cmd/backup-manager/app/compact/options" + "github.com/stretchr/testify/require" +) + +type callRecorder []call + +type call struct { + cmd string + attrs map[string]any +} + +func (c *callRecorder) GetCompaction(ctx context.Context, ref compact.CompactionRef) (options.CompactOpts, error) { + *c = append(*c, call{cmd: "GetCompaction", attrs: map[string]any{ + "ref": ref, + }}) + return options.CompactOpts{}, nil +} + +func (c *callRecorder) OnStart(ctx context.Context) { + *c = append(*c, call{cmd: "OnStart"}) +} + +func (c *callRecorder) OnProgress(ctx context.Context, p compact.Progress) { + *c = append(*c, call{cmd: "OnProgress", attrs: map[string]any{ + "MetaCompleted": p.MetaCompleted, + "MetaTotal": p.MetaTotal, + "BytesToCompact": p.BytesToCompact, + "BytesCompacted": p.BytesCompacted, + }}) +} + +func (c *callRecorder) OnFinish(ctx context.Context, err error) { + *c = append(*c, call{cmd: "OnFinish", attrs: map[string]any{ + "error": err, + }}) +} + +type dummyCommands struct { + theBase64 string + t *testing.T +} + +func (d dummyCommands) OverrideBase64ify(context.Context, []string) *exec.Cmd { + cmd := exec.Command("cat", "-") + cmd.Stdin = bytes.NewBufferString(d.theBase64) + return cmd +} + +func (d dummyCommands) OverrideCompact(_ context.Context, args []string) *exec.Cmd { + storage := "" + for i, arg := range args { + if arg == "--storage-base64" && i+1 < len(args) { + storage = args[i+1] + break + } + } + + require.Equal(d.t, storage, d.theBase64, "%#v", args) + return exec.Command("true") +} + +type empty struct { + compact.BaseDelegate +} + +func (e empty) GetCompaction(context.Context, compact.CompactionRef) (options.CompactOpts, error) { + return options.CompactOpts{}, nil +} + +func TestNormal(t *testing.T) { + cx := compact.New(options.KubeOpts{}, empty{}) + dc := dummyCommands{ + theBase64: "was yae", + t: t, + } + cx.OverrideBase64ify = dc.OverrideBase64ify + cx.OverrideCompact = dc.OverrideCompact + + require.NoError(t, cx.Run(context.Background())) +} + +type dummyProgress struct { + progs []compact.Progress + t *testing.T +} + +func (d dummyProgress) OverrideBase64ify(context.Context, []string) *exec.Cmd { + return exec.Command("true") +} + +func toMap(t *testing.T, v any) (res map[string]any) { + data, err := json.Marshal(v) + require.NoError(t, err) + require.NoError(t, json.Unmarshal(data, &res)) + return +} + +func (d dummyProgress) OverrideCompact(_ context.Context, args []string) *exec.Cmd { + cmd := exec.Command("sh", "-c", "cat - >&2") + rx, tx := io.Pipe() + + enc := json.NewEncoder(tx) + go func() { + for _, prog := range d.progs { + m := toMap(d.t, prog) + m["message"] = "Finishing compaction." + require.NoError(d.t, enc.Encode(m)) + } + tx.Close() + }() + + // Simulating streaming progress. + cmd.Stdin = iotest.OneByteReader(rx) + return cmd +} + +func TestWithProgress(t *testing.T) { + rec := callRecorder{} + cx := compact.New(options.KubeOpts{Namespace: "foo", ResourceName: "bar"}, &rec) + dp := dummyProgress{ + progs: []compact.Progress{ + {MetaCompleted: 1, MetaTotal: 2, BytesToCompact: 3, BytesCompacted: 4}, + {MetaCompleted: 2, MetaTotal: 2, BytesToCompact: 7, BytesCompacted: 8}, + {MetaCompleted: 2, MetaTotal: 2, BytesToCompact: 8, BytesCompacted: 8}, + }, + t: t, + } + cx.OverrideBase64ify = dp.OverrideBase64ify + cx.OverrideCompact = dp.OverrideCompact + + require.NoError(t, cx.Run(context.Background())) + + require.Equal(t, []call(rec), []call{ + {cmd: "GetCompaction", attrs: map[string]any{ + "ref": compact.CompactionRef{ + Namespace: "foo", + Name: "bar", + }, + }}, + {cmd: "OnStart"}, + {cmd: "OnProgress", attrs: map[string]any{ + "MetaCompleted": uint64(1), + "MetaTotal": uint64(2), + "BytesToCompact": uint64(3), + "BytesCompacted": uint64(4), + }}, + {cmd: "OnProgress", attrs: map[string]any{ + "MetaCompleted": uint64(2), + "MetaTotal": uint64(2), + "BytesToCompact": uint64(7), + "BytesCompacted": uint64(8), + }}, + {cmd: "OnProgress", attrs: map[string]any{ + "MetaCompleted": uint64(2), + "MetaTotal": uint64(2), + "BytesToCompact": uint64(8), + "BytesCompacted": uint64(8), + }}, + {cmd: "OnFinish", attrs: map[string]any{ + "error": error(nil), + }}, + }) +} diff --git a/cmd/backup-manager/app/util/util.go b/cmd/backup-manager/app/util/util.go index 877972fb27..2b5b9102ae 100644 --- a/cmd/backup-manager/app/util/util.go +++ b/cmd/backup-manager/app/util/util.go @@ -535,7 +535,9 @@ func ReadAllStdErrToChannel(stdErr io.Reader, errMsgCh chan []byte) { func GracefullyShutDownSubProcess(ctx context.Context, cmd *exec.Cmd) { <-ctx.Done() klog.Errorf("context done, err: %s. start to shut down sub process gracefully", ctx.Err().Error()) - if err := cmd.Process.Signal(syscall.SIGTERM); err != nil { + if cmd.Process == nil { + klog.Infof("sub process not started, won't send SIGTERM") + } else if err := cmd.Process.Signal(syscall.SIGTERM); err != nil { klog.Errorf("send SIGTERM to sub process error: %s", err.Error()) } else { klog.Infof("send SIGTERM to sub process successfully") diff --git a/images/tidb-backup-manager/entrypoint.sh b/images/tidb-backup-manager/entrypoint.sh index dbc8722784..6804abdc28 100755 --- a/images/tidb-backup-manager/entrypoint.sh +++ b/images/tidb-backup-manager/entrypoint.sh @@ -105,6 +105,11 @@ case "$1" in echo "$BACKUP_BIN import $@" $EXEC_COMMAND $BACKUP_BIN import "$@" ;; + compact) + shift 1 + echo "$BACKUP_BIN compact $@" + $EXEC_COMMAND $BACKUP_BIN compact "$@" + ;; clean) shift 1 echo "$BACKUP_BIN clean $@" @@ -113,7 +118,7 @@ case "$1" in $EXEC_COMMAND $BACKUP_BIN clean "$@" ;; *) - echo "Usage: $0 {backup|restore|clean}" + echo "Usage: $0 {backup|restore|import|compact|clean}" echo "Now runs your command." echo "$@" From 8f81357ff1cb17c8a1cb62f9d02c6e0adefbb145 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Thu, 31 Oct 2024 11:39:31 +0100 Subject: [PATCH 16/49] adapt backup-manager params --- docs/api-references/docs.md | 4 +- manifests/crd.yaml | 4 +- .../crd/v1/pingcap.com_compactbackups.yaml | 4 +- .../pingcap/v1alpha1/openapi_generated.go | 2 +- pkg/apis/pingcap/v1alpha1/types.go | 2 +- .../compact_backup_controller.go | 41 +++++-------------- 6 files changed, 18 insertions(+), 39 deletions(-) diff --git a/docs/api-references/docs.md b/docs/api-references/docs.md index c83464c988..57abb8805a 100644 --- a/docs/api-references/docs.md +++ b/docs/api-references/docs.md @@ -5481,7 +5481,7 @@ string
-commitTs
+startTs
string @@ -5847,7 +5847,7 @@ string
-commitTs
+startTs
string diff --git a/manifests/crd.yaml b/manifests/crd.yaml index 336e5e1171..2394657360 100644 --- a/manifests/crd.yaml +++ b/manifests/crd.yaml @@ -8210,8 +8210,6 @@ spec: type: object brImage: type: string - commitTs: - type: string concurrency: type: integer endTs: @@ -9193,6 +9191,8 @@ spec: type: object serviceAccount: type: string + startTs: + type: string storageClassName: type: string storageSize: diff --git a/manifests/crd/v1/pingcap.com_compactbackups.yaml b/manifests/crd/v1/pingcap.com_compactbackups.yaml index c58dbfe4ae..e3b434ae2d 100644 --- a/manifests/crd/v1/pingcap.com_compactbackups.yaml +++ b/manifests/crd/v1/pingcap.com_compactbackups.yaml @@ -1160,8 +1160,6 @@ spec: type: object brImage: type: string - commitTs: - type: string concurrency: type: integer endTs: @@ -2143,6 +2141,8 @@ spec: type: object serviceAccount: type: string + startTs: + type: string storageClassName: type: string storageSize: diff --git a/pkg/apis/pingcap/v1alpha1/openapi_generated.go b/pkg/apis/pingcap/v1alpha1/openapi_generated.go index bb6adb867a..9c2de35e06 100644 --- a/pkg/apis/pingcap/v1alpha1/openapi_generated.go +++ b/pkg/apis/pingcap/v1alpha1/openapi_generated.go @@ -1775,7 +1775,7 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) Format: "", }, }, - "commitTs": { + "startTs": { SchemaProps: spec.SchemaProps{ Description: "StartTs is the start ts of the compact backup. Format supports TSO or datetime, e.g. '400036290571534337', '2018-05-11 01:42:23'.", Type: []string{"string"}, diff --git a/pkg/apis/pingcap/v1alpha1/types.go b/pkg/apis/pingcap/v1alpha1/types.go index c5f536be1d..8f7fa95280 100644 --- a/pkg/apis/pingcap/v1alpha1/types.go +++ b/pkg/apis/pingcap/v1alpha1/types.go @@ -3483,7 +3483,7 @@ type CompactSpec struct { StorageSize string `json:"storageSize,omitempty"` // StartTs is the start ts of the compact backup. // Format supports TSO or datetime, e.g. '400036290571534337', '2018-05-11 01:42:23'. - StartTs string `json:"commitTs,omitempty"` + StartTs string `json:"startTs,omitempty"` // EndTs is the end ts of the compact backup. // Format supports TSO or datetime, e.g. '400036290571534337', '2018-05-11 01:42:23'. // Default is current timestamp. diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index 10710f539b..e7ee80e2f3 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -3,7 +3,6 @@ package compact import ( "context" "fmt" - "strconv" "strings" "time" @@ -11,7 +10,6 @@ import ( perrors "github.com/pingcap/errors" "github.com/pingcap/tidb-operator/pkg/apis/label" "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1" - "github.com/pingcap/tidb-operator/pkg/apis/util/config" "github.com/pingcap/tidb-operator/pkg/backup/constants" backuputil "github.com/pingcap/tidb-operator/pkg/backup/util" "github.com/pingcap/tidb-operator/pkg/client/clientset/versioned" @@ -302,29 +300,12 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job fmt.Sprintf("--namespace=%s", ns), fmt.Sprintf("--resourceName=%s", name), } - startTS, err := config.ParseTSString(backup.Spec.StartTs) - if err != nil { - return nil, fmt.Sprintf("failed to parse startTs(%v)", backup.Spec.StartTs), err - } - args = append(args, "--from-ts", strconv.FormatUint(startTS, 10)) - endTS, err := config.ParseTSString(backup.Spec.EndTs) - if err != nil { - return nil, fmt.Sprintf("failed to parse endTs(%v)", backup.Spec.EndTs), err - } - args = append(args, "--until-ts", strconv.FormatUint(endTS, 10)) - args = append(args, "--concurrency", backup.Spec.EndTs) - args = append(args, "--name", backup.GetObjectMeta().GetName()) - strg, err := backuputil.GetStoragePath(backup.Spec.StorageProvider) - if err != nil { - return nil, fmt.Sprintf("failed to get storage path: %v", err), err - } - args = append(args, "--storage-string", strg) tikvImage := "pingcap/tikv" if backup.Spec.TiKVImage != "" { tikvImage = backup.Spec.TiKVImage } - + tikvVersion := backup.Spec.Version _, imageVersion := backuputil.ParseImage(tikvImage) if imageVersion != "" { @@ -404,8 +385,8 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job Command: []string{"/bin/sh", "-c"}, Args: []string{fmt.Sprintf("cp /br %s/br; echo 'BR copy finished'", util.BRBinPath)}, ImagePullPolicy: corev1.PullIfNotPresent, - VolumeMounts: volumeMounts, - Resources: backup.Spec.ResourceRequirements, + VolumeMounts: volumeMounts, + Resources: backup.Spec.ResourceRequirements, }, { Name: "tikv-ctl", @@ -413,20 +394,18 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job Command: []string{"/bin/sh", "-c"}, Args: []string{fmt.Sprintf("cp /tikv-ctl %s/tikv-ctl; echo 'tikv-ctl copy finished'", util.KVCTLBinPath)}, ImagePullPolicy: corev1.PullIfNotPresent, - VolumeMounts: volumeMounts, - Resources: backup.Spec.ResourceRequirements, + VolumeMounts: volumeMounts, + Resources: backup.Spec.ResourceRequirements, }, }, Containers: []corev1.Container{ { Name: "backup-manager", - Image: c.deps.CLIConfig.TiDBBackupManagerImage, - Command: []string{ - "/bin/sh", "-c", - }, - Args: args, - Env: envVars, - VolumeMounts: volumeMounts, + Image: c.deps.CLIConfig.TiDBBackupManagerImage, + Args: args, + Env: envVars, + VolumeMounts: volumeMounts, + ImagePullPolicy: corev1.PullAlways, }, }, RestartPolicy: corev1.RestartPolicyNever, From bf9b18519496204f13803a6a5c8d2965b0c758ed Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Wed, 4 Dec 2024 07:31:07 +0100 Subject: [PATCH 17/49] attach to tc --- pkg/apis/pingcap/v1alpha1/types.go | 12 +++----- .../compact_backup_controller.go | 29 ++++++++----------- 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/pkg/apis/pingcap/v1alpha1/types.go b/pkg/apis/pingcap/v1alpha1/types.go index 8f7fa95280..5f1f2f6902 100644 --- a/pkg/apis/pingcap/v1alpha1/types.go +++ b/pkg/apis/pingcap/v1alpha1/types.go @@ -3447,7 +3447,7 @@ type CompactBackup struct { Status CompactStatus `json:"status,omitempty"` } -// BackupSpec contains the backup specification for a tidb cluster. +// CompactSpec contains the backup specification for a tidb cluster. // +k8s:openapi-gen=true type CompactSpec struct { corev1.ResourceRequirements `json:"resources,omitempty"` @@ -3498,17 +3498,13 @@ type CompactSpec struct { // Base tolerations of backup Pods, components may add more tolerations upon this respectively // +optional Tolerations []corev1.Toleration `json:"tolerations,omitempty"` - // Version specifies the tool image version used in compact `Backup`. - Version string `json:"version,omitempty"` // BrImage specifies the br image used in compact `Backup`. // For examples `spec.brImage: pingcap/br:v4.0.8` // For BR image, if it does not contain tag, Pod will use image 'BrImage:${TiKV_Version}'. // +optional - BrImage string `json:"brImage,omitempty"` - // TiKVImage specifies the tikv image used in compact `Backup`. - // For examples `spec.tikvImage: pingcap/tikv:v4.0.8` - // +optional - TiKVImage string `json:"tikvImage,omitempty"` + ToolImage string `json:"brImage,omitempty"` + // BRConfig is the configs for BR + BR *BRConfig `json:"br,omitempty"` // ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images. // +optional ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index e7ee80e2f3..0c6964a2fd 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -301,28 +301,23 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job fmt.Sprintf("--resourceName=%s", name), } - tikvImage := "pingcap/tikv" - if backup.Spec.TiKVImage != "" { - tikvImage = backup.Spec.TiKVImage - } - - tikvVersion := backup.Spec.Version - _, imageVersion := backuputil.ParseImage(tikvImage) - if imageVersion != "" { - tikvVersion = imageVersion + tc, err := c.deps.TiDBClusterLister.TidbClusters(ns).Get(backup.Spec.BR.Cluster) + if err != nil { + return nil, fmt.Sprintf("failed to fetch tidbcluster %s/%s", ns, backup.Spec.BR.Cluster), err } + tikvImage := tc.TiKVImage() + _, tikvVersion := backuputil.ParseImage(tikvImage) if tikvVersion != "" { args = append(args, fmt.Sprintf("--tikvVersion=%s", tikvVersion)) - } else { - return nil, "tikv version is empty", fmt.Errorf("tikv version is empty") } - - brImage := fmt.Sprintf("pingcap/br:%s", tikvVersion) - if backup.Spec.BrImage != "" { - brImage = backup.Spec.BrImage - if !strings.ContainsRune(brImage, ':') { - brImage = fmt.Sprintf("%s:%s", brImage, tikvVersion) + brImage := "pingcap/br:" + tikvVersion + if backup.Spec.ToolImage != "" { + toolImage := backup.Spec.ToolImage + if !strings.ContainsRune(backup.Spec.ToolImage, ':') { + toolImage = fmt.Sprintf("%s:%s", toolImage, tikvVersion) } + + brImage = toolImage } //TODO: (Ris)What is the instance here? From f9aafcbe52e2ef0149192164dc1695f5d7f53827 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Wed, 4 Dec 2024 07:34:33 +0100 Subject: [PATCH 18/49] lint --- docs/api-references/docs.md | 45 +++++-------------- manifests/crd.yaml | 40 +++++++++++++++-- .../crd/v1/pingcap.com_compactbackups.yaml | 40 +++++++++++++++-- .../pingcap/v1alpha1/openapi_generated.go | 18 +++----- .../pingcap/v1alpha1/zz_generated.deepcopy.go | 5 +++ .../compact_backup_controller.go | 4 +- 6 files changed, 96 insertions(+), 56 deletions(-) diff --git a/docs/api-references/docs.md b/docs/api-references/docs.md index 57abb8805a..0755f48c1a 100644 --- a/docs/api-references/docs.md +++ b/docs/api-references/docs.md @@ -3447,6 +3447,7 @@ string

(Appears on: BackupSpec, +CompactSpec, RestoreSpec)

@@ -5544,17 +5545,6 @@ bool

-version
- -string - -
-

Version specifies the tool image version used in compact Backup.

-
brImage
string @@ -5569,15 +5559,15 @@ For BR image, if it does not contain tag, Pod will use image ‘BrImage:${Ti
-tikvImage
+br
-string + +BRConfig +
-(Optional) -

TiKVImage specifies the tikv image used in compact Backup. -For examples spec.tikvImage: pingcap/tikv:v4.0.8

+

BRConfig is the configs for BR

@@ -5910,17 +5900,6 @@ bool - - - - diff --git a/manifests/crd.yaml b/manifests/crd.yaml index 2394657360..23ebbaa0f6 100644 --- a/manifests/crd.yaml +++ b/manifests/crd.yaml @@ -8208,6 +8208,42 @@ spec: default: 30m type: string type: object + br: + properties: + checkRequirements: + type: boolean + checksum: + type: boolean + cluster: + type: string + clusterNamespace: + type: string + concurrency: + format: int32 + type: integer + db: + type: string + logLevel: + type: string + onLine: + type: boolean + options: + items: + type: string + type: array + rateLimit: + type: integer + sendCredToTikv: + type: boolean + statusAddr: + type: string + table: + type: string + timeAgo: + type: string + required: + - cluster + type: object brImage: type: string concurrency: @@ -9199,8 +9235,6 @@ spec: type: string tikvGCLifeTime: type: string - tikvImage: - type: string tolerations: items: properties: @@ -9219,8 +9253,6 @@ spec: type: array useKMS: type: boolean - version: - type: string volumeBackupInitJobMaxActiveSeconds: default: 600 type: integer diff --git a/manifests/crd/v1/pingcap.com_compactbackups.yaml b/manifests/crd/v1/pingcap.com_compactbackups.yaml index e3b434ae2d..19ee6b2a4a 100644 --- a/manifests/crd/v1/pingcap.com_compactbackups.yaml +++ b/manifests/crd/v1/pingcap.com_compactbackups.yaml @@ -1158,6 +1158,42 @@ spec: default: 30m type: string type: object + br: + properties: + checkRequirements: + type: boolean + checksum: + type: boolean + cluster: + type: string + clusterNamespace: + type: string + concurrency: + format: int32 + type: integer + db: + type: string + logLevel: + type: string + onLine: + type: boolean + options: + items: + type: string + type: array + rateLimit: + type: integer + sendCredToTikv: + type: boolean + statusAddr: + type: string + table: + type: string + timeAgo: + type: string + required: + - cluster + type: object brImage: type: string concurrency: @@ -2149,8 +2185,6 @@ spec: type: string tikvGCLifeTime: type: string - tikvImage: - type: string tolerations: items: properties: @@ -2169,8 +2203,6 @@ spec: type: array useKMS: type: boolean - version: - type: string volumeBackupInitJobMaxActiveSeconds: default: 600 type: integer diff --git a/pkg/apis/pingcap/v1alpha1/openapi_generated.go b/pkg/apis/pingcap/v1alpha1/openapi_generated.go index 9c2de35e06..cc705cd3a5 100644 --- a/pkg/apis/pingcap/v1alpha1/openapi_generated.go +++ b/pkg/apis/pingcap/v1alpha1/openapi_generated.go @@ -1706,7 +1706,7 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) return common.OpenAPIDefinition{ Schema: spec.Schema{ SchemaProps: spec.SchemaProps{ - Description: "BackupSpec contains the backup specification for a tidb cluster.", + Description: "CompactSpec contains the backup specification for a tidb cluster.", Type: []string{"object"}, Properties: map[string]spec.Schema{ "resources": { @@ -1817,13 +1817,6 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) }, }, }, - "version": { - SchemaProps: spec.SchemaProps{ - Description: "Version specifies the tool image version used in compact `Backup`.", - Type: []string{"string"}, - Format: "", - }, - }, "brImage": { SchemaProps: spec.SchemaProps{ Description: "BrImage specifies the br image used in compact `Backup`. For examples `spec.brImage: pingcap/br:v4.0.8` For BR image, if it does not contain tag, Pod will use image 'BrImage:${TiKV_Version}'.", @@ -1831,11 +1824,10 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) Format: "", }, }, - "tikvImage": { + "br": { SchemaProps: spec.SchemaProps{ - Description: "TiKVImage specifies the tikv image used in compact `Backup`. For examples `spec.tikvImage: pingcap/tikv:v4.0.8`", - Type: []string{"string"}, - Format: "", + Description: "BRConfig is the configs for BR", + Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BRConfig"), }, }, "imagePullSecrets": { @@ -1931,7 +1923,7 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) }, }, Dependencies: []string{ - "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.AzblobStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BackoffRetryPolicy", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.GcsStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.LocalStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.S3StorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiDBAccessConfig", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume", "k8s.io/api/core/v1.VolumeMount"}, + "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.AzblobStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BRConfig", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BackoffRetryPolicy", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.GcsStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.LocalStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.S3StorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiDBAccessConfig", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume", "k8s.io/api/core/v1.VolumeMount"}, } } diff --git a/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go index 51b722ada7..fbb1d8acf1 100644 --- a/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go @@ -960,6 +960,11 @@ func (in *CompactSpec) DeepCopyInto(out *CompactSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.BR != nil { + in, out := &in.BR, &out.BR + *out = new(BRConfig) + (*in).DeepCopyInto(*out) + } if in.ImagePullSecrets != nil { in, out := &in.ImagePullSecrets, &out.ImagePullSecrets *out = make([]v1.LocalObjectReference, len(*in)) diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index 0c6964a2fd..a78cad4f5a 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -395,8 +395,8 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job }, Containers: []corev1.Container{ { - Name: "backup-manager", - Image: c.deps.CLIConfig.TiDBBackupManagerImage, + Name: "backup-manager", + Image: c.deps.CLIConfig.TiDBBackupManagerImage, Args: args, Env: envVars, VolumeMounts: volumeMounts, From 523281b5d116624f97ac3c744de366785a472ee1 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Wed, 4 Dec 2024 07:50:51 +0100 Subject: [PATCH 19/49] copyright --- cmd/backup-manager/app/compact/kubelink.go | 13 +++++++++++++ cmd/backup-manager/app/compact/options/options.go | 13 +++++++++++++ cmd/backup-manager/app/compact/run.go | 13 +++++++++++++ cmd/backup-manager/app/compact/run_test.go | 13 +++++++++++++ .../compactbackup/compact_backup_controller.go | 13 +++++++++++++ 5 files changed, 65 insertions(+) diff --git a/cmd/backup-manager/app/compact/kubelink.go b/cmd/backup-manager/app/compact/kubelink.go index e2efbfd0a4..4bc356a0c1 100644 --- a/cmd/backup-manager/app/compact/kubelink.go +++ b/cmd/backup-manager/app/compact/kubelink.go @@ -1,3 +1,16 @@ +// Copyright 2024 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + package compact import ( diff --git a/cmd/backup-manager/app/compact/options/options.go b/cmd/backup-manager/app/compact/options/options.go index 280a9ea122..8890e8bcd0 100644 --- a/cmd/backup-manager/app/compact/options/options.go +++ b/cmd/backup-manager/app/compact/options/options.go @@ -1,3 +1,16 @@ +// Copyright 2024 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + package options import ( diff --git a/cmd/backup-manager/app/compact/run.go b/cmd/backup-manager/app/compact/run.go index 8693ae5f9f..68f2e063df 100644 --- a/cmd/backup-manager/app/compact/run.go +++ b/cmd/backup-manager/app/compact/run.go @@ -1,3 +1,16 @@ +// Copyright 2024 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + package compact import ( diff --git a/cmd/backup-manager/app/compact/run_test.go b/cmd/backup-manager/app/compact/run_test.go index 99d0d63739..57b8ab06be 100644 --- a/cmd/backup-manager/app/compact/run_test.go +++ b/cmd/backup-manager/app/compact/run_test.go @@ -1,3 +1,16 @@ +// Copyright 2024 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + package compact_test import ( diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index a78cad4f5a..54a4e000d2 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -1,3 +1,16 @@ +// Copyright 2024 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + package compact import ( From 00e395d9558b226a6cbafa4494be2da7c3d01c20 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Fri, 6 Dec 2024 09:12:49 +0100 Subject: [PATCH 20/49] refactor --- cmd/backup-manager/app/clean/clean_test.go | 5 +- cmd/backup-manager/app/cmd/compact.go | 55 ++-- cmd/backup-manager/app/compact/kubelink.go | 160 ----------- cmd/backup-manager/app/compact/manager.go | 222 +++++++++++++++ .../app/compact/options/options.go | 51 ++-- cmd/backup-manager/app/compact/run.go | 267 ------------------ cmd/backup-manager/app/compact/run_test.go | 188 ------------ .../app/compact/status_updater.go | 96 +++++++ cmd/backup-manager/main.go | 4 +- go.mod | 1 + go.sum | 2 + 11 files changed, 385 insertions(+), 666 deletions(-) delete mode 100644 cmd/backup-manager/app/compact/kubelink.go create mode 100644 cmd/backup-manager/app/compact/manager.go delete mode 100644 cmd/backup-manager/app/compact/run.go delete mode 100644 cmd/backup-manager/app/compact/run_test.go create mode 100644 cmd/backup-manager/app/compact/status_updater.go diff --git a/cmd/backup-manager/app/clean/clean_test.go b/cmd/backup-manager/app/clean/clean_test.go index 7207565d16..c1fb71a1b5 100644 --- a/cmd/backup-manager/app/clean/clean_test.go +++ b/cmd/backup-manager/app/clean/clean_test.go @@ -23,11 +23,10 @@ import ( "github.com/agiledragon/gomonkey/v2" "github.com/aws/aws-sdk-go/service/s3/s3iface" . "github.com/onsi/gomega" - "gocloud.dev/blob" - "gocloud.dev/blob/driver" - "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1" "github.com/pingcap/tidb-operator/pkg/backup/util" + "gocloud.dev/blob" + "gocloud.dev/blob/driver" ) func TestCleanBRRemoteBackupDataOnce(t *testing.T) { diff --git a/cmd/backup-manager/app/cmd/compact.go b/cmd/backup-manager/app/cmd/compact.go index 774335e440..b890456d9b 100644 --- a/cmd/backup-manager/app/cmd/compact.go +++ b/cmd/backup-manager/app/cmd/compact.go @@ -17,31 +17,54 @@ import ( "context" "github.com/pingcap/tidb-operator/cmd/backup-manager/app/compact" - coptions "github.com/pingcap/tidb-operator/cmd/backup-manager/app/compact/options" + "github.com/pingcap/tidb-operator/cmd/backup-manager/app/compact/options" + "github.com/pingcap/tidb-operator/cmd/backup-manager/app/constants" + "github.com/pingcap/tidb-operator/cmd/backup-manager/app/util" + informers "github.com/pingcap/tidb-operator/pkg/client/informers/externalversions" "github.com/spf13/cobra" + "k8s.io/client-go/tools/cache" + cmdutil "k8s.io/kubectl/pkg/cmd/util" ) func NewCompactCommand() *cobra.Command { + opts := options.CompactOpts{} + cmd := &cobra.Command{ Use: "compact", Short: "Compact log backup.", - RunE: func(cmd *cobra.Command, args []string) error { - opts := coptions.KubeOpts{} - if err := opts.ParseFromFlags(cmd.Flags()); err != nil { - return err - } - opts.Kubeconfig = kubecfg - - ctx := context.Background() - link, err := compact.NewKubelink(opts.Kubeconfig) - if err != nil { - return err - } - cx := compact.New(opts, link) - return cx.Run(ctx) + Run: func(cmd *cobra.Command, args []string) { + util.ValidCmdFlags(cmd.CommandPath(), cmd.LocalFlags()) + cmdutil.CheckErr(runCompact(opts, kubecfg)) }, } - coptions.DefineFlags(cmd.Flags()) + cmd.Flags().StringVar(&opts.Namespace, "namespace", "", "Backup CR's namespace") + cmd.Flags().StringVar(&opts.ResourceName, "resourceName", "", "Backup CRD object name") + cmd.Flags().StringVar(&opts.TikvVersion, "tikvVersion", util.DefaultVersion, "TiKV version") return cmd } + +func runCompact(compactOpts options.CompactOpts, kubecfg string) error { + kubeCli, cli, err := util.NewKubeAndCRCli(kubecfg) + if err != nil { + return err + } + options := []informers.SharedInformerOption{ + informers.WithNamespace(compactOpts.Namespace), + } + informerFactory := informers.NewSharedInformerFactoryWithOptions(cli, constants.ResyncDuration, options...) + recorder := util.NewEventRecorder(kubeCli, "compact") + compactInformer := informerFactory.Pingcap().V1alpha1().CompactBackups() + statusUpdater := compact.NewCompactStatusUpdater(recorder, compactInformer.Lister(), cli) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + go informerFactory.Start(ctx.Done()) + + // waiting for the shared informer's store has synced. + cache.WaitForCacheSync(ctx.Done(), compactInformer.Informer().HasSynced) + + // klog.Infof("start to process backup %s", compactOpts.String()) + cm := compact.NewManager(compactInformer.Lister(), statusUpdater, compactOpts) + return cm.ProcessCompact() +} diff --git a/cmd/backup-manager/app/compact/kubelink.go b/cmd/backup-manager/app/compact/kubelink.go deleted file mode 100644 index 4bc356a0c1..0000000000 --- a/cmd/backup-manager/app/compact/kubelink.go +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2024 PingCAP, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// See the License for the specific language governing permissions and -// limitations under the License. - -package compact - -import ( - "context" - "fmt" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb-operator/cmd/backup-manager/app/compact/options" - "github.com/pingcap/tidb-operator/cmd/backup-manager/app/util" - "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1" - "github.com/pingcap/tidb-operator/pkg/apis/util/config" - pkgutil "github.com/pingcap/tidb-operator/pkg/backup/util" - "github.com/pingcap/tidb-operator/pkg/client/clientset/versioned" - corev1 "k8s.io/api/core/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/record" - "k8s.io/klog/v2" -) - -type Kubelink struct { - kube kubernetes.Interface - cr versioned.Interface - - ref *CompactionRef - recorder record.EventRecorder -} - -func NewKubelink(kubeconfig string) (*Kubelink, error) { - kube, cr, err := util.NewKubeAndCRCli(kubeconfig) - if err != nil { - return nil, err - } - return &Kubelink{ - kube: kube, - cr: cr, - }, nil -} - -func (k *Kubelink) GetCompaction(ctx context.Context, opts CompactionRef) (options.CompactOpts, error) { - if k.ref != nil { - return options.CompactOpts{}, errors.New("GetCompaction called twice") - } - k.ref = &opts - k.recorder = util.NewEventRecorder(k.kube, "compact.Kubelink") - - cb, err := k.cr.PingcapV1alpha1(). - CompactBackups(opts.Namespace). - Get(ctx, opts.Name, v1.GetOptions{}) - if err != nil { - return options.CompactOpts{}, err - } - - out := options.CompactOpts{} - args, err := pkgutil.GenStorageArgsForFlag(cb.Spec.StorageProvider, "") - if err != nil { - return options.CompactOpts{}, err - } - out.StorageOpts = args - - startTs, err := config.ParseTSString(cb.Spec.StartTs) - if err != nil { - return options.CompactOpts{}, errors.Annotatef(err, "failed to parse startTs %s", cb.Spec.StartTs) - } - endTs, err := config.ParseTSString(cb.Spec.EndTs) - if err != nil { - return options.CompactOpts{}, errors.Annotatef(err, "failed to parse endTs %s", cb.Spec.EndTs) - } - out.FromTS = startTs - out.UntilTS = endTs - - out.Name = cb.ObjectMeta.Name - out.Concurrency = uint64(cb.Spec.Concurrency) - - if err := out.Verify(); err != nil { - return options.CompactOpts{}, err - } - - return out, nil -} - -type cOP func(*v1alpha1.CompactBackup) error - -func (k *Kubelink) setState(newState string) cOP { - return func(cb *v1alpha1.CompactBackup) error { - cb.Status.State = newState - return nil - } -} - -func (k *Kubelink) event(ty, reason, msg string) cOP { - return func(cb *v1alpha1.CompactBackup) error { - k.recorder.Event(cb, ty, reason, msg) - return nil - } -} - -func (k *Kubelink) edit(ctx context.Context, extraOps ...cOP) error { - lister := k.cr.PingcapV1alpha1(). - CompactBackups(k.ref.Namespace) - cb, err := lister. - Get(ctx, k.ref.Name, v1.GetOptions{}) - if err != nil { - return err - } - - for _, op := range extraOps { - if err := op(cb); err != nil { - return err - } - } - - _, err = lister.Update(ctx, cb, v1.UpdateOptions{}) - if err != nil { - return err - } - return nil -} - -func (k *Kubelink) editOrWarn(ctx context.Context, extraOps ...cOP) { - if err := k.edit(ctx, extraOps...); err != nil { - klog.Warningf( - "failed to edit state for %s/%s: %v", - k.ref.Namespace, - k.ref.Name, - err, - ) - } -} - -func (k *Kubelink) OnStart(ctx context.Context) { - k.editOrWarn(ctx, k.setState("STARTED"), k.event(corev1.EventTypeNormal, "Started", "CompactionStarted")) -} - -func (k *Kubelink) OnProgress(ctx context.Context, p Progress) { - message := fmt.Sprintf("RUNNING[READ_META(%d/%d),COMPACT_WORK(%d/%d)]", - p.MetaCompleted, p.MetaTotal, p.BytesCompacted, p.BytesToCompact) - k.editOrWarn(ctx, k.setState(message)) -} - -func (k *Kubelink) OnFinish(ctx context.Context, err error) { - if err != nil { - k.editOrWarn(ctx, k.setState(fmt.Sprintf("ERR[%s]", err)), k.event(corev1.EventTypeWarning, "Failed", err.Error())) - } else { - k.editOrWarn(ctx, k.setState("DONE"), k.event(corev1.EventTypeNormal, "Succeeded", "CompactionDone")) - } -} diff --git a/cmd/backup-manager/app/compact/manager.go b/cmd/backup-manager/app/compact/manager.go new file mode 100644 index 0000000000..57d7da02d7 --- /dev/null +++ b/cmd/backup-manager/app/compact/manager.go @@ -0,0 +1,222 @@ +// Copyright 2019 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +package compact + +import ( + "bytes" + "context" + "encoding/json" + "io" + "os" + "os/exec" + "path/filepath" + "strconv" + + "github.com/pingcap/errors" + "github.com/pingcap/tidb-operator/cmd/backup-manager/app/compact/options" + backuputil "github.com/pingcap/tidb-operator/cmd/backup-manager/app/util" + "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1" + pkgutil "github.com/pingcap/tidb-operator/pkg/backup/util" + listers "github.com/pingcap/tidb-operator/pkg/client/listers/pingcap/v1alpha1" + "github.com/pingcap/tidb-operator/pkg/util" + "k8s.io/klog/v2" +) + +type Progress struct { + MetaCompleted uint64 `json:"meta_completed"` + MetaTotal uint64 `json:"meta_total"` + BytesToCompact uint64 `json:"bytes_to_compact"` + BytesCompacted uint64 `json:"bytes_compacted"` +} + +// logLine is line of JSON log. +// It just extracted the message from the JSON and keeps the origin json bytes. +// So you may extract fields from it by `json.Unmarshal(l.Raw, ...)`. +type logLine struct { + Message string + + // Raw is the original log JSON. + Raw []byte +} + +// Manager mainly used to manage backup related work +type Manager struct { + compact *v1alpha1.CompactBackup + resourceLister listers.CompactBackupLister + statusUpdater *CompactStatusUpdater + options options.CompactOpts +} + +// NewManager return a Manager +func NewManager( + lister listers.CompactBackupLister, + statusUpdater *CompactStatusUpdater, + compactOpts options.CompactOpts) *Manager { + compact, err := lister.CompactBackups(compactOpts.Namespace).Get(compactOpts.ResourceName) + if err != nil { + klog.Errorf("can't find compact %s:%s CRD object, err: %v", compactOpts.Namespace, compactOpts.ResourceName, err) + return nil + } + return &Manager{ + compact, + lister, + statusUpdater, + compactOpts, + } +} + +func (cm *Manager) brBin() string { + return filepath.Join(util.BRBinPath, "br") +} + +func (cm *Manager) kvCtlBin() string { + return filepath.Join(util.KVCTLBinPath, "tikv-ctl") +} + +// ProcessBackup used to process the backup logic +func (cm *Manager) ProcessCompact() error { + ctx, cancel := backuputil.GetContextForTerminationSignals(cm.options.ResourceName) + defer cancel() + + compact, err := cm.resourceLister.CompactBackups(cm.options.Namespace).Get(cm.options.ResourceName) + if err != nil { + return errors.New("backup not found") + } + if err := options.ParseCompactOptions(compact, &cm.options); err != nil { + return errors.Annotate(err, "failed to parse compact options") + } + + b64, err := cm.base64ifyStorage(ctx) + if err != nil { + return errors.Annotate(err, "failed to base64ify storage") + } + return cm.runCompaction(ctx, b64) +} + +func (cm *Manager) base64ifyStorage(ctx context.Context) (string, error) { + brCmd, err := cm.base64ifyCmd(ctx) + if err != nil { + return "", err + } + out, err := brCmd.Output() + if err != nil { + eerr := err.(*exec.ExitError) + klog.Warningf("Failed to execute base64ify; stderr = %s", string(eerr.Stderr)) + return "", errors.Annotatef(err, "failed to execute BR with args %v", brCmd.Args) + } + out = bytes.Trim(out, "\r\n \t") + return string(out), nil +} + +func (cm *Manager) base64ifyCmd(ctx context.Context) (*exec.Cmd, error) { + br := cm.brBin() + args := []string{ + "operator", + "base64ify", + } + StorageOpts, err := pkgutil.GenStorageArgsForFlag(cm.compact.Spec.StorageProvider, "") + if err != nil { + return nil, err + } + args = append(args, StorageOpts...) + return exec.CommandContext(ctx, br, args...), nil +} + +func (cm *Manager) runCompaction(ctx context.Context, base64Storage string) (err error) { + cmd := cm.compactCmd(ctx, base64Storage) + defer func() { cm.statusUpdater.OnFinish(ctx, err, cm.compact) }() + + logs, err := cmd.StderrPipe() + if err != nil { + return errors.Annotate(err, "failed to create stderr pipe for compact") + } + if err := cmd.Start(); err != nil { + return errors.Annotate(err, "failed to start compact") + } + + cm.statusUpdater.OnStart(ctx, cm.compact) + err = cm.processCompactionLogs(ctx, io.TeeReader(logs, os.Stdout)) + if err != nil { + return err + } + + return cmd.Wait() +} + +func (cm *Manager) compactCmd(ctx context.Context, base64Storage string) *exec.Cmd { + ctl := cm.kvCtlBin() + // You should not change the log configuration here, it should sync with the upstream setting + args := []string{ + "--log-level", + "INFO", + "--log-format", + "json", + "compact-log-backup", + "--storage-base64", + base64Storage, + "--from", + strconv.FormatUint(cm.options.FromTS, 10), + "--until", + strconv.FormatUint(cm.options.UntilTS, 10), + "-N", + strconv.FormatUint(cm.options.Concurrency, 10), + } + return exec.CommandContext(ctx, ctl, args...) +} + +func (cm *Manager) processCompactionLogs(ctx context.Context, logStream io.Reader) error { + dec := json.NewDecoder(logStream) + + var line logLine + for dec.More() { + if ctx.Err() != nil { + return ctx.Err() + } + if err := dec.Decode(&line); err != nil { + return errors.Annotate(err, "failed to decode the line of log") + } + if err := cm.processLogLine(ctx, line); err != nil { + return errors.Annotate(err, "error during processing log line") + } + } + + return nil +} + +func (cm *Manager) processLogLine(ctx context.Context, l logLine) error { + const ( + messageCompactionDone = "Finishing compaction." + messageCompactAborted = "Compaction aborted." + ) + + switch l.Message { + case messageCompactionDone: + var prog Progress + if err := json.Unmarshal(l.Raw, &prog); err != nil { + return errors.Annotate(err, "failed to decode progress") + } + cm.statusUpdater.OnProgress(ctx, prog, cm.compact) + return nil + case messageCompactAborted: + errContainer := struct { + Err string `json:"err"` + }{} + if err := json.Unmarshal(l.Raw, &errContainer); err != nil { + return errors.Annotate(err, "failed to decode error message") + } + return errors.Errorf("compaction aborted: %s", errContainer.Err) + default: + return nil + } +} diff --git a/cmd/backup-manager/app/compact/options/options.go b/cmd/backup-manager/app/compact/options/options.go index 8890e8bcd0..8ef0c32dff 100644 --- a/cmd/backup-manager/app/compact/options/options.go +++ b/cmd/backup-manager/app/compact/options/options.go @@ -16,9 +16,9 @@ package options import ( "math" - "github.com/pingcap/errors" - - "github.com/spf13/pflag" + "github.com/juju/errors" + "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1" + "github.com/pingcap/tidb-operator/pkg/apis/util/config" ) const ( @@ -35,41 +35,34 @@ const ( concurrencyFlag = "concurrency" ) -type KubeOpts struct { - // This should be fill by the caller. - Kubeconfig string `json:"-"` +type CompactOpts struct { + FromTS uint64 + UntilTS uint64 + Name string + Concurrency uint64 Namespace string `json:"namespace"` ResourceName string `json:"resourceName"` - TiKVVersion string `json:"tikvVersion"` + TikvVersion string `json:"tikvVersion"` } -type CompactOpts struct { - FromTS uint64 - UntilTS uint64 - Name string - Concurrency uint64 - StorageOpts []string -} +func ParseCompactOptions(compact *v1alpha1.CompactBackup, opts *CompactOpts) error { -func DefineFlags(fs *pflag.FlagSet) { - fs.String(tiKVVersionFlag, "", "TiKV version of the resource") - fs.String(namespaceFlag, "", "Namespace of the resource") - fs.String(resourceNameFlag, "", "Name of the resource") -} - -func (k *KubeOpts) ParseFromFlags(fs *pflag.FlagSet) error { - var err error - k.Namespace, err = fs.GetString(namespaceFlag) + startTs, err := config.ParseTSString(compact.Spec.StartTs) if err != nil { - return errors.Trace(err) + return errors.Annotatef(err, "failed to parse startTs %s", compact.Spec.StartTs) } - k.ResourceName, err = fs.GetString(resourceNameFlag) + endTs, err := config.ParseTSString(compact.Spec.EndTs) if err != nil { - return errors.Trace(err) + return errors.Annotatef(err, "failed to parse endTs %s", compact.Spec.EndTs) } - k.TiKVVersion, err = fs.GetString(tiKVVersionFlag) - if err != nil { - return errors.Trace(err) + opts.FromTS = startTs + opts.UntilTS = endTs + + opts.Name = compact.ObjectMeta.Name + opts.Concurrency = uint64(compact.Spec.Concurrency) + + if err := opts.Verify(); err != nil { + return err } return nil diff --git a/cmd/backup-manager/app/compact/run.go b/cmd/backup-manager/app/compact/run.go deleted file mode 100644 index 68f2e063df..0000000000 --- a/cmd/backup-manager/app/compact/run.go +++ /dev/null @@ -1,267 +0,0 @@ -// Copyright 2024 PingCAP, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// See the License for the specific language governing permissions and -// limitations under the License. - -package compact - -import ( - "bytes" - "context" - "encoding/json" - "io" - "os" - "os/exec" - "path/filepath" - "strconv" - - "github.com/pingcap/errors" - "github.com/pingcap/tidb-operator/cmd/backup-manager/app/compact/options" - "github.com/pingcap/tidb-operator/pkg/util" - "k8s.io/klog/v2" -) - -// CompactCtx is the context for *a* compaction. -type CompactCtx struct { - kopts options.KubeOpts - opts options.CompactOpts - - // Delegate defines some hooks that allow you to listen over events and - // controls what compactions should be done. - Delegate Delegate - - /* Below fields are used for testing. */ - - // OverrideBase64ify overrides the shell command used for generate - // the storage base64. - OverrideBase64ify func(ctx context.Context, args []string) *exec.Cmd - // OverrideCompact overrides the shell command used for compacting. - OverrideCompact func(ctx context.Context, args []string) *exec.Cmd -} - -type Progress struct { - MetaCompleted uint64 `json:"meta_completed"` - MetaTotal uint64 `json:"meta_total"` - BytesToCompact uint64 `json:"bytes_to_compact"` - BytesCompacted uint64 `json:"bytes_compacted"` -} - -type CompactionRef struct { - Namespace string - Name string -} - -// Delegate describes the "environment" that a compaction runs. -type Delegate interface { - // GetCompaction will be called at the very beginning. - // This should return a set of parameter of a compaction, then a compaction - // defined by these parameters will be executed. - // - // The argument `ref` refs to a `CompactBackup` resource in the cluster, - // and the compaction should be generated according to it. - GetCompaction(ctx context.Context, ref CompactionRef) (options.CompactOpts, error) - - // OnStart will be called when the `tikv-ctl compact-log-backup` process is about to be spawned. - OnStart(ctx context.Context) - // OnPrgress will be called when the progress of compaction updated. - OnProgress(ctx context.Context, p Progress) - // OnFinish will be called when the `tikv-ctl` process exits. - OnFinish(ctx context.Context, err error) -} - -// BaseDelegate is an empty delegate. -// -// You shouldn't directly use this delegate, instead you may embed it into -// your delegate implementation and override the methods you need. -type BaseDelegate struct{} - -func (BaseDelegate) GetCompaction(ctx context.Context, ref CompactionRef) (options.CompactOpts, error) { - return options.CompactOpts{}, errors.New("override `GetCompaction` to provide a meanful execution") -} -func (BaseDelegate) OnStart(ctx context.Context) {} -func (BaseDelegate) OnProgress(ctx context.Context, p Progress) {} -func (BaseDelegate) OnFinish(ctx context.Context, err error) {} - -func (r *CompactCtx) brBin() string { - return filepath.Join(util.BRBinPath, "br") -} - -func (r *CompactCtx) kvCtlBin() string { - return filepath.Join(util.KVCTLBinPath, "tikv-ctl") -} - -func (r *CompactCtx) base64ifyCmd(ctx context.Context, extraArgs []string) *exec.Cmd { - br := r.brBin() - args := []string{ - "operator", - "base64ify", - } - args = append(args, extraArgs...) - - if r.OverrideBase64ify != nil { - return r.OverrideBase64ify(ctx, args) - } - return exec.CommandContext(ctx, br, args...) -} - -func (r *CompactCtx) base64ifyStorage(ctx context.Context) (string, error) { - brCmd := r.base64ifyCmd(ctx, r.opts.StorageOpts) - out, err := brCmd.Output() - if err != nil { - eerr := err.(*exec.ExitError) - klog.Warningf("Failed to execute base64ify; stderr = %s", string(eerr.Stderr)) - return "", errors.Annotatef(err, "failed to execute BR with args %v", brCmd.Args) - } - out = bytes.Trim(out, "\r\n \t") - return string(out), nil -} - -func (r *CompactCtx) compactCmd(ctx context.Context, base64Storage string) *exec.Cmd { - ctl := r.kvCtlBin() - args := []string{ - "--log-level", - "INFO", - "--log-format", - "json", - "compact-log-backup", - "--storage-base64", - base64Storage, - "--from", - strconv.FormatUint(r.opts.FromTS, 10), - "--until", - strconv.FormatUint(r.opts.UntilTS, 10), - "-N", - strconv.FormatUint(r.opts.Concurrency, 10), - } - - if r.OverrideCompact != nil { - return r.OverrideCompact(ctx, args) - } - return exec.CommandContext(ctx, ctl, args...) -} - -func (r *CompactCtx) runCompaction(ctx context.Context, base64Storage string) (err error) { - cmd := r.compactCmd(ctx, base64Storage) - defer func() { r.Delegate.OnFinish(ctx, err) }() - - logs, err := cmd.StderrPipe() - if err != nil { - return errors.Annotate(err, "failed to create stderr pipe for compact") - } - if err := cmd.Start(); err != nil { - return errors.Annotate(err, "failed to start compact") - } - - r.Delegate.OnStart(ctx) - err = r.processCompactionLogs(ctx, io.TeeReader(logs, os.Stdout)) - if err != nil { - return err - } - - return cmd.Wait() -} - -// logLine is line of JSON log. -// It just extracted the message from the JSON and keeps the origin json bytes. -// So you may extract fields from it by `json.Unmarshal(l.Raw, ...)`. -type logLine struct { - Message string - - // Raw is the original log JSON. - Raw []byte -} - -var _ json.Unmarshaler = &logLine{} - -func (l *logLine) UnmarshalJSON(bytes []byte) error { - item := struct { - Message string `json:"message"` - }{} - if err := json.Unmarshal(bytes, &item); err != nil { - return err - } - l.Message = item.Message - l.Raw = bytes - - return nil -} - -func (r *CompactCtx) processLogLine(ctx context.Context, l logLine) error { - const ( - messageCompactionDone = "Finishing compaction." - messageCompactAborted = "Compaction aborted." - ) - - switch l.Message { - case messageCompactionDone: - var prog Progress - if err := json.Unmarshal(l.Raw, &prog); err != nil { - return errors.Annotate(err, "failed to decode progress") - } - r.Delegate.OnProgress(ctx, prog) - return nil - case messageCompactAborted: - errContainer := struct { - Err string `json:"err"` - }{} - if err := json.Unmarshal(l.Raw, &errContainer); err != nil { - return errors.Annotate(err, "failed to decode error message") - } - return errors.Errorf("compaction aborted: %s", errContainer.Err) - default: - return nil - } -} - -func (r *CompactCtx) processCompactionLogs(ctx context.Context, logStream io.Reader) error { - dec := json.NewDecoder(logStream) - - var line logLine - for dec.More() { - if ctx.Err() != nil { - return ctx.Err() - } - if err := dec.Decode(&line); err != nil { - return errors.Annotate(err, "failed to decode the line of log") - } - if err := r.processLogLine(ctx, line); err != nil { - return errors.Annotate(err, "error during processing log line") - } - } - - return nil -} - -func (r *CompactCtx) Run(ctx context.Context) error { - ref := CompactionRef{ - Namespace: r.kopts.Namespace, - Name: r.kopts.ResourceName, - } - opts, err := r.Delegate.GetCompaction(ctx, ref) - if err != nil { - return errors.Annotate(err, "failed to get storage string") - } - r.opts = opts - - b64, err := r.base64ifyStorage(ctx) - if err != nil { - return errors.Annotate(err, "failed to base64ify storage") - } - return r.runCompaction(ctx, b64) -} - -// New creates a new compaction context. -func New(opts options.KubeOpts, dele Delegate) *CompactCtx { - return &CompactCtx{ - kopts: opts, - Delegate: dele, - } -} diff --git a/cmd/backup-manager/app/compact/run_test.go b/cmd/backup-manager/app/compact/run_test.go deleted file mode 100644 index 57b8ab06be..0000000000 --- a/cmd/backup-manager/app/compact/run_test.go +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2024 PingCAP, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// See the License for the specific language governing permissions and -// limitations under the License. - -package compact_test - -import ( - "bytes" - "context" - "encoding/json" - "io" - "os/exec" - "testing" - "testing/iotest" - - "github.com/pingcap/tidb-operator/cmd/backup-manager/app/compact" - "github.com/pingcap/tidb-operator/cmd/backup-manager/app/compact/options" - "github.com/stretchr/testify/require" -) - -type callRecorder []call - -type call struct { - cmd string - attrs map[string]any -} - -func (c *callRecorder) GetCompaction(ctx context.Context, ref compact.CompactionRef) (options.CompactOpts, error) { - *c = append(*c, call{cmd: "GetCompaction", attrs: map[string]any{ - "ref": ref, - }}) - return options.CompactOpts{}, nil -} - -func (c *callRecorder) OnStart(ctx context.Context) { - *c = append(*c, call{cmd: "OnStart"}) -} - -func (c *callRecorder) OnProgress(ctx context.Context, p compact.Progress) { - *c = append(*c, call{cmd: "OnProgress", attrs: map[string]any{ - "MetaCompleted": p.MetaCompleted, - "MetaTotal": p.MetaTotal, - "BytesToCompact": p.BytesToCompact, - "BytesCompacted": p.BytesCompacted, - }}) -} - -func (c *callRecorder) OnFinish(ctx context.Context, err error) { - *c = append(*c, call{cmd: "OnFinish", attrs: map[string]any{ - "error": err, - }}) -} - -type dummyCommands struct { - theBase64 string - t *testing.T -} - -func (d dummyCommands) OverrideBase64ify(context.Context, []string) *exec.Cmd { - cmd := exec.Command("cat", "-") - cmd.Stdin = bytes.NewBufferString(d.theBase64) - return cmd -} - -func (d dummyCommands) OverrideCompact(_ context.Context, args []string) *exec.Cmd { - storage := "" - for i, arg := range args { - if arg == "--storage-base64" && i+1 < len(args) { - storage = args[i+1] - break - } - } - - require.Equal(d.t, storage, d.theBase64, "%#v", args) - return exec.Command("true") -} - -type empty struct { - compact.BaseDelegate -} - -func (e empty) GetCompaction(context.Context, compact.CompactionRef) (options.CompactOpts, error) { - return options.CompactOpts{}, nil -} - -func TestNormal(t *testing.T) { - cx := compact.New(options.KubeOpts{}, empty{}) - dc := dummyCommands{ - theBase64: "was yae", - t: t, - } - cx.OverrideBase64ify = dc.OverrideBase64ify - cx.OverrideCompact = dc.OverrideCompact - - require.NoError(t, cx.Run(context.Background())) -} - -type dummyProgress struct { - progs []compact.Progress - t *testing.T -} - -func (d dummyProgress) OverrideBase64ify(context.Context, []string) *exec.Cmd { - return exec.Command("true") -} - -func toMap(t *testing.T, v any) (res map[string]any) { - data, err := json.Marshal(v) - require.NoError(t, err) - require.NoError(t, json.Unmarshal(data, &res)) - return -} - -func (d dummyProgress) OverrideCompact(_ context.Context, args []string) *exec.Cmd { - cmd := exec.Command("sh", "-c", "cat - >&2") - rx, tx := io.Pipe() - - enc := json.NewEncoder(tx) - go func() { - for _, prog := range d.progs { - m := toMap(d.t, prog) - m["message"] = "Finishing compaction." - require.NoError(d.t, enc.Encode(m)) - } - tx.Close() - }() - - // Simulating streaming progress. - cmd.Stdin = iotest.OneByteReader(rx) - return cmd -} - -func TestWithProgress(t *testing.T) { - rec := callRecorder{} - cx := compact.New(options.KubeOpts{Namespace: "foo", ResourceName: "bar"}, &rec) - dp := dummyProgress{ - progs: []compact.Progress{ - {MetaCompleted: 1, MetaTotal: 2, BytesToCompact: 3, BytesCompacted: 4}, - {MetaCompleted: 2, MetaTotal: 2, BytesToCompact: 7, BytesCompacted: 8}, - {MetaCompleted: 2, MetaTotal: 2, BytesToCompact: 8, BytesCompacted: 8}, - }, - t: t, - } - cx.OverrideBase64ify = dp.OverrideBase64ify - cx.OverrideCompact = dp.OverrideCompact - - require.NoError(t, cx.Run(context.Background())) - - require.Equal(t, []call(rec), []call{ - {cmd: "GetCompaction", attrs: map[string]any{ - "ref": compact.CompactionRef{ - Namespace: "foo", - Name: "bar", - }, - }}, - {cmd: "OnStart"}, - {cmd: "OnProgress", attrs: map[string]any{ - "MetaCompleted": uint64(1), - "MetaTotal": uint64(2), - "BytesToCompact": uint64(3), - "BytesCompacted": uint64(4), - }}, - {cmd: "OnProgress", attrs: map[string]any{ - "MetaCompleted": uint64(2), - "MetaTotal": uint64(2), - "BytesToCompact": uint64(7), - "BytesCompacted": uint64(8), - }}, - {cmd: "OnProgress", attrs: map[string]any{ - "MetaCompleted": uint64(2), - "MetaTotal": uint64(2), - "BytesToCompact": uint64(8), - "BytesCompacted": uint64(8), - }}, - {cmd: "OnFinish", attrs: map[string]any{ - "error": error(nil), - }}, - }) -} diff --git a/cmd/backup-manager/app/compact/status_updater.go b/cmd/backup-manager/app/compact/status_updater.go new file mode 100644 index 0000000000..32714c8651 --- /dev/null +++ b/cmd/backup-manager/app/compact/status_updater.go @@ -0,0 +1,96 @@ +// Copyright 2019 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +package compact + +import ( + "context" + "fmt" + + "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1" + "github.com/pingcap/tidb-operator/pkg/client/clientset/versioned" + listers "github.com/pingcap/tidb-operator/pkg/client/listers/pingcap/v1alpha1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/client-go/tools/record" + "k8s.io/client-go/util/retry" + "k8s.io/klog" +) + +type CompactStatusUpdater struct { + recorder record.EventRecorder + lister listers.CompactBackupLister + cli versioned.Interface +} + +func NewCompactStatusUpdater(recorder record.EventRecorder, lister listers.CompactBackupLister, cli versioned.Interface) *CompactStatusUpdater { + return &CompactStatusUpdater{ + recorder: recorder, + lister: lister, + cli: cli, + } +} + +func (r *CompactStatusUpdater) Event(compact *v1alpha1.CompactBackup, ty, reason, msg string) { + r.recorder.Event(compact, ty, reason, msg) +} + +func (r *CompactStatusUpdater) UpdateStatus(compact *v1alpha1.CompactBackup, newState string) error { + ns := compact.GetNamespace() + backupName := compact.GetName() + // try best effort to guarantee backup is updated. + err := retry.OnError(retry.DefaultRetry, func(e error) bool { return e != nil }, func() error { + // Always get the latest backup before update. + if updated, err := r.lister.CompactBackups(ns).Get(backupName); err == nil { + // make a copy so we don't mutate the shared cache + *compact = *(updated.DeepCopy()) + } else { + utilruntime.HandleError(fmt.Errorf("error getting updated backup %s/%s from lister: %v", ns, backupName, err)) + return err + } + if compact.Status.State != newState { + compact.Status.State = newState + _, updateErr := r.cli.PingcapV1alpha1().CompactBackups(ns).Update(context.TODO(), compact, metav1.UpdateOptions{}) + if updateErr == nil { + klog.Infof("Backup: [%s/%s] updated successfully", ns, backupName) + return nil + } + klog.Errorf("Failed to update backup [%s/%s], error: %v", ns, backupName, updateErr) + return updateErr + } + return nil + }) + return err +} + +func (r *CompactStatusUpdater) OnStart(ctx context.Context, compact *v1alpha1.CompactBackup) { + r.UpdateStatus(compact, "RUNNING") + r.Event(compact, corev1.EventTypeNormal, "Started", "The compaction process has started successfully.") +} + +func (r *CompactStatusUpdater) OnProgress(ctx context.Context, p Progress, compact *v1alpha1.CompactBackup) { + message := fmt.Sprintf("RUNNING[READ_META(%d/%d),COMPACT_WORK(%d/%d)]", + p.MetaCompleted, p.MetaTotal, p.BytesCompacted, p.BytesToCompact) + r.UpdateStatus(compact, message) +} + +func (r *CompactStatusUpdater) OnFinish(ctx context.Context, err error, compact *v1alpha1.CompactBackup) { + if err != nil { + r.UpdateStatus(compact, "FAILED") + r.Event(compact, corev1.EventTypeWarning, "Failed", err.Error()) + } else { + r.UpdateStatus(compact, "FINISHED") + r.Event(compact, corev1.EventTypeNormal, "Finished", "The compaction process has finished successfully.") + } +} diff --git a/cmd/backup-manager/main.go b/cmd/backup-manager/main.go index 37b7032194..8a0f64f2f8 100644 --- a/cmd/backup-manager/main.go +++ b/cmd/backup-manager/main.go @@ -16,12 +16,10 @@ package main import ( "os" - "k8s.io/klog/v2" - "github.com/pingcap/tidb-operator/cmd/backup-manager/app" - // Enable FIPS when necessary _ "github.com/pingcap/tidb-operator/pkg/fips" + "k8s.io/klog/v2" ) func main() { diff --git a/go.mod b/go.mod index 631351aa68..8a1a9f7ea3 100644 --- a/go.mod +++ b/go.mod @@ -153,6 +153,7 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/juju/errors v1.0.0 github.com/klauspost/compress v1.17.4 // indirect github.com/klauspost/cpuid v1.2.3 // indirect github.com/klauspost/pgzip v1.2.5 // indirect diff --git a/go.sum b/go.sum index 55733aeb9f..f83c1e335f 100644 --- a/go.sum +++ b/go.sum @@ -511,6 +511,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juju/errors v1.0.0 h1:yiq7kjCLll1BiaRuNY53MGI0+EQ3rF6GB+wvboZDefM= +github.com/juju/errors v1.0.0/go.mod h1:B5x9thDqx0wIMH3+aLIMP9HjItInYWObRovoCFM5Qe8= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs= github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw= From b0f3c6582513fa14b1f46aad9535da2489f8490e Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Fri, 6 Dec 2024 10:50:51 +0100 Subject: [PATCH 21/49] fix --- cmd/backup-manager/app/cmd/compact.go | 2 +- cmd/backup-manager/app/compact/manager.go | 2 +- cmd/backup-manager/app/compact/status_updater.go | 4 ++-- pkg/apis/pingcap/v1alpha1/types.go | 2 +- pkg/controller/compactbackup/compact_backup_controller.go | 5 +++-- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/cmd/backup-manager/app/cmd/compact.go b/cmd/backup-manager/app/cmd/compact.go index b890456d9b..89d0a61754 100644 --- a/cmd/backup-manager/app/cmd/compact.go +++ b/cmd/backup-manager/app/cmd/compact.go @@ -53,7 +53,7 @@ func runCompact(compactOpts options.CompactOpts, kubecfg string) error { informers.WithNamespace(compactOpts.Namespace), } informerFactory := informers.NewSharedInformerFactoryWithOptions(cli, constants.ResyncDuration, options...) - recorder := util.NewEventRecorder(kubeCli, "compact") + recorder := util.NewEventRecorder(kubeCli, "compact-manager") compactInformer := informerFactory.Pingcap().V1alpha1().CompactBackups() statusUpdater := compact.NewCompactStatusUpdater(recorder, compactInformer.Lister(), cli) diff --git a/cmd/backup-manager/app/compact/manager.go b/cmd/backup-manager/app/compact/manager.go index 57d7da02d7..58e6494c59 100644 --- a/cmd/backup-manager/app/compact/manager.go +++ b/cmd/backup-manager/app/compact/manager.go @@ -125,7 +125,7 @@ func (cm *Manager) base64ifyCmd(ctx context.Context) (*exec.Cmd, error) { "operator", "base64ify", } - StorageOpts, err := pkgutil.GenStorageArgsForFlag(cm.compact.Spec.StorageProvider, "") + StorageOpts, err := pkgutil.GenStorageArgsForFlag(cm.compact.Spec.StorageProvider, "storage") if err != nil { return nil, err } diff --git a/cmd/backup-manager/app/compact/status_updater.go b/cmd/backup-manager/app/compact/status_updater.go index 32714c8651..6a1f353299 100644 --- a/cmd/backup-manager/app/compact/status_updater.go +++ b/cmd/backup-manager/app/compact/status_updater.go @@ -87,10 +87,10 @@ func (r *CompactStatusUpdater) OnProgress(ctx context.Context, p Progress, compa func (r *CompactStatusUpdater) OnFinish(ctx context.Context, err error, compact *v1alpha1.CompactBackup) { if err != nil { - r.UpdateStatus(compact, "FAILED") r.Event(compact, corev1.EventTypeWarning, "Failed", err.Error()) + r.UpdateStatus(compact, "FAILED") } else { - r.UpdateStatus(compact, "FINISHED") r.Event(compact, corev1.EventTypeNormal, "Finished", "The compaction process has finished successfully.") + r.UpdateStatus(compact, "FINISHED") } } diff --git a/pkg/apis/pingcap/v1alpha1/types.go b/pkg/apis/pingcap/v1alpha1/types.go index 2752ebd8a6..cebee32d18 100644 --- a/pkg/apis/pingcap/v1alpha1/types.go +++ b/pkg/apis/pingcap/v1alpha1/types.go @@ -3515,7 +3515,7 @@ type CompactSpec struct { // For examples `spec.brImage: pingcap/br:v4.0.8` // For BR image, if it does not contain tag, Pod will use image 'BrImage:${TiKV_Version}'. // +optional - ToolImage string `json:"brImage,omitempty"` + ToolImage string `json:"toolImage,omitempty"` // BRConfig is the configs for BR BR *BRConfig `json:"br,omitempty"` // ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images. diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index 54a4e000d2..8c554edf33 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -256,7 +256,8 @@ func (c *Controller) sync(key string) (err error) { var newState string if err != nil { - newState = "Failed" + newState = "Failed:" + err.Error() + klog.Errorf("Backup: [%s/%s] sync failed, error: %v", ns, name, err) } else { newState = "Running" } @@ -314,7 +315,7 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job fmt.Sprintf("--resourceName=%s", name), } - tc, err := c.deps.TiDBClusterLister.TidbClusters(ns).Get(backup.Spec.BR.Cluster) + tc, err := c.deps.TiDBClusterLister.TidbClusters(backup.Spec.BR.ClusterNamespace).Get(backup.Spec.BR.Cluster) if err != nil { return nil, fmt.Sprintf("failed to fetch tidbcluster %s/%s", ns, backup.Spec.BR.Cluster), err } From 25693544cad288e81a0a68702b17402cf32d2d91 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Mon, 9 Dec 2024 08:56:32 +0100 Subject: [PATCH 22/49] fix types name --- cmd/backup-manager/app/cmd/compact.go | 1 - docs/api-references/docs.md | 4 ++-- manifests/crd.yaml | 4 ++-- manifests/crd/v1/pingcap.com_compactbackups.yaml | 4 ++-- pkg/apis/pingcap/v1alpha1/openapi_generated.go | 2 +- pkg/backup/backup/backup_cleaner.go | 6 +++++- pkg/controller/compactbackup/compact_backup_controller.go | 4 +--- 7 files changed, 13 insertions(+), 12 deletions(-) diff --git a/cmd/backup-manager/app/cmd/compact.go b/cmd/backup-manager/app/cmd/compact.go index 89d0a61754..5f8796ed75 100644 --- a/cmd/backup-manager/app/cmd/compact.go +++ b/cmd/backup-manager/app/cmd/compact.go @@ -40,7 +40,6 @@ func NewCompactCommand() *cobra.Command { cmd.Flags().StringVar(&opts.Namespace, "namespace", "", "Backup CR's namespace") cmd.Flags().StringVar(&opts.ResourceName, "resourceName", "", "Backup CRD object name") - cmd.Flags().StringVar(&opts.TikvVersion, "tikvVersion", util.DefaultVersion, "TiKV version") return cmd } diff --git a/docs/api-references/docs.md b/docs/api-references/docs.md index f217ad30b3..193f96923c 100644 --- a/docs/api-references/docs.md +++ b/docs/api-references/docs.md @@ -5545,7 +5545,7 @@ bool + + + + + + + +
-version
- -string - -
-

Version specifies the tool image version used in compact Backup.

-
brImage
string @@ -5935,15 +5914,15 @@ For BR image, if it does not contain tag, Pod will use image ‘BrImage:${Ti
-tikvImage
+br
-string + +BRConfig +
-(Optional) -

TiKVImage specifies the tikv image used in compact Backup. -For examples spec.tikvImage: pingcap/tikv:v4.0.8

+

BRConfig is the configs for BR

-brImage
+toolImage
string @@ -5900,7 +5900,7 @@ bool
-brImage
+toolImage
string diff --git a/manifests/crd.yaml b/manifests/crd.yaml index 7079fab07f..c73a2e42ee 100644 --- a/manifests/crd.yaml +++ b/manifests/crd.yaml @@ -8244,8 +8244,6 @@ spec: required: - cluster type: object - brImage: - type: string concurrency: type: integer endTs: @@ -9251,6 +9249,8 @@ spec: type: string type: object type: array + toolImage: + type: string useKMS: type: boolean volumeBackupInitJobMaxActiveSeconds: diff --git a/manifests/crd/v1/pingcap.com_compactbackups.yaml b/manifests/crd/v1/pingcap.com_compactbackups.yaml index 19ee6b2a4a..a837d98f18 100644 --- a/manifests/crd/v1/pingcap.com_compactbackups.yaml +++ b/manifests/crd/v1/pingcap.com_compactbackups.yaml @@ -1194,8 +1194,6 @@ spec: required: - cluster type: object - brImage: - type: string concurrency: type: integer endTs: @@ -2201,6 +2199,8 @@ spec: type: string type: object type: array + toolImage: + type: string useKMS: type: boolean volumeBackupInitJobMaxActiveSeconds: diff --git a/pkg/apis/pingcap/v1alpha1/openapi_generated.go b/pkg/apis/pingcap/v1alpha1/openapi_generated.go index 4906ef9a5f..030f7dc7ce 100644 --- a/pkg/apis/pingcap/v1alpha1/openapi_generated.go +++ b/pkg/apis/pingcap/v1alpha1/openapi_generated.go @@ -1817,7 +1817,7 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) }, }, }, - "brImage": { + "toolImage": { SchemaProps: spec.SchemaProps{ Description: "BrImage specifies the br image used in compact `Backup`. For examples `spec.brImage: pingcap/br:v4.0.8` For BR image, if it does not contain tag, Pod will use image 'BrImage:${TiKV_Version}'.", Type: []string{"string"}, diff --git a/pkg/backup/backup/backup_cleaner.go b/pkg/backup/backup/backup_cleaner.go index d80967dd2c..8f859f6fe6 100644 --- a/pkg/backup/backup/backup_cleaner.go +++ b/pkg/backup/backup/backup_cleaner.go @@ -73,7 +73,11 @@ func (bc *backupCleaner) StopLogBackup(backup *v1alpha1.Backup) error { return fmt.Errorf("backup %s/%s spec.BR shouldn't be nil", backup.GetNamespace(), backup.GetName()) } if !v1alpha1.IsLogBackupAlreadyStart(backup) { - return nil + return bc.statusUpdater.Update(backup, &v1alpha1.BackupCondition{ + Command: v1alpha1.LogStopCommand, + Type: v1alpha1.BackupComplete, + Status: corev1.ConditionTrue, + }, nil) } if v1alpha1.IsLogBackupAlreadyStop(backup) { return nil diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index 8c554edf33..a84cca4f26 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -321,9 +321,6 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job } tikvImage := tc.TiKVImage() _, tikvVersion := backuputil.ParseImage(tikvImage) - if tikvVersion != "" { - args = append(args, fmt.Sprintf("--tikvVersion=%s", tikvVersion)) - } brImage := "pingcap/br:" + tikvVersion if backup.Spec.ToolImage != "" { toolImage := backup.Spec.ToolImage @@ -333,6 +330,7 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job brImage = toolImage } + klog.Infof("backup %s/%s use br image %s and tikv image %s", ns, name, brImage, tikvImage) //TODO: (Ris)What is the instance here? jobLabels := util.CombineStringMap(label.NewBackup().Instance("Compact-Backup").BackupJob().Backup(name), backup.Labels) From 53a443ce4f8bae06ea21ef03b2c3e34e1e852d0a Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Wed, 11 Dec 2024 09:11:12 +0100 Subject: [PATCH 23/49] delete unused --- cmd/backup-manager/app/compact/options/options.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/cmd/backup-manager/app/compact/options/options.go b/cmd/backup-manager/app/compact/options/options.go index 8ef0c32dff..56e2afe930 100644 --- a/cmd/backup-manager/app/compact/options/options.go +++ b/cmd/backup-manager/app/compact/options/options.go @@ -24,15 +24,6 @@ import ( const ( fromTSUnset = math.MaxUint64 untilTSUnset = 0 - - namespaceFlag = "namespace" - resourceNameFlag = "resourceName" - tiKVVersionFlag = "tikvVersion" - storageStringFlag = "storage-string" - fromTSFlag = "from-ts" - untilTSFlag = "until-ts" - nameFlag = "name" - concurrencyFlag = "concurrency" ) type CompactOpts struct { From ce4005a60ec5400030836fc040dbbd6334b9f7d3 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Thu, 12 Dec 2024 07:41:05 +0100 Subject: [PATCH 24/49] more detailed status --- cmd/backup-manager/app/compact/manager.go | 27 +++++---- .../app/compact/status_updater.go | 60 +++++++++++++++---- docs/api-references/docs.md | 20 +++++++ manifests/crd.yaml | 12 ++++ .../crd/v1/pingcap.com_compactbackups.yaml | 12 ++++ pkg/apis/pingcap/v1alpha1/types.go | 6 +- .../compact_backup_controller.go | 16 +++-- 7 files changed, 123 insertions(+), 30 deletions(-) diff --git a/cmd/backup-manager/app/compact/manager.go b/cmd/backup-manager/app/compact/manager.go index 58e6494c59..28ba18dcf7 100644 --- a/cmd/backup-manager/app/compact/manager.go +++ b/cmd/backup-manager/app/compact/manager.go @@ -44,10 +44,8 @@ type Progress struct { // It just extracted the message from the JSON and keeps the origin json bytes. // So you may extract fields from it by `json.Unmarshal(l.Raw, ...)`. type logLine struct { - Message string - - // Raw is the original log JSON. - Raw []byte + Message string `json:"Message"` + Raw json.RawMessage `json:"-"` } // Manager mainly used to manage backup related work @@ -178,16 +176,24 @@ func (cm *Manager) compactCmd(ctx context.Context, base64Storage string) *exec.C func (cm *Manager) processCompactionLogs(ctx context.Context, logStream io.Reader) error { dec := json.NewDecoder(logStream) - var line logLine for dec.More() { if ctx.Err() != nil { return ctx.Err() } - if err := dec.Decode(&line); err != nil { + + var raw json.RawMessage + if err := dec.Decode(&raw); err != nil { + return errors.Annotate(err, "failed to decode raw log line") + } + + var line logLine + if err := json.Unmarshal(raw, &line); err != nil { return errors.Annotate(err, "failed to decode the line of log") } + line.Raw = raw + if err := cm.processLogLine(ctx, line); err != nil { - return errors.Annotate(err, "error during processing log line") + return err } } @@ -204,7 +210,7 @@ func (cm *Manager) processLogLine(ctx context.Context, l logLine) error { case messageCompactionDone: var prog Progress if err := json.Unmarshal(l.Raw, &prog); err != nil { - return errors.Annotate(err, "failed to decode progress") + return errors.Annotatef(err, "failed to decode progress message: %s", string(l.Raw)) } cm.statusUpdater.OnProgress(ctx, prog, cm.compact) return nil @@ -213,10 +219,11 @@ func (cm *Manager) processLogLine(ctx context.Context, l logLine) error { Err string `json:"err"` }{} if err := json.Unmarshal(l.Raw, &errContainer); err != nil { - return errors.Annotate(err, "failed to decode error message") + return errors.Annotatef(err, "failed to decode error message: %s", string(l.Raw)) } - return errors.Errorf("compaction aborted: %s", errContainer.Err) + return errors.New(errContainer.Err) default: + klog.Infof("progress log: %s", l.Message) return nil } } diff --git a/cmd/backup-manager/app/compact/status_updater.go b/cmd/backup-manager/app/compact/status_updater.go index 6a1f353299..0dd5d5f9d5 100644 --- a/cmd/backup-manager/app/compact/status_updater.go +++ b/cmd/backup-manager/app/compact/status_updater.go @@ -16,6 +16,7 @@ package compact import ( "context" "fmt" + "time" "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1" "github.com/pingcap/tidb-operator/pkg/client/clientset/versioned" @@ -28,10 +29,16 @@ import ( "k8s.io/klog" ) +const ( + // progressDebounceDuration is the minimum time interval between two progress updates for a backup + progressDebounceDuration = 3 * time.Second +) + type CompactStatusUpdater struct { - recorder record.EventRecorder - lister listers.CompactBackupLister - cli versioned.Interface + recorder record.EventRecorder + lister listers.CompactBackupLister + cli versioned.Interface + progressLastUpdate time.Time } func NewCompactStatusUpdater(recorder record.EventRecorder, lister listers.CompactBackupLister, cli versioned.Interface) *CompactStatusUpdater { @@ -46,21 +53,48 @@ func (r *CompactStatusUpdater) Event(compact *v1alpha1.CompactBackup, ty, reason r.recorder.Event(compact, ty, reason, msg) } -func (r *CompactStatusUpdater) UpdateStatus(compact *v1alpha1.CompactBackup, newState string) error { +func (r *CompactStatusUpdater) UpdateStatus(compact *v1alpha1.CompactBackup, newState string, progress string, message string) error { ns := compact.GetNamespace() backupName := compact.GetName() - // try best effort to guarantee backup is updated. + key := fmt.Sprintf("%s/%s", ns, backupName) // Unique key for the backup + + now := time.Now() + updateProgress := true + if progress != "" { + if now.Sub(r.progressLastUpdate) < progressDebounceDuration { + klog.Infof("Skipping progress update for [%s] due to debounce interval", key) + updateProgress = false + } + } + + // Update the status err := retry.OnError(retry.DefaultRetry, func(e error) bool { return e != nil }, func() error { - // Always get the latest backup before update. + // Always get the latest CompactBackup before updating if updated, err := r.lister.CompactBackups(ns).Get(backupName); err == nil { - // make a copy so we don't mutate the shared cache *compact = *(updated.DeepCopy()) } else { utilruntime.HandleError(fmt.Errorf("error getting updated backup %s/%s from lister: %v", ns, backupName, err)) return err } - if compact.Status.State != newState { + + updated := false + if newState !="" && compact.Status.State != newState { compact.Status.State = newState + updated = true + updateProgress = true + } + if message != "" && compact.Status.Message != message { + compact.Status.Message = message + updated = true + } + if updateProgress && progress != "" && compact.Status.Progress != progress { + compact.Status.Progress = progress + updated = true + r.progressLastUpdate = now + } + + // Apply the update if any field changed + if updated { _, updateErr := r.cli.PingcapV1alpha1().CompactBackups(ns).Update(context.TODO(), compact, metav1.UpdateOptions{}) if updateErr == nil { klog.Infof("Backup: [%s/%s] updated successfully", ns, backupName) @@ -75,22 +109,22 @@ func (r *CompactStatusUpdater) UpdateStatus(compact *v1alpha1.CompactBackup, new } func (r *CompactStatusUpdater) OnStart(ctx context.Context, compact *v1alpha1.CompactBackup) { - r.UpdateStatus(compact, "RUNNING") + r.UpdateStatus(compact, string(v1alpha1.BackupRunning), "", "") r.Event(compact, corev1.EventTypeNormal, "Started", "The compaction process has started successfully.") } func (r *CompactStatusUpdater) OnProgress(ctx context.Context, p Progress, compact *v1alpha1.CompactBackup) { - message := fmt.Sprintf("RUNNING[READ_META(%d/%d),COMPACT_WORK(%d/%d)]", + progress := fmt.Sprintf("[READ_META(%d/%d),COMPACT_WORK(%d/%d)]", p.MetaCompleted, p.MetaTotal, p.BytesCompacted, p.BytesToCompact) - r.UpdateStatus(compact, message) + r.UpdateStatus(compact, "", progress, "") } func (r *CompactStatusUpdater) OnFinish(ctx context.Context, err error, compact *v1alpha1.CompactBackup) { if err != nil { r.Event(compact, corev1.EventTypeWarning, "Failed", err.Error()) - r.UpdateStatus(compact, "FAILED") + r.UpdateStatus(compact, string(v1alpha1.BackupFailed), "", err.Error()) } else { r.Event(compact, corev1.EventTypeNormal, "Finished", "The compaction process has finished successfully.") - r.UpdateStatus(compact, "FINISHED") + r.UpdateStatus(compact, string(v1alpha1.BackupComplete), "", "") } } diff --git a/docs/api-references/docs.md b/docs/api-references/docs.md index 193f96923c..e56d99ecd0 100644 --- a/docs/api-references/docs.md +++ b/docs/api-references/docs.md @@ -6079,6 +6079,26 @@ string
+progress
+ +string + +
+
+message
+ +string + +
+

ComponentAccessor

diff --git a/manifests/crd.yaml b/manifests/crd.yaml index c73a2e42ee..5b2865926a 100644 --- a/manifests/crd.yaml +++ b/manifests/crd.yaml @@ -7071,6 +7071,14 @@ spec: jsonPath: .status.state name: Status type: string + - description: The progress of the compact backup + jsonPath: .status.progress + name: Progress + type: string + - description: The message of the compact backup + jsonPath: .status.message + name: Message + type: string name: v1alpha1 schema: openAPIV3Schema: @@ -9259,6 +9267,10 @@ spec: type: object status: properties: + message: + type: string + progress: + type: string state: type: string type: object diff --git a/manifests/crd/v1/pingcap.com_compactbackups.yaml b/manifests/crd/v1/pingcap.com_compactbackups.yaml index a837d98f18..d852d4659f 100644 --- a/manifests/crd/v1/pingcap.com_compactbackups.yaml +++ b/manifests/crd/v1/pingcap.com_compactbackups.yaml @@ -21,6 +21,14 @@ spec: jsonPath: .status.state name: Status type: string + - description: The progress of the compact backup + jsonPath: .status.progress + name: Progress + type: string + - description: The message of the compact backup + jsonPath: .status.message + name: Message + type: string name: v1alpha1 schema: openAPIV3Schema: @@ -2209,6 +2217,10 @@ spec: type: object status: properties: + message: + type: string + progress: + type: string state: type: string type: object diff --git a/pkg/apis/pingcap/v1alpha1/types.go b/pkg/apis/pingcap/v1alpha1/types.go index cebee32d18..042935592e 100644 --- a/pkg/apis/pingcap/v1alpha1/types.go +++ b/pkg/apis/pingcap/v1alpha1/types.go @@ -3450,6 +3450,8 @@ type ScalePolicy struct { // +k8s:openapi-gen=true // +kubebuilder:resource:shortName="cpbk" // +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.state`,description="The current status of the compact backup" +// +kubebuilder:printcolumn:name="Progress",type=string,JSONPath=`.status.progress`,description="The progress of the compact backup" +// +kubebuilder:printcolumn:name="Message",type=string,JSONPath=`.status.message`,description="The message of the compact backup" type CompactBackup struct { metav1.TypeMeta `json:",inline"` // +k8s:openapi-gen=false @@ -3551,7 +3553,9 @@ type CompactSpec struct { } type CompactStatus struct { - State string `json:"state,omitempty"` + State string `json:"state,omitempty"` + Progress string `json:"progress,omitempty"` + Message string `json:"message,omitempty"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index a84cca4f26..9e363e598c 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -102,7 +102,7 @@ func (c *Controller) worker() { } } -func (c *Controller) UpdateStatus(backup *v1alpha1.CompactBackup, newState string) error { +func (c *Controller) UpdateStatus(backup *v1alpha1.CompactBackup, newState string, message ...string) error { ns := backup.GetNamespace() backupName := backup.GetName() // try best effort to guarantee backup is updated. @@ -117,6 +117,9 @@ func (c *Controller) UpdateStatus(backup *v1alpha1.CompactBackup, newState strin } if backup.Status.State != newState { backup.Status.State = newState + if len(message) > 0 { + backup.Status.Message = message[0] + } _, updateErr := c.cli.PingcapV1alpha1().CompactBackups(ns).Update(context.TODO(), backup, metav1.UpdateOptions{}) if updateErr == nil { klog.Infof("Backup: [%s/%s] updated successfully", ns, backupName) @@ -250,18 +253,19 @@ func (c *Controller) sync(key string) (err error) { return nil } - c.UpdateStatus(backup, "Preparing") + c.UpdateStatus(backup, string(v1alpha1.BackupPrepare)) err = c.doCompact(backup.DeepCopy()) - var newState string + var newState, message string if err != nil { - newState = "Failed:" + err.Error() + newState = string(v1alpha1.BackupFailed) + message = err.Error() klog.Errorf("Backup: [%s/%s] sync failed, error: %v", ns, name, err) } else { - newState = "Running" + newState = string(v1alpha1.BackupRunning) } - c.UpdateStatus(backup, newState) + c.UpdateStatus(backup, newState, message) return err } From d3537b434113091206e1ab57a859116fea24885f Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Thu, 12 Dec 2024 07:42:24 +0100 Subject: [PATCH 25/49] fix --- cmd/backup-manager/app/compact/status_updater.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmd/backup-manager/app/compact/status_updater.go b/cmd/backup-manager/app/compact/status_updater.go index 0dd5d5f9d5..cb096332f8 100644 --- a/cmd/backup-manager/app/compact/status_updater.go +++ b/cmd/backup-manager/app/compact/status_updater.go @@ -56,13 +56,11 @@ func (r *CompactStatusUpdater) Event(compact *v1alpha1.CompactBackup, ty, reason func (r *CompactStatusUpdater) UpdateStatus(compact *v1alpha1.CompactBackup, newState string, progress string, message string) error { ns := compact.GetNamespace() backupName := compact.GetName() - key := fmt.Sprintf("%s/%s", ns, backupName) // Unique key for the backup now := time.Now() updateProgress := true if progress != "" { if now.Sub(r.progressLastUpdate) < progressDebounceDuration { - klog.Infof("Skipping progress update for [%s] due to debounce interval", key) updateProgress = false } } From cde1ef6cb2dc391bb289fd216af1cc7643ff2cca Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Thu, 12 Dec 2024 07:50:08 +0100 Subject: [PATCH 26/49] lint --- cmd/backup-manager/app/compact/status_updater.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/backup-manager/app/compact/status_updater.go b/cmd/backup-manager/app/compact/status_updater.go index cb096332f8..d3e9a1c286 100644 --- a/cmd/backup-manager/app/compact/status_updater.go +++ b/cmd/backup-manager/app/compact/status_updater.go @@ -76,7 +76,7 @@ func (r *CompactStatusUpdater) UpdateStatus(compact *v1alpha1.CompactBackup, new } updated := false - if newState !="" && compact.Status.State != newState { + if newState != "" && compact.Status.State != newState { compact.Status.State = newState updated = true updateProgress = true From 2ebd69e11841b437c94c9a37733071ae91c00c45 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Thu, 12 Dec 2024 09:01:04 +0100 Subject: [PATCH 27/49] add test --- tests/e2e/br/br.go | 107 ++++++++++++++++++++++++++++++ tests/e2e/br/framework/br/data.go | 28 ++++++++ tests/e2e/br/framework/br/wait.go | 23 +++++++ 3 files changed, 158 insertions(+) diff --git a/tests/e2e/br/br.go b/tests/e2e/br/br.go index 8308f11bd5..dbda6c09af 100644 --- a/tests/e2e/br/br.go +++ b/tests/e2e/br/br.go @@ -1106,6 +1106,80 @@ var _ = ginkgo.Describe("Backup and Restore", func() { framework.ExpectEqual(cleaned, true, "storage should be cleaned") }) }) + + + ginkgo.Context("Compact backup Test", func() { + ginkgo.It("test normal function", func() { + backupVersion := utilimage.TiDBLatest + enableTLS := false + skipCA := false + ns := f.Namespace.Name + dbName := "e2etest" + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + ginkgo.By("Create log-backup.enable TiDB cluster for pitr-master") + masterClusterName := "pitr-master" + err := createLogBackupEnableTidbCluster(f, masterClusterName, backupVersion, enableTLS, skipCA) + framework.ExpectNoError(err) + ginkgo.By("Wait for pitr-master TiDB cluster ready") + err = utiltidbcluster.WaitForTCConditionReady(f.ExtClient, ns, masterClusterName, tidbReadyTimeout, 0) + framework.ExpectNoError(err) + + ginkgo.By("Create RBAC for backup") + err = createRBAC(f) + framework.ExpectNoError(err) + + logBackupName := "log-backup" + typ := strings.ToLower(typeBR) + ginkgo.By("Start log backup") + logBackup, err := createBackupAndWaitForComplete(f, logBackupName, masterClusterName, typ, func(backup *v1alpha1.Backup) { + backup.Spec.CleanPolicy = v1alpha1.CleanPolicyTypeDelete + backup.Spec.Mode = v1alpha1.BackupModeLog + }) + framework.ExpectNoError(err) + framework.ExpectNotEqual(logBackup.Status.CommitTs, "") + + fullBackupName := "full-backup" + ginkgo.By("Start full backup") + fullBackup, err := createBackupAndWaitForComplete(f, fullBackupName, masterClusterName, typ, func(backup *v1alpha1.Backup) { + backup.Spec.CleanPolicy = v1alpha1.CleanPolicyTypeDelete + }) + framework.ExpectNoError(err) + framework.ExpectNotEqual(fullBackup.Status.CommitTs, "") + + ginkgo.By("Forward master TiDB cluster service") + masterHost, err := portforward.ForwardOnePort(ctx, f.PortForwarder, ns, getTiDBServiceResourceName(masterClusterName), int(v1alpha1.DefaultTiDBServerPort)) + framework.ExpectNoError(err) + err = initDatabase(masterHost, dbName) + framework.ExpectNoError(err) + + ginkgo.By("Write data into master TiDB cluster") + masterDSN := getDefaultDSN(masterHost, dbName) + err = blockwriter.New().Write(context.Background(), masterDSN) + framework.ExpectNoError(err) + + ginkgo.By("Forward master PD service") + masterPDHost, err := portforward.ForwardOnePort(ctx, f.PortForwarder, ns, getPDServiceResourceName(masterClusterName), int(v1alpha1.DefaultPDClientPort)) + framework.ExpectNoError(err) + ginkgo.By("Wait log backup reach current ts") + currentTS := strconv.FormatUint(config.GoTimeToTS(time.Now()), 10) + err = brutil.WaitForLogBackupReachTS(logBackupName, masterPDHost, currentTS, logbackupCatchUpTimeout) + framework.ExpectNoError(err) + + ginkgo.By("wait log backup progress reach current ts") + err = brutil.WaitForLogBackupProgressReachTS(f.ExtClient, ns, logBackupName, currentTS, logbackupCatchUpTimeout) + framework.ExpectNoError(err) + + compactName := "compact-backup" + ginkgo.By("Start a compact backup") + _,err = createCompactBackupAndWaitForComplete(f, compactName, masterClusterName, func(compact *v1alpha1.CompactBackup) { + compact.Spec.StartTs = fullBackup.Status.CommitTs + compact.Spec.EndTs = currentTS + }) + framework.ExpectNoError(err) + }) + }) }) func getTiDBServiceResourceName(tcName string) string { @@ -1620,3 +1694,36 @@ func getTableList(db *sql.DB) ([]string, error) { } return tables, nil } + +func createCompactBackupAndWaitForComplete(f *e2eframework.Framework, name, tcName string, configure func(*v1alpha1.CompactBackup)) (*v1alpha1.CompactBackup, error) { + ns := f.Namespace.Name + // secret to visit tidb cluster + s := brutil.GetSecret(ns, name, "") + // Check if the secret already exists + if _, err := f.ClientSet.CoreV1().Secrets(ns).Get(context.TODO(), name, metav1.GetOptions{}); err != nil { + if apierrors.IsNotFound(err) { + if _, err := f.ClientSet.CoreV1().Secrets(ns).Create(context.TODO(), s, metav1.CreateOptions{}); err != nil { + return nil, err + } + } else { + return nil, err + } + } + + backupFolder := time.Now().Format(time.RFC3339) + cfg := f.Storage.Config(ns, backupFolder) + compact := brutil.GetCompactBackup(ns, name, tcName, cfg) + + if configure != nil { + configure(compact) + } + + if _, err := f.ExtClient.PingcapV1alpha1().CompactBackups(ns).Create(context.TODO(), compact, metav1.CreateOptions{}); err != nil { + return nil, err + } + + if err := brutil.WaitForCompactComplete(f.ExtClient, ns, name, backupCompleteTimeout); err != nil { + return compact, err + } + return f.ExtClient.PingcapV1alpha1().CompactBackups(ns).Get(context.TODO(), name, metav1.GetOptions{}) +} diff --git a/tests/e2e/br/framework/br/data.go b/tests/e2e/br/framework/br/data.go index 92315fb4f0..96b78698f9 100644 --- a/tests/e2e/br/framework/br/data.go +++ b/tests/e2e/br/framework/br/data.go @@ -178,3 +178,31 @@ func GetRestore(ns, name, tcName, typ string, s3Config *v1alpha1.S3StorageProvid } return restore } + +func GetCompactBackup(ns, name, tcName string, s3Config *v1alpha1.S3StorageProvider) *v1alpha1.CompactBackup { + sendCredToTikv := true + compact := &v1alpha1.CompactBackup{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + Spec: v1alpha1.CompactSpec{ + StorageProvider: v1alpha1.StorageProvider{ + S3: s3Config, + }, + From: &v1alpha1.TiDBAccessConfig{ + Host: controller.TiDBMemberName(tcName), + SecretName: name, + Port: v1alpha1.DefaultTiDBServerPort, + User: "root", + }, + BR: &v1alpha1.BRConfig{ + Cluster: tcName, + ClusterNamespace: ns, + SendCredToTikv: &sendCredToTikv, + }, + }, + + } + return compact +} diff --git a/tests/e2e/br/framework/br/wait.go b/tests/e2e/br/framework/br/wait.go index 72e510c62b..df46ab7e36 100644 --- a/tests/e2e/br/framework/br/wait.go +++ b/tests/e2e/br/framework/br/wait.go @@ -319,3 +319,26 @@ func WaitBackupPodOnPhase(f *framework.Framework, backup *v1alpha1.Backup, phase } return nil } + +func WaitForCompactComplete(c versioned.Interface, ns, name string, timeout time.Duration) error { + if err := wait.PollImmediate(poll, timeout, func() (bool, error) { + cpbk, err := c.PingcapV1alpha1().CompactBackups(ns).Get(context.TODO(), name, metav1.GetOptions{}) + if err != nil { + return false, err + } + + switch cpbk.Status.State{ + case string(v1alpha1.BackupComplete): + return true,nil + case string(v1alpha1.BackupCleanFailed): + return false,fmt.Errorf("Compact failed: %s", cpbk.Status.Message) + default: + //do nothing + } + + return false, nil + }); err != nil { + return fmt.Errorf("can't wait for backup complete: %v", err) + } + return nil +} From 67142e8ac1c623c58f59cd7bc6e9cf036c2eab51 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Thu, 12 Dec 2024 09:57:55 +0100 Subject: [PATCH 28/49] fix test --- pkg/controller/compactbackup/compact_backup_controller.go | 2 +- tests/e2e/br/br.go | 4 ++-- tests/e2e/br/framework/br/data.go | 1 - tests/e2e/br/framework/br/wait.go | 8 +++++--- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index 9e363e598c..d98ba4b986 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -416,7 +416,7 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job Args: args, Env: envVars, VolumeMounts: volumeMounts, - ImagePullPolicy: corev1.PullAlways, + ImagePullPolicy: corev1.PullIfNotPresent, }, }, RestartPolicy: corev1.RestartPolicyNever, diff --git a/tests/e2e/br/br.go b/tests/e2e/br/br.go index dbda6c09af..0083e52fa4 100644 --- a/tests/e2e/br/br.go +++ b/tests/e2e/br/br.go @@ -1107,7 +1107,6 @@ var _ = ginkgo.Describe("Backup and Restore", func() { }) }) - ginkgo.Context("Compact backup Test", func() { ginkgo.It("test normal function", func() { backupVersion := utilimage.TiDBLatest @@ -1173,9 +1172,10 @@ var _ = ginkgo.Describe("Backup and Restore", func() { compactName := "compact-backup" ginkgo.By("Start a compact backup") - _,err = createCompactBackupAndWaitForComplete(f, compactName, masterClusterName, func(compact *v1alpha1.CompactBackup) { + _, err = createCompactBackupAndWaitForComplete(f, compactName, masterClusterName, func(compact *v1alpha1.CompactBackup) { compact.Spec.StartTs = fullBackup.Status.CommitTs compact.Spec.EndTs = currentTS + compact.Spec.S3 = logBackup.Spec.S3 }) framework.ExpectNoError(err) }) diff --git a/tests/e2e/br/framework/br/data.go b/tests/e2e/br/framework/br/data.go index 96b78698f9..dab5c5ac57 100644 --- a/tests/e2e/br/framework/br/data.go +++ b/tests/e2e/br/framework/br/data.go @@ -202,7 +202,6 @@ func GetCompactBackup(ns, name, tcName string, s3Config *v1alpha1.S3StorageProvi SendCredToTikv: &sendCredToTikv, }, }, - } return compact } diff --git a/tests/e2e/br/framework/br/wait.go b/tests/e2e/br/framework/br/wait.go index df46ab7e36..399a95f670 100644 --- a/tests/e2e/br/framework/br/wait.go +++ b/tests/e2e/br/framework/br/wait.go @@ -26,6 +26,7 @@ import ( "github.com/pingcap/tidb-operator/pkg/client/clientset/versioned" "github.com/pingcap/tidb-operator/pkg/pdapi" "github.com/pingcap/tidb-operator/tests/e2e/br/framework" + "github.com/pingcap/tidb-operator/tests/third_party/k8s/log" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -327,12 +328,13 @@ func WaitForCompactComplete(c versioned.Interface, ns, name string, timeout time return false, err } - switch cpbk.Status.State{ + switch cpbk.Status.State { case string(v1alpha1.BackupComplete): - return true,nil + return true, nil case string(v1alpha1.BackupCleanFailed): - return false,fmt.Errorf("Compact failed: %s", cpbk.Status.Message) + return false, fmt.Errorf("Compact failed: %s", cpbk.Status.Message) default: + log.Logf("the current status is: %s", cpbk.Status.State) //do nothing } From 6fc663f5b54be5ef7506d7a2ea0f1d42b0f46970 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Thu, 12 Dec 2024 10:48:16 +0100 Subject: [PATCH 29/49] report status when fail --- cmd/backup-manager/app/compact/manager.go | 2 +- manifests/backup/backup-rbac.yaml | 2 +- tests/e2e/br/framework/br/data.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/backup-manager/app/compact/manager.go b/cmd/backup-manager/app/compact/manager.go index 28ba18dcf7..fe8d7ea3cc 100644 --- a/cmd/backup-manager/app/compact/manager.go +++ b/cmd/backup-manager/app/compact/manager.go @@ -88,6 +88,7 @@ func (cm *Manager) ProcessCompact() error { defer cancel() compact, err := cm.resourceLister.CompactBackups(cm.options.Namespace).Get(cm.options.ResourceName) + defer func() { cm.statusUpdater.OnFinish(ctx, err, cm.compact) }() if err != nil { return errors.New("backup not found") } @@ -133,7 +134,6 @@ func (cm *Manager) base64ifyCmd(ctx context.Context) (*exec.Cmd, error) { func (cm *Manager) runCompaction(ctx context.Context, base64Storage string) (err error) { cmd := cm.compactCmd(ctx, base64Storage) - defer func() { cm.statusUpdater.OnFinish(ctx, err, cm.compact) }() logs, err := cmd.StderrPipe() if err != nil { diff --git a/manifests/backup/backup-rbac.yaml b/manifests/backup/backup-rbac.yaml index 9b59576c63..9790aea290 100644 --- a/manifests/backup/backup-rbac.yaml +++ b/manifests/backup/backup-rbac.yaml @@ -10,7 +10,7 @@ rules: resources: ["events"] verbs: ["*"] - apiGroups: ["pingcap.com"] - resources: ["backups", "restores"] + resources: ["backups", "restores","compactbackups"] verbs: ["get", "watch", "list", "update"] --- diff --git a/tests/e2e/br/framework/br/data.go b/tests/e2e/br/framework/br/data.go index dab5c5ac57..4e68c99b8c 100644 --- a/tests/e2e/br/framework/br/data.go +++ b/tests/e2e/br/framework/br/data.go @@ -47,7 +47,7 @@ func GetRole(ns string) *rbacv1.Role { }, { APIGroups: []string{"pingcap.com"}, - Resources: []string{"backups", "restores"}, + Resources: []string{"backups", "restores", "compactbackups"}, Verbs: []string{"get", "watch", "list", "update"}, }, }, From da1f000f73c406a0f5c775d8fab61d23a35986f4 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Thu, 12 Dec 2024 11:01:33 +0100 Subject: [PATCH 30/49] report progress in e2e test --- tests/e2e/br/framework/br/wait.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/br/framework/br/wait.go b/tests/e2e/br/framework/br/wait.go index 399a95f670..45dcf0d4c6 100644 --- a/tests/e2e/br/framework/br/wait.go +++ b/tests/e2e/br/framework/br/wait.go @@ -334,7 +334,7 @@ func WaitForCompactComplete(c versioned.Interface, ns, name string, timeout time case string(v1alpha1.BackupCleanFailed): return false, fmt.Errorf("Compact failed: %s", cpbk.Status.Message) default: - log.Logf("the current status is: %s", cpbk.Status.State) + log.Logf("the current status is: %s %s", cpbk.Status.State, cpbk.Status.Progress) //do nothing } From ab3419ad4ec21fb4c3783f9fd4cdeb9e81bd9f7a Mon Sep 17 00:00:00 2001 From: ris <79858083+RidRisR@users.noreply.github.com> Date: Tue, 17 Dec 2024 15:27:34 +0800 Subject: [PATCH 31/49] Update manifests/backup/backup-rbac.yaml Co-authored-by: WangLe1321 --- manifests/backup/backup-rbac.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manifests/backup/backup-rbac.yaml b/manifests/backup/backup-rbac.yaml index 9790aea290..efd7722a0d 100644 --- a/manifests/backup/backup-rbac.yaml +++ b/manifests/backup/backup-rbac.yaml @@ -10,7 +10,7 @@ rules: resources: ["events"] verbs: ["*"] - apiGroups: ["pingcap.com"] - resources: ["backups", "restores","compactbackups"] + resources: ["backups", "restores", "compactbackups"] verbs: ["get", "watch", "list", "update"] --- From 975fec26f3682fc27a22368c6355ade19ab9be94 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Tue, 17 Dec 2024 08:33:48 +0100 Subject: [PATCH 32/49] mod types --- cmd/backup-manager/app/compact/manager.go | 15 +++++++-------- pkg/apis/pingcap/v1alpha1/types.go | 15 --------------- 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/cmd/backup-manager/app/compact/manager.go b/cmd/backup-manager/app/compact/manager.go index fe8d7ea3cc..aab8e0dd90 100644 --- a/cmd/backup-manager/app/compact/manager.go +++ b/cmd/backup-manager/app/compact/manager.go @@ -33,6 +33,11 @@ import ( "k8s.io/klog/v2" ) +const ( + messageCompactionDone = "Finishing compaction." + messageCompactAborted = "Compaction aborted." +) + type Progress struct { MetaCompleted uint64 `json:"meta_completed"` MetaTotal uint64 `json:"meta_total"` @@ -135,7 +140,7 @@ func (cm *Manager) base64ifyCmd(ctx context.Context) (*exec.Cmd, error) { func (cm *Manager) runCompaction(ctx context.Context, base64Storage string) (err error) { cmd := cm.compactCmd(ctx, base64Storage) - logs, err := cmd.StderrPipe() + tikvLog, err := cmd.StderrPipe() if err != nil { return errors.Annotate(err, "failed to create stderr pipe for compact") } @@ -144,7 +149,7 @@ func (cm *Manager) runCompaction(ctx context.Context, base64Storage string) (err } cm.statusUpdater.OnStart(ctx, cm.compact) - err = cm.processCompactionLogs(ctx, io.TeeReader(logs, os.Stdout)) + err = cm.processCompactionLogs(ctx, io.TeeReader(tikvLog, os.Stdout)) if err != nil { return err } @@ -201,11 +206,6 @@ func (cm *Manager) processCompactionLogs(ctx context.Context, logStream io.Reade } func (cm *Manager) processLogLine(ctx context.Context, l logLine) error { - const ( - messageCompactionDone = "Finishing compaction." - messageCompactAborted = "Compaction aborted." - ) - switch l.Message { case messageCompactionDone: var prog Progress @@ -223,7 +223,6 @@ func (cm *Manager) processLogLine(ctx context.Context, l logLine) error { } return errors.New(errContainer.Err) default: - klog.Infof("progress log: %s", l.Message) return nil } } diff --git a/pkg/apis/pingcap/v1alpha1/types.go b/pkg/apis/pingcap/v1alpha1/types.go index 042935592e..e08be21f19 100644 --- a/pkg/apis/pingcap/v1alpha1/types.go +++ b/pkg/apis/pingcap/v1alpha1/types.go @@ -3485,17 +3485,8 @@ type CompactSpec struct { // - BR_LOG_TO_TERM // +optional Env []corev1.EnvVar `json:"env,omitempty"` - // From is the tidb cluster that needs to backup. - From *TiDBAccessConfig `json:"from,omitempty"` - TikvGCLifeTime *string `json:"tikvGCLifeTime,omitempty"` // StorageProvider configures where and how backups should be stored. StorageProvider `json:",inline"` - // The storageClassName of the persistent volume for Backup data storage. - // Defaults to Kubernetes default storage class. - // +optional - StorageClassName *string `json:"storageClassName,omitempty"` - // StorageSize is the request storage size for backup job - StorageSize string `json:"storageSize,omitempty"` // StartTs is the start ts of the compact backup. // Format supports TSO or datetime, e.g. '400036290571534337', '2018-05-11 01:42:23'. StartTs string `json:"startTs,omitempty"` @@ -3504,12 +3495,9 @@ type CompactSpec struct { // Default is current timestamp. // +optional EndTs string `json:"endTs,omitempty"` - // ResumeGcSchedule indicates whether resume gc and pd scheduler for EBS volume snapshot backup - // +optional // Concurrency is the concurrency of compact backup job // +default=4 Concurrency int `json:"concurrency,omitempty"` - ResumeGcSchedule bool `json:"resumeGcSchedule,omitempty"` // Base tolerations of backup Pods, components may add more tolerations upon this respectively // +optional Tolerations []corev1.Toleration `json:"tolerations,omitempty"` @@ -3547,9 +3535,6 @@ type CompactSpec struct { // Additional volume mounts of component pod. // +optional AdditionalVolumeMounts []corev1.VolumeMount `json:"additionalVolumeMounts,omitempty"` - // VolumeBackupInitJobMaxActiveSeconds represents the deadline (in seconds) of the vbk init job - // +kubebuilder:default=600 - VolumeBackupInitJobMaxActiveSeconds int `json:"volumeBackupInitJobMaxActiveSeconds,omitempty"` } type CompactStatus struct { From 9d7fc30d6d69b4df3fb1753e439d39effd5fe405 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Tue, 17 Dec 2024 08:43:08 +0100 Subject: [PATCH 33/49] make generate --- docs/api-references/docs.md | 145 +----------------- manifests/crd.yaml | 28 ---- .../crd/v1/pingcap.com_compactbackups.yaml | 28 ---- .../pingcap/v1alpha1/openapi_generated.go | 43 +----- pkg/apis/pingcap/v1alpha1/types.go | 2 +- .../pingcap/v1alpha1/zz_generated.deepcopy.go | 15 -- 6 files changed, 5 insertions(+), 256 deletions(-) diff --git a/docs/api-references/docs.md b/docs/api-references/docs.md index e56d99ecd0..fe726a0266 100644 --- a/docs/api-references/docs.md +++ b/docs/api-references/docs.md @@ -5419,29 +5419,6 @@ Note that the following builtin env vars will be overwritten by values set here -from
- - -TiDBAccessConfig - - - - -

From is the tidb cluster that needs to backup.

- - - - -tikvGCLifeTime
- -string - - - - - - - StorageProvider
@@ -5458,30 +5435,6 @@ StorageProvider -storageClassName
- -string - - - -(Optional) -

The storageClassName of the persistent volume for Backup data storage. -Defaults to Kubernetes default storage class.

- - - - -storageSize
- -string - - - -

StorageSize is the request storage size for backup job

- - - - startTs
string @@ -5514,19 +5467,7 @@ int -(Optional) -

ResumeGcSchedule indicates whether resume gc and pd scheduler for EBS volume snapshot backup -Concurrency is the concurrency of compact backup job

- - - - -resumeGcSchedule
- -bool - - - +

Concurrency is the concurrency of compact backup job

@@ -5686,17 +5627,6 @@ BackoffRetryPolicy

Additional volume mounts of component pod.

- - -volumeBackupInitJobMaxActiveSeconds
- -int - - - -

VolumeBackupInitJobMaxActiveSeconds represents the deadline (in seconds) of the vbk init job

- - @@ -5774,29 +5704,6 @@ Note that the following builtin env vars will be overwritten by values set here -from
- -
-TiDBAccessConfig - - - - -

From is the tidb cluster that needs to backup.

- - - - -tikvGCLifeTime
- -string - - - - - - - StorageProvider
@@ -5813,30 +5720,6 @@ StorageProvider -storageClassName
- -string - - - -(Optional) -

The storageClassName of the persistent volume for Backup data storage. -Defaults to Kubernetes default storage class.

- - - - -storageSize
- -string - - - -

StorageSize is the request storage size for backup job

- - - - startTs
string @@ -5869,19 +5752,7 @@ int -(Optional) -

ResumeGcSchedule indicates whether resume gc and pd scheduler for EBS volume snapshot backup -Concurrency is the concurrency of compact backup job

- - - - -resumeGcSchedule
- -bool - - - +

Concurrency is the concurrency of compact backup job

@@ -6041,17 +5912,6 @@ BackoffRetryPolicy

Additional volume mounts of component pod.

- - -volumeBackupInitJobMaxActiveSeconds
- -int - - - -

VolumeBackupInitJobMaxActiveSeconds represents the deadline (in seconds) of the vbk init job

- -

CompactStatus

@@ -17517,7 +17377,6 @@ map[github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.StorageVolumeName

(Appears on: BackupSpec, -CompactSpec, RestoreSpec)

diff --git a/manifests/crd.yaml b/manifests/crd.yaml index 5b2865926a..883a6351fd 100644 --- a/manifests/crd.yaml +++ b/manifests/crd.yaml @@ -8320,23 +8320,6 @@ spec: - name type: object type: array - from: - properties: - host: - type: string - port: - format: int32 - type: integer - secretName: - type: string - tlsClientSecretName: - type: string - user: - type: string - required: - - host - - secretName - type: object gcs: properties: bucket: @@ -9200,8 +9183,6 @@ spec: x-kubernetes-int-or-string: true type: object type: object - resumeGcSchedule: - type: boolean s3: properties: acl: @@ -9235,12 +9216,6 @@ spec: type: string startTs: type: string - storageClassName: - type: string - storageSize: - type: string - tikvGCLifeTime: - type: string tolerations: items: properties: @@ -9261,9 +9236,6 @@ spec: type: string useKMS: type: boolean - volumeBackupInitJobMaxActiveSeconds: - default: 600 - type: integer type: object status: properties: diff --git a/manifests/crd/v1/pingcap.com_compactbackups.yaml b/manifests/crd/v1/pingcap.com_compactbackups.yaml index d852d4659f..f280e97dd9 100644 --- a/manifests/crd/v1/pingcap.com_compactbackups.yaml +++ b/manifests/crd/v1/pingcap.com_compactbackups.yaml @@ -1270,23 +1270,6 @@ spec: - name type: object type: array - from: - properties: - host: - type: string - port: - format: int32 - type: integer - secretName: - type: string - tlsClientSecretName: - type: string - user: - type: string - required: - - host - - secretName - type: object gcs: properties: bucket: @@ -2150,8 +2133,6 @@ spec: x-kubernetes-int-or-string: true type: object type: object - resumeGcSchedule: - type: boolean s3: properties: acl: @@ -2185,12 +2166,6 @@ spec: type: string startTs: type: string - storageClassName: - type: string - storageSize: - type: string - tikvGCLifeTime: - type: string tolerations: items: properties: @@ -2211,9 +2186,6 @@ spec: type: string useKMS: type: boolean - volumeBackupInitJobMaxActiveSeconds: - default: 600 - type: integer type: object status: properties: diff --git a/pkg/apis/pingcap/v1alpha1/openapi_generated.go b/pkg/apis/pingcap/v1alpha1/openapi_generated.go index 030f7dc7ce..14d4952415 100644 --- a/pkg/apis/pingcap/v1alpha1/openapi_generated.go +++ b/pkg/apis/pingcap/v1alpha1/openapi_generated.go @@ -1729,18 +1729,6 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) }, }, }, - "from": { - SchemaProps: spec.SchemaProps{ - Description: "From is the tidb cluster that needs to backup.", - Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiDBAccessConfig"), - }, - }, - "tikvGCLifeTime": { - SchemaProps: spec.SchemaProps{ - Type: []string{"string"}, - Format: "", - }, - }, "s3": { SchemaProps: spec.SchemaProps{ Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.S3StorageProvider"), @@ -1761,20 +1749,6 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.LocalStorageProvider"), }, }, - "storageClassName": { - SchemaProps: spec.SchemaProps{ - Description: "The storageClassName of the persistent volume for Backup data storage. Defaults to Kubernetes default storage class.", - Type: []string{"string"}, - Format: "", - }, - }, - "storageSize": { - SchemaProps: spec.SchemaProps{ - Description: "StorageSize is the request storage size for backup job", - Type: []string{"string"}, - Format: "", - }, - }, "startTs": { SchemaProps: spec.SchemaProps{ Description: "StartTs is the start ts of the compact backup. Format supports TSO or datetime, e.g. '400036290571534337', '2018-05-11 01:42:23'.", @@ -1791,18 +1765,12 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) }, "concurrency": { SchemaProps: spec.SchemaProps{ - Description: "ResumeGcSchedule indicates whether resume gc and pd scheduler for EBS volume snapshot backup Concurrency is the concurrency of compact backup job", + Description: "Concurrency is the concurrency of compact backup job", Default: 4, Type: []string{"integer"}, Format: "int32", }, }, - "resumeGcSchedule": { - SchemaProps: spec.SchemaProps{ - Type: []string{"boolean"}, - Format: "", - }, - }, "tolerations": { SchemaProps: spec.SchemaProps{ Description: "Base tolerations of backup Pods, components may add more tolerations upon this respectively", @@ -1912,18 +1880,11 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) }, }, }, - "volumeBackupInitJobMaxActiveSeconds": { - SchemaProps: spec.SchemaProps{ - Description: "VolumeBackupInitJobMaxActiveSeconds represents the deadline (in seconds) of the vbk init job", - Type: []string{"integer"}, - Format: "int32", - }, - }, }, }, }, Dependencies: []string{ - "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.AzblobStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BRConfig", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BackoffRetryPolicy", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.GcsStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.LocalStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.S3StorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.TiDBAccessConfig", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume", "k8s.io/api/core/v1.VolumeMount"}, + "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.AzblobStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BRConfig", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BackoffRetryPolicy", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.GcsStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.LocalStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.S3StorageProvider", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume", "k8s.io/api/core/v1.VolumeMount"}, } } diff --git a/pkg/apis/pingcap/v1alpha1/types.go b/pkg/apis/pingcap/v1alpha1/types.go index e08be21f19..5a1fb5053f 100644 --- a/pkg/apis/pingcap/v1alpha1/types.go +++ b/pkg/apis/pingcap/v1alpha1/types.go @@ -3497,7 +3497,7 @@ type CompactSpec struct { EndTs string `json:"endTs,omitempty"` // Concurrency is the concurrency of compact backup job // +default=4 - Concurrency int `json:"concurrency,omitempty"` + Concurrency int `json:"concurrency,omitempty"` // Base tolerations of backup Pods, components may add more tolerations upon this respectively // +optional Tolerations []corev1.Toleration `json:"tolerations,omitempty"` diff --git a/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go index fbb1d8acf1..5653f71a37 100644 --- a/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go @@ -937,22 +937,7 @@ func (in *CompactSpec) DeepCopyInto(out *CompactSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - if in.From != nil { - in, out := &in.From, &out.From - *out = new(TiDBAccessConfig) - (*in).DeepCopyInto(*out) - } - if in.TikvGCLifeTime != nil { - in, out := &in.TikvGCLifeTime, &out.TikvGCLifeTime - *out = new(string) - **out = **in - } in.StorageProvider.DeepCopyInto(&out.StorageProvider) - if in.StorageClassName != nil { - in, out := &in.StorageClassName, &out.StorageClassName - *out = new(string) - **out = **in - } if in.Tolerations != nil { in, out := &in.Tolerations, &out.Tolerations *out = make([]v1.Toleration, len(*in)) From 6594acf3a9059770bacf53cb1fc0fa8ed78252e3 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Wed, 18 Dec 2024 08:02:34 +0100 Subject: [PATCH 34/49] handle error --- pkg/controller/compactbackup/compact_backup_controller.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index d98ba4b986..ed2fcf1a54 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -248,12 +248,16 @@ func (c *Controller) sync(key string) (err error) { return err } + if backup.Status.State == string(v1alpha1.BackupFailed) { + return nil + } + //Skip if backup.Status.State != "" { return nil } - c.UpdateStatus(backup, string(v1alpha1.BackupPrepare)) + c.UpdateStatus(backup, string(v1alpha1.BackupScheduled)) err = c.doCompact(backup.DeepCopy()) From 74d00156e02dbd8b045121e1caa675071acb0b98 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Mon, 23 Dec 2024 09:14:44 +0100 Subject: [PATCH 35/49] add necessary interface for retry --- pkg/apis/pingcap/v1alpha1/types.go | 5 ++ .../compactbackup/compact_backup_control.go | 41 +++++++++++++ .../compact_backup_controller.go | 57 ++++++++++++++++--- .../controller}/status_updater.go | 8 ++- 4 files changed, 102 insertions(+), 9 deletions(-) create mode 100644 pkg/controller/compactbackup/compact_backup_control.go rename {cmd/backup-manager/app/compact => pkg/controller}/status_updater.go (94%) diff --git a/pkg/apis/pingcap/v1alpha1/types.go b/pkg/apis/pingcap/v1alpha1/types.go index 5a1fb5053f..55de09367c 100644 --- a/pkg/apis/pingcap/v1alpha1/types.go +++ b/pkg/apis/pingcap/v1alpha1/types.go @@ -3538,9 +3538,14 @@ type CompactSpec struct { } type CompactStatus struct { + // State is the current state of the backup State string `json:"state,omitempty"` + // Progress is the progress of the backup Progress string `json:"progress,omitempty"` + // Message is the message of the backup Message string `json:"message,omitempty"` + // BackoffRetryStatus is status of the backoff retry, it will be used when backup pod or job exited unexpectedly + BackoffRetryStatus []BackoffRetryRecord `json:"backoffRetryStatus,omitempty"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/controller/compactbackup/compact_backup_control.go b/pkg/controller/compactbackup/compact_backup_control.go new file mode 100644 index 0000000000..eab668f25a --- /dev/null +++ b/pkg/controller/compactbackup/compact_backup_control.go @@ -0,0 +1,41 @@ +// Copyright 2024 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +package compact + +import ( + "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1" + "github.com/pingcap/tidb-operator/pkg/backup" + "github.com/pingcap/tidb-operator/pkg/client/clientset/versioned" + "github.com/pingcap/tidb-operator/pkg/controller" +) + +// ControlInterface implements the control logic for updating Compact Backup +// It is implemented as an interface to allow for extensions that provide different semantics. +// Currently, there is only one implementation. +type ControlInterface interface { + // UpdateStatus updates the status for a Compact Backup, include condition and status info + UpdateStatus(backup *v1alpha1.Backup, condition *v1alpha1.BackupCondition, newStatus *controller.BackupUpdateStatus) error +} + +type defaultCompactControl struct { + cli versioned.Interface + backupManager backup.BackupManager +} + +// NewDefaultCompactControl returns a new instance of the default implementation ControlInterface for Compact Backup +func NewDefaultCompactControl( + cli versioned.Interface, + deps *controller.Dependencies) ControlInterface { + +} \ No newline at end of file diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index ed2fcf1a54..4421ae39a2 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -238,7 +238,7 @@ func (c *Controller) sync(key string) (err error) { return err } klog.Infof("Backup: [%s/%s] start to sync", ns, name) - backup, err := c.deps.CompactBackupLister.CompactBackups(ns).Get(name) + compact, err := c.deps.CompactBackupLister.CompactBackups(ns).Get(name) if err != nil { if errors.IsNotFound(err) { klog.Infof("Backup has been deleted %v", key) @@ -248,18 +248,29 @@ func (c *Controller) sync(key string) (err error) { return err } - if backup.Status.State == string(v1alpha1.BackupFailed) { + if compact.Status.State == string(v1alpha1.BackupFailed) || compact.Status.State == string(v1alpha1.BackupScheduled) { return nil } - //Skip - if backup.Status.State != "" { - return nil + if compact.Status.State == string(v1alpha1.BackupRunning) { + jobFailed, reason, originalReason, err := c.detectBackupJobFailure(compact) + if err != nil { + klog.Errorf("Fail to detect backup %s/%s running status, error %v", ns, name, err) + return nil + } + + if jobFailed { + // retry backup after detect failure + if err := c.retryAfterFailureDetected(compact, reason, originalReason); err != nil { + klog.Errorf("Fail to restart snapshot backup %s/%s, error %v", ns, name, err) + } + return nil + } } - c.UpdateStatus(backup, string(v1alpha1.BackupScheduled)) + c.UpdateStatus(compact, string(v1alpha1.BackupScheduled)) - err = c.doCompact(backup.DeepCopy()) + err = c.doCompact(compact.DeepCopy()) var newState, message string if err != nil { @@ -269,7 +280,7 @@ func (c *Controller) sync(key string) (err error) { } else { newState = string(v1alpha1.BackupRunning) } - c.UpdateStatus(backup, newState, message) + c.UpdateStatus(compact, newState, message) return err } @@ -450,3 +461,33 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job return job, "", nil } + +func (c *Controller) detectBackupJobFailure(compact *v1alpha1.CompactBackup) ( + jobFailed bool, reason string, originalReason string, err error) { + var ( + ns = compact.GetNamespace() + name = compact.GetName() + ) + job, err := c.deps.JobLister.Jobs(ns).Get(name) + if err != nil && !errors.IsNotFound(err) { + klog.Errorf("Fail to get job %s for backup %s/%s, error %v ", name, ns, name, err) + return false, "", "", err + } + if job != nil { + for _, condition := range job.Status.Conditions { + if condition.Type == batchv1.JobFailed && condition.Status == corev1.ConditionTrue { + reason = fmt.Sprintf("Job %s has failed", name) + originalReason = condition.Reason + return true, reason, originalReason, nil + } + } + } + + klog.Infof("Detect backup %s/%s job failed, will retry, reason %s, original reason %s ", ns, name, reason, originalReason) + // record failure when detect failure + err = c.recordDetectedFailure(backup, reason, originalReason) + if err != nil { + klog.Errorf("failed to record detected failed %s for backup %s/%s", reason, ns, name) + } + return jobFailed, reason, originalReason, nil +} diff --git a/cmd/backup-manager/app/compact/status_updater.go b/pkg/controller/status_updater.go similarity index 94% rename from cmd/backup-manager/app/compact/status_updater.go rename to pkg/controller/status_updater.go index d3e9a1c286..322880ffe1 100644 --- a/cmd/backup-manager/app/compact/status_updater.go +++ b/pkg/controller/status_updater.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package compact +package controller import ( "context" @@ -34,6 +34,12 @@ const ( progressDebounceDuration = 3 * time.Second ) +type CompactStatusUpdaterInterface interface { + OnStart(ctx context.Context, compact *v1alpha1.CompactBackup) + OnProgress(ctx context.Context, p Progress, compact *v1alpha1.CompactBackup) + OnFinish(ctx context.Context, compact *v1alpha1.CompactBackup) +} + type CompactStatusUpdater struct { recorder record.EventRecorder lister listers.CompactBackupLister From 09873ae0fa40b4efcf6c4f94571706b013007856 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Mon, 23 Dec 2024 09:16:03 +0100 Subject: [PATCH 36/49] add control interface --- pkg/controller/compact_backup_control.go | 67 ++++++++++++++++++++++++ pkg/controller/dependences.go | 1 + 2 files changed, 68 insertions(+) create mode 100644 pkg/controller/compact_backup_control.go diff --git a/pkg/controller/compact_backup_control.go b/pkg/controller/compact_backup_control.go new file mode 100644 index 0000000000..2b79a7c701 --- /dev/null +++ b/pkg/controller/compact_backup_control.go @@ -0,0 +1,67 @@ +// Copyright 2019 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License. + +// Copyright 2019 PingCAP, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// See the License for the specific language governing permissions and +// limitations under the License.i + +package controller + +import ( + "context" + + "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1" + "github.com/pingcap/tidb-operator/pkg/client/clientset/versioned" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/record" +) + +// BackupControlInterface manages Backups used in BackupSchedule +type CompactBackupControlInterface interface { + CreateCompactBackup(compact *v1alpha1.CompactBackup) (*v1alpha1.CompactBackup, error) + DeleteCompactBackup(compact *v1alpha1.CompactBackup) error +} + +type realCompactControl struct { + cli versioned.Interface + recorder record.EventRecorder +} + +func NewRealCompactControl(cli versioned.Interface, recorder record.EventRecorder) CompactBackupControlInterface { + return &realCompactControl{ + cli: cli, + recorder: recorder, + } +} + +func (c *realCompactControl) CreateCompactBackup(compact *v1alpha1.CompactBackup) (*v1alpha1.CompactBackup, error) { + ns := compact.GetNamespace() + + return c.cli.PingcapV1alpha1().CompactBackups(ns).Create(context.TODO(), compact, metav1.CreateOptions{}) +} + +func (c *realCompactControl) DeleteCompactBackup(compact *v1alpha1.CompactBackup) error { + ns := compact.GetNamespace() + compactName := compact.GetName() + + return c.cli.PingcapV1alpha1().CompactBackups(ns).Delete(context.TODO(), compactName, metav1.DeleteOptions{}) +} \ No newline at end of file diff --git a/pkg/controller/dependences.go b/pkg/controller/dependences.go index 34878c0c44..52f8d50ce2 100644 --- a/pkg/controller/dependences.go +++ b/pkg/controller/dependences.go @@ -195,6 +195,7 @@ type Controls struct { ProxyControl TiProxyControlInterface TiDBControl TiDBControlInterface BackupControl BackupControlInterface + CompactControl CompactBackupControlInterface RestoreControl RestoreControlInterface SecretControl SecretControlInterface } From c9e78c834408c98a9c51a72ced1afed46b276884 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Mon, 23 Dec 2024 09:23:52 +0100 Subject: [PATCH 37/49] fix updater usage --- cmd/backup-manager/app/compact/manager.go | 18 ++++++------------ pkg/controller/status_updater.go | 13 ++++++++++--- tests/e2e/br/framework/br/data.go | 6 ------ 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/cmd/backup-manager/app/compact/manager.go b/cmd/backup-manager/app/compact/manager.go index aab8e0dd90..edd49e7752 100644 --- a/cmd/backup-manager/app/compact/manager.go +++ b/cmd/backup-manager/app/compact/manager.go @@ -29,6 +29,7 @@ import ( "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1" pkgutil "github.com/pingcap/tidb-operator/pkg/backup/util" listers "github.com/pingcap/tidb-operator/pkg/client/listers/pingcap/v1alpha1" + "github.com/pingcap/tidb-operator/pkg/controller" "github.com/pingcap/tidb-operator/pkg/util" "k8s.io/klog/v2" ) @@ -38,13 +39,6 @@ const ( messageCompactAborted = "Compaction aborted." ) -type Progress struct { - MetaCompleted uint64 `json:"meta_completed"` - MetaTotal uint64 `json:"meta_total"` - BytesToCompact uint64 `json:"bytes_to_compact"` - BytesCompacted uint64 `json:"bytes_compacted"` -} - // logLine is line of JSON log. // It just extracted the message from the JSON and keeps the origin json bytes. // So you may extract fields from it by `json.Unmarshal(l.Raw, ...)`. @@ -57,14 +51,14 @@ type logLine struct { type Manager struct { compact *v1alpha1.CompactBackup resourceLister listers.CompactBackupLister - statusUpdater *CompactStatusUpdater + statusUpdater controller.CompactStatusUpdaterInterface options options.CompactOpts } // NewManager return a Manager func NewManager( lister listers.CompactBackupLister, - statusUpdater *CompactStatusUpdater, + statusUpdater controller.CompactStatusUpdaterInterface, compactOpts options.CompactOpts) *Manager { compact, err := lister.CompactBackups(compactOpts.Namespace).Get(compactOpts.ResourceName) if err != nil { @@ -93,7 +87,7 @@ func (cm *Manager) ProcessCompact() error { defer cancel() compact, err := cm.resourceLister.CompactBackups(cm.options.Namespace).Get(cm.options.ResourceName) - defer func() { cm.statusUpdater.OnFinish(ctx, err, cm.compact) }() + defer func() { cm.statusUpdater.OnFinish(ctx, cm.compact, err) }() if err != nil { return errors.New("backup not found") } @@ -208,11 +202,11 @@ func (cm *Manager) processCompactionLogs(ctx context.Context, logStream io.Reade func (cm *Manager) processLogLine(ctx context.Context, l logLine) error { switch l.Message { case messageCompactionDone: - var prog Progress + var prog controller.Progress if err := json.Unmarshal(l.Raw, &prog); err != nil { return errors.Annotatef(err, "failed to decode progress message: %s", string(l.Raw)) } - cm.statusUpdater.OnProgress(ctx, prog, cm.compact) + cm.statusUpdater.OnProgress(ctx, cm.compact, prog) return nil case messageCompactAborted: errContainer := struct { diff --git a/pkg/controller/status_updater.go b/pkg/controller/status_updater.go index 322880ffe1..0efe59cb72 100644 --- a/pkg/controller/status_updater.go +++ b/pkg/controller/status_updater.go @@ -34,10 +34,17 @@ const ( progressDebounceDuration = 3 * time.Second ) +type Progress struct { + MetaCompleted uint64 `json:"meta_completed"` + MetaTotal uint64 `json:"meta_total"` + BytesToCompact uint64 `json:"bytes_to_compact"` + BytesCompacted uint64 `json:"bytes_compacted"` +} + type CompactStatusUpdaterInterface interface { OnStart(ctx context.Context, compact *v1alpha1.CompactBackup) - OnProgress(ctx context.Context, p Progress, compact *v1alpha1.CompactBackup) - OnFinish(ctx context.Context, compact *v1alpha1.CompactBackup) + OnProgress(ctx context.Context, compact *v1alpha1.CompactBackup, p Progress) + OnFinish(ctx context.Context, compact *v1alpha1.CompactBackup, err error) } type CompactStatusUpdater struct { @@ -123,7 +130,7 @@ func (r *CompactStatusUpdater) OnProgress(ctx context.Context, p Progress, compa r.UpdateStatus(compact, "", progress, "") } -func (r *CompactStatusUpdater) OnFinish(ctx context.Context, err error, compact *v1alpha1.CompactBackup) { +func (r *CompactStatusUpdater) OnFinish(ctx context.Context, compact *v1alpha1.CompactBackup, err error) { if err != nil { r.Event(compact, corev1.EventTypeWarning, "Failed", err.Error()) r.UpdateStatus(compact, string(v1alpha1.BackupFailed), "", err.Error()) diff --git a/tests/e2e/br/framework/br/data.go b/tests/e2e/br/framework/br/data.go index 4e68c99b8c..1b1ece903f 100644 --- a/tests/e2e/br/framework/br/data.go +++ b/tests/e2e/br/framework/br/data.go @@ -190,12 +190,6 @@ func GetCompactBackup(ns, name, tcName string, s3Config *v1alpha1.S3StorageProvi StorageProvider: v1alpha1.StorageProvider{ S3: s3Config, }, - From: &v1alpha1.TiDBAccessConfig{ - Host: controller.TiDBMemberName(tcName), - SecretName: name, - Port: v1alpha1.DefaultTiDBServerPort, - User: "root", - }, BR: &v1alpha1.BRConfig{ Cluster: tcName, ClusterNamespace: ns, From 13d51a105692ff74f27868829c82840c76ab4043 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Mon, 23 Dec 2024 09:27:40 +0100 Subject: [PATCH 38/49] status_updater formally done --- cmd/backup-manager/app/cmd/compact.go | 3 ++- .../{status_updater.go => compact_status_updater.go} | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) rename pkg/controller/{status_updater.go => compact_status_updater.go} (97%) diff --git a/cmd/backup-manager/app/cmd/compact.go b/cmd/backup-manager/app/cmd/compact.go index 5f8796ed75..f4fab17ed7 100644 --- a/cmd/backup-manager/app/cmd/compact.go +++ b/cmd/backup-manager/app/cmd/compact.go @@ -21,6 +21,7 @@ import ( "github.com/pingcap/tidb-operator/cmd/backup-manager/app/constants" "github.com/pingcap/tidb-operator/cmd/backup-manager/app/util" informers "github.com/pingcap/tidb-operator/pkg/client/informers/externalversions" + "github.com/pingcap/tidb-operator/pkg/controller" "github.com/spf13/cobra" "k8s.io/client-go/tools/cache" cmdutil "k8s.io/kubectl/pkg/cmd/util" @@ -54,7 +55,7 @@ func runCompact(compactOpts options.CompactOpts, kubecfg string) error { informerFactory := informers.NewSharedInformerFactoryWithOptions(cli, constants.ResyncDuration, options...) recorder := util.NewEventRecorder(kubeCli, "compact-manager") compactInformer := informerFactory.Pingcap().V1alpha1().CompactBackups() - statusUpdater := compact.NewCompactStatusUpdater(recorder, compactInformer.Lister(), cli) + statusUpdater := controller.NewCompactStatusUpdater(recorder, compactInformer.Lister(), cli) ctx, cancel := context.WithCancel(context.Background()) defer cancel() diff --git a/pkg/controller/status_updater.go b/pkg/controller/compact_status_updater.go similarity index 97% rename from pkg/controller/status_updater.go rename to pkg/controller/compact_status_updater.go index 0efe59cb72..823b889199 100644 --- a/pkg/controller/status_updater.go +++ b/pkg/controller/compact_status_updater.go @@ -124,7 +124,7 @@ func (r *CompactStatusUpdater) OnStart(ctx context.Context, compact *v1alpha1.Co r.Event(compact, corev1.EventTypeNormal, "Started", "The compaction process has started successfully.") } -func (r *CompactStatusUpdater) OnProgress(ctx context.Context, p Progress, compact *v1alpha1.CompactBackup) { +func (r *CompactStatusUpdater) OnProgress(ctx context.Context, compact *v1alpha1.CompactBackup, p Progress) { progress := fmt.Sprintf("[READ_META(%d/%d),COMPACT_WORK(%d/%d)]", p.MetaCompleted, p.MetaTotal, p.BytesCompacted, p.BytesToCompact) r.UpdateStatus(compact, "", progress, "") From 8fb8ae8700c3783635dae0bdeca12b27987b424c Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Mon, 23 Dec 2024 10:12:39 +0100 Subject: [PATCH 39/49] simplify codes --- pkg/controller/compact_status_updater.go | 14 +++++++ .../compactbackup/compact_backup_control.go | 41 ------------------- .../compact_backup_controller.go | 15 +++---- 3 files changed, 19 insertions(+), 51 deletions(-) delete mode 100644 pkg/controller/compactbackup/compact_backup_control.go diff --git a/pkg/controller/compact_status_updater.go b/pkg/controller/compact_status_updater.go index 823b889199..f06a9668ab 100644 --- a/pkg/controller/compact_status_updater.go +++ b/pkg/controller/compact_status_updater.go @@ -42,6 +42,8 @@ type Progress struct { } type CompactStatusUpdaterInterface interface { + OnSchedule(ctx context.Context, compact *v1alpha1.CompactBackup) + OnCreateJob(ctx context.Context, compact *v1alpha1.CompactBackup, err error) OnStart(ctx context.Context, compact *v1alpha1.CompactBackup) OnProgress(ctx context.Context, compact *v1alpha1.CompactBackup, p Progress) OnFinish(ctx context.Context, compact *v1alpha1.CompactBackup, err error) @@ -119,6 +121,18 @@ func (r *CompactStatusUpdater) UpdateStatus(compact *v1alpha1.CompactBackup, new return err } +func (r *CompactStatusUpdater) OnSchedule(ctx context.Context, compact *v1alpha1.CompactBackup) { + r.UpdateStatus(compact, string(v1alpha1.BackupScheduled), "", "") +} + +func (r *CompactStatusUpdater) OnCreateJob(ctx context.Context, compact *v1alpha1.CompactBackup, err error) { + if err != nil { + r.UpdateStatus(compact, string(v1alpha1.BackupFailed), "", err.Error()) + } else { + r.UpdateStatus(compact, string(v1alpha1.BackupPrepare), "", "") + } +} + func (r *CompactStatusUpdater) OnStart(ctx context.Context, compact *v1alpha1.CompactBackup) { r.UpdateStatus(compact, string(v1alpha1.BackupRunning), "", "") r.Event(compact, corev1.EventTypeNormal, "Started", "The compaction process has started successfully.") diff --git a/pkg/controller/compactbackup/compact_backup_control.go b/pkg/controller/compactbackup/compact_backup_control.go deleted file mode 100644 index eab668f25a..0000000000 --- a/pkg/controller/compactbackup/compact_backup_control.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2024 PingCAP, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// See the License for the specific language governing permissions and -// limitations under the License. - -package compact - -import ( - "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1" - "github.com/pingcap/tidb-operator/pkg/backup" - "github.com/pingcap/tidb-operator/pkg/client/clientset/versioned" - "github.com/pingcap/tidb-operator/pkg/controller" -) - -// ControlInterface implements the control logic for updating Compact Backup -// It is implemented as an interface to allow for extensions that provide different semantics. -// Currently, there is only one implementation. -type ControlInterface interface { - // UpdateStatus updates the status for a Compact Backup, include condition and status info - UpdateStatus(backup *v1alpha1.Backup, condition *v1alpha1.BackupCondition, newStatus *controller.BackupUpdateStatus) error -} - -type defaultCompactControl struct { - cli versioned.Interface - backupManager backup.BackupManager -} - -// NewDefaultCompactControl returns a new instance of the default implementation ControlInterface for Compact Backup -func NewDefaultCompactControl( - cli versioned.Interface, - deps *controller.Dependencies) ControlInterface { - -} \ No newline at end of file diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index 4421ae39a2..04f5852dae 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -47,6 +47,7 @@ type Controller struct { // backups that need to be synced. queue workqueue.RateLimitingInterface cli versioned.Interface + statusUpdater controller.CompactStatusUpdaterInterface } // NewController creates a backup controller. @@ -268,19 +269,12 @@ func (c *Controller) sync(key string) (err error) { } } - c.UpdateStatus(compact, string(v1alpha1.BackupScheduled)) + c.statusUpdater.OnSchedule(context.TODO(), compact) err = c.doCompact(compact.DeepCopy()) + klog.Errorf("Backup: [%s/%s] sync failed, error: %v", ns, name, err) - var newState, message string - if err != nil { - newState = string(v1alpha1.BackupFailed) - message = err.Error() - klog.Errorf("Backup: [%s/%s] sync failed, error: %v", ns, name, err) - } else { - newState = string(v1alpha1.BackupRunning) - } - c.UpdateStatus(compact, newState, message) + c.statusUpdater.OnCreateJob(context.TODO(), compact, err) return err } @@ -310,6 +304,7 @@ func (c *Controller) doCompact(backup *v1alpha1.CompactBackup) error { func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job, string, error) { ns := backup.GetNamespace() name := backup.GetName() + // Do we need a unique name for the job? jobName := backup.GetName() var ( From ed3e4ba9b05fee71f51390ef9264837ffcabfaa0 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Mon, 23 Dec 2024 11:29:30 +0100 Subject: [PATCH 40/49] caller side --- pkg/apis/pingcap/v1alpha1/types.go | 2 +- pkg/controller/compact_status_updater.go | 94 +++++++++++++++---- .../compact_backup_controller.go | 43 ++++++++- 3 files changed, 119 insertions(+), 20 deletions(-) diff --git a/pkg/apis/pingcap/v1alpha1/types.go b/pkg/apis/pingcap/v1alpha1/types.go index 55de09367c..2a264ea18b 100644 --- a/pkg/apis/pingcap/v1alpha1/types.go +++ b/pkg/apis/pingcap/v1alpha1/types.go @@ -3542,7 +3542,7 @@ type CompactStatus struct { State string `json:"state,omitempty"` // Progress is the progress of the backup Progress string `json:"progress,omitempty"` - // Message is the message of the backup + // Message is the error message of the backup Message string `json:"message,omitempty"` // BackoffRetryStatus is status of the backoff retry, it will be used when backup pod or job exited unexpectedly BackoffRetryStatus []BackoffRetryRecord `json:"backoffRetryStatus,omitempty"` diff --git a/pkg/controller/compact_status_updater.go b/pkg/controller/compact_status_updater.go index f06a9668ab..8576a883b8 100644 --- a/pkg/controller/compact_status_updater.go +++ b/pkg/controller/compact_status_updater.go @@ -34,19 +34,39 @@ const ( progressDebounceDuration = 3 * time.Second ) +type RetryStatus struct { + // RetryNum is the number of retry + RetryNum *int + // DetectFailedAt is the time when detect failure + DetectFailedAt *metav1.Time + // ExpectedRetryAt is the time we calculate and expect retry after it + ExpectedRetryAt *metav1.Time + // RealRetryAt is the time when the retry was actually initiated + RealRetryAt *metav1.Time + // Reason is the reason of retry + RetryReason *string + // OriginalReason is the original reason of backup job or pod failed + OriginalReason *string +} + type Progress struct { + // MetaCompleted is the number of meta files compacted MetaCompleted uint64 `json:"meta_completed"` + // MetaTotal is the total number of meta files MetaTotal uint64 `json:"meta_total"` + // BytesToCompact is the number of bytes to compact BytesToCompact uint64 `json:"bytes_to_compact"` + // BytesCompacted is the number of bytes compacted BytesCompacted uint64 `json:"bytes_compacted"` } type CompactStatusUpdaterInterface interface { - OnSchedule(ctx context.Context, compact *v1alpha1.CompactBackup) - OnCreateJob(ctx context.Context, compact *v1alpha1.CompactBackup, err error) - OnStart(ctx context.Context, compact *v1alpha1.CompactBackup) - OnProgress(ctx context.Context, compact *v1alpha1.CompactBackup, p Progress) - OnFinish(ctx context.Context, compact *v1alpha1.CompactBackup, err error) + OnSchedule(ctx context.Context, compact *v1alpha1.CompactBackup) error + OnCreateJob(ctx context.Context, compact *v1alpha1.CompactBackup, err error) error + OnStart(ctx context.Context, compact *v1alpha1.CompactBackup) error + OnProgress(ctx context.Context, compact *v1alpha1.CompactBackup, p Progress) error + OnFinish(ctx context.Context, compact *v1alpha1.CompactBackup, err error) error + OnRetriableFailed(ctx context.Context, compact *v1alpha1.CompactBackup, retry *RetryStatus) error } type CompactStatusUpdater struct { @@ -121,35 +141,75 @@ func (r *CompactStatusUpdater) UpdateStatus(compact *v1alpha1.CompactBackup, new return err } -func (r *CompactStatusUpdater) OnSchedule(ctx context.Context, compact *v1alpha1.CompactBackup) { - r.UpdateStatus(compact, string(v1alpha1.BackupScheduled), "", "") +func (r *CompactStatusUpdater) UpdateRetryStatus(compact *v1alpha1.CompactBackup, newState string, status *RetryStatus) error { + ns := compact.GetNamespace() + backupName := compact.GetName() + + // Update the status + err := retry.OnError(retry.DefaultRetry, func(e error) bool { return e != nil }, func() error { + // Always get the latest CompactBackup before updating + if updated, err := r.lister.CompactBackups(ns).Get(backupName); err == nil { + *compact = *(updated.DeepCopy()) + } else { + utilruntime.HandleError(fmt.Errorf("error getting updated backup %s/%s from lister: %v", ns, backupName, err)) + return err + } + + updated := false + if newState != "" && compact.Status.State != newState { + compact.Status.State = newState + updated = true + } + //TODO: (Ris) update the retry status here + + // Apply the update if any field changed + if updated { + _, updateErr := r.cli.PingcapV1alpha1().CompactBackups(ns).Update(context.TODO(), compact, metav1.UpdateOptions{}) + if updateErr == nil { + klog.Infof("Backup: [%s/%s] updated successfully", ns, backupName) + return nil + } + klog.Errorf("Failed to update backup [%s/%s], error: %v", ns, backupName, updateErr) + return updateErr + } + return nil + }) + return err +} + +func (r *CompactStatusUpdater) OnSchedule(ctx context.Context, compact *v1alpha1.CompactBackup) error { + return r.UpdateStatus(compact, string(v1alpha1.BackupScheduled), "", "") } -func (r *CompactStatusUpdater) OnCreateJob(ctx context.Context, compact *v1alpha1.CompactBackup, err error) { +func (r *CompactStatusUpdater) OnCreateJob(ctx context.Context, compact *v1alpha1.CompactBackup, err error) error { if err != nil { - r.UpdateStatus(compact, string(v1alpha1.BackupFailed), "", err.Error()) + return r.UpdateStatus(compact, string(v1alpha1.BackupFailed), "", err.Error()) } else { - r.UpdateStatus(compact, string(v1alpha1.BackupPrepare), "", "") + return r.UpdateStatus(compact, string(v1alpha1.BackupPrepare), "", "") } } -func (r *CompactStatusUpdater) OnStart(ctx context.Context, compact *v1alpha1.CompactBackup) { - r.UpdateStatus(compact, string(v1alpha1.BackupRunning), "", "") +func (r *CompactStatusUpdater) OnStart(ctx context.Context, compact *v1alpha1.CompactBackup) error { r.Event(compact, corev1.EventTypeNormal, "Started", "The compaction process has started successfully.") + return r.UpdateStatus(compact, string(v1alpha1.BackupRunning), "", "") } -func (r *CompactStatusUpdater) OnProgress(ctx context.Context, compact *v1alpha1.CompactBackup, p Progress) { +func (r *CompactStatusUpdater) OnProgress(ctx context.Context, compact *v1alpha1.CompactBackup, p Progress) error { progress := fmt.Sprintf("[READ_META(%d/%d),COMPACT_WORK(%d/%d)]", p.MetaCompleted, p.MetaTotal, p.BytesCompacted, p.BytesToCompact) - r.UpdateStatus(compact, "", progress, "") + return r.UpdateStatus(compact, "", progress, "") } -func (r *CompactStatusUpdater) OnFinish(ctx context.Context, compact *v1alpha1.CompactBackup, err error) { +func (r *CompactStatusUpdater) OnFinish(ctx context.Context, compact *v1alpha1.CompactBackup, err error) error { if err != nil { r.Event(compact, corev1.EventTypeWarning, "Failed", err.Error()) - r.UpdateStatus(compact, string(v1alpha1.BackupFailed), "", err.Error()) + return r.UpdateStatus(compact, string(v1alpha1.BackupFailed), "", err.Error()) } else { r.Event(compact, corev1.EventTypeNormal, "Finished", "The compaction process has finished successfully.") - r.UpdateStatus(compact, string(v1alpha1.BackupComplete), "", "") + return r.UpdateStatus(compact, string(v1alpha1.BackupComplete), "", "") } } + +func (r *CompactStatusUpdater) OnRetriableFailed(ctx context.Context, compact *v1alpha1.CompactBackup, retry *RetryStatus) error { + return r.UpdateRetryStatus(compact, string(v1alpha1.BackupRetryTheFailed), retry) +} diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index 04f5852dae..7181cca5c9 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -249,7 +249,13 @@ func (c *Controller) sync(key string) (err error) { return err } - if compact.Status.State == string(v1alpha1.BackupFailed) || compact.Status.State == string(v1alpha1.BackupScheduled) { + if compact.Status.State == string(v1alpha1.BackupFailed){ + klog.Errorf("Backup %s/%s is failed, skip", ns, name) + return nil + } + + if compact.Status.State == string(v1alpha1.BackupScheduled) { + klog.Infof("Backup %s/%s is scheduled, skip", ns, name) return nil } @@ -262,6 +268,7 @@ func (c *Controller) sync(key string) (err error) { if jobFailed { // retry backup after detect failure + //TODO: (Ris)impl this if err := c.retryAfterFailureDetected(compact, reason, originalReason); err != nil { klog.Errorf("Fail to restart snapshot backup %s/%s, error %v", ns, name, err) } @@ -457,6 +464,38 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job return job, "", nil } +func (c *Controller) recordDetectedFailure(compact *v1alpha1.CompactBackup, reason, originalReason string) error { + ns := compact.GetNamespace() + name := compact.GetName() + + retryNum := len(compact.Status.BackoffRetryStatus) + 1 + detectFailedAt := metav1.Now() + minDuration, err := time.ParseDuration(compact.Spec.BackoffRetryPolicy.MinRetryDuration) + if err != nil { + klog.Errorf("fail to parse minRetryDuration %s of compact backup %s/%s, %v", compact.Spec.BackoffRetryPolicy.MinRetryDuration, ns, name, err) + return err + } + duration := time.Duration(minDuration.Nanoseconds() << (retryNum - 1)) + expectedRetryAt := metav1.NewTime(detectFailedAt.Add(duration)) + + // update + newStatus := &controller.RetryStatus{ + RetryNum: &retryNum, + DetectFailedAt: &detectFailedAt, + ExpectedRetryAt: &expectedRetryAt, + RetryReason: &reason, + OriginalReason: &originalReason, + } + + klog.Infof("Record backup %s/%s retry status, %v", ns, name, newStatus) + if err := c.statusUpdater.OnRetriableFailed(context.TODO(), compact, newStatus); err != nil { + klog.Errorf("Fail to update the retry status of backup %s/%s, %v", ns, name, err) + return err + } + + return nil +} + func (c *Controller) detectBackupJobFailure(compact *v1alpha1.CompactBackup) ( jobFailed bool, reason string, originalReason string, err error) { var ( @@ -480,7 +519,7 @@ func (c *Controller) detectBackupJobFailure(compact *v1alpha1.CompactBackup) ( klog.Infof("Detect backup %s/%s job failed, will retry, reason %s, original reason %s ", ns, name, reason, originalReason) // record failure when detect failure - err = c.recordDetectedFailure(backup, reason, originalReason) + err = c.recordDetectedFailure(compact, reason, originalReason) if err != nil { klog.Errorf("failed to record detected failed %s for backup %s/%s", reason, ns, name) } From b6e88317cdfd7283e251054045f48d0d19cc60b7 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Wed, 25 Dec 2024 08:56:18 +0100 Subject: [PATCH 41/49] add retry impl --- pkg/controller/compact_status_updater.go | 117 ++++++------- .../compact_backup_controller.go | 154 ++++++++++++++++-- 2 files changed, 184 insertions(+), 87 deletions(-) diff --git a/pkg/controller/compact_status_updater.go b/pkg/controller/compact_status_updater.go index 8576a883b8..5496ae63cd 100644 --- a/pkg/controller/compact_status_updater.go +++ b/pkg/controller/compact_status_updater.go @@ -16,6 +16,7 @@ package controller import ( "context" "fmt" + "reflect" "time" "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1" @@ -34,21 +35,6 @@ const ( progressDebounceDuration = 3 * time.Second ) -type RetryStatus struct { - // RetryNum is the number of retry - RetryNum *int - // DetectFailedAt is the time when detect failure - DetectFailedAt *metav1.Time - // ExpectedRetryAt is the time we calculate and expect retry after it - ExpectedRetryAt *metav1.Time - // RealRetryAt is the time when the retry was actually initiated - RealRetryAt *metav1.Time - // Reason is the reason of retry - RetryReason *string - // OriginalReason is the original reason of backup job or pod failed - OriginalReason *string -} - type Progress struct { // MetaCompleted is the number of meta files compacted MetaCompleted uint64 `json:"meta_completed"` @@ -66,7 +52,7 @@ type CompactStatusUpdaterInterface interface { OnStart(ctx context.Context, compact *v1alpha1.CompactBackup) error OnProgress(ctx context.Context, compact *v1alpha1.CompactBackup, p Progress) error OnFinish(ctx context.Context, compact *v1alpha1.CompactBackup, err error) error - OnRetriableFailed(ctx context.Context, compact *v1alpha1.CompactBackup, retry *RetryStatus) error + OnRetriableFailed(ctx context.Context, compact *v1alpha1.CompactBackup, retry *v1alpha1.BackoffRetryRecord) error } type CompactStatusUpdater struct { @@ -88,15 +74,15 @@ func (r *CompactStatusUpdater) Event(compact *v1alpha1.CompactBackup, ty, reason r.recorder.Event(compact, ty, reason, msg) } -func (r *CompactStatusUpdater) UpdateStatus(compact *v1alpha1.CompactBackup, newState string, progress string, message string) error { +func (r *CompactStatusUpdater) UpdateStatus(compact *v1alpha1.CompactBackup, newStatus v1alpha1.CompactStatus) error { ns := compact.GetNamespace() backupName := compact.GetName() now := time.Now() - updateProgress := true - if progress != "" { + canUpdateProgress := true + if newStatus.Progress != "" { if now.Sub(r.progressLastUpdate) < progressDebounceDuration { - updateProgress = false + canUpdateProgress = false } } @@ -111,56 +97,24 @@ func (r *CompactStatusUpdater) UpdateStatus(compact *v1alpha1.CompactBackup, new } updated := false - if newState != "" && compact.Status.State != newState { - compact.Status.State = newState + if newStatus.State != "" && compact.Status.State != newStatus.State { + compact.Status.State = newStatus.State updated = true - updateProgress = true + canUpdateProgress = true } - if message != "" && compact.Status.Message != message { - compact.Status.Message = message + if newStatus.Message != "" && compact.Status.Message != newStatus.Message { + compact.Status.Message = newStatus.Message updated = true } - if updateProgress && progress != "" && compact.Status.Progress != progress { - compact.Status.Progress = progress + if canUpdateProgress && newStatus.Progress != "" && compact.Status.Progress != newStatus.Progress { + compact.Status.Progress = newStatus.Progress updated = true r.progressLastUpdate = now } - - // Apply the update if any field changed - if updated { - _, updateErr := r.cli.PingcapV1alpha1().CompactBackups(ns).Update(context.TODO(), compact, metav1.UpdateOptions{}) - if updateErr == nil { - klog.Infof("Backup: [%s/%s] updated successfully", ns, backupName) - return nil - } - klog.Errorf("Failed to update backup [%s/%s], error: %v", ns, backupName, updateErr) - return updateErr - } - return nil - }) - return err -} - -func (r *CompactStatusUpdater) UpdateRetryStatus(compact *v1alpha1.CompactBackup, newState string, status *RetryStatus) error { - ns := compact.GetNamespace() - backupName := compact.GetName() - - // Update the status - err := retry.OnError(retry.DefaultRetry, func(e error) bool { return e != nil }, func() error { - // Always get the latest CompactBackup before updating - if updated, err := r.lister.CompactBackups(ns).Get(backupName); err == nil { - *compact = *(updated.DeepCopy()) - } else { - utilruntime.HandleError(fmt.Errorf("error getting updated backup %s/%s from lister: %v", ns, backupName, err)) - return err - } - - updated := false - if newState != "" && compact.Status.State != newState { - compact.Status.State = newState + if newStatus.BackoffRetryStatus != nil && !reflect.DeepEqual(newStatus.BackoffRetryStatus, compact.Status.BackoffRetryStatus) { + compact.Status.BackoffRetryStatus = newStatus.BackoffRetryStatus updated = true } - //TODO: (Ris) update the retry status here // Apply the update if any field changed if updated { @@ -178,38 +132,61 @@ func (r *CompactStatusUpdater) UpdateRetryStatus(compact *v1alpha1.CompactBackup } func (r *CompactStatusUpdater) OnSchedule(ctx context.Context, compact *v1alpha1.CompactBackup) error { - return r.UpdateStatus(compact, string(v1alpha1.BackupScheduled), "", "") + newStatus := v1alpha1.CompactStatus{ + State: string(v1alpha1.BackupScheduled), + } + return r.UpdateStatus(compact, newStatus) } func (r *CompactStatusUpdater) OnCreateJob(ctx context.Context, compact *v1alpha1.CompactBackup, err error) error { + newStatus := v1alpha1.CompactStatus{ + } if err != nil { - return r.UpdateStatus(compact, string(v1alpha1.BackupFailed), "", err.Error()) + newStatus.State = string(v1alpha1.BackupFailed) + newStatus.Message = err.Error() } else { - return r.UpdateStatus(compact, string(v1alpha1.BackupPrepare), "", "") + newStatus.State = string(v1alpha1.BackupRunning) } + return r.UpdateStatus(compact, newStatus) } func (r *CompactStatusUpdater) OnStart(ctx context.Context, compact *v1alpha1.CompactBackup) error { r.Event(compact, corev1.EventTypeNormal, "Started", "The compaction process has started successfully.") - return r.UpdateStatus(compact, string(v1alpha1.BackupRunning), "", "") + + newStauts := v1alpha1.CompactStatus{ + State: string(v1alpha1.BackupRunning), + } + return r.UpdateStatus(compact, newStauts) } func (r *CompactStatusUpdater) OnProgress(ctx context.Context, compact *v1alpha1.CompactBackup, p Progress) error { progress := fmt.Sprintf("[READ_META(%d/%d),COMPACT_WORK(%d/%d)]", p.MetaCompleted, p.MetaTotal, p.BytesCompacted, p.BytesToCompact) - return r.UpdateStatus(compact, "", progress, "") + + newStatus := v1alpha1.CompactStatus{ + Progress: progress, + } + return r.UpdateStatus(compact, newStatus) } func (r *CompactStatusUpdater) OnFinish(ctx context.Context, compact *v1alpha1.CompactBackup, err error) error { + newStatus := v1alpha1.CompactStatus{ + } if err != nil { + newStatus.State = string(v1alpha1.BackupFailed) + newStatus.Message = err.Error() r.Event(compact, corev1.EventTypeWarning, "Failed", err.Error()) - return r.UpdateStatus(compact, string(v1alpha1.BackupFailed), "", err.Error()) } else { + newStatus.State = string(v1alpha1.BackupComplete) r.Event(compact, corev1.EventTypeNormal, "Finished", "The compaction process has finished successfully.") - return r.UpdateStatus(compact, string(v1alpha1.BackupComplete), "", "") } + return r.UpdateStatus(compact, newStatus) } -func (r *CompactStatusUpdater) OnRetriableFailed(ctx context.Context, compact *v1alpha1.CompactBackup, retry *RetryStatus) error { - return r.UpdateRetryStatus(compact, string(v1alpha1.BackupRetryTheFailed), retry) +func (r *CompactStatusUpdater) OnRetriableFailed(ctx context.Context, compact *v1alpha1.CompactBackup, retry *v1alpha1.BackoffRetryRecord) error { + newStatus := v1alpha1.CompactStatus{ + BackoffRetryStatus: append(compact.Status.BackoffRetryStatus, *retry), + } + r.Event(compact, corev1.EventTypeWarning, "RetryableFailed", retry.OriginalReason) + return r.UpdateStatus(compact, newStatus) } diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index 7181cca5c9..f7a82e8597 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -259,21 +259,18 @@ func (c *Controller) sync(key string) (err error) { return nil } - if compact.Status.State == string(v1alpha1.BackupRunning) { - jobFailed, reason, originalReason, err := c.detectBackupJobFailure(compact) - if err != nil { - klog.Errorf("Fail to detect backup %s/%s running status, error %v", ns, name, err) - return nil - } + jobFailed, reason, originalReason, err := c.detectBackupJobFailure(compact) + if err != nil { + klog.Errorf("Fail to detect backup %s/%s running status, error %v", ns, name, err) + return nil + } - if jobFailed { - // retry backup after detect failure - //TODO: (Ris)impl this - if err := c.retryAfterFailureDetected(compact, reason, originalReason); err != nil { - klog.Errorf("Fail to restart snapshot backup %s/%s, error %v", ns, name, err) - } - return nil + if jobFailed { + // retry backup after detect failure + if err := c.retryAfterFailureDetected(compact, reason, originalReason); err != nil { + klog.Errorf("Fail to restart snapshot backup %s/%s, error %v", ns, name, err) } + return nil } c.statusUpdater.OnSchedule(context.TODO(), compact) @@ -479,12 +476,12 @@ func (c *Controller) recordDetectedFailure(compact *v1alpha1.CompactBackup, reas expectedRetryAt := metav1.NewTime(detectFailedAt.Add(duration)) // update - newStatus := &controller.RetryStatus{ - RetryNum: &retryNum, + newStatus := &v1alpha1.BackoffRetryRecord{ + RetryNum: retryNum, DetectFailedAt: &detectFailedAt, ExpectedRetryAt: &expectedRetryAt, - RetryReason: &reason, - OriginalReason: &originalReason, + RetryReason: reason, + OriginalReason: originalReason, } klog.Infof("Record backup %s/%s retry status, %v", ns, name, newStatus) @@ -525,3 +522,126 @@ func (c *Controller) detectBackupJobFailure(compact *v1alpha1.CompactBackup) ( } return jobFailed, reason, originalReason, nil } + +func (c *Controller) retryAfterFailureDetected(compact *v1alpha1.CompactBackup, reason, originalReason string) error { + var ( + ns = compact.GetNamespace() + name = compact.GetName() + err error + ) + + // retry snapshot backup according to backoff policy + err = c.retrySnapshotBackupAccordingToBackoffPolicy(compact) + if err != nil { + klog.Errorf("Fail to retry compact job %s/%s according to backoff policy , %v", ns, name, err) + return err + } + klog.Infof("Retry compact job %s/%s according to backoff policy", ns, name) + return nil +} + +// retrySnapshotBackupAccordingToBackoffPolicy retry snapshot backup according to spec.backoffRetryPolicy. +// the main logic is reentrant: +// 1. check whether is retrying which is marked as BackupRetryFailed, +// if true, clean job and mark RealRetryAt which means current retry is done. +// 2. check whether is retry done, skip. +// 3. check whether exceed retry limit, if true, mark as failed. +// 4. check whether exceed the retry interval which is recorded as ExpectedRetryAt, +// the value is the time detect failure + MinRetryDuration << (retry num -1), +// if true, mark as BackupRetryFailed, if not, wait to next loop. +// 5. after mark as BackupRetryFailed, the logic will back to 1 in next loop. +func (c *Controller) retrySnapshotBackupAccordingToBackoffPolicy(compact *v1alpha1.CompactBackup) error { + if len(compact.Status.BackoffRetryStatus) == 0 { + return nil + } + var ( + ns = compact.GetNamespace() + name = compact.GetName() + now = time.Now() + err error + failedReason string + retryRecord = compact.Status.BackoffRetryStatus[len(compact.Status.BackoffRetryStatus)-1] + ) + klog.V(4).Infof("retry backup %s/%s, retry reason %s, original reason %s", ns, name, retryRecord.RetryReason, retryRecord.OriginalReason) + + // check retrying + if isBackoffRetrying(compact) { + return c.doRetryFailedBackup(compact) + } + + // check retry done + if isCurrentBackoffRetryDone(compact) { + return nil + } + + // check is exceed retry limit + isExceedRetryTimes := isExceedRetryTimes(compact) + if isExceedRetryTimes { + failedReason = fmt.Sprintf("exceed retry times, max is %d, failed reason %s, original reason %s", backup.Spec.BackoffRetryPolicy.MaxRetryTimes, retryRecord.RetryReason, retryRecord.OriginalReason) + } + isRetryTimeout, err := isRetryTimeout(compact, &now) + if err != nil { + klog.Errorf("fail to check whether the retry is timeout, backup is %s/%s, %v", ns, name, err) + return err + } + if isRetryTimeout { + failedReason = fmt.Sprintf("retry timeout, max is %s, failed reason %s, original reason %s", backup.Spec.BackoffRetryPolicy.RetryTimeout, retryRecord.RetryReason, retryRecord.OriginalReason) + } + + if isExceedRetryTimes || isRetryTimeout { + klog.Infof("backup %s/%s exceed retry limit, failed reason %s", ns, name, failedReason) + err = c.control.UpdateStatus(backup, &v1alpha1.BackupCondition{ + Type: v1alpha1.BackupFailed, + Status: corev1.ConditionTrue, + Reason: "AlreadyFailed", + Message: failedReason, + }, nil) + if err != nil { + klog.Errorf("Fail to update the condition of backup %s/%s, %v", ns, name, err) + return err + } + return nil + } + + // check is time to retry + if !isTimeToRetry(backup, &now) { + klog.V(4).Infof("backup %s/%s is not the time to retry, expected retry time is %s, now is %s", ns, name, retryRecord.ExpectedRetryAt, now) + return nil + } + + klog.V(4).Infof("backup %s/%s is the time to retry, expected retry time is %s, now is %s", ns, name, retryRecord.ExpectedRetryAt, now) + + // update retry status + err = c.control.UpdateStatus(backup, &v1alpha1.BackupCondition{ + Type: v1alpha1.BackupRetryTheFailed, + Status: corev1.ConditionTrue, + Reason: "RetryFailedBackup", + Message: fmt.Sprintf("reason %s, original reason %s", retryRecord.RetryReason, retryRecord.OriginalReason), + }, nil) + if err != nil { + klog.Errorf("Fail to update the retry status of backup %s/%s, %v", ns, name, err) + return err + } + + return nil +} + +func isBackoffRetrying(backup *v1alpha1.Backup) bool { + if backup.Spec.Mode != v1alpha1.BackupModeSnapshot { + return false + } + if len(backup.Status.BackoffRetryStatus) == 0 { + return false + } + return backup.Status.Phase == v1alpha1.BackupRetryTheFailed +} + +func isCurrentBackoffRetryDone(backup *v1alpha1.Backup) bool { + if backup.Spec.Mode != v1alpha1.BackupModeSnapshot { + return false + } + if len(backup.Status.BackoffRetryStatus) == 0 { + return false + } + return backup.Status.BackoffRetryStatus[len(backup.Status.BackoffRetryStatus)-1].RealRetryAt != nil +} From d4d8a86a4abdf0c051019c7f467e79ef9cedf14e Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Wed, 25 Dec 2024 09:57:00 +0100 Subject: [PATCH 42/49] add helper functions --- .../compact_backup_controller.go | 84 +++++++++++++++++-- 1 file changed, 75 insertions(+), 9 deletions(-) diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index f7a82e8597..f3c3b13c83 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -577,7 +577,7 @@ func (c *Controller) retrySnapshotBackupAccordingToBackoffPolicy(compact *v1alph // check is exceed retry limit isExceedRetryTimes := isExceedRetryTimes(compact) if isExceedRetryTimes { - failedReason = fmt.Sprintf("exceed retry times, max is %d, failed reason %s, original reason %s", backup.Spec.BackoffRetryPolicy.MaxRetryTimes, retryRecord.RetryReason, retryRecord.OriginalReason) + failedReason = fmt.Sprintf("exceed retry times, max is %d, failed reason %s, original reason %s", compact.Spec.BackoffRetryPolicy.MaxRetryTimes, retryRecord.RetryReason, retryRecord.OriginalReason) } isRetryTimeout, err := isRetryTimeout(compact, &now) if err != nil { @@ -626,22 +626,88 @@ func (c *Controller) retrySnapshotBackupAccordingToBackoffPolicy(compact *v1alph return nil } -func isBackoffRetrying(backup *v1alpha1.Backup) bool { - if backup.Spec.Mode != v1alpha1.BackupModeSnapshot { +func (c *Controller) doRetryFailedBackup(compact *v1alpha1.CompactBackup) error { + ns := compact.GetNamespace() + name := compact.GetName() + klog.V(4).Infof("backup %s/%s is retrying after it has been scheduled", ns, name) + + // retry done + if isCurrentBackoffRetryDone(compact) { + // clean job is asynchronous, we need enqueue again, + // the backup status will be scheduled after create new job, and then this reconcile is really done. + c.enqueueBackup(compact) + return nil + } + + // clean job + err := c.cleanBackupOldJobIfExist(backup) + if err != nil { + klog.Errorf("Fail to clean job of backup %s/%s, error is %v", ns, name, err) + return err + } + + // retry done, mark RealRetryAt + RealRetryStatus := &controller.BackupUpdateStatus{ + RealRetryAt: &metav1.Time{Time: time.Now()}, + } + + // add restart condition to clean data before run br command + err = c.control.UpdateStatus(backup, &v1alpha1.BackupCondition{ + Type: v1alpha1.BackupRestart, + Status: corev1.ConditionTrue, + }, RealRetryStatus) + if err != nil { + klog.Errorf("Fail to update the condition of backup %s/%s, %v", ns, name, err) + return err + } + + c.enqueueBackup(compact) + return nil +} + +func isBackoffRetrying(compact *v1alpha1.CompactBackup) bool { + if len(compact.Status.BackoffRetryStatus) == 0 { return false } - if len(backup.Status.BackoffRetryStatus) == 0 { + return compact.Status.State == string(v1alpha1.BackupRetryTheFailed) +} + +func isCurrentBackoffRetryDone(compact *v1alpha1.CompactBackup) bool { + if len(compact.Status.BackoffRetryStatus) == 0 { return false } - return backup.Status.Phase == v1alpha1.BackupRetryTheFailed + return compact.Status.BackoffRetryStatus[len(compact.Status.BackoffRetryStatus)-1].RealRetryAt != nil } -func isCurrentBackoffRetryDone(backup *v1alpha1.Backup) bool { - if backup.Spec.Mode != v1alpha1.BackupModeSnapshot { +func isExceedRetryTimes(compact *v1alpha1.CompactBackup) bool { + records := compact.Status.BackoffRetryStatus + if len(records) == 0 { return false } - if len(backup.Status.BackoffRetryStatus) == 0 { + + currentRetryNum := records[len(records)-1].RetryNum + return currentRetryNum > compact.Spec.BackoffRetryPolicy.MaxRetryTimes +} + +func isRetryTimeout(compact *v1alpha1.CompactBackup, now *time.Time) (bool, error) { + records := compact.Status.BackoffRetryStatus + if len(records) == 0 { + return false, nil + } + firstDetectAt := records[0].DetectFailedAt + retryTimeout, err := time.ParseDuration(compact.Spec.BackoffRetryPolicy.RetryTimeout) + if err != nil { + klog.Errorf("fail to parse retryTimeout %s of backup %s/%s, %v", compact.Spec.BackoffRetryPolicy.RetryTimeout, compact.Namespace, compact.Name, err) + return false, err + } + return now.Unix()-firstDetectAt.Unix() > int64(retryTimeout)/int64(time.Second), nil +} + +func isTimeToRetry(compact *v1alpha1.CompactBackup, now *time.Time) bool { + if len(compact.Status.BackoffRetryStatus) == 0 { return false } - return backup.Status.BackoffRetryStatus[len(backup.Status.BackoffRetryStatus)-1].RealRetryAt != nil + retryRecord := compact.Status.BackoffRetryStatus[len(compact.Status.BackoffRetryStatus)-1] + return time.Now().Unix() > retryRecord.ExpectedRetryAt.Unix() + } From 26bf6ed122c5bba0b9e0a1c496f5fc75a4fdd845 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Wed, 25 Dec 2024 10:41:19 +0100 Subject: [PATCH 43/49] revert backoff logic --- .../compact_backup_controller.go | 265 +----------------- 1 file changed, 1 insertion(+), 264 deletions(-) diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index f3c3b13c83..2523656869 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -259,19 +259,7 @@ func (c *Controller) sync(key string) (err error) { return nil } - jobFailed, reason, originalReason, err := c.detectBackupJobFailure(compact) - if err != nil { - klog.Errorf("Fail to detect backup %s/%s running status, error %v", ns, name, err) - return nil - } - - if jobFailed { - // retry backup after detect failure - if err := c.retryAfterFailureDetected(compact, reason, originalReason); err != nil { - klog.Errorf("Fail to restart snapshot backup %s/%s, error %v", ns, name, err) - } - return nil - } + //TODO:(ris)add retry logic c.statusUpdater.OnSchedule(context.TODO(), compact) @@ -460,254 +448,3 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job return job, "", nil } - -func (c *Controller) recordDetectedFailure(compact *v1alpha1.CompactBackup, reason, originalReason string) error { - ns := compact.GetNamespace() - name := compact.GetName() - - retryNum := len(compact.Status.BackoffRetryStatus) + 1 - detectFailedAt := metav1.Now() - minDuration, err := time.ParseDuration(compact.Spec.BackoffRetryPolicy.MinRetryDuration) - if err != nil { - klog.Errorf("fail to parse minRetryDuration %s of compact backup %s/%s, %v", compact.Spec.BackoffRetryPolicy.MinRetryDuration, ns, name, err) - return err - } - duration := time.Duration(minDuration.Nanoseconds() << (retryNum - 1)) - expectedRetryAt := metav1.NewTime(detectFailedAt.Add(duration)) - - // update - newStatus := &v1alpha1.BackoffRetryRecord{ - RetryNum: retryNum, - DetectFailedAt: &detectFailedAt, - ExpectedRetryAt: &expectedRetryAt, - RetryReason: reason, - OriginalReason: originalReason, - } - - klog.Infof("Record backup %s/%s retry status, %v", ns, name, newStatus) - if err := c.statusUpdater.OnRetriableFailed(context.TODO(), compact, newStatus); err != nil { - klog.Errorf("Fail to update the retry status of backup %s/%s, %v", ns, name, err) - return err - } - - return nil -} - -func (c *Controller) detectBackupJobFailure(compact *v1alpha1.CompactBackup) ( - jobFailed bool, reason string, originalReason string, err error) { - var ( - ns = compact.GetNamespace() - name = compact.GetName() - ) - job, err := c.deps.JobLister.Jobs(ns).Get(name) - if err != nil && !errors.IsNotFound(err) { - klog.Errorf("Fail to get job %s for backup %s/%s, error %v ", name, ns, name, err) - return false, "", "", err - } - if job != nil { - for _, condition := range job.Status.Conditions { - if condition.Type == batchv1.JobFailed && condition.Status == corev1.ConditionTrue { - reason = fmt.Sprintf("Job %s has failed", name) - originalReason = condition.Reason - return true, reason, originalReason, nil - } - } - } - - klog.Infof("Detect backup %s/%s job failed, will retry, reason %s, original reason %s ", ns, name, reason, originalReason) - // record failure when detect failure - err = c.recordDetectedFailure(compact, reason, originalReason) - if err != nil { - klog.Errorf("failed to record detected failed %s for backup %s/%s", reason, ns, name) - } - return jobFailed, reason, originalReason, nil -} - -func (c *Controller) retryAfterFailureDetected(compact *v1alpha1.CompactBackup, reason, originalReason string) error { - var ( - ns = compact.GetNamespace() - name = compact.GetName() - err error - ) - - // retry snapshot backup according to backoff policy - err = c.retrySnapshotBackupAccordingToBackoffPolicy(compact) - if err != nil { - klog.Errorf("Fail to retry compact job %s/%s according to backoff policy , %v", ns, name, err) - return err - } - klog.Infof("Retry compact job %s/%s according to backoff policy", ns, name) - return nil -} - -// retrySnapshotBackupAccordingToBackoffPolicy retry snapshot backup according to spec.backoffRetryPolicy. -// the main logic is reentrant: -// 1. check whether is retrying which is marked as BackupRetryFailed, -// if true, clean job and mark RealRetryAt which means current retry is done. -// 2. check whether is retry done, skip. -// 3. check whether exceed retry limit, if true, mark as failed. -// 4. check whether exceed the retry interval which is recorded as ExpectedRetryAt, -// the value is the time detect failure + MinRetryDuration << (retry num -1), -// if true, mark as BackupRetryFailed, if not, wait to next loop. -// 5. after mark as BackupRetryFailed, the logic will back to 1 in next loop. -func (c *Controller) retrySnapshotBackupAccordingToBackoffPolicy(compact *v1alpha1.CompactBackup) error { - if len(compact.Status.BackoffRetryStatus) == 0 { - return nil - } - var ( - ns = compact.GetNamespace() - name = compact.GetName() - now = time.Now() - err error - failedReason string - retryRecord = compact.Status.BackoffRetryStatus[len(compact.Status.BackoffRetryStatus)-1] - ) - klog.V(4).Infof("retry backup %s/%s, retry reason %s, original reason %s", ns, name, retryRecord.RetryReason, retryRecord.OriginalReason) - - // check retrying - if isBackoffRetrying(compact) { - return c.doRetryFailedBackup(compact) - } - - // check retry done - if isCurrentBackoffRetryDone(compact) { - return nil - } - - // check is exceed retry limit - isExceedRetryTimes := isExceedRetryTimes(compact) - if isExceedRetryTimes { - failedReason = fmt.Sprintf("exceed retry times, max is %d, failed reason %s, original reason %s", compact.Spec.BackoffRetryPolicy.MaxRetryTimes, retryRecord.RetryReason, retryRecord.OriginalReason) - } - isRetryTimeout, err := isRetryTimeout(compact, &now) - if err != nil { - klog.Errorf("fail to check whether the retry is timeout, backup is %s/%s, %v", ns, name, err) - return err - } - if isRetryTimeout { - failedReason = fmt.Sprintf("retry timeout, max is %s, failed reason %s, original reason %s", backup.Spec.BackoffRetryPolicy.RetryTimeout, retryRecord.RetryReason, retryRecord.OriginalReason) - } - - if isExceedRetryTimes || isRetryTimeout { - klog.Infof("backup %s/%s exceed retry limit, failed reason %s", ns, name, failedReason) - err = c.control.UpdateStatus(backup, &v1alpha1.BackupCondition{ - Type: v1alpha1.BackupFailed, - Status: corev1.ConditionTrue, - Reason: "AlreadyFailed", - Message: failedReason, - }, nil) - if err != nil { - klog.Errorf("Fail to update the condition of backup %s/%s, %v", ns, name, err) - return err - } - return nil - } - - // check is time to retry - if !isTimeToRetry(backup, &now) { - klog.V(4).Infof("backup %s/%s is not the time to retry, expected retry time is %s, now is %s", ns, name, retryRecord.ExpectedRetryAt, now) - return nil - } - - klog.V(4).Infof("backup %s/%s is the time to retry, expected retry time is %s, now is %s", ns, name, retryRecord.ExpectedRetryAt, now) - - // update retry status - err = c.control.UpdateStatus(backup, &v1alpha1.BackupCondition{ - Type: v1alpha1.BackupRetryTheFailed, - Status: corev1.ConditionTrue, - Reason: "RetryFailedBackup", - Message: fmt.Sprintf("reason %s, original reason %s", retryRecord.RetryReason, retryRecord.OriginalReason), - }, nil) - if err != nil { - klog.Errorf("Fail to update the retry status of backup %s/%s, %v", ns, name, err) - return err - } - - return nil -} - -func (c *Controller) doRetryFailedBackup(compact *v1alpha1.CompactBackup) error { - ns := compact.GetNamespace() - name := compact.GetName() - klog.V(4).Infof("backup %s/%s is retrying after it has been scheduled", ns, name) - - // retry done - if isCurrentBackoffRetryDone(compact) { - // clean job is asynchronous, we need enqueue again, - // the backup status will be scheduled after create new job, and then this reconcile is really done. - c.enqueueBackup(compact) - return nil - } - - // clean job - err := c.cleanBackupOldJobIfExist(backup) - if err != nil { - klog.Errorf("Fail to clean job of backup %s/%s, error is %v", ns, name, err) - return err - } - - // retry done, mark RealRetryAt - RealRetryStatus := &controller.BackupUpdateStatus{ - RealRetryAt: &metav1.Time{Time: time.Now()}, - } - - // add restart condition to clean data before run br command - err = c.control.UpdateStatus(backup, &v1alpha1.BackupCondition{ - Type: v1alpha1.BackupRestart, - Status: corev1.ConditionTrue, - }, RealRetryStatus) - if err != nil { - klog.Errorf("Fail to update the condition of backup %s/%s, %v", ns, name, err) - return err - } - - c.enqueueBackup(compact) - return nil -} - -func isBackoffRetrying(compact *v1alpha1.CompactBackup) bool { - if len(compact.Status.BackoffRetryStatus) == 0 { - return false - } - return compact.Status.State == string(v1alpha1.BackupRetryTheFailed) -} - -func isCurrentBackoffRetryDone(compact *v1alpha1.CompactBackup) bool { - if len(compact.Status.BackoffRetryStatus) == 0 { - return false - } - return compact.Status.BackoffRetryStatus[len(compact.Status.BackoffRetryStatus)-1].RealRetryAt != nil -} - -func isExceedRetryTimes(compact *v1alpha1.CompactBackup) bool { - records := compact.Status.BackoffRetryStatus - if len(records) == 0 { - return false - } - - currentRetryNum := records[len(records)-1].RetryNum - return currentRetryNum > compact.Spec.BackoffRetryPolicy.MaxRetryTimes -} - -func isRetryTimeout(compact *v1alpha1.CompactBackup, now *time.Time) (bool, error) { - records := compact.Status.BackoffRetryStatus - if len(records) == 0 { - return false, nil - } - firstDetectAt := records[0].DetectFailedAt - retryTimeout, err := time.ParseDuration(compact.Spec.BackoffRetryPolicy.RetryTimeout) - if err != nil { - klog.Errorf("fail to parse retryTimeout %s of backup %s/%s, %v", compact.Spec.BackoffRetryPolicy.RetryTimeout, compact.Namespace, compact.Name, err) - return false, err - } - return now.Unix()-firstDetectAt.Unix() > int64(retryTimeout)/int64(time.Second), nil -} - -func isTimeToRetry(compact *v1alpha1.CompactBackup, now *time.Time) bool { - if len(compact.Status.BackoffRetryStatus) == 0 { - return false - } - retryRecord := compact.Status.BackoffRetryStatus[len(compact.Status.BackoffRetryStatus)-1] - return time.Now().Unix() > retryRecord.ExpectedRetryAt.Unix() - -} From 482609331a9db3d7db678b75016ed62e636fdc13 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Thu, 26 Dec 2024 08:59:49 +0100 Subject: [PATCH 44/49] add skip for already running job --- pkg/apis/pingcap/v1alpha1/types.go | 5 +- pkg/controller/compact_status_updater.go | 13 +- .../compact_backup_controller.go | 151 ++++++++++++------ 3 files changed, 105 insertions(+), 64 deletions(-) diff --git a/pkg/apis/pingcap/v1alpha1/types.go b/pkg/apis/pingcap/v1alpha1/types.go index 2a264ea18b..48b91a5e35 100644 --- a/pkg/apis/pingcap/v1alpha1/types.go +++ b/pkg/apis/pingcap/v1alpha1/types.go @@ -3527,7 +3527,8 @@ type CompactSpec struct { PriorityClassName string `json:"priorityClassName,omitempty"` // BackoffRetryPolicy the backoff retry policy, currently only valid for snapshot backup - BackoffRetryPolicy BackoffRetryPolicy `json:"backoffRetryPolicy,omitempty"` + // +default=2 + MaxRetryTimes int32 `json:"maxRetryTimes,omitempty"` // Additional volumes of component pod. // +optional @@ -3544,8 +3545,6 @@ type CompactStatus struct { Progress string `json:"progress,omitempty"` // Message is the error message of the backup Message string `json:"message,omitempty"` - // BackoffRetryStatus is status of the backoff retry, it will be used when backup pod or job exited unexpectedly - BackoffRetryStatus []BackoffRetryRecord `json:"backoffRetryStatus,omitempty"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/controller/compact_status_updater.go b/pkg/controller/compact_status_updater.go index 5496ae63cd..bd7b6d2d39 100644 --- a/pkg/controller/compact_status_updater.go +++ b/pkg/controller/compact_status_updater.go @@ -16,7 +16,6 @@ package controller import ( "context" "fmt" - "reflect" "time" "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1" @@ -52,7 +51,7 @@ type CompactStatusUpdaterInterface interface { OnStart(ctx context.Context, compact *v1alpha1.CompactBackup) error OnProgress(ctx context.Context, compact *v1alpha1.CompactBackup, p Progress) error OnFinish(ctx context.Context, compact *v1alpha1.CompactBackup, err error) error - OnRetriableFailed(ctx context.Context, compact *v1alpha1.CompactBackup, retry *v1alpha1.BackoffRetryRecord) error + OnJobFailed(ctx context.Context, compact *v1alpha1.CompactBackup, reason string) error } type CompactStatusUpdater struct { @@ -111,10 +110,6 @@ func (r *CompactStatusUpdater) UpdateStatus(compact *v1alpha1.CompactBackup, new updated = true r.progressLastUpdate = now } - if newStatus.BackoffRetryStatus != nil && !reflect.DeepEqual(newStatus.BackoffRetryStatus, compact.Status.BackoffRetryStatus) { - compact.Status.BackoffRetryStatus = newStatus.BackoffRetryStatus - updated = true - } // Apply the update if any field changed if updated { @@ -183,10 +178,10 @@ func (r *CompactStatusUpdater) OnFinish(ctx context.Context, compact *v1alpha1.C return r.UpdateStatus(compact, newStatus) } -func (r *CompactStatusUpdater) OnRetriableFailed(ctx context.Context, compact *v1alpha1.CompactBackup, retry *v1alpha1.BackoffRetryRecord) error { +func (r *CompactStatusUpdater) OnJobFailed(ctx context.Context, compact *v1alpha1.CompactBackup, reason string) error { newStatus := v1alpha1.CompactStatus{ - BackoffRetryStatus: append(compact.Status.BackoffRetryStatus, *retry), + State: string(v1alpha1.BackupFailed), } - r.Event(compact, corev1.EventTypeWarning, "RetryableFailed", retry.OriginalReason) + r.Event(compact, corev1.EventTypeWarning, "The compact job is failed.", reason) return r.UpdateStatus(compact, newStatus) } diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index 2523656869..0d44be9949 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -38,7 +38,7 @@ import ( "k8s.io/client-go/util/retry" "k8s.io/client-go/util/workqueue" "k8s.io/klog" - "k8s.io/utils/pointer" + "k8s.io/utils/ptr" ) // Controller controls backup. @@ -64,11 +64,11 @@ func NewController(deps *controller.Dependencies) *Controller { backupInformer := deps.InformerFactory.Pingcap().V1alpha1().CompactBackups() jobInformer := deps.KubeInformerFactory.Batch().V1().Jobs() backupInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: c.updateBackup, + AddFunc: c.updateCompact, UpdateFunc: func(old, cur interface{}) { - c.updateBackup(cur) + c.updateCompact(cur) }, - DeleteFunc: c.updateBackup, + DeleteFunc: c.updateCompact, }) jobInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ DeleteFunc: c.deleteJob, @@ -167,14 +167,24 @@ func (c *Controller) deleteJob(obj interface{}) { return } klog.V(4).Infof("Job %s/%s deleted through %v.", ns, jobName, utilruntime.GetCaller()) - c.updateBackup(backup) + c.updateCompact(backup) } -func (c *Controller) updateBackup(cur interface{}) { +func (c *Controller) updateCompact(cur interface{}) { newBackup := cur.(*v1alpha1.CompactBackup) ns := newBackup.GetNamespace() name := newBackup.GetName() + if newBackup.Status.State == string(v1alpha1.BackupFailed){ + klog.Errorf("Backup %s/%s is failed, skip", ns, name) + return + } + + if newBackup.Status.State == string(v1alpha1.BackupComplete){ + klog.Errorf("Backup %s/%s is complete, skip", ns, name) + return + } + klog.Infof("backup object %s/%s enqueue", ns, name) c.enqueueBackup(newBackup) } @@ -249,18 +259,15 @@ func (c *Controller) sync(key string) (err error) { return err } - if compact.Status.State == string(v1alpha1.BackupFailed){ - klog.Errorf("Backup %s/%s is failed, skip", ns, name) - return nil + running,err := c.isCompactJobAlreadyRunning(compact) + if err != nil { + return err } - - if compact.Status.State == string(v1alpha1.BackupScheduled) { - klog.Infof("Backup %s/%s is scheduled, skip", ns, name) + if running { + klog.Infof("Backup %s/%s is running, skip", ns, name) return nil } - //TODO:(ris)add retry logic - c.statusUpdater.OnSchedule(context.TODO(), compact) err = c.doCompact(compact.DeepCopy()) @@ -270,34 +277,34 @@ func (c *Controller) sync(key string) (err error) { return err } -func (c *Controller) doCompact(backup *v1alpha1.CompactBackup) error { - ns := backup.GetNamespace() - name := backup.GetName() - backupJobName := backup.GetName() +func (c *Controller) doCompact(compact *v1alpha1.CompactBackup) error { + ns := compact.GetNamespace() + name := compact.GetName() + backupJobName := compact.GetName() // make backup job var err error var job *batchv1.Job var reason string - if job, reason, err = c.makeBackupJob(backup); err != nil { + if job, reason, err = c.makeCompactJob(compact); err != nil { klog.Errorf("backup %s/%s create job %s failed, reason is %s, error %v.", ns, name, backupJobName, reason, err) return err } // create k8s job klog.Infof("backup %s/%s creating job %s.", ns, name, backupJobName) - if err := c.deps.JobControl.CreateJob(backup, job); err != nil { + if err := c.deps.JobControl.CreateJob(compact, job); err != nil { errMsg := fmt.Errorf("create backup %s/%s job %s failed, err: %v", ns, name, backupJobName, err) return errMsg } return nil } -func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job, string, error) { - ns := backup.GetNamespace() - name := backup.GetName() +func (c *Controller) makeCompactJob(compact *v1alpha1.CompactBackup) (*batchv1.Job, string, error) { + ns := compact.GetNamespace() + name := compact.GetName() // Do we need a unique name for the job? - jobName := backup.GetName() + jobName := compact.GetName() var ( envVars []corev1.EnvVar @@ -305,7 +312,7 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job err error ) - storageEnv, reason, err := backuputil.GenerateStorageCertEnv(ns, backup.Spec.UseKMS, backup.Spec.StorageProvider, c.deps.SecretLister) + storageEnv, reason, err := backuputil.GenerateStorageCertEnv(ns, compact.Spec.UseKMS, compact.Spec.StorageProvider, c.deps.SecretLister) if err != nil { return nil, reason, fmt.Errorf("backup %s/%s, %v", ns, name, err) } @@ -313,7 +320,7 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job envVars = append(envVars, storageEnv...) // set env vars specified in backup.Spec.Env - envVars = util.AppendOverwriteEnv(envVars, backup.Spec.Env) + envVars = util.AppendOverwriteEnv(envVars, compact.Spec.Env) args := []string{ "compact", @@ -321,16 +328,16 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job fmt.Sprintf("--resourceName=%s", name), } - tc, err := c.deps.TiDBClusterLister.TidbClusters(backup.Spec.BR.ClusterNamespace).Get(backup.Spec.BR.Cluster) + tc, err := c.deps.TiDBClusterLister.TidbClusters(compact.Spec.BR.ClusterNamespace).Get(compact.Spec.BR.Cluster) if err != nil { - return nil, fmt.Sprintf("failed to fetch tidbcluster %s/%s", ns, backup.Spec.BR.Cluster), err + return nil, fmt.Sprintf("failed to fetch tidbcluster %s/%s", ns, compact.Spec.BR.Cluster), err } tikvImage := tc.TiKVImage() _, tikvVersion := backuputil.ParseImage(tikvImage) brImage := "pingcap/br:" + tikvVersion - if backup.Spec.ToolImage != "" { - toolImage := backup.Spec.ToolImage - if !strings.ContainsRune(backup.Spec.ToolImage, ':') { + if compact.Spec.ToolImage != "" { + toolImage := compact.Spec.ToolImage + if !strings.ContainsRune(compact.Spec.ToolImage, ':') { toolImage = fmt.Sprintf("%s:%s", toolImage, tikvVersion) } @@ -339,9 +346,9 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job klog.Infof("backup %s/%s use br image %s and tikv image %s", ns, name, brImage, tikvImage) //TODO: (Ris)What is the instance here? - jobLabels := util.CombineStringMap(label.NewBackup().Instance("Compact-Backup").BackupJob().Backup(name), backup.Labels) + jobLabels := util.CombineStringMap(label.NewBackup().Instance("Compact-Backup").BackupJob().Backup(name), compact.Labels) podLabels := jobLabels - jobAnnotations := backup.Annotations + jobAnnotations := compact.Annotations podAnnotations := jobAnnotations volumeMounts := []corev1.VolumeMount{} @@ -365,22 +372,22 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job }, ) - if len(backup.Spec.AdditionalVolumes) > 0 { - volumes = append(volumes, backup.Spec.AdditionalVolumes...) + if len(compact.Spec.AdditionalVolumes) > 0 { + volumes = append(volumes, compact.Spec.AdditionalVolumes...) } - if len(backup.Spec.AdditionalVolumeMounts) > 0 { - volumeMounts = append(volumeMounts, backup.Spec.AdditionalVolumeMounts...) + if len(compact.Spec.AdditionalVolumeMounts) > 0 { + volumeMounts = append(volumeMounts, compact.Spec.AdditionalVolumeMounts...) } // mount volumes if specified - if backup.Spec.Local != nil { - volumes = append(volumes, backup.Spec.Local.Volume) - volumeMounts = append(volumeMounts, backup.Spec.Local.VolumeMount) + if compact.Spec.Local != nil { + volumes = append(volumes, compact.Spec.Local.Volume) + volumeMounts = append(volumeMounts, compact.Spec.Local.VolumeMount) } serviceAccount := constants.DefaultServiceAccountName - if backup.Spec.ServiceAccount != "" { - serviceAccount = backup.Spec.ServiceAccount + if compact.Spec.ServiceAccount != "" { + serviceAccount = compact.Spec.ServiceAccount } podSpec := &corev1.PodTemplateSpec{ @@ -389,7 +396,7 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job Annotations: podAnnotations, }, Spec: corev1.PodSpec{ - SecurityContext: backup.Spec.PodSecurityContext, + SecurityContext: compact.Spec.PodSecurityContext, ServiceAccountName: serviceAccount, InitContainers: []corev1.Container{ { @@ -399,7 +406,7 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job Args: []string{fmt.Sprintf("cp /br %s/br; echo 'BR copy finished'", util.BRBinPath)}, ImagePullPolicy: corev1.PullIfNotPresent, VolumeMounts: volumeMounts, - Resources: backup.Spec.ResourceRequirements, + Resources: compact.Spec.ResourceRequirements, }, { Name: "tikv-ctl", @@ -408,7 +415,7 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job Args: []string{fmt.Sprintf("cp /tikv-ctl %s/tikv-ctl; echo 'tikv-ctl copy finished'", util.KVCTLBinPath)}, ImagePullPolicy: corev1.PullIfNotPresent, VolumeMounts: volumeMounts, - Resources: backup.Spec.ResourceRequirements, + Resources: compact.Spec.ResourceRequirements, }, }, Containers: []corev1.Container{ @@ -421,12 +428,12 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job ImagePullPolicy: corev1.PullIfNotPresent, }, }, - RestartPolicy: corev1.RestartPolicyNever, - Tolerations: backup.Spec.Tolerations, - ImagePullSecrets: backup.Spec.ImagePullSecrets, - Affinity: backup.Spec.Affinity, + RestartPolicy: corev1.RestartPolicyOnFailure, + Tolerations: compact.Spec.Tolerations, + ImagePullSecrets: compact.Spec.ImagePullSecrets, + Affinity: compact.Spec.Affinity, Volumes: volumes, - PriorityClassName: backup.Spec.PriorityClassName, + PriorityClassName: compact.Spec.PriorityClassName, }, } @@ -437,14 +444,54 @@ func (c *Controller) makeBackupJob(backup *v1alpha1.CompactBackup) (*batchv1.Job Labels: jobLabels, Annotations: jobAnnotations, OwnerReferences: []metav1.OwnerReference{ - controller.GetCompactBackupOwnerRef(backup), + controller.GetCompactBackupOwnerRef(compact), }, }, Spec: batchv1.JobSpec{ - BackoffLimit: pointer.Int32Ptr(0), Template: *podSpec, + BackoffLimit: ptr.To(compact.Spec.MaxRetryTimes), }, } return job, "", nil } + +func (c *Controller) isCompactJobAlreadyRunning(compact *v1alpha1.CompactBackup) (bool, error) { + ns := compact.GetNamespace() + name := compact.GetName() + + job, err := c.deps.KubeClientset.BatchV1().Jobs(ns).Get(context.TODO(), name, metav1.GetOptions{}) + if err != nil { + if errors.IsNotFound(err) { + return false, nil + } + klog.Errorf("Failed to get job %s for compact %s/%s, error %v", name, ns, name, err) + return false, err + } + + for _, condition := range job.Status.Conditions { + if condition.Type == batchv1.JobFailed && condition.Status == corev1.ConditionTrue { + // Check events if job failed + events, err := c.deps.KubeClientset.CoreV1().Events(ns).List(context.TODO(), metav1.ListOptions{ + FieldSelector: fmt.Sprintf("involvedObject.kind=Job,involvedObject.name=%s", name), + }) + if err != nil { + if errors.IsNotFound(err) { + return true, nil // No events found, treat as "running" + } + klog.Errorf("Failed to get events for job %s/%s, error %v", ns, name, err) + return true, err + } + + for _, event := range events.Items { + if event.Reason == "BackoffLimitExceeded" { + klog.Warningf("Job %s has exceeded the backoff limit, no further retries will be attempted.", name) + c.statusUpdater.OnJobFailed(context.TODO(), compact, event.Message) + } + } + return true, nil + } + } + + return true, nil +} From 129ff36e82b3e6dd2f92cc9cb4380bd0988466ff Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Thu, 26 Dec 2024 09:02:10 +0100 Subject: [PATCH 45/49] make generate --- docs/api-references/docs.md | 18 ++++++++---------- manifests/crd.yaml | 15 +++------------ .../crd/v1/pingcap.com_compactbackups.yaml | 15 +++------------ pkg/apis/pingcap/v1alpha1/openapi_generated.go | 9 +++++---- pkg/apis/pingcap/v1alpha1/types.go | 4 ++-- .../pingcap/v1alpha1/zz_generated.deepcopy.go | 1 - pkg/controller/compact_backup_control.go | 2 +- pkg/controller/compact_status_updater.go | 12 +++++------- .../compactbackup/compact_backup_controller.go | 10 +++++----- 9 files changed, 32 insertions(+), 54 deletions(-) diff --git a/docs/api-references/docs.md b/docs/api-references/docs.md index fe726a0266..b4a9a79bcd 100644 --- a/docs/api-references/docs.md +++ b/docs/api-references/docs.md @@ -3620,8 +3620,7 @@ bool

BackoffRetryPolicy

(Appears on: -BackupSpec, -CompactSpec) +BackupSpec)

BackoffRetryPolicy is the backoff retry policy, currently only valid for snapshot backup. @@ -5588,11 +5587,9 @@ string -backoffRetryPolicy
+maxRetryTimes
- -BackoffRetryPolicy - +int32 @@ -5873,11 +5870,9 @@ string -backoffRetryPolicy
+maxRetryTimes
- -BackoffRetryPolicy - +int32 @@ -5937,6 +5932,7 @@ string
+

State is the current state of the backup

@@ -5947,6 +5943,7 @@ string
+

Progress is the progress of the backup

@@ -5957,6 +5954,7 @@ string +

Message is the error message of the backup

diff --git a/manifests/crd.yaml b/manifests/crd.yaml index 883a6351fd..5483554828 100644 --- a/manifests/crd.yaml +++ b/manifests/crd.yaml @@ -8204,18 +8204,6 @@ spec: storageAccount: type: string type: object - backoffRetryPolicy: - properties: - maxRetryTimes: - default: 2 - type: integer - minRetryDuration: - default: 300s - type: string - retryTimeout: - default: 30m - type: string - type: object br: properties: checkRequirements: @@ -9086,6 +9074,9 @@ spec: - volume - volumeMount type: object + maxRetryTimes: + format: int32 + type: integer podSecurityContext: properties: fsGroup: diff --git a/manifests/crd/v1/pingcap.com_compactbackups.yaml b/manifests/crd/v1/pingcap.com_compactbackups.yaml index f280e97dd9..e72dde7d4f 100644 --- a/manifests/crd/v1/pingcap.com_compactbackups.yaml +++ b/manifests/crd/v1/pingcap.com_compactbackups.yaml @@ -1154,18 +1154,6 @@ spec: storageAccount: type: string type: object - backoffRetryPolicy: - properties: - maxRetryTimes: - default: 2 - type: integer - minRetryDuration: - default: 300s - type: string - retryTimeout: - default: 30m - type: string - type: object br: properties: checkRequirements: @@ -2036,6 +2024,9 @@ spec: - volume - volumeMount type: object + maxRetryTimes: + format: int32 + type: integer podSecurityContext: properties: fsGroup: diff --git a/pkg/apis/pingcap/v1alpha1/openapi_generated.go b/pkg/apis/pingcap/v1alpha1/openapi_generated.go index 14d4952415..70f4f84866 100644 --- a/pkg/apis/pingcap/v1alpha1/openapi_generated.go +++ b/pkg/apis/pingcap/v1alpha1/openapi_generated.go @@ -1845,11 +1845,12 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) Format: "", }, }, - "backoffRetryPolicy": { + "maxRetryTimes": { SchemaProps: spec.SchemaProps{ Description: "BackoffRetryPolicy the backoff retry policy, currently only valid for snapshot backup", - Default: map[string]interface{}{}, - Ref: ref("github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BackoffRetryPolicy"), + Default: 2, + Type: []string{"integer"}, + Format: "int32", }, }, "additionalVolumes": { @@ -1884,7 +1885,7 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) }, }, Dependencies: []string{ - "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.AzblobStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BRConfig", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BackoffRetryPolicy", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.GcsStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.LocalStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.S3StorageProvider", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume", "k8s.io/api/core/v1.VolumeMount"}, + "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.AzblobStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.BRConfig", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.GcsStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.LocalStorageProvider", "github.com/pingcap/tidb-operator/pkg/apis/pingcap/v1alpha1.S3StorageProvider", "k8s.io/api/core/v1.Affinity", "k8s.io/api/core/v1.EnvVar", "k8s.io/api/core/v1.LocalObjectReference", "k8s.io/api/core/v1.PodSecurityContext", "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.Toleration", "k8s.io/api/core/v1.Volume", "k8s.io/api/core/v1.VolumeMount"}, } } diff --git a/pkg/apis/pingcap/v1alpha1/types.go b/pkg/apis/pingcap/v1alpha1/types.go index 48b91a5e35..f648c0b9ad 100644 --- a/pkg/apis/pingcap/v1alpha1/types.go +++ b/pkg/apis/pingcap/v1alpha1/types.go @@ -3540,11 +3540,11 @@ type CompactSpec struct { type CompactStatus struct { // State is the current state of the backup - State string `json:"state,omitempty"` + State string `json:"state,omitempty"` // Progress is the progress of the backup Progress string `json:"progress,omitempty"` // Message is the error message of the backup - Message string `json:"message,omitempty"` + Message string `json:"message,omitempty"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go index 5653f71a37..7763bacaf7 100644 --- a/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/pingcap/v1alpha1/zz_generated.deepcopy.go @@ -965,7 +965,6 @@ func (in *CompactSpec) DeepCopyInto(out *CompactSpec) { *out = new(v1.PodSecurityContext) (*in).DeepCopyInto(*out) } - out.BackoffRetryPolicy = in.BackoffRetryPolicy if in.AdditionalVolumes != nil { in, out := &in.AdditionalVolumes, &out.AdditionalVolumes *out = make([]v1.Volume, len(*in)) diff --git a/pkg/controller/compact_backup_control.go b/pkg/controller/compact_backup_control.go index 2b79a7c701..cd28fb5e62 100644 --- a/pkg/controller/compact_backup_control.go +++ b/pkg/controller/compact_backup_control.go @@ -64,4 +64,4 @@ func (c *realCompactControl) DeleteCompactBackup(compact *v1alpha1.CompactBackup compactName := compact.GetName() return c.cli.PingcapV1alpha1().CompactBackups(ns).Delete(context.TODO(), compactName, metav1.DeleteOptions{}) -} \ No newline at end of file +} diff --git a/pkg/controller/compact_status_updater.go b/pkg/controller/compact_status_updater.go index bd7b6d2d39..56925db73f 100644 --- a/pkg/controller/compact_status_updater.go +++ b/pkg/controller/compact_status_updater.go @@ -36,9 +36,9 @@ const ( type Progress struct { // MetaCompleted is the number of meta files compacted - MetaCompleted uint64 `json:"meta_completed"` + MetaCompleted uint64 `json:"meta_completed"` // MetaTotal is the total number of meta files - MetaTotal uint64 `json:"meta_total"` + MetaTotal uint64 `json:"meta_total"` // BytesToCompact is the number of bytes to compact BytesToCompact uint64 `json:"bytes_to_compact"` // BytesCompacted is the number of bytes compacted @@ -49,7 +49,7 @@ type CompactStatusUpdaterInterface interface { OnSchedule(ctx context.Context, compact *v1alpha1.CompactBackup) error OnCreateJob(ctx context.Context, compact *v1alpha1.CompactBackup, err error) error OnStart(ctx context.Context, compact *v1alpha1.CompactBackup) error - OnProgress(ctx context.Context, compact *v1alpha1.CompactBackup, p Progress) error + OnProgress(ctx context.Context, compact *v1alpha1.CompactBackup, p Progress) error OnFinish(ctx context.Context, compact *v1alpha1.CompactBackup, err error) error OnJobFailed(ctx context.Context, compact *v1alpha1.CompactBackup, reason string) error } @@ -134,8 +134,7 @@ func (r *CompactStatusUpdater) OnSchedule(ctx context.Context, compact *v1alpha1 } func (r *CompactStatusUpdater) OnCreateJob(ctx context.Context, compact *v1alpha1.CompactBackup, err error) error { - newStatus := v1alpha1.CompactStatus{ - } + newStatus := v1alpha1.CompactStatus{} if err != nil { newStatus.State = string(v1alpha1.BackupFailed) newStatus.Message = err.Error() @@ -165,8 +164,7 @@ func (r *CompactStatusUpdater) OnProgress(ctx context.Context, compact *v1alpha1 } func (r *CompactStatusUpdater) OnFinish(ctx context.Context, compact *v1alpha1.CompactBackup, err error) error { - newStatus := v1alpha1.CompactStatus{ - } + newStatus := v1alpha1.CompactStatus{} if err != nil { newStatus.State = string(v1alpha1.BackupFailed) newStatus.Message = err.Error() diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index 0d44be9949..ea0aab28e8 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -45,8 +45,8 @@ import ( type Controller struct { deps *controller.Dependencies // backups that need to be synced. - queue workqueue.RateLimitingInterface - cli versioned.Interface + queue workqueue.RateLimitingInterface + cli versioned.Interface statusUpdater controller.CompactStatusUpdaterInterface } @@ -175,12 +175,12 @@ func (c *Controller) updateCompact(cur interface{}) { ns := newBackup.GetNamespace() name := newBackup.GetName() - if newBackup.Status.State == string(v1alpha1.BackupFailed){ + if newBackup.Status.State == string(v1alpha1.BackupFailed) { klog.Errorf("Backup %s/%s is failed, skip", ns, name) return } - if newBackup.Status.State == string(v1alpha1.BackupComplete){ + if newBackup.Status.State == string(v1alpha1.BackupComplete) { klog.Errorf("Backup %s/%s is complete, skip", ns, name) return } @@ -259,7 +259,7 @@ func (c *Controller) sync(key string) (err error) { return err } - running,err := c.isCompactJobAlreadyRunning(compact) + running, err := c.isCompactJobAlreadyRunning(compact) if err != nil { return err } From 101c5cc3f5381dc5aff31f14b31864608029ddcc Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Thu, 26 Dec 2024 10:11:11 +0100 Subject: [PATCH 46/49] initialize status updater --- pkg/controller/compactbackup/compact_backup_controller.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/controller/compactbackup/compact_backup_controller.go b/pkg/controller/compactbackup/compact_backup_controller.go index ea0aab28e8..efb571957c 100644 --- a/pkg/controller/compactbackup/compact_backup_controller.go +++ b/pkg/controller/compactbackup/compact_backup_controller.go @@ -59,6 +59,9 @@ func NewController(deps *controller.Dependencies) *Controller { "compactBackup", ), cli: deps.Clientset, + statusUpdater: controller.NewCompactStatusUpdater( + deps.Recorder, deps.CompactBackupLister, deps.Clientset, + ), } backupInformer := deps.InformerFactory.Pingcap().V1alpha1().CompactBackups() From 6411b8adc4886bf53bcb5b521b54470753deff0a Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Thu, 26 Dec 2024 10:44:22 +0100 Subject: [PATCH 47/49] apply backoff limit --- manifests/crd.yaml | 1 + manifests/crd/v1/pingcap.com_compactbackups.yaml | 1 + pkg/apis/pingcap/v1alpha1/openapi_generated.go | 1 - pkg/apis/pingcap/v1alpha1/types.go | 2 +- 4 files changed, 3 insertions(+), 2 deletions(-) diff --git a/manifests/crd.yaml b/manifests/crd.yaml index 5483554828..28d0d3cb28 100644 --- a/manifests/crd.yaml +++ b/manifests/crd.yaml @@ -9075,6 +9075,7 @@ spec: - volumeMount type: object maxRetryTimes: + default: 6 format: int32 type: integer podSecurityContext: diff --git a/manifests/crd/v1/pingcap.com_compactbackups.yaml b/manifests/crd/v1/pingcap.com_compactbackups.yaml index e72dde7d4f..43a4f17529 100644 --- a/manifests/crd/v1/pingcap.com_compactbackups.yaml +++ b/manifests/crd/v1/pingcap.com_compactbackups.yaml @@ -2025,6 +2025,7 @@ spec: - volumeMount type: object maxRetryTimes: + default: 6 format: int32 type: integer podSecurityContext: diff --git a/pkg/apis/pingcap/v1alpha1/openapi_generated.go b/pkg/apis/pingcap/v1alpha1/openapi_generated.go index 70f4f84866..858e8d992c 100644 --- a/pkg/apis/pingcap/v1alpha1/openapi_generated.go +++ b/pkg/apis/pingcap/v1alpha1/openapi_generated.go @@ -1848,7 +1848,6 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) "maxRetryTimes": { SchemaProps: spec.SchemaProps{ Description: "BackoffRetryPolicy the backoff retry policy, currently only valid for snapshot backup", - Default: 2, Type: []string{"integer"}, Format: "int32", }, diff --git a/pkg/apis/pingcap/v1alpha1/types.go b/pkg/apis/pingcap/v1alpha1/types.go index f648c0b9ad..497542a91d 100644 --- a/pkg/apis/pingcap/v1alpha1/types.go +++ b/pkg/apis/pingcap/v1alpha1/types.go @@ -3527,7 +3527,7 @@ type CompactSpec struct { PriorityClassName string `json:"priorityClassName,omitempty"` // BackoffRetryPolicy the backoff retry policy, currently only valid for snapshot backup - // +default=2 + // +kubebuilder:default=6 MaxRetryTimes int32 `json:"maxRetryTimes,omitempty"` // Additional volumes of component pod. From f0d740a9b74e80d8f586d7afafbfde98e1e16d97 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Thu, 26 Dec 2024 10:45:41 +0100 Subject: [PATCH 48/49] add check for eerr --- cmd/backup-manager/app/compact/manager.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/backup-manager/app/compact/manager.go b/cmd/backup-manager/app/compact/manager.go index edd49e7752..67fe6dd196 100644 --- a/cmd/backup-manager/app/compact/manager.go +++ b/cmd/backup-manager/app/compact/manager.go @@ -109,7 +109,10 @@ func (cm *Manager) base64ifyStorage(ctx context.Context) (string, error) { } out, err := brCmd.Output() if err != nil { - eerr := err.(*exec.ExitError) + eerr,ok := err.(*exec.ExitError) + if !ok { + return "", errors.Annotatef(err, "failed to execute BR with args %v", brCmd.Args) + } klog.Warningf("Failed to execute base64ify; stderr = %s", string(eerr.Stderr)) return "", errors.Annotatef(err, "failed to execute BR with args %v", brCmd.Args) } From 9e1f0f876e1fcebc2bd54e310e7080050a7c9cf1 Mon Sep 17 00:00:00 2001 From: RidRisR <79858083+RidRisR@users.noreply.github.com> Date: Thu, 26 Dec 2024 10:48:35 +0100 Subject: [PATCH 49/49] make generate --- cmd/backup-manager/app/compact/manager.go | 2 +- manifests/crd.yaml | 1 + manifests/crd/v1/pingcap.com_compactbackups.yaml | 1 + pkg/apis/pingcap/v1alpha1/openapi_generated.go | 1 - pkg/apis/pingcap/v1alpha1/types.go | 2 +- 5 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/backup-manager/app/compact/manager.go b/cmd/backup-manager/app/compact/manager.go index 67fe6dd196..683ac1de37 100644 --- a/cmd/backup-manager/app/compact/manager.go +++ b/cmd/backup-manager/app/compact/manager.go @@ -109,7 +109,7 @@ func (cm *Manager) base64ifyStorage(ctx context.Context) (string, error) { } out, err := brCmd.Output() if err != nil { - eerr,ok := err.(*exec.ExitError) + eerr, ok := err.(*exec.ExitError) if !ok { return "", errors.Annotatef(err, "failed to execute BR with args %v", brCmd.Args) } diff --git a/manifests/crd.yaml b/manifests/crd.yaml index 28d0d3cb28..d25c83eec9 100644 --- a/manifests/crd.yaml +++ b/manifests/crd.yaml @@ -8241,6 +8241,7 @@ spec: - cluster type: object concurrency: + default: 4 type: integer endTs: type: string diff --git a/manifests/crd/v1/pingcap.com_compactbackups.yaml b/manifests/crd/v1/pingcap.com_compactbackups.yaml index 43a4f17529..97eab0db5e 100644 --- a/manifests/crd/v1/pingcap.com_compactbackups.yaml +++ b/manifests/crd/v1/pingcap.com_compactbackups.yaml @@ -1191,6 +1191,7 @@ spec: - cluster type: object concurrency: + default: 4 type: integer endTs: type: string diff --git a/pkg/apis/pingcap/v1alpha1/openapi_generated.go b/pkg/apis/pingcap/v1alpha1/openapi_generated.go index 858e8d992c..fee2810aaa 100644 --- a/pkg/apis/pingcap/v1alpha1/openapi_generated.go +++ b/pkg/apis/pingcap/v1alpha1/openapi_generated.go @@ -1766,7 +1766,6 @@ func schema_pkg_apis_pingcap_v1alpha1_CompactSpec(ref common.ReferenceCallback) "concurrency": { SchemaProps: spec.SchemaProps{ Description: "Concurrency is the concurrency of compact backup job", - Default: 4, Type: []string{"integer"}, Format: "int32", }, diff --git a/pkg/apis/pingcap/v1alpha1/types.go b/pkg/apis/pingcap/v1alpha1/types.go index 497542a91d..4ea73e9cd8 100644 --- a/pkg/apis/pingcap/v1alpha1/types.go +++ b/pkg/apis/pingcap/v1alpha1/types.go @@ -3496,7 +3496,7 @@ type CompactSpec struct { // +optional EndTs string `json:"endTs,omitempty"` // Concurrency is the concurrency of compact backup job - // +default=4 + // +kubebuilder:default=4 Concurrency int `json:"concurrency,omitempty"` // Base tolerations of backup Pods, components may add more tolerations upon this respectively // +optional