%PDF- %PDF-
Direktori : /usr/lib/python2.7/site-packages/salt/states/ |
Current File : //usr/lib/python2.7/site-packages/salt/states/docker_image.py |
# -*- coding: utf-8 -*- ''' Management of Docker images .. versionadded:: 2017.7.0 :depends: docker_ Python module .. note:: Older releases of the Python bindings for Docker were called docker-py_ in PyPI. All releases of docker_, and releases of docker-py_ >= 1.6.0 are supported. These python bindings can easily be installed using :py:func:`pip.install <salt.modules.pip.install>`: .. code-block:: bash salt myminion pip.install docker To upgrade from docker-py_ to docker_, you must first uninstall docker-py_, and then install docker_: .. code-block:: bash salt myminion pip.uninstall docker-py salt myminion pip.install docker .. _docker: https://pypi.python.org/pypi/docker .. _docker-py: https://pypi.python.org/pypi/docker-py These states were moved from the :mod:`docker <salt.states.docker>` state module (formerly called **dockerng**) in the 2017.7.0 release. .. note:: To pull from a Docker registry, authentication must be configured. See :ref:`here <docker-authentication>` for more information on how to configure access to docker registries in :ref:`Pillar <pillar>` data. ''' from __future__ import absolute_import, print_function, unicode_literals import logging # Import salt libs import salt.utils.docker import salt.utils.args from salt.ext.six.moves import zip from salt.ext import six from salt.exceptions import CommandExecutionError # Enable proper logging log = logging.getLogger(__name__) # pylint: disable=invalid-name # Define the module's virtual name __virtualname__ = 'docker_image' __virtual_aliases__ = ('moby_image',) def __virtual__(): ''' Only load if the docker execution module is available ''' if 'docker.version' in __salt__: return __virtualname__ return (False, __salt__.missing_fun_string('docker.version')) def present(name, tag=None, build=None, load=None, force=False, insecure_registry=False, client_timeout=salt.utils.docker.CLIENT_TIMEOUT, dockerfile=None, sls=None, base='opensuse/python', saltenv='base', pillarenv=None, pillar=None, **kwargs): ''' .. versionchanged:: 2018.3.0 The ``tag`` argument has been added. It is now required unless pulling from a registry. Ensure that an image is present. The image can either be pulled from a Docker registry, built from a Dockerfile, loaded from a saved image, or built by running SLS files against a base image. If none of the ``build``, ``load``, or ``sls`` arguments are used, then Salt will pull from the :ref:`configured registries <docker-authentication>`. If the specified image already exists, it will not be pulled unless ``force`` is set to ``True``. Here is an example of a state that will pull an image from the Docker Hub: .. code-block:: yaml myuser/myimage: docker_image.present: - tag: mytag tag Tag name for the image. Required when using ``build``, ``load``, or ``sls`` to create the image, but optional if pulling from a repository. .. versionadded:: 2018.3.0 build Path to directory on the Minion containing a Dockerfile .. code-block:: yaml myuser/myimage: docker_image.present: - build: /home/myuser/docker/myimage - tag: mytag myuser/myimage: docker_image.present: - build: /home/myuser/docker/myimage - tag: mytag - dockerfile: Dockerfile.alternative The image will be built using :py:func:`docker.build <salt.modules.dockermod.build>` and the specified image name and tag will be applied to it. .. versionadded:: 2016.11.0 .. versionchanged:: 2018.3.0 The ``tag`` must be manually specified using the ``tag`` argument. load Loads a tar archive created with :py:func:`docker.load <salt.modules.dockermod.load>` (or the ``docker load`` Docker CLI command), and assigns it the specified repo and tag. .. code-block:: yaml myuser/myimage: docker_image.present: - load: salt://path/to/image.tar - tag: mytag .. versionchanged:: 2018.3.0 The ``tag`` must be manually specified using the ``tag`` argument. force : False Set this parameter to ``True`` to force Salt to pull/build/load the image even if it is already present. client_timeout Timeout in seconds for the Docker client. This is not a timeout for the state, but for receiving a response from the API. dockerfile Allows for an alternative Dockerfile to be specified. Path to alternative Dockefile is relative to the build path for the Docker container. .. versionadded:: 2016.11.0 sls Allow for building of image with :py:func:`docker.sls_build <salt.modules.dockermod.sls_build>` by specifying the SLS files with which to build. This can be a list or comma-seperated string. .. code-block:: yaml myuser/myimage: docker_image.present: - tag: latest - sls: - webapp1 - webapp2 - base: centos - saltenv: base .. versionadded: 2017.7.0 .. versionchanged:: 2018.3.0 The ``tag`` must be manually specified using the ``tag`` argument. base Base image with which to start :py:func:`docker.sls_build <salt.modules.dockermod.sls_build>` .. versionadded:: 2017.7.0 saltenv Specify the environment from which to retrieve the SLS indicated by the `mods` parameter. .. versionadded:: 2017.7.0 .. versionchanged:: 2018.3.0 Now uses the effective saltenv if not explicitly passed. In earlier versions, ``base`` was assumed as a default. pillarenv Specify a Pillar environment to be used when applying states. This can also be set in the minion config file using the :conf_minion:`pillarenv` option. When neither the :conf_minion:`pillarenv` minion config option nor this CLI argument is used, all Pillar environments will be merged together. .. versionadded:: 2018.3.0 pillar Custom Pillar values, passed as a dictionary of key-value pairs .. note:: Values passed this way will override Pillar values set via ``pillar_roots`` or an external Pillar source. .. versionadded:: 2018.3.0 ''' ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} if not isinstance(name, six.string_types): name = six.text_type(name) # At most one of the args that result in an image being built can be used num_build_args = len([x for x in (build, load, sls) if x is not None]) if num_build_args > 1: ret['comment'] = \ 'Only one of \'build\', \'load\', or \'sls\' is permitted.' return ret elif num_build_args == 1: # If building, we need the tag to be specified if not tag: ret['comment'] = ( 'The \'tag\' argument is required if any one of \'build\', ' '\'load\', or \'sls\' is used.' ) return ret if not isinstance(tag, six.string_types): tag = six.text_type(tag) full_image = ':'.join((name, tag)) else: if tag: name = '{0}:{1}'.format(name, tag) full_image = name try: image_info = __salt__['docker.inspect_image'](full_image) except CommandExecutionError as exc: msg = exc.__str__() if '404' in msg: # Image not present image_info = None else: ret['comment'] = msg return ret if image_info is not None: # Specified image is present if not force: ret['result'] = True ret['comment'] = 'Image {0} already present'.format(full_image) return ret if build or sls: action = 'built' elif load: action = 'loaded' else: action = 'pulled' if __opts__['test']: ret['result'] = None if (image_info is not None and force) or image_info is None: ret['comment'] = 'Image {0} will be {1}'.format(full_image, action) return ret if build: # Get the functions default value and args argspec = salt.utils.args.get_function_argspec(__salt__['docker.build']) # Map any if existing args from kwargs into the build_args dictionary build_args = dict(list(zip(argspec.args, argspec.defaults))) for k, v in build_args.items(): if k in kwargs.get('kwargs', {}): build_args[k] = kwargs.get('kwargs', {}).get(k) try: # map values passed from the state to the build args build_args['path'] = build build_args['repository'] = name build_args['tag'] = tag build_args['dockerfile'] = dockerfile image_update = __salt__['docker.build'](**build_args) except Exception as exc: ret['comment'] = ( 'Encountered error building {0} as {1}: {2}'.format( build, full_image, exc ) ) return ret if image_info is None or image_update['Id'] != image_info['Id'][:12]: ret['changes'] = image_update elif sls: _locals = locals() sls_build_kwargs = {k: _locals[k] for k in ('saltenv', 'pillarenv', 'pillar') if _locals[k] is not None} try: image_update = __salt__['docker.sls_build'](repository=name, tag=tag, base=base, mods=sls, **sls_build_kwargs) except Exception as exc: ret['comment'] = ( 'Encountered error using SLS {0} for building {1}: {2}' .format(sls, full_image, exc) ) return ret if image_info is None or image_update['Id'] != image_info['Id'][:12]: ret['changes'] = image_update elif load: try: image_update = __salt__['docker.load'](path=load, repository=name, tag=tag) except Exception as exc: ret['comment'] = ( 'Encountered error loading {0} as {1}: {2}' .format(load, full_image, exc) ) return ret if image_info is None or image_update.get('Layers', []): ret['changes'] = image_update else: try: image_update = __salt__['docker.pull']( name, insecure_registry=insecure_registry, client_timeout=client_timeout ) except Exception as exc: ret['comment'] = \ 'Encountered error pulling {0}: {1}'.format(full_image, exc) return ret if (image_info is not None and image_info['Id'][:12] == image_update .get('Layers', {}) .get('Already_Pulled', [None])[0]): # Image was pulled again (because of force) but was also # already there. No new image was available on the registry. pass elif image_info is None or image_update.get('Layers', {}).get('Pulled'): # Only add to the changes dict if layers were pulled ret['changes'] = image_update error = False try: __salt__['docker.inspect_image'](full_image) except CommandExecutionError as exc: msg = exc.__str__() if '404' not in msg: error = 'Failed to inspect image \'{0}\' after it was {1}: {2}'.format( full_image, action, msg ) if error: ret['comment'] = error else: ret['result'] = True if not ret['changes']: ret['comment'] = ( 'Image \'{0}\' was {1}, but there were no changes'.format( name, action ) ) else: ret['comment'] = 'Image \'{0}\' was {1}'.format(full_image, action) return ret def absent(name=None, images=None, force=False): ''' Ensure that an image is absent from the Minion. Image names can be specified either using ``repo:tag`` notation, or just the repo name (in which case a tag of ``latest`` is assumed). images Run this state on more than one image at a time. The following two examples accomplish the same thing: .. code-block:: yaml remove_images: docker_image.absent: - names: - busybox - centos:6 - nginx .. code-block:: yaml remove_images: docker_image.absent: - images: - busybox - centos:6 - nginx However, the second example will be a bit quicker since Salt will do all the deletions in a single run, rather than executing the state separately on each image (as it would in the first example). force : False Salt will fail to remove any images currently in use by a container. Set this option to true to remove the image even if it is already present. .. note:: This option can also be overridden by Pillar data. If the Minion 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="{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 ``docker.force.image_name`` will affect only the named image. ''' ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''} if not name and not images: ret['comment'] = 'One of \'name\' and \'images\' must be provided' return ret elif images is not None: targets = images elif name: targets = [name] to_delete = [] for target in targets: resolved_tag = __salt__['docker.resolve_tag'](target) if resolved_tag is not False: to_delete.append(resolved_tag) if not to_delete: ret['result'] = True if len(targets) == 1: ret['comment'] = 'Image {0} is not present'.format(name) else: ret['comment'] = 'All specified images are not present' return ret if __opts__['test']: ret['result'] = None if len(to_delete) == 1: ret['comment'] = 'Image {0} will be removed'.format(to_delete[0]) else: ret['comment'] = ( 'The following images will be removed: {0}'.format( ', '.join(to_delete) ) ) return ret 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: if [x for x in to_delete if x not in post_tags]: ret['changes'] = result ret['comment'] = ( 'The following image(s) failed to be removed: {0}'.format( ', '.join(failed) ) ) else: ret['comment'] = 'None of the specified images were removed' if 'Errors' in result: ret['comment'] += ( '. The following errors were encountered: {0}' .format('; '.join(result['Errors'])) ) else: ret['changes'] = result if len(to_delete) == 1: ret['comment'] = 'Image {0} was removed'.format(to_delete[0]) else: ret['comment'] = ( 'The following images were removed: {0}'.format( ', '.join(to_delete) ) ) ret['result'] = True return ret def mod_watch(name, sfun=None, **kwargs): ''' The docker_image watcher, called to invoke the watch command. .. note:: This state exists to support special handling of the ``watch`` :ref:`requisite <requisites>`. It should not be called directly. Parameters for this function should be set by the state being triggered. ''' if sfun == 'present': # Force image to be updated kwargs['force'] = True return present(name, **kwargs) return {'name': name, 'changes': {}, 'result': False, 'comment': 'watch requisite is not implemented for ' '{0}'.format(sfun)}