Helm template not able to read ip address - can't evaluate field ipAddress in type string - kubernetes

I am trying to create a helm template for Istio's ServiceEntry which has a list of addresses for static ip addresses. In values.yaml, I have
- name: test-se
namespace: test-se-ns
egressUrls:
- mydbhost.com
port: 32306
protocol: TCP
ipAddress: 10.2.2.2
In the .tpl file I am trying to add the value of ipAddress to a list
{{- with .ipAddress }}
addresses:
- {{ .ipAddress | quote }}
{{- end }}
Always fails with exception
templates/_service_entry.tpl:18:13: executing "common.serviceentry.tpl" at <.ipAddress>: can't evaluate field ipAddress in type string
Any idea what I am doing wrong?

If you use with you make the thing that you have used as with the context inside that block.
So, use the dot to refer to it.
{{- with .ipAddress }}
addresses:
- {{ . | quote }}
{{- end }}
From the docs:
{{with pipeline}} T1 {{end}}
If the value of the pipeline is empty, no output is generated;
otherwise, dot is set to the value of the pipeline and T1 is
executed.
In this case, an if seems also fitting, since you do not much with the new context.
{{- if .ipAddress }}
addresses:
- {{ .ipAddress | quote }}
{{- end }}

when you use with in Helm, you change the scope of the ., so Helm looks for an object and not a string, you can read more about it in the docs.
but anyway, I think that in your case, you need to use range instead of with, you can see an example here

Related

Merging two Dictionaries in Helm

