make bucket prefix for logical backup configurable (#2609)
* make bucket prefix for logical backup configurable * include container comparison in logical backup diff * add unit test and update description for compareContainers * don't rely on users putting / in the config - reflect other comments from review
This commit is contained in:
parent
6ddafadc09
commit
83878fe447
|
|
@ -81,7 +81,7 @@ jobs:
|
|||
- name: Build and push multiarch logical-backup image to ghcr
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: docker/logical-backup
|
||||
context: logical-backup
|
||||
push: true
|
||||
build-args: BASE_IMAGE=ubuntu:22.04
|
||||
tags: "${{ steps.image_lb.outputs.BACKUP_IMAGE }}"
|
||||
|
|
|
|||
|
|
@ -528,6 +528,8 @@ spec:
|
|||
type: string
|
||||
logical_backup_s3_bucket:
|
||||
type: string
|
||||
logical_backup_s3_bucket_prefix:
|
||||
type: string
|
||||
logical_backup_s3_endpoint:
|
||||
type: string
|
||||
logical_backup_s3_region:
|
||||
|
|
|
|||
|
|
@ -372,6 +372,8 @@ configLogicalBackup:
|
|||
logical_backup_s3_access_key_id: ""
|
||||
# S3 bucket to store backup results
|
||||
logical_backup_s3_bucket: "my-bucket-url"
|
||||
# S3 bucket prefix to use
|
||||
logical_backup_s3_bucket_prefix: "spilo"
|
||||
# S3 region of bucket
|
||||
logical_backup_s3_region: ""
|
||||
# S3 endpoint url when not using AWS
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ pipeline:
|
|||
commands:
|
||||
- desc: Build image
|
||||
cmd: |
|
||||
cd docker/logical-backup
|
||||
cd logical-backup
|
||||
export TAG=$(git describe --tags --always --dirty)
|
||||
IMAGE="registry-write.opensource.zalan.do/acid/logical-backup"
|
||||
docker build --rm -t "$IMAGE:$TAG$CDP_TAG" .
|
||||
|
|
|
|||
|
|
@ -1283,7 +1283,7 @@ but only snapshots of your data. In its current state, see logical backups as a
|
|||
way to quickly create SQL dumps that you can easily restore in an empty test
|
||||
cluster.
|
||||
|
||||
2. The [example image](https://github.com/zalando/postgres-operator/blob/master/docker/logical-backup/Dockerfile) implements the backup
|
||||
2. The [example image](https://github.com/zalando/postgres-operator/blob/master/logical-backup/Dockerfile) implements the backup
|
||||
via `pg_dumpall` and upload of compressed and encrypted results to an S3 bucket.
|
||||
`pg_dumpall` requires a `superuser` access to a DB and runs on the replica when
|
||||
possible.
|
||||
|
|
|
|||
|
|
@ -813,9 +813,9 @@ grouped under the `logical_backup` key.
|
|||
default values from `postgres_pod_resources` will be used.
|
||||
|
||||
* **logical_backup_docker_image**
|
||||
An image for pods of the logical backup job. The [example image](https://github.com/zalando/postgres-operator/blob/master/docker/logical-backup/Dockerfile)
|
||||
An image for pods of the logical backup job. The [example image](https://github.com/zalando/postgres-operator/blob/master/logical-backup/Dockerfile)
|
||||
runs `pg_dumpall` on a replica if possible and uploads compressed results to
|
||||
an S3 bucket under the key `/spilo/pg_cluster_name/cluster_k8s_uuid/logical_backups`.
|
||||
an S3 bucket under the key `/<configured-s3-bucket-prefix>/<pg_cluster_name>/<cluster_k8s_uuid>/logical_backups`.
|
||||
The default image is the same image built with the Zalando-internal CI
|
||||
pipeline. Default: "registry.opensource.zalan.do/acid/logical-backup:v1.11.0"
|
||||
|
||||
|
|
@ -845,6 +845,9 @@ grouped under the `logical_backup` key.
|
|||
S3 bucket to store backup results. The bucket has to be present and
|
||||
accessible by Postgres pods. Default: empty.
|
||||
|
||||
* **logical_backup_s3_bucket_prefix**
|
||||
S3 bucket prefix to use in configured bucket. Default: "spilo"
|
||||
|
||||
* **logical_backup_s3_endpoint**
|
||||
When using non-AWS S3 storage, endpoint can be set as a ENV variable. The default is empty.
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ function compress {
|
|||
}
|
||||
|
||||
function az_upload {
|
||||
PATH_TO_BACKUP=$LOGICAL_BACKUP_S3_BUCKET"/spilo/"$SCOPE$LOGICAL_BACKUP_S3_BUCKET_SCOPE_SUFFIX"/logical_backups/"$(date +%s).sql.gz
|
||||
PATH_TO_BACKUP=$LOGICAL_BACKUP_S3_BUCKET"/"$LOGICAL_BACKUP_S3_BUCKET_PREFIX"/"$SCOPE$LOGICAL_BACKUP_S3_BUCKET_SCOPE_SUFFIX"/logical_backups/"$(date +%s).sql.gz
|
||||
|
||||
az storage blob upload --file "$1" --account-name "$LOGICAL_BACKUP_AZURE_STORAGE_ACCOUNT_NAME" --account-key "$LOGICAL_BACKUP_AZURE_STORAGE_ACCOUNT_KEY" -c "$LOGICAL_BACKUP_AZURE_STORAGE_CONTAINER" -n "$PATH_TO_BACKUP"
|
||||
}
|
||||
|
|
@ -72,7 +72,7 @@ function aws_delete_outdated {
|
|||
cutoff_date=$(date -d "$LOGICAL_BACKUP_S3_RETENTION_TIME ago" +%F)
|
||||
|
||||
# mimic bucket setup from Spilo
|
||||
prefix="spilo/"$SCOPE$LOGICAL_BACKUP_S3_BUCKET_SCOPE_SUFFIX"/logical_backups/"
|
||||
prefix=$LOGICAL_BACKUP_S3_BUCKET_PREFIX"/"$SCOPE$LOGICAL_BACKUP_S3_BUCKET_SCOPE_SUFFIX"/logical_backups/"
|
||||
|
||||
args=(
|
||||
"--no-paginate"
|
||||
|
|
@ -107,7 +107,7 @@ function aws_upload {
|
|||
# mimic bucket setup from Spilo
|
||||
# to keep logical backups at the same path as WAL
|
||||
# NB: $LOGICAL_BACKUP_S3_BUCKET_SCOPE_SUFFIX already contains the leading "/" when set by the Postgres Operator
|
||||
PATH_TO_BACKUP=s3://$LOGICAL_BACKUP_S3_BUCKET"/spilo/"$SCOPE$LOGICAL_BACKUP_S3_BUCKET_SCOPE_SUFFIX"/logical_backups/"$(date +%s).sql.gz
|
||||
PATH_TO_BACKUP=s3://$LOGICAL_BACKUP_S3_BUCKET"/"$LOGICAL_BACKUP_S3_BUCKET_PREFIX"/"$SCOPE$LOGICAL_BACKUP_S3_BUCKET_SCOPE_SUFFIX"/logical_backups/"$(date +%s).sql.gz
|
||||
|
||||
args=()
|
||||
|
||||
|
|
@ -120,7 +120,7 @@ function aws_upload {
|
|||
}
|
||||
|
||||
function gcs_upload {
|
||||
PATH_TO_BACKUP=gs://$LOGICAL_BACKUP_S3_BUCKET"/spilo/"$SCOPE$LOGICAL_BACKUP_S3_BUCKET_SCOPE_SUFFIX"/logical_backups/"$(date +%s).sql.gz
|
||||
PATH_TO_BACKUP=gs://$LOGICAL_BACKUP_S3_BUCKET"/"$LOGICAL_BACKUP_S3_BUCKET_PREFIX"/"$SCOPE$LOGICAL_BACKUP_S3_BUCKET_SCOPE_SUFFIX"/logical_backups/"$(date +%s).sql.gz
|
||||
|
||||
gsutil -o Credentials:gs_service_key_file=$LOGICAL_BACKUP_GOOGLE_APPLICATION_CREDENTIALS cp - "$PATH_TO_BACKUP"
|
||||
}
|
||||
|
|
@ -90,6 +90,7 @@ data:
|
|||
logical_backup_provider: "s3"
|
||||
# logical_backup_s3_access_key_id: ""
|
||||
logical_backup_s3_bucket: "my-bucket-url"
|
||||
# logical_backup_s3_bucket_prefix: "spilo"
|
||||
# logical_backup_s3_region: ""
|
||||
# logical_backup_s3_endpoint: ""
|
||||
# logical_backup_s3_secret_access_key: ""
|
||||
|
|
|
|||
|
|
@ -526,6 +526,8 @@ spec:
|
|||
type: string
|
||||
logical_backup_s3_bucket:
|
||||
type: string
|
||||
logical_backup_s3_bucket_prefix:
|
||||
type: string
|
||||
logical_backup_s3_endpoint:
|
||||
type: string
|
||||
logical_backup_s3_region:
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@ configuration:
|
|||
logical_backup_provider: "s3"
|
||||
# logical_backup_s3_access_key_id: ""
|
||||
logical_backup_s3_bucket: "my-bucket-url"
|
||||
# logical_backup_s3_bucket_prefix: "spilo"
|
||||
# logical_backup_s3_endpoint: ""
|
||||
# logical_backup_s3_region: ""
|
||||
# logical_backup_s3_secret_access_key: ""
|
||||
|
|
|
|||
|
|
@ -1762,6 +1762,9 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{
|
|||
"logical_backup_s3_bucket": {
|
||||
Type: "string",
|
||||
},
|
||||
"logical_backup_s3_bucket_prefix": {
|
||||
Type: "string",
|
||||
},
|
||||
"logical_backup_s3_endpoint": {
|
||||
Type: "string",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -228,6 +228,7 @@ type OperatorLogicalBackupConfiguration struct {
|
|||
AzureStorageContainer string `json:"logical_backup_azure_storage_container,omitempty"`
|
||||
AzureStorageAccountKey string `json:"logical_backup_azure_storage_account_key,omitempty"`
|
||||
S3Bucket string `json:"logical_backup_s3_bucket,omitempty"`
|
||||
S3BucketPrefix string `json:"logical_backup_s3_bucket_prefix,omitempty"`
|
||||
S3Region string `json:"logical_backup_s3_region,omitempty"`
|
||||
S3Endpoint string `json:"logical_backup_s3_endpoint,omitempty"`
|
||||
S3AccessKeyID string `json:"logical_backup_s3_access_key_id,omitempty"`
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/zalando/postgres-operator/pkg/util/users"
|
||||
"github.com/zalando/postgres-operator/pkg/util/volumes"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
policyv1 "k8s.io/api/policy/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
|
|
@ -438,8 +439,8 @@ func (c *Cluster) compareStatefulSetWith(statefulSet *appsv1.StatefulSet) *compa
|
|||
reasons = append(reasons, "new statefulset's persistent volume claim retention policy do not match")
|
||||
}
|
||||
|
||||
needsRollUpdate, reasons = c.compareContainers("initContainers", c.Statefulset.Spec.Template.Spec.InitContainers, statefulSet.Spec.Template.Spec.InitContainers, needsRollUpdate, reasons)
|
||||
needsRollUpdate, reasons = c.compareContainers("containers", c.Statefulset.Spec.Template.Spec.Containers, statefulSet.Spec.Template.Spec.Containers, needsRollUpdate, reasons)
|
||||
needsRollUpdate, reasons = c.compareContainers("statefulset initContainers", c.Statefulset.Spec.Template.Spec.InitContainers, statefulSet.Spec.Template.Spec.InitContainers, needsRollUpdate, reasons)
|
||||
needsRollUpdate, reasons = c.compareContainers("statefulset containers", c.Statefulset.Spec.Template.Spec.Containers, statefulSet.Spec.Template.Spec.Containers, needsRollUpdate, reasons)
|
||||
|
||||
if len(c.Statefulset.Spec.Template.Spec.Containers) == 0 {
|
||||
c.logger.Warningf("statefulset %q has no container", util.NameFromMeta(c.Statefulset.ObjectMeta))
|
||||
|
|
@ -571,30 +572,30 @@ func newCheck(msg string, cond containerCondition) containerCheck {
|
|||
|
||||
func (c *Cluster) compareContainers(description string, setA, setB []v1.Container, needsRollUpdate bool, reasons []string) (bool, []string) {
|
||||
if len(setA) != len(setB) {
|
||||
return true, append(reasons, fmt.Sprintf("new statefulset %s's length does not match the current ones", description))
|
||||
return true, append(reasons, fmt.Sprintf("new %s's length does not match the current ones", description))
|
||||
}
|
||||
|
||||
checks := []containerCheck{
|
||||
newCheck("new statefulset %s's %s (index %d) name does not match the current one",
|
||||
newCheck("new %s's %s (index %d) name does not match the current one",
|
||||
func(a, b v1.Container) bool { return a.Name != b.Name }),
|
||||
newCheck("new statefulset %s's %s (index %d) readiness probe does not match the current one",
|
||||
newCheck("new %s's %s (index %d) readiness probe does not match the current one",
|
||||
func(a, b v1.Container) bool { return !reflect.DeepEqual(a.ReadinessProbe, b.ReadinessProbe) }),
|
||||
newCheck("new statefulset %s's %s (index %d) ports do not match the current one",
|
||||
newCheck("new %s's %s (index %d) ports do not match the current one",
|
||||
func(a, b v1.Container) bool { return !comparePorts(a.Ports, b.Ports) }),
|
||||
newCheck("new statefulset %s's %s (index %d) resources do not match the current ones",
|
||||
newCheck("new %s's %s (index %d) resources do not match the current ones",
|
||||
func(a, b v1.Container) bool { return !compareResources(&a.Resources, &b.Resources) }),
|
||||
newCheck("new statefulset %s's %s (index %d) environment does not match the current one",
|
||||
newCheck("new %s's %s (index %d) environment does not match the current one",
|
||||
func(a, b v1.Container) bool { return !compareEnv(a.Env, b.Env) }),
|
||||
newCheck("new statefulset %s's %s (index %d) environment sources do not match the current one",
|
||||
newCheck("new %s's %s (index %d) environment sources do not match the current one",
|
||||
func(a, b v1.Container) bool { return !reflect.DeepEqual(a.EnvFrom, b.EnvFrom) }),
|
||||
newCheck("new statefulset %s's %s (index %d) security context does not match the current one",
|
||||
newCheck("new %s's %s (index %d) security context does not match the current one",
|
||||
func(a, b v1.Container) bool { return !reflect.DeepEqual(a.SecurityContext, b.SecurityContext) }),
|
||||
newCheck("new statefulset %s's %s (index %d) volume mounts do not match the current one",
|
||||
newCheck("new %s's %s (index %d) volume mounts do not match the current one",
|
||||
func(a, b v1.Container) bool { return !reflect.DeepEqual(a.VolumeMounts, b.VolumeMounts) }),
|
||||
}
|
||||
|
||||
if !c.OpConfig.EnableLazySpiloUpgrade {
|
||||
checks = append(checks, newCheck("new statefulset %s's %s (index %d) image does not match the current one",
|
||||
checks = append(checks, newCheck("new %s's %s (index %d) image does not match the current one",
|
||||
func(a, b v1.Container) bool { return a.Image != b.Image }))
|
||||
}
|
||||
|
||||
|
|
@ -786,6 +787,47 @@ func (c *Cluster) compareServices(old, new *v1.Service) (bool, string) {
|
|||
return true, ""
|
||||
}
|
||||
|
||||
func (c *Cluster) compareLogicalBackupJob(cur, new *batchv1.CronJob) (match bool, reason string) {
|
||||
|
||||
if cur.Spec.Schedule != new.Spec.Schedule {
|
||||
return false, fmt.Sprintf("new job's schedule %q does not match the current one %q",
|
||||
new.Spec.Schedule, cur.Spec.Schedule)
|
||||
}
|
||||
|
||||
newImage := new.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Image
|
||||
curImage := cur.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Image
|
||||
if newImage != curImage {
|
||||
return false, fmt.Sprintf("new job's image %q does not match the current one %q",
|
||||
newImage, curImage)
|
||||
}
|
||||
|
||||
newPgVersion := getPgVersion(new)
|
||||
curPgVersion := getPgVersion(cur)
|
||||
if newPgVersion != curPgVersion {
|
||||
return false, fmt.Sprintf("new job's env PG_VERSION %q does not match the current one %q",
|
||||
newPgVersion, curPgVersion)
|
||||
}
|
||||
|
||||
needsReplace := false
|
||||
reasons := make([]string, 0)
|
||||
needsReplace, reasons = c.compareContainers("cronjob container", cur.Spec.JobTemplate.Spec.Template.Spec.Containers, new.Spec.JobTemplate.Spec.Template.Spec.Containers, needsReplace, reasons)
|
||||
if needsReplace {
|
||||
return false, fmt.Sprintf("logical backup container specs do not match: %v", strings.Join(reasons, `', '`))
|
||||
}
|
||||
|
||||
return true, ""
|
||||
}
|
||||
|
||||
func getPgVersion(cronJob *batchv1.CronJob) string {
|
||||
envs := cronJob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Env
|
||||
for _, env := range envs {
|
||||
if env.Name == "PG_VERSION" {
|
||||
return env.Value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// addFinalizer patches the postgresql CR to add finalizer
|
||||
func (c *Cluster) addFinalizer() error {
|
||||
if c.hasFinalizer() {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import (
|
|||
"github.com/zalando/postgres-operator/pkg/util/constants"
|
||||
"github.com/zalando/postgres-operator/pkg/util/k8sutil"
|
||||
"github.com/zalando/postgres-operator/pkg/util/teams"
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
|
|
@ -1649,16 +1650,92 @@ func TestCompareServices(t *testing.T) {
|
|||
if match && !tt.match {
|
||||
t.Logf("match=%v current=%v, old=%v reason=%s", match, tt.current.Annotations, tt.new.Annotations, reason)
|
||||
t.Errorf("%s - expected services to do not match: %q and %q", t.Name(), tt.current, tt.new)
|
||||
return
|
||||
}
|
||||
if !match && tt.match {
|
||||
t.Errorf("%s - expected services to be the same: %q and %q", t.Name(), tt.current, tt.new)
|
||||
return
|
||||
}
|
||||
if !match && !tt.match {
|
||||
if !strings.HasPrefix(reason, tt.reason) {
|
||||
t.Errorf("%s - expected reason prefix %s, found %s", t.Name(), tt.reason, reason)
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func newCronJob(image, schedule string, vars []v1.EnvVar) *batchv1.CronJob {
|
||||
cron := &batchv1.CronJob{
|
||||
Spec: batchv1.CronJobSpec{
|
||||
Schedule: schedule,
|
||||
JobTemplate: batchv1.JobTemplateSpec{
|
||||
Spec: batchv1.JobSpec{
|
||||
Template: v1.PodTemplateSpec{
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
v1.Container{
|
||||
Name: "logical-backup",
|
||||
Image: image,
|
||||
Env: vars,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return cron
|
||||
}
|
||||
|
||||
func TestCompareLogicalBackupJob(t *testing.T) {
|
||||
|
||||
img1 := "registry.opensource.zalan.do/acid/logical-backup:v1.0"
|
||||
img2 := "registry.opensource.zalan.do/acid/logical-backup:v2.0"
|
||||
|
||||
tests := []struct {
|
||||
about string
|
||||
current *batchv1.CronJob
|
||||
new *batchv1.CronJob
|
||||
match bool
|
||||
reason string
|
||||
}{
|
||||
{
|
||||
about: "two equal cronjobs",
|
||||
current: newCronJob(img1, "0 0 * * *", []v1.EnvVar{}),
|
||||
new: newCronJob(img1, "0 0 * * *", []v1.EnvVar{}),
|
||||
match: true,
|
||||
},
|
||||
{
|
||||
about: "two cronjobs with different image",
|
||||
current: newCronJob(img1, "0 0 * * *", []v1.EnvVar{}),
|
||||
new: newCronJob(img2, "0 0 * * *", []v1.EnvVar{}),
|
||||
match: false,
|
||||
reason: fmt.Sprintf("new job's image %q does not match the current one %q", img2, img1),
|
||||
},
|
||||
{
|
||||
about: "two cronjobs with different schedule",
|
||||
current: newCronJob(img1, "0 0 * * *", []v1.EnvVar{}),
|
||||
new: newCronJob(img1, "0 * * * *", []v1.EnvVar{}),
|
||||
match: false,
|
||||
reason: fmt.Sprintf("new job's schedule %q does not match the current one %q", "0 * * * *", "0 0 * * *"),
|
||||
},
|
||||
{
|
||||
about: "two cronjobs with different environment variables",
|
||||
current: newCronJob(img1, "0 0 * * *", []v1.EnvVar{{Name: "LOGICAL_BACKUP_S3_BUCKET_PREFIX", Value: "spilo"}}),
|
||||
new: newCronJob(img1, "0 0 * * *", []v1.EnvVar{{Name: "LOGICAL_BACKUP_S3_BUCKET_PREFIX", Value: "logical-backup"}}),
|
||||
match: false,
|
||||
reason: "logical backup container specs do not match: new cronjob container's logical-backup (index 0) environment does not match the current one",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.about, func(t *testing.T) {
|
||||
match, reason := cl.compareLogicalBackupJob(tt.current, tt.new)
|
||||
if match != tt.match {
|
||||
t.Errorf("%s - unexpected match result %t when comparing cronjobs %q and %q", t.Name(), match, tt.current, tt.new)
|
||||
} else {
|
||||
if !strings.HasPrefix(reason, tt.reason) {
|
||||
t.Errorf("%s - expected reason prefix %s, found %s", t.Name(), tt.reason, reason)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -2403,6 +2403,10 @@ func (c *Cluster) generateLogicalBackupPodEnvVars() []v1.EnvVar {
|
|||
Name: "LOGICAL_BACKUP_S3_RETENTION_TIME",
|
||||
Value: c.OpConfig.LogicalBackup.LogicalBackupS3RetentionTime,
|
||||
},
|
||||
{
|
||||
Name: "LOGICAL_BACKUP_S3_BUCKET_PREFIX",
|
||||
Value: c.OpConfig.LogicalBackup.LogicalBackupS3BucketPrefix,
|
||||
},
|
||||
{
|
||||
Name: "LOGICAL_BACKUP_S3_BUCKET_SCOPE_SUFFIX",
|
||||
Value: getBucketScopeSuffix(string(c.Postgresql.GetUID())),
|
||||
|
|
|
|||
|
|
@ -1367,7 +1367,7 @@ func (c *Cluster) syncLogicalBackupJob() error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("could not generate the desired logical backup job state: %v", err)
|
||||
}
|
||||
if match, reason := k8sutil.SameLogicalBackupJob(job, desiredJob); !match {
|
||||
if match, reason := c.compareLogicalBackupJob(job, desiredJob); !match {
|
||||
c.logger.Infof("logical job %s is not in the desired state and needs to be updated",
|
||||
c.getLogicalBackupJobName(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -184,6 +184,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
|
|||
result.LogicalBackupAzureStorageAccountKey = fromCRD.LogicalBackup.AzureStorageAccountKey
|
||||
result.LogicalBackupAzureStorageContainer = fromCRD.LogicalBackup.AzureStorageContainer
|
||||
result.LogicalBackupS3Bucket = fromCRD.LogicalBackup.S3Bucket
|
||||
result.LogicalBackupS3BucketPrefix = util.Coalesce(fromCRD.LogicalBackup.S3BucketPrefix, "spilo")
|
||||
result.LogicalBackupS3Region = fromCRD.LogicalBackup.S3Region
|
||||
result.LogicalBackupS3Endpoint = fromCRD.LogicalBackup.S3Endpoint
|
||||
result.LogicalBackupS3AccessKeyID = fromCRD.LogicalBackup.S3AccessKeyID
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@ type LogicalBackup struct {
|
|||
LogicalBackupAzureStorageContainer string `name:"logical_backup_azure_storage_container" default:""`
|
||||
LogicalBackupAzureStorageAccountKey string `name:"logical_backup_azure_storage_account_key" default:""`
|
||||
LogicalBackupS3Bucket string `name:"logical_backup_s3_bucket" default:""`
|
||||
LogicalBackupS3BucketPrefix string `name:"logical_backup_s3_bucket_prefix" default:"spilo"`
|
||||
LogicalBackupS3Region string `name:"logical_backup_s3_region" default:""`
|
||||
LogicalBackupS3Endpoint string `name:"logical_backup_s3_endpoint" default:""`
|
||||
LogicalBackupS3AccessKeyID string `name:"logical_backup_s3_access_key_id" default:""`
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import (
|
|||
b64 "encoding/base64"
|
||||
"encoding/json"
|
||||
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
clientbatchv1 "k8s.io/client-go/kubernetes/typed/batch/v1"
|
||||
|
||||
apiacidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
|
||||
|
|
@ -254,45 +253,6 @@ func SamePDB(cur, new *apipolicyv1.PodDisruptionBudget) (match bool, reason stri
|
|||
return
|
||||
}
|
||||
|
||||
func getJobImage(cronJob *batchv1.CronJob) string {
|
||||
return cronJob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Image
|
||||
}
|
||||
|
||||
func getPgVersion(cronJob *batchv1.CronJob) string {
|
||||
envs := cronJob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Env
|
||||
for _, env := range envs {
|
||||
if env.Name == "PG_VERSION" {
|
||||
return env.Value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// SameLogicalBackupJob compares Specs of logical backup cron jobs
|
||||
func SameLogicalBackupJob(cur, new *batchv1.CronJob) (match bool, reason string) {
|
||||
|
||||
if cur.Spec.Schedule != new.Spec.Schedule {
|
||||
return false, fmt.Sprintf("new job's schedule %q does not match the current one %q",
|
||||
new.Spec.Schedule, cur.Spec.Schedule)
|
||||
}
|
||||
|
||||
newImage := getJobImage(new)
|
||||
curImage := getJobImage(cur)
|
||||
if newImage != curImage {
|
||||
return false, fmt.Sprintf("new job's image %q does not match the current one %q",
|
||||
newImage, curImage)
|
||||
}
|
||||
|
||||
newPgVersion := getPgVersion(new)
|
||||
curPgVersion := getPgVersion(cur)
|
||||
if newPgVersion != curPgVersion {
|
||||
return false, fmt.Sprintf("new job's env PG_VERSION %q does not match the current one %q",
|
||||
newPgVersion, curPgVersion)
|
||||
}
|
||||
|
||||
return true, ""
|
||||
}
|
||||
|
||||
func (c *mockSecret) Get(ctx context.Context, name string, options metav1.GetOptions) (*v1.Secret, error) {
|
||||
oldFormatSecret := &v1.Secret{}
|
||||
oldFormatSecret.Name = "testcluster"
|
||||
|
|
|
|||
Loading…
Reference in New Issue