Generic helm chart - templates

I am beginner in helm chart templating and I'm looking for best practise.
I would like to create a helm chart template enough generics to use same chart for all team project (backend, front-end,..)
On order to make it generics I can let developer to specify list of many cases in values.yml (volumes for deployment, network policy ingress egress etc...).
And i could keep the kubernetes template deployment, service etc.. Enough generics to never mention any specific keys.
So the developer could only modify values yml for add values for their application behavior.
The disadvantage is that the kubernetes generics template will not contain any logic about the application , and the generic template will be hard to maintain (because it will handle every possible case).
The advantage is that the developer doesn't need to understand helm because they will not modify the kubernetes template.
So you have any experience about that?

You can use _*.tpl files to define generic templates, They are located in ./templates/_*.tpl (. being the directory with global Chart.yaml and values.yaml).
Also by defaul in helm global values override local values. Solution to this can be found here - https://github.com/helm/helm/issues/5676
By using these 2 techniques in conjunction you can make generic templates and only use values.yaml to render what you want to render.
For example:
values.yaml:
global:
defaults:
switches:
volumesEnabled: false
ingressEnabled: false
ingress:
host: "generic-host.com"
volumes:
volumeName: "generic-volume-name"
subchart1:
defaultOverrides:
switches:
volumesEnabled: true
volumes:
volumeName: "not-so-generic-name"
subchart2:
defaultOverrides:
switches:
volumesEnabled: true
ingressEnabled: true
Then templates (java is just for grouping templates in one category, you can try to guess in which language my backend microservices are written :) )
./templates/java/_deployment.tpl:
{{- define "templates.java.deployment" }}
{{- $properties := merge .Values.defaultOverrides $.Values.global.defaults -}}
{{*/ generic deployment structure */}}
{{- if $properties.switches.volumesEnabled -}}
volume: {{ $properties.volumes.volumeName }}
{{- end }}
{{*/ generic deployment structure */}}
{{- end }}
./templates/java/_ingress.tpl:
{{- define "templates.java.ingress" }}
{{- $properties := merge .Values.defaultOverrides $.Values.global.defaults -}}
{{- if $properties.switches.ingressEnabled -}}
host: {{ $properties.ingress.host }}
{{*/ generic ingress structure */}}
{{- end }}
{{- end }}
And then subchart templates
./charts/subchart1/templates/deployment.yaml:
{{ include "templates.java.deployment" . }}
./charts/subchart1/templates/ingress.yaml:
{{ include "templates.java.ingress" . }}
subchart2 has exactly the same includes.
In the end we will have:
subchart1:
has deployment
volumeName is overriden from local values with "not-so-generic-name"
ingress is not rendered at all
subchart2:
has deployment
volumeName is default from global values
ingress host is default from global values
But I would say that it's a bad practice to generlize to much, because it will make your templates overly complex. In my case I found 2 distinct groups which have nearly identical manifests within them (basically frontend and backend) and made a set of _*.tpl files for each of them and settings default values for each group respectivelly in global values.yaml.

Related

Unable to add if condition to the helm yaml values.yam;

Hi Helm and Yaml Experts,
I am a beginner with Helm/YAML. Is it possible to add if-else conditions in the values.yaml file in helm charts?
I need to check a variable and set some attribute values accordingly. Here is a snippet example YAML which I am trying to work on the www.yamllint.com currently.
---
cp-kafka:
brokers: 3
enabled: true
heapOptions: "-Xms512M -Xmx512M"
image: confluentinc/cp-enterprise-kafka
imagePullSecrets: ~
imageTag: "6.1.0"
persistence:
disksPerBroker: 1
enabled: true
size: 5Gi
resources: {}
securityContext:
runAsUser: 0
configurationOverrides:
{{- if .Values.prometheus.jmx.enabled }}
-
containerPort: 10
name: jmx
{{- else }}
containerPort: 11
name: jmx
{{- end }}
I am getting the following error on line 17 - if condition. Tried various variants like
{{ if .Values.prometheus.jmx.enabled }}, only if and end, indentations. Nothing helped so far.
(): did not find expected node content while parsing a flow node at line 17 column 7
Can someone please help here? Please let me know if you need any further information from my end to answer this.
Thanks !
Is it possible to add if-else conditions in the values.yaml file in helm charts?
No.
It's possible the chart could support using the template language in limited contexts: if the chart code uses the tpl function to process a specific value, then the value's text can include template syntax, but it must be properly quoted. Most values in most contexts in most charts don't allow this.
Since you as the operator control the content of the values.yaml file, you can just change this setting in the file you're passing to helm install -f. It's fine to have a separate values file per environment.
If you're actually authoring the chart, I'd suggest having a higher-level values file, rather than writing out a list of individual Kubernetes object settings. If you could write within the chart:
containerPorts:
{{- with .Values.prometheus.jmx | default dict }}
{{- if .enabled }}
- containerPort: {{ .port | default 9875 }}
name: imx
{{- end }}
{{- end }}
and then configure
prometheus:
jmx:
enabled: true
port: 11

Consul-Termplate - What's the syntax for inserting variables into a "with secret" certificate call

I'm trying to download a server specific certificate and key from vault to each of my consul servers using the template function of vault-agent.
The recommended approach is to set a common name and SAN as HOSTNAME.DATACENTER.DOMAIN. I am using a template variable to build the CN specific to the node within the template but I am failing to get the correct syntax to use that variable correctly within the "with secret" call
The template looks like this at the moment
{{ with node }}
{{ $CN := {{ .Node.Node }}.{{ .Node.Datacenter }}.paradigm }}
{{ with secret "pki/issue/certs" "common_name=$CN" "alt_names=localhost $CN" "ip_sans=127.0.0.1" "ttl=72h" }}
{{- .Data.certificate -}}
{{ end }}
{{ end }}
When I manually type the required CN into the with secret call it works fine and a certificate is written out so my Vault access is working correctly. However when using the variable approach this fails with "$CN is not an allowed common name for this role". This seems to suggest the template is reading the "$CN" as literal text rather than resolving it to the set value.
This looks like a syntax error to me but having tried multiple options over the last 2 days I'm hoping that someone can give me a pointer as to the correct syntax (or put me out of my misery and tell me that I'm trying an impossible task)
Thanks in advance for your help

