1

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.

lordnoob
  • 190
  • 12

2 Answers2

2

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.

larsks
  • 277,717
  • 41
  • 399
  • 399
  • Thanks for the answer! Will give it a go shortly and report back, I had issues before w/ this but gave up quickly as I wasn’t sure it was the solution. Thanks also for the comment about image versions, am still new to deployments and k8s so have a lot of best practices to learn – lordnoob Jan 26 '23 at 10:35
1

... 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.

David Maze
  • 130,717
  • 29
  • 175
  • 215
  • I ended up getting the dates working, but this is interesting to know -- I had no idea you could pass down meta information about the pod! – lordnoob Jan 26 '23 at 20:28