helmfile/docs/hooks.md

9.5 KiB

Hooks

Hooks

A Helmfile hook is a per-release extension point that is composed of:

  • events
  • command
  • args
  • showlogs
  • kubectlApply (alternative to command/args)

Helmfile triggers various events while it is running. Once events are triggered, associated hooks are executed, by running the command with args. The standard output of the command will be displayed if showlogs is set and it's value is true.

Hooks exec order follows the order of definition in the helmfile state.

Currently supported events are:

  • prepare
  • preapply
  • presync
  • preuninstall
  • postuninstall
  • postsync
  • cleanup

Hooks associated to prepare events are triggered after each release in your helmfile is loaded from YAML, before execution. prepare hooks are triggered on the release as long as it is not excluded by the helmfile selector(e.g. helmfile -l key=value).

Hooks associated to presync events are triggered before each release is synced (installed or upgraded) on the cluster. This is the ideal event to execute any commands that may mutate the cluster state as it will not be run for read-only operations like lint, diff or template.

preapply hooks are triggered before a release is uninstalled, installed, or upgraded as part of helmfile apply. This is the ideal event to hook into when you are going to use helmfile apply for every kind of change. Note that preapply hooks will only run if at least one release has changes to apply. Be sure to make each preapply hook command idempotent. Otherwise, rerunning helmfile apply on a transient failure may end up either breaking your cluster, or the hook that runs for the second time will never succeed.

preuninstall hooks are triggered immediately before a release is uninstalled as part of helmfile apply, helmfile sync, helmfile delete, and helmfile destroy.

postuninstall hooks are triggered immediately after successful uninstall of a release while running helmfile apply, helmfile sync, helmfile delete, helmfile destroy.

postsync hooks are triggered after each release is synced (installed or upgraded) on the cluster, regardless if the sync was successful or not. This is the ideal place to execute any commands that may mutate the cluster state as it will not be run for read-only operations like lint, diff or template.

cleanup hooks are triggered after each release is processed. This is the counterpart to prepare, as any release on which prepare has been triggered gets cleanup triggered as well.

The following is an example hook that just prints the contextual information provided to hook:

releases:
- name: myapp
  chart: mychart
  # *snip*
  hooks:
  - events: ["prepare", "cleanup"]
    showlogs: true
    command: "echo"
    args: ["{{`{{.Environment.Name}}`}}", "{{`{{.Release.Name}}`}}", "{{`{{.HelmfileCommand}}`}}\
"]

Let's say you ran helmfile --environment prod sync, the above hook results in executing:

echo {{Environment.Name}} {{.Release.Name}} {{.HelmfileCommand}}

Whereas the template expressions are executed thus the command becomes:

echo prod myapp sync

Now, replace echo with any command you like, and rewrite args that actually conforms to the command, so that you can integrate any command that does:

  • templating
  • linting
  • testing

Hooks expose additional template expressions:

.Event.Name is the name of the hook event.

.Event.Error is the error generated by a failed release, exposed for postsync hooks only when a release fails, otherwise its value is nil.

You can use the hooks event expressions to send notifications to platforms such as Slack, MS Teams, etc.

The following example passes arguments to a script which sends a notification:

releases:
- name: myapp
  chart: mychart
  # *snip*
  hooks:
  - events:
    - presync
    - postsync
    showlogs: true
    command: notify.sh
    args:
    - --event
    - '{{`{{ .Event.Name }}`}}'
    - --status
    - '{{`{{ if .Event.Error }}failure{{ else }}success{{ end }}`}}'
    - --environment
    - '{{`{{ .Environment.Name }}`}}'
    - --namespace
    - '{{`{{ .Release.Namespace }}`}}'
    - --release
    - '{{`{{ .Release.Name }}`}}'

For templating, imagine that you created a hook that generates a helm chart on-the-fly by running an external tool like ksonnet, kustomize, or your own template engine. It will allow you to write your helm releases with any language you like, while still leveraging goodies provided by helm.

Hooks, Kubectl and Environments

Hooks can also be used in combination with small tasks using kubectl directly, e.g.: in order to install a custom storage class.

In the following example, a specific release depends on a custom storage class. Further, all enviroments have a default kube context configured where releases are deployed into. The .Environment.KubeContext is used in order to apply / remove the YAML to the correct context depending on the environment.

environments.yaml:

