Helm value from template - yaml

I'd like my Helm chart to use different image repository depending on cloud provider (AWS vs. Aliyun) unfortunately I am getting the following error when trying to run helm package command:
Error: cannot load values.yaml: error converting YAML to JSON: yaml: invalid map key: map[interface {}]interface {}{"tpl (.Files.Get \"config/repository.config\") . | quote":interface {}(nil)}
In my values.yaml
I have:
configuration:
system:
mode:
cloud_provider: aws
image:
repository: {{ tpl (.Files.Get "config/repository.config") . | quote }}
in my config/repository.config file I have:
{{- if eq ( include "cloud_provider" . | trim ) "aliyun" -}}
registry.cn-qingdao.aliyuncs.com/x/kube-state-metrics
{{- end -}}
{{- if eq ( include "cloud_provider" . | trim ) "aws" -}}
k8s.gcr.io/kube-state-metrics/kube-state-metrics
{{- end -}}
in templates/_helpers.tpl file I've added:
{{- define "cloud_provider" -}}
{{ required "Cloud provider required at .Values.configuration.system.mode.cloud_provider due to Google to unreachable in China" .Values.configuration.system.mode.cloud_provider }}
{{ $valid_cloud_provider := list "aws" "aliyun" }}
{{- if not (has .Values.configuration.system.mode.cloud_provider $valid_cloud_provider ) -}}
{{ fail "Invalid cloud provider set. Should be aws or aliyun" }}
{{end}}
{{- else -}}
{{ .Values.configuration.system.mode.cloud_provider | default "disabled" }}
{{- end -}}
{{- end -}}

This is not a direct solution for the error but an alternative approach. I hope this also could help:
I won't put such expressions in the values file values.yaml
repository: {{ tpl (.Files.Get "config/repository.config") . | quote }}
I would use instead different yaml files for each environment:
Add a aliyun.yaml values file
registry: registry.cn-qingdao.aliyuncs.com/x/kube-state-metrics
And call helm with this values file
helm ... -f aliyun.yaml

Related

Helm template for Vault annotation

I'm trying to nest one template into another, namely I have a k8s deployment.yaml which should be templated by helm. However, it is already using Vault template in annotation field responsible for injecting secrets into the pod.
Without Helm template it looks like this:
vault.hashicorp.com/agent-inject-template-.env: |
{{- with secret (print "envs/data/test") -}}{{- range $k, $v := .Data.data -}}
{{ $k }}={{ $v }}
{{ end }}{{- end -}}
Now, I would like to make a template for Helm and replace path envs/data/test with value coming from Helm values. So I've tried to use it like that:
vault.hashicorp.com/agent-inject-template-.env: |
{{- with secret (print {{ .Values.path }}) -}}{{- range $k, $v := .Data.data -}}
{{ $k }}={{ $v }}
{{ end }}{{- end -}}
This however does not work meaning {{ .Values.path }} is outputed as is, as a string.
How can I template over another template and resolve {{ .Values.path }} as a variable?

Check if a key/value exists in helm chart yaml and add a value to it if exists, otherwise create a new key-pair value

Let's say that I have the following structure in one helm template file
{{- define "deployAnnotations" -}}
"key1" : "value1",
"key2" : "value2"
"ip_range": "20.20.20.20/32"
{{- end -}}
and the above are included in the deployment.yaml file of my helm chart
template:
metadata:
annotations:
{{ include "podAnnotations" . | nindent 8 }}
Now I want to add one more annotation that also define in a template file the following one:
{{- define "ipdeployAnnotations" -}}
ip_range: 10.10.10.10/32
{{- end -}}
My question is how can I search if ip_range exists in the podAnnotations structure. If exists I just want to add the 10.10.10.10/32 to the existing line , otherwise I would like to create a new one.
I imagine something like below but in operator is not working for me , and also I do not know how to add the additional value to ip_range
template:
metadata:
annotations:
{{ include "podAnnotations" . | nindent 8 }}
{{ - if "ip_range" in "podAnnotations" }}
// do something in order to have the following result
"ip_range": "20.20.20.20/32", "10.10.10.10/32"
{{- else }}
{{ include "ipdeployAnnotations" . | nindent 8 }}
{{- end }}
You may convert it to a struct first, and then use it in the way of manipulating dictionary.
templates/_helper.tpl
{{- define "deployAnnotations" -}}
"key1" : "value1"
"key2" : "value2"
"ip_range": "20.20.20.20/32"
{{- end -}}
{{- define "ipdeployAnnotations" -}}
ip_range: "10.10.10.10/32"
{{- end -}}
templates/xxx.yaml
...
{{- $da := (include "deployAnnotations" . | fromYaml) }}
{{- $ida := (include "ipdeployAnnotations" . | fromYaml) }}
{{- if (hasKey $da "ip_range") }}
{{- $_ := set $da "ip_range" (printf "%s, %s" $da.ip_range $ida.ip_range) }}
{{- else }}
{{- $_ := set $da "ip_range" $ida.ip_range }}
{{- end }}
{{- range $k, $v := $da }}
{{ $k }}: {{ $v }}
{{- end }}
...
output
ip_range: 20.20.20.20/32, 10.10.10.10/32
key1: value1
key2: value2

