Sfoglia il codice sorgente

Remove fig.packages replace with real deps.

Signed-off-by: Daniel Nephin <[email protected]>
Daniel Nephin 11 anni fa
parent
commit
7fd37c89b9

+ 1 - 1
.travis.yml

@@ -3,9 +3,9 @@ python:
 - '2.6'
 - '2.6'
 - '2.7'
 - '2.7'
 install:
 install:
-- pip install .
 - pip install -r requirements.txt
 - pip install -r requirements.txt
 - pip install -r requirements-dev.txt
 - pip install -r requirements-dev.txt
+- pip install .
 script:
 script:
  - script/validate-dco
  - script/validate-dco
  - flake8 fig
  - flake8 fig

+ 9 - 4
Dockerfile

@@ -1,12 +1,17 @@
 FROM ubuntu:14.04
 FROM ubuntu:14.04
-RUN apt-get update -qq && apt-get install -qy python python-pip python-dev
-ADD requirements.txt /code/
+RUN apt-get update -qq && apt-get install -qy python python-pip python-dev git
+RUN useradd -d /home/user -m -s /bin/bash user
+
 WORKDIR /code/
 WORKDIR /code/
+
+ADD requirements.txt /code/
 RUN pip install -r requirements.txt
 RUN pip install -r requirements.txt
+
 ADD requirements-dev.txt /code/
 ADD requirements-dev.txt /code/
 RUN pip install -r requirements-dev.txt
 RUN pip install -r requirements-dev.txt
+
 ADD . /code/
 ADD . /code/
-RUN python setup.py develop
-RUN useradd -d /home/user -m -s /bin/bash user
+RUN python setup.py install
+
 RUN chown -R user /code/
 RUN chown -R user /code/
 USER user
 USER user

+ 2 - 2
fig/cli/command.py

@@ -1,13 +1,13 @@
 from __future__ import unicode_literals
 from __future__ import unicode_literals
 from __future__ import absolute_import
 from __future__ import absolute_import
-from ..packages.docker import Client
+from docker import Client
 from requests.exceptions import ConnectionError
 from requests.exceptions import ConnectionError
 import errno
 import errno
 import logging
 import logging
 import os
 import os
 import re
 import re
 import yaml
 import yaml
-from ..packages import six
+import six
 
 
 from ..project import Project
 from ..project import Project
 from ..service import ConfigError
 from ..service import ConfigError

+ 1 - 1
fig/cli/main.py

@@ -16,7 +16,7 @@ from .formatter import Formatter
 from .log_printer import LogPrinter
 from .log_printer import LogPrinter
 from .utils import yesno
 from .utils import yesno
 
 
-from ..packages.docker.errors import APIError
+from docker.errors import APIError
 from .errors import UserError
 from .errors import UserError
 from .docopt_command import NoSuchCommand
 from .docopt_command import NoSuchCommand
 
 

+ 1 - 1
fig/cli/verbose_proxy.py

@@ -4,7 +4,7 @@ from itertools import chain
 import logging
 import logging
 import pprint
 import pprint
 
 
-from fig.packages import six
+import six
 
 
 
 
 def format_call(args, kwargs):
 def format_call(args, kwargs):

+ 0 - 0
fig/packages/__init__.py


+ 0 - 20
fig/packages/docker/__init__.py

@@ -1,20 +0,0 @@
-# Copyright 2013 dotCloud inc.
-
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-
-#        http://www.apache.org/licenses/LICENSE-2.0
-
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-
-from .version import version
-
-__version__ = version
-__title__ = 'docker-py'
-
-from .client import Client # flake8: noqa

+ 0 - 7
fig/packages/docker/auth/__init__.py

@@ -1,7 +0,0 @@
-from .auth import (
-    INDEX_URL,
-    encode_header,
-    load_config,
-    resolve_authconfig,
-    resolve_repository_name
-)  # flake8: noqa

+ 0 - 167
fig/packages/docker/auth/auth.py

@@ -1,167 +0,0 @@
-# Copyright 2013 dotCloud inc.
-
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-
-#        http://www.apache.org/licenses/LICENSE-2.0
-
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-
-import base64
-import fileinput
-import json
-import os
-
-from fig.packages import six
-
-from ..utils import utils
-from .. import errors
-
-INDEX_URL = 'https://index.docker.io/v1/'
-DOCKER_CONFIG_FILENAME = '.dockercfg'
-
-
-def swap_protocol(url):
-    if url.startswith('http://'):
-        return url.replace('http://', 'https://', 1)
-    if url.startswith('https://'):
-        return url.replace('https://', 'http://', 1)
-    return url
-
-
-def expand_registry_url(hostname):
-    if hostname.startswith('http:') or hostname.startswith('https:'):
-        if '/' not in hostname[9:]:
-            hostname = hostname + '/v1/'
-        return hostname
-    if utils.ping('https://' + hostname + '/v1/_ping'):
-        return 'https://' + hostname + '/v1/'
-    return 'http://' + hostname + '/v1/'
-
-
-def resolve_repository_name(repo_name):
-    if '://' in repo_name:
-        raise errors.InvalidRepository(
-            'Repository name cannot contain a scheme ({0})'.format(repo_name))
-    parts = repo_name.split('/', 1)
-    if '.' not in parts[0] and ':' not in parts[0] and parts[0] != 'localhost':
-        # This is a docker index repo (ex: foo/bar or ubuntu)
-        return INDEX_URL, repo_name
-    if len(parts) < 2:
-        raise errors.InvalidRepository(
-            'Invalid repository name ({0})'.format(repo_name))
-
-    if 'index.docker.io' in parts[0]:
-        raise errors.InvalidRepository(
-            'Invalid repository name, try "{0}" instead'.format(parts[1]))
-
-    return expand_registry_url(parts[0]), parts[1]
-
-
-def resolve_authconfig(authconfig, registry=None):
-    """Return the authentication data from the given auth configuration for a
-    specific registry. We'll do our best to infer the correct URL for the
-    registry, trying both http and https schemes. Returns an empty dictionnary
-    if no data exists."""
-    # Default to the public index server
-    registry = registry or INDEX_URL
-
-    # Ff its not the index server there are three cases:
-    #
-    # 1. this is a full config url -> it should be used as is
-    # 2. it could be a full url, but with the wrong protocol
-    # 3. it can be the hostname optionally with a port
-    #
-    # as there is only one auth entry which is fully qualified we need to start
-    # parsing and matching
-    if '/' not in registry:
-        registry = registry + '/v1/'
-    if not registry.startswith('http:') and not registry.startswith('https:'):
-        registry = 'https://' + registry
-
-    if registry in authconfig:
-        return authconfig[registry]
-    return authconfig.get(swap_protocol(registry), None)
-
-
-def encode_auth(auth_info):
-    return base64.b64encode(auth_info.get('username', '') + b':' +
-                            auth_info.get('password', ''))
-
-
-def decode_auth(auth):
-    if isinstance(auth, six.string_types):
-        auth = auth.encode('ascii')
-    s = base64.b64decode(auth)
-    login, pwd = s.split(b':')
-    return login.decode('ascii'), pwd.decode('ascii')
-
-
-def encode_header(auth):
-    auth_json = json.dumps(auth).encode('ascii')
-    return base64.b64encode(auth_json)
-
-
-def encode_full_header(auth):
-    """ Returns the given auth block encoded for the X-Registry-Config header.
-    """
-    return encode_header({'configs': auth})
-
-
-def load_config(root=None):
-    """Loads authentication data from a Docker configuration file in the given
-    root directory."""
-    conf = {}
-    data = None
-
-    config_file = os.path.join(root or os.environ.get('HOME', '.'),
-                               DOCKER_CONFIG_FILENAME)
-
-    # First try as JSON
-    try:
-        with open(config_file) as f:
-            conf = {}
-            for registry, entry in six.iteritems(json.load(f)):
-                username, password = decode_auth(entry['auth'])
-                conf[registry] = {
-                    'username': username,
-                    'password': password,
-                    'email': entry['email'],
-                    'serveraddress': registry,
-                }
-            return conf
-    except:
-        pass
-
-    # If that fails, we assume the configuration file contains a single
-    # authentication token for the public registry in the following format:
-    #
-    # auth = AUTH_TOKEN
-    # email = [email protected]
-    try:
-        data = []
-        for line in fileinput.input(config_file):
-            data.append(line.strip().split(' = ')[1])
-        if len(data) < 2:
-            # Not enough data
-            raise errors.InvalidConfigFile(
-                'Invalid or empty configuration file!')
-
-        username, password = decode_auth(data[0])
-        conf[INDEX_URL] = {
-            'username': username,
-            'password': password,
-            'email': data[1],
-            'serveraddress': INDEX_URL,
-        }
-        return conf
-    except:
-        pass
-
-    # If all fails, return an empty config
-    return {}

