From c9244107ac5f1a6edecd604b1fe3e92f4f4505ef Mon Sep 17 00:00:00 2001 From: acekingke Date: Wed, 7 Sep 2022 14:43:44 +0800 Subject: [PATCH] *: add backup gtid and fix some bugs #698 --- api/v1alpha1/backup_types.go | 2 + api/v1alpha1/mysqlcluster_types.go | 3 + backup/cronbackup.go | 18 +- backup/syncer/job.go | 10 +- .../crds/mysql.radondb.com_backups.yaml | 144 ++ .../crds/mysql.radondb.com_mysqlclusters.yaml | 1496 +++++++++++++++++ .../crd/bases/mysql.radondb.com_backups.yaml | 3 + controllers/backupcron_controller.go | 2 +- mysqlcluster/syncer/status.go | 38 + sidecar/takebackup.go | 102 ++ utils/constants.go | 3 + 11 files changed, 1814 insertions(+), 7 deletions(-) create mode 100644 charts/mysql-operator/crds/mysql.radondb.com_backups.yaml create mode 100644 charts/mysql-operator/crds/mysql.radondb.com_mysqlclusters.yaml create mode 100644 sidecar/takebackup.go diff --git a/api/v1alpha1/backup_types.go b/api/v1alpha1/backup_types.go index 23e3e22a..053d733c 100644 --- a/api/v1alpha1/backup_types.go +++ b/api/v1alpha1/backup_types.go @@ -64,6 +64,8 @@ type BackupStatus struct { BackupDate string `json:"backupDate,omitempty"` // Get the backup Type BackupType string `json:"backupType,omitempty"` + // Get the Gtid + Gtid string `json:"gtid,omitempty"` // Conditions represents the backup resource conditions list. Conditions []BackupCondition `json:"conditions,omitempty"` } diff --git a/api/v1alpha1/mysqlcluster_types.go b/api/v1alpha1/mysqlcluster_types.go index bf2489e0..7e2ac9d5 100644 --- a/api/v1alpha1/mysqlcluster_types.go +++ b/api/v1alpha1/mysqlcluster_types.go @@ -469,6 +469,9 @@ type MysqlClusterStatus struct { ReadyNodes int `json:"readyNodes,omitempty"` // State State ClusterState `json:"state,omitempty"` + // LastBackup + LastBackup string `json:"lastbackup,omitempty"` + LastBackupGtid string `json:"lastbackupGtid,omitempty"` // Conditions contains the list of the cluster conditions fulfilled. Conditions []ClusterCondition `json:"conditions,omitempty"` // Nodes contains the list of the node status fulfilled. diff --git a/backup/cronbackup.go b/backup/cronbackup.go index e50ca95d..9bc4d4a3 100644 --- a/backup/cronbackup.go +++ b/backup/cronbackup.go @@ -75,14 +75,22 @@ func (j *CronJob) scheduledBackupsRunningCount() int { backupsList := &apiv1alpha1.BackupList{} // select all backups with labels recurrent=true and and not completed of the cluster selector := j.backupSelector() - client.MatchingFields{"status.completed": "false"}.ApplyToList(selector) + // Because k8s do not support fieldSelector with custom resources + // https://github.com/kubernetes/kubernetes/issues/51046 + // So this cannot use fields selector. + // client.MatchingFields{"status.completed": "false"}.ApplyToList(selector) - if err := j.Client.List(context.TODO(), backupsList, selector); err != nil { + if err := j.Client.List(context.TODO(), backupsList); err != nil { log.Error(err, "failed getting backups", "selector", selector) return 0 } - - return len(backupsList.Items) + var rest []apiv1alpha1.Backup + for _, b := range backupsList.Items { + if !b.Status.Completed { + rest = append(rest, b) + } + } + return len(rest) } func (j *CronJob) backupSelector() *client.ListOptions { @@ -151,10 +159,12 @@ func (j *CronJob) createBackup() (*apiv1alpha1.Backup, error) { //RemoteDeletePolicy: j.BackupRemoteDeletePolicy, HostName: fmt.Sprintf("%s-mysql-0", j.ClusterName), }, + Status: apiv1alpha1.BackupStatus{Completed: false}, } if len(j.NFSServerAddress) > 0 { backup.Spec.NFSServerAddress = j.NFSServerAddress } + return backup, j.Client.Create(context.TODO(), backup) } diff --git a/backup/syncer/job.go b/backup/syncer/job.go index b1e7e8a2..689098ee 100644 --- a/backup/syncer/job.go +++ b/backup/syncer/job.go @@ -25,6 +25,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" v1alpha1 "github.com/radondb/radondb-mysql-kubernetes/api/v1alpha1" "github.com/radondb/radondb-mysql-kubernetes/backup" @@ -101,6 +102,9 @@ func (s *jobSyncer) updateStatus(job *batchv1.Job) { if backType := s.job.Annotations[utils.JobAnonationType]; backType != "" { s.backup.Status.BackupType = backType } + if gtid := s.job.Annotations[utils.JobAnonationGtid]; gtid != "" { + s.backup.Status.Gtid = gtid + } } // check for failed condition @@ -154,10 +158,12 @@ func (s *jobSyncer) ensurePodSpec(in corev1.PodSpec) corev1.PodSpec { "/bin/bash", "-c", "--", } backupToDir, DateTime := utils.BuildBackupName(s.backup.Spec.ClusterName) + // add the gtid script strAnnonations := fmt.Sprintf(`curl -X PATCH -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" -H "Content-Type: application/json-patch+json" \ --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt https://$KUBERNETES_SERVICE_HOST:$KUBERNETES_PORT_443_TCP_PORT/apis/batch/v1/namespaces/%s/jobs/%s \ - -d '[{"op": "add", "path": "/metadata/annotations/backupName", "value": "%s"}, {"op": "add", "path": "/metadata/annotations/backupDate", "value": "%s"}, {"op": "add", "path": "/metadata/annotations/backupType", "value": "NFS"}]';`, - s.backup.Namespace, s.backup.GetNameForJob(), backupToDir, DateTime) + -d "[{\"op\": \"add\", \"path\": \"/metadata/annotations/backupName\", \"value\": \"%s\"}, {\"op\": \"add\", \"path\": \"/metadata/annotations/backupDate\", \"value\": \"%s\"},{\"op\": \"add\", \"path\": \"/metadata/annotations/gtid\", \"value\": \"$( cat /backup/%s/xtrabackup_binlog_info|awk '{print $3}')\"}, {\"op\": \"add\", \"path\": \"/metadata/annotations/backupType\", \"value\": \"NFS\"}]";`, + s.backup.Namespace, s.backup.GetNameForJob(), backupToDir, DateTime, backupToDir) + log.Log.Info(strAnnonations) // Add the check DiskUsage // use expr because shell cannot compare float number checkUsage := `[ $(echo "$(df /backup|awk 'NR>1 {print $4}') > $(du /backup |awk 'END {if (NR > 1) {print $1 /(NR-1)} else print 0}')"|bc) -eq '1' ] || { echo disk available may be too small; exit 1;};` diff --git a/charts/mysql-operator/crds/mysql.radondb.com_backups.yaml b/charts/mysql-operator/crds/mysql.radondb.com_backups.yaml new file mode 100644 index 00000000..a58c1969 --- /dev/null +++ b/charts/mysql-operator/crds/mysql.radondb.com_backups.yaml @@ -0,0 +1,144 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.4.1 + creationTimestamp: null + name: backups.mysql.radondb.com +spec: + group: mysql.radondb.com + names: + kind: Backup + listKind: BackupList + plural: backups + singular: backup + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The Backup name + jsonPath: .status.backupName + name: BackupName + type: string + - description: The Backup Date time + jsonPath: .status.backupDate + name: BackupDate + type: string + - description: The Backup Type + jsonPath: .status.backupType + name: Type + type: string + - description: Whether the backup Success? + jsonPath: .status.conditions[?(@.type=="Complete")].status + name: Success + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: Backup is the Schema for the backups API + properties: + apiVersion: + 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 + kind: + 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 + metadata: + type: object + spec: + description: This is the backup Job CRD. BackupSpec defines the desired + state of Backup + properties: + clusterName: + description: ClusterName represents the cluster name to backup + type: string + historyLimit: + default: 3 + description: History Limit of job + format: int32 + type: integer + hostName: + description: HostName represents the host for which to take backup + If is empty, is use leader HostName + type: string + image: + default: radondb/mysql57-sidecar:v2.3.0 + description: To specify the image that will be used for sidecar container. + type: string + nfsServerAddress: + description: Represents the ip address of the nfs server. + type: string + required: + - clusterName + type: object + status: + description: BackupStatus defines the observed state of Backup + properties: + backupDate: + description: Get the backup Date + type: string + backupName: + description: Get the backup path. + type: string + backupType: + description: Get the backup Type + type: string + completed: + default: false + description: Completed indicates whether the backup is in a final + state, no matter whether its' corresponding job failed or succeeded + type: boolean + conditions: + description: Conditions represents the backup resource conditions + list. + items: + description: BackupCondition defines condition struct for backup + resource + properties: + lastTransitionTime: + description: LastTransitionTime + format: date-time + type: string + message: + description: Message + type: string + reason: + description: Reason + type: string + status: + description: Status of the condition, one of (\"True\", \"False\", + \"Unknown\") + type: string + type: + description: type of cluster condition, values in (\"Ready\") + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + gtid: + description: Get the Gtid + type: string + required: + - completed + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/charts/mysql-operator/crds/mysql.radondb.com_mysqlclusters.yaml b/charts/mysql-operator/crds/mysql.radondb.com_mysqlclusters.yaml new file mode 100644 index 00000000..e0deb405 --- /dev/null +++ b/charts/mysql-operator/crds/mysql.radondb.com_mysqlclusters.yaml @@ -0,0 +1,1496 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.4.1 + creationTimestamp: null + name: mysqlclusters.mysql.radondb.com +spec: + group: mysql.radondb.com + names: + kind: MysqlCluster + listKind: MysqlClusterList + plural: mysqlclusters + shortNames: + - mysql + singular: mysqlcluster + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The cluster status + jsonPath: .status.state + name: State + type: string + - description: The number of desired replicas + jsonPath: .spec.replicas + name: Desired + type: integer + - description: The number of current replicas + jsonPath: .status.readyNodes + name: Current + type: integer + - description: Name of the leader node + jsonPath: .status.nodes[?(@.raftStatus.role == 'LEADER')].name + name: Leader + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: MysqlCluster is the Schema for the mysqlclusters API + properties: + apiVersion: + 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 + kind: + 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 + metadata: + type: object + spec: + description: MysqlClusterSpec defines the desired state of MysqlCluster + properties: + backupSchedule: + description: Specify under crontab format interval to take backups + leave it empty to deactivate the backup process Defaults to "" + type: string + backupScheduleJobsHistoryLimit: + default: 6 + description: If set keeps last BackupScheduleJobsHistoryLimit Backups + type: integer + backupSecretName: + description: Represents the name of the secret that contains credentials + to connect to the storage provider to store backups. + type: string + bothS3NFS: + description: Specify that crontab job backup both on NFS and S3 storage. + properties: + nfsSchedule: + description: NFS schedule. + type: string + s3Schedule: + type: string + type: object + metricsOpts: + default: + enabled: false + image: prom/mysqld-exporter:v0.12.1 + resources: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 10m + memory: 32Mi + description: MetricsOpts is the options of metrics container. + properties: + enabled: + default: false + description: Enabled represents if start a metrics container. + type: boolean + image: + default: prom/mysqld-exporter:v0.12.1 + description: To specify the image that will be used for metrics + container. + type: string + resources: + default: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 10m + memory: 32Mi + description: The compute resource requirements. + properties: + 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 + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + 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 + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + type: object + minAvailable: + default: 50% + description: The number of pods from that set that must still be available + after the eviction, even in the absence of the evicted pod + type: string + mysqlOpts: + default: + database: radondb + initTokuDB: false + password: RadonDB@123 + resources: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 100m + memory: 256Mi + rootHost: localhost + rootPassword: "" + user: radondb_usr + description: MysqlOpts is the options of MySQL container. + properties: + database: + default: radondb + description: Name for new database to create. + type: string + image: + default: percona/percona-server:5.7.34 + description: Specifies mysql image to use. + type: string + initTokuDB: + default: false + description: InitTokuDB represents if install tokudb engine. + type: boolean + maxLagTime: + default: 30 + description: MaxLagSeconds configures the readiness probe of mysqld + container if the replication lag is greater than MaxLagSeconds, + the mysqld container will not be not healthy. + minimum: 0 + type: integer + mysqlConf: + additionalProperties: + type: string + description: A map[string]string that will be passed to my.cnf + file. The key/value pairs is persisted in the configmap. Delete + key is not valid, it is recommended to edit the configmap directly. + type: object + mysqlConfTemplate: + description: MysqlConfTemplate is the configmap name of the template + for mysql config. The configmap should contain the keys `mysql.cnf` + and `plugin.cnf` at least, key `init.sql` is optional. If empty, + operator will generate a default template named -mysql. + type: string + password: + default: RadonDB@123 + description: 'Password for the new user, must be 8~32 characters + long. Only be a combination of uppercase letters, lowercase + letters, numbers or special characters. Special characters are + supported: @#$%^&*_+-=.' + pattern: ^[A-Za-z0-9@#$%^&*_+\-=]{8,32}$ + type: string + pluginConf: + additionalProperties: + type: string + description: A map[string]string that will be passed to plugin.cnf + file. The key/value pairs is persisted in the configmap. Delete + key is not valid, it is recommended to edit the configmap directly. + type: object + resources: + default: + limits: + cpu: 500m + memory: 1Gi + requests: + cpu: 100m + memory: 256Mi + description: The compute resource requirements. + properties: + 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 + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + 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 + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + rootHost: + default: localhost + description: 'Unchangeable: Use super users instead. The root + user''s host.' + enum: + - localhost + type: string + rootPassword: + default: "" + description: 'Unchangeable: Use super users instead Password for + the root user, can be empty or 8~32 characters long. Only be + a combination of uppercase letters, lowercase letters, numbers + or special characters. Special characters are supported: @#$%^&*_+-=.' + enum: + - "" + type: string + user: + default: radondb_usr + description: Username of new user to create. Only be a combination + of letters, numbers or underlines. The length can not exceed + 26 characters. + pattern: ^[A-Za-z0-9_]{2,26}$ + type: string + type: object + mysqlVersion: + default: "5.7" + description: 'Represents the MySQL version that will be run. The available + version can be found here: This field should be set even if the + Image is set to let the operator know which mysql version is running. + Based on this version the operator can take decisions which features + can be used.' + type: string + nfsServerAddress: + description: Represents NFS ip address where cluster restore from. + type: string + persistence: + default: + accessModes: + - ReadWriteOnce + enabled: true + size: 10Gi + description: PVC extra specifiaction. + properties: + accessModes: + default: + - ReadWriteOnce + description: 'AccessModes contains the desired access modes the + volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + enabled: + default: true + description: Create a volume to store data. + type: boolean + size: + default: 10Gi + description: Size of persistent volume claim. + type: string + storageClass: + description: 'Name of the StorageClass required by the claim. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + type: string + type: object + podPolicy: + default: + busyboxImage: busybox:1.32 + extraResources: + requests: + cpu: 10m + memory: 32Mi + imagePullPolicy: IfNotPresent + sidecarImage: radondb/mysql57-sidecar:v2.3.0 + description: PodPolicy defines the policy to extra specification. + properties: + affinity: + description: Affinity is a group of affinity scheduling rules. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for + the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the affinity expressions specified + by this field, but it may choose a node that violates + one or more of the expressions. The node that is most + preferred is the one with the greatest sum of weights, + i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node matches the corresponding matchExpressions; + the node(s) with the highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches + all objects with implicit weight 0 (i.e. it's a no-op). + A null preferred scheduling term matches no objects + (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with + the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the + corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. If the affinity requirements + specified by this field cease to be met at some point + during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from + its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: A null or empty node selector term + matches no objects. The requirements of them are + ANDed. The TopologySelectorTerm type implements + a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. + co-locate this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the affinity expressions specified + by this field, but it may choose a node that violates + one or more of the expressions. The node that is most + preferred is the one with the greatest sum of weights, + i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node has pods which matches the corresponding + podAffinityTerm; the node(s) with the highest sum are + the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by + this field and the ones listed in the namespaces + field. null selector and null or empty namespaces + list means "this pod's namespace". An empty + selector ({}) matches all namespaces. This + field is beta-level and is only honored when + PodAffinityNamespaceSelector feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. + The term is applied to the union of the namespaces + listed in this field and the ones selected + by namespaceSelector. null or empty namespaces + list and null namespaceSelector means "this + pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the + pods matching the labelSelector in the specified + namespaces, where co-located is defined as + running on a node whose value of the label + with key topologyKey matches that of any node + on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the + corresponding podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. If the affinity requirements + specified by this field cease to be met at some point + during pod execution (e.g. due to a pod label update), + the system may or may not try to eventually evict the + pod from its node. When there are multiple elements, + the lists of nodes corresponding to each podAffinityTerm + are intersected, i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not + co-located (anti-affinity) with, where co-located + is defined as running on a node whose value of the + label with key matches that of any node + on which a pod of the set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + null selector and null or empty namespaces list + means "this pod's namespace". An empty selector + ({}) matches all namespaces. This field is beta-level + and is only honored when PodAffinityNamespaceSelector + feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. null or empty namespaces list + and null namespaceSelector means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules + (e.g. avoid putting this pod in the same node, zone, etc. + as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the anti-affinity expressions + specified by this field, but it may choose a node that + violates one or more of the expressions. The node that + is most preferred is the one with the greatest sum of + weights, i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + anti-affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node has pods which matches the corresponding + podAffinityTerm; the node(s) with the highest sum are + the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by + this field and the ones listed in the namespaces + field. null selector and null or empty namespaces + list means "this pod's namespace". An empty + selector ({}) matches all namespaces. This + field is beta-level and is only honored when + PodAffinityNamespaceSelector feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. + The term is applied to the union of the namespaces + listed in this field and the ones selected + by namespaceSelector. null or empty namespaces + list and null namespaceSelector means "this + pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the + pods matching the labelSelector in the specified + namespaces, where co-located is defined as + running on a node whose value of the label + with key topologyKey matches that of any node + on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the + corresponding podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified + by this field are not met at scheduling time, the pod + will not be scheduled onto the node. If the anti-affinity + requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod + label update), the system may or may not try to eventually + evict the pod from its node. When there are multiple + elements, the lists of nodes corresponding to each podAffinityTerm + are intersected, i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not + co-located (anti-affinity) with, where co-located + is defined as running on a node whose value of the + label with key matches that of any node + on which a pod of the set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + null selector and null or empty namespaces list + means "this pod's namespace". An empty selector + ({}) matches all namespaces. This field is beta-level + and is only honored when PodAffinityNamespaceSelector + feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. null or empty namespaces list + and null namespaceSelector means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + annotations: + additionalProperties: + type: string + type: object + auditLogTail: + default: false + description: AuditLogTail represents if tail the mysql audit log. + type: boolean + busyboxImage: + default: busybox:1.32 + description: The busybox image. + type: string + extraResources: + default: + requests: + cpu: 10m + memory: 32Mi + description: ExtraResources defines quotas for containers other + than mysql or xenon. These containers take up less resources, + so quotas are set uniformly. + properties: + 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 + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + 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 + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + imagePullPolicy: + default: IfNotPresent + description: PullPolicy describes a policy for if/when to pull + a container image + enum: + - Always + - IfNotPresent + - Never + type: string + labels: + additionalProperties: + type: string + type: object + priorityClassName: + type: string + schedulerName: + type: string + sidecarImage: + default: radondb/mysql57-sidecar:v2.3.0 + description: To specify the image that will be used for sidecar + container. + type: string + slowLogTail: + default: false + description: SlowLogTail represents if tail the mysql slow log. + type: boolean + tolerations: + items: + description: The pod this Toleration is attached to tolerates + any taint that matches the triple using + the matching operator . + properties: + effect: + description: Effect indicates the taint effect to match. + Empty means match all taint effects. When specified, allowed + values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match + all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to + the value. Valid operators are Exists and Equal. Defaults + to Equal. Exists is equivalent to wildcard for value, + so that a pod can tolerate all taints of a particular + category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of + time the toleration (which must be of effect NoExecute, + otherwise this field is ignored) tolerates the taint. + By default, it is not set, which means tolerate the taint + forever (do not evict). Zero and negative values will + be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string + type: object + type: array + type: object + replicas: + default: 3 + description: Replicas is the number of pods. + enum: + - 0 + - 1 + - 2 + - 3 + - 5 + format: int32 + type: integer + restoreFrom: + description: Represents the name of the cluster restore from backup + path. + type: string + tlsSecretName: + description: Containing CA (ca.crt) and server cert (tls.crt), server + private key (tls.key) for SSL + type: string + xenonOpts: + default: + admitDefeatHearbeatCount: 5 + electionTimeout: 10000 + image: radondb/xenon:v2.3.0 + resources: + limits: + cpu: 100m + memory: 256Mi + requests: + cpu: 50m + memory: 128Mi + description: XenonOpts is the options of xenon container. + properties: + admitDefeatHearbeatCount: + default: 5 + description: High available component admit defeat heartbeat count. + format: int32 + type: integer + electionTimeout: + default: 10000 + description: High available component election timeout. The unit + is millisecond. + format: int32 + type: integer + enableAutoRebuild: + default: false + description: If true, when the data is inconsistent, Xenon will + automatically rebuild the invalid node. + type: boolean + image: + default: radondb/xenon:v2.3.0 + description: To specify the image that will be used for xenon + container. + type: string + resources: + default: + limits: + cpu: 100m + memory: 256Mi + requests: + cpu: 50m + memory: 128Mi + description: The compute resource requirements. + properties: + 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 + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + 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 + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + type: object + type: object + status: + description: MysqlClusterStatus defines the observed state of MysqlCluster + properties: + conditions: + description: Conditions contains the list of the cluster conditions + fulfilled. + items: + description: ClusterCondition defines type for cluster conditions. + properties: + lastTransitionTime: + description: The last time this Condition type changed. + format: date-time + type: string + message: + description: Full text reason for current status of the condition. + type: string + reason: + description: One word, camel-case reason for current status + of the condition. + type: string + status: + description: Status of the condition, one of (\"True\", \"False\", + \"Unknown\"). + type: string + type: + description: Type of cluster condition, values in (\"Initializing\", + \"Ready\", \"Error\"). + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + lastbackup: + description: LastBackup + type: string + lastbackupGtid: + type: string + nodes: + description: Nodes contains the list of the node status fulfilled. + items: + description: NodeStatus defines type for status of a node into cluster. + properties: + conditions: + description: Conditions contains the list of the node conditions + fulfilled. + items: + description: NodeCondition defines type for representing node + conditions. + properties: + lastTransitionTime: + description: The last time this Condition type changed. + format: date-time + type: string + status: + description: Status of the node, one of (\"True\", \"False\", + \"Unknown\"). + type: string + type: + description: Type of the node condition. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + message: + description: Full text reason for current status of the node. + type: string + name: + description: Name of the node. + type: string + raftStatus: + description: RaftStatus is the raft status of the node. + properties: + leader: + description: Leader is the name of the Leader of the current + node. + type: string + nodes: + description: Nodes is a list of nodes that can be identified + by the current node. + items: + type: string + type: array + role: + description: Role is one of (LEADER/CANDIDATE/FOLLOWER/IDLE/INVALID) + type: string + type: object + required: + - name + type: object + type: array + readyNodes: + description: ReadyNodes represents number of the nodes that are in + ready state. + type: integer + state: + description: State + type: string + type: object + type: object + served: true + storage: true + subresources: + scale: + specReplicasPath: .spec.replicas + statusReplicasPath: .status.readyNodes + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/bases/mysql.radondb.com_backups.yaml b/config/crd/bases/mysql.radondb.com_backups.yaml index 142e1dbd..2bc7daeb 100644 --- a/config/crd/bases/mysql.radondb.com_backups.yaml +++ b/config/crd/bases/mysql.radondb.com_backups.yaml @@ -125,6 +125,9 @@ spec: - type type: object type: array + gtid: + description: Get the Gtid + type: string required: - completed type: object diff --git a/controllers/backupcron_controller.go b/controllers/backupcron_controller.go index 4f1abd82..e0056e13 100644 --- a/controllers/backupcron_controller.go +++ b/controllers/backupcron_controller.go @@ -157,7 +157,7 @@ func (r *BackupCronReconciler) updateClusterSchedule(ctx context.Context, cluste log.V(1).Info("cluster already added to cron.", "key", cluster) // change scheduler for already added crons - if !reflect.DeepEqual(entry.Schedule, schedule) { + if !reflect.DeepEqual(entry.Schedule, schedule) || j.NFSServerAddress != cluster.Spec.NFSServerAddress { log.Info("update cluster scheduler", "key", cluster, "scheduler", schedule) diff --git a/mysqlcluster/syncer/status.go b/mysqlcluster/syncer/status.go index 73a268e1..2e9967fc 100644 --- a/mysqlcluster/syncer/status.go +++ b/mysqlcluster/syncer/status.go @@ -19,6 +19,7 @@ package syncer import ( "context" "fmt" + "sort" "strconv" "strings" "time" @@ -164,16 +165,53 @@ func (s *StatusSyncer) Sync(ctx context.Context) (syncer.SyncResult, error) { if len(s.Status.Conditions) > maxStatusesQuantity { s.Status.Conditions = s.Status.Conditions[len(s.Status.Conditions)-maxStatusesQuantity:] } + //(RO) because the ReadOnly Pods create after the cluster ready, so the ReadOnly pods are always // the last part of node status if err := s.updateReadOnlyNodeStatus(ctx, s.cli, list.Items); err != nil { //Notice!!! ReadOnly node fail, just show the error log, do not return here! s.log.Error(err, "ReadOnly pod fail", "namespace", s.Namespace) } + + // update backup Status + s.updateLastBackup() + // Update all nodes' status. return syncer.SyncResult{}, s.updateNodeStatus(ctx, s.cli, list.Items) } +func (s *StatusSyncer) updateLastBackup() error { + // 1. fetch all finished backup cr + backupsList := &apiv1alpha1.BackupList{} + labelSet := labels.Set{"cluster": s.Name} + if err := s.cli.List(context.TODO(), backupsList, &client.ListOptions{ + Namespace: s.Namespace, LabelSelector: labelSet.AsSelector(), + }); err != nil { + return err + } + var finisheds []apiv1alpha1.Backup + for _, b := range backupsList.Items { + if b.Status.Completed { + finisheds = append(finisheds, b) + } + } + // 2. sort descent + sort.Slice(finisheds, func(i, j int) bool { + return finisheds[i].ObjectMeta.CreationTimestamp.Before(&finisheds[j].ObjectMeta.CreationTimestamp) + }) + // 3. get first backup which has backup Name + for _, b := range finisheds { + if len(b.Status.BackupName) != 0 { + s.Status.LastBackup = b.Status.BackupName + s.Status.LastBackupGtid = b.Status.Gtid + break + } + + } + + return nil +} + // updateClusterStatus update the cluster status and returns condition. func (s *StatusSyncer) updateClusterStatus() apiv1alpha1.ClusterCondition { clusterCondition := apiv1alpha1.ClusterCondition{ diff --git a/sidecar/takebackup.go b/sidecar/takebackup.go new file mode 100644 index 00000000..5a84c51e --- /dev/null +++ b/sidecar/takebackup.go @@ -0,0 +1,102 @@ +/* +Copyright 2021 RadonDB. + +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, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package sidecar + +import ( + "bufio" + "fmt" + "os" + "os/exec" + "strings" + "sync" +) + +// RunTakeBackupCommand starts a backup command +func RunTakeBackupCommand(cfg *Config) (string, string, string, error) { + // cfg->XtrabackupArgs() + xtrabackup := exec.Command(xtrabackupCommand, cfg.XtrabackupArgs()...) + + var err error + backupName, DateTime := cfg.XBackupName() + Gtid := "" + xcloud := exec.Command(xcloudCommand, cfg.XCloudArgs(backupName)...) + log.Info("xargs ", "xargs", strings.Join(cfg.XCloudArgs(backupName), " ")) + if xcloud.Stdin, err = xtrabackup.StdoutPipe(); err != nil { + log.Error(err, "failed to pipline") + return "", "", "", err + } + //xtrabackup.Stderr = os.Stderr + xcloud.Stderr = os.Stderr + + var wg sync.WaitGroup + Stderr, err := xtrabackup.StderrPipe() + if err != nil { + return "", "", "", fmt.Errorf("RunCommand: cmd.StderrPipe(): %v", err) + } + if err := xtrabackup.Start(); err != nil { + log.Error(err, "failed to start xtrabackup command") + return "", "", "", err + } + if err := xcloud.Start(); err != nil { + log.Error(err, "fail start xcloud ") + return "", "", "", err + } + scanner := bufio.NewScanner(Stderr) + //scanner.Split(ScanLinesR) + wg.Add(1) + go func() { + for scanner.Scan() { + text := scanner.Text() + fmt.Println(text) + if index := strings.Index(text, "GTID"); index != -1 { + // Mysql5.7 examples: MySQL binlog position: filename 'mysql-bin.000002', position '588', GTID of the last change '319bd6eb-2ea2-11ed-bf40-7e1ef582b427:1-2' + // MySQL8.0 no gtid: MySQL binlog position: filename 'mysql-bin.000025', position '156' + length := len("GTID of the last change") + Gtid = strings.Trim(text[index+length:], " '") // trim space and \' + if len(Gtid) != 0 { + log.Info("Catch gtid: " + Gtid) + } + + } + } + wg.Done() + }() + + wg.Wait() + // pipe command fail one, whole things fail + errorChannel := make(chan error, 2) + go func() { + errorChannel <- xcloud.Wait() + }() + go func() { + errorChannel <- xtrabackup.Wait() + }() + defer xtrabackup.Wait() + defer xcloud.Wait() + + for i := 0; i < 2; i++ { + if err = <-errorChannel; err != nil { + log.Info("catch error , need to stop") + _ = xtrabackup.Process.Kill() + _ = xcloud.Process.Kill() + + return "", "", "", err + } + } + + return backupName, DateTime, Gtid, nil +} diff --git a/utils/constants.go b/utils/constants.go index 07f0868f..43e8e57d 100644 --- a/utils/constants.go +++ b/utils/constants.go @@ -178,6 +178,8 @@ const ( JobAnonationName = "backupName" // Job Annonations date JobAnonationDate = "backupDate" + // Job Anonations Gtid + JobAnonationGtid = "gtid" // Job Annonations type JobAnonationType = "backupType" // Job Annonations size @@ -215,6 +217,7 @@ const ( type JsonResult struct { Status string `json:"status"` BackupName string `json:"backupName"` + Gtid string `json:"gtid"` Date string `json:"date"` BackupSize int64 `json:"backupSize"` }