environments:
  dev:
    values:
    - ../values/default.yaml
    - ../values/dev.yaml
    kubeContext: dev-cluster
  prod:
    values:
    - ../values/default.yaml
    - ../values/prod.yaml
    kubeContext: prod-cluster

helmfile.yaml:

bases:
  - ./environments.yaml

---
releases:
  - name: myService
    namespace: my-ns
    installed: true
    chart: mychart
    version: "1.2.3"
    values:
      - ../services/my-service/values.yaml.gotmpl
    hooks:
      - events: ["presync"]
        showlogs: true
        command: "kubectl"
        args:
          - "apply"
          - "-f"
          - "./custom-storage-class.yaml"
          - "--context"
          - "{{`{{.Environment.KubeContext}}`}}"
      - events: ["postuninstall"]
        showlogs: true
        command: "kubectl"
        args:
          - "delete"
          - "-f"
          - "./custom-storage-class.yaml"
          - "--context"
          - "{{`{{.Environment.KubeContext}}`}}"

Global Hooks

In contrast to the per release hooks mentioned above these are run only once at the very beginning and end of the execution of a helmfile command and only the prepare and cleanup hooks are available respectively.

They use the same syntax as per release hooks, but at the top level of your helmfile:

hooks:
- events: ["prepare", "cleanup"]
  showlogs: true
  command: "echo"
  args: ["{{`{{.Environment.Name}}`}}", "{{`{{.HelmfileCommand}}`}}\
"]

Helmfile + Kustomize

Do you prefer kustomize to write and organize your Kubernetes apps, but still want to leverage helm's useful features like rollback, history, and so on? This section is for you!

The combination of hooks and helmify-kustomize enables you to integrate kustomize into Helmfile.

That is, you can use kustomize to build a local helm chart from a kustomize overlay.

Let's assume you have a kustomize project named foo-kustomize like this:

foo-kustomize/
├── base
│   ├── configMap.yaml
│   ├── deployment.yaml
│   ├── kustomization.yaml
│   └── service.yaml
└── overlays
    ├── default
    │   ├── kustomization.yaml
    │   └── map.yaml
    ├── production
    │   ├── deployment.yaml
    │   └── kustomization.yaml
    └── staging
        ├── kustomization.yaml
        └── map.yaml

5 directories, 10 files

Write helmfile.yaml:

- name: kustomize
  chart: ./foo
  hooks:
  - events: ["prepare", "cleanup"]
    command: "../helmify"
    args: ["{{`{{if eq .Event.Name \"prepare\"}}build{{else}}clean{{end}}`}}", "{{`{{.Release.Ch\
art}}`}}", "{{`{{.Environment.Name}}`}}"]

Run helmfile --environment staging sync and see it results in helmfile running kustomize build foo-kustomize/overlays/staging > foo/templates/all.yaml.

Voilà! You can mix helm releases that are backed by remote charts, local charts, and even kustomize overlays.

kubectlApply Hook

Instead of specifying command and args, you can use the kubectlApply field to run kubectl apply directly:

releases:
- name: myapp
  chart: mychart
  hooks:
  - events: ["presync"]
    showlogs: true
    kubectlApply:
      filename: manifests/custom-resource.yaml

Or apply a kustomize overlay:

  hooks:
  - events: ["presync"]
    showlogs: true
    kubectlApply:
      kustomize: overlays/default/

The kubectlApply field accepts either:

  • filename: - runs kubectl apply -f <value>
  • kustomize: - runs kubectl apply -k <value>

Note: filename and kustomize cannot be used together. When kubectlApply is set, the command field is ignored with a warning.

Hook Template Data

Hooks have access to the following template data:

Per-release hooks:

  • {{ .Environment.Name }} - the environment name
  • {{ .Environment.KubeContext }} - the environment kube context
  • {{ .Release.Name }} - the release name
  • {{ .Release.Namespace }} - the release namespace
  • {{ .Release.Labels }} - the release labels
  • {{ .Release.Chart }} - the release chart
  • {{ .Values }} - state values
  • {{ .HelmfileCommand }} - the helmfile command name (e.g., sync, apply)
  • {{ .Event.Name }} - the hook event name
  • {{ .Event.Error }} - the error (available in postsync hooks when a release fails)

Global hooks:

  • {{ .Environment.Name }} - the environment name
  • {{ .HelmfileCommand }} - the helmfile command name
  • {{ .Event.Name }} - the hook event name
  • {{ .Event.Error }} - the error