+ 0 - 860
fig/packages/docker/client.py

@@ -1,860 +0,0 @@
-# Copyright 2013 dotCloud inc.
-
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-
-#        http://www.apache.org/licenses/LICENSE-2.0
-
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-
-import json
-import re
-import shlex
-import struct
-import warnings
-
-import requests
-import requests.exceptions
-from fig.packages import six
-
-from .auth import auth
-from .unixconn import unixconn
-from .utils import utils
-from . import errors
-
-if not six.PY3:
-    import websocket
-
-DEFAULT_DOCKER_API_VERSION = '1.12'
-DEFAULT_TIMEOUT_SECONDS = 60
-STREAM_HEADER_SIZE_BYTES = 8
-
-
-class Client(requests.Session):
-    def __init__(self, base_url=None, version=DEFAULT_DOCKER_API_VERSION,
-                 timeout=DEFAULT_TIMEOUT_SECONDS):
-        super(Client, self).__init__()
-        if base_url is None:
-            base_url = "http+unix://var/run/docker.sock"
-        if 'unix:///' in base_url:
-            base_url = base_url.replace('unix:/', 'unix:')
-        if base_url.startswith('unix:'):
-            base_url = "http+" + base_url
-        if base_url.startswith('tcp:'):
-            base_url = base_url.replace('tcp:', 'http:')
-        if base_url.endswith('/'):
-            base_url = base_url[:-1]
-        self.base_url = base_url
-        self._version = version
-        self._timeout = timeout
-        self._auth_configs = auth.load_config()
-
-        self.mount('http+unix://', unixconn.UnixAdapter(base_url, timeout))
-
-    def _set_request_timeout(self, kwargs):
-        """Prepare the kwargs for an HTTP request by inserting the timeout
-        parameter, if not already present."""
-        kwargs.setdefault('timeout', self._timeout)
-        return kwargs
-
-    def _post(self, url, **kwargs):
-        return self.post(url, **self._set_request_timeout(kwargs))
-
-    def _get(self, url, **kwargs):
-        return self.get(url, **self._set_request_timeout(kwargs))
-
-    def _delete(self, url, **kwargs):
-        return self.delete(url, **self._set_request_timeout(kwargs))
-
-    def _url(self, path):
-        return '{0}/v{1}{2}'.format(self.base_url, self._version, path)
-
-    def _raise_for_status(self, response, explanation=None):
-        """Raises stored :class:`APIError`, if one occurred."""
-        try:
-            response.raise_for_status()
-        except requests.exceptions.HTTPError as e:
-            raise errors.APIError(e, response, explanation=explanation)
-
-    def _result(self, response, json=False, binary=False):
-        assert not (json and binary)
-        self._raise_for_status(response)
-
-        if json:
-            return response.json()
-        if binary:
-            return response.content
-        return response.text
-
-    def _container_config(self, image, command, hostname=None, user=None,
-                          detach=False, stdin_open=False, tty=False,
-                          mem_limit=0, ports=None, environment=None, dns=None,
-                          volumes=None, volumes_from=None,
-                          network_disabled=False, entrypoint=None,
-                          cpu_shares=None, working_dir=None, domainname=None,
-                          memswap_limit=0):
-        if isinstance(command, six.string_types):
-            command = shlex.split(str(command))
-        if isinstance(environment, dict):
-            environment = [
-                '{0}={1}'.format(k, v) for k, v in environment.items()
-            ]
-
-        if isinstance(ports, list):
-            exposed_ports = {}
-            for port_definition in ports:
-                port = port_definition
-                proto = 'tcp'
-                if isinstance(port_definition, tuple):
-                    if len(port_definition) == 2:
-                        proto = port_definition[1]
-                    port = port_definition[0]
-                exposed_ports['{0}/{1}'.format(port, proto)] = {}
-            ports = exposed_ports
-
-        if isinstance(volumes, list):
-            volumes_dict = {}
-            for vol in volumes:
-                volumes_dict[vol] = {}
-            volumes = volumes_dict
-
-        if volumes_from:
-            if not isinstance(volumes_from, six.string_types):
-                volumes_from = ','.join(volumes_from)
-        else:
-            # Force None, an empty list or dict causes client.start to fail
-            volumes_from = None
-
-        attach_stdin = False
-        attach_stdout = False
-        attach_stderr = False
-        stdin_once = False
-
-        if not detach:
-            attach_stdout = True
-            attach_stderr = True
-
-            if stdin_open:
-                attach_stdin = True
-                stdin_once = True
-
-        if utils.compare_version('1.10', self._version) >= 0:
-            message = ('{0!r} parameter has no effect on create_container().'
-                       ' It has been moved to start()')
-            if dns is not None:
-                raise errors.DockerException(message.format('dns'))
-            if volumes_from is not None:
-                raise errors.DockerException(message.format('volumes_from'))
-
-        return {
-            'Hostname': hostname,
-            'Domainname': domainname,
-            'ExposedPorts': ports,
-            'User': user,
-            'Tty': tty,
-            'OpenStdin': stdin_open,
-            'StdinOnce': stdin_once,
-            'Memory': mem_limit,
-            'AttachStdin': attach_stdin,
-            'AttachStdout': attach_stdout,
-            'AttachStderr': attach_stderr,
-            'Env': environment,
-            'Cmd': command,
-            'Dns': dns,
-            'Image': image,
-            'Volumes': volumes,
-            'VolumesFrom': volumes_from,
-            'NetworkDisabled': network_disabled,
-            'Entrypoint': entrypoint,
-            'CpuShares': cpu_shares,
-            'WorkingDir': working_dir,
-            'MemorySwap': memswap_limit
-        }
-
-    def _post_json(self, url, data, **kwargs):
-        # Go <1.1 can't unserialize null to a string
-        # so we do this disgusting thing here.
-        data2 = {}
-        if data is not None:
-            for k, v in six.iteritems(data):
-                if v is not None:
-                    data2[k] = v
-
-        if 'headers' not in kwargs:
-            kwargs['headers'] = {}
-        kwargs['headers']['Content-Type'] = 'application/json'
-        return self._post(url, data=json.dumps(data2), **kwargs)
-
-    def _attach_params(self, override=None):
-        return override or {
-            'stdout': 1,
-            'stderr': 1,
-            'stream': 1
-        }
-
-    def _attach_websocket(self, container, params=None):
-        if six.PY3:
-            raise NotImplementedError("This method is not currently supported "
-                                      "under python 3")
-        url = self._url("/containers/{0}/attach/ws".format(container))
-        req = requests.Request("POST", url, params=self._attach_params(params))
-        full_url = req.prepare().url
-        full_url = full_url.replace("http://", "ws://", 1)
-        full_url = full_url.replace("https://", "wss://", 1)
-        return self._create_websocket_connection(full_url)
-
-    def _create_websocket_connection(self, url):
-        return websocket.create_connection(url)
-
-    def _get_raw_response_socket(self, response):
-        self._raise_for_status(response)
-        if six.PY3:
-            return response.raw._fp.fp.raw._sock
-        else:
-            return response.raw._fp.fp._sock
-
-    def _stream_helper(self, response):
-        """Generator for data coming from a chunked-encoded HTTP response."""
-        socket_fp = self._get_raw_response_socket(response)
-        socket_fp.setblocking(1)
-        socket = socket_fp.makefile()
-        while True:
-            # Because Docker introduced newlines at the end of chunks in v0.9,
-            # and only on some API endpoints, we have to cater for both cases.
-            size_line = socket.readline()
-            if size_line == '\r\n':
-                size_line = socket.readline()
-
-            size = int(size_line, 16)
-            if size <= 0:
-                break
-            data = socket.readline()
-            if not data:
-                break
-            yield data
-
-    def _multiplexed_buffer_helper(self, response):
-        """A generator of multiplexed data blocks read from a buffered
-        response."""
-        buf = self._result(response, binary=True)
-        walker = 0
-        while True:
-            if len(buf[walker:]) < 8:
-                break
-            _, length = struct.unpack_from('>BxxxL', buf[walker:])
-            start = walker + STREAM_HEADER_SIZE_BYTES
-            end = start + length
-            walker = end
-            yield buf[start:end]
-
-    def _multiplexed_socket_stream_helper(self, response):
-        """A generator of multiplexed data blocks coming from a response
-        socket."""
-        socket = self._get_raw_response_socket(response)
-
-        def recvall(socket, size):
-            blocks = []
-            while size > 0:
-                block = socket.recv(size)
-                if not block:
-                    return None
-
-                blocks.append(block)
-                size -= len(block)
-
-            sep = bytes() if six.PY3 else str()
-            data = sep.join(blocks)
-            return data
-
-        while True:
-            socket.settimeout(None)
-            header = recvall(socket, STREAM_HEADER_SIZE_BYTES)
-            if not header:
-                break
-            _, length = struct.unpack('>BxxxL', header)
-            if not length:
-                break
-            data = recvall(socket, length)
-            if not data:
-                break
-            yield data
-
-    def attach(self, container, stdout=True, stderr=True,
-               stream=False, logs=False):
-        if isinstance(container, dict):
-            container = container.get('Id')
-        params = {
-            'logs': logs and 1 or 0,
-            'stdout': stdout and 1 or 0,
-            'stderr': stderr and 1 or 0,
-            'stream': stream and 1 or 0,
-        }
-        u = self._url("/containers/{0}/attach".format(container))
-        response = self._post(u, params=params, stream=stream)
-
-        # Stream multi-plexing was only introduced in API v1.6. Anything before
-        # that needs old-style streaming.
-        if utils.compare_version('1.6', self._version) < 0:
-            def stream_result():
-                self._raise_for_status(response)
-                for line in response.iter_lines(chunk_size=1,
-                                                decode_unicode=True):
-                    # filter out keep-alive new lines
-                    if line:
-                        yield line
-
-            return stream_result() if stream else \
-                self._result(response, binary=True)
-
-        sep = bytes() if six.PY3 else str()
-
-        return stream and self._multiplexed_socket_stream_helper(response) or \
-            sep.join([x for x in self._multiplexed_buffer_helper(response)])
-
-    def attach_socket(self, container, params=None, ws=False):
-        if params is None:
-            params = {
-                'stdout': 1,
-                'stderr': 1,
-                'stream': 1
-            }
-
-        if ws:
-            return self._attach_websocket(container, params)
-
-        if isinstance(container, dict):
-            container = container.get('Id')
-
-        u = self._url("/containers/{0}/attach".format(container))
-        return self._get_raw_response_socket(self.post(
-            u, None, params=self._attach_params(params), stream=True))
-
-    def build(self, path=None, tag=None, quiet=False, fileobj=None,
-              nocache=False, rm=False, stream=False, timeout=None,
-              custom_context=False, encoding=None):
-        remote = context = headers = None
-        if path is None and fileobj is None:
-            raise TypeError("Either path or fileobj needs to be provided.")
-
-        if custom_context:
-            if not fileobj:
-                raise TypeError("You must specify fileobj with custom_context")
-            context = fileobj
-        elif fileobj is not None:
-            context = utils.mkbuildcontext(fileobj)
-        elif path.startswith(('http://', 'https://',
-                              'git://', 'github.com/')):
-            remote = path
-        else:
-            context = utils.tar(path)
-
-        if utils.compare_version('1.8', self._version) >= 0:
-            stream = True
-
-        u = self._url('/build')
-        params = {
-            't': tag,
-            'remote': remote,
-            'q': quiet,
-            'nocache': nocache,
-            'rm': rm
-        }
-
-        if context is not None:
-            headers = {'Content-Type': 'application/tar'}
-            if encoding:
-                headers['Content-Encoding'] = encoding
-
-        if utils.compare_version('1.9', self._version) >= 0:
-            # If we don't have any auth data so far, try reloading the config
-            # file one more time in case anything showed up in there.
-            if not self._auth_configs:
-                self._auth_configs = auth.load_config()
-
-            # Send the full auth configuration (if any exists), since the build
-            # could use any (or all) of the registries.
-            if self._auth_configs:
-                headers['X-Registry-Config'] = auth.encode_full_header(
-                    self._auth_configs
-                )
-
-        response = self._post(
-            u,
-            data=context,
-            params=params,
-            headers=headers,
-            stream=stream,
-            timeout=timeout,
-        )
-
-        if context is not None:
-            context.close()
-
-        if stream:
-            return self._stream_helper(response)
-        else:
-            output = self._result(response)
-            srch = r'Successfully built ([0-9a-f]+)'
-            match = re.search(srch, output)
-            if not match:
-                return None, output
-            return match.group(1), output
-
-    def commit(self, container, repository=None, tag=None, message=None,
-               author=None, conf=None):
-        params = {
-            'container': container,
-            'repo': repository,
-            'tag': tag,
-            'comment': message,
-            'author': author
-        }
-        u = self._url("/commit")
-        return self._result(self._post_json(u, data=conf, params=params),
-                            json=True)
-
-    def containers(self, quiet=False, all=False, trunc=True, latest=False,
-                   since=None, before=None, limit=-1, size=False):
-        params = {
-            'limit': 1 if latest else limit,
-            'all': 1 if all else 0,
-            'size': 1 if size else 0,
-            'trunc_cmd': 1 if trunc else 0,
-            'since': since,
-            'before': before
-        }
-        u = self._url("/containers/json")
-        res = self._result(self._get(u, params=params), True)
-
-        if quiet:
-            return [{'Id': x['Id']} for x in res]
-        return res
-
-    def copy(self, container, resource):
-        if isinstance(container, dict):
-            container = container.get('Id')
-        res = self._post_json(
-            self._url("/containers/{0}/copy".format(container)),
-            data={"Resource": resource},
-            stream=True
-        )
-        self._raise_for_status(res)
-        return res.raw
-
-    def create_container(self, image, command=None, hostname=None, user=None,
-                         detach=False, stdin_open=False, tty=False,
-                         mem_limit=0, ports=None, environment=None, dns=None,
-                         volumes=None, volumes_from=None,
-                         network_disabled=False, name=None, entrypoint=None,
-                         cpu_shares=None, working_dir=None, domainname=None,
-                         memswap_limit=0):
-
-        config = self._container_config(
-            image, command, hostname, user, detach, stdin_open, tty, mem_limit,
-            ports, environment, dns, volumes, volumes_from, network_disabled,
-            entrypoint, cpu_shares, working_dir, domainname, memswap_limit
-        )
-        return self.create_container_from_config(config, name)
-
-    def create_container_from_config(self, config, name=None):
-        u = self._url("/containers/create")
-        params = {
-            'name': name
-        }
-        res = self._post_json(u, data=config, params=params)
-        return self._result(res, True)
-
-    def diff(self, container):
-        if isinstance(container, dict):
-            container = container.get('Id')
-        return self._result(self._get(self._url("/containers/{0}/changes".
-                            format(container))), True)
-
-    def events(self):
-        return self._stream_helper(self.get(self._url('/events'), stream=True))
-
-    def export(self, container):
-        if isinstance(container, dict):
-            container = container.get('Id')
-        res = self._get(self._url("/containers/{0}/export".format(container)),
-                        stream=True)
-        self._raise_for_status(res)
-        return res.raw
-
-    def get_image(self, image):
-        res = self._get(self._url("/images/{0}/get".format(image)),
-                        stream=True)
-        self._raise_for_status(res)
-        return res.raw
-
-    def history(self, image):
-        res = self._get(self._url("/images/{0}/history".format(image)))
-        self._raise_for_status(res)
-        return self._result(res)
-
-    def images(self, name=None, quiet=False, all=False, viz=False):
-        if viz:
-            if utils.compare_version('1.7', self._version) >= 0:
-                raise Exception('Viz output is not supported in API >= 1.7!')
-            return self._result(self._get(self._url("images/viz")))
-        params = {
-            'filter': name,
-            'only_ids': 1 if quiet else 0,
-            'all': 1 if all else 0,
-        }
-        res = self._result(self._get(self._url("/images/json"), params=params),
-                           True)
-        if quiet:
-            return [x['Id'] for x in res]
-        return res
-
-    def import_image(self, src=None, repository=None, tag=None, image=None):
-        u = self._url("/images/create")
-        params = {
-            'repo': repository,
-            'tag': tag
-        }
-
-        if src:
-            try:
-                # XXX: this is ways not optimal but the only way
-                # for now to import tarballs through the API
-                fic = open(src)
-                data = fic.read()
-                fic.close()
-                src = "-"
-            except IOError:
-                # file does not exists or not a file (URL)
-                data = None
-            if isinstance(src, six.string_types):
-                params['fromSrc'] = src
-                return self._result(self._post(u, data=data, params=params))
-            return self._result(self._post(u, data=src, params=params))
-
-        if image:
-            params['fromImage'] = image
-            return self._result(self._post(u, data=None, params=params))
-
-        raise Exception("Must specify a src or image")
-
-    def info(self):
-        return self._result(self._get(self._url("/info")),
-                            True)
-
-    def insert(self, image, url, path):
-        if utils.compare_version('1.12', self._version) >= 0:
-            raise errors.DeprecatedMethod(
-                'insert is not available for API version >=1.12'
-            )
-        api_url = self._url("/images/" + image + "/insert")
-        params = {
-            'url': url,
-            'path': path
-        }
-        return self._result(self._post(api_url, params=params))
-
-    def inspect_container(self, container):
-        if isinstance(container, dict):
-            container = container.get('Id')
-        return self._result(
-            self._get(self._url("/containers/{0}/json".format(container))),
-            True)
-
-    def inspect_image(self, image_id):
-        return self._result(
-            self._get(self._url("/images/{0}/json".format(image_id))),
-            True
-        )
-
-    def kill(self, container, signal=None):
-        if isinstance(container, dict):
-            container = container.get('Id')
-        url = self._url("/containers/{0}/kill".format(container))
-        params = {}
-        if signal is not None:
-            params['signal'] = signal
-        res = self._post(url, params=params)
-
-        self._raise_for_status(res)
-
-    def load_image(self, data):
-        res = self._post(self._url("/images/load"), data=data)
-        self._raise_for_status(res)
-
-    def login(self, username, password=None, email=None, registry=None,
-              reauth=False):
-        # If we don't have any auth data so far, try reloading the config file
-        # one more time in case anything showed up in there.
-        if not self._auth_configs:
-            self._auth_configs = auth.load_config()
-
-        registry = registry or auth.INDEX_URL
-
-        authcfg = auth.resolve_authconfig(self._auth_configs, registry)
-        # If we found an existing auth config for this registry and username
-        # combination, we can return it immediately unless reauth is requested.
-        if authcfg and authcfg.get('username', None) == username \
-                and not reauth:
-            return authcfg
-
-        req_data = {
-            'username': username,
-            'password': password,
-            'email': email,
-            'serveraddress': registry,
-        }
-
-        response = self._post_json(self._url('/auth'), data=req_data)
-        if response.status_code == 200:
-            self._auth_configs[registry] = req_data
-        return self._result(response, json=True)
-
-    def logs(self, container, stdout=True, stderr=True, stream=False,
-             timestamps=False):
-        if isinstance(container, dict):
-            container = container.get('Id')
-        if utils.compare_version('1.11', self._version) >= 0:
-            params = {'stderr': stderr and 1 or 0,
-                      'stdout': stdout and 1 or 0,
-                      'timestamps': timestamps and 1 or 0,
-                      'follow': stream and 1 or 0}
-            url = self._url("/containers/{0}/logs".format(container))
-            res = self._get(url, params=params, stream=stream)
-            if stream:
-                return self._multiplexed_socket_stream_helper(res)
-            elif six.PY3:
-                return bytes().join(
-                    [x for x in self._multiplexed_buffer_helper(res)]
-                )
-            else:
-                return str().join(
-                    [x for x in self._multiplexed_buffer_helper(res)]
-                )
-        return self.attach(
-            container,
-            stdout=stdout,
-            stderr=stderr,
-            stream=stream,
-            logs=True
-        )
-
-    def ping(self):
-        return self._result(self._get(self._url('/_ping')))
-
-    def port(self, container, private_port):
-        if isinstance(container, dict):
-            container = container.get('Id')
-        res = self._get(self._url("/containers/{0}/json".format(container)))
-        self._raise_for_status(res)
-        json_ = res.json()
-        s_port = str(private_port)
-        h_ports = None
-
-        h_ports = json_['NetworkSettings']['Ports'].get(s_port + '/udp')
-        if h_ports is None:
-            h_ports = json_['NetworkSettings']['Ports'].get(s_port + '/tcp')
-
-        return h_ports
-
-    def pull(self, repository, tag=None, stream=False):
-        if not tag:
-            repository, tag = utils.parse_repository_tag(repository)
-        registry, repo_name = auth.resolve_repository_name(repository)
-        if repo_name.count(":") == 1:
-            repository, tag = repository.rsplit(":", 1)
-
-        params = {
-            'tag': tag,
-            'fromImage': repository
-        }
-        headers = {}
-
-        if utils.compare_version('1.5', self._version) >= 0:
-            # If we don't have any auth data so far, try reloading the config
-            # file one more time in case anything showed up in there.
-            if not self._auth_configs:
-                self._auth_configs = auth.load_config()
-            authcfg = auth.resolve_authconfig(self._auth_configs, registry)
-
-            # Do not fail here if no authentication exists for this specific
-            # registry as we can have a readonly pull. Just put the header if
-            # we can.
-            if authcfg:
-                headers['X-Registry-Auth'] = auth.encode_header(authcfg)
-
-        response = self._post(self._url('/images/create'), params=params,
-                              headers=headers, stream=stream, timeout=None)
-
-        if stream:
-            return self._stream_helper(response)
-        else:
-            return self._result(response)
-
-    def push(self, repository, stream=False):
-        registry, repo_name = auth.resolve_repository_name(repository)
-        u = self._url("/images/{0}/push".format(repository))
-        headers = {}
-
-        if utils.compare_version('1.5', self._version) >= 0:
-            # If we don't have any auth data so far, try reloading the config
-            # file one more time in case anything showed up in there.
-            if not self._auth_configs:
-                self._auth_configs = auth.load_config()
-            authcfg = auth.resolve_authconfig(self._auth_configs, registry)
-
-            # Do not fail here if no authentication exists for this specific
-            # registry as we can have a readonly pull. Just put the header if
-            # we can.
-            if authcfg:
-                headers['X-Registry-Auth'] = auth.encode_header(authcfg)
-
-            response = self._post_json(u, None, headers=headers, stream=stream)
-        else:
-            response = self._post_json(u, None, stream=stream)
-
-        return stream and self._stream_helper(response) \
-            or self._result(response)
-
-    def remove_container(self, container, v=False, link=False, force=False):
-        if isinstance(container, dict):
-            container = container.get('Id')
-        params = {'v': v, 'link': link, 'force': force}
-        res = self._delete(self._url("/containers/" + container),
-                           params=params)
-        self._raise_for_status(res)
-
-    def remove_image(self, image, force=False, noprune=False):
-        params = {'force': force, 'noprune': noprune}
-        res = self._delete(self._url("/images/" + image), params=params)
-        self._raise_for_status(res)
-
-    def restart(self, container, timeout=10):
-        if isinstance(container, dict):
-            container = container.get('Id')
-        params = {'t': timeout}
-        url = self._url("/containers/{0}/restart".format(container))
-        res = self._post(url, params=params)
-        self._raise_for_status(res)
-
-    def search(self, term):
-        return self._result(self._get(self._url("/images/search"),
-                                      params={'term': term}),
-                            True)
-
-    def start(self, container, binds=None, port_bindings=None, lxc_conf=None,
-              publish_all_ports=False, links=None, privileged=False,
-              dns=None, dns_search=None, volumes_from=None, network_mode=None):
-        if isinstance(container, dict):
-            container = container.get('Id')
-
-        if isinstance(lxc_conf, dict):
-            formatted = []
-            for k, v in six.iteritems(lxc_conf):
-                formatted.append({'Key': k, 'Value': str(v)})
-            lxc_conf = formatted
-
-        start_config = {
-            'LxcConf': lxc_conf
-        }
-        if binds:
-            start_config['Binds'] = utils.convert_volume_binds(binds)
-
-        if port_bindings:
-            start_config['PortBindings'] = utils.convert_port_bindings(
-                port_bindings
-            )
-
-        start_config['PublishAllPorts'] = publish_all_ports
-
-        if links:
-            if isinstance(links, dict):
-                links = six.iteritems(links)
-
-            formatted_links = [
-                '{0}:{1}'.format(k, v) for k, v in sorted(links)
-            ]
-
-            start_config['Links'] = formatted_links
-
-        start_config['Privileged'] = privileged
-
-        if utils.compare_version('1.10', self._version) >= 0:
-            if dns is not None:
-                start_config['Dns'] = dns
-            if volumes_from is not None:
-                if isinstance(volumes_from, six.string_types):
-                    volumes_from = volumes_from.split(',')
-                start_config['VolumesFrom'] = volumes_from
-        else:
-            warning_message = ('{0!r} parameter is discarded. It is only'
-                               ' available for API version greater or equal'
-                               ' than 1.10')
-
-            if dns is not None:
-                warnings.warn(warning_message.format('dns'),
-                              DeprecationWarning)
-            if volumes_from is not None:
-                warnings.warn(warning_message.format('volumes_from'),
-                              DeprecationWarning)
-
-        if dns_search:
-            start_config['DnsSearch'] = dns_search
-
-        if network_mode:
-            start_config['NetworkMode'] = network_mode
-
-        url = self._url("/containers/{0}/start".format(container))
-        res = self._post_json(url, data=start_config)
-        self._raise_for_status(res)
-
-    def resize(self, container, height, width):
-        if isinstance(container, dict):
-            container = container.get('Id')
-
-        params = {'h': height, 'w': width}
-        url = self._url("/containers/{0}/resize".format(container))
-        res = self._post(url, params=params)
-        self._raise_for_status(res)
-
-    def stop(self, container, timeout=10):
-        if isinstance(container, dict):
-            container = container.get('Id')
-        params = {'t': timeout}
-        url = self._url("/containers/{0}/stop".format(container))
-        res = self._post(url, params=params,
-                         timeout=max(timeout, self._timeout))
-        self._raise_for_status(res)
-
-    def tag(self, image, repository, tag=None, force=False):
-        params = {
-            'tag': tag,
-            'repo': repository,
-            'force': 1 if force else 0
-        }
-        url = self._url("/images/{0}/tag".format(image))
-        res = self._post(url, params=params)
-        self._raise_for_status(res)
-        return res.status_code == 201
-
-    def top(self, container):
-        u = self._url("/containers/{0}/top".format(container))
-        return self._result(self._get(u), True)
-
-    def version(self):
-        return self._result(self._get(self._url("/version")), True)
-
-    def wait(self, container):
-        if isinstance(container, dict):
-            container = container.get('Id')
-        url = self._url("/containers/{0}/wait".format(container))
-        res = self._post(url, timeout=None)
-        self._raise_for_status(res)
-        json_ = res.json()
-        if 'StatusCode' in json_:
-            return json_['StatusCode']
-        return -1

