Skip to content

Commit 83878fe

Browse files
authored
make bucket prefix for logical backup configurable (zalando#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
1 parent 6ddafad commit 83878fe

20 files changed

+165
-65
lines changed

.github/workflows/publish_ghcr_image.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ jobs:
8181
- name: Build and push multiarch logical-backup image to ghcr
8282
uses: docker/build-push-action@v3
8383
with:
84-
context: docker/logical-backup
84+
context: logical-backup
8585
push: true
8686
build-args: BASE_IMAGE=ubuntu:22.04
8787
tags: "${{ steps.image_lb.outputs.BACKUP_IMAGE }}"

charts/postgres-operator/crds/operatorconfigurations.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,8 @@ spec:
528528
type: string
529529
logical_backup_s3_bucket:
530530
type: string
531+
logical_backup_s3_bucket_prefix:
532+
type: string
531533
logical_backup_s3_endpoint:
532534
type: string
533535
logical_backup_s3_region:

charts/postgres-operator/values.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,8 @@ configLogicalBackup:
372372
logical_backup_s3_access_key_id: ""
373373
# S3 bucket to store backup results
374374
logical_backup_s3_bucket: "my-bucket-url"
375+
# S3 bucket prefix to use
376+
logical_backup_s3_bucket_prefix: "spilo"
375377
# S3 region of bucket
376378
logical_backup_s3_region: ""
377379
# S3 endpoint url when not using AWS

delivery.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ pipeline:
9090
commands:
9191
- desc: Build image
9292
cmd: |
93-
cd docker/logical-backup
93+
cd logical-backup
9494
export TAG=$(git describe --tags --always --dirty)
9595
IMAGE="registry-write.opensource.zalan.do/acid/logical-backup"
9696
docker build --rm -t "$IMAGE:$TAG$CDP_TAG" .

docs/administrator.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1283,7 +1283,7 @@ but only snapshots of your data. In its current state, see logical backups as a
12831283
way to quickly create SQL dumps that you can easily restore in an empty test
12841284
cluster.
12851285

1286-
2. The [example image](https://github.com/zalando/postgres-operator/blob/master/docker/logical-backup/Dockerfile) implements the backup
1286+
2. The [example image](https://github.com/zalando/postgres-operator/blob/master/logical-backup/Dockerfile) implements the backup
12871287
via `pg_dumpall` and upload of compressed and encrypted results to an S3 bucket.
12881288
`pg_dumpall` requires a `superuser` access to a DB and runs on the replica when
12891289
possible.

docs/reference/operator_parameters.md

+5-2
Original file line numberDiff line numberDiff line change
@@ -813,9 +813,9 @@ grouped under the `logical_backup` key.
813813
default values from `postgres_pod_resources` will be used.
814814

815815
* **logical_backup_docker_image**
816-
An image for pods of the logical backup job. The [example image](https://github.com/zalando/postgres-operator/blob/master/docker/logical-backup/Dockerfile)
816+
An image for pods of the logical backup job. The [example image](https://github.com/zalando/postgres-operator/blob/master/logical-backup/Dockerfile)
817817
runs `pg_dumpall` on a replica if possible and uploads compressed results to
818-
an S3 bucket under the key `/spilo/pg_cluster_name/cluster_k8s_uuid/logical_backups`.
818+
an S3 bucket under the key `/<configured-s3-bucket-prefix>/<pg_cluster_name>/<cluster_k8s_uuid>/logical_backups`.
819819
The default image is the same image built with the Zalando-internal CI
820820
pipeline. Default: "registry.opensource.zalan.do/acid/logical-backup:v1.11.0"
821821

@@ -845,6 +845,9 @@ grouped under the `logical_backup` key.
845845
S3 bucket to store backup results. The bucket has to be present and
846846
accessible by Postgres pods. Default: empty.
847847

848+
* **logical_backup_s3_bucket_prefix**
849+
S3 bucket prefix to use in configured bucket. Default: "spilo"
850+
848851
* **logical_backup_s3_endpoint**
849852
When using non-AWS S3 storage, endpoint can be set as a ENV variable. The default is empty.
850853

File renamed without changes.

docker/logical-backup/dump.sh renamed to logical-backup/dump.sh

+4-4
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ function compress {
4545
}
4646

4747
function az_upload {
48-
PATH_TO_BACKUP=$LOGICAL_BACKUP_S3_BUCKET"/spilo/"$SCOPE$LOGICAL_BACKUP_S3_BUCKET_SCOPE_SUFFIX"/logical_backups/"$(date +%s).sql.gz
48+
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
4949

5050
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"
5151
}
@@ -72,7 +72,7 @@ function aws_delete_outdated {
7272
cutoff_date=$(date -d "$LOGICAL_BACKUP_S3_RETENTION_TIME ago" +%F)
7373

7474
# mimic bucket setup from Spilo
75-
prefix="spilo/"$SCOPE$LOGICAL_BACKUP_S3_BUCKET_SCOPE_SUFFIX"/logical_backups/"
75+
prefix=$LOGICAL_BACKUP_S3_BUCKET_PREFIX"/"$SCOPE$LOGICAL_BACKUP_S3_BUCKET_SCOPE_SUFFIX"/logical_backups/"
7676

7777
args=(
7878
"--no-paginate"
@@ -107,7 +107,7 @@ function aws_upload {
107107
# mimic bucket setup from Spilo
108108
# to keep logical backups at the same path as WAL
109109
# NB: $LOGICAL_BACKUP_S3_BUCKET_SCOPE_SUFFIX already contains the leading "/" when set by the Postgres Operator
110-
PATH_TO_BACKUP=s3://$LOGICAL_BACKUP_S3_BUCKET"/spilo/"$SCOPE$LOGICAL_BACKUP_S3_BUCKET_SCOPE_SUFFIX"/logical_backups/"$(date +%s).sql.gz
110+
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
111111

112112
args=()
113113

@@ -120,7 +120,7 @@ function aws_upload {
120120
}
121121

122122
function gcs_upload {
123-
PATH_TO_BACKUP=gs://$LOGICAL_BACKUP_S3_BUCKET"/spilo/"$SCOPE$LOGICAL_BACKUP_S3_BUCKET_SCOPE_SUFFIX"/logical_backups/"$(date +%s).sql.gz
123+
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
124124

125125
gsutil -o Credentials:gs_service_key_file=$LOGICAL_BACKUP_GOOGLE_APPLICATION_CREDENTIALS cp - "$PATH_TO_BACKUP"
126126
}

manifests/configmap.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ data:
9090
logical_backup_provider: "s3"
9191
# logical_backup_s3_access_key_id: ""
9292
logical_backup_s3_bucket: "my-bucket-url"
93+
# logical_backup_s3_bucket_prefix: "spilo"
9394
# logical_backup_s3_region: ""
9495
# logical_backup_s3_endpoint: ""
9596
# logical_backup_s3_secret_access_key: ""

manifests/operatorconfiguration.crd.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,8 @@ spec:
526526
type: string
527527
logical_backup_s3_bucket:
528528
type: string
529+
logical_backup_s3_bucket_prefix:
530+
type: string
529531
logical_backup_s3_endpoint:
530532
type: string
531533
logical_backup_s3_region:

manifests/postgresql-operator-default-configuration.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ configuration:
172172
logical_backup_provider: "s3"
173173
# logical_backup_s3_access_key_id: ""
174174
logical_backup_s3_bucket: "my-bucket-url"
175+
# logical_backup_s3_bucket_prefix: "spilo"
175176
# logical_backup_s3_endpoint: ""
176177
# logical_backup_s3_region: ""
177178
# logical_backup_s3_secret_access_key: ""

pkg/apis/acid.zalan.do/v1/crds.go

+3
Original file line numberDiff line numberDiff line change
@@ -1762,6 +1762,9 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{
17621762
"logical_backup_s3_bucket": {
17631763
Type: "string",
17641764
},
1765+
"logical_backup_s3_bucket_prefix": {
1766+
Type: "string",
1767+
},
17651768
"logical_backup_s3_endpoint": {
17661769
Type: "string",
17671770
},

pkg/apis/acid.zalan.do/v1/operator_configuration_type.go

+1
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ type OperatorLogicalBackupConfiguration struct {
228228
AzureStorageContainer string `json:"logical_backup_azure_storage_container,omitempty"`
229229
AzureStorageAccountKey string `json:"logical_backup_azure_storage_account_key,omitempty"`
230230
S3Bucket string `json:"logical_backup_s3_bucket,omitempty"`
231+
S3BucketPrefix string `json:"logical_backup_s3_bucket_prefix,omitempty"`
231232
S3Region string `json:"logical_backup_s3_region,omitempty"`
232233
S3Endpoint string `json:"logical_backup_s3_endpoint,omitempty"`
233234
S3AccessKeyID string `json:"logical_backup_s3_access_key_id,omitempty"`

pkg/cluster/cluster.go

+54-12
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/zalando/postgres-operator/pkg/util/users"
2929
"github.com/zalando/postgres-operator/pkg/util/volumes"
3030
appsv1 "k8s.io/api/apps/v1"
31+
batchv1 "k8s.io/api/batch/v1"
3132
v1 "k8s.io/api/core/v1"
3233
policyv1 "k8s.io/api/policy/v1"
3334
rbacv1 "k8s.io/api/rbac/v1"
@@ -438,8 +439,8 @@ func (c *Cluster) compareStatefulSetWith(statefulSet *appsv1.StatefulSet) *compa
438439
reasons = append(reasons, "new statefulset's persistent volume claim retention policy do not match")
439440
}
440441

441-
needsRollUpdate, reasons = c.compareContainers("initContainers", c.Statefulset.Spec.Template.Spec.InitContainers, statefulSet.Spec.Template.Spec.InitContainers, needsRollUpdate, reasons)
442-
needsRollUpdate, reasons = c.compareContainers("containers", c.Statefulset.Spec.Template.Spec.Containers, statefulSet.Spec.Template.Spec.Containers, needsRollUpdate, reasons)
442+
needsRollUpdate, reasons = c.compareContainers("statefulset initContainers", c.Statefulset.Spec.Template.Spec.InitContainers, statefulSet.Spec.Template.Spec.InitContainers, needsRollUpdate, reasons)
443+
needsRollUpdate, reasons = c.compareContainers("statefulset containers", c.Statefulset.Spec.Template.Spec.Containers, statefulSet.Spec.Template.Spec.Containers, needsRollUpdate, reasons)
443444

444445
if len(c.Statefulset.Spec.Template.Spec.Containers) == 0 {
445446
c.logger.Warningf("statefulset %q has no container", util.NameFromMeta(c.Statefulset.ObjectMeta))
@@ -571,30 +572,30 @@ func newCheck(msg string, cond containerCondition) containerCheck {
571572

572573
func (c *Cluster) compareContainers(description string, setA, setB []v1.Container, needsRollUpdate bool, reasons []string) (bool, []string) {
573574
if len(setA) != len(setB) {
574-
return true, append(reasons, fmt.Sprintf("new statefulset %s's length does not match the current ones", description))
575+
return true, append(reasons, fmt.Sprintf("new %s's length does not match the current ones", description))
575576
}
576577

577578
checks := []containerCheck{
578-
newCheck("new statefulset %s's %s (index %d) name does not match the current one",
579+
newCheck("new %s's %s (index %d) name does not match the current one",
579580
func(a, b v1.Container) bool { return a.Name != b.Name }),
580-
newCheck("new statefulset %s's %s (index %d) readiness probe does not match the current one",
581+
newCheck("new %s's %s (index %d) readiness probe does not match the current one",
581582
func(a, b v1.Container) bool { return !reflect.DeepEqual(a.ReadinessProbe, b.ReadinessProbe) }),
582-
newCheck("new statefulset %s's %s (index %d) ports do not match the current one",
583+
newCheck("new %s's %s (index %d) ports do not match the current one",
583584
func(a, b v1.Container) bool { return !comparePorts(a.Ports, b.Ports) }),
584-
newCheck("new statefulset %s's %s (index %d) resources do not match the current ones",
585+
newCheck("new %s's %s (index %d) resources do not match the current ones",
585586
func(a, b v1.Container) bool { return !compareResources(&a.Resources, &b.Resources) }),
586-
newCheck("new statefulset %s's %s (index %d) environment does not match the current one",
587+
newCheck("new %s's %s (index %d) environment does not match the current one",
587588
func(a, b v1.Container) bool { return !compareEnv(a.Env, b.Env) }),
588-
newCheck("new statefulset %s's %s (index %d) environment sources do not match the current one",
589+
newCheck("new %s's %s (index %d) environment sources do not match the current one",
589590
func(a, b v1.Container) bool { return !reflect.DeepEqual(a.EnvFrom, b.EnvFrom) }),
590-
newCheck("new statefulset %s's %s (index %d) security context does not match the current one",
591+
newCheck("new %s's %s (index %d) security context does not match the current one",
591592
func(a, b v1.Container) bool { return !reflect.DeepEqual(a.SecurityContext, b.SecurityContext) }),
592-
newCheck("new statefulset %s's %s (index %d) volume mounts do not match the current one",
593+
newCheck("new %s's %s (index %d) volume mounts do not match the current one",
593594
func(a, b v1.Container) bool { return !reflect.DeepEqual(a.VolumeMounts, b.VolumeMounts) }),
594595
}
595596

596597
if !c.OpConfig.EnableLazySpiloUpgrade {
597-
checks = append(checks, newCheck("new statefulset %s's %s (index %d) image does not match the current one",
598+
checks = append(checks, newCheck("new %s's %s (index %d) image does not match the current one",
598599
func(a, b v1.Container) bool { return a.Image != b.Image }))
599600
}
600601

@@ -786,6 +787,47 @@ func (c *Cluster) compareServices(old, new *v1.Service) (bool, string) {
786787
return true, ""
787788
}
788789

790+
func (c *Cluster) compareLogicalBackupJob(cur, new *batchv1.CronJob) (match bool, reason string) {
791+
792+
if cur.Spec.Schedule != new.Spec.Schedule {
793+
return false, fmt.Sprintf("new job's schedule %q does not match the current one %q",
794+
new.Spec.Schedule, cur.Spec.Schedule)
795+
}
796+
797+
newImage := new.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Image
798+
curImage := cur.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Image
799+
if newImage != curImage {
800+
return false, fmt.Sprintf("new job's image %q does not match the current one %q",
801+
newImage, curImage)
802+
}
803+
804+
newPgVersion := getPgVersion(new)
805+
curPgVersion := getPgVersion(cur)
806+
if newPgVersion != curPgVersion {
807+
return false, fmt.Sprintf("new job's env PG_VERSION %q does not match the current one %q",
808+
newPgVersion, curPgVersion)
809+
}
810+
811+
needsReplace := false
812+
reasons := make([]string, 0)
813+
needsReplace, reasons = c.compareContainers("cronjob container", cur.Spec.JobTemplate.Spec.Template.Spec.Containers, new.Spec.JobTemplate.Spec.Template.Spec.Containers, needsReplace, reasons)
814+
if needsReplace {
815+
return false, fmt.Sprintf("logical backup container specs do not match: %v", strings.Join(reasons, `', '`))
816+
}
817+
818+
return true, ""
819+
}
820+
821+
func getPgVersion(cronJob *batchv1.CronJob) string {
822+
envs := cronJob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Env
823+
for _, env := range envs {
824+
if env.Name == "PG_VERSION" {
825+
return env.Value
826+
}
827+
}
828+
return ""
829+
}
830+
789831
// addFinalizer patches the postgresql CR to add finalizer
790832
func (c *Cluster) addFinalizer() error {
791833
if c.hasFinalizer() {

pkg/cluster/cluster_test.go

+80-3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"github.com/zalando/postgres-operator/pkg/util/constants"
2020
"github.com/zalando/postgres-operator/pkg/util/k8sutil"
2121
"github.com/zalando/postgres-operator/pkg/util/teams"
22+
batchv1 "k8s.io/api/batch/v1"
2223
v1 "k8s.io/api/core/v1"
2324
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2425
"k8s.io/client-go/kubernetes/fake"
@@ -1649,16 +1650,92 @@ func TestCompareServices(t *testing.T) {
16491650
if match && !tt.match {
16501651
t.Logf("match=%v current=%v, old=%v reason=%s", match, tt.current.Annotations, tt.new.Annotations, reason)
16511652
t.Errorf("%s - expected services to do not match: %q and %q", t.Name(), tt.current, tt.new)
1652-
return
16531653
}
16541654
if !match && tt.match {
16551655
t.Errorf("%s - expected services to be the same: %q and %q", t.Name(), tt.current, tt.new)
1656-
return
16571656
}
16581657
if !match && !tt.match {
16591658
if !strings.HasPrefix(reason, tt.reason) {
16601659
t.Errorf("%s - expected reason prefix %s, found %s", t.Name(), tt.reason, reason)
1661-
return
1660+
}
1661+
}
1662+
})
1663+
}
1664+
}
1665+
1666+
func newCronJob(image, schedule string, vars []v1.EnvVar) *batchv1.CronJob {
1667+
cron := &batchv1.CronJob{
1668+
Spec: batchv1.CronJobSpec{
1669+
Schedule: schedule,
1670+
JobTemplate: batchv1.JobTemplateSpec{
1671+
Spec: batchv1.JobSpec{
1672+
Template: v1.PodTemplateSpec{
1673+
Spec: v1.PodSpec{
1674+
Containers: []v1.Container{
1675+
v1.Container{
1676+
Name: "logical-backup",
1677+
Image: image,
1678+
Env: vars,
1679+
},
1680+
},
1681+
},
1682+
},
1683+
},
1684+
},
1685+
},
1686+
}
1687+
return cron
1688+
}
1689+
1690+
func TestCompareLogicalBackupJob(t *testing.T) {
1691+
1692+
img1 := "registry.opensource.zalan.do/acid/logical-backup:v1.0"
1693+
img2 := "registry.opensource.zalan.do/acid/logical-backup:v2.0"
1694+
1695+
tests := []struct {
1696+
about string
1697+
current *batchv1.CronJob
1698+
new *batchv1.CronJob
1699+
match bool
1700+
reason string
1701+
}{
1702+
{
1703+
about: "two equal cronjobs",
1704+
current: newCronJob(img1, "0 0 * * *", []v1.EnvVar{}),
1705+
new: newCronJob(img1, "0 0 * * *", []v1.EnvVar{}),
1706+
match: true,
1707+
},
1708+
{
1709+
about: "two cronjobs with different image",
1710+
current: newCronJob(img1, "0 0 * * *", []v1.EnvVar{}),
1711+
new: newCronJob(img2, "0 0 * * *", []v1.EnvVar{}),
1712+
match: false,
1713+
reason: fmt.Sprintf("new job's image %q does not match the current one %q", img2, img1),
1714+
},
1715+
{
1716+
about: "two cronjobs with different schedule",
1717+
current: newCronJob(img1, "0 0 * * *", []v1.EnvVar{}),
1718+
new: newCronJob(img1, "0 * * * *", []v1.EnvVar{}),
1719+
match: false,
1720+
reason: fmt.Sprintf("new job's schedule %q does not match the current one %q", "0 * * * *", "0 0 * * *"),
1721+
},
1722+
{
1723+
about: "two cronjobs with different environment variables",
1724+
current: newCronJob(img1, "0 0 * * *", []v1.EnvVar{{Name: "LOGICAL_BACKUP_S3_BUCKET_PREFIX", Value: "spilo"}}),
1725+
new: newCronJob(img1, "0 0 * * *", []v1.EnvVar{{Name: "LOGICAL_BACKUP_S3_BUCKET_PREFIX", Value: "logical-backup"}}),
1726+
match: false,
1727+
reason: "logical backup container specs do not match: new cronjob container's logical-backup (index 0) environment does not match the current one",
1728+
},
1729+
}
1730+
1731+
for _, tt := range tests {
1732+
t.Run(tt.about, func(t *testing.T) {
1733+
match, reason := cl.compareLogicalBackupJob(tt.current, tt.new)
1734+
if match != tt.match {
1735+
t.Errorf("%s - unexpected match result %t when comparing cronjobs %q and %q", t.Name(), match, tt.current, tt.new)
1736+
} else {
1737+
if !strings.HasPrefix(reason, tt.reason) {
1738+
t.Errorf("%s - expected reason prefix %s, found %s", t.Name(), tt.reason, reason)
16621739
}
16631740
}
16641741
})

pkg/cluster/k8sres.go

+4
Original file line numberDiff line numberDiff line change
@@ -2403,6 +2403,10 @@ func (c *Cluster) generateLogicalBackupPodEnvVars() []v1.EnvVar {
24032403
Name: "LOGICAL_BACKUP_S3_RETENTION_TIME",
24042404
Value: c.OpConfig.LogicalBackup.LogicalBackupS3RetentionTime,
24052405
},
2406+
{
2407+
Name: "LOGICAL_BACKUP_S3_BUCKET_PREFIX",
2408+
Value: c.OpConfig.LogicalBackup.LogicalBackupS3BucketPrefix,
2409+
},
24062410
{
24072411
Name: "LOGICAL_BACKUP_S3_BUCKET_SCOPE_SUFFIX",
24082412
Value: getBucketScopeSuffix(string(c.Postgresql.GetUID())),

pkg/cluster/sync.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -1367,7 +1367,7 @@ func (c *Cluster) syncLogicalBackupJob() error {
13671367
if err != nil {
13681368
return fmt.Errorf("could not generate the desired logical backup job state: %v", err)
13691369
}
1370-
if match, reason := k8sutil.SameLogicalBackupJob(job, desiredJob); !match {
1370+
if match, reason := c.compareLogicalBackupJob(job, desiredJob); !match {
13711371
c.logger.Infof("logical job %s is not in the desired state and needs to be updated",
13721372
c.getLogicalBackupJobName(),
13731373
)

pkg/controller/operator_config.go

+1
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
184184
result.LogicalBackupAzureStorageAccountKey = fromCRD.LogicalBackup.AzureStorageAccountKey
185185
result.LogicalBackupAzureStorageContainer = fromCRD.LogicalBackup.AzureStorageContainer
186186
result.LogicalBackupS3Bucket = fromCRD.LogicalBackup.S3Bucket
187+
result.LogicalBackupS3BucketPrefix = util.Coalesce(fromCRD.LogicalBackup.S3BucketPrefix, "spilo")
187188
result.LogicalBackupS3Region = fromCRD.LogicalBackup.S3Region
188189
result.LogicalBackupS3Endpoint = fromCRD.LogicalBackup.S3Endpoint
189190
result.LogicalBackupS3AccessKeyID = fromCRD.LogicalBackup.S3AccessKeyID

pkg/util/config/config.go

+1
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ type LogicalBackup struct {
132132
LogicalBackupAzureStorageContainer string `name:"logical_backup_azure_storage_container" default:""`
133133
LogicalBackupAzureStorageAccountKey string `name:"logical_backup_azure_storage_account_key" default:""`
134134
LogicalBackupS3Bucket string `name:"logical_backup_s3_bucket" default:""`
135+
LogicalBackupS3BucketPrefix string `name:"logical_backup_s3_bucket_prefix" default:"spilo"`
135136
LogicalBackupS3Region string `name:"logical_backup_s3_region" default:""`
136137
LogicalBackupS3Endpoint string `name:"logical_backup_s3_endpoint" default:""`
137138
LogicalBackupS3AccessKeyID string `name:"logical_backup_s3_access_key_id" default:""`

0 commit comments

Comments
 (0)