How to fail a helm release based on inputs in values.yaml - kubernetes

I'm installing up a helm chart using helm install command. I have values.yaml which takes a few inputs from the user. One of the keys in values.yaml is action, which can only take three predefined values (let's say action1, action2 and action3) as an input. Any other value other than this is invalid.
When a user provides the value to action field in values.yaml and trigger the helm install command, the first thing I need to check is that if the action key has a valid value or not. If the action value is invalid, I want the release to be failed with a proper error message.
e.g.: In case the user has given action: action4, this is not valid and release should fail as .Values.action can only be action1, action2, or action3.
How I can achieve this use case and which file should be best to handle this validation considering the helm structure?

I was able to achieve the use case with below changes.
Added the following code in _helpers.tpl
{{- define "actionValidate" -}}
{{ $action := .Values.actions }}
{{- if or (eq $action "action1") (eq $action "action2") (eq $action "action3") -}}
true
{{- end -}}
{{- end -}}
Invoked this function from a .tpl file like this:-
{{ include "actionValidate" . | required "Action value is incorrect. The valid values are 'action1', 'action2', 'action3' " }}

With HelmV3 there is now an easier way. Just specify a schema that includes your values.
For example:
title: Values
type: object
properties:
action:
description: Some action
type: string
pattern: "^(action1|action2|action3)$"

Not enough rep to comment. However, in response to #Saurabh, you now must YAML comment out the function invocation. Otherwise, Go Template will encounter an invalid return type runtime error from that random true value existing in the middle of the invocation file.
Example
# validation.yaml
---
#{{ include "actionValidate" . | required "Action value is incorrect. The valid values are 'action1', 'action2', 'action3' " }}

Related

How to substitute Boolean value from values.yaml

In some of the examples I am following, I see, for a string value, I can do this:
{{- with .Values.cookieDomain }}
- --cookieDomain={{- toString . }}
{{- end }}
with this, if I have, e.g. cookieDomain: .mydomain.com in the values.yaml, the template gets the correct value.
How to do the same/similar for boolean values?
For example, if I have this: proxyPass: true in values.yaml, how do I interpret that in template, as there is no toBool function.
The function toString, in your example, is actually moot, if the value contained in cookieDoman is already a string it will just do nothing.
What you have to understand in with .Values.cookieDoman is that the context has now changed from . being the root of the variables definition to being .Values.cookieDoman.
A little like doing a change of directory in a computer, if I cd /tmp, then ./some_file looks for a file in /tmp/some_file. Now if I cd /etc, the same command, ./some_file, will now look for a file /etc/some_file.
This controls variable scoping. Recall that . is a reference to the current scope. So .Values tells the template to find the Values object in the current scope.
Source: https://helm.sh/docs/chart_template_guide/control_structures/#modifying-scope-using-with
So, in your example, it is fine enough already to do
flags:
{{- with .Values.cookieDomain }}
- --cookieDomain={{- . }}
{{- end }}
This will renders in
flags:
- --cookieDomain=.mydomain.com
And, so, if you have a boolean, it is the exact same:
flags:
{{- with .Values.proxyPass }}
- --proxyPass={{- . }}
{{- end }}
Will give:
flags:
- --proxyPass=true

can't access helm .Values from a named template with non global context passed in

I'm trying to use a Helm named template that I plan to include with several different contexts, and the template has many values that are the same for all contexts.
Whenever I pass a context to template or include to invoke the named template, the references to .Values do not work, which is understandable because I'm explicitly setting a lower context.
In the Helm documentation for with, it claims there is a "global" variable $ that will allow reference to the global .Values, e.g., {{ $.Values... }}. This does not work (the example below shows the error).
I've also tried defining variables (using :=) and "enclosing" the include inside that variable definition (via indentation - I don't know if it matters) to make that variable available within the named template, but this doesn't work either.
I've also tried putting these in "globals" as described here which is more of a subchart thing and this doesn't work either.
So, I'm out of Helm tricks to make this work and will sadly have to re-define these many same variable many times - which makes the entire named template solution a bit less elegant - or just go back to having largely duplicate partially-parameterized templates.
What am I missing?
$ helm version
Client: &version.Version{SemVer:"v2.9+unreleased", GitCommit:"", GitTreeState:"clean"}
Values.yaml:
---
commonSetting1: "common1"
commonSetting2: "common2"
context1:
setting1: "c1s1"
setting2: "c1s2"
context2:
setting1: "c2s1"
setting2: "c2s2"
deployment.yaml:
---
{{- define "myNamedTemplate" }}
- name: {{ .setting1 }}
image: {{ $.Values.commonSetting1 }}
{{- include "myNamedTemplate" .Values.context1 }}
{{- include "myNamedTemplate" .Values.context2 }}
$ helm template test-0.1.0.tgz
Error: render error in "test/templates/deployment.yaml": template: test/templates/deployment.yaml:7:4: executing "test/templates/deployment.yaml" at <include "myNamedTemp...>: error calling include: template: test/templates/deployment.yaml:4:19: executing "myNamedTemplate" at <$.Values.commonSetti...>: can't evaluate field commonSetting1 in type interface {}
When I do this, I tend to explicitly pass in the top-level context object as a parameter. This gets a little tricky because the Go text/template templates only take a single parameter, so you need to use the (Helm/Sprig) list function to package multiple parameters together, and then the (standard text/template) index function to unpack them.
The template definition would look like:
{{- define "myNamedTemplate" }}
{{- $top := index . 0 }}
{{- $context := index . 1 }}
- name: {{ $context.setting1 }}
image: {{ $top.Values.commonSetting1 }}
{{ end }}
When you invoke it, you would then need to explicitly pass the current context as a parameter:
{{ include "myNamedTemplate" (list . .Values.context1) }}