+ 0 - 65
fig/packages/docker/errors.py

@@ -1,65 +0,0 @@
-#    Copyright 2014 dotCloud inc.
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-#
-#        http://www.apache.org/licenses/LICENSE-2.0
-#
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-
-import requests
-
-
-class APIError(requests.exceptions.HTTPError):
-    def __init__(self, message, response, explanation=None):
-        # requests 1.2 supports response as a keyword argument, but
-        # requests 1.1 doesn't
-        super(APIError, self).__init__(message)
-        self.response = response
-
-        self.explanation = explanation
-
-        if self.explanation is None and response.content:
-            self.explanation = response.content.strip()
-
-    def __str__(self):
-        message = super(APIError, self).__str__()
-
-        if self.is_client_error():
-            message = '%s Client Error: %s' % (
-                self.response.status_code, self.response.reason)
-
-        elif self.is_server_error():
-            message = '%s Server Error: %s' % (
-                self.response.status_code, self.response.reason)
-
-        if self.explanation:
-            message = '%s ("%s")' % (message, self.explanation)
-
-        return message
-
-    def is_client_error(self):
-        return 400 <= self.response.status_code < 500
-
-    def is_server_error(self):
-        return 500 <= self.response.status_code < 600
-
-
-class DockerException(Exception):
-    pass
-
-
-class InvalidRepository(DockerException):
-    pass
-
-
-class InvalidConfigFile(DockerException):
-    pass
-
-
-class DeprecatedMethod(DockerException):
-    pass

