view speedtest/docker.py @ 0:26e919a36f86 default tip

speedtest containerized dispatching software
author Jeff Hammel <k0scist@gmail.com>
date Thu, 09 Feb 2017 09:47:04 -0800
parents
children
line wrap: on
line source

# -*- coding: utf-8 -*-

"""
docker interface using the command line
"""

# See also:
# - https://docker-py.readthedocs.org/en/latest/
# - https://github.com/victorlin/crane

# imports
import argparse
import json
import os
import subprocess
import sys
import time
import uuid
from parse import parse_columns

# docker CLI conventions
NONE = '<none>'

def build(directory):
    """
    # docker build .
    Sending build context to Docker daemon 2.048 kB
    Step 1 : FROM centos:6
    ---> 3bbbf0aca359
    Successfully built 3bbbf0aca359
    """

    # ensure directory exists
    if not os.path.isdir(directory):
        raise AssertionError("Not a directory: {directory}".format(directory=directory))

    # build it!
    output = subprocess.check_output(['docker', 'build', '.'],
                                     cwd=directory)

    # return the image ID (e.g. `3bbbf0aca359`)
    # for further interaction
    # See also possibly
    # https://docker-py.readthedocs.org/en/latest/
    lines = output.strip().splitlines()
    final = lines[-1]
    assert final.startswith('Successfully built')
    return final.rsplit()[-1]


def images():
    """list images locally"""

    headers = ['REPOSITORY',
               'TAG',
               'IMAGE ID',
               'CREATED',
               'VIRTUAL SIZE']
    output = subprocess.check_output(['docker', 'images'])
    images = parse_columns(output, headers)
    for image in images:
        for key in ('REPOSITORY', 'TAG'):
            if image[key] == NONE:
                image[key] = None
    return images


def ps():
    """return `docker ps` information"""

    headers = ['CONTAINER ID',
               'IMAGE'
               'COMMAND',
               'CREATED',
               'STATUS',
               'PORTS',
               'NAMES']
    output = subprocess.check_output(['docker', 'ps'])
    return parse_columns(output, headers)


def port(container):
    """
    returns the port mapping of a container

    (Ebeneezer)# docker port 9b74817b0f30
    9200/tcp -> 0.0.0.0:32769
    9300/tcp -> 0.0.0.0:32768
    """

    output = subprocess.check_output(['docker', 'port', container])
    output = output.strip()
    port_mapping = {}
    for line in output.splitlines():
        guest_port, host_port = [i.strip()
                                 for i in line.split('->')]
        guest_port, protocol = guest_port.split('/')
        guest_port = int(guest_port)
        address, host_port = host_port.split(':')
        host_port = int(host_port)
        port_mapping[guest_port] = host_port
    return port_mapping


def inspect(container):
    """
    Return low-level information on a container or image:

    https://docs.docker.com/engine/reference/commandline/inspect/
    """

    return json.loads(subprocess.check_output(['docker', 'inspect', container]))

def remove_image(*image):
    """remove a docker image"""

    subprocess.check_output(["docker", "rmi"] + list(image))


class Container(object):
    """context manager for containers"""

    def __init__(self, image, name=None, timeout=120.):
        if name is None:
            name = uuid.uuid1()
        self.name = str(name)
        self.process = subprocess.Popen(['docker', 'run', '-P', '--name', self.name, image])
        self.timeout = timeout

        # TODO:

        # * --cidfile:
        # https://docs.docker.com/engine/reference/commandline/run/

        # * poll `self.running` until we see if the container is
        # running or the process has exited

    def running(self):
        """returns if the container is running"""

    def __enter__(self):
        return self

    def kill(self):
        """kill the running container"""
        self.process.kill()

    def __exit__(self, type, value, traceback):
        self.kill()