docker_*: make modules more robust on Docker errors (#57913)

* Improve general error behavior if a Docker error is not caught.

* Don't die when network doesn't exist.

* Add changelog.

* Make API version always available. Also catch errors when retrieving version.

* Fix error message.

* Adjust fallback error messages.
pull/55733/head
Felix Fontein 6 years ago committed by ansibot
parent 122d4164c5
commit f8f2738351

@ -0,0 +1,2 @@
bugfixes:
- "docker_* modules - improve robustness when not handled Docker errors occur."

@ -332,14 +332,14 @@ class AnsibleDockerClient(Client):
try:
super(AnsibleDockerClient, self).__init__(**self._connect_params)
self.docker_api_version_str = self.version()['ApiVersion']
except APIError as exc:
self.fail("Docker API error: %s" % exc)
except Exception as exc:
self.fail("Error connecting: %s" % exc)
if min_docker_api_version is not None:
self.docker_api_version_str = self.version()['ApiVersion']
self.docker_api_version = LooseVersion(self.docker_api_version_str)
if min_docker_api_version is not None:
if self.docker_api_version < LooseVersion(min_docker_api_version):
self.fail('Docker API version is %s. Minimum version required is %s.' % (self.docker_api_version_str, min_docker_api_version))

@ -443,6 +443,7 @@ import os
import re
import sys
import tempfile
import traceback
from contextlib import contextmanager
from distutils.version import LooseVersion
@ -452,7 +453,13 @@ try:
HAS_YAML_EXC = None
except ImportError as exc:
HAS_YAML = False
HAS_YAML_EXC = str(exc)
HAS_YAML_EXC = traceback.format_exc()
try:
from docker.errors import DockerException
except ImportError:
# missing Docker SDK for Python handled in ansible.module_utils.docker.common
pass
try:
from compose import __version__ as compose_version
@ -463,10 +470,9 @@ try:
HAS_COMPOSE = True
HAS_COMPOSE_EXC = None
MINIMUM_COMPOSE_VERSION = '1.7.0'
except ImportError as exc:
HAS_COMPOSE = False
HAS_COMPOSE_EXC = str(exc)
HAS_COMPOSE_EXC = traceback.format_exc()
DEFAULT_TIMEOUT = 10
from ansible.module_utils.docker.common import AnsibleDockerClient, DockerBaseClass
@ -1090,8 +1096,11 @@ def main():
if client.module._name == 'docker_service':
client.module.deprecate("The 'docker_service' module has been renamed to 'docker_compose'.", version='2.12')
try:
result = ContainerManager(client).exec_module()
client.module.exit_json(**result)
except DockerException as e:
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
if __name__ == '__main__':

@ -152,9 +152,10 @@ config_id:
import base64
import hashlib
import traceback
try:
from docker.errors import APIError
from docker.errors import DockerException, APIError
except ImportError:
# missing Docker SDK for Python handled in ansible.module_utils.docker.common
pass
@ -281,12 +282,15 @@ def main():
min_docker_api_version='1.30',
)
try:
results = dict(
changed=False,
)
ConfigManager(client, results)()
client.module.exit_json(**results)
except DockerException as e:
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
if __name__ == '__main__':

@ -942,6 +942,7 @@ container:
import os
import re
import shlex
import traceback
from distutils.version import LooseVersion
from ansible.module_utils.common.text.formatters import human_to_bytes
@ -964,7 +965,7 @@ try:
from docker.types import Ulimit, LogConfig
else:
from docker.utils.types import Ulimit, LogConfig
from docker.errors import APIError, NotFound
from docker.errors import DockerException, APIError, NotFound
except Exception:
# missing Docker SDK for Python handled in ansible.module_utils.docker.common
pass
@ -1410,11 +1411,17 @@ class TaskParameters(DockerBaseClass):
return ip
for net in self.networks:
if net.get('name'):
try:
network = self.client.inspect_network(net['name'])
if network.get('Driver') == 'bridge' and \
network.get('Options', {}).get('com.docker.network.bridge.host_binding_ipv4'):
ip = network['Options']['com.docker.network.bridge.host_binding_ipv4']
break
except NotFound as e:
self.client.fail(
"Cannot inspect the network '{0}' to determine the default IP.".format(net['name']),
exception=traceback.format_exc()
)
return ip
def _parse_publish_ports(self):
@ -3017,8 +3024,11 @@ def main():
version='2.12'
)
try:
cm = ContainerManager(client)
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())
if __name__ == '__main__':