+ 0 - 1
fig/packages/docker/unixconn/__init__.py

@@ -1 +0,0 @@
-from .unixconn import UnixAdapter  # flake8: noqa

+ 0 - 71
fig/packages/docker/unixconn/unixconn.py

@@ -1,71 +0,0 @@
-# Copyright 2013 dotCloud inc.
-
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-
-#        http://www.apache.org/licenses/LICENSE-2.0
-
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-from fig.packages import six
-
-if six.PY3:
-    import http.client as httplib
-else:
-    import httplib
-import requests.adapters
-import socket
-
-try:
-    import requests.packages.urllib3.connectionpool as connectionpool
-except ImportError:
-    import urllib3.connectionpool as connectionpool
-
-
-class UnixHTTPConnection(httplib.HTTPConnection, object):
-    def __init__(self, base_url, unix_socket, timeout=60):
-        httplib.HTTPConnection.__init__(self, 'localhost', timeout=timeout)
-        self.base_url = base_url
-        self.unix_socket = unix_socket
-        self.timeout = timeout
-
-    def connect(self):
-        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-        sock.settimeout(self.timeout)
-        sock.connect(self.base_url.replace("http+unix:/", ""))
-        self.sock = sock
-
-    def _extract_path(self, url):
-        # remove the base_url entirely..
-        return url.replace(self.base_url, "")
-
-    def request(self, method, url, **kwargs):
-        url = self._extract_path(self.unix_socket)
-        super(UnixHTTPConnection, self).request(method, url, **kwargs)
-
-
-class UnixHTTPConnectionPool(connectionpool.HTTPConnectionPool):
-    def __init__(self, base_url, socket_path, timeout=60):
-        connectionpool.HTTPConnectionPool.__init__(self, 'localhost',
-                                                   timeout=timeout)
-        self.base_url = base_url
-        self.socket_path = socket_path
-        self.timeout = timeout
-
-    def _new_conn(self):
-        return UnixHTTPConnection(self.base_url, self.socket_path,
-                                  self.timeout)
-
-
-class UnixAdapter(requests.adapters.HTTPAdapter):
-    def __init__(self, base_url, timeout=60):
-        self.base_url = base_url
-        self.timeout = timeout
-        super(UnixAdapter, self).__init__()
-
-    def get_connection(self, socket_path, proxies=None):
-        return UnixHTTPConnectionPool(self.base_url, socket_path, self.timeout)

