Home > Software design >  How can I make the shell evaluate a filename argument in a Kubernetes CronJob?
How can I make the shell evaluate a filename argument in a Kubernetes CronJob?

Time:01-26

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.

  • Related