Search code examples
kuberneteshookkubernetes-helm

DB Migration with helm


I am trying to migrate our cassandra tables to use liquibase. Basically the idea is trivial, have a pre-install and pre-upgrade job that will run some liquibase scripts and will manage our database upgrade.

For that purpose I have created a custom docker image that will have the actual liquibase cli and then I can invoke it from the job. For example:

apiVersion: batch/v1
kind: Job
metadata:
  name: "{{ .Release.Name }}-update-job"
  namespace: spring-k8s
  labels:
    app.kubernetes.io/managed-by: {{ .Release.Service | quote }}
    app.kubernetes.io/instance: {{ .Release.Name | quote }}
    app.kubernetes.io/version: {{ .Chart.AppVersion }}
    helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
  annotations:
    # This is what defines this resource as a hook. Without this line, the
    # job is considered part of the release.
    "helm.sh/hook": pre-install, pre-upgrade
    "helm.sh/hook-weight": "5"
    "helm.sh/hook-delete-policy": before-hook-creation
spec:
  template:
    metadata:
      name: "{{ .Release.Name }}-cassandra-update-job"
      namespace: spring-k8s
      labels:
        app.kubernetes.io/managed-by: {{ .Release.Service | quote }}
        app.kubernetes.io/instance: {{ .Release.Name | quote }}
        helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
    spec:
      restartPolicy: Never
      containers:
        - name: pre-install-upgrade-job
          image: "lq/liquibase-cassandra:1.0.0"
          command: ["/bin/bash"]
          args:
            - "-c"
            - "./liquibase-cassandra.sh --username {{ .Values.liquibase.username }} --password {{ .Values.liquibase.username }} --url {{ .Values.liquibase.url | squote }} --file {{ .Values.liquibase.file }}"

Where .Values.liquibase.file == databaseChangelog.json.

So this image lq/liquibase-cassandra:1.0.0 basically has a script liquibase-cassandra.sh that when passed some arguments can do its magic and update the DB schema (not going to go into the details).

The problem is that last argument: --file {{ .Values.liquibase.file }}. This file resides not in the image, obviously, but in each micro-services repository.

I need a way to "copy" that file to the image, so that I could invoke it. One way would be to build this lq/liquibase-cassandra all the time (with the same lifecycle of the project itself) and copy the file into it, but that will take time and seems like at least cumbersome. What am I missing?


Solution

  • It turns out that helm hooks can be used for other things, not only jobs. As such, I can mount this file into a ConfigMap before the Job even starts (the file I care about resides in resources/databaseChangelog.json):

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: "liquibase-changelog-config-map"
      namespace: spring-k8s
      annotations:
        helm.sh/hook: pre-install, pre-upgrade
        helm.sh/hook-delete-policy: hook-succeeded
        helm.sh/hook-weight: "1"
    data:
    {{ (.Files.Glob "resources/*").AsConfig | indent 2 }}
    

    And then just reference it inside the job:

      .....
      spec:
      restartPolicy: Never
      volumes:
        - name: liquibase-changelog-config-map
          configMap:
            name: liquibase-changelog-config-map
            defaultMode: 0755
      containers:
        - name: pre-install-upgrade-job
          volumeMounts:
            - name: liquibase-changelog-config-map
              mountPath: /liquibase-changelog-file
          image: "lq/liquibase-cassandra:1.0.0"
          command: ["/bin/bash"]
          args:
            - "-c"
            - "./liquibase-cassandra.sh --username {{ .Values.liquibase.username }} --password {{ .Values.liquibase.username }} --url {{ .Values.liquibase.url | squote }} --file {{ printf "/liquibase-changelog-file/%s" .Values.liquibase.file }}"