I am using Helm 3. I have two values.yaml files. In common/values.yaml I have defined:
deployment:
ports:
- name: http
protocol: TCP
The common is of the type library. In my-app, which is of the type application, the common is added as a dependency. In my-app/values.yaml I have added:
deployment:
ports:
- containerPort: 8081
I have defined a template _deployment.yaml in common/templates. In this file I am trying to merge these two deployment dictionaries into one by using:
{{- $deployment := merge .Values.common.deployment .Values.deployment -}}
When I am printing {{ $deployment }}, it is giving output:
map[ports:[map[containerPort:8080 name:http protocol:TCP]]]
And if I do:
{{- $deployment := merge .Values.deployment .Values.common.deployment -}}
The output of {{ $deployment }} is:
map[ports:[map[containerPort:8081]]]
Moreover the output of {{ .Values.common.deployment }} is:
map[ports:[map[name:http protocol:TCP]]]
And the output of {{ .Values.deployment }} is:
map[ports:[map[containerPort:8081]]]
What I would like to have after merging is:
deployment:
ports:
- name: http
protocol: TCP
containerPort: 8081
Any advice you could give would be much appreciated.
Looks like the merge operation does not work as expected on lists (it's a common problem, as the merge operation is ambiguous on list: should a list be appended or replaced when merging ?)
Anyway, I would suggest to merge the ports data with:
{{- $ports := merge .Values.deployment.ports[0] .Values.common.deployment.ports[0] -}}
and render the result with:
deployment:
ports:
- {{- toJson $ports }}
HTH

How to use range function to keep values in same line in helm

I am trying to build configmap data out of values I have in values.yaml.
CASE 1:
values.yaml:
dns_domains: abc xyz
dns_servers: IP1 IP2 IP3
I want configmap data something as below for the above values.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: Corefile
data:
abc:53 {
log
errors
cache 30
forward . IP1 IP2 IP3
}
xyz:53 {
log
errors
cache 30
forward . IP1 IP2 IP3
}
CASE 2:
values.yaml:
dns_domains: abc xyz
dns_servers:
or
dns_domains: abc xyz
I want configmap data something as below for the above values.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: Corefile
data:
abc:53 {
log
errors
cache 30
}
xyz:53 {
log
errors
cache 30
}
I tried something as below and got stucked how to make "forward" line to have all values in the range
{{- range $domain := splitList " " .Values.dns_int_domains }}
$domain:53 {
log
errors
cache 30
{{- range $dns_int_server := splitList " " .Values.dns_int_servers }}
{{- if $dns_int_server }}
forward . $dns_int_server # how to make this line to have all values in dns_int_server list
{{- end }}
}
{{- end }}
Note: we should have only one forward plugin inside a server block. i.e, below is not allowed
abc:53 {
forward . IP1
forward . IP2
}
It would be really grateful if someone helps me out. Thanks in advance!!!
With the format you currently have, .Values.dns_servers is already a string with space-separated values, which is the format you want out. You don't need to split it to a list and write it out again.
{{- if .Values.dns_servers }}
forward . {{ .Values.dns_servers }}
{{- end }}
Helm contains (almost all of) the extension functions in the Sprig library, not all of which are in the Helm documentation proper. If you do have this as a list, there is a join template function that can combine them together.
{{- $dns_servers := splitList " " .Values.dns_servers }}
{{- if $dns_servers }}
forward . {{ join " " $dns_servers }}
{{- end }}
Rather than a space-separated string, you might find it easier to manipulate these values if you use native YAML lists in your values.yaml file. Any valid YAML list syntax will work here, including formats that put the entire list on one line.
# values.yaml, reformatted to use YAML lists and snakeCase names
dnsDomains: [abc,xyz]
dnsServers:
- 10.10.10.10
- 10.10.10.11
- 10.10.20.20
As one final option, if you're very careful about the whitespace handling, you can put the templating anywhere you want, even in the middle of a line.
{{- with .Values.dnsServers }}
forward .
{{- range . }} {{ . }}{{ end }}
{{- end }}
The important trick with this last example is that the - whitespace control before range also consumes the newline at the end of the previous line. Then inside the range block, repeated once for each element and with no whitespace control, there is a space and a list element. Finally after the very last end there is a newline.
You can double-check this with helm template, which will validate the YAML syntax and print out what got rendered (with --debug it will print it out even if it's invalid YAML).

Detect if variable is dict / array / list

I've the following values.yaml:
env:
NODES:
value:
- 192.168.178.1:1234
- 192.168.178.2:1234
- 192.168.178.3:1234
- 192.168.178.4:1234
PASSWORD:
valueFrom:
secretKeyRef:
name: foo
key: bar
Now, in the deployment I've the following lines to specify the environment:
env:
{{- range $name, $item := .Values.env }}
- name: {{ $name }}
{{- $item | toYaml | nindent 14 }}
{{- end }}
This works as long as "NODES" is no list because they are not allowed as env variable, but I would like to specify them as list in the values.yaml. So the question is, how I can test if $item.value is a dict or a simple string.
I tried using typeOf, but it tells me that NODES is "[]interface {}". So I wonder what's the correct way to test?!
My goal is, if I see an array in the env of my deployment to join them using ";", e.g. value: {{ join ";" .env.NODES.value | quote }}.
Helm has some template functions to inspect object types (from the Sprig support library) but these quickly get into the underlying Go type system.
For the specific thing you're asking, you might try the Helm/Sprig kindIs function. This passes through to the underlying Go reflect package but in particular it does distinguish slice (list) and map (dict) as specific kinds.
{{- if and $item.value (kindIs "slice" $item.value) }}
value: {{ join ";" $item.value }}
{{- end }}
Rather than trying to have your Helm values directly reflect the Kubernetes object structure, you might consider having configuration values like this list of nodes as top-level configuration values.
# values.yaml
# not inside an `env:` block, at the top level
nodes:
- 192.168.178.1:1234
- 192.168.178.2:1234
Then you can construct these from a known format in your template. This will be much more straightforward than trying to use the reflection functions.
env:
- name: NODES
value: {{ join ";" .Values.nodes }}

Helm private values

I could not find anything by just googling, does Helm support private values?
So I have my chart and my values.yaml
privateProp: hello
publicProp: world
I have some values that I want to exposed to the end user of my chart and others that I do not want, however those "private" values are being used in many places.
For example: publicProps is overridable by the user of the chart, but I would like to block access to privateProp, however it is reused in many places:
containers:
name: {{.Values.privateProp}}
nodeSelector:
name: {{.Values.privateProp}}
I saw there is {{$privateProp := "hello"}}, but it is not clear how I can access it elsewhere in my files
How can I achieve this?
Ok, I have found a solution to my problem.
You can create a file called _variables.tpl, the name does not matter
and then declare a variable:
{{- define "privateProp" -}}
{{- print "hello" -}}
{{- end -}}
and then you can use it wherever you want in your chart by doing this:
spec:
containers:
- name: {{ .Values.dashboard.containers.name }}
image: {{ .Values.dashboard.containers.image.repository }}:{{ .Values.dashboard.containers.image.tag }}
imagePullPolicy: Always
ports:
- containerPort: {{ include "privateProp" . }} # <== This

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