embeding conf files into helm chart - templates

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

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

Is it possible to include a templated blob text into values.yaml?

For motives of backwards compatibility (and to reduce the burden of having to update src code) follows:
I have an user input:
$values.yaml
output:
endpoint: MYENDPOINT
specialFile:
config1: |-
Some very nicely formated text.
Wow, such sample.
Much text.
[...]
config2: |-
# this is also text but i need to remove |- for interpretation
{{- $.Files.Get "configs/include.conf" | nindent 4 }}
That i want to replace with a blob of text:
$configs/include.conf
<match **>
#type http
endpoint_url {{ .Values.output.endpoint }}
serializer json
</match>
So that output replaces var on text and text replaces value on values.yaml. Is this possible?
I know this is something that is pretty close to impossible. I already saw someone replacing values.yaml with itself (didnt try myself) but is this possible?
Or can i overwrite the .Values.specialFile.configs2 value without writing directly into the .Values file and overwriting the variable somehow, maybe using templates?
This is possible by processing the included file as nested template with tpl:
{{ $endpoint := "MYENDPOINT" }}
output:
endpoint: {{ $endpoint }}
specialFile:
config1: |-
Some very nicely formated text.
Wow, such sample.
Much text.
[...]
config2: |-
# this is also text but i need to remove |- for interpretation
{{- tpl ($.Files.Get "configs/include.conf") $endpoint | nindent 4 }}
To avoid repetition, I put the endpoint in a variable and passed it to the loaded template. Therefore, it will be available as {{.}} there:
<match **>
#type http
endpoint_url {{ . }}
serializer json
</match>

Generic helm chart

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.

Use ansible templating but rysnc to move files

I have quite a few files (Nginx configs) that are candidates for templating but I want to move them using rysnc/synchronize modules.
Is there a way to achieve this?
Right now I do this
- name: Copy configuration
synchronize:
src: "{{ nginx_path }}/"
dest: /etc/nginx/
rsync_path: "sudo rsync"
rsync_opts:
- "--no-motd"
- "--exclude=.git"
- "--exclude=modules"
- "--delete"
notify:
- Reload Nginx
The templating engine is combined with the move/copy action and therefore I can’t use it to apply the templates and keep it in the source itself and then use rsync to move it.
Edit:
Another way to rephrase this would be:
Is there a way to apply templates, and keep the applied output it in the source machine itself?
Not in a single task no. However, the following playbook stub achieves what, I believe, you desire:
---
- hosts: localhost
gather_facts: no
tasks:
- name: "1. Create temporary directory"
tempfile:
state: directory
register: temp_file_path
- name: "2. Template source files to temp directory"
template:
src: "{{ item }}"
dest: "{{ temp_file_path.path }}/{{ item | basename | regex_replace('.j2$', '') }}"
loop: "{{ query('fileglob', 'source/*.j2') }}"
delegate_to: localhost
- name: "3. Sync these to the destination"
synchronize:
src: "{{ temp_file_path.path }}/"
dest: "dest"
delete: yes
- name: "4. Delete the temporary directory (optional)"
file:
path: "{{ temp_file_path.path }}"
state: absent
Explanation:
For testing, this playbook was written to target localhost and connect using the local method, I developed it to simply look for all .j2 files in ./source/*.j2 and rsync the created files to ./dest/ on my workstation. I ran it using ansible-playbook -i localhost, playbook_name.yml --connection=local
Task 1. We are going to template out the source files to the local host first (using the delegate_to: localhost option on the template task). You can either create a specific directory to do this, or use Ansible's tempfile module to create one somewhere (typically) under /tmp.
Task 2. Use the template module to convert your jinja templates (with arbitrary filenames ending in .j2) found in "./source/" to output files written to the directory created in task 1 above.
Task 3. Use the synchronize module to rsync these to the destination server (for testing I used ./dest on the same box).
Task 4. Delete the temporary directory created in task 1 above.
There is with_filetree plugin that not only allows you to copy a bunch of templates, it also allows you to recreate the folder structure of your source on your destination:
- name: Template complete tree
template:
src: '{{ item.src }}'
dest: /web/{{ item.path }}
force: yes
with_community.general.filetree: web/
when: item.state == 'file'
For more details take a look at the original pull request: https://github.com/ansible/ansible/pull/14332
On the latest versions, it seems to be under community general
https://docs.ansible.com/ansible/latest/collections/community/general/filetree_lookup.html

Ansible list variable reference

I have a list of variables, let's say
servers:
- server1
- server2
And I have folders on my hosts:
/tmp/server1
/tmp/server2
Now I would like to create files in those folders. I need to reference to all variables in the list separately. If I reference in the task to {{ servers }} it creates folder /tmp/[server1, server2]. When I do {{ server[0] }} it creates a file in server1 folder which is good, but I need to reference to all variables in the list separately not all at once. I think the answer will be something like {{ server[*] }}
Have a look at Loops in Ansible.
Your task will look like this:
- file:
path: "/tmp/{{ item }}/myfile"
state: touch
with_items: "{{ servers }}"
The above will create empty files (or update their datetime, just like touch command) /tmp/server1/myfile, /tmp/server2/myfile on all target servers.