Helm for loop list - kubernetes-helm

I wanted to use one deployment file and value file to create charts for multiple services.
My value file has the values of all the service, that has to be used one deployment file.
below is my deployment file content
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Values.PA.name }}-deployment
labels:
app: {{ .Values.PA.name }}
spec:
replicas: {{ .Values.PA.replicas }}
selector:
matchLabels:
app: {{ .Values.PA.name }}
template:
metadata:
labels:
app: {{ .Values.PA.name }}
spec:
containers:
- name: {{ .Values.PA.name }}
image: {{ .Values.PA.image }}:{{ .Values.PA.tag }}
ports:
- containerPort: {{ .Values.PA.port }}
Below is my values file
PA:
name: povisioning_adapter
replicas: 1
env: dev
image: provisioning_adapter
tag: master
port: 8001
service:
protocol: TCP
port: 8001
targetPort: 8001
nodePort: 30100
SA:
name: service_adapter
replicas: 1
env: dev
image: service_adapter
tag: master
port: 8002
service:
protocol: TCP
port: 8002
targetPort: 8002
nodePort: 30200
Now I want to iterate through PA, SA values, etc. inside my deployment file.
How to declare list [PA,SA,..] and for loop through it inside deployment file?

You can wrap this in a range loop:
{{- range list .Values.PA .Values.SA -}}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .name }}-deployment
...
{{ end -}}
If you need to refer to the top-level .Values, in this setup you'd need to "escape" the range loop's scoping by explicitly referring to the top-level value $. You also might need this in a template parameter.
metadata:
labels:
name: {{ .name }}
{{ include "myapp.labels" $ | indent 4 }}
{{/* ^ */}}
You could do something similar breaking this out into a helper template that produced one of the Kubernetes objects. You may be able to restructure this to use the name of the component rather than its specific settings; where you currently have .Values.PA.name, if you have the top-level $.Values object and you know the name, then index $.Values "PA" "name" is equivalent, and any of those parts can be replaced by variables.

Related

Service creation loop using range function in Helm

I am trying to create k8s service of type load balancer using range loop in helm.I need to create k8s service pointing to dedicated POD.I have deployed 3 pods, up and running.I am trying to create 3 services pointing to 3 different pods.
{{- $replicas := .Values.replicaCount | int -}}
{{- $namespace := .Release.Namespace }}
{{- range $i,$e := until $replicas }}
---
apiVersion: v1
kind: Service
metadata:
labels:
app: abc-svc
statefulset.kubernetes.io/pod-name: abc-{{ $i }}
name: service-{{ $i }}
namespace: {{ $namespace }}
spec:
ports:
- protocol: TCP
targetPort: 2000
port: {{ . | printf ".Value.ports.port_%d" | int }}
selector:
app: abc-svc
statefulset.kubernetes.io/pod-name: abc-{{ $i }}
type: LoadBalancer
{{- end }}
my values.yaml
ports:
port_1: 30001
port_2: 30002
port_3: 30003
replicaCount: 3
dry-run is giving below put:
# Source: t1/templates/svc.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: abc-svc
statefulset.kubernetes.io/pod-name: abc-0
name: service-0
namespace: xyz
spec:
ports:
- protocol: TCP
targetPort: 2000
port: 0
selector:
app: abc-svc
statefulset.kubernetes.io/pod-name: abc-0
type: LoadBalancer
---
# Source: t1/templates/svc.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: abc-svc
statefulset.kubernetes.io/pod-name: abc-1
name: service-1
namespace: xyz
spec:
ports:
- protocol: TCP
targetPort: 2000
port: 0
selector:
app: abc-svc
statefulset.kubernetes.io/pod-name: abc-1
type: LoadBalancer
---
# Source: t1/templates/svc.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: abc-svc
statefulset.kubernetes.io/pod-name: abc-2
name: service-2
namespace: xyz
spec:
ports:
- protocol: TCP
targetPort: 2000
port: 0
selector:
app: abc-svc
statefulset.kubernetes.io/pod-name: abc-2
type: LoadBalancer
I need port number pointing to correct port in according to values.yaml file.For service-0,service-1,service-2 I need 30001,30002,30002 ports assigned.Please suggest.Thank you !!
To do the dynamic lookup in the port list, you need to use the index function. This is part of the standard Go text/template language and not a Helm extension:
port: {{ index .Values.ports (add $i 1 | printf "port_%d") }}
This could be slightly simpler if you change the list of ports to be a list and not a map:
# values.yaml
ports:
- 30001
- 30002
- 30003
replicaCount: 3
port: {{ index .Values.ports $i }}
If you didn't require access to specific pods from outside the cluster, a StatefulSet creates a cluster-internal DNS name for each pod on its own, and you could avoid this loop entirely.

Looping with helm

