Testing the existence of helm element defined in values.yaml - kubernetes-helm

I am new in helm and currently come up with a situation where I need to test the existence of 2 parameters defined in values.yaml and if present, use the value of the same in sample.yaml.
Note - This is sure that one of them will be present.
For example:
I am testing for these 2 values:
1 - {{ .Values.probes.xyz.readiness.initialDelaySeconds }}
2 - {{ .Values.readiness.xyz.initialDelaySeconds }}
And inside sample.yaml, I have to implement one of the above parameter based on existence:
initialDelaySeconds: <one of the above values needs to be implemented based on the existence>
I understand it's a simple if else condition, but I am unable to implement.
Any help would be appreciated.

For logic where you want to use some value if it's set and some other value if not, the Helm default function is usually a good match.
{{- $v1 := .Values.probes.xyz.readiness.initialDelaySeconds }}
{{- $v2 := .Values.readiness.xyz.initialDelaySeconds }}
initialDelaySeconds: {{ $v1 | default $v2 }}
If you have multiple settings like this, you can also do things like merge the two values-item dictionaries together.
{{- $readiness := merge dict .Values.probes.xyz.readiness .Values.readiness.xyz }}
initialDelaySeconds: {{ $readiness.initialDelaySeconds }}

{{- if .Values.probes.xyz.readiness.initialDelaySeconds }}
initialDelaySeconds: {{ .Values.probes.xyz.readiness.initialDelaySeconds }}
{{ else if .Values.readiness.xyz.initialDelaySeconds }}
initialDelaySeconds: {{ .Values.readiness.xyz.initialDelaySeconds }}
{{- end }}
helm Flow control

Related

How to filter out file names from a .File.Glob in a Helm Chart

I have the following snippet in a helm library which is supposed to add in all the files in a folder into a ConfigMap except for those ending in .tz.
{{- /*
add the contents of every file in the config folder of this IOC helm chart
into the config map - this must include start.sh the startup script.
The files must be text only. Any files ending in .tz are explicitly ommitted
*/ -}}
{{ (.Files.Glob "config/*[!.tz]").AsConfig | indent 2 }}
version.txt: |
IOC {{ .Release.Name }} version {{ .Chart.AppVersion }}
This does not quite work in that it filters out all files ending in . or t or z.
I cant see how to do this with Go globbing. I also cannot work out the syntax for using 'without' for the list created by .Files.Glob.
Can anyone enlighten me? Thanks!
Thanks #David Maze. This almost worked but the reference to .Files.Get failed and I think that is because the . context becomes the range counter within the range loop.
Adding $ to .Files within the loop got it working.
Also to fully replicate AsConfig I needed to extract the basename from
the path which I did by adding regexReplaceAll.
data:
{{- range $path, $_ := .Files.Glob "config/*" }}
{{- if not (hasSuffix ".tz" $path) }}
{{ regexReplaceAll "(.*)/" $path "" }}: |
{{ $.Files.Get $path | indent 4 }}
{{- end }}
{{- end }}
The Helm documentation references the Go "github.com/gobwas/glob" package for the supported glob syntax; that doesn't support any sort of "except" cases, except for single characters.
What you can do instead is iterate over all of the files, and then use a normal conditional to exclude the ones you don't want. You have to construct the ConfigMap structure yourself rather than relying on the AsConfig helper.
data:
{{- range $path, $_ := .Files.Glob "config/*" }}
{{- if not (hasSuffix ".tz" $path) }}
{{ $path }}: |
{{ .Files.Get $path | indent 4 }}
{{- end }}
{{- end }}

Helm include only existing named template

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.

Helm control input values

I'm looking for a solution to control the input values(defined in values.yaml). I would like to check if the input value is authorized.
Example:
values.yaml
provider: aws
services:
- nginx
- varnish
- php
And in another file(maybe _helpers.tpl?)
authorized_providers:
- aws
- azure
authorized_services:
- nginx
- php
And raise an error(custom message if it's possible) to indicate that the input values are not supported/authorized.
My goal is to avoid to generate a Kubernetes configmap with unsupported values(helm install works but this configuration will generate container errors).
EDIT:
I finally found a solution using "required" with some tricks.
Following my example with my values.yaml config file.
I define in _helpers.tpl:
{{/*
Define the authorized Value for the parameter: .Values.provider
*/}}
{{- define "authorized.provider" }}
{{- printf "aws,azure" }}
{{- end }}
{{/*
Define the error message if the .Values.provider doesn't respect the authorized.provider condition.
*/}}
{{- define "error.provider" }}
{{- $provider := include "authorized.provider" . }}
{{- printf "Invalid value set for .Values.provider - Must be one of %s" $provider }}
{{- end }}
{{/*
Define the authorized Value for the parameter: .Values.services
*/}}
{{- define "authorized.services" }}
{{- printf "nginx,php" }}
{{- end }}
{{/*
Define the error message if the .Values.services doesn't respect the authorized.services condition.
*/}}
{{- define "error.services" }}
{{- $services := include "authorized.services" . }}
{{- printf "Invalid value set for .Values.services - Authorized values are %s" $services }}
{{- end }}
And next, I've created another file: input-values-validation.yaml
{{- $provider := include "authorized.provider" . }}
{{- $errorProvider := include "error.provider" . }}
{{- if not (has .Values.provider (splitList "," $provider)) }}
{{ required $errorProvider .Values.foo }}
{{- end }}
{{- $services := include "authorized.services" . }}
{{- $errorServices := include "error.Services" . }}
{{- $root := . -}}
{{- range .Values.services }}
{{- if not (has . (splitList "," $services)) }}
{{ required $errorServices $root.Values.foo }}
{{- end }}
{{- end }}
Output if bad input value:
==> Linting
[ERROR] templates/: render error in "templates/input-values-validation.yaml": template: templates/input-values-validation.yaml:12:3: executing "templates/input-values-validation.yaml" at<required $errorServ...>: error calling required: Invalid value set for .Values.services - Authorized values are nginx,php
Infos:
".Values.foo" must never be set in the values.yaml file. I used it to fail the "required" check and raise the error.
I've tried to put the content of "input-values-validation.yaml" in the _helpers.tpl file but this generate an error "[ERROR] templates/: rendering template failed: runtime error: invalid memory address or nil pointer dereference". It seems that the "required" function must be used only in yaml files.
So with this solution, I'm able to define the authorized values in the _helpers.tpl file and generate a "custom" error message. And if in the futur I support more providers/services(my example), I'll only need to modify the value in "authorized.provider" and "authorized.services".
I've not seen it done with helm2, at least not in a scan of the official charts, the attempt to define common functions in an incubator chart.
The trickiest bit is being able to give a good error - the closest I've seen is the sprig fail function
But helm3 should provide for this kind of validation, either with schemas or lua
Otherwise perhaps you could do it like:
aws:
nginx: false
varnish: false
php: true
So that the chart user chooses which services they want with a true/false

Helm simple for loop

I can not find a way to just iterate over a simple range, 10 -> 20 using helm templating.
{{range 10 until 20 }}
- port: {{ . }}
targetPort: {{ . }}
protocol: TCP
name: brick-{{ . }}
{{end}}
Helm uses the standard Go text/template system for rendering templates, plus (most of) the Sprig extension library, plus a couple more things. In particular, Sprig includes until and untilStep functions to generate lists of numbers, which you can then range over. So you should be able to:
{{- range untilStep 10 20 1 }}
- port: {{ . }}
...
{{- end }}

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.