Ansible - 'unicode object' has no attribute 'file_input' - unicode

I'm working with Ansible 2.2.1.0 and I work on a old project made by someone else with errors.
I have the following variables in my code:
software_output:
- { file_input: 'Download_me.zip', file_output: 'download.zip' }
software_version:"0.5,0.6"
And I have this shell module instruction to download on a FTP:
- name: "MySoftware | get package on FTP"
shell: >
curl --ftp-ssl -k {{ ' --ssl-allow-beast ' if os == 'aix' else "" }} -# -f -u {{ ftp_user }}:{{ ftp_password }} -f "{{ ftp_url | replace('##software_version##',item[1]) }}{{ item[0].file_input }}"
-o {{ require_inst_dir }}/{{ item[0].file_output }} 2>/dev/null
with_nested:
- software_output
- "{{ software_version.split(',') }}"
when: software_version is defined
But it doesn't work at all, I have the following error:
'unicode object' has no attribute 'file_input'
It looks like with_nested is not used as it has to be used, did I missed something?

In:
with_nested:
- software_output
software_output is a string software_output.
To refer to the variable value, change to:
with_nested:
- "{{ software_output }}"
Long time ago the first syntax was valid, but it was long time ago.

Related

Add data from comand line in Ansible task

I have some task:
---
- name: Ansible multiple replace data in file {{ path_agentd_conf }}
replace:
path: "{{ item.path }}"
regexp: "{{ item.regexp }}"
replace: "{{ item.replace }}"
with_items:
- {path: "{{ path_agentd_conf }}", regexp: "Server=127.0.0.1", replace: "Server={{ ip_zabbix_server }}"}
- {path: "{{ path_agentd_conf }}", regexp: "ServerActive=127.0.0.1", replace: "ServerActive={{ ip_zabbix_server }}"}
- {path: "{{ path_agentd_conf }}", regexp: "Hostname=Zabbix server", replace: "Hostname={{ ansible_hostname }}"}
- {path: "{{ path_agentd_conf }}", regexp: "# HostMetadata=", replace: "HostMetadata=73dc46da7abd1908951478965ca5b5be"}
And also i have variables in path /etc/ansible/roles/zabbix/zabbix-agent/agent/roles/agent/defaults/main.yml
---
# defaults file for agent
dir_download: /home/
path_agentd_conf: /etc/zabbix/zabbix_agentd.conf
url_zabbix: https://repo.zabbix.com/zabbix/6.0/ubuntu/pool/main/z/zabbix-release/zabbix-release_6.0-1+ubuntu20.04_all.deb
deb_zabbix: zabbix-release_6.0-1+ubuntu20.04_all.deb
ip_zabbix_server: 95.111.111.111
My first goal is, to use command line when I started playbook and change in line ip_zabbix_server: IP, for example ansible-playbook start.yaml -ip 55.55.55.55 and this IP should be replaced IP 95.111.111.111.
And second, is, to add also from command line in line - {path: "{{ path_agentd_conf }}", regexp: "# HostMetadata=", replace: "HostMetadata=73dc46da7abd1908951478965ca5b5be"} after numbers 73dc46da7abd1908951478965ca5b5be some worlds lxd mysq
- {path: "{{ path_agentd_conf }}", regexp: "# HostMetadata=", replace: "HostMetadata=73dc46da7abd1908951478965ca5b5be lxd mysq"}
I do not know is it passable in ansible or not ? I know that for example in bash it is possible, when we are using in bash scripts variables $1 $2 etc.
Could you please help? Thank you!
If I understood correctly, you want to modify this "default" value in the variables with one in command line:
Taking into consideration the order of the variables as you might know, Command Line prevails over what's already defined in roles, plays, tasks, defaults, etc.
So, for get that result you need to run your playbook like this:
ansible-playbook start.yaml -e "ip_zabbix_server=55.55.55.55"
basically it should be similar to bash but with the parameter -e | --extra-vars
For the second part I'm not sure but I would try if I can set that as variable and do in a similar way.
My first goal is, to use command line when I started playbook and change in line ip_zabbix_server
This a very common scenario of overriding a default role variable. You can have a look at where to put a variable for all possible solutions.
But in your specific case, to override from the command line, you would use an extra-vars:
ansible-playbook start.yaml --extra-vars ip_zabbix_server=55.55.55.55
And second, is, to add also from command line in line - {path: "{{ path_agentd_conf }}", regexp: "# HostMetadata=", replace: "HostMetadata=73dc46da7abd1908951478965ca5b5be"}
That one is trickier for 2 reasons:
you have hard-coded the list of dictionaries inside the task which makes it hard (not to say impossible) to overide
passing a long list of dictionaries directly on the command line is not really user friendly (but possible).
My below proposition is not the only solution and you can adapt depending on your exact requirement (see above link on variable precedence).
First add a new default variable in roles/agent/defaults/main.yml:
my_replace_list:
- {path: "{{ path_agentd_conf }}", regexp: "Server=127.0.0.1", replace: "Server={{ ip_zabbix_server }}"}
- {path: "{{ path_agentd_conf }}", regexp: "ServerActive=127.0.0.1", replace: "ServerActive={{ ip_zabbix_server }}"}
- {path: "{{ path_agentd_conf }}", regexp: "Hostname=Zabbix server", replace: "Hostname={{ ansible_hostname }}"}
- {path: "{{ path_agentd_conf }}", regexp: "# HostMetadata=", replace: "HostMetadata=73dc46da7abd1908951478965ca5b5be"}
Then modify your task to use that var:
- name: Ansible multiple replace data in file {{ path_agentd_conf }}
replace:
path: "{{ item.path }}"
regexp: "{{ item.regexp }}"
replace: "{{ item.replace }}"
with_items: "{{ my_replace_list }}"
We are then in the exact same situation as above for the zabbix server ip and the list can be overridden with an extra var in your command. The easiest way here is to declare the var in a separate file, e.g. my_list_override.yaml
my_replace_list:
- {path: "/toto", regexp: "from", replace: "to"}
- {path: "/titi", regexp: "a", replace: "b"}
You can then easily use that file to override your variable:
ansible-playbook start.yaml --extra-vars #/path/to/my_list_override.yml
Now, if you really want to pass that variable directly on the command line, it is possible but you will have to pass it as a correct json which is probably not the most user friendly method:
ansible-playbook start.yaml \
--extra-vars '{"my_replace_list":[{"path":"/toto","regexp":"from","replace":"to"},{"path":"/titi","regexp":"a","replace":"b"}]}'
Thank you all!
I just deleted line ip_zabbix_server: 95.217.188.202 from the file /etc/ansible/roles/zabbix/zabbix-agent/agent/roles/agent/defaults/main.yml, then add to lines variables {{ ip }}
- {path: "{{ path_agentd_conf }}", regexp: "Server=127.0.0.1", replace: "Server={{ ip }}"}
- {path: "{{ path_agentd_conf }}", regexp: "ServerActive=127.0.0.1", replace: "ServerActive={{ ip }}"}
and after I am using command ansible-playbook agent.yml --extra-vars ip=77.77.77.77 to add 77.77.77.77 to the line Server=127.0.0.1" and ServerActive=127.0.0.1.

