summaryrefslogtreecommitdiff
path: root/docker/api
diff options
context:
space:
mode:
authorTianon Gravi <tianon@debian.org>2015-11-08 10:31:39 -0800
committerTianon Gravi <tianon@debian.org>2015-11-08 10:31:39 -0800
commit0db60a1e341b25c36bebb5903f0ce87126d7eee4 (patch)
tree601d639621dd6d0a0301861c1279f8fcc866528c /docker/api
parent90baa554e7b2f26eb2b43bb528d82b170b505903 (diff)
Import python-docker_1.5.0.orig.tar.gz
Diffstat (limited to 'docker/api')
-rw-r--r--docker/api/__init__.py8
-rw-r--r--docker/api/build.py132
-rw-r--r--docker/api/container.py382
-rw-r--r--docker/api/daemon.py78
-rw-r--r--docker/api/exec_api.py70
-rw-r--r--docker/api/image.py271
-rw-r--r--docker/api/network.py55
-rw-r--r--docker/api/volume.py36
8 files changed, 1032 insertions, 0 deletions
diff --git a/docker/api/__init__.py b/docker/api/__init__.py
new file mode 100644
index 0000000..9e74428
--- /dev/null
+++ b/docker/api/__init__.py
@@ -0,0 +1,8 @@
+# flake8: noqa
+from .build import BuildApiMixin
+from .container import ContainerApiMixin
+from .daemon import DaemonApiMixin
+from .exec_api import ExecApiMixin
+from .image import ImageApiMixin
+from .volume import VolumeApiMixin
+from .network import NetworkApiMixin
diff --git a/docker/api/build.py b/docker/api/build.py
new file mode 100644
index 0000000..b303ba6
--- /dev/null
+++ b/docker/api/build.py
@@ -0,0 +1,132 @@
+import logging
+import os
+import re
+
+from .. import constants
+from .. import errors
+from .. import auth
+from .. import utils
+
+
+log = logging.getLogger(__name__)
+
+
+class BuildApiMixin(object):
+ def build(self, path=None, tag=None, quiet=False, fileobj=None,
+ nocache=False, rm=False, stream=False, timeout=None,
+ custom_context=False, encoding=None, pull=False,
+ forcerm=False, dockerfile=None, container_limits=None,
+ decode=False):
+ remote = context = headers = None
+ container_limits = container_limits or {}
+ if path is None and fileobj is None:
+ raise TypeError("Either path or fileobj needs to be provided.")
+
+ for key in container_limits.keys():
+ if key not in constants.CONTAINER_LIMITS_KEYS:
+ raise errors.DockerException(
+ 'Invalid container_limits key {0}'.format(key)
+ )
+
+ 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/', 'git@')):
+ remote = path
+ elif not os.path.isdir(path):
+ raise TypeError("You must specify a directory to build in path")
+ else:
+ dockerignore = os.path.join(path, '.dockerignore')
+ exclude = None
+ if os.path.exists(dockerignore):
+ with open(dockerignore, 'r') as f:
+ exclude = list(filter(bool, f.read().splitlines()))
+ context = utils.tar(path, exclude=exclude, dockerfile=dockerfile)
+
+ if utils.compare_version('1.8', self._version) >= 0:
+ stream = True
+
+ if dockerfile and utils.compare_version('1.17', self._version) < 0:
+ raise errors.InvalidVersion(
+ 'dockerfile was only introduced in API version 1.17'
+ )
+
+ if utils.compare_version('1.19', self._version) < 0:
+ pull = 1 if pull else 0
+
+ u = self._url('/build')
+ params = {
+ 't': tag,
+ 'remote': remote,
+ 'q': quiet,
+ 'nocache': nocache,
+ 'rm': rm,
+ 'forcerm': forcerm,
+ 'pull': pull,
+ 'dockerfile': dockerfile,
+ }
+ params.update(container_limits)
+
+ 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:
+ self._set_auth_headers(headers)
+
+ response = self._post(
+ u,
+ data=context,
+ params=params,
+ headers=headers,
+ stream=stream,
+ timeout=timeout,
+ )
+
+ if context is not None and not custom_context:
+ context.close()
+
+ if stream:
+ return self._stream_helper(response, decode=decode)
+ 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 _set_auth_headers(self, headers):
+ log.debug('Looking for auth config')
+
+ # 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:
+ log.debug("No auth config in memory - loading from filesystem")
+ 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:
+ log.debug(
+ 'Sending auth config ({0})'.format(
+ ', '.join(repr(k) for k in self._auth_configs.keys())
+ )
+ )
+ if headers is None:
+ headers = {}
+ if utils.compare_version('1.19', self._version) >= 0:
+ headers['X-Registry-Config'] = auth.encode_header(
+ self._auth_configs
+ )
+ else:
+ headers['X-Registry-Config'] = auth.encode_header({
+ 'configs': self._auth_configs
+ })
+ else:
+ log.debug('No auth config found')
diff --git a/docker/api/container.py b/docker/api/container.py
new file mode 100644
index 0000000..142bd0f
--- /dev/null
+++ b/docker/api/container.py
@@ -0,0 +1,382 @@
+import six
+import warnings
+
+from .. import errors
+from .. import utils
+
+
+class ContainerApiMixin(object):
+ @utils.check_resource
+ def attach(self, container, stdout=True, stderr=True,
+ stream=False, logs=False):
+ 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", container)
+ response = self._post(u, params=params, stream=stream)
+
+ return self._get_result(container, stream, response)
+
+ @utils.check_resource
+ 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)
+
+ u = self._url("/containers/{0}/attach", container)
+ return self._get_raw_response_socket(self.post(
+ u, None, params=self._attach_params(params), stream=True))
+
+ @utils.check_resource
+ 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=False, latest=False,
+ since=None, before=None, limit=-1, size=False,
+ filters=None):
+ 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
+ }
+ if filters:
+ params['filters'] = utils.convert_filters(filters)
+ u = self._url("/containers/json")
+ res = self._result(self._get(u, params=params), True)
+
+ if quiet:
+ return [{'Id': x['Id']} for x in res]
+ if trunc:
+ for x in res:
+ x['Id'] = x['Id'][:12]
+ return res
+
+ @utils.check_resource
+ def copy(self, container, resource):
+ if utils.version_gte(self._version, '1.20'):
+ warnings.warn(
+ 'Client.copy() is deprecated for API version >= 1.20, '
+ 'please use get_archive() instead',
+ DeprecationWarning
+ )
+ 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=None, 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=None, cpuset=None, host_config=None,
+ mac_address=None, labels=None, volume_driver=None):
+
+ if isinstance(volumes, six.string_types):
+ volumes = [volumes, ]
+
+ if host_config and utils.compare_version('1.15', self._version) < 0:
+ raise errors.InvalidVersion(
+ 'host_config is not supported in API < 1.15'
+ )
+
+ config = self.create_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, cpuset, host_config, mac_address, labels,
+ volume_driver
+ )
+ return self.create_container_from_config(config, name)
+
+ def create_container_config(self, *args, **kwargs):
+ return utils.create_container_config(self._version, *args, **kwargs)
+
+ 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 create_host_config(self, *args, **kwargs):
+ if not kwargs:
+ kwargs = {}
+ if 'version' in kwargs:
+ raise TypeError(
+ "create_host_config() got an unexpected "
+ "keyword argument 'version'"
+ )
+ kwargs['version'] = self._version
+ return utils.create_host_config(*args, **kwargs)
+
+ @utils.check_resource
+ def diff(self, container):
+ return self._result(
+ self._get(self._url("/containers/{0}/changes", container)), True
+ )
+
+ @utils.check_resource
+ def export(self, container):
+ res = self._get(
+ self._url("/containers/{0}/export", container), stream=True
+ )
+ self._raise_for_status(res)
+ return res.raw
+
+ @utils.check_resource
+ @utils.minimum_version('1.20')
+ def get_archive(self, container, path):
+ params = {
+ 'path': path
+ }
+ url = self._url('/containers/{0}/archive', container)
+ res = self._get(url, params=params, stream=True)
+ self._raise_for_status(res)
+ encoded_stat = res.headers.get('x-docker-container-path-stat')
+ return (
+ res.raw,
+ utils.decode_json_header(encoded_stat) if encoded_stat else None
+ )
+
+ @utils.check_resource
+ def inspect_container(self, container):
+ return self._result(
+ self._get(self._url("/containers/{0}/json", container)), True
+ )
+
+ @utils.check_resource
+ def kill(self, container, signal=None):
+ url = self._url("/containers/{0}/kill", container)
+ params = {}
+ if signal is not None:
+ params['signal'] = signal
+ res = self._post(url, params=params)
+
+ self._raise_for_status(res)
+
+ @utils.check_resource
+ def logs(self, container, stdout=True, stderr=True, stream=False,
+ timestamps=False, tail='all'):
+ 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,
+ }
+ if utils.compare_version('1.13', self._version) >= 0:
+ if tail != 'all' and (not isinstance(tail, int) or tail <= 0):
+ tail = 'all'
+ params['tail'] = tail
+ url = self._url("/containers/{0}/logs", container)
+ res = self._get(url, params=params, stream=stream)
+ return self._get_result(container, stream, res)
+ return self.attach(
+ container,
+ stdout=stdout,
+ stderr=stderr,
+ stream=stream,
+ logs=True
+ )
+
+ @utils.check_resource
+ def pause(self, container):
+ url = self._url('/containers/{0}/pause', container)
+ res = self._post(url)
+ self._raise_for_status(res)
+
+ @utils.check_resource
+ def port(self, container, private_port):
+ res = self._get(self._url("/containers/{0}/json", container))
+ self._raise_for_status(res)
+ json_ = res.json()
+ private_port = str(private_port)
+ h_ports = None
+
+ # Port settings is None when the container is running with
+ # network_mode=host.
+ port_settings = json_.get('NetworkSettings', {}).get('Ports')
+ if port_settings is None:
+ return None
+
+ if '/' in private_port:
+ return port_settings.get(private_port)
+
+ h_ports = port_settings.get(private_port + '/tcp')
+ if h_ports is None:
+ h_ports = port_settings.get(private_port + '/udp')
+
+ return h_ports
+
+ @utils.check_resource
+ @utils.minimum_version('1.20')
+ def put_archive(self, container, path, data):
+ params = {'path': path}
+ url = self._url('/containers/{0}/archive', container)
+ res = self._put(url, params=params, data=data)
+ self._raise_for_status(res)
+ return res.status_code == 200
+
+ @utils.check_resource
+ def remove_container(self, container, v=False, link=False, force=False):
+ params = {'v': v, 'link': link, 'force': force}
+ res = self._delete(
+ self._url("/containers/{0}", container), params=params
+ )
+ self._raise_for_status(res)
+
+ @utils.minimum_version('1.17')
+ @utils.check_resource
+ def rename(self, container, name):
+ url = self._url("/containers/{0}/rename", container)
+ params = {'name': name}
+ res = self._post(url, params=params)
+ self._raise_for_status(res)
+
+ @utils.check_resource
+ def resize(self, container, height, width):
+ params = {'h': height, 'w': width}
+ url = self._url("/containers/{0}/resize", container)
+ res = self._post(url, params=params)
+ self._raise_for_status(res)
+
+ @utils.check_resource
+ def restart(self, container, timeout=10):
+ params = {'t': timeout}
+ url = self._url("/containers/{0}/restart", container)
+ res = self._post(url, params=params)
+ self._raise_for_status(res)
+
+ @utils.check_resource
+ def start(self, container, binds=None, port_bindings=None, lxc_conf=None,
+ publish_all_ports=None, links=None, privileged=None,
+ dns=None, dns_search=None, volumes_from=None, network_mode=None,
+ restart_policy=None, cap_add=None, cap_drop=None, devices=None,
+ extra_hosts=None, read_only=None, pid_mode=None, ipc_mode=None,
+ security_opt=None, ulimits=None):
+
+ if utils.compare_version('1.10', self._version) < 0:
+ if dns is not None:
+ raise errors.InvalidVersion(
+ 'dns is only supported for API version >= 1.10'
+ )
+ if volumes_from is not None:
+ raise errors.InvalidVersion(
+ 'volumes_from is only supported for API version >= 1.10'
+ )
+
+ if utils.compare_version('1.15', self._version) < 0:
+ if security_opt is not None:
+ raise errors.InvalidVersion(
+ 'security_opt is only supported for API version >= 1.15'
+ )
+ if ipc_mode:
+ raise errors.InvalidVersion(
+ 'ipc_mode is only supported for API version >= 1.15'
+ )
+
+ if utils.compare_version('1.17', self._version) < 0:
+ if read_only is not None:
+ raise errors.InvalidVersion(
+ 'read_only is only supported for API version >= 1.17'
+ )
+ if pid_mode is not None:
+ raise errors.InvalidVersion(
+ 'pid_mode is only supported for API version >= 1.17'
+ )
+
+ if utils.compare_version('1.18', self._version) < 0:
+ if ulimits is not None:
+ raise errors.InvalidVersion(
+ 'ulimits is only supported for API version >= 1.18'
+ )
+
+ start_config_kwargs = dict(
+ binds=binds, port_bindings=port_bindings, lxc_conf=lxc_conf,
+ publish_all_ports=publish_all_ports, links=links, dns=dns,
+ privileged=privileged, dns_search=dns_search, cap_add=cap_add,
+ cap_drop=cap_drop, volumes_from=volumes_from, devices=devices,
+ network_mode=network_mode, restart_policy=restart_policy,
+ extra_hosts=extra_hosts, read_only=read_only, pid_mode=pid_mode,
+ ipc_mode=ipc_mode, security_opt=security_opt, ulimits=ulimits
+ )
+ start_config = None
+
+ if any(v is not None for v in start_config_kwargs.values()):
+ if utils.compare_version('1.15', self._version) > 0:
+ warnings.warn(
+ 'Passing host config parameters in start() is deprecated. '
+ 'Please use host_config in create_container instead!',
+ DeprecationWarning
+ )
+ start_config = self.create_host_config(**start_config_kwargs)
+
+ url = self._url("/containers/{0}/start", container)
+ res = self._post_json(url, data=start_config)
+ self._raise_for_status(res)
+
+ @utils.minimum_version('1.17')
+ @utils.check_resource
+ def stats(self, container, decode=None):
+ url = self._url("/containers/{0}/stats", container)
+ return self._stream_helper(self._get(url, stream=True), decode=decode)
+
+ @utils.check_resource
+ def stop(self, container, timeout=10):
+ params = {'t': timeout}
+ url = self._url("/containers/{0}/stop", container)
+
+ res = self._post(url, params=params,
+ timeout=(timeout + (self.timeout or 0)))
+ self._raise_for_status(res)
+
+ @utils.check_resource
+ def top(self, container, ps_args=None):
+ u = self._url("/containers/{0}/top", container)
+ params = {}
+ if ps_args is not None:
+ params['ps_args'] = ps_args
+ return self._result(self._get(u, params=params), True)
+
+ @utils.check_resource
+ def unpause(self, container):
+ url = self._url('/containers/{0}/unpause', container)
+ res = self._post(url)
+ self._raise_for_status(res)
+
+ @utils.check_resource
+ def wait(self, container, timeout=None):
+ url = self._url("/containers/{0}/wait", container)
+ res = self._post(url, timeout=timeout)
+ self._raise_for_status(res)
+ json_ = res.json()
+ if 'StatusCode' in json_:
+ return json_['StatusCode']
+ return -1
diff --git a/docker/api/daemon.py b/docker/api/daemon.py
new file mode 100644
index 0000000..a149e5e
--- /dev/null
+++ b/docker/api/daemon.py
@@ -0,0 +1,78 @@
+import os
+import warnings
+from datetime import datetime
+
+from ..auth import auth
+from ..constants import INSECURE_REGISTRY_DEPRECATION_WARNING
+from ..utils import utils
+
+
+class DaemonApiMixin(object):
+ def events(self, since=None, until=None, filters=None, decode=None):
+ if isinstance(since, datetime):
+ since = utils.datetime_to_timestamp(since)
+
+ if isinstance(until, datetime):
+ until = utils.datetime_to_timestamp(until)
+
+ if filters:
+ filters = utils.convert_filters(filters)
+
+ params = {
+ 'since': since,
+ 'until': until,
+ 'filters': filters
+ }
+
+ return self._stream_helper(
+ self.get(self._url('/events'), params=params, stream=True),
+ decode=decode
+ )
+
+ def info(self):
+ return self._result(self._get(self._url("/info")), True)
+
+ def login(self, username, password=None, email=None, registry=None,
+ reauth=False, insecure_registry=False, dockercfg_path=None):
+ if insecure_registry:
+ warnings.warn(
+ INSECURE_REGISTRY_DEPRECATION_WARNING.format('login()'),
+ DeprecationWarning
+ )
+
+ # 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 dockercfg_path is passed check to see if the config file exists,
+ # if so load that config.
+ if dockercfg_path and os.path.exists(dockercfg_path):
+ self._auth_configs = auth.load_config(dockercfg_path)
+ elif 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 ping(self):
+ return self._result(self._get(self._url('/_ping')))
+
+ def version(self, api_version=True):
+ url = self._url("/version", versioned_api=api_version)
+ return self._result(self._get(url), json=True)
diff --git a/docker/api/exec_api.py b/docker/api/exec_api.py
new file mode 100644
index 0000000..c66b9dd
--- /dev/null
+++ b/docker/api/exec_api.py
@@ -0,0 +1,70 @@
+import shlex
+
+import six
+
+from .. import errors
+from .. import utils
+
+
+class ExecApiMixin(object):
+ @utils.minimum_version('1.15')
+ @utils.check_resource
+ def exec_create(self, container, cmd, stdout=True, stderr=True, tty=False,
+ privileged=False, user=''):
+ if privileged and utils.compare_version('1.19', self._version) < 0:
+ raise errors.InvalidVersion(
+ 'Privileged exec is not supported in API < 1.19'
+ )
+ if user and utils.compare_version('1.19', self._version) < 0:
+ raise errors.InvalidVersion(
+ 'User-specific exec is not supported in API < 1.19'
+ )
+ if isinstance(cmd, six.string_types):
+ cmd = shlex.split(str(cmd))
+
+ data = {
+ 'Container': container,
+ 'User': user,
+ 'Privileged': privileged,
+ 'Tty': tty,
+ 'AttachStdin': False,
+ 'AttachStdout': stdout,
+ 'AttachStderr': stderr,
+ 'Cmd': cmd
+ }
+
+ url = self._url('/containers/{0}/exec', container)
+ res = self._post_json(url, data=data)
+ return self._result(res, True)
+
+ @utils.minimum_version('1.16')
+ def exec_inspect(self, exec_id):
+ if isinstance(exec_id, dict):
+ exec_id = exec_id.get('Id')
+ res = self._get(self._url("/exec/{0}/json", exec_id))
+ return self._result(res, True)
+
+ @utils.minimum_version('1.15')
+ def exec_resize(self, exec_id, height=None, width=None):
+ if isinstance(exec_id, dict):
+ exec_id = exec_id.get('Id')
+
+ params = {'h': height, 'w': width}
+ url = self._url("/exec/{0}/resize", exec_id)
+ res = self._post(url, params=params)
+ self._raise_for_status(res)
+
+ @utils.minimum_version('1.15')
+ def exec_start(self, exec_id, detach=False, tty=False, stream=False):
+ if isinstance(exec_id, dict):
+ exec_id = exec_id.get('Id')
+
+ data = {
+ 'Tty': tty,
+ 'Detach': detach
+ }
+
+ res = self._post_json(
+ self._url('/exec/{0}/start', exec_id), data=data, stream=stream
+ )
+ return self._get_result_tty(stream, res, tty)
diff --git a/docker/api/image.py b/docker/api/image.py
new file mode 100644
index 0000000..f891e21
--- /dev/null
+++ b/docker/api/image.py
@@ -0,0 +1,271 @@
+import logging
+import six
+import warnings
+
+from ..auth import auth
+from ..constants import INSECURE_REGISTRY_DEPRECATION_WARNING
+from .. import utils
+from .. import errors
+
+log = logging.getLogger(__name__)
+
+
+class ImageApiMixin(object):
+
+ @utils.check_resource
+ def get_image(self, image):
+ res = self._get(self._url("/images/{0}/get", image), stream=True)
+ self._raise_for_status(res)
+ return res.raw
+
+ @utils.check_resource
+ def history(self, image):
+ res = self._get(self._url("/images/{0}/history", image))
+ return self._result(res, True)
+
+ def images(self, name=None, quiet=False, all=False, viz=False,
+ filters=None):
+ 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,
+ }
+ if filters:
+ params['filters'] = utils.convert_filters(filters)
+ 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):
+ if src:
+ if isinstance(src, six.string_types):
+ try:
+ result = self.import_image_from_file(
+ src, repository=repository, tag=tag)
+ except IOError:
+ result = self.import_image_from_url(
+ src, repository=repository, tag=tag)
+ else:
+ result = self.import_image_from_data(
+ src, repository=repository, tag=tag)
+ elif image:
+ result = self.import_image_from_image(
+ image, repository=repository, tag=tag)
+ else:
+ raise Exception("Must specify a src or image")
+
+ return result
+
+ def import_image_from_data(self, data, repository=None, tag=None):
+ u = self._url("/images/create")
+ params = {
+ 'fromSrc': '-',
+ 'repo': repository,
+ 'tag': tag
+ }
+ headers = {
+ 'Content-Type': 'application/tar',
+ }
+ return self._result(
+ self._post(u, data=data, params=params, headers=headers))
+
+ def import_image_from_file(self, filename, repository=None, tag=None):
+ u = self._url("/images/create")
+ params = {
+ 'fromSrc': '-',
+ 'repo': repository,
+ 'tag': tag
+ }
+ headers = {
+ 'Content-Type': 'application/tar',
+ }
+ with open(filename, 'rb') as f:
+ return self._result(
+ self._post(u, data=f, params=params, headers=headers,
+ timeout=None))
+
+ def import_image_from_stream(self, stream, repository=None, tag=None):
+ u = self._url("/images/create")
+ params = {
+ 'fromSrc': '-',
+ 'repo': repository,
+ 'tag': tag
+ }
+ headers = {
+ 'Content-Type': 'application/tar',
+ 'Transfer-Encoding': 'chunked',
+ }
+ return self._result(
+ self._post(u, data=stream, params=params, headers=headers))
+
+ def import_image_from_url(self, url, repository=None, tag=None):
+ u = self._url("/images/create")
+ params = {
+ 'fromSrc': url,
+ 'repo': repository,
+ 'tag': tag
+ }
+ return self._result(
+ self._post(u, data=None, params=params))
+
+ def import_image_from_image(self, image, repository=None, tag=None):
+ u = self._url("/images/create")
+ params = {
+ 'fromImage': image,
+ 'repo': repository,
+ 'tag': tag
+ }
+ return self._result(
+ self._post(u, data=None, params=params))
+
+ @utils.check_resource
+ 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/{0}/insert", image)
+ params = {
+ 'url': url,
+ 'path': path
+ }
+ return self._result(self._post(api_url, params=params))
+
+ @utils.check_resource
+ def inspect_image(self, image):
+ return self._result(
+ self._get(self._url("/images/{0}/json", image)), True
+ )
+
+ def load_image(self, data):
+ res = self._post(self._url("/images/load"), data=data)
+ self._raise_for_status(res)
+
+ def pull(self, repository, tag=None, stream=False,
+ insecure_registry=False, auth_config=None):
+ if insecure_registry:
+ warnings.warn(
+ INSECURE_REGISTRY_DEPRECATION_WARNING.format('pull()'),
+ DeprecationWarning
+ )
+
+ 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 auth_config is None:
+ log.debug('Looking for auth config')
+ if not self._auth_configs:
+ log.debug(
+ "No auth config in memory - loading from filesystem")
+ 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:
+ log.debug('Found auth config')
+ # auth_config needs to be a dict in the format used by
+ # auth.py username , password, serveraddress, email
+ headers['X-Registry-Auth'] = auth.encode_header(
+ authcfg
+ )
+ else:
+ log.debug('No auth config found')
+ else:
+ log.debug('Sending supplied auth config')
+ headers['X-Registry-Auth'] = auth.encode_header(auth_config)
+
+ response = self._post(
+ self._url('/images/create'), params=params, headers=headers,
+ stream=stream, timeout=None
+ )
+
+ self._raise_for_status(response)
+
+ if stream:
+ return self._stream_helper(response)
+
+ return self._result(response)
+
+ def push(self, repository, tag=None, stream=False,
+ insecure_registry=False):
+ if insecure_registry:
+ warnings.warn(
+ INSECURE_REGISTRY_DEPRECATION_WARNING.format('push()'),
+ DeprecationWarning
+ )
+
+ if not tag:
+ repository, tag = utils.parse_repository_tag(repository)
+ registry, repo_name = auth.resolve_repository_name(repository)
+ u = self._url("/images/{0}/push", repository)
+ params = {
+ 'tag': tag
+ }
+ 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, params=params
+ )
+
+ self._raise_for_status(response)
+
+ if stream:
+ return self._stream_helper(response)
+
+ return self._result(response)
+
+ @utils.check_resource
+ def remove_image(self, image, force=False, noprune=False):
+ params = {'force': force, 'noprune': noprune}
+ res = self._delete(self._url("/images/{0}", image), params=params)
+ self._raise_for_status(res)
+
+ def search(self, term):
+ return self._result(
+ self._get(self._url("/images/search"), params={'term': term}),
+ True
+ )
+
+ @utils.check_resource
+ 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", image)
+ res = self._post(url, params=params)
+ self._raise_for_status(res)
+ return res.status_code == 201
diff --git a/docker/api/network.py b/docker/api/network.py
new file mode 100644
index 0000000..2dea679
--- /dev/null
+++ b/docker/api/network.py
@@ -0,0 +1,55 @@
+import json
+
+from ..utils import check_resource, minimum_version
+
+
+class NetworkApiMixin(object):
+ @minimum_version('1.21')
+ def networks(self, names=None, ids=None):
+ filters = {}
+ if names:
+ filters['name'] = names
+ if ids:
+ filters['id'] = ids
+
+ params = {'filters': json.dumps(filters)}
+
+ url = self._url("/networks")
+ res = self._get(url, params=params)
+ return self._result(res, json=True)
+
+ @minimum_version('1.21')
+ def create_network(self, name, driver=None):
+ data = {
+ 'name': name,
+ 'driver': driver,
+ }
+ url = self._url("/networks/create")
+ res = self._post_json(url, data=data)
+ return self._result(res, json=True)
+
+ @minimum_version('1.21')
+ def remove_network(self, net_id):
+ url = self._url("/networks/{0}", net_id)
+ res = self._delete(url)
+ self._raise_for_status(res)
+
+ @minimum_version('1.21')
+ def inspect_network(self, net_id):
+ url = self._url("/networks/{0}", net_id)
+ res = self._get(url)
+ return self._result(res, json=True)
+
+ @check_resource
+ @minimum_version('1.21')
+ def connect_container_to_network(self, container, net_id):
+ data = {"container": container}
+ url = self._url("/networks/{0}/connect", net_id)
+ self._post_json(url, data=data)
+
+ @check_resource
+ @minimum_version('1.21')
+ def disconnect_container_from_network(self, container, net_id):
+ data = {"container": container}
+ url = self._url("/networks/{0}/disconnect", net_id)
+ self._post_json(url, data=data)
diff --git a/docker/api/volume.py b/docker/api/volume.py
new file mode 100644
index 0000000..e9e7127
--- /dev/null
+++ b/docker/api/volume.py
@@ -0,0 +1,36 @@
+from .. import utils
+
+
+class VolumeApiMixin(object):
+ @utils.minimum_version('1.21')
+ def volumes(self, filters=None):
+ params = {
+ 'filter': utils.convert_filters(filters) if filters else None
+ }
+ url = self._url('/volumes')
+ return self._result(self._get(url, params=params), True)
+
+ @utils.minimum_version('1.21')
+ def create_volume(self, name, driver=None, driver_opts=None):
+ url = self._url('/volumes')
+ if driver_opts is not None and not isinstance(driver_opts, dict):
+ raise TypeError('driver_opts must be a dictionary')
+
+ data = {
+ 'Name': name,
+ 'Driver': driver,
+ 'DriverOpts': driver_opts,
+ }
+ return self._result(self._post_json(url, data=data), True)
+
+ @utils.minimum_version('1.21')
+ def inspect_volume(self, name):
+ url = self._url('/volumes/{0}', name)
+ return self._result(self._get(url), True)
+
+ @utils.minimum_version('1.21')
+ def remove_volume(self, name):
+ url = self._url('/volumes/{0}', name)
+ resp = self._delete(url)
+ self._raise_for_status(resp)
+ return True