+ 0 - 4
fig/packages/docker/utils/__init__.py

@@ -1,4 +0,0 @@
-from .utils import (
-    compare_version, convert_port_bindings, convert_volume_binds,
-    mkbuildcontext, ping, tar, parse_repository_tag
-) # flake8: noqa

+ 0 - 147
fig/packages/docker/utils/utils.py

@@ -1,147 +0,0 @@
-# Copyright 2013 dotCloud inc.
-
-#    Licensed under the Apache License, Version 2.0 (the "License");
-#    you may not use this file except in compliance with the License.
-#    You may obtain a copy of the License at
-
-#        http://www.apache.org/licenses/LICENSE-2.0
-
-#    Unless required by applicable law or agreed to in writing, software
-#    distributed under the License is distributed on an "AS IS" BASIS,
-#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#    See the License for the specific language governing permissions and
-#    limitations under the License.
-
-import io
-import tarfile
-import tempfile
-from distutils.version import StrictVersion
-
-import requests
-from fig.packages import six
-
-
-def mkbuildcontext(dockerfile):
-    f = tempfile.NamedTemporaryFile()
-    t = tarfile.open(mode='w', fileobj=f)
-    if isinstance(dockerfile, io.StringIO):
-        dfinfo = tarfile.TarInfo('Dockerfile')
-        if six.PY3:
-            raise TypeError('Please use io.BytesIO to create in-memory '
-                            'Dockerfiles with Python 3')
-        else:
-            dfinfo.size = len(dockerfile.getvalue())
-    elif isinstance(dockerfile, io.BytesIO):
-        dfinfo = tarfile.TarInfo('Dockerfile')
-        dfinfo.size = len(dockerfile.getvalue())
-    else:
-        dfinfo = t.gettarinfo(fileobj=dockerfile, arcname='Dockerfile')
-    t.addfile(dfinfo, dockerfile)
-    t.close()
-    f.seek(0)
-    return f
-
-
-def tar(path):
-    f = tempfile.NamedTemporaryFile()
-    t = tarfile.open(mode='w', fileobj=f)
-    t.add(path, arcname='.')
-    t.close()
-    f.seek(0)
-    return f
-
-
-def compare_version(v1, v2):
-    """Compare docker versions
-
-    >>> v1 = '1.9'
-    >>> v2 = '1.10'
-    >>> compare_version(v1, v2)
-    1
-    >>> compare_version(v2, v1)
-    -1
-    >>> compare_version(v2, v2)
-    0
-    """
-    s1 = StrictVersion(v1)
-    s2 = StrictVersion(v2)
-    if s1 == s2:
-        return 0
-    elif s1 > s2:
-        return -1
-    else:
-        return 1
-
-
-def ping(url):
-    try:
-        res = requests.get(url)
-    except Exception:
-        return False
-    else:
-        return res.status_code < 400
-
-
-def _convert_port_binding(binding):
-    result = {'HostIp': '', 'HostPort': ''}
-    if isinstance(binding, tuple):
-        if len(binding) == 2:
-            result['HostPort'] = binding[1]
-            result['HostIp'] = binding[0]
-        elif isinstance(binding[0], six.string_types):
-            result['HostIp'] = binding[0]
-        else:
-            result['HostPort'] = binding[0]
-    elif isinstance(binding, dict):
-        if 'HostPort' in binding:
-            result['HostPort'] = binding['HostPort']
-            if 'HostIp' in binding:
-                result['HostIp'] = binding['HostIp']
-        else:
-            raise ValueError(binding)
-    else:
-        result['HostPort'] = binding
-
-    if result['HostPort'] is None:
-        result['HostPort'] = ''
-    else:
-        result['HostPort'] = str(result['HostPort'])
-
-    return result
-
-
-def convert_port_bindings(port_bindings):
-    result = {}
-    for k, v in six.iteritems(port_bindings):
-        key = str(k)
-        if '/' not in key:
-            key = key + '/tcp'
-        if isinstance(v, list):
-            result[key] = [_convert_port_binding(binding) for binding in v]
-        else:
-            result[key] = [_convert_port_binding(v)]
-    return result
-
-
-def convert_volume_binds(binds):
-    result = []
-    for k, v in binds.items():
-        if isinstance(v, dict):
-            result.append('%s:%s:%s' % (
-                k, v['bind'], 'ro' if v.get('ro', False) else 'rw'
-            ))
-        else:
-            result.append('%s:%s:rw' % (k, v))
-    return result
-
-
-def parse_repository_tag(repo):
-    column_index = repo.rfind(':')
-    if column_index < 0:
-        return repo, None
-    tag = repo[column_index+1:]
-    slash_index = tag.find('/')
-    if slash_index < 0:
-        return repo[:column_index], tag
-
-    return repo, None

