import six import warnings from datetime import datetime from .. import errors from .. import utils from ..utils.utils import create_networking_config, create_endpoint_config 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 } headers = { 'Connection': 'Upgrade', 'Upgrade': 'tcp' } u = self._url("/containers/{0}/attach", container) response = self._post(u, headers=headers, params=params, stream=stream) return self._read_from_socket(response, stream) @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) headers = { 'Connection': 'Upgrade', 'Upgrade': 'tcp' } u = self._url("/containers/{0}/attach", container) return self._get_raw_response_socket( self.post( u, None, params=self._attach_params(params), stream=True, headers=headers ) ) @utils.check_resource def commit(self, container, repository=None, tag=None, message=None, author=None, changes=None, conf=None): params = { 'container': container, 'repo': repository, 'tag': tag, 'comment': message, 'author': author, 'changes': changes } 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, stop_signal=None, networking_config=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, stop_signal, networking_config, ) 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) def create_networking_config(self, *args, **kwargs): return create_networking_config(*args, **kwargs) def create_endpoint_config(self, *args, **kwargs): return create_endpoint_config(self._version, *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: if not isinstance(signal, six.string_types): signal = int(signal) 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', since=None, follow=None): if utils.compare_version('1.11', self._version) >= 0: if follow is None: follow = stream params = {'stderr': stderr and 1 or 0, 'stdout': stdout and 1 or 0, 'timestamps': timestamps and 1 or 0, 'follow': follow 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 if since is not None: if utils.compare_version('1.19', self._version) < 0: raise errors.InvalidVersion( 'since is not supported in API < 1.19' ) else: if isinstance(since, datetime): params['since'] = utils.datetime_to_timestamp(since) elif (isinstance(since, int) and since > 0): params['since'] = since 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, stream=True): url = self._url("/containers/{0}/stats", container) if stream: return self._stream_helper(self._get(url, stream=True), decode=decode) else: return self._result(self._get(url, params={'stream': False}), json=True) @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.minimum_version('1.22') @utils.check_resource def update_container( self, container, blkio_weight=None, cpu_period=None, cpu_quota=None, cpu_shares=None, cpuset_cpus=None, cpuset_mems=None, mem_limit=None, mem_reservation=None, memswap_limit=None, kernel_memory=None ): url = self._url('/containers/{0}/update', container) data = {} if blkio_weight: data['BlkioWeight'] = blkio_weight if cpu_period: data['CpuPeriod'] = cpu_period if cpu_shares: data['CpuShares'] = cpu_shares if cpu_quota: data['CpuQuota'] = cpu_quota if cpuset_cpus: data['CpusetCpus'] = cpuset_cpus if cpuset_mems: data['CpusetMems'] = cpuset_mems if mem_limit: data['Memory'] = utils.parse_bytes(mem_limit) if mem_reservation: data['MemoryReservation'] = utils.parse_bytes(mem_reservation) if memswap_limit: data['MemorySwap'] = utils.parse_bytes(memswap_limit) if kernel_memory: data['KernelMemory'] = utils.parse_bytes(kernel_memory) res = self._post_json(url, data=data) return self._result(res, True) @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