I am putting labels on a container with ansible. The labels I want (in docker compose syntax) are as follows:
labels:
com.datadoghq.ad.check_names: '["portainer"]'
com.datadoghq.ad.init_configs: '[{}]'
com.datadoghq.ad.instances: '[{"host": "%%host%%","port":"9999"}]'
Note that the labels are pure strings that contain some json-like formatting.
Now to do this with ansible, my naïve syntax was this:
- docker_container:
name: portainer
# ....
labels:
com.datadoghq.ad.check_names: "[\"portainer\"]"
com.datadoghq.ad.init_configs: "[{}]"
com.datadoghq.ad.instances: "[{\"host\": \"%%host%%\", \"port\":\" {{ host_port }}\" }]"
This fails with
"msg": "Error creating container: 500 Server Error for http docker://localhost/v1.41/containers/create?name=portainer: Internal Server Error ("json: cannot unmarshal array into Go struct field ContainerConfigWrapper.Labels of type string")"
After some trial and error I have found the reason to be that the 3rd label somehow is parsed as json, as evident from this log output from ansible-playbook -vvv:
"labels": {
"com.datadoghq.ad.check_names": "[\"portainer\"]",
"com.datadoghq.ad.init_configs": "[{}]",
"com.datadoghq.ad.instances": [
{
"host": "%%host%%",
"port": " 9000"
}
]
},
You can see check_names and init_configs are properly parsed as strings, while instances has been converted to a structure suggesting it was parsed as json.
Two tested methods that don't work in my case:
!unsafe
will work but disallows template_variables which I depend on (host_port).
| to_json
will work if the original string is in a template_variable, but I don't want that.
My question is, how can I avoid ansible parsing strings as json?
CodePudding user response:
Ansible will more or less reinterpret every string it comes accross as a yaml/json datastructure whenever a variable is expanded.
As you already found out, you can use the !unsafe
yaml datatype to turn templating off for needed variables.
labels:
com.datadoghq.ad.check_names: !unsafe "[\"portainer\"]"
com.datadoghq.ad.init_configs: !unsafe "[{}]"
com.datadoghq.ad.instances: !unsafe "[{\"host\": \"%%host%%\", \"port\":\" {{ host_port }}\" }]"
Unfortunately, as you also pointed out, this will block the expansion of {{ host_port }}
which will appear raw in the result string and that is not what we want. So we have to be a bit trickier for the definition of com.datadoghq.ad.instances
. Possible solution demonstrated in the following playbook:
---
- host: localhost
gather_facts: false
vars:
host_port: 9000
instances:
- hosts: !unsafe "%%host%%"
port: "{{ host_port }}"
labels:
com.datadoghq.ad.check_names: !unsafe "[\"portainer\"]"
com.datadoghq.ad.init_configs: !unsafe "[{}]"
com.datadoghq.ad.instances: "{{ instances | to_json }}"
tasks:
- debug:
msg: "{{ labels }}"
which gives:
PLAY [localhost] *****************************************************
TASK [debug] *********************************************************
ok: [localhost] => {
"msg": {
"com.datadoghq.ad.check_names": "[\"portainer\"]",
"com.datadoghq.ad.init_configs": "[{}]",
"com.datadoghq.ad.instances": "[{\"hosts\": \"%%host%%\", \"port\": 9000}]"
}
}
PLAY RECAP ***********************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
If you don't like that solution, an other one is to declare the vars as pure inline json inside a var expansion and transform it to a json string on the fly:
labels:
com.datadoghq.ad.check_names: '{{ ["portainer"] | to_json }}'
com.datadoghq.ad.init_configs: '{{ [{}] | to_json }}'
com.datadoghq.ad.instances: '{{ [{"host": "%%host%%", "port": host_port}] | to_json }}'
If this is still not what you are looking for... sorry that's all I have in my stock for now.