Helm has/merge option for overrides - kubernetes-helm

I am trying to create a template whereby values have a default set of service names, unless they are overriden. example:
default:
service:
- name: nginx1
service: "dev-nginx1"
port: 8080
- name: nginx2
service: "dev-nginx2"
port: 8080
identifiers:
- identifier: "cust1"
- identifier: "cust2"
- identifier: "cust3"
overrides:
identifiers:
- identifier: "cust2"
service:
- name: nginx4
service: "cust2-nginx4"
port: 8080
- name: nginx12
service: "cust2-nginx12"
port: 8080
Where the above would yield:
---
identifier: cust1
service: dev-nginx
port: 8080
service: dev-nginx2
port: 8080
---
identifier: cust2
service: cust2-nginx4
port: 8080
service: cust2-nginx12
port: 8080
---
identifier: cust3
service: dev-nginx
port: 8080
service: dev-nginx2
port: 8080
I have tried the following, but i'm getting in a mess with the iterations in the incorrect place. Is there an easier way to accomplish this in helm with some sort of merge function?
{{- range $key, $values := $.Values.default.identifiers -}}
{{- range $overrideKey, $overrideValues := $.Values.overrides.identifiers -}}
{{ if eq $values.identifier $overrideValues.identifier }}
---
identifier: {{ $values.identifier }}
{{- range $value := $overrideValues.service }}
service: {{ $value.name }}
port: {{ $value.port }}
{{- end }}
{{ else }}
---
identifier: {{ $values.identifier }}
{{- range $value := $overrideValues.service }}
service: {{ $value.name }}
port: {{ $value.port }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}

Related

Helm with nested lists using range

I have the following content in values.yaml and virtualservice.yaml:
I want to create template file using above value but getting error while helm rendenring template.
values.yaml:
istio:
enabled: true
virtualService:
enabled: true
virtualServices:
"0":
name: hello-app
gateways:
- gateway-new
hosts:
- prod.abc.com
apps:
name: primary
path: "/api"
routes:
"0":
weight: 100
port: 8080
name: hello-app
"1":
weight: 0
port: 8080
name: hello-app-canary
"1":
name: hello-app-internal
gateways:
- mesh
hosts:
- hello-app.test.prod.svc.cluster.local
apps:
name: internal
path: "/api"
routes:
"0":
weight: 100
port: 9081
name: hello-app
"1":
weight: 0
port: 9081
name: hello-app-canary
virtualservice.yaml:
{{- if ((.Values.istio).enabled) }}
{{- if ((.Values.istio.virtualService).enabled) }}
{{- range $key, $value := .Values.istio.virtualService.virtualServices }}
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
name: {{ .name }}
namespace: {{ $.Release.Namespace }}
labels:
{{ include "common.labels" $ | indent 4 }}
spec:
gateways:
{{- range .gateways }}
- {{.}}
{{- end }}
hosts:
{{- range .hosts }}
- {{.}}
{{- end }}
http:
{{- range $app := $value.apps }}
- match:
- uri:
prefix: "/{{ $app.path }}"
name: {{ $app.name }}
route:
{{- range $route := $app.routes }}
- destination:
host: {{ $route.name }}
port:
number: {{ $route.port }}
weight: {{ $route.weight }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
How do I iterate if I have more than one list using range? With the above template, I am getting the error:
at <$app.path>: can't evaluate field path in type interface {}
Any help is highly appreciated.
First of all, it looks like the virtual service "1" has one extra indent (right now it is under virtual service "0" section, but it should be under "virtualServices").
Moreover, If you want to iterate over the apps of each virtual service, the apps field should be a list, but right now it is a dictionary. Therefore, when you iterate with range $app := $value.apps you are actually iterating over the fields of apps, which leads to the error you are getting.
Here's the fixed version of values.yam:
istio:
enabled: true
virtualService:
enabled: true
virtualServices:
"0":
name: hello-app
gateways:
- gateway-new
hosts:
- prod.abc.com
apps:
- name: primary
path: "/api"
routes:
"0":
weight: 100
port: 8080
name: hello-app
"1":
weight: 0
port: 8080
name: hello-app-canary
"1":
name: hello-app-internal
gateways:
- mesh
hosts:
- hello-app.test.prod.svc.cluster.local
apps:
- name: internal
path: "/api"
routes:
"0":
weight: 100
port: 9081
name: hello-app
"1":
weight: 0
port: 9081
name: hello-app-canary

I want use helm range for below code to iterate its with proper indentation

I want to iterate the list from Values.yaml through range in helm.
So how to use the range function in helm for below list?
Values.yaml
Service:
- name: jenkins-db
protocol: TCP
port: 8000
- name: jenkins-ui
protocol: TCP
port: 9000
Thanks in advance
Try out this:
{{- range .Values.Service }}
apiVersion: v1
kind: Service
metadata:
name: {{ .name }}
spec:
selector:
app: {{ .name }}
ports:
- protocol: {{ .protocol }}
port: {{ .port }}
targetPort: {{ .port }}
---
{{- end }}

Helm chart ingress.yaml return error error nil pointer evaluating interface

Following a task on using helm chart, but was not able to install ingress.yaml. It is complaining about a class. The error is pasted below. Is there anything am missing here?
ingress/templates/ingress.yaml:6:43: executing "ingress/templates/ingress.yaml" at <.Values.ingress.annotations.class>: nil pointer evaluating interface {}.class
The code for the ingress.yaml is below :
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Values.ingress.name }}
annotations:
kubernetes.io/ingress.class: {{ .Values.ingress.annotations.class }}
spec:
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: Prefix
backend:
service:
name: {{ .backend.service.name }}
port:
number: {{ .backend.service.port.number }}
{{- end }}
{{- end }}
The values in the values.yaml is :
ingress:
name: ingress-service
replicaCount: 1
annotations:
class: nginx
hosts:
- host: chart-example.local
paths:
- path: /
backend:
service:
name: ServiceName
port:
number: 8080
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Values.ingress.name }}
annotations:
kubernetes.io/ingress.class: {{ .Values.ingress.annotations.class }}
spec:
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: Prefix
backend:
service:
name: {{ .backend.service.name }}
port:
number: {{ .backend.service.port }}
{{- end }}
{{- end }}