Use variable in HELM template define function

Is it possible to use a variable inside the template define function? I attempted to wrap the variable in brackets but it seems to fail. Example
{{- define {{ .Chart.Name }}.deployment -}}
The names of template functions are always fixed strings. (This is common with almost all programming languages.) Since these names don't appear anywhere in the rendered YAML, it doesn't really matter what they're called. The only place there's a potential conflict is if your chart includes other subcharts as dependencies, or is included as a subchart; in that case all template functions share the same function namespace.
A common convention is to name templates following the current chart name; that is, matching the fixed string in the Chart.yaml file
{{- define "mychart.deployment" -}}
Using the Helm include function you can call templates with a dynamic name, but this is a somewhat unusual use.
values.yaml
global:
key: {}
deployment: appdeployv1
If you were to expand the name of the chart, you would do it like this
_helpers.tpl
{{- define "key.name" -}}
{{- default .Chart.Name .Values.deployment | trunc 63 | trimSuffix "-" -}}
{{- end -}}
That's all I can understand from your question. I hope this helps. You can try exploring docs and check Declaring and using templates with define and template heading for detailed info.

How to use Lookup function in Helm Chart

While deploying a Kubernetes application, I want to check if a particular PodSecurityPolicy exists, and if it does then skip installing it again.
I came across the helm lookup function, which allows us to check the existing K8 resources.
While I understand how to use this function to get all the resources of same kind, how do I use this function to check if a PodSecurityPolicy named "myPodSecurityPolicy" exists.
I tried something like this:
{{- if ne (lookup "v1" "PodSecurityPolicy" "" "") "myPodSecurityPolicy"}}
<do my stuff>
{{- end }}
But it doesn't look like I can compare it this way, seeing an error -
error calling ne: invalid type for comparison
Any inputs? Thanks in advance.
Please check your API version and PSP name. Lookup is returning a map or nil not a string and that's why you are getting that error. The following is working for me. For negative expression, just add not after if.
{{- if (lookup "policy/v1beta1" "PodSecurityPolicy" "" "example") }}
<found: do your stuff>
{{- end }}
HTH

Helm Charts - How do I use `default` on undefined object property values?

Using Helm, I was under the impression default would be the fallback if a variable is not defined. However, it doesn't appear Helm can get to values in sub-object hashes:
type: {{ default "NodePort" .Values.fpm.service.type }}
If .Values.fpm.service or service.type is not defined, it should use 9000.
However, attempting to template this throws a nil pointer error:
<.Values.fpm.service.type>: nil pointer evaluating interface {}.type
Is there a way to simply perform this level of variable testing? Or am I subjected to an if/else test?
The intent of this is to optionally define .fpm.service (and [..].type) within your values.yaml file.
(I'm building a Helm Library chart to handle optional definitions by main charts)
According to the official Helm doc (Using Default Function), the syntax is different and you should use it this way:
type: {{ .Values.fpm.service.type | default "NodePort" | quote }}
Doesn't look like there's really a good way to stop Helm from trying to dive into non-existing objects. I moved into a single line if condition, and it worked:
type: {{ if .Values.fpm.service -}} {{ .default "NodePort" .Values.fpm.service.type | quote }} {{- else -}} "NodePort" {{- end }}
This way, I check if fpm.service exists first, before trying .type check. It works, whether .service and .service.type is or is not defined.