Escaping double curly braces in Ansible - docker

How to escape double curly braces in Ansible 1.9.2?
For instance, how can I escape double curly braces in the following shell command?
- name: Test
shell: "docker inspect --format '{{ .NetworkSettings.IPAddress }}' instance1"

Whenever you have problems with conflicting characters in Ansible, a rule of thumb is to output them as a string in a Jinja expression.
So instead of {{ you would use {{ '{{' }}:
- debug: msg="docker inspect --format '{{ '{{' }} .NetworkSettings.IPAddress {{ '}}' }}' instance1"
Topic "Escaping" in the Jinja2 docs.

This:
- name: Test
shell: "docker inspect --format {% raw %}'{{ .NetworkSettings.IPAddress }}' {% endraw %} instance1"
Should work
Another way to do is using backslashes like \{\{ .NetworkSettings.IPAddress \}\}
Hope it helps

Tried on with ansible 2.1.1.0
{%raw%}...{%endraw%} block seems the clear way
- name: list container images and name date on the server
shell: docker ps --format {%raw%}"{{.Image}} {{.Names}}"{%endraw%}
Only need to escape leading '{{'
tasks:
- name: list container images and names
shell: docker ps --format "{{'{{'}}.Image}} {{'{{'}}.Names}}"
No harm to escap the tailing '}}', except more difficult to read.
tasks:
- name: list container images and names
shell: docker ps --format "{{'{{'}}.Image{{'}}'}} {{'{{'}}.Names{{'}}'}}"
Backslash '\' seems do not work

New in Ansible 2.0 is the ability to declare a value as unsafe with the !unsafe tag.
In your example you could do:
- name: Test
shell: !unsafe "docker inspect --format '{{ .NetworkSettings.IPAddress }}' instance1"
See the docs for details.

I have a similar issue: i need to post a JSON doc made from a jinja2 template containing some go templates variables (yes, i know :-P), such as
"NAME_TEMPLATE": %{{service_name}}.%{{stack_name}}.%{{environment_name}}
Trying to fence this part of the template between
{% raw %} ... {% endraw %}
didn't work because there is some sort of magic in ansible which will run the template and variable substition twice (i'm not sure about that, but it definitively looks like this)
You end up with "undefined variable service_name" when trying to use the template...
So i ended up using a combination of !unsafe and {% raw %} ... {% endraw %} to define a fact that's later used in the template.
- set_fact:
__rancher_init_root_domain: "{{ rancher_root_domain }}"
#!unsafe: try to trick ansible into not doing substitutions in that string, then use %raw% so the value won't substituted another time
__rancher_init_name_template: !unsafe "{%raw%}%{{service_name}}.%{{stack_name}}.%{{environment_name}}{%endraw%}"
- name: build a template for a project
set_fact:
__rancher_init_template_doc: "{{ lookup('template', 'templates/project_template.json.j2') }}"
the template contains this:
"ROOT_DOMAIN":"{{__rancher_init_root_domain}}",
"ROUTE53_ZONE_ID":"",
"NAME_TEMPLATE":"{{__rancher_init_name_template }}",
"HEALTH_CHECK":"10000",
and the output is ok:
"NAME_TEMPLATE": "%{{service_name}}.%{{stack_name}}.%{{environment_name}}",

Here's a shorter alternative to udondan's answer; surround the whole string with double brackets:
shell: "docker inspect --format {{ '{{ .NetworkSettings.IPAddress }}' }} instance1"

The solution by using raw has been already mentioned but the command in the answer before unfortunately didn't work for me.
Without ansible:
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' docker_instance_name
With ansible:
- name: Get ip of db container
shell: "{% raw %}docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' docker_instance_name{% endraw %}"
register: db_ip_addr
- debug:
var: db_ip_addr.stdout

I managed to work around my issue using a small script:
#!/usr/bin/env bash
docker inspect --format '{{ .NetworkSettings.IPAddress }}' "$1"
And the following Ansible play
- copy:
src: files/get_docker_ip.sh
dest: /usr/local/bin/get_docker_ip.sh
owner: root
group: root
mode: 0770
- shell: "/usr/local/bin/get_docker_ip.sh {{ SWIFT_ACCOUNT_HOSTNAME }}"
register: swift_account_info
Nevertheless, it's very surprising that Ansible doesn't allow escaping double curly braces!

I was unable to get #Ben's answer to work (shell: !unsafe ...)
What follows here is a complete (and working!) answer to the OP's question, updated for Ansible >2.0
---
# file: play.yml
- hosts: localhost
connection: local
gather_facts: no
vars:
# regarding !unsafe, please see:
# https://docs.ansible.com/ansible/latest/user_guide/playbooks_advanced_syntax.html
#
- NetworkSettings_IPAddress: !unsafe "{{.NetworkSettings.IPAddress}}"
tasks:
- shell: "docker inspect --format '{{NetworkSettings_IPAddress}}' instance1"
register: out
- debug: var="{{item}}"
with_items:
- out.cmd
- out.stdout
outputs: ([WARNINGS] removed)
# ansible-playbook play.yml
PLAY [localhost] ***************************************************************
TASK [shell] *******************************************************************
changed: [localhost]
TASK [debug] *******************************************************************
ok: [localhost] => (item=out.cmd) => {
"item": "out.cmd",
"out.cmd": "docker inspect --format '{{.NetworkSettings.IPAddress}}' instance1"
}
ok: [localhost] => (item=out.stdout) => {
"item": "out.stdout",
"out.stdout": "172.17.0.2"
}
PLAY RECAP *********************************************************************
localhost : ok=2 changed=1 unreachable=0 failed=0
# ansible --version | head -1
ansible 2.6.1

Here is a mostly clean and Ansible native workaround not depending on docker --inspect with curly braces. We assume we have just referenced one container with the Ansible docker module before:
- name: query IP of client container
shell: "docker exec {{ docker_containers[0].Id }} hostname -I"
register: _container_query
- name: get IP of query result
set_fact:
_container_ip: "{{ _container_query.stdout | regex_replace('\\s','') }}"
You now have the IP of the Docker container in the Variable _container_ip. I also published this workaround on my article The Marriage of Ansible with Docker.
[Update 2015-11-03] Removed whitespaces of the stdout of the container query.
[Update 2015-11-04] BTW, there were two pull requests in the official Ansible repository, that would made this workaround needless by recovering the facts returned by the Docker module. So you could acces the IP of a docker container via docker_containers[0].NetworkSettings.IPAddress. So please vote for those pull requests:
fixed broken facts #1457
docker module: fix regressions introduced by f38186c and 80aca4b #2093

Related

Get the last element of a docker image path using Go templates

I would like to generate a synthetic docker ps table but my docker image paths are very long. The solution I am trying to do is to display only the last part of the image name:
This is what I have:
docker ps --format "table {{.Names}}\\t{{.Image}}\\t{{.Status}}\\t{{.Command}}"
NAMES IMAGE STATUS COMMAND
a_container a_local_image Up 36 minutes "python…"
another_container registry.example.com/group/subgroup/project/my_image:latest Up 38 minutes "go…"
I would like:
docker ps --format "table {{.Names}}\\t{{ <magic .Image> }}\\t{{.Status}}\\t{{.Command}}"
NAMES IMAGE STATUS COMMAND
a_container a_local_image Up 36 minutes "python…"
another_container my_image:latest Up 38 minutes "go…"
so basically, get what is after the last /, like basename does.
This is what I tried:
# No fixed length
docker ps --format "table {{ slice .Image 0 10}}"
# No last index available
docker ps --format 'table {{ (split "/" .Image) }}'
Any help or workaround is appreciated.
p.s.: since I use docker cli, I don't think I can define custom functions to use in the template expression.
There is no good way to get the last element of a template array. Even though you can use index and len built-ins, you can't use arithmetic to get len - 1.
This is a template-only trick:
{{ $image := "" }}{{ range split .Image "/" }}{{ $image = . }}{{ end }}{{ $image }}
Basically it does the following:
declare a var $image
split the .Image on /
iterate over the array string and just assign to $image
at the end of the range, $image will hold the value of the last element
print $image
Full command (with enclosing apex ' so you don't have to escape quotes):
docker ps --format 'table {{.Names}}\t{{ $image := "" }}{{ range split .Image "/" }}{{ $image = . }}{{ end }}{{ $image }}\t{{.Status}}\t{{.Command}}'
You may could use next workaround, I put it in a shell file just because it's too long, but you are ok to call it directly in shell.
test.sh
paste <(docker ps --format 'table {{.ID}}') \
<(docker ps --format 'table {{.Image}}' | awk -F '/' '{print $NF}') \
<(docker ps --format 'table {{.Command}} {{.CreatedAt}} {{.Status}} {{.Ports}} {{.Names}}')
Explain:
docker ps --format 'table {{.ID}}' get the ID column of docker ps
docker ps --format 'table {{.Image}}' | awk -F '/' '{print $NF}'), frist get the Image column, then use awk to split /, get the last sub column as you required.
the left is used to get remained columns as you needed of docker ps
Finally, use paste command to combine the output of above command.

