I have the following CronJob to run a backup of my database, and I'd like the backup files to be appended with the date:
{{- if .Values.postgresqlBackup.enabled }}
apiVersion: batch/v1
kind: CronJob
metadata:
name: postgres-backup
spec:
schedule: {{ .Values.postgresqlBackup.cron | quote }}
jobTemplate:
spec:
template:
spec:
containers:
- name: postgres
image: postgres:latest
imagePullPolicy: IfNotPresent
command:
- pg_dump
- --username=postgres
- --no-password
- --format=custom
- --file=/backups/dragalia-api-$(date "%Y-%m-%d_%H-%M-%S").bak
- --host={{ include "dragalia-api.fullname" . }}-postgresql
- --verbose
volumeMounts:
- name: data
mountPath: /backups
env:
- name: PGPASSWORD
valueFrom:
secretKeyRef:
name: {{ include "dragalia-api.fullname" . }}-postgresql
key: postgres-password
optional: false
restartPolicy: Never
volumes:
- name: data
persistentVolumeClaim:
claimName: {{ include "dragalia-api.fullname" . }}-db-backup
{{- end }}
The job executes successfully but I am left with files like:
docker@minikube:/dragalia-api/db-backup$ ls
'dragalia-api-$(date "%Y-%m-%d_%H-%M-%S").bak'
The entire filename is quoted and the string is not evaluated. How can I make it so that the string is evaluated by the shell?
Things I've tried:
- using backticks:
--file=/backups/dragalia-api-1`date "%Y-%m-%d_%H-%M-%S".bak`
: still rendered literally - defining a DATE env var and putting ${DATE} in the string: rendered literally
- escaping the % signs e.g.
\%Y
: rendered literally - passing a multi-line string to
sh -c
: this caused the job to fail on being unable to connect to the db, so I guess--host
was not passed properly
The only other thing I can think of is passing in a shell script, but I'd rather not create another resource if possible.
Equivalently, since the date information is stored by the filesystem, if there's some other way to pass a unique string into the filename that would work. Anything so that it keeps rolling backups instead of just the one.
CodePudding user response:
If you want to use shell substitution, then you need to execute your command with a shell. For example:
containers:
- name: postgres
image: postgres:latest
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- >
pg_dump
--username=postgres
--no-password
--format=custom
--file=/backups/dragalia-api-$(date "%Y-%m-%d_%H-%M-%S").bak
--host={{ include "dragalia-api.fullname" . }}-postgresql
--verbose
Also, unrelated to your question, you should pin your postgres
image to a specific version (postgres:14
) or you'll be in for a rude surprise when :latest
is unexpectedly a new major version.
CodePudding user response:
... if there's some other way to pass a unique string into the filename ...
If you don't actually care about the format of the filename, you can use the container name. The CronJob creates a sequence of Job objects, but each will have a different name indirectly based on the execution time (also see Predictable pod name in kubernetes cron job). The Job name will become the Pod name; then you can use the downward API to get that into an environment variable; and then you can substitute environment variables into the command without a shell.
containers:
- name: postgres
image: postgres:latest
env:
- name: PGPASSWORD
valueFrom: {...}
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
command:
- pg_dump
- ...
- --file=/backups/dragalia-api-$(POD_NAME).bak
- ...
Your current filename format will almost certainly be easier to read and manage, but this particular invocation at least doesn't require invoking a shell.