summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoffrey F <joffrey@docker.com>2015-09-10 16:01:14 -0700
committerJoffrey F <joffrey@docker.com>2015-09-11 16:19:29 -0700
commitc735cc69a02f6a34417a24d37a7934bc235bc99e (patch)
treea74f3459647457dd06f7aa52d74662199da9ea65
parentde5aab87b419ce24c7e3e84225e9a06b9e880300 (diff)
downloaddocker-py-c735cc69a02f6a34417a24d37a7934bc235bc99e.tar.gz
Split different APIs into several mixins for clarity / easier maintenance
Signed-off-by: Joffrey F <joffrey@docker.com>
-rw-r--r--docker/api/__init__.py6
-rw-r--r--docker/api/build.py132
-rw-r--r--docker/api/container.py352
-rw-r--r--docker/api/daemon.py78
-rw-r--r--docker/api/exec_api.py76
-rw-r--r--docker/api/image.py271
-rw-r--r--docker/client.py881
-rw-r--r--tests/test.py4
8 files changed, 926 insertions, 874 deletions
diff --git a/docker/api/__init__.py b/docker/api/__init__.py
new file mode 100644
index 0000000..836f07e
--- /dev/null
+++ b/docker/api/__init__.py
@@ -0,0 +1,6 @@
+# flake8: noqa
+from .build import BuildApiMixin
+from .container import ContainerApiMixin
+from .daemon import DaemonApiMixin
+from .exec_api import ExecApiMixin
+from .image import ImageApiMixin
diff --git a/docker/api/build.py b/docker/api/build.py
new file mode 100644
index 0000000..99f72b0
--- /dev/null
+++ b/docker/api/build.py
@@ -0,0 +1,132 @@
+import logging
+import os
+import re
+
+from .. import constants
+from .. import errors
+from ..auth import auth
+from ..utils 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(self, 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..f90e8a1
--- /dev/null
+++ b/docker/api/container.py
@@ -0,0 +1,352 @@
+import six
+import warnings
+
+from .. import errors
+from ..utils import utils, check_resource
+
+
+class ContainerApiMixin(object):
+ @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)
+
+ @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))
+
+ @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
+
+ @check_resource
+ def copy(self, container, resource):
+ 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)
+
+ @check_resource
+ def diff(self, container):
+ return self._result(
+ self._get(self._url("/containers/{0}/changes", container)), True
+ )
+
+ @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
+
+ @check_resource
+ def inspect_container(self, container):
+ return self._result(
+ self._get(self._url("/containers/{0}/json", container)), True
+ )
+
+ @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)
+
+ @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
+ )
+
+ @check_resource
+ def pause(self, container):
+ url = self._url('/containers/{0}/pause', container)
+ res = self._post(url)
+ self._raise_for_status(res)
+
+ @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()
+ s_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
+
+ h_ports = port_settings.get(s_port + '/udp')
+ if h_ports is None:
+ h_ports = port_settings.get(s_port + '/tcp')
+
+ return h_ports
+
+ @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)
+
+ @check_resource
+ def rename(self, container, name):
+ if utils.compare_version('1.17', self._version) < 0:
+ raise errors.InvalidVersion(
+ 'rename was only introduced in API version 1.17'
+ )
+ url = self._url("/containers/{0}/rename", container)
+ params = {'name': name}
+ res = self._post(url, params=params)
+ self._raise_for_status(res)
+
+ @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)
+
+ @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)
+
+ @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)
+
+ @check_resource
+ def stats(self, container, decode=None):
+ if utils.compare_version('1.17', self._version) < 0:
+ raise errors.InvalidVersion(
+ 'Stats retrieval is not supported in API < 1.17!')
+
+ url = self._url("/containers/{0}/stats", container)
+ return self._stream_helper(self._get(url, stream=True), decode=decode)
+
+ @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)
+
+ @check_resource
+ def top(self, container):
+ u = self._url("/containers/{0}/top", container)
+ return self._result(self._get(u), True)
+
+ @check_resource
+ def unpause(self, container):
+ url = self._url('/containers/{0}/unpause', container)
+ res = self._post(url)
+ self._raise_for_status(res)
+
+ @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..87eb143
--- /dev/null
+++ b/docker/api/exec_api.py
@@ -0,0 +1,76 @@
+import shlex
+
+import six
+
+from .. import errors
+from ..utils import utils, check_resource
+
+
+class ExecApiMixin(object):
+ @check_resource
+ def exec_create(self, container, cmd, stdout=True, stderr=True, tty=False,
+ privileged=False, user=''):
+ if utils.compare_version('1.15', self._version) < 0:
+ raise errors.InvalidVersion('Exec is not supported in API < 1.15')
+ 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)
+
+ def exec_inspect(self, exec_id):
+ if utils.compare_version('1.16', self._version) < 0:
+ raise errors.InvalidVersion(
+ 'exec_inspect is not supported in API < 1.16'
+ )
+ 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)
+
+ def exec_resize(self, exec_id, height=None, width=None):
+ if utils.compare_version('1.15', self._version) < 0:
+ raise errors.InvalidVersion('Exec is not supported in API < 1.15')
+ 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)
+
+ def exec_start(self, exec_id, detach=False, tty=False, stream=False):
+ if utils.compare_version('1.15', self._version) < 0:
+ raise errors.InvalidVersion('Exec is not supported in API < 1.15')
+ 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..c6939ef
--- /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 ..utils import utils, check_resource
+from .. import errors
+
+log = logging.getLogger(__name__)
+
+
+class ImageApiMixin(object):
+
+ @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
+
+ @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))
+
+ @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))
+
+ @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)
+
+ @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
+ )
+
+ @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/client.py b/docker/client.py
index 88bc50d..65777b7 100644
--- a/docker/client.py
+++ b/docker/client.py
@@ -12,882 +12,19 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import logging
-import os
-import re
-import shlex
-import warnings
-from datetime import datetime
-
-import six
-
from . import clientbase
-from . import constants
from . import errors
-from .auth import auth
-from .utils import utils, check_resource
-from .constants import INSECURE_REGISTRY_DEPRECATION_WARNING
-
-log = logging.getLogger(__name__)
-
-
-class Client(clientbase.ClientBase):
- @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)
-
- @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))
-
- 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:
- 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')
-
- 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
-
- @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
-
- @check_resource
- def copy(self, container, resource):
- 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)
-
- @check_resource
- def diff(self, container):
- return self._result(
- self._get(self._url("/containers/{0}/changes", container)), True
- )
-
- 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
- )
-
- @check_resource
- def exec_create(self, container, cmd, stdout=True, stderr=True, tty=False,
- privileged=False, user=''):
- if utils.compare_version('1.15', self._version) < 0:
- raise errors.InvalidVersion('Exec is not supported in API < 1.15')
- 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)
-
- def exec_inspect(self, exec_id):
- if utils.compare_version('1.16', self._version) < 0:
- raise errors.InvalidVersion(
- 'exec_inspect is not supported in API < 1.16'
- )
- 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)
-
- def exec_resize(self, exec_id, height=None, width=None):
- if utils.compare_version('1.15', self._version) < 0:
- raise errors.InvalidVersion('Exec is not supported in API < 1.15')
- 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)
-
- def exec_start(self, exec_id, detach=False, tty=False, stream=False):
- if utils.compare_version('1.15', self._version) < 0:
- raise errors.InvalidVersion('Exec is not supported in API < 1.15')
- 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)
-
- @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
-
- @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
-
- @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))
-
- def info(self):
- return self._result(self._get(self._url("/info")),
- True)
-
- @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))
-
- @check_resource
- def inspect_container(self, container):
- return self._result(
- self._get(self._url("/containers/{0}/json", container)), True
- )
-
- @check_resource
- def inspect_image(self, image):
- return self._result(
- self._get(self._url("/images/{0}/json", image)), True
- )
-
- @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)
-
- 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, 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)
-
- @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
- )
-
- @check_resource
- def pause(self, container):
- url = self._url('/containers/{0}/pause', container)
- res = self._post(url)
- self._raise_for_status(res)
-
- def ping(self):
- return self._result(self._get(self._url('/_ping')))
-
- @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()
- s_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
-
- h_ports = port_settings.get(s_port + '/udp')
- if h_ports is None:
- h_ports = port_settings.get(s_port + '/tcp')
-
- return h_ports
-
- 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)
-
- @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)
-
- @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)
-
- @check_resource
- def rename(self, container, name):
- if utils.compare_version('1.17', self._version) < 0:
- raise errors.InvalidVersion(
- 'rename was only introduced in API version 1.17'
- )
- url = self._url("/containers/{0}/rename", container)
- params = {'name': name}
- res = self._post(url, params=params)
- self._raise_for_status(res)
-
- @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)
-
- @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)
-
- def search(self, term):
- return self._result(
- self._get(self._url("/images/search"), params={'term': term}),
- True
- )
-
- @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)
-
- @check_resource
- def stats(self, container, decode=None):
- if utils.compare_version('1.17', self._version) < 0:
- raise errors.InvalidVersion(
- 'Stats retrieval is not supported in API < 1.17!')
-
- url = self._url("/containers/{0}/stats", container)
- return self._stream_helper(self._get(url, stream=True), decode=decode)
-
- @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)
-
- @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
-
- @check_resource
- def top(self, container):
- u = self._url("/containers/{0}/top", container)
- return self._result(self._get(u), True)
-
- def version(self, api_version=True):
- url = self._url("/version", versioned_api=api_version)
- return self._result(self._get(url), json=True)
+from . import api
- @check_resource
- def unpause(self, container):
- url = self._url('/containers/{0}/unpause', container)
- res = self._post(url)
- self._raise_for_status(res)
- @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
+class Client(
+ clientbase.ClientBase,
+ api.BuildApiMixin,
+ api.ContainerApiMixin,
+ api.DaemonApiMixin,
+ api.ExecApiMixin,
+ api.ImageApiMixin):
+ pass
class AutoVersionClient(Client):
diff --git a/tests/test.py b/tests/test.py
index 9cf94a1..52a3503 100644
--- a/tests/test.py
+++ b/tests/test.py
@@ -45,7 +45,7 @@ except ImportError:
import mock
-DEFAULT_TIMEOUT_SECONDS = docker.client.constants.DEFAULT_TIMEOUT_SECONDS
+DEFAULT_TIMEOUT_SECONDS = docker.constants.DEFAULT_TIMEOUT_SECONDS
def response(status_code=200, content='', headers=None, reason=None, elapsed=0,
@@ -81,7 +81,7 @@ def fake_resp(url, data=None, **kwargs):
fake_request = mock.Mock(side_effect=fake_resp)
url_prefix = 'http+docker://localunixsocket/v{0}/'.format(
- docker.client.constants.DEFAULT_DOCKER_API_VERSION)
+ docker.constants.DEFAULT_DOCKER_API_VERSION)
class Cleanup(object):