From a6a17d58aaa5b478d25870037561d4a7e4fc233e Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Sat, 18 Feb 2017 19:37:46 -0600 Subject: [PATCH 1/2] Handle docker-py 2.0's new host_config path docker-py 2.0 made some changes to the location of the function which creates the host config. This adds a function to get the proper argspec for host config, networking config, and container config, irrespective of the installed version of docker-py. --- salt/modules/dockerng.py | 93 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 3 deletions(-) diff --git a/salt/modules/dockerng.py b/salt/modules/dockerng.py index f91b4e3af2..fe077228db 100644 --- a/salt/modules/dockerng.py +++ b/salt/modules/dockerng.py @@ -264,7 +264,6 @@ import distutils.version # pylint: disable=import-error,no-name-in-module,unuse import fnmatch import functools import gzip -import inspect as inspect_module import json import logging import os @@ -278,6 +277,7 @@ import time # Import Salt libs from salt.exceptions import CommandExecutionError, SaltInvocationError from salt.ext.six.moves import map # pylint: disable=import-error,redefined-builtin +from salt.utils.args import get_function_argspec as _argspec from salt.utils.decorators \ import identical_signature_wrapper as _mimic_signature import salt.utils @@ -288,11 +288,22 @@ import salt.ext.six as six # pylint: disable=import-error try: import docker - import docker.utils HAS_DOCKER_PY = True except ImportError: HAS_DOCKER_PY = False +# These next two imports are only necessary to have access to the needed +# functions so that we can get argspecs for the container config, host config, +# and networking config (see the get_client_args() function). +try: + import docker.types +except ImportError: + pass +try: + import docker.utils +except ImportError: + pass + try: PY_VERSION = sys.version_info[0] if PY_VERSION == 2: @@ -2994,7 +3005,7 @@ def create(image, # https://docs.docker.com/engine/reference/api/docker_remote_api_v1.15/#create-a-container if salt.utils.version_cmp(version()['ApiVersion'], '1.15') > 0: client = __context__['docker.client'] - host_config_args = inspect_module.getargspec(docker.utils.create_host_config).args + host_config_args = get_client_args()['host_config'] create_kwargs['host_config'] = client.create_host_config( **dict((arg, create_kwargs.pop(arg, None)) for arg in host_config_args if arg != 'version') ) @@ -5492,3 +5503,79 @@ def script_retcode(name, ignore_retcode=ignore_retcode, use_vt=use_vt, keep_env=keep_env)['retcode'] + + +def get_client_args(): + ''' + .. versionadded:: 2016.3.6,2016.11.4,Nitrogen + + Returns the args for docker-py's `low-level API`_, organized by container + config, host config, and networking config. This is designed for use by the + :mod:`docker states ` to more gracefully handle API + changes. + + .. _`low-level API`: http://docker-py.readthedocs.io/en/stable/api.html + + CLI Example: + + .. code-block:: bash + + salt myminion docker.get_client_args + ''' + try: + config_args = _argspec(docker.types.ContainerConfig.__init__).args + except AttributeError: + try: + config_args = _argspec(docker.utils.create_container_config).args + except AttributeError: + raise CommandExecutionError( + 'Failed to get create_container_config argspec' + ) + + try: + host_config_args = \ + _argspec(docker.types.HostConfig.__init__).args + except AttributeError: + try: + host_config_args = _argspec(docker.utils.create_host_config).args + except AttributeError: + raise CommandExecutionError( + 'Failed to get create_host_config argspec' + ) + + try: + endpoint_config_args = \ + _argspec(docker.types.EndpointConfig.__init__).args + except AttributeError: + try: + endpoint_config_args = \ + _argspec(docker.utils.create_endpoint_config).args + except AttributeError: + raise CommandExecutionError( + 'Failed to get create_host_config argspec' + ) + + for arglist in (config_args, host_config_args, endpoint_config_args): + try: + # The API version is passed automagically by the API code that + # imports these classes/functions and is not an arg that we will be + # passing, so remove it if present. + arglist.remove('version') + except ValueError: + pass + + # Remove any args in host or networking config from the main config dict. + # This keeps us from accidentally allowing args that have been moved from + # the container config to the host config (but are still accepted by + # create_container_config so warnings can be issued). + for arglist in (host_config_args, endpoint_config_args): + for item in arglist: + try: + config_args.remove(item) + except ValueError: + # Arg is not in config_args + pass + + return {'config': config_args, + 'host_config': host_config_args, + 'networking_config': endpoint_config_args} From cbd0270bac6d03bd037102cc3aa8afb556df2108 Mon Sep 17 00:00:00 2001 From: Erik Johnson Date: Sat, 18 Feb 2017 19:47:36 -0600 Subject: [PATCH 2/2] docker: make docker-exec the default execution driver Docker 1.13.1 removed the ExecutionDriver from the ``docker info`` return data. This causes all attempts to run commands in containers to fall back to the old lxc-attach driver, which is incompatible with newer docker releases. --- salt/modules/dockerng.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/salt/modules/dockerng.py b/salt/modules/dockerng.py index fe077228db..bbcecfe90e 100644 --- a/salt/modules/dockerng.py +++ b/salt/modules/dockerng.py @@ -203,6 +203,14 @@ Functions Executing Commands Within a Running Container --------------------------------------------- +.. note:: + With the release of Docker 1.13.1, the Execution Driver has been removed. + Starting in versions 2016.3.6, 2016.11.4, and Nitrogen, Salt defaults to + using ``docker exec`` to run commands in containers, however for older Salt + releases it will be necessary to set the ``docker.exec_driver`` config + option to either ``docker-exec`` or ``nsenter`` for Docker versions 1.13.1 + and newer. + Multiple methods exist for executing commands within Docker containers: - lxc-attach_: Default for older versions of docker @@ -843,10 +851,12 @@ def _get_exec_driver(): __context__[contextkey] = from_config return from_config - # For old versions of docker, lxc was the only supported driver. - # This is a sane default. - driver = info().get('ExecutionDriver', 'lxc-') - if driver.startswith('lxc-'): + # The execution driver was removed in Docker 1.13.1, docker-exec is now + # the default. + driver = info().get('ExecutionDriver', 'docker-exec') + if driver == 'docker-exec': + __context__[contextkey] = driver + elif driver.startswith('lxc-'): __context__[contextkey] = 'lxc-attach' elif driver.startswith('native-') and HAS_NSENTER: __context__[contextkey] = 'nsenter' @@ -5510,9 +5520,7 @@ def get_client_args(): .. versionadded:: 2016.3.6,2016.11.4,Nitrogen Returns the args for docker-py's `low-level API`_, organized by container - config, host config, and networking config. This is designed for use by the - :mod:`docker states ` to more gracefully handle API - changes. + config, host config, and networking config. .. _`low-level API`: http://docker-py.readthedocs.io/en/stable/api.html