Restart mutiple Docker Containers using Ansible not happening

I am trying to restart my docker containers one by one for a particular image using Ansible but it doesn't seem to be happening. Below is my yml and what it is doing is exiting the current running container.
---
- name: restart app servers
hosts: shashank-VM
connection: local
become: yes
become_method: sudo
tasks:
- name: Get info on the Container
shell: docker ps | awk '/{{ item }}/{print $1}'
register: list_of_containers
with_items:
- ubuntu
- name: Restart Docker Service
docker_container:
name: "{{ item }}"
# image: ubuntu
state: started
restart: yes
with_items: "{{ list_of_containers.results | map(attribute='stdout_lines') | list }}"
If you see the below output when i run docker ps there are no running containers.
TASK [Restart Docker Service] ****************************************************************************************************************
/usr/lib/python2.7/dist-packages/requests/__init__.py:80: RequestsDependencyWarning: urllib3 (1.25.9) or chardet (3.0.4) doesn't match a supported version!
RequestsDependencyWarning)
changed: [shashank-VM] => (item=c2310b76b005)
PLAY RECAP ***********************************************************************************************************************************
shashank-VM : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
shashank#shashank-VM:~/ansible$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
What am i doing wrong? Can someone help?
I don't think the docker_container module is designed to do what you want (i.e., restart an existing container). The module is designed to manage containers by name, not by id, and will check that the running container matches the options provided to docker_container.
You're probably better off simply using the docker command to restart your containers:
---
- name: restart app servers
hosts: shashank-VM
connection: local
become: yes
become_method: sudo
tasks:
- name: Get info on the Container
shell: docker ps | awk '/{{ item }}/{print $1}'
register: list_of_containers
with_items:
- ubuntu
- name: Restart Docker Service
command: docker restart {{ item }}
with_items: "{{ list_of_containers.results | map(attribute='stdout_lines') | list }}"