I'm reading the helm documentation about how to do some loops for Kubernetes, basically what i want to do is something like this.
What i have...
values.yaml
dnsAliases:
- test1
- test2
- test3
on services-external.yaml
{{- if and .Values.var1.var1parent (eq .Values.var2.var2parent "value") }}
{{- range .Values.dnsAliases }}
apiVersion: v1
kind: Service
metadata:
name: name-{{ . }} ( for creating the name "name-test1/test2 and so on"
spec:
type: ExternalName
externalName: {{ .Values.var3.var3parent }}-{{ .Values.var4.var4parent }}-{{ .}}.svc.cluster.local
ports:
- port: 80
protocol: TCP
targetPort: 80
{{ end }}
{{ end }}
but im having the error
Error: UPGRADE FAILED: render error in "services-external.yaml": template: templates/services-external.yaml:312:32: executing "services-external.yaml" at <.Values.var3.var3parent>: can't evaluate field Values in type interface {}
I tried also with "with" but same error. Is there some way to achieve it by using the "if" with a loop on helm?
Error you have shows that template can't find values for <.Values.var3.var3parent>. Since you're using range block, . refers to local variables within the loop. You need to refer to global variables. This can be achieved with two approaches:
Use $ before a variable you need to invoke (see it with var3)
Define a new variable and save values you need to this variable (see it with var4)
Here's a tested template with two approaches from the above:
{{- if and .Values.var1.var1parent (eq .Values.var2.var2parent "value") }}
{{- $var4 := .Values.var4 -}}
{{- range .Values.dnsAliases }}
apiVersion: v1
kind: Service
metadata:
name: name-{{ . }} ( for creating the name "name-test1/test2 and so on"
spec:
type: ExternalName
externalName: {{ $.Values.var3.var3parent }}-{{ $var4.var4parent }}-{{ .}}.svc.cluster.local
ports:
- port: 80
protocol: TCP
targetPort: 80
{{ end }}
{{ end }}
You can read about it more here
Also there is one more possible solution for this to reset the scope to root and work with loop as usually (but it's more sketchy approach) (here's a link)
Thanks #moonkotte i managed to make it work using the approach of defining a new variable to save the scope, here the example.
On values.yaml
dnsShortNames:
short1: "short1"
short2: "short2"
short3: "short3"
dnsAliases:
- test1
- test2
- test3
on services-external.yaml
{{- $dns_short_names := .Values.dnsShortNames }}
{{- range .Values.dnsAliases }}
---
apiVersion: v1
kind: Service
metadata:
name: name-{{ . }}
spec:
type: ExternalName
externalName: {{ $dns_short_names.short1 }}-{{ $dns_short_names.short2 }}-{{ $dns_short_names.short3 }}{{.}}.svc.cluster.local
ports:
- port: 80
protocol: TCP
targetPort: 80
{{- end }}
Applying this Kubernetes will create 3 different external services.
short1-short2-short3.test1.svc.cluster.local
short1-short2-short3.test2.svc.cluster.local
short1-short2-short3.test3.svc.cluster.local
Public thanks to my friend Xavi <3.

HELM cannot find Deployment.spec.template.spec.containers[0]

I am building a boilerplate HELM Chart, but HELM cannot find the container name. I have tried a hard coded name as well as various formulations of the variable. Nothing works. I am stumped. Please help!
ERROR MSG
Error: unable to build kubernetes objects from release manifest: error validating "": error validating data: ValidationError(Deployment.spec.template.spec.containers[0]): missing required field "name" in io.k8s.api.core.v1.Container
deployment.yaml
apiVersion: "apps/ {{ .Release.ApiVersion }}"
kind: Deployment
metadata:
name: {{ .Release.Name }}
labels:
app: {{ .Values.deploy.image.name }}
spec:
replicas: {{ .Values.deploy.replicas }}
selector:
matchLabels:
app: {{ .Values.deploy.image.name }}
template:
metadata:
labels:
app: {{ .Values.deploy.image.name }}
spec:
containers:
- name: {{ .Values.deploy.image.name }}
image: {{ .Values.deploy.image.repository }}
imagePullPolicy: {{ .Values.deploy.image.pullPolicy }}
resources: {}
values.yaml
deploy:
type: ClusterIP
replicas: 5
image:
name: test
repository: k8stest
pullPolicy: IfNotPresent
service:
name: http
protocol: TCP
port: 80
targetPort: 8000
Your example works for me just fine, I copy pasted your code and only changed apiVersion to apps/v1. Since you say you have tried to hard code the name and still isn't working for you, I would think the problem is somewhere in the white space characters.

Is it allowed to have multi service in helm chart?

I am pretty newbie in Helm and would like to know, if it is allowed to have multi services in service.yaml file like:
apiVersion: v1
kind: Service
metadata:
name: {{ include "keycloak.fullname" . }}
labels:
{{- include "keycloak.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "keycloak.selectorLabels" . | nindent 4 }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ include "keycloak.fullname" . }}
labels:
{{- include "keycloak.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "keycloak.selectorLabels" . | nindent 4 }}
Yes it is, are you facing any issue?
A cleaner way is to use two different files service-a.yaml and service-b.yaml
Note: Better not to have both the services with the same name.

Does helm support Endpoints object type?

I've created the following to objects:
apiVersion: v1
kind: Service
metadata:
name: {{ .Values.serviceName }}
namespace: {{ .Values.global.namespace }}
labels:
chart: {{ template "chartName" . }}
env: {{ .Values.global.env }}
annotations:
"helm.sh/hook": "pre-install"
"helm.sh/hook-weight": "10"
"helm.sh/hook-delete-policy": "before-hook-creation"
spec:
ports:
- port: {{ .Values.postgres.port}}
selector: {}
for a service and its endpoint:
kind: Endpoints
apiVersion: v1
metadata:
name: {{ .Values.serviceName }}
namespace: {{ .Values.global.namespace }}
labels:
chart: {{ template "chartName" . }}
env: {{ .Values.global.env }}
annotations:
"helm.sh/hook": "pre-install"
"helm.sh/hook-weight": "10"
"helm.sh/hook-delete-policy": "before-hook-creation"
subsets:
- addresses:
- ip: "{{ .Values.external.ip }}"
ports:
- name: "db"
port: {{ .Values.external.port }}
When I use helm even in a dry run mode I can see the service object and cant see the endpoint object.
Why? Doesn't helm support all k8s objects?
Helm is just a "templating" tool, so technically it supports everything that your underlying k8 supports.
In your case, please check that both files are in the templates directory
Actually it does work. The problem was that the service and the endpoint MUST have same names (which I new) and MUST have port names exactly the same