├── .gitignore ├── README.md ├── env-magic.gotmpl ├── environments ├── default.yaml ├── local.yaml └── prod.yaml └── helmfile.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore the output of helmfile write-values 2 | helmfile-* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # How to handle helmfile values nicely 2 | 3 | [helmfile](https://github.com/helmfile/helmfile) is a very powerful tool, but his way of handling release values is daunting for the beginners (and experts). I propose here a simple pattern to handle values, that is completely generic, intuitive and works in all situations. 4 | 5 |

6 | Read the article !
7 | 👉 ✨ helmfile: a simple trick to handle values intuitively ✨ 👈 8 | 9 | 10 | ## TL;DR 11 | 12 | This repo reproduces the way values are handled in umbrella charts (Helm Charts with sub-charts). 13 | 14 | **If you don't like to read** but want to experiment instead: 15 | - clone this repo, 16 | - customize the different release values by editing `environments/default.yaml`, 17 | - override values per environment by editing `environments/.yaml` (available environements are `local` and `prod`), 18 | - see for yourself how your changes work by running: `helmfile -e write-values` and see the output. 19 | 20 | To apply this in your helmfile: 21 | 22 | 1. ensure you reference `env-magic.gotmpl` under *all* release values (`releases..values`) and, 23 | 2. ensure you reference both `default.yaml` and `.yaml` files under `environments..values`. 24 | 25 | Done. 26 | 27 | -------------- 28 | 29 | 30 | - [🐌 Before we start: what are values](#-before-we-start-what-are-values) 31 | - [👎 How helmfile handles values](#-how-helmfile-handles-values) 32 | * [Values and global values](#values-and-global-values) 33 | * [Value overrides per environment](#value-overrides-per-environment) 34 | - [👌 Handling values better in helmfile](#-handling-values-better-in-helmfile) 35 | * [What it entails](#what-it-entails) 36 | * [How it works](#how-it-works) 37 | 38 | 39 | ## 🐌 Before we start: what are values 40 | 41 | Values are parameters passed to a Helm chart. In helmfile, all releases are instances of a given helm chart, that defines its parameters in a `values.yaml` file. Those parameters can be overridden using different mechanisms (`--set` and `--values` using `helm`). 42 | 43 | We often distinguish between two kind of values: 44 | 1. *release-specific values*: values that need to be set only for one release 45 | (we will refer to then as simply *values* in this README), and 46 | 2. *global values*: values that must be set in all charts, such as the `domain` or the URL to some kafka broker. 47 | Global values are especially useful when most of the releases in a helmfile rely on a common library chart. 48 | 49 | Furthermore, some values may need to be set differently depending on the environment. 50 | We can thus talk about *global values* and *global environment values*. 51 | 52 | ## 👎 How helmfile handles values 53 | 54 | ### Values and global values 55 | 56 | In a helmfile, *values* can be overriden at the release level: 57 | ```yaml 58 | releases: 59 | - name: foo 60 | # ... 61 | values: 62 | - simple: value # raw value 63 | - path/to/raw-yaml-file.yaml # values read as is from a file 64 | - path/to/template-outputting-yaml.gotmpl # go template outputting valid YAML values 65 | ``` 66 | 67 | In order to set *global values*, one has to reference the same values/file/template below each releases, as seen above. There is no built-in support per se. 68 | 69 | ### Value overrides per environment 70 | 71 | When we need to set *environment values*, helmfile supports a `environments..values` property: 72 | ```yaml 73 | releases: [...] 74 | environments: 75 | prod: 76 | values: 77 | - global: value # raw value 78 | - path/to/raw-yaml-file.yaml # values read as is from a file 79 | - path/to/template-outputting-yaml.gotmpl # go template outputting valid YAML values 80 | ``` 81 | 82 | However, unintuitively enough, this **doesn't set any value at all**, but simply makes them available to templates. 83 | That is, in go templates attached to releases, the `.Values` will be populated with the environment values (on top of the other values defined at the release level), such that a template may do: 84 | ```yaml 85 | global: {{ .Values | get "global" "UNSET" }} 86 | ``` 87 | Notice the convoluted syntax, which is necessary because `global` may be undefined in other environments. 88 | 89 | When all environment values are meant to be global, one trick is to pass the following template inline: `{{ toYaml .Values | nindent 10 }}`. 90 | 91 | Take this helmfile for example: 92 | ```yaml 93 | releases: 94 | - name: foo 95 | # ... reference some chart here 96 | values: 97 | - release-specific: check 98 | - {{ toYaml .Values | nindent 10 }} # <-- output all values (env + normal) 99 | environments: 100 | prod: 101 | values: 102 | - env-prod: check 103 | ``` 104 | The output of `helmfile -e prod write-values` will be: 105 | ```yaml 106 | release-specific: check 107 | env-prod: check 108 | ``` 109 | 110 | This is nice for global environment values, but what about release-specific ones ? 111 | Well, there is no way around writing a specific template for each release... Or is there ? 112 | 113 | ## 👌 Handling values better in helmfile 114 | 115 | In this repository is shown a simpler way of handling values in helmfile, that doesn't require release-specific templates and covers all value types (global/specific, per environment). 116 | 117 | Basically, it reproduces the way values are handled in umbrella charts (Helm Charts with subcharts). 118 | 119 | ### What it entails 120 | 121 | The idea is: 122 | 123 | * define release-specific values in `environments/default.yaml`, under the release name; 124 | * define global values in `environments/default.yaml`, under `global`; 125 | * for environment values, use the same logic, but in a different file (e.g. `environments/.yaml`). 126 | 127 | For this pattern to work: 128 | 129 | 1. all environments must reference `environments/default.yaml`, plus their specific file. This is also necessary for the default environment ! However, in this case, only the default file is listed. 130 | 2. all releases must reference `env-magic.gotmpl` in their `values`. To simplify and keep it DRY, you can use YAML anchors (see the example `helmfile.yaml`). 131 | 132 | That is, you should have something like this: 133 | ```yaml 134 | releases: 135 | - name: foo 136 | # ... 137 | values: # this block should be under all releases 138 | - &env env-magic.gotmpl # <- the magic 139 | 140 | environments: 141 | default: 142 | values: 143 | - environments/default.yaml # always reference the default values files 144 | prod: 145 | values: 146 | - environments/default.yaml 147 | - environments/prod.yaml # apply prod values on top of default 148 | ``` 149 | 150 | Note that the actual name of the files do not matter, of course. 151 | 152 | ### How it works 153 | 154 | All the magic comes from the `env-magic.gotmpl` file, which consists of one single line: 155 | ```go 156 | {{ merge (.Values | get .Release.Name dict) (.Values | get "global" dict) | toYaml }} 157 | ``` 158 | 159 | Since helmfile merges environment values and normal values automatically and makes them available through `.Values` in templates, the only thing left to do is to select only the values for the current release (`.Release.Name`), 160 | and the global values (`global`). We then merge the two, giving precedence to the release-specific values, 161 | and render them as YAML.](https://community.ops.io/derlin/helmfile-a-simple-trick-to-handle-values-intuitively-50cb) 162 | -------------------------------------------------------------------------------- /env-magic.gotmpl: -------------------------------------------------------------------------------- 1 | {{/* 2 | extract both globals and