diff --git a/doc/ref/modules/all/index.rst b/doc/ref/modules/all/index.rst index 0dd3604509..9d3e90fb29 100644 --- a/doc/ref/modules/all/index.rst +++ b/doc/ref/modules/all/index.rst @@ -108,8 +108,7 @@ execution modules dnsmasq dnsutil dockercompose - dockerio - dockerng + docker dpkg drac dracr diff --git a/doc/ref/modules/all/salt.modules.docker.rst b/doc/ref/modules/all/salt.modules.docker.rst new file mode 100644 index 0000000000..fa45699307 --- /dev/null +++ b/doc/ref/modules/all/salt.modules.docker.rst @@ -0,0 +1,7 @@ +=================== +salt.modules.docker +=================== + +.. automodule:: salt.modules.docker + :members: + :exclude-members: cp, freeze, unfreeze diff --git a/doc/ref/modules/all/salt.modules.dockerio.rst b/doc/ref/modules/all/salt.modules.dockerio.rst deleted file mode 100644 index 07b3d0ae28..0000000000 --- a/doc/ref/modules/all/salt.modules.dockerio.rst +++ /dev/null @@ -1,6 +0,0 @@ -===================== -salt.modules.dockerio -===================== - -.. automodule:: salt.modules.dockerio - :members: \ No newline at end of file diff --git a/doc/ref/modules/all/salt.modules.dockerng.rst b/doc/ref/modules/all/salt.modules.dockerng.rst deleted file mode 100644 index de7e14e862..0000000000 --- a/doc/ref/modules/all/salt.modules.dockerng.rst +++ /dev/null @@ -1,7 +0,0 @@ -===================== -salt.modules.dockerng -===================== - -.. automodule:: salt.modules.dockerng - :members: - :exclude-members: cp, freeze, unfreeze diff --git a/doc/ref/states/all/index.rst b/doc/ref/states/all/index.rst index ae902b4d8e..d890615ca7 100644 --- a/doc/ref/states/all/index.rst +++ b/doc/ref/states/all/index.rst @@ -71,8 +71,7 @@ state modules debconfmod dellchassis disk - dockerio - dockerng + docker drac elasticsearch_index elasticsearch_index_template diff --git a/doc/ref/states/all/salt.states.docker.rst b/doc/ref/states/all/salt.states.docker.rst new file mode 100644 index 0000000000..fdcf03d73f --- /dev/null +++ b/doc/ref/states/all/salt.states.docker.rst @@ -0,0 +1,6 @@ +================== +salt.states.docker +================== + +.. automodule:: salt.states.docker + :members: diff --git a/doc/ref/states/all/salt.states.dockerio.rst b/doc/ref/states/all/salt.states.dockerio.rst deleted file mode 100644 index 07bee4f6ac..0000000000 --- a/doc/ref/states/all/salt.states.dockerio.rst +++ /dev/null @@ -1,6 +0,0 @@ -==================== -salt.states.dockerio -==================== - -.. automodule:: salt.states.dockerio - :members: \ No newline at end of file diff --git a/doc/ref/states/all/salt.states.dockerng.rst b/doc/ref/states/all/salt.states.dockerng.rst deleted file mode 100644 index c678b15af9..0000000000 --- a/doc/ref/states/all/salt.states.dockerng.rst +++ /dev/null @@ -1,6 +0,0 @@ -==================== -salt.states.dockerng -==================== - -.. automodule:: salt.states.dockerng - :members: \ No newline at end of file diff --git a/doc/spelling_wordlist.txt b/doc/spelling_wordlist.txt index 16f6904e4d..f2eb2a614b 100644 --- a/doc/spelling_wordlist.txt +++ b/doc/spelling_wordlist.txt @@ -188,7 +188,6 @@ dnspython dnsservers dnsutil dockerfile -dockerio docstring docstrings dpkg diff --git a/doc/topics/releases/nitrogen.rst b/doc/topics/releases/nitrogen.rst index 5549c560c5..3bb68c0b29 100644 --- a/doc/topics/releases/nitrogen.rst +++ b/doc/topics/releases/nitrogen.rst @@ -139,11 +139,25 @@ Custom Refspecs in GitFS / git_pillar / winrepo =============================================== It is now possible to specify the refspecs to use when fetching from remote -repositores for GitFS, git_pillar, and winrepo. More information on how this +repositories for GitFS, git_pillar, and winrepo. More information on how this feature works can be found :ref:`here ` in the GitFS Walkthrough. The git_pillar and winrepo versions of this feature work the same as their GitFS counterpart. +``dockerng`` State/Execution Module Renamed to ``docker`` +========================================================= + +The old ``docker`` state and execution modules have been moved to +salt-contrib_. The ``dockerng`` state and execution module have been renamed to +``docker`` and now serve as the official Docker state and execution modules. + +These state and execution modules can be used interchangeably both with +``docker`` and ``dockerng`` to preserve backward-compatibility, but it is +recommended to update your SLS files to use ``docker`` instead of ``dockerng`` +in event that the ``dockerng`` alias is dropped in a future release. + +.. _salt-contrib: https://github.com/saltstack/salt-contrib + Deprecations ============ diff --git a/salt/modules/container_resource.py b/salt/modules/container_resource.py index 78277ceec1..4148a90659 100644 --- a/salt/modules/container_resource.py +++ b/salt/modules/container_resource.py @@ -6,7 +6,7 @@ Common resources for LXC and systemd-nspawn containers These functions are not designed to be called directly, but instead from the :mod:`lxc `, :mod:`nspawn `, and -:mod:`dockerng ` execution modules. They provide for +:mod:`docker ` execution modules. They provide for common logic to be re-used for common actions. ''' @@ -40,7 +40,7 @@ def _validate(wrapped): container_type = kwargs.get('container_type') exec_driver = kwargs.get('exec_driver') valid_driver = { - 'dockerng': ('lxc-attach', 'nsenter', 'docker-exec'), + 'docker': ('lxc-attach', 'nsenter', 'docker-exec'), 'lxc': ('lxc-attach',), 'nspawn': ('nsenter',), } diff --git a/salt/modules/dockerng.py b/salt/modules/docker.py similarity index 91% rename from salt/modules/dockerng.py rename to salt/modules/docker.py index ecee6d65be..83a2e78516 100644 --- a/salt/modules/dockerng.py +++ b/salt/modules/docker.py @@ -4,26 +4,7 @@ Management of Docker Containers .. versionadded:: 2015.8.0 - -Why Make a Second Docker Execution Module? ------------------------------------------- - -We have received a lot of feedback on our Docker support. In the process of -implementing recommended improvements, it became obvious that major changes -needed to be made to the functions and return data. In the end, a complete -rewrite was done. - -The changes being too significant, it was decided that making a separate -execution module and state module (called ``dockerng``) would be the best -option. This will give users a couple release cycles to modify their scripts, -SLS files, etc. to use the new functionality, rather than forcing users to -change everything immediately. - -In the **Nitrogen** release of Salt (due in 2017), this execution module will -take the place of the default Docker execution module, and backwards-compatible -naming will be maintained for a couple releases after that to allow users time -to replace references to ``dockerng`` with ``docker``. - +:depends: docker-py_ Python module Installation Prerequisites -------------------------- @@ -44,12 +25,13 @@ version 1.9.0 of Docker_. docker-py can easily be installed using Authentication -------------- -To push or pull images, credentials must be configured. By default dockerng will -try to get the credentials from the default docker auth file, located under the -home directory of the user running the salt-minion (HOME/.docker/config.json). -Because a password must be used, it is recommended to place this configuration -in :ref:`Pillar ` data. If pillar data specifies a registry already -present in the default docker auth file, it will override. +To push or pull images, credentials must be configured. By default this module +will try to get the credentials from the default docker auth file, located +under the home directory of the user running the salt-minion +(HOME/.docker/config.json). Because a password must be used, it is recommended +to place this configuration in :ref:`Pillar ` data. If pillar data +specifies a registry already present in the default docker auth file, it will +override. The configuration schema is as follows: @@ -144,62 +126,62 @@ Functions --------- - Information Gathering - - :py:func:`dockerng.depends ` - - :py:func:`dockerng.diff ` - - :py:func:`dockerng.exists ` - - :py:func:`dockerng.history ` - - :py:func:`dockerng.images ` - - :py:func:`dockerng.info ` - - :py:func:`dockerng.inspect ` - - :py:func:`dockerng.inspect_container - ` - - :py:func:`dockerng.inspect_image ` - - :py:func:`dockerng.list_containers - ` - - :py:func:`dockerng.list_tags ` - - :py:func:`dockerng.logs ` - - :py:func:`dockerng.pid ` - - :py:func:`dockerng.port ` - - :py:func:`dockerng.ps ` - - :py:func:`dockerng.state ` - - :py:func:`dockerng.search ` - - :py:func:`dockerng.top ` - - :py:func:`dockerng.version ` + - :py:func:`docker.depends ` + - :py:func:`docker.diff ` + - :py:func:`docker.exists ` + - :py:func:`docker.history ` + - :py:func:`docker.images ` + - :py:func:`docker.info ` + - :py:func:`docker.inspect ` + - :py:func:`docker.inspect_container + ` + - :py:func:`docker.inspect_image ` + - :py:func:`docker.list_containers + ` + - :py:func:`docker.list_tags ` + - :py:func:`docker.logs ` + - :py:func:`docker.pid ` + - :py:func:`docker.port ` + - :py:func:`docker.ps ` + - :py:func:`docker.state ` + - :py:func:`docker.search ` + - :py:func:`docker.top ` + - :py:func:`docker.version ` - Container Management - - :py:func:`dockerng.create ` - - :py:func:`dockerng.copy_from ` - - :py:func:`dockerng.copy_to ` - - :py:func:`dockerng.export ` - - :py:func:`dockerng.rm ` + - :py:func:`docker.create ` + - :py:func:`docker.copy_from ` + - :py:func:`docker.copy_to ` + - :py:func:`docker.export ` + - :py:func:`docker.rm ` - Management of Container State - - :py:func:`dockerng.kill ` - - :py:func:`dockerng.pause ` - - :py:func:`dockerng.restart ` - - :py:func:`dockerng.start ` - - :py:func:`dockerng.stop ` - - :py:func:`dockerng.unpause ` - - :py:func:`dockerng.wait ` + - :py:func:`docker.kill ` + - :py:func:`docker.pause ` + - :py:func:`docker.restart ` + - :py:func:`docker.start ` + - :py:func:`docker.stop ` + - :py:func:`docker.unpause ` + - :py:func:`docker.wait ` - Image Management - - :py:func:`dockerng.build ` - - :py:func:`dockerng.commit ` - - :py:func:`dockerng.dangling ` - - :py:func:`dockerng.import ` - - :py:func:`dockerng.load ` - - :py:func:`dockerng.pull ` - - :py:func:`dockerng.push ` - - :py:func:`dockerng.rmi ` - - :py:func:`dockerng.save ` - - :py:func:`dockerng.tag ` + - :py:func:`docker.build ` + - :py:func:`docker.commit ` + - :py:func:`docker.dangling ` + - :py:func:`docker.import ` + - :py:func:`docker.load ` + - :py:func:`docker.pull ` + - :py:func:`docker.push ` + - :py:func:`docker.rmi ` + - :py:func:`docker.save ` + - :py:func:`docker.tag ` - Network Management - - :py:func:`dockerng.networks ` - - :py:func:`dockerng.create_network ` - - :py:func:`dockerng.remove_network ` - - :py:func:`dockerng.inspect_network - ` - - :py:func:`dockerng.connect_container_to_network - ` - - :py:func:`dockerng.disconnect_container_from_network - ` + - :py:func:`docker.networks ` + - :py:func:`docker.create_network ` + - :py:func:`docker.remove_network ` + - :py:func:`docker.inspect_network + ` + - :py:func:`docker.connect_container_to_network + ` + - :py:func:`docker.disconnect_container_from_network + ` @@ -208,6 +190,13 @@ Functions Executing Commands Within a Running Container --------------------------------------------- +.. note:: + With the release of Docker 1.3.1, the Execution Driver has been removed. + Starting in Salt 2016.3.6, 2016.11.4, and Nitrogen, Salt defaults to using + the ``docker-exec`` driver, 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.3.1 and newer. + Multiple methods exist for executing commands within Docker containers: - lxc-attach_: Default for older versions of docker @@ -242,13 +231,13 @@ a little work. This execution module provides functions that shadow those from the :mod:`cmd ` module. They are as follows: -- :py:func:`dockerng.retcode ` -- :py:func:`dockerng.run ` -- :py:func:`dockerng.run_all ` -- :py:func:`dockerng.run_stderr ` -- :py:func:`dockerng.run_stdout ` -- :py:func:`dockerng.script ` -- :py:func:`dockerng.script_retcode ` +- :py:func:`docker.retcode ` +- :py:func:`docker.run ` +- :py:func:`docker.run_all ` +- :py:func:`docker.run_stderr ` +- :py:func:`docker.run_stdout ` +- :py:func:`docker.script ` +- :py:func:`docker.script_retcode ` Detailed Function Documentation @@ -346,13 +335,14 @@ STOP_TIMEOUT = 10 NOTSET = object() -# Define the module's virtual name -__virtualname__ = 'dockerng' +# Define the module's virtual name and alias +__virtualname__ = 'docker' +__virtual_aliases__ = ('dockerng',) ''' The below two dictionaries contain information on the kwargs which are -acceptable in the dockerng.create and dockerng.start functions, respectively. -The aim is to make adding new arguments easy, and to keep all of the various +acceptable in the docker.create and docker.start functions, respectively. The +aim is to make adding new arguments easy, and to keep all of the various information needed to validate the input organized nicely. There are 6 different keys which may be present in the sub-dict for each @@ -369,9 +359,9 @@ argument name: there should be a function named in the format _valid_ (e.g. _valid_bool) within _validate_input(). -3. path - Refers to the nested path in the dockerng.inspect_container return +3. path - Refers to the nested path in the docker.inspect_container return data where that value is stored. This will only be used by the _compare() - function in salt/states/dockerng.py. + function in salt/states/docker.py. 4. default - Many of the CLI options will default to None, but for booleans and integer values (and some others) we want to ensure that someone isn't able @@ -598,16 +588,16 @@ def __virtual__(): return __virtualname__ else: return (False, - 'Insufficient Docker version for dockerng (required: ' - '{0}, installed: {1}); You need to "pip install -U docker-py"'.format( + 'Insufficient Docker version (required: {0}, ' + 'installed: {1})'.format( '.'.join(map(str, MIN_DOCKER)), '.'.join(map(str, docker_versioninfo)))) return (False, - 'Insufficient docker-py version for dockerng (required: ' - '{0}, installed: {1})'.format( + 'Insufficient docker-py version (required: {0}, ' + 'installed: {1})'.format( '.'.join(map(str, MIN_DOCKER_PY)), '.'.join(map(str, docker_py_versioninfo)))) - return (False, 'Docker module could not get imported; You need to "pip install docker-py"') + return (False, 'Could not import docker module, is docker-py installed?') class DockerJSONDecoder(json.JSONDecoder): @@ -728,7 +718,7 @@ def _refresh_mine_cache(wrapped): refresh salt mine on exit. ''' returned = wrapped(*args, **salt.utils.clean_kwargs(**kwargs)) - __salt__['mine.send']('dockerng.ps', verbose=True, all=True, host=True) + __salt__['mine.send']('docker.ps', verbose=True, all=True, host=True) return returned return wrapper @@ -764,7 +754,7 @@ def _clear_context(): # an exception will be raised if the size of the dict is modified during # iteration. keep_context = ( - 'docker.client', 'docker.exec_driver', 'dockerng._pull_status', + 'docker.client', 'docker.exec_driver', 'docker._pull_status', 'docker.docker_version', 'docker.docker_py_version' ) for key in list(__context__): @@ -890,10 +880,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' @@ -902,7 +894,7 @@ def _get_exec_driver(): 'ExecutionDriver from \'docker info\' is blank, falling ' 'back to using \'nsenter\'. To squelch this warning, set ' 'docker.exec_driver. See the Salt documentation for the ' - 'dockerng module for more information.' + 'docker module for more information.' ) __context__[contextkey] = 'nsenter' else: @@ -923,8 +915,7 @@ def _get_repo_tag(image, default_tag='latest'): # Would happen if some wiseguy requests a tag ending in a colon # (e.g. 'somerepo:') log.warning( - 'Assuming tag \'{0}\' for repo \'{1}\'' - .format(default_tag, image) + 'Assuming tag \'%s\' for repo \'%s\'', default_tag, image ) r_tag = default_tag else: @@ -955,8 +946,7 @@ def _prep_pull(): Populate __context__ with the current (pre-pull) image IDs (see the docstring for _pull_status for more information). ''' - __context__['dockerng._pull_status'] = \ - [x[:12] for x in images(all=True)] + __context__['docker._pull_status'] = [x[:12] for x in images(all=True)] def _size_fmt(num): @@ -973,7 +963,7 @@ def _size_fmt(num): return '{0:3.1f} {1}'.format(num, unit) num /= 1024.0 except Exception: - log.error('Unable to format file size for \'{0}\''.format(num)) + log.error('Unable to format file size for \'%s\'', num) return 'unknown' @@ -1044,9 +1034,12 @@ def _image_wrapper(attr, *args, **kwargs): 'password': password, 'email': email }}) - except Exception as e: - log.debug('dockerng was unable to load credential from ~/.docker/config.json' - ' trying with pillar now ({0})'.format(e)) + except Exception as exc: + log.debug( + 'Salt was unable to load credentials from ~/.docker/config.json. ' + 'Attempting to load credentials from Pillar data. Error ' + 'message: %s', exc + ) # Set credentials from pillar - Overwrite auth from config.json registry_auth_config.update(__pillar__.get('docker-registries', {})) for key, data in six.iteritems(__pillar__): @@ -1054,7 +1047,7 @@ def _image_wrapper(attr, *args, **kwargs): registry_auth_config.update(data) err = ( - '{0} Docker credentials{1}. Please see the dockerng remote ' + '{0} Docker credentials{1}. Please see the docker remote ' 'execution module documentation for information on how to ' 'configure authentication.' ) @@ -1166,14 +1159,14 @@ def _pull_status(data, item): if id_ not in pulled: pulled.append(id_) - if 'dockerng._pull_status' not in __context__: + if 'docker._pull_status' not in __context__: log.warning( '_pull_status context variable was not populated, information on ' 'downloaded layers may be inaccurate. Please report this to the ' 'SaltStack development team, and if possible include the image ' '(and tag) that was being pulled.' ) - __context__['dockerng._pull_status'] = NOTSET + __context__['docker._pull_status'] = NOTSET status = item['status'] if status == 'Already exists': _already_exists(item['id']) @@ -1182,9 +1175,9 @@ def _pull_status(data, item): elif status.startswith('Status: '): data['Status'] = status[8:] elif status == 'Download complete': - if __context__['dockerng._pull_status'] is not NOTSET: + if __context__['docker._pull_status'] is not NOTSET: id_ = item['id'] - if id_ in __context__['dockerng._pull_status']: + if id_ in __context__['docker._pull_status']: _already_exists(id_) else: _new_layer(id_) @@ -1214,10 +1207,16 @@ def _error_detail(data, item): ''' err = item['errorDetail'] if 'code' in err: - msg = '{0}: {1}'.format( - item['errorDetail']['code'], - item['errorDetail']['message'], - ) + try: + msg = ': '.join(( + item['errorDetail']['code'], + item['errorDetail']['message'] + )) + except TypeError: + msg = '{0}: {1}'.format( + item['errorDetail']['code'], + item['errorDetail']['message'], + ) else: msg = item['errorDetail']['message'] data.append(msg) @@ -1577,9 +1576,10 @@ def _validate_input(kwargs, 'Host path {0} in bind {1} is not absolute' .format(container_path, bind) ) - log.warning('Host path {0} in bind {1} is not absolute,' - ' assuming it is a docker volume.' - .format(host_path, bind)) + log.warning( + 'Host path %s in bind %s is not absolute, assuming it is ' + 'a docker volume.', host_path, bind + ) if not os.path.isabs(container_path): raise SaltInvocationError( 'Container path {0} in bind {1} is not absolute' @@ -1766,8 +1766,8 @@ def _validate_input(kwargs, else: # just a name assume it is a network log.info( - 'Assuming network_mode \'{0}\' is a network.'.format( - kwargs['network_mode']) + 'Assuming network_mode \'%s\' is a network.', + kwargs['network_mode'] ) except SaltInvocationError: raise SaltInvocationError( @@ -1922,7 +1922,7 @@ def _validate_input(kwargs, # Have to call this func using the __salt__ dunder (instead of just # version()) because this _validate_input() will be imported into the # state module, and the state module won't have a version() func. - _version = __salt__['dockerng.version']() + _version = __salt__['docker.version']() if 'VersionInfo' not in _version: log.warning( 'Unable to determine docker version. Feature version checking ' @@ -2025,8 +2025,8 @@ def depends(name): .. code-block:: bash - salt myminion dockerng.depends myimage - salt myminion dockerng.depends 0123456789ab + salt myminion docker.depends myimage + salt myminion docker.depends 0123456789ab ''' # Resolve tag or short-SHA to full SHA image_id = inspect_image(name)['Id'] @@ -2071,7 +2071,7 @@ def diff(name): .. code-block:: bash - salt myminion dockerng.diff mycontainer + salt myminion docker.diff mycontainer ''' changes = _client_wrapper('diff', name) kind_map = {0: 'Changed', 1: 'Added', 2: 'Deleted'} @@ -2081,9 +2081,9 @@ def diff(name): ret.setdefault(key, []).append(change['Path']) if 'Unknown' in ret: log.error( - 'Unknown changes detected in docker.diff of container {0}. ' + 'Unknown changes detected in docker.diff of container %s. ' 'This is probably due to a change in the Docker API. Please ' - 'report this to the SaltStack developers'.format(name) + 'report this to the SaltStack developers', name ) return ret @@ -2105,7 +2105,7 @@ def exists(name): .. code-block:: bash - salt myminion dockerng.exists mycontainer + salt myminion docker.exists mycontainer ''' contextkey = 'docker.exists.{0}'.format(name) if contextkey in __context__: @@ -2133,7 +2133,7 @@ def history(name, quiet=False): .. code-block:: bash - $ salt myminion dockerng.history nginx:latest quiet=True + $ salt myminion docker.history nginx:latest quiet=True myminion: - FROM scratch - ADD file:ef063ed0ae9579362871b9f23d2bc0781ef7cd4de6ac822052cf6c9c5a12b1e2 in / @@ -2172,7 +2172,7 @@ def history(name, quiet=False): .. code-block:: bash - salt myminion dockerng.exists mycontainer + salt myminion docker.exists mycontainer ''' response = _client_wrapper('history', name) key_map = { @@ -2231,8 +2231,8 @@ def images(verbose=False, **kwargs): .. code-block:: bash - salt myminion dockerng.images - salt myminion dockerng.images all=True + salt myminion docker.images + salt myminion docker.images all=True ''' if 'docker.images' not in __context__: response = _client_wrapper('images', all=kwargs.get('all', False)) @@ -2287,7 +2287,7 @@ def info(): .. code-block:: bash - salt myminion dockerng.info + salt myminion docker.info ''' return _client_wrapper('info') @@ -2297,11 +2297,11 @@ def inspect(name): This is a generic container/image inspecton function. It will first attempt to get container information for the passed name/ID using :py:func:`docker.inspect_container - `, and then will try to get image + `, and then will try to get image information for the passed name/ID using :py:func:`docker.inspect_image - `. If it is already known that the + `. If it is already known that the name/ID is an image, it is slightly more efficient to use - :py:func:`docker.inspect_image `. + :py:func:`docker.inspect_image `. name Container/image name or ID @@ -2316,8 +2316,8 @@ def inspect(name): .. code-block:: bash - salt myminion dockerng.inspect mycontainer - salt myminion dockerng.inspect busybox + salt myminion docker.inspect mycontainer + salt myminion docker.inspect busybox ''' try: return inspect_container(name) @@ -2353,8 +2353,8 @@ def inspect_container(name): .. code-block:: bash - salt myminion dockerng.inspect_container mycontainer - salt myminion dockerng.inspect_container 0123456789ab + salt myminion docker.inspect_container mycontainer + salt myminion docker.inspect_container 0123456789ab ''' return _client_wrapper('inspect_container', name) @@ -2382,9 +2382,9 @@ def inspect_image(name): .. code-block:: bash - salt myminion dockerng.inspect_image busybox - salt myminion dockerng.inspect_image centos:6 - salt myminion dockerng.inspect_image 0123456789ab + salt myminion docker.inspect_image busybox + salt myminion docker.inspect_image centos:6 + salt myminion docker.inspect_image 0123456789ab ''' ret = _client_wrapper('inspect_image', name) for param in ('Size', 'VirtualSize'): @@ -2396,8 +2396,8 @@ def inspect_image(name): def list_containers(**kwargs): ''' Returns a list of containers by name. This is different from - :py:func:`dockerng.ps ` in that - :py:func:`dockerng.ps ` returns its results + :py:func:`docker.ps ` in that + :py:func:`docker.ps ` returns its results organized by container ID. all : False @@ -2407,7 +2407,7 @@ def list_containers(**kwargs): .. code-block:: bash - salt myminion dockerng.inspect_image + salt myminion docker.inspect_image ''' ret = set() for item in six.itervalues(ps_(all=kwargs.get('all', False))): @@ -2427,7 +2427,7 @@ def list_tags(): .. code-block:: bash - salt myminion dockerng.list_tags + salt myminion docker.list_tags ''' ret = set() for item in six.itervalues(images()): @@ -2449,7 +2449,7 @@ def logs(name): .. code-block:: bash - salt myminion dockerng.logs mycontainer + salt myminion docker.logs mycontainer ''' return _client_wrapper('logs', name) @@ -2466,8 +2466,8 @@ def pid(name): .. code-block:: bash - salt myminion dockerng.pid mycontainer - salt myminion dockerng.pid 0123456789ab + salt myminion docker.pid mycontainer + salt myminion docker.pid 0123456789ab ''' return inspect_container(name)['State']['Pid'] @@ -2499,9 +2499,9 @@ def port(name, private_port=None): .. code-block:: bash - salt myminion dockerng.port mycontainer - salt myminion dockerng.port mycontainer 5000 - salt myminion dockerng.port mycontainer 5000/udp + salt myminion docker.port mycontainer + salt myminion docker.port mycontainer 5000 + salt myminion docker.port mycontainer 5000/udp ''' # docker.client.Client.port() doesn't do what we need, so just inspect the # container and get the information from there. It's what they're already @@ -2568,9 +2568,9 @@ def ps_(filters=None, **kwargs): .. code-block:: bash - salt myminion dockerng.ps - salt myminion dockerng.ps all=True - salt myminion dockerng.ps filters="{'label': 'role=web'}" + salt myminion docker.ps + salt myminion docker.ps all=True + salt myminion docker.ps filters="{'label': 'role=web'}" ''' response = _client_wrapper('containers', all=True, filters=filters) key_map = { @@ -2630,7 +2630,7 @@ def state(name): .. code-block:: bash - salt myminion dockerng.state mycontainer + salt myminion docker.state mycontainer ''' contextkey = 'docker.state.{0}'.format(name) if contextkey in __context__: @@ -2677,8 +2677,8 @@ def search(name, official=False, trusted=False): .. code-block:: bash - salt myminion dockerng.search centos - salt myminion dockerng.search centos official=True + salt myminion docker.search centos + salt myminion docker.search centos official=True ''' response = _client_wrapper('search', name) if not response: @@ -2736,8 +2736,8 @@ def top(name): .. code-block:: bash - salt myminion dockerng.top mycontainer - salt myminion dockerng.top 0123456789ab + salt myminion docker.top mycontainer + salt myminion docker.top 0123456789ab ''' response = _client_wrapper('top', name) @@ -2765,7 +2765,7 @@ def version(): .. code-block:: bash - salt myminion dockerng.version + salt myminion docker.version ''' ret = _client_wrapper('version') version_re = re.compile(VERSION_RE) @@ -2949,7 +2949,7 @@ def create(image, port_bindings Bind exposed ports which were exposed using the ``ports`` argument to - :py:func:`dockerng.create `. These + :py:func:`docker.create `. These should be passed in the same way as the ``--publish`` argument to the ``docker run`` CLI command: @@ -2997,7 +2997,7 @@ def create(image, publish_all_ports : False Allocates a random host port for each port exposed using the ``ports`` - argument to :py:func:`dockerng.create `. + argument to :py:func:`docker.create `. Example: ``publish_all_ports=True`` @@ -3140,9 +3140,9 @@ def create(image, .. code-block:: bash # Create a data-only container - salt myminion dockerng.create myuser/mycontainer volumes="/mnt/vol1,/mnt/vol2" + salt myminion docker.create myuser/mycontainer volumes="/mnt/vol1,/mnt/vol2" # Create a CentOS 7 container that will stay running once started - salt myminion dockerng.create centos:7 name=mycent7 interactive=True tty=True command=bash + salt myminion docker.create centos:7 name=mycent7 interactive=True tty=True command=bash ''' if 'cmd' in kwargs: if 'command' in kwargs: @@ -3186,7 +3186,7 @@ def create(image, ) log.debug( - 'dockerng.create is using the following kwargs to create ' + 'docker.create is using the following kwargs to create ' 'container \'{0}\' from image \'{1}\': {2}' .format(name, image, create_kwargs) ) @@ -3237,7 +3237,7 @@ def copy_from(name, source, dest, overwrite=False, makedirs=False): .. code-block:: bash - salt myminion dockerng.copy_from mycontainer /var/log/nginx/access.log /home/myuser + salt myminion docker.copy_from mycontainer /var/log/nginx/access.log /home/myuser ''' c_state = state(name) if c_state != 'running': @@ -3296,14 +3296,22 @@ def copy_from(name, source, dest, overwrite=False, makedirs=False): # Before we try to replace the file, compare checksums. source_md5 = _get_md5(name, source) if source_md5 == __salt__['file.get_sum'](dest, 'md5'): - log.debug('{0}:{1} and {2} are the same file, skipping copy' - .format(name, source, dest)) + log.debug( + '%s:%s and %s are the same file, skipping copy', + name, source, dest + ) return True - log.debug('Copying {0} from container \'{1}\' to local path {2}' - .format(source, name, dest)) + log.debug( + 'Copying %s from container \'%s\' to local path %s', + source, name, dest + ) - cmd = ['docker', 'cp', '{0}:{1}'.format(name, source), dest_dir] + try: + src_path = ':'.join((name, source)) + except TypeError: + src_path = '{0}:{1}'.format(name, source) + cmd = ['docker', 'cp', src_path, dest_dir] __salt__['cmd.run'](cmd, python_shell=False) return source_md5 == __salt__['file.get_sum'](dest, 'md5') @@ -3356,7 +3364,7 @@ def copy_to(name, .. code-block:: bash - salt myminion dockerng.copy_to mycontainer /tmp/foo /root/foo + salt myminion docker.copy_to mycontainer /tmp/foo /root/foo ''' return __salt__['container_resource.copy_to']( name, @@ -3437,8 +3445,8 @@ def export(name, .. code-block:: bash - salt myminion dockerng.export mycontainer /tmp/mycontainer.tar - salt myminion dockerng.export mycontainer /tmp/mycontainer.tar.xz push=True + salt myminion docker.export mycontainer /tmp/mycontainer.tar + salt myminion docker.export mycontainer /tmp/mycontainer.tar.xz push=True ''' err = 'Path \'{0}\' is not absolute'.format(path) try: @@ -3578,8 +3586,8 @@ def rm_(name, force=False, volumes=False): .. code-block:: bash - salt myminion dockerng.rm mycontainer - salt myminion dockerng.rm mycontainer force=True + salt myminion docker.rm mycontainer + salt myminion docker.rm mycontainer force=True ''' if state(name) == 'running' and not force: raise CommandExecutionError( @@ -3666,12 +3674,12 @@ def build(path=None, .. code-block:: bash - salt myminion dockerng.build /path/to/docker/build/dir image=myimage:dev - salt myminion dockerng.build https://github.com/myuser/myrepo.git image=myimage:latest + salt myminion docker.build /path/to/docker/build/dir image=myimage:dev + salt myminion docker.build https://github.com/myuser/myrepo.git image=myimage:latest .. versionadded:: develop - salt myminion dockerng.build /path/to/docker/build/dir dockerfile=Dockefile.different image=myimage:dev + salt myminion docker.build /path/to/docker/build/dir dockerfile=Dockefile.different image=myimage:dev ''' _prep_pull() @@ -3775,8 +3783,8 @@ def commit(name, .. code-block:: bash - salt myminion dockerng.commit mycontainer myuser/myimage - salt myminion dockerng.commit mycontainer myuser/myimage:mytag + salt myminion docker.commit mycontainer myuser/myimage + salt myminion docker.commit mycontainer myuser/myimage:mytag ''' repo_name, repo_tag = _get_repo_tag(image) time_started = time.time() @@ -3813,7 +3821,7 @@ def dangling(prune=False, force=False): which were superseded by committing a new copy of an existing tagged image. - Images which were loaded using :py:func:`docker.load - ` (or the ``docker load`` Docker CLI + ` (or the ``docker load`` Docker CLI command), but not tagged. prune : False @@ -3840,8 +3848,8 @@ def dangling(prune=False, force=False): .. code-block:: bash - salt myminion dockerng.dangling - salt myminion dockerng.dangling prune=True + salt myminion docker.dangling + salt myminion docker.dangling prune=True ''' all_images = images(all=True) dangling_images = [x[:12] for x in _get_top_level_images(all_images) @@ -3854,7 +3862,7 @@ def dangling(prune=False, force=False): try: ret.setdefault(image, {})['Removed'] = rmi(image, force=force) except Exception as exc: - err = '{0}'.format(exc) + err = exc.__str__() log.error(err) ret.setdefault(image, {})['Comment'] = err ret[image]['Removed'] = False @@ -3897,9 +3905,9 @@ def import_(source, .. code-block:: bash - salt myminion dockerng.import /tmp/cent7-minimal.tar.xz myuser/centos - salt myminion dockerng.import /tmp/cent7-minimal.tar.xz myuser/centos:7 - salt myminion dockerng.import salt://dockerimages/cent7-minimal.tar.xz myuser/centos:7 + salt myminion docker.import /tmp/cent7-minimal.tar.xz myuser/centos + salt myminion docker.import /tmp/cent7-minimal.tar.xz myuser/centos:7 + salt myminion docker.import salt://dockerimages/cent7-minimal.tar.xz myuser/centos:7 ''' repo_name, repo_tag = _get_repo_tag(image) path = __salt__['container_resource.cache_file'](source) @@ -3947,9 +3955,8 @@ def import_(source, def load(path, image=None): ''' - Load a tar archive that was created using :py:func:`dockerng.save - ` (or via the Docker CLI using ``docker - save``). + Load a tar archive that was created using :py:func:`docker.save + ` (or via the Docker CLI using ``docker save``). path Path to docker tar archive. Path can be a file on the Minion, or the @@ -3960,8 +3967,8 @@ def load(path, image=None): image : None If specified, the topmost layer of the newly-loaded image will be - tagged with the specified repo and tag using :py:func:`dockerng.tag - `. The image name should be specified in + tagged with the specified repo and tag using :py:func:`docker.tag + `. The image name should be specified in ``repo:tag`` notation. If just the repository name is passed, a tag name of ``latest`` will be assumed. @@ -3988,8 +3995,8 @@ def load(path, image=None): .. code-block:: bash - salt myminion dockerng.load /path/to/image.tar - salt myminion dockerng.load salt://path/to/docker/saved/image.tar image=myuser/myimage:mytag + salt myminion docker.load /path/to/image.tar + salt myminion docker.load salt://path/to/docker/saved/image.tar image=myuser/myimage:mytag ''' if image is not None: image = ':'.join(_get_repo_tag(image)) @@ -4048,7 +4055,7 @@ def layers(name): .. code-block:: bash - salt myminion dockerng.layers centos:7 + salt myminion docker.layers centos:7 ''' ret = [] cmd = ['docker', 'history', '-q', name] @@ -4107,8 +4114,8 @@ def pull(image, .. code-block:: bash - salt myminion dockerng.pull centos - salt myminion dockerng.pull centos:6 + salt myminion docker.pull centos + salt myminion docker.pull centos:6 ''' _prep_pull() @@ -4207,16 +4214,15 @@ def push(image, .. code-block:: bash - salt myminion dockerng.push myuser/mycontainer - salt myminion dockerng.push myuser/mycontainer:mytag + salt myminion docker.push myuser/mycontainer + salt myminion docker.push myuser/mycontainer:mytag ''' if ':' in image: repo_name, repo_tag = _get_repo_tag(image) else: repo_name = image repo_tag = None - log.info('Attempting to push all tagged images matching {0}' - .format(repo_name)) + log.info('Attempting to push all tagged images matching %s', repo_name) kwargs = {'tag': repo_tag, 'stream': True, @@ -4282,9 +4288,9 @@ def rmi(*names, **kwargs): .. code-block:: bash - salt myminion dockerng.rmi busybox - salt myminion dockerng.rmi busybox force=True - salt myminion dockerng.rmi foo bar baz + salt myminion docker.rmi busybox + salt myminion docker.rmi busybox force=True + salt myminion docker.rmi foo bar baz ''' pre_images = images(all=True) pre_tags = list_tags() @@ -4368,7 +4374,7 @@ def save(name, .. note:: Since the Docker API does not support ``docker save``, compression will be a bit slower with this function than with - :py:func:`docker.export ` since the + :py:func:`docker.export ` since the image(s) will first be saved and then the compression done afterwards. @@ -4403,8 +4409,8 @@ def save(name, .. code-block:: bash - salt myminion dockerng.save centos:7 /tmp/cent7.tar - salt myminion dockerng.save 0123456789ab cdef01234567 /tmp/saved.tar + salt myminion docker.save centos:7 /tmp/cent7.tar + salt myminion docker.save 0123456789ab cdef01234567 /tmp/saved.tar ''' err = 'Path \'{0}\' is not absolute'.format(path) try: @@ -4547,8 +4553,8 @@ def tag_(name, image, force=False): .. code-block:: bash - salt myminion dockerng.tag 0123456789ab myrepo/mycontainer - salt myminion dockerng.tag 0123456789ab myrepo/mycontainer:mytag + salt myminion docker.tag 0123456789ab myrepo/mycontainer + salt myminion docker.tag 0123456789ab myrepo/mycontainer:mytag ''' image_id = inspect_image(name)['Id'] repo_name, repo_tag = _get_repo_tag(image) @@ -4580,8 +4586,8 @@ def networks(names=None, ids=None): .. code-block:: bash - salt myminion dockerng.networks names="['network-web']" - salt myminion dockerng.networks ids="['1f9d2454d0872b68dd9e8744c6e7a4c66b86f10abaccc21e14f7f014f729b2bc']" + salt myminion docker.networks names="['network-web']" + salt myminion docker.networks ids="['1f9d2454d0872b68dd9e8744c6e7a4c66b86f10abaccc21e14f7f014f729b2bc']" ''' response = _client_wrapper('networks', names=names, @@ -4608,7 +4614,7 @@ def create_network(name, driver=None): .. code-block:: bash - salt myminion dockerng.create_network web_network driver=bridge + salt myminion docker.create_network web_network driver=bridge ''' response = _client_wrapper('create_network', name, driver=driver) _clear_context() @@ -4629,7 +4635,7 @@ def remove_network(network_id): .. code-block:: bash - salt myminion dockerng.remove_network 1f9d2454d0872b68dd9e8744c6e7a4c66b86f10abaccc21e14f7f014f729b2bc + salt myminion docker.remove_network 1f9d2454d0872b68dd9e8744c6e7a4c66b86f10abaccc21e14f7f014f729b2bc ''' response = _client_wrapper('remove_network', network_id) _clear_context() @@ -4650,7 +4656,7 @@ def inspect_network(network_id): .. code-block:: bash - salt myminion dockerng.inspect_network 1f9d2454d0872b68dd9e8744c6e7a4c66b86f10abaccc21e14f7f014f729b2bc + salt myminion docker.inspect_network 1f9d2454d0872b68dd9e8744c6e7a4c66b86f10abaccc21e14f7f014f729b2bc ''' response = _client_wrapper('inspect_network', network_id) _clear_context() @@ -4677,7 +4683,7 @@ def connect_container_to_network(container, network_id, ipv4_address=None): .. code-block:: bash - salt myminion dockerng.connect_container_from_network web-1 1f9d2454d0872b68dd9e8744c6e7a4c66b86f10abaccc21e14f7f014f729b2bc + salt myminion docker.connect_container_from_network web-1 1f9d2454d0872b68dd9e8744c6e7a4c66b86f10abaccc21e14f7f014f729b2bc ''' response = _client_wrapper('connect_container_to_network', container, @@ -4704,7 +4710,7 @@ def disconnect_container_from_network(container, network_id): .. code-block:: bash - salt myminion dockerng.disconnect_container_from_network web-1 1f9d2454d0872b68dd9e8744c6e7a4c66b86f10abaccc21e14f7f014f729b2bc + salt myminion docker.disconnect_container_from_network web-1 1f9d2454d0872b68dd9e8744c6e7a4c66b86f10abaccc21e14f7f014f729b2bc ''' response = _client_wrapper('disconnect_container_from_network', container, @@ -4731,7 +4737,7 @@ def volumes(filters=None): .. code-block:: bash - salt myminion dockerng.volumes filters="{'dangling': True}" + salt myminion docker.volumes filters="{'dangling': True}" ''' response = _client_wrapper('volumes', filters=filters) _clear_context() @@ -4760,7 +4766,7 @@ def create_volume(name, driver=None, driver_opts=None): .. code-block:: bash - salt myminion dockerng.create_volume my_volume driver=local + salt myminion docker.create_volume my_volume driver=local ''' response = _client_wrapper('create_volume', name, driver=driver, driver_opts=driver_opts) @@ -4784,7 +4790,7 @@ def remove_volume(name): .. code-block:: bash - salt myminion dockerng.remove_volume my_volume + salt myminion docker.remove_volume my_volume ''' response = _client_wrapper('remove_volume', name) _clear_context() @@ -4807,7 +4813,7 @@ def inspect_volume(name): .. code-block:: bash - salt myminion dockerng.inspect_volume my_volume + salt myminion docker.inspect_volume my_volume ''' response = _client_wrapper('inspect_volume', name) _clear_context() @@ -4840,7 +4846,7 @@ def kill(name): .. code-block:: bash - salt myminion dockerng.kill mycontainer + salt myminion docker.kill mycontainer ''' return _change_state(name, 'kill', 'stopped') @@ -4870,7 +4876,7 @@ def pause(name): .. code-block:: bash - salt myminion dockerng.pause mycontainer + salt myminion docker.pause mycontainer ''' orig_state = state(name) if orig_state == 'stopped': @@ -4911,8 +4917,8 @@ def restart(name, timeout=10): .. code-block:: bash - salt myminion dockerng.restart mycontainer - salt myminion dockerng.restart mycontainer timeout=20 + salt myminion docker.restart mycontainer + salt myminion docker.restart mycontainer timeout=20 ''' ret = _change_state(name, 'restart', 'running', timeout=timeout) if ret['result']: @@ -4943,7 +4949,7 @@ def signal_(name, signal): .. code-block:: bash - salt myminion dockerng.signal mycontainer SIGHUP + salt myminion docker.signal mycontainer SIGHUP ''' _client_wrapper('kill', name, signal=signal) return True @@ -4972,7 +4978,7 @@ def start(name): .. code-block:: bash - salt myminion dockerng.start mycontainer + salt myminion docker.start mycontainer ''' orig_state = state(name) if orig_state == 'paused': @@ -5016,9 +5022,9 @@ def stop(name, timeout=STOP_TIMEOUT, **kwargs): .. code-block:: bash - salt myminion dockerng.stop mycontainer - salt myminion dockerng.stop mycontainer unpause=True - salt myminion dockerng.stop mycontainer timeout=20 + salt myminion docker.stop mycontainer + salt myminion docker.stop mycontainer unpause=True + salt myminion docker.stop mycontainer timeout=20 ''' orig_state = state(name) if orig_state == 'paused': @@ -5065,7 +5071,7 @@ def unpause(name): .. code-block:: bash - salt myminion dockerng.pause mycontainer + salt myminion docker.pause mycontainer ''' orig_state = state(name) if orig_state == 'stopped': @@ -5112,7 +5118,7 @@ def wait(name, ignore_already_stopped=False, fail_on_exit_status=False): .. code-block:: bash - salt myminion dockerng.wait mycontainer + salt myminion docker.wait mycontainer ''' try: pre = state(name) @@ -5207,10 +5213,8 @@ def _script(name, os.remove(path) except (IOError, OSError) as exc: log.error( - 'cmd.script: Unable to clean tempfile \'{0}\': {1}'.format( - path, - exc - ) + 'cmd.script: Unable to clean tempfile \'%s\': %s', + path, exc ) path = salt.utils.files.mkstemp(dir='/tmp', @@ -5300,7 +5304,7 @@ def retcode(name, .. code-block:: bash - salt myminion dockerng.retcode mycontainer 'ls -l /etc' + salt myminion docker.retcode mycontainer 'ls -l /etc' ''' return _run(name, cmd, @@ -5357,7 +5361,7 @@ def run(name, .. code-block:: bash - salt myminion dockerng.run mycontainer 'ls -l /etc' + salt myminion docker.run mycontainer 'ls -l /etc' ''' return _run(name, cmd, @@ -5420,7 +5424,7 @@ def run_all(name, .. code-block:: bash - salt myminion dockerng.run_all mycontainer 'ls -l /etc' + salt myminion docker.run_all mycontainer 'ls -l /etc' ''' return _run(name, cmd, @@ -5478,7 +5482,7 @@ def run_stderr(name, .. code-block:: bash - salt myminion dockerng.run_stderr mycontainer 'ls -l /etc' + salt myminion docker.run_stderr mycontainer 'ls -l /etc' ''' return _run(name, cmd, @@ -5536,7 +5540,7 @@ def run_stdout(name, .. code-block:: bash - salt myminion dockerng.run_stdout mycontainer 'ls -l /etc' + salt myminion docker.run_stdout mycontainer 'ls -l /etc' ''' return _run(name, cmd, @@ -5610,9 +5614,9 @@ def script(name, .. code-block:: bash - salt myminion dockerng.script mycontainer salt://docker_script.py - salt myminion dockerng.script mycontainer salt://scripts/runme.sh 'arg1 arg2 "arg 3"' - salt myminion dockerng.script mycontainer salt://scripts/runme.sh stdin='one\\ntwo\\nthree\\nfour\\nfive\\n' output_loglevel=quiet + salt myminion docker.script mycontainer salt://docker_script.py + salt myminion docker.script mycontainer salt://scripts/runme.sh 'arg1 arg2 "arg 3"' + salt myminion docker.script mycontainer salt://scripts/runme.sh stdin='one\\ntwo\\nthree\\nfour\\nfive\\n' output_loglevel=quiet ''' return _script(name, source, @@ -5683,9 +5687,9 @@ def script_retcode(name, .. code-block:: bash - salt myminion dockerng.script_retcode mycontainer salt://docker_script.py - salt myminion dockerng.script_retcode mycontainer salt://scripts/runme.sh 'arg1 arg2 "arg 3"' - salt myminion dockerng.script_retcode mycontainer salt://scripts/runme.sh stdin='one\\ntwo\\nthree\\nfour\\nfive\\n' output_loglevel=quiet + salt myminion docker.script_retcode mycontainer salt://docker_script.py + salt myminion docker.script_retcode mycontainer salt://scripts/runme.sh 'arg1 arg2 "arg 3"' + salt myminion docker.script_retcode mycontainer salt://scripts/runme.sh stdin='one\\ntwo\\nthree\\nfour\\nfive\\n' output_loglevel=quiet ''' return _script(name, source, @@ -5712,7 +5716,7 @@ def _mk_fileclient(): def _generate_tmp_path(): return os.path.join( '/tmp', - 'salt.dockerng.{0}'.format(uuid.uuid4().hex[:6])) + 'salt.docker.{0}'.format(uuid.uuid4().hex[:6])) def _prepare_trans_tar(name, mods=None, saltenv='base', pillar=None): @@ -5784,7 +5788,7 @@ def call(name, function, *args, **kwargs): .. code-block:: bash - salt myminion dockerng.call test.ping + salt myminion docker.call test.ping salt myminion test.arg arg1 arg2 key1=val1 @@ -5799,7 +5803,7 @@ def call(name, function, *args, **kwargs): mkdirp_thin_argv = ['mkdir', '-p', thin_dest_path] # put_archive reqires the path to exist - ret = __salt__['dockerng.run_all'](name, subprocess.list2cmdline(mkdirp_thin_argv)) + ret = run_all(name, subprocess.list2cmdline(mkdirp_thin_argv)) if ret['retcode'] != 0: return {'result': False, 'comment': ret['stderr']} @@ -5824,8 +5828,7 @@ def call(name, function, *args, **kwargs): function ] + list(args) + ['{0}={1}'.format(key, value) for (key, value) in kwargs.items() if not key.startswith('__')] - ret = __salt__['dockerng.run_all'](name, - subprocess.list2cmdline(map(str, salt_argv))) + ret = run_all(name, subprocess.list2cmdline(map(str, salt_argv))) # python not found if ret['retcode'] != 0: raise CommandExecutionError(ret['stderr']) @@ -5844,7 +5847,7 @@ def call(name, function, *args, **kwargs): finally: # delete the thin dir so that it does not end in the image rm_thin_argv = ['rm', '-rf', thin_dest_path] - __salt__['dockerng.run_all'](name, subprocess.list2cmdline(rm_thin_argv)) + run_all(name, subprocess.list2cmdline(rm_thin_argv)) def sls(name, mods=None, saltenv='base', **kwargs): @@ -5859,7 +5862,7 @@ def sls(name, mods=None, saltenv='base', **kwargs): .. code-block:: bash - salt myminion dockerng.sls compassionate_mirzakhani mods=rails,web + salt myminion docker.sls compassionate_mirzakhani mods=rails,web The container does not need to have Salt installed, but Python is required. @@ -5869,7 +5872,7 @@ def sls(name, mods=None, saltenv='base', **kwargs): mods = [item.strip() for item in mods.split(',')] if mods else [] # gather grains from the container - grains = __salt__['dockerng.call'](name, 'grains.items') + grains = call(name, 'grains.items') # compile pillar with container grains pillar = _gather_pillar(saltenv, {}, **grains) @@ -5880,34 +5883,36 @@ def sls(name, mods=None, saltenv='base', **kwargs): trans_dest_path = _generate_tmp_path() mkdirp_trans_argv = ['mkdir', '-p', trans_dest_path] # put_archive requires the path to exist - ret = __salt__['dockerng.run_all'](name, subprocess.list2cmdline(mkdirp_trans_argv)) + ret = run_all(name, subprocess.list2cmdline(mkdirp_trans_argv)) if ret['retcode'] != 0: return {'result': False, 'comment': ret['stderr']} ret = None try: trans_tar_sha256 = salt.utils.get_hash(trans_tar, 'sha256') - __salt__['dockerng.copy_to'](name, trans_tar, - os.path.join(trans_dest_path, 'salt_state.tgz'), - exec_driver='nsenter', - overwrite=True) + copy_to(name, + trans_tar, + os.path.join(trans_dest_path, 'salt_state.tgz'), + exec_driver='nsenter', + overwrite=True) # Now execute the state into the container - ret = __salt__['dockerng.call'](name, 'state.pkg', os.path.join(trans_dest_path, 'salt_state.tgz'), - trans_tar_sha256, 'sha256') + ret = call(name, + 'state.pkg', + os.path.join(trans_dest_path, 'salt_state.tgz'), + trans_tar_sha256, + 'sha256') finally: # delete the trans dir so that it does not end in the image rm_trans_argv = ['rm', '-rf', trans_dest_path] - __salt__['dockerng.run_all'](name, subprocess.list2cmdline(rm_trans_argv)) + run_all(name, subprocess.list2cmdline(rm_trans_argv)) # delete the local version of the trans tar try: os.remove(trans_tar) except (IOError, OSError) as exc: log.error( - 'dockerng.sls: Unable to remove state tarball \'{0}\': {1}'.format( - trans_tar, - exc - ) + 'docker.sls: Unable to remove state tarball \'%s\': %s', + trans_tar, exc ) if not isinstance(ret, dict): __context__['retcode'] = 1 @@ -5949,7 +5954,7 @@ def sls_build(name, base='opensuse/python', mods=None, saltenv='base', .. code-block:: bash - salt myminion dockerng.sls_build imgname base=mybase mods=rails,web + salt myminion docker.sls_build imgname base=mybase mods=rails,web The base image does not need to have Salt installed, but Python is required. @@ -5965,24 +5970,24 @@ def sls_build(name, base='opensuse/python', mods=None, saltenv='base', pass # start a new container - ret = __salt__['dockerng.create'](image=base, - name=name, - cmd='sleep infinity', - interactive=True, tty=True, - **create_kwargs) + ret = create(image=base, + name=name, + cmd='sleep infinity', + interactive=True, tty=True, + **create_kwargs) id_ = ret['Id'] try: - __salt__['dockerng.start'](id_) + start(id_) # Now execute the state into the container - ret = __salt__['dockerng.sls'](id_, mods, saltenv, **kwargs) + ret = sls(id_, mods, saltenv, **kwargs) # fail if the state was not successful if not dryrun and not salt.utils.check_state_result(ret): raise CommandExecutionError(ret) finally: - __salt__['dockerng.stop'](id_) + stop(id_) if dryrun: - __salt__['dockerng.rm'](id_) + rm_(id_) return ret - return __salt__['dockerng.commit'](id_, name) + return commit(id_, name) diff --git a/salt/modules/dockerio.py b/salt/modules/dockerio.py deleted file mode 100644 index 499ac9a4cb..0000000000 --- a/salt/modules/dockerio.py +++ /dev/null @@ -1,2336 +0,0 @@ -# -*- coding: utf-8 -*- -''' -Management of Docker Containers - -.. versionadded:: 2014.1.0 - -.. deprecated:: 2015.8.0 - Future feature development will be done only in :mod:`dockerng - `. See the documentation for this module for - information on the deprecation path. - -.. note:: - - The DockerIO integration is still in beta; the API is subject to change - -General Notes -------------- - -As we use states, we don't want to be continuously popping dockers, so we -will map each container id (or image) with a grain whenever it is relevant. - -As a corollary, we will resolve a container id either directly by the id -or try to find a container id matching something stocked in grain. - -Installation Prerequisites --------------------------- - -- You will need the ``docker-py`` python package in your python installation - path that is running salt. Its version should support `Docker Remote API - v1.12 `_. - - Currently, ``docker-py 0.6.0`` is known to support `Docker Remote API v1.12 - `_ - - .. code-block:: bash - - pip install docker-py==0.6.0 - -Prerequisite Pillar Configuration for Authentication ----------------------------------------------------- - -- To push or pull you will need to be authenticated as the ``docker-py`` bindings - require it -- For this to happen, you will need to configure a mapping in the pillar - representing your per URL authentication bits: - - .. code-block:: yaml - - docker-registries: - registry_url: - email: foo@foo.com - password: s3cr3t - username: foo - -- You need at least an entry to the default docker index: - - .. code-block:: yaml - - docker-registries: - https://index.docker.io/v1/: - email: foo@foo.com - password: s3cr3t - username: foo - -- You can define multiple registry blocks for them to be aggregated. The only thing to keep - in mind is that their ID must finish with ``-docker-registries``: - - .. code-block:: yaml - - ac-docker-registries: - https://index.bar.io/v1/: - email: foo@foo.com - password: s3cr3t - username: foo - - ab-docker-registries: - https://index.foo.io/v1/: - email: foo@foo.com - password: s3cr3t - username: foo - - This could be also written as: - - .. code-block:: yaml - - docker-registries: - https://index.bar.io/v1/: - email: foo@foo.com - password: s3cr3t - username: foo - https://index.foo.io/v1/: - email: foo@foo.com - password: s3cr3t - username: foo - -Methods -_______ - -- Registry Dialog - - :py:func:`login` - - :py:func:`push` - - :py:func:`pull` -- Docker Management - - :py:func:`version` - - :py:func:`info` -- Image Management - - :py:func:`search` - - :py:func:`inspect_image` - - :py:func:`get_images` - - :py:func:`remove_image` - - :py:func:`import_image` - - :py:func:`build` - - :py:func:`tag` - - :py:func:`save` - - :py:func:`load` -- Container Management - - :py:func:`start` - - :py:func:`stop` - - :py:func:`restart` - - :py:func:`kill` - - :py:func:`wait` - - :py:func:`get_containers` - - :py:func:`inspect_container` - - :py:func:`remove_container` - - :py:func:`is_running` - - :py:func:`top` - - :py:func:`port` - - :py:func:`logs` - - :py:func:`diff` - - :py:func:`commit` - - :py:func:`create_container` - - :py:func:`export` - - :py:func:`get_container_root` - -Runtime Execution within a specific, already existing/running container --------------------------------------------------------------------------- - -Idea is to use `lxc-attach `_ to execute -inside the container context. -We do not want to use ``docker run`` but want to execute something inside a -running container. - -These are the available methods: - -- :py:func:`retcode` -- :py:func:`run` -- :py:func:`run_all` -- :py:func:`run_stderr` -- :py:func:`run_stdout` -- :py:func:`script` -- :py:func:`script_retcode` - -''' - -# Import Python Futures -from __future__ import absolute_import - -__docformat__ = 'restructuredtext en' - -# Import Python libs -import datetime -import json -import logging -import os -import re -import traceback -import shutil -import types - -# Import Salt libs -from salt.modules import cmdmod -from salt.exceptions import CommandExecutionError, SaltInvocationError -import salt.utils -import salt.utils.files -import salt.utils.odict - -# Import 3rd-party libs -import salt.ext.six as six -# pylint: disable=import-error -from salt.ext.six.moves import range # pylint: disable=no-name-in-module,redefined-builtin -try: - import docker - HAS_DOCKER = True -except ImportError: - HAS_DOCKER = False -# pylint: enable=import-error - -HAS_NSENTER = bool(salt.utils.which('nsenter')) - - -log = logging.getLogger(__name__) - -INVALID_RESPONSE = 'We did not get any expected answer from docker' -VALID_RESPONSE = '' -NOTSET = object() -base_status = { - 'status': None, - 'id': None, - 'comment': '', - 'out': None -} - -# Define the module's virtual name -__virtualname__ = 'docker' - - -def __virtual__(): - ''' - Only load if docker libs are present - ''' - if HAS_DOCKER: - return __virtualname__ - return (False, 'dockerio execution module not loaded: docker python library not available.') - - -def _sizeof_fmt(num): - ''' - Return disk format size data - ''' - for unit in ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB']: - if num < 1024.0: - return '{0:3.1f} {1}'.format(num, unit) - num /= 1024.0 - - -def _set_status(m, - id_=NOTSET, - comment=INVALID_RESPONSE, - status=False, - out=None): - ''' - Assign status data to a dict - ''' - m['comment'] = comment - m['status'] = status - m['out'] = out - if id_ is not NOTSET: - m['id'] = id_ - return m - - -def _invalid(m, id_=NOTSET, comment=INVALID_RESPONSE, out=None): - ''' - Return invalid status - ''' - return _set_status(m, status=False, id_=id_, comment=comment, out=out) - - -def _valid(m, id_=NOTSET, comment=VALID_RESPONSE, out=None): - ''' - Return valid status - ''' - return _set_status(m, status=True, id_=id_, comment=comment, out=out) - - -def _get_client(timeout=None): - ''' - Get a connection to a docker API (socket or URL) - based on config.get mechanism (pillar -> grains) - - By default it will use the base docker-py defaults which - at the time of writing are using the local socket and - the 1.4 API - - Set those keys in your configuration tree somehow: - - - docker.url: URL to the docker service - - docker.version: API version to use - - ''' - kwargs = {} - get = __salt__['config.get'] - for key, val in (('base_url', 'docker.url'), - ('version', 'docker.version')): - param = get(val, NOTSET) - if param is not NOTSET: - kwargs[key] = param - if timeout is not None: - # make sure we override default timeout of docker-py - # only if defined by user. - kwargs['timeout'] = timeout - - if 'version' not in kwargs: - # Let docker-py auto detect docker version incase - # it's not defined by user. - kwargs['version'] = 'auto' - - if 'base_url' not in kwargs and 'DOCKER_HOST' in os.environ: - # Check if the DOCKER_HOST environment variable has been set - kwargs['base_url'] = os.environ.get('DOCKER_HOST') - - client = docker.Client(**kwargs) - - # try to authenticate the client using credentials - # found in pillars - registry_auth_config = __pillar__.get('docker-registries', {}) - for key, data in six.iteritems(__pillar__): - if key.endswith('-docker-registries'): - registry_auth_config.update(data) - - for registry, creds in six.iteritems(registry_auth_config): - client.login(creds['username'], password=creds['password'], - email=creds.get('email'), registry=registry) - - return client - - -def _get_image_infos(image): - ''' - Verify that the image exists - We will try to resolve either by: - - name - - image_id - - tag - - image - Image Name / Image Id / Image Tag - - Returns the image id - ''' - status = base_status.copy() - client = _get_client() - try: - infos = client.inspect_image(image) - if infos: - _valid(status, - id_=infos['Id'], - out=infos, - comment='found') - except Exception: - pass - if not status['id']: - _invalid(status) - raise CommandExecutionError( - 'ImageID \'{0}\' could not be resolved to ' - 'an existing Image'.format(image) - ) - return status['out'] - - -def _get_container_infos(container): - ''' - Get container infos - We will try to resolve either by: - - the mapping grain->docker id or directly - - dockerid - - container - Image Id / grain name - ''' - status = base_status.copy() - client = _get_client() - try: - container_info = client.inspect_container(container) - if container_info: - _valid(status, - id_=container_info['Id'], - out=container_info) - except Exception: - pass - if not status['id']: - raise CommandExecutionError( - 'Container_id {0} could not be resolved to ' - 'an existing container'.format( - container) - ) - if 'id' not in status['out'] and 'Id' in status['out']: - status['out']['id'] = status['out']['Id'] - return status['out'] - - -def get_containers(all=True, - trunc=False, - since=None, - before=None, - limit=-1, - host=False, - inspect=False): - ''' - Get a list of mappings representing all containers - - all - return all containers, Default is ``True`` - - trunc - set it to True to have the short ID, Default is ``False`` - - host - include the Docker host's ipv4 and ipv6 address in return, Default is ``False`` - - inspect - Get more granular information about each container by running a docker inspect - - CLI Example: - - .. code-block:: bash - - salt '*' docker.get_containers - salt '*' docker.get_containers host=True - salt '*' docker.get_containers host=True inspect=True - ''' - - client = _get_client() - status = base_status.copy() - - if host: - status['host'] = {} - status['host']['interfaces'] = __salt__['network.interfaces']() - - containers = client.containers(all=all, - trunc=trunc, - since=since, - before=before, - limit=limit) - - # Optionally for each container get more granular information from them - # by inspecting the container - if inspect: - for container in containers: - container_id = container.get('Id') - if container_id: - inspect = _get_container_infos(container_id) - container['detail'] = inspect.copy() - - _valid(status, comment='All containers in out', out=containers) - - return status - - -def logs(container): - ''' - Return logs for a specified container - - container - container id - - CLI Example: - - .. code-block:: bash - - salt '*' docker.logs - ''' - status = base_status.copy() - client = _get_client() - try: - container_logs = client.logs(_get_container_infos(container)['Id']) - _valid(status, id_=container, out=container_logs) - except Exception: - _invalid(status, id_=container, out=traceback.format_exc()) - return status - - -def commit(container, - repository=None, - tag=None, - message=None, - author=None, - conf=None): - ''' - Commit a container (promotes it to an image) - - container - container id - repository - repository/image to commit to - tag - tag of the image (Optional) - message - commit message (Optional) - author - author name (Optional) - conf - conf (Optional) - - CLI Example: - - .. code-block:: bash - - salt '*' docker.commit - ''' - status = base_status.copy() - client = _get_client() - try: - container = _get_container_infos(container)['Id'] - commit_info = client.commit( - container, - repository=repository, - tag=tag, - message=message, - author=author, - conf=conf) - found = False - for k in ('Id', 'id', 'ID'): - if k in commit_info: - found = True - image_id = commit_info[k] - if not found: - raise Exception('Invalid commit return') - image = _get_image_infos(image_id)['Id'] - comment = 'Image {0} created from {1}'.format(image, container) - _valid(status, id_=image, out=commit_info, comment=comment) - except Exception: - _invalid(status, id_=container, out=traceback.format_exc()) - return status - - -def diff(container): - ''' - Get container diffs - - container - container id - - CLI Example: - - .. code-block:: bash - - salt '*' docker.diff - ''' - status = base_status.copy() - client = _get_client() - try: - container_diff = client.diff(_get_container_infos(container)['Id']) - _valid(status, id_=container, out=container_diff) - except Exception: - _invalid(status, id_=container, out=traceback.format_exc()) - return status - - -def export(container, path): - ''' - Export a container to a file - - container - container id - path - path to which file is to be exported - - CLI Example: - - .. code-block:: bash - - salt '*' docker.export - ''' - try: - ppath = os.path.abspath(path) - with salt.utils.fopen(ppath, 'w') as fic: - status = base_status.copy() - client = _get_client() - response = client.export(_get_container_infos(container)['Id']) - byte = response.read(4096) - fic.write(byte) - while byte != '': - # Do stuff with byte. - byte = response.read(4096) - fic.write(byte) - fic.flush() - _valid(status, - id_=container, out=ppath, - comment='Exported to {0}'.format(ppath)) - except Exception: - _invalid(status, id_=container, out=traceback.format_exc()) - return status - - -def create_container(image, - command=None, - hostname=None, - user=None, - detach=True, - stdin_open=False, - tty=False, - mem_limit=None, - ports=None, - environment=None, - dns=None, - volumes=None, - volumes_from=None, - name=None, - cpu_shares=None, - cpuset=None, - binds=None): - ''' - Create a new container - - image - image to create the container from - command - command to execute while starting - hostname - hostname of the container - user - user to run docker as - detach - daemon mode, Default is ``True`` - environment - environment variable mapping ``({'foo':'BAR'})`` - ports - port redirections ``({'222': {}})`` - volumes - list of volume mappings in either local volume, bound volume, or read-only - bound volume form:: - - (['/var/lib/mysql/', '/usr/local/etc/ssl:/etc/ssl', '/etc/passwd:/etc/passwd:ro']) - binds - complete dictionary of bound volume mappings:: - - { '/usr/local/etc/ssl/certs/internal.crt': { - 'bind': '/etc/ssl/certs/com.example.internal.crt', - 'ro': True - }, - '/var/lib/mysql': { - 'bind': '/var/lib/mysql/', - 'ro': False - } - } - - This dictionary is suitable for feeding directly into the Docker API, and all - keys are required. - (see https://docker-py.readthedocs.io/en/latest/volumes/) - tty - attach ttys, Default is ``False`` - stdin_open - let stdin open, Default is ``False`` - name - name given to container - cpu_shares - CPU shares (relative weight) - cpuset - CPUs in which to allow execution ('0-3' or '0,1') - - CLI Example: - - .. code-block:: bash - - salt '*' docker.create_container o/ubuntu volumes="['/s','/m:/f']" - - ''' - log.trace("modules.dockerio.create_container() called for image " + image) - status = base_status.copy() - client = _get_client() - - # In order to permit specification of bind volumes in the volumes field, - # we'll look through it for bind-style specs and move them. This is purely - # for CLI convenience and backwards-compatibility, as states.dockerio - # should parse volumes before this, and the binds argument duplicates this. - # N.B. this duplicates code in states.dockerio._parse_volumes() - if isinstance(volumes, list): - for volume in volumes: - if ':' in volume: - volspec = volume.split(':') - source = volspec[0] - target = volspec[1] - ro = False - try: - if len(volspec) > 2: - ro = volspec[2] == "ro" - except IndexError: - pass - binds[source] = {'bind': target, 'ro': ro} - volumes.remove(volume) - - try: - if salt.utils.version_cmp(client.version()['ApiVersion'], '1.18') == 1: - container_info = client.create_container( - image=image, - command=command, - hostname=hostname, - user=user, - detach=detach, - stdin_open=stdin_open, - tty=tty, - ports=ports, - environment=environment, - dns=dns, - volumes=volumes, - volumes_from=volumes_from, - name=name, - cpu_shares=cpu_shares, - cpuset=cpuset, - host_config=docker.utils.create_host_config(binds=binds, - mem_limit=mem_limit) - ) - else: - container_info = client.create_container( - image=image, - command=command, - hostname=hostname, - user=user, - detach=detach, - stdin_open=stdin_open, - tty=tty, - mem_limit=mem_limit, - ports=ports, - environment=environment, - dns=dns, - volumes=volumes, - volumes_from=volumes_from, - name=name, - cpu_shares=cpu_shares, - cpuset=cpuset, - host_config=docker.utils.create_host_config(binds=binds) - ) - - log.trace("docker.client.create_container returned: " + str(container_info)) - container = container_info['Id'] - callback = _valid - comment = 'Container created' - out = { - 'info': _get_container_infos(container), - 'out': container_info - } - __salt__['mine.send']('dockerng.ps', verbose=True, all=True, host=True) - return callback(status, id_=container, comment=comment, out=out) - except Exception as e: - _invalid(status, id_=image, out=traceback.format_exc()) - raise e - __salt__['mine.send']('dockerng.ps', verbose=True, all=True, host=True) - return status - - -def version(): - ''' - Get docker version - - CLI Example: - - .. code-block:: bash - - salt '*' docker.version - ''' - status = base_status.copy() - client = _get_client() - try: - docker_version = client.version() - _valid(status, out=docker_version) - except Exception: - _invalid(status, out=traceback.format_exc()) - return status - - -def info(): - ''' - Get the version information about docker. This is similar to ``docker info`` command - - CLI Example: - - .. code-block:: bash - - salt '*' docker.info - ''' - status = base_status.copy() - client = _get_client() - try: - version_info = client.info() - _valid(status, out=version_info) - except Exception: - _invalid(status, out=traceback.format_exc()) - return status - - -def port(container, private_port): - ''' - Private port mapping allocation information. This method is broken on docker-py - side. Just use the result of inspect to mangle port - allocation - - container - container id - - private_port - private port on the container to query for - - CLI Example: - - .. code-block:: bash - - salt '*' docker.port - ''' - status = base_status.copy() - client = _get_client() - try: - port_info = client.port( - _get_container_infos(container)['Id'], - private_port) - _valid(status, id_=container, out=port_info) - except Exception: - _invalid(status, id_=container, out=traceback.format_exc()) - return status - - -def stop(container, timeout=10): - ''' - Stop a running container - - container - container id - - timeout - timeout for container to exit gracefully before killing it, Default is ``10`` seconds - - CLI Example: - - .. code-block:: bash - - salt '*' docker.stop [timeout=20] - ''' - client = _get_client() - status = base_status.copy() - try: - dcontainer = _get_container_infos(container)['Id'] - if is_running(dcontainer): - client.stop(dcontainer, timeout=timeout) - if not is_running(dcontainer): - _valid( - status, - comment='Container {0} was stopped'.format( - container), - id_=container) - else: - _invalid(status) - else: - _valid(status, - comment='Container {0} was already stopped'.format( - container), - id_=container) - except Exception: - _invalid(status, id_=container, out=traceback.format_exc(), - comment=( - 'An exception occurred while stopping ' - 'your container {0}').format(container)) - __salt__['mine.send']('dockerng.ps', verbose=True, all=True, host=True) - return status - - -def kill(container, signal=None): - ''' - Kill a running container - - container - container id - signal - signal to send - - .. versionadded:: 2015.8.0 - - CLI Example: - - .. code-block:: bash - - salt '*' docker.kill - ''' - client = _get_client() - status = base_status.copy() - try: - dcontainer = _get_container_infos(container)['Id'] - if is_running(dcontainer): - client.kill(dcontainer, signal=signal) - if signal: - # no need to check if container is running - # because some signals might not stop the container. - _valid(status, - comment='Kill signal \'{0}\' successfully' - ' sent to the container \'{1}\''.format(signal, container), - id_=container) - else: - if not is_running(dcontainer): - _valid(status, - comment='Container {0} was killed'.format(container), - id_=container) - else: - _invalid(status, - comment='Container {0} was not killed'.format( - container)) - else: - _valid(status, - comment='Container {0} was already stopped'.format( - container), - id_=container) - except Exception: - _invalid(status, - id_=container, - out=traceback.format_exc(), - comment=( - 'An exception occurred while killing ' - 'your container {0}').format(container)) - __salt__['mine.send']('dockerng.ps', verbose=True, all=True, host=True) - return status - - -def restart(container, timeout=10): - ''' - Restart a running container - - container - container id - - timeout - timeout for container to exit gracefully before killing it, Default is ``10`` seconds - - CLI Example: - - .. code-block:: bash - - salt '*' docker.restart [timeout=20] - ''' - client = _get_client() - status = base_status.copy() - try: - dcontainer = _get_container_infos(container)['Id'] - client.restart(dcontainer, timeout=timeout) - if is_running(dcontainer): - _valid(status, - comment='Container {0} was restarted'.format(container), - id_=container) - else: - _invalid(status) - except Exception: - _invalid(status, id_=container, out=traceback.format_exc(), - comment=( - 'An exception occurred while restarting ' - 'your container {0}').format(container)) - __salt__['mine.send']('dockerng.ps', verbose=True, all=True, host=True) - return status - - -def start(container, - binds=None, - port_bindings=None, - lxc_conf=None, - publish_all_ports=None, - links=None, - privileged=False, - dns=None, - volumes_from=None, - network_mode=None, - restart_policy=None, - cap_add=None, - cap_drop=None): - ''' - Start the specified container - - container - container id - - CLI Example: - - .. code-block:: bash - - salt '*' docker.start - ''' - if binds: - if not isinstance(binds, dict): - raise SaltInvocationError('binds must be formatted as a dictionary') - - client = _get_client() - status = base_status.copy() - try: - dcontainer = _get_container_infos(container)['Id'] - if not is_running(container): - bindings = None - if port_bindings is not None: - try: - bindings = {} - for key, val in six.iteritems(port_bindings): - bindings[key] = (val.get('HostIp', ''), val['HostPort']) - except AttributeError: - raise SaltInvocationError( - 'port_bindings must be formatted as a dictionary of ' - 'dictionaries' - ) - client.start(dcontainer, - binds=binds, - port_bindings=bindings, - lxc_conf=lxc_conf, - publish_all_ports=publish_all_ports, - links=links, - privileged=privileged, - dns=dns, - volumes_from=volumes_from, - network_mode=network_mode, - restart_policy=restart_policy, - cap_add=cap_add, - cap_drop=cap_drop) - - if is_running(dcontainer): - _valid(status, - comment='Container {0} was started'.format(container), - id_=container) - else: - _invalid(status) - else: - _valid(status, - comment='Container {0} was already started'.format(container), - id_=container) - except Exception: - _invalid(status, - id_=container, - out=traceback.format_exc(), - comment=( - 'An exception occurred while starting ' - 'your container {0}').format(container)) - __salt__['mine.send']('dockerng.ps', verbose=True, all=True, host=True) - return status - - -def wait(container): - ''' - Wait for a container to exit gracefully - - container - container id - - CLI Example: - - .. code-block:: bash - - salt '*' docker.wait - ''' - client = _get_client() - status = base_status.copy() - try: - dcontainer = _get_container_infos(container)['Id'] - if is_running(dcontainer): - client.wait(dcontainer) - if not is_running(container): - _valid(status, - id_=container, - comment='Container waited for stop') - else: - _invalid(status) - else: - _valid(status, - comment='Container {0} was already stopped'.format(container), - id_=container) - except Exception: - _invalid(status, id_=container, out=traceback.format_exc(), - comment=( - 'An exception occurred while waiting ' - 'your container {0}').format(container)) - __salt__['mine.send']('dockerng.ps', verbose=True, all=True, host=True) - return status - - -def exists(container): - ''' - Check if a given container exists - - container - container id - - Returns ``True`` if container exists otherwise returns ``False`` - - CLI Example: - - .. code-block:: bash - - salt '*' docker.exists - - ''' - try: - _get_container_infos(container) - return True - except Exception: - return False - - -def is_running(container): - ''' - Check if the specified container is running - - container - container id - - Returns ``True`` if container is running otherwise returns ``False`` - - CLI Example: - - .. code-block:: bash - - salt '*' docker.is_running - ''' - try: - infos = _get_container_infos(container) - return infos.get('State', {}).get('Running') - except Exception: - return False - - -def remove_container(container, force=False, v=False): - ''' - Remove a container from a docker installation - - container - container id - - force - remove a running container, Default is ``False`` - - v - remove the volumes associated to the container, Default is ``False`` - - CLI Example: - - .. code-block:: bash - - salt '*' docker.remove_container [force=True|False] [v=True|False] - ''' - client = _get_client() - status = base_status.copy() - status['id'] = container - dcontainer = None - try: - dcontainer = _get_container_infos(container)['Id'] - if is_running(dcontainer): - if not force: - _invalid(status, id_=container, out=None, - comment=( - 'Container {0} is running, ' - 'won\'t remove it').format(container)) - __salt__['mine.send']('dockerng.ps', verbose=True, all=True, host=True) - return status - else: - kill(dcontainer) - client.remove_container(dcontainer, v=v) - try: - _get_container_infos(dcontainer) - _invalid(status, - comment='Container was not removed: {0}'.format(container)) - except Exception: - status['status'] = True - status['comment'] = 'Container {0} was removed'.format(container) - except Exception: - _invalid(status, id_=container, out=traceback.format_exc()) - __salt__['mine.send']('dockerng.ps', verbose=True, all=True, host=True) - return status - - -def top(container): - ''' - Run the docker top command on a specific container - - container - container id - - CLI Example: - - .. code-block:: bash - - salt '*' docker.top - ''' - client = _get_client() - status = base_status.copy() - try: - dcontainer = _get_container_infos(container)['Id'] - if is_running(dcontainer): - ret = client.top(dcontainer) - if ret: - ret['mprocesses'] = [] - titles = ret['Titles'] - for i in ret['Processes']: - data = salt.utils.odict.OrderedDict() - for k, j in enumerate(titles): - data[j] = i[k] - ret['mprocesses'].append(data) - _valid(status, - out=ret, - id_=container, - comment='Current top for container') - if not status['id']: - _invalid(status) - else: - _invalid(status, - comment='Container {0} is not running'.format(container)) - except Exception: - _invalid(status, id_=container, out=traceback.format_exc()) - return status - - -def inspect_container(container): - ''' - Get container information. This is similar to ``docker inspect`` command but only for containers - - container - container id - - CLI Example: - - .. code-block:: bash - - salt '*' docker.inspect_container - - ''' - status = base_status.copy() - status['id'] = container - try: - infos = _get_container_infos(container) - _valid(status, id_=container, out=infos) - except Exception: - _invalid(status, id_=container, out=traceback.format_exc(), - comment='Container does not exit: {0}'.format(container)) - return status - - -def login(url=None, username=None, password=None, email=None): - ''' - Wrapper to the ``docker.py`` login method (does not do much yet) - - url - registry url to authenticate to - - username - username to authenticate - - password - password to authenticate - - email - email to authenticate - - CLI Example: - - .. code-block:: bash - - salt '*' docker.login - ''' - client = _get_client() - return client.login(username, password, email, url) - - -def search(term): - ''' - Search for an image on the registry - - term - search keyword - - CLI Example: - - .. code-block:: bash - - salt '*' docker.search - ''' - client = _get_client() - status = base_status.copy() - ret = client.search(term) - if ret: - _valid(status, out=ret, id_=term) - else: - _invalid(status) - return status - - -def _create_image_assemble_error_status(status, ret, image_logs): - ''' - Given input in this form:: - - [{u'error': u'Get file:///r.tar.gz: unsupported protocol scheme "file"', - u'errorDetail': { - u'message':u'Get file:///r.tar.gz:unsupported protocol scheme "file"'}}, - {u'status': u'Downloading from file:///r.tar.gz'}] - ''' - comment = 'An error occurred while importing your image' - out = None - is_invalid = True - status['out'] = '' - try: - is_invalid = False - status['out'] += '\n' + ret - for err_log in image_logs: - if isinstance(err_log, dict): - if 'errorDetail' in err_log: - if 'code' in err_log['errorDetail']: - msg = '\n{0}\n{1}: {2}'.format( - err_log['error'], - err_log['errorDetail']['code'], - err_log['errorDetail']['message'] - ) - else: - msg = '\n{0}\n{1}'.format( - err_log['error'], - err_log['errorDetail']['message'], - ) - comment += msg - except Exception: - is_invalid = True - trace = traceback.format_exc() - out = ( - 'An error occurred while ' - 'parsing error output:\n{0}' - ).format(trace) - if is_invalid: - _invalid(status, out=out, comment=comment) - return status - - -def import_image(src, repo, tag=None): - ''' - Import content from a local tarball or a URL to a docker image - - src - content to import (URL or absolute path to a tarball) - - repo - repository to import to - - tag - set tag of the image (Optional) - - CLI Example: - - .. code-block:: bash - - salt '*' docker.import_image [tag] - ''' - client = _get_client() - status = base_status.copy() - try: - ret = client.import_image(src, repository=repo, tag=tag) - if ret: - image_logs, _info = _parse_image_multilogs_string(ret) - _create_image_assemble_error_status(status, ret, image_logs) - if status['status'] is not False: - infos = _get_image_infos(image_logs[0]['status']) - _valid(status, - comment='Image {0} was created'.format(infos['Id']), - id_=infos['Id'], - out=ret) - else: - _invalid(status) - except Exception: - _invalid(status, out=traceback.format_exc()) - return status - - -def tag(image, repository, tag=None, force=False): - ''' - Tag an image into a repository - - image - name of image - - repository - name of repository - - tag - tag to apply (Optional) - - force - force apply tag, Default is ``False`` - - CLI Example: - - .. code-block:: bash - - salt '*' docker.tag [tag] [force=True|False] - ''' - client = _get_client() - status = base_status.copy() - try: - dimage = _get_image_infos(image)['Id'] - ret = client.tag(dimage, repository, tag=tag, force=force) - except Exception: - _invalid(status, - out=traceback.format_exc(), - comment='Cant tag image {0} {1}{2}'.format( - image, repository, - tag and (':' + tag) or '').strip()) - return status - if ret: - _valid(status, - id_=image, - comment='Image was tagged: {0}{1}'.format( - repository, - tag and (':' + tag) or '').strip()) - else: - _invalid(status) - return status - - -def get_images(name=None, quiet=False, all=True): - ''' - List docker images - - name - repository name - - quiet - only show image id, Default is ``False`` - - all - show all images, Default is ``True`` - - CLI Example: - - .. code-block:: bash - - salt '*' docker.get_images [quiet=True|False] [all=True|False] - ''' - client = _get_client() - status = base_status.copy() - try: - infos = client.images(name=name, quiet=quiet, all=all) - for i in range(len(infos)): - inf = infos[i] - try: - inf['Human_Size'] = _sizeof_fmt(int(inf['Size'])) - except ValueError: - pass - try: - ts = int(inf['Created']) - dts = datetime.datetime.fromtimestamp(ts) - inf['Human_IsoCreated'] = dts.isoformat() - inf['Human_Created'] = dts.strftime( - '%Y-%m-%d %H:%M:%S') - except Exception: - pass - try: - inf['Human_VirtualSize'] = ( - _sizeof_fmt(int(inf['VirtualSize']))) - except ValueError: - pass - _valid(status, out=infos) - except Exception: - _invalid(status, out=traceback.format_exc()) - return status - - -def build(path=None, - tag=None, - quiet=False, - fileobj=None, - nocache=False, - rm=True, - timeout=None): - ''' - Build a docker image from a dockerfile or an URL - - path - url/branch/docker_dir or path on the filesystem to the dockerfile - - tag - tag of the image - - quiet - quiet mode, Default is ``False`` - - nocache - do not use docker image cache, Default is ``False`` - - rm - remove intermediate commits, Default is ``True`` - - timeout - timeout value before aborting (in seconds) - - CLI Example: - - .. code-block:: bash - - salt '*' docker.build vieux/apache - salt '*' docker.build github.com/creack/docker-firefox - ''' - client = _get_client(timeout=timeout) - status = base_status.copy() - if path or fileobj: - try: - ret = client.build(path=path, - tag=tag, - quiet=quiet, - fileobj=fileobj, - rm=rm, - nocache=nocache) - - if isinstance(ret, types.GeneratorType): - - message = json.loads(list(ret)[-1]) - if 'stream' in message: - if 'Successfully built' in message['stream']: - _valid(status, out=message['stream']) - if 'errorDetail' in message: - _invalid(status, out=message['errorDetail']['message']) - - elif isinstance(ret, tuple): - id_, out = ret[0], ret[1] - if id_: - _valid(status, id_=id_, out=out, comment='Image built') - else: - _invalid(status, id_=id_, out=out) - - except Exception: - _invalid(status, - out=traceback.format_exc(), - comment='Unexpected error while building an image') - return status - - return status - - -def remove_image(image): - ''' - Remove an image from a system. - - image - name of image - - CLI Example: - - .. code-block:: bash - - salt '*' docker.remove_image - ''' - client = _get_client() - status = base_status.copy() - # will raise an error if no deletion - try: - infos = _get_image_infos(image) - if infos: - status['id'] = infos['Id'] - try: - client.remove_image(infos['Id']) - except Exception: - _invalid(status, - id_=image, - out=traceback.format_exc(), - comment='Image could not be deleted') - try: - infos = _get_image_infos(image) - _invalid(status, - comment=( - 'Image marked to be deleted but not deleted yet')) - except Exception: - _valid(status, id_=image, comment='Image deleted') - else: - _invalid(status) - except Exception: - _invalid(status, - out=traceback.format_exc(), - comment='Image does not exist: {0}'.format(image)) - return status - - -def inspect_image(image): - ''' - Inspect the status of an image and return relative data. This is similar to - ``docker inspect`` command but only for images. - - image - name of the image - - CLI Example: - - .. code-block:: bash - - salt '*' docker.inspect_image - ''' - status = base_status.copy() - try: - infos = _get_image_infos(image) - try: - for k in ['Size']: - infos[ - 'Human_{0}'.format(k) - ] = _sizeof_fmt(int(infos[k])) - except Exception: - pass - _valid(status, id_=image, out=infos) - except Exception: - _invalid(status, id_=image, out=traceback.format_exc(), - comment='Image does not exist') - return status - - -def _parse_image_multilogs_string(ret): - ''' - Parse image log strings into grokable data - ''' - image_logs, infos = [], None - if ret and ret.strip().startswith('{') and ret.strip().endswith('}'): - pushd = 0 - buf = '' - for char in ret: - buf += char - if char == '{': - pushd += 1 - if char == '}': - pushd -= 1 - if pushd == 0: - try: - buf = json.loads(buf) - except Exception: - pass - else: - image_logs.append(buf) - buf = '' - image_logs.reverse() - - # Valid statest when pulling an image from the docker registry - valid_states = [ - 'Download complete', - 'Already exists', - ] - - # search last layer grabbed - for ilog in image_logs: - if isinstance(ilog, dict): - if ilog.get('status') in valid_states and ilog.get('id'): - infos = _get_image_infos(ilog['id']) - break - - return image_logs, infos - - -def _pull_assemble_error_status(status, ret, logs): - ''' - Given input in this form:: - - u'{"status":"Pulling repository foo/ubuntubox"}: - "image (latest) from foo/ ... - rogress":"complete","id":"2c80228370c9"}' - - construct something like that (load JSON data is possible):: - - [u'{"status":"Pulling repository foo/ubuntubox"', - {"status":"Download","progress":"complete","id":"2c80228370c9"}] - ''' - comment = 'An error occurred pulling your image' - out = '' - try: - out = '\n' + ret - for err_log in logs: - if isinstance(err_log, dict): - if 'errorDetail' in err_log: - if 'code' in err_log['errorDetail']: - msg = '\n{0}\n{1}: {2}'.format( - err_log['error'], - err_log['errorDetail']['code'], - err_log['errorDetail']['message'] - ) - else: - msg = '\n{0}\n{1}'.format( - err_log['error'], - err_log['errorDetail']['message'], - ) - comment += msg - except Exception: - out = traceback.format_exc() - _invalid(status, out=out, comment=comment) - return status - - -def pull(repo, tag=None, insecure_registry=False): - ''' - Pulls an image from any registry. See documentation at top of this page to - configure authenticated access - - repo - name of repository - - tag - specific tag to pull (Optional) - - insecure_registry - set as ``True`` to use insecure (non HTTPS) registry. Default is ``False`` - (only available if using docker-py >= 0.5.0) - - CLI Example: - - .. code-block:: bash - - salt '*' docker.pull [tag] - ''' - client = _get_client() - status = base_status.copy() - try: - kwargs = {'tag': tag} - # if docker-py version is greater than 0.5.0 use the - # insecure_registry parameter - if salt.utils.compare_versions(ver1=docker.__version__, - oper='>=', - ver2='0.5.0'): - kwargs['insecure_registry'] = insecure_registry - ret = client.pull(repo, **kwargs) - if ret: - image_logs, infos = _parse_image_multilogs_string(ret) - if infos and infos.get('Id', None): - repotag = repo - if tag: - repotag = '{0}:{1}'.format(repo, tag) - _valid(status, - out=image_logs if image_logs else ret, - id_=infos['Id'], - comment='Image {0} was pulled ({1})'.format( - repotag, infos['Id'])) - - else: - _pull_assemble_error_status(status, ret, image_logs) - else: - _invalid(status) - except Exception: - _invalid(status, id_=repo, out=traceback.format_exc()) - return status - - -def _push_assemble_error_status(status, ret, logs): - ''' - Given input in this form:: - - u'{"status":"Pulling repository foo/ubuntubox"}: - "image (latest) from foo/ ... - rogress":"complete","id":"2c80228370c9"}' - - construct something like that (load json data is possible):: - - [u'{"status":"Pulling repository foo/ubuntubox"', - {"status":"Download","progress":"complete","id":"2c80228370c9"}] - ''' - comment = 'An error occurred pushing your image' - status['out'] = '' - try: - status['out'] += '\n' + ret - for err_log in logs: - if isinstance(err_log, dict): - if 'errorDetail' in err_log: - if 'code' in err_log['errorDetail']: - msg = '\n{0}\n{1}: {2}'.format( - err_log['error'], - err_log['errorDetail']['code'], - err_log['errorDetail']['message'] - ) - else: - msg = '\n{0}\n{1}'.format( - err_log['error'], - err_log['errorDetail']['message'], - ) - comment += msg - except Exception: - trace = traceback.format_exc() - status['out'] = ( - 'An error occurred while ' - 'parsing error output:\n{0}' - ).format(trace) - _invalid(status, comment=comment) - return status - - -def push(repo, tag=None, quiet=False, insecure_registry=False): - ''' - Pushes an image to any registry. See documentation at top of this page to - configure authenticated access - - repo - name of repository - - tag - specific tag to push (Optional) - - quiet - set as ``True`` to quiet output, Default is ``False`` - - insecure_registry - set as ``True`` to use insecure (non HTTPS) registry. Default is ``False`` - (only available if using docker-py >= 0.5.0) - - CLI Example: - - .. code-block:: bash - - salt '*' docker.push [tag] [quiet=True|False] - ''' - client = _get_client() - status = base_status.copy() - registry, repo_name = docker.auth.resolve_repository_name(repo) - try: - kwargs = {'tag': tag} - # if docker-py version is greater than 0.5.0 use the - # insecure_registry parameter - if salt.utils.compare_versions(ver1=docker.__version__, - oper='>=', - ver2='0.5.0'): - kwargs['insecure_registry'] = insecure_registry - ret = client.push(repo, **kwargs) - if ret: - image_logs, infos = _parse_image_multilogs_string(ret) - if image_logs: - repotag = repo_name - if tag: - repotag = '{0}:{1}'.format(repo, tag) - if not quiet: - status['out'] = image_logs - else: - status['out'] = None - laststatus = image_logs[2].get('status', None) - if laststatus and ( - ('already pushed' in laststatus) - or ('Pushing tags for rev' in laststatus) - or ('Pushing tag for rev' in laststatus) - ): - status['status'] = True - status['id'] = _get_image_infos(repo)['Id'] - status['comment'] = 'Image {0}({1}) was pushed'.format( - repotag, status['id']) - else: - _push_assemble_error_status(status, ret, image_logs) - else: - status['out'] = ret - _push_assemble_error_status(status, ret, image_logs) - else: - _invalid(status) - except Exception: - _invalid(status, id_=repo, out=traceback.format_exc()) - return status - - -def _run_wrapper(status, container, func, cmd, *args, **kwargs): - ''' - Wrapper to a cmdmod function - - Idea is to prefix the call to cmdrun with the relevant driver to - execute inside a container context - - .. note:: - - Only lxc and native drivers are implemented. - - status - status object - container - container id to execute in - func - cmd function to execute - cmd - command to execute in the container - ''' - - client = _get_client() - # For old version of docker. lxc was the only supported driver. - # We can safely hardcode it - driver = client.info().get('ExecutionDriver', 'lxc-') - container_info = _get_container_infos(container) - container_id = container_info['Id'] - if driver.startswith('lxc-'): - full_cmd = 'lxc-attach -n {0} -- {1}'.format(container_id, cmd) - elif driver.startswith('native-'): - if HAS_NSENTER: - # http://jpetazzo.github.io/2014/03/23/lxc-attach-nsinit-nsenter-docker-0-9/ - container_pid = container_info['State']['Pid'] - if container_pid == 0: - _invalid(status, id_=container, - comment='Container is not running') - return status - full_cmd = ( - 'nsenter --target {pid} --mount --uts --ipc --net --pid' - ' -- {cmd}'.format(pid=container_pid, cmd=cmd) - ) - else: - raise CommandExecutionError( - 'nsenter is not installed on the minion, cannot run command' - ) - else: - raise NotImplementedError( - 'Unknown docker ExecutionDriver \'{0}\'. Or didn\'t find command' - ' to attach to the container'.format(driver)) - - # now execute the command - comment = 'Executed {0}'.format(full_cmd) - try: - ret = __salt__[func](full_cmd, *args, **kwargs) - if ((isinstance(ret, dict) and ('retcode' in ret) and (ret['retcode'] != 0)) - or (func == 'cmd.retcode' and ret != 0)): - _invalid(status, id_=container, out=ret, comment=comment) - else: - _valid(status, id_=container, out=ret, comment=comment) - except Exception: - _invalid(status, id_=container, comment=comment, out=traceback.format_exc()) - return status - - -def load(imagepath): - ''' - Load the specified file at imagepath into docker that was generated from - a docker save command - e.g. `docker load < imagepath` - - imagepath - imagepath to docker tar file - - CLI Example: - - .. code-block:: bash - - salt '*' docker.load /path/to/image - ''' - - status = base_status.copy() - if os.path.isfile(imagepath): - try: - dockercmd = ['docker', 'load', '-i', imagepath] - ret = __salt__['cmd.run'](dockercmd, python_shell=False) - if isinstance(ret, dict) and ('retcode' in ret) and (ret['retcode'] != 0): - return _invalid(status, id_=None, - out=ret, - comment='Command to load image {0} failed.'.format(imagepath)) - - _valid(status, id_=None, out=ret, comment='Image load success') - except Exception: - _invalid(status, id_=None, - comment="Image not loaded.", - out=traceback.format_exc()) - else: - _invalid(status, id_=None, - comment='Image file {0} could not be found.'.format(imagepath), - out=traceback.format_exc()) - - return status - - -def save(image, filename): - ''' - .. versionadded:: 2015.5.0 - - Save the specified image to filename from docker - e.g. `docker save image > filename` - - image - name of image - - filename - The filename of the saved docker image - - CLI Example: - - .. code-block:: bash - - salt '*' docker.save arch_image /path/to/save/image - ''' - status = base_status.copy() - ok = False - try: - _info = _get_image_infos(image) - ok = True - except Exception: - _invalid(status, id_=image, - comment="docker image {0} could not be found.".format(image), - out=traceback.format_exc()) - - if ok: - try: - dockercmd = ['docker', 'save', '-o', filename, image] - ret = __salt__['cmd.run'](dockercmd) - if isinstance(ret, dict) and ('retcode' in ret) and (ret['retcode'] != 0): - return _invalid(status, - id_=image, - out=ret, - comment='Command to save image {0} to {1} failed.'.format(image, filename)) - - _valid(status, id_=image, out=ret, comment='Image save success') - except Exception: - _invalid(status, id_=image, comment="Image not saved.", out=traceback.format_exc()) - - return status - - -def run(container, cmd): - ''' - Wrapper for :py:func:`cmdmod.run` inside a container context - - container - container id (or grain) - - cmd - command to execute - - .. note:: - The return is a bit different as we use the docker struct. - Output of the command is in 'out' and result is always ``True``. - - .. warning:: - Be advised that this function allows for raw shell access to the named - container! If allowing users to execute this directly it may allow more - rights than intended! - - CLI Example: - - .. code-block:: bash - - salt '*' docker.run 'ls -l /etc' - ''' - status = base_status.copy() - return _run_wrapper( - status, container, 'cmd.run', cmd) - - -def run_all(container, cmd): - ''' - Wrapper for :py:func:`cmdmod.run_all` inside a container context - - container - container id (or grain) - - cmd - command to execute - - .. note:: - The return is a bit different as we use the docker struct. - Output of the command is in 'out' and result is ``False`` if - command failed to execute. - - .. warning:: - Be advised that this function allows for raw shell access to the named - container! If allowing users to execute this directly it may allow more - rights than intended! - - CLI Example: - - .. code-block:: bash - - salt '*' docker.run_all 'ls -l /etc' - ''' - status = base_status.copy() - return _run_wrapper( - status, container, 'cmd.run_all', cmd) - - -def run_stderr(container, cmd): - ''' - Wrapper for :py:func:`cmdmod.run_stderr` inside a container context - - container - container id (or grain) - - cmd - command to execute - - .. note:: - The return is a bit different as we use the docker struct. - Output of the command is in 'out' and result is always ``True``. - - .. warning:: - Be advised that this function allows for raw shell access to the named - container! If allowing users to execute this directly it may allow more - rights than intended! - - CLI Example: - - .. code-block:: bash - - salt '*' docker.run_stderr 'ls -l /etc' - ''' - status = base_status.copy() - return _run_wrapper( - status, container, 'cmd.run_stderr', cmd) - - -def run_stdout(container, cmd): - ''' - Wrapper for :py:func:`cmdmod.run_stdout` inside a container context - - container - container id (or grain) - - cmd - command to execute - - .. note:: - The return is a bit different as we use the docker struct. - Output of the command is in 'out' and result is always ``True``. - - .. warning:: - Be advised that this function allows for raw shell access to the named - container! If allowing users to execute this directly it may allow more - rights than intended! - - CLI Example: - - .. code-block:: bash - - salt '*' docker.run_stdout 'ls -l /etc' - ''' - status = base_status.copy() - return _run_wrapper( - status, container, 'cmd.run_stdout', cmd) - - -def retcode(container, cmd): - ''' - Wrapper for :py:func:`cmdmod.retcode` inside a container context - - container - container id (or grain) - - cmd - command to execute - - .. note:: - The return is True or False depending on the commands success. - - .. warning:: - Be advised that this function allows for raw shell access to the named - container! If allowing users to execute this directly it may allow more - rights than intended! - - CLI Example: - - .. code-block:: bash - - salt '*' docker.retcode 'ls -l /etc' - ''' - status = base_status.copy() - return _run_wrapper( - status, container, 'cmd.retcode', cmd)['status'] - - -def get_container_root(container): - ''' - Get the container rootfs path - - container - container id or grain - - CLI Example: - - .. code-block:: bash - - salt '*' docker.get_container_root - ''' - default_path = os.path.join( - '/var/lib/docker', - 'containers', - _get_container_infos(container)['Id'], - ) - default_rootfs = os.path.join(default_path, 'rootfs') - rootfs_re = re.compile(r'^lxc.rootfs\s*=\s*(.*)\s*$', re.U) - try: - lxcconfig = os.path.join(default_path, 'config.lxc') - with salt.utils.fopen(lxcconfig) as fhr: - lines = fhr.readlines() - rlines = lines[:] - rlines.reverse() - for rline in rlines: - robj = rootfs_re.search(rline) - if robj: - rootfs = robj.groups()[0] - break - except Exception: - rootfs = default_rootfs - return rootfs - - -def _script(status, - container, - source, - args=None, - cwd=None, - stdin=None, - runas=None, - shell=cmdmod.DEFAULT_SHELL, - template='jinja', - umask=None, - timeout=None, - reset_system_locale=True, - run_func_=None, - no_clean=False, - saltenv='base', - output_loglevel='info', - quiet=False, - **kwargs): - try: - if not run_func_: - run_func_ = run_all - rpath = get_container_root(container) - tpath = os.path.join(rpath, 'tmp') - - if 'env' in kwargs: - salt.utils.warn_until( - 'Oxygen', - 'Parameter \'env\' has been detected in the argument list. This ' - 'parameter is no longer used and has been replaced by \'saltenv\' ' - 'as of Salt 2016.11.0. This warning will be removed in Salt Oxygen.' - ) - kwargs.pop('env') - - path = salt.utils.files.mkstemp(dir=tpath) - if template: - __salt__['cp.get_template']( - source, path, template, saltenv, **kwargs) - else: - fn_ = __salt__['cp.cache_file'](source, saltenv) - if not fn_: - return {'pid': 0, - 'retcode': 1, - 'stdout': '', - 'stderr': '', - 'cache_error': True} - shutil.copyfile(fn_, path) - in_path = os.path.join('/', os.path.relpath(path, rpath)) - os.chmod(path, 0o755) - command = in_path + ' ' + str(args) if args else in_path - status = run_func_(container, - command, - cwd=cwd, - stdin=stdin, - output_loglevel=output_loglevel, - quiet=quiet, - runas=runas, - shell=shell, - umask=umask, - timeout=timeout, - reset_system_locale=reset_system_locale) - if not no_clean: - os.remove(path) - except Exception: - _invalid(status, id_=container, out=traceback.format_exc()) - return status - - -def script(container, - source, - args=None, - cwd=None, - stdin=None, - runas=None, - shell=cmdmod.DEFAULT_SHELL, - template='jinja', - umask=None, - timeout=None, - reset_system_locale=True, - no_clean=False, - saltenv='base'): - ''' - Wrapper for :py:func:`cmdmod.script` inside a container context - - container - container id (or grain) - - additional parameters - See :py:func:`cmd.script ` - - .. warning:: - Be advised that this function allows for raw shell access to the named - container! If allowing users to execute this directly it may allow more - rights than intended! - - Download a script from a remote location and execute the script in the container. - The script can be located on the salt master file server or on an HTTP/FTP server. - - The script will be executed directly, so it can be written in any available programming - language. - - The script can also be formatted as a template, the default is jinja. Arguments for the - script can be specified as well. - - CLI Example: - - .. code-block:: bash - - salt '*' docker.script salt://docker_script.py - salt '*' docker.script salt://scripts/runme.sh 'arg1 arg2 "arg 3"' - salt '*' docker.script salt://scripts/windows_task.ps1 args=' -Input c:\\tmp\\infile.txt' shell='powershell' - - A string of standard input can be specified for the command to be run using the stdin - parameter. This can be useful in cases where sensitive information must be read from - standard input: - - CLI Example: - - .. code-block:: bash - - salt '*' docker.script salt://scripts/runme.sh stdin='one\\ntwo\\nthree\\nfour\\nfive\\n' - ''' - status = base_status.copy() - - return _script(status, - container, - source, - args=args, - cwd=cwd, - stdin=stdin, - runas=runas, - shell=shell, - template=template, - umask=umask, - timeout=timeout, - reset_system_locale=reset_system_locale, - no_clean=no_clean, - saltenv=saltenv) - - -def script_retcode(container, - source, - cwd=None, - stdin=None, - runas=None, - shell=cmdmod.DEFAULT_SHELL, - template='jinja', - umask=None, - timeout=None, - reset_system_locale=True, - no_clean=False, - saltenv='base'): - ''' - Wrapper for :py:func:`cmdmod.script_retcode` inside a container context - - container - container id (or grain) - - additional parameters - See :py:func:`cmd.script_retcode ` - - .. warning:: - Be advised that this function allows for raw shell access to the named - container! If allowing users to execute this directly it may allow more - rights than intended! - - CLI Example: - - .. code-block:: bash - - salt '*' docker.script_retcode salt://docker_script.py - ''' - status = base_status.copy() - - return _script(status, - container, - source=source, - cwd=cwd, - stdin=stdin, - runas=runas, - shell=shell, - template=template, - umask=umask, - timeout=timeout, - reset_system_locale=reset_system_locale, - run_func_=retcode, - no_clean=no_clean, - saltenv=saltenv) diff --git a/salt/modules/mine.py b/salt/modules/mine.py index 6af2d47618..184f037da4 100644 --- a/salt/modules/mine.py +++ b/salt/modules/mine.py @@ -407,7 +407,7 @@ def get_docker(interfaces=None, cidrs=None, with_container_id=False): cidrs = cidr_ # Get docker info - cmd = 'dockerng.ps' + cmd = 'docker.ps' docker_hosts = get('*', cmd) proxy_lists = {} diff --git a/salt/states/dockerng.py b/salt/states/docker.py similarity index 90% rename from salt/states/dockerng.py rename to salt/states/docker.py index e38f418b71..ca5f79415f 100644 --- a/salt/states/dockerng.py +++ b/salt/states/docker.py @@ -4,30 +4,12 @@ Management of Docker containers .. versionadded:: 2015.8.0 +:depends: docker-py_ Python module -This is the state module to accompany the :mod:`dockerng -` execution module. - - -Why Make a Second Docker State Module? --------------------------------------- - -We have received a lot of feedback on our Docker support. In the process of -implementing recommended improvements, it became obvious that major changes -needed to be made to the functions and return data. In the end, a complete -rewrite was done. - -The changes being too significant, it was decided that making a separate -execution module and state module (called ``dockerng``) would be the best -option. This will give users a couple release cycles to modify their scripts, -SLS files, etc. to use the new functionality, rather than forcing users to -change everything immediately. - -In the **Nitrogen** release of Salt (due in 2017), this execution module will -take the place of the default Docker execution module, and backwards-compatible -naming will be maintained for a couple releases after that to allow users time -to replace references to ``dockerng`` with ``docker``. +.. _docker-py: https://pypi.python.org/pypi/docker-py +This is the state module to accompany the :mod:`docker ` +execution module. .. note:: @@ -45,7 +27,7 @@ import traceback # Import salt libs from salt.exceptions import CommandExecutionError, SaltInvocationError # pylint: disable=no-name-in-module,import-error -from salt.modules.dockerng import ( +from salt.modules.docker import ( CLIENT_TIMEOUT, STOP_TIMEOUT, VALID_CREATE_OPTS, @@ -60,20 +42,21 @@ import salt.ext.six as six log = logging.getLogger(__name__) # pylint: disable=invalid-name # Define the module's virtual name -__virtualname__ = 'dockerng' +__virtualname__ = 'docker' +__virtual_aliases__ = ('dockerng',) def __virtual__(): ''' - Only load if the dockerng execution module is available + Only load if the docker execution module is available ''' - if 'dockerng.version' in __salt__: + if 'docker.version' in __salt__: global _validate_input # pylint: disable=global-statement _validate_input = salt.utils.namespaced_function( _validate_input, globals(), preserve_context=True, ) return __virtualname__ - return (False, __salt__.missing_fun_string('dockerng.version')) + return (False, __salt__.missing_fun_string('docker.version')) def _format_comments(comments): @@ -135,7 +118,7 @@ def _prep_input(kwargs): def _compare(actual, create_kwargs, defaults_from_image): ''' Compare the desired configuration against the actual configuration returned - by dockerng.inspect_container + by docker.inspect_container ''' def _get(path, default=None): return salt.utils.traverse_dict(actual, path, default, delimiter=':') @@ -156,15 +139,15 @@ def _compare(actual, create_kwargs, defaults_from_image): else: data = config.get('default') - log.trace('dockerng.running: comparing ' + item) + log.trace('docker.running: comparing ' + item) conf_path = config['path'] if isinstance(conf_path, tuple): actual_data = [_get(x) for x in conf_path] else: actual_data = _get(conf_path, default=config.get('default')) - log.trace('dockerng.running ({0}): desired value: {1}' + log.trace('docker.running ({0}): desired value: {1}' .format(item, data)) - log.trace('dockerng.running ({0}): actual value: {1}' + log.trace('docker.running ({0}): actual value: {1}' .format(item, actual_data)) # 'create' comparison params @@ -172,7 +155,7 @@ def _compare(actual, create_kwargs, defaults_from_image): # Something unique here. Two fields to check, if both are False # then detach is True actual_detach = all(x is False for x in actual_data) - log.trace('dockerng.running ({0}): munged actual value: {1}' + log.trace('docker.running ({0}): munged actual value: {1}' .format(item, actual_detach)) if actual_detach != data: ret.update({item: {'old': actual_detach, 'new': data}}) @@ -193,7 +176,7 @@ def _compare(actual, create_kwargs, defaults_from_image): continue else: actual_env[key] = val - log.trace('dockerng.running ({0}): munged actual value: {1}' + log.trace('docker.running ({0}): munged actual value: {1}' .format(item, actual_env)) env_diff = {} for key in data: @@ -232,9 +215,9 @@ def _compare(actual, create_kwargs, defaults_from_image): k for k in _image_get(config['image_path']) or [] if k not in desired_ports]) desired_ports.sort() - log.trace('dockerng.running ({0}): munged actual value: {1}' + log.trace('docker.running ({0}): munged actual value: {1}' .format(item, actual_ports)) - log.trace('dockerng.running ({0}): munged desired value: {1}' + log.trace('docker.running ({0}): munged desired value: {1}' .format(item, desired_ports)) if actual_ports != desired_ports: ret.update({item: {'old': actual_ports, @@ -343,9 +326,9 @@ def _compare(actual, create_kwargs, defaults_from_image): desired_binds.append(bind_def) actual_binds.sort() desired_binds.sort() - log.trace('dockerng.running ({0}): munged actual value: {1}' + log.trace('docker.running ({0}): munged actual value: {1}' .format(item, actual_binds)) - log.trace('dockerng.running ({0}): munged desired value: {1}' + log.trace('docker.running ({0}): munged desired value: {1}' .format(item, desired_binds)) if actual_binds != desired_binds: ret.update({item: {'old': actual_binds, @@ -472,9 +455,9 @@ def _compare(actual, create_kwargs, defaults_from_image): actual_data = [] actual_data = sorted(actual_data) desired_data = sorted(data) - log.trace('dockerng.running ({0}): munged actual value: {1}' + log.trace('docker.running ({0}): munged actual value: {1}' .format(item, actual_data)) - log.trace('dockerng.running ({0}): munged desired value: {1}' + log.trace('docker.running ({0}): munged desired value: {1}' .format(item, desired_data)) if actual_data != desired_data: ret.update({item: {'old': actual_data, @@ -493,7 +476,7 @@ def _find_volume(name): ''' Find volume by name on minion ''' - docker_volumes = __salt__['dockerng.volumes']()['Volumes'] + docker_volumes = __salt__['docker.volumes']()['Volumes'] if docker_volumes: volumes = [v for v in docker_volumes if v['Name'] == name] if volumes: @@ -503,7 +486,7 @@ def _find_volume(name): def _get_defaults_from_image(image_id): - return __salt__['dockerng.inspect_image'](image_id) + return __salt__['docker.inspect_image'](image_id) def image_present(name, @@ -531,7 +514,7 @@ def image_present(name, .. code-block:: yaml myuser/myimage:mytag: - dockerng.image_present + docker.image_present build Path to directory on the Minion containing a Dockerfile @@ -539,30 +522,30 @@ def image_present(name, .. code-block:: yaml myuser/myimage:mytag: - dockerng.image_present: + docker.image_present: - build: /home/myuser/docker/myimage myuser/myimage:mytag: - dockerng.image_present: + docker.image_present: - build: /home/myuser/docker/myimage - dockerfile: Dockerfile.alternative .. versionadded:: develop - The image will be built using :py:func:`dockerng.build - ` and the specified image name and tag + The image will be built using :py:func:`docker.build + ` and the specified image name and tag will be applied to it. load - Loads a tar archive created with :py:func:`dockerng.load - ` (or the ``docker load`` Docker CLI + Loads a tar archive created with :py:func:`docker.load + ` (or the ``docker load`` Docker CLI command), and assigns it the specified repo and tag. .. code-block:: yaml myuser/myimage:mytag: - dockerng.image_present: + docker.image_present: - load: salt://path/to/image.tar force : False @@ -590,7 +573,7 @@ def image_present(name, # Ensure that we have repo:tag notation image = ':'.join(_get_repo_tag(name)) - all_tags = __salt__['dockerng.list_tags']() + all_tags = __salt__['docker.list_tags']() if image in all_tags: if not force: @@ -599,7 +582,7 @@ def image_present(name, return ret else: try: - image_info = __salt__['dockerng.inspect_image'](name) + image_info = __salt__['docker.inspect_image'](name) except Exception as exc: ret['comment'] = \ 'Unable to get info for image \'{0}\': {1}'.format(name, exc) @@ -622,7 +605,7 @@ def image_present(name, if build: try: - image_update = __salt__['dockerng.build'](path=build, + image_update = __salt__['docker.build'](path=build, image=image, dockerfile=dockerfile) except Exception as exc: @@ -636,7 +619,7 @@ def image_present(name, elif load: try: - image_update = __salt__['dockerng.load'](path=load, image=image) + image_update = __salt__['docker.load'](path=load, image=image) except Exception as exc: ret['comment'] = ( 'Encountered error loading {0} as {1}: {2}' @@ -648,7 +631,7 @@ def image_present(name, else: try: - image_update = __salt__['dockerng.pull']( + image_update = __salt__['docker.pull']( image, insecure_registry=insecure_registry, client_timeout=client_timeout @@ -669,7 +652,7 @@ def image_present(name, # Only add to the changes dict if layers were pulled ret['changes'] = image_update - ret['result'] = image in __salt__['dockerng.list_tags']() + ret['result'] = image in __salt__['docker.list_tags']() if not ret['result']: # This shouldn't happen, failure to pull should be caught above @@ -697,7 +680,7 @@ def image_absent(name=None, images=None, force=False): .. code-block:: yaml remove_images: - dockerng.image_absent: + docker.image_absent: - names: - busybox - centos:6 @@ -706,7 +689,7 @@ def image_absent(name=None, images=None, force=False): .. code-block:: yaml remove_images: - dockerng.image_absent: + docker.image_absent: - images: - busybox - centos:6 @@ -724,19 +707,19 @@ def image_absent(name=None, images=None, force=False): .. note:: This option can also be overridden by Pillar data. If the Minion - has a pillar variable named ``dockerng.running.force`` which is + has a pillar variable named ``docker.running.force`` which is set to ``True``, it will turn on this option. This pillar variable can even be set at runtime. For example: .. code-block:: bash - salt myminion state.sls docker_stuff pillar="{dockerng.force: True}" + salt myminion state.sls docker_stuff pillar="{docker.force: True}" If this pillar variable is present and set to ``False``, then it will turn off this option. For more granular control, setting a pillar variable named - ``dockerng.force.image_name`` will affect only the named image. + ``docker.force.image_name`` will affect only the named image. ''' ret = {'name': name, 'changes': {}, @@ -762,7 +745,7 @@ def image_absent(name=None, images=None, force=False): except TypeError: targets = [':'.join(_get_repo_tag(str(name)))] - pre_tags = __salt__['dockerng.list_tags']() + pre_tags = __salt__['docker.list_tags']() to_delete = [x for x in targets if x in pre_tags] log.debug('targets = {0}'.format(targets)) log.debug('to_delete = {0}'.format(to_delete)) @@ -785,8 +768,8 @@ def image_absent(name=None, images=None, force=False): .format(', '.join(to_delete))) return ret - result = __salt__['dockerng.rmi'](*to_delete, force=force) - post_tags = __salt__['dockerng.list_tags']() + result = __salt__['docker.rmi'](*to_delete, force=force) + post_tags = __salt__['docker.list_tags']() failed = [x for x in to_delete if x in post_tags] if failed: @@ -844,8 +827,8 @@ def running(name, the image needs to be built from a Dockerfile or loaded from a saved image, or if you would like to use requisites to trigger a replacement of the container when the image is updated, then the - :py:func:`dockerng.image_present - ` should be used to manage the + :py:func:`docker.image_present + ` should be used to manage the image. force : False @@ -854,9 +837,9 @@ def running(name, stop_timeout : 10 If the container needs to be replaced, the container will be stopped - using :py:func:`dockerng.stop `. The value - of this parameter will be passed to :py:func:`dockerng.stop - ` as the ``timeout`` value, telling Docker + using :py:func:`docker.stop `. The value + of this parameter will be passed to :py:func:`docker.stop + ` as the ``timeout`` value, telling Docker how long to wait for a graceful shutdown before killing the container. validate_ip_addrs : True @@ -879,7 +862,7 @@ def running(name, .. code-block:: yaml mycontainer: - dockerng.running: + docker.running: - image: busybox - watch_action: SIGHUP - watch: @@ -908,7 +891,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - command: bash @@ -917,7 +900,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - cmd: bash @@ -932,7 +915,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - hostname: web1 @@ -944,7 +927,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - hostname: web1 - network_mode: host @@ -955,7 +938,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - hostname: domain.tld @@ -966,7 +949,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - interactive: True @@ -976,7 +959,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - tty: True @@ -987,7 +970,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - detach: True @@ -997,7 +980,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - user: foo @@ -1009,7 +992,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - memory: 512M @@ -1020,7 +1003,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - memory_swap: 1G @@ -1031,7 +1014,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - mac_address: 01:23:45:67:89:0a @@ -1041,7 +1024,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - network_disabled: True @@ -1051,7 +1034,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - working_dir: /var/log/nginx @@ -1061,7 +1044,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - entrypoint: "mycmd --arg1 --arg2" @@ -1070,7 +1053,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - entrypoint: - mycmd @@ -1084,7 +1067,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - environment: - VAR1: value @@ -1093,7 +1076,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - environment: - VAR1=value @@ -1113,14 +1096,14 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - ports: 1111,2222/udp .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - ports: - 1111 @@ -1134,14 +1117,14 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - volumes: /mnt/vol1,/mnt/vol2 .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - volumes: - /mnt/vol1 @@ -1153,7 +1136,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - cpu_shares: 256 @@ -1165,7 +1148,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - cpuset: "0,1" @@ -1178,7 +1161,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - binds: /srv/www:/var/www:ro,/etc/foo.conf:/usr/local/etc/foo.conf:rw @@ -1187,7 +1170,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - binds: - /srv/www:/var/www:ro @@ -1200,7 +1183,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - binds: - /srv/www:/var/www:ro @@ -1225,14 +1208,14 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - port_bindings: "5000:5000,2123:2123/udp,8080" .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - port_bindings: - 5000:5000 @@ -1252,7 +1235,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - lxc_conf: - lxc.utsname: docker @@ -1269,7 +1252,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - security_opts: - 'apparmor:unconfined' @@ -1286,7 +1269,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - ports: 8080 - publish_all_ports: True @@ -1300,14 +1283,14 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - links: web1:link1,web2:link2 .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - links: - web1:link1 @@ -1320,14 +1303,14 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - dns: 8.8.8.8,8.8.4.4 .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - dns: - 8.8.8.8 @@ -1344,14 +1327,14 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - dns_search: foo1.domain.tld,foo2.domain.tld .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - dns_search: - foo1.domain.tld @@ -1365,14 +1348,14 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - volumes_from: foo .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - volumes_from: - foo @@ -1387,7 +1370,7 @@ def running(name, - ``container:`` - Reuses another container's network stack - ``host`` - Use the host's network stack inside the container - Any name that identifies an existing network that might be created - with ``dockerng.network_present``. + with ``docker.network_present``. .. warning:: @@ -1398,7 +1381,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - network_mode: null @@ -1412,12 +1395,12 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - restart_policy: on-failure:5 bar: - dockerng.running: + docker.running: - image: bar/baz:latest - restart_policy: always @@ -1429,14 +1412,14 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - cap_add: SYS_ADMIN,MKNOD .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - cap_add: - SYS_ADMIN @@ -1454,14 +1437,14 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - cap_drop: SYS_ADMIN,MKNOD .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - cap_drop: - SYS_ADMIN @@ -1487,14 +1470,14 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - extra_hosts: web1:10.9.8.7,web2:10.9.8.8 .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - extra_hosts: - web1:10.9.8.7 @@ -1515,7 +1498,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - pid_mode: host @@ -1530,7 +1513,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - labels: - LABEL1 @@ -1539,7 +1522,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - labels: KEY1: VALUE1 @@ -1548,7 +1531,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - labels: - KEY1: VALUE1 @@ -1569,7 +1552,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - stop_signal: SIGRTMIN+3 @@ -1587,7 +1570,7 @@ def running(name, .. code-block:: yaml foo: - dockerng.running: + docker.running: - image: bar/baz:latest - log_config: Type: syslog @@ -1627,10 +1610,10 @@ def running(name, except TypeError: image = ':'.join(_get_repo_tag(str(image))) - if image not in __salt__['dockerng.list_tags']() and not __opts__['test']: + if image not in __salt__['docker.list_tags']() and not __opts__['test']: try: # Pull image - pull_result = __salt__['dockerng.pull']( + pull_result = __salt__['docker.pull']( image, client_timeout=client_timeout, ) @@ -1642,17 +1625,17 @@ def running(name, ret['changes']['image'] = pull_result try: - image_id = __salt__['dockerng.inspect_image'](image)['Id'] + image_id = __salt__['docker.inspect_image'](image)['Id'] except CommandExecutionError: if not __opts__['test']: raise image_id = None - if name not in __salt__['dockerng.list_containers'](all=True): + if name not in __salt__['docker.list_containers'](all=True): pre_config = {} else: try: - pre_config = __salt__['dockerng.inspect_container'](name) + pre_config = __salt__['docker.inspect_container'](name) try: current_image_id = pre_config['Image'] except KeyError: @@ -1696,7 +1679,7 @@ def running(name, _prep_input(create_kwargs) # Perform data type validation and, where necessary, munge # the data further so it is in a format that can be passed - # to dockerng.create. + # to docker.create. _validate_input(create_kwargs, validate_ip_addrs=validate_ip_addrs) @@ -1746,13 +1729,13 @@ def running(name, defaults_from_image) if changes_needed: log.debug( - 'dockerng.running: Analysis of container \'{0}\' ' + 'docker.running: Analysis of container \'{0}\' ' 'reveals the following changes need to be made: ' '{1}'.format(name, changes_needed) ) else: log.debug( - 'dockerng.running: Container \'{0}\' already ' + 'docker.running: Container \'{0}\' already ' 'matches the desired configuration'.format(name) ) except Exception as exc: @@ -1792,13 +1775,13 @@ def running(name, if not pre_config: pre_state = None else: - pre_state = __salt__['dockerng.state'](name) + pre_state = __salt__['docker.state'](name) if new_container: if pre_config: # Container exists, stop if necessary, then remove and recreate if pre_state != 'stopped': - result = __salt__['dockerng.stop'](name, + result = __salt__['docker.stop'](name, timeout=stop_timeout, unpause=True)['result'] if result is not True: @@ -1810,7 +1793,7 @@ def running(name, return ret # Remove existing container - removed_ids = __salt__['dockerng.rm'](name) + removed_ids = __salt__['docker.rm'](name) if not removed_ids: comments.append('Failed to remove container {0}'.format(name)) ret['comment'] = _format_comments(comments) @@ -1820,10 +1803,10 @@ def running(name, # changes dict. ret['changes']['removed'] = removed_ids - if image not in __salt__['dockerng.list_tags'](): + if image not in __salt__['docker.list_tags'](): try: # Pull image - pull_result = __salt__['dockerng.pull']( + pull_result = __salt__['docker.pull']( image, client_timeout=client_timeout, ) @@ -1836,7 +1819,7 @@ def running(name, try: # Create new container - create_result = __salt__['dockerng.create']( + create_result = __salt__['docker.create']( image, name=name, validate_ip_addrs=False, @@ -1857,7 +1840,7 @@ def running(name, if new_container or (pre_state != 'running' and start): try: # Start container - __salt__['dockerng.start']( + __salt__['docker.start']( name, ) except Exception as exc: @@ -1868,7 +1851,7 @@ def running(name, ret['comment'] = _format_comments(comments) return ret - post_state = __salt__['dockerng.state'](name) + post_state = __salt__['docker.state'](name) if pre_state != post_state: # If the container changed states at all, note this change in the # return dict. @@ -1879,19 +1862,19 @@ def running(name, if changes_needed: try: - post_config = __salt__['dockerng.inspect_container'](name) + post_config = __salt__['docker.inspect_container'](name) defaults_from_image = _get_defaults_from_image(image_id) changes_still_needed = _compare(post_config, create_kwargs, defaults_from_image) if changes_still_needed: log.debug( - 'dockerng.running: Analysis of container \'{0}\' after ' + 'docker.running: Analysis of container \'{0}\' after ' 'creation/replacement reveals the following changes still ' 'need to be made: {1}'.format(name, changes_still_needed) ) else: log.debug( - 'dockerng.running: Changes successfully applied to ' + 'docker.running: Changes successfully applied to ' 'container \'{0}\''.format(name) ) except Exception as exc: @@ -1936,7 +1919,7 @@ def running(name, if not new_container: if send_signal: try: - __salt__['dockerng.signal'](name, signal=watch_action) + __salt__['docker.signal'](name, signal=watch_action) except CommandExecutionError as exc: comments.append( 'Failed to signal container: {0}'.format(exc) @@ -1951,7 +1934,7 @@ def running(name, elif ret['changes']: if not comments: log.warning( - 'dockerng.running: we detected changes without ' + 'docker.running: we detected changes without ' 'a specific comment for container \'{0}\'.'.format( name) ) @@ -2003,7 +1986,7 @@ def stopped(name=None, .. code-block:: yaml stopped_containers: - dockerng.stopped: + docker.stopped: - names: - foo - bar @@ -2012,7 +1995,7 @@ def stopped(name=None, .. code-block:: yaml stopped_containers: - dockerng.stopped: + docker.stopped: - containers: - foo - bar @@ -2060,7 +2043,7 @@ def stopped(name=None, containers = {} for target in targets: try: - c_state = __salt__['dockerng.state'](target) + c_state = __salt__['docker.state'](target) except CommandExecutionError: containers.setdefault('absent', []).append(target) else: @@ -2110,7 +2093,7 @@ def stopped(name=None, stop_errors = [] for target in to_stop: - changes = __salt__['dockerng.stop'](target, + changes = __salt__['docker.stop'](target, timeout=stop_timeout, unpause=unpause) if changes['result'] is True: @@ -2150,10 +2133,10 @@ def absent(name, force=False): .. code-block:: yaml mycontainer: - dockerng.absent + docker.absent multiple_containers: - dockerng.absent: + docker.absent: - names: - foo - bar @@ -2164,12 +2147,12 @@ def absent(name, force=False): 'result': False, 'comment': ''} - if name not in __salt__['dockerng.list_containers'](all=True): + if name not in __salt__['docker.list_containers'](all=True): ret['result'] = True ret['comment'] = 'Container \'{0}\' does not exist'.format(name) return ret - pre_state = __salt__['dockerng.state'](name) + pre_state = __salt__['docker.state'](name) if pre_state != 'stopped' and not force: ret['comment'] = ('Container is running, set force to True to ' 'forcibly remove it') @@ -2181,13 +2164,13 @@ def absent(name, force=False): return ret try: - ret['changes']['removed'] = __salt__['dockerng.rm'](name, force=force) + ret['changes']['removed'] = __salt__['docker.rm'](name, force=force) except Exception as exc: ret['comment'] = ('Failed to remove container \'{0}\': {1}' .format(name, exc)) return ret - if name in __salt__['dockerng.list_containers'](all=True): + if name in __salt__['docker.list_containers'](all=True): ret['comment'] = 'Failed to remove container \'{0}\''.format(name) else: if force and pre_state != 'stopped': @@ -2216,13 +2199,13 @@ def network_present(name, driver=None, containers=None): .. code-block:: yaml network_foo: - dockerng.network_present + docker.network_present .. code-block:: yaml network_bar: - dockerng.network_present + docker.network_present - name: bar - containers: - cont1 @@ -2236,8 +2219,8 @@ def network_present(name, driver=None, containers=None): if containers is None: containers = [] # map containers to container's Ids. - containers = [__salt__['dockerng.inspect_container'](c)['Id'] for c in containers] - networks = __salt__['dockerng.networks'](names=[name]) + containers = [__salt__['docker.inspect_container'](c)['Id'] for c in containers] + networks = __salt__['docker.networks'](names=[name]) if networks: network = networks[0] # we expect network's name to be unique if all(c in network['Containers'] for c in containers): @@ -2248,7 +2231,7 @@ def network_present(name, driver=None, containers=None): for container in containers: if container not in network['Containers']: try: - ret['changes']['connected'] = __salt__['dockerng.connect_container_to_network']( + ret['changes']['connected'] = __salt__['docker.connect_container_to_network']( container, name) except Exception as exc: ret['comment'] = ('Failed to connect container \'{0}\' to network \'{1}\' {2}'.format( @@ -2258,7 +2241,7 @@ def network_present(name, driver=None, containers=None): else: try: - ret['changes']['created'] = __salt__['dockerng.create_network']( + ret['changes']['created'] = __salt__['docker.create_network']( name, driver=driver) except Exception as exc: ret['comment'] = ('Failed to create network \'{0}\': {1}' @@ -2267,7 +2250,7 @@ def network_present(name, driver=None, containers=None): result = True for container in containers: try: - ret['changes']['connected'] = __salt__['dockerng.connect_container_to_network']( + ret['changes']['connected'] = __salt__['docker.connect_container_to_network']( container, name) except Exception as exc: ret['comment'] = ('Failed to connect container \'{0}\' to network \'{1}\' {2}'.format( @@ -2289,7 +2272,7 @@ def network_absent(name, driver=None): .. code-block:: yaml network_foo: - dockerng.network_absent + docker.network_absent ''' ret = {'name': name, @@ -2297,7 +2280,7 @@ def network_absent(name, driver=None): 'result': False, 'comment': ''} - networks = __salt__['dockerng.networks'](names=[name]) + networks = __salt__['docker.networks'](names=[name]) if not networks: ret['result'] = True ret['comment'] = 'Network \'{0}\' already absent'.format(name) @@ -2305,12 +2288,12 @@ def network_absent(name, driver=None): for container in networks[0]['Containers']: try: - ret['changes']['disconnected'] = __salt__['dockerng.disconnect_container_from_network'](container, name) + ret['changes']['disconnected'] = __salt__['docker.disconnect_container_from_network'](container, name) except Exception as exc: ret['comment'] = ('Failed to disconnect container \'{0}\' to network \'{1}\' {2}'.format( container, name, exc)) try: - ret['changes']['removed'] = __salt__['dockerng.remove_network'](name) + ret['changes']['removed'] = __salt__['docker.remove_network'](name) ret['result'] = True except Exception as exc: ret['comment'] = ('Failed to remove network \'{0}\': {1}' @@ -2358,13 +2341,13 @@ def volume_present(name, driver=None, driver_opts=None, force=False): .. code-block:: yaml volume_foo: - dockerng.volume_present + docker.volume_present .. code-block:: yaml volume_bar: - dockerng.volume_present + docker.volume_present - name: bar - driver: local - driver_opts: @@ -2373,7 +2356,7 @@ def volume_present(name, driver=None, driver_opts=None, force=False): .. code-block:: yaml volume_bar: - dockerng.volume_present + docker.volume_present - name: bar - driver: local - driver_opts: @@ -2394,7 +2377,7 @@ def volume_present(name, driver=None, driver_opts=None, force=False): ret['comment'] = ('The volume \'{0}\' will be created'.format(name)) return ret try: - ret['changes']['created'] = __salt__['dockerng.create_volume']( + ret['changes']['created'] = __salt__['docker.create_volume']( name, driver=driver, driver_opts=driver_opts) except Exception as exc: ret['comment'] = ('Failed to create volume \'{0}\': {1}' @@ -2420,14 +2403,14 @@ def volume_present(name, driver=None, driver_opts=None, force=False): name, volume) return ret try: - ret['changes']['removed'] = __salt__['dockerng.remove_volume'](name) + ret['changes']['removed'] = __salt__['docker.remove_volume'](name) except Exception as exc: ret['comment'] = ('Failed to remove volume \'{0}\': {1}' .format(name, exc)) return ret else: try: - ret['changes']['created'] = __salt__['dockerng.create_volume']( + ret['changes']['created'] = __salt__['docker.create_volume']( name, driver=driver, driver_opts=driver_opts) except Exception as exc: ret['comment'] = ('Failed to create volume \'{0}\': {1}' @@ -2457,7 +2440,7 @@ def volume_absent(name, driver=None): .. code-block:: yaml volume_foo: - dockerng.volume_absent + docker.volume_absent ''' ret = {'name': name, @@ -2472,7 +2455,7 @@ def volume_absent(name, driver=None): return ret try: - ret['changes']['removed'] = __salt__['dockerng.remove_volume'](name) + ret['changes']['removed'] = __salt__['docker.remove_volume'](name) ret['result'] = True except Exception as exc: ret['comment'] = ('Failed to remove volume \'{0}\': {1}' diff --git a/salt/states/dockerio.py b/salt/states/dockerio.py deleted file mode 100644 index 22d62e3903..0000000000 --- a/salt/states/dockerio.py +++ /dev/null @@ -1,1269 +0,0 @@ -# -*- coding: utf-8 -*- -''' -Manage Docker containers -======================== - -.. deprecated:: 2015.8.0 - Future feature development will be done only in :mod:`dockerng - `. See the documentation for this module for - information on the deprecation path. - -`Docker `_ -is a lightweight, portable, self-sufficient software container -wrapper. The base supported wrapper type is -`LXC `_, -`cgroups `_, and the -`Linux Kernel `_. - -.. note:: - - This state module requires - `docker-py `_ version >= 0.6.0 - which supports `Docker Remote API version 1.12 - `_. - -Available Functions -------------------- - -- built - - .. code-block:: yaml - - corp/mysuperdocker_img: - docker.built: - - path: /path/to/dir/container - -- pulled - - .. code-block:: yaml - - ubuntu: - docker.pulled: - - tag: latest - -- pushed - - .. code-block:: yaml - - corp/mysuperdocker_img: - docker.pushed - -- installed - - .. code-block:: yaml - - mysuperdocker-container: - docker.installed: - - name: mysuperdocker - - hostname: superdocker - - image: corp/mysuperdocker_img - -- loaded - - .. code-block:: yaml - - mysuperdocker-file: - docker.loaded: - - name: mysuperdocker - - source: salt://_files/tmp/docker_image.tar - -- running - - .. code-block:: yaml - - my_service: - docker.running: - - container: mysuperdocker - - image: corp/mysuperdocker_img - - ports: - - "5000/tcp": - HostIp: "" - HostPort: "5000" - - .. note:: - - The ``ports`` argument above is a dictionary. The double - indentation is required for PyYAML to load the data structure - properly as a python dictionary. More information can be found - :ref:`here ` - -- absent - - .. code-block:: yaml - - mys_old_uperdocker: - docker.absent - -- run - - .. code-block:: yaml - - /finish-install.sh: - docker.run: - - cid: mysuperdocker - - unless: grep -q something /var/log/foo - - docker_unless: grep -q done /install_log - -Use Cases ---------- - - Ensures the container is running with the latest image available - - .. code-block:: yaml - - my-service-image: - docker.pulled: - - name: registry/my-service:latest - - force: true - - my-service-container: - docker.installed: - - image: registry/my-service:latest - - watch: - - docker: my-service-image - - my-service: - docker.running: - - container: my-service-container - - watch: - - docker: my-service-container - -.. note:: - - The docker modules are named ``dockerio`` because - the name 'docker' would conflict with the underlying docker-py library. - - -''' - -from __future__ import absolute_import -import functools -import logging - -# Import salt libs -from salt.ext.six import string_types -import salt.utils -import salt.utils.files -import salt.ext.six as six - -# Enable proper logging -log = logging.getLogger(__name__) - -# Define the module's virtual name -__virtualname__ = 'docker' - - -def __virtual__(): - ''' - Only load if the dockerio execution module is available - ''' - if 'docker.version' in __salt__: - return __virtualname__ - return False - - -INVALID_RESPONSE = 'We did not get an acceptable answer from docker' -VALID_RESPONSE = '' -NOTSET = object() - - -def _ret_status(exec_status=None, - name='', - comment='', - result=None, - changes=None): - if not changes: - changes = {} - if exec_status is None: - exec_status = {} - if exec_status: - if result is None: - result = exec_status['status'] - scomment = exec_status.get('comment', None) - if scomment: - comment += '\n' + scomment - out = exec_status.get('out', None) - if out: - if isinstance(out, string_types): - comment += '\n' + out - return { - 'changes': changes, - 'result': result, - 'name': name, - 'comment': comment, - } - - -def _valid(exec_status=None, name='', comment='', changes=None): - return _ret_status(exec_status=exec_status, - comment=comment, - name=name, - changes=changes, - result=True) - - -def _invalid(exec_status=None, name='', comment='', changes=None): - return _ret_status(exec_status=exec_status, - comment=comment, - name=name, - changes=changes, - result=False) - - -def _get_image_name(image, tag): - if ':' not in image: - # backward compatibility: name could be already tagged - return ':'.join((image, tag)) - return image - - -def _parse_volumes(volumes): - ''' - Parse a given volumes state specification for later use in - modules.docker.create_container(). This produces a dict that can be directly - consumed by the Docker API /containers/create. - - Note: this only really exists for backwards-compatibility, and because - modules.dockerio.start() currently takes a binds argument. - - volumes - A structure containing information about the volumes to be included in the - container that will be created, either: - - a bare dictionary - - a list of dictionaries and lists - - .. code-block:: yaml - - # bare dict style - - volumes: - /usr/local/etc/ssl/certs/example.crt: - bind: /etc/ssl/certs/com.example.internal.crt - ro: True - /var/run: - bind: /var/run/host/ - ro: False - - # list of dicts style: - - volumes: - - /usr/local/etc/ssl/certs/example.crt: - bind: /etc/ssl/certs/com.example.internal.crt - ro: True - - /var/run: /var/run/host/ # read-write bound volume - - /var/lib/mysql # un-bound, container-only volume - - note: bind mounts specified like "/etc/timezone:/tmp/host_tz" will fall - through this parser. - - Returns a dict of volume specifications: - - .. code-block:: yaml - - { - 'bindvols': { - '/usr/local/etc/ssl/certs/example.crt': { - 'bind': '/etc/ssl/certs/com.example.internal.crt', - 'ro': True - }, - '/var/run/': { - 'bind': '/var/run/host', - 'ro': False - }, - }, - 'contvols': [ '/var/lib/mysql/' ] - } - - ''' - log.trace("Parsing given volumes dict: " + str(volumes)) - bindvolumes = {} - contvolumes = [] - if isinstance(volumes, dict): - # If volumes as a whole is a dict, then there's no way to specify a non-bound volume - # so we exit early and assume the dict is properly formed. - bindvolumes = volumes - if isinstance(volumes, list): - for vol in volumes: - if isinstance(vol, dict): - for volsource, voldef in vol.items(): - if isinstance(voldef, dict): - target = voldef['bind'] - read_only = voldef.get('ro', False) - else: - target = str(voldef) - read_only = False - source = volsource - else: # isinstance(vol, dict) - if ':' in vol: - volspec = vol.split(':') - source = volspec[0] - target = volspec[1] - read_only = False - try: - if len(volspec) > 2: - read_only = volspec[2] == "ro" - except IndexError: - pass - else: - contvolumes.append(str(vol)) - continue - bindvolumes[source] = { - 'bind': target, - 'ro': read_only - } - result = {'bindvols': bindvolumes, 'contvols': contvolumes} - log.trace("Finished parsing volumes, with result: " + str(result)) - return result - - -def mod_watch(name, sfun=None, *args, **kw): - if sfun == 'built': - # Needs to refresh the image - kw['force'] = True - build_status = built(name, **kw) - result = build_status['result'] - status = _ret_status(build_status, name, result=result, - changes={name: result}) - return status - elif sfun == 'installed': - # Throw away the old container and create a new one - remove_container = __salt__['docker.remove_container'] - remove_status = _ret_status(remove_container(container=name, - force=True), - name=name) - installed_status = installed(name=name, **kw) - result = installed_status['result'] and remove_status['result'] - comment = remove_status['comment'] - status = _ret_status(installed_status, name=name, - result=result, - changes={name: result}, - comment=comment) - return status - elif sfun == 'running': - # Force a restart or kill the container - container = kw.get('container', name) - kill_signal = kw.get('kill_signal') - if kill_signal: - killer = __salt__['docker.kill'] - status = _ret_status(killer(container, signal=kill_signal), - name=name, - changes={name: True}) - else: - restarter = __salt__['docker.restart'] - status = _ret_status(restarter(container), - name=name, - changes={name: True}) - return status - - return {'name': name, - 'changes': {}, - 'result': False, - 'comment': ('watch requisite is not' - ' implemented for {0}'.format(sfun))} - - -def pulled(name, - tag='latest', - force=False, - insecure_registry=False, - *args, - **kwargs): - ''' - Pull an image from a docker registry. (`docker pull`) - - .. note:: - - See first the documentation for `docker login`, `docker pull`, - `docker push`, - and `docker.import_image `_ - (`docker import - `_). - NOTE that we added in SaltStack a way to authenticate yourself with the - Docker Hub Registry by supplying your credentials (username, email & - password) using pillars. For more information, see salt.modules.dockerio - execution module. - - name - Name of the image - - tag - Tag of the image - - force - Pull even if the image is already pulled - - insecure_registry - Set to ``True`` to allow connections to non-HTTPS registries. Default ``False``. - ''' - - inspect_image = __salt__['docker.inspect_image'] - image_name = _get_image_name(name, tag) - image_infos = inspect_image(image_name) - if image_infos['status'] and not force: - return _valid( - name=name, - comment='Image already pulled: {0}'.format(image_name)) - - if __opts__['test']: - comment = 'Image {0} will be pulled'.format(image_name) - return _ret_status(name=name, comment=comment) - - previous_id = image_infos['out']['Id'] if image_infos['status'] else None - pull = __salt__['docker.pull'] - returned = pull(name, tag=tag, insecure_registry=insecure_registry) - if previous_id != returned['id']: - changes = {name: {'old': previous_id, - 'new': returned['id']}} - comment = 'Image {0} pulled'.format(image_name) - else: - changes = {} - comment = '' - return _ret_status(returned, name, changes=changes, comment=comment) - - -def pushed(name, tag='latest', insecure_registry=False): - ''' - Push an image from a docker registry. (`docker push`) - - .. note:: - - See first the documentation for `docker login`, `docker pull`, - `docker push`, - and `docker.import_image `_ - (`docker import - `_). - NOTE that we added in SaltStack a way to authenticate yourself with the - Docker Hub Registry by supplying your credentials (username, email - & password) using pillars. For more information, see - salt.modules.dockerio execution module. - - name - Name of the image - - tag - Tag of the image [Optional] - - insecure_registry - Set to ``True`` to allow connections to non-HTTPS registries. Default ``False``. - ''' - - image_name = _get_image_name(name, tag) - if __opts__['test']: - comment = 'Image {0} will be pushed'.format(image_name) - return _ret_status(name=name, comment=comment) - - push = __salt__['docker.push'] - returned = push(name, tag=tag, insecure_registry=insecure_registry) - log.debug("Returned: "+str(returned)) - if returned['status']: - changes = {name: {'Rev': returned['id']}} - else: - changes = {} - return _ret_status(returned, name, changes=changes) - - -def loaded(name, tag='latest', source=None, source_hash='', force=False): - ''' - Load an image into the local docker registry (`docker load`) - - name - Name of the docker image - - tag - tag of the image (defaults to 'latest') - - source - The source .tar file to download to the minion, created by docker save - this source file can be hosted on either the salt master server, - or on an HTTP or FTP server. - - If the file is hosted on a HTTP or FTP server then the source_hash - argument is also required - - .. note:: - - See first the documentation for Salt `file.managed - `_ - - source_hash - This can be one of the following: - 1. a source hash string - 2. the URI of a file that contains source hash strings - - force - Load even if the image exists - ''' - - inspect_image = __salt__['docker.inspect_image'] - image_name = _get_image_name(name, tag) - image_infos = inspect_image(image_name) - if image_infos['status'] and not force: - return _valid( - name=name, - comment='Image already loaded: {0}'.format(image_name)) - - if __opts__['test']: - comment = 'Image {0} will be loaded'.format(image_name) - return _ret_status(name=name, comment=comment) - - tmp_filename = salt.utils.files.mkstemp() - __states__['file.managed'](name=tmp_filename, - source=source, - source_hash=source_hash) - changes = {} - - if image_infos['status']: - changes['old'] = image_infos['out']['Id'] - remove_image = __salt__['docker.remove_image'] - remove_info = remove_image(image_name) - if not remove_info['status']: - return _invalid(name=name, - comment='Image could not be removed: {0}'.format(name)) - - load = __salt__['docker.load'] - returned = load(tmp_filename) - - image_infos = inspect_image(image_name) - if image_infos['status']: - changes['new'] = image_infos['out']['Id'] - else: - return _invalid( - name=name, - comment='Image {0} was not loaded into docker'.format(image_name)) - - return _ret_status(returned, name, changes=changes) - - -def built(name, - tag='latest', - path=None, - quiet=False, - nocache=False, - rm=True, - force=False, - timeout=None, - *args, **kwargs): - ''' - Build a docker image from a path or URL to a dockerfile. (`docker build`) - - name - Name of the image - - tag - tag of the image (defaults to 'latest') - - path - URL (e.g. `url/branch/docker_dir/dockerfile`) - or filesystem path to the dockerfile - - ''' - inspect_image = __salt__['docker.inspect_image'] - image_name = _get_image_name(name, tag) - image_infos = inspect_image(image_name) - if image_infos['status'] and not force: - return _valid( - name=name, - comment='Image already built: {0}, id: {1}'.format( - image_name, image_infos['out']['Id'])) - - if __opts__['test']: - comment = 'Image {0} will be built'.format(image_name) - return {'name': name, - 'changes': {}, - 'result': None, - 'comment': comment} - - previous_id = image_infos['out']['Id'] if image_infos['status'] else None - build = __salt__['docker.build'] - kw = dict(tag=image_name, - path=path, - quiet=quiet, - nocache=nocache, - rm=rm, - timeout=timeout, - ) - returned = build(**kw) - if previous_id != returned['id']: - changes = {name: {'old': previous_id, - 'new': returned['id']}} - comment = 'Image {0} built'.format(image_name) - else: - changes = {} - comment = '' - return _ret_status(exec_status=returned, - name=name, - changes=changes, - comment=comment) - - -def installed(name, - image, - tag='latest', - command=None, - hostname=None, - user=None, - detach=True, - stdin_open=False, - tty=False, - mem_limit=None, - ports=None, - environment=None, - dns=None, - volumes=None, - volumes_from=None, - cpu_shares=None, - cpuset=None, - *args, **kwargs): - ''' - Ensure that a container with the given name exists; - if not, build a new container from the specified image. - (`docker run`) - - name - Name for the container - - image - Image from which to build this container - - tag - tag of the image (defaults to 'latest') - - environment - Environment variables for the container, either - - a mapping of key, values - - a list of mappings of key, values - ports - List of ports definitions, either: - - a port to map - - a mapping of mapping portInHost : PortInContainer - volumes - List of volumes (see notes for the running function) - - For other parameters, see absolutely first the salt.modules.dockerio - execution module and the `docker-py python bindings for docker - documentation `_ for - `docker.create_container`. - - .. note:: - This command does not verify that the named container - is running the specified image. - ''' - ins_image = __salt__['docker.inspect_image'] - ins_container = __salt__['docker.inspect_container'] - create = __salt__['docker.create_container'] - image_name = _get_image_name(image, tag) - iinfos = ins_image(image_name) - if not iinfos['status']: - return _invalid(comment='Image "{0}" does not exist'.format(image_name)) - cinfos = ins_container(name) - already_exists = cinfos['status'] - # if container exists but is not started, try to start it - if already_exists: - return _valid(comment='Container \'{0}\' already exists'.format(name)) - dports, denvironment = {}, {} - - if __opts__['test']: - comment = 'Container \'{0}\' will be created'.format(name) - return _ret_status(name=name, comment=comment) - - if not ports: - ports = [] - if not volumes: - volumes = [] - if isinstance(environment, dict): - for k in environment: - denvironment[six.text_type(k)] = six.text_type(environment[k]) - if isinstance(environment, list): - for p in environment: - if isinstance(p, dict): - for k in p: - denvironment[six.text_type(k)] = six.text_type(p[k]) - for p in ports: - if not isinstance(p, dict): - dports[str(p)] = {} - else: - for k in p: - dports[str(p)] = {} - - parsed_volumes = _parse_volumes(volumes) - bindvolumes = parsed_volumes['bindvols'] - contvolumes = parsed_volumes['contvols'] - - kw = dict( - binds=bindvolumes, - command=command, - hostname=hostname, - user=user, - detach=detach, - stdin_open=stdin_open, - tty=tty, - mem_limit=mem_limit, - ports=dports, - environment=denvironment, - dns=dns, - volumes=contvolumes, - volumes_from=volumes_from, - name=name, - cpu_shares=cpu_shares, - cpuset=cpuset) - out = create(image_name, **kw) - # if container has been created, even if not started, we mark - # it as installed - changes = 'Container created' - try: - cid = out['out']['info']['id'] - except Exception as e: - log.debug(str(e)) - else: - changes = 'Container {0} created'.format(cid) - out['comment'] = changes - ret = _ret_status(out, name, changes=changes) - return ret - - -def absent(name): - ''' - Ensure that the container is absent; if not, it will - will be killed and destroyed. (`docker inspect`) - - name: - Either the container name or id - ''' - ins_container = __salt__['docker.inspect_container'] - cinfos = ins_container(name) - changes = {} - - if cinfos['status']: - cid = cinfos['id'] - changes[cid] = {} - is_running = __salt__['docker.is_running'](cid) - - if __opts__['test']: - comment = 'Container \'{0}\' will be stopped and destroyed'.format(cid) - return _ret_status(name=name, comment=comment) - - # Stop container gracefully, if running - if is_running: - changes[cid]['old'] = 'running' - __salt__['docker.stop'](cid) - is_running = __salt__['docker.is_running'](cid) - if is_running: - return _invalid(comment=('Container \'{0}\' could not be stopped' - .format(cid))) - else: - __salt__['docker.remove_container'](cid) - is_gone = __salt__['docker.exists'](cid) - if is_gone: - return _valid(comment=('Container \'{0}\'' - ' was stopped and destroyed, '.format(cid)), - changes={name: True}) - else: - return _valid(comment=('Container \'{0}\'' - ' was stopped but could not be destroyed,'.format(cid)), - changes={name: True}) - else: - __salt__['docker.remove_container'](cid) - is_gone = __salt__['docker.exists'](cid) - if is_gone: - return _valid(comment=('Container \'{0}\'' - 'is stopped and was destroyed, '.format(cid)), - changes={name: True}) - else: - return _valid(comment=('Container \'{0}\'' - ' is stopped but could not be destroyed,'.format(cid)), - changes={name: True}) - else: - return _valid(comment='Container \'{0}\' not found'.format(name)) - - -def present(name, image=None, tag='latest', is_latest=False): - ''' - If a container with the given name is not present, this state will fail. - Supports optionally checking for specific image/tag - (`docker inspect`) - - name: - container id - image: - image the container should be running (defaults to any) - tag: - tag of the image (defaults to 'latest') - is_latest: - also check if the container runs the latest version of the image ( - latest defined as the latest pulled onto the local machine) - ''' - ins_container = __salt__['docker.inspect_container'] - cinfos = ins_container(name) - if 'id' in cinfos: - cid = cinfos['id'] - else: - cid = name - if not cinfos['status']: - return _invalid(comment='Container {0} not found'.format(cid or name)) - if cinfos['status'] and image is None: - return _valid(comment='Container {0} exists'.format(cid)) - image_name = _get_image_name(image, tag) - if cinfos['status'] and cinfos['out']['Config']["Image"] == image_name and not is_latest: - return _valid(comment='Container {0} exists and has image {1}'.format(cid, image_name)) - ins_image = __salt__['docker.inspect_image'] - iinfos = ins_image(image_name) - if cinfos['status'] and cinfos['out']['Image'] == iinfos['out']['Id']: - return _valid(comment='Container {0} exists and has latest version of image {1}'.format(cid, image_name)) - return _invalid(comment='Container {0} found with wrong image'.format(cid or name)) - - -def run(name, - cid=None, - hostname=None, - onlyif=None, - unless=None, - docked_onlyif=None, - docked_unless=None, - *args, **kwargs): - ''' - Run a command in a specific container - - You can match by either name or hostname - - name - command to run in the container - - cid - Container id or name - - state_id - state_id - - onlyif - Only execute cmd if statement on the host returns 0 - - unless - Do not execute cmd if statement on the host returns 0 - - docked_onlyif - Only execute cmd if statement in the container returns 0 - - docked_unless - Do not execute cmd if statement in the container returns 0 - - ''' - if hostname: - salt.utils.warn_until( - 'Helium', - 'The \'hostname\' argument has been deprecated.' - ) - retcode = __salt__['docker.retcode'] - drun_all = __salt__['docker.run_all'] - valid = functools.partial(_valid, name=name) - if onlyif is not None: - if not isinstance(onlyif, string_types): - if not onlyif: - return valid(comment='onlyif execution failed') - elif isinstance(onlyif, string_types): - if not __salt__['cmd.retcode'](onlyif) == 0: - return valid(comment='onlyif execution failed') - - if unless is not None: - if not isinstance(unless, string_types): - if unless: - return valid(comment='unless execution succeeded') - elif isinstance(unless, string_types): - if __salt__['cmd.retcode'](unless) == 0: - return valid(comment='unless execution succeeded') - - if docked_onlyif is not None: - if not isinstance(docked_onlyif, string_types): - if not docked_onlyif: - return valid(comment='docked_onlyif execution failed') - elif isinstance(docked_onlyif, string_types): - if not retcode(cid, docked_onlyif): - return valid(comment='docked_onlyif execution failed') - - if docked_unless is not None: - if not isinstance(docked_unless, string_types): - if docked_unless: - return valid(comment='docked_unless execution succeeded') - elif isinstance(docked_unless, string_types): - if retcode(cid, docked_unless): - return valid(comment='docked_unless execution succeeded') - - if __opts__['test']: - comment = 'Command \'{0}\' will be executed on container {1}'.format(name, cid) - return _ret_status(name=name, comment=comment) - - result = drun_all(cid, name) - if result['status']: - return valid(comment=result['comment']) - else: - return _invalid(comment=result['comment'], name=name) - - -def script(*args, **kw): - ''' - Placeholder function for a cmd.script alike. - - .. note:: - - Not yet implemented. - Its implementation might be very similar from - :mod:`salt.states.dockerio.run` - ''' - raise NotImplementedError - - -def running(name, - image, - tag='latest', - container=None, - command=None, - hostname=None, - user=None, - detach=True, - stdin_open=False, - tty=False, - mem_limit=None, - ports=None, - environment=None, - dns=None, - volumes=None, - volumes_from=None, - start=True, - cap_add=None, - cap_drop=None, - privileged=None, - lxc_conf=None, - network_mode=None, - check_is_running=True, - publish_all_ports=False, - links=None, - restart_policy=None, - cpu_shares=None, - cpuset=None, - kill_signal=None, - *args, **kwargs): - ''' - Ensure that a container is running. If the container does not exist, it - will be created from the specified image. (`docker run`) - - name / container - Name for the container - - image - Image from which to build this container - - tag - tag of the image (defaults to 'latest') - - environment - Environment variables for the container, either - - a mapping of key, values - - a list of mappings of key, values - ports - List of ports definitions, either: - - a port to map - - a mapping of mapping portInHost : PortInContainer - - .. code-block:: yaml - - - ports: - - "5000/tcp": - HostIp: "" - HostPort: "5000" - - publish_all_ports - Publish all ports from the port list (default is false, - only meaningful if port does not contain portinhost:portincontainer mapping) - - volumes - List of volumes to mount or create in the container (like ``-v`` of ``docker run`` command), - mapping host directory to container directory. - - To specify a volume in the container in terse list format: - - .. code-block:: yaml - - - volumes: - - "/var/log/service" # container-only volume - - "/srv/timezone:/etc/timezone" # bound volume - - "/usr/local/etc/passwd:/etc/passwd:ro" # read-only bound volume - - You can also use the short dictionary form (note that the notion of - source:target from docker is preserved): - - .. code-block:: yaml - - - volumes: - - /var/log/service: /var/log/service # mandatory read-write implied - - Or, alternatively, to specify read-only mounting, use the extended form: - - .. code-block:: yaml - - - volumes: - - /home/user1: - bind: /mnt/vol2 - ro: True - - /var/www: - bind: /mnt/vol1 - ro: False - - Or (for backwards compatibility) another dict style: - - .. code-block:: yaml - - - volumes: - /home/user1: - bind: /mnt/vol2 - ro: True - /var/www: - bind: /mnt/vol1 - ro: False - - volumes_from - List of containers to share volumes with - - dns - List of DNS servers. - - .. code-block:: yaml - - - dns: - - 127.0.0.1 - - network_mode - - 'bridge': creates a new network stack for the container on the docker bridge - - 'none': no networking for this container - - 'container:[name|id]': reuses another container network stack) - - 'host': use the host network stack inside the container - - .. code-block:: yaml - - - network_mode: host - - restart_policy - Restart policy to apply when a container exits (no, on-failure[:max-retry], always) - - .. code-block:: yaml - - - restart_policy: - MaximumRetryCount: 5 - Name: on-failure - - cap_add - List of capabilities to add in a container. - - cap_drop - List of capabilities to drop in a container. - - check_is_running - Enable checking if a container should run or not. - Useful for data-only containers that must be linked to another one. - e.g. nginx <- static-files - - cpu_shares - CPU shares (relative weight) - - .. code-block:: yaml - - - cpu_shares: 2 - cpuset - CPUs in which to allow execution ('0-3' or '0,1') - - .. code-block:: yaml - - - cpuset: '0-3' - kill_signal - If defined, its value will be sent as a kill signal to the running - container. i.e. It will use client.kill(signal=kill_signal) - instead of client.restart(), when the state is triggered by a watcher - requisite. - - possible use case: Soft reload of nginx - - .. code-block:: yaml - - nginx: - docker.running: - - image: some-fictional-registry.com/nginx - - tag: latest - - kill_signal: SIGHUP - - watch: - - file: /etc/nginx/nginx.conf - - This state will ask nginx to reload (instead of restart) - each time the /etc/nginx/nginx.conf is modified. - - .. versionadded:: 2015.8.0 - - - For other parameters, see salt.modules.dockerio execution module - and the docker-py python bindings for docker documentation - `_ for - `docker.create_container`. - - .. note:: - This command does not verify that the named container - is running the specified image. - ''' - if container is None: - container = name - ins_image = __salt__['docker.inspect_image'] - ins_container = __salt__['docker.inspect_container'] - create = __salt__['docker.create_container'] - image_name = _get_image_name(image, tag) - iinfos = ins_image(image_name) - image_exists = iinfos['status'] - - if not image_exists: - return _invalid(comment='image "{0}" does not exists'.format(image_name)) - - cinfos = ins_container(name) - already_exists = cinfos['status'] - already_exists_with_same_image = ( - # if container is known by name, - already_exists - # and the container is based on expected image, - and cinfos['out']['Image'] == iinfos['out']['Id'] - # then assume it already exists. - ) - - is_running = __salt__['docker.is_running'](container) - - # if container exists but is not started, try to start it - if already_exists_with_same_image and (is_running or not start): - return _valid(comment='container \'{0}\' already exists'.format(name)) - if not already_exists_with_same_image and already_exists: - # Outdated container: It means it runs against an old image. - # We're gonna have to stop and remove the old container, to let - # the name available for the new one. - if __opts__['test']: - comment = 'Will replace outdated container \'{0}\''.format(name) - return _ret_status(name=name, comment=comment) - if is_running: - stop_status = __salt__['docker.stop'](name) - if not stop_status['status']: - return _invalid(comment='Failed to stop outdated container \'{0}\''.format(name)) - - remove_status = __salt__['docker.remove_container'](name) - if not remove_status['status']: - return _invalid(comment='Failed to remove outdated container \'{0}\''.format(name)) - already_exists = False - # now it's clear, the name is available for the new container - - if __opts__['test']: - comment = 'Will create container \'{0}\''.format(name) - return _ret_status(name=name, comment=comment) - - # parse input data - exposeports, bindports, contvolumes, bindvolumes, denvironment, changes = [], {}, [], {}, {}, [] - if not ports: - ports = {} - if not volumes: - volumes = {} - if not volumes_from: - volumes_from = [] - if isinstance(environment, dict): - for key in environment: - denvironment[six.text_type(key)] = six.text_type(environment[key]) - if isinstance(environment, list): - for var in environment: - if isinstance(var, dict): - for key in var: - denvironment[six.text_type(key)] = six.text_type(var[key]) - if isinstance(volumes, dict): - bindvolumes = volumes - if isinstance(volumes, list): - for vol in volumes: - if isinstance(vol, dict): - # get source as the dict key - source = list(vol.keys())[0] - # then find target - if isinstance(vol[source], dict): - target = vol[source]['bind'] - read_only = vol[source].get('ro', False) - else: - target = str(vol[source]) - read_only = False - bindvolumes[source] = {'bind': target, - 'ro': read_only - } - else: - # assume just an own volumes - contvolumes.append(str(vol)) - if isinstance(ports, dict): - bindports = ports - # in dict form all ports bind, so no need for exposeports - if isinstance(ports, list): - for port in ports: - if isinstance(port, dict): - container_port = list(port.keys())[0] - # find target - if isinstance(port[container_port], dict): - host_port = port[container_port]['HostPort'] - host_ip = port[container_port].get('HostIp', '0.0.0.0') - else: - host_port = str(port[container_port]) - host_ip = '0.0.0.0' - bindports[container_port] = { - 'HostPort': host_port, - 'HostIp': host_ip - } - else: - # assume just a port to expose - exposeports.append(str(port)) - - parsed_volumes = _parse_volumes(volumes) - bindvolumes = parsed_volumes['bindvols'] - contvolumes = parsed_volumes['contvols'] - - if not already_exists: - kwargs = dict(command=command, - hostname=hostname, - user=user, - detach=detach, - stdin_open=stdin_open, - tty=tty, - mem_limit=mem_limit, - ports=exposeports, - environment=denvironment, - dns=dns, - binds=bindvolumes, - volumes=contvolumes, - name=name, - cpu_shares=cpu_shares, - cpuset=cpuset) - out = create(image_name, **kwargs) - # if container has been created, even if not started, we mark - # it as installed - try: - cid = out['out']['info']['id'] - log.debug(str(cid)) - except Exception as e: - changes.append('Container created') - log.debug(str(e)) - else: - changes.append('Container {0} created'.format(cid)) - if start: - started = __salt__['docker.start'](name, - binds=bindvolumes, - port_bindings=bindports, - lxc_conf=lxc_conf, - publish_all_ports=publish_all_ports, - links=links, - privileged=privileged, - dns=dns, - volumes_from=volumes_from, - network_mode=network_mode, - restart_policy=restart_policy, - cap_add=cap_add, - cap_drop=cap_drop) - if check_is_running: - is_running = __salt__['docker.is_running'](name) - log.debug("Docker-io running:" + str(started)) - log.debug("Docker-io running:" + str(is_running)) - if is_running: - changes.append('Container \'{0}\' started.\n'.format(name)) - else: - return _invalid(comment=('Container \'{0}\' cannot be started\n{1!s}' - .format(name, started['out'],))) - else: - changes.append('Container \'{0}\' started.\n'.format(name)) - return _valid(comment='\n'.join(changes), changes={name: True}) diff --git a/tests/integration/modules/dockertest.py b/tests/integration/modules/dockertest.py deleted file mode 100644 index a274ca9f3a..0000000000 --- a/tests/integration/modules/dockertest.py +++ /dev/null @@ -1,138 +0,0 @@ -# -*- coding: utf-8 -*- -''' -Tests for integration with Docker's Python library - -:codeauthor: :email:`C. R. Oldham ` -''' - -# Import python libs -from __future__ import absolute_import -import os -import string -import logging - -# Import Salt Testing libs -from salttesting.helpers import ensure_in_syspath, requires_salt_modules -from salttesting import skipIf -ensure_in_syspath('../../') - -# Import salt libs -import integration - -log = logging.getLogger(__name__) - - -@requires_salt_modules('docker') -class DockerTest(integration.ModuleCase): - ''' - Test docker integration - ''' - - def _get_container_id(self, image_name=None): - cmdstring = 'docker ps | grep {0}'.format(image_name) - ret_cmdrun = self.run_function('cmd.run_all', cmd=cmdstring) - ids = [] - for l in ret_cmdrun['stdout'].splitlines(): - try: - ids.append(string.split(ret_cmdrun['stdout'])[0]) - except IndexError: - pass - return ids - - def test_version(self): - ''' - dockerio.version - ''' - ret = self.run_function('docker.version') - ret_cmdrun = self.run_function('cmd.run_all', cmd='docker version | grep "Client version:"') - self.assertEqual('Client version: {0}'.format(ret['out']['Version']), ret_cmdrun['stdout']) - - def test_build(self): - ''' - dockerio.build - - Long timeout here because build will transfer many images from the Internet - before actually creating the final container - ''' - dockerfile_path = os.path.join(integration.INTEGRATION_TEST_DIR, 'files/file/base/') - ret = self.run_function('docker.build', timeout=300, path=dockerfile_path, tag='testsuite_image') - self.assertTrue(ret['status'], 'Image built') - - def test_images(self): - ''' - dockerio.get_images - ''' - ret = self.run_function('docker.get_images') - foundit = False - for i in ret['out']: - try: - if i['RepoTags'][0] == 'testsuite_image:latest': - foundit = True - break - except KeyError: - pass - self.assertTrue(foundit, 'Could not find created image.') - - def test_create_container(self): - ''' - dockerio.create_container - ''' - - ret = self.run_function('docker.create_container', image='testsuite_image', command='echo ping') - self.assertTrue(ret['status'], 'Container was not created') - - @skipIf(True, "Currently broken") - def test_stop(self): - ''' - dockerio.stop - ''' - - container_id = self._get_container_id(image_name='testsuite_image') - self.assertTrue(container_id, 'test_stop: found no containers running') - for i in container_id: - ret = self.run_function('docker.stop', i) - self.assertFalse(self.run_function('docker.is_running', i)) - - @skipIf(True, "Currently broken") - def test_run_stdout(self): - ''' - dockerio.run_stdout - - The testsuite Dockerfile creates a file in the image's /tmp folder called 'cheese' - The Dockerfile is in salt/tests/integration/files/file/base/Dockerfile - - ''' - - run_ret = self.run_function('docker.create_container', image='testsuite_image') - base_container_id = run_ret['id'] - ret = self.run_function('docker.run_stdout', container=base_container_id, cmd="cat /tmp/cheese") - run_container_id = ret['id'] - self.assertEqual(ret['out'], 'The cheese shop is open') - self.run_function('docker.stop', base_container_id) - self.run_function('docker.stop', run_container_id) - self.assertFalse(self.run_function('docker.is_running', base_container_id)) - self.assertFalse(self.run_function('docker.is_running', run_container_id)) - - @skipIf(True, "Currently broken") - def test_commit(self): - ''' - dockerio.commit - ''' - - run_ret = self.run_function('docker.create_container', image='testsuite_image') - log.debug("first container: {0}".format(run_ret)) - base_container_id = run_ret['id'] - ret = self.run_function('docker.run_stdout', container=base_container_id, cmd='echo "The cheese shop is now closed." > /tmp/deadcheese') - log.debug("second container: {0}".format(ret)) - run_container_id = ret['id'] - commit_ret = self.run_function('docker.commit', container=base_container_id, repository='testsuite_committed_img', message='This image was created by the testsuite') - log.debug("post-commit: {0}".format(commit_ret)) - self.run_function('docker.stop', run_container_id) - new_container = self.run_function('docker.create_container', image='testsuite_committed_img') - final_ret = self.run_function('docker.run_stdout', container=new_container['id'], cmd='cat /tmp/cheese') - self.assertEqual(final_ret['out'], 'The cheese shop is now closed.') - - -if __name__ == '__main__': - from integration import run_tests - run_tests(DockerTest) diff --git a/tests/unit/modules/dockerng_test.py b/tests/unit/modules/docker_test.py similarity index 74% rename from tests/unit/modules/dockerng_test.py rename to tests/unit/modules/docker_test.py index 360d52c053..dcd5c64817 100644 --- a/tests/unit/modules/dockerng_test.py +++ b/tests/unit/modules/docker_test.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- ''' -Unit tests for the dockerng module +Unit tests for the docker module ''' # Import Python Libs @@ -21,52 +21,52 @@ ensure_in_syspath('../../') # Import Salt Libs from salt.ext.six.moves import range -from salt.modules import dockerng as dockerng_mod +from salt.modules import docker as docker_mod from salt.exceptions import CommandExecutionError, SaltInvocationError -dockerng_mod.__context__ = {'docker.docker_version': ''} -dockerng_mod.__salt__ = {} -dockerng_mod.__opts__ = {} +docker_mod.__context__ = {'docker.docker_version': ''} +docker_mod.__salt__ = {} +docker_mod.__opts__ = {} def _docker_py_version(): - return dockerng_mod.docker.version_info if dockerng_mod.HAS_DOCKER_PY else (0,) + return docker_mod.docker.version_info if docker_mod.HAS_DOCKER_PY else (0,) @skipIf(NO_MOCK, NO_MOCK_REASON) -class DockerngTestCase(TestCase): +class DockerTestCase(TestCase): ''' - Validate dockerng module + Validate docker module ''' def test_ps_with_host_true(self): ''' - Check that dockerng.ps called with host is ``True``, + Check that docker.ps called with host is ``True``, include resutlt of ``network.interfaces`` command in returned result. ''' network_interfaces = Mock(return_value={'mocked': None}) - with patch.dict(dockerng_mod.__salt__, + with patch.dict(docker_mod.__salt__, {'network.interfaces': network_interfaces}): - with patch.dict(dockerng_mod.__context__, + with patch.dict(docker_mod.__context__, {'docker.client': MagicMock()}): - ret = dockerng_mod.ps_(host=True) + ret = docker_mod.ps_(host=True) self.assertEqual(ret, {'host': {'interfaces': {'mocked': None}}}) def test_ps_with_filters(self): ''' - Check that dockerng.ps accept filters parameter. + Check that docker.ps accept filters parameter. ''' client = MagicMock() - with patch.dict(dockerng_mod.__context__, + with patch.dict(docker_mod.__context__, {'docker.client': client}): - dockerng_mod.ps_(filters={'label': 'KEY'}) + docker_mod.ps_(filters={'label': 'KEY'}) client.containers.assert_called_once_with( all=True, filters={'label': 'KEY'}) @skipIf(_docker_py_version() > 0, 'docker-py needs to be installed for this test to run') - @patch.object(dockerng_mod, '_get_exec_driver') + @patch.object(docker_mod, '_get_exec_driver') def test_check_mine_cache_is_refreshed_on_container_change_event(self, _): ''' Every command that might modify docker containers state. @@ -85,24 +85,24 @@ class DockerngTestCase(TestCase): ('_script', ('command',)), ): mine_send = Mock() - command = getattr(dockerng_mod, command_name) + command = getattr(docker_mod, command_name) docker_client = MagicMock() docker_client.api_version = '1.12' - with patch.dict(dockerng_mod.__salt__, + with patch.dict(docker_mod.__salt__, {'mine.send': mine_send, 'container_resource.run': MagicMock(), 'cp.cache_file': MagicMock(return_value=False)}): - with patch.dict(dockerng_mod.__context__, + with patch.dict(docker_mod.__context__, {'docker.client': docker_client}): command('container', *args) - mine_send.assert_called_with('dockerng.ps', verbose=True, all=True, + mine_send.assert_called_with('docker.ps', verbose=True, all=True, host=True) @skipIf(_docker_py_version() < (1, 4, 0), 'docker module must be installed to run this test or is too old. >=1.4.0') - @patch.object(dockerng_mod, 'images', MagicMock()) - @patch.object(dockerng_mod, 'inspect_image') - @patch.object(dockerng_mod, 'version', Mock(return_value={'ApiVersion': '1.19'})) + @patch.object(docker_mod, 'images', MagicMock()) + @patch.object(docker_mod, 'inspect_image') + @patch.object(docker_mod, 'version', Mock(return_value={'ApiVersion': '1.19'})) def test_create_with_arg_cmd(self, *args): ''' When cmd argument is passed check it is renamed to command. @@ -116,11 +116,11 @@ class DockerngTestCase(TestCase): client.api_version = '1.19' client.create_host_config.return_value = host_config client.create_container.return_value = {} - with patch.dict(dockerng_mod.__dict__, + with patch.dict(docker_mod.__dict__, {'__salt__': __salt__}): - with patch.dict(dockerng_mod.__context__, + with patch.dict(docker_mod.__context__, {'docker.client': client}): - dockerng_mod.create('image', cmd='ls', name='ctn') + docker_mod.create('image', cmd='ls', name='ctn') client.create_container.assert_called_once_with( command='ls', host_config=host_config, @@ -129,9 +129,9 @@ class DockerngTestCase(TestCase): @skipIf(_docker_py_version() < (1, 4, 0), 'docker module must be installed to run this test or is too old. >=1.4.0') - @patch.object(dockerng_mod, 'images', MagicMock()) - @patch.object(dockerng_mod, 'inspect_image') - @patch.object(dockerng_mod, 'version', Mock(return_value={'ApiVersion': '1.19'})) + @patch.object(docker_mod, 'images', MagicMock()) + @patch.object(docker_mod, 'inspect_image') + @patch.object(docker_mod, 'version', Mock(return_value={'ApiVersion': '1.19'})) def test_create_send_host_config(self, *args): ''' Check host_config object is passed to create_container. @@ -145,11 +145,11 @@ class DockerngTestCase(TestCase): client.api_version = '1.19' client.create_host_config.return_value = host_config client.create_container.return_value = {} - with patch.dict(dockerng_mod.__dict__, + with patch.dict(docker_mod.__dict__, {'__salt__': __salt__}): - with patch.dict(dockerng_mod.__context__, + with patch.dict(docker_mod.__context__, {'docker.client': client}): - dockerng_mod.create('image', name='ctn', publish_all_ports=True) + docker_mod.create('image', name='ctn', publish_all_ports=True) client.create_container.assert_called_once_with( host_config=host_config, image='image', @@ -157,9 +157,9 @@ class DockerngTestCase(TestCase): @skipIf(_docker_py_version() < (1, 4, 0), 'docker module must be installed to run this test or is too old. >=1.4.0') - @patch.object(dockerng_mod, 'images', MagicMock()) - @patch.object(dockerng_mod, 'inspect_image') - @patch.object(dockerng_mod, 'version', Mock(return_value={'ApiVersion': '1.19'})) + @patch.object(docker_mod, 'images', MagicMock()) + @patch.object(docker_mod, 'inspect_image') + @patch.object(docker_mod, 'version', Mock(return_value={'ApiVersion': '1.19'})) def test_create_with_labels_dict(self, *args): ''' Create container with labels dictionary. @@ -173,11 +173,11 @@ class DockerngTestCase(TestCase): client.api_version = '1.19' client.create_host_config.return_value = host_config client.create_container.return_value = {} - with patch.dict(dockerng_mod.__dict__, + with patch.dict(docker_mod.__dict__, {'__salt__': __salt__}): - with patch.dict(dockerng_mod.__context__, + with patch.dict(docker_mod.__context__, {'docker.client': client}): - dockerng_mod.create( + docker_mod.create( 'image', name='ctn', labels={'KEY': 'VALUE'}, @@ -192,9 +192,9 @@ class DockerngTestCase(TestCase): @skipIf(_docker_py_version() < (1, 4, 0), 'docker module must be installed to run this test or is too old. >=1.4.0') - @patch.object(dockerng_mod, 'images', MagicMock()) - @patch.object(dockerng_mod, 'inspect_image') - @patch.object(dockerng_mod, 'version', Mock(return_value={'ApiVersion': '1.19'})) + @patch.object(docker_mod, 'images', MagicMock()) + @patch.object(docker_mod, 'inspect_image') + @patch.object(docker_mod, 'version', Mock(return_value={'ApiVersion': '1.19'})) def test_create_with_labels_list(self, *args): ''' Create container with labels list. @@ -208,11 +208,11 @@ class DockerngTestCase(TestCase): client.api_version = '1.19' client.create_host_config.return_value = host_config client.create_container.return_value = {} - with patch.dict(dockerng_mod.__dict__, + with patch.dict(docker_mod.__dict__, {'__salt__': __salt__}): - with patch.dict(dockerng_mod.__context__, + with patch.dict(docker_mod.__context__, {'docker.client': client}): - dockerng_mod.create( + docker_mod.create( 'image', name='ctn', labels=['KEY1', 'KEY2'], @@ -227,9 +227,9 @@ class DockerngTestCase(TestCase): @skipIf(_docker_py_version() < (1, 4, 0), 'docker module must be installed to run this test or is too old. >=1.4.0') - @patch.object(dockerng_mod, 'images', MagicMock()) - @patch.object(dockerng_mod, 'inspect_image') - @patch.object(dockerng_mod, 'version', Mock(return_value={'ApiVersion': '1.19'})) + @patch.object(docker_mod, 'images', MagicMock()) + @patch.object(docker_mod, 'inspect_image') + @patch.object(docker_mod, 'version', Mock(return_value={'ApiVersion': '1.19'})) def test_create_with_labels_error(self, *args): ''' Create container with invalid labels. @@ -243,12 +243,12 @@ class DockerngTestCase(TestCase): client.api_version = '1.19' client.create_host_config.return_value = host_config client.create_container.return_value = {} - with patch.dict(dockerng_mod.__dict__, + with patch.dict(docker_mod.__dict__, {'__salt__': __salt__}): - with patch.dict(dockerng_mod.__context__, + with patch.dict(docker_mod.__context__, {'docker.client': client}): self.assertRaises(SaltInvocationError, - dockerng_mod.create, + docker_mod.create, 'image', name='ctn', labels=22, @@ -257,9 +257,9 @@ class DockerngTestCase(TestCase): @skipIf(_docker_py_version() < (1, 4, 0), 'docker module must be installed to run this test or is too old. >=1.4.0') - @patch.object(dockerng_mod, 'images', MagicMock()) - @patch.object(dockerng_mod, 'inspect_image') - @patch.object(dockerng_mod, 'version', Mock(return_value={'ApiVersion': '1.19'})) + @patch.object(docker_mod, 'images', MagicMock()) + @patch.object(docker_mod, 'inspect_image') + @patch.object(docker_mod, 'version', Mock(return_value={'ApiVersion': '1.19'})) def test_create_with_labels_dictlist(self, *args): ''' Create container with labels dictlist. @@ -273,11 +273,11 @@ class DockerngTestCase(TestCase): client.api_version = '1.19' client.create_host_config.return_value = host_config client.create_container.return_value = {} - with patch.dict(dockerng_mod.__dict__, + with patch.dict(docker_mod.__dict__, {'__salt__': __salt__}): - with patch.dict(dockerng_mod.__context__, + with patch.dict(docker_mod.__context__, {'docker.client': client}): - dockerng_mod.create( + docker_mod.create( 'image', name='ctn', labels=[{'KEY1': 'VALUE1'}, {'KEY2': 'VALUE2'}], @@ -303,11 +303,11 @@ class DockerngTestCase(TestCase): host_config = {} client = Mock() client.api_version = '1.21' - with patch.dict(dockerng_mod.__dict__, + with patch.dict(docker_mod.__dict__, {'__salt__': __salt__}): - with patch.dict(dockerng_mod.__context__, + with patch.dict(docker_mod.__context__, {'docker.client': client}): - dockerng_mod.networks( + docker_mod.networks( names=['foo'], ids=['01234'], ) @@ -329,11 +329,11 @@ class DockerngTestCase(TestCase): host_config = {} client = Mock() client.api_version = '1.21' - with patch.dict(dockerng_mod.__dict__, + with patch.dict(docker_mod.__dict__, {'__salt__': __salt__}): - with patch.dict(dockerng_mod.__context__, + with patch.dict(docker_mod.__context__, {'docker.client': client}): - dockerng_mod.create_network( + docker_mod.create_network( 'foo', driver='bridge', ) @@ -355,11 +355,11 @@ class DockerngTestCase(TestCase): host_config = {} client = Mock() client.api_version = '1.21' - with patch.dict(dockerng_mod.__dict__, + with patch.dict(docker_mod.__dict__, {'__salt__': __salt__}): - with patch.dict(dockerng_mod.__context__, + with patch.dict(docker_mod.__context__, {'docker.client': client}): - dockerng_mod.remove_network('foo') + docker_mod.remove_network('foo') client.remove_network.assert_called_once_with('foo') @skipIf(_docker_py_version() < (1, 5, 0), @@ -375,11 +375,11 @@ class DockerngTestCase(TestCase): host_config = {} client = Mock() client.api_version = '1.21' - with patch.dict(dockerng_mod.__dict__, + with patch.dict(docker_mod.__dict__, {'__salt__': __salt__}): - with patch.dict(dockerng_mod.__context__, + with patch.dict(docker_mod.__context__, {'docker.client': client}): - dockerng_mod.inspect_network('foo') + docker_mod.inspect_network('foo') client.inspect_network.assert_called_once_with('foo') @skipIf(_docker_py_version() < (1, 5, 0), @@ -395,13 +395,16 @@ class DockerngTestCase(TestCase): host_config = {} client = Mock() client.api_version = '1.21' - with patch.dict(dockerng_mod.__dict__, + + context = {'docker.client': client, + 'docker.exec_driver': 'docker-exec'} + + with patch.dict(docker_mod.__dict__, {'__salt__': __salt__}): - with patch.dict(dockerng_mod.__context__, - {'docker.client': client}): - dockerng_mod.connect_container_to_network('container', 'foo') + with patch.dict(docker_mod.__context__, context): + docker_mod.connect_container_to_network('container', 'foo') client.connect_container_to_network.assert_called_once_with( - 'container', 'foo') + 'container', 'foo', None) @skipIf(_docker_py_version() < (1, 5, 0), 'docker module must be installed to run this test or is too old. >=1.5.0') @@ -416,11 +419,11 @@ class DockerngTestCase(TestCase): host_config = {} client = Mock() client.api_version = '1.21' - with patch.dict(dockerng_mod.__dict__, + with patch.dict(docker_mod.__dict__, {'__salt__': __salt__}): - with patch.dict(dockerng_mod.__context__, + with patch.dict(docker_mod.__context__, {'docker.client': client}): - dockerng_mod.disconnect_container_from_network('container', 'foo') + docker_mod.disconnect_container_from_network('container', 'foo') client.disconnect_container_from_network.assert_called_once_with( 'container', 'foo') @@ -436,11 +439,11 @@ class DockerngTestCase(TestCase): } client = Mock() client.api_version = '1.21' - with patch.dict(dockerng_mod.__dict__, + with patch.dict(docker_mod.__dict__, {'__salt__': __salt__}): - with patch.dict(dockerng_mod.__context__, + with patch.dict(docker_mod.__context__, {'docker.client': client}): - dockerng_mod.volumes( + docker_mod.volumes( filters={'dangling': [True]}, ) client.volumes.assert_called_once_with( @@ -459,11 +462,11 @@ class DockerngTestCase(TestCase): } client = Mock() client.api_version = '1.21' - with patch.dict(dockerng_mod.__dict__, + with patch.dict(docker_mod.__dict__, {'__salt__': __salt__}): - with patch.dict(dockerng_mod.__context__, + with patch.dict(docker_mod.__context__, {'docker.client': client}): - dockerng_mod.create_volume( + docker_mod.create_volume( 'foo', driver='bridge', driver_opts={}, @@ -486,11 +489,11 @@ class DockerngTestCase(TestCase): } client = Mock() client.api_version = '1.21' - with patch.dict(dockerng_mod.__dict__, + with patch.dict(docker_mod.__dict__, {'__salt__': __salt__}): - with patch.dict(dockerng_mod.__context__, + with patch.dict(docker_mod.__context__, {'docker.client': client}): - dockerng_mod.remove_volume('foo') + docker_mod.remove_volume('foo') client.remove_volume.assert_called_once_with('foo') @skipIf(_docker_py_version() < (1, 5, 0), @@ -505,26 +508,26 @@ class DockerngTestCase(TestCase): } client = Mock() client.api_version = '1.21' - with patch.dict(dockerng_mod.__dict__, + with patch.dict(docker_mod.__dict__, {'__salt__': __salt__}): - with patch.dict(dockerng_mod.__context__, + with patch.dict(docker_mod.__context__, {'docker.client': client}): - dockerng_mod.inspect_volume('foo') + docker_mod.inspect_volume('foo') client.inspect_volume.assert_called_once_with('foo') def test_wait_success(self): client = Mock() client.api_version = '1.21' client.wait = Mock(return_value=0) - dockerng_inspect_container = Mock(side_effect=[ + docker_inspect_container = Mock(side_effect=[ {'State': {'Running': True}}, {'State': {'Stopped': True}}]) - with patch.object(dockerng_mod, 'inspect_container', - dockerng_inspect_container): - with patch.dict(dockerng_mod.__context__, + with patch.object(docker_mod, 'inspect_container', + docker_inspect_container): + with patch.dict(docker_mod.__context__, {'docker.client': client}): - dockerng_mod._clear_context() - result = dockerng_mod.wait('foo') + docker_mod._clear_context() + result = docker_mod.wait('foo') self.assertEqual(result, {'result': True, 'exit_status': 0, 'state': {'new': 'stopped', @@ -534,16 +537,16 @@ class DockerngTestCase(TestCase): client = Mock() client.api_version = '1.21' client.wait = Mock(return_value=0) - dockerng_inspect_container = Mock(side_effect=[ + docker_inspect_container = Mock(side_effect=[ {'State': {'Stopped': True}}, {'State': {'Stopped': True}}, ]) - with patch.object(dockerng_mod, 'inspect_container', - dockerng_inspect_container): - with patch.dict(dockerng_mod.__context__, + with patch.object(docker_mod, 'inspect_container', + docker_inspect_container): + with patch.dict(docker_mod.__context__, {'docker.client': client}): - dockerng_mod._clear_context() - result = dockerng_mod.wait('foo') + docker_mod._clear_context() + result = docker_mod.wait('foo') self.assertEqual(result, {'result': False, 'comment': "Container 'foo' already stopped", 'exit_status': 0, @@ -554,16 +557,16 @@ class DockerngTestCase(TestCase): client = Mock() client.api_version = '1.21' client.wait = Mock(return_value=0) - dockerng_inspect_container = Mock(side_effect=[ + docker_inspect_container = Mock(side_effect=[ {'State': {'Stopped': True}}, {'State': {'Stopped': True}}, ]) - with patch.object(dockerng_mod, 'inspect_container', - dockerng_inspect_container): - with patch.dict(dockerng_mod.__context__, + with patch.object(docker_mod, 'inspect_container', + docker_inspect_container): + with patch.dict(docker_mod.__context__, {'docker.client': client}): - dockerng_mod._clear_context() - result = dockerng_mod.wait('foo', ignore_already_stopped=True) + docker_mod._clear_context() + result = docker_mod.wait('foo', ignore_already_stopped=True) self.assertEqual(result, {'result': True, 'comment': "Container 'foo' already stopped", 'exit_status': 0, @@ -573,13 +576,13 @@ class DockerngTestCase(TestCase): def test_wait_success_absent_container(self): client = Mock() client.api_version = '1.21' - dockerng_inspect_container = Mock(side_effect=CommandExecutionError) - with patch.object(dockerng_mod, 'inspect_container', - dockerng_inspect_container): - with patch.dict(dockerng_mod.__context__, + docker_inspect_container = Mock(side_effect=CommandExecutionError) + with patch.object(docker_mod, 'inspect_container', + docker_inspect_container): + with patch.dict(docker_mod.__context__, {'docker.client': client}): - dockerng_mod._clear_context() - result = dockerng_mod.wait('foo', ignore_already_stopped=True) + docker_mod._clear_context() + result = docker_mod.wait('foo', ignore_already_stopped=True) self.assertEqual(result, {'result': True, 'comment': "Container 'foo' absent"}) @@ -587,15 +590,15 @@ class DockerngTestCase(TestCase): client = Mock() client.api_version = '1.21' client.wait = Mock(return_value=1) - dockerng_inspect_container = Mock(side_effect=[ + docker_inspect_container = Mock(side_effect=[ {'State': {'Running': True}}, {'State': {'Stopped': True}}]) - with patch.object(dockerng_mod, 'inspect_container', - dockerng_inspect_container): - with patch.dict(dockerng_mod.__context__, + with patch.object(docker_mod, 'inspect_container', + docker_inspect_container): + with patch.dict(docker_mod.__context__, {'docker.client': client}): - dockerng_mod._clear_context() - result = dockerng_mod.wait('foo', fail_on_exit_status=True) + docker_mod._clear_context() + result = docker_mod.wait('foo', fail_on_exit_status=True) self.assertEqual(result, {'result': False, 'exit_status': 1, 'state': {'new': 'stopped', @@ -605,15 +608,15 @@ class DockerngTestCase(TestCase): client = Mock() client.api_version = '1.21' client.wait = Mock(return_value=1) - dockerng_inspect_container = Mock(side_effect=[ + docker_inspect_container = Mock(side_effect=[ {'State': {'Stopped': True}}, {'State': {'Stopped': True}}]) - with patch.object(dockerng_mod, 'inspect_container', - dockerng_inspect_container): - with patch.dict(dockerng_mod.__context__, + with patch.object(docker_mod, 'inspect_container', + docker_inspect_container): + with patch.dict(docker_mod.__context__, {'docker.client': client}): - dockerng_mod._clear_context() - result = dockerng_mod.wait('foo', + docker_mod._clear_context() + result = docker_mod.wait('foo', ignore_already_stopped=True, fail_on_exit_status=True) self.assertEqual(result, {'result': False, @@ -659,16 +662,15 @@ class DockerngTestCase(TestCase): }) ret = None - with patch.dict(dockerng_mod.__salt__, { - 'dockerng.start': docker_start_mock, - 'dockerng.create': docker_create_mock, - 'dockerng.stop': docker_stop_mock, - 'dockerng.commit': docker_commit_mock, - 'dockerng.sls': docker_sls_mock}): - ret = dockerng_mod.sls_build( - 'foo', - mods='foo', - ) + with patch.object(docker_mod, 'start', docker_start_mock): + with patch.object(docker_mod, 'create', docker_create_mock): + with patch.object(docker_mod, 'stop', docker_stop_mock): + with patch.object(docker_mod, 'commit', docker_commit_mock): + with patch.object(docker_mod, 'sls', docker_sls_mock): + ret = docker_mod.sls_build( + 'foo', + mods='foo', + ) docker_create_mock.assert_called_once_with( cmd='sleep infinity', image='opensuse/python', interactive=True, name='foo', tty=True) @@ -716,17 +718,16 @@ class DockerngTestCase(TestCase): }) ret = None - with patch.dict(dockerng_mod.__salt__, { - 'dockerng.start': docker_start_mock, - 'dockerng.create': docker_create_mock, - 'dockerng.stop': docker_stop_mock, - 'dockerng.rm': docker_rm_mock, - 'dockerng.sls': docker_sls_mock}): - ret = dockerng_mod.sls_build( - 'foo', - mods='foo', - dryrun=True - ) + with patch.object(docker_mod, 'start', docker_start_mock): + with patch.object(docker_mod, 'create', docker_create_mock): + with patch.object(docker_mod, 'stop', docker_stop_mock): + with patch.object(docker_mod, 'rm_', docker_rm_mock): + with patch.object(docker_mod, 'sls', docker_sls_mock): + ret = docker_mod.sls_build( + 'foo', + mods='foo', + dryrun=True + ) docker_create_mock.assert_called_once_with( cmd='sleep infinity', image='opensuse/python', interactive=True, name='foo', tty=True) @@ -778,18 +779,22 @@ class DockerngTestCase(TestCase): client = Mock() client.put_archive = Mock() - with patch.dict(dockerng_mod.__opts__, {'cachedir': '/tmp'}): - with patch.dict(dockerng_mod.__salt__, {'dockerng.run_all': docker_run_all_mock, - 'dockerng.copy_to': docker_copy_to_mock, - 'config.option': docker_config_mock}): - with patch.dict(dockerng_mod.__context__, {'docker.client': client}): - # call twice to verify tmp path later - for i in range(2): - ret = dockerng_mod.call( - 'ID', - 'test.arg', - 1, 2, - arg1='val1') + context = {'docker.client': client, + 'docker.exec_driver': 'docker-exec'} + salt_dunder = {'config.option': docker_config_mock} + + with patch.object(docker_mod, 'run_all', docker_run_all_mock): + with patch.object(docker_mod, 'copy_to', docker_copy_to_mock): + with patch.dict(docker_mod.__opts__, {'cachedir': '/tmp'}): + with patch.dict(docker_mod.__salt__, salt_dunder): + with patch.dict(docker_mod.__context__, context): + # call twice to verify tmp path later + for i in range(2): + ret = docker_mod.call( + 'ID', + 'test.arg', + 1, 2, + arg1='val1') # Check that the directory is different each time # [ call(name, [args]), ... @@ -823,14 +828,14 @@ class DockerngTestCase(TestCase): {'Id': 'sha256:abcdef'}, {'Id': 'sha256:abcdefg', 'RepoTags': ['image:latest']}]) - with patch.dict(dockerng_mod.__context__, + with patch.dict(docker_mod.__context__, {'docker.client': client}): - dockerng_mod._clear_context() - result = dockerng_mod.images() + docker_mod._clear_context() + result = docker_mod.images() self.assertEqual(result, {'sha256:abcdefg': {'RepoTags': ['image:latest']}}) if __name__ == '__main__': from integration import run_tests - run_tests(DockerngTestCase, needs_daemon=False) + run_tests(DockerTestCase, needs_daemon=False) diff --git a/tests/unit/modules/dockerio_test.py b/tests/unit/modules/dockerio_test.py deleted file mode 100644 index 151ebb8c2a..0000000000 --- a/tests/unit/modules/dockerio_test.py +++ /dev/null @@ -1,33 +0,0 @@ -# -*- coding: utf-8 -*- -''' - :codeauthor: :email:`Mike Place ` -''' - -# Import python libs -from __future__ import absolute_import - -# Import Salt Testing libs -from salttesting import TestCase, skipIf -from salttesting.helpers import ensure_in_syspath - -ensure_in_syspath('../../') - -from salt.modules import dockerio - -HAS_DOCKER = dockerio.__virtual__() - - -@skipIf(not HAS_DOCKER, 'The docker execution module must be available to run the DockerIO test case') -class DockerIoTestCase(TestCase): - def test__sizeof_fmt(self): - self.assertEqual('0.0 bytes', dockerio._sizeof_fmt(0)) - self.assertEqual('1.0 KB', dockerio._sizeof_fmt(1024)) - self.assertEqual('1.0 MB', dockerio._sizeof_fmt(1024**2)) - self.assertEqual('1.0 GB', dockerio._sizeof_fmt(1024**3)) - self.assertEqual('1.0 TB', dockerio._sizeof_fmt(1024**4)) - self.assertEqual('1.0 PB', dockerio._sizeof_fmt(1024**5)) - - -if __name__ == '__main__': - from integration import run_tests - run_tests(DockerIoTestCase, needs_daemon=False) diff --git a/tests/unit/modules/mine_test.py b/tests/unit/modules/mine_test.py index 9120587943..cdf85c4413 100644 --- a/tests/unit/modules/mine_test.py +++ b/tests/unit/modules/mine_test.py @@ -33,7 +33,7 @@ class MineTestCase(TestCase): ''' def test_get_docker(self): ''' - Test for Get all mine data for 'dockerng.ps' and run an + Test for Get all mine data for 'docker.ps' and run an aggregation. ''' ps_response = { @@ -81,7 +81,7 @@ class MineTestCase(TestCase): def test_get_docker_with_container_id(self): ''' - Test for Get all mine data for 'dockerng.ps' and run an + Test for Get all mine data for 'docker.ps' and run an aggregation. ''' ps_response = { diff --git a/tests/unit/states/dockerng_test.py b/tests/unit/states/docker_test.py similarity index 67% rename from tests/unit/states/dockerng_test.py rename to tests/unit/states/docker_test.py index b18ae66948..ccd8dfdb43 100644 --- a/tests/unit/states/dockerng_test.py +++ b/tests/unit/states/docker_test.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- ''' -Unit tests for the dockerng state +Unit tests for the docker state ''' # Import Python Libs @@ -22,46 +22,46 @@ ensure_in_syspath('../../') # Import Salt Libs from salt.exceptions import CommandExecutionError -from salt.modules import dockerng as dockerng_mod -from salt.states import dockerng as dockerng_state +from salt.modules import docker as docker_mod +from salt.states import docker as docker_state -dockerng_mod.__context__ = {'docker.docker_version': ''} -dockerng_mod.__salt__ = {} -dockerng_state.__context__ = {} -dockerng_state.__opts__ = {'test': False} +docker_mod.__context__ = {'docker.docker_version': ''} +docker_mod.__salt__ = {} +docker_state.__context__ = {} +docker_state.__opts__ = {'test': False} @skipIf(NO_MOCK, NO_MOCK_REASON) -class DockerngTestCase(TestCase): +class DockerTestCase(TestCase): ''' - Validate dockerng state + Validate docker state ''' def test_running_with_no_predifined_volume(self): ''' - Test dockerng.running function with an image + Test docker.running function with an image that doens't have VOLUME defined. The ``binds`` argument, should create a container with respective volumes extracted from ``binds``. ''' - dockerng_create = Mock() - dockerng_start = Mock() - __salt__ = {'dockerng.list_containers': MagicMock(), - 'dockerng.list_tags': MagicMock(), - 'dockerng.pull': MagicMock(), - 'dockerng.state': MagicMock(), - 'dockerng.inspect_image': MagicMock(), - 'dockerng.create': dockerng_create, - 'dockerng.start': dockerng_start, + docker_create = Mock() + docker_start = Mock() + __salt__ = {'docker.list_containers': MagicMock(), + 'docker.list_tags': MagicMock(), + 'docker.pull': MagicMock(), + 'docker.state': MagicMock(), + 'docker.inspect_image': MagicMock(), + 'docker.create': docker_create, + 'docker.start': docker_start, } - with patch.dict(dockerng_state.__dict__, + with patch.dict(docker_state.__dict__, {'__salt__': __salt__}): - dockerng_state.running( + docker_state.running( 'cont', image='image:latest', binds=['/host-0:/container-0:ro']) - dockerng_create.assert_called_with( + docker_create.assert_called_with( 'image:latest', validate_input=False, name='cont', @@ -69,37 +69,37 @@ class DockerngTestCase(TestCase): volumes=['/container-0'], validate_ip_addrs=False, client_timeout=60) - dockerng_start.assert_called_with('cont') + docker_start.assert_called_with('cont') def test_running_with_predifined_volume(self): ''' - Test dockerng.running function with an image + Test docker.running function with an image that already have VOLUME defined. The ``binds`` argument, should create a container with ``volumes`` extracted from ``binds``. ''' - dockerng_create = Mock() - dockerng_start = Mock() - dockerng_inspect_image = Mock(return_value={ + docker_create = Mock() + docker_start = Mock() + docker_inspect_image = Mock(return_value={ 'Id': 'abcd', 'Config': {'Config': {'Volumes': ['/host-1']}}, }) - __salt__ = {'dockerng.list_containers': MagicMock(), - 'dockerng.list_tags': MagicMock(), - 'dockerng.pull': MagicMock(), - 'dockerng.state': MagicMock(), - 'dockerng.inspect_image': dockerng_inspect_image, - 'dockerng.create': dockerng_create, - 'dockerng.start': dockerng_start, + __salt__ = {'docker.list_containers': MagicMock(), + 'docker.list_tags': MagicMock(), + 'docker.pull': MagicMock(), + 'docker.state': MagicMock(), + 'docker.inspect_image': docker_inspect_image, + 'docker.create': docker_create, + 'docker.start': docker_start, } - with patch.dict(dockerng_state.__dict__, + with patch.dict(docker_state.__dict__, {'__salt__': __salt__}): - dockerng_state.running( + docker_state.running( 'cont', image='image:latest', binds=['/host-0:/container-0:ro']) - dockerng_create.assert_called_with( + docker_create.assert_called_with( 'image:latest', validate_input=False, binds={'/host-0': {'bind': '/container-0', 'ro': True}}, @@ -107,37 +107,37 @@ class DockerngTestCase(TestCase): validate_ip_addrs=False, name='cont', client_timeout=60) - dockerng_start.assert_called_with('cont') + docker_start.assert_called_with('cont') def test_running_with_no_predifined_ports(self): ''' - Test dockerng.running function with an image + Test docker.running function with an image that doens't have EXPOSE defined. The ``port_bindings`` argument, should create a container with ``ports`` extracted from ``port_bindings``. ''' - dockerng_create = Mock() - dockerng_start = Mock() - dockerng_inspect_image = Mock(return_value={ + docker_create = Mock() + docker_start = Mock() + docker_inspect_image = Mock(return_value={ 'Id': 'abcd', 'Config': {'Config': {'ExposedPorts': {}}}, }) - __salt__ = {'dockerng.list_containers': MagicMock(), - 'dockerng.list_tags': MagicMock(), - 'dockerng.pull': MagicMock(), - 'dockerng.state': MagicMock(), - 'dockerng.inspect_image': dockerng_inspect_image, - 'dockerng.create': dockerng_create, - 'dockerng.start': dockerng_start, + __salt__ = {'docker.list_containers': MagicMock(), + 'docker.list_tags': MagicMock(), + 'docker.pull': MagicMock(), + 'docker.state': MagicMock(), + 'docker.inspect_image': docker_inspect_image, + 'docker.create': docker_create, + 'docker.start': docker_start, } - with patch.dict(dockerng_state.__dict__, + with patch.dict(docker_state.__dict__, {'__salt__': __salt__}): - dockerng_state.running( + docker_state.running( 'cont', image='image:latest', port_bindings=['9090:9797/tcp']) - dockerng_create.assert_called_with( + docker_create.assert_called_with( 'image:latest', validate_input=False, name='cont', @@ -145,11 +145,11 @@ class DockerngTestCase(TestCase): port_bindings={9797: [9090]}, validate_ip_addrs=False, client_timeout=60) - dockerng_start.assert_called_with('cont') + docker_start.assert_called_with('cont') def test_running_with_predifined_ports(self): ''' - Test dockerng.running function with an image + Test docker.running function with an image that expose ports (via Dockerfile EXPOSE statement). Check that `ports` contains ports defined on Image and by @@ -166,32 +166,32 @@ class DockerngTestCase(TestCase): .. code-block:: yaml container: - dockerng.running: + docker.running: - port_bindings: - '9090:9797/tcp' ''' - dockerng_create = Mock() - dockerng_start = Mock() - dockerng_inspect_image = Mock(return_value={ + docker_create = Mock() + docker_start = Mock() + docker_inspect_image = Mock(return_value={ 'Id': 'abcd', 'Config': {'ExposedPorts': {'9898/tcp': {}}} }) - __salt__ = {'dockerng.list_containers': MagicMock(), - 'dockerng.list_tags': MagicMock(), - 'dockerng.pull': MagicMock(), - 'dockerng.state': MagicMock(), - 'dockerng.inspect_image': dockerng_inspect_image, - 'dockerng.create': dockerng_create, - 'dockerng.start': dockerng_start, + __salt__ = {'docker.list_containers': MagicMock(), + 'docker.list_tags': MagicMock(), + 'docker.pull': MagicMock(), + 'docker.state': MagicMock(), + 'docker.inspect_image': docker_inspect_image, + 'docker.create': docker_create, + 'docker.start': docker_start, } - with patch.dict(dockerng_state.__dict__, + with patch.dict(docker_state.__dict__, {'__salt__': __salt__}): - dockerng_state.running( + docker_state.running( 'cont', image='image:latest', port_bindings=['9090:9797/tcp']) - dockerng_create.assert_called_with( + docker_create.assert_called_with( 'image:latest', validate_input=False, name='cont', @@ -199,7 +199,7 @@ class DockerngTestCase(TestCase): port_bindings={9797: [9090]}, validate_ip_addrs=False, client_timeout=60) - dockerng_start.assert_called_with('cont') + docker_start.assert_called_with('cont') def test_running_with_udp_bindings(self): ''' @@ -214,7 +214,7 @@ class DockerngTestCase(TestCase): .. code-block:: yaml container: - dockerng.running: + docker.running: - port_bindings: - '9090:9797/udp' @@ -223,33 +223,33 @@ class DockerngTestCase(TestCase): .. code-block:: yaml container: - dockerng.running: + docker.running: - ports: - 9797/udp - port_bindings: - '9090:9797/udp' ''' - dockerng_create = Mock() - dockerng_start = Mock() - dockerng_inspect_image = Mock(return_value={ + docker_create = Mock() + docker_start = Mock() + docker_inspect_image = Mock(return_value={ 'Id': 'abcd', 'Config': {'ExposedPorts': {}} }) - __salt__ = {'dockerng.list_containers': MagicMock(), - 'dockerng.list_tags': MagicMock(), - 'dockerng.pull': MagicMock(), - 'dockerng.state': MagicMock(), - 'dockerng.inspect_image': dockerng_inspect_image, - 'dockerng.create': dockerng_create, - 'dockerng.start': dockerng_start, + __salt__ = {'docker.list_containers': MagicMock(), + 'docker.list_tags': MagicMock(), + 'docker.pull': MagicMock(), + 'docker.state': MagicMock(), + 'docker.inspect_image': docker_inspect_image, + 'docker.create': docker_create, + 'docker.start': docker_start, } - with patch.dict(dockerng_state.__dict__, + with patch.dict(docker_state.__dict__, {'__salt__': __salt__}): - dockerng_state.running( + docker_state.running( 'cont', image='image:latest', port_bindings=['9090:9797/udp']) - dockerng_create.assert_called_with( + docker_create.assert_called_with( 'image:latest', validate_input=False, name='cont', @@ -257,7 +257,7 @@ class DockerngTestCase(TestCase): port_bindings={'9797/udp': [9090]}, validate_ip_addrs=False, client_timeout=60) - dockerng_start.assert_called_with('cont') + docker_start.assert_called_with('cont') def test_running_compare_images_by_id(self): ''' @@ -272,33 +272,33 @@ class DockerngTestCase(TestCase): ''' new_fake_image_id = 'abcdefgh' old_fake_image_id = '123456789' - dockerng_inspect_image = Mock(return_value={'Id': new_fake_image_id}) - dockerng_inspect_container = Mock( + docker_inspect_image = Mock(return_value={'Id': new_fake_image_id}) + docker_inspect_container = Mock( return_value={'Image': old_fake_image_id, 'Config': {'Image': 'image:latest'}}) - dockerng_list_containers = Mock(return_value=['cont']) - dockerng__state = Mock(return_value='running') - dockerng_stop = Mock(return_value={'result': True}) - dockerng_rm = Mock(return_value=['container-id']) - __salt__ = {'dockerng.list_containers': dockerng_list_containers, - 'dockerng.inspect_container': dockerng_inspect_container, - 'dockerng.inspect_image': dockerng_inspect_image, - 'dockerng.list_tags': MagicMock(), - 'dockerng.state': dockerng__state, - 'dockerng.pull': MagicMock(return_value=new_fake_image_id), - 'dockerng.create': MagicMock(return_value='new_container'), - 'dockerng.start': MagicMock(), - 'dockerng.stop': dockerng_stop, - 'dockerng.rm': dockerng_rm, + docker_list_containers = Mock(return_value=['cont']) + docker__state = Mock(return_value='running') + docker_stop = Mock(return_value={'result': True}) + docker_rm = Mock(return_value=['container-id']) + __salt__ = {'docker.list_containers': docker_list_containers, + 'docker.inspect_container': docker_inspect_container, + 'docker.inspect_image': docker_inspect_image, + 'docker.list_tags': MagicMock(), + 'docker.state': docker__state, + 'docker.pull': MagicMock(return_value=new_fake_image_id), + 'docker.create': MagicMock(return_value='new_container'), + 'docker.start': MagicMock(), + 'docker.stop': docker_stop, + 'docker.rm': docker_rm, } - with patch.dict(dockerng_state.__dict__, + with patch.dict(docker_state.__dict__, {'__salt__': __salt__}): - ret = dockerng_state.running( + ret = docker_state.running( 'cont', image='image:latest', ) - dockerng_stop.assert_called_with('cont', timeout=10, unpause=True) - dockerng_rm.assert_called_with('cont') + docker_stop.assert_called_with('cont', timeout=10, unpause=True) + docker_rm.assert_called_with('cont') self.assertEqual(ret, {'name': 'cont', 'comment': "Container 'cont' was replaced", 'result': True, @@ -314,30 +314,30 @@ class DockerngTestCase(TestCase): .. code-block:: yaml image:latest: - dockerng.image_present: + docker.image_present: - force: true if ``image:latest`` is already downloaded locally the state should not report changes. ''' - dockerng_inspect_image = Mock( + docker_inspect_image = Mock( return_value={'Id': 'abcdefghijk'}) - dockerng_pull = Mock( + docker_pull = Mock( return_value={'Layers': {'Already_Pulled': ['abcdefghijk'], 'Pulled': []}, 'Status': 'Image is up to date for image:latest', 'Time_Elapsed': 1.1}) - dockerng_list_tags = Mock( + docker_list_tags = Mock( return_value=['image:latest'] ) - __salt__ = {'dockerng.list_tags': dockerng_list_tags, - 'dockerng.pull': dockerng_pull, - 'dockerng.inspect_image': dockerng_inspect_image, + __salt__ = {'docker.list_tags': docker_list_tags, + 'docker.pull': docker_pull, + 'docker.inspect_image': docker_inspect_image, } - with patch.dict(dockerng_state.__dict__, + with patch.dict(docker_state.__dict__, {'__salt__': __salt__}): - ret = dockerng_state.image_present('image:latest', force=True) + ret = docker_state.image_present('image:latest', force=True) self.assertEqual(ret, {'changes': {}, 'result': True, @@ -353,31 +353,31 @@ class DockerngTestCase(TestCase): .. code-block:: yaml image:latest: - dockerng.image_present: + docker.image_present: - force: true if ``image:latest`` is not downloaded and force is true should pull a new image successfuly. ''' - dockerng_inspect_image = Mock( + docker_inspect_image = Mock( side_effect=CommandExecutionError( 'Error 404: No such image/container: image:latest')) - dockerng_pull = Mock( + docker_pull = Mock( return_value={'Layers': {'Already_Pulled': ['abcdefghijk'], 'Pulled': ['abcdefghijk']}, 'Status': "Image 'image:latest' was pulled", 'Time_Elapsed': 1.1}) - dockerng_list_tags = Mock( + docker_list_tags = Mock( side_effect=[[], ['image:latest']] ) - __salt__ = {'dockerng.list_tags': dockerng_list_tags, - 'dockerng.pull': dockerng_pull, - 'dockerng.inspect_image': dockerng_inspect_image, + __salt__ = {'docker.list_tags': docker_list_tags, + 'docker.pull': docker_pull, + 'docker.inspect_image': docker_inspect_image, } - with patch.dict(dockerng_state.__dict__, + with patch.dict(docker_state.__dict__, {'__salt__': __salt__}): - ret = dockerng_state.image_present('image:latest', force=True) + ret = docker_state.image_present('image:latest', force=True) self.assertEqual(ret, {'changes': { 'Layers': {'Already_Pulled': ['abcdefghijk'], @@ -391,14 +391,14 @@ class DockerngTestCase(TestCase): def test_check_start_false(self): ''' - If start is False, then dockerng.running will not try + If start is False, then docker.running will not try to start a container that is stopped. ''' image_id = 'abcdefg' - dockerng_create = Mock() - dockerng_start = Mock() - dockerng_list_containers = Mock(return_value=['cont']) - dockerng_inspect_container = Mock( + docker_create = Mock() + docker_start = Mock() + docker_list_containers = Mock(return_value=['cont']) + docker_inspect_container = Mock( return_value={ 'Config': { 'Image': 'image:latest', @@ -442,7 +442,7 @@ class DockerngTestCase(TestCase): 'MacAddress': '00:00:00:00:00:01', }, 'Image': image_id}) - dockerng_inspect_image = MagicMock( + docker_inspect_image = MagicMock( return_value={ 'Id': image_id, 'Config': { @@ -454,20 +454,20 @@ class DockerngTestCase(TestCase): 'ExposedPorts': {}, }, }) - __salt__ = {'dockerng.list_containers': dockerng_list_containers, - 'dockerng.inspect_container': dockerng_inspect_container, - 'dockerng.inspect_image': dockerng_inspect_image, - 'dockerng.list_tags': MagicMock( + __salt__ = {'docker.list_containers': docker_list_containers, + 'docker.inspect_container': docker_inspect_container, + 'docker.inspect_image': docker_inspect_image, + 'docker.list_tags': MagicMock( return_value=['image:latest']), - 'dockerng.pull': MagicMock(), - 'dockerng.state': MagicMock(side_effect=['stopped', + 'docker.pull': MagicMock(), + 'docker.state': MagicMock(side_effect=['stopped', 'running']), - 'dockerng.create': dockerng_create, - 'dockerng.start': dockerng_start, + 'docker.create': docker_create, + 'docker.start': docker_start, } - with patch.dict(dockerng_state.__dict__, + with patch.dict(docker_state.__dict__, {'__salt__': __salt__}): - ret = dockerng_state.running( + ret = docker_state.running( 'cont', image='image:latest', start=False, @@ -481,14 +481,14 @@ class DockerngTestCase(TestCase): def test_check_start_true(self): ''' - If start is True, then dockerng.running will try + If start is True, then docker.running will try to start a container that is stopped. ''' image_id = 'abcdefg' - dockerng_create = Mock() - dockerng_start = Mock() - dockerng_list_containers = Mock(return_value=['cont']) - dockerng_inspect_container = Mock( + docker_create = Mock() + docker_start = Mock() + docker_list_containers = Mock(return_value=['cont']) + docker_inspect_container = Mock( return_value={ 'Config': { 'Image': 'image:latest', @@ -532,7 +532,7 @@ class DockerngTestCase(TestCase): 'MacAddress': '00:00:00:00:00:01', }, 'Image': image_id}) - dockerng_inspect_image = MagicMock( + docker_inspect_image = MagicMock( return_value={ 'Id': image_id, 'Config': { @@ -544,19 +544,19 @@ class DockerngTestCase(TestCase): 'ExposedPorts': {}, }, }) - __salt__ = {'dockerng.list_containers': dockerng_list_containers, - 'dockerng.inspect_container': dockerng_inspect_container, - 'dockerng.inspect_image': dockerng_inspect_image, - 'dockerng.list_tags': MagicMock(), - 'dockerng.pull': MagicMock(return_value=True), - 'dockerng.state': MagicMock(side_effect=['stopped', + __salt__ = {'docker.list_containers': docker_list_containers, + 'docker.inspect_container': docker_inspect_container, + 'docker.inspect_image': docker_inspect_image, + 'docker.list_tags': MagicMock(), + 'docker.pull': MagicMock(return_value=True), + 'docker.state': MagicMock(side_effect=['stopped', 'running']), - 'dockerng.create': dockerng_create, - 'dockerng.start': dockerng_start, + 'docker.create': docker_create, + 'docker.start': docker_start, } - with patch.dict(dockerng_state.__dict__, + with patch.dict(docker_state.__dict__, {'__salt__': __salt__}): - ret = dockerng_state.running( + ret = docker_state.running( 'cont', image='image:latest', start=True, @@ -577,7 +577,7 @@ class DockerngTestCase(TestCase): .. code-block:: yaml container: - dockerng.running: + docker.running: - environment: - KEY: 1 @@ -586,23 +586,23 @@ class DockerngTestCase(TestCase): .. code-block:: yaml container: - dockerng.running: + docker.running: - environment: - KEY: "1" ''' - __salt__ = {'dockerng.list_containers': MagicMock(), - 'dockerng.list_tags': MagicMock( + __salt__ = {'docker.list_containers': MagicMock(), + 'docker.list_tags': MagicMock( return_value=['image:latest']), - 'dockerng.inspect_image': MagicMock(), - 'dockerng.pull': MagicMock(), - 'dockerng.state': MagicMock(), - 'dockerng.create': MagicMock(), - 'dockerng.start': MagicMock(), + 'docker.inspect_image': MagicMock(), + 'docker.pull': MagicMock(), + 'docker.state': MagicMock(), + 'docker.create': MagicMock(), + 'docker.start': MagicMock(), } - with patch.dict(dockerng_state.__dict__, + with patch.dict(docker_state.__dict__, {'__salt__': __salt__}): for wrong_value in (1, .2, (), [], {}): - ret = dockerng_state.running( + ret = docker_state.running( 'cont', image='image:latest', environment=[{'KEY': wrong_value}]) @@ -615,24 +615,24 @@ class DockerngTestCase(TestCase): def test_running_with_labels(self): ''' - Test dockerng.running with labels parameter. + Test docker.running with labels parameter. ''' - dockerng_create = Mock() - __salt__ = {'dockerng.list_containers': MagicMock(), - 'dockerng.list_tags': MagicMock(), - 'dockerng.pull': MagicMock(), - 'dockerng.state': MagicMock(), - 'dockerng.inspect_image': MagicMock(), - 'dockerng.create': dockerng_create, + docker_create = Mock() + __salt__ = {'docker.list_containers': MagicMock(), + 'docker.list_tags': MagicMock(), + 'docker.pull': MagicMock(), + 'docker.state': MagicMock(), + 'docker.inspect_image': MagicMock(), + 'docker.create': docker_create, } - with patch.dict(dockerng_state.__dict__, + with patch.dict(docker_state.__dict__, {'__salt__': __salt__}): - dockerng_state.running( + docker_state.running( 'cont', image='image:latest', labels=['LABEL1', 'LABEL2'], ) - dockerng_create.assert_called_with( + docker_create.assert_called_with( 'image:latest', validate_input=False, validate_ip_addrs=False, @@ -642,13 +642,13 @@ class DockerngTestCase(TestCase): def test_running_with_labels_from_image(self): ''' - Test dockerng.running with labels parameter supports also + Test docker.running with labels parameter supports also labels carried by the image. ''' - dockerng_create = Mock() + docker_create = Mock() image_id = 'a' * 128 - dockerng_inspect_image = MagicMock( + docker_inspect_image = MagicMock( return_value={ 'Id': image_id, 'Config': { @@ -662,21 +662,21 @@ class DockerngTestCase(TestCase): 'LABEL1': 'label1'}, }, }) - __salt__ = {'dockerng.list_containers': MagicMock(), - 'dockerng.list_tags': MagicMock(), - 'dockerng.pull': MagicMock(), - 'dockerng.state': MagicMock(), - 'dockerng.inspect_image': dockerng_inspect_image, - 'dockerng.create': dockerng_create, + __salt__ = {'docker.list_containers': MagicMock(), + 'docker.list_tags': MagicMock(), + 'docker.pull': MagicMock(), + 'docker.state': MagicMock(), + 'docker.inspect_image': docker_inspect_image, + 'docker.create': docker_create, } - with patch.dict(dockerng_state.__dict__, + with patch.dict(docker_state.__dict__, {'__salt__': __salt__}): - dockerng_state.running( + docker_state.running( 'cont', image='image:latest', labels=[{'LABEL1': 'foo1'}, {'LABEL2': 'foo2'}], ) - dockerng_create.assert_called_with( + docker_create.assert_called_with( 'image:latest', validate_input=False, validate_ip_addrs=False, @@ -686,24 +686,24 @@ class DockerngTestCase(TestCase): def test_network_present(self): ''' - Test dockerng.network_present + Test docker.network_present ''' - dockerng_create_network = Mock(return_value='created') - dockerng_connect_container_to_network = Mock(return_value='connected') - dockerng_inspect_container = Mock(return_value={'Id': 'abcd'}) - __salt__ = {'dockerng.create_network': dockerng_create_network, - 'dockerng.inspect_container': dockerng_inspect_container, - 'dockerng.connect_container_to_network': dockerng_connect_container_to_network, - 'dockerng.networks': Mock(return_value=[]), + docker_create_network = Mock(return_value='created') + docker_connect_container_to_network = Mock(return_value='connected') + docker_inspect_container = Mock(return_value={'Id': 'abcd'}) + __salt__ = {'docker.create_network': docker_create_network, + 'docker.inspect_container': docker_inspect_container, + 'docker.connect_container_to_network': docker_connect_container_to_network, + 'docker.networks': Mock(return_value=[]), } - with patch.dict(dockerng_state.__dict__, + with patch.dict(docker_state.__dict__, {'__salt__': __salt__}): - ret = dockerng_state.network_present( + ret = docker_state.network_present( 'network_foo', containers=['container'], ) - dockerng_create_network.assert_called_with('network_foo', driver=None) - dockerng_connect_container_to_network.assert_called_with('abcd', + docker_create_network.assert_called_with('network_foo', driver=None) + docker_connect_container_to_network.assert_called_with('abcd', 'network_foo') self.assertEqual(ret, {'name': 'network_foo', 'comment': '', @@ -713,22 +713,22 @@ class DockerngTestCase(TestCase): def test_network_absent(self): ''' - Test dockerng.network_absent + Test docker.network_absent ''' - dockerng_remove_network = Mock(return_value='removed') - dockerng_disconnect_container_from_network = Mock(return_value='disconnected') - __salt__ = {'dockerng.remove_network': dockerng_remove_network, - 'dockerng.disconnect_container_from_network': dockerng_disconnect_container_from_network, - 'dockerng.networks': Mock(return_value=[{'Containers': {'container': {}}}]), + docker_remove_network = Mock(return_value='removed') + docker_disconnect_container_from_network = Mock(return_value='disconnected') + __salt__ = {'docker.remove_network': docker_remove_network, + 'docker.disconnect_container_from_network': docker_disconnect_container_from_network, + 'docker.networks': Mock(return_value=[{'Containers': {'container': {}}}]), } - with patch.dict(dockerng_state.__dict__, + with patch.dict(docker_state.__dict__, {'__salt__': __salt__}): - ret = dockerng_state.network_absent( + ret = docker_state.network_absent( 'network_foo', ) - dockerng_disconnect_container_from_network.assert_called_with('container', + docker_disconnect_container_from_network.assert_called_with('container', 'network_foo') - dockerng_remove_network.assert_called_with('network_foo') + docker_remove_network.assert_called_with('network_foo') self.assertEqual(ret, {'name': 'network_foo', 'comment': '', 'changes': {'disconnected': 'disconnected', @@ -737,7 +737,7 @@ class DockerngTestCase(TestCase): def test_volume_present(self): ''' - Test dockerng.volume_present + Test docker.volume_present ''' volumes = [] default_driver = 'dummy_default' @@ -762,17 +762,17 @@ class DockerngTestCase(TestCase): volumes.remove(removed[0]) return removed[0] - dockerng_create_volume = Mock(side_effect=create_volume) - __salt__ = {'dockerng.create_volume': dockerng_create_volume, - 'dockerng.volumes': Mock(return_value={'Volumes': volumes}), - 'dockerng.remove_volume': Mock(side_effect=remove_volume), + docker_create_volume = Mock(side_effect=create_volume) + __salt__ = {'docker.create_volume': docker_create_volume, + 'docker.volumes': Mock(return_value={'Volumes': volumes}), + 'docker.remove_volume': Mock(side_effect=remove_volume), } - with patch.dict(dockerng_state.__dict__, + with patch.dict(docker_state.__dict__, {'__salt__': __salt__}): - ret = dockerng_state.volume_present( + ret = docker_state.volume_present( 'volume_foo', ) - dockerng_create_volume.assert_called_with('volume_foo', + docker_create_volume.assert_called_with('volume_foo', driver=None, driver_opts=None) self.assertEqual( @@ -794,7 +794,7 @@ class DockerngTestCase(TestCase): # run it again with the same arguments orig_volumes = [volumes[0].copy()] - ret = dockerng_state.volume_present('volume_foo') + ret = docker_state.volume_present('volume_foo') self.assertEqual( { 'name': 'volume_foo', @@ -806,7 +806,7 @@ class DockerngTestCase(TestCase): self.assertEqual(orig_volumes, volumes) # run it again with a different driver but don't force - ret = dockerng_state.volume_present('volume_foo', driver='local') + ret = docker_state.volume_present('volume_foo', driver='local') self.assertEqual( { 'name': 'volume_foo', @@ -820,7 +820,7 @@ class DockerngTestCase(TestCase): self.assertEqual(orig_volumes, volumes) # run it again with a different driver and force - ret = dockerng_state.volume_present( + ret = docker_state.volume_present( 'volume_foo', driver='local', force=True) self.assertEqual( { @@ -845,25 +845,25 @@ class DockerngTestCase(TestCase): def test_volume_present_with_another_driver(self): ''' - Test dockerng.volume_present + Test docker.volume_present ''' - dockerng_create_volume = Mock(return_value='created') - dockerng_remove_volume = Mock(return_value='removed') - __salt__ = {'dockerng.create_volume': dockerng_create_volume, - 'dockerng.remove_volume': dockerng_remove_volume, - 'dockerng.volumes': Mock(return_value={ + docker_create_volume = Mock(return_value='created') + docker_remove_volume = Mock(return_value='removed') + __salt__ = {'docker.create_volume': docker_create_volume, + 'docker.remove_volume': docker_remove_volume, + 'docker.volumes': Mock(return_value={ 'Volumes': [{'Name': 'volume_foo', 'Driver': 'foo'}]}), } - with patch.dict(dockerng_state.__dict__, + with patch.dict(docker_state.__dict__, {'__salt__': __salt__}): - ret = dockerng_state.volume_present( + ret = docker_state.volume_present( 'volume_foo', driver='bar', force=True, ) - dockerng_remove_volume.assert_called_with('volume_foo') - dockerng_create_volume.assert_called_with('volume_foo', + docker_remove_volume.assert_called_with('volume_foo') + docker_create_volume.assert_called_with('volume_foo', driver='bar', driver_opts=None) self.assertEqual(ret, {'name': 'volume_foo', @@ -874,22 +874,22 @@ class DockerngTestCase(TestCase): def test_volume_present_wo_existing_volumes(self): ''' - Test dockerng.volume_present without existing volumes. + Test docker.volume_present without existing volumes. ''' - dockerng_create_volume = Mock(return_value='created') - dockerng_remove_volume = Mock(return_value='removed') - __salt__ = {'dockerng.create_volume': dockerng_create_volume, - 'dockerng.remove_volume': dockerng_remove_volume, - 'dockerng.volumes': Mock(return_value={'Volumes': None}), + docker_create_volume = Mock(return_value='created') + docker_remove_volume = Mock(return_value='removed') + __salt__ = {'docker.create_volume': docker_create_volume, + 'docker.remove_volume': docker_remove_volume, + 'docker.volumes': Mock(return_value={'Volumes': None}), } - with patch.dict(dockerng_state.__dict__, + with patch.dict(docker_state.__dict__, {'__salt__': __salt__}): - ret = dockerng_state.volume_present( + ret = docker_state.volume_present( 'volume_foo', driver='bar', force=True, ) - dockerng_create_volume.assert_called_with('volume_foo', + docker_create_volume.assert_called_with('volume_foo', driver='bar', driver_opts=None) self.assertEqual(ret, {'name': 'volume_foo', @@ -899,19 +899,19 @@ class DockerngTestCase(TestCase): def test_volume_absent(self): ''' - Test dockerng.volume_absent + Test docker.volume_absent ''' - dockerng_remove_volume = Mock(return_value='removed') - __salt__ = {'dockerng.remove_volume': dockerng_remove_volume, - 'dockerng.volumes': Mock(return_value={ + docker_remove_volume = Mock(return_value='removed') + __salt__ = {'docker.remove_volume': docker_remove_volume, + 'docker.volumes': Mock(return_value={ 'Volumes': [{'Name': 'volume_foo'}]}), } - with patch.dict(dockerng_state.__dict__, + with patch.dict(docker_state.__dict__, {'__salt__': __salt__}): - ret = dockerng_state.volume_absent( + ret = docker_state.volume_absent( 'volume_foo', ) - dockerng_remove_volume.assert_called_with('volume_foo') + docker_remove_volume.assert_called_with('volume_foo') self.assertEqual(ret, {'name': 'volume_foo', 'comment': '', 'changes': {'removed': 'removed'}, @@ -919,14 +919,14 @@ class DockerngTestCase(TestCase): def test_removal_of_parameter_is_detected(self): ''' - Test dockerng.running with deleted parameter. + Test docker.running with deleted parameter. 1. define your sls .. code-block:: yaml container: - dockerng.running: + docker.running: - name: super-container - binds: - /path:/path:ro @@ -938,16 +938,16 @@ class DockerngTestCase(TestCase): .. code-block:: yaml container: - dockerng.running: + docker.running: - name: super-container 4. enjoy your new created container without mounted volumes. ''' image_id = 'abcdefg' - dockerng_create = Mock(return_value=True) - dockerng_start = Mock() - dockerng_list_containers = Mock(return_value=['cont']) - dockerng_inspect_container = Mock( + docker_create = Mock(return_value=True) + docker_start = Mock() + docker_list_containers = Mock(return_value=['cont']) + docker_inspect_container = Mock( side_effect=[{ 'Config': { 'Image': 'image:latest', @@ -1034,7 +1034,7 @@ class DockerngTestCase(TestCase): }, 'Image': image_id}] ) - dockerng_inspect_image = MagicMock( + docker_inspect_image = MagicMock( return_value={ 'Id': image_id, 'Config': { @@ -1046,20 +1046,20 @@ class DockerngTestCase(TestCase): 'ExposedPorts': {}, }, }) - __salt__ = {'dockerng.list_containers': dockerng_list_containers, - 'dockerng.inspect_container': dockerng_inspect_container, - 'dockerng.inspect_image': dockerng_inspect_image, - 'dockerng.list_tags': MagicMock(), - 'dockerng.pull': MagicMock(return_value=True), - 'dockerng.state': MagicMock(side_effect=['stopped', + __salt__ = {'docker.list_containers': docker_list_containers, + 'docker.inspect_container': docker_inspect_container, + 'docker.inspect_image': docker_inspect_image, + 'docker.list_tags': MagicMock(), + 'docker.pull': MagicMock(return_value=True), + 'docker.state': MagicMock(side_effect=['stopped', 'running']), - 'dockerng.rm': MagicMock(return_value='cont'), - 'dockerng.create': dockerng_create, - 'dockerng.start': dockerng_start, + 'docker.rm': MagicMock(return_value='cont'), + 'docker.create': docker_create, + 'docker.start': docker_start, } - with patch.dict(dockerng_state.__dict__, + with patch.dict(docker_state.__dict__, {'__salt__': __salt__}): - ret = dockerng_state.running( + ret = docker_state.running( 'cont', image='image:latest', ) @@ -1078,7 +1078,7 @@ class DockerngTestCase(TestCase): }, 'result': True, }) - dockerng_create.assert_called_with('image:latest', + docker_create.assert_called_with('image:latest', validate_ip_addrs=False, validate_input=False, name='cont', @@ -1087,16 +1087,16 @@ class DockerngTestCase(TestCase): def test_validate_input_min_docker_py(self): docker_mock = Mock() docker_mock.version_info = (1, 0, 0) - dockerng_mod.docker = None - with patch.dict(dockerng_mod.VALID_CREATE_OPTS['command'], + docker_mod.docker = None + with patch.dict(docker_mod.VALID_CREATE_OPTS['command'], {'path': 'Config:Cmd', 'image_path': 'Config:Cmd', 'min_docker_py': (999, 0, 0)}): - with patch.object(dockerng_mod, 'docker', docker_mock): + with patch.object(docker_mod, 'docker', docker_mock): self.assertRaisesRegexp(SaltInvocationError, "The 'command' parameter requires at" " least docker-py 999.0.0.*$", - dockerng_state._validate_input, + docker_state._validate_input, {'command': 'echo boom'}) def test_command_defined_on_image_layer_dont_diff_if_attempted_to_blank(self): @@ -1111,7 +1111,7 @@ class DockerngTestCase(TestCase): .. code-block:: yaml cont: - dockerng.running: + docker.running: - image: image:latest @@ -1120,10 +1120,10 @@ class DockerngTestCase(TestCase): No diff should be reported ''' image_id = 'abcdefg' - dockerng_create = Mock(return_value=True) - dockerng_start = Mock() - dockerng_list_containers = Mock(return_value=['cont']) - dockerng_inspect_container = Mock( + docker_create = Mock(return_value=True) + docker_start = Mock() + docker_list_containers = Mock(return_value=['cont']) + docker_inspect_container = Mock( side_effect=[{ 'Config': { 'Image': 'image:latest', @@ -1168,7 +1168,7 @@ class DockerngTestCase(TestCase): }, 'Image': image_id}] ) - dockerng_inspect_image = MagicMock( + docker_inspect_image = MagicMock( return_value={ 'Id': image_id, 'Config': { @@ -1180,15 +1180,15 @@ class DockerngTestCase(TestCase): 'ExposedPorts': {}, }, }) - __salt__ = {'dockerng.list_containers': dockerng_list_containers, - 'dockerng.inspect_container': dockerng_inspect_container, - 'dockerng.inspect_image': dockerng_inspect_image, - 'dockerng.list_tags': MagicMock(side_effect=[['image:latest']]), - 'dockerng.state': MagicMock(side_effect=['running']), + __salt__ = {'docker.list_containers': docker_list_containers, + 'docker.inspect_container': docker_inspect_container, + 'docker.inspect_image': docker_inspect_image, + 'docker.list_tags': MagicMock(side_effect=[['image:latest']]), + 'docker.state': MagicMock(side_effect=['running']), } - with patch.dict(dockerng_state.__dict__, + with patch.dict(docker_state.__dict__, {'__salt__': __salt__}): - ret = dockerng_state.running( + ret = docker_state.running( 'cont', image='image:latest', ) @@ -1202,4 +1202,4 @@ class DockerngTestCase(TestCase): if __name__ == '__main__': from integration import run_tests - run_tests(DockerngTestCase, needs_daemon=False) + run_tests(DockerTestCase, needs_daemon=False) diff --git a/tests/unit/states/dockerio_test.py b/tests/unit/states/dockerio_test.py deleted file mode 100644 index 54f51bea97..0000000000 --- a/tests/unit/states/dockerio_test.py +++ /dev/null @@ -1,113 +0,0 @@ -# -*- coding: utf-8 -*- - -# Import Python libs -from __future__ import absolute_import -from contextlib import contextmanager - -# Import Salt Testing libs -from salttesting import skipIf, TestCase -from salttesting.mock import NO_MOCK, NO_MOCK_REASON, MagicMock - - -@contextmanager -def provision_state(module, fixture): - previous_dict = getattr(module, '__salt__', {}).copy() - try: - module.__dict__.setdefault('__salt__', {}).update(fixture) - yield - finally: - setattr(module, '__salt__', previous_dict) - - -@skipIf(NO_MOCK, NO_MOCK_REASON) -@skipIf(True, 'Skipped: This module has been deprecated.') -class DockerStateTestCase(TestCase): - def test_docker_run_success(self): - from salt.states import dockerio - salt_fixture = {'docker.retcode': MagicMock(return_value=0), - 'docker.run_all': MagicMock( - return_value={'stdout': '.\n..\n', - 'stderr': '', - 'status': True, - 'comment': 'Success', - 'retcode': 0})} - - with provision_state(dockerio, salt_fixture): - result = dockerio.run('ls /', 'ubuntu') - - self.assertEqual(result, {'name': 'ls /', - 'result': True, - 'comment': 'Success', - 'changes': {}}) - - def test_docker_run_failure(self): - from salt.states import dockerio - salt_fixture = {'docker.retcode': MagicMock(return_value=0), - 'docker.run_all': MagicMock( - return_value={'stdout': '', - 'stderr': 'Error', - 'status': False, - 'comment': 'Failure', - 'retcode': 1})} - - with provision_state(dockerio, salt_fixture): - result = dockerio.run('ls /', 'ubuntu') - - self.assertEqual(result, {'name': 'ls /', - 'result': False, - 'comment': 'Failure', - 'changes': {}}) - - def test_docker_run_onlyif(self): - from salt.states import dockerio - salt_fixture = {'docker.retcode': MagicMock(return_value=1), - 'docker.run_all': None} - with provision_state(dockerio, salt_fixture): - result = dockerio.run('ls /', 'ubuntu', - onlyif='ls -l') - self.assertEqual(result, {'name': 'ls /', - 'result': True, - 'comment': 'onlyif execution failed', - 'changes': {}}) - - def test_docker_run_unless(self): - from salt.states import dockerio - salt_fixture = {'docker.retcode': MagicMock(return_value=0), - 'docker.run_all': None} - with provision_state(dockerio, salt_fixture): - result = dockerio.run('ls /', 'ubuntu', - unless='ls -l') - self.assertEqual(result, {'name': 'ls /', - 'result': True, - 'comment': 'unless execution succeeded', - 'changes': {}}) - - def test_docker_run_docked_onlyif(self): - from salt.states import dockerio - salt_fixture = {'docker.retcode': MagicMock(return_value=1), - 'docker.run_all': None} - with provision_state(dockerio, salt_fixture): - result = dockerio.run('ls /', 'ubuntu', - docked_onlyif='ls -l') - self.assertEqual(result, {'name': 'ls /', - 'result': True, - 'comment': 'docked_onlyif execution failed', - 'changes': {}}) - - def test_docker_run_docked_unless(self): - from salt.states import dockerio - salt_fixture = {'docker.retcode': MagicMock(return_value=0), - 'docker.run_all': None} - with provision_state(dockerio, salt_fixture): - result = dockerio.run('ls /', 'ubuntu', - docked_unless='ls -l') - self.assertEqual(result, {'name': 'ls /', - 'result': True, - 'comment': ('docked_unless execution' - ' succeeded'), - 'changes': {}}) - - -if __name__ == '__main__': - from integration import run_tests - run_tests(DockerStateTestCase, needs_daemon=False)