How to write below condition in Helm chart syntax in a job.yaml file?

I have to write this condition in helm chart syntax in job.yaml file so that imagePullSecrets get's executed only when the condition is satisfied.
Condition is
when: (network.docker.username | default('', true) | trim != '') and (network.docker.password | default('', true) | trim != '')
To write above condition below this code:
imagePullSecrets:
- name: "{{ $.Values.image.pullSecret }}"
Ideally, Docker username & password should come from Secrets. Here's the Sample helm code to use if in yaml file:
imagePullSecrets:
{{ if and (ne $.Values.network.docker.password '') (ne $.Values.network.docker.username '') }}
- name: "{{ $.Values.image.pullSecret }}"
{{ end }}
And values.yaml should have:
network:
docker:
username: your-uname
password: your-pwd

Helm debug unknown field

I'm trying to install Kritis using :
azureuser#Azure:~/kritis/docs/standalone$ helm install kritis https://storage.googleapis.com/kritis-charts/repository/kritis-charts-0.2.0.tgz --set certificates.ca="$(cat ca.crt)" --set certificates.cert="$(cat kritis.crt)" --set certificates.key="$(cat kritis.key)" --debug
But I'm getting the next error:
install.go:148: [debug] Original chart version: ""
install.go:165: [debug] CHART PATH: /home/azureuser/.cache/helm/repository/kritis-charts-0.2.0.tgz
Error: unable to build kubernetes objects from release manifest: error validating "": error validating data: ValidationError(ClusterRole.metadata): unknown field "kritis.grafeas.io/install" in io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta
helm.go:76: [debug] error validating "": error validating data: ValidationError(ClusterRole.metadata): unknown field "kritis.grafeas.io/install" in io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta
helm.sh/helm/v3/pkg/kube.scrubValidationError
/home/circleci/helm.sh/helm/pkg/kube/client.go:520
helm.sh/helm/v3/pkg/kube.(*Client).Build
/home/circleci/helm.sh/helm/pkg/kube/client.go:135
Is there a way to know exactly on which file the error is being triggered? and what exactly that error means?
The original chart files are available here : https://github.com/grafeas/kritis/blob/master/kritis-charts/templates/preinstall/clusterrolebinding.yaml
You cant get from where exactly this coming from but this output is giving some clues regarding that.
In your error message we have some useful information:
helm.go:76: [debug] error validating "": error validating data: ValidationError(ClusterRole.metadata): unknown field "kritis.grafeas.io/install" in io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta
error validating ""
ClusterRole
kritis.grafeas
You can download your chart and dig into it for these terms using cat as follows:
$ wget https://storage.googleapis.com/kritis-charts/repository/kritis-charts-0.2.0.tgz
$ tar xzvf kritis-charts-0.2.0.tgz
$ cd kritis-charts/
If your grep for kritis.grafeas.io/install, you can see a "variable" being set:
$ grep -R "kritis.grafeas.io/install" *
values.yaml:kritisInstallLabel: "kritis.grafeas.io/install"
Now we can grep this variable and check what we can find:
$ grep -R "kritisInstallLabel" *
templates/rbac.yaml: {{ .Values.kritisInstallLabel }}: ""
templates/rbac.yaml: {{ .Values.kritisInstallLabel }}: ""
templates/kritis-server-deployment.yaml: {{ .Values.kritisInstallLabel }}: ""
templates/preinstall/pod.yaml: {{ .Values.kritisInstallLabel }}: ""
templates/preinstall/pod.yaml: - {{ .Values.kritisInstallLabel }}
templates/preinstall/serviceaccount.yaml: {{ .Values.kritisInstallLabel }}: ""
templates/preinstall/clusterrolebinding.yaml: {{ .Values.kritisInstallLabel }}: ""
templates/postinstall/pod.yaml: {{ .Values.kritisInstallLabel }}: ""
templates/postinstall/pod.yaml: - {{ .Values.kritisInstallLabel }}
templates/secrets.yaml: {{ .Values.kritisInstallLabel }}: ""
templates/predelete/pod.yaml: {{ .Values.kritisInstallLabel }}: ""
templates/kritis-server-service.yaml: {{ .Values.kritisInstallLabel }}: ""
values.yaml:kritisInstallLabel: "kritis.grafeas.io/install"
In this output we can see a rbac.yaml file. That matches with one of the terms we are looking for (ClusterRole):
If we read this file, we can see the ClusterRole and a line referring to kritisInstallLabel:
- apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: {{ .Values.clusterRoleBindingName }}
labels:
{{ .Values.kritisInstallLabel }}: ""
{{ .Values.kritisInstallLabel }}: "" will be translated as .Values.kritis.grafeas.io/install by helm and that's where your error is coming from.

