Nested values are not able to be evaluated - kubernetes-helm

I'm using helm v 3.7.0 and I have a parent chart with a few subcharts as dependancies.
One of the subcharts has a virtual service defined as per below.
Subchart Values.yaml:
ingress:
enabled: true
host: "hostname.local"
annotations: {}
tls: []
Subchart virtual.service.yaml:
{{- if .Values.ingress.enabled -}}
apiVersion: types.kubefed.io/v1beta1
kind: FederatedVirtualService
metadata:
name: {{ template "product.fullname" . }}-web-vservice
spec:
placement:
clusterSelector: {}
template:
spec:
gateways:
- {{ template "product.fullname" . }}-web-gateway
hosts:
- {{ .Values.ingress.host }}
http:
# Website
- match:
- uri:
prefix: /
route:
- destination:
host: {{ template "product.fullname" . }}-web
port:
number: 80
{{- end }}
When I run:
helm template . --debug
It errors out with:
Error: template: virtual.service.yaml:1:14: executing "virtual.service.yaml" at <.Values.ingress.enabled>: nil pointer evaluating interface {}.enabled
If I move the enabled boolean outside of ingress and update the if statement it works.
i.e.
new values:
ingressEnabled: true
ingress:
host: "hostname.local"
annotations: {}
tls: []
new virtual service:
{{- if .Values.ingressEnabled -}}
apiVersion: types.kubefed.io/v1beta1
kind: FederatedVirtualService
The problem is that this is happening all over the place with lots of different values but only where they are nested and I cannot make all values flat.
I have virtual services being specified in exactly the same way in other projects and they work perfectly. I don't believe the issue is with how I'm defining this (unless anyone can correct me?) so I think something else is preventing helm from being able to read nested values but I don't know where to look to investigate this odd behaviour.
What would make helm unable to read nested values?

Based on the comment above, I see you were able to overcome the issue and I want to add the answer to the question for other to find.
The default values.yaml file is lower case (even though we use .Values in the template).
What you can do is either:
make the filename start with a lowercase letter or
pass the values file explicitly with with -f option

Related

Error converting YAML to JSON: yaml: line 15: did not find expected alphabetic or numeric character

I want to set wildcard subdomain for my project, using k8s, nginx ingress controller, helm chart:
In ingress.yaml file:
...
rules:
- host: {{ .Values.ingress.host }}
...
In values.yaml file, I change host example.local to *.example.local:
...
ingress:
enabled: true
host: "*.example.local"
...
Then, when I install chart using helm chart:
Error: YAML parse error on example/templates/ingress.yaml: error converting YAML to JSON: yaml: line 15: did not find expected alphabetic or numeric character
How can I fix it?
Thank for your support.
YAML treats strings starting with asterisk in a special way - that's why the hostname with wildcards like *.example.local breaks the ingress on helm install.
In order to be recognized as strings, the values in ingress.yaml file should be quoted with " " characters:
...
rules:
- host: "{{ .Values.ingress.host }}"
...
One more option here - adding | quote :
...
rules:
- host: {{ .Values.ingress.host | quote }}
...
I've reproduced your issue, both these options worked correctly. More information on quoting special characters for YAML is here.
In your ingress.yaml put quotes around the host key.
host: "{{ .Values.ingress.host }}"
ingress:
enabled: true
host: "*.example.local"
you may need to use "---" instead of

Expose mulitple containerPort via values.yaml

Is there any way to pass array of ports in values.yaml file. I want to have multiple ContainerPorts to be set. I tried with --set "test.containerPort={8080,10102,19905} and got error message as invalid type for io.k8s.apimachinery.pkg.util.intstr.IntOrString: got "array", expected "string".
Any example/suggestions will be really helpful.
Helm uses the Go templating mechanism, so it actually takes your parameters from values.yaml and puts them into template/* files.
In other words, the way how you set multiple containers ports depends on the Helm Chart you use.
For example, if had a file template/my-statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
...
spec:
template:
spec:
containers:
ports:
{{ toYaml .Values.ports| indent 10 }}
...
Then, you could use the following values.yaml to set multiple container ports.
ports:
- name: my first port
containerPort: 5678
- name: my second port
containerPort: 5679

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

Not able to render the helm template without quotes

I have used almost all possible ways to render the helm template. But now I am out of ideas and seeking help:
values.yaml:
rollout:
namespace: xyz
project: default
baseDomain: "stage.danger.zone"
clusterDomain: cluster.local
manifest.yaml
apps:
certmanager:
source:
repoURL: 'https://artifactory.intern.example.io/artifactory/helm'
targetRevision: 0.0.6
chart: abc
helm:
releaseName: abc
values:
global:
imagePullSecrets:
- name: artifactory
baseDomain: "{{ .Values.rollout.baseDomain }}"
When I try to render the template using the below command in my main.yaml file that will produce the final result:
values: {{- tpl (toYaml $appValue.values | indent 6) $ }}
Expected result:
baseDomain: stage.danger.zone (without quotes)
What I am getting is:
baseDomain: 'stage.danger.zone'
If I try to remove the double quotes from: baseDomain: "{{ .Values.rollout.baseDomain }}", I get the following error:
[debug] error converting YAML to JSON: yaml: invalid map key: map[interface {}]interface {}{".Values.baseDomain":interface {}(nil)}
Any help or ideas to achieve the same?
This is an expected behaviour by YAML.
One dirty and bad hack would be
values: {{- tpl (toYaml $appValue.values | fromYaml | toYaml | indent 6) $ }}
and then you will not see the single quotes.
However, This is not a problem at all even if you have ' single quotes in your value. You can include this variable for example something like this below:
hosts:
- host: some-gateway.{{ .Values.rollout.baseDomain }}
serviceName: gateway
servicePort: 8080
path: /
Then it will show you your variable value without ' single quotes.
Example rendered output:
hosts:
- host: some-gateway.stage.danger.zone
path: /
serviceName: gateway
servicePort: 8080

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.