fix(mongodb): set the s3 bucket and prefix on the bootstrap restore

With bootstrap.enabled the chart rendered a PerconaServerMongoDBRestore
without backupSource.s3.bucket. The operator CRD requires that field, so
the API server rejected the restore and the HelmRelease failed. The prefix
was missing too, and it matters just as much. For a backupSource restore
the operator builds the PBM storage from backupSource.s3 alone and takes
only the backup name from backupSource.destination, so a restore without
the prefix would look for the backup at the bucket root.

The restore now takes the bucket and prefix from the same helpers that
fill the backup storage, so it reads from where the backups are written.
Both places quote the two values, so a numeric bucket or prefix stays a
string instead of making the resource invalid. The backup storage still
leaves out an empty prefix, so running releases keep the same object. A
destinationPath that names no bucket fails at render time instead of
reaching the operator with an empty bucket.

Assisted-by: LLM
Signed-off-by: Yan Bondarenko <202671653+yankawai@users.noreply.github.com>
This commit is contained in:
Yan Bondarenko committed 2026-10-06 21:30:33 +03:00
1 parent 9620be0103
commit a6cf0e5ce4
5 files changed
+272 -2

No files matched your search

@@ -0,0 +1,13 @@
{{- /*
Bucket and prefix of backup.destinationPath. The bootstrap restore has to
read from the place the backup storage writes to. The operator builds the
restore's storage from backupSource.s3 alone and takes only the backup name
from backupSource.destination, so both templates derive the pair here.
*/}}
{{- define "mongodb.backup.bucket" -}}
{{- .Values.backup.destinationPath | trimPrefix "s3://" | regexFind "^[^/]+" -}}
{{- end -}}
{{- define "mongodb.backup.prefix" -}}
{{- .Values.backup.destinationPath | trimPrefix "s3://" | splitList "/" | rest | join "/" -}}
{{- end -}}
+4 -2
View File
@@ -244,8 +244,10 @@ spec:
s3-storage:
type: s3
s3:
bucket: {{ .Values.backup.destinationPath | trimPrefix "s3://" | regexFind "^[^/]+" }}
prefix: {{ .Values.backup.destinationPath | trimPrefix "s3://" | splitList "/" | rest | join "/" }}
bucket: {{ include "mongodb.backup.bucket" . | quote }}
{{- with include "mongodb.backup.prefix" . }}
prefix: {{ . | quote }}
{{- end }}
endpointUrl: {{ .Values.backup.endpointURL }}
credentialsSecret: {{ .Release.Name }}-s3-creds
insecureSkipTLSVerify: {{ .Values.backup.insecureSkipTLSVerify | default false }}
@@ -8,6 +8,9 @@
{{- if not .Values.backup.destinationPath }}
{{- fail "backup.destinationPath is required when bootstrap.enabled is true" }}
{{- end }}
{{- if not (include "mongodb.backup.bucket" .) }}
{{- fail "backup.destinationPath must name a bucket (s3://bucket/path/) when bootstrap.enabled is true" }}
{{- end }}
{{- if not .Values.backup.endpointURL }}
{{- fail "backup.endpointURL is required when bootstrap.enabled is true" }}
{{- end }}
@@ -33,6 +36,8 @@ spec:
type: logical
destination: {{ .Values.backup.destinationPath | trimSuffix "/" }}/{{ .Values.bootstrap.backupName }}
s3:
bucket: {{ include "mongodb.backup.bucket" . | quote }}
prefix: {{ include "mongodb.backup.prefix" . | quote }}
credentialsSecret: {{ .Release.Name }}-s3-creds
endpointUrl: {{ .Values.backup.endpointURL }}
insecureSkipTLSVerify: {{ .Values.backup.insecureSkipTLSVerify | default false }}
@@ -0,0 +1,133 @@
suite: bootstrap restore reads where the backup storage writes
templates:
- templates/mongodb.yaml
- templates/restore.yaml
release:
name: test-mongodb
namespace: tenant-test
tests:
- it: gives the restore the same S3 location and access as the backup storage
set:
_cluster:
cluster-domain: cozy.local
bootstrap:
enabled: true
backupName: "2026-10-01T02:00:21Z"
backup:
enabled: true
destinationPath: "s3://mybucket/mongodb/prod/"
endpointURL: "https://s3.example.org"
insecureSkipTLSVerify: true
s3AccessKey: "access"
s3SecretKey: "secret"
asserts:
- equal:
path: spec.backup.storages.s3-storage.s3.bucket
value: mybucket
template: templates/mongodb.yaml
documentSelector:
path: kind
value: PerconaServerMongoDB
- equal:
path: spec.backupSource.s3.bucket
value: mybucket
template: templates/restore.yaml
- equal:
path: spec.backup.storages.s3-storage.s3.prefix
value: mongodb/prod/
template: templates/mongodb.yaml
documentSelector:
path: kind
value: PerconaServerMongoDB
- equal:
path: spec.backupSource.s3.prefix
value: mongodb/prod/
template: templates/restore.yaml
- equal:
path: spec.backup.storages.s3-storage.s3.endpointUrl
value: https://s3.example.org
template: templates/mongodb.yaml
documentSelector:
path: kind
value: PerconaServerMongoDB
- equal:
path: spec.backupSource.s3.endpointUrl
value: https://s3.example.org
template: templates/restore.yaml
- equal:
path: spec.backup.storages.s3-storage.s3.credentialsSecret
value: test-mongodb-s3-creds
template: templates/mongodb.yaml
documentSelector:
path: kind
value: PerconaServerMongoDB
- equal:
path: spec.backupSource.s3.credentialsSecret
value: test-mongodb-s3-creds
template: templates/restore.yaml
- equal:
path: spec.backup.storages.s3-storage.s3.insecureSkipTLSVerify
value: true
template: templates/mongodb.yaml
documentSelector:
path: kind
value: PerconaServerMongoDB
- equal:
path: spec.backupSource.s3.insecureSkipTLSVerify
value: true
template: templates/restore.yaml
- equal:
path: spec.backup.storages.s3-storage.s3.forcePathStyle
value: true
template: templates/mongodb.yaml
documentSelector:
path: kind
value: PerconaServerMongoDB
- equal:
path: spec.backupSource.s3.forcePathStyle
value: true
template: templates/restore.yaml
- equal:
path: spec.backupSource.destination
value: s3://mybucket/mongodb/prod/2026-10-01T02:00:21Z
template: templates/restore.yaml
- it: keeps a numeric bucket and prefix as strings on both sides
set:
_cluster:
cluster-domain: cozy.local
bootstrap:
enabled: true
backupName: "2026-10-01T02:00:21Z"
backup:
enabled: true
destinationPath: "s3://2024/10"
endpointURL: "https://s3.example.org"
s3AccessKey: "access"
s3SecretKey: "secret"
asserts:
- equal:
path: spec.backup.storages.s3-storage.s3.bucket
value: "2024"
template: templates/mongodb.yaml
documentSelector:
path: kind
value: PerconaServerMongoDB
- equal:
path: spec.backup.storages.s3-storage.s3.prefix
value: "10"
template: templates/mongodb.yaml
documentSelector:
path: kind
value: PerconaServerMongoDB
- equal:
path: spec.backupSource.s3.bucket
value: "2024"
template: templates/restore.yaml
- equal:
path: spec.backupSource.s3.prefix
value: "10"
template: templates/restore.yaml
@@ -281,6 +281,123 @@ tests:
path: spec.backupSource.s3.forcePathStyle
value: true
- it: sets bucket and prefix from destinationPath
release:
name: test-mongodb
namespace: tenant-test
set:
bootstrap:
enabled: true
backupName: "backup"
backup:
destinationPath: "s3://mybucket/mongodb/prod/"
endpointURL: "http://minio:9000"
s3AccessKey: "access"
s3SecretKey: "secret"
asserts:
- equal:
path: spec.backupSource.s3.bucket
value: mybucket
- equal:
path: spec.backupSource.s3.prefix
value: mongodb/prod/
- it: keeps a prefix without a trailing slash as given
release:
name: test-mongodb
namespace: tenant-test
set:
bootstrap:
enabled: true
backupName: "backup"
backup:
destinationPath: "s3://mybucket/mongodb/prod"
endpointURL: "http://minio:9000"
s3AccessKey: "access"
s3SecretKey: "secret"
asserts:
- equal:
path: spec.backupSource.s3.prefix
value: mongodb/prod
- equal:
path: spec.backupSource.destination
value: s3://mybucket/mongodb/prod/backup
- it: sets an empty prefix when destinationPath is the bucket root
release:
name: test-mongodb
namespace: tenant-test
set:
bootstrap:
enabled: true
backupName: "backup"
backup:
destinationPath: "s3://mybucket"
endpointURL: "http://minio:9000"
s3AccessKey: "access"
s3SecretKey: "secret"
asserts:
- equal:
path: spec.backupSource.s3.bucket
value: mybucket
- equal:
path: spec.backupSource.s3.prefix
value: ""
- equal:
path: spec.backupSource.destination
value: s3://mybucket/backup
- it: keeps a numeric bucket and prefix as strings
release:
name: test-mongodb
namespace: tenant-test
set:
bootstrap:
enabled: true
backupName: "backup"
backup:
destinationPath: "s3://2024/10"
endpointURL: "http://minio:9000"
s3AccessKey: "access"
s3SecretKey: "secret"
asserts:
- equal:
path: spec.backupSource.s3.bucket
value: "2024"
- equal:
path: spec.backupSource.s3.prefix
value: "10"
- it: fails when destinationPath names no bucket
release:
name: test-mongodb
namespace: tenant-test
set:
bootstrap:
enabled: true
backupName: "backup"
backup:
destinationPath: "s3://"
endpointURL: "http://minio:9000"
s3AccessKey: "access"
s3SecretKey: "secret"
asserts:
- failedTemplate:
errorMessage: "backup.destinationPath must name a bucket (s3://bucket/path/) when bootstrap.enabled is true"
- it: does not check the bucket when bootstrap is disabled
release:
name: test-mongodb
namespace: tenant-test
set:
bootstrap:
enabled: false
backup:
destinationPath: "s3://"
asserts:
- hasDocuments:
count: 0
#####################
# PITR #
#####################