How to override ingress rules helm-chart template - kubernetes

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 }}

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

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.

Error: UPGRADE FAILED: error validating "": error validating data: ValidationError(Ingress.spec.rules[0].http): missing required field "paths"

I am very new to using helm charts and not sure why I get this error when I try to install my helm chart. I am using --set with helm install command to set the hostname at ingress.hosts[0].host.I do not understand why it says missing paths where as "paths" is already present.
ingress.yaml
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "project.fullname" . -}}
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
app.kubernetes.io/name: {{ include "project.name" . }}
helm.sh/chart: {{ include "project.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- with .Values.ingress.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: {{ $fullName }}
servicePort: http
{{- end }}
{{- end }}
{{- end }}
values.yaml
...
...
...
ingress:
enabled: true
hostname: some_hostname
annotations:
kubernetes.io/ingress.class: nginx
kubernetes.io/tls-acme: "true"
nginx.ingress.kubernetes.io/proxy-connect-timeout: "180"
nginx.ingress.kubernetes.io/proxy-send-timeout: "180"
nginx.ingress.kubernetes.io/proxy-read-timeout: "180"
hosts:
- host: some_hostname
paths: [/]
tls:
- secretName: some_secretname
hosts:
- some_hostname
resources: {}
...
...
...
command to install helm
helm upgrade --install $(PROJECT_NAME) --set ingress.hosts[0].host="${HOST_NAME} --set ingress.tls[0].hosts="{${HOST_NAME}}""
error:
Error: UPGRADE FAILED: error validating "": error validating data: ValidationError(Ingress.spec.rules[0].http): missing required field "paths" in io.k8s.api.extensions.v1beta1.HTTPIngressRuleValue
I had the same issue, for some reason if you define the host in the --set you also have to define the path in the set (even though it matches the yaml). Like so,
helm upgrade --install $(PROJECT_NAME) --set ingress.hosts[0].host=${HOST_NAME} --set ingress.hosts[0].paths[0]=/
I haven't done tls yet so I am not sure if that has the same issue.

Helm template is not compatible to values.yaml structure

I have the following ingress.yaml:
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 }} // row 31
http:
paths:
{{- range .paths }}
- path: {{ . | quote }}
backend:
serviceName: {{ $fullName }}
servicePort: {{ $svcPort }}
{{- end }}
{{- end }}
{{- end }}
And the following values to feed this template:
hosts:
host: "app.example.com"
paths:
- "/api"
- "/oauth"
tls:
- secretName: "example-tls"
hosts:
- "*.app.example.com"
- "dev.example.com"
When I run "helm install" it fails on:
Error: UPGRADE FAILED: template: templates/ingress.yaml:31:15:
executing "templates/ingress.yaml" at <.host>: can't evaluate field
host in type interface {}
So for me it looks like hosts must be a list, not a dictionary (because of range instruction). So I convert it:
hosts:
- host: "app.example.com"
paths:
- "/api"
- "/oauth"
But in this case I get:
warning: destination for hosts is a table. Ignoring non-table value
[map[host:app.example.com paths:[/api /oauth]]]
and the same error as above in addition.
How to make it working?
UPDATE 1
Values:
ingress:
enabled: true
rules:
- host: c1.app.example.com
paths:
- /api
- /oauth
- host: c2.app.example.com
paths:
- /api
- /oauth
tls:
- secretName: "example-tls"
hosts:
- "*.app.example.com"
- "dev.example.com"
Template:
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.rules }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ . | quote }}
backend:
serviceName: {{ $fullName }}
servicePort: {{ $svcPort }}
{{- end }}
{{- end }}
{{- end }}
UPDATE 2
I understood that problem was not in code but in command line. I fed with string instead of array.
helm template ... --set ingress.hosts.host=c1.app.example.com ...
I will try to figure out how to provide multiple values and update it here.
UPDATE 3
I erased data from values:
ingress:
enabled: false
rules:
- host:
tls:
- secretName:
hosts: []
The template is looking for .Values.ingress.hosts, whereas in your displayed values there is no ingress prefix. And as range operator is being used, we should have a list of dictionary.
Also, before doing a helm install, it would be good to run helm template just to make sure the the YAML definitions are rendered correctly.
Considering the below content in values.yaml:
---
ingress:
hosts:
-
host: app1.example.com
paths:
- /api
- /oauth
-
host: app2.example.com
paths:
- /api1
- /authz
tls:
- hosts:
- "*.app.example.com"
- "dev.example.com"
secretName: "example-tls"
Running helm template command results in (I have defined serviceName as haproxy, and servicePort as 8080 for illustration):
spec:
tls:
- hosts:
- "*.app.example.com"
- "dev.example.com"
secretName: example-tls
rules:
- host: app1.example.com // row 31
http:
paths:
- path: "/api"
backend:
serviceName: haproxy
servicePort: 8080
- path: "/oauth"
backend:
serviceName: haproxy
servicePort: 8080
- host: app2.example.com // row 31
# similar output for app2.example.com
Answering my own question.
The problem was in mismatch of ingress template structure and command line arguments that I have provided for params override.
This is the proper fit of command line arguments:
helm upgrade <some other options here>
--values ./values.yaml
--set ingress.enabled=True
--set ingress.tls[0].hosts[0]=app.example.com
--set ingress.tls[0].secretName=example-tls
--set ingress.rules[0].host=app.example.com
That populates values.yaml:
ingress:
enabled: false
rules:
- host:
tls:
- secretName:
hosts: []
For ingress template:
spec:
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- secretName: {{ .secretName }}
hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.rules }}
- host: {{ .host | quote }}
http:
paths:
- path: /api
backend:
serviceName: {{ $fullName }}
servicePort: {{ $svcPort }}
{{- end }}
{{- end }}