+ 0 - 1
fig/packages/docker/version.py

@@ -1 +0,0 @@
-version = "0.3.2"

+ 0 - 404
fig/packages/six.py

@@ -1,404 +0,0 @@
-"""Utilities for writing code that runs on Python 2 and 3"""
-
-# Copyright (c) 2010-2013 Benjamin Peterson
-#
-# 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 operator
-import sys
-import types
-
-__author__ = "Benjamin Peterson <[email protected]>"
-__version__ = "1.3.0"
-
-
-# True if we are running on Python 3.
-PY3 = sys.version_info[0] == 3
-
-if PY3:
-    string_types = str,
-    integer_types = int,
-    class_types = type,
-    text_type = str
-    binary_type = bytes
-
-    MAXSIZE = sys.maxsize
-else:
-    string_types = basestring,
-    integer_types = (int, long)
-    class_types = (type, types.ClassType)
-    text_type = unicode
-    binary_type = str
-
-    if sys.platform.startswith("java"):
-        # Jython always uses 32 bits.
-        MAXSIZE = int((1 << 31) - 1)
-    else:
-        # It's possible to have sizeof(long) != sizeof(Py_ssize_t).
-        class X(object):
-            def __len__(self):
-                return 1 << 31
-        try:
-            len(X())
-        except OverflowError:
-            # 32-bit
-            MAXSIZE = int((1 << 31) - 1)
-        else:
-            # 64-bit
-            MAXSIZE = int((1 << 63) - 1)
-            del X
-
-
-def _add_doc(func, doc):
-    """Add documentation to a function."""
-    func.__doc__ = doc
-
-
-def _import_module(name):
-    """Import module, returning the module after the last dot."""
-    __import__(name)
-    return sys.modules[name]
-
-
-class _LazyDescr(object):
-
-    def __init__(self, name):
-        self.name = name
-
-    def __get__(self, obj, tp):
-        result = self._resolve()
-        setattr(obj, self.name, result)
-        # This is a bit ugly, but it avoids running this again.
-        delattr(tp, self.name)
-        return result
-
-
-class MovedModule(_LazyDescr):
-
-    def __init__(self, name, old, new=None):
-        super(MovedModule, self).__init__(name)
-        if PY3:
-            if new is None:
-                new = name
-            self.mod = new
-        else:
-            self.mod = old
-
-    def _resolve(self):
-        return _import_module(self.mod)
-
-
-class MovedAttribute(_LazyDescr):
-
-    def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
-        super(MovedAttribute, self).__init__(name)
-        if PY3:
-            if new_mod is None:
-                new_mod = name
-            self.mod = new_mod
-            if new_attr is None:
-                if old_attr is None:
-                    new_attr = name
-                else:
-                    new_attr = old_attr
-            self.attr = new_attr
-        else:
-            self.mod = old_mod
-            if old_attr is None:
-                old_attr = name
-            self.attr = old_attr
-
-    def _resolve(self):
-        module = _import_module(self.mod)
-        return getattr(module, self.attr)
-
-
-
-class _MovedItems(types.ModuleType):
-    """Lazy loading of moved objects"""
-
-
-_moved_attributes = [
-    MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
-    MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
-    MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
-    MovedAttribute("map", "itertools", "builtins", "imap", "map"),
-    MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
-    MovedAttribute("reduce", "__builtin__", "functools"),
-    MovedAttribute("StringIO", "StringIO", "io"),
-    MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
-    MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
-
-    MovedModule("builtins", "__builtin__"),
-    MovedModule("configparser", "ConfigParser"),
-    MovedModule("copyreg", "copy_reg"),
-    MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
-    MovedModule("http_cookies", "Cookie", "http.cookies"),
-    MovedModule("html_entities", "htmlentitydefs", "html.entities"),
-    MovedModule("html_parser", "HTMLParser", "html.parser"),
-    MovedModule("http_client", "httplib", "http.client"),
-    MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"),
-    MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
-    MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"),
-    MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
-    MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
-    MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
-    MovedModule("cPickle", "cPickle", "pickle"),
-    MovedModule("queue", "Queue"),
-    MovedModule("reprlib", "repr"),
-    MovedModule("socketserver", "SocketServer"),
-    MovedModule("tkinter", "Tkinter"),
-    MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
-    MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
-    MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
-    MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
-    MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
-    MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
-    MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
-    MovedModule("tkinter_colorchooser", "tkColorChooser",
-                "tkinter.colorchooser"),
-    MovedModule("tkinter_commondialog", "tkCommonDialog",
-                "tkinter.commondialog"),
-    MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
-    MovedModule("tkinter_font", "tkFont", "tkinter.font"),
-    MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
-    MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
-                "tkinter.simpledialog"),
-    MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
-    MovedModule("winreg", "_winreg"),
-]
-for attr in _moved_attributes:
-    setattr(_MovedItems, attr.name, attr)
-del attr
-
-moves = sys.modules[__name__ + ".moves"] = _MovedItems("moves")
-
-
-def add_move(move):
-    """Add an item to six.moves."""
-    setattr(_MovedItems, move.name, move)
-
-
-def remove_move(name):
-    """Remove item from six.moves."""
-    try:
-        delattr(_MovedItems, name)
-    except AttributeError:
-        try:
-            del moves.__dict__[name]
-        except KeyError:
-            raise AttributeError("no such move, %r" % (name,))
-
-
-if PY3:
-    _meth_func = "__func__"
-    _meth_self = "__self__"
-
-    _func_closure = "__closure__"
-    _func_code = "__code__"
-    _func_defaults = "__defaults__"
-    _func_globals = "__globals__"
-
-    _iterkeys = "keys"
-    _itervalues = "values"
-    _iteritems = "items"
-    _iterlists = "lists"
-else:
-    _meth_func = "im_func"
-    _meth_self = "im_self"
-
-    _func_closure = "func_closure"
-    _func_code = "func_code"
-    _func_defaults = "func_defaults"
-    _func_globals = "func_globals"
-
-    _iterkeys = "iterkeys"
-    _itervalues = "itervalues"
-    _iteritems = "iteritems"
-    _iterlists = "iterlists"
-
-
-try:
-    advance_iterator = next
-except NameError:
-    def advance_iterator(it):
-        return it.next()
-next = advance_iterator
-
-
-try:
-    callable = callable
-except NameError:
-    def callable(obj):
-        return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
-
-
-if PY3:
-    def get_unbound_function(unbound):
-        return unbound
-
-    Iterator = object
-else:
-    def get_unbound_function(unbound):
-        return unbound.im_func
-
-    class Iterator(object):
-
-        def next(self):
-            return type(self).__next__(self)
-
-    callable = callable
-_add_doc(get_unbound_function,
-         """Get the function out of a possibly unbound function""")
-
-
-get_method_function = operator.attrgetter(_meth_func)
-get_method_self = operator.attrgetter(_meth_self)
-get_function_closure = operator.attrgetter(_func_closure)
-get_function_code = operator.attrgetter(_func_code)
-get_function_defaults = operator.attrgetter(_func_defaults)
-get_function_globals = operator.attrgetter(_func_globals)
-
-
-def iterkeys(d, **kw):
-    """Return an iterator over the keys of a dictionary."""
-    return iter(getattr(d, _iterkeys)(**kw))
-
-def itervalues(d, **kw):
-    """Return an iterator over the values of a dictionary."""
-    return iter(getattr(d, _itervalues)(**kw))
-
-def iteritems(d, **kw):
-    """Return an iterator over the (key, value) pairs of a dictionary."""
-    return iter(getattr(d, _iteritems)(**kw))
-
-def iterlists(d, **kw):
-    """Return an iterator over the (key, [values]) pairs of a dictionary."""
-    return iter(getattr(d, _iterlists)(**kw))
-
-
-if PY3:
-    def b(s):
-        return s.encode("latin-1")
-    def u(s):
-        return s
-    if sys.version_info[1] <= 1:
-        def int2byte(i):
-            return bytes((i,))
-    else:
-        # This is about 2x faster than the implementation above on 3.2+
-        int2byte = operator.methodcaller("to_bytes", 1, "big")
-    import io
-    StringIO = io.StringIO
-    BytesIO = io.BytesIO
-else:
-    def b(s):
-        return s
-    def u(s):
-        return unicode(s, "unicode_escape")
-    int2byte = chr
-    import StringIO
-    StringIO = BytesIO = StringIO.StringIO
-_add_doc(b, """Byte literal""")
-_add_doc(u, """Text literal""")
-
-
-if PY3:
-    import builtins
-    exec_ = getattr(builtins, "exec")
-
-
-    def reraise(tp, value, tb=None):
-        if value.__traceback__ is not tb:
-            raise value.with_traceback(tb)
-        raise value
-
-
-    print_ = getattr(builtins, "print")
-    del builtins
-
-else:
-    def exec_(_code_, _globs_=None, _locs_=None):
-        """Execute code in a namespace."""
-        if _globs_ is None:
-            frame = sys._getframe(1)
-            _globs_ = frame.f_globals
-            if _locs_ is None:
-                _locs_ = frame.f_locals
-            del frame
-        elif _locs_ is None:
-            _locs_ = _globs_
-        exec("""exec _code_ in _globs_, _locs_""")
-
-
-    exec_("""def reraise(tp, value, tb=None):
-    raise tp, value, tb
-""")
-
-
-    def print_(*args, **kwargs):
-        """The new-style print function."""
-        fp = kwargs.pop("file", sys.stdout)
-        if fp is None:
-            return
-        def write(data):
-            if not isinstance(data, basestring):
-                data = str(data)
-            fp.write(data)
-        want_unicode = False
-        sep = kwargs.pop("sep", None)
-        if sep is not None:
-            if isinstance(sep, unicode):
-                want_unicode = True
-            elif not isinstance(sep, str):
-                raise TypeError("sep must be None or a string")
-        end = kwargs.pop("end", None)
-        if end is not None:
-            if isinstance(end, unicode):
-                want_unicode = True
-            elif not isinstance(end, str):
-                raise TypeError("end must be None or a string")
-        if kwargs:
-            raise TypeError("invalid keyword arguments to print()")
-        if not want_unicode:
-            for arg in args:
-                if isinstance(arg, unicode):
-                    want_unicode = True
-                    break
-        if want_unicode:
-            newline = unicode("\n")
-            space = unicode(" ")
-        else:
-            newline = "\n"
-            space = " "
-        if sep is None:
-            sep = space
-        if end is None:
-            end = newline
-        for i, arg in enumerate(args):
-            if i:
-                write(sep)
-            write(arg)
-        write(end)
-
-_add_doc(reraise, """Reraise an exception.""")
-
-
-def with_metaclass(meta, base=object):
-    """Create a base class with a metaclass."""
-    return meta("NewBase", (base,), {})

