Helm yaml keys from values.yaml - yaml

I want to make a yaml KEY (not the value) dynamically.
In my values.yaml
failoverip1: 0.0.0.0` (<- this is only a demo IP)
In my templates/configmap.yaml I have this:
apiVersion: v1
kind: ConfigMap
metadata:
name: vip-configmap
data:
{{- .Values.failoverip1 -}}: {{ .Release.Namespace -}}/{{- .Values.target -}}
^^^^^^^^^^^^^^^^^^^^^----> here should be an IP address from values.yaml
{{ .Release.Namespace -}}/{{- .Values.target -}} renders successfully.
But if I add {{- .Values.failoverip1 -}} to the key part, it renders nothing.
(Nothing means, the whole data: block, does not get rendered.
This is the error message when I run helm install --name hetzner-failover .
Error: YAML parse error on hetzner-failover/templates/configmap-ip.yaml: error converting YAML to JSON: yaml: line 4: mapping values are not allowed in this context
Is it not allowed to make a
key dynamic?
If not, how to drive around that?
Here is the repo I am talking of:
https://github.com/exocode/helm-charts/blob/master/hetzner-failover/templates/configmap-ip.yaml

The error seems to be, that the leading - got cut.
So the correct way is to remove that minus:
Before:
{{- .Values.failoverip1 | indent 2 -}}
After:
{{ .Values.failoverip1 | indent 2 -}}
The yaml is now:
apiVersion: v1
kind: ConfigMap
metadata:
name: vip-configmap
data:
{{ .Values.failoverip1 | indent 2 -}}: {{ .Release.Namespace -}}/{{- .Values.target -}} # add your config map here. must map the base64 encoded IP in secrets.yaml
And the rendered result is:
kubectl get configmap -o yaml
apiVersion: v1
items:
- apiVersion: v1
data:
0.0.0.0: default/nginx# add your config map here. must map the base64 encoded
IP in secrets.yaml
kind: ConfigMap

Related

Helm yaml command output for the default value of JAVA_OPTS_APPEND in bitnami/Keycloak

Could anybody please help me understand this command - What would be the output of this key -value pair:
JAVA_OPTS_APPEND: {{ printf "-Djgroups.dns.query=%s-headless.%s.svc.%s" (include "common.names.fullname" .) (include "common.names.namespace" .) .Values.clusterDomain | quote }}
where
common.names.fullname: ""
common.names.namespace: ""
clusterDomain: cluster.local
This piece of code is from here:
https://github.com/bitnami/charts/blob/main/bitnami/keycloak/templates/configmap-env-vars.yaml
I am fairly new to Kubernetes and I am trying to understand what would be the value of JAVA_OPTS_APPEND.
Thanks in advance.
Nafee
You can render helm templates locally with helm template command, this will render your values so you see the outputs of this command.
If you don't enough permissions on your Kubernetes cluster, you can spin a local mininkube or kind instance and then render the template:
helm repo add bitnami https://charts.bitnami.com/bitnami
helm template bitnami/keycloak --namespace mhajeb
In the rendered manifest you will find the following ConfigMap:
# Source: keycloak/templates/configmap-env-vars.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: release-name-keycloak-env-vars
namespace: "mhajeb"
labels:
app.kubernetes.io/name: keycloak
helm.sh/chart: keycloak-13.0.4
app.kubernetes.io/instance: release-name
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/component: keycloak
data:
KEYCLOAK_ADMIN: "user"
KEYCLOAK_HTTP_PORT: "8080"
KEYCLOAK_PROXY: "passthrough"
KEYCLOAK_ENABLE_STATISTICS: "false"
KEYCLOAK_DATABASE_HOST: "release-name-postgresql"
KEYCLOAK_DATABASE_PORT: "5432"
KEYCLOAK_DATABASE_NAME: "bitnami_keycloak"
KEYCLOAK_DATABASE_USER: "bn_keycloak"
KEYCLOAK_PRODUCTION: "false"
KEYCLOAK_ENABLE_HTTPS: "false"
KEYCLOAK_CACHE_TYPE: "ispn"
KEYCLOAK_CACHE_STACK: "kubernetes"
JAVA_OPTS_APPEND: "-Djgroups.dns.query=release-name-keycloak-headless.mhajeb.svc.cluster.local"
KEYCLOAK_LOG_OUTPUT: "default"
KC_LOG_LEVEL: "INFO"
Now note that JAVA_OPTS_APPEND: {{ printf "-Djgroups.dns.query=%s-headless.%s.svc.%s" (include "common.names.fullname" .) (include "common.names.namespace" .) .Values.clusterDomain | quote }} rendered:
JAVA_OPTS_APPEND: "-Djgroups.dns.query=release-name-keycloak-headless.mhajeb.svc.cluster.local"
And that was done with printf function which rendered common.names.fullname and common.names.namespacce from the templates helpers which are defined in the "parent" chart:
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "common.names.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
and
{{/*
Allow the release namespace to be overridden for multi-namespace deployments in combined charts.
*/}}
{{- define "common.names.namespace" -}}
{{- default .Release.Namespace .Values.namespaceOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
TLDR;
It is taking the Chart, Release names as defaults. And if you want to override them have a look at the documentation: https://github.com/bitnami/charts/tree/main/bitnami/keycloak#common-parameters, or the templates :), and just set:
fullnameOverride String to fully override common.names.fullname
namespaceOverride String to fully override common.names.namespace
Other examples
helm template my-food-release bitnami/keycloak --namespace mhajeb
Result:
# Source: keycloak/templates/configmap-env-vars.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: my-food-release-keycloak-env-vars
namespace: "mhajeb"
labels:
app.kubernetes.io/name: keycloak
helm.sh/chart: keycloak-13.0.4
app.kubernetes.io/instance: my-food-release
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/component: keycloak
data:
KEYCLOAK_DATABASE_HOST: "my-food-release-postgresql"
...
JAVA_OPTS_APPEND: "-Djgroups.dns.query=my-food-release-keycloak-headless.mhajeb.svc.cluster.local"
...
helm template my-food-release bitnami/keycloak --namespace mhajeb --set fullnameOverride=daNewName --set namespaceOverride=daNewNamespaceOverride
Result:
# Source: keycloak/templates/configmap-env-vars.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: daNewName-env-vars
namespace: "daNewNamespaceOverride"
labels:
app.kubernetes.io/name: keycloak
helm.sh/chart: keycloak-13.0.4
app.kubernetes.io/instance: my-food-release
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/component: keycloak
data:
...
JAVA_OPTS_APPEND: "-Djgroups.dns.query=daNewName-headless.daNewNamespaceOverride.svc.cluster.local"
...

How to set an SpringBoot array property as a kubernetes secret?

I want to use the direct translation from k8s secret-keys to SpringBoot properties.
Therefore I have a helm chart (but similar with plain k8s):
apiVersion: v1
data:
app.entry[0].name: {{.Values.firstEntry.name | b64enc }}
kind: Secret
metadata:
name: my-secret
type: Opaque
With that my intention is that this behaves as if I'd set the spring property file:
app.entry[0].name: "someName"
But when I do this I get an error:
Invalid value: "[app.entry[0].name]": a valid config key must consist of alphanumeric characters, '-', '_' or '.' (e.g. 'key.name', or 'KEY_NAME', or 'key-name', regex used for validation is '[-._a-zA-Z0-9]+'),
So, [0] seems not to be allowed as a key name for the secrets.
Any idea how I can inject an array entry into spring directly from a k8s secret name?
Shooting around wildly I tried these that all failed:
app.entry[0].name: ... -- k8s rejects '['
app.entry__0.name: ... -- k8s ok, but Spring does not recognize this as array (I think)
"app.entry[0].name": ... -- k8s rejects '['
'app.entry[0].name': ... -- k8s rejects '['
You should be able to use environnment variables like described in sprint-boot-env.
app.entry[0].name property will be set using APP_ENTRY_0_NAME environment variable. This could be set in your deployment.
Using secret like:
apiVersion: v1
data:
value: {{.Values.firstEntry.name | b64enc }}
kind: Secret
metadata:
name: my-secret
type: Opaque
and then use it with
env:
- name: APP_ENTRY_0_NAME
valueFrom:
secretKeyRef:
name: my-secret
key: value
What you can do is passing the application.properties file specified within a k8s Secret to your Spring Boot application.
For instance, define your k8s Opaque Secret this way:
apiVersion: v1
kind: Secret
type: Opaque
metadata:
name: my-secret
data:
application.properties: "app.entry[0].name={{ .Values.firstEntry.name }}"
Of course you will have more properties that you want to set in your application.properties file, so just see this as an example with the type of entry that you need to specify, as stated in your question. I'm not a Spring Boot specialist, but an idea could be (if possible) to tell the Spring Boot application to look for more than a single application.properties file so that you would only need to pass some of the configuration parameters from the outside in instead of all of the parameters.
When using kubernetes secrets as files in pods, as specified within the official kubernetes documentation, each key in the secret data map becomes a filename under a volume mountpath (See point 4).
Hence, you can just mount the application.properties file defined within your k8s secret into your container in which your Spring Boot application is running. Assuming that you make use of a deployment template in your helm chart, here is a sample deployment.yaml template would do the job (please focus on the part where the volumes and volumeMount are specified):
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "sample.fullname" . }}
labels:
{{- include "sample.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "sample.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "sample.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "sample.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 80
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumeMounts:
- name: my-awesome-volume
mountPath: /path/where/springboot/app/expects/application.properties
subPath: application.properties
volumes:
- name: my-awesome-volume
secret:
secretName: my-secret
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
As desired, this gives you a solution with no necessity of changing any of your application code. I hope that this gets you going in the intended way.
You can do saving json file as a secret
Step 1:
Create json file which needs to be stored as secret example : secret-data.json
{
"entry": [
{
"name1": "data1",
"key1": "dataX"
},
{
"name2": "data2",
"key2": "dataY"
}
]
}
Step2 : Create a secret from a file
kubectl create secret generic data-1 --from-file=secret-data.json
Step 3: Attach secret to pod
env:
- name: APP_DATA
valueFrom:
secretKeyRef:
name: data-1
key: secret-data.json
You can verify the same by exec into container and checking env

Helm iterate over string with comma separated values

I'd like to iterate over string which contain comma separated values using range. For example:
DNS_NAMES: example.com, example2.com, example3.com
and then iterate using range over values: example.com, example2.com, example3.com
use split
values.yaml
DNS_NAMES: example.com, example2.com, example3.com
templates/cm.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
name: test
data:
config.yaml: |-
args:
{{- range ( split ", " $.Values.DNS_NAMES ) }}
- {{ . }}
{{- end }}
output.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
name: test
data:
config.yaml: |-
args:
- example.com
- example2.com
- example3.com

How to avoid translate some `{{` of the helm chart?

I want to put the following CRD into helm chart, but it contains go raw template. How to make helm not translate {{ and }} inside rawTemplate. Thanks for your response.
https://github.com/kubeflow/katib/blob/master/examples/random-example.yaml
apiVersion: "kubeflow.org/v1alpha1"
kind: StudyJob
metadata:
namespace: katib
labels:
controller-tools.k8s.io: "1.0"
name: random-example
spec:
studyName: random-example
owner: crd
optimizationtype: maximize
objectivevaluename: Validation-accuracy
optimizationgoal: 0.99
requestcount: 4
metricsnames:
- accuracy
workerSpec:
goTemplate:
rawTemplate: |-
apiVersion: batch/v1
kind: Job
metadata:
name: {{.WorkerId}}
namespace: katib
spec:
template:
spec:
containers:
- name: {{.WorkerId}}
image: katib/mxnet-mnist-example
command:
- "python"
- "/mxnet/example/image-classification/train_mnist.py"
- "--batch-size=64"
{{- with .HyperParameters}}
{{- range .}}
- "{{.Name}}={{.Value}}"
{{- end}}
{{- end}}
restartPolicy: Never
In the Go template language, the expression
{{ "{{" }}
will expand to two open curly braces, for cases when you need to use Go template syntax to generate documents in Go template syntax; for example
{{ "{{" }}- if .Values.foo }}
- name: FOO
value: {{ "{{" }} .Values.foo }}
{{ "{{" }}- end }}
(In a Kubernetes Helm context where you're using this syntax to generate YAML, be extra careful with how whitespace is handled; consider using helm template to dump out what gets generated.)

Is it possible to use a template inside a template with go template

Using https://golang.org/pkg/text/template/, I sometimes need to use variables in the accessed path (for kubernetes deployments).
I end up writing something like :
{{ if (eq .Values.cluster "aws" }}{{ .Values.redis.aws.masterHost | quote }}{{else}}{{ .Values.redis.gcp.masterHost | quote }}{{end}}
What I'd really like to write is pretty much {{ .Values.redis.{{.Values.cluster}}.masterHost | quote }} , which doesn't compile.
Is there a way to write something similar ? (so having a kind of variable in the accessed path).
You can use _helpers.tpl file to define logic and operate with values.
_helpers.tpl
{{/*
Get redis host based on cluster.
*/}}
{{- define "chart.getRedis" -}}
{{- if eq .Values.cluster "aws" -}}
{{- .Values.redis.aws.masterHost | quote -}}
{{- else -}}
{{- .Values.redis.gcp.masterHost | quote -}}
{{- end -}}
{{- end -}}
values.yaml
cluster: local
redis:
aws:
masterHost: "my-aws-host"
gcp:
masterHost: "my-gcp-host"
And use it in your Deployment (here's a ConfigMap example to keep it shorter)
configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: Configmap
data:
redis: {{ template "chart.getRedis" . }}
Output:
helm install --dry-run --debug mychart
[debug] Created tunnel using local port: '64712'
...
COMPUTED VALUES:
cluster: local
redis:
aws:
masterHost: my-aws-host
gcp:
masterHost: my-gcp-host
HOOKS:
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: Configmap
data:
redis: "my-gcp-host"
Set cluster value to aws:
helm install --dry-run --debug mychart --set-string=cluster=aws
[debug] Created tunnel using local port: '64712'
...
COMPUTED VALUES:
cluster: local
redis:
aws:
masterHost: my-aws-host
gcp:
masterHost: my-gcp-host
HOOKS:
MANIFEST:
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: Configmap
data:
redis: "my-aws-host"

Resources