summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Houseknecht <chouseknecht@ansible.com>2016-04-25 23:46:26 -0400
committerChris Houseknecht <chouseknecht@ansible.com>2016-04-25 23:46:26 -0400
commitb68114f838c00ed6d53c21527c6a86e3a2b6f63d (patch)
tree05749ecdede8c2232ab29869daa3f87d9898bff4
parente6fb933c3c68552300f98f9b449087ddbf73e09f (diff)
parent1d2be970132bc314c1bdf90e55515dd05079d75c (diff)
downloadansible-modules-core-b68114f838c00ed6d53c21527c6a86e3a2b6f63d.tar.gz
Merge pull request #3505 from chouseknecht/docker_image
Adding refactored docker_image module.
-rw-r--r--cloud/docker/docker_image.py835
1 files changed, 447 insertions, 388 deletions
diff --git a/cloud/docker/docker_image.py b/cloud/docker/docker_image.py
index 12ba67de..2715eadd 100644
--- a/cloud/docker/docker_image.py
+++ b/cloud/docker/docker_image.py
@@ -1,457 +1,516 @@
#!/usr/bin/python
#
-
-# (c) 2014, Pavel Antonov <antonov@adwz.ru>
+# Copyright 2016 Red Hat | Ansible
#
# This file is part of Ansible
#
-# This module is free software: you can redistribute it and/or modify
+# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
-# This software is distributed in the hope that it will be useful,
+# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this software. If not, see <http://www.gnu.org/licenses/>.
-
-######################################################################
+# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
DOCUMENTATION = '''
---
module: docker_image
-author: "Pavel Antonov (@softzilla)"
+
+short_description: Manage docker images.
+
version_added: "1.5"
-short_description: manage docker images
+
description:
- - Create, check and remove docker images
+ - Build, load or pull an image, making the image available for creating containers. Also supports tagging an
+ image into a repository and archiving an image to a .tar file.
+
options:
- path:
+ archive_path:
description:
- - Path to directory with Dockerfile
+ - Use with state 'present' to archive an image to a .tar file.
required: false
default: null
- aliases: []
+ version_added: "2.1"
dockerfile:
description:
- - Dockerfile to use
- required: false
+ - Use with state 'present' to provide an alternate name for the Dockerfile to use when building an image.
default: Dockerfile
version_added: "2.0"
- name:
+ force:
description:
- - Image name to work with
- required: true
- default: null
- aliases: []
- tag:
- description:
- - Image tag to work with
- required: false
- default: "latest"
- aliases: []
- nocache:
- description:
- - Do not use cache with building
- required: false
+ - Use with absent state to un-tag and remove all images matching the specified name. Use with states 'present'
+ and 'tagged' to take action even when an image already exists.
default: false
- aliases: []
- docker_url:
+ version_added: "2.1"
+ http_timeout:
description:
- - URL of docker host to issue commands to
+ - Timeout for HTTP requests during the image build operation. Provide a positive integer value for the number of
+ seconds.
+ default: null
required: false
- default: ${DOCKER_HOST} or unix://var/run/docker.sock
- aliases: []
- use_tls:
+ version_added: "2.1"
+ name:
description:
- - Whether to use tls to connect to the docker server. "no" means not to
- use tls (and ignore any other tls related parameters). "encrypt" means
- to use tls to encrypt the connection to the server. "verify" means to
- also verify that the server's certificate is valid for the server
- (this both verifies the certificate against the CA and that the
- certificate was issued for that host. If this is unspecified, tls will
- only be used if one of the other tls options require it.
- choices: [ "no", "encrypt", "verify" ]
- version_added: "2.0"
- tls_client_cert:
+ - "Image name. Name format will be one of: name, repository/name, registry_server:port/name.
+ When pushing or pulling an image the name can optionally include the tag by appending ':tag_name'."
+ required: true
+ path:
description:
- - Path to the PEM-encoded certificate used to authenticate docker client.
- If specified tls_client_key must be valid
- default: ${DOCKER_CERT_PATH}/cert.pem
- version_added: "2.0"
- tls_client_key:
+ - Use with state 'present' to build an image. Will be the path to a directory containing the context and
+ Dockerfile for building an image.
+ aliases:
+ - build_path
+ default: null
+ required: false
+ pull:
description:
- - Path to the PEM-encoded key used to authenticate docker client. If
- specified tls_client_cert must be valid
- default: ${DOCKER_CERT_PATH}/key.pem
- version_added: "2.0"
- tls_ca_cert:
+ - When building an image downloads any updates to the FROM image in Dockerfile.
+ default: true
+ version_added: "2.1"
+ rm:
description:
- - Path to a PEM-encoded certificate authority to secure the Docker connection.
- This has no effect if use_tls is encrypt.
- default: ${DOCKER_CERT_PATH}/ca.pem
- version_added: "2.0"
- tls_hostname:
+ - Remove intermediate containers after build.
+ default: true
+ version_added: "2.1"
+ nocache:
description:
- - A hostname to check matches what's supplied in the docker server's
- certificate. If unspecified, the hostname is taken from the docker_url.
- default: Taken from docker_url
- version_added: "2.0"
- docker_api_version:
+ - Do not use cache when building an image.
+ default: false
+ repository:
description:
- - Remote API version to use. This defaults to the current default as
- specified by docker-py.
- default: docker-py default remote API version
- version_added: "2.0"
+ - Full path to a repository. Use with state 'present' to tag the image into the repository.
+ required: false
+ default: null
+ version_added: "2.1"
state:
description:
- - Set the state of the image
- required: false
+ - Make assertions about the state of an image.
+ - When 'absent' an image will be removed. Use the force option to un-tag and remove all images
+ matching the provided name.
+ - When 'present' check if an image exists using the provided name and tag. If the image is not found or the
+ force option is used, the image will either be pulled, built or loaded. By default the image will be pulled
+ from Docker Hub. To build the image, provide a path value set to a directory containing a context and
+ Dockerfile. To load an image, specify load_path to provide a path to an archive file. To tag an image to a
+ repository, provide a repository path. If the name contains a repository path, it will be pushed.
+ - "NOTE: 'build' is DEPRECATED. Specifying 'build' will behave the same as 'present'."
default: present
- choices: [ "present", "absent", "build" ]
- aliases: []
- timeout:
+ choices:
+ - absent
+ - present
+ - build (DEPRECATED)
+ tag:
description:
- - Set image operation timeout
- required: false
- default: 600
- aliases: []
-requirements:
- - "python >= 2.6"
- - "docker-py"
- - "requests"
-'''
-
-EXAMPLES = '''
-Build docker image if required. Path should contains Dockerfile to build image:
+ - Used to select an image when pulling. Will be added to the image when pushing, tagging or building. Defaults to
+ 'latest' when pulling an image.
+ default: latest
+ use_tls:
+ description:
+ - "DEPRECATED. Whether to use tls to connect to the docker server. Set to 'no' when TLS will not be used. Set to
+ 'encrypt' to use TLS. And set to 'verify' to use TLS and verify that the server's certificate is valid for the
+ server. NOTE: If you specify this option, it will set the value of the tls or tls_verify parameters."
+ choices:
+ - no
+ - encrypt
+ - verify
+ default: no
+ version_added: "2.0"
-- hosts: web
- become: yes
- tasks:
- - name: check or build image
- docker_image: path="/path/to/build/dir" name="my/app" state=present
+extends_documentation_fragment:
+ - docker
-Build new version of image:
+requirements:
+ - "python >= 2.6"
+ - "docker-py >= 1.7.0"
+ - "Docker API >= 1.20"
-- hosts: web
- become: yes
- tasks:
- - name: check or build image
- docker_image: path="/path/to/build/dir" name="my/app" state=build
+authors:
+ - Pavel Antonov (@softzilla)
+ - Chris Houseknecht (@chouseknecht)
+ - James Tanner (@jctanner)
-Remove image from local docker storage:
+'''
-- hosts: web
- become: yes
- tasks:
- - name: remove image
- docker_image: name="my/app" state=absent
+EXAMPLES = '''
+- name: pull an image
+ docker_image:
+ name: pacur/centos-7
+
+- name: Tag to repository to a private registry and push it
+ docker_image:
+ name: pacur/centos-7
+ repository: registry.ansible.com/chouseknecht/centos_images
+ tag: 7.0
+
+- name: Remove image
+ docker_image:
+ state: absent
+ name: registry.ansible.com/chouseknecht/sinatra
+ tag: v1
+
+- name: Build an image ad push it to a private repo
+ docker_image:
+ path: ./sinatra
+ name: registry.ansible.com/chouseknecht/sinatra
+ tag: v1
+
+- name: Archive image
+ docker_image:
+ name: registry.ansible.com/chouseknecht/sinatra
+ tag: v1
+ archive_path: my_sinatra.tar
+
+- name: Load image from archive and push it to a private registry
+ docker_image:
+ name: registry.ansible.com/chouseknecht/sinatra
+ tag: v1
+ load_path: my_sinatra.tar
'''
-import re
-import os
-from urlparse import urlparse
+RETURN = '''
+actions:
+ description: List of actions taken by the module.
+ returned: always
+ type: list
+ sample: [
+ "Removed image myimage"
+ ]
+image:
+ description: Facts representing the current state of the image.
+ returned: always
+ type: dict
+ sample: {
+
+ }
+'''
-try:
- import json
-except ImportError:
- import simplejson as json
+from ansible.module_utils.docker_common import *
try:
- from requests.exceptions import *
- HAS_REQUESTS = True
+ from docker import auth
+ from docker import utils
except ImportError:
- HAS_REQUESTS = False
-
-try:
- import docker.client
- HAS_DOCKER_CLIENT = True
-except ImportError:
- HAS_DOCKER_CLIENT = False
-
-DEFAULT_DOCKER_API_VERSION = None
-if HAS_DOCKER_CLIENT:
- try:
- from docker.errors import APIError as DockerAPIError
- except ImportError:
- from docker.client import APIError as DockerAPIError
-
- try:
- # docker-py 1.2+
- import docker.constants
- DEFAULT_DOCKER_API_VERSION = docker.constants.DEFAULT_DOCKER_API_VERSION
- except (ImportError, AttributeError):
- # docker-py less than 1.2
- DEFAULT_DOCKER_API_VERSION = docker.client.DEFAULT_DOCKER_API_VERSION
-
-class DockerImageManager:
-
- def __init__(self, module):
- self.module = module
- self.path = self.module.params.get('path')
- self.dockerfile = self.module.params.get('dockerfile')
- self.name = self.module.params.get('name')
- self.tag = self.module.params.get('tag')
- self.nocache = self.module.params.get('nocache')
-
- # Connect to the docker server using any configured host and TLS settings.
-
- env_host = os.getenv('DOCKER_HOST')
- env_docker_verify = os.getenv('DOCKER_TLS_VERIFY')
- env_cert_path = os.getenv('DOCKER_CERT_PATH')
- env_docker_hostname = os.getenv('DOCKER_TLS_HOSTNAME')
-
- docker_url = module.params.get('docker_url')
- if not docker_url:
- if env_host:
- docker_url = env_host
+ # missing docker-py handled in docker_common
+ pass
+
+
+class ImageManager(DockerBaseClass):
+
+ def __init__(self, client, results):
+
+ super(ImageManager, self).__init__()
+
+ self.client = client
+ self.results = results
+ parameters = self.client.module.params
+ self.check_mode = self.client.check_mode
+
+ self.archive_path = parameters.get('archive_path')
+ self.container_limits = parameters.get('container_limits')
+ self.dockerfile = parameters.get('dockerfile')
+ self.force = parameters.get('force')
+ self.load_path = parameters.get('load_path')
+ self.name = parameters.get('name')
+ self.nocache = parameters.get('nocache')
+ self.path = parameters.get('path')
+ self.pull = parameters.get('pull')
+ self.repository = parameters.get('repository')
+ self.rm = parameters.get('rm')
+ self.state = parameters.get('state')
+ self.tag = parameters.get('tag')
+ self.http_timeout = parameters.get('http_timeout')
+ self.debug = parameters.get('debug')
+ self.push = False
+
+ if self.state in ['present', 'build']:
+ self.present()
+ elif self.state == 'absent':
+ self.absent()
+
+ def fail(self, msg):
+ self.client.fail(msg)
+
+ def present(self):
+ '''
+ Handles state = 'present', which includes building, loading or pulling an image,
+ depending on user provided parameters.
+
+ :returns None
+ '''
+ image = self.client.find_image(name=self.name, tag=self.tag)
+
+ if not image or self.force:
+ if self.path:
+ # Build the image
+ image_name = self.name
+ if self.tag:
+ image_name = "%s:%s" % (self.name, self.tag)
+ self.log("Building image %s" % image_name)
+ self.results['actions'].append("Built image %s from %s" % (image_name, self.path))
+ self.results['changed'] = True
+ self.push = True
+ if not self.check_mode:
+ self.results['image'] = self.build_image()
+ elif self.load_path:
+ # Load the image from an archive
+ if not os.path.isfile(self.load_path):
+ self.fail("Error loading image %s. Specified path %s does not exist." % (self.name,
+ self.load_path))
+ self.push = True
+ image_name = self.name
+ if self.tag:
+ image_name = "%s:%s" % (self.name, self.tag)
+ self.results['actions'].append("Loaded image %s from %s" % (image_name, self.load_path))
+ self.results['changed'] = True
+ if not self.check_mode:
+ self.results['image'] = self.load_image()
else:
- docker_url = 'unix://var/run/docker.sock'
-
- docker_api_version = module.params.get('docker_api_version')
+ # pull the image
+ self.results['actions'].append('Pulled image %s:%s' % (self.name, self.tag))
+ self.results['changed'] = True
+ if not self.check_mode:
+ self.results['image'] = self.client.pull_image(self.name, tag=self.tag)
+
+ if self.archive_path:
+ self.archive_image(self.name, self.tag)
+
+ if self.push and not self.repository:
+ self.push_image(self.name, self.tag)
+ elif self.repository:
+ self.tag_image(self.name, self.tag, self.repository, force=self.force)
+
+ def absent(self):
+ '''
+ Handles state = 'absent', which removes an image.
+
+ :return None
+ '''
+ image = self.client.find_image(self.name, self.tag)
+ if image:
+ name = self.name
+ if self.tag:
+ name = "%s:%s" % (self.name, self.tag)
+ if not self.check_mode:
+ try:
+ self.client.remove_image(name, force=self.force)
+ except Exception as exc:
+ self.fail("Error removing image %s - %s" % (name, str(exc)))
+
+ self.results['changed'] = True
+ self.results['actions'].append("Removed image %s" % (name))
+ self.results['image']['state'] = 'Deleted'
+
+ def archive_image(self, name, tag):
+ '''
+ Archive an image to a .tar file. Called when archive_path is passed.
+
+ :param name - name of the image. Type: str
+ :return None
+ '''
+
+ if not tag:
+ tag = "latest"
+
+ image = self.client.find_image(name=name, tag=tag)
+ if not image:
+ self.log("archive image: image %s:%s not found" % (name, tag))
+ return
+
+ image_name = "%s:%s" % (name, tag)
+ self.results['actions'].append('Archived image %s to %s' % (image_name, self.archive_path))
+ self.results['changed'] = True
+ if not self.check_mode:
+ self.log("Getting archive of image %s" % image_name)
+ try:
+ image = self.client.get_image(image_name)
+ except Exception as exc:
+ self.fail("Error getting image %s - %s" % (image_name, str(exc)))
- tls_client_cert = module.params.get('tls_client_cert', None)
- if not tls_client_cert and env_cert_path:
- tls_client_cert = os.path.join(env_cert_path, 'cert.pem')
+ try:
+ image_tar = open(self.archive_path, 'w')
+ image_tar.write(image.data)
+ image_tar.close()
+ except Exception as exc:
+ self.fail("Error writing image archive %s - %s" % (self.archive_path, str(exc)))
+
+ image = self.client.find_image(name=name, tag=tag)
+ if image:
+ self.results['image'] = image
+
+ def push_image(self, name, tag=None):
+ '''
+ If the name of the image contains a repository path, then push the image.
+
+ :param name Name of the image to push.
+ :param tag Use a specific tag.
+ :return: None
+ '''
+
+ repository = name
+ if not tag:
+ repository, tag = utils.parse_repository_tag(name)
+ registry, repo_name = auth.resolve_repository_name(repository)
+
+ if re.search('/', repository):
+ if registry:
+ config = auth.load_config()
+ if not auth.resolve_authconfig(config, registry):
+ self.fail("Error: configuration for %s not found. Try logging into %s first." % registry)
+
+ self.log("pushing image %s" % repository)
+ self.results['actions'].append("Pushed image %s to %s:%s" % (self.name, self.repository, self.tag))
+ self.results['changed'] = True
+ if not self.check_mode:
+ status = None
+ try:
+ for line in self.client.push(repository, tag=tag, stream=True):
+ line = json.loads(line)
+ self.log(line, pretty_print=True)
+ if line.get('errorDetail'):
+ raise Exception(line['errorDetail']['message'])
+ status = line.get('status')
+ except Exception as exc:
+ if re.search('unauthorized', str(exc)):
+ self.fail("Error pushing image %s: %s. Does the repository exist?" % (repository, str(exc)))
+ self.fail("Error pushing image %s: %s" % (repository, str(exc)))
+ self.results['image'] = self.client.find_image(name=repository, tag=tag)
+ if not self.results['image']:
+ self.results['image'] = dict()
+ self.results['image']['push_status'] = status
+
+ def tag_image(self, name, tag, repository, force=False):
+ '''
+ Tag an image into a repository.
+
+ :param name: name of the image. required.
+ :param tag: image tag.
+ :param repository: path to the repository. required.
+ :param force: bool. force tagging, even it image already exists with the repository path.
+ :param push: bool. push the image once it's tagged.
+ :return: None
+ '''
+ repo, repo_tag = utils.parse_repository_tag(repository)
+ image = self.client.find_image(name=repo, tag=repo_tag)
+ found = 'found' if image else 'not found'
+ self.log("image %s was %s" % (repo, found))
+ if not image or force:
+ self.log("tagging %s:%s to %s" % (name, tag, repository))
+ self.results['changed'] = True
+ self.results['actions'].append("Tagged image %s:%s to %s" % (name, tag, repository))
+ if not self.check_mode:
+ try:
+ # Finding the image does not always work, especially running a localhost registry. In those
+ # cases, if we don't set force=True, it errors.
+ image_name = name
+ if tag and not re.search(tag, name):
+ image_name = "%s:%s" % (name, tag)
+ tag_status = self.client.tag(image_name, repository, tag=tag, force=True)
+ if not tag_status:
+ raise Exception("Tag operation failed.")
+ except Exception as exc:
+ self.fail("Error: failed to tag image %s - %s" % (name, str(exc)))
+ self.results['image'] = self.client.find_image(name=repository, tag=tag)
+ self.push_image(repository, tag)
+
+ def build_image(self):
+ '''
+ Build an image
+
+ :return: image dict
+ '''
+ params = dict(
+ path=self.path,
+ tag=self.name,
+ rm=self.rm,
+ nocache=self.nocache,
+ stream=True,
+ timeout=self.http_timeout,
+ pull=self.pull,
+ forcerm=self.rm,
+ dockerfile=self.dockerfile,
+ decode=True
+ )
+ if self.tag:
+ params['tag'] = "%s:%s" % (self.name, self.tag)
+ if self.container_limits:
+ params['container_limits'] = self.container_limits,
+ for line in self.client.build(**params):
+ # line = json.loads(line)
+ self.log(line, pretty_print=True)
+ if line.get('error'):
+ if line.get('errorDetail'):
+ errorDetail = line.get('errorDetail')
+ self.fail("Error building %s - code: %s message: %s" % (self.name,
+ errorDetail.get('code'),
+ errorDetail.get('message')))
+ else:
+ self.fail("Error building %s - %s" % (self.name, line.get('error')))
+ return self.client.find_image(name=self.name, tag=self.tag)
- tls_client_key = module.params.get('tls_client_key', None)
- if not tls_client_key and env_cert_path:
- tls_client_key = os.path.join(env_cert_path, 'key.pem')
+ def load_image(self):
+ '''
+ Load an image from a .tar archive
- tls_ca_cert = module.params.get('tls_ca_cert')
- if not tls_ca_cert and env_cert_path:
- tls_ca_cert = os.path.join(env_cert_path, 'ca.pem')
+ :return: image dict
+ '''
+ try:
+ self.log("Reading image data from %s" % self.load_path)
+ image_tar = open(self.load_path, 'r')
+ image_data = image_tar.read()
+ image_tar.close()
+ except Exception as exc:
+ self.fail("Error reading image data %s - %s" % (self.load_path, str(exc)))
- tls_hostname = module.params.get('tls_hostname')
- if tls_hostname is None:
- if env_docker_hostname:
- tls_hostname = env_docker_hostname
- else:
- parsed_url = urlparse(docker_url)
- if ':' in parsed_url.netloc:
- tls_hostname = parsed_url.netloc[:parsed_url.netloc.rindex(':')]
- else:
- tls_hostname = parsed_url
- if not tls_hostname:
- tls_hostname = True
-
- # use_tls can be one of four values:
- # no: Do not use tls
- # encrypt: Use tls. We may do client auth. We will not verify the server
- # verify: Use tls. We may do client auth. We will verify the server
- # None: Only use tls if the parameters for client auth were specified
- # or tls_ca_cert (which requests verifying the server with
- # a specific ca certificate)
- use_tls = module.params.get('use_tls')
- if use_tls is None and env_docker_verify is not None:
- use_tls = 'verify'
-
- tls_config = None
- if use_tls != 'no':
- params = {}
-
- # Setup client auth
- if tls_client_cert and tls_client_key:
- params['client_cert'] = (tls_client_cert, tls_client_key)
-
- # We're allowed to verify the connection to the server
- if use_tls == 'verify' or (use_tls is None and tls_ca_cert):
- if tls_ca_cert:
- params['ca_cert'] = tls_ca_cert
- params['verify'] = True
- params['assert_hostname'] = tls_hostname
- else:
- params['verify'] = True
- params['assert_hostname'] = tls_hostname
- elif use_tls == 'encrypt':
- params['verify'] = False
-
- if params:
- # See https://github.com/docker/docker-py/blob/d39da11/docker/utils/utils.py#L279-L296
- docker_url = docker_url.replace('tcp://', 'https://')
- tls_config = docker.tls.TLSConfig(**params)
-
- self.client = docker.Client(
- base_url=docker_url,
- version=module.params.get('docker_api_version'),
- timeout=module.params.get('timeout'),
- tls=tls_config)
-
- self.changed = False
- self.log = []
- self.error_msg = None
-
- def get_log(self, as_string=True):
- return "".join(self.log) if as_string else self.log
-
- def build(self):
- stream = self.client.build(self.path, dockerfile=self.dockerfile, tag=':'.join([self.name, self.tag]), nocache=self.nocache, rm=True, stream=True)
- success_search = r'Successfully built ([0-9a-f]+)'
- image_id = None
- self.changed = True
-
- for chunk in stream:
- if not chunk:
- continue
+ try:
+ self.log("Loading image from %s" % self.load_path)
+ self.client.load_image(image_data)
+ except Exception as exc:
+ self.fail("Error loading image %s - %s" % (self.name, str(exc)))
- try:
- chunk_json = json.loads(chunk)
- except ValueError:
- continue
-
- if 'error' in chunk_json:
- self.error_msg = chunk_json['error']
- return None
-
- if 'stream' in chunk_json:
- output = chunk_json['stream']
- self.log.append(output)
- match = re.search(success_search, output)
- if match:
- image_id = match.group(1)
-
- # Just in case we skipped evaluating the JSON returned from build
- # during every iteration, add an error if the image_id was never
- # populated
- if not image_id:
- self.error_msg = 'Unknown error encountered'
-
- return image_id
-
- def has_changed(self):
- return self.changed
-
- def get_images(self):
- filtered_images = []
- images = self.client.images()
- for i in images:
- # Docker-py version >= 0.3 (Docker API >= 1.8)
- if 'RepoTags' in i:
- repotag = ':'.join([self.name, self.tag])
- if not self.name or repotag in i['RepoTags']:
- filtered_images.append(i)
- # Docker-py version < 0.3 (Docker API < 1.8)
- elif (not self.name or self.name == i['Repository']) and (not self.tag or self.tag == i['Tag']):
- filtered_images.append(i)
- return filtered_images
-
- def remove_images(self):
- images = self.get_images()
- for i in images:
- try:
- self.client.remove_image(i['Id'])
- self.changed = True
- except DockerAPIError as e:
- # image can be removed by docker if not used
- pass
+ return self.client.find_image(self.name, self.tag)
def main():
- module = AnsibleModule(
- argument_spec = dict(
- path = dict(required=False, default=None, type='path'),
- dockerfile = dict(required=False, default="Dockerfile"),
- name = dict(required=True),
- tag = dict(required=False, default="latest"),
- nocache = dict(default=False, type='bool'),
- state = dict(default='present', choices=['absent', 'present', 'build']),
- use_tls = dict(default=None, choices=['no', 'encrypt', 'verify']),
- tls_client_cert = dict(required=False, default=None, type='str'),
- tls_client_key = dict(required=False, default=None, type='str'),
- tls_ca_cert = dict(required=False, default=None, type='str'),
- tls_hostname = dict(required=False, type='str', default=None),
- docker_url = dict(),
- docker_api_version = dict(required=False,
- default=DEFAULT_DOCKER_API_VERSION,
- type='str'),
- timeout = dict(default=600, type='int'),
- )
+ argument_spec = dict(
+ archive_path=dict(type='str'),
+ container_limits=dict(type='dict'),
+ dockerfile=dict(type='str'),
+ force=dict(type='bool', default=False),
+ http_timeout=dict(type='int'),
+ load_path=dict(type='str'),
+ name=dict(type='str', required=True),
+ nocache=dict(type='str', default=False),
+ path=dict(type='str', aliases=['build_path']),
+ pull=dict(type='bool', default=True),
+ repository=dict(type='str'),
+ rm=dict(type='bool', default=True),
+ state=dict(type='str', choices=['absent', 'present'], default='present'),
+ tag=dict(type='str', default='latest'),
+ use_tls=dict(type='str', default='no', choices=['no', 'encrypt', 'verify'])
)
- if not HAS_DOCKER_CLIENT:
- module.fail_json(msg='docker-py is needed for this module')
- if not HAS_REQUESTS:
- module.fail_json(msg='requests is needed for this module')
-
- try:
- manager = DockerImageManager(module)
- state = module.params.get('state')
- failed = False
- image_id = None
- msg = ''
- do_build = False
-
- # build image if not exists
- if state == "present":
- images = manager.get_images()
- if len(images) == 0:
- do_build = True
- # build image
- elif state == "build":
- do_build = True
- # remove image or images
- elif state == "absent":
- manager.remove_images()
-
- if do_build:
- image_id = manager.build()
- if image_id:
- msg = "Image built: %s" % image_id
- else:
- failed = True
- msg = "Error: %s\nLog:%s" % (manager.error_msg, manager.get_log())
-
- module.exit_json(failed=failed, changed=manager.has_changed(), msg=msg, image_id=image_id)
-
- except SSLError as e:
- if get_platform() == "Darwin":
- # Ensure that the environment variables has been set
- if "DOCKER_HOST" not in os.environ:
- environment_error = '''
- It looks like you have not set your docker environment
- variables. Please ensure that you have set the requested
- variables as instructed when running boot2docker up. If
- they are set in .bash_profile you will need to symlink
- it to .bashrc.
- '''
- module.exit_json(failed=True, changed=manager.has_changed(), msg="SSLError: " + str(e) + environment_error)
- # If the above is true it's likely the hostname does not match
- else:
- environment_error = '''
- You may need to ignore hostname missmatches by setting
- tls_hostname=boot2docker in your role. If this does not
- resolve the issue please open an issue at
- ansible/ansible-modules-core and ping michaeljs1990
- '''
- module.exit_json(failed=True, changed=manager.has_changed(), msg="SSLError: " + str(e) + environment_error)
- # General error for non darwin users
- else:
- module.exit_json(failed=True, changed=manager.has_changed(), msg="SSLError: " + str(e))
-
- except ConnectionError as e:
- if get_platform() == "Darwin" and "DOCKER_HOST" not in os.environ:
- # Ensure that the environment variables has been set
- environment_error = '''
- It looks like you have not set your docker environment
- variables. Please ensure that you have set the requested
- variables as instructed when running boot2docker up. If
- they are set in .bash_profile you will need to symlink
- it to .bashrc.
- '''
- module.exit_json(failed=True, changed=manager.has_changed(), msg="ConnectionError: " + str(e) + environment_error)
-
- module.exit_json(failed=True, changed=manager.has_changed(), msg="ConnectionError: " + str(e))
-
- except DockerAPIError as e:
- module.exit_json(failed=True, changed=manager.has_changed(), msg="Docker API error: " + e.explanation)
-
- except RequestException as e:
- module.exit_json(failed=True, changed=manager.has_changed(), msg=repr(e))
+
+ client = AnsibleDockerClient(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ )
+
+ results = dict(
+ changed=False,
+ actions=[],
+ image={}
+ )
+
+ ImageManager(client, results)
+ client.module.exit_json(**results)
+
# import module snippets
from ansible.module_utils.basic import *
+
if __name__ == '__main__':
main()