Helm include only existing named template - kubernetes-helm

I'm using Helm for to deploy multiple K8s deployments. In some deployments I need to include extra environment variables, but for the majority of deployment the standard env. variables are enough. I would like to have named template for those deployments that must have extra env. variables.
Can I include a named template only if the named template exist?
Something like this:
{{ range $idx, $svc := .Values.services }}
kind: Deployment
metadata:
name: {{ $svc.name }}
spec:
containers:
- name: {{ $svc.name }}
env:
- name: JAVA_OPTS
- value: {{ $svc.javaOpts }}
# if template_exists (print $svc.name "-env")
{{ include (print $svc.name "-env") . | indent 12 }}
# end
{{- end -}}
It's in pseudo-code. How to do the # if part?
Thank you.

The easiest way would be to add additional key like templateExists: true to your services and check it with a simple if statement in your deployment:
{{- if $svc.templateExists }}
{{ include (print $svc.name "-env") . | indent 8 }}
{{- end -}}
services:
svc1:
templateExists: true
name: svc1
javaOpts: "-Xms128m -Xmx512m"
svc2:
name: svc2
javaOpts: "-Xms256m -Xmx512m"

Here is a solution that I came up now. I check if there are any files in the chart that matches the pattern [service]-env.yaml and if there are then I include the content of that file in the deployment.
{{- range $path, $_ := $f.Glob "**-env.yaml" }}
{{- if contains $svc.app.name $path }}
{{ $f.Get $path | indent 8 }}
{{- end }}
{{- end }}
That way, for services that require extra env. variables we can include a file in files/service-env.yaml and those variables will be added to the deployment. For services that do not require such variables, it's left empty.

Related

How to use if/else loop in Helm