Ingress.yaml for multiple Ingresses in values.yaml

I have the following values.yaml
ingresses:
- name: public
class: "nginx"
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: 122m
nginx.ingress.kubernetes.io/proxy-connect-timeout: "7"
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
nginx.ingress.kubernetes.io/proxy-send-timeout: "30"
labels: {}
rules:
- host: example.com
http:
paths:
- path: /asd/as
pathType: ImplementationSpecific
backend:
service:
name: one
port:
number: 8080
- backend:
service:
name: log
port:
number: 8081
path: /path/log
pathType: ImplementationSpecific
- backend:
service:
name: got
port:
number: 8082
path: /api/got
pathType: ImplementationSpecific
tls:
- hosts:
- example.com
secretName: cert
- name: public
annotations:
labels: {}
rules:
- host: example1.com
http:
paths:
- backend:
service:
name: web
port:
number: 8090
pathType: ImplementationSpecific
tls:
- hosts:
- example1.com
secretName: qwe
and I have the following ingress file:
{{- $top := . -}}
{{- range $ingress := .Values.ingresses }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ $ingress.name }}
namespace: {{ $ingress.namespace }}
{{- with $ingress.annotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- if and $ingress.class (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
ingressClassName: {{ $ingress.class }}
{{- end }}
{{- if $ingress.tls }}
tls:
{{- range $ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range $ingress.rules }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
{{- if and .path (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
- path: {{ .path }}
{{ end }}
{{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }}
pathType: {{ .pathType }}
{{- end }}
backend:
service:
name: {{ .backend.service.name }}
port:
number: {{ .backend.service.port.number}}
{{- end }}
{{- end }}
{{- end }}
This only generates one ingress (whichever is the last one in values files). I tried using range $ingress := .Values.ingress but it keeps giving me an error whenever I try $ingress.name . What changes do I make to the ingress.yaml to be able to deploy both these ingresses.
Edit: Made edits based on David's answer.
You need to break the two separate ingress configurations up in the Helm values somehow. Right now they're in a single map object under ingress:, so .Values.ingress.name for example only has one value rather than being something you can iterate over.
A YAML list here makes sense:
# values.yaml
ingresses:
- name: example-com
class: nginx
rules: [...]
- name: example1-com
class: nginx
rules: [...]
Then you can iterate over this list with a range loop. The important thing to know about a range loop is that it rebinds the . special variable, which is the base of constructs like .Values; that means that you need to save the original value of . outside the loop (the $ special variable may work as well). You can generate multiple Kubernetes objects in a single Helm template file so long as each begins with the YAML --- start-of-document marker (and it's valid to generate no output at all).
{{-/* save the original value of . */-}}
{{- $top := . -}}
{{-/* iterate over the ingress configurations */-}}
{{- range $ingress := .Values.ingresses }}
---
{{-/* your existing conditionals can go here, simplifying */}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
{{-/* this comes from the per-ingress config */}}
{{- with $ingress.annotations }}
annotations: {{- toYaml . | nindent 4 }}
{{- end }}
{{-/* if you need to use "standard" helper functions, make sure
to pass the saved $top value as their parameter */}}
name: {{ include "mychart.fullname $top }}-{{ $ingress.name }}
spec: { ... }
{{- end }}
You also may want to reconsider how much of this is appropriate to include in arbitrarily-configurable values. Rather than essentially write out the entire Ingress object in Helm values, you may find it easier to write out things like the path mappings in the template files themselves, and have a few high-level controls ("enabled", "host name", "TLS secret name") exposed. Things like the backend service name and port will correspond to other things in your chart and you may need to compute the service name; someone just installing your chart shouldn't need to configure this.

How to override ingress rules helm-chart template

Here is my ingress.yml file
spec:
rules:
- host: {{- if .Values.ingress.host }} {{ tpl .Values.ingress.host . }} {{- end }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ssl-redirect
port:
name: use-annotation
{{- range $port := .Values.container.app.port }}
- path: {{ tpl $port.path $ }}
pathType: Prefix
backend:
service:
name: {{ $.Release.Name }}
port:
number: {{ int $port.port }}
{{- end }}
{{ end }}
I want to override this rules some service here is what I try to do in my values.yml file
ingress:
scheme: internal
host: test.com
paths:
- path: /
pathType: Prefix
backend:
service:
name: ssl-redirect
port:
name: use-annotation
- path: /
pathType: Prefix
backend:
service:
name: test
port:
number: 3000
but after I deploy it's not override at all may be in wrong format not sure
UPDATE
I try to this way but It still didn't override rules in ingress
hosts:
- host: test.com
paths:
- path: /test
pathType: Prefix
backend:
service:
name: ssl-redirect
port:
name: use-annotation
- path: /test
pathType: Prefix
backend:
service:
name: test-dev
port:
number: 3000
ingress.yaml should be something like
kind: Ingress
metadata:
name: {{ $name }}
labels:
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- with $_annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ . }}
backend:
serviceName: {{ $name }}
servicePort: 80
{{- end }}
{{- end }}
{{- end }}
values.yaml
ingress:
enabled: false
annotations: {}
# kubernetes.io/ingress.class: nginx
# kubernetes.io/tls-acme: "true"
hosts:
- host: test.example.io
paths: [/path]
# tls: []
# - secretName: chart-example-tls
# hosts:
# - chart-example.local
Another example :
Ingress.yaml : https://github.com/helm/charts/blob/master/stable/ghost/templates/ingress.yaml
values.yaml : https://github.com/helm/charts/blob/master/stable/ghost/values.yaml
i'm assuming that this is not the whole ingress.yaml template because if it is, you are missing the apiVersion, kind and metadata block entirely. :)
helm can create a baseline chart with helm create <chartName>. in this baseline chart, there is a ingress.yaml template with a corresponding values.yaml which works just fine and follows helm best practices. i recommend you use that one to ease and speed up your workflow instead of writing your own template, even if you do not understand it at first. the generated templates by helm create are worth studying because there is a lot to learn from them and they are a good showcase of how to solve some common problems when writing helm charts and how to follow best practices for labeling, annotations etc.
anyways, if you want to stick to that data structure and create your own template, this is the template that worked during my tests. i used the helm template command to check if there are any rendering errors whilst providing your values.
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ .Release.Name }}
spec:
rules:
- host: {{ .Values.ingress.host }}
http:
paths:
{{- range .Values.ingress.paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
backend:
service: {{ .backend.service | toYaml | nindent 14 }}
{{- end }}