# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import unicode_literals
import logging
import os
import sys
import warnings
from collections import defaultdict
from distutils.version import LooseVersion
import six
from docker.errors import DockerException
from docker.utils import (
compare_version,
convert_bool,
parse_bytes,
parse_host,
parse_repository_tag,
convert_port_string,
parse_env_file,
normalize_name,
convert_service_names,
convert_service_ports,
)
log = logging.getLogger(__name__)
try:
from ConfigParser import SafeConfigParser
except ImportError:
from configparser import SafeConfigParser
# This class is a container for various configuration settings.
class DockerClientConfig(object):
"""
This class is a container for various configuration settings.
:param config_file: Path to the configuration file to use.
:type config_file: str
:param version: Version of the Docker API to use. If not specified, the
client will attempt to auto-detect the server API version.
:type version: str
:param timeout: Default timeout for HTTP requests in seconds.
:type timeout: int
:param max_pool_size: Maximum number of connections to keep in the pool.
:type max_pool_size: int
:param base_url: Base URL of the Docker daemon.
:type base_url: str
:param tls: Use TLS to connect to the Docker daemon.
:type tls: bool
:param tlsverify: Verify TLS connections.
:type tlsverify: bool
:param cert_path: Path to TLS certificate.
:type cert_path: str
:param client_cert: Path to TLS client certificate.
:type client_cert: str
:param client_key: Path to TLS client key.
:type client_key: str
:param ca_cert: Path to the certificate authority certificate.
:type ca_cert: str
:param assert_hostname: Verify hostname of Docker daemon.
:type assert_hostname: bool
:param registry: Default registry to use when building images.
:type registry: str
:param insecure_registry: A list of insecure registries.
:type insecure_registry: list
:param email: Default email to use when pushing images.
:type email: str
:param username: Default username to use when pushing images.
:type username: str
:param password: Default password to use when pushing images.
:type password: str
:param auth_config: Dictionary containing the auth config for different
registries.
:type auth_config: dict
:param environment: A list of environment variables to set.
:type environment: list
:param http_proxy: Proxy to use for HTTP requests.
:type http_proxy: str
:param https_proxy: Proxy to use for HTTPS requests.
:type https_proxy: str
:param no_proxy: A list of hosts that should not be proxied.
:type no_proxy: list
:param stream: Default for stream argument.
:type stream: bool
:param raise_for_status: Raise for API response errors.
:type raise_for_status: bool
:param api_version: Use a specific API version.
:type api_version: str
:param version: Version of the Docker API to use. If not specified, the
client will attempt to auto-detect the server API version.
:type version: str
:param user_agent: Set a custom user agent.
:type user_agent: str
:param universal_fe_compatible: Whether to use a universal feed endpoint
:type universal_fe_compatible: bool
:param log_level: Set the log level for the client.
:type log_level: int
:param log_config: Set the log configuration for the client.
:type log_config: dict
:param machine_name: The name of the machine.
:type machine_name: str
:param machine_storage_path: The path to the machine storage.
:type machine_storage_path: str
:param tls_hostname: The TLS hostname to use for the Docker daemon.
:type tls_hostname: str
:param platform: Platform to use for building images.
:type platform: str
:param experimental: Enable experimental features.
:type experimental: bool
:param image_load_parallelism: The maximum number of image layers that
will be loaded in parallel.
:type image_load_parallelism: int
:param network_mode: Default network mode for containers.
:type network_mode: str
:param default_command_shell: Default command shell for containers.
:type default_command_shell: str
:param default_log_driver: Default log driver for containers.
:type default_log_driver: str
:param system_context: Dictionary containing system context values.
:type system_context: dict
:param network_alias: A map of network aliases for containers.
:type network_alias: dict
:param default_backend: The default backend to use for operations.
:type default_backend: str
:param client_timeout: Client timeout in seconds.
:type client_timeout: int
:param max_concurrent_builds: The maximum number of builds to run concurrently.
:type max_concurrent_builds: int
:param max_concurrent_dind_builds: The maximum number of builds to run concurrently for dind builds.
:type max_concurrent_dind_builds: int
:param version_check: Check the Docker API version of the server on startup.
:type version_check: bool
:param build_cache_max_size: The maximum size of the build cache in bytes.
:type build_cache_max_size: int
:raises: :py:exc:`~docker.errors.DockerException`
"""
def __init__(
self,
config_file=None,
version=None,
timeout=None,
max_pool_size=None,
base_url=None,
tls=None,
tlsverify=None,
cert_path=None,
client_cert=None,
client_key=None,
ca_cert=None,
assert_hostname=None,
registry=None,
insecure_registry=None,
email=None,
username=None,
password=None,
auth_config=None,
environment=None,
http_proxy=None,
https_proxy=None,
no_proxy=None,
stream=None,
raise_for_status=None,
api_version=None,
user_agent=None,
universal_fe_compatible=None,
log_level=None,
log_config=None,
machine_name=None,
machine_storage_path=None,
tls_hostname=None,
platform=None,
experimental=None,
image_load_parallelism=None,
network_mode=None,
default_command_shell=None,
default_log_driver=None,
system_context=None,
network_alias=None,
default_backend=None,
client_timeout=None,
max_concurrent_builds=None,
max_concurrent_dind_builds=None,
version_check=None,
build_cache_max_size=None,
):
self.config_file = config_file
self._base_url = base_url
self._tls = tls
self._tlsverify = tlsverify
self._cert_path = cert_path
self._client_cert = client_cert
self._client_key = client_key
self._ca_cert = ca_cert
self._assert_hostname = assert_hostname
self._registry = registry
self._insecure_registry = insecure_registry
self._email = email
self._username = username
self._password = password
self.auth_config = auth_config or {}
self._environment = environment
self._http_proxy = http_proxy
self._https_proxy = https_proxy
self._no_proxy = no_proxy
self._stream = stream
self._raise_for_status = raise_for_status
self._api_version = api_version
self.user_agent = user_agent
self._universal_fe_compatible = universal_fe_compatible
self._log_level = log_level
self._log_config = log_config
self._machine_name = machine_name
self._machine_storage_path = machine_storage_path
self._tls_hostname = tls_hostname
self._platform = platform
self._experimental = experimental
self._image_load_parallelism = image_load_parallelism
self._network_mode = network_mode
self._default_command_shell = default_command_shell
self._default_log_driver = default_log_driver
self._system_context = system_context or {}
self._network_alias = network_alias
self._default_backend = default_backend
self._client_timeout = client_timeout
self._max_concurrent_builds = max_concurrent_builds
self._max_concurrent_dind_builds = max_concurrent_dind_builds
self._version_check = version_check
self._build_cache_max_size = build_cache_max_size
if timeout is not None:
warnings.warn(
"The `timeout` parameter is deprecated and will be removed "
"in a future release. Use `client_timeout` instead.",
DeprecationWarning,
)
self.client_timeout = timeout
if max_pool_size is not None:
warnings.warn(
"The `max_pool_size` parameter is deprecated and will be "
"removed in a future release.",
DeprecationWarning,
)
if version is not None:
warnings.warn(
"The `version` parameter is deprecated and will be removed "
"in a future release. Use `api_version` instead.",
DeprecationWarning,
)
self.api_version = version
self._load_config(config_file)
def _get_env_config(self, config_file):
"""Return a dictionary with environment variables to be injected
from a docker environment file.
"""
if not config_file:
return None
env_file = os.path.join(os.path.dirname(config_file), ".dockerenv")
if os.path.isfile(env_file):
return parse_env_file(env_file)
return None
def _load_config(self, config_file):
"""Loads the configuration from the provided file.
:param config_file: Path to the configuration file to use.
:type config_file: str
"""
env_config = self._get_env_config(config_file)
if not config_file:
return
# Load config from .dockercfg
self.config = SafeConfigParser()
try:
self.config.read(config_file)
except Exception as e:
# If there is an error, the client can still be used,
# although it will not be able to authenticate with Docker
# Hub. This is not a critical issue for most users, so we
# just log a warning.
log.warning("Could not load config file: %s" % e)
# Set values from config file
if self.config.has_section("DEFAULT"):
# Environment
self._environment = (
self.config.get("DEFAULT", "environment").split(":")
if self.config.has_option("DEFAULT", "environment")
else None
)
# HTTP Proxy
self._http_proxy = (
self.config.get("DEFAULT", "http_proxy")
if self.config.has_option("DEFAULT", "http_proxy")
else None
)
# HTTPS Proxy
self._https_proxy = (
self.config.get("DEFAULT", "https_proxy")
if self.config.has_option("DEFAULT", "https_proxy")
else None
)
# No Proxy
self._no_proxy = (
self.config.get("DEFAULT", "no_proxy").split(",")
if self.config.has_option("DEFAULT", "no_proxy")
else None
)
# Registry
self._registry = (
self.config.get("DEFAULT", "registry")
if self.config.has_option("DEFAULT", "registry")
else None
)
# Insecure registry
self._insecure_registry = (
self.config.get("DEFAULT", "insecure-registry").split(",")
if self.config.has_option("DEFAULT", "insecure-registry")
else None
)
# Auth config
if self.config.has_option("DEFAULT", "auths"):
auths = self.config.get("DEFAULT", "auths")
try:
self.auth_config = eval(auths)
except Exception as e:
log.warning(
"Error parsing auths config in %s: %s" % (config_file, e)
)
if isinstance(self.auth_config, dict):
self.auth_config = defaultdict(dict, self.auth_config)
# Use environment vars if available
if env_config:
for k, v in env_config.items():
if isinstance(v, six.string_types):
# Some environment variables may be in a format like
# "https://user:pass@host:port"
# Docker uses URL quoting to allow the use of
# special characters, so we need to unquote them
# before using the environment variable as password
# or username
if "://" in v:
(
protocol,
host_auth,
) = v.split("://", 1)
if "@" in host_auth:
(
user_pass,
host_port,
) = host_auth.split("@", 1)
(
username,
password,
) = user_pass.split(":", 1)
self.auth_config[host_port]["username"] = (
username
)
self.auth_config[host_port]["password"] = (
password
)
self.auth_config[k]["username"] = (
env_config.get(k, {}).get("username", None)
)
self.auth_config[k]["password"] = (
env_config.get(k, {}).get("password", None)
)
self.auth_config[k]["email"] = (
env_config.get(k, {}).get("email", None)
)
self.auth_config[k]["auth"] = (
env_config.get(k, {}).get("auth", None)
)
for section in self.config.sections():
if not section == "DEFAULT":
auth_config = self.auth_config[section] = {}
if self.config.has_option(section, "username"):
auth_config["username"] = self.config.get(
section, "username"
)
if self.config.has_option(section, "password"):
auth_config["password"] = self.config.get(
section, "password"
)
if self.config.has_option(section, "email"):
auth_config["email"] = self.config.get(
section, "email"
)
if self.config.has_option(section, "auth"):
auth_config["auth"] = self.config.get(
section, "auth"
)
def _get_base_url(self):
"""Retrieve the base URL of the Docker daemon.
This method considers the environment variables and
configuration file.
"""
if self._base_url:
return self._base_url
# Environment Variables
if "DOCKER_HOST" in os.environ:
host = parse_host(os.environ["DOCKER_HOST"])
base_url = "http" + ("://" + host if host else "")
return base_url
# Configuration File
if self.config.has_section("DEFAULT"):
if self.config.has_option("DEFAULT", "url"):
base_url = self.config.get("DEFAULT", "url")