I am not able to reference variable inside a nested variable in Helm. I am not able to do this nested reference, I want to retrieve app1_image and app1_tag using the value of the apps_label variable. How can I do that?
values.yaml:
apps:
- name: web-server
label: app1
command: /root/web.sh
port: 80
- name: app-server
label: app2
command: /root/app.sh
port: 8080
app1_image:
name: nginx
tag: v1.0
app2_image:
name: tomcat
tag: v1.0
deployment.yaml:
{{- range $apps := .Values.apps
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ $apps.name }}
labels:
app: {{ $apps.name }}
spec:
replicas: 1
selector:
matchLabels:
app:
template:
metadata:
labels:
app: {{ $apps.name }}
spec:
containers:
- name: {{ $apps.name }}
image: {{ $.Values.$apps.label.image }}: {{ $.Values.$apps.label.tag }}
ports:
- containerPort: {{ $apps.port}}
{{- end }}
The core Go text/template language includes an index function that you can use as a more dynamic version of the . operator. Given the values file you show, you could do the lookup (inside the loop) as something like:
{{- $key := printf "%s_image" $apps.label }}
{{- $settings := index $.Values $key | required (printf "could not find top-level settings for %s" $key) }}
- name: {{ $apps.name }}
image: {{ $settings.image }}:{{ $settings.tag }}
You could probably rearrange the layout of the values.yaml file to make this clearer. You also might experiment with what you can provide with multiple helm install -f options to override options at install time; if you can keep all of these settings in one place it is easier to manage.
Related
I'm trying to assign static IPs for Load Balancers in GKE to services by storing them in the values.yaml file as:
ip:
sandbox:
service1: xxx.xxx.201.74
service2: xxx.xxx.80.114
dev:
service1: xxx.xxx.249.203
service2: xxx.xxx.197.77
test:
service1: xxx.xxx.123.212
service2: xxx.xxx.194.133
prod:
service1: xxx.xx.244.211
service2: xxx.xxx.207.177
All works fine till I want to deploy to prod and that will fail as:
Error: UPGRADE FAILED: template: chart-v1/templates/service2-service.yaml:24:28: executing "chart-v1/templates/service2-service.yaml" at <.Values.ip.prod.service2>: nil pointer evaluating interface {}.service2
helm.go:94: [debug] template: chart-v1/templates/service2-service.yaml:24:28: executing "chart-v1/templates/service2-service.yaml" at <.Values.ip.prod.service2>: nil pointer evaluating interface {}.service2
and the part for service2-service.yaml looks like:
apiVersion: v1
kind: Service
metadata:
annotations:
appName: {{ include "common.fullname" . }}
componentName: service2
labels:
io.kompose.service: service2
name: service2
spec:
ports:
- name: "{{ .Values.service.service2.ports.name }}"
port: {{ .Values.service.service2.ports.port }}
protocol: {{ .Values.service.service2.ports.protocol }}
targetPort: {{ .Values.service.service2.ports.port }}
type: LoadBalancer
{{ if eq .Values.target.deployment.namespace "sandbox" }}
loadBalancerIP: {{ .Values.ip.sandbox.service2 }}
{{ else if eq .Values.target.deployment.namespace "dev" }}
loadBalancerIP: {{ .Values.ip.dev.service2 }}
{{ else if eq .Values.target.deployment.namespace "test" }}
loadBalancerIP: {{ .Values.ip.test.service2 }}
{{ else if eq .Values.target.deployment.namespace "prod" }}
loadBalancerIP: {{ .Values.ip.prod.service2 }}
{{ else }}
{{ end }}
selector:
io.kompose.service: service2
status:
loadBalancer: {}
Any clue why is complaining that is nil (empty)?
it could be due to the function changing the context and defined in values.yaml
Normally with range, we can use the $ for global scope, appName: {{ include "common.fullname" $ }}
When tested the same template by keeping the static value of the appName it worked for me, so there is no issue with access from values.yaml unless nil is getting set at .Values.ip.prod.service2.
in other case as you mentioned {{ (.Values.ip.prod).service2 }} multiple level nesting will solve issue.
I have a deployment file which takes the environment variables from the values.yaml file.
Also I want to add one more variable named "PURPOSE".
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Values.scheduler.name }}
spec:
selector:
matchLabels:
app: {{ .Values.scheduler.name }}
template:
metadata:
labels:
app: {{ .Values.scheduler.name }}
spec:
containers:
- name: {{ .Values.scheduler.name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
ports:
- containerPort: {{ .Values.scheduler.targetPort }}
imagePullPolicy: Always
env:
{{- toYaml .Values.envVariables | nindent 10 }}
- name: PURPOSE
value: "SCHEDULER"
The error I get is the following:
error converting YAML to JSON: yaml: line 140: did not find expected key
The env varaibles from the values file work fine,
the problem seems to be the variable "PURPOSE"
The problem was the formatting of the environment block.
I have used the below Solution to fix the error :
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Values.scheduler.name }}
spec:
selector:
matchLabels:
app: {{ .Values.scheduler.name }}
template:
metadata:
labels:
app: {{ .Values.scheduler.name }}
spec:
containers:
- name: {{ .Values.scheduler.name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
ports:
- containerPort: {{ .Values.scheduler.targetPort }}
imagePullPolicy: Always
env:
- name: PURPOSE
value: "SCHEDULER"
{{- toYaml .Values.envVariables | nindent 10 }}
I am thinking about creating a Kubernetes job in Ansible with random string (password) generated on the fly and injected to the args/command line. However I am not sure if what I am trying to achieve will work as the below Jinja template itself already imports data from the values YAML file.
apiVersion: batch/v1
kind: Job
metadata:
namespace: {{ deployment.namespace }} <- taken from the values YAML
name: create-secret
labels:
app: test
app.kubernetes.io/name: create-secret
app.kubernetes.io/component: test
app.kubernetes.io/part-of: test
app.kubernetes.io/managed-by: test
annotations:
spec:
backoffLimit: 0
template:
metadata:
namespace: {{ deployment.namespace }} <- taken from the values YAML
name: create-secret
labels:
app: test
app.kubernetes.io/name: create-secret
app.kubernetes.io/component: test
app.kubernetes.io/part-of: test
app.kubernetes.io/managed-by: test
spec:
restartPolicy: Never
containers:
- name: create-secret
command: ["/bin/bash"]
args: ["-c", "somecommand create --secret {{ lookup('community.general.random_string', min_lower=1, min_upper=1, min_special=1, min_numeric=1, length=15) }} --name 'test'"]
image: {{ registry.host }}/{{ images.docker.image.name }}:{{ images.docker.image.tag }} <- taken from the values YAML
It'll work fine, but (as you pointed out) due to golang/helm using the same template characters as jinja2 {{, you'll need to take one of two approaches: either wrap every golang set of mustaches in {{ "{{" }} in order for jinja2 to emit the text {{ in the resulting file, or change the jinja2 template delimiters to something other than {{
example 1
apiVersion: batch/v1
kind: Job
metadata:
namespace: {{ "{{" }} deployment.namespace {{ "}}" }}
name: create-secret
...
containers:
- name: create-secret
command: ["/bin/bash"]
args: ["-c", "somecommand create --secret {{ lookup('community.general.random_string', min_lower=1, min_upper=1, min_special=1, min_numeric=1, length=15) }} --name 'test'"]
image: {{ "{{" }} registry.host {{ "}}" }}/{{ "{{" }} images.docker.image.name {{ "}}:{{" }} images.docker.image.tag {{ "}}" }} <- taken from the values YAML
Although you'll also likely want to use | quote for that random_string since in my local example, it produce a password of 2-b19e2k#HUF=k` and that ` will be interpreted by the sh -c leading to an error
example 2
# my-job.yml.j2
apiVersion: batch/v1
kind: Job
metadata:
namespace: {{ deployment.namespace }} <- taken from the values YAML
name: create-secret
...
containers:
- name: create-secret
command: ["/bin/bash"]
args: ["-c", "somecommand create --secret [% lookup('community.general.random_string', min_lower=1, min_upper=1, min_special=1, min_numeric=1, length=15) %] --name 'test'"]
image: {{ registry.host }}/{{ images.docker.image.name }}:{{ images.docker.image.tag }} <- taken from the values YAML
- template:
src: my-job.yml.j2
dest: my-job.yml
variable_start_string: '[%'
variable_end_string: '%]'
I have defined the values.yaml like the following:
name: custom-streams
image: streams-docker-images
imagePullPolicy: Always
restartPolicy: Always
replicas: 1
port: 8080
nodeSelector:
nodetype: free
configHocon: |-
streams {
monitoring {
custom {
uri = ${?URI}
method = ${?METHOD}
}
}
}
And configmap.yaml like the following:
apiVersion: v1
kind: ConfigMap
metadata:
name: custom-streams-configmap
data:
config.hocon: {{ .Values.configHocon | indent 4}}
Lastly, I have defined the deployment.yaml like the following:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Values.name }}
spec:
replicas: {{ default 1 .Values.replicas }}
strategy: {}
template:
spec:
containers:
- env:
{{- range $key, $value := .Values.env }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
image: {{ .Values.image }}
name: {{ .Values.name }}
volumeMounts:
- name: config-hocon
mountPath: /config
ports:
- containerPort: {{ .Values.port }}
restartPolicy: {{ .Values.restartPolicy }}
volumes:
- name: config-hocon
configmap:
name: custom-streams-configmap
items:
- key: config.hocon
path: config.hocon
status: {}
When I run the container via:
helm install --name custom-streams custom-streams -f values.yaml --debug --namespace streaming
Then the pods are running fine, but I cannot see the config.hocon file in the container:
$ kubectl exec -it custom-streams-55b45b7756-fb292 sh -n streaming
/ # ls
...
config
...
/ # cd config/
/config # ls
/config #
I need the config.hocon written in the /config folder. Can anyone let me know what is wrong with the configurations?
I was able to resolve the issue. The issue was using configmap in place configMap in deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Values.name }}
spec:
replicas: {{ default 1 .Values.replicas }}
strategy: {}
template:
spec:
containers:
- env:
{{- range $key, $value := .Values.env }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
image: {{ .Values.image }}
name: {{ .Values.name }}
volumeMounts:
- name: config-hocon
mountPath: /config
ports:
- containerPort: {{ .Values.port }}
restartPolicy: {{ .Values.restartPolicy }}
volumes:
- name: config-hocon
configMap:
name: custom-streams-configmap
items:
- key: config.hocon
path: config.hocon
status: {}
I got an error in my Deoloyment.ysml file. I have made env in this file and assign values in values file. I got a syntax error in this file
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}
labels:
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/name: {{ include "name" . }}
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/name: {{ include "name" . }}
template:
metadata:
labels:
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/name: {{ include "name" . }}
spec:
containers:
- name: {{ .Release.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
resources: {}
env:
- name: MONGODB_ADDRESS
value: {{ .Values.mongodb.db.address }}
- name: MONGODB
value: "akira-article"
- name: MONGODB_USER
value: {{ .Values.mongodb.db.user | quote }}
- name: MONGODB_PASS
valueFrom:
secretKeyRef:
name: {{ include "name" . }}
key: mongodb-password
- name: MONGODB_AUTH_DB
value: {{ .Values.mongodb.db.name | quote }}
- name: DAKEN_USERID
value: {{ .Values.mongodb.db.userId | quote }}
- name: DAKEN_PASSWORD
valueFrom:
secretKeyRef:
name: {{ include "name" . }}
key: daken-pass
- name: JWT_PRIVATE_KEY
valueFrom:
secretKeyRef:
name: {{ include "name" . }}
key: jwt-Privat-Key
- name: WEBSITE_NAME
value: {{ .Values.website.Name }}
- name: WEBSITE_SHORT_NAME
value: {{ .Values.website.shortName }}
- name: AKIRA_HTTP_PORT
value: {{ .Values.website.port }}
ports:
- containerPort: {{ .Values.service.port }}
I got this error:
Error: Deployment in version "v1" cannot be handled as a Deployment:
v1.Deployment.Spec: v1.DeploymentSpec.Template:
v1.PodTemplateSpec.Spec: v1.PodSpec.Containers: []v1.Container:
v1.Container.Env: []v1.EnvVar: v1.EnvVar.Value: ReadString: expects "
or n, but found 8, error found in #10 byte of
...|,"value":8080}],"ima|..., bigger context
...|,"value":"AA"},{"name":"AKIRA_HTTP_PORT","value":8080}],"image":"dr.xenon.team/websites/akira-fronte|...
Answer to your problem is available in Helm documentation QUOTE STRINGS, DON’T QUOTE INTEGERS.
When you are working with string data, you are always safer quoting the strings than leaving them as bare words:
name: {{ .Values.MyName | quote }}
But when working with integers do not quote the values. That can, in many cases, cause parsing errors inside of Kubernetes.
port: {{ .Values.Port }}
This remark does not apply to env variables values which are expected to be string, even if they represent integers:
env:
- name: HOST
value: "http://host"
- name: PORT
value: "1234"
I'm assuming you have put the port value of AKIRA_HTTP_PORT inside quotes, that's why you are getting the error.
You can read the docs about Template Functions and Pipelines.
With AKIRA_HTTP_PORT: "8080" in values.yaml, in the env variables write:
env:
- name: AKIRA_HTTP_PORT
value: {{ .Values.website.port | quote }}
It should have to work