mirror of https://github.com/docker/compose.git
Use docker context interface from docker-py
Signed-off-by: Anca Iordache <anca.iordache@docker.com>
This commit is contained in:
parent
eb20915c6e
commit
2dfd85e30e
|
@ -8,7 +8,6 @@ import re
|
|||
import six
|
||||
|
||||
from . import errors
|
||||
from . import verbose_proxy
|
||||
from .. import config
|
||||
from .. import parallel
|
||||
from ..config.environment import Environment
|
||||
|
@ -17,10 +16,10 @@ from ..const import LABEL_CONFIG_FILES
|
|||
from ..const import LABEL_ENVIRONMENT_FILE
|
||||
from ..const import LABEL_WORKING_DIR
|
||||
from ..project import Project
|
||||
from .docker_client import docker_client
|
||||
from .docker_client import get_tls_version
|
||||
from .docker_client import tls_config_from_options
|
||||
from .utils import get_version_info
|
||||
from .docker_client import get_client
|
||||
from .docker_client import load_context
|
||||
from .docker_client import make_context
|
||||
from .errors import UserError
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -48,16 +47,28 @@ def project_from_options(project_dir, options, additional_options=None):
|
|||
environment.silent = options.get('COMMAND', None) in SILENT_COMMANDS
|
||||
set_parallel_limit(environment)
|
||||
|
||||
host = options.get('--host')
|
||||
# get the context for the run
|
||||
context = None
|
||||
context_name = options.get('--context', None)
|
||||
if context_name:
|
||||
context = load_context(context_name)
|
||||
if not context:
|
||||
raise UserError("Context '{}' not found".format(context_name))
|
||||
|
||||
host = options.get('--host', None)
|
||||
if host is not None:
|
||||
if context:
|
||||
raise UserError(
|
||||
"-H, --host and -c, --context are mutually exclusive. Only one should be set.")
|
||||
host = host.lstrip('=')
|
||||
context = make_context(host, options, environment)
|
||||
|
||||
return get_project(
|
||||
project_dir,
|
||||
get_config_path_from_options(project_dir, options, environment),
|
||||
project_name=options.get('--project-name'),
|
||||
verbose=options.get('--verbose'),
|
||||
host=host,
|
||||
tls_config=tls_config_from_options(options, environment),
|
||||
context=context,
|
||||
environment=environment,
|
||||
override_dir=override_dir,
|
||||
compatibility=compatibility_from_options(project_dir, options, environment),
|
||||
|
@ -112,25 +123,8 @@ def get_config_path_from_options(base_dir, options, environment):
|
|||
return None
|
||||
|
||||
|
||||
def get_client(environment, verbose=False, version=None, tls_config=None, host=None,
|
||||
tls_version=None):
|
||||
|
||||
client = docker_client(
|
||||
version=version, tls_config=tls_config, host=host,
|
||||
environment=environment, tls_version=get_tls_version(environment)
|
||||
)
|
||||
if verbose:
|
||||
version_info = six.iteritems(client.version())
|
||||
log.info(get_version_info('full'))
|
||||
log.info("Docker base_url: %s", client.base_url)
|
||||
log.info("Docker version: %s",
|
||||
", ".join("%s=%s" % item for item in version_info))
|
||||
return verbose_proxy.VerboseProxy('docker', client)
|
||||
return client
|
||||
|
||||
|
||||
def get_project(project_dir, config_path=None, project_name=None, verbose=False,
|
||||
host=None, tls_config=None, environment=None, override_dir=None,
|
||||
context=None, environment=None, override_dir=None,
|
||||
compatibility=False, interpolate=True, environment_file=None):
|
||||
if not environment:
|
||||
environment = Environment.from_env_file(project_dir)
|
||||
|
@ -145,8 +139,7 @@ def get_project(project_dir, config_path=None, project_name=None, verbose=False,
|
|||
API_VERSIONS[config_data.version])
|
||||
|
||||
client = get_client(
|
||||
verbose=verbose, version=api_version, tls_config=tls_config,
|
||||
host=host, environment=environment
|
||||
verbose=verbose, version=api_version, context=context, environment=environment
|
||||
)
|
||||
|
||||
with errors.handle_connection_errors(client):
|
||||
|
|
|
@ -5,17 +5,22 @@ import logging
|
|||
import os.path
|
||||
import ssl
|
||||
|
||||
import six
|
||||
from docker import APIClient
|
||||
from docker import Context
|
||||
from docker import ContextAPI
|
||||
from docker import TLSConfig
|
||||
from docker.errors import TLSParameterError
|
||||
from docker.tls import TLSConfig
|
||||
from docker.utils import kwargs_from_env
|
||||
from docker.utils.config import home_dir
|
||||
|
||||
from . import verbose_proxy
|
||||
from ..config.environment import Environment
|
||||
from ..const import HTTP_TIMEOUT
|
||||
from ..utils import unquote_path
|
||||
from .errors import UserError
|
||||
from .utils import generate_user_agent
|
||||
from .utils import get_version_info
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -24,6 +29,33 @@ def default_cert_path():
|
|||
return os.path.join(home_dir(), '.docker')
|
||||
|
||||
|
||||
def make_context(host, options, environment):
|
||||
tls = tls_config_from_options(options, environment)
|
||||
ctx = Context("compose", host=host)
|
||||
if tls:
|
||||
ctx.set_endpoint("docker", host, tls, skip_tls_verify=not tls.verify)
|
||||
return ctx
|
||||
|
||||
|
||||
def load_context(name=None):
|
||||
return ContextAPI.get_context(name)
|
||||
|
||||
|
||||
def get_client(environment, verbose=False, version=None, context=None):
|
||||
client = docker_client(
|
||||
version=version, context=context,
|
||||
environment=environment, tls_version=get_tls_version(environment)
|
||||
)
|
||||
if verbose:
|
||||
version_info = six.iteritems(client.version())
|
||||
log.info(get_version_info('full'))
|
||||
log.info("Docker base_url: %s", client.base_url)
|
||||
log.info("Docker version: %s",
|
||||
", ".join("%s=%s" % item for item in version_info))
|
||||
return verbose_proxy.VerboseProxy('docker', client)
|
||||
return client
|
||||
|
||||
|
||||
def get_tls_version(environment):
|
||||
compose_tls_version = environment.get('COMPOSE_TLS_VERSION', None)
|
||||
if not compose_tls_version:
|
||||
|
@ -87,8 +119,7 @@ def tls_config_from_options(options, environment=None):
|
|||
return None
|
||||
|
||||
|
||||
def docker_client(environment, version=None, tls_config=None, host=None,
|
||||
tls_version=None):
|
||||
def docker_client(environment, version=None, context=None, tls_version=None):
|
||||
"""
|
||||
Returns a docker-py client configured using environment variables
|
||||
according to the same logic as the official Docker client.
|
||||
|
@ -101,10 +132,21 @@ def docker_client(environment, version=None, tls_config=None, host=None,
|
|||
"and DOCKER_CERT_PATH are set correctly.\n"
|
||||
"You might need to run `eval \"$(docker-machine env default)\"`")
|
||||
|
||||
if host:
|
||||
kwargs['base_url'] = host
|
||||
if tls_config:
|
||||
kwargs['tls'] = tls_config
|
||||
if not context:
|
||||
# check env for DOCKER_HOST and certs path
|
||||
host = kwargs.get("base_url", None)
|
||||
tls = kwargs.get("tls", None)
|
||||
verify = False if not tls else tls.verify
|
||||
if host:
|
||||
context = Context("compose", host=host)
|
||||
else:
|
||||
context = ContextAPI.get_current_context()
|
||||
if tls:
|
||||
context.set_endpoint("docker", host=host, tls_cfg=tls, skip_tls_verify=not verify)
|
||||
|
||||
kwargs['base_url'] = context.Host
|
||||
if context.TLSConfig:
|
||||
kwargs['tls'] = context.TLSConfig
|
||||
|
||||
if version:
|
||||
kwargs['version'] = version
|
||||
|
|
|
@ -192,6 +192,7 @@ class TopLevelCommand(object):
|
|||
(default: docker-compose.yml)
|
||||
-p, --project-name NAME Specify an alternate project name
|
||||
(default: directory name)
|
||||
-c, --context NAME Specify a context name
|
||||
--verbose Show more output
|
||||
--log-level LEVEL Set log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
||||
--no-ansi Do not print ANSI control characters
|
||||
|
|
|
@ -4,7 +4,7 @@ cached-property==1.5.1
|
|||
certifi==2019.11.28
|
||||
chardet==3.0.4
|
||||
colorama==0.4.3; sys_platform == 'win32'
|
||||
docker==4.1.0
|
||||
docker==4.2.0
|
||||
docker-pycreds==0.4.0
|
||||
dockerpty==0.4.1
|
||||
docopt==0.6.2
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import unittest
|
||||
|
||||
from docker import ContextAPI
|
||||
|
||||
from tests.acceptance.cli_test import dispatch
|
||||
|
||||
|
||||
class ContextTestCase(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.docker_dir = os.path.join(os.environ.get("HOME", "/tmp"), '.docker')
|
||||
if not os.path.exists(cls.docker_dir):
|
||||
os.makedirs(cls.docker_dir)
|
||||
f = open(os.path.join(cls.docker_dir, "config.json"), "w")
|
||||
f.write("{}")
|
||||
f.close()
|
||||
cls.docker_config = os.path.join(cls.docker_dir, "config.json")
|
||||
os.environ['DOCKER_CONFIG'] = cls.docker_config
|
||||
ContextAPI.create_context("testcontext", host="tcp://doesnotexist:8000")
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
shutil.rmtree(cls.docker_dir, ignore_errors=True)
|
||||
|
||||
def setUp(self):
|
||||
self.base_dir = 'tests/fixtures/simple-composefile'
|
||||
self.override_dir = None
|
||||
|
||||
def dispatch(self, options, project_options=None, returncode=0, stdin=None):
|
||||
return dispatch(self.base_dir, options, project_options, returncode, stdin)
|
||||
|
||||
def test_help(self):
|
||||
result = self.dispatch(['help'], returncode=0)
|
||||
assert '-c, --context NAME' in result.stdout
|
||||
|
||||
def test_fail_on_both_host_and_context_opt(self):
|
||||
result = self.dispatch(['-H', 'unix://', '-c', 'default', 'up'], returncode=1)
|
||||
assert '-H, --host and -c, --context are mutually exclusive' in result.stderr
|
||||
|
||||
def test_fail_run_on_inexistent_context(self):
|
||||
result = self.dispatch(['-c', 'testcontext', 'up', '-d'], returncode=1)
|
||||
assert "Couldn't connect to Docker daemon" in result.stderr
|
Loading…
Reference in New Issue