+ 1 - 1
fig/project.py

@@ -4,7 +4,7 @@ import logging
 
 
 from .service import Service
 from .service import Service
 from .container import Container
 from .container import Container
-from .packages.docker.errors import APIError
+from docker.errors import APIError
 
 
 log = logging.getLogger(__name__)
 log = logging.getLogger(__name__)
 
 

+ 1 - 1
fig/service.py

@@ -1,7 +1,7 @@
 from __future__ import unicode_literals
 from __future__ import unicode_literals
 from __future__ import absolute_import
 from __future__ import absolute_import
 from collections import namedtuple
 from collections import namedtuple
-from .packages.docker.errors import APIError
+from docker.errors import APIError
 import logging
 import logging
 import re
 import re
 import os
 import os

+ 3 - 3
requirements-dev.txt

@@ -1,5 +1,5 @@
-mock==1.0.1
-nose==1.3.0
-pyinstaller==2.1
+mock >= 1.0.1
+nose
+git+https://github.com/pyinstaller/pyinstaller.git@12e40471c77f588ea5be352f7219c873ddaae056#egg=pyinstaller
 unittest2
 unittest2
 flake8
 flake8

+ 4 - 2
requirements.txt

@@ -1,6 +1,8 @@
-docopt==0.6.1
 PyYAML==3.10
 PyYAML==3.10