What does the syntax `default (dict) .Values.outer.inner` mean in a helm template?

Given values.yaml:
outer:
inner:
someKey: false
What does the following syntax in a helm template file mean?
{{- if index (default (dict) .Values.outer.inner) "someKey" }}
{{- .... }}
{{- end }}
From context, I can infer what I think it's supposed to do: check if the specified key exists at the specified location.
But where does the default (dict)... syntax come from? Sprig? I can't find it documented in any of these places:
https://v2.helm.sh/docs/chart_template_guide/#template-functions-and-pipelines
https://golang.org/pkg/text/template/#hdr-Functions
http://masterminds.github.io/sprig/
http://masterminds.github.io/sprig/defaults.html
And what does it actually mean?
This particular code avoids a failure if the values outer: {...} doesn't contain an inner key within it.
dict is a Sprig function that creates a new dictionary. It can be called with any (even) number of parameters; (dict) with no parameters creates an empty dictionary.
default x y is the same as y | default x and calls the Sprig default function.
The important thing this is trying to protect against is if .Values.outer doesn't have an inner key within it. If that happened, .Values.outer.inner would be nil, and .Values.outer.inner.someKey would produce an error; the default dict block replaces nil with an empty dictionary, which can be used with index and similar template code.
I'll often write similar template blocks one layer at a time:
{{- $outer := .Values.outer | default dict -}}
{{- $inner := $outer.inner | default dict -}}
{{- if $inner.someKey }}
...
{{- end }}

Extracting part of {{ inventory_hostname }} in Ansible to use in a playbook

I'm currently writing a small Ansible playbook whose job is to put in an additional domain in the search list in /etc/resolv.conf.
The second domain to add to the search list must contain part of the hostname of the target hosts. I'm getting the hostname of each of the target hosts during playbook execution using the magic variable {{ inventory_hostname }}.
I then need to extract characters 4 - 6 from the {{ inventory_hostname }} (say 'xyz') such that the second domain to add to the search list is xyz.foo.bar. In bash, this would be obtained with something like:
SERVER=$('hostname':3:3)
env=${SERVER:3:3}
... and the variable 'env' would be equal to 'xyz'.
The playbook works as long as 'xyz' is manually defined.
I am aware that Ansible has regular expression filters which can help with something like this, however I could not figure out a regular expression which does what I need.
For completeness sake, I have tried something like this in ansible:
{{ inventory_hostname|3:3 }}
Any help would be greatly appreciated.
It's almost the same, you can use "{{ inventory_hostname[3:6] }}" to select characters 3 to 6.
For example this task
- debug:
msg: "{{ inventory_hostname[3:6] }}"
Will output
ok: [localhost] => {
"msg": "alh"
}

embeding conf files into helm chart

Im new at helm. Im building a splunk helm chart with numerous conf files. I currently use something like this in a configmap ..
apiVersion: v1
kind: ConfigMap
metadata:
name: splunk-master-configmap
data:
indexes.conf: |
# global settings
# Inheritable by all indexes: no hot/warm bucket can exceed 1 TB.
# Individual indexes can override this setting.
homePath.maxDataSizeMB = 1000000
but I would prefer to have the conf files in a seperate folder e.g. configs/helloworld.conf and have come accross "tpl" but am struggling to understand how to implement it. - can anyone advise best practices. On a side note splunk has orders of presidences >> so there may be many indexes.conf files used in various locations. does anyone have any thoughts on how best to implement this?!??!
Cheers.
If the content of the files is static then you could create a files directory in your chart at the same level as the templates directory (not inside it) and reference them like:
kind: ConfigMap
metadata:
name: splunk-master-configmap
data:
{{ (.Files.Glob "files/indexes.conf").AsConfig | indent 2 }}
{{ (.Files.Glob "files/otherfile.conf").AsConfig | indent 2 }}
# ... and so on
Where this would break down is if you want to be able to reference the values of variables inside the files so that the content is controlled from the values.yaml. If you want to expose each value individually then there's an example in the helm documentation using range. But I think a good fit or your case is what the stable/mysql chart does. It has a ConfigMap that takes values as strings:
{{- if .Values.configurationFiles }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "mysql.fullname" . }}-configuration
data:
{{- range $key, $val := .Values.configurationFiles }}
{{ $key }}: |-
{{ $val | indent 4}}
{{- end }}
{{- end -}}
And the values.yaml allows both the files and their content to be set and overridden by the user of the chart:
# Custom mysql configuration files used to override default mysql settings
configurationFiles:
# mysql.cnf: |-
# [mysqld]
# skip-name-resolve
# ssl-ca=/ssl/ca.pem
# ssl-cert=/ssl/server-cert.pem
# ssl-key=/ssl/server-key.pem
It comments out that content and leaves it to the user of the chart to set but you could have defaults in the values.yaml.
You would only need tpl if you needed further flexibility. The stable/keycloak chart lets the user of the chart create their own configmap and point it into the keycloak deployment via tpl. But I think your case is probably closest to the mysql one.
Edit: the tpl function can also be used to take the content of files loaded with Files.Get and effectively make that content part of the template - see How do I load multiple templated config files into a helm chart? if you're interested in this