Helm: Pass a multiline env variable to deployment - deployment

I need to pass a private RSA key as ENV var to my deployment file, and I can't do it at the moment.
containers:
env:
- name: MY_PRIVATE_KEY
value: |+
{{ .Values.fpm.dot_env.MY_PRIVATE_KEY}}
I've tried with indent, without indent, using toYaml (there is no error with this but my env var start with |-)...
Any idea?
This is the error I get from that code:
Error: UPGRADE FAILED: YAML parse error on broker-api/templates/deployment.yaml: error converting YAML to JSON: yaml: line 59: could not find expected ':'

If you're trying to embed a multi-line string in a Kubernetes artifact in a Helm chart, the easiest recipe is
Use the YAML | block scalar form to preserve newlines;
Start the Go template {{ ... }} macro at the first column; and
Use the sprig indent function to indent every line of the block, including the first one.
(You frequently will see |- which trims the final newline; for this I can imagine wanting to keep the final newline |+ or just plain |; the difference between these last two is whether extra empty lines at the end are kept or not.)
containers:
env:
- name: MY_PRIVATE_KEY
value: |+
{{ .Values.fpm.dot_env.MY_PRIVATE_KEY | indent 12 }}
(Usually for actual secrets it's considered preferable to store them in Kubernetes Secret objects. Those values are base64 encoded in the Kubernetes API, so when you declare the Secret object in Helm you'd use ... | b64enc instead of this indent recipe.)

Finally I solved my problem b64encoding my key, and b64decoding it from my backend.
Thanks.

Related

How can I pass imagePullSecrets, as it is defined in the default template, to helm via set commands

When you run helm create mychart it has imagePullSecrets defined like this:
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }
In default values file it looks like it's passing it a blank array:
imagePullSecrets: []
I already have a bunch of charts built from this default template that have this setting. Previously I didn't need to use imagePullSecrets so I just left it as is, but now I have some cases where I want to set this at deploy time via the cli.
Helm supports arrays now but this doesn't seem to work:
--set "mychart.imagePullSecrets[0].name={reg-creds}"
Returns:
Error: UPGRADE FAILED: error validating "": error validating data: ValidationError(Deployment.spec.template.spec.imagePullSecrets[0].name): invalid type for io.k8s.api.core.v1.LocalObjectReference.name: got "array", expected "string"
Then I tried passing a string:
--set "mychart.imagePullSecrets='- name: reg-creds'"
Error: unable to build kubernetes objects from release manifest: error validating "": error validating data: ValidationError(Deployment.spec.template.spec.imagePullSecrets): invalid type for io.k8s.api.core.v1.PodSpec.imagePullSecrets: got "string", expected "array"
These error messages are infuriating. Is it possible to set this value with --set so I can avoid refactoring all my charts?
The helm install --set syntax is unique and complex. One unusual bit of syntax there is that a value {foo,bar} in curly braces sets the value to an array. In your example, then, --set object.path={value} sets the value to a single-element array; the error you see is that it needs to be a string instead.
That means a simple workaround here is to remove the curly braces on the right-hand side of --set. There is also a --set-string option that forces the value to be interpreted as a string, even if it contains curly braces or commas.
helm install ... --set "mychart.imagePullSecrets[0].name=reg-creds"
# no curly braces around the value ^^^^^^^^^
It might be clearer, and have a more standard syntax, to use a YAML file to provide this value instead.
# image-pull-secrets.yaml
imagePullSecrets:
- name: reg-creds
You can include this in a per-environment values file, or pass it as a standalone values file. In either case you'd use the helm install -f option to supply the file. It's fine to have multiple helm install -f values files.
helm install ... -f image-pull-secrets.yaml

Kubernetes: Cannot convert int64 to string. Kubernetes fails to interpret integer value in helmchart values.yaml file

I have a values.yaml file in which I have given spring_datasource_hikari_maximum_pool_size: "10"
In deployment yaml I have used this value as
- name: SPRING_DATASOURCE_HIKARI_MAXIMUM-POOL-SIZE
value: {{ .Values.spring_datasource_hikari_maximum_pool_size }}
However, when used inside the deployment.yaml file it fails with the below error.
Deploy failed: The request is invalid: patch: Invalid value: "map[metadata:map[annotations:map[kubectl.kubernetes.io/last-applied-configuration:{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":
{
(helm values etc)
`{"name":"SPRING_DATASOURCE_HIKARI_MAXIMUM-POOL-SIZE","value":10}]` **(this is the incorrect value)**
}
cannot convert int64 to string
What is the correct format of using an integer value from values.yaml file in a deployment.yaml file?
I have also tried multiple combinations with quotes "" but nothing seems to be working.
Any help is appreciated, Thanks in advance.
I was able to resolve this by using double quotes on the value itself in deployment.yaml file
- name: SPRING_DATASOURCE_HIKARI_MAXIMUM-POOL-SIZE
value: "{{ .Values.spring_datasource_hikari_maximum_pool_size }}"
Since this was a production instance I could not check with #David Maze and Vit's solution.
Edit:
Tried with quote option and it worked too.
- name: SPRING_DATASOURCE_HIKARI_MAXIMUMPOOLSIZE
value: {{ quote .Values.spring_datasource_hikari_maximum_pool_size }}
Check solutions from Helm Environment Variables with Booleans and Integers
We can use !!str to convert the output to a string, Alternatively we
can also use a undefined !! and get the same behaviour giving later
developers nice hints of what we intended !!booleanEnv or !!integerEnv
will cast the values to string (or even just !!boolean)
- name: SPRING_DATASOURCE_HIKARI_MAXIMUM-POOL-SIZE
value: !!integerEnv {{ .Values.spring_datasource_hikari_maximum_pool_size }}
- name: FAVORITE_DRINK
value: !!stringEnv {{ .Values.favoriteDrink }}
- name: TAKES_SUGAR
value: !!booleanEnv {{ .Values.takesSugar }}
YAML values have types, and the standard rule is to consider a string of digits like 10 to be a number. In the Kubernetes YAML format, though, the names and values of environment variables have to be strings.
The easiest way to do this is to use the Helm (Sprig) quote function, which will wrap its parameter in double quotes:
- name: SPRING_DATASOURCE_HIKARI_MAXIMUMPOOLSIZE
value: {{ quote .Values.spring_datasource_hikari_maximum_pool_size }}
{{/* ^^^^^ */}}
quote isn't especially intelligent; it's the same as value: "{{ .Values...}}". There's a similar squote that would wrap the value in single quotes.
If you wanted a really robust solution, you could use print to convert an arbitrary value to a string, then the lightly-documented toJson function to convert that to JSON. By design, valid JSON is valid YAML, and "converting a string to JSON" will mean double-quoting it and otherwise escaping it as needed.
value: {{ .Values...pool_size | print | toJson }}

Syntax Error in Yaml File - missed comma between flow collection entries