@ -107,6 +107,14 @@ container:
}'
'''
import traceback
try:
from docker.errors import DockerException
except ImportError:
# missing Docker SDK for Python handled in ansible.module_utils.docker.common
pass
from ansible.module_utils.docker.common import AnsibleDockerClient
@ -121,6 +129,7 @@ def main():
min_docker_api_version='1.20',
)
try:
container = client.get_container(client.module.params['name'])
client.module.exit_json(
@ -128,6 +137,8 @@ def main():
exists=(True if container else False),
container=container,
)
except DockerException as e:
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
if __name__ == '__main__':

@ -185,11 +185,13 @@ disk_usage:
'''
import traceback
from ansible.module_utils.docker.common import AnsibleDockerClient, DockerBaseClass
from ansible.module_utils._text import to_native
try:
from docker.errors import APIError
from docker.errors import DockerException, APIError
except ImportError:
# Missing Docker SDK for Python handled in ansible.module_utils.docker.common
pass
@ -321,12 +323,15 @@ def main():
)
client.fail_results['can_talk_to_docker'] = True
try:
results = dict(
changed=False,
)
DockerHostManager(client, results)
client.module.exit_json(**results)
except DockerException as e:
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
if __name__ == '__main__':

@ -410,8 +410,10 @@ image:
type: dict
sample: {}
'''
import os
import re
import traceback
from distutils.version import LooseVersion
@ -427,6 +429,7 @@ if docker_version is not None:
else:
from docker.auth.auth import resolve_repository_name
from docker.utils.utils import parse_repository_tag
from docker.errors import DockerException
except ImportError:
# missing Docker SDK for Python handled in module_utils.docker.common
pass
@ -914,6 +917,7 @@ def main():
'use the "force_source", "force_absent" or "force_tag" option '
'instead, depending on what you want to force.')
try:
results = dict(
changed=False,
actions=[],
@ -922,6 +926,8 @@ def main():
ImageManager(client, results)
client.module.exit_json(**results)
except DockerException as e:
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
if __name__ == '__main__':

@ -154,8 +154,11 @@ images:
]
'''
import traceback
try:
from docker import utils
from docker.errors import DockerException
except ImportError:
# missing Docker SDK for Python handled in ansible.module_utils.docker.common
pass
@ -234,6 +237,7 @@ def main():
if client.module._name == 'docker_image_facts':
client.module.deprecate("The 'docker_image_facts' module has been renamed to 'docker_image_info'", version='2.12')
try:
results = dict(
changed=False,
images=[]
@ -241,6 +245,8 @@ def main():
ImageManager(client, results)
client.module.exit_json(**results)
except DockerException as e:
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
if __name__ == '__main__':

@ -129,6 +129,13 @@ import base64
import json
import os
import re
import traceback
try:
from docker.errors import DockerException
except ImportError:
# missing Docker SDK for Python handled in ansible.module_utils.docker.common
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
@ -311,6 +318,7 @@ def main():
min_docker_api_version='1.20',
)
try:
results = dict(
changed=False,
actions=[],
@ -321,6 +329,8 @@ def main():
if 'actions' in results:
del results['actions']
client.module.exit_json(**results)
except DockerException as e:
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
if __name__ == '__main__':

@ -276,6 +276,7 @@ network:
'''
import re
import traceback
from distutils.version import LooseVersion
@ -289,6 +290,7 @@ from ansible.module_utils.docker.common import (
try:
from docker import utils
from docker.errors import DockerException
if LooseVersion(docker_version) >= LooseVersion('2.0.0'):
from docker.types import IPAMPool, IPAMConfig
except Exception:
@ -681,8 +683,11 @@ def main():
option_minimal_versions=option_minimal_versions,
)
try:
cm = DockerNetworkManager(client)
client.module.exit_json(**cm.results)
except DockerException as e:
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
if __name__ == '__main__':

@ -103,6 +103,14 @@ network:
}'
'''
import traceback
try:
from docker.errors import DockerException
except ImportError:
# missing Docker SDK for Python handled in ansible.module_utils.docker.common
pass
from ansible.module_utils.docker.common import AnsibleDockerClient
@ -117,6 +125,7 @@ def main():
min_docker_api_version='1.21',
)
try:
network = client.get_network(client.module.params['name'])
client.module.exit_json(
@ -124,6 +133,8 @@ def main():
exists=(True if network else False),
network=network,
)
except DockerException as e:
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
if __name__ == '__main__':

@ -129,8 +129,10 @@ node:
'''
import traceback
try:
from docker.errors import APIError
from docker.errors import DockerException, APIError
except ImportError:
# missing Docker SDK for Python handled in ansible.module_utils.docker.common
pass
@ -277,12 +279,15 @@ def main():
min_docker_api_version='1.25',
)
try:
results = dict(
changed=False,
)
SwarmNodeManager(client, results)
client.module.exit_json(**results)
except DockerException as e:
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
if __name__ == '__main__':

@ -87,10 +87,12 @@ nodes:
type: list
'''
import traceback
from ansible.module_utils.docker.swarm import AnsibleDockerSwarmClient
try:
from docker.errors import APIError, NotFound
from docker.errors import DockerException, APIError, NotFound
except ImportError:
# missing Docker SDK for Python handled in ansible.module_utils.docker.common
pass
@ -136,12 +138,15 @@ def main():
client.fail_task_if_not_swarm_manager()
try:
nodes = get_node_facts(client)
client.module.exit_json(
changed=False,
nodes=nodes,
)
except DockerException as e:
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
if __name__ == '__main__':

