9.5 KiB
Hooks
Hooks
A Helmfile hook is a per-release extension point that is composed of:
eventscommandargsshowlogskubectlApply(alternative tocommand/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:
preparepreapplypresyncpreuninstallpostuninstallpostsynccleanup
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:- runskubectl apply -f <value>kustomize:- runskubectl 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 inpostsynchooks 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