Support for helmfile with argo-cd.
argo-cd already supports helm in 2 distinct ways, why is this useful?
- It helps decouple configuration from chart development
- It's similar to using a repo type of
helmbut you can still manage configuration with git. - Because I like the power afforded using
helmfile's features such asenvironments,selectors, templates, and being able to useENVvars as conditionals AND values. - https://github.com/helmfile/helmfile/blob/main/docs/writing-helmfile.md
- https://github.com/helmfile/helmfile/blob/main/docs/shared-configuration-across-teams.md
Please make note that helmfile itself allows execution of arbitrary scripts.
Due to this feature, execution of arbitrary scripts are allowed by this plugin,
both explicitly (see HELMFILE_INIT_SCRIPT_FILE env below) and implicity.
Consider these implications for your environment and act appropriately.
- https://github.com/roboll/helmfile#templating (
execdescription) - helmfile/helmfile#1 (can disable
execusing env vars) - the execution pod/context is the
argocd-repo-server
helm>= 3.19 (Helm 4 recommended; Helm 2 and older Helm 3 minors are not supported)helmfile>= 1, and >= 1.2 when used with Helm 4
The plugin checks both versions in the init and generate phases and fails
with a clear message if they are not supported.
Argo CD passes the destination cluster's version and APIs as KUBE_VERSION and
KUBE_API_VERSIONS. The plugin passes them to helm template, so charts can use
.Capabilities.KubeVersion and .Capabilities.APIVersions.Has:
KUBE_VERSIONis normalized first: a leadingvand anything after the first+or-are removed (v1.29.0+k3s1→1.29.0,1.29.0-eks-5e0fdde→1.29.0). Values that are still not<major>.<minor>[.<patch>]are ignored with a warning.KUBE_API_VERSIONSis passed as--api-versions.
- Post-renderers are Helm plugins in Helm 4.
--post-rendererinHELM_TEMPLATE_OPTIONSorpostRenderer:in helmfile must name an installed plugin, not an executable path. helm registry logintakes a domain name only (no path). CheckHELMFILE_INIT_SCRIPT_FILEscripts that log in to OCI registries.- Plugins installed via
HELMFILE_INIT_SCRIPT_FILEneed--verify=falseunless they are signed and their key is available.
This shows optional use of sops/age integration. You may add/remove others as necessary.
repoServer:
volumes:
...
- name: age-secret-keys
secret:
secretName: argocd-age-secret-keys
- emptyDir: {}
name: helmfile-cmp-tmp
extraContainers:
- name: helmfile-plugin
image: code-tool/argocd-helmfile-plugin:latest
command: [/var/run/argocd/argocd-cmp-server]
env:
...
- name: SOPS_AGE_KEY_FILE
value: /sops/age/keys.txt
securityContext:
runAsNonRoot: true
runAsUser: 999
volumeMounts:
...
- mountPath: /sops/age
name: age-secret-keys
- mountPath: /var/run/argocd
name: var-files
- mountPath: /home/argocd/cmp-server/plugins
name: plugins
- mountPath: /tmp
name: helmfile-cmp-tmpConfigure your argo-cd app to use a repo/directory which holds a valid
helmfile configuration. This can be a directory which contains a
helmfile.yaml OR helmfile.yaml.gotmpl file OR a helmfile.d directory containing any number of
*.yaml or *.yaml.gotmpl files. You cannot have both configurations.
There are a number of specially handled ENV variables which can be set (all
optional):
HELM_BINARY- custom path tohelmbinaryHELM_TEMPLATE_OPTIONS- pass-through options for the templating operationhelm template --helpHELMFILE_BINARY- custom path tohelmfilebinaryHELMFILE_USE_CONTEXT_NAMESPACE- do not set helmfile namespace toARGOCD_APP_NAMESPACE, for use with multi-namespace appsHELMFILE_GLOBAL_OPTIONS- pass-through options for allhelmfileoperationshelmfile --helpHELMFILE_TEMPLATE_OPTIONS- pass-through options for the templating operationhelmfile template --helpHELMFILE_INIT_SCRIPT_FILE- path to script to execute during init phaseHELMFILE_HELMFILE- a completehelmfile.yamlorhelmfile.yaml.gotmplcontentHELMFILE_HELMFILE_STRATEGY- one ofREPLACEorINCLUDEREPLACE- the default option, only the content ofHELMFILE_HELMFILEis rendered, if any valid files exist in the repo they are ignoredINCLUDE- any valid files in the repo AND the content ofHELMFILE_HELMFILEare rendered, precedence is given toHELMFILE_HELMFILEshould the same release name be declared in multiple files
HELMFILE_CACHE_CLEANUP- run helmfile cache cleanup on initPLUGIN_APP_HOME- per-application directory used asHOMEwhile runninghelm/helmfile, so applications do not share repositories, registry logins or caches. Defaults to/tmp/__argocd-helmfile-plugin.sh__/apps/${ARGOCD_APP_NAME}HELM_HOME- deprecated alias forPLUGIN_APP_HOME(Helm itself ignores it since v3). Still accepted with a warning;PLUGIN_APP_HOMEwins if both are set
Of the above ENV variables, the following do variable expansion on the value:
HELMFILE_GLOBAL_OPTIONSHELMFILE_TEMPLATE_OPTIONSHELM_TEMPLATE_OPTIONSHELMFILE_INIT_SCRIPT_FILEPLUGIN_APP_HOME(and deprecatedHELM_HOME)HELM_CACHE_HOMEHELM_CONFIG_HOMEHELM_DATA_HOME
Meaning, you can do things like:
HELMFILE_GLOBAL_OPTIONS="--environment ${ARGOCD_APP_NAME} --selector cluster=${CLUSTER_ID}
Any of the standard Build Environment variables can be used as well as
variables declared in the application spec.
- https://argoproj.github.io/argo-cd/user-guide/config-management-plugins/#environment
- https://argoproj.github.io/argo-cd/user-guide/build-environment/
To use the various helm plugins the recommended approach is the install the
plugins using the/an initContainers (explicitly set the HELM_DATA_HOME env
var during the helm plugin add command) and simply set the HELM_DATA_HOME
environment variable in your application spec (or globally in the pod). This
prevents the plugin(s) from being downloaded over and over each run.
# repo server deployment
volumes:
...
- name: helm-data-home
emptyDir: {}
# repo-server container
volumeMounts:
...
- mountPath: /home/argocd/.local/share/helm
name: helm-data-home
# init container
volumeMounts:
...
- mountPath: /helm/data
name: helm-data-home
[[ ! -d "${HELM_DATA_HOME}/plugins/helm-secrets" ]] && /custom-tools/helm plugin install https://github.com/jkroepke/helm-secrets --version ${HELM_SECRETS_VERSION} --verify=false
chown -R 999:999 "${HELM_DATA_HOME}"
# lastly, in your app definition
...
plugin:
env:
- name: HELM_DATA_HOME
value: /home/argocd/.local/share/helmIf the above is not possible/desired, the recommended approach would be to use
HELMFILE_INIT_SCRIPT_FILE to execute an arbitrary script during the init
phase. Within the script it's desireable to run helm plugin list and only
install the plugin only if it's not already installed.
You can use the HELMFILE_INIT_SCRIPT_FILE feature to do any kind of init
logic required including installing helm plugins, downloading external files,
etc. The value can be a relative or absolute path and the file itself can be
injected using an initContainers or stored in the application git repository.
Tests use bats-core and run the plugin
against the real helm and helmfile binaries, using the versions pinned in
docker/Dockerfile. No cluster or network access to chart repositories is needed.
make test # downloads helm, helmfile and bats into .tools/, then runs test/*.bats
make lint # shellcheck
make test-docker # builds the image and runs test/docker-smoke.sh inside itRequirements: bash, git, wget, xz, jq, make (and docker for test-docker).
make downloads pinned shellcheck, helm, helmfile and bats into .tools/.
Override tool versions with e.g. make test HELM_VERSION=v3.19.4.
Tests for known bugs are marked with skip "known bug: ...". Remove the skip
together with the fix.
# Create fork.
# Add the original repository as a new remote called "upstream" (only once, if not done before)
git remote add upstream https://github.com/code-tool/argocd-helmfile-plugin.git
# List all remotes to verify that "upstream" exists
git remote -v
# 1. Fetch the latest changes from the original repository
git fetch upstream
# 2. Switch to your main branch (your fork’s main branch, usually `master` or `main`)
git checkout main
# 3. Merge the latest changes from the original repository into your `main`
git merge upstream/main
# 4. Push the updated `main` branch to your fork on GitHub
git push origin main
# 5. Create a new feature branch from the updated `main` for your next changes
git checkout -b new-feature-branch
# (Now you can edit files, commit changes, and push this branch, then open a new pull request)