Skip to content

Commit bbba15f

Browse files
authored
Logical backup secret (#2051)
* Add logical backup secret
1 parent f6add42 commit bbba15f

File tree

12 files changed

+145
-0
lines changed

12 files changed

+145
-0
lines changed

charts/postgres-operator/crds/operatorconfigurations.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,8 @@ spec:
539539
type: string
540540
pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'
541541
default: "30 00 * * *"
542+
logical_backup_cronjob_environment_secret:
543+
type: string
542544
debug:
543545
type: object
544546
properties:

charts/postgres-operator/values.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,8 @@ configLogicalBackup:
376376
logical_backup_s3_retention_time: ""
377377
# backup schedule in the cron format
378378
logical_backup_schedule: "30 00 * * *"
379+
# secret to be used as reference for env variables in cronjob
380+
logical_backup_cronjob_environment_secret: ""
379381

380382
# automate creation of human users with teams API service
381383
configTeamsApi:

docs/reference/operator_parameters.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,9 @@ grouped under the `logical_backup` key.
825825
[reference schedule format](https://kubernetes.io/docs/tasks/job/automated-tasks-with-cron-jobs/#schedule)
826826
into account. Default: "30 00 \* \* \*"
827827

828+
* **logical_backup_cronjob_environment_secret**
829+
Reference to a Kubernetes secret, which keys will be added as environment variables to the cronjob. Default: ""
830+
828831
## Debugging the operator
829832

830833
Options to aid debugging of the operator itself. Grouped under the `debug` key.

manifests/configmap.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ data:
9494
logical_backup_s3_sse: "AES256"
9595
# logical_backup_s3_retention_time: ""
9696
logical_backup_schedule: "30 00 * * *"
97+
# logical_backup_cronjob_environment_secret: ""
9798
major_version_upgrade_mode: "manual"
9899
# major_version_upgrade_team_allow_list: ""
99100
master_dns_name_format: "{cluster}.{namespace}.{hostedzone}"

manifests/operatorconfiguration.crd.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,8 @@ spec:
537537
type: string
538538
pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'
539539
default: "30 00 * * *"
540+
logical_backup_cronjob_environment_secret:
541+
type: string
540542
debug:
541543
type: object
542544
properties:

manifests/postgresql-operator-default-configuration.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ configuration:
175175
logical_backup_s3_sse: "AES256"
176176
# logical_backup_s3_retention_time: ""
177177
logical_backup_schedule: "30 00 * * *"
178+
# logical_backup_cronjob_environment_secret: ""
178179
debug:
179180
debug_logging: true
180181
enable_database_access: true

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1746,6 +1746,9 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{
17461746
Type: "string",
17471747
Pattern: "^(\\d+|\\*)(/\\d+)?(\\s+(\\d+|\\*)(/\\d+)?){4}$",
17481748
},
1749+
"logical_backup_cronjob_environment_secret": {
1750+
Type: "string",
1751+
},
17491752
},
17501753
},
17511754
"debug": {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ type OperatorLogicalBackupConfiguration struct {
233233
RetentionTime string `json:"logical_backup_s3_retention_time,omitempty"`
234234
GoogleApplicationCredentials string `json:"logical_backup_google_application_credentials,omitempty"`
235235
JobPrefix string `json:"logical_backup_job_prefix,omitempty"`
236+
CronjobEnvironmentSecret string `json:"logical_backup_cronjob_environment_secret,omitempty"`
236237
CPURequest string `json:"logical_backup_cpu_request,omitempty"`
237238
MemoryRequest string `json:"logical_backup_memory_request,omitempty"`
238239
CPULimit string `json:"logical_backup_cpu_limit,omitempty"`

pkg/cluster/k8sres.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,6 +1137,37 @@ func (c *Cluster) getPodEnvironmentSecretVariables() ([]v1.EnvVar, error) {
11371137
return secretPodEnvVarsList, nil
11381138
}
11391139

1140+
// Return list of variables the cronjob received from the configured Secret
1141+
func (c *Cluster) getCronjobEnvironmentSecretVariables() ([]v1.EnvVar, error) {
1142+
secretCronjobEnvVarsList := make([]v1.EnvVar, 0)
1143+
1144+
if c.OpConfig.LogicalBackupCronjobEnvironmentSecret == "" {
1145+
return secretCronjobEnvVarsList, nil
1146+
}
1147+
1148+
secret, err := c.KubeClient.Secrets(c.Namespace).Get(
1149+
context.TODO(),
1150+
c.OpConfig.LogicalBackupCronjobEnvironmentSecret,
1151+
metav1.GetOptions{})
1152+
if err != nil {
1153+
return nil, fmt.Errorf("could not read Secret CronjobEnvironmentSecretName: %v", err)
1154+
}
1155+
1156+
for k := range secret.Data {
1157+
secretCronjobEnvVarsList = append(secretCronjobEnvVarsList,
1158+
v1.EnvVar{Name: k, ValueFrom: &v1.EnvVarSource{
1159+
SecretKeyRef: &v1.SecretKeySelector{
1160+
LocalObjectReference: v1.LocalObjectReference{
1161+
Name: c.OpConfig.LogicalBackupCronjobEnvironmentSecret,
1162+
},
1163+
Key: k,
1164+
},
1165+
}})
1166+
}
1167+
1168+
return secretCronjobEnvVarsList, nil
1169+
}
1170+
11401171
func getSidecarContainer(sidecar acidv1.Sidecar, index int, resources *v1.ResourceRequirements) *v1.Container {
11411172
name := sidecar.Name
11421173
if name == "" {
@@ -2172,7 +2203,13 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1.CronJob, error) {
21722203
return nil, fmt.Errorf("could not generate resource requirements for logical backup pods: %v", err)
21732204
}
21742205

2206+
secretEnvVarsList, err := c.getCronjobEnvironmentSecretVariables()
2207+
if err != nil {
2208+
return nil, err
2209+
}
2210+
21752211
envVars := c.generateLogicalBackupPodEnvVars()
2212+
envVars = append(envVars, secretEnvVarsList...)
21762213
logicalBackupContainer := generateContainer(
21772214
logicalBackupContainerName,
21782215
&c.OpConfig.LogicalBackup.LogicalBackupDockerImage,

pkg/cluster/k8sres_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ func TestExtractPgVersionFromBinPath(t *testing.T) {
192192
const (
193193
testPodEnvironmentConfigMapName = "pod_env_cm"
194194
testPodEnvironmentSecretName = "pod_env_sc"
195+
testCronjobEnvironmentSecretName = "pod_env_sc"
195196
testPodEnvironmentObjectNotExists = "idonotexist"
196197
testPodEnvironmentSecretNameAPIError = "pod_env_sc_apierror"
197198
testResourceCheckInterval = 3
@@ -448,6 +449,96 @@ func TestPodEnvironmentSecretVariables(t *testing.T) {
448449

449450
}
450451

452+
// Test if the keys of an existing secret are properly referenced
453+
func TestCronjobEnvironmentSecretVariables(t *testing.T) {
454+
testName := "TestCronjobEnvironmentSecretVariables"
455+
tests := []struct {
456+
subTest string
457+
opConfig config.Config
458+
envVars []v1.EnvVar
459+
err error
460+
}{
461+
{
462+
subTest: "No CronjobEnvironmentSecret configured",
463+
envVars: []v1.EnvVar{},
464+
},
465+
{
466+
subTest: "Secret referenced by CronjobEnvironmentSecret does not exist",
467+
opConfig: config.Config{
468+
LogicalBackup: config.LogicalBackup{
469+
LogicalBackupCronjobEnvironmentSecret: "idonotexist",
470+
},
471+
},
472+
err: fmt.Errorf("could not read Secret CronjobEnvironmentSecretName: secret.core \"idonotexist\" not found"),
473+
},
474+
{
475+
subTest: "Cronjob environment vars reference all keys from secret configured by CronjobEnvironmentSecret",
476+
opConfig: config.Config{
477+
LogicalBackup: config.LogicalBackup{
478+
LogicalBackupCronjobEnvironmentSecret: testCronjobEnvironmentSecretName,
479+
},
480+
},
481+
envVars: []v1.EnvVar{
482+
{
483+
Name: "clone_aws_access_key_id",
484+
ValueFrom: &v1.EnvVarSource{
485+
SecretKeyRef: &v1.SecretKeySelector{
486+
LocalObjectReference: v1.LocalObjectReference{
487+
Name: testPodEnvironmentSecretName,
488+
},
489+
Key: "clone_aws_access_key_id",
490+
},
491+
},
492+
},
493+
{
494+
Name: "custom_variable",
495+
ValueFrom: &v1.EnvVarSource{
496+
SecretKeyRef: &v1.SecretKeySelector{
497+
LocalObjectReference: v1.LocalObjectReference{
498+
Name: testPodEnvironmentSecretName,
499+
},
500+
Key: "custom_variable",
501+
},
502+
},
503+
},
504+
{
505+
Name: "standby_google_application_credentials",
506+
ValueFrom: &v1.EnvVarSource{
507+
SecretKeyRef: &v1.SecretKeySelector{
508+
LocalObjectReference: v1.LocalObjectReference{
509+
Name: testPodEnvironmentSecretName,
510+
},
511+
Key: "standby_google_application_credentials",
512+
},
513+
},
514+
},
515+
},
516+
},
517+
}
518+
519+
for _, tt := range tests {
520+
c := newMockCluster(tt.opConfig)
521+
vars, err := c.getCronjobEnvironmentSecretVariables()
522+
sort.Slice(vars, func(i, j int) bool { return vars[i].Name < vars[j].Name })
523+
if !reflect.DeepEqual(vars, tt.envVars) {
524+
t.Errorf("%s %s: expected `%v` but got `%v`",
525+
testName, tt.subTest, tt.envVars, vars)
526+
}
527+
if tt.err != nil {
528+
if err.Error() != tt.err.Error() {
529+
t.Errorf("%s %s: expected error `%v` but got `%v`",
530+
testName, tt.subTest, tt.err, err)
531+
}
532+
} else {
533+
if err != nil {
534+
t.Errorf("%s %s: expected no error but got error: `%v`",
535+
testName, tt.subTest, err)
536+
}
537+
}
538+
}
539+
540+
}
541+
451542
func testEnvs(cluster *Cluster, podSpec *v1.PodTemplateSpec, role PostgresRole) error {
452543
required := map[string]bool{
453544
"PGHOST": false,

0 commit comments

Comments
 (0)