Unable to concat two variables along with underscore for ansible hosts

Below is the command generated by ansible plugin for Jenkins.
ansible-playbook /app/stop.yml -i /app/my-hosts -l test_west -e app_name=test -e environments=west -v
Here is my ansible inventory hosts file.
cat my-hosts
[test_west]
10.0.9.88
10.0.9.89
-l option helps match the ansible inventory host file 'test_west'
My question is ... what do I have to mention in the ansible playbook for the hosts ?
My playbook looks like below however this does not seem correct or needed as the hosts are match using the -l paramter passed to ansible:
---
- hosts: "{{ app_name + '_' + environments }}
Can you please suggest what should I set the hosts: to in my ansible playbook so it is the same as -l argument i.e 'test_west' ?
You should use: - hosts: "{{ app_name }}_{{ environments }}"
Sample output:
[root#greenhat-30 tests]$ ansible-playbook -i hosts test.yml -e app_name=test -e environments=west
[WARNING]: Could not match supplied host pattern, ignoring: test_west
PLAY [test_west] *******************************************************************************************************************************************************************************************************
skipping: no hosts matched
PLAY RECAP *************************************************************************************************************************************************************************************************************
[http_offline#greenhat-30 tests]$
You can see the group selected is PLAY [test_west].

docker inspect: how to select only certain keys from range

I created an docker image that has few labels, here is my Dockerfile section on LABELS:
grep LABEL Dockerfile
LABEL "css1"="/var/www/css1"
LABEL "css2"="/var/www/css2"
LABEL "img"="/var/www/img"
LABEL "js"="/var/www/js"
Then:
docker image inspect --format='{{.Config.Labels}}' labels-test
map[css1:/var/www/css1 css2:/var/www/css2 img:/var/www/img js:/var/www/js]
I need to get for example all labels starting with css. This is as far as i was able to figure:
docker image inspect --format='{{range $k,$v:=.Config.Labels}}{{$k}}:{{$v}} {{end}}' labels-test
css1:/var/www/css1 css2:/var/www/css2 img:/var/www/img js:/var/www/js
Desired output would be:
css1:/var/www/css1 css2:/var/www/css2
The Go template functions are available in golang docco
eq can test if arg1 == arg2.
printf "%.3s" $k will give you the first 3 chars of a string.
docker image inspect \
--format='{{ range $k,$v:=.Config.Labels }}{{ if eq (printf "%.3s" $k) "css" }}{{ $k }}:{{ $v }} {{end}}{{end}}' \
IMAGE
You might want to look at the querying the Docker API images endpoint /images/IMAGE/json directly or processing the JSON output somewhere if you need to do any more advanced processing:
docker image inspect \
--format='{{json .Config.Labels}}' \
IMAGE
You can do something like
docker inspect --format='{{index (index (.Config.Labels)).css1 }}' labels-test
which shows for me
/var/www/css1
and also
docker inspect --format='{{index (index (.Config.Labels)).css2 }}' labels-test
which shows for me
/var/www/css2
See my previous answer on that subject
How to get ENV variable when doing Docker Inspect
Edit
The following gives exactly what you ask for
docker inspect --format='{{index (index (.Config.Labels)).css1 }} {{index (index (.Config.Labels)).css2 }} labels-test
as i get
/var/www/css1 /var/www/css2

Ansible w/ Docker - Show current Container state

Im working on a little Ansible project in which I'm using Docker Containers.
I'll keep my question short:
I want to get the state of a running Dockercontainer!
What I mean by that is, that i want to get the current state of the container, that Docker shows you by using the "docker ps" command.
Examples would be:
Up
Exited
Restarting
I want to get one of those results from a specific container. But without using the Command or the Shell module!
KR
As of Ansible 2.8 you can use the docker_container_info, which essentially returns the input from docker inspect <container>:
- name: Get infos on container
docker_container_info:
name: my_container
register: result
- name: Does container exist?
debug:
msg: "The container {{ 'exists' if result.exists else 'does not exist' }}"
- name: Print the status of the container
debug:
msg: "The container status is {{ result.container['State']['Status'] }}"
when: result.exists
With my Docker version, State contains this:
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 8235,
"ExitCode": 0,
"Error": "",
"StartedAt": "2019-01-25T14:10:08.3206714Z",
"FinishedAt": "0001-01-01T00:00:00Z"
}
See https://docs.ansible.com/ansible/2.8/modules/docker_container_info_module.html for more details.
Unfortunately, none of the modules around docker can currently "List containers".
I did the following as work around to grab the status:
- name: get container status
shell: docker ps -a -f name={{ container }} --format {%raw%}"table {{.Status}}"{%endraw%} | awk 'FNR == 2 {print}' | awk '{print $1}'
register: status
Result will then be available in the status variable
This worked for me:
- name: Get container status
shell: docker inspect --format={{ '{{.State.Running}}' }} {{ container_name }}
register: status
#Start the container if it is not running
- name: Start the container if it is in stopeed state.
shell: docker start heuristic_mestorf
when: status.stdout != "true"
Edit: If you are running Ansible 2.8+ you can use docker_container_info. See David Pärsson's answer for details.
Here is one way to craft it using the docker_container module (note that it will create the container if it does not exist):
- name: "Check if container is running"
docker_container:
name: "{{ container_name }}"
state: present
register: container_test_started
ignore_errors: yes
- set_fact:
container_exists: "{{ container_test_started.ansible_facts is defined }}"
- set_fact:
container_is_running: "{{ container_test_started.ansible_facts is defined and container_test_started.ansible_facts.docker_container.State.Status == 'running' }}"
container_is_paused: "{{ container_test_started.ansible_facts is defined and container_test_started.ansible_facts.docker_container.State.Status == 'paused' }}"
For me the gotchya was that if the container doesn't exist, ansible_facts is not defined. If it does though, then that contains basically the whole docker inspect <container> output so I navigate that for the status.
If you just need to short circuit, a simpler alternative would be to move the desired set_fact'd value into a failed_when on the docker_container task.
I do it through set_fact to keep my options open for forking behavior elsewhere.. e.g. stop, do task, then put back how it was.
I included pause because it is commonly forgotten as a state :)
There is an ansible module docker_image_facts which give you information about images. You are looking for something that would be docker_container_facts, which does not currently exist. Good idea though.
The question is not clear, but generally speaking you can use ansible with docker in two cases:
by using docker module of ansible
http://docs.ansible.com/ansible/latest/docker_module.html
- name: data container
docker:
name: mydata
image: busybox
state: present
volumes:
- /data
by calling ansible inside Dockerfile
FROM centos7
RUN ansible-playbook -i inventory playbook.yml
Your question is slightly unclear.
My best try - you want to have output of 'docker ps' - the first thing comes in mind is to use the Ansible command module, but you don't want to use it.
Then there are few docker modules:
docker - This is the original Ansible module for managing the Docker container life cycle.
docker_container - ansible module to manage the life cycle of docker containers.
You can look into the options -> parameters to get what exactly you're looking for.
Here's the complete list of Ansible modules for managing/using Docker.

Resources