+docker-py==0.4.0
+dockerpty==0.2.3
+docopt==0.6.1
 requests==2.2.1
 requests==2.2.1
+six==1.7.3
 texttable==0.8.1
 texttable==0.8.1
 websocket-client==0.11.0
 websocket-client==0.11.0
-dockerpty==0.2.3

+ 2 - 1
script/build-osx

@@ -2,7 +2,8 @@
 set -ex
 set -ex
 rm -rf venv
 rm -rf venv
 virtualenv venv
 virtualenv venv
-venv/bin/pip install pyinstaller==2.1
+venv/bin/pip install -r requirements.txt
+venv/bin/pip install -r requirements-dev.txt
 venv/bin/pip install .
 venv/bin/pip install .
 venv/bin/pyinstaller -F bin/fig
 venv/bin/pyinstaller -F bin/fig
 dist/fig --version
 dist/fig --version

+ 15 - 4
setup.py

@@ -3,9 +3,10 @@
 from __future__ import unicode_literals
 from __future__ import unicode_literals
 from __future__ import absolute_import
 from __future__ import absolute_import
 from setuptools import setup, find_packages
 from setuptools import setup, find_packages
-import re
-import os
 import codecs
 import codecs
+import os
+import re
+import sys
 
 
 
 
 def read(*parts):
 def read(*parts):
@@ -30,11 +31,21 @@ install_requires = [
     'texttable >= 0.8.1, < 0.9',
     'texttable >= 0.8.1, < 0.9',
     'websocket-client >= 0.11.0, < 0.12',
     'websocket-client >= 0.11.0, < 0.12',
     'dockerpty >= 0.2.3, < 0.3',
     'dockerpty >= 0.2.3, < 0.3',
+    'docker-py >= 0.3.2, < 0.5',
+    'six >= 1.3.0, < 2',
 ]
 ]
 
 
+tests_require = [
+    'mock >= 1.0.1',
+    'nose',
+    'pyinstaller',
+    'flake8',
+]
+
+
+if sys.version_info < (2, 7):
+    tests_require.append('unittest2')
 
 
-with open('requirements-dev.txt') as f:
-    tests_require = f.read().splitlines()
 
 
 setup(
 setup(
     name='fig',
     name='fig',

+ 1 - 1
tests/integration/cli_test.py

@@ -1,7 +1,7 @@
 from __future__ import absolute_import
 from __future__ import absolute_import
 import sys
 import sys
 
 
-from fig.packages.six import StringIO
+from six import StringIO
 from mock import patch
 from mock import patch
 
 
 from .testcases import DockerClientTestCase
 from .testcases import DockerClientTestCase

+ 1 - 1
tests/integration/service_test.py

@@ -5,7 +5,7 @@ import os
 from fig import Service
 from fig import Service
 from fig.service import CannotBeScaledError
 from fig.service import CannotBeScaledError
 from fig.container import Container
 from fig.container import Container
-from fig.packages.docker.errors import APIError
+from docker.errors import APIError
 from .testcases import DockerClientTestCase
 from .testcases import DockerClientTestCase
 
 
 
 

+ 1 - 1
tests/integration/testcases.py

@@ -1,6 +1,6 @@
 from __future__ import unicode_literals
 from __future__ import unicode_literals
 from __future__ import absolute_import
 from __future__ import absolute_import
-from fig.packages.docker import Client
+from docker import Client
 from fig.service import Service
 from fig.service import Service
 from fig.cli.utils import docker_url
 from fig.cli.utils import docker_url
 from .. import unittest
 from .. import unittest

+ 1 - 1
tests/unit/cli_test.py

@@ -8,7 +8,7 @@ import mock
 
 
 from fig.cli import main
 from fig.cli import main
 from fig.cli.main import TopLevelCommand
 from fig.cli.main import TopLevelCommand
-from fig.packages.six import StringIO
+from six import StringIO
 
 
 
 
 class CLITestCase(unittest.TestCase):
 class CLITestCase(unittest.TestCase):

+ 1 - 1
tests/unit/container_test.py

@@ -2,7 +2,7 @@ from __future__ import unicode_literals
 from .. import unittest
 from .. import unittest
 
 
 import mock
 import mock
-from fig.packages import docker
+import docker
 
 
 from fig.container import Container
 from fig.container import Container
 
 

+ 1 - 1
tests/unit/service_test.py

@@ -5,7 +5,7 @@ import os
 from .. import unittest
 from .. import unittest
 import mock
 import mock
 
 
-from fig.packages import docker
+import docker
 
 
 from fig import Service
 from fig import Service
 from fig.service import (
 from fig.service import (

+ 1 - 1
tox.ini

@@ -2,6 +2,7 @@
 envlist = py26,py27,py32,py33,pypy
 envlist = py26,py27,py32,py33,pypy
 
 
 [testenv]
 [testenv]
+usedevelop=True
 deps =
 deps =
     -rrequirements.txt
     -rrequirements.txt
     -rrequirements-dev.txt
     -rrequirements-dev.txt
@@ -12,4 +13,3 @@ commands =
 [flake8]
 [flake8]
 # ignore line-length for now
 # ignore line-length for now
 ignore = E501,E203
 ignore = E501,E203
-exclude = fig/packages/