├── .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