Search code examples
dockeransible

Stop all but one container base on their name, using Ansible


Ssing ansible, I want to stop all running Docker containers except for the container whose name I will specify.

I have tried to get the container info

- name: Get info on docker host and list images
  docker_host_info:
    containers: yes
  register: docker_info

- name: Stop running docker containers
  docker_container:
    name: '{{ item.Names.0}}'
    state: stopped
  loop: '{{ docker_info.containers }}'
  when: ??

I'm not sure how to write the when condition correctly.

Below is a reduced version of the content of the registered docker_host_info variable

{
    "changed": false, 
    "containers": [
        {
            "Command": "/bin/sh -c 'java -jar test.jar'", 
            "Created": 1716300998, 
            "Id": "abcd", 
            "Image": "test", 
            "Names": [
                "/testapp"
            ], 
            "Status": "Up 12 hours"
        }, 
        {
            "Command": "/bin/sh -c 'java -jar test2.jar'", 
            "Created": 1716300992, 
            "Id": "abscsd", 
            "Image": "test2", 
            "Names": [
                "/testapp2"
            ], 
            "Status": "Up 12 hours"
        }
    ]
}

I am trying to access the Names but I am not sure how to properly use it, plus it has a / in front of the name and I am not sure why?


Solution

  • I am trying to access the Names but I am not sure how to properly use it

    The same way you used it in your docker_container task, with item.Names.0.
    The only thing you have to remember is that when will be templated by Ansible already, so you do not have to include it in an expression block {{ ... }}.

    The when clause is a raw Jinja2 expression without double curly braces

    Reference: Basic conditionals with when


    it has a / in front of the name and I am not sure why

    That is correct and it will always be the case, the information gathered by the module docker_host_info are similar to what you will get doing a docker inspect <my-container> — because it uses the same API endpoint reference at the end of this answer.

    If you do that on a container, you will realise that the internal implementation of Docker prefixes the name with a slash.

    Running those two commands:

    docker run \
      --detach \
      --rm \
      --name demo \
      alpine \
      tail -f /dev/null
    docker inspect \
      demo \
      -f "{{.Name}}"
    

    Would yield:

    /demo
    

    This is also pointed out in Docker's issue tracker:

    Inspect exposes the inner details of how docker is handling the container. Names are prefixed with their parent and / == the docker daemon. That is why every name will have this prefixed. This will be more important when nesting and multihost come into play. The / is correct for the inspect command

    Reference: https://github.com/moby/moby/issues/6705

    Which means that, if you want to stop the above container based on the return of the docker_host_info module, you will have to compare its returned name to /demo.


    So, given your actual output, if you want to stop all but the first container you have running, your condition ends up being:

    when: "item.Names.0 != '/testapp'"
    

    The module docker_host_info uses the endpoint /containers/json:

    items = self.client.get_json("/containers/json", params=params)
    

    Reference: https://github.com/ansible-collections/community.docker/blob/78673904731d2fe4deebff7d4be20b65585776f9/plugins/modules/docker_host_info.py#L299

    And if we browse the API documentation of Docker, we can see that this endpoint:

    Returns a list of containers. For details on the format, see the inspect endpoint.

    Reference: https://docs.docker.com/engine/api/v1.45/#tag/Container/operation/ContainerList

    Which means the inspect API endpoint and Docker command do actually return the same information as the containers return parameter of the module docker_host_info.