| |
@@ -0,0 +1,186 @@
|
| |
+ import os
|
| |
+ import random
|
| |
+ import subprocess as sp
|
| |
+
|
| |
+ from libtaskotron.logger import log
|
| |
+
|
| |
+
|
| |
+ class DockerClient(object):
|
| |
+ '''Helper class for working with the docker daemon.'''
|
| |
+
|
| |
+ def __init__(self, host_ip="127.0.0.1"):
|
| |
+ ''':param host_ip: The host IP where the docker daemon runs. We
|
| |
+ assume for now that it's running on localhost.'''
|
| |
+
|
| |
+ # FIXME: Support running commands against a remote host.
|
| |
+ self.host = host_ip
|
| |
+ self.port = random.randrange(2200, 2300)
|
| |
+ self.images = self._get_images()
|
| |
+ self.containers = self._get_containers()
|
| |
+ # We need to keep a list of the containers we create, so
|
| |
+ # we can co-exist with other users of whatever docker
|
| |
+ # daemon we might be interacting with.
|
| |
+ self.owned_containers = []
|
| |
+ self.owned_images = []
|
| |
+
|
| |
+ def _get_images(self):
|
| |
+ '''Return a list of image dicts.'''
|
| |
+
|
| |
+ template = "{{.ID}}\t{{.Repository}}"
|
| |
+ raw_output = sp.check_output(['docker',
|
| |
+ 'images',
|
| |
+ '--format',
|
| |
+ template])
|
| |
+
|
| |
+ images = raw_output.split('\n')
|
| |
+ attrs = ['id', 'name']
|
| |
+ images = [image.split('\t') for image in images[:-1]]
|
| |
+ images = [dict(zip(attrs, image)) for image in images]
|
| |
+
|
| |
+ return images
|
| |
+
|
| |
+ def _get_containers(self):
|
| |
+ '''Return a list of all containers on the host.'''
|
| |
+
|
| |
+ # This is a go template to clean up docker ps output
|
| |
+ template = "{{.ID}}\t{{.Command}}\t{{.Status}}\t{{.Ports}}\t{{.Names}}"
|
| |
+
|
| |
+ raw_output = sp.check_output(['docker',
|
| |
+ 'ps',
|
| |
+ '-a',
|
| |
+ '--format',
|
| |
+ template])
|
| |
+
|
| |
+ containers = raw_output.split('\n')
|
| |
+
|
| |
+ # Clean up the output a bit, also remove the last, empty, entry
|
| |
+ containers = [x.split('\t') for x in containers][:-1]
|
| |
+
|
| |
+ attrs = ['id', 'command', 'status', 'ports', 'names']
|
| |
+ containers = [dict(zip(attrs, container)) for container in containers]
|
| |
+
|
| |
+ return containers
|
| |
+
|
| |
+ def _find_container(self, search_term):
|
| |
+ '''Find a container based on either name or id.'''
|
| |
+
|
| |
+ for container in self.containers:
|
| |
+ if search_term in container.values():
|
| |
+ return container
|
| |
+ else:
|
| |
+ pass
|
| |
+
|
| |
+ return "No container found by {}.".format(search_term)
|
| |
+
|
| |
+ def _manipulate_container(self, identifier, action=None):
|
| |
+ '''Start or stop a container based on either it's name or id.
|
| |
+
|
| |
+ :param action: command for docker, either 'start', 'stop', or 'rm'
|
| |
+ :param identifier: a container ID or Name'''
|
| |
+
|
| |
+ if action not in ['start', 'stop', 'rm']:
|
| |
+ raise ClientError("Available actions are: start, stop, and rm. "
|
| |
+ "Could not perform: {}".format(action))
|
| |
+
|
| |
+ container = self._find_container(identifier)
|
| |
+ try:
|
| |
+ sp.check_call(['docker',
|
| |
+ action,
|
| |
+ container['names']])
|
| |
+
|
| |
+ except:
|
| |
+ log.debug("Failed to {} the container '{}'.".format(action, container['names']))
|
| |
+ raise ClientError("Could not {} the container.".format(action))
|
| |
+
|
| |
+ def build_image(self, path_to_dockerfile_dir, imgtag='taskotron-worker'):
|
| |
+ '''Run `docker build Dockerfile` against the specified path.'''
|
| |
+ sp.check_call(['docker',
|
| |
+ 'build',
|
| |
+ '-t',
|
| |
+ imgtag,
|
| |
+ path_to_dockerfile_dir])
|
| |
+ # Update our lists
|
| |
+ self.images = self._get_images()
|
| |
+ self.owned_images.append(imgtag)
|
| |
+
|
| |
+ def create_container(self,
|
| |
+ image_name='test',
|
| |
+ container_name='taskotron-worker',
|
| |
+ uuid=None,
|
| |
+ artifacts=None):
|
| |
+ '''Method to create a container from a specified image.
|
| |
+ :param image_name: the name of the built image you want to launch.
|
| |
+ '''
|
| |
+
|
| |
+ # Build the image if it's not already there
|
| |
+ image_found = False
|
| |
+ for image in self.images:
|
| |
+ if image_name == image['name']:
|
| |
+ log.debug("Image already exists, skipping image build...")
|
| |
+ image_found = True
|
| |
+
|
| |
+ if not image_found:
|
| |
+ log.debug("Image not found, building...")
|
| |
+ self.build_image(os.path.abspath(os.curdir) + '/libtaskotron/ext/docker/')
|
| |
+
|
| |
+ if uuid:
|
| |
+ container_name += "-{}".format(uuid)
|
| |
+
|
| |
+ with open (os.path.join('port'),'w') as f:
|
| |
+ f.write('%s'%self.port)
|
| |
+ # check if the port is being used to avoid port bind failure
|
| |
+ port_idle=sp.call('netstat -anp|grep -f port',shell=True)
|
| |
+ if port_idle:
|
| |
+ host_port=self.port
|
| |
+ else:
|
| |
+ host_port = random.randrange(2200, 2300)
|
| |
+ self.port = host_port
|
| |
+
|
| |
+ log.info("Container will be available on port: {}".format(host_port))
|
| |
+ print artifacts
|
| |
+ sp.check_call(['docker',
|
| |
+ 'run',
|
| |
+ '-d',
|
| |
+ '--name', container_name,
|
| |
+ '-p', '{}:22'.format(host_port),
|
| |
+ '-v', '{}:/artifacts'.format(artifacts),
|
| |
+ image_name])
|
| |
+
|
| |
+ # Update our list of all containers
|
| |
+ self.containers = self._get_containers()
|
| |
+
|
| |
+ # Update the list of owned_containers
|
| |
+ self.owned_containers.append(self._find_container(container_name))
|
| |
+
|
| |
+
|
| |
+ def start_container(self, identifier):
|
| |
+ self._manipulate_container(identifier, action='start')
|
| |
+
|
| |
+ def stop_container(self, identifier):
|
| |
+ self._manipulate_container(identifier, action='stop')
|
| |
+
|
| |
+ def rm_container(self, identifier):
|
| |
+ # Update our list of owned containers, since we're deleting it
|
| |
+ for i in xrange(len(self.owned_containers)):
|
| |
+ if self.owned_containers[i]['names'] or self.owned_containers[i]['id'] == identifier:
|
| |
+ self.owned_containers.pop(i)
|
| |
+ break
|
| |
+
|
| |
+ self._manipulate_container(identifier, action='rm')
|
| |
+
|
| |
+ def clear_all(self):
|
| |
+ '''Clean out all owned containers and images.'''
|
| |
+
|
| |
+ log.info("Clearing out all our containers...")
|
| |
+ for container in self.owned_containers:
|
| |
+ self.stop_container(container['names'])
|
| |
+ self.rm_container(container['names'])
|
| |
+
|
| |
+ log.info("Clearing out all our images...")
|
| |
+ for image in self.owned_images:
|
| |
+ sp.check_call(['docker',
|
| |
+ 'rmi',
|
| |
+ image])
|
| |
+ class ClientError(Exception):
|
| |
+ '''General exceptions we might run into using this client.'''
|
| |
+ pass
|
| |
I have modified roshi's docker code:)
Just want to have a try and lots of follow-up work need to be done,that's for sure.
test plan:
git clone https://pagure.io/task-rpmlint-ansible
runtask -d -i gzip-1.8-1.fc25 -t koji_build --docker -a x86_64 task-rpmlint-ansible/run_tests.yml
or git clone https://github.com/stefwalter/gzip-dist-git
runtask -d -i gzip -t koji_build -a x86_64 --docker gzip-dist-git/tests/test_rpm.yml