We have an application deployed in AKS , the kubernetes version we were using is 1.15 now we want to upgrade the Kubernetes to 1.16, I notice that some of the APIs have been deprecated in 1.16.We have deployment.yaml file in which I had to change the from
apiVersion: extensions/v1beta1 to apiVersion: apps/v1 for Deployment.
after doing this change I see that the deployment YAML is failing lint test for another entry :
- name : APP_HOST
{{- range $host := .Values.ingress.hosts }}
value: {{ $host }}
{{- end }}
Error :
npx yaml-lint yamllint deployment.yaml
npx: installed 45 in 14.04s
× YAML Lint failed for deployment.yaml
× missed comma between flow collection entries at line 88, column 11:
{{- range $host := .Values.ingress ...
Can some one help me with the syntax needed for this. Mind you it was working fine before. Not sure if I have added and extra space or corrupted the file.
Thanks
The syntax you have looks like a correct Helm template, including correct whitespace controls. However, it is not valid YAML; the template {{ ... }} syntax looks at least a little bit like an inline mapping { key: value }, and this confuses the linter.
You can't run the un-rendered Helm template files through yamllint or another plain YAML validator. You can run helm template to render the template to plain text, and then run yamllint on that. The current version of Helm will try to parse the produced YAML as it generates it, so just running helm template will give you some protection against whitespace errors.

How can we specify custom path to .Files.Get when creating ConfigMap with Helm

I am creating a config map as below
kubectl create configmap testconfigmap --from-file=testkey=/var/opt/testfile.txt
As I am using helm charts, I would like to create the config map using YAML file instead of running kubectl.
I went through Kubernetes - How to define ConfigMap built using a file in a yaml? and we can use .Files.Get to access the files.
But then testfile.txt needs to be a part of helm. I would like to have something like
kind: ConfigMap
metadata:
name: testconfigmap
data:
fromfile: |-
{{ .Files.Get "/var/opt/testfile.txt" | indent 4 }}
It works when "testfile.txt" is under the main helm directory. So, {{ .Files.Get "testfile.txt" | indent 4 }} works but {{ .Files.Get "/var/opt/testfile.txt" | indent 4 }} doesn't. With custom path, the value for the ConfigMap is empty.
Is is possible to place the file at a custom path outside the helm folder, so I can define my path in Values.yaml and read it in my ConfigMap yaml ?
This is a Community Wiki answer so feel free to edit it and add any additional details you consider important.
As mdaniel has already stated in his comment:
Is is possible to place the file at a custom path outside the helm
folder no, because helm considers that a security risk – mdaniel 2
days ago
You can also compare it with this feature request on GitHub where you can find very similar requirement described in short e.g. in this comment:
I have this exact need. My chart publishes a secret read from file at
/keybase. This file is deliberately not in the chart.
I believe files for .Files.Get should not be assumed to be inside the
chart ...
One interesting comment:
lenalebt commented on Dec 23, 2017 I am quite sure .Files.Get not
being able to access the file system arbitrarily is a security
feature, so I don't think the current behaviour is wrong - it just
does not fulfill all use cases.
This issue was created quite long time ago (Dec 19, 2017) but has been recently reopened. There are even some specific proposals on how it could be handled:
titou10titou10 commented on Apr 2 #misberner can you confirm that
using--include-dir =will allow us to use
.Files.Glob().AsConfig(), and so create a ConfigMap with one
entry in the CM per file in?
#misberner misberner commented on Apr 2 Yeah that's the idea. An open
question from my point of view is whether an --include-dir with a
specified introduces an overlay, or shadows everything under
/ from previous args and from the bundle itself. I'm not super
opinionated on that one but would prefer the former.
The most recent comments give some hope that this feature might become available in future releases of helm.
As mdaniel and mario already mentioned, for now this is not possible, as it's considered a security risk.
But actually there is a workaround.
You can use Helm templating to parse your property file and load it into a ConfigMap.
# create the following ConfigMap in your Chart
# this is just a simple prototype
# it requires strict key=value syntax in your property file (no empty strings etc.)
# but it shows the idea - improve the syntax, if needed
apiVersion: v1
kind: ConfigMap
metadata:
name: example
data:
{{- if .Values.example.map }}
{{- range $line := splitList "\n" .Values.example.map }}
{{- $words := splitList "=" $line }}
{{- $key := index $words 0 | trim }}
{{- $value := rest $words | join "=" | trim }}
{{ $key }}: "{{ $value }}"
{{- end }}
{{- end }}
{{- end }}
And after that you may load your properties file into this ConfigMap.
helm install mychart --set-file example.map="/test/my.properties"
Of course it is safe to use ONLY if you fully control the input, i. e. how each and every line of your property file is populated.

overriding values in kubernetes helm subcharts

I'm building a helm chart for my application, and I'm using stable/nginx-ingress as a subchart. I have a single overrides.yml file that contains (among other overrides):
nginx-ingress:
controller:
annotations:
external-dns.alpha.kubernetes.io/hostname: "*.{{ .Release.Name }}.mydomain.com"
So, I'm trying to use the release name in the overrides file, and my command looks something like: helm install mychart --values overrides.yml, but the resulting annotation does not do the variable interpolation, and instead results in something like
Annotations: external-dns.alpha.kubernetes.io/hostname=*.{{ .Release.Name }}.mydomain.com
I installed the subchart by using helm fetch, and I'm under the (misguided?) impression that it would be best to leave the fetched thing as-is, and override values in it - however, if variable interpolation isn't available with that method, I will have to put my values in the subchart's values.yaml.
Is there a best practice for this? Is it ok to put my own values in the fetched subchart's values.yaml? If I someday helm fetch this subchart again, I'll have to put those values back in by hand, instead of leaving them in an untouched overrides file...
Thanks in advance for any feedback!
I found the issue on github -- it is not supported yet:
https://github.com/kubernetes/helm/issues/2133
Helm 3.x (Q4 2019) now includes more about this, but for chart only, not for subchart (see TBBle's comment)
Milan Masek adds as a comment:
Thankfully, latest Helm manual says how to achieve this.
The trick is:
enclosing variable in " or in a yaml block |-, and
then referencing it in a template as {{ tpl .Values.variable . }}
This seems to make Helm happy.
Example:
$ cat Chart.yaml | grep appVersion
appVersion: 0.0.1-SNAPSHOT-d2e2f42
$ cat platform/shared/t/values.yaml | grep -A2 image:
image:
tag: |-
{{ .Chart.AppVersion }}
$ cat templates/deployment.yaml | grep image:
image: "{{ .Values.image.repository }}:{{ tpl .Values.image.tag . }}"
$ helm template . --values platform/shared/t/values.betradar.yaml | grep image
image: "docker-registry.default.svc:5000/namespace/service:0.0.1-SNAPSHOT-d2e2f42"
imagePullPolicy: Always
image: busybox
Otherwise there is an error thrown..
$ cat platform/shared/t/values.yaml | grep -A1 image:
image:
tag: {{ .Chart.AppVersion }}
1 $ helm template . --values platform/shared/t/values.yaml | grep image
Error: failed to parse platform/shared/t/values.yaml: error converting YAML to JSON: yaml: invalid map key: map[interface {}]interface {}{".Chart.AppVersion":interface {}(nil)}
For Helm subchart, TBBle adds to issue 2133
#MilanMasek 's solution won't work in general for subcharts, because the context . passed into tpl will have the subchart's values, not the parent chart's values.
!<
It happens to work in the specific example this ticket was opened for, because .Release.Name should be the same in all the subcharts.
It won't work for .Chart.AppVersion as in the tpl example.
There was a proposal to support tval in #3252 for interpolating templates in values files, but that was dropped in favour of a lua-based Hook system which has been proposed for Helm v3: #2492 (comment)
That last issue 2492 include workarounds like this one:
You can put a placeholder in the text that you want to template and then replace that placeholder with the template that you would like to use in yaml files in the template.
For now, what I've done in the CI job is run helm template on the values.yaml file.
It works pretty well atm.
cp values.yaml templates/
helm template $CI_BUILD_REF_NAME ./ | sed -ne '/^# Source:
templates\/values.yaml/,/^---/p' > values.yaml
rm templates/values.yaml
helm upgrade --install ...
This breaks if you have multiple -f values.yml files, but I'm thinking of writing a small helm wrapper that runs essentially runs that bash script for each values.yaml file.
fsniper illustrates again the issue:
There is a use case where you would need to pass deployment name to dependency charts where you have no control.
For example I am trying to set podAffinity for zookeeper. And I have an application helm chart which sets zookeeper as a dependency.
In this case, I am passing pod antiaffinity to zookeeper via values. So in my apps values.yaml file I have a zookeeper.affinity section.
If I had the ability to get the release name inside the values yaml I would just set this as default and be done with it.
But now for every deployment I have to override this value, which is a big problem.
Update Oct. 2022, from issue 2133:
lazychanger proposes
I submitted a plugin to override values.yaml with additional templates.
See lazychanger/helm-viv: "Helm-variable-in-values" and its example.