Helm lint error but everything looks ok to me

I'm getting this error when linting my helm project
$ helm lint --debug
==> Linting .
[INFO] Chart.yaml: icon is recommended
[ERROR] templates/: render error in "myProject/templates/configmap.yaml": template: myProject/templates/configmap.yaml:26:27: executing "myProject/templates/configmap.yaml" at <.Values.fileServiceH...>: can't evaluate field fileHost in type interface {}
Error: 1 chart(s) linted, 1 chart(s) failed
This is my configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myProject-configmap
data:
tkn.yaml: |
iss: "{{ .Values.iss }}"
aud: "{{ .Values.aud }}"
db.yaml: |
database: "{{ .Values.database }}"
user: "{{ .Values.user }}"
host: "{{ .Values.host }}"
dialect: "{{ .Values.dialect }}"
pool:
min: "{{ .Values.pool.min }}"
max: "{{ .Values.pool.max }}"
acquire: "{{ .Values.pool.acquire }}"
idle: "{{ .Values.pool.idle }}"
fileservice.yaml: |
fileServiceHost:
fileHost: "{{ .Values.fileServiceHost.fileHost }}"
notificationservice.yaml: |
notificationServiceHost:
notificationHost: "{{ .Values.notificationservice.notificationHost }}"
organizationservice.yaml: |
organizationServiceHost:
organizationHost: "{{ .Values.organizationservice.organizationHost }}"
organizations.yaml: |
organizations: {{ .Values.organizations | toJson | indent 4 }}
epic.yaml: |
redirectUri: "{{ .Values.redirectUri }}"
This is my /vars/dev/fileservice.yaml file
fileServiceHost:
fileHost: 'https://example.com'
What is wrong that i'm getting this lint error?
You want to either use .Files.Get to load the yaml files or take the yaml content that you have in the yaml files and capture it in the values.yaml so that you can insert it directly in your configmap with toYaml.
If the values are just static and you don't need the user to override them then .Files.Get is better for you. If you want to be able to override the content in the yaml files easily at install time then just represent them in the values.yaml file.

