I am using a helm chart (with sub-charts) to deploy my application.
I am using a value file for setting values.
I am looking a way to set variables in my value file (or any other place) that will be valid for my value file.
I have some sections (services) in my value files that I need to use the same value in it
so I am looking for a variable in my value file.
Is there any way that I can use variables for my value file?
Thx
Helm on its own can't do this.
If you control all of the charts and subcharts, you can allow specific values to have embedded Go templating. Helm includes a tpl extension function that will let you render an arbitrary string as a template. So if you have values
global:
commonKey: some value
otherKey: '{{ .Values.global.commonKey }}'
then you can render
- name: OTHER_KEY
value: '{{ tpl .Values.otherKey . }}'
But, you have to use tpl every place you access the key value(s); if you don't control the subcharts you may not be able to do this.
Higher-level tools may also let you do this. I'm familiar in particular with Helmfile which lets you declare multiple Helm charts and their settings, but also lets you use almost-Helm templating in many places. So your helmfile.yaml could specify:
environments:
default:
# These values are available when rendering templates in this file
values:
- commonKey: some value
releases:
- name: my-service
namespace: my-service
chart: ./charts/my-service
values:
# List items can be file names or YAML dictionaries.
# If it's a dictionary, arbitrary nested values.yaml content.
# If it's a *.yaml.gotmpl file name, templating is applied to the file.
- otherKey: '{{ .Values.commonKey }}'
yetAnotherKey: '{{ .Values.commonKey }}'
- ./my-service.yaml.gotmpl
Helmsman is simpler, but can only set chart values from environment variables; but I believe you can reference the same environment variable in different setString: options. You could also do something similar with the Terraform Helm provider, using Terraform's native expression syntax, particularly if you're already familiar with Terraform.
Related
I'm new to helm charts and K8s, so forgive me. I'm working on a project that deploys an application project with several apps as part of it. The previous dev that put the charts together was using a "find-and-replace" technique to fill in values for things like the image repository, tags, etc. This is making our CICD pipeline development tricky and not scalable. I'm trying to update the charts to use variables and values.yml files. Most of it seems to be working, values are getting passed down to the templates except for one part and I can't figure out why. Its a large project so I won't copy all the chart files. I'll try to lay out the important parts:
Folder structure:
helm
project1
dev
charts
app1
templates
*template files
Chart.yaml
values.yaml
app2
*same subfolders
app3
*same subfolders
Chart.yml
values.yml
Base Values.yml
artifactory_base_url: company.repo.io/repo_folder
imageversions:
app1_tag: 6.1.2-alpine-edge
app2_tag: 8.1.0.0-edge
app3_tag: 8.1.0.0-alpine-edge
app4_tag: 10.1.1-alpine-edge
initcontainer: latest
App Values.yml file
app:
image:
repository: "{{ .Values.artifactory_base_url }}/pingaccess"
tag: "{{ .Values.pa_tag }}"
deployment.yml template file
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.app.image }}"
I'm running the following helm template command to confirm that I'm getting the proper output for at least the app1 part before actually trying to deploy to the k8s cluster.
helm template app1 --set date="$EPOCHSECONDS" --set namespace='porject_namespace' --values helm/project1/dev/values.yaml helm/project1/dev/charts/app1
Most of the resulting yaml looks great, and it looks like the values I have defined in the base values.yml file are getting passed through in other areas like this example:
initContainers:
- name: appinitcontainer
image: "company.repo.io/repo_folder/initcontainer:latest"
But there is one portion that is populated from the deployment.yml template file that is still showing the curly braces for variables
containers:
- name: app1
image: "map[repository:{{ .Values.image_repo_base_url }}/app1 tag:{{ .Values.app1_tag }}]"
imagePullPolicy: Always
I've tried making variations in all the files mentioned above to remove quotes, use single quotes, etc. In those attempts I usually get a variation of the following errors:
"error converting yaml to json. did not find expected key"
"error mapping values"
I haven't been able to find a solution. I'm assuming that the "helm template" command should not contain any braces like that, all variables and values should be resolved. I'm hoping somebody can provide some tips of things I might be missing.
You're hitting two issues here. First, .Values.app.image is a map containing the two keys repository and tag; that's why you get the weird map[repository:... tag:...] syntax in the output. Second, string values in values.yaml aren't reinterpreted for Helm template syntax; that's why the {{ ... }} markup gets passed through to the output.
This in turn means you need to do two things. To resolve the map, construct the string from the contents of the dictionary; and to resolve the templating markup inside the string values, use Helm's tpl function.
{{- $repository := tpl .Values.app.image.repository . }}
{{- $tag := tpl .Values.app.image.tag . }}
image: "{{ $repository }}:{{ $tag }}"
(You may find it useful to separate "repository", "registry" or "image", and "tag" into three separate parts, since probably all of your images are coming from the same repository; that would let you configure the repository in one place and customize the image name per component. The bitnami/postgresql chart is one example of this setup.)
We are using helm charts templates for deployment to kube and Azure devops for CI/CD.in my values.yaml data in below section will change as per environment and saved as config map in pod.
My question is how can I update it during deployment in azure pipeline. We are using Helm upgrade task OR any other way to handle it better.
environment:
enabled: true
env:
enabled: false
internalConfigMap:
enabled: true
**data:
AZ_DIRECTORY: xxx
MODEL_ID_SVM: xxx
MODEL_ID_MULTI: xxx
MODEL_THRESHOLD_SVM: 'xx'
SINGLE_ACC_ENDPT: 'xx'
MODEL_WT_SVM: 'xx'**
here is deployment task:(ignore indentation)
task: HelmDeploy#0
displayName: Helm upgrade
inputs:
command: upgrade
chartType: Name
chartName: chart/$(chartname)
releaseName: $(chartname)-${{ parameters.CI_ENVIRONMENT_SLUG }}
namespace: $(NAMESPACE)
connectionType: Azure Resource Manager
#azureSubscriptionEndpoint: ${{ variables.AZ_SUBSCRIPTION }}
#azureResourceGroup: $(AKS_RESOURCE_GROUP)
# kubernetesCluster: $(K8S_CLUSTER)
install: true
waitForExecution: true
useClusterAdmin: true
overrideValues: |
template.image.tag=$(imagetag)
Option 1: One value file per environment
If you have one values.yaml per environment (environment1-values.yaml, environment2-values.yaml etc) you can refer to different files for each stage in your pipeline.
The Helm Upgrade command accepts the parameter valueFile which you can use to point to the correct values.yaml for the environment you are deploying to
(Optional) Specify values in a YAML file or a URL. For example,
specifying myvalues.yaml will result in helm install
--values=myvals.yaml
Option 2: Override values on deployment
The Helm Upgrade command accepts the parameter overrideValues by which you can pass values directly to helm:
(Optional) Set values on the command line. You can specify multiple
values by separating values with commas. For example,
key1=val1,key2=val2. You can also specify multiple values by
delimiting them with newline as so: key1=val1 key2=val2 Please note
that if you have a value which itself contains newlines, use the
valueFile option, else the task will treat the newline as a delimiter.
The task will construct the helm command by using these set values.
For example, helm install --set key1=val1 ./redis
In your case this would mean
overrideValues: template.image.tag=$(imagetag),environment.internalConfigMap.data.AZ_DIRECTORY=xxx,environment.internalConfigMap.data.MODEL_ID_SVM=xxx
Given a yaml file (helmfile) like the following
releases:
- chart: ../charts/foo
name: foo
namespace: '{{ .Values.stack }}'
values:
- ../config/templates/foo-values.yaml.gotmpl
set:
- name: image.tag
value: 22
- name: replicas
value: 1
- chart: ../charts/bar
name: bar
namespace: '{{ .Values.stack }}'
values:
- ../config/templates/bar-values.yaml.gotmpl
set:
- name: image.bar_proxy.tag
value: 46
- name: image.bar.tag
value: 29
- name: replicas
value: 1
I'm trying to figure out a clean way to update a specific image tag. For example, I'd like to update image.bar_proxy.tag from 46 to 51.
I have the following, which does the job, but it requires that you know the exact index of the array item:
yq -y '.releases[] |= if .name=="bar" then .set[0].value |= 51 else . end' helmfile-example.yaml
So if the array order were to change at some point this would break.
A preferred solution would be: "update image.bar_proxy.tag value from 46 to 51 where set[].name==image.bar_proxy.tag". Any ideas on how to achieve a more specific conditional selection like this?
FYI our yq version:
$ yq --version
yq 2.10.0
You can use the following filter to make it work. It works by dynamically selecting the index of the object where your tag exists. On the selected object .value=51 will update the value as you wanted. You can also use the -i flag to do in-place modification of the original file.
yq -y '.releases[].set |= map(select(.name == "image.bar_proxy.tag").value=51)' yaml
See the underlying jq filter acting on the JSON object at jq-playground
Given the context of using Helmfile, there are a couple of ways you can approach this without necessarily editing the helmfile.yaml. Helmfile allows using the Go text/template language in many places, similarly to the underlying Helm tool, and has some other features that can help.
One of the easiest things you can do is take advantage of values: being a list, and of unknown values generally being ignored. You (or your CI/CD system) can write a separate YAML file that contains just the tags (JSON may be easier to write and is valid YAML)
# tags.yaml
image:
tag: 22
bar: {tag: 29}
bar_proxy: {tag: 46}
and then include this file as an additional file in the helmfile.yaml. (This would be equivalent to using helm install -f with multiple local values files, rather than helm install --set individual values.)
releases:
- name: foo
values:
- ../config/templates/foo-values.yaml.gotmpl
- tags.yaml
# no `set:`
- name: bar
values:
- ../config/templates/bar-values.yaml.gotmpl
- tags.yaml
- replicas: 1
# no `set:`
Helmfile's templating extensions also include env and requiredEnv to read ordinary environment variables from the host system. Helm proper does not have these to try to minimize the number of implicit inputs to a chart, but for Helmfile it's a possible way to provide values at deploy time.
releases:
- name: bar
set:
- name: image.bar_proxy.tag
value: {{ env "BAR_PROXY_TAG" | default "46" }}
- name: image.bar.tag
value: {{ requiredEnv "BAR_TAG" }}
How do you override values in a Helm list with --set param in Azure DevOps?
Simple use case in values.yaml:
environment:
- name: foo
value: override_me
- name: bar
value: override_me
- name: baz
value: override_me
In the deployment.yaml file I use it like so:
env:
{{ toYaml .Values.environment | indent 10}}
One thing that kind of works, but not really, is:
environment[0].name=foo,environment[0].value=hello,{...}
The problem with this override is that it will override the entire list, even if I only want to replace value [0], not [1] and [2].
Also I get parsing errors when I pass url:s or int's (not on localhost, only AZ DevOps) - to overcome that paring error, you can escape it with \" - but then the chart is messed up - even though it passes the validation.
So, is it possible to override the env list in my case in Azure DevOps helm deployment? Or do I need to restructure the list to individual key=value pairs?
I've got weird experience when doing this, in 2 similar cases in one case it replaces them, in one overrides the whole array. so in the second case what I had to do is this:
environment:
- name: v1
value: keep_me
- name: v2
value: keep_me
- name: v3
value: keep_me
- name: foo
value: override_me
- name: bar
value: override_me
and I was doing this in the Azure Devops:
--set environment[3].name=foo,environment[03.value=xxx
for the other one I didnt have to do that, it would gladly overwrite only the values I've input. no idea why it did that.
Get yourself some variables defined in the task:
Use a standard set:
I was using a bash task in a release pipeline pointed at a deploy.sh file which existed as a published artifact. You need to chmod +x the file for this to work properly.
I'm attempting to create multiple pipelines in Azure DevOps but I would like to reuse the same pipeline YAML file with the differences per environment being loaded from a separate template variables file.
For that purpose I've created two variable files, which are located in the same folder as the pipeline definition:
# vars.dev.yml
variables:
- name: EnvironmentName
value: Development
# vars.prd.yml
variables:
- name: EnvironmentName
value: Production
And the definition of the pipeline is the following:
trigger: none
pr: none
variables:
- name: EnvironmentCode
value: dev
- name: EnvironmentFileName
value: vars.$('EnvironmentCode').yml
stages:
- stage: LoadVariablesPerEnvironment
displayName: Load Variables Per Environment
variables:
- template: $(EnvironmentFileName)
jobs:
- job: ShowcaseLoadedVariables
steps:
- pwsh: Write-Host "Variables have been loaded for the '$ENV:ENVIRONMENTNAME' environment"
displayName: Output Environment Variables
After importing the pipelines using the Azure DevOps UI, I can go to settings of each and set the Environment Code variable to whatever desired environment code:
However I'm always getting the same error when I try to run the pipeline, regardless of the code I fill in the variable value:
So the question here is: Is this kind of variable expansion not supported or is there a different way that I should use to accomplish this?
Thanks!
EDIT
I was able to expand the variables using another method. The new version of the pipeline is as such:
variables:
- name: EnvironmentCode
value: dev
- name: EnvironmentFileName
value: vars.${{ variables.EnvironmentCode }}.yml
stages:
- stage: LoadVariablesPerEnvironment
displayName: Load Variables Per Environment
variables:
- template: ${{ variables.EnvironmentFileName }}
jobs:
- job: ShowcaseLoadedVariables
steps:
- pwsh: Write-Host "Variables have been loaded for the '$ENV:ENVIRONMENTNAME' environment"
displayName: Output Environment Variables
However there is yet the issue of loading different files. I made different attempts and verified the following:
If you give a different environment code using the UI, when running
the pipeline, the value it assumes is still the one that's on the
pipeline definition;
If you remove from the pipeline definition the
default value or the variable entirely the expression
${{variables.EnvironmentCode}} will return an empty string
assuming the filename to be vars..yml which doesn't exist.
Is this kind of variable expansion not supported or is there a
different way that I should use to accomplish this?
If I am not misunderstand, at first, you want to use $() to get the variable you defined using the UI but failed. But later, ${{ }} can give you the value of the variable EnvironmentCode.
In fact, while you change to use ${{ }}, it just accessing the variable you predefined in the YAML files instead of the one you defined with UI. Just see this doc: Variable templates.
For the variable you defined with UI, it can be get and used with the format $()(Note: ${{ }} is the format of get the variables which defined in YAML file). But also, there some things you need to pay attention is for the variables you defined in UI, it can only be get/accessed after the build begin to run, because the variable which defined with UI exists in environment only after the build compiled begin. In one word, they are the agent-scope variable. That's why the value it used is still the one that's on the pipeline definition instead of on the UI.
If you remove from the pipeline definition the default value or the
variable entirely the expression ${{variables.EnvironmentCode}} will
return an empty string assuming the filename to be vars..yml which
doesn't exist.
As the document defined and I mentioned before, ${{}} is format which used to get the value of variable which defined in YAML files rather than the one which defined using UI.
In the steps of job, the variable that defined in the UI or defined in the YAML file can all be get/accessed with the format $(). Still, for the variable which defined in the YAML file can also get with ${{variables.xxxx}}. But at this time, if the variable name which defined in YAML file is same with the one defined with UI, the server can only get the one defined in YAML file.