diff options
author | Ben Firshman <ben@firshman.co.uk> | 2016-11-07 18:06:31 -0800 |
---|---|---|
committer | Ben Firshman <ben@firshman.co.uk> | 2016-11-22 17:03:04 +0000 |
commit | f32c0c170917518b7e224adf8627e95b5e620a91 (patch) | |
tree | 585932db9889b9f9ef60cab88df58efd910321c1 | |
parent | ed959f2144ed4719113cc4a570cd58ac0cca25da (diff) | |
download | docker-py-f32c0c170917518b7e224adf8627e95b5e620a91.tar.gz |
Add docstrings to low-level API
Signed-off-by: Ben Firshman <ben@firshman.co.uk>
-rw-r--r-- | docker/api/build.py | 83 | ||||
-rw-r--r-- | docker/api/client.py | 28 | ||||
-rw-r--r-- | docker/api/container.py | 794 | ||||
-rw-r--r-- | docker/api/daemon.py | 84 | ||||
-rw-r--r-- | docker/api/exec_api.py | 62 | ||||
-rw-r--r-- | docker/api/image.py | 244 | ||||
-rw-r--r-- | docker/api/network.py | 100 | ||||
-rw-r--r-- | docker/api/service.py | 126 | ||||
-rw-r--r-- | docker/api/swarm.py | 204 | ||||
-rw-r--r-- | docker/api/volume.py | 85 | ||||
-rw-r--r-- | docker/tls.py | 17 | ||||
-rw-r--r-- | docker/utils/utils.py | 51 |
12 files changed, 1876 insertions, 2 deletions
diff --git a/docker/api/build.py b/docker/api/build.py index 68aa962..297c9e0 100644 --- a/docker/api/build.py +++ b/docker/api/build.py @@ -19,6 +19,89 @@ class BuildApiMixin(object): forcerm=False, dockerfile=None, container_limits=None, decode=False, buildargs=None, gzip=False, shmsize=None, labels=None): + """ + Similar to the ``docker build`` command. Either ``path`` or ``fileobj`` + needs to be set. ``path`` can be a local path (to a directory + containing a Dockerfile) or a remote URL. ``fileobj`` must be a + readable file-like object to a Dockerfile. + + If you have a tar file for the Docker build context (including a + Dockerfile) already, pass a readable file-like object to ``fileobj`` + and also pass ``custom_context=True``. If the stream is compressed + also, set ``encoding`` to the correct value (e.g ``gzip``). + + Example: + >>> from io import BytesIO + >>> from docker import Client + >>> dockerfile = ''' + ... # Shared Volume + ... FROM busybox:buildroot-2014.02 + ... VOLUME /data + ... CMD ["/bin/sh"] + ... ''' + >>> f = BytesIO(dockerfile.encode('utf-8')) + >>> cli = Client(base_url='tcp://127.0.0.1:2375') + >>> response = [line for line in cli.build( + ... fileobj=f, rm=True, tag='yourname/volume' + ... )] + >>> response + ['{"stream":" ---\\u003e a9eb17255234\\n"}', + '{"stream":"Step 1 : VOLUME /data\\n"}', + '{"stream":" ---\\u003e Running in abdc1e6896c6\\n"}', + '{"stream":" ---\\u003e 713bca62012e\\n"}', + '{"stream":"Removing intermediate container abdc1e6896c6\\n"}', + '{"stream":"Step 2 : CMD [\\"/bin/sh\\"]\\n"}', + '{"stream":" ---\\u003e Running in dba30f2a1a7e\\n"}', + '{"stream":" ---\\u003e 032b8b2855fc\\n"}', + '{"stream":"Removing intermediate container dba30f2a1a7e\\n"}', + '{"stream":"Successfully built 032b8b2855fc\\n"}'] + + Args: + path (str): Path to the directory containing the Dockerfile + fileobj: A file object to use as the Dockerfile. (Or a file-like + object) + tag (str): A tag to add to the final image + quiet (bool): Whether to return the status + nocache (bool): Don't use the cache when set to ``True`` + rm (bool): Remove intermediate containers. The ``docker build`` + command now defaults to ``--rm=true``, but we have kept the old + default of `False` to preserve backward compatibility + stream (bool): *Deprecated for API version > 1.8 (always True)*. + Return a blocking generator you can iterate over to retrieve + build output as it happens + timeout (int): HTTP timeout + custom_context (bool): Optional if using ``fileobj`` + encoding (str): The encoding for a stream. Set to ``gzip`` for + compressing + pull (bool): Downloads any updates to the FROM image in Dockerfiles + forcerm (bool): Always remove intermediate containers, even after + unsuccessful builds + dockerfile (str): path within the build context to the Dockerfile + buildargs (dict): A dictionary of build arguments + container_limits (dict): A dictionary of limits applied to each + container created by the build process. Valid keys: + + - memory (int): set memory limit for build + - memswap (int): Total memory (memory + swap), -1 to disable + swap + - cpushares (int): CPU shares (relative weight) + - cpusetcpus (str): CPUs in which to allow execution, e.g., + ``"0-3"``, ``"0,1"`` + decode (bool): If set to ``True``, the returned stream will be + decoded into dicts on the fly. Default ``False``. + shmsize (int): Size of `/dev/shm` in bytes. The size must be + greater than 0. If omitted the system uses 64MB. + labels (dict): A dictionary of labels to set on the image. + + Returns: + A generator for the build output. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + ``TypeError`` + If neither ``path`` nor ``fileobj`` is specified. + """ remote = context = None headers = {} container_limits = container_limits or {} diff --git a/docker/api/client.py b/docker/api/client.py index 2fc2ef0..5c26d63 100644 --- a/docker/api/client.py +++ b/docker/api/client.py @@ -51,8 +51,32 @@ class APIClient( """ A low-level client for the Docker Remote API. - Each method maps one-to-one with a REST API endpoint, so calling each - method results in a single API call. + Example: + + >>> import docker + >>> client = docker.APIClient(base_url='unix://var/run/docker.sock') + >>> client.version() + {u'ApiVersion': u'1.24', + u'Arch': u'amd64', + u'BuildTime': u'2016-09-27T23:38:15.810178467+00:00', + u'Experimental': True, + u'GitCommit': u'45bed2c', + u'GoVersion': u'go1.6.3', + u'KernelVersion': u'4.4.22-moby', + u'Os': u'linux', + u'Version': u'1.12.2-rc1'} + + Args: + base_url (str): URL to the Docker server. For example, + ``unix:///var/run/docker.sock`` or ``tcp://127.0.0.1:1234``. + version (str): The version of the API to use. Set to ``auto`` to + automatically detect the server's version. Default: ``1.24`` + timeout (int): Default timeout for API calls, in seconds. + tls (bool or :py:class:`~docker.tls.TLSConfig`): Enable TLS. Pass + ``True`` to enable it with default options, or pass a + :py:class:`~docker.tls.TLSConfig` object to use custom + configuration. + user_agent (str): Set a custom user agent for requests to the server. """ def __init__(self, base_url=None, version=None, timeout=DEFAULT_TIMEOUT_SECONDS, tls=False, diff --git a/docker/api/container.py b/docker/api/container.py index 338b79f..e6d01e7 100644 --- a/docker/api/container.py +++ b/docker/api/container.py @@ -11,6 +11,30 @@ class ContainerApiMixin(object): @utils.check_resource def attach(self, container, stdout=True, stderr=True, stream=False, logs=False): + """ + Attach to a container. + + The ``.logs()`` function is a wrapper around this method, which you can + use instead if you want to fetch/stream container output without first + retrieving the entire backlog. + + Args: + container (str): The container to attach to. + stdout (bool): Include stdout. + stderr (bool): Include stderr. + stream (bool): Return container output progressively as an iterator + of strings, rather than a single string. + logs (bool): Include the container's previous output. + + Returns: + By default, the container's output as a single string. + + If ``stream=True``, an iterator of output strings. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ params = { 'logs': logs and 1 or 0, 'stdout': stdout and 1 or 0, @@ -30,6 +54,20 @@ class ContainerApiMixin(object): @utils.check_resource def attach_socket(self, container, params=None, ws=False): + """ + Like ``attach``, but returns the underlying socket-like object for the + HTTP request. + + Args: + container (str): The container to attach to. + params (dict): Dictionary of request parameters (e.g. ``stdout``, + ``stderr``, ``stream``). + ws (bool): Use websockets instead of raw HTTP. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ if params is None: params = { 'stdout': 1, @@ -56,6 +94,26 @@ class ContainerApiMixin(object): @utils.check_resource def commit(self, container, repository=None, tag=None, message=None, author=None, changes=None, conf=None): + """ + Commit a container to an image. Similar to the ``docker commit`` + command. + + Args: + container (str): The image hash of the container + repository (str): The repository to push the image to + tag (str): The tag to push + message (str): A commit message + author (str): The name of the author + changes (str): Dockerfile instructions to apply while committing + conf (dict): The configuration for the container. See the + `Remote API documentation + <https://docs.docker.com/reference/api/docker_remote_api/>`_ + for full details. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ params = { 'container': container, 'repo': repository, @@ -71,6 +129,50 @@ class ContainerApiMixin(object): def containers(self, quiet=False, all=False, trunc=False, latest=False, since=None, before=None, limit=-1, size=False, filters=None): + """ + List containers. Similar to the ``docker ps`` command. + + Args: + quiet (bool): Only display numeric Ids + all (bool): Show all containers. Only running containers are shown + by default trunc (bool): Truncate output + latest (bool): Show only the latest created container, include + non-running ones. + since (str): Show only containers created since Id or Name, include + non-running ones + before (str): Show only container created before Id or Name, + include non-running ones + limit (int): Show `limit` last created containers, include + non-running ones + size (bool): Display sizes + filters (dict): Filters to be processed on the image list. + Available filters: + + - `exited` (int): Only containers with specified exit code + - `status` (str): One of ``restarting``, ``running``, + ``paused``, ``exited`` + - `label` (str): format either ``"key"`` or ``"key=value"`` + - `id` (str): The id of the container. + - `name` (str): The name of the container. + - `ancestor` (str): Filter by container ancestor. Format of + ``<image-name>[:tag]``, ``<image-id>``, or + ``<image@digest>``. + - `before` (str): Only containers created before a particular + container. Give the container name or id. + - `since` (str): Only containers created after a particular + container. Give container name or id. + + A comprehensive list can be found in the documentation for + `docker ps + <https://docs.docker.com/engine/reference/commandline/ps>`_. + + Returns: + A list of dicts, one per container + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ params = { 'limit': 1 if latest else limit, 'all': 1 if all else 0, @@ -93,6 +195,24 @@ class ContainerApiMixin(object): @utils.check_resource def copy(self, container, resource): + """ + Identical to the ``docker cp`` command. Get files/folders from the + container. + + **Deprecated for API version >= 1.20.** Use + :py:meth:`~ContainerApiMixin.get_archive` instead. + + Args: + container (str): The container to copy from + resource (str): The path within the container + + Returns: + The contents of the file as a string + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ if utils.version_gte(self._version, '1.20'): warnings.warn( 'Client.copy() is deprecated for API version >= 1.20, ' @@ -117,7 +237,190 @@ class ContainerApiMixin(object): mac_address=None, labels=None, volume_driver=None, stop_signal=None, networking_config=None, healthcheck=None): + """ + Creates a container. Parameters are similar to those for the ``docker + run`` command except it doesn't support the attach options (``-a``). + + The arguments that are passed directly to this function are + host-independent configuration options. Host-specific configuration + is passed with the `host_config` argument. You'll normally want to + use this method in combination with the :py:meth:`create_host_config` + method to generate ``host_config``. + + **Port bindings** + + Port binding is done in two parts: first, provide a list of ports to + open inside the container with the ``ports`` parameter, then declare + bindings with the ``host_config`` parameter. For example: + + .. code-block:: python + + container_id = cli.create_container( + 'busybox', 'ls', ports=[1111, 2222], + host_config=cli.create_host_config(port_bindings={ + 1111: 4567, + 2222: None + }) + ) + + + You can limit the host address on which the port will be exposed like + such: + + .. code-block:: python + + cli.create_host_config(port_bindings={1111: ('127.0.0.1', 4567)}) + + Or without host port assignment: + + .. code-block:: python + + cli.create_host_config(port_bindings={1111: ('127.0.0.1',)}) + + If you wish to use UDP instead of TCP (default), you need to declare + ports as such in both the config and host config: + + .. code-block:: python + + container_id = cli.create_container( + 'busybox', 'ls', ports=[(1111, 'udp'), 2222], + host_config=cli.create_host_config(port_bindings={ + '1111/udp': 4567, 2222: None + }) + ) + + To bind multiple host ports to a single container port, use the + following syntax: + + .. code-block:: python + cli.create_host_config(port_bindings={ + 1111: [1234, 4567] + }) + + You can also bind multiple IPs to a single container port: + + .. code-block:: python + + cli.create_host_config(port_bindings={ + 1111: [ + ('192.168.0.100', 1234), + ('192.168.0.101', 1234) + ] + }) + + **Using volumes** + + Volume declaration is done in two parts. Provide a list of mountpoints + to the with the ``volumes`` parameter, and declare mappings in the + ``host_config`` section. + + .. code-block:: python + + container_id = cli.create_container( + 'busybox', 'ls', volumes=['/mnt/vol1', '/mnt/vol2'], + host_config=cli.create_host_config(binds={ + '/home/user1/': { + 'bind': '/mnt/vol2', + 'mode': 'rw', + }, + '/var/www': { + 'bind': '/mnt/vol1', + 'mode': 'ro', + } + }) + ) + + You can alternatively specify binds as a list. This code is equivalent + to the example above: + + .. code-block:: python + + container_id = cli.create_container( + 'busybox', 'ls', volumes=['/mnt/vol1', '/mnt/vol2'], + host_config=cli.create_host_config(binds=[ + '/home/user1/:/mnt/vol2', + '/var/www:/mnt/vol1:ro', + ]) + ) + + **Networking** + + You can specify networks to connect the container to by using the + ``networking_config`` parameter. At the time of creation, you can + only connect a container to a single networking, but you + can create more connections by using + :py:meth:`~connect_container_to_network`. + + For example: + + .. code-block:: python + + networking_config = docker_client.create_networking_config({ + 'network1': docker_client.create_endpoint_config( + ipv4_address='172.28.0.124', + aliases=['foo', 'bar'], + links=['container2'] + ) + }) + + ctnr = docker_client.create_container( + img, command, networking_config=networking_config + ) + + Args: + image (str): The image to run + command (str or list): The command to be run in the container + hostname (str): Optional hostname for the container + user (str or int): Username or UID + detach (bool): Detached mode: run container in the background and + return container ID + stdin_open (bool): Keep STDIN open even if not attached + tty (bool): Allocate a pseudo-TTY + mem_limit (float or str): Memory limit. Accepts float values (which + represent the memory limit of the created container in bytes) + or a string with a units identification char (``100000b``, + ``1000k``, ``128m``, ``1g``). If a string is specified without + a units character, bytes are assumed as an intended unit. + ports (list of ints): A list of port numbers + environment (dict or list): A dictionary or a list of strings in + the following format ``["PASSWORD=xxx"]`` or + ``{"PASSWORD": "xxx"}``. + dns (list): DNS name servers. Deprecated since API version 1.10. + Use ``host_config`` instead. + dns_opt (list): Additional options to be added to the container's + ``resolv.conf`` file + volumes (str or list): + volumes_from (list): List of container names or Ids to get + volumes from. + network_disabled (bool): Disable networking + name (str): A name for the container + entrypoint (str or list): An entrypoint + working_dir (str): Path to the working directory + domainname (str or list): Set custom DNS search domains + memswap_limit (int): + host_config (dict): A dictionary created with + :py:meth:`create_host_config`. + mac_address (str): The Mac Address to assign the container + labels (dict or list): A dictionary of name-value labels (e.g. + ``{"label1": "value1", "label2": "value2"}``) or a list of + names of labels to set with empty values (e.g. + ``["label1", "label2"]``) + volume_driver (str): The name of a volume driver/plugin. + stop_signal (str): The stop signal to use to stop the container + (e.g. ``SIGINT``). + networking_config (dict): A networking configuration generated + by :py:meth:`create_networking_config`. + + Returns: + A dictionary with an image 'Id' key and a 'Warnings' key. + + Raises: + :py:class:`docker.errors.ImageNotFound` + If the specified image does not exist. + :py:class:`docker.errors.APIError` + If the server returns an error. + """ if isinstance(volumes, six.string_types): volumes = [volumes, ] @@ -147,6 +450,130 @@ class ContainerApiMixin(object): return self._result(res, True) def create_host_config(self, *args, **kwargs): + """ + Create a dictionary for the ``host_config`` argument to + :py:meth:`create_container`. + + Args: + binds (dict): Volumes to bind. See :py:meth:`create_container` + for more information. + blkio_weight_device: Block IO weight (relative device weight) in + the form of: ``[{"Path": "device_path", "Weight": weight}]``. + blkio_weight: Block IO weight (relative weight), accepts a weight + value between 10 and 1000. + cap_add (list of str): Add kernel capabilities. For example, + ``["SYS_ADMIN", "MKNOD"]``. + cap_drop (list of str): Drop kernel capabilities. + cpu_group (int): The length of a CPU period in microseconds. + cpu_period (int): Microseconds of CPU time that the container can + get in a CPU period. + cpu_shares (int): CPU shares (relative weight). + cpuset_cpus (str): CPUs in which to allow execution (``0-3``, + ``0,1``). + device_read_bps: Limit read rate (bytes per second) from a device + in the form of: `[{"Path": "device_path", "Rate": rate}]` + device_read_iops: Limit read rate (IO per second) from a device. + device_write_bps: Limit write rate (bytes per second) from a + device. + device_write_iops: Limit write rate (IO per second) from a device. + devices (list): Expose host devices to the container, as a list + of strings in the form + ``<path_on_host>:<path_in_container>:<cgroup_permissions>``. + + For example, ``/dev/sda:/dev/xvda:rwm`` allows the container + to have read-write access to the host's ``/dev/sda`` via a + node named ``/dev/xvda`` inside the container. + dns (list): Set custom DNS servers. + dns_search (list): DNS search domains. + extra_hosts (dict): Addtional hostnames to resolve inside the + container, as a mapping of hostname to IP address. + group_add (list): List of additional group names and/or IDs that + the container process will run as. + ipc_mode (str): Set the IPC mode for the container. + isolation (str): Isolation technology to use. Default: `None`. + links (dict or list of tuples): Either a dictionary mapping name + to alias or as a list of ``(name, alias)`` tuples. + log_config (dict): Logging configuration, as a dictionary with + keys: + + - ``type`` The logging driver name. + - ``config`` A dictionary of configuration for the logging + driver. + + lxc_conf (dict): LXC config. + mem_limit (float or str): Memory limit. Accepts float values + (which represent the memory limit of the created container in + bytes) or a string with a units identification char + (``100000b``, ``1000k``, ``128m``, ``1g``). If a string is + specified without a units character, bytes are assumed as an + mem_swappiness (int): Tune a container's memory swappiness + behavior. Accepts number between 0 and 100. + memswap_limit (str or int): Maximum amount of memory + swap a + container is allowed to consume. + network_mode (str): One of: + + - ``bridge`` Create a new network stack for the container on + on the bridge network. + - ``none`` No networking for this container. + - ``container:<name|id>`` Reuse another container's network + stack. + - ``host`` Use the host network stack. + oom_kill_disable (bool): Whether to disable OOM killer. + oom_score_adj (int): An integer value containing the score given + to the container in order to tune OOM killer preferences. + pid_mode (str): If set to ``host``, use the host PID namespace + inside the container. + pids_limit (int): Tune a container's pids limit. Set ``-1`` for + unlimited. + port_bindings (dict): See :py:meth:`create_container` + for more information. + privileged (bool): Give extended privileges to this container. + publish_all_ports (bool): Publish all ports to the host. + read_only (bool): Mount the container's root filesystem as read + only. + restart_policy (dict): Restart the container when it exits. + Configured as a dictionary with keys: + + - ``Name`` One of ``on-failure``, or ``always``. + - ``MaximumRetryCount`` Number of times to restart the + container on failure. + security_opt (list): A list of string values to customize labels + for MLS systems, such as SELinux. + shm_size (str or int): Size of /dev/shm (e.g. ``1G``). + sysctls (dict): Kernel parameters to set in the container. + tmpfs (dict): Temporary filesystems to mount, as a dictionary + mapping a path inside the container to options for that path. + + For example: + + .. code-block:: python + + { + '/mnt/vol2': '', + '/mnt/vol1': 'size=3G,uid=1000' + } + + ulimits (list): Ulimits to set inside the container, as a list of + dicts. + userns_mode (str): Sets the user namespace mode for the container + when user namespace remapping option is enabled. Supported + values are: ``host`` + volumes_from (list): List of container names or IDs to get + volumes from. + + + Returns: + (dict) A dictionary which can be passed to the ``host_config`` + argument to :py:meth:`create_container`. + + Example: + + >>> cli.create_host_config(privileged=True, cap_drop=['MKNOD'], + volumes_from=['nostalgic_newton']) + {'CapDrop': ['MKNOD'], 'LxcConf': None, 'Privileged': True, + 'VolumesFrom': ['nostalgic_newton'], 'PublishAllPorts': False} + +""" if not kwargs: kwargs = {} if 'version' in kwargs: @@ -158,19 +585,98 @@ class ContainerApiMixin(object): return utils.create_host_config(*args, **kwargs) def create_networking_config(self, *args, **kwargs): + """ + Create a networking config dictionary to be used as the + ``networking_config`` parameter in :py:meth:`create_container`. + + Args: + endpoints_config (dict): A dictionary mapping network names to + endpoint configurations generated by + :py:meth:`create_endpoint_config`. + + Returns: + (dict) A networking config. + + Example: + + >>> docker_client.create_network('network1') + >>> networking_config = docker_client.create_networking_config({ + 'network1': docker_client.create_endpoint_config() + }) + >>> container = docker_client.create_container( + img, command, networking_config=networking_config + ) + + """ return create_networking_config(*args, **kwargs) def create_endpoint_config(self, *args, **kwargs): + """ + Create an endpoint config dictionary to be used with + :py:meth:`create_networking_config`. + + Args: + aliases (list): A list of aliases for this endpoint. Names in + that list can be used within the network to reach the + container. Defaults to ``None``. + links (list): A list of links for this endpoint. Containers + declared in this list will be linked to this container. + Defaults to ``None``. + ipv4_address (str): The IP address of this container on the + network, using the IPv4 protocol. Defaults to ``None``. + ipv6_address (str): The IP address of this container on the + network, using the IPv6 protocol. Defaults to ``None``. + link_local_ips (list): A list of link-local (IPv4/IPv6) + addresses. + + Returns: + (dict) An endpoint config. + + Example: + + >>> endpoint_config = client.create_endpoint_config( + aliases=['web', 'app'], + links=['app_db'], + ipv4_address='132.65.0.123' + ) + + """ return create_endpoint_config(self._version, *args, **kwargs) @utils.check_resource def diff(self, container): + """ + Inspect changes on a container's filesystem. + + Args: + container (str): The container to diff + + Returns: + (str) + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ return self._result( self._get(self._url("/containers/{0}/changes", container)), True ) @utils.check_resource def export(self, container): + """ + Export the contents of a filesystem as a tar archive. + + Args: + container (str): The container to export + + Returns: + (str): The filesystem tar archive + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ res = self._get( self._url("/containers/{0}/export", container), stream=True ) @@ -180,6 +686,22 @@ class ContainerApiMixin(object): @utils.check_resource @utils.minimum_version('1.20') def get_archive(self, container, path): + """ + Retrieve a file or folder from a container in the form of a tar + archive. + + Args: + container (str): The container where the file is located + path (str): Path to the file or folder to retrieve + + Returns: + (tuple): First element is a raw tar data stream. Second element is + a dict containing ``stat`` information on the specified ``path``. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ params = { 'path': path } @@ -194,12 +716,37 @@ class ContainerApiMixin(object): @utils.check_resource def inspect_container(self, container): + """ + Identical to the `docker inspect` command, but only for containers. + + Args: + container (str): The container to inspect + + Returns: + (dict): Similar to the output of `docker inspect`, but as a + single dict + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ return self._result( self._get(self._url("/containers/{0}/json", container)), True ) @utils.check_resource def kill(self, container, signal=None): + """ + Kill a container or send a signal to a container. + + Args: + container (str): The container to kill + signal (str or int): The signal to send. Defaults to ``SIGKILL`` + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ url = self._url("/containers/{0}/kill", container) params = {} if signal is not None: @@ -213,6 +760,32 @@ class ContainerApiMixin(object): @utils.check_resource def logs(self, container, stdout=True, stderr=True, stream=False, timestamps=False, tail='all', since=None, follow=None): + """ + Get logs from a container. Similar to the ``docker logs`` command. + + The ``stream`` parameter makes the ``logs`` function return a blocking + generator you can iterate over to retrieve log output as it happens. + + Args: + container (str): The container to get logs from + stdout (bool): Get ``STDOUT`` + stderr (bool): Get ``STDERR`` + stream (bool): Stream the response + timestamps (bool): Show timestamps + tail (str or int): Output specified number of lines at the end of + logs. Either an integer of number of lines or the string + ``all``. Default ``all`` + since (datetime or int): Show logs since a given datetime or + integer epoch (in seconds) + follow (bool): Follow log output + + Returns: + (generator or str) + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ if utils.compare_version('1.11', self._version) >= 0: if follow is None: follow = stream @@ -249,12 +822,48 @@ class ContainerApiMixin(object): @utils.check_resource def pause(self, container): + """ + Pauses all processes within a container. + + Args: + container (str): The container to pause + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ 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): + """ + Lookup the public-facing port that is NAT-ed to ``private_port``. + Identical to the ``docker port`` command. + + Args: + container (str): The container to look up + private_port (int): The private port to inspect + + Returns: + (list of dict): The mapping for the host ports + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + + Example: + .. code-block:: bash + + $ docker run -d -p 80:80 ubuntu:14.04 /bin/sleep 30 + 7174d6347063a83f412fad6124c99cffd25ffe1a0807eb4b7f9cec76ac8cb43b + + .. code-block:: python + + >>> cli.port('7174d6347063', 80) + [{'HostIp': '0.0.0.0', 'HostPort': '80'}] + """ res = self._get(self._url("/containers/{0}/json", container)) self._raise_for_status(res) json_ = res.json() @@ -279,6 +888,26 @@ class ContainerApiMixin(object): @utils.check_resource @utils.minimum_version('1.20') def put_archive(self, container, path, data): + """ + Insert a file or folder in an existing container using a tar archive as + source. + + Args: + container (str): The container where the file(s) will be extracted + path (str): Path inside the container where the file(s) will be + extracted. Must exist. + data (bytes): tar data to be extracted + + Returns: + (bool): True if the call succeeds. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + + Raises: + :py:class:`~docker.errors.APIError` If an error occurs. + """ params = {'path': path} url = self._url('/containers/{0}/archive', container) res = self._put(url, params=params, data=data) @@ -287,6 +916,21 @@ class ContainerApiMixin(object): @utils.check_resource def remove_container(self, container, v=False, link=False, force=False): + """ + Remove a container. Similar to the ``docker rm`` command. + + Args: + container (str): The container to remove + v (bool): Remove the volumes associated with the container + link (bool): Remove the specified link and not the underlying + container + force (bool): Force the removal of a running container (uses + ``SIGKILL``) + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ params = {'v': v, 'link': link, 'force': force} res = self._delete( self._url("/containers/{0}", container), params=params @@ -296,6 +940,17 @@ class ContainerApiMixin(object): @utils.minimum_version('1.17') @utils.check_resource def rename(self, container, name): + """ + Rename a container. Similar to the ``docker rename`` command. + + Args: + container (str): ID of the container to rename + name (str): New name for the container + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ url = self._url("/containers/{0}/rename", container) params = {'name': name} res = self._post(url, params=params) @@ -303,6 +958,18 @@ class ContainerApiMixin(object): @utils.check_resource def resize(self, container, height, width): + """ + Resize the tty session. + + Args: + container (str or dict): The container to resize + height (int): Height of tty session + width (int): Width of tty session + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ params = {'h': height, 'w': width} url = self._url("/containers/{0}/resize", container) res = self._post(url, params=params) @@ -310,6 +977,20 @@ class ContainerApiMixin(object): @utils.check_resource def restart(self, container, timeout=10): + """ + Restart a container. Similar to the ``docker restart`` command. + + Args: + container (str or dict): The container to restart. If a dict, the + ``Id`` key is used. + timeout (int): Number of seconds to try to stop for before killing + the container. Once killed it will then be restarted. Default + is 10 seconds. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ params = {'t': timeout} url = self._url("/containers/{0}/restart", container) res = self._post(url, params=params) @@ -322,7 +1003,28 @@ class ContainerApiMixin(object): 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): + """ + Start a container. Similar to the ``docker start`` command, but + doesn't support attach options. + + **Deprecation warning:** For API version > 1.15, it is highly + recommended to provide host config options in the ``host_config`` + parameter of :py:meth:`~ContainerApiMixin.create_container`. + Args: + container (str): The container to start + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + + Example: + + >>> container = cli.create_container( + ... image='busybox:latest', + ... command='/bin/sleep 30') + >>> cli.start(container=container.get('Id')) + """ if utils.compare_version('1.10', self._version) < 0: if dns is not None: raise errors.InvalidVersion( @@ -386,6 +1088,22 @@ class ContainerApiMixin(object): @utils.minimum_version('1.17') @utils.check_resource def stats(self, container, decode=None, stream=True): + """ + Stream statistics for a specific container. Similar to the + ``docker stats`` command. + + Args: + container (str): The container to stream statistics from + decode (bool): If set to true, stream will be decoded into dicts + on the fly. False by default. + stream (bool): If set to false, only the current stats will be + returned instead of a stream. True by default. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + + """ url = self._url("/containers/{0}/stats", container) if stream: return self._stream_helper(self._get(url, stream=True), @@ -396,6 +1114,18 @@ class ContainerApiMixin(object): @utils.check_resource def stop(self, container, timeout=10): + """ + Stops a container. Similar to the ``docker stop`` command. + + Args: + container (str): The container to stop + timeout (int): Timeout in seconds to wait for the container to + stop before sending a ``SIGKILL``. Default: 10 + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ params = {'t': timeout} url = self._url("/containers/{0}/stop", container) @@ -405,6 +1135,20 @@ class ContainerApiMixin(object): @utils.check_resource def top(self, container, ps_args=None): + """ + Display the running processes of a container. + + Args: + container (str): The container to inspect + ps_args (str): An optional arguments passed to ps (e.g. ``aux``) + + Returns: + (str): The output of the top + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ u = self._url("/containers/{0}/top", container) params = {} if ps_args is not None: @@ -413,6 +1157,12 @@ class ContainerApiMixin(object): @utils.check_resource def unpause(self, container): + """ + Unpause all processes within a container. + + Args: + container (str): The container to unpause + """ url = self._url('/containers/{0}/unpause', container) res = self._post(url) self._raise_for_status(res) @@ -425,6 +1175,31 @@ class ContainerApiMixin(object): mem_reservation=None, memswap_limit=None, kernel_memory=None, restart_policy=None ): + """ + Update resource configs of one or more containers. + + Args: + container (str): The container to inspect + blkio_weight (int): Block IO (relative weight), between 10 and 1000 + cpu_period (int): Limit CPU CFS (Completely Fair Scheduler) period + cpu_quota (int): Limit CPU CFS (Completely Fair Scheduler) quota + cpu_shares (int): CPU shares (relative weight) + cpuset_cpus (str): CPUs in which to allow execution + cpuset_mems (str): MEMs in which to allow execution + mem_limit (int or str): Memory limit + mem_reservation (int or str): Memory soft limit + memswap_limit (int or str): Total memory (memory + swap), -1 to + disable swap + kernel_memory (int or str): Kernel memory limit + restart_policy (dict): Restart policy dictionary + + Returns: + (dict): Dictionary containing a ``Warnings`` key. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ url = self._url('/containers/{0}/update', container) data = {} if blkio_weight: @@ -460,6 +1235,25 @@ class ContainerApiMixin(object): @utils.check_resource def wait(self, container, timeout=None): + """ + Block until a container stops, then return its exit code. Similar to + the ``docker wait`` command. + + Args: + container (str or dict): The container to wait on. If a dict, the + ``Id`` key is used. + timeout (int): Request timeout + + Returns: + (int): The exit code of the container. Returns ``-1`` if the API + responds without a ``StatusCode`` attribute. + + Raises: + :py:class:`requests.exceptions.ReadTimeout` + If the timeout is exceeded. + :py:class:`docker.errors.APIError` + If the server returns an error. + """ url = self._url("/containers/{0}/wait", container) res = self._post(url, timeout=timeout) self._raise_for_status(res) diff --git a/docker/api/daemon.py b/docker/api/daemon.py index 2e87cf0..d40631f 100644 --- a/docker/api/daemon.py +++ b/docker/api/daemon.py @@ -8,6 +8,36 @@ from ..constants import INSECURE_REGISTRY_DEPRECATION_WARNING class DaemonApiMixin(object): def events(self, since=None, until=None, filters=None, decode=None): + """ + Get real-time events from the server. Similar to the ``docker events`` + command. + + Args: + since (UTC datetime or int): Get events from this point + until (UTC datetime or int): Get events until this point + filters (dict): Filter the events by event time, container or image + decode (bool): If set to true, stream will be decoded into dicts on + the fly. False by default. + + Returns: + (generator): A blocking generator you can iterate over to retrieve + events as they happen. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + + Example: + + >>> for event in client.events() + ... print event + {u'from': u'image/with:tag', + u'id': u'container-id', + u'status': u'start', + u'time': 1423339459} + ... + """ + if isinstance(since, datetime): since = utils.datetime_to_timestamp(since) @@ -29,10 +59,42 @@ class DaemonApiMixin(object): ) def info(self): + """ + Display system-wide information. Identical to the ``docker info`` + command. + + Returns: + (dict): The info as a dict + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ 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): + """ + Authenticate with a registry. Similar to the ``docker login`` command. + + Args: + username (str): The registry username + password (str): The plaintext password + email (str): The email for the registry account + registry (str): URL to the registry. E.g. + ``https://index.docker.io/v1/`` + reauth (bool): Whether refresh existing authentication on the + Docker server. + dockercfg_path (str): Use a custom path for the ``.dockercfg`` file + (default ``$HOME/.dockercfg``) + + Returns: + (dict): The response from the login request + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ if insecure_registry: warnings.warn( INSECURE_REGISTRY_DEPRECATION_WARNING.format('login()'), @@ -68,8 +130,30 @@ class DaemonApiMixin(object): return self._result(response, json=True) def ping(self): + """ + Checks the server is responsive. An exception will be raised if it + isn't responding. + + Returns: + (bool) The response from the server. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ return self._result(self._get(self._url('/_ping'))) == 'OK' def version(self, api_version=True): + """ + Returns version information from the server. Similar to the ``docker + version`` command. + + Returns: + (dict): The server version information + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ 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 index 6e49996..694b30a 100644 --- a/docker/api/exec_api.py +++ b/docker/api/exec_api.py @@ -9,6 +9,28 @@ class ExecApiMixin(object): @utils.check_resource def exec_create(self, container, cmd, stdout=True, stderr=True, stdin=False, tty=False, privileged=False, user=''): + """ + Sets up an exec instance in a running container. + + Args: + container (str): Target container where exec instance will be + created + cmd (str or list): Command to be executed + stdout (bool): Attach to stdout. Default: ``True`` + stderr (bool): Attach to stderr. Default: ``True`` + stdin (bool): Attach to stdin. Default: ``False`` + tty (bool): Allocate a pseudo-TTY. Default: False + privileged (bool): Run as privileged. + user (str): User to execute command as. Default: root + + Returns: + (dict): A dictionary with an exec ``Id`` key. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ + if privileged and utils.compare_version('1.19', self._version) < 0: raise errors.InvalidVersion( 'Privileged exec is not supported in API < 1.19' @@ -37,6 +59,19 @@ class ExecApiMixin(object): @utils.minimum_version('1.16') def exec_inspect(self, exec_id): + """ + Return low-level information about an exec command. + + Args: + exec_id (str): ID of the exec instance + + Returns: + (dict): Dictionary of values returned by the endpoint. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ if isinstance(exec_id, dict): exec_id = exec_id.get('Id') res = self._get(self._url("/exec/{0}/json", exec_id)) @@ -44,6 +79,15 @@ class ExecApiMixin(object): @utils.minimum_version('1.15') def exec_resize(self, exec_id, height=None, width=None): + """ + Resize the tty session used by the specified exec command. + + Args: + exec_id (str): ID of the exec instance + height (int): Height of tty session + width (int): Width of tty session + """ + if isinstance(exec_id, dict): exec_id = exec_id.get('Id') @@ -55,6 +99,24 @@ class ExecApiMixin(object): @utils.minimum_version('1.15') def exec_start(self, exec_id, detach=False, tty=False, stream=False, socket=False): + """ + Start a previously set up exec instance. + + Args: + exec_id (str): ID of the exec instance + detach (bool): If true, detach from the exec command. + Default: False + tty (bool): Allocate a pseudo-TTY. Default: False + stream (bool): Stream response data. Default: False + + Returns: + (generator or str): If ``stream=True``, a generator yielding + response chunks. A string containing response data otherwise. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ # we want opened socket if socket == True if isinstance(exec_id, dict): exec_id = exec_id.get('Id') diff --git a/docker/api/image.py b/docker/api/image.py index 978a0c1..2c8cbb2 100644 --- a/docker/api/image.py +++ b/docker/api/image.py @@ -14,17 +14,71 @@ class ImageApiMixin(object): @utils.check_resource def get_image(self, image): + """ + Get a tarball of an image. Similar to the ``docker save`` command. + + Args: + image (str): Image name to get + + Returns: + (urllib3.response.HTTPResponse object): The response from the + daemon. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + + Example: + + >>> image = cli.get_image("fedora:latest") + >>> f = open('/tmp/fedora-latest.tar', 'w') + >>> f.write(image.data) + >>> f.close() + """ 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): + """ + Show the history of an image. + + Args: + image (str): The image to show history for + + Returns: + (str): The history of the image + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ 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): + """ + List images. Similar to the ``docker images`` command. + + Args: + name (str): Only show images belonging to the repository ``name`` + quiet (bool): Only return numeric IDs as a list. + all (bool): Show intermediate image layers. By default, these are + filtered out. + filters (dict): Filters to be processed on the image list. + Available filters: + - ``dangling`` (bool) + - ``label`` (str): format either ``key`` or ``key=value`` + + Returns: + (dict or list): A list if ``quiet=True``, otherwise a dict. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ if viz: if utils.compare_version('1.7', self._version) >= 0: raise Exception('Viz output is not supported in API >= 1.7!') @@ -44,6 +98,25 @@ class ImageApiMixin(object): def import_image(self, src=None, repository=None, tag=None, image=None, changes=None, stream_src=False): + """ + Import an image. Similar to the ``docker import`` command. + + If ``src`` is a string or unicode string, it will first be treated as a + path to a tarball on the local system. If there is an error reading + from that file, ``src`` will be treated as a URL instead to fetch the + image from. You can also pass an open file handle as ``src``, in which + case the data will be read from that file. + + If ``src`` is unset but ``image`` is set, the ``image`` parameter will + be taken as the name of an existing image to import from. + + Args: + src (str or file): Path to tarfile, URL, or file-like object + repository (str): The repository to create + tag (str): The tag to apply + image (str): Use another image like the ``FROM`` Dockerfile + parameter + """ if not (src or image): raise errors.DockerException( 'Must specify src or image to import from' @@ -77,6 +150,16 @@ class ImageApiMixin(object): def import_image_from_data(self, data, repository=None, tag=None, changes=None): + """ + Like :py:meth:`~docker.api.image.ImageApiMixin.import_image`, but + allows importing in-memory bytes data. + + Args: + data (bytes collection): Bytes collection containing valid tar data + repository (str): The repository to create + tag (str): The tag to apply + """ + u = self._url('/images/create') params = _import_image_params( repository, tag, src='-', changes=changes @@ -90,6 +173,19 @@ class ImageApiMixin(object): def import_image_from_file(self, filename, repository=None, tag=None, changes=None): + """ + Like :py:meth:`~docker.api.image.ImageApiMixin.import_image`, but only + supports importing from a tar file on disk. + + Args: + filename (str): Full path to a tar file. + repository (str): The repository to create + tag (str): The tag to apply + + Raises: + IOError: File does not exist. + """ + return self.import_image( src=filename, repository=repository, tag=tag, changes=changes ) @@ -103,12 +199,31 @@ class ImageApiMixin(object): def import_image_from_url(self, url, repository=None, tag=None, changes=None): + """ + Like :py:meth:`~docker.api.image.ImageApiMixin.import_image`, but only + supports importing from a URL. + + Args: + url (str): A URL pointing to a tar file. + repository (str): The repository to create + tag (str): The tag to apply + """ return self.import_image( src=url, repository=repository, tag=tag, changes=changes ) def import_image_from_image(self, image, repository=None, tag=None, changes=None): + """ + Like :py:meth:`~docker.api.image.ImageApiMixin.import_image`, but only + supports importing from another image, like the ``FROM`` Dockerfile + parameter. + + Args: + image (str): Image name to import from + repository (str): The repository to create + tag (str): The tag to apply + """ return self.import_image( image=image, repository=repository, tag=tag, changes=changes ) @@ -128,16 +243,75 @@ class ImageApiMixin(object): @utils.check_resource def inspect_image(self, image): + """ + Get detailed information about an image. Similar to the ``docker + inspect`` command, but only for containers. + + Args: + container (str): The container to inspect + + Returns: + (dict): Similar to the output of ``docker inspect``, but as a + single dict + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ return self._result( self._get(self._url("/images/{0}/json", image)), True ) def load_image(self, data): + """ + Load an image that was previously saved using + :py:meth:`~docker.api.image.ImageApiMixin.get_image` (or ``docker + save``). Similar to ``docker load``. + + Args: + data (binary): Image data to be loaded. + """ 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, decode=False): + """ + Pulls an image. Similar to the ``docker pull`` command. + + Args: + repository (str): The repository to pull + tag (str): The tag to pull + stream (bool): Stream the output as a generator + insecure_registry (bool): Use an insecure registry + auth_config (dict): Override the credentials that + :py:meth:`~docker.api.daemon.DaemonApiMixin.login` has set for + this request. ``auth_config`` should contain the ``username`` + and ``password`` keys to be valid. + + Returns: + (generator or str): The output + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + + Example: + + >>> for line in cli.pull('busybox', stream=True): + ... print(json.dumps(json.loads(line), indent=4)) + { + "status": "Pulling image (latest) from busybox", + "progressDetail": {}, + "id": "e72ac664f4f0" + } + { + "status": "Pulling image (latest) from busybox, endpoint: ...", + "progressDetail": {}, + "id": "e72ac664f4f0" + } + + """ if insecure_registry: warnings.warn( INSECURE_REGISTRY_DEPRECATION_WARNING.format('pull()'), @@ -177,6 +351,38 @@ class ImageApiMixin(object): def push(self, repository, tag=None, stream=False, insecure_registry=False, auth_config=None, decode=False): + """ + Push an image or a repository to the registry. Similar to the ``docker + push`` command. + + Args: + repository (str): The repository to push to + tag (str): An optional tag to push + stream (bool): Stream the output as a blocking generator + insecure_registry (bool): Use ``http://`` to connect to the + registry + auth_config (dict): Override the credentials that + :py:meth:`~docker.api.daemon.DaemonApiMixin.login` has set for + this request. ``auth_config`` should contain the ``username`` + and ``password`` keys to be valid. + + Returns: + (generator or str): The output from the server. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + + Example: + >>> for line in cli.push('yourname/app', stream=True): + ... print line + {"status":"Pushing repository yourname/app (1 tags)"} + {"status":"Pushing","progressDetail":{},"id":"511136ea3c5a"} + {"status":"Image already pushed, skipping","progressDetail":{}, + "id":"511136ea3c5a"} + ... + + """ if insecure_registry: warnings.warn( INSECURE_REGISTRY_DEPRECATION_WARNING.format('push()'), @@ -214,11 +420,33 @@ class ImageApiMixin(object): @utils.check_resource def remove_image(self, image, force=False, noprune=False): + """ + Remove an image. Similar to the ``docker rmi`` command. + + Args: + image (str): The image to remove + force (bool): Force removal of the image + noprune (bool): Do not delete untagged parents + """ params = {'force': force, 'noprune': noprune} res = self._delete(self._url("/images/{0}", image), params=params) self._raise_for_status(res) def search(self, term): + """ + Search for images on Docker Hub. Similar to the ``docker search`` + command. + + Args: + term (str): A term to search for. + + Returns: + (list of dicts): The response of the search. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ return self._result( self._get(self._url("/images/search"), params={'term': term}), True @@ -226,6 +454,22 @@ class ImageApiMixin(object): @utils.check_resource def tag(self, image, repository, tag=None, force=False): + """ + Tag an image into a repository. Similar to the ``docker tag`` command. + + Args: + image (str): The image to tag + repository (str): The repository to set for the tag + tag (str): The tag name + force (bool): Force + + Returns: + (bool): ``True`` if successful + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ params = { 'tag': tag, 'repo': repository, diff --git a/docker/api/network.py b/docker/api/network.py index 0ee0dab..65aeb30 100644 --- a/docker/api/network.py +++ b/docker/api/network.py @@ -8,6 +8,21 @@ from ..utils import version_lt class NetworkApiMixin(object): @minimum_version('1.21') def networks(self, names=None, ids=None): + """ + List networks. Similar to the ``docker networks ls`` command. + + Args: + names (list): List of names to filter by + ids (list): List of ids to filter by + + Returns: + (dict): List of network objects. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ + filters = {} if names: filters['name'] = names @@ -24,6 +39,51 @@ class NetworkApiMixin(object): def create_network(self, name, driver=None, options=None, ipam=None, check_duplicate=None, internal=False, labels=None, enable_ipv6=False): + """ + Create a network. Similar to the ``docker network create``. + + Args: + name (str): Name of the network + driver (str): Name of the driver used to create the network + options (dict): Driver options as a key-value dictionary + ipam (dict): Optional custom IP scheme for the network. + Created with :py:meth:`~docker.utils.create_ipam_config`. + check_duplicate (bool): Request daemon to check for networks with + same name. Default: ``True``. + internal (bool): Restrict external access to the network. Default + ``False``. + labels (dict): Map of labels to set on the network. Default + ``None``. + enable_ipv6 (bool): Enable IPv6 on the network. Default ``False``. + + Returns: + (dict): The created network reference object + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + + Example: + A network using the bridge driver: + + >>> client.create_network("network1", driver="bridge") + + You can also create more advanced networks with custom IPAM + configurations. For example, setting the subnet to + ``192.168.52.0/24`` and gateway address to ``192.168.52.254``. + + .. code-block:: python + + >>> ipam_pool = docker.utils.create_ipam_pool( + subnet='192.168.52.0/24', + gateway='192.168.52.254' + ) + >>> ipam_config = docker.utils.create_ipam_config( + pool_configs=[ipam_pool] + ) + >>> docker_client.create_network("network1", driver="bridge", + ipam=ipam_config) + """ if options is not None and not isinstance(options, dict): raise TypeError('options must be a dictionary') @@ -63,12 +123,24 @@ class NetworkApiMixin(object): @minimum_version('1.21') def remove_network(self, net_id): + """ + Remove a network. Similar to the ``docker network rm`` command. + + Args: + net_id (str): The network's 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): + """ + Get detailed information about a network. + + Args: + net_id (str): ID of network + """ url = self._url("/networks/{0}", net_id) res = self._get(url) return self._result(res, json=True) @@ -79,6 +151,24 @@ class NetworkApiMixin(object): ipv4_address=None, ipv6_address=None, aliases=None, links=None, link_local_ips=None): + """ + Connect a container to a network. + + Args: + container (str): container-id/name to be connected to the network + net_id (str): network id + aliases (list): A list of aliases for this endpoint. Names in that + list can be used within the network to reach the container. + Defaults to ``None``. + links (list): A list of links for this endpoint. Containers + declared in this list will be linkedto this container. + Defaults to ``None``. + ipv4_address (str): The IP address of this container on the + network, using the IPv4 protocol. Defaults to ``None``. + ipv6_address (str): The IP address of this container on the + network, using the IPv6 protocol. Defaults to ``None``. + link_local_ips (list): A list of link-local (IPv4/IPv6) addresses. + """ data = { "Container": container, "EndpointConfig": self.create_endpoint_config( @@ -95,6 +185,16 @@ class NetworkApiMixin(object): @minimum_version('1.21') def disconnect_container_from_network(self, container, net_id, force=False): + """ + Disconnect a container from a network. + + Args: + container (str): container ID or name to be disconnected from the + network + net_id (str): network ID + force (bool): Force the container to disconnect from a network. + Default: ``False`` + """ data = {"Container": container} if force: if version_lt(self._version, '1.22'): diff --git a/docker/api/service.py b/docker/api/service.py index ec2a303..7708b75 100644 --- a/docker/api/service.py +++ b/docker/api/service.py @@ -9,6 +9,32 @@ class ServiceApiMixin(object): update_config=None, networks=None, endpoint_config=None, endpoint_spec=None ): + """ + Create a service. + + Args: + task_template (dict): Specification of the task to start as part + of the new service. + name (string): User-defined name for the service. Optional. + labels (dict): A map of labels to associate with the service. + Optional. + mode (string): Scheduling mode for the service (``replicated`` or + ``global``). Defaults to ``replicated``. + update_config (dict): Specification for the update strategy of the + service. Default: ``None`` + networks (list): List of network names or IDs to attach the + service to. Default: ``None``. + endpoint_config (dict): Properties that can be configured to + access and load balance a service. Default: ``None``. + + Returns: + A dictionary containing an ``ID`` key for the newly created + service. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ if endpoint_config is not None: warnings.warn( 'endpoint_config has been renamed to endpoint_spec.', @@ -43,18 +69,58 @@ class ServiceApiMixin(object): @utils.minimum_version('1.24') @utils.check_resource def inspect_service(self, service): + """ + Return information about a service. + + Args: + service (str): Service name or ID + + Returns: + ``True`` if successful. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ url = self._url('/services/{0}', service) return self._result(self._get(url), True) @utils.minimum_version('1.24') @utils.check_resource def inspect_task(self, task): + """ + Retrieve information about a task. + + Args: + task (str): Task ID + + Returns: + (dict): Information about the task. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ url = self._url('/tasks/{0}', task) return self._result(self._get(url), True) @utils.minimum_version('1.24') @utils.check_resource def remove_service(self, service): + """ + Stop and remove a service. + + Args: + service (str): Service name or ID + + Returns: + ``True`` if successful. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ + url = self._url('/services/{0}', service) resp = self._delete(url) self._raise_for_status(resp) @@ -62,6 +128,20 @@ class ServiceApiMixin(object): @utils.minimum_version('1.24') def services(self, filters=None): + """ + List services. + + Args: + filters (dict): Filters to process on the nodes list. Valid + filters: ``id`` and ``name``. Default: ``None``. + + Returns: + A list of dictionaries containing data about each service. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ params = { 'filters': utils.convert_filters(filters) if filters else None } @@ -70,6 +150,22 @@ class ServiceApiMixin(object): @utils.minimum_version('1.24') def tasks(self, filters=None): + """ + Retrieve a list of tasks. + + Args: + filters (dict): A map of filters to process on the tasks list. + Valid filters: ``id``, ``name``, ``service``, ``node``, + ``label`` and ``desired-state``. + + Returns: + (list): List of task dictionaries. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ + params = { 'filters': utils.convert_filters(filters) if filters else None } @@ -82,7 +178,37 @@ class ServiceApiMixin(object): labels=None, mode=None, update_config=None, networks=None, endpoint_config=None, endpoint_spec=None): + """ + Update a service. + + Args: + service (string): A service identifier (either its name or service + ID). + version (int): The version number of the service object being + updated. This is required to avoid conflicting writes. + task_template (dict): Specification of the updated task to start + as part of the service. See the [TaskTemplate + class](#TaskTemplate) for details. + name (string): New name for the service. Optional. + labels (dict): A map of labels to associate with the service. + Optional. + mode (string): Scheduling mode for the service (``replicated`` or + ``global``). Defaults to ``replicated``. + update_config (dict): Specification for the update strategy of the + service. See the [UpdateConfig class](#UpdateConfig) for + details. Default: ``None``. + networks (list): List of network names or IDs to attach the + service to. Default: ``None``. + endpoint_config (dict): Properties that can be configured to + access and load balance a service. Default: ``None``. + + Returns: + ``True`` if successful. + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ if endpoint_config is not None: warnings.warn( 'endpoint_config has been renamed to endpoint_spec.', diff --git a/docker/api/swarm.py b/docker/api/swarm.py index 2fc8774..a4cb8dd 100644 --- a/docker/api/swarm.py +++ b/docker/api/swarm.py @@ -7,11 +7,87 @@ log = logging.getLogger(__name__) class SwarmApiMixin(object): def create_swarm_spec(self, *args, **kwargs): + """ + Create a ``docker.types.SwarmSpec`` instance that can be used as the + ``swarm_spec`` argument in + :py:meth:`~docker.api.swarm.SwarmApiMixin.init_swarm`. + + Args: + task_history_retention_limit (int): Maximum number of tasks + history stored. + snapshot_interval (int): Number of logs entries between snapshot. + keep_old_snapshots (int): Number of snapshots to keep beyond the + current snapshot. + log_entries_for_slow_followers (int): Number of log entries to + keep around to sync up slow followers after a snapshot is + created. + heartbeat_tick (int): Amount of ticks (in seconds) between each + heartbeat. + election_tick (int): Amount of ticks (in seconds) needed without a + leader to trigger a new election. + dispatcher_heartbeat_period (int): The delay for an agent to send + a heartbeat to the dispatcher. + node_cert_expiry (int): Automatic expiry for nodes certificates. + external_ca (dict): Configuration for forwarding signing requests + to an external certificate authority. Use + ``docker.types.SwarmExternalCA``. + name (string): Swarm's name + + Returns: + ``docker.types.SwarmSpec`` instance. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + + Example: + + >>> spec = client.create_swarm_spec( + snapshot_interval=5000, log_entries_for_slow_followers=1200 + ) + >>> client.init_swarm( + advertise_addr='eth0', listen_addr='0.0.0.0:5000', + force_new_cluster=False, swarm_spec=spec + ) + """ return utils.SwarmSpec(*args, **kwargs) @utils.minimum_version('1.24') def init_swarm(self, advertise_addr=None, listen_addr='0.0.0.0:2377', force_new_cluster=False, swarm_spec=None): + """ + Initialize a new Swarm using the current connected engine as the first + node. + + Args: + advertise_addr (string): Externally reachable address advertised + to other nodes. This can either be an address/port combination + in the form ``192.168.1.1:4567``, or an interface followed by a + port number, like ``eth0:4567``. If the port number is omitted, + the port number from the listen address is used. If + ``advertise_addr`` is not specified, it will be automatically + detected when possible. Default: None + listen_addr (string): Listen address used for inter-manager + communication, as well as determining the networking interface + used for the VXLAN Tunnel Endpoint (VTEP). This can either be + an address/port combination in the form ``192.168.1.1:4567``, + or an interface followed by a port number, like ``eth0:4567``. + If the port number is omitted, the default swarm listening port + is used. Default: '0.0.0.0:2377' + force_new_cluster (bool): Force creating a new Swarm, even if + already part of one. Default: False + swarm_spec (dict): Configuration settings of the new Swarm. Use + ``Client.create_swarm_spec`` to generate a valid + configuration. Default: None + + Returns: + ``True`` if successful. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ + url = self._url('/swarm/init') if swarm_spec is not None and not isinstance(swarm_spec, dict): raise TypeError('swarm_spec must be a dictionary') @@ -27,18 +103,67 @@ class SwarmApiMixin(object): @utils.minimum_version('1.24') def inspect_swarm(self): + """ + Retrieve low-level information about the current swarm. + + Returns: + A dictionary containing data about the swarm. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ url = self._url('/swarm') return self._result(self._get(url), True) @utils.check_resource @utils.minimum_version('1.24') def inspect_node(self, node_id): + """ + Retrieve low-level information about a swarm node + + Args: + node_id (string): ID of the node to be inspected. + + Returns: + A dictionary containing data about this node. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ url = self._url('/nodes/{0}', node_id) return self._result(self._get(url), True) @utils.minimum_version('1.24') def join_swarm(self, remote_addrs, join_token, listen_addr=None, advertise_addr=None): + """ + Make this Engine join a swarm that has already been created. + + Args: + remote_addrs (list): Addresses of one or more manager nodes already + participating in the Swarm to join. + join_token (string): Secret token for joining this Swarm. + listen_addr (string): Listen address used for inter-manager + communication if the node gets promoted to manager, as well as + determining the networking interface used for the VXLAN Tunnel + Endpoint (VTEP). Default: ``None`` + advertise_addr (string): Externally reachable address advertised + to other nodes. This can either be an address/port combination + in the form ``192.168.1.1:4567``, or an interface followed by a + port number, like ``eth0:4567``. If the port number is omitted, + the port number from the listen address is used. If + AdvertiseAddr is not specified, it will be automatically + detected when possible. Default: ``None`` + + Returns: + ``True`` if the request went through. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ data = { "RemoteAddrs": remote_addrs, "ListenAddr": listen_addr, @@ -52,6 +177,20 @@ class SwarmApiMixin(object): @utils.minimum_version('1.24') def leave_swarm(self, force=False): + """ + Leave a swarm. + + Args: + force (bool): Leave the swarm even if this node is a manager. + Default: ``False`` + + Returns: + ``True`` if the request went through. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ url = self._url('/swarm/leave') response = self._post(url, params={'force': force}) # Ignore "this node is not part of a swarm" error @@ -62,6 +201,21 @@ class SwarmApiMixin(object): @utils.minimum_version('1.24') def nodes(self, filters=None): + """ + List swarm nodes. + + Args: + filters (dict): Filters to process on the nodes list. Valid + filters: ``id``, ``name``, ``membership`` and ``role``. + Default: ``None`` + + Returns: + A list of dictionaries containing data about each swarm node. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ url = self._url('/nodes') params = {} if filters: @@ -71,6 +225,34 @@ class SwarmApiMixin(object): @utils.minimum_version('1.24') def update_node(self, node_id, version, node_spec=None): + """ + Update the Node's configuration + + Args: + + version (int): The version number of the node object being + updated. This is required to avoid conflicting writes. + node_spec (dict): Configuration settings to update. Any values + not provided will be removed. Default: ``None`` + + Returns: + `True` if the request went through. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + + Example: + + >>> node_spec = {'Availability': 'active', + 'Name': 'node-name', + 'Role': 'manager', + 'Labels': {'foo': 'bar'} + } + >>> client.update_node(node_id='24ifsmvkjbyhk', version=8, + node_spec=node_spec) + + """ url = self._url('/nodes/{0}/update?version={1}', node_id, str(version)) res = self._post_json(url, data=node_spec) self._raise_for_status(res) @@ -79,6 +261,28 @@ class SwarmApiMixin(object): @utils.minimum_version('1.24') def update_swarm(self, version, swarm_spec=None, rotate_worker_token=False, rotate_manager_token=False): + """ + Update the Swarm's configuration + + Args: + version (int): The version number of the swarm object being + updated. This is required to avoid conflicting writes. + swarm_spec (dict): Configuration settings to update. Use + :py:meth:`~docker.api.swarm.SwarmApiMixin.create_swarm_spec` to + generate a valid configuration. Default: ``None``. + rotate_worker_token (bool): Rotate the worker join token. Default: + ``False``. + rotate_manager_token (bool): Rotate the manager join token. + Default: ``False``. + + Returns: + ``True`` if the request went through. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + """ + url = self._url('/swarm/update') response = self._post_json(url, data=swarm_spec, params={ 'rotateWorkerToken': rotate_worker_token, diff --git a/docker/api/volume.py b/docker/api/volume.py index afc72cb..9c6d5f8 100644 --- a/docker/api/volume.py +++ b/docker/api/volume.py @@ -5,6 +5,32 @@ from .. import utils class VolumeApiMixin(object): @utils.minimum_version('1.21') def volumes(self, filters=None): + """ + List volumes currently registered by the docker daemon. Similar to the + ``docker volume ls`` command. + + Args: + filters (dict): Server-side list filtering options. + + Returns: + (dict): Dictionary with list of volume objects as value of the + ``Volumes`` key. + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + + Example: + + >>> cli.volumes() + {u'Volumes': [{u'Driver': u'local', + u'Mountpoint': u'/var/lib/docker/volumes/foobar/_data', + u'Name': u'foobar'}, + {u'Driver': u'local', + u'Mountpoint': u'/var/lib/docker/volumes/baz/_data', + u'Name': u'baz'}]} + """ + params = { 'filters': utils.convert_filters(filters) if filters else None } @@ -13,6 +39,34 @@ class VolumeApiMixin(object): @utils.minimum_version('1.21') def create_volume(self, name, driver=None, driver_opts=None, labels=None): + """ + Create and register a named volume + + Args: + name (str): Name of the volume + driver (str): Name of the driver used to create the volume + driver_opts (dict): Driver options as a key-value dictionary + labels (dict): Labels to set on the volume + + Returns: + (dict): The created volume reference object + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + + Example: + + >>> volume = cli.create_volume(name='foobar', driver='local', + driver_opts={'foo': 'bar', 'baz': 'false'}, + labels={"key": "value"}) + >>> print(volume) + {u'Driver': u'local', + u'Labels': {u'key': u'value'}, + u'Mountpoint': u'/var/lib/docker/volumes/foobar/_data', + u'Name': u'foobar'} + + """ url = self._url('/volumes/create') if driver_opts is not None and not isinstance(driver_opts, dict): raise TypeError('driver_opts must be a dictionary') @@ -36,11 +90,42 @@ class VolumeApiMixin(object): @utils.minimum_version('1.21') def inspect_volume(self, name): + """ + Retrieve volume info by name. + + Args: + name (str): volume name + + Returns: + (dict): Volume information dictionary + + Raises: + :py:class:`docker.errors.APIError` + If the server returns an error. + + Example: + + >>> cli.inspect_volume('foobar') + {u'Driver': u'local', + u'Mountpoint': u'/var/lib/docker/volumes/foobar/_data', + u'Name': u'foobar'} + + """ url = self._url('/volumes/{0}', name) return self._result(self._get(url), True) @utils.minimum_version('1.21') def remove_volume(self, name): + """ + Remove a volume. Similar to the ``docker volume rm`` command. + + Args: + name (str): The volume's name + + Raises: + + ``docker.errors.APIError``: If volume failed to remove. + """ url = self._url('/volumes/{0}', name) resp = self._delete(url) self._raise_for_status(resp) diff --git a/docker/tls.py b/docker/tls.py index 7c3a2ca..3a0827a 100644 --- a/docker/tls.py +++ b/docker/tls.py @@ -5,6 +5,20 @@ from . import errors, ssladapter class TLSConfig(object): + """ + TLS configuration. + + Args: + client_cert (tuple of str): Path to client cert, path to client key. + ca_cert (str): Path to CA cert file. + verify (bool or str): This can be ``False`` or a path to a CA cert + file. + ssl_version (int): A valid `SSL version`_. + assert_hostname (bool): Verify the hostname of the server. + + .. _`SSL version`: + https://docs.python.org/3.5/library/ssl.html#ssl.PROTOCOL_TLSv1 + """ cert = None ca_cert = None verify = None @@ -57,6 +71,9 @@ class TLSConfig(object): ) def configure_client(self, client): + """ + Configure a client with these TLS options. + """ client.ssl_version = self.ssl_version if self.verify and self.ca_cert: diff --git a/docker/utils/utils.py b/docker/utils/utils.py index b1db884..b107f22 100644 --- a/docker/utils/utils.py +++ b/docker/utils/utils.py @@ -39,6 +39,38 @@ BYTE_UNITS = { def create_ipam_pool(subnet=None, iprange=None, gateway=None, aux_addresses=None): + """ + Create an IPAM pool config dictionary to be added to the + ``pool_configs`` parameter of + :py:meth:`~docker.utils.create_ipam_config`. + + Args: + + subnet (str): Custom subnet for this IPAM pool using the CIDR + notation. Defaults to ``None``. + iprange (str): Custom IP range for endpoints in this IPAM pool using + the CIDR notation. Defaults to ``None``. + gateway (str): Custom IP address for the pool's gateway. + aux_addresses (dict): A dictionary of ``key -> ip_address`` + relationships specifying auxiliary addresses that need to be + allocated by the IPAM driver. + + Returns: + (dict) An IPAM pool config + + Example: + + >>> ipam_pool = docker.utils.create_ipam_pool( + subnet='124.42.0.0/16', + iprange='124.42.0.0/24', + gateway='124.42.0.254', + aux_addresses={ + 'reserved1': '124.42.1.1' + } + ) + >>> ipam_config = docker.utils.create_ipam_config( + pool_configs=[ipam_pool]) + """ return { 'Subnet': subnet, 'IPRange': iprange, @@ -48,6 +80,25 @@ def create_ipam_pool(subnet=None, iprange=None, gateway=None, def create_ipam_config(driver='default', pool_configs=None): + """ + Create an IPAM (IP Address Management) config dictionary to be used with + :py:meth:`~docker.api.network.NetworkApiMixin.create_network`. + + Args: + driver (str): The IPAM driver to use. Defaults to ``default``. + pool_configs (list): A list of pool configuration dictionaries as + created by :py:meth:`~docker.utils.create_ipam_pool`. Defaults to + empty list. + + Returns: + (dict) An IPAM config. + + Example: + + >>> ipam_config = docker.utils.create_ipam_config(driver='default') + >>> network = client.create_network('network1', ipam=ipam_config) + + """ return { 'Driver': driver, 'Config': pool_configs or [] |