Ansible - Append variable for same precedence - append

I'd like to append my variable for a set_fact in the same precedence. See this link: http://docs.ansible.com/ansible/playbooks_variables.html#variable-precedence-where-should-i-put-a-variable
The naming of the variable has to be the same in both yml files.
In group_vars/all.yml I have defined:
my_var:
- value
- another
And in group_vars/hostgroup.yml I have defined
my_var:
- win
What I expect my_var is:
{{ value }}{{ another }}{{ win }}
What it actually is:
{{ win }}
How do I ensure my set_fact appends both variables.

There is no way to merge lists with default inventory plugins.
Your only option is to merge them in runtime with set_fact:
- set_fact:
my_var: "{{ my_var + ['win'] }}"

Related

Helm template not able to read ip address - can't evaluate field ipAddress in type string

I am trying to create a helm template for Istio's ServiceEntry which has a list of addresses for static ip addresses. In values.yaml, I have
- name: test-se
namespace: test-se-ns
egressUrls:
- mydbhost.com
port: 32306
protocol: TCP
ipAddress: 10.2.2.2
In the .tpl file I am trying to add the value of ipAddress to a list
{{- with .ipAddress }}
addresses:
- {{ .ipAddress | quote }}
{{- end }}
Always fails with exception
templates/_service_entry.tpl:18:13: executing "common.serviceentry.tpl" at <.ipAddress>: can't evaluate field ipAddress in type string
Any idea what I am doing wrong?
If you use with you make the thing that you have used as with the context inside that block.
So, use the dot to refer to it.
{{- with .ipAddress }}
addresses:
- {{ . | quote }}
{{- end }}
From the docs:
{{with pipeline}} T1 {{end}}
If the value of the pipeline is empty, no output is generated;
otherwise, dot is set to the value of the pipeline and T1 is
executed.
In this case, an if seems also fitting, since you do not much with the new context.
{{- if .ipAddress }}
addresses:
- {{ .ipAddress | quote }}
{{- end }}
when you use with in Helm, you change the scope of the ., so Helm looks for an object and not a string, you can read more about it in the docs.
but anyway, I think that in your case, you need to use range instead of with, you can see an example here

Detect if variable is dict / array / list

I've the following values.yaml:
env:
NODES:
value:
- 192.168.178.1:1234
- 192.168.178.2:1234
- 192.168.178.3:1234
- 192.168.178.4:1234
PASSWORD:
valueFrom:
secretKeyRef:
name: foo
key: bar
Now, in the deployment I've the following lines to specify the environment:
env:
{{- range $name, $item := .Values.env }}
- name: {{ $name }}
{{- $item | toYaml | nindent 14 }}
{{- end }}
This works as long as "NODES" is no list because they are not allowed as env variable, but I would like to specify them as list in the values.yaml. So the question is, how I can test if $item.value is a dict or a simple string.
I tried using typeOf, but it tells me that NODES is "[]interface {}". So I wonder what's the correct way to test?!
My goal is, if I see an array in the env of my deployment to join them using ";", e.g. value: {{ join ";" .env.NODES.value | quote }}.
Helm has some template functions to inspect object types (from the Sprig support library) but these quickly get into the underlying Go type system.
For the specific thing you're asking, you might try the Helm/Sprig kindIs function. This passes through to the underlying Go reflect package but in particular it does distinguish slice (list) and map (dict) as specific kinds.
{{- if and $item.value (kindIs "slice" $item.value) }}
value: {{ join ";" $item.value }}
{{- end }}
Rather than trying to have your Helm values directly reflect the Kubernetes object structure, you might consider having configuration values like this list of nodes as top-level configuration values.
# values.yaml
# not inside an `env:` block, at the top level
nodes:
- 192.168.178.1:1234
- 192.168.178.2:1234
Then you can construct these from a known format in your template. This will be much more straightforward than trying to use the reflection functions.
env:
- name: NODES
value: {{ join ";" .Values.nodes }}

Helm tpl function pass in file specific variable and global Values

Using the helm function tpl or other similar functions, how do you pass in a file specific variable and the top level Values? Here is a concrete example:
# values
template: "{{ .Values.name }} drinks {{ $drink }}"
name: "Tom"
# template
{{- $drink := "coffee" -}}
# how do I pass $drink into tpl???
{{ tpl .Values.template . }}
# expected output
Tom drinks coffee
It seems like when I do this it just passes in the .Values, but not the file specific $drink variable that's defined within the template and I get the error: error calling tpl. I don't see anything in the docs for how to merge these values together or just pass them both into the function.
Helm is using a slightly modified version of sprig functions. Most things from sprig are available.
You can use one of the dict functions to set the value or create a new dict that you pass as context.
{{ $_ := set .Values "drink" $drink }}
{{ tpl .Values.template . }}
In this case I have set a new key on the Values dict.
template: "{{ .Values.name }} drinks {{ .Values.drink }}"

Kubernetes multiple environment variables per environment using Helm 3 go template

I have 2 different values.yaml files per stage and production environment such as values.dev.yaml > values.prod.yaml and using with Helm 3. I would like to learn the best practices how to pass environment variables per environments.For instance we need to set different parameters to NODE_ENV variable.
-Should I specify the variable as hard coded as below and pass the environment variables when running helm upgrade/install command with --set flag?
-What is the correct way to use go template to do this. Can we specify something {{ .Values.node_env.value}} and then pass this env value in values yaml and use only -f values.yaml flag?
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 8080
protocol: TCP
resources:
{{- toYaml .Values.resources | nindent 12 }}
env:
- name: "NODE_ENV"
value: "stage"
- name: "NODE_ENV"
value: "production"
If you have one value file per environment (it is not clear to me this is your case.) like values.prod.yaml (for prod) and values.dev.yaml (for dev), then your templeate can look like this.
This will cause the template to look for extraEnv: in your values{dev/prod}.yaml and iterate over all key/values from that section.
env:
{{- range $key, $value := .Values.extraEnv }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
In your values.dev.yaml files you add all your KEY: values that are specific for this environment. Note you can have multiple key values here, all of them will be loaded. In this case we have NODE_ENV, ANOTHER_KEY, YET_ANOTHER_KEY - all of them will be loaded.
extraEnv:
NODE_ENV: stage
ANOTHER_KEY: value
YET_ANOTHER_KEY: value
same in your values.prod.yaml multiple KEY: value pairs can be specified and all of them will be loaded.
extraEnv:
NODE_ENV: production
ANOTHER_KEY: value

Ansible copy module with {{ item }}

I have a yml file for variables which goes like this.
- newHosts
- hostIP: 192.168.1.22
filename: file1
- hostIP: 192.168.1.23
filename: file2
I am using add_host: {{ item.hostIP }} with_items {{ newHosts }}.
I want to copy respective file to respective hosts with something like {{ item.filename }} but it copies all files to each host. How I just copy only the corresponding file to the node. How can I do that?
You can use conditionals that are applied at each iteration of the loop for example:
- hosts: all
tasks:
- name: copy file to appropriate server
copy: src={{item.filename}} dest=/var/foo/{{item.filename}}
with_items: newHosts
when: item.hostIP == ansible_ssh_host