Choosing between alternative dependencies in helm - kubernetes

I have a helm chart for an application that needs some kind of database.
Both mysql or postgresql would be fine.
I would like to give the chart user the option to install one of these as a dependency like this:
dependencies:
- name: mysql
version: 0.10.2
repository: https://kubernetes-charts.storage.googleapis.com/
condition: mysql.enabled
- name: postgresql
version: 3.11.5
repository: https://kubernetes-charts.storage.googleapis.com/
condition: postgresql.enabled
However this makes it possible to enable both of them.
Is there an easy way to make sure only one is selected?
I was thinking of a single variable selecting one of [mysql, postgres, manual] and depend on a specific database if it is selected. - Is there a way to do so?

I don't think there's a straightforward way to do this. In particular, it looks like the requirements.yaml condition: field only takes a boolean value (or a list of them) and not an arbitrary expression. From the Helm documentation:
The condition field holds one or more YAML paths (delimited by commas). If this path exists in the top parent’s values and resolves to a boolean value, the chart will be enabled or disabled based on that boolean value. Only the first valid path found in the list is evaluated and if no paths exist then the condition has no effect.
(The tags mechanism described below that is extremely similar and doesn't really help.)
When it comes down to actually writing your Deployment spec you have a more normal conditional system and can test that only one of the values is set; so I don't think you can prevent having redundant databases installed, but you'll at least only use one of them. You could also put an after-the-fact warning to this effect in your NOTES.txt file.
{{ if and .Values.mysql.enabled .Values.postgresql.enabled -}}
WARNING: you have multiple databases enabled in your Helm values file.
Both MySQL and PostgreSQL are installed as part of this chart, but only
PostgreSQL is being used. You can update this chart installation setting
`--set mysql.enabled=false` to remove the redundant database.
{{ end -}}

Related

Helm globally controlling values of dependencies from requirements

We have a set of Micro-Services (MS-a, MS-b, MS-c..) each has its own dependencies specified in requirements.yaml. Some have the same requirements, e.g mongodb.
For deployment of the overall application we created an umbrella-chart that references all MSs as its dependencies in requirements.yaml.
We provide a single values.yaml file to the umbrella-chart. That contains all the values for all the MS charts. That works fine till it comes to providing values to all the dependency charts of MS charts.
One prominent example would be mongodb.clusterDomain
In values.yaml file the clusterDomain value would have to be repeated for each MS section:
MS-a:
mongodb:
clusterDomain: cluster.local
MS-b:
mongodb:
clusterDomain: cluster.local
that screams for trouble, when it comes to maintainability. Is there a way to move those values to some global section, so that it is only specified once ? e.g:
global:
mongodb:
clusterDomain: cluster.local
I have tried to use anchors https://helm.sh/docs/chart_template_guide/yaml_techniques/#yaml-anchors
it would look like that:
global:
mongodb:
clusterDomain: &clusterDomain cluster.local
MS-a:
mongodb:
clusterDomain: *clusterDomain
MS-b:
mongodb:
clusterDomain: *clusterDomain
It does not reduce the structure complexity, but it makes it easier to maintain, because the value needs to be set in one place only.
however, it seems to have a very nasty pitfall when it comes to overriding values via --set.
According to the link above:
While there are a few cases where anchors are useful, there is one aspect of them that can cause subtle bugs: The first time the YAML is consumed, the reference is expanded and then discarded.
It practically means it will not be possible to override the clusterDomain values by providing:
--set global.mongodb.clusterDomain=cluster.local
because it will only replace the global section, while all the other places will remain with their original value.
Is there any other way(s) to make it possible to set subcharts values globally in one place?

Kubernetes + Helm - only restart pods if new version/change

Whenever I run my basic deploy command, everything is redeployed in my environment. Is there any way to tell Helm to only apply things if there were changes made or is this just the way it works?
I'm running:
helm upgrade --atomic MyInstall . -f CustomEnvironmentData.yaml
I didn't see anything in the Helm Upgrade documentation that seemed to indicate this capability.
I don't want to bounce my whole evironment unless I have to.
There's no way to tell Helm to do this, but also no need. If you submit an object to the Kubernetes API server that exactly matches something that's already there, generally nothing will happen.
For example, say you have a Deployment object that specifies image: my/image:{{ .Values.tag }} and replicas: 3. You submit this once with tag: 20200904.01. Now you run the helm upgrade command you show, with that tag value unchanged in the CustomEnvironmentData.yaml file. This will in fact trigger the deployment controller inside Kubernetes. That sees that it wants 3 pods to exist with the image my/image:20200904.01. Those 3 pods already exist, so it does nothing.
(This is essentially the same as the "don't use the latest tag" advice: if you try to set image: my/image:latest, and redeploy your Deployment with this tag, since the Deployment spec is unchanged Kubernetes won't do anything, even if the version of the image in the registry has changed.)
You should probably use helm diff upgrade
https://github.com/databus23/helm-diff
$ helm diff upgrade - h
Show a diff explaining what a helm upgrade would change.
This fetches the currently deployed version of a release
and compares it to a chart plus values.
This can be used visualize what changes a helm upgrade will
perform.
Usage:
diff upgrade[flags] [RELEASE] [CHART]
Examples:
helm diff upgrade my-release stable / postgresql--values values.yaml
Flags:
-h, --help help for upgrade
--detailed - exitcode return a non - zero exit code when there are changes
--post - renderer string the path to an executable to be used for post rendering. If it exists in $PATH, the binary will be used, otherwise it will try to look for the executable at the given path
--reset - values reset the values to the ones built into the chart and merge in any new values
--reuse - values reuse the last release's values and merge in any new values
--set stringArray set values on the command line(can specify multiple or separate values with commas: key1 = val1, key2 = val2)
--suppress stringArray allows suppression of the values listed in the diff output
- q, --suppress - secrets suppress secrets in the output
- f, --values valueFiles specify values in a YAML file(can specify multiple)(default[])
--version string specify the exact chart version to use.If this is not specified, the latest version is used
Global Flags:
--no - color remove colors from the output

Reference previously declared value in HELM values.yaml

I have a HELM Chart with a few requirements (i.e. subcharts).
When deploying that chart, I use a values.yaml containing all the values for both the main chart and the subcharts :
globalFoo: "bar"
subchart1:
foo: subchart1-{{ globalFoo }}
subchart2:
localFoo: "bar2"
foo: subchart2-{{ subchart2.localFoo }}
I'd like to achieve two things :
Reference a previously declared global variable (i.e. in the global chart scope) in a subchart value (subchart1.foo in my example)
Reference a previously declared local variable in the same subchart scope (subchart2.foo in my example)
The exemple above doesn't work. I tried several syntaxes and none of them worked. I didn't find anything like that in the HELM documentation.
Is it doable ?
Reference a previously declared global variable in a subchart value, Reference a previously declared local variable in the same subchart scope
This can be achieved to some extent using anchors and aliases.
global:
foo: &global-foo bar
subchart1:
# this verbatim copies the content of the anchor
foo: *global-foo
local: &subchart1-local bar
subchart2:
foo: *subchart1-local
Values can naturally be combined in a helm template:
kind: ConfigMap
...
data:
FOO: "subchart2-{{ .Values.subchart2.foo }}"
If find yourself needing "templated values" the tpl function may be helpful:
# values
global:
user: foo
pass: bar
dbhost: dbserver.com
mychart:
connection: "db://{{.d.user}}:{{d.pass}}/{{d.dbhost}}"
# template
kind: ConfigMap
...
data:
DBURL: "{{ tpl .Values.mychart.connection (dict "d" .Values.global "Template" $.Template }}"
Note the (dict ...) syntax is derived from a hint in this helm github comment. This helps to shorten the template string by giving the ".d" context instead ".Values", i.e. .d.user is short for .Values.global.user
Currently (as of Helm version 3) this is not supported.
A similar issue is discussed in Proposal: Allow templating in values.yaml
And is rejected for several reasons. One of them stated by the creator of Helm
The bigger constraint is that the values.yaml file MUST always be a valid YAML file.
And also in Support for using {{ values }} within values.yaml
the file that is passed into the template engine as a source of data for templates is itself not passed through the template engine. We almost definitely will not do that because it gets very confusing for users. It also breaks both standard YAML compatibility and backward compatibility with all existing Helm versions.
And last but not least here
The tl;dr
At its most simple, the reason behind not wanting to do this is you don’t template the file that provides the values that you template with. Furthermore, this feature will be made obsolete by Helm 3
...
Obsolete by Helm 3
In Helm 3 as part of the eventing/hook system, we will allow Lua-based modification of values in a much easier way than having to template them out. Seeing as we are well underway with development for Helm 3, adding a feature with all of the drawbacks mentioned above would cause more churn than the value added. So we aren’t rejecting this outright, but are implementing it (albeit in a different way) for Helm 3
But the mentioned support via Lua has been closed with this comment
We should close this as Lua support is not coming anytime soon and we have other (arguably better) ideas on handling this (like WASM). This was in the plan but isn't any longer.

Kubernetes Helm chart requirements.yaml file dependencies false condition

I am using the Kubernetes Helm requirements.yaml file for dependencies addition. Based on the values.yaml condition, it will create the dependencies pods.
Here I want to execute required dependencies when apache.enabled == false
values.yaml
external_apache:
enabled: false
File requirements.yaml
dependencies:
- name:
version:
repository:
condition: external_apache.enabled
How do I add a false condition?
I have tried the below condition, but it's not working:
condition: external_apache.enabled == false
What version of Helm are you using?
There was a similar issue in the Kubernetes repository on GitHub:
Unable to use condition in 'requirements.yaml' #2111
The solution was to upgrade Helm to v2.2.0+. In that version, condition support was added.
Helm 2 to Helm 3 upgrade note:
Chart apiVersion bumped to "v2" for following specification changes:
Dynamically linked chart dependencies moved to Chart.yaml (requirements.yaml removed and requirements --> dependencies)
Library charts (helper/common charts) can now be added as dynamically linked chart dependencies
Charts have a type metadata field to define the chart to be of an application or library chart. It is application by default which means it is renderable and installable
Helm 2 charts (apiVersion=v1) are still installable
In the Helm documentation or repository, there is an explanation of how the condition works:
(I've added some comments to make reading easier)
Condition - The condition field holds one or more YAML paths (delimited by commas).
Tags - The tags field is a YAML list of labels to associate with this chart.
# parentchart/requirements.yaml
dependencies:
- name: subchart1
repository: http://localhost:10191
version: 0.1.0
condition: subchart1.enabled, global.subchart1.enabled
tags:
- front-end #(chart should be disabled because the tags.front-end is “false” in values.yaml file , but ...)
- subchart1 #(subchart1.enabled condition path is present in values.yaml file and it has "true" value...)
#(this condition, so it overrides tag front-end and this chart will be enabled)
- name: subchart2
repository: http://localhost:10191
version: 0.1.0
condition: subchart2.enabled,global.subchart2.enabled
#(as soon as no one from these paths is exists in values.yaml this condition has ho effect)
tags:
- back-end #(chart should be enabled because the tags.back-end is “true” in values.yaml file)
- subchart2 #(and there is no condition path found in values.yaml to override it)
If this condition path exists in the top parent’s values and resolves to a boolean value, the chart will be enabled or disabled based on that boolean value.
Only the first valid path found in the list is evaluated and if no paths exist then the condition has no effect.
In the top parent’s values, all charts with tags can be enabled or disabled by specifying the tag and a boolean value.
# parentchart/values.yaml
subchart1:
enabled: true #(this could be found from requirements as subchart1.enabled and override tags in this case)
tags:
front-end: false #(this disables charts with tag front-end)
back-end: true #(this enables charts with tag back-end)
The logic and sequence of conditions and tags are described in Tags and Condition Resolution:
Conditions (when set in values) always override tags. The first condition path that exists wins and subsequent ones for that chart are ignored.
Tags are evaluated as ‘if any of the chart’s tags are true then enable the chart’.
Tags and conditions values must be set in the top parent’s values.
The tags: key in values must be a top level key. Globals and nested tags: tables are not currently supported.
You can also set tags and conditions in the command line:
helm install --set tags.front-end=true --set subchart2.enabled=false
Helm version v2.2.2 worked, while v2.10.0 didn't.
Based on the documentation and the answer from #VAS, the answer to your question is it's not possible to use a negation of a condition in requirements.yaml.
Though its a bit late, but users may find it helpful. I am not running helm install for the parent chart from command line, but, I have a shell script for running it and another shell script for environment properties.
I have set the conditional boolean values to true or false in environment script and using the values in other script which runs helm for parent helm chart, I have set individual child chart enabled property in parent's values.yaml.
Also, in version 3.0.0+, the configuration done earlier in
requirements.yaml are now done in parent's chart.yaml itself.

What is the best way of creating Helm Charts in a single repository for different deployment environments?

We are using Helm Charts for deploying a service in several environments on Kubernetes cluster. Now for each environment there are a list of variables like the database url, docker image tag etc. What is the most obvious and correct way of defining Helm related values.yaml in such case where all the Helm template files remain same for all the environment except for some parameters as stated above.
One way to do this would be using multiple value files, which helm now allows. Assume you have the following values files:
values1.yaml:
image:
repository: myimage
tag: 1.3
values2.yaml
image:
pullPolicy: Always
These can both be used on command line with helm as:
$ helm install -f values1.yaml,values2.yaml <mychart>
In this case, these values will be merged into
image:
repository: myimage
tag: 1.3
pullPolicy: Always
You can see the values that will be used by giving the "--dry-run --debug" options to the "helm install" command.
Order is important. If the same value appears in both files, the values from values2.yaml will take precedent, as it was specified last. Each chart also comes with a values file. Those values will be used for anything not specified in your own values file, as if it were first in the list of values files you provided.
In your case, you could specify all the common settings in values1.yaml and override them as necessary with values2.yaml.