@ -177,6 +177,14 @@ builder_cache_space_reclaimed:
sample: '0'
'''
import traceback
try:
from docker.errors import DockerException
except ImportError:
# missing Docker SDK for Python handled in ansible.module_utils.docker.common
pass
from distutils.version import LooseVersion
from ansible.module_utils.docker.common import AnsibleDockerClient
@ -214,6 +222,7 @@ def main():
msg = "Error: Docker SDK for Python's version is %s. Minimum version required for builds option is %s. Use `pip install --upgrade docker` to upgrade."
client.fail(msg % (docker_version, cache_min_version))
try:
result = dict()
if client.module.params['containers']:
@ -244,6 +253,8 @@ def main():
result['builder_cache_space_reclaimed'] = res['SpaceReclaimed']
client.module.exit_json(**result)
except DockerException as e:
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
if __name__ == '__main__':

@ -152,9 +152,10 @@ secret_id:
import base64
import hashlib
import traceback
try:
from docker.errors import APIError
from docker.errors import DockerException, APIError
except ImportError:
# missing Docker SDK for Python handled in ansible.module_utils.docker.common
pass
@ -281,6 +282,7 @@ def main():
min_docker_api_version='1.25',
)
try:
results = dict(
changed=False,
secret_id=''
@ -288,6 +290,8 @@ def main():
SecretManager(client, results)()
client.module.exit_json(**results)
except DockerException as e:
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
if __name__ == '__main__':

@ -267,9 +267,10 @@ actions:
'''
import json
import traceback
try:
from docker.errors import APIError
from docker.errors import DockerException, APIError
except ImportError:
# missing Docker SDK for Python handled in ansible.module_utils.docker.common
pass
@ -659,6 +660,7 @@ def main():
option_minimal_versions=option_minimal_versions,
)
try:
results = dict(
changed=False,
result='',
@ -667,6 +669,8 @@ def main():
SwarmManager(client, results)()
client.module.exit_json(**results)
except DockerException as e:
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
if __name__ == '__main__':

@ -192,8 +192,10 @@ tasks:
'''
import traceback
try:
from docker.errors import APIError, NotFound
from docker.errors import DockerException, APIError, NotFound
except ImportError:
# missing Docker SDK for Python handled in ansible.module_utils.docker_common
pass
@ -361,6 +363,7 @@ def main():
client.fail_results['docker_swarm_active'] = client.check_if_swarm_node()
client.fail_results['docker_swarm_manager'] = client.check_if_swarm_manager()
try:
results = dict(
changed=False,
)
@ -368,6 +371,8 @@ def main():
DockerSwarmManager(client, results)
results.update(client.fail_results)
client.module.exit_json(**results)
except DockerException as e:
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
if __name__ == '__main__':

@ -1046,6 +1046,7 @@ EXAMPLES = '''
import shlex
import time
import operator
import traceback
from distutils.version import LooseVersion
@ -2740,6 +2741,7 @@ def main():
option_minimal_versions=option_minimal_versions,
)
try:
dsm = DockerServiceManager(client)
msg, changed, rebuilt, changes, facts = dsm.run_safe()
@ -2755,6 +2757,8 @@ def main():
results['diff'] = dict(before=before, after=after)
client.module.exit_json(**results)
except DockerException as e:
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
if __name__ == '__main__':

@ -66,6 +66,14 @@ service:
type: dict
'''
import traceback
try:
from docker.errors import DockerException
except ImportError:
# missing Docker SDK for Python handled in ansible.module_utils.docker.common
pass
from ansible.module_utils.docker.swarm import AnsibleDockerSwarmClient
@ -91,6 +99,7 @@ def main():
client.fail_task_if_not_swarm_manager()
try:
service = get_service_info(client)
client.module.exit_json(
@ -98,6 +107,8 @@ def main():
service=service,
exists=bool(service)
)
except DockerException as e:
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
if __name__ == '__main__':

@ -125,8 +125,10 @@ volume:
sample: {}
'''
import traceback
try:
from docker.errors import APIError
from docker.errors import DockerException, APIError
except ImportError:
# missing Docker SDK for Python handled in ansible.module_utils.docker.common
pass
@ -323,8 +325,11 @@ def main():
option_minimal_versions=option_minimal_versions,
)
try:
cm = DockerVolumeManager(client)
client.module.exit_json(**cm.results)
except DockerException as e:
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
if __name__ == '__main__':

@ -80,8 +80,10 @@ volume:
}'
'''
import traceback
try:
from docker.errors import NotFound
from docker.errors import DockerException, NotFound
except ImportError:
# missing Docker SDK for Python handled in ansible.module_utils.docker.common
pass
@ -110,6 +112,7 @@ def main():
min_docker_api_version='1.21',
)
try:
volume = get_existing_volume(client, client.module.params['name'])
client.module.exit_json(
@ -117,6 +120,8 @@ def main():
exists=(True if volume else False),
volume=volume,
)
except DockerException as e:
client.fail('An unexpected docker error occurred: {0}'.format(e), exception=traceback.format_exc())
if __name__ == '__main__':

Loading…
Cancel
Save