I am trying to use if/else-if/else loop in helm chart. Basically, I want to add ENV configs in configfile based on the if/else condition. Below is the logic:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Values.applicationName }}-configmap
labels:
projectName: {{ .Values.applicationName }}
environment: {{ .Values.environment }}
type: configmap
data:
{{- if eq .Values.environment "production" }}
{{ .Files.Get "config-prod.yaml" | nindent 2}}
{{- else if eq .Values.environment "development" }}
{{ .Files.Get "config-dev.yaml" | nindent 2}}
{{- else }}
{{ .Files.Get "config-stage.yaml" | nindent 2}}
{{- end }}
But I am not getting the desired output and facing some issue. Can anybody help me out with this?
Edit1: I have added my modified configmap.yaml as per the suggestions, helm install/template command gives Error: YAML parse error on demo2/templates/configmap.yaml: error converting YAML to JSON: yaml: line 14: did not find expected key error.
also my config-prod and config-stage is being rendered (as per the condition if I give environment: production then config-prod.yaml is being added and if I give environment: stage/null then config-stage.yaml is being added.
Your question would benefit from more specifics.
Please consider adding the following to your question:
How are you trying this? What commands exactly did you run?
How are you "not getting the desired output"? What output did you get?
Please also include:
the relevant entries from your values.yaml
the config-dev.yaml and config-stage.yaml files
Have you run helm template to generate the templates that Helm would apply to your cluster? This would be a good way to diagnose the issue.
I wonder whether you're chomping too much whitespace.
And you should just chomp left, i.e. {{- .... }} rather than left+right {{- ... -}}.
Sorry guys, it was my mistake, my dev-config.yaml has envs and it was defined like key=value, instead of key: value.

Use variable to define other variables

Iā€™m trying to defines some variables in order to make my helm chart non-repeatable
I created a helpers file which contains the following section:
{{ $config := .Values.service }}
{{- range .Values.services }}
{{ $config.$serviceName }}
{{- define "{{ $serviceName }}.selectorLabels" -}}
app.kubernetes.io/name: {{ .name }}
app.kubernetes.io/instance: {{ .instance }}
{{- end -}}
{{- end -}}
values.yaml:
services:
- service1
- service2
- service3
service:
- service1:
name: service1
- service2:
name: service2
- service3:
name: service3
but it keeps prompting an error for bad character U+0024 ā€˜$ā€™
Do you know how can I define a variable by other variable?
Neither the Go text/template language, the Sprig extensions, nor Helm's local extensions have a way to define a template with a dynamic name. The name in a define call must be a fixed string. The text/template documentation notes (under "Nested template definitions"):
Template definitions must appear at the top level of the template.... The define action names the template being created by providing a string constant.
However, templates take a (single) parameter. Instead of trying to define a separate template for each dynamically-specified value, you could define a single template that produces this content, and then call it with the dynamic settings.
{{- define "selectorLabels" -}}{{/* <-- fixed name */-}}
{{/* .name is relative to the template parameter . */-}}
app.kubernetes.io/name: {{ .name }}
app.kubernetes.io/instance: {{ .instance }}
{{- end -}}
{{- range .Values.services }}
{{-/* . is one item from the services list */}}
{{ include "selectorLabels" . }}
{{- end -}}
You may find a simpler Helm values structure easier to work with as well. If you decompose .Values.service, it is a list, where each list is a single-item dictionary, where the key comes from a separate list. You might structure this as a single flat list of settings dictionaries, embedding the item name as a name: value within the structure (like for example the containers: list in a pod spec).
services:
- name: service1
instance: foo
- name: service2
instance: bar
- name: service3
instance: baz

What is the difference between fullnameOverride and nameOverride in Helm?

I could find both fullnameOverride and nameOverride in Helm chart.Please help clarifying what is the difference between these two with an example.
nameOverride replaces the name of the chart in the Chart.yaml file, when this is used to construct Kubernetes object names. fullnameOverride completely replaces the generated name.
These come from the template provided by Helm for new charts. A typical object in the templates is named
name: {{ include "<CHARTNAME>.fullname" . }}
If you install a chart with a deployment with this name, and where the Chart.yaml file specifies name: chart-name...
helm install release-name ., the Deployment will be named release-name-chart-name
helm install release-name . --set nameOverride=name-override, the Deployment will be named release-name-name-override
helm install release-name . --set fullnameOverride=fullname-override, the Deployment will be named fullname-override
The generated ...fullname template is (one code branch omitted, still from the above link)
{{- define "<CHARTNAME>.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
So if fullnameOverride is provided, that completely replaces the rest of the logic in the template. Otherwise the name is constructed from the release name and the chart name, where nameOverride overrides the chart name.

Recommended way to add features to a 3rd party helm chart?

currently we're adding features to 3rd party helm charts we're deploying (for example - in prometheus we're adding an authentication support as we use nginx ingress controller).
Obviously, this will cause us headaches when we want to upgrade those helm charts, we will need to perform "diffs" with our changes.
What's the recommended way to add functionality to existing 3rd party helm charts? Should i use umbrella charts and use prometheus as a dependency? then import value from the chart? (https://github.com/helm/helm/blob/master/docs/charts.md#importing-child-values-via-requirementsyaml)
Or any other recommended way?
-- EDIT --
Example - as you can see, i've added 3 nginx.ingress.* annotations to support basic auth on prometheus ingress resource - of course if i'll upgrade, i'll need to manually add them again, which will cause problems
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
{{- if .Values.prometheus.ingress.annotations }}
annotations:
{{ toYaml .Values.prometheus.ingress.annotations | indent 4 }}
{{- end }}
{{- if .Values.alertmanager.ingress.nginxBasicAuthEnabled }}
nginx.ingress.kubernetes.io/auth-realm: "Authentication Required - ok"
nginx.ingress.kubernetes.io/auth-secret: {{ template "prometheus-operator.fullname" . }}-prometheus-basicauth
nginx.ingress.kubernetes.io/auth-type: "basic"
{{- end }}
name: {{ $serviceName }}
labels:
app: {{ template "prometheus-operator.name" . }}-prometheus
{{ include "prometheus-operator.labels" . | indent 4 }}
{{- if .Values.prometheus.ingress.labels }}
{{ toYaml .Values.prometheus.ingress.labels | indent 4 }}
{{- end }}
spec:
rules:
{{- range $host := .Values.prometheus.ingress.hosts }}
- host: {{ . }}
http:
paths:
- path: "{{ $routePrefix }}"
backend:
serviceName: {{ $serviceName }}
servicePort: 9090
{{- end }}
{{- if .Values.prometheus.ingress.tls }}
tls:
{{ toYaml .Values.prometheus.ingress.tls | indent 4 }}
{{- end }}
{{- end }}
I think that might answer your question.
Subcharts and Globals
Requirements
Helm Dependencies
This led me to find the specific part I was looking for, where the parent chart can override sub-charts by specifying the chart name as a key in the parent values.yaml.
In the application chart's requirements.yaml:
dependencies:
- name: jenkins
# Can be found with "helm search jenkins"
version: '0.18.0'
# This is the binaries repository, as documented in the GitHub repo
repository: 'https://kubernetes-charts.storage.googleapis.com/'
Run:
helm dependency update
In the application chart's values.yaml:
# ...other normal config values
# Name matches the sub-chart
jenkins:
# This will be override "someJenkinsConfig" in the "jenkins" sub-chart
someJenkinsConfig: value
I would either fork and handle integrating the changes when you upgrade/rebase, or if possible disable the ingress elements for those you want to customise via the values.yaml file. Then create your own ingress instances manually with the customisations you need in another custom chart, and provide it the references it needs from the prometheus chart as normal values.yaml inputs.
Obviously this approach has it's limitations, if the customisations are too tightly coupled to the chart it might not be possible to split them out.
Hope this helps.

helm getting subchart service names

Whats the best way to get the helm subchart service names to reference into my ingress controller that will sit in the parent chart
values.yaml
---
ingress:
paths:
- serviceName: app-1
path: /app-1/*
port: 8080
- serviceName: app-2
path: /app-2/*
port: 8080
ingress.yaml
---
{{- range .Values.ingress.paths }}
- path: {{ .path }}
backend:
{{- $subchart := .serviceName -}}
serviceName: {{- include "$subchart.fullname" .}}
servicePort: {{ .port }}
{{- end }}
template: no template "$subchart.fullname" associated with template "gotpl"
helm 3.7 version has solved the problem
https://github.com/helm/helm/pull/9957.
You can use like this
{{ template "bar.fullname" .Subcharts.bar }}
How about hardcoded subchart name scoped by release ?
{{ .Release.Name }}-<subchart_name>
I have found that the best way to reference a service name is to override the template that they are using. There are some caveats to doing this however.
The subchart and your chart will have different contexts so they will most likely render the template differently
There are some things that are only available to the subchart
Most charts have a template similar to the one below in their _helpers.tpl file.
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "newchart.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
The subchart has different .Values to your chart. We will fix this when we render this template by creating a context that is similar to the subcharts context.
Instead of calling it with the . context we create a new context by replacing the .Values with the subcharts .Values.
{{ template "newchart.fullname" (set (deepCopy .) "Values" .Values.newchart }}
We use deepCopy so that we don't actually change the . context but rather create a new one to use.
The subchart has access to its own .Chart values that we can't replicate. In this case we will have to hardcode the value of .Chart.Name to the template. In this we can just replace it with the chart name newchart.
Once we have done this both nameOverride and fullnameOverride on the subchart will work without you having to manually change anything in your template files.
If the subchart uses the fullname function from _helpers.tpl (provided by helm by default for new charts) you can use this (replace postgresql with the name of the subchart):
{{- $fullName := include "postgresql.fullname" (mustMerge (dict "Chart" (dict "Name" "postgresql") "Values" .Values.postgresql) (deepCopy .)) -}}
It depends on the sub-chart definition!
As an example, elasticsearch chart, see here https://github.com/elastic/helm-charts/blob/master/elasticsearch/templates/service.yaml, is defining 2 services.
Both services name can be declared as value clusterName.