How to pass dynamic arguments to a helm chart that runs a job

I'd like to allow our developers to pass dynamic arguments to a helm template (Kubernetes job). Currently my arguments in the helm template are somewhat static (apart from certain values) and look like this
Args:
--arg1
value1
--arg2
value2
--sql-cmd
select * from db
If I were run a task using the docker container without Kubernetes, I would pass parameters like so:
docker run my-image --arg1 value1 --arg2 value2 --sql-cmd "select * from db"
Is there any way to templatize arguments in a helm chart in such way that any number of arguments could be passed to a template.
For example.
cat values.yaml
...
arguments: --arg1 value1 --arg2 value2 --sql-cmd "select * from db"
...
or
cat values.yaml
...
arguments: --arg3 value3
...
I've tried a few approaches but was not successful. Here is one example:
Args:
{{ range .Values.arguments }}
{{ . }}
{{ end }}
Yes. In values.yaml you need to give it an array instead of a space delimited string.
cat values.yaml
...
arguments: ['--arg3', 'value3', '--arg2', 'value2']
...
or
cat values.yaml
...
arguments:
- --arg3
- value3
- --arg2
- value2
...
and then you like you mentioned in the template should do it:
args:
{{ range .Values.arguments }}
- {{ . }}
{{ end }}
If you want to override the arguments on the command line you can pass an array with --set like this:
--set arguments={--arg1, value1, --arg2, value2, --arg3, value3, ....}
In your values file define arguments as:
extraArgs:
argument1: value1
argument2: value2
booleanArg1:
In your template do:
args:
{{- range $key, $value := .Values.extraArgs }}
{{- if $value }}
- --{{ $key }}={{ $value }}
{{- else }}
- --{{ $key }}
{{- end }}
{{- end }}
Rico's answer needed to be improved.
Using the previous example I've received errors like:
templates/deployment.yaml: error converting YAML to JSON: yaml or
failed to get versionedObject: unable to convert unstructured object to apps/v1beta2, Kind=Deployment: cannot restore slice from string
This is my working setup with coma in elements:
( the vertical format for the list is more readable )
cat values.yaml
...
arguments: [
"--arg3,",
"value3,",
"--arg2,",
"value2,",
]
...
in the template should do it:
args: [
{{ range .Values.arguments }}
{{ . }}
{{ end }}
]
because of some limitations, I had to work with split and to use a delimiter, so in my case:
deployment.yaml :
{{- if .Values.deployment.args }}
args:
{{- range (split " " .Values.deployment.args) }}
- {{ . }}
{{- end }}
{{- end }}
when use --set:
helm install --set deployment.args="--inspect server.js" ...
results with:
- args:
- --inspect
- server.js
The arguments format needs to be kept consistent in such cases.
Here is my case and it works fine.
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Values.app.name }}
labels:
app: {{ .Values.app.name }}
instance: test
spec:
replicas: {{ .Values.master.replicaCount }}
selector:
matchLabels:
app: {{ .Values.app.name }}
instance: test
template:
metadata:
labels:
app: {{ .Values.app.name }}
instance: test
spec:
imagePullSecrets:
- name: gcr-pull-secret
containers:
- name: {{ .Values.app.name }}
image: {{ .Values.app.image }}
env:
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
args:
[
"--users={{int .Values.cmd.users}}",
"--spawn-rate={{int .Values.cmd.rate}}",
"--host={{.Values.cmd.host}}",
"--logfile={{.Values.cmd.logfile}}",
"--{{.Values.cmd.role}}"]
ports:
- containerPort: {{ .Values.container.port }}
resources:
requests:
memory: {{ .Values.container.requests.memory }}
cpu: {{ .Values.container.requests.cpu }}
limits:
memory: {{ .Values.container.limits.memory }}
cpu: {{ .Values.container.limits.cpu }}
Unfortunately following mixed args format does not work within container construct -
mycommand -ArgA valA --ArgB valB --ArgBool1 -ArgBool2 --ArgC=valC
The correct format of above command expected is -
mycommand --ArgA=valA --ArgB=valB --ArgC=valC --ArgBool1 --ArgBool2
This can be achieved by following constructs -
#Dockerfile last line
ENTRYPOINT [mycommand]
#deployment.yaml
containers:
- name: {{ .Values.app.name }}
image: {{ .Values.app.image }}
args: [
"--ArgA={{ .Values.cmd.ArgA }}",
"--ArgB={{ .Values.cmd.ArgB }}",
"--ArgC={{ .Values.cmd.ArgC }}",
"--{{ .Values.cmd.ArgBool1 }}",
"--{{ .Values.cmd.ArgBool2 }}" ]
#values.yaml
cmd:
ArgA: valA
ArgB: valB
ArgC: valC
ArgBool1: "ArgBool1"
ArgBool2: "ArgBool2"
helm install --name "airflow" stable/airflow --set secrets.database=mydatabase,secrets.password=mypassword
So this is the helm chart in question: https://github.com/helm/charts/tree/master/stable/airflow
Now I want to overwrite the default values in the helm chart
secrets.database and
secrets.password so I use --set argument and then it is key=value pairs separated by commas.
helm install --name "<name for your chart>" <chart> --set key0=value0,key1=value1,key2=value2,key3=value3
Did you try this?
{{ range .Values.arguments }}
{{ . | quote }}
{{ end }}
Acid R's key/value solution was the only thing that worked for me.
I ended up with this:
values.yaml
arguments:
url1: 'http://something1.example.com'
url2: 'http://something2.example.com'
url3: 'http://something3.example.com'
url4: 'http://something3.example.com'
And in my template:
args:
{{- range $key, $value := .Values.arguments }}
- --url={{ $value }}
{{- end }}