Rewrite the docker_containers inventory plugin (#413)

* Rewrite the docker_containers inventory plugin.

* Improve error messages.
This commit is contained in:
Felix Fontein 2022-07-06 21:48:32 +02:00 committed by GitHub
parent c3a76007d0
commit 23a90668c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 33 additions and 34 deletions

View File

@ -0,0 +1,4 @@
major_changes:
- "docker_containers inventory plugin - no longer uses the Docker SDK for Python. It requires ``requests`` to be installed,
and depending on the features used has some more requirements. If the Docker SDK for Python is installed,
these requirements are likely met (https://github.com/ansible-collections/community.docker/pull/413)."

View File

@ -17,12 +17,9 @@ short_description: Ansible dynamic inventory plugin for Docker containers.
version_added: 1.1.0 version_added: 1.1.0
author: author:
- Felix Fontein (@felixfontein) - Felix Fontein (@felixfontein)
requirements:
- L(Docker SDK for Python,https://docker-py.readthedocs.io/en/stable/) >= 1.10.0
extends_documentation_fragment: extends_documentation_fragment:
- ansible.builtin.constructed - ansible.builtin.constructed
- community.docker.docker - community.docker.docker.api_documentation
- community.docker.docker.docker_py_1_documentation
description: description:
- Reads inventories from the Docker API. - Reads inventories from the Docker API.
- Uses a YAML configuration file that ends with C(docker.[yml|yaml]). - Uses a YAML configuration file that ends with C(docker.[yml|yaml]).
@ -154,23 +151,18 @@ from ansible.errors import AnsibleError
from ansible.module_utils.common.text.converters import to_native from ansible.module_utils.common.text.converters import to_native
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable from ansible.plugins.inventory import BaseInventoryPlugin, Constructable
from ansible_collections.community.docker.plugins.module_utils.common import ( from ansible_collections.community.docker.plugins.module_utils.common_api import (
RequestException, RequestException,
) )
from ansible_collections.community.docker.plugins.module_utils.util import ( from ansible_collections.community.docker.plugins.module_utils.util import (
DOCKER_COMMON_ARGS_VARS, DOCKER_COMMON_ARGS_VARS,
) )
from ansible_collections.community.docker.plugins.plugin_utils.common import ( from ansible_collections.community.docker.plugins.plugin_utils.common_api import (
AnsibleDockerClient, AnsibleDockerClient,
) )
try: from ansible_collections.community.docker.plugins.module_utils._api.errors import APIError, DockerException
from docker.errors import DockerException, APIError
except Exception:
# missing Docker SDK for Python handled in ansible_collections.community.docker.plugins.module_utils.common
pass
MIN_DOCKER_PY = '1.7.0'
MIN_DOCKER_API = None MIN_DOCKER_API = None
@ -193,7 +185,15 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
add_legacy_groups = self.get_option('add_legacy_groups') add_legacy_groups = self.get_option('add_legacy_groups')
try: try:
containers = client.containers(all=True) params = {
'limit': -1,
'all': 1,
'size': 0,
'trunc_cmd': 0,
'since': None,
'before': None,
}
containers = client.get_json('/containers/json', params=params)
except APIError as exc: except APIError as exc:
raise AnsibleError("Error listing containers: %s" % to_native(exc)) raise AnsibleError("Error listing containers: %s" % to_native(exc))
@ -227,7 +227,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
full_facts = dict() full_facts = dict()
try: try:
inspect = client.inspect_container(id) inspect = client.get_json('/containers/{0}/json', id)
except APIError as exc: except APIError as exc:
raise AnsibleError("Error inspecting container %s - %s" % (name, str(exc))) raise AnsibleError("Error inspecting container %s - %s" % (name, str(exc)))
@ -261,7 +261,9 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
# Figure out ssh IP and Port # Figure out ssh IP and Port
try: try:
# Lookup the public facing port Nat'ed to ssh port. # Lookup the public facing port Nat'ed to ssh port.
port = client.port(container, ssh_port)[0] network_settings = inspect.get('NetworkSettings') or {}
port_settings = network_settings.get('Ports') or {}
port = port_settings.get('%d/tcp' % (ssh_port, ))[0]
except (IndexError, AttributeError, TypeError): except (IndexError, AttributeError, TypeError):
port = dict() port = dict()
@ -330,7 +332,7 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
path.endswith(('docker.yaml', 'docker.yml'))) path.endswith(('docker.yaml', 'docker.yml')))
def _create_client(self): def _create_client(self):
return AnsibleDockerClient(self, min_docker_version=MIN_DOCKER_PY, min_docker_api_version=MIN_DOCKER_API) return AnsibleDockerClient(self, min_docker_api_version=MIN_DOCKER_API)
def parse(self, inventory, loader, path, cache=True): def parse(self, inventory, loader, path, cache=True):
super(InventoryModule, self).parse(inventory, loader, path, cache) super(InventoryModule, self).parse(inventory, loader, path, cache)
@ -340,9 +342,9 @@ class InventoryModule(BaseInventoryPlugin, Constructable):
self._populate(client) self._populate(client)
except DockerException as e: except DockerException as e:
raise AnsibleError( raise AnsibleError(
'An unexpected docker error occurred: {0}'.format(e) 'An unexpected Docker error occurred: {0}'.format(e)
) )
except RequestException as e: except RequestException as e:
raise AnsibleError( raise AnsibleError(
'An unexpected requests error occurred when Docker SDK for Python tried to talk to the docker daemon: {0}'.format(e) 'An unexpected requests error occurred when trying to talk to the Docker daemon: {0}'.format(e)
) )

View File

@ -93,29 +93,22 @@ def create_get_option(options, default=False):
class FakeClient(object): class FakeClient(object):
def __init__(self, *hosts): def __init__(self, *hosts):
self.hosts = dict() self.get_results = {}
self.list_reply = [] list_reply = []
for host in hosts: for host in hosts:
self.list_reply.append({ list_reply.append({
'Id': host['Id'], 'Id': host['Id'],
'Names': [host['Name']] if host['Name'] else [], 'Names': [host['Name']] if host['Name'] else [],
'Image': host['Config']['Image'], 'Image': host['Config']['Image'],
'ImageId': host['Image'], 'ImageId': host['Image'],
}) })
self.hosts[host['Name']] = host self.get_results['/containers/{0}/json'.format(host['Name'])] = host
self.hosts[host['Id']] = host self.get_results['/containers/{0}/json'.format(host['Id'])] = host
self.get_results['/containers/json'] = list_reply
def containers(self, all=False): def get_json(self, url, *param, **kwargs):
return list(self.list_reply) url = url.format(*param)
return self.get_results[url]
def inspect_container(self, id):
return self.hosts[id]
def port(self, container, port):
host = self.hosts[container['Id']]
network_settings = host.get('NetworkSettings') or dict()
ports = network_settings.get('Ports') or dict()
return ports.get('{0}/tcp'.format(port)) or []
def test_populate(inventory, mocker): def test_populate(inventory, mocker):