mirror of
https://github.com/ansible-collections/community.docker.git
synced 2026-03-27 15:49:48 +00:00
Move common utility code to plugins.module_utils.util (#390)
* Move common utility code to plugins.module_utils.util. * Also adjust plugins. * Fix import.
This commit is contained in:
parent
a44e0736cf
commit
f41d7ac1b8
2
changelogs/fragments/390-util.yml
Normal file
2
changelogs/fragments/390-util.yml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
minor_changes:
|
||||||
|
- "Move common utility functions from the ``common`` module_util to a new module_util called ``util``. This should not have any user-visible effect (https://github.com/ansible-collections/community.docker/pull/390)."
|
||||||
@ -156,6 +156,8 @@ 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 import (
|
||||||
RequestException,
|
RequestException,
|
||||||
|
)
|
||||||
|
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 import (
|
||||||
|
|||||||
@ -147,7 +147,8 @@ keyed_groups:
|
|||||||
|
|
||||||
from ansible.errors import AnsibleError
|
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_collections.community.docker.plugins.module_utils.common import update_tls_hostname, get_connect_params
|
from ansible_collections.community.docker.plugins.module_utils.common import get_connect_params
|
||||||
|
from ansible_collections.community.docker.plugins.module_utils.util import update_tls_hostname
|
||||||
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable
|
from ansible.plugins.inventory import BaseInventoryPlugin, Constructable
|
||||||
from ansible.parsing.utils.addresses import parse_address
|
from ansible.parsing.utils.addresses import parse_address
|
||||||
|
|
||||||
|
|||||||
@ -77,43 +77,34 @@ except ImportError:
|
|||||||
class RequestException(Exception):
|
class RequestException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
from ansible_collections.community.docker.plugins.module_utils.util import (
|
||||||
DEFAULT_DOCKER_HOST = 'unix://var/run/docker.sock'
|
DEFAULT_DOCKER_HOST,
|
||||||
DEFAULT_TLS = False
|
DEFAULT_TLS,
|
||||||
DEFAULT_TLS_VERIFY = False
|
DEFAULT_TLS_VERIFY,
|
||||||
DEFAULT_TLS_HOSTNAME = 'localhost' # deprecated
|
DEFAULT_TLS_HOSTNAME,
|
||||||
MIN_DOCKER_VERSION = "1.8.0"
|
DEFAULT_TIMEOUT_SECONDS,
|
||||||
DEFAULT_TIMEOUT_SECONDS = 60
|
DOCKER_COMMON_ARGS,
|
||||||
|
DOCKER_COMMON_ARGS_VARS,
|
||||||
DOCKER_COMMON_ARGS = dict(
|
DOCKER_MUTUALLY_EXCLUSIVE,
|
||||||
docker_host=dict(type='str', default=DEFAULT_DOCKER_HOST, fallback=(env_fallback, ['DOCKER_HOST']), aliases=['docker_url']),
|
DOCKER_REQUIRED_TOGETHER,
|
||||||
tls_hostname=dict(type='str', fallback=(env_fallback, ['DOCKER_TLS_HOSTNAME'])),
|
DEFAULT_DOCKER_REGISTRY,
|
||||||
api_version=dict(type='str', default='auto', fallback=(env_fallback, ['DOCKER_API_VERSION']), aliases=['docker_api_version']),
|
BYTE_SUFFIXES,
|
||||||
timeout=dict(type='int', default=DEFAULT_TIMEOUT_SECONDS, fallback=(env_fallback, ['DOCKER_TIMEOUT'])),
|
is_image_name_id,
|
||||||
ca_cert=dict(type='path', aliases=['tls_ca_cert', 'cacert_path']),
|
is_valid_tag,
|
||||||
client_cert=dict(type='path', aliases=['tls_client_cert', 'cert_path']),
|
sanitize_result,
|
||||||
client_key=dict(type='path', aliases=['tls_client_key', 'key_path']),
|
DockerBaseClass,
|
||||||
ssl_version=dict(type='str', fallback=(env_fallback, ['DOCKER_SSL_VERSION'])),
|
update_tls_hostname,
|
||||||
tls=dict(type='bool', default=DEFAULT_TLS, fallback=(env_fallback, ['DOCKER_TLS'])),
|
compare_dict_allow_more_present,
|
||||||
use_ssh_client=dict(type='bool', default=False),
|
compare_generic,
|
||||||
validate_certs=dict(type='bool', default=DEFAULT_TLS_VERIFY, fallback=(env_fallback, ['DOCKER_TLS_VERIFY']), aliases=['tls_verify']),
|
DifferenceTracker,
|
||||||
debug=dict(type='bool', default=False)
|
clean_dict_booleans_for_docker_api,
|
||||||
|
convert_duration_to_nanosecond,
|
||||||
|
parse_healthcheck,
|
||||||
|
omit_none_from_dict,
|
||||||
)
|
)
|
||||||
|
|
||||||
DOCKER_COMMON_ARGS_VARS = dict([
|
|
||||||
[option_name, 'ansible_docker_%s' % option_name]
|
|
||||||
for option_name in DOCKER_COMMON_ARGS
|
|
||||||
if option_name != 'debug'
|
|
||||||
])
|
|
||||||
|
|
||||||
DOCKER_MUTUALLY_EXCLUSIVE = []
|
MIN_DOCKER_VERSION = "1.8.0"
|
||||||
|
|
||||||
DOCKER_REQUIRED_TOGETHER = [
|
|
||||||
['client_cert', 'client_key']
|
|
||||||
]
|
|
||||||
|
|
||||||
DEFAULT_DOCKER_REGISTRY = 'https://index.docker.io/v1/'
|
|
||||||
BYTE_SUFFIXES = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
|
|
||||||
|
|
||||||
|
|
||||||
if not HAS_DOCKER_PY:
|
if not HAS_DOCKER_PY:
|
||||||
@ -132,75 +123,6 @@ if not HAS_DOCKER_PY:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def is_image_name_id(name):
|
|
||||||
"""Check whether the given image name is in fact an image ID (hash)."""
|
|
||||||
if re.match('^sha256:[0-9a-fA-F]{64}$', name):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def is_valid_tag(tag, allow_empty=False):
|
|
||||||
"""Check whether the given string is a valid docker tag name."""
|
|
||||||
if not tag:
|
|
||||||
return allow_empty
|
|
||||||
# See here ("Extended description") for a definition what tags can be:
|
|
||||||
# https://docs.docker.com/engine/reference/commandline/tag/
|
|
||||||
return bool(re.match('^[a-zA-Z0-9_][a-zA-Z0-9_.-]{0,127}$', tag))
|
|
||||||
|
|
||||||
|
|
||||||
def sanitize_result(data):
|
|
||||||
"""Sanitize data object for return to Ansible.
|
|
||||||
|
|
||||||
When the data object contains types such as docker.types.containers.HostConfig,
|
|
||||||
Ansible will fail when these are returned via exit_json or fail_json.
|
|
||||||
HostConfig is derived from dict, but its constructor requires additional
|
|
||||||
arguments. This function sanitizes data structures by recursively converting
|
|
||||||
everything derived from dict to dict and everything derived from list (and tuple)
|
|
||||||
to a list.
|
|
||||||
"""
|
|
||||||
if isinstance(data, dict):
|
|
||||||
return dict((k, sanitize_result(v)) for k, v in data.items())
|
|
||||||
elif isinstance(data, (list, tuple)):
|
|
||||||
return [sanitize_result(v) for v in data]
|
|
||||||
else:
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
class DockerBaseClass(object):
|
|
||||||
def __init__(self):
|
|
||||||
self.debug = False
|
|
||||||
|
|
||||||
def log(self, msg, pretty_print=False):
|
|
||||||
pass
|
|
||||||
# if self.debug:
|
|
||||||
# log_file = open('docker.log', 'a')
|
|
||||||
# if pretty_print:
|
|
||||||
# log_file.write(json.dumps(msg, sort_keys=True, indent=4, separators=(',', ': ')))
|
|
||||||
# log_file.write(u'\n')
|
|
||||||
# else:
|
|
||||||
# log_file.write(msg + u'\n')
|
|
||||||
|
|
||||||
|
|
||||||
def update_tls_hostname(result, old_behavior=False, deprecate_function=None, uses_tls=True):
|
|
||||||
if result['tls_hostname'] is None:
|
|
||||||
if old_behavior:
|
|
||||||
result['tls_hostname'] = DEFAULT_TLS_HOSTNAME
|
|
||||||
if uses_tls and deprecate_function is not None:
|
|
||||||
deprecate_function(
|
|
||||||
'The default value "localhost" for tls_hostname is deprecated and will be removed in community.docker 3.0.0.'
|
|
||||||
' From then on, docker_host will be used to compute tls_hostname. If you want to keep using "localhost",'
|
|
||||||
' please set that value explicitly.',
|
|
||||||
version='3.0.0', collection_name='community.docker')
|
|
||||||
return
|
|
||||||
|
|
||||||
# get default machine name from the url
|
|
||||||
parsed_url = urlparse(result['docker_host'])
|
|
||||||
if ':' in parsed_url.netloc:
|
|
||||||
result['tls_hostname'] = parsed_url.netloc[:parsed_url.netloc.rindex(':')]
|
|
||||||
else:
|
|
||||||
result['tls_hostname'] = parsed_url
|
|
||||||
|
|
||||||
|
|
||||||
def _get_tls_config(fail_function, **kwargs):
|
def _get_tls_config(fail_function, **kwargs):
|
||||||
try:
|
try:
|
||||||
tls_config = TLSConfig(**kwargs)
|
tls_config = TLSConfig(**kwargs)
|
||||||
@ -773,284 +695,3 @@ class AnsibleDockerClient(AnsibleDockerClientBase):
|
|||||||
self.module.warn('Docker warning: {0}'.format(warning))
|
self.module.warn('Docker warning: {0}'.format(warning))
|
||||||
elif isinstance(result, string_types) and result:
|
elif isinstance(result, string_types) and result:
|
||||||
self.module.warn('Docker warning: {0}'.format(result))
|
self.module.warn('Docker warning: {0}'.format(result))
|
||||||
|
|
||||||
|
|
||||||
def compare_dict_allow_more_present(av, bv):
|
|
||||||
'''
|
|
||||||
Compare two dictionaries for whether every entry of the first is in the second.
|
|
||||||
'''
|
|
||||||
for key, value in av.items():
|
|
||||||
if key not in bv:
|
|
||||||
return False
|
|
||||||
if bv[key] != value:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def compare_generic(a, b, method, datatype):
|
|
||||||
'''
|
|
||||||
Compare values a and b as described by method and datatype.
|
|
||||||
|
|
||||||
Returns ``True`` if the values compare equal, and ``False`` if not.
|
|
||||||
|
|
||||||
``a`` is usually the module's parameter, while ``b`` is a property
|
|
||||||
of the current object. ``a`` must not be ``None`` (except for
|
|
||||||
``datatype == 'value'``).
|
|
||||||
|
|
||||||
Valid values for ``method`` are:
|
|
||||||
- ``ignore`` (always compare as equal);
|
|
||||||
- ``strict`` (only compare if really equal)
|
|
||||||
- ``allow_more_present`` (allow b to have elements which a does not have).
|
|
||||||
|
|
||||||
Valid values for ``datatype`` are:
|
|
||||||
- ``value``: for simple values (strings, numbers, ...);
|
|
||||||
- ``list``: for ``list``s or ``tuple``s where order matters;
|
|
||||||
- ``set``: for ``list``s, ``tuple``s or ``set``s where order does not
|
|
||||||
matter;
|
|
||||||
- ``set(dict)``: for ``list``s, ``tuple``s or ``sets`` where order does
|
|
||||||
not matter and which contain ``dict``s; ``allow_more_present`` is used
|
|
||||||
for the ``dict``s, and these are assumed to be dictionaries of values;
|
|
||||||
- ``dict``: for dictionaries of values.
|
|
||||||
'''
|
|
||||||
if method == 'ignore':
|
|
||||||
return True
|
|
||||||
# If a or b is None:
|
|
||||||
if a is None or b is None:
|
|
||||||
# If both are None: equality
|
|
||||||
if a == b:
|
|
||||||
return True
|
|
||||||
# Otherwise, not equal for values, and equal
|
|
||||||
# if the other is empty for set/list/dict
|
|
||||||
if datatype == 'value':
|
|
||||||
return False
|
|
||||||
# For allow_more_present, allow a to be None
|
|
||||||
if method == 'allow_more_present' and a is None:
|
|
||||||
return True
|
|
||||||
# Otherwise, the iterable object which is not None must have length 0
|
|
||||||
return len(b if a is None else a) == 0
|
|
||||||
# Do proper comparison (both objects not None)
|
|
||||||
if datatype == 'value':
|
|
||||||
return a == b
|
|
||||||
elif datatype == 'list':
|
|
||||||
if method == 'strict':
|
|
||||||
return a == b
|
|
||||||
else:
|
|
||||||
i = 0
|
|
||||||
for v in a:
|
|
||||||
while i < len(b) and b[i] != v:
|
|
||||||
i += 1
|
|
||||||
if i == len(b):
|
|
||||||
return False
|
|
||||||
i += 1
|
|
||||||
return True
|
|
||||||
elif datatype == 'dict':
|
|
||||||
if method == 'strict':
|
|
||||||
return a == b
|
|
||||||
else:
|
|
||||||
return compare_dict_allow_more_present(a, b)
|
|
||||||
elif datatype == 'set':
|
|
||||||
set_a = set(a)
|
|
||||||
set_b = set(b)
|
|
||||||
if method == 'strict':
|
|
||||||
return set_a == set_b
|
|
||||||
else:
|
|
||||||
return set_b >= set_a
|
|
||||||
elif datatype == 'set(dict)':
|
|
||||||
for av in a:
|
|
||||||
found = False
|
|
||||||
for bv in b:
|
|
||||||
if compare_dict_allow_more_present(av, bv):
|
|
||||||
found = True
|
|
||||||
break
|
|
||||||
if not found:
|
|
||||||
return False
|
|
||||||
if method == 'strict':
|
|
||||||
# If we would know that both a and b do not contain duplicates,
|
|
||||||
# we could simply compare len(a) to len(b) to finish this test.
|
|
||||||
# We can assume that b has no duplicates (as it is returned by
|
|
||||||
# docker), but we don't know for a.
|
|
||||||
for bv in b:
|
|
||||||
found = False
|
|
||||||
for av in a:
|
|
||||||
if compare_dict_allow_more_present(av, bv):
|
|
||||||
found = True
|
|
||||||
break
|
|
||||||
if not found:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class DifferenceTracker(object):
|
|
||||||
def __init__(self):
|
|
||||||
self._diff = []
|
|
||||||
|
|
||||||
def add(self, name, parameter=None, active=None):
|
|
||||||
self._diff.append(dict(
|
|
||||||
name=name,
|
|
||||||
parameter=parameter,
|
|
||||||
active=active,
|
|
||||||
))
|
|
||||||
|
|
||||||
def merge(self, other_tracker):
|
|
||||||
self._diff.extend(other_tracker._diff)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def empty(self):
|
|
||||||
return len(self._diff) == 0
|
|
||||||
|
|
||||||
def get_before_after(self):
|
|
||||||
'''
|
|
||||||
Return texts ``before`` and ``after``.
|
|
||||||
'''
|
|
||||||
before = dict()
|
|
||||||
after = dict()
|
|
||||||
for item in self._diff:
|
|
||||||
before[item['name']] = item['active']
|
|
||||||
after[item['name']] = item['parameter']
|
|
||||||
return before, after
|
|
||||||
|
|
||||||
def has_difference_for(self, name):
|
|
||||||
'''
|
|
||||||
Returns a boolean if a difference exists for name
|
|
||||||
'''
|
|
||||||
return any(diff for diff in self._diff if diff['name'] == name)
|
|
||||||
|
|
||||||
def get_legacy_docker_container_diffs(self):
|
|
||||||
'''
|
|
||||||
Return differences in the docker_container legacy format.
|
|
||||||
'''
|
|
||||||
result = []
|
|
||||||
for entry in self._diff:
|
|
||||||
item = dict()
|
|
||||||
item[entry['name']] = dict(
|
|
||||||
parameter=entry['parameter'],
|
|
||||||
container=entry['active'],
|
|
||||||
)
|
|
||||||
result.append(item)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def get_legacy_docker_diffs(self):
|
|
||||||
'''
|
|
||||||
Return differences in the docker_container legacy format.
|
|
||||||
'''
|
|
||||||
result = [entry['name'] for entry in self._diff]
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def clean_dict_booleans_for_docker_api(data, allow_sequences=False):
|
|
||||||
'''
|
|
||||||
Go doesn't like Python booleans 'True' or 'False', while Ansible is just
|
|
||||||
fine with them in YAML. As such, they need to be converted in cases where
|
|
||||||
we pass dictionaries to the Docker API (e.g. docker_network's
|
|
||||||
driver_options and docker_prune's filters). When `allow_sequences=True`
|
|
||||||
YAML sequences (lists, tuples) are converted to [str] instead of str([...])
|
|
||||||
which is the expected format of filters which accept lists such as labels.
|
|
||||||
'''
|
|
||||||
def sanitize(value):
|
|
||||||
if value is True:
|
|
||||||
return 'true'
|
|
||||||
elif value is False:
|
|
||||||
return 'false'
|
|
||||||
else:
|
|
||||||
return str(value)
|
|
||||||
|
|
||||||
result = dict()
|
|
||||||
if data is not None:
|
|
||||||
for k, v in data.items():
|
|
||||||
result[str(k)] = [sanitize(e) for e in v] if allow_sequences and is_sequence(v) else sanitize(v)
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def convert_duration_to_nanosecond(time_str):
|
|
||||||
"""
|
|
||||||
Return time duration in nanosecond.
|
|
||||||
"""
|
|
||||||
if not isinstance(time_str, str):
|
|
||||||
raise ValueError('Missing unit in duration - %s' % time_str)
|
|
||||||
|
|
||||||
regex = re.compile(
|
|
||||||
r'^(((?P<hours>\d+)h)?'
|
|
||||||
r'((?P<minutes>\d+)m(?!s))?'
|
|
||||||
r'((?P<seconds>\d+)s)?'
|
|
||||||
r'((?P<milliseconds>\d+)ms)?'
|
|
||||||
r'((?P<microseconds>\d+)us)?)$'
|
|
||||||
)
|
|
||||||
parts = regex.match(time_str)
|
|
||||||
|
|
||||||
if not parts:
|
|
||||||
raise ValueError('Invalid time duration - %s' % time_str)
|
|
||||||
|
|
||||||
parts = parts.groupdict()
|
|
||||||
time_params = {}
|
|
||||||
for (name, value) in parts.items():
|
|
||||||
if value:
|
|
||||||
time_params[name] = int(value)
|
|
||||||
|
|
||||||
delta = timedelta(**time_params)
|
|
||||||
time_in_nanoseconds = (
|
|
||||||
delta.microseconds + (delta.seconds + delta.days * 24 * 3600) * 10 ** 6
|
|
||||||
) * 10 ** 3
|
|
||||||
|
|
||||||
return time_in_nanoseconds
|
|
||||||
|
|
||||||
|
|
||||||
def parse_healthcheck(healthcheck):
|
|
||||||
"""
|
|
||||||
Return dictionary of healthcheck parameters and boolean if
|
|
||||||
healthcheck defined in image was requested to be disabled.
|
|
||||||
"""
|
|
||||||
if (not healthcheck) or (not healthcheck.get('test')):
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
result = dict()
|
|
||||||
|
|
||||||
# All supported healthcheck parameters
|
|
||||||
options = dict(
|
|
||||||
test='test',
|
|
||||||
interval='interval',
|
|
||||||
timeout='timeout',
|
|
||||||
start_period='start_period',
|
|
||||||
retries='retries'
|
|
||||||
)
|
|
||||||
|
|
||||||
duration_options = ['interval', 'timeout', 'start_period']
|
|
||||||
|
|
||||||
for (key, value) in options.items():
|
|
||||||
if value in healthcheck:
|
|
||||||
if healthcheck.get(value) is None:
|
|
||||||
# due to recursive argument_spec, all keys are always present
|
|
||||||
# (but have default value None if not specified)
|
|
||||||
continue
|
|
||||||
if value in duration_options:
|
|
||||||
time = convert_duration_to_nanosecond(healthcheck.get(value))
|
|
||||||
if time:
|
|
||||||
result[key] = time
|
|
||||||
elif healthcheck.get(value):
|
|
||||||
result[key] = healthcheck.get(value)
|
|
||||||
if key == 'test':
|
|
||||||
if isinstance(result[key], (tuple, list)):
|
|
||||||
result[key] = [str(e) for e in result[key]]
|
|
||||||
else:
|
|
||||||
result[key] = ['CMD-SHELL', str(result[key])]
|
|
||||||
elif key == 'retries':
|
|
||||||
try:
|
|
||||||
result[key] = int(result[key])
|
|
||||||
except ValueError:
|
|
||||||
raise ValueError(
|
|
||||||
'Cannot parse number of retries for healthcheck. '
|
|
||||||
'Expected an integer, got "{0}".'.format(result[key])
|
|
||||||
)
|
|
||||||
|
|
||||||
if result['test'] == ['NONE']:
|
|
||||||
# If the user explicitly disables the healthcheck, return None
|
|
||||||
# as the healthcheck object, and set disable_healthcheck to True
|
|
||||||
return None, True
|
|
||||||
|
|
||||||
return result, False
|
|
||||||
|
|
||||||
|
|
||||||
def omit_none_from_dict(d):
|
|
||||||
"""
|
|
||||||
Return a copy of the dictionary with all keys with value None omitted.
|
|
||||||
"""
|
|
||||||
return dict((k, v) for (k, v) in d.items() if v is not None)
|
|
||||||
|
|||||||
406
plugins/module_utils/util.py
Normal file
406
plugins/module_utils/util.py
Normal file
@ -0,0 +1,406 @@
|
|||||||
|
# Copyright 2016 Red Hat | Ansible
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
from __future__ import (absolute_import, division, print_function)
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
|
||||||
|
import abc
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
from ansible.module_utils.basic import env_fallback
|
||||||
|
from ansible.module_utils.common.collections import is_sequence
|
||||||
|
from ansible.module_utils.common._collections_compat import Sequence
|
||||||
|
from ansible.module_utils.six.moves.urllib.parse import urlparse
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_DOCKER_HOST = 'unix://var/run/docker.sock'
|
||||||
|
DEFAULT_TLS = False
|
||||||
|
DEFAULT_TLS_VERIFY = False
|
||||||
|
DEFAULT_TLS_HOSTNAME = 'localhost' # deprecated
|
||||||
|
DEFAULT_TIMEOUT_SECONDS = 60
|
||||||
|
|
||||||
|
DOCKER_COMMON_ARGS = dict(
|
||||||
|
docker_host=dict(type='str', default=DEFAULT_DOCKER_HOST, fallback=(env_fallback, ['DOCKER_HOST']), aliases=['docker_url']),
|
||||||
|
tls_hostname=dict(type='str', fallback=(env_fallback, ['DOCKER_TLS_HOSTNAME'])),
|
||||||
|
api_version=dict(type='str', default='auto', fallback=(env_fallback, ['DOCKER_API_VERSION']), aliases=['docker_api_version']),
|
||||||
|
timeout=dict(type='int', default=DEFAULT_TIMEOUT_SECONDS, fallback=(env_fallback, ['DOCKER_TIMEOUT'])),
|
||||||
|
ca_cert=dict(type='path', aliases=['tls_ca_cert', 'cacert_path']),
|
||||||
|
client_cert=dict(type='path', aliases=['tls_client_cert', 'cert_path']),
|
||||||
|
client_key=dict(type='path', aliases=['tls_client_key', 'key_path']),
|
||||||
|
ssl_version=dict(type='str', fallback=(env_fallback, ['DOCKER_SSL_VERSION'])),
|
||||||
|
tls=dict(type='bool', default=DEFAULT_TLS, fallback=(env_fallback, ['DOCKER_TLS'])),
|
||||||
|
use_ssh_client=dict(type='bool', default=False),
|
||||||
|
validate_certs=dict(type='bool', default=DEFAULT_TLS_VERIFY, fallback=(env_fallback, ['DOCKER_TLS_VERIFY']), aliases=['tls_verify']),
|
||||||
|
debug=dict(type='bool', default=False)
|
||||||
|
)
|
||||||
|
|
||||||
|
DOCKER_COMMON_ARGS_VARS = dict([
|
||||||
|
[option_name, 'ansible_docker_%s' % option_name]
|
||||||
|
for option_name in DOCKER_COMMON_ARGS
|
||||||
|
if option_name != 'debug'
|
||||||
|
])
|
||||||
|
|
||||||
|
DOCKER_MUTUALLY_EXCLUSIVE = []
|
||||||
|
|
||||||
|
DOCKER_REQUIRED_TOGETHER = [
|
||||||
|
['client_cert', 'client_key']
|
||||||
|
]
|
||||||
|
|
||||||
|
DEFAULT_DOCKER_REGISTRY = 'https://index.docker.io/v1/'
|
||||||
|
BYTE_SUFFIXES = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
|
||||||
|
|
||||||
|
|
||||||
|
def is_image_name_id(name):
|
||||||
|
"""Check whether the given image name is in fact an image ID (hash)."""
|
||||||
|
if re.match('^sha256:[0-9a-fA-F]{64}$', name):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid_tag(tag, allow_empty=False):
|
||||||
|
"""Check whether the given string is a valid docker tag name."""
|
||||||
|
if not tag:
|
||||||
|
return allow_empty
|
||||||
|
# See here ("Extended description") for a definition what tags can be:
|
||||||
|
# https://docs.docker.com/engine/reference/commandline/tag/
|
||||||
|
return bool(re.match('^[a-zA-Z0-9_][a-zA-Z0-9_.-]{0,127}$', tag))
|
||||||
|
|
||||||
|
|
||||||
|
def sanitize_result(data):
|
||||||
|
"""Sanitize data object for return to Ansible.
|
||||||
|
|
||||||
|
When the data object contains types such as docker.types.containers.HostConfig,
|
||||||
|
Ansible will fail when these are returned via exit_json or fail_json.
|
||||||
|
HostConfig is derived from dict, but its constructor requires additional
|
||||||
|
arguments. This function sanitizes data structures by recursively converting
|
||||||
|
everything derived from dict to dict and everything derived from list (and tuple)
|
||||||
|
to a list.
|
||||||
|
"""
|
||||||
|
if isinstance(data, dict):
|
||||||
|
return dict((k, sanitize_result(v)) for k, v in data.items())
|
||||||
|
elif isinstance(data, (list, tuple)):
|
||||||
|
return [sanitize_result(v) for v in data]
|
||||||
|
else:
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class DockerBaseClass(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.debug = False
|
||||||
|
|
||||||
|
def log(self, msg, pretty_print=False):
|
||||||
|
pass
|
||||||
|
# if self.debug:
|
||||||
|
# log_file = open('docker.log', 'a')
|
||||||
|
# if pretty_print:
|
||||||
|
# log_file.write(json.dumps(msg, sort_keys=True, indent=4, separators=(',', ': ')))
|
||||||
|
# log_file.write(u'\n')
|
||||||
|
# else:
|
||||||
|
# log_file.write(msg + u'\n')
|
||||||
|
|
||||||
|
|
||||||
|
def update_tls_hostname(result, old_behavior=False, deprecate_function=None, uses_tls=True):
|
||||||
|
if result['tls_hostname'] is None:
|
||||||
|
if old_behavior:
|
||||||
|
result['tls_hostname'] = DEFAULT_TLS_HOSTNAME
|
||||||
|
if uses_tls and deprecate_function is not None:
|
||||||
|
deprecate_function(
|
||||||
|
'The default value "localhost" for tls_hostname is deprecated and will be removed in community.docker 3.0.0.'
|
||||||
|
' From then on, docker_host will be used to compute tls_hostname. If you want to keep using "localhost",'
|
||||||
|
' please set that value explicitly.',
|
||||||
|
version='3.0.0', collection_name='community.docker')
|
||||||
|
return
|
||||||
|
|
||||||
|
# get default machine name from the url
|
||||||
|
parsed_url = urlparse(result['docker_host'])
|
||||||
|
if ':' in parsed_url.netloc:
|
||||||
|
result['tls_hostname'] = parsed_url.netloc[:parsed_url.netloc.rindex(':')]
|
||||||
|
else:
|
||||||
|
result['tls_hostname'] = parsed_url
|
||||||
|
|
||||||
|
|
||||||
|
def compare_dict_allow_more_present(av, bv):
|
||||||
|
'''
|
||||||
|
Compare two dictionaries for whether every entry of the first is in the second.
|
||||||
|
'''
|
||||||
|
for key, value in av.items():
|
||||||
|
if key not in bv:
|
||||||
|
return False
|
||||||
|
if bv[key] != value:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def compare_generic(a, b, method, datatype):
|
||||||
|
'''
|
||||||
|
Compare values a and b as described by method and datatype.
|
||||||
|
|
||||||
|
Returns ``True`` if the values compare equal, and ``False`` if not.
|
||||||
|
|
||||||
|
``a`` is usually the module's parameter, while ``b`` is a property
|
||||||
|
of the current object. ``a`` must not be ``None`` (except for
|
||||||
|
``datatype == 'value'``).
|
||||||
|
|
||||||
|
Valid values for ``method`` are:
|
||||||
|
- ``ignore`` (always compare as equal);
|
||||||
|
- ``strict`` (only compare if really equal)
|
||||||
|
- ``allow_more_present`` (allow b to have elements which a does not have).
|
||||||
|
|
||||||
|
Valid values for ``datatype`` are:
|
||||||
|
- ``value``: for simple values (strings, numbers, ...);
|
||||||
|
- ``list``: for ``list``s or ``tuple``s where order matters;
|
||||||
|
- ``set``: for ``list``s, ``tuple``s or ``set``s where order does not
|
||||||
|
matter;
|
||||||
|
- ``set(dict)``: for ``list``s, ``tuple``s or ``sets`` where order does
|
||||||
|
not matter and which contain ``dict``s; ``allow_more_present`` is used
|
||||||
|
for the ``dict``s, and these are assumed to be dictionaries of values;
|
||||||
|
- ``dict``: for dictionaries of values.
|
||||||
|
'''
|
||||||
|
if method == 'ignore':
|
||||||
|
return True
|
||||||
|
# If a or b is None:
|
||||||
|
if a is None or b is None:
|
||||||
|
# If both are None: equality
|
||||||
|
if a == b:
|
||||||
|
return True
|
||||||
|
# Otherwise, not equal for values, and equal
|
||||||
|
# if the other is empty for set/list/dict
|
||||||
|
if datatype == 'value':
|
||||||
|
return False
|
||||||
|
# For allow_more_present, allow a to be None
|
||||||
|
if method == 'allow_more_present' and a is None:
|
||||||
|
return True
|
||||||
|
# Otherwise, the iterable object which is not None must have length 0
|
||||||
|
return len(b if a is None else a) == 0
|
||||||
|
# Do proper comparison (both objects not None)
|
||||||
|
if datatype == 'value':
|
||||||
|
return a == b
|
||||||
|
elif datatype == 'list':
|
||||||
|
if method == 'strict':
|
||||||
|
return a == b
|
||||||
|
else:
|
||||||
|
i = 0
|
||||||
|
for v in a:
|
||||||
|
while i < len(b) and b[i] != v:
|
||||||
|
i += 1
|
||||||
|
if i == len(b):
|
||||||
|
return False
|
||||||
|
i += 1
|
||||||
|
return True
|
||||||
|
elif datatype == 'dict':
|
||||||
|
if method == 'strict':
|
||||||
|
return a == b
|
||||||
|
else:
|
||||||
|
return compare_dict_allow_more_present(a, b)
|
||||||
|
elif datatype == 'set':
|
||||||
|
set_a = set(a)
|
||||||
|
set_b = set(b)
|
||||||
|
if method == 'strict':
|
||||||
|
return set_a == set_b
|
||||||
|
else:
|
||||||
|
return set_b >= set_a
|
||||||
|
elif datatype == 'set(dict)':
|
||||||
|
for av in a:
|
||||||
|
found = False
|
||||||
|
for bv in b:
|
||||||
|
if compare_dict_allow_more_present(av, bv):
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
if not found:
|
||||||
|
return False
|
||||||
|
if method == 'strict':
|
||||||
|
# If we would know that both a and b do not contain duplicates,
|
||||||
|
# we could simply compare len(a) to len(b) to finish this test.
|
||||||
|
# We can assume that b has no duplicates (as it is returned by
|
||||||
|
# docker), but we don't know for a.
|
||||||
|
for bv in b:
|
||||||
|
found = False
|
||||||
|
for av in a:
|
||||||
|
if compare_dict_allow_more_present(av, bv):
|
||||||
|
found = True
|
||||||
|
break
|
||||||
|
if not found:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class DifferenceTracker(object):
|
||||||
|
def __init__(self):
|
||||||
|
self._diff = []
|
||||||
|
|
||||||
|
def add(self, name, parameter=None, active=None):
|
||||||
|
self._diff.append(dict(
|
||||||
|
name=name,
|
||||||
|
parameter=parameter,
|
||||||
|
active=active,
|
||||||
|
))
|
||||||
|
|
||||||
|
def merge(self, other_tracker):
|
||||||
|
self._diff.extend(other_tracker._diff)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def empty(self):
|
||||||
|
return len(self._diff) == 0
|
||||||
|
|
||||||
|
def get_before_after(self):
|
||||||
|
'''
|
||||||
|
Return texts ``before`` and ``after``.
|
||||||
|
'''
|
||||||
|
before = dict()
|
||||||
|
after = dict()
|
||||||
|
for item in self._diff:
|
||||||
|
before[item['name']] = item['active']
|
||||||
|
after[item['name']] = item['parameter']
|
||||||
|
return before, after
|
||||||
|
|
||||||
|
def has_difference_for(self, name):
|
||||||
|
'''
|
||||||
|
Returns a boolean if a difference exists for name
|
||||||
|
'''
|
||||||
|
return any(diff for diff in self._diff if diff['name'] == name)
|
||||||
|
|
||||||
|
def get_legacy_docker_container_diffs(self):
|
||||||
|
'''
|
||||||
|
Return differences in the docker_container legacy format.
|
||||||
|
'''
|
||||||
|
result = []
|
||||||
|
for entry in self._diff:
|
||||||
|
item = dict()
|
||||||
|
item[entry['name']] = dict(
|
||||||
|
parameter=entry['parameter'],
|
||||||
|
container=entry['active'],
|
||||||
|
)
|
||||||
|
result.append(item)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_legacy_docker_diffs(self):
|
||||||
|
'''
|
||||||
|
Return differences in the docker_container legacy format.
|
||||||
|
'''
|
||||||
|
result = [entry['name'] for entry in self._diff]
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def clean_dict_booleans_for_docker_api(data, allow_sequences=False):
|
||||||
|
'''
|
||||||
|
Go doesn't like Python booleans 'True' or 'False', while Ansible is just
|
||||||
|
fine with them in YAML. As such, they need to be converted in cases where
|
||||||
|
we pass dictionaries to the Docker API (e.g. docker_network's
|
||||||
|
driver_options and docker_prune's filters). When `allow_sequences=True`
|
||||||
|
YAML sequences (lists, tuples) are converted to [str] instead of str([...])
|
||||||
|
which is the expected format of filters which accept lists such as labels.
|
||||||
|
'''
|
||||||
|
def sanitize(value):
|
||||||
|
if value is True:
|
||||||
|
return 'true'
|
||||||
|
elif value is False:
|
||||||
|
return 'false'
|
||||||
|
else:
|
||||||
|
return str(value)
|
||||||
|
|
||||||
|
result = dict()
|
||||||
|
if data is not None:
|
||||||
|
for k, v in data.items():
|
||||||
|
result[str(k)] = [sanitize(e) for e in v] if allow_sequences and is_sequence(v) else sanitize(v)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def convert_duration_to_nanosecond(time_str):
|
||||||
|
"""
|
||||||
|
Return time duration in nanosecond.
|
||||||
|
"""
|
||||||
|
if not isinstance(time_str, str):
|
||||||
|
raise ValueError('Missing unit in duration - %s' % time_str)
|
||||||
|
|
||||||
|
regex = re.compile(
|
||||||
|
r'^(((?P<hours>\d+)h)?'
|
||||||
|
r'((?P<minutes>\d+)m(?!s))?'
|
||||||
|
r'((?P<seconds>\d+)s)?'
|
||||||
|
r'((?P<milliseconds>\d+)ms)?'
|
||||||
|
r'((?P<microseconds>\d+)us)?)$'
|
||||||
|
)
|
||||||
|
parts = regex.match(time_str)
|
||||||
|
|
||||||
|
if not parts:
|
||||||
|
raise ValueError('Invalid time duration - %s' % time_str)
|
||||||
|
|
||||||
|
parts = parts.groupdict()
|
||||||
|
time_params = {}
|
||||||
|
for (name, value) in parts.items():
|
||||||
|
if value:
|
||||||
|
time_params[name] = int(value)
|
||||||
|
|
||||||
|
delta = timedelta(**time_params)
|
||||||
|
time_in_nanoseconds = (
|
||||||
|
delta.microseconds + (delta.seconds + delta.days * 24 * 3600) * 10 ** 6
|
||||||
|
) * 10 ** 3
|
||||||
|
|
||||||
|
return time_in_nanoseconds
|
||||||
|
|
||||||
|
|
||||||
|
def parse_healthcheck(healthcheck):
|
||||||
|
"""
|
||||||
|
Return dictionary of healthcheck parameters and boolean if
|
||||||
|
healthcheck defined in image was requested to be disabled.
|
||||||
|
"""
|
||||||
|
if (not healthcheck) or (not healthcheck.get('test')):
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
result = dict()
|
||||||
|
|
||||||
|
# All supported healthcheck parameters
|
||||||
|
options = dict(
|
||||||
|
test='test',
|
||||||
|
interval='interval',
|
||||||
|
timeout='timeout',
|
||||||
|
start_period='start_period',
|
||||||
|
retries='retries'
|
||||||
|
)
|
||||||
|
|
||||||
|
duration_options = ['interval', 'timeout', 'start_period']
|
||||||
|
|
||||||
|
for (key, value) in options.items():
|
||||||
|
if value in healthcheck:
|
||||||
|
if healthcheck.get(value) is None:
|
||||||
|
# due to recursive argument_spec, all keys are always present
|
||||||
|
# (but have default value None if not specified)
|
||||||
|
continue
|
||||||
|
if value in duration_options:
|
||||||
|
time = convert_duration_to_nanosecond(healthcheck.get(value))
|
||||||
|
if time:
|
||||||
|
result[key] = time
|
||||||
|
elif healthcheck.get(value):
|
||||||
|
result[key] = healthcheck.get(value)
|
||||||
|
if key == 'test':
|
||||||
|
if isinstance(result[key], (tuple, list)):
|
||||||
|
result[key] = [str(e) for e in result[key]]
|
||||||
|
else:
|
||||||
|
result[key] = ['CMD-SHELL', str(result[key])]
|
||||||
|
elif key == 'retries':
|
||||||
|
try:
|
||||||
|
result[key] = int(result[key])
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError(
|
||||||
|
'Cannot parse number of retries for healthcheck. '
|
||||||
|
'Expected an integer, got "{0}".'.format(result[key])
|
||||||
|
)
|
||||||
|
|
||||||
|
if result['test'] == ['NONE']:
|
||||||
|
# If the user explicitly disables the healthcheck, return None
|
||||||
|
# as the healthcheck object, and set disable_healthcheck to True
|
||||||
|
return None, True
|
||||||
|
|
||||||
|
return result, False
|
||||||
|
|
||||||
|
|
||||||
|
def omit_none_from_dict(d):
|
||||||
|
"""
|
||||||
|
Return a copy of the dictionary with all keys with value None omitted.
|
||||||
|
"""
|
||||||
|
return dict((k, v) for (k, v) in d.items() if v is not None)
|
||||||
@ -510,10 +510,13 @@ from ansible.module_utils.common.text.converters import to_native
|
|||||||
|
|
||||||
from ansible_collections.community.docker.plugins.module_utils.common import (
|
from ansible_collections.community.docker.plugins.module_utils.common import (
|
||||||
AnsibleDockerClient,
|
AnsibleDockerClient,
|
||||||
DockerBaseClass,
|
|
||||||
RequestException,
|
RequestException,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from ansible_collections.community.docker.plugins.module_utils.util import (
|
||||||
|
DockerBaseClass,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
AUTH_PARAM_MAPPING = {
|
AUTH_PARAM_MAPPING = {
|
||||||
u'docker_host': u'--host',
|
u'docker_host': u'--host',
|
||||||
|
|||||||
@ -199,9 +199,11 @@ except ImportError:
|
|||||||
|
|
||||||
from ansible_collections.community.docker.plugins.module_utils.common import (
|
from ansible_collections.community.docker.plugins.module_utils.common import (
|
||||||
AnsibleDockerClient,
|
AnsibleDockerClient,
|
||||||
|
RequestException,
|
||||||
|
)
|
||||||
|
from ansible_collections.community.docker.plugins.module_utils.util import (
|
||||||
DockerBaseClass,
|
DockerBaseClass,
|
||||||
compare_generic,
|
compare_generic,
|
||||||
RequestException,
|
|
||||||
)
|
)
|
||||||
from ansible.module_utils.common.text.converters import to_native, to_bytes
|
from ansible.module_utils.common.text.converters import to_native, to_bytes
|
||||||
|
|
||||||
|
|||||||
@ -1217,6 +1217,9 @@ from ansible_collections.community.docker.plugins.module_utils.version import Lo
|
|||||||
|
|
||||||
from ansible_collections.community.docker.plugins.module_utils.common import (
|
from ansible_collections.community.docker.plugins.module_utils.common import (
|
||||||
AnsibleDockerClient,
|
AnsibleDockerClient,
|
||||||
|
RequestException,
|
||||||
|
)
|
||||||
|
from ansible_collections.community.docker.plugins.module_utils.util import (
|
||||||
DifferenceTracker,
|
DifferenceTracker,
|
||||||
DockerBaseClass,
|
DockerBaseClass,
|
||||||
compare_generic,
|
compare_generic,
|
||||||
@ -1226,7 +1229,6 @@ from ansible_collections.community.docker.plugins.module_utils.common import (
|
|||||||
omit_none_from_dict,
|
omit_none_from_dict,
|
||||||
parse_healthcheck,
|
parse_healthcheck,
|
||||||
DOCKER_COMMON_ARGS,
|
DOCKER_COMMON_ARGS,
|
||||||
RequestException,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -203,12 +203,12 @@ disk_usage:
|
|||||||
|
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
|
from ansible.module_utils.common.text.converters import to_native
|
||||||
|
|
||||||
from ansible_collections.community.docker.plugins.module_utils.common import (
|
from ansible_collections.community.docker.plugins.module_utils.common import (
|
||||||
AnsibleDockerClient,
|
AnsibleDockerClient,
|
||||||
DockerBaseClass,
|
|
||||||
RequestException,
|
RequestException,
|
||||||
)
|
)
|
||||||
from ansible.module_utils.common.text.converters import to_native
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from docker.errors import DockerException, APIError
|
from docker.errors import DockerException, APIError
|
||||||
@ -216,7 +216,10 @@ except ImportError:
|
|||||||
# Missing Docker SDK for Python handled in ansible.module_utils.docker.common
|
# Missing Docker SDK for Python handled in ansible.module_utils.docker.common
|
||||||
pass
|
pass
|
||||||
|
|
||||||
from ansible_collections.community.docker.plugins.module_utils.common import clean_dict_booleans_for_docker_api
|
from ansible_collections.community.docker.plugins.module_utils.util import (
|
||||||
|
DockerBaseClass,
|
||||||
|
clean_dict_booleans_for_docker_api,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DockerHostManager(DockerBaseClass):
|
class DockerHostManager(DockerBaseClass):
|
||||||
|
|||||||
@ -329,13 +329,15 @@ import os
|
|||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from ansible_collections.community.docker.plugins.module_utils.common import (
|
from ansible_collections.community.docker.plugins.module_utils.common import (
|
||||||
clean_dict_booleans_for_docker_api,
|
|
||||||
docker_version,
|
|
||||||
AnsibleDockerClient,
|
AnsibleDockerClient,
|
||||||
|
RequestException,
|
||||||
|
docker_version,
|
||||||
|
)
|
||||||
|
from ansible_collections.community.docker.plugins.module_utils.util import (
|
||||||
|
clean_dict_booleans_for_docker_api,
|
||||||
DockerBaseClass,
|
DockerBaseClass,
|
||||||
is_image_name_id,
|
is_image_name_id,
|
||||||
is_valid_tag,
|
is_valid_tag,
|
||||||
RequestException,
|
|
||||||
)
|
)
|
||||||
from ansible.module_utils.common.text.converters import to_native
|
from ansible.module_utils.common.text.converters import to_native
|
||||||
|
|
||||||
|
|||||||
@ -176,9 +176,11 @@ except ImportError:
|
|||||||
|
|
||||||
from ansible_collections.community.docker.plugins.module_utils.common import (
|
from ansible_collections.community.docker.plugins.module_utils.common import (
|
||||||
AnsibleDockerClient,
|
AnsibleDockerClient,
|
||||||
|
RequestException,
|
||||||
|
)
|
||||||
|
from ansible_collections.community.docker.plugins.module_utils.util import (
|
||||||
DockerBaseClass,
|
DockerBaseClass,
|
||||||
is_image_name_id,
|
is_image_name_id,
|
||||||
RequestException,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -77,9 +77,11 @@ from ansible.module_utils.common.text.converters import to_native
|
|||||||
|
|
||||||
from ansible_collections.community.docker.plugins.module_utils.common import (
|
from ansible_collections.community.docker.plugins.module_utils.common import (
|
||||||
AnsibleDockerClient,
|
AnsibleDockerClient,
|
||||||
|
RequestException,
|
||||||
|
)
|
||||||
|
from ansible_collections.community.docker.plugins.module_utils.util import (
|
||||||
DockerBaseClass,
|
DockerBaseClass,
|
||||||
is_image_name_id,
|
is_image_name_id,
|
||||||
RequestException,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -138,11 +138,13 @@ except ImportError:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
from ansible_collections.community.docker.plugins.module_utils.common import (
|
from ansible_collections.community.docker.plugins.module_utils.common import (
|
||||||
AnsibleDockerClient,
|
|
||||||
HAS_DOCKER_PY,
|
HAS_DOCKER_PY,
|
||||||
|
AnsibleDockerClient,
|
||||||
|
RequestException,
|
||||||
|
)
|
||||||
|
from ansible_collections.community.docker.plugins.module_utils.util import (
|
||||||
DEFAULT_DOCKER_REGISTRY,
|
DEFAULT_DOCKER_REGISTRY,
|
||||||
DockerBaseClass,
|
DockerBaseClass,
|
||||||
RequestException,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
NEEDS_DOCKER_PYCREDS = False
|
NEEDS_DOCKER_PYCREDS = False
|
||||||
|
|||||||
@ -258,11 +258,13 @@ from ansible_collections.community.docker.plugins.module_utils.version import Lo
|
|||||||
|
|
||||||
from ansible_collections.community.docker.plugins.module_utils.common import (
|
from ansible_collections.community.docker.plugins.module_utils.common import (
|
||||||
AnsibleDockerClient,
|
AnsibleDockerClient,
|
||||||
DockerBaseClass,
|
RequestException,
|
||||||
docker_version,
|
docker_version,
|
||||||
|
)
|
||||||
|
from ansible_collections.community.docker.plugins.module_utils.util import (
|
||||||
|
DockerBaseClass,
|
||||||
DifferenceTracker,
|
DifferenceTracker,
|
||||||
clean_dict_booleans_for_docker_api,
|
clean_dict_booleans_for_docker_api,
|
||||||
RequestException,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -129,11 +129,13 @@ except ImportError:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
from ansible_collections.community.docker.plugins.module_utils.common import (
|
from ansible_collections.community.docker.plugins.module_utils.common import (
|
||||||
DockerBaseClass,
|
|
||||||
AnsibleDockerClient,
|
AnsibleDockerClient,
|
||||||
DifferenceTracker,
|
|
||||||
RequestException
|
RequestException
|
||||||
)
|
)
|
||||||
|
from ansible_collections.community.docker.plugins.module_utils.util import (
|
||||||
|
DockerBaseClass,
|
||||||
|
DifferenceTracker,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TaskParameters(DockerBaseClass):
|
class TaskParameters(DockerBaseClass):
|
||||||
|
|||||||
@ -191,9 +191,11 @@ except ImportError:
|
|||||||
|
|
||||||
from ansible_collections.community.docker.plugins.module_utils.common import (
|
from ansible_collections.community.docker.plugins.module_utils.common import (
|
||||||
AnsibleDockerClient,
|
AnsibleDockerClient,
|
||||||
|
RequestException,
|
||||||
|
)
|
||||||
|
from ansible_collections.community.docker.plugins.module_utils.util import (
|
||||||
DockerBaseClass,
|
DockerBaseClass,
|
||||||
compare_generic,
|
compare_generic,
|
||||||
RequestException,
|
|
||||||
)
|
)
|
||||||
from ansible.module_utils.common.text.converters import to_native, to_bytes
|
from ansible.module_utils.common.text.converters import to_native, to_bytes
|
||||||
|
|
||||||
|
|||||||
@ -291,9 +291,11 @@ except ImportError:
|
|||||||
|
|
||||||
from ansible_collections.community.docker.plugins.module_utils.common import (
|
from ansible_collections.community.docker.plugins.module_utils.common import (
|
||||||
DockerBaseClass,
|
DockerBaseClass,
|
||||||
DifferenceTracker,
|
|
||||||
RequestException,
|
RequestException,
|
||||||
)
|
)
|
||||||
|
from ansible_collections.community.docker.plugins.module_utils.util import (
|
||||||
|
DifferenceTracker,
|
||||||
|
)
|
||||||
|
|
||||||
from ansible_collections.community.docker.plugins.module_utils.swarm import AnsibleDockerSwarmClient
|
from ansible_collections.community.docker.plugins.module_utils.swarm import AnsibleDockerSwarmClient
|
||||||
|
|
||||||
|
|||||||
@ -202,10 +202,10 @@ except ImportError:
|
|||||||
from ansible.module_utils.common.text.converters import to_native
|
from ansible.module_utils.common.text.converters import to_native
|
||||||
|
|
||||||
from ansible_collections.community.docker.plugins.module_utils.swarm import AnsibleDockerSwarmClient
|
from ansible_collections.community.docker.plugins.module_utils.swarm import AnsibleDockerSwarmClient
|
||||||
from ansible_collections.community.docker.plugins.module_utils.common import (
|
from ansible_collections.community.docker.plugins.module_utils.common import RequestException
|
||||||
|
from ansible_collections.community.docker.plugins.module_utils.util import (
|
||||||
DockerBaseClass,
|
DockerBaseClass,
|
||||||
clean_dict_booleans_for_docker_api,
|
clean_dict_booleans_for_docker_api,
|
||||||
RequestException,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -941,12 +941,14 @@ from ansible_collections.community.docker.plugins.module_utils.version import Lo
|
|||||||
|
|
||||||
from ansible_collections.community.docker.plugins.module_utils.common import (
|
from ansible_collections.community.docker.plugins.module_utils.common import (
|
||||||
AnsibleDockerClient,
|
AnsibleDockerClient,
|
||||||
|
RequestException,
|
||||||
|
)
|
||||||
|
from ansible_collections.community.docker.plugins.module_utils.util import (
|
||||||
DifferenceTracker,
|
DifferenceTracker,
|
||||||
DockerBaseClass,
|
DockerBaseClass,
|
||||||
convert_duration_to_nanosecond,
|
convert_duration_to_nanosecond,
|
||||||
parse_healthcheck,
|
parse_healthcheck,
|
||||||
clean_dict_booleans_for_docker_api,
|
clean_dict_booleans_for_docker_api,
|
||||||
RequestException,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
from ansible.module_utils.basic import human_to_bytes
|
from ansible.module_utils.basic import human_to_bytes
|
||||||
|
|||||||
@ -118,11 +118,13 @@ except ImportError:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
from ansible_collections.community.docker.plugins.module_utils.common import (
|
from ansible_collections.community.docker.plugins.module_utils.common import (
|
||||||
DockerBaseClass,
|
|
||||||
AnsibleDockerClient,
|
AnsibleDockerClient,
|
||||||
DifferenceTracker,
|
|
||||||
RequestException,
|
RequestException,
|
||||||
)
|
)
|
||||||
|
from ansible_collections.community.docker.plugins.module_utils.util import (
|
||||||
|
DockerBaseClass,
|
||||||
|
DifferenceTracker,
|
||||||
|
)
|
||||||
from ansible.module_utils.six import iteritems, text_type
|
from ansible.module_utils.six import iteritems, text_type
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,9 @@ from ansible.utils.display import Display
|
|||||||
|
|
||||||
from ansible_collections.community.docker.plugins.module_utils.common import (
|
from ansible_collections.community.docker.plugins.module_utils.common import (
|
||||||
AnsibleDockerClientBase,
|
AnsibleDockerClientBase,
|
||||||
|
)
|
||||||
|
|
||||||
|
from ansible_collections.community.docker.plugins.module_utils.util import (
|
||||||
DOCKER_COMMON_ARGS,
|
DOCKER_COMMON_ARGS,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@ __metaclass__ = type
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from ansible_collections.community.docker.plugins.module_utils.common import (
|
from ansible_collections.community.docker.plugins.module_utils.util import (
|
||||||
compare_dict_allow_more_present,
|
compare_dict_allow_more_present,
|
||||||
compare_generic,
|
compare_generic,
|
||||||
convert_duration_to_nanosecond,
|
convert_duration_to_nanosecond,
|
||||||
Loading…
Reference in New Issue
Block a user