Merge two Value files in helm

I'm looking to merge two values files in helm.
secrets.yaml:
serviceMonitor:
endpoints:
- module: oracledb
port: http
scheme: http
url: "http://user:password#ip:port/xxx"
I have another values.yaml file which has multiple endpoints. I want to merge both the values files. I'm tried using append function to do that: {{$endpoints := (append .Values.serviceMonitor.endpoints .Values.oracle_db.serviceMonitor.endpoints) }} When I do a dry-run, I see its picking up both the values but won't merge. Any one come across this?
In the current Helm version (3) merging values is not supported.
This feature was discussed in this Github issue: Helm should preform deep merge on multiple values files.
One important quote from there
If this is implemented, it should be optional, i. e. the merge mode should be specified as a command-line flag. Anything else would be a breaking change and, I think, in most cases not desirable. The problem is that you would not be able to override defaults for lists.
See: https://github.com/helm/helm/issues/3486#issuecomment-364534501
You can use a python script to merge the values files before passing them. Below is a code snippet of what I am using.
import yaml
from deepmerge import always_merger
fileA = “tmp.yaml"
fileB = “feature.yaml"
with open(fileA,'r+') as f:
fileAdictionary= yaml.load(f)
with open(fileB,'r+') as f:
fileBdictionary = yaml.load(f)
result = always_merger.merge(fileAdictionary, fileBdictionary)
with open(‘newFile.yaml’,'w+') as f:
yaml.dump(result,f)
another option:
{{- define "template.valueOrDefault" -}}
{{- $value := dict -}}
{{- range (rest .) -}}
{{- $value = merge $value . -}}
{{- end -}}
{{- if $value -}}
{{- printf "%s:" (first .) }}
{{- toYaml $value | nindent 2 }}
{{- end }}
{{- end -}}
usage:
containers:
- name: db-migrations-job
{{- include "template.valueOrDefault" (list "resources" .Values.migrationJob.resources .Values.resources) | nindent 8 }}

Kubernetes Helm chart: Use _helpers.tpl to concat values and feed back into Values.yaml

I am new to Kubernetes, Helm, and Golang. Basically, I'm trying to concat strings in my values.yaml file to populate another value in my values.yaml file. I'm using _helpers.tpl to do this (in an attempt to understand Helm and Golang better). I could just write "users: "datafeed:password:::incoming" " in my values.yaml file and be done with it; but would like to avoid that.
I have the following in my values.yaml file:
sftp:
users: ""
username: "datafeed"
password: "password"
incoming: "incoming"
And want the final values.yaml file to read:
sftp:
users: "datafeed:password:::incoming"
username: "datafeed"
password: "password"
incoming: "incoming"
To do this, I am trying to edit the _helpers.tpl file. I have tried
{{- define "sftp.users" -}}
{{- .Values.sftp.users: .Values.sftp.username+":"+.Values.sftp.password+":::"+.Values.sftp.incoming -}}
{{- end -}}
and
{{- define "sftp.users" -}}
{{- .Values.sftp.users:= .Values.sftp.username+":"+.Values.sftp.password+":::"+.Values.sftp.incoming -}}
{{- end -}}
Then I tried making each segment a variable (and deleted the explicit values in the values.yaml file):
{{- define "sftp.users" -}}
{{ $username:= "datafeed" }}
{{ $password:= "password" }}
{{ $incoming:= "incoming" }}
{{- .Values.sftp.users= {{$username}}+":"+{{$password}}":::"+{{$incoming}} -}}
and then setting the fields/keys explicitly:
username: {{ .Values.sftp.username | default "datafeed" }}
password: {{ .Values.sftp.password | default "password" }}
incoming: {{ .Values.sftp.incoming | default "incoming" }}
{{- .Values.sftp.users:= username+":"+password+":::"+incoming -}}
and:
{{define "username"}}datafeed{{end}}
{{define "password"}}password{{end}}
{{define "incoming"}}incoming{{end}}
{{define "users"}}{{template "username"}}:{{template "password"}}:::{{template "incoming"}}{{end}}
{{- printf "users" -}}
{{- .Values.sftp.users: users -}}
I have also looked at previous posts:
Helm _helpers.tpl: Calling defined templates in other template definitions
Kubernetes Helm, combine two variables with a string in the middle
How to get values from values.yaml to _helpers.tpl in helm charts
None of this seems to work. I can't tell if it's my approach or my syntax. Probably both.

