From 77204d9a8c6636cba6a7590dec728381dbb4439c Mon Sep 17 00:00:00 2001 From: Andreas Bieber Date: Fri, 4 May 2018 14:51:55 +0200 Subject: [PATCH] Introduce template function `requiredEnv` The new template function `requiredEnv` ensures that the given env var is set and not empty. --- README.md | 26 +++++++++++++++++++------- state/state.go | 16 +++++++++++++++- state/state_test.go | 19 +++++++++++++++++++ 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 32d57aa6..9eae9345 100644 --- a/README.md +++ b/README.md @@ -52,9 +52,11 @@ releases: - name: address value: https://vault.example.com - name: db.password - value: {{ env "DB_PASSWORD" }} # value taken from environment variable. Quotes are necessary. Will throw an error if the environment variable is not set. $DB_PASSWORD needs to be set in the calling environment ex: export DB_PASSWORD='password1' + value: {{ requiredEnv "DB_PASSWORD" }} # value taken from environment variable. Quotes are necessary. Will throw an error if the environment variable is not set. $DB_PASSWORD needs to be set in the calling environment ex: export DB_PASSWORD='password1' - name: proxy.domain - value: {{ env "PLATFORM_ID" }}.my-domain.com # Interpolate environment variable with a fixed string + value: {{ requiredEnv "PLATFORM_ID" }}.my-domain.com # Interpolate environment variable with a fixed string + - name: proxy.scheme + value: {{ env "SCHEME" | default "https" }} # Local chart example - name: grafana # name of this release @@ -62,10 +64,18 @@ releases: chart: ../my-charts/grafana # the chart being installed to create this release, referenced by relative path to local chart values: - "../../my-values/grafana/values.yaml" # Values file (relative path to manifest) - - ./values/{{ env "PLATFORM_ENV" }}/config.yaml # Values file taken from path with environment variable. $PLATFORM_ENV must be set in the calling environment. + - ./values/{{ requiredEnv "PLATFORM_ENV" }}/config.yaml # Values file taken from path with environment variable. $PLATFORM_ENV must be set in the calling environment. ``` +## Templating + +Helmfile uses [Go templates](https://godoc.org/text/template) for templating your helmfile.yaml. While go ships several built-in functions, we have added all of the functions in the [Sprig library](https://godoc.org/github.com/Masterminds/sprig). + +We also added one special template function: `requiredEnv`. +The `required_env` function allows you to declare a particular environment variable as required for template rendering. +If the environment variable is unset or empty, the template rendering will fail with an error message. + ## Using environment variables Environment variables can be used in most places for templating the helmfile. Currently this is supported for `name`, `namespace`, `value` (in set) and `url` (in repositories). @@ -75,17 +85,19 @@ Examples: ```yaml respositories: - name: your-private-git-repo-hosted-charts - url: https://{{ env "GITHUB_TOKEN"}}@raw.githubusercontent.com/kmzfs/helm-repo-in-github/master/ + url: https://{{ requiredEnv "GITHUB_TOKEN"}}@raw.githubusercontent.com/kmzfs/helm-repo-in-github/master/ ``` ```yaml releases: - - name: {{ env "NAME" }}-vault - namespace: {{ env "NAME" }} + - name: {{ requiredEnv "NAME" }}-vault + namespace: {{ requiredEnv "NAME" }} chart: roboll/vault-secret-manager set: - name: proxy.domain - value: {{ env "PLATFORM_ID" }}.my-domain.com + value: {{ requiredEnv "PLATFORM_ID" }}.my-domain.com + - name: proxy.scheme + value: {{ env "SCHEME" | default "https" }} ``` ## installation diff --git a/state/state.go b/state/state.go index f63f2fdb..48357059 100644 --- a/state/state.go +++ b/state/state.go @@ -107,7 +107,21 @@ func readFromYaml(content []byte, file string) (*HelmState, error) { } func stringTemplate() *template.Template { - return template.New("stringTemplate").Funcs(sprig.TxtFuncMap()) + funcMap := sprig.TxtFuncMap() + alterFuncMap(&funcMap) + return template.New("stringTemplate").Funcs(funcMap) +} + +func alterFuncMap(funcMap *template.FuncMap) { + (*funcMap)["requiredEnv"] = getRequiredEnv +} + +func getRequiredEnv(name string) (string, error) { + if val, exists := os.LookupEnv(name); exists && len(val) > 0 { + return val, nil + } + + return "", fmt.Errorf("required env var `%s` is not set", name) } func renderTemplateString(s string) (string, error) { diff --git a/state/state_test.go b/state/state_test.go index cbc0937d..111ec4b1 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -351,6 +351,25 @@ func Test_renderTemplateString(t *testing.T) { }, wantErr: true, }, + { + name: "required env var", + args: args{ + s: "{{ requiredEnv \"HF_TEST\" }}", + envs: map[string]string{ + "HF_TEST": "value", + }, + }, + want: "value", + wantErr: false, + }, + { + name: "required env var not set", + args: args{ + s: "{{ requiredEnv \"HF_TEST_NONE\" }}", + envs: map[string]string{}, + }, + wantErr: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {