Skip to content

Instantly share code, notes, and snippets.

@retr0h
Last active March 1, 2017 01:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save retr0h/5fcd058f6512cef92cfd81f8fac534b9 to your computer and use it in GitHub Desktop.
Save retr0h/5fcd058f6512cef92cfd81f8fac534b9 to your computer and use it in GitHub Desktop.
# Copyright (c) 2015-2016 Cisco Systems, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
import collections
import io
import json
import sys
try:
import docker
except ImportError: # pragma: no cover
sys.exit('ERROR: Driver missing, install docker-py.')
from molecule import util
from molecule.driver import basedriver
class DockerDriver(basedriver.BaseDriver):
def __init__(self, molecule):
super(DockerDriver, self).__init__(molecule)
self._docker = docker.Client(
version='auto', **docker.utils.kwargs_from_env())
self._containers = self.molecule.config.config['docker']['containers']
self._provider = self._get_provider()
self._platform = self._get_platform()
self.image_tag = 'molecule_local/{}:{}'
if 'build_image' not in self.molecule.config.config['docker']:
self.molecule.config.config['docker']['build_image'] = True
@property
def name(self):
return 'docker'
@property
def instances(self):
created_containers = self._docker.containers(all=True)
created_container_names = [
container.get('Names')[0][1:].encode('utf-8')
for container in created_containers
]
for container in self._containers:
if container.get('name') in created_container_names:
container['created'] = True
else:
container['created'] = False
return self._containers
@property
def default_provider(self):
return self._provider
@property
def default_platform(self):
return self._platform
@property
def provider(self):
return self._provider
@property
def platform(self):
return self._platform
@platform.setter
def platform(self, val):
self._platform = val
@property
def valid_providers(self):
return [{'name': self.provider}]
@property
def valid_platforms(self):
return [{'name': self.platform}]
@property
def ssh_config_file(self):
return
@property
def ansible_connection_params(self):
return {'user': 'root', 'connection': 'docker'}
@property
def testinfra_args(self):
return {'connection': 'docker'}
@property
def serverspec_args(self):
return {}
def up(self, no_provision=True):
self.molecule.state.change_state('driver', self.name)
if self.molecule.config.config['docker']['build_image']:
self._build_ansible_compatible_image()
else:
self.image_tag = '{}:{}'
for container in self.instances:
privileged = container.get('privileged', False)
port_bindings = container.get('port_bindings', {})
volume_mounts = container.get('volume_mounts', [])
links = container.get('links', {})
network_mode = container.get('network_mode', '')
cap_add = container.get('cap_add', [])
cap_drop = container.get('cap_drop', [])
command = container.get('command', '')
environment = container.get('environment')
docker_host_config = self._docker.create_host_config(
privileged=privileged,
port_bindings=port_bindings,
binds=volume_mounts,
links=links,
network_mode=network_mode,
cap_add=cap_add,
cap_drop=cap_drop)
if (container['created'] is not True):
msg = ('Creating container {} '
'with base image {}:{}...').format(
container['name'], container['image'],
container['image_version'])
util.print_warn(msg)
container = self._docker.create_container(
image=self.image_tag.format(container['image'],
container['image_version']),
tty=True,
detach=False,
name=container['name'],
ports=port_bindings.keys(),
host_config=docker_host_config,
environment=environment,
command=command)
self._docker.start(container=container.get('Id'))
container['created'] = True
util.print_success('Container created.')
else:
self._docker.start(container['name'])
msg = 'Starting container {}...'.format(container['name'])
util.print_info(msg)
def destroy(self):
for container in self.instances:
if (container['created']):
msg = 'Stopping container {}...'.format(container['name'])
util.print_warn(msg)
self._docker.stop(container['name'], timeout=0)
self._docker.remove_container(container['name'])
msg = 'Removed container {}.'.format(container['name'])
util.print_success(msg)
container['created'] = False
def status(self):
Status = collections.namedtuple(
'Status', ['name', 'state', 'provider', 'ports'])
status_list = []
for container in self.instances:
name = container.get('name')
try:
d = self._docker.containers(filters={'name': name})[0]
state = d.get('Status')
ports = d.get('Ports')
except IndexError:
state = 'not_created'
ports = []
status_list.append(
Status(
name=name,
state=state,
provider=self.provider,
ports=ports))
return status_list
def conf(self, vm_name=None, ssh_config=False):
pass
def inventory_entry(self, instance):
template = '{} ansible_connection=docker\n'
return template.format(instance['name'])
def login_cmd(self, instance):
return 'docker exec -ti {} bash'
def login_args(self, instance):
return [instance]
def _get_platform(self):
return 'docker'
def _get_provider(self):
return 'docker'
def _build_ansible_compatible_image(self):
available_images = [
tag.encode('utf-8')
for image in self._docker.images()
for tag in image.get('RepoTags', [])
]
for container in self.instances:
if container.get('build_image'):
msg = ('Creating Ansible compatible '
'image of {}:{} ...').format(container['image'],
container['image_version'])
util.print_info(msg)
if 'registry' in container:
container['registry'] += '/'
else:
container['registry'] = ''
dockerfile = '''
FROM {container_image}:{container_version}
{container_environment}
RUN bash -c 'if [ -x "$(command -v apt-get)" ]; then apt-get update && apt-get install -y python sudo; fi'
RUN bash -c 'if [ -x "$(command -v yum)" ]; then yum makecache fast && yum update -y && yum install -y python sudo yum-plugin-ovl && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf; fi'
RUN bash -c 'if [ -x "$(command -v zypper)" ]; then zypper refresh && zypper update -y && zypper install -y python sudo; fi'
''' # noqa
if 'dockerfile' in container:
dockerfile = container['dockerfile']
f = io.open(dockerfile)
else:
environment = container.get('environment')
if environment:
environment = '\n'.join(
'ENV {} {}'.format(k, v)
for k, v in environment.iteritems())
else:
environment = ''
dockerfile = dockerfile.format(
container_image=container['registry'] + container['image'],
container_version=container['image_version'],
container_environment=environment)
f = io.BytesIO(dockerfile.encode('utf-8'))
container['image'] = container['registry'].replace(
'/', '_').replace(':', '_') + container['image']
tag_string = self.image_tag.format(container['image'],
container['image_version'])
errors = False
if tag_string not in available_images or 'dockerfile' in container:
import os
p = os.path.dirname(os.path.abspath(container['dockerfile']))
util.print_info('Building ansible compatible image...')
previous_line = ''
self._docker.build(dockerfile=container['dockerfile'], tag=tag_string, path=p)
if errors:
msg = 'Build failed for {}.'.format(tag_string)
util.print_error(msg)
return
else:
util.print_success('Finished building {}.'.format(
tag_string))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment