diff --git a/api/v1alpha1/perconaservermysql_types.go b/api/v1alpha1/perconaservermysql_types.go index f2067eaec..4e115dc91 100644 --- a/api/v1alpha1/perconaservermysql_types.go +++ b/api/v1alpha1/perconaservermysql_types.go @@ -39,6 +39,7 @@ import ( "github.com/percona/percona-server-mysql-operator/pkg/naming" "github.com/percona/percona-server-mysql-operator/pkg/platform" + "github.com/percona/percona-server-mysql-operator/pkg/util" "github.com/percona/percona-server-mysql-operator/pkg/version" ) @@ -279,6 +280,57 @@ type BackupStorageSpec struct { ContainerSecurityContext *corev1.SecurityContext `json:"containerSecurityContext,omitempty"` RuntimeClassName *string `json:"runtimeClassName,omitempty"` VerifyTLS *bool `json:"verifyTLS,omitempty"` + ContainerOptions *BackupContainerOptions `json:"containerOptions,omitempty"` +} + +type BackupContainerOptions struct { + Env []corev1.EnvVar `json:"env,omitempty"` + Args BackupContainerArgs `json:"args"` +} + +func (b *BackupContainerOptions) GetEnv() []corev1.EnvVar { + return util.MergeEnvLists(b.Env, b.Args.Env()) +} + +func (b *BackupContainerOptions) GetEnvVar(cluster *PerconaServerMySQL, storage *BackupStorageSpec) []corev1.EnvVar { + if b != nil { + return b.GetEnv() + } + + if storage == nil || storage.ContainerOptions == nil { + return nil + } + + return storage.ContainerOptions.GetEnvVar(nil, nil) +} + +type BackupContainerArgs struct { + Xtrabackup []string `json:"xtrabackup,omitempty"` + Xbcloud []string `json:"xbcloud,omitempty"` + Xbstream []string `json:"xbstream,omitempty"` +} + +func (b *BackupContainerArgs) Env() []corev1.EnvVar { + envs := []corev1.EnvVar{} + if len(b.Xtrabackup) > 0 { + envs = append(envs, corev1.EnvVar{ + Name: "XB_EXTRA_ARGS", + Value: strings.Join(b.Xtrabackup, " "), + }) + } + if len(b.Xbcloud) > 0 { + envs = append(envs, corev1.EnvVar{ + Name: "XBCLOUD_EXTRA_ARGS", + Value: strings.Join(b.Xbcloud, " "), + }) + } + if len(b.Xbstream) > 0 { + envs = append(envs, corev1.EnvVar{ + Name: "XBSTREAM_EXTRA_ARGS", + Value: strings.Join(b.Xbstream, " "), + }) + } + return envs } type BackupStorageS3Spec struct { diff --git a/api/v1alpha1/perconaservermysqlrestore_types.go b/api/v1alpha1/perconaservermysqlrestore_types.go index 20b78a6b2..0e988044f 100644 --- a/api/v1alpha1/perconaservermysqlrestore_types.go +++ b/api/v1alpha1/perconaservermysqlrestore_types.go @@ -27,9 +27,10 @@ import ( // PerconaServerMySQLRestoreSpec defines the desired state of PerconaServerMySQLRestore type PerconaServerMySQLRestoreSpec struct { - ClusterName string `json:"clusterName"` - BackupName string `json:"backupName,omitempty"` - BackupSource *PerconaServerMySQLBackupStatus `json:"backupSource,omitempty"` + ClusterName string `json:"clusterName"` + BackupName string `json:"backupName,omitempty"` + BackupSource *PerconaServerMySQLBackupStatus `json:"backupSource,omitempty"` + ContainerOptions *BackupContainerOptions `json:"containerOptions,omitempty"` } type RestoreState string diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 06c7984bd..1ea82ac2e 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -28,6 +28,59 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupContainerArgs) DeepCopyInto(out *BackupContainerArgs) { + *out = *in + if in.Xtrabackup != nil { + in, out := &in.Xtrabackup, &out.Xtrabackup + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Xbcloud != nil { + in, out := &in.Xbcloud, &out.Xbcloud + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Xbstream != nil { + in, out := &in.Xbstream, &out.Xbstream + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupContainerArgs. +func (in *BackupContainerArgs) DeepCopy() *BackupContainerArgs { + if in == nil { + return nil + } + out := new(BackupContainerArgs) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupContainerOptions) DeepCopyInto(out *BackupContainerOptions) { + *out = *in + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]corev1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + in.Args.DeepCopyInto(&out.Args) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupContainerOptions. +func (in *BackupContainerOptions) DeepCopy() *BackupContainerOptions { + if in == nil { + return nil + } + out := new(BackupContainerOptions) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BackupSchedule) DeepCopyInto(out *BackupSchedule) { *out = *in @@ -225,6 +278,11 @@ func (in *BackupStorageSpec) DeepCopyInto(out *BackupStorageSpec) { *out = new(bool) **out = **in } + if in.ContainerOptions != nil { + in, out := &in.ContainerOptions, &out.ContainerOptions + *out = new(BackupContainerOptions) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupStorageSpec. @@ -659,6 +717,11 @@ func (in *PerconaServerMySQLRestoreSpec) DeepCopyInto(out *PerconaServerMySQLRes *out = new(PerconaServerMySQLBackupStatus) (*in).DeepCopyInto(*out) } + if in.ContainerOptions != nil { + in, out := &in.ContainerOptions, &out.ContainerOptions + *out = new(BackupContainerOptions) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PerconaServerMySQLRestoreSpec. diff --git a/build/run-backup.sh b/build/run-backup.sh index a4ec6932b..aed360ebd 100755 --- a/build/run-backup.sh +++ b/build/run-backup.sh @@ -11,6 +11,7 @@ request_data() { { "destination": "$(json_escape "${BACKUP_DEST}")", "type": "$(json_escape "${STORAGE_TYPE}")", + "containerOptions": ${CONTAINER_OPTIONS}, "verifyTLS": $(json_escape "${VERIFY_TLS}"), "s3": { "bucket": "$(json_escape "${S3_BUCKET}")", @@ -29,6 +30,7 @@ request_data() { "destination": "$(json_escape "${BACKUP_DEST}")", "verifyTLS": $(json_escape "${VERIFY_TLS}"), "type": "$(json_escape "${STORAGE_TYPE}")", + "containerOptions": ${CONTAINER_OPTIONS}, "gcs": { "bucket": "$(json_escape "${GCS_BUCKET}")", "endpointUrl": "$(json_escape "${GCS_ENDPOINT}")", @@ -45,6 +47,7 @@ request_data() { "destination": "$(json_escape "${BACKUP_DEST}")", "verifyTLS": $(json_escape "${VERIFY_TLS}"), "type": "$(json_escape "${STORAGE_TYPE}")", + "containerOptions": ${CONTAINER_OPTIONS}, "azure": { "containerName": "$(json_escape "${AZURE_CONTAINER_NAME}")", "storageAccount": "$(json_escape "${AZURE_STORAGE_ACCOUNT}")", diff --git a/build/run-restore.sh b/build/run-restore.sh index 1b70dae9d..79ce7864f 100755 --- a/build/run-restore.sh +++ b/build/run-restore.sh @@ -5,7 +5,7 @@ set -o xtrace DATADIR=${DATADIR:-/var/lib/mysql} PARALLEL=$(grep -c processor /proc/cpuinfo) -XBCLOUD_ARGS="--curl-retriable-errors=7 --parallel=${PARALLEL}" +XBCLOUD_ARGS="--curl-retriable-errors=7 --parallel=${PARALLEL} ${XBCLOUD_EXTRA_ARGS}" if [ -n "$VERIFY_TLS" ] && [[ $VERIFY_TLS == "false" ]]; then XBCLOUD_ARGS="${XBCLOUD_ARGS} --insecure" fi @@ -25,7 +25,8 @@ run_azure() { extract() { local targetdir=$1 - xbstream -xv -C "${targetdir}" --parallel="${PARALLEL}" + # shellcheck disable=SC2086 + xbstream -xv -C "${targetdir}" --parallel="${PARALLEL}" ${XBSTREAM_EXTRA_ARGS} } main() { @@ -47,8 +48,10 @@ main() { keyring="--keyring-vault-config=${KEYRING_VAULT_PATH}" fi - xtrabackup --prepare --rollback-prepared-trx --target-dir="${tmpdir}" ${keyring} - xtrabackup --datadir="${DATADIR}" --move-back --force-non-empty-directories --target-dir="${tmpdir}" + # shellcheck disable=SC2086 + xtrabackup --prepare --rollback-prepared-trx --target-dir="${tmpdir}" ${XB_EXTRA_ARGS} ${keyring} + # shellcheck disable=SC2086 + xtrabackup --datadir="${DATADIR}" --move-back --force-non-empty-directories --target-dir="${tmpdir}" ${XB_EXTRA_ARGS} rm -rf "${tmpdir}" diff --git a/cmd/sidecar/main.go b/cmd/sidecar/main.go index 419a4a4bd..a09a68a13 100644 --- a/cmd/sidecar/main.go +++ b/cmd/sidecar/main.go @@ -19,12 +19,11 @@ import ( "syscall" "time" + "github.com/pkg/errors" "golang.org/x/sync/errgroup" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" - "github.com/pkg/errors" - apiv1alpha1 "github.com/percona/percona-server-mysql-operator/api/v1alpha1" "github.com/percona/percona-server-mysql-operator/pkg/mysql" xb "github.com/percona/percona-server-mysql-operator/pkg/xtrabackup" @@ -148,8 +147,8 @@ func getNamespace() (string, error) { return string(ns), nil } -func xtrabackupArgs(user, pass string) []string { - return []string{ +func xtrabackupArgs(user, pass string, conf *xb.BackupConfig) []string { + args := []string{ "--backup", "--stream=xbstream", "--safe-slave-backup", @@ -158,6 +157,10 @@ func xtrabackupArgs(user, pass string) []string { fmt.Sprintf("--user=%s", user), fmt.Sprintf("--password=%s", pass), } + if conf != nil && conf.ContainerOptions != nil { + args = append(args, conf.ContainerOptions.Args.Xtrabackup...) + } + return args } func backupHandler(w http.ResponseWriter, req *http.Request) { @@ -381,6 +384,16 @@ func createBackupHandler(w http.ResponseWriter, req *http.Request) { } } + if backupConf.ContainerOptions != nil { + for _, env := range backupConf.ContainerOptions.Env { + if err := os.Setenv(env.Name, env.Value); err != nil { + log.Error(err, "failed to set env") + http.Error(w, "failed to set env", http.StatusInternalServerError) + return + } + } + } + w.Header().Set("Content-Type", "application/octet-stream") w.Header().Set("Connection", "keep-alive") @@ -393,7 +406,7 @@ func createBackupHandler(w http.ResponseWriter, req *http.Request) { } g, gCtx := errgroup.WithContext(req.Context()) - xtrabackup := exec.CommandContext(gCtx, "xtrabackup", xtrabackupArgs(string(backupUser), backupPass)...) + xtrabackup := exec.CommandContext(gCtx, "xtrabackup", xtrabackupArgs(string(backupUser), backupPass, &backupConf)...) xbOut, err := xtrabackup.StdoutPipe() if err != nil { diff --git a/config/crd/bases/ps.percona.com_perconaservermysqlbackups.yaml b/config/crd/bases/ps.percona.com_perconaservermysqlbackups.yaml index 2c49132d5..3f6d83be8 100644 --- a/config/crd/bases/ps.percona.com_perconaservermysqlbackups.yaml +++ b/config/crd/bases/ps.percona.com_perconaservermysqlbackups.yaml @@ -527,6 +527,92 @@ spec: - container - credentialsSecret type: object + containerOptions: + properties: + args: + properties: + xbcloud: + items: + type: string + type: array + xbstream: + items: + type: string + type: array + xtrabackup: + items: + type: string + type: array + type: object + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + 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: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + required: + - args + type: object containerSecurityContext: properties: allowPrivilegeEscalation: diff --git a/config/crd/bases/ps.percona.com_perconaservermysqlrestores.yaml b/config/crd/bases/ps.percona.com_perconaservermysqlrestores.yaml index 78fba4602..fa7d88238 100644 --- a/config/crd/bases/ps.percona.com_perconaservermysqlrestores.yaml +++ b/config/crd/bases/ps.percona.com_perconaservermysqlrestores.yaml @@ -511,6 +511,92 @@ spec: - container - credentialsSecret type: object + containerOptions: + properties: + args: + properties: + xbcloud: + items: + type: string + type: array + xbstream: + items: + type: string + type: array + xtrabackup: + items: + type: string + type: array + type: object + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + 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: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + required: + - args + type: object containerSecurityContext: properties: allowPrivilegeEscalation: @@ -934,6 +1020,92 @@ spec: type: object clusterName: type: string + containerOptions: + properties: + args: + properties: + xbcloud: + items: + type: string + type: array + xbstream: + items: + type: string + type: array + xtrabackup: + items: + type: string + type: array + type: object + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + 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: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + required: + - args + type: object required: - clusterName type: object diff --git a/config/crd/bases/ps.percona.com_perconaservermysqls.yaml b/config/crd/bases/ps.percona.com_perconaservermysqls.yaml index 38a71a851..b59881486 100644 --- a/config/crd/bases/ps.percona.com_perconaservermysqls.yaml +++ b/config/crd/bases/ps.percona.com_perconaservermysqls.yaml @@ -1900,6 +1900,92 @@ spec: - container - credentialsSecret type: object + containerOptions: + properties: + args: + properties: + xbcloud: + items: + type: string + type: array + xbstream: + items: + type: string + type: array + xtrabackup: + items: + type: string + type: array + type: object + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + 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: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + required: + - args + type: object containerSecurityContext: properties: allowPrivilegeEscalation: diff --git a/deploy/bundle.yaml b/deploy/bundle.yaml index 817a5cd65..7b3668d6b 100644 --- a/deploy/bundle.yaml +++ b/deploy/bundle.yaml @@ -531,6 +531,92 @@ spec: - container - credentialsSecret type: object + containerOptions: + properties: + args: + properties: + xbcloud: + items: + type: string + type: array + xbstream: + items: + type: string + type: array + xtrabackup: + items: + type: string + type: array + type: object + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + 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: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + required: + - args + type: object containerSecurityContext: properties: allowPrivilegeEscalation: @@ -1475,6 +1561,92 @@ spec: - container - credentialsSecret type: object + containerOptions: + properties: + args: + properties: + xbcloud: + items: + type: string + type: array + xbstream: + items: + type: string + type: array + xtrabackup: + items: + type: string + type: array + type: object + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + 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: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + required: + - args + type: object containerSecurityContext: properties: allowPrivilegeEscalation: @@ -1898,6 +2070,92 @@ spec: type: object clusterName: type: string + containerOptions: + properties: + args: + properties: + xbcloud: + items: + type: string + type: array + xbstream: + items: + type: string + type: array + xtrabackup: + items: + type: string + type: array + type: object + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + 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: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + required: + - args + type: object required: - clusterName type: object @@ -3823,6 +4081,92 @@ spec: - container - credentialsSecret type: object + containerOptions: + properties: + args: + properties: + xbcloud: + items: + type: string + type: array + xbstream: + items: + type: string + type: array + xtrabackup: + items: + type: string + type: array + type: object + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + 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: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + required: + - args + type: object containerSecurityContext: properties: allowPrivilegeEscalation: diff --git a/deploy/cr.yaml b/deploy/cr.yaml index 78af79a94..3f25fe2db 100644 --- a/deploy/cr.yaml +++ b/deploy/cr.yaml @@ -530,6 +530,17 @@ spec: # fsGroup: 1001 # supplementalGroups: [1001, 1002, 1003] # runtimeClassName: image-rc +# containerOptions: +# env: +# - name: VERIFY_TLS +# value: "false" +# args: +# xtrabackup: +# - "--someflag=abc" +# xbcloud: +# - "--someflag=abc" +# xbstream: +# - "--someflag=abc" s3: bucket: S3-BACKUP-BUCKET-NAME-HERE credentialsSecret: cluster1-s3-credentials diff --git a/deploy/crd.yaml b/deploy/crd.yaml index 11599fa9c..7c0888d18 100644 --- a/deploy/crd.yaml +++ b/deploy/crd.yaml @@ -531,6 +531,92 @@ spec: - container - credentialsSecret type: object + containerOptions: + properties: + args: + properties: + xbcloud: + items: + type: string + type: array + xbstream: + items: + type: string + type: array + xtrabackup: + items: + type: string + type: array + type: object + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + 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: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + required: + - args + type: object containerSecurityContext: properties: allowPrivilegeEscalation: @@ -1475,6 +1561,92 @@ spec: - container - credentialsSecret type: object + containerOptions: + properties: + args: + properties: + xbcloud: + items: + type: string + type: array + xbstream: + items: + type: string + type: array + xtrabackup: + items: + type: string + type: array + type: object + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + 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: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + required: + - args + type: object containerSecurityContext: properties: allowPrivilegeEscalation: @@ -1898,6 +2070,92 @@ spec: type: object clusterName: type: string + containerOptions: + properties: + args: + properties: + xbcloud: + items: + type: string + type: array + xbstream: + items: + type: string + type: array + xtrabackup: + items: + type: string + type: array + type: object + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + 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: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + required: + - args + type: object required: - clusterName type: object @@ -3823,6 +4081,92 @@ spec: - container - credentialsSecret type: object + containerOptions: + properties: + args: + properties: + xbcloud: + items: + type: string + type: array + xbstream: + items: + type: string + type: array + xtrabackup: + items: + type: string + type: array + type: object + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + 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: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + required: + - args + type: object containerSecurityContext: properties: allowPrivilegeEscalation: diff --git a/deploy/cw-bundle.yaml b/deploy/cw-bundle.yaml index e8659ef47..010ddef17 100644 --- a/deploy/cw-bundle.yaml +++ b/deploy/cw-bundle.yaml @@ -531,6 +531,92 @@ spec: - container - credentialsSecret type: object + containerOptions: + properties: + args: + properties: + xbcloud: + items: + type: string + type: array + xbstream: + items: + type: string + type: array + xtrabackup: + items: + type: string + type: array + type: object + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + 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: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + required: + - args + type: object containerSecurityContext: properties: allowPrivilegeEscalation: @@ -1475,6 +1561,92 @@ spec: - container - credentialsSecret type: object + containerOptions: + properties: + args: + properties: + xbcloud: + items: + type: string + type: array + xbstream: + items: + type: string + type: array + xtrabackup: + items: + type: string + type: array + type: object + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + 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: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + required: + - args + type: object containerSecurityContext: properties: allowPrivilegeEscalation: @@ -1898,6 +2070,92 @@ spec: type: object clusterName: type: string + containerOptions: + properties: + args: + properties: + xbcloud: + items: + type: string + type: array + xbstream: + items: + type: string + type: array + xtrabackup: + items: + type: string + type: array + type: object + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + 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: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + required: + - args + type: object required: - clusterName type: object @@ -3823,6 +4081,92 @@ spec: - container - credentialsSecret type: object + containerOptions: + properties: + args: + properties: + xbcloud: + items: + type: string + type: array + xbstream: + items: + type: string + type: array + xtrabackup: + items: + type: string + type: array + type: object + env: + items: + properties: + name: + type: string + value: + type: string + valueFrom: + properties: + configMapKeyRef: + properties: + key: + type: string + name: + default: "" + 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: + default: "" + type: string + optional: + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + required: + - args + type: object containerSecurityContext: properties: allowPrivilegeEscalation: diff --git a/deploy/restore.yaml b/deploy/restore.yaml index f1cc7d511..5e2fbc55d 100644 --- a/deploy/restore.yaml +++ b/deploy/restore.yaml @@ -5,6 +5,17 @@ metadata: spec: clusterName: cluster1 backupName: backup1 +# containerOptions: +# env: +# - name: VERIFY_TLS +# value: "false" +# args: +# xtrabackup: +# - "--someflag=abc" +# xbcloud: +# - "--someflag=abc" +# xbstream: +# - "--someflag=abc" # backupSource: # destination: s3://S3-BACKUP-BUCKET-NAME-HERE/backup-path # storage: diff --git a/pkg/controller/psbackup/controller.go b/pkg/controller/psbackup/controller.go index a1688abee..ab130e656 100644 --- a/pkg/controller/psbackup/controller.go +++ b/pkg/controller/psbackup/controller.go @@ -254,7 +254,10 @@ func (r *PerconaServerMySQLBackupReconciler) createBackupJob(ctx context.Context if err != nil { return errors.Wrap(err, "get backup destination") } - job := xtrabackup.Job(cluster, cr, destination, initImage, storage) + job, err := xtrabackup.Job(cluster, cr, destination, initImage, storage) + if err != nil { + return errors.Wrap(err, "create backup job") + } switch storage.Type { case apiv1alpha1.BackupStorageS3: @@ -442,8 +445,9 @@ func (r *PerconaServerMySQLBackupReconciler) backupConfig(ctx context.Context, c return nil, errors.Wrap(err, "get backup destination") } conf := &xtrabackup.BackupConfig{ - Destination: destination.PathWithoutBucket(), - VerifyTLS: verifyTLS, + Destination: destination.PathWithoutBucket(), + VerifyTLS: verifyTLS, + ContainerOptions: storage.ContainerOptions, } s := new(corev1.Secret) nn := types.NamespacedName{ diff --git a/pkg/controller/psbackup/controller_test.go b/pkg/controller/psbackup/controller_test.go index f6fec7a3e..d48f3d079 100644 --- a/pkg/controller/psbackup/controller_test.go +++ b/pkg/controller/psbackup/controller_test.go @@ -367,7 +367,10 @@ func TestRunningState(t *testing.T) { if !ok { t.Fatal("storage not found") } - job := xtrabackup.Job(tt.cluster, tt.cr, "s3://bucket/container", "init-image", storage) + job, err := xtrabackup.Job(tt.cluster, tt.cr, "s3://bucket/container", "init-image", storage) + if err != nil { + t.Fatal(err) + } job.Status.Active = 1 cb := fake.NewClientBuilder().WithScheme(scheme).WithObjects(tt.cr, tt.cluster, job).WithStatusSubresource(tt.cr, tt.cluster, job) @@ -379,7 +382,7 @@ func TestRunningState(t *testing.T) { return tt.sidecarClient }, } - _, err := r.Reconcile(ctx, controllerruntime.Request{ + _, err = r.Reconcile(ctx, controllerruntime.Request{ NamespacedName: types.NamespacedName{ Name: tt.cr.Name, Namespace: tt.cr.Namespace, diff --git a/pkg/util/env.go b/pkg/util/env.go new file mode 100644 index 000000000..aaebdc7fc --- /dev/null +++ b/pkg/util/env.go @@ -0,0 +1,29 @@ +package util + +import ( + corev1 "k8s.io/api/core/v1" +) + +func MergeEnvLists(envLists ...[]corev1.EnvVar) []corev1.EnvVar { + resultList := make([]corev1.EnvVar, 0) + for _, list := range envLists { + for _, env := range list { + idx := FindEnvIndex(resultList, env.Name) + if idx == -1 { + resultList = append(resultList, env) + continue + } + resultList[idx] = env + } + } + return resultList +} + +func FindEnvIndex(envs []corev1.EnvVar, name string) int { + for i, env := range envs { + if env.Name == name { + return i + } + } + return -1 +} diff --git a/pkg/xtrabackup/xtrabackup.go b/pkg/xtrabackup/xtrabackup.go index e56137ec2..3106daf40 100644 --- a/pkg/xtrabackup/xtrabackup.go +++ b/pkg/xtrabackup/xtrabackup.go @@ -1,6 +1,7 @@ package xtrabackup import ( + "encoding/json" "fmt" "strconv" @@ -102,7 +103,7 @@ func Job( cr *apiv1alpha1.PerconaServerMySQLBackup, destination apiv1alpha1.BackupDestination, initImage string, storage *apiv1alpha1.BackupStorageSpec, -) *batchv1.Job { +) (*batchv1.Job, error) { var one int32 = 1 t := true @@ -111,6 +112,11 @@ func Job( if cluster.Spec.Backup.BackoffLimit != nil { backoffLimit = *cluster.Spec.Backup.BackoffLimit } + xbContainer, err := xtrabackupContainer(cluster, cr.Name, destination, storage) + if err != nil { + return nil, errors.Wrap(err, "xtrabackup container") + } + return &batchv1.Job{ TypeMeta: metav1.TypeMeta{ APIVersion: "batch/v1", @@ -145,7 +151,7 @@ func Job( ), }, Containers: []corev1.Container{ - xtrabackupContainer(cluster, cr.Name, destination, storage), + xbContainer, }, ImagePullSecrets: cluster.Spec.Backup.ImagePullSecrets, SecurityContext: storage.PodSecurityContext, @@ -190,16 +196,20 @@ func Job( }, }, }, - } + }, nil } -func xtrabackupContainer(cluster *apiv1alpha1.PerconaServerMySQL, backupName string, destination apiv1alpha1.BackupDestination, storage *apiv1alpha1.BackupStorageSpec) corev1.Container { +func xtrabackupContainer(cluster *apiv1alpha1.PerconaServerMySQL, backupName string, destination apiv1alpha1.BackupDestination, storage *apiv1alpha1.BackupStorageSpec) (corev1.Container, error) { spec := cluster.Spec.Backup verifyTLS := true if storage.VerifyTLS != nil { verifyTLS = *storage.VerifyTLS } + containerOptionsJSON, err := json.Marshal(storage.ContainerOptions) + if err != nil { + return corev1.Container{}, errors.Wrap(err, "marshal container options") + } return corev1.Container{ Name: appName, @@ -218,6 +228,10 @@ func xtrabackupContainer(cluster *apiv1alpha1.PerconaServerMySQL, backupName str Name: "VERIFY_TLS", Value: strconv.FormatBool(verifyTLS), }, + { + Name: "CONTAINER_OPTIONS", + Value: string(containerOptionsJSON), + }, }, VolumeMounts: []corev1.VolumeMount{ { @@ -238,7 +252,7 @@ func xtrabackupContainer(cluster *apiv1alpha1.PerconaServerMySQL, backupName str TerminationMessagePolicy: corev1.TerminationMessageReadFile, SecurityContext: storage.ContainerSecurityContext, Resources: storage.Resources, - } + }, nil } type XBCloudAction string @@ -255,6 +269,10 @@ func XBCloudArgs(action XBCloudAction, conf *BackupConfig) []string { args = append(args, "--insecure") } + if conf.ContainerOptions != nil { + args = append(args, conf.ContainerOptions.Args.Xbcloud...) + } + switch conf.Type { case apiv1alpha1.BackupStorageGCS: args = append( @@ -316,6 +334,7 @@ func deleteContainer(image string, conf *BackupConfig, storage *apiv1alpha1.Back MountPath: apiv1alpha1.BinVolumePath, }, }, + Env: storage.ContainerOptions.GetEnv(), Command: append([]string{"xbcloud"}, XBCloudArgs(XBCloudActionDelete, conf)...), TerminationMessagePath: "/dev/termination-log", TerminationMessagePolicy: corev1.TerminationMessageReadFile, @@ -508,11 +527,8 @@ func restoreContainer( verifyTLS = *storage.VerifyTLS } - container := corev1.Container{ - Name: appName, - Image: spec.Image, - ImagePullPolicy: spec.ImagePullPolicy, - Env: []corev1.EnvVar{ + envs := util.MergeEnvLists( + []corev1.EnvVar{ { Name: "RESTORE_NAME", Value: restore.Name, @@ -530,35 +546,42 @@ func restoreContainer( Value: fmt.Sprintf("%s/keyring_vault.conf", vaultSecretMountPath), }, }, - VolumeMounts: []corev1.VolumeMount{ - { - Name: apiv1alpha1.BinVolumeName, - MountPath: apiv1alpha1.BinVolumePath, - }, - { - Name: dataVolumeName, - MountPath: dataMountPath, - }, - { - Name: tlsVolumeName, - MountPath: tlsMountPath, - }, + restore.Spec.ContainerOptions.GetEnvVar(cluster, storage), + ) + + volumeMounts := []corev1.VolumeMount{ + { + Name: apiv1alpha1.BinVolumeName, + MountPath: apiv1alpha1.BinVolumePath, + }, + { + Name: dataVolumeName, + MountPath: dataMountPath, + }, + { + Name: tlsVolumeName, + MountPath: tlsMountPath, }, - Command: []string{"/opt/percona/run-restore.sh"}, - TerminationMessagePath: "/dev/termination-log", - TerminationMessagePolicy: corev1.TerminationMessageReadFile, - SecurityContext: storage.ContainerSecurityContext, - Resources: storage.Resources, } - if cluster.Spec.MySQL.VaultSecretName != "" { - container.VolumeMounts = append(container.VolumeMounts, corev1.VolumeMount{ + volumeMounts = append(volumeMounts, corev1.VolumeMount{ Name: vaultSecretVolumeName, MountPath: vaultSecretMountPath, }) } - return container + return corev1.Container{ + Name: appName, + Image: spec.Image, + ImagePullPolicy: spec.ImagePullPolicy, + Env: envs, + VolumeMounts: volumeMounts, + Command: []string{"/opt/percona/run-restore.sh"}, + TerminationMessagePath: "/dev/termination-log", + TerminationMessagePolicy: corev1.TerminationMessageReadFile, + SecurityContext: storage.ContainerSecurityContext, + Resources: storage.Resources, + } } func PVC(cluster *apiv1alpha1.PerconaServerMySQL, cr *apiv1alpha1.PerconaServerMySQLBackup, storage *apiv1alpha1.BackupStorageSpec) *corev1.PersistentVolumeClaim { @@ -767,10 +790,11 @@ func SetSourceNode(job *batchv1.Job, src string) error { } type BackupConfig struct { - Destination string `json:"destination"` - Type apiv1alpha1.BackupStorageType `json:"type"` - VerifyTLS bool `json:"verifyTLS,omitempty"` - S3 struct { + Destination string `json:"destination"` + Type apiv1alpha1.BackupStorageType `json:"type"` + VerifyTLS bool `json:"verifyTLS,omitempty"` + ContainerOptions *apiv1alpha1.BackupContainerOptions `json:"containerOptions,omitempty"` + S3 struct { Bucket string `json:"bucket"` Region string `json:"region,omitempty"` EndpointURL string `json:"endpointUrl,omitempty"` diff --git a/pkg/xtrabackup/xtrabackup_test.go b/pkg/xtrabackup/xtrabackup_test.go index 5a9825226..a00bdde3e 100644 --- a/pkg/xtrabackup/xtrabackup_test.go +++ b/pkg/xtrabackup/xtrabackup_test.go @@ -46,7 +46,10 @@ func TestJob(t *testing.T) { cluster := cr.DeepCopy() cr := backup.DeepCopy() - j := Job(cluster, cr, destination, initImage, cluster.Spec.Backup.Storages[storageName]) + j, err := Job(cluster, cr, destination, initImage, cluster.Spec.Backup.Storages[storageName]) + if err != nil { + t.Fatal(err) + } assert.NotNil(t, j) assert.Equal(t, "xb-backup-minio", j.Name) @@ -66,7 +69,10 @@ func TestJob(t *testing.T) { cluster := cr.DeepCopy() cr := backup.DeepCopy() - j := Job(cluster, cr, destination, initImage, cluster.Spec.Backup.Storages[storageName]) + j, err := Job(cluster, cr, destination, initImage, cluster.Spec.Backup.Storages[storageName]) + if err != nil { + t.Fatal(err) + } assert.Equal(t, []corev1.LocalObjectReference(nil), j.Spec.Template.Spec.ImagePullSecrets) imagePullSecrets := []corev1.LocalObjectReference{ @@ -79,7 +85,10 @@ func TestJob(t *testing.T) { } cluster.Spec.Backup.ImagePullSecrets = imagePullSecrets - j = Job(cluster, cr, destination, initImage, cluster.Spec.Backup.Storages[storageName]) + j, err = Job(cluster, cr, destination, initImage, cluster.Spec.Backup.Storages[storageName]) + if err != nil { + t.Fatal(err) + } assert.Equal(t, imagePullSecrets, j.Spec.Template.Spec.ImagePullSecrets) }) @@ -87,14 +96,20 @@ func TestJob(t *testing.T) { cluster := cr.DeepCopy() cr := backup.DeepCopy() - j := Job(cluster, cr, destination, initImage, cluster.Spec.Backup.Storages[storageName]) + j, err := Job(cluster, cr, destination, initImage, cluster.Spec.Backup.Storages[storageName]) + if err != nil { + t.Fatal(err) + } var e *string assert.Equal(t, e, j.Spec.Template.Spec.RuntimeClassName) const runtimeClassName = "runtimeClassName" cluster.Spec.Backup.Storages[storageName].RuntimeClassName = ptr.To(runtimeClassName) - j = Job(cluster, cr, destination, initImage, cluster.Spec.Backup.Storages[storageName]) + j, err = Job(cluster, cr, destination, initImage, cluster.Spec.Backup.Storages[storageName]) + if err != nil { + t.Fatal(err) + } assert.Equal(t, runtimeClassName, *j.Spec.Template.Spec.RuntimeClassName) }) }