docker_* modules: improve tag validation and requests error handling (#58791)
* Add method to validate docker tags. * Validate tag option of docker_image. * Fix regex. Always return boolean, not None vs. Matcher object. * Also catch requests errors. * Linting. * Add changelog.
This commit is contained in:
parent
fa7c387f9b
commit
8d6f1846a6
22 changed files with 150 additions and 23 deletions
3
changelogs/fragments/58791-docker-errors.yml
Normal file
3
changelogs/fragments/58791-docker-errors.yml
Normal file
|
@ -0,0 +1,3 @@
|
|||
bugfixes:
|
||||
- "docker_image - validate ``tag`` option value."
|
||||
- "docker_* modules - behave better when requests errors are not caught by docker-py."
|
|
@ -74,6 +74,16 @@ except ImportError:
|
|||
HAS_DOCKER_SSLADAPTER = False
|
||||
|
||||
|
||||
try:
|
||||
from request.exceptions import RequestException
|
||||
except ImportError:
|
||||
# Either docker-py is no longer using requests, or docker-py isn't around either,
|
||||
# or docker-py's dependency requests is missing. In any case, define an exception
|
||||
# class RequestException so that our code doesn't break.
|
||||
class RequestException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
DEFAULT_DOCKER_HOST = 'unix://var/run/docker.sock'
|
||||
DEFAULT_TLS = False
|
||||
DEFAULT_TLS_VERIFY = False
|
||||
|
@ -123,12 +133,21 @@ if not HAS_DOCKER_PY:
|
|||
|
||||
|
||||
def is_image_name_id(name):
|
||||
"""Checks whether the given image name is in fact an image ID (hash)."""
|
||||
"""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.
|
||||
|
||||
|
|
|
@ -451,7 +451,7 @@ try:
|
|||
import yaml
|
||||
HAS_YAML = True
|
||||
HAS_YAML_EXC = None
|
||||
except ImportError as exc:
|
||||
except ImportError as dummy:
|
||||
HAS_YAML = False
|
||||
HAS_YAML_EXC = traceback.format_exc()
|
||||
|
||||
|
@ -470,12 +470,16 @@ try:
|
|||
HAS_COMPOSE = True
|
||||
HAS_COMPOSE_EXC = None
|
||||
MINIMUM_COMPOSE_VERSION = '1.7.0'
|
||||
except ImportError as exc:
|
||||
except ImportError as dummy:
|
||||
HAS_COMPOSE = False
|
||||
HAS_COMPOSE_EXC = traceback.format_exc()
|
||||
DEFAULT_TIMEOUT = 10
|
||||
|
||||
from ansible.module_utils.docker.common import AnsibleDockerClient, DockerBaseClass
|
||||
from ansible.module_utils.docker.common import (
|
||||
AnsibleDockerClient,
|
||||
DockerBaseClass,
|
||||
RequestException,
|
||||
)
|
||||
|
||||
|
||||
AUTH_PARAM_MAPPING = {
|
||||
|
@ -1101,6 +1105,8 @@ def main():
|
|||
client.module.exit_json(**result)
|
||||
except DockerException as e:
|
||||
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
|
||||
except RequestException as e:
|
||||
client.fail('An unexpected requests error occurred when docker-py tried to talk to the docker daemon: {0}'.format(e), exception=traceback.format_exc())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -160,7 +160,12 @@ except ImportError:
|
|||
# missing Docker SDK for Python handled in ansible.module_utils.docker.common
|
||||
pass
|
||||
|
||||
from ansible.module_utils.docker.common import AnsibleDockerClient, DockerBaseClass, compare_generic
|
||||
from ansible.module_utils.docker.common import (
|
||||
AnsibleDockerClient,
|
||||
DockerBaseClass,
|
||||
compare_generic,
|
||||
RequestException,
|
||||
)
|
||||
from ansible.module_utils._text import to_native, to_bytes
|
||||
|
||||
|
||||
|
@ -291,6 +296,8 @@ def main():
|
|||
client.module.exit_json(**results)
|
||||
except DockerException as e:
|
||||
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
|
||||
except RequestException as e:
|
||||
client.fail('An unexpected requests error occurred when docker-py tried to talk to the docker daemon: {0}'.format(e), exception=traceback.format_exc())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -955,6 +955,7 @@ from ansible.module_utils.docker.common import (
|
|||
sanitize_result,
|
||||
parse_healthcheck,
|
||||
DOCKER_COMMON_ARGS,
|
||||
RequestException,
|
||||
)
|
||||
from ansible.module_utils.six import string_types
|
||||
|
||||
|
@ -1419,7 +1420,7 @@ class TaskParameters(DockerBaseClass):
|
|||
break
|
||||
except NotFound as e:
|
||||
self.client.fail(
|
||||
"Cannot inspect the network '{0}' to determine the default IP.".format(net['name']),
|
||||
"Cannot inspect the network '{0}' to determine the default IP: {1}".format(net['name'], e),
|
||||
exception=traceback.format_exc()
|
||||
)
|
||||
return ip
|
||||
|
@ -2651,9 +2652,9 @@ class ContainerManager(DockerBaseClass):
|
|||
if not self.check_mode:
|
||||
try:
|
||||
if self.parameters.stop_timeout:
|
||||
response = self.client.restart(container_id, timeout=self.parameters.stop_timeout)
|
||||
dummy = self.client.restart(container_id, timeout=self.parameters.stop_timeout)
|
||||
else:
|
||||
response = self.client.restart(container_id)
|
||||
dummy = self.client.restart(container_id)
|
||||
except Exception as exc:
|
||||
self.fail("Error restarting container %s: %s" % (container_id, str(exc)))
|
||||
return self._get_container(container_id)
|
||||
|
@ -3029,6 +3030,8 @@ def main():
|
|||
client.module.exit_json(**sanitize_result(cm.results))
|
||||
except DockerException as e:
|
||||
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
|
||||
except RequestException as e:
|
||||
client.fail('An unexpected requests error occurred when docker-py tried to talk to the docker daemon: {0}'.format(e), exception=traceback.format_exc())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -115,7 +115,10 @@ except ImportError:
|
|||
# missing Docker SDK for Python handled in ansible.module_utils.docker.common
|
||||
pass
|
||||
|
||||
from ansible.module_utils.docker.common import AnsibleDockerClient
|
||||
from ansible.module_utils.docker.common import (
|
||||
AnsibleDockerClient,
|
||||
RequestException,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
|
@ -139,6 +142,8 @@ def main():
|
|||
)
|
||||
except DockerException as e:
|
||||
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
|
||||
except RequestException as e:
|
||||
client.fail('An unexpected requests error occurred when docker-py tried to talk to the docker daemon: {0}'.format(e), exception=traceback.format_exc())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -187,7 +187,11 @@ disk_usage:
|
|||
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.docker.common import AnsibleDockerClient, DockerBaseClass
|
||||
from ansible.module_utils.docker.common import (
|
||||
AnsibleDockerClient,
|
||||
DockerBaseClass,
|
||||
RequestException,
|
||||
)
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
try:
|
||||
|
@ -332,6 +336,8 @@ def main():
|
|||
client.module.exit_json(**results)
|
||||
except DockerException as e:
|
||||
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
|
||||
except RequestException as e:
|
||||
client.fail('An unexpected requests error occurred when docker-py tried to talk to the docker daemon: {0}'.format(e), exception=traceback.format_exc())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -424,7 +424,12 @@ import traceback
|
|||
from distutils.version import LooseVersion
|
||||
|
||||
from ansible.module_utils.docker.common import (
|
||||
docker_version, AnsibleDockerClient, DockerBaseClass, is_image_name_id,
|
||||
docker_version,
|
||||
AnsibleDockerClient,
|
||||
DockerBaseClass,
|
||||
is_image_name_id,
|
||||
is_valid_tag,
|
||||
RequestException,
|
||||
)
|
||||
from ansible.module_utils._text import to_native
|
||||
|
||||
|
@ -879,6 +884,9 @@ def main():
|
|||
'and will be removed in Ansible 2.11. Please use the'
|
||||
'"tls" and "tls_verify" options instead.')
|
||||
|
||||
if not is_valid_tag(client.module.params['tag'], allow_empty=True):
|
||||
client.fail('"{0}" is not a valid docker tag!'.format(client.module.params['tag']))
|
||||
|
||||
build_options = dict(
|
||||
container_limits='container_limits',
|
||||
dockerfile='dockerfile',
|
||||
|
@ -942,6 +950,8 @@ def main():
|
|||
client.module.exit_json(**results)
|
||||
except DockerException as e:
|
||||
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
|
||||
except RequestException as e:
|
||||
client.fail('An unexpected requests error occurred when docker-py tried to talk to the docker daemon: {0}'.format(e), exception=traceback.format_exc())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -163,7 +163,12 @@ except ImportError:
|
|||
# missing Docker SDK for Python handled in ansible.module_utils.docker.common
|
||||
pass
|
||||
|
||||
from ansible.module_utils.docker.common import AnsibleDockerClient, DockerBaseClass, is_image_name_id
|
||||
from ansible.module_utils.docker.common import (
|
||||
AnsibleDockerClient,
|
||||
DockerBaseClass,
|
||||
is_image_name_id,
|
||||
RequestException,
|
||||
)
|
||||
|
||||
|
||||
class ImageManager(DockerBaseClass):
|
||||
|
@ -247,6 +252,8 @@ def main():
|
|||
client.module.exit_json(**results)
|
||||
except DockerException as e:
|
||||
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
|
||||
except RequestException as e:
|
||||
client.fail('An unexpected requests error occurred when docker-py tried to talk to the docker daemon: {0}'.format(e), exception=traceback.format_exc())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -138,7 +138,13 @@ except ImportError:
|
|||
pass
|
||||
|
||||
from ansible.module_utils._text import to_bytes, to_text
|
||||
from ansible.module_utils.docker.common import AnsibleDockerClient, DEFAULT_DOCKER_REGISTRY, DockerBaseClass, EMAIL_REGEX
|
||||
from ansible.module_utils.docker.common import (
|
||||
AnsibleDockerClient,
|
||||
DEFAULT_DOCKER_REGISTRY,
|
||||
DockerBaseClass,
|
||||
EMAIL_REGEX,
|
||||
RequestException,
|
||||
)
|
||||
|
||||
|
||||
class LoginManager(DockerBaseClass):
|
||||
|
@ -331,6 +337,8 @@ def main():
|
|||
client.module.exit_json(**results)
|
||||
except DockerException as e:
|
||||
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
|
||||
except RequestException as e:
|
||||
client.fail('An unexpected requests error occurred when docker-py tried to talk to the docker daemon: {0}'.format(e), exception=traceback.format_exc())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -286,6 +286,7 @@ from ansible.module_utils.docker.common import (
|
|||
docker_version,
|
||||
DifferenceTracker,
|
||||
clean_dict_booleans_for_docker_api,
|
||||
RequestException,
|
||||
)
|
||||
|
||||
try:
|
||||
|
@ -688,6 +689,8 @@ def main():
|
|||
client.module.exit_json(**cm.results)
|
||||
except DockerException as e:
|
||||
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
|
||||
except RequestException as e:
|
||||
client.fail('An unexpected requests error occurred when docker-py tried to talk to the docker daemon: {0}'.format(e), exception=traceback.format_exc())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -111,7 +111,10 @@ except ImportError:
|
|||
# missing Docker SDK for Python handled in ansible.module_utils.docker.common
|
||||
pass
|
||||
|
||||
from ansible.module_utils.docker.common import AnsibleDockerClient
|
||||
from ansible.module_utils.docker.common import (
|
||||
AnsibleDockerClient,
|
||||
RequestException,
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
|
@ -135,6 +138,8 @@ def main():
|
|||
)
|
||||
except DockerException as e:
|
||||
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
|
||||
except RequestException as e:
|
||||
client.fail('An unexpected requests error occurred when docker-py tried to talk to the docker daemon: {0}'.format(e), exception=traceback.format_exc())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -139,6 +139,7 @@ except ImportError:
|
|||
|
||||
from ansible.module_utils.docker.common import (
|
||||
DockerBaseClass,
|
||||
RequestException,
|
||||
)
|
||||
|
||||
from ansible.module_utils._text import to_native
|
||||
|
@ -288,6 +289,8 @@ def main():
|
|||
client.module.exit_json(**results)
|
||||
except DockerException as e:
|
||||
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
|
||||
except RequestException as e:
|
||||
client.fail('An unexpected requests error occurred when docker-py tried to talk to the docker daemon: {0}'.format(e), exception=traceback.format_exc())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -89,10 +89,13 @@ nodes:
|
|||
|
||||
import traceback
|
||||
|
||||
from ansible.module_utils.docker.common import (
|
||||
RequestException,
|
||||
)
|
||||
from ansible.module_utils.docker.swarm import AnsibleDockerSwarmClient
|
||||
|
||||
try:
|
||||
from docker.errors import DockerException, APIError, NotFound
|
||||
from docker.errors import DockerException
|
||||
except ImportError:
|
||||
# missing Docker SDK for Python handled in ansible.module_utils.docker.common
|
||||
pass
|
||||
|
@ -147,6 +150,8 @@ def main():
|
|||
)
|
||||
except DockerException as e:
|
||||
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
|
||||
except RequestException as e:
|
||||
client.fail('An unexpected requests error occurred when docker-py tried to talk to the docker daemon: {0}'.format(e), exception=traceback.format_exc())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -187,7 +187,10 @@ except ImportError:
|
|||
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
from ansible.module_utils.docker.common import AnsibleDockerClient
|
||||
from ansible.module_utils.docker.common import (
|
||||
AnsibleDockerClient,
|
||||
RequestException,
|
||||
)
|
||||
|
||||
try:
|
||||
from ansible.module_utils.docker.common import docker_version, clean_dict_booleans_for_docker_api
|
||||
|
@ -255,6 +258,8 @@ def main():
|
|||
client.module.exit_json(**result)
|
||||
except DockerException as e:
|
||||
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
|
||||
except RequestException as e:
|
||||
client.fail('An unexpected requests error occurred when docker-py tried to talk to the docker daemon: {0}'.format(e), exception=traceback.format_exc())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -160,7 +160,12 @@ except ImportError:
|
|||
# missing Docker SDK for Python handled in ansible.module_utils.docker.common
|
||||
pass
|
||||
|
||||
from ansible.module_utils.docker.common import AnsibleDockerClient, DockerBaseClass, compare_generic
|
||||
from ansible.module_utils.docker.common import (
|
||||
AnsibleDockerClient,
|
||||
DockerBaseClass,
|
||||
compare_generic,
|
||||
RequestException,
|
||||
)
|
||||
from ansible.module_utils._text import to_native, to_bytes
|
||||
|
||||
|
||||
|
@ -292,6 +297,8 @@ def main():
|
|||
client.module.exit_json(**results)
|
||||
except DockerException as e:
|
||||
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
|
||||
except RequestException as e:
|
||||
client.fail('An unexpected requests error occurred when docker-py tried to talk to the docker daemon: {0}'.format(e), exception=traceback.format_exc())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -278,7 +278,7 @@ except ImportError:
|
|||
from ansible.module_utils.docker.common import (
|
||||
DockerBaseClass,
|
||||
DifferenceTracker,
|
||||
LooseVersion,
|
||||
RequestException,
|
||||
)
|
||||
|
||||
from ansible.module_utils.docker.swarm import AnsibleDockerSwarmClient
|
||||
|
@ -671,6 +671,8 @@ def main():
|
|||
client.module.exit_json(**results)
|
||||
except DockerException as e:
|
||||
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
|
||||
except RequestException as e:
|
||||
client.fail('An unexpected requests error occurred when docker-py tried to talk to the docker daemon: {0}'.format(e), exception=traceback.format_exc())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -195,7 +195,7 @@ tasks:
|
|||
import traceback
|
||||
|
||||
try:
|
||||
from docker.errors import DockerException, APIError, NotFound
|
||||
from docker.errors import DockerException, APIError
|
||||
except ImportError:
|
||||
# missing Docker SDK for Python handled in ansible.module_utils.docker_common
|
||||
pass
|
||||
|
@ -203,7 +203,11 @@ except ImportError:
|
|||
from ansible.module_utils._text import to_native
|
||||
|
||||
from ansible.module_utils.docker.swarm import AnsibleDockerSwarmClient
|
||||
from ansible.module_utils.docker.common import DockerBaseClass, clean_dict_booleans_for_docker_api
|
||||
from ansible.module_utils.docker.common import (
|
||||
DockerBaseClass,
|
||||
clean_dict_booleans_for_docker_api,
|
||||
RequestException,
|
||||
)
|
||||
|
||||
|
||||
class DockerSwarmManager(DockerBaseClass):
|
||||
|
@ -373,6 +377,8 @@ def main():
|
|||
client.module.exit_json(**results)
|
||||
except DockerException as e:
|
||||
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
|
||||
except RequestException as e:
|
||||
client.fail('An unexpected requests error occurred when docker-py tried to talk to the docker daemon: {0}'.format(e), exception=traceback.format_exc())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -1051,9 +1051,10 @@ from ansible.module_utils.docker.common import (
|
|||
DifferenceTracker,
|
||||
DockerBaseClass,
|
||||
convert_duration_to_nanosecond,
|
||||
parse_healthcheck
|
||||
|
||||
parse_healthcheck,
|
||||
RequestException,
|
||||
)
|
||||
|
||||
from ansible.module_utils.basic import human_to_bytes
|
||||
from ansible.module_utils.six import string_types
|
||||
from ansible.module_utils._text import to_text
|
||||
|
@ -2813,6 +2814,8 @@ def main():
|
|||
client.module.exit_json(**results)
|
||||
except DockerException as e:
|
||||
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
|
||||
except RequestException as e:
|
||||
client.fail('An unexpected requests error occurred when docker-py tried to talk to the docker daemon: {0}'.format(e), exception=traceback.format_exc())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -74,6 +74,10 @@ except ImportError:
|
|||
# missing Docker SDK for Python handled in ansible.module_utils.docker.common
|
||||
pass
|
||||
|
||||
from ansible.module_utils.docker.common import (
|
||||
RequestException,
|
||||
)
|
||||
|
||||
from ansible.module_utils.docker.swarm import AnsibleDockerSwarmClient
|
||||
|
||||
|
||||
|
@ -109,6 +113,8 @@ def main():
|
|||
)
|
||||
except DockerException as e:
|
||||
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
|
||||
except RequestException as e:
|
||||
client.fail('An unexpected requests error occurred when docker-py tried to talk to the docker daemon: {0}'.format(e), exception=traceback.format_exc())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -137,6 +137,7 @@ from ansible.module_utils.docker.common import (
|
|||
DockerBaseClass,
|
||||
AnsibleDockerClient,
|
||||
DifferenceTracker,
|
||||
RequestException,
|
||||
)
|
||||
from ansible.module_utils.six import iteritems, text_type
|
||||
|
||||
|
@ -330,6 +331,8 @@ def main():
|
|||
client.module.exit_json(**cm.results)
|
||||
except DockerException as e:
|
||||
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
|
||||
except RequestException as e:
|
||||
client.fail('An unexpected requests error occurred when docker-py tried to talk to the docker daemon: {0}'.format(e), exception=traceback.format_exc())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -88,7 +88,10 @@ except ImportError:
|
|||
# missing Docker SDK for Python handled in ansible.module_utils.docker.common
|
||||
pass
|
||||
|
||||
from ansible.module_utils.docker.common import AnsibleDockerClient
|
||||
from ansible.module_utils.docker.common import (
|
||||
AnsibleDockerClient,
|
||||
RequestException,
|
||||
)
|
||||
|
||||
|
||||
def get_existing_volume(client, volume_name):
|
||||
|
@ -122,6 +125,8 @@ def main():
|
|||
)
|
||||
except DockerException as e:
|
||||
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
|
||||
except RequestException as e:
|
||||
client.fail('An unexpected requests error occurred when docker-py tried to talk to the docker daemon: {0}'.format(e), exception=traceback.format_exc())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
Loading…
Reference in a new issue