Merge remote-tracking branch 'upstream/2016.3' into merge-2016.3-2016.11

This commit is contained in:
Erik Johnson 2017-04-06 10:15:47 -05:00
commit a6d68f50fe
11 changed files with 425 additions and 558 deletions

View File

@ -9,5 +9,7 @@ External Logging Handlers
:toctree:
:template: autosummary.rst.tmpl
fluent_mod
log4mongo_mod
logstash_mod
sentry_mod
sentry_mod

View File

@ -0,0 +1,5 @@
============================
salt.log.handlers.fluent_mod
============================
.. automodule:: salt.log.handlers.fluent_mod

View File

@ -0,0 +1,5 @@
===============================
salt.log.handlers.log4mongo_mod
===============================
.. automodule:: salt.log.handlers.log4mongo_mod

View File

@ -1 +1,5 @@
.. automodule:: salt.log.handlers.logstash_mod
==============================
salt.log.handlers.logstash_mod
==============================
.. automodule:: salt.log.handlers.logstash_mod

View File

@ -1 +1,5 @@
.. automodule:: salt.log.handlers.sentry_mod
============================
salt.log.handlers.sentry_mod
============================
.. automodule:: salt.log.handlers.sentry_mod

View File

@ -1680,6 +1680,29 @@ def _validate_opts(opts):
return True
def _validate_ssh_minion_opts(opts):
'''
Ensure we're not using any invalid ssh_minion_opts. We want to make sure
that the ssh_minion_opts does not override any pillar or fileserver options
inherited from the master config. To add other items, modify the if
statement in the for loop below.
'''
ssh_minion_opts = opts.get('ssh_minion_opts', {})
if not isinstance(ssh_minion_opts, dict):
log.error('Invalidly-formatted ssh_minion_opts')
opts.pop('ssh_minion_opts')
for opt_name in list(ssh_minion_opts):
if re.match('^[a-z0-9]+fs_', opt_name, flags=re.IGNORECASE) \
or 'pillar' in opt_name \
or opt_name in ('fileserver_backend',):
log.warning(
'\'%s\' is not a valid ssh_minion_opts parameter, ignoring',
opt_name
)
ssh_minion_opts.pop(opt_name)
def _append_domain(opts):
'''
Append a domain to the existing id if it doesn't already exist
@ -3269,6 +3292,7 @@ def master_config(path, env_var='SALT_MASTER_CONFIG', defaults=None, exit_on_con
overrides.update(include_config(include, path, verbose=True),
exit_on_config_errors=exit_on_config_errors)
opts = apply_master_config(overrides, defaults)
_validate_ssh_minion_opts(opts)
_validate_opts(opts)
# If 'nodegroups:' is uncommented in the master config file, and there are
# no nodegroups defined, opts['nodegroups'] will be None. Fix this by

View File

@ -44,85 +44,66 @@ Docker_. docker-py can easily be installed using :py:func:`pip.install
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 <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:
If you have previously performed a ``docker login`` from the minion, then the
credentials saved in ``~/.docker/config.json`` will be used for any actions
which require authentication. If not, then credentials can be configured in
Pillar data. The configuration schema is as follows:
.. code-block:: yaml
docker-registries:
<registry_url>:
email: <email_address>
password: <password>
username: <username>
reauth: <boolean>
password: <password>
For example:
.. code-block:: yaml
docker-registries:
https://index.docker.io/v1/:
email: foo@foo.com
password: s3cr3t
hub:
username: foo
password: s3cr3t
Reauth is an optional parameter that forces the docker login to reauthorize using
the credentials passed in the pillar data. Defaults to false.
.. note::
As of the 2016.3.7, 2016.11.4, and Nitrogen releases of Salt, credentials
for the Docker Hub can be configured simply by specifying ``hub`` in place
of the registry URL. In earlier releases, it is necessary to specify the
actual registry URL for the Docker Hub (i.e.
``https://index.docker.io/v1/``).
.. versionadded:: 2016.3.5,2016.11.1
For example:
More than one registry can be configured. Salt will look for Docker credentials
in the ``docker-registries`` Pillar key, as well as any key ending in
``-docker-registries``. For example:
.. code-block:: yaml
docker-registries:
https://index.docker.io/v1/:
email: foo@foo.com
password: s3cr3t
'https://mydomain.tld/registry:5000':
username: foo
reauth: True
Mulitiple registries can be configured. This can be done in one of two ways.
The first way is to configure each registry under the ``docker-registries``
pillar key.
.. code-block:: yaml
docker-registries:
https://index.foo.io/v1/:
email: foo@foo.com
password: s3cr3t
username: foo
https://index.bar.io/v1/:
email: foo@foo.com
password: s3cr3t
username: foo
The second way is to use separate pillar variables ending in
``-docker-registries``:
.. code-block:: yaml
foo-docker-registries:
https://index.foo.io/v1/:
email: foo@foo.com
password: s3cr3t
username: foo
password: s3cr3t
bar-docker-registries:
https://index.bar.io/v1/:
email: foo@foo.com
password: s3cr3t
username: foo
password: s3cr3t
To login to the configured registries, use the :py:func:`docker.login
<salt.modules.dockermod.login>` function. This only needs to be done once for a
given registry, and it will store/update the credentials in
``~/.docker/config.json``.
.. note::
For Salt releases before 2016.3.7 and 2016.11.4, :py:func:`docker.login
<salt.modules.dockermod.login>` is not available. Instead, Salt will try to
authenticate using each of your configured registries for each push/pull,
behavior which is not correct and has been resolved in newer releases.
Both methods can be combined; any registry configured under
``docker-registries`` or ``*-docker-registries`` will be detected.
Configuration Options
---------------------
@ -131,7 +112,8 @@ The following configuration options can be set to fine-tune how Salt uses
Docker:
- ``docker.url``: URL to the docker service (default: local socket).
- ``docker.version``: API version to use
- ``docker.version``: API version to use (should not need to be set manually in
the vast majority of cases)
- ``docker.exec_driver``: Execution driver to use, one of ``nsenter``,
``lxc-attach``, or ``docker-exec``. See the :ref:`Executing Commands Within a
Running Container <docker-execution-driver>` section for more details on how
@ -140,72 +122,6 @@ Docker:
These configuration options are retrieved using :py:mod:`config.get
<salt.modules.config.get>` (click the link for further information).
Functions
---------
- Information Gathering
- :py:func:`dockerng.depends <salt.modules.dockerng.depends>`
- :py:func:`dockerng.diff <salt.modules.dockerng.diff>`
- :py:func:`dockerng.exists <salt.modules.dockerng.exists>`
- :py:func:`dockerng.history <salt.modules.dockerng.history>`
- :py:func:`dockerng.images <salt.modules.dockerng.images>`
- :py:func:`dockerng.info <salt.modules.dockerng.info>`
- :py:func:`dockerng.inspect <salt.modules.dockerng.inspect>`
- :py:func:`dockerng.inspect_container
<salt.modules.dockerng.inspect_container>`
- :py:func:`dockerng.inspect_image <salt.modules.dockerng.inspect_image>`
- :py:func:`dockerng.list_containers
<salt.modules.dockerng.list_containers>`
- :py:func:`dockerng.list_tags <salt.modules.dockerng.list_tags>`
- :py:func:`dockerng.logs <salt.modules.dockerng.logs>`
- :py:func:`dockerng.pid <salt.modules.dockerng.pid>`
- :py:func:`dockerng.port <salt.modules.dockerng.port>`
- :py:func:`dockerng.ps <salt.modules.dockerng.ps>`
- :py:func:`dockerng.state <salt.modules.dockerng.state>`
- :py:func:`dockerng.search <salt.modules.dockerng.search>`
- :py:func:`dockerng.top <salt.modules.dockerng.top>`
- :py:func:`dockerng.version <salt.modules.dockerng.version>`
- Container Management
- :py:func:`dockerng.create <salt.modules.dockerng.create>`
- :py:func:`dockerng.copy_from <salt.modules.dockerng.copy_from>`
- :py:func:`dockerng.copy_to <salt.modules.dockerng.copy_to>`
- :py:func:`dockerng.export <salt.modules.dockerng.export>`
- :py:func:`dockerng.rm <salt.modules.dockerng.rm>`
- Management of Container State
- :py:func:`dockerng.kill <salt.modules.dockerng.kill>`
- :py:func:`dockerng.pause <salt.modules.dockerng.pause>`
- :py:func:`dockerng.restart <salt.modules.dockerng.restart>`
- :py:func:`dockerng.start <salt.modules.dockerng.start>`
- :py:func:`dockerng.stop <salt.modules.dockerng.stop>`
- :py:func:`dockerng.unpause <salt.modules.dockerng.unpause>`
- :py:func:`dockerng.wait <salt.modules.dockerng.wait>`
- Image Management
- :py:func:`dockerng.build <salt.modules.dockerng.build>`
- :py:func:`dockerng.commit <salt.modules.dockerng.commit>`
- :py:func:`dockerng.dangling <salt.modules.dockerng.dangling>`
- :py:func:`dockerng.import <salt.modules.dockerng.import>`
- :py:func:`dockerng.load <salt.modules.dockerng.load>`
- :py:func:`dockerng.pull <salt.modules.dockerng.pull>`
- :py:func:`dockerng.push <salt.modules.dockerng.push>`
- :py:func:`dockerng.rmi <salt.modules.dockerng.rmi>`
- :py:func:`dockerng.save <salt.modules.dockerng.save>`
- :py:func:`dockerng.tag <salt.modules.dockerng.tag>`
- Network Management
- :py:func:`dockerng.networks <salt.modules.dockerng.networks>`
- :py:func:`dockerng.create_network <salt.modules.dockerng.create_network>`
- :py:func:`dockerng.remove_network <salt.modules.dockerng.remove_network>`
- :py:func:`dockerng.inspect_network
<salt.modules.dockerng.inspect_network>`
- :py:func:`dockerng.connect_container_to_network
<salt.modules.dockerng.connect_container_to_network>`
- :py:func:`dockerng.disconnect_container_from_network
<salt.modules.dockerng.disconnect_container_from_network>`
- Salt Functions and States Execution
- :py:func:`dockerng.call <salt.modules.dockerng.call>`
- :py:func:`dockerng.sls <salt.modules.dockerng.sls>`
- :py:func:`dockerng.sls_build <salt.modules.dockerng.sls_build>`
.. _docker-execution-driver:
Executing Commands Within a Running Container
@ -655,60 +571,55 @@ def _get_docker_py_versioninfo():
pass
def _get_client(**kwargs):
client_kwargs = {}
if 'client_timeout' in kwargs:
client_kwargs['timeout'] = kwargs.pop('client_timeout')
for key, val in (('base_url', 'docker.url'),
('version', 'docker.version')):
param = __salt__['config.get'](val, NOTSET)
if param is not NOTSET:
client_kwargs[key] = param
if 'base_url' not in client_kwargs and 'DOCKER_HOST' in os.environ:
# Check if the DOCKER_HOST environment variable has been set
client_kwargs['base_url'] = os.environ.get('DOCKER_HOST')
if 'version' not in client_kwargs:
# Let docker-py auto detect docker version incase
# it's not defined by user.
client_kwargs['version'] = 'auto'
docker_machine = __salt__['config.get']('docker.machine', NOTSET)
if docker_machine is not NOTSET:
docker_machine_json = __salt__['cmd.run'](
['docker-machine', 'inspect', docker_machine],
python_shell=False)
try:
docker_machine_json = json.loads(docker_machine_json)
docker_machine_tls = \
docker_machine_json['HostOptions']['AuthOptions']
docker_machine_ip = docker_machine_json['Driver']['IPAddress']
client_kwargs['base_url'] = \
'https://' + docker_machine_ip + ':2376'
client_kwargs['tls'] = docker.tls.TLSConfig(
client_cert=(docker_machine_tls['ClientCertPath'],
docker_machine_tls['ClientKeyPath']),
ca_cert=docker_machine_tls['CaCertPath'],
assert_hostname=False,
verify=True)
except Exception as exc:
raise CommandExecutionError(
'Docker machine {0} failed: {1}'.format(docker_machine, exc))
try:
# docker-py 2.0 renamed this client attribute
return docker.APIClient(**client_kwargs)
except AttributeError:
return docker.Client(**client_kwargs)
# Decorators
class _api_version(object):
'''
Enforce a specific Docker Remote API version
'''
def __init__(self, api_version):
self.api_version = api_version
def __call__(self, func):
def wrapper(*args, **kwargs):
'''
Get the current client version and check it against the one passed
'''
_get_client()
current_api_version = __context__['docker.client'].api_version
if float(current_api_version) < self.api_version:
raise CommandExecutionError(
'This function requires a Docker API version of at least '
'{0}. API version in use is {1}.'
.format(self.api_version, current_api_version)
)
return func(*args, **salt.utils.clean_kwargs(**kwargs))
return salt.utils.decorators.identical_signature_wrapper(func, wrapper)
class _client_version(object):
'''
Enforce a specific Docker client version
'''
def __init__(self, version):
self.version = distutils.version.StrictVersion(version)
def __call__(self, func):
def wrapper(*args, **kwargs):
'''
Get the current client version and check it against the one passed
'''
_get_client()
current_version = '.'.join(map(str, _get_docker_py_versioninfo()))
if distutils.version.StrictVersion(current_version) < self.version:
error_message = (
'This function requires a Docker Client version of at least '
'{0}. Version in use is {1}.'
.format(self.version, current_version))
minion_conf = __salt__['config.get']('docker.version', NOTSET)
if minion_conf is not NOTSET:
error_message += (
' Hint: Your minion configuration specified'
' `docker.version` = "{0}"'.format(minion_conf))
raise CommandExecutionError(error_message)
return func(*args, **salt.utils.clean_kwargs(**kwargs))
return salt.utils.decorators.identical_signature_wrapper(func, wrapper)
def _docker_client(wrapped):
'''
Decorator to run a function that requires the use of a docker.Client()
@ -719,8 +630,8 @@ def _docker_client(wrapped):
'''
Ensure that the client is present
'''
client_timeout = __context__.get('docker.timeout', CLIENT_TIMEOUT)
_get_client(timeout=client_timeout)
if 'docker.client' not in __context__:
__context__['docker.client'] = _get_client(**kwargs)
return wrapped(*args, **salt.utils.clean_kwargs(**kwargs))
return wrapper
@ -799,70 +710,6 @@ def _clear_context():
pass
def _get_client(timeout=None):
'''
Obtains 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 (default: "auto")
'''
# In some edge cases, the client instance is missing attributes. Don't use
# the cached client in those cases.
if 'docker.client' not in __context__ \
or not hasattr(__context__['docker.client'], 'timeout'):
client_kwargs = {}
for key, val in (('base_url', 'docker.url'),
('version', 'docker.version')):
param = __salt__['config.get'](val, NOTSET)
if param is not NOTSET:
client_kwargs[key] = param
if 'base_url' not in client_kwargs and 'DOCKER_HOST' in os.environ:
# Check if the DOCKER_HOST environment variable has been set
client_kwargs['base_url'] = os.environ.get('DOCKER_HOST')
if 'version' not in client_kwargs:
# Let docker-py auto detect docker version incase
# it's not defined by user.
client_kwargs['version'] = 'auto'
docker_machine = __salt__['config.get']('docker.machine', NOTSET)
if docker_machine is not NOTSET:
docker_machine_json = __salt__['cmd.run']('docker-machine inspect ' + docker_machine)
try:
docker_machine_json = json.loads(docker_machine_json)
docker_machine_tls = docker_machine_json['HostOptions']['AuthOptions']
docker_machine_ip = docker_machine_json['Driver']['IPAddress']
client_kwargs['base_url'] = 'https://' + docker_machine_ip + ':2376'
client_kwargs['tls'] = docker.tls.TLSConfig(
client_cert=(docker_machine_tls['ClientCertPath'],
docker_machine_tls['ClientKeyPath']),
ca_cert=docker_machine_tls['CaCertPath'],
assert_hostname=False,
verify=True)
except Exception as exc:
raise CommandExecutionError(
'Docker machine {0} failed: {1}'.format(docker_machine, exc))
try:
# docker-py 2.0 renamed this client attribute
__context__['docker.client'] = docker.APIClient(**client_kwargs)
except AttributeError:
__context__['docker.client'] = docker.Client(**client_kwargs)
# Set a new timeout if one was passed
if timeout is not None and __context__['docker.client'].timeout != timeout:
__context__['docker.client'].timeout = timeout
def _get_md5(name, path):
'''
Get the MD5 checksum of a file from a container
@ -882,33 +729,6 @@ def _get_exec_driver():
Get the method to be used in shell commands
'''
contextkey = 'docker.exec_driver'
'''
docker-exec won't be used by default until we reach a version where it
supports running commands as a user other than the effective user of the
container.
See: https://groups.google.com/forum/#!topic/salt-users/i6Eq4rf5ml0
if contextkey in __context__:
return __context__[contextkey]
from_config = __salt__['config.get'](contextkey, None)
if from_config is not None:
__context__[contextkey] = from_config
else:
_version = version()
if 'VersionInfo' in _version:
if _version['VersionInfo'] >= (1, 3, 0):
__context__[contextkey] = 'docker-exec'
elif distutils.version.LooseVersion(version()['Version']) \
>= distutils.version.LooseVersion('1.3.0'):
# LooseVersion is less preferable, but OK as a fallback.
__context__[contextkey] = 'docker-exec'
# If the version_info tuple revealed a version < 1.3.0, the key will yet to
# have been set in __context__, so we'll check if it's there yet and if
# not, proceed with detecting execution driver from the output of info().
''' # pylint: disable=pointless-string-statement
if contextkey not in __context__:
from_config = __salt__['config.get'](contextkey, None)
# This if block can be removed once we make docker-exec a default
@ -946,19 +766,24 @@ def _get_repo_tag(image, default_tag='latest'):
'''
Resolves the docker repo:tag notation and returns repo name and tag
'''
if ':' in image:
if not isinstance(image, six.string_types):
image = str(image)
try:
r_name, r_tag = image.rsplit(':', 1)
if not r_tag:
# 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)
)
r_tag = default_tag
else:
except ValueError:
r_name = image
r_tag = default_tag
if not r_tag:
# Would happen if some wiseguy requests a tag ending in a colon
# (e.g. 'somerepo:')
log.warning(
'Assuming tag \'%s\' for repo \'%s\'', default_tag, image
)
r_tag = default_tag
elif '/' in r_tag:
# Public registry notation with no tag specified
# (e.g. foo.bar.com:5000/imagename)
return image, default_tag
return r_name, r_tag
@ -1009,15 +834,19 @@ def _size_fmt(num):
@_docker_client
def _client_wrapper(attr, *args, **kwargs):
'''
Common functionality for getting information from a container
Common functionality for running low-level API calls
'''
catch_api_errors = kwargs.pop('catch_api_errors', True)
func = getattr(__context__['docker.client'], attr)
func = getattr(__context__['docker.client'], attr, None)
if func is None:
raise SaltInvocationError('Invalid client action \'{0}\''.format(attr))
err = ''
try:
return func(*args, **kwargs)
log.debug(
'Attempting to run docker-py\'s "%s" function '
'with args=%s and kwargs=%s', attr, args, kwargs
)
ret = func(*args, **kwargs)
except docker.errors.APIError as exc:
if catch_api_errors:
# Generic handling of Docker API errors
@ -1028,107 +857,34 @@ def _client_wrapper(attr, *args, **kwargs):
else:
# Allow API errors to be caught further up the stack
raise
except docker.errors.DockerException as exc:
# More general docker exception (catches InvalidVersion, etc.)
raise CommandExecutionError(exc.__str__())
except Exception as exc:
err = '{0}'.format(exc)
err = exc.__str__()
else:
if kwargs.get('stream', False):
api_events = []
try:
for event in ret:
api_events.append(json.loads(event))
except Exception as exc:
raise CommandExecutionError(
'Unable to interpret API event: \'{0}\''.format(event),
info={'Error': exc.__str__()}
)
return api_events
else:
return ret
# If we're here, it's because an exception was caught earlier, and the
# API command failed.
msg = 'Unable to perform {0}'.format(attr)
if err:
msg += ': {0}'.format(err)
raise CommandExecutionError(msg)
@_docker_client
def _image_wrapper(attr, *args, **kwargs):
'''
Wrapper to run a docker-py function and return a list of dictionaries
'''
catch_api_errors = kwargs.pop('catch_api_errors', True)
if kwargs.pop('client_auth', False):
# Get credential from the home directory of the user running
# salt-minion, default auth file for docker (~/.docker/config.json)
registry_auth_config = {}
try:
home = os.path.expanduser("~")
docker_auth_file = os.path.join(home, '.docker', 'config.json')
with salt.utils.fopen(docker_auth_file) as fp:
try:
docker_auth = json.load(fp)
fp.close()
except (OSError, IOError) as exc:
if exc.errno != errno.ENOENT:
log.error('Failed to read docker auth file %s: %s', docker_auth_file, exc)
docker_auth = {}
if isinstance(docker_auth, dict):
if 'auths' in docker_auth and isinstance(docker_auth['auths'], dict):
for key, data in six.iteritems(docker_auth['auths']):
if isinstance(data, dict):
email = str(data.get('email', ''))
b64_auth = base64.b64decode(data.get('auth', ''))
username, password = b64_auth.split(':')
registry = 'https://{registry}'.format(registry=key)
registry_auth_config.update({registry: {
'username': username,
'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))
# Set credentials from pillar - Overwrite auth from config.json
registry_auth_config.update(__pillar__.get('docker-registries', {}))
for key, data in six.iteritems(__pillar__):
if key.endswith('-docker-registries'):
registry_auth_config.update(data)
err = (
'{0} Docker credentials{1}. Please see the dockerng remote '
'execution module documentation for information on how to '
'configure authentication.'
)
try:
for registry, creds in six.iteritems(registry_auth_config):
__context__['docker.client'].login(
creds['username'],
password=creds['password'],
email=creds.get('email'),
registry=registry,
reauth=creds.get('reauth', False))
except KeyError:
raise SaltInvocationError(
err.format('Incomplete', ' for registry {0}'.format(registry))
)
client_timeout = kwargs.pop('client_timeout', None)
if client_timeout is not None:
__context__['docker.client'].timeout = client_timeout
func = getattr(__context__['docker.client'], attr)
if func is None:
raise SaltInvocationError('Invalid client action \'{0}\''.format(attr))
ret = []
try:
output = func(*args, **kwargs)
if not kwargs.get('stream', False):
output = output.splitlines()
for line in output:
ret.append(json.loads(line))
except docker.errors.APIError as exc:
if catch_api_errors:
# Generic handling of Docker API errors
raise CommandExecutionError(
'Error {0}: {1}'.format(exc.response.status_code,
exc.explanation)
)
else:
# Allow API errors to be caught further up the stack
raise
except Exception as exc:
raise CommandExecutionError(
'Error occurred performing docker {0}: {1}'.format(attr, exc)
)
return ret
def _build_status(data, item):
'''
Process a status update from a docker build, updating the data structure
@ -2055,6 +1811,98 @@ def _validate_input(kwargs,
pass
def login(*registries):
'''
.. versionadded:: 2016.3.7,2016.11.4,Nitrogen
Performs a ``docker login`` to authenticate to one or more configured
repositories. See the documentation at the top of this page to configure
authentication credentials.
Multiple registry URLs (matching those configured in Pillar) can be passed,
and Salt will attempt to login to *just* those registries. If no registry
URLs are provided, Salt will attempt to login to *all* configured
registries.
**RETURN DATA**
A dictionary containing the following keys:
- ``Results`` - A dictionary mapping registry URLs to the authentication
result. ``True`` means a successful login, ``False`` means a failed
login.
- ``Errors`` - A list of errors encountered during the course of this
function.
CLI Example:
.. code-block:: bash
salt myminion docker.login
salt myminion docker.login hub
salt myminion docker.login hub https://mydomain.tld/registry/
'''
# NOTE: This function uses the "docker login" CLI command so that login
# information is added to the config.json, since docker-py isn't designed
# to do so.
registry_auth = __pillar__.get('docker-registries', {})
ret = {}
errors = ret.setdefault('Errors', [])
if not isinstance(registry_auth, dict):
errors.append('\'docker-registries\' Pillar value must be a dictionary')
registry_auth = {}
for key, data in six.iteritems(__pillar__):
try:
if key.endswith('-docker-registries'):
try:
registry_auth.update(data)
except TypeError:
errors.append(
'\'{0}\' Pillar value must be a dictionary'.format(key)
)
except AttributeError:
pass
# If no registries passed, we will auth to all of them
if not registries:
registries = list(registry_auth)
results = ret.setdefault('Results', {})
for registry in registries:
if registry not in registry_auth:
errors.append(
'No match found for registry \'{0}\''.format(registry)
)
continue
try:
username = registry_auth[registry]['username']
password = registry_auth[registry]['password']
except TypeError:
errors.append(
'Invalid configuration for registry \'{0}\''.format(registry)
)
except KeyError as exc:
errors.append(
'Missing {0} for registry \'{1}\''.format(exc, registry)
)
else:
cmd = ['docker', 'login', '-u', username, '-p', password]
if registry.lower() != 'hub':
cmd.append(registry)
login_cmd = __salt__['cmd.run_all'](
cmd,
python_shell=False,
output_loglevel='quiet',
)
results[registry] = login_cmd['retcode'] == 0
if not results[registry]:
if login_cmd['stderr']:
errors.append(login_cmd['stderr'])
elif login_cmd['stdout']:
errors.append(login_cmd['stdout'])
return ret
# Functions for information gathering
def depends(name):
'''
@ -2328,7 +2176,6 @@ def images(verbose=False, **kwargs):
return ret
@_docker_client
def info():
'''
Returns a dictionary of system-wide information. Equivalent to running
@ -3208,7 +3055,7 @@ def create(image,
# API v1.15 introduced HostConfig parameter
# https://docs.docker.com/engine/reference/api/docker_remote_api_v1.15/#create-a-container
if salt.utils.version_cmp(version()['ApiVersion'], '1.15') > 0:
client = __context__['docker.client']
client = _get_client()
host_config_args = get_client_args()['host_config']
create_kwargs['host_config'] = client.create_host_config(
**dict((arg, create_kwargs.pop(arg, None)) for arg in host_config_args if arg != 'version')
@ -3928,10 +3775,10 @@ def import_(source,
path = __salt__['container_resource.cache_file'](source)
time_started = time.time()
response = _image_wrapper('import_image',
path,
repository=repo_name,
tag=repo_tag)
response = _client_wrapper('import_image',
path,
repository=repo_name,
tag=repo_tag)
ret = {'Time_Elapsed': time.time() - time_started}
_clear_context()
@ -4088,8 +3935,7 @@ def pull(image,
api_response=False,
client_timeout=CLIENT_TIMEOUT):
'''
Pulls an image from a Docker registry. See the documentation at the top of
this page to configure authenticated access.
Pulls an image from a Docker registry
image
Image to be pulled, in ``repo:tag`` notation. If just the repository
@ -4138,13 +3984,12 @@ def pull(image,
repo_name, repo_tag = _get_repo_tag(image)
kwargs = {'tag': repo_tag,
'stream': True,
'client_auth': True,
'client_timeout': client_timeout}
if insecure_registry:
kwargs['insecure_registry'] = insecure_registry
time_started = time.time()
response = _image_wrapper('pull', repo_name, **kwargs)
response = _client_wrapper('pull', repo_name, **kwargs)
ret = {'Time_Elapsed': time.time() - time_started}
_clear_context()
@ -4192,7 +4037,7 @@ def push(image,
This is due to changes in the Docker Remote API.
Pushes an image to a Docker registry. See the documentation at top of this
page to configure authenticated access.
page to configure authentication credentials.
image
Image to be pushed, in ``repo:tag`` notation.
@ -4243,13 +4088,12 @@ def push(image,
kwargs = {'tag': repo_tag,
'stream': True,
'client_auth': True,
'client_timeout': client_timeout}
if insecure_registry:
kwargs['insecure_registry'] = insecure_registry
time_started = time.time()
response = _image_wrapper('push', repo_name, **kwargs)
response = _client_wrapper('push', repo_name, **kwargs)
ret = {'Time_Elapsed': time.time() - time_started}
_clear_context()
@ -4273,6 +4117,8 @@ def push(image,
elif item_type == 'errorDetail':
_error_detail(errors, item)
if errors:
ret['Errors'] = errors
return ret
@ -4325,18 +4171,19 @@ def rmi(*names, **kwargs):
catch_api_errors=False)
except docker.errors.APIError as exc:
if exc.response.status_code == 409:
err = ('Unable to remove image \'{0}\' because it is in '
'use by '.format(name))
errors.append(exc.explanation)
deps = depends(name)
if deps['Containers']:
err += 'container(s): {0}'.format(
', '.join(deps['Containers'])
)
if deps['Images']:
if deps['Containers'] or deps['Images']:
err = 'Image is in use by '
if deps['Containers']:
err += ' and '
err += 'image(s): {0}'.format(', '.join(deps['Images']))
errors.append(err)
err += 'container(s): {0}'.format(
', '.join(deps['Containers'])
)
if deps['Images']:
if deps['Containers']:
err += ' and '
err += 'image(s): {0}'.format(', '.join(deps['Images']))
errors.append(err)
else:
errors.append('Error {0}: {1}'.format(exc.response.status_code,
exc.explanation))
@ -4584,11 +4431,8 @@ def tag_(name, image, force=False):
# Only non-error return case is a True return, so just return the response
return response
# Network Management
@_api_version(1.21)
@_client_version('1.5.0')
def networks(names=None, ids=None):
'''
List existing networks
@ -4615,8 +4459,6 @@ def networks(names=None, ids=None):
return response
@_api_version(1.21)
@_client_version('1.5.0')
def create_network(name, driver=None):
'''
Create a new network
@ -4639,8 +4481,6 @@ def create_network(name, driver=None):
return response
@_api_version(1.21)
@_client_version('1.5.0')
def remove_network(network_id):
'''
Remove a network
@ -4660,8 +4500,6 @@ def remove_network(network_id):
return response
@_api_version(1.21)
@_client_version('1.5.0')
def inspect_network(network_id):
'''
Inspect Network
@ -4681,8 +4519,6 @@ def inspect_network(network_id):
return response
@_api_version(1.21)
@_client_version('1.5.0')
def connect_container_to_network(container, network_id):
'''
Connect container to network.
@ -4707,8 +4543,6 @@ def connect_container_to_network(container, network_id):
return response
@_api_version(1.21)
@_client_version('1.5.0')
def disconnect_container_from_network(container, network_id):
'''
Disconnect container from network.
@ -4735,8 +4569,6 @@ def disconnect_container_from_network(container, network_id):
# Volume Management
@_api_version(1.21)
@_client_version('1.5.0')
def volumes(filters=None):
'''
List existing volumes
@ -4758,8 +4590,6 @@ def volumes(filters=None):
return response
@_api_version(1.21)
@_client_version('1.5.0')
def create_volume(name, driver=None, driver_opts=None):
'''
Create a new volume
@ -4788,8 +4618,6 @@ def create_volume(name, driver=None, driver_opts=None):
return response
@_api_version(1.21)
@_client_version('1.5.0')
def remove_volume(name):
'''
Remove a volume
@ -4811,8 +4639,6 @@ def remove_volume(name):
return response
@_api_version(1.21)
@_client_version('1.5.0')
def inspect_volume(name):
'''
Inspect Volume
@ -4865,7 +4691,6 @@ def kill(name):
@_refresh_mine_cache
@_api_version(1.12)
@_ensure_exists
def pause(name):
'''
@ -5060,7 +4885,6 @@ def stop(name, timeout=STOP_TIMEOUT, **kwargs):
@_refresh_mine_cache
@_api_version(1.12)
@_ensure_exists
def unpause(name):
'''

View File

@ -12,7 +12,6 @@ Provides the service module for systemd
'''
# Import python libs
from __future__ import absolute_import
import copy
import errno
import glob
import logging
@ -125,8 +124,7 @@ def _clear_context():
# raise a RuntimeError.
for key in list(__context__):
try:
if key.startswith('systemd._systemctl_status.') \
or key in ('systemd.systemd_services',):
if key.startswith('systemd._systemctl_status.'):
__context__.pop(key)
except AttributeError:
continue
@ -178,9 +176,6 @@ def _get_systemd_services():
'''
Use os.listdir() to get all the unit files
'''
contextkey = 'systemd.systemd_services'
if contextkey in __context__:
return __context__[contextkey]
ret = set()
for path in SYSTEM_CONFIG_PATHS + (LOCAL_CONFIG_PATH,):
# Make sure user has access to the path, and if the path is a link
@ -194,11 +189,10 @@ def _get_systemd_services():
continue
if unit_type in VALID_UNIT_TYPES:
ret.add(unit_name if unit_type == 'service' else fullname)
__context__[contextkey] = copy.deepcopy(ret)
return ret
def _get_sysv_services():
def _get_sysv_services(systemd_services=None):
'''
Use os.listdir() and os.access() to get all the initscripts
'''
@ -220,7 +214,9 @@ def _get_sysv_services():
)
return []
systemd_services = _get_systemd_services()
if systemd_services is None:
systemd_services = _get_systemd_services()
ret = []
for sysv_service in sysv_services:
if os.access(os.path.join(INITSCRIPT_PATH, sysv_service), os.X_OK):
@ -537,7 +533,7 @@ def get_all():
salt '*' service.get_all
'''
ret = _get_systemd_services()
ret.update(set(_get_sysv_services()))
ret.update(set(_get_sysv_services(systemd_services=ret)))
return sorted(ret)

View File

@ -14,6 +14,7 @@ from __future__ import absolute_import
# Import python libs
import copy
import errno
import logging
import os
import re
@ -1589,6 +1590,7 @@ def latest(name,
return _uptodate(ret, target, _format_comments(comments))
else:
if os.path.isdir(target):
target_contents = os.listdir(target)
if force_clone:
# Clone is required, and target directory exists, but the
# ``force`` option is enabled, so we need to clear out its
@ -1607,22 +1609,28 @@ def latest(name,
'place (force_clone=True set in git.latest state)'
.format(target, name)
)
try:
if os.path.islink(target):
os.unlink(target)
else:
salt.utils.rm_rf(target)
except OSError as exc:
removal_errors = {}
for target_object in target_contents:
target_path = os.path.join(target, target_object)
try:
salt.utils.rm_rf(target_path)
except OSError as exc:
if exc.errno != errno.ENOENT:
removal_errors[target_path] = exc
if removal_errors:
err_strings = [
' {0}\n {1}'.format(k, v)
for k, v in six.iteritems(removal_errors)
]
return _fail(
ret,
'Unable to remove {0}: {1}'.format(target, exc),
'Unable to remove\n{0}'.format('\n'.join(err_strings)),
comments
)
else:
ret['changes']['forced clone'] = True
ret['changes']['forced clone'] = True
# Clone is required, but target dir exists and is non-empty. We
# can't proceed.
elif os.listdir(target):
elif target_contents:
return _fail(
ret,
'Target \'{0}\' exists, is non-empty and is not a git '

View File

@ -25,7 +25,6 @@ ensure_in_syspath('../../')
import salt.modules.dockerng as dockerng_mod
from salt.exceptions import CommandExecutionError, SaltInvocationError
dockerng_mod.__context__ = {}
dockerng_mod.__salt__ = {}
dockerng_mod.__opts__ = {}
@ -36,49 +35,30 @@ class DockerngTestCase(TestCase):
'''
Validate dockerng module
'''
def setUp(self):
'''
Ensure we aren't persisting context dunders between tests
'''
dockerng_mod.__context__ = {'docker.docker_version': ''}
try:
docker_version = dockerng_mod.docker.version_info
except AttributeError:
docker_version = 0,
client_args_mock = MagicMock(return_value={
'create_container': [
'image', 'command', 'hostname', 'user', 'detach', 'stdin_open',
'tty', 'ports', 'environment', 'volumes', 'network_disabled',
'name', 'entrypoint', 'working_dir', 'domainname', 'cpuset',
'host_config', 'mac_address', 'labels', 'volume_driver',
'stop_signal', 'networking_config', 'healthcheck',
'stop_timeout'],
'host_config': [
'binds', 'port_bindings', 'lxc_conf', 'publish_all_ports',
'links', 'privileged', 'dns', 'dns_search', 'volumes_from',
'network_mode', 'restart_policy', 'cap_add', 'cap_drop',
'devices', 'extra_hosts', 'read_only', 'pid_mode', 'ipc_mode',
'security_opt', 'ulimits', 'log_config', 'mem_limit',
'memswap_limit', 'mem_reservation', 'kernel_memory',
'mem_swappiness', 'cgroup_parent', 'group_add', 'cpu_quota',
'cpu_period', 'blkio_weight', 'blkio_weight_device',
'device_read_bps', 'device_write_bps', 'device_read_iops',
'device_write_iops', 'oom_kill_disable', 'shm_size', 'sysctls',
'tmpfs', 'oom_score_adj', 'dns_opt', 'cpu_shares',
'cpuset_cpus', 'userns_mode', 'pids_limit', 'isolation',
'auto_remove', 'storage_opt'],
'networking_config': [
'aliases', 'links', 'ipv4_address', 'ipv6_address',
'link_local_ips'],
})
def test_ps_with_host_true(self):
'''
Check that dockerng.ps called with host is ``True``,
include resutlt of ``network.interfaces`` command in returned result.
'''
client = Mock()
client.containers = MagicMock(return_value=[])
get_client_mock = MagicMock(return_value=client)
network_interfaces = Mock(return_value={'mocked': None})
with patch.dict(dockerng_mod.__salt__,
{'network.interfaces': network_interfaces}):
with patch.dict(dockerng_mod.__context__,
{'docker.client': MagicMock()}):
with patch.object(dockerng_mod, '_get_client', get_client_mock):
ret = dockerng_mod.ps_(host=True)
self.assertEqual(ret,
{'host': {'interfaces': {'mocked': None}}})
@ -87,9 +67,11 @@ class DockerngTestCase(TestCase):
'''
Check that dockerng.ps accept filters parameter.
'''
client = MagicMock()
with patch.dict(dockerng_mod.__context__,
{'docker.client': client}):
client = Mock()
client.containers = MagicMock(return_value=[])
get_client_mock = MagicMock(return_value=client)
with patch.object(dockerng_mod, '_get_client', get_client_mock):
dockerng_mod.ps_(filters={'label': 'KEY'})
client.containers.assert_called_once_with(
all=True,
@ -115,15 +97,16 @@ class DockerngTestCase(TestCase):
):
mine_send = Mock()
command = getattr(dockerng_mod, command_name)
docker_client = MagicMock()
docker_client.api_version = '1.12'
client = MagicMock()
client.api_version = '1.12'
get_client_mock = MagicMock(return_value=client)
with patch.dict(dockerng_mod.__salt__,
{'mine.send': mine_send,
'container_resource.run': MagicMock(),
'cp.cache_file': MagicMock(return_value=False)}):
with patch.object(dockerng_mod, 'get_client_args', self.client_args_mock):
with patch.dict(dockerng_mod.__context__, {'docker.client': docker_client}):
command('container', *args)
with patch.object(dockerng_mod, '_get_client', get_client_mock):
command('container', *args)
mine_send.assert_called_with('dockerng.ps', verbose=True, all=True,
host=True)
@ -145,10 +128,11 @@ class DockerngTestCase(TestCase):
client.api_version = '1.19'
client.create_host_config.return_value = host_config
client.create_container.return_value = {}
get_client_mock = MagicMock(return_value=client)
with patch.dict(dockerng_mod.__dict__, {'__salt__': __salt__}):
with patch.object(dockerng_mod, 'get_client_args', self.client_args_mock):
with patch.dict(dockerng_mod.__context__, {'docker.client': client}):
dockerng_mod.create('image', cmd='ls', name='ctn')
with patch.object(dockerng_mod, '_get_client', get_client_mock):
dockerng_mod.create('image', cmd='ls', name='ctn')
client.create_container.assert_called_once_with(
command='ls',
host_config=host_config,
@ -173,10 +157,11 @@ class DockerngTestCase(TestCase):
client.api_version = '1.19'
client.create_host_config.return_value = host_config
client.create_container.return_value = {}
get_client_mock = MagicMock(return_value=client)
with patch.dict(dockerng_mod.__dict__, {'__salt__': __salt__}):
with patch.object(dockerng_mod, 'get_client_args', self.client_args_mock):
with patch.dict(dockerng_mod.__context__, {'docker.client': client}):
dockerng_mod.create('image', name='ctn', publish_all_ports=True)
with patch.object(dockerng_mod, '_get_client', get_client_mock):
dockerng_mod.create('image', name='ctn', publish_all_ports=True)
client.create_container.assert_called_once_with(
host_config=host_config,
image='image',
@ -201,15 +186,16 @@ class DockerngTestCase(TestCase):
client.api_version = '1.19'
client.create_host_config.return_value = host_config
client.create_container.return_value = {}
get_client_mock = MagicMock(return_value=client)
with patch.dict(dockerng_mod.__dict__, {'__salt__': __salt__}):
with patch.object(dockerng_mod, 'get_client_args', self.client_args_mock):
with patch.dict(dockerng_mod.__context__, {'docker.client': client}):
dockerng_mod.create(
'image',
name='ctn',
labels={'KEY': 'VALUE'},
validate_input=True,
)
with patch.object(dockerng_mod, '_get_client', get_client_mock):
dockerng_mod.create(
'image',
name='ctn',
labels={'KEY': 'VALUE'},
validate_input=True,
)
client.create_container.assert_called_once_with(
labels={'KEY': 'VALUE'},
host_config=host_config,
@ -236,15 +222,15 @@ class DockerngTestCase(TestCase):
client.api_version = '1.19'
client.create_host_config.return_value = host_config
client.create_container.return_value = {}
get_client_mock = MagicMock(return_value=client)
with patch.dict(dockerng_mod.__dict__, {'__salt__': __salt__}):
with patch.object(dockerng_mod, 'get_client_args', self.client_args_mock):
with patch.dict(dockerng_mod.__context__, {'docker.client': client}):
dockerng_mod.create(
'image',
name='ctn',
labels=['KEY1', 'KEY2'],
validate_input=True,
)
with patch.object(dockerng_mod, '_get_client', get_client_mock):
dockerng_mod.create(
'image',
name='ctn',
labels=['KEY1', 'KEY2'],
validate_input=True,
)
client.create_container.assert_called_once_with(
labels=['KEY1', 'KEY2'],
host_config=host_config,
@ -271,10 +257,11 @@ class DockerngTestCase(TestCase):
client.api_version = '1.19'
client.create_host_config.return_value = host_config
client.create_container.return_value = {}
get_client_mock = MagicMock(return_value=client)
with patch.dict(dockerng_mod.__dict__,
{'__salt__': __salt__}):
with patch.dict(dockerng_mod.__context__,
{'docker.client': client}):
with patch.object(dockerng_mod, '_get_client', get_client_mock):
self.assertRaises(SaltInvocationError,
dockerng_mod.create,
'image',
@ -302,15 +289,16 @@ class DockerngTestCase(TestCase):
client.api_version = '1.19'
client.create_host_config.return_value = host_config
client.create_container.return_value = {}
get_client_mock = MagicMock(return_value=client)
with patch.dict(dockerng_mod.__dict__, {'__salt__': __salt__}):
with patch.object(dockerng_mod, 'get_client_args', self.client_args_mock):
with patch.dict(dockerng_mod.__context__, {'docker.client': client}):
dockerng_mod.create(
'image',
name='ctn',
labels=[{'KEY1': 'VALUE1'}, {'KEY2': 'VALUE2'}],
validate_input=True,
)
with patch.object(dockerng_mod, '_get_client', get_client_mock):
dockerng_mod.create(
'image',
name='ctn',
labels=[{'KEY1': 'VALUE1'}, {'KEY2': 'VALUE2'}],
validate_input=True,
)
client.create_container.assert_called_once_with(
labels={'KEY1': 'VALUE1', 'KEY2': 'VALUE2'},
host_config=host_config,
@ -333,10 +321,11 @@ class DockerngTestCase(TestCase):
host_config = {}
client = Mock()
client.api_version = '1.21'
get_client_mock = MagicMock(return_value=client)
with patch.dict(dockerng_mod.__dict__,
{'__salt__': __salt__}):
with patch.dict(dockerng_mod.__context__,
{'docker.client': client}):
with patch.object(dockerng_mod, '_get_client', get_client_mock):
dockerng_mod.networks(
names=['foo'],
ids=['01234'],
@ -361,18 +350,13 @@ class DockerngTestCase(TestCase):
host_config = {}
client = Mock()
client.api_version = '1.21'
get_client_mock = MagicMock(return_value=client)
with patch.dict(dockerng_mod.__dict__,
{'__salt__': __salt__}):
with patch.dict(dockerng_mod.__context__,
{'docker.client': client}):
dockerng_mod.create_network(
'foo',
driver='bridge',
)
client.create_network.assert_called_once_with(
'foo',
driver='bridge',
)
with patch.object(dockerng_mod, '_get_client', get_client_mock):
dockerng_mod.create_network('foo', driver='bridge')
client.create_network.assert_called_once_with('foo', driver='bridge')
@skipIf(docker_version < (1, 5, 0),
'docker module must be installed to run this test or is too old. >=1.5.0')
@ -389,10 +373,11 @@ class DockerngTestCase(TestCase):
host_config = {}
client = Mock()
client.api_version = '1.21'
get_client_mock = MagicMock(return_value=client)
with patch.dict(dockerng_mod.__dict__,
{'__salt__': __salt__}):
with patch.dict(dockerng_mod.__context__,
{'docker.client': client}):
with patch.object(dockerng_mod, '_get_client', get_client_mock):
dockerng_mod.remove_network('foo')
client.remove_network.assert_called_once_with('foo')
@ -411,10 +396,11 @@ class DockerngTestCase(TestCase):
host_config = {}
client = Mock()
client.api_version = '1.21'
get_client_mock = MagicMock(return_value=client)
with patch.dict(dockerng_mod.__dict__,
{'__salt__': __salt__}):
with patch.dict(dockerng_mod.__context__,
{'docker.client': client}):
with patch.object(dockerng_mod, '_get_client', get_client_mock):
dockerng_mod.inspect_network('foo')
client.inspect_network.assert_called_once_with('foo')
@ -433,10 +419,11 @@ class DockerngTestCase(TestCase):
host_config = {}
client = Mock()
client.api_version = '1.21'
get_client_mock = MagicMock(return_value=client)
with patch.dict(dockerng_mod.__dict__,
{'__salt__': __salt__}):
with patch.dict(dockerng_mod.__context__,
{'docker.client': client}):
with patch.object(dockerng_mod, '_get_client', get_client_mock):
dockerng_mod.connect_container_to_network('container', 'foo')
client.connect_container_to_network.assert_called_once_with(
'container', 'foo')
@ -456,10 +443,11 @@ class DockerngTestCase(TestCase):
host_config = {}
client = Mock()
client.api_version = '1.21'
get_client_mock = MagicMock(return_value=client)
with patch.dict(dockerng_mod.__dict__,
{'__salt__': __salt__}):
with patch.dict(dockerng_mod.__context__,
{'docker.client': client}):
with patch.object(dockerng_mod, '_get_client', get_client_mock):
dockerng_mod.disconnect_container_from_network('container', 'foo')
client.disconnect_container_from_network.assert_called_once_with(
'container', 'foo')
@ -478,16 +466,13 @@ class DockerngTestCase(TestCase):
}
client = Mock()
client.api_version = '1.21'
get_client_mock = MagicMock(return_value=client)
with patch.dict(dockerng_mod.__dict__,
{'__salt__': __salt__}):
with patch.dict(dockerng_mod.__context__,
{'docker.client': client}):
dockerng_mod.volumes(
filters={'dangling': [True]},
)
client.volumes.assert_called_once_with(
filters={'dangling': [True]},
)
with patch.object(dockerng_mod, '_get_client', get_client_mock):
dockerng_mod.volumes(filters={'dangling': [True]})
client.volumes.assert_called_once_with(filters={'dangling': [True]})
@skipIf(docker_version < (1, 5, 0),
'docker module must be installed to run this test or is too old. >=1.5.0')
@ -503,10 +488,11 @@ class DockerngTestCase(TestCase):
}
client = Mock()
client.api_version = '1.21'
get_client_mock = MagicMock(return_value=client)
with patch.dict(dockerng_mod.__dict__,
{'__salt__': __salt__}):
with patch.dict(dockerng_mod.__context__,
{'docker.client': client}):
with patch.object(dockerng_mod, '_get_client', get_client_mock):
dockerng_mod.create_volume(
'foo',
driver='bridge',
@ -532,10 +518,11 @@ class DockerngTestCase(TestCase):
}
client = Mock()
client.api_version = '1.21'
get_client_mock = MagicMock(return_value=client)
with patch.dict(dockerng_mod.__dict__,
{'__salt__': __salt__}):
with patch.dict(dockerng_mod.__context__,
{'docker.client': client}):
with patch.object(dockerng_mod, '_get_client', get_client_mock):
dockerng_mod.remove_volume('foo')
client.remove_volume.assert_called_once_with('foo')
@ -553,10 +540,11 @@ class DockerngTestCase(TestCase):
}
client = Mock()
client.api_version = '1.21'
get_client_mock = MagicMock(return_value=client)
with patch.dict(dockerng_mod.__dict__,
{'__salt__': __salt__}):
with patch.dict(dockerng_mod.__context__,
{'docker.client': client}):
with patch.object(dockerng_mod, '_get_client', get_client_mock):
dockerng_mod.inspect_volume('foo')
client.inspect_volume.assert_called_once_with('foo')
@ -564,13 +552,14 @@ class DockerngTestCase(TestCase):
client = Mock()
client.api_version = '1.21'
client.wait = Mock(return_value=0)
get_client_mock = MagicMock(return_value=client)
dockerng_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__,
{'docker.client': client}):
with patch.object(dockerng_mod, '_get_client', get_client_mock):
dockerng_mod._clear_context()
result = dockerng_mod.wait('foo')
self.assertEqual(result, {'result': True,
@ -582,14 +571,15 @@ class DockerngTestCase(TestCase):
client = Mock()
client.api_version = '1.21'
client.wait = Mock(return_value=0)
get_client_mock = MagicMock(return_value=client)
dockerng_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__,
{'docker.client': client}):
with patch.object(dockerng_mod, '_get_client', get_client_mock):
dockerng_mod._clear_context()
result = dockerng_mod.wait('foo')
self.assertEqual(result, {'result': False,
@ -602,14 +592,15 @@ class DockerngTestCase(TestCase):
client = Mock()
client.api_version = '1.21'
client.wait = Mock(return_value=0)
get_client_mock = MagicMock(return_value=client)
dockerng_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__,
{'docker.client': client}):
with patch.object(dockerng_mod, '_get_client', get_client_mock):
dockerng_mod._clear_context()
result = dockerng_mod.wait('foo', ignore_already_stopped=True)
self.assertEqual(result, {'result': True,
@ -621,11 +612,12 @@ class DockerngTestCase(TestCase):
def test_wait_success_absent_container(self):
client = Mock()
client.api_version = '1.21'
get_client_mock = MagicMock(return_value=client)
dockerng_inspect_container = Mock(side_effect=CommandExecutionError)
with patch.object(dockerng_mod, 'inspect_container',
dockerng_inspect_container):
with patch.dict(dockerng_mod.__context__,
{'docker.client': client}):
with patch.object(dockerng_mod, '_get_client', get_client_mock):
dockerng_mod._clear_context()
result = dockerng_mod.wait('foo', ignore_already_stopped=True)
self.assertEqual(result, {'result': True,
@ -635,13 +627,14 @@ class DockerngTestCase(TestCase):
client = Mock()
client.api_version = '1.21'
client.wait = Mock(return_value=1)
get_client_mock = MagicMock(return_value=client)
dockerng_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__,
{'docker.client': client}):
with patch.object(dockerng_mod, '_get_client', get_client_mock):
dockerng_mod._clear_context()
result = dockerng_mod.wait('foo', fail_on_exit_status=True)
self.assertEqual(result, {'result': False,
@ -653,13 +646,14 @@ class DockerngTestCase(TestCase):
client = Mock()
client.api_version = '1.21'
client.wait = Mock(return_value=1)
get_client_mock = MagicMock(return_value=client)
dockerng_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__,
{'docker.client': client}):
with patch.object(dockerng_mod, '_get_client', get_client_mock):
dockerng_mod._clear_context()
result = dockerng_mod.wait('foo',
ignore_already_stopped=True,
@ -799,8 +793,9 @@ class DockerngTestCase(TestCase):
{'Id': 'sha256:abcdef'},
{'Id': 'sha256:abcdefg',
'RepoTags': ['image:latest']}])
with patch.dict(dockerng_mod.__context__,
{'docker.client': client}):
get_client_mock = MagicMock(return_value=client)
with patch.object(dockerng_mod, '_get_client', get_client_mock):
dockerng_mod._clear_context()
result = dockerng_mod.images()
self.assertEqual(result,

View File

@ -168,8 +168,8 @@ class SystemdTestCase(TestCase):
'''
listdir_mock = MagicMock(side_effect=[
['foo.service', 'multi-user.target.wants', 'mytimer.timer'],
[],
['foo.service', 'multi-user.target.wants', 'bar.service'],
['mysql', 'nginx', 'README'],
['mysql', 'nginx', 'README']
])
access_mock = MagicMock(