|
1 | 1 | # argocd-helmfile-plugin |
2 | | -argocd-helmfile-plugin |
| 2 | + |
| 3 | + |
| 4 | + |
| 5 | + |
| 6 | +# Intro |
| 7 | + |
| 8 | +Support for `helmfile` with `argo-cd`. |
| 9 | + |
| 10 | +`argo-cd` already supports `helm` in 2 distinct ways, why is this useful? |
| 11 | + |
| 12 | +- It helps decouple configuration from chart development |
| 13 | +- It's similar to using a repo type of `helm` but you can still manage |
| 14 | + configuration with git. |
| 15 | +- Because I like the power afforded using `helmfile`'s features such as |
| 16 | + `environments`, `selectors`, templates, and being able to use `ENV` vars as |
| 17 | + conditionals **AND** values. |
| 18 | +- https://github.com/helmfile/helmfile/blob/main/docs/writing-helmfile.md |
| 19 | +- https://github.com/helmfile/helmfile/blob/main/docs/shared-configuration-across-teams.md |
| 20 | + |
| 21 | +# Security |
| 22 | + |
| 23 | +Please make note that `helmfile` itself allows execution of arbitrary scripts. |
| 24 | +Due to this feature, execution of arbitrary scripts are allowed by this plugin, |
| 25 | +both explicitly (see `HELMFILE_INIT_SCRIPT_FILE` env below) and implicity. |
| 26 | + |
| 27 | +Consider these implications for your environment and act appropriately. |
| 28 | + |
| 29 | +- https://github.com/roboll/helmfile#templating (`exec` description) |
| 30 | +- https://github.com/helmfile/helmfile/pull/1 (can disable `exec` using env vars) |
| 31 | +- the execution pod/context is the `argocd-repo-server` |
| 32 | + |
| 33 | +# Installation |
| 34 | + |
| 35 | +- https://argo-cd.readthedocs.io/en/stable/operator-manual/config-management-plugins/ |
| 36 | + |
| 37 | +## Sidecar |
| 38 | + |
| 39 | +This shows optional use of sops/age integration. You may add/remove others as necessary. |
| 40 | + |
| 41 | +```yaml |
| 42 | +repoServer: |
| 43 | + volumes: |
| 44 | + ... |
| 45 | + - name: age-secret-keys |
| 46 | + secret: |
| 47 | + secretName: argocd-age-secret-keys |
| 48 | + - emptyDir: {} |
| 49 | + name: helmfile-cmp-tmp |
| 50 | + |
| 51 | + extraContainers: |
| 52 | + - name: helmfile-plugin |
| 53 | + image: code-tool/argocd-helmfile-plugin:latest |
| 54 | + command: [/var/run/argocd/argocd-cmp-server] |
| 55 | + env: |
| 56 | + ... |
| 57 | + - name: SOPS_AGE_KEY_FILE |
| 58 | + value: /sops/age/keys.txt |
| 59 | + securityContext: |
| 60 | + runAsNonRoot: true |
| 61 | + runAsUser: 999 |
| 62 | + volumeMounts: |
| 63 | + ... |
| 64 | + - mountPath: /sops/age |
| 65 | + name: age-secret-keys |
| 66 | + - mountPath: /var/run/argocd |
| 67 | + name: var-files |
| 68 | + - mountPath: /home/argocd/cmp-server/plugins |
| 69 | + name: plugins |
| 70 | + - mountPath: /tmp |
| 71 | + name: helmfile-cmp-tmp |
| 72 | +``` |
| 73 | +
|
| 74 | +# Usage |
| 75 | +
|
| 76 | +Configure your `argo-cd` app to use a repo/directory which holds a valid |
| 77 | +`helmfile` configuration. This can be a directory which contains a |
| 78 | +`helmfile.yaml` **OR** `helmfile.yaml.gotmpl` file **OR** a `helmfile.d` directory containing any number of |
| 79 | +`*.yaml` or `*.yaml.gotmpl` files. You cannot have both configurations. |
| 80 | + |
| 81 | +There are a number of specially handled `ENV` variables which can be set (all |
| 82 | +optional): |
| 83 | + |
| 84 | +- `HELM_BINARY` - custom path to `helm` binary |
| 85 | +- `HELM_TEMPLATE_OPTIONS` - pass-through options for the templating operation |
| 86 | + `helm template --help` |
| 87 | +- `HELMFILE_BINARY` - custom path to `helmfile` binary |
| 88 | +- `HELMFILE_USE_CONTEXT_NAMESPACE` - do not set helmfile namespace to `ARGOCD_APP_NAMESPACE`, |
| 89 | + for use with multi-namespace apps |
| 90 | +- `HELMFILE_GLOBAL_OPTIONS` - pass-through options for all `helmfile` |
| 91 | + operations `helmfile --help` |
| 92 | +- `HELMFILE_TEMPLATE_OPTIONS` - pass-through options for the templating |
| 93 | + operation `helmfile template --help` |
| 94 | +- `HELMFILE_INIT_SCRIPT_FILE` - path to script to execute during init phase |
| 95 | +- `HELMFILE_HELMFILE` - a complete `helmfile.yaml` or `helmfile.yaml.gotmpl` content |
| 96 | +- `HELMFILE_HELMFILE_STRATEGY` - one of `REPLACE` or `INCLUDE` |
| 97 | + - `REPLACE` - the default option, only the content of `HELMFILE_HELMFILE` is |
| 98 | + rendered, if any valid files exist in the repo they are ignored |
| 99 | + - `INCLUDE` - any valid files in the repo **AND** the content of |
| 100 | + `HELMFILE_HELMFILE` are rendered, precedence is given to |
| 101 | + `HELMFILE_HELMFILE` should the same release name be declared in multiple |
| 102 | + files |
| 103 | +- `HELMFILE_CACHE_CLEANUP` - run helmfile cache cleanup on init |
| 104 | + |
| 105 | +Of the above `ENV` variables, the following do variable expansion on the value: |
| 106 | + |
| 107 | +- `HELMFILE_GLOBAL_OPTIONS` |
| 108 | +- `HELMFILE_TEMPLATE_OPTIONS` |
| 109 | +- `HELM_TEMPLATE_OPTIONS` |
| 110 | +- `HELMFILE_INIT_SCRIPT_FILE` |
| 111 | +- `HELM_DATA_HOME` |
| 112 | + |
| 113 | +Meaning, you can do things like: |
| 114 | + |
| 115 | +- `HELMFILE_GLOBAL_OPTIONS="--environment ${ARGOCD_APP_NAME} --selector cluster=${CLUSTER_ID}` |
| 116 | + |
| 117 | +Any of the standard `Build Environment` variables can be used as well as |
| 118 | +variables declared in the application spec. |
| 119 | + |
| 120 | +- https://argoproj.github.io/argo-cd/user-guide/config-management-plugins/#environment |
| 121 | +- https://argoproj.github.io/argo-cd/user-guide/build-environment/ |
| 122 | + |
| 123 | +## Helm Plugins |
| 124 | + |
| 125 | +To use the various helm plugins the recommended approach is the install the |
| 126 | +plugins using the/an `initContainers` (explicitly set the `HELM_DATA_HOME` env |
| 127 | +var during the `helm plugin add` command) and simply set the `HELM_DATA_HOME` |
| 128 | +environment variable in your application spec (or globally in the pod). This |
| 129 | +prevents the plugin(s) from being downloaded over and over each run. |
| 130 | + |
| 131 | +```yaml |
| 132 | +# repo server deployment |
| 133 | + volumes: |
| 134 | + ... |
| 135 | + - name: helm-data-home |
| 136 | + emptyDir: {} |
| 137 | +
|
| 138 | +# repo-server container |
| 139 | + volumeMounts: |
| 140 | + ... |
| 141 | + - mountPath: /home/argocd/.local/share/helm |
| 142 | + name: helm-data-home |
| 143 | +
|
| 144 | +# init container |
| 145 | + volumeMounts: |
| 146 | + ... |
| 147 | + - mountPath: /helm/data |
| 148 | + name: helm-data-home |
| 149 | +
|
| 150 | + [[ ! -d "${HELM_DATA_HOME}/plugins/helm-secrets" ]] && /custom-tools/helm-v3 plugin install https://github.com/jkroepke/helm-secrets --version ${HELM_SECRETS_VERSION} |
| 151 | + chown -R 999:999 "${HELM_DATA_HOME}" |
| 152 | +
|
| 153 | +# lastly, in your app definition |
| 154 | +... |
| 155 | +plugin: |
| 156 | + env: |
| 157 | + - name: HELM_DATA_HOME |
| 158 | + value: /home/argocd/.local/share/helm |
| 159 | +``` |
| 160 | + |
| 161 | +If the above is not possible/desired, the recommended approach would be to use |
| 162 | +`HELMFILE_INIT_SCRIPT_FILE` to execute an arbitrary script during the `init` |
| 163 | +phase. Within the script it's desireable to run `helm plugin list` and only |
| 164 | +install the plugin only if it's not already installed. |
| 165 | + |
| 166 | +## Custom Init |
| 167 | + |
| 168 | +You can use the `HELMFILE_INIT_SCRIPT_FILE` feature to do any kind of _init_ |
| 169 | +logic required including installing helm plugins, downloading external files, |
| 170 | +etc. The value can be a relative or absolute path and the file itself can be |
| 171 | +injected using an `initContainers` or stored in the application git repository. |
| 172 | + |
| 173 | +## Development |
| 174 | +```declarative |
| 175 | +# Create fork. |
| 176 | +# Add the original repository as a new remote called "upstream" (only once, if not done before) |
| 177 | +git remote add upstream https://github.com/code-tool/argocd-helmfile-plugin.git |
| 178 | +
|
| 179 | +# List all remotes to verify that "upstream" exists |
| 180 | +git remote -v |
| 181 | +
|
| 182 | +# 1. Fetch the latest changes from the original repository |
| 183 | +git fetch upstream |
| 184 | +
|
| 185 | +# 2. Switch to your main branch (your fork’s main branch, usually `master` or `main`) |
| 186 | +git checkout main |
| 187 | + |
| 188 | +# 3. Merge the latest changes from the original repository into your `main` |
| 189 | +git merge upstream/main |
| 190 | + |
| 191 | +# 4. Push the updated `main` branch to your fork on GitHub |
| 192 | +git push origin main |
| 193 | + |
| 194 | +# 5. Create a new feature branch from the updated `main` for your next changes |
| 195 | +git checkout -b new-feature-branch |
| 196 | + |
| 197 | +# (Now you can edit files, commit changes, and push this branch, then open a new pull request) |
| 198 | +``` |
0 commit comments