Convert YAML string to dict in Helm template

I am creating a chart for a project that has a binary that when executed generates a configuration file in YAML format that looks like this:
---
PARAM_1: value1
PARAM_2: value2
My chart needs to read this file and and load all of its values into environment variables in a container, so I created a variable config in my values.yaml file and when the chart is installed I am passing the file content using --set-file:
helm install <CHART> --set-file config=/path/to/yaml/config/file
Next I create a ConfigMap withe the value of .Values.config:
apiVersion: v1
kind: ConfigMap
metadata:
...
data:
{{ .Values.config }}
The problem I am having is that I need to do two things with values of config:
prefix all keys with a predefined value (so in the example above I would MY_APP_PARAM_1 as key)
make sure the values are all string, otherwise the ConfigMap will fail
How can I parse the value of .Values.config in my template as a dict so that I can use a range loop do these changes?
In the end I was able to do something like this:
{{ $lines := splitList "\n" .Values.config -}}
{{- range $lines }}
{{- if not (. | trim | empty) -}}
{{- $kv := . | splitn ":" 2 -}}
{{ printf "MY_APP_%s: %s" $kv._0 ($kv._1 | trim | quote) | indent 2 }}
{{ end -}}
{{- end -}}
I had a hard time getting the {{- vs {{ right, and helm install --debug --dry-run . help a lot in this part.
It's kind of messy, so I would be very interested in seeing if anyone has a better solution.
If You need to work with more tricky YAML (map in a map) You can use this:
List Handler (list to comma separated string)
{{- define "helm-toolkit.utils.joinListWithComma" -}}
{{- $local := dict "first" true -}}
{{- range $k, $v := . -}}{{- if not $local.first -}}, {{ end -}}{{ $v -}}{{- $_ := set $local "first" false -}}{{- end -}}
{{- end -}}
Convert Yaml dict to the properties-like look
{{- define "utils.yaml2properties" }}
{{- $yaml := . -}}
{{- range $key, $value := $yaml }}
{{- if kindIs "map" $value -}}
{{ $top:=$key }}
{{- range $key, $value := $value }}
{{- if kindIs "map" $value }}
{{- $newTop := printf "%s.%s" $top $key }}
{{- include "utils.yaml2properties" (dict $newTop $value) }}
{{- else if kindIs "slice" $value }}
{{ $top }}.{{ $key }}={{ include "helm-toolkit.utils.joinListWithComma" $value }}
{{- else }}
{{ $top }}.{{ $key }}={{ $value }}
{{- end }}
{{- end }}
{{- else if kindIs "slice" $value }}
{{ $key }}={{ include "helm-toolkit.utils.joinListWithComma" $value }}
{{- else }}
{{ $key }}={{ $value }}
{{- end }}
{{- end }}
{{- end }}
Use (example from K8s configMap)
{{- define "config.yaml" }}
vault.pki:
enabled: {{.Values.vault.pki.enabled}}
role: idverify
common-name: idverify
role:
map:
list:
- one
- two
{{- end }}
data:
bootstrap.properties: |
{{ (include "utils.yaml2properties" (include "config.yaml" . | fromYaml )) | indent 4 }}

Resources