mirror of
https://github.com/docker/compose.git
synced 2025-07-22 21:24:38 +02:00
Merge pull request #3139 from shin-/1716-tls-flags-support
Add support for TLS config command-line options
This commit is contained in:
commit
2be2029791
@ -12,6 +12,7 @@ from .. import config
|
|||||||
from ..const import API_VERSIONS
|
from ..const import API_VERSIONS
|
||||||
from ..project import Project
|
from ..project import Project
|
||||||
from .docker_client import docker_client
|
from .docker_client import docker_client
|
||||||
|
from .docker_client import tls_config_from_options
|
||||||
from .utils import get_version_info
|
from .utils import get_version_info
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -23,6 +24,8 @@ def project_from_options(project_dir, options):
|
|||||||
get_config_path_from_options(options),
|
get_config_path_from_options(options),
|
||||||
project_name=options.get('--project-name'),
|
project_name=options.get('--project-name'),
|
||||||
verbose=options.get('--verbose'),
|
verbose=options.get('--verbose'),
|
||||||
|
host=options.get('--host'),
|
||||||
|
tls_config=tls_config_from_options(options),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -37,8 +40,8 @@ def get_config_path_from_options(options):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def get_client(verbose=False, version=None):
|
def get_client(verbose=False, version=None, tls_config=None, host=None):
|
||||||
client = docker_client(version=version)
|
client = docker_client(version=version, tls_config=tls_config, host=host)
|
||||||
if verbose:
|
if verbose:
|
||||||
version_info = six.iteritems(client.version())
|
version_info = six.iteritems(client.version())
|
||||||
log.info(get_version_info('full'))
|
log.info(get_version_info('full'))
|
||||||
@ -49,7 +52,8 @@ def get_client(verbose=False, version=None):
|
|||||||
return client
|
return client
|
||||||
|
|
||||||
|
|
||||||
def get_project(project_dir, config_path=None, project_name=None, verbose=False):
|
def get_project(project_dir, config_path=None, project_name=None, verbose=False,
|
||||||
|
host=None, tls_config=None):
|
||||||
config_details = config.find(project_dir, config_path)
|
config_details = config.find(project_dir, config_path)
|
||||||
project_name = get_project_name(config_details.working_dir, project_name)
|
project_name = get_project_name(config_details.working_dir, project_name)
|
||||||
config_data = config.load(config_details)
|
config_data = config.load(config_details)
|
||||||
@ -57,7 +61,10 @@ def get_project(project_dir, config_path=None, project_name=None, verbose=False)
|
|||||||
api_version = os.environ.get(
|
api_version = os.environ.get(
|
||||||
'COMPOSE_API_VERSION',
|
'COMPOSE_API_VERSION',
|
||||||
API_VERSIONS[config_data.version])
|
API_VERSIONS[config_data.version])
|
||||||
client = get_client(verbose=verbose, version=api_version)
|
client = get_client(
|
||||||
|
verbose=verbose, version=api_version, tls_config=tls_config,
|
||||||
|
host=host
|
||||||
|
)
|
||||||
|
|
||||||
return Project.from_config(project_name, config_data, client)
|
return Project.from_config(project_name, config_data, client)
|
||||||
|
|
||||||
|
@ -6,7 +6,9 @@ import os
|
|||||||
|
|
||||||
from docker import Client
|
from docker import Client
|
||||||
from docker.errors import TLSParameterError
|
from docker.errors import TLSParameterError
|
||||||
|
from docker.tls import TLSConfig
|
||||||
from docker.utils import kwargs_from_env
|
from docker.utils import kwargs_from_env
|
||||||
|
from requests.utils import urlparse
|
||||||
|
|
||||||
from ..const import HTTP_TIMEOUT
|
from ..const import HTTP_TIMEOUT
|
||||||
from .errors import UserError
|
from .errors import UserError
|
||||||
@ -14,7 +16,33 @@ from .errors import UserError
|
|||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def docker_client(version=None):
|
def tls_config_from_options(options):
|
||||||
|
tls = options.get('--tls', False)
|
||||||
|
ca_cert = options.get('--tlscacert')
|
||||||
|
cert = options.get('--tlscert')
|
||||||
|
key = options.get('--tlskey')
|
||||||
|
verify = options.get('--tlsverify')
|
||||||
|
hostname = urlparse(options.get('--host') or '').hostname
|
||||||
|
|
||||||
|
advanced_opts = any([ca_cert, cert, key, verify])
|
||||||
|
|
||||||
|
if tls is True and not advanced_opts:
|
||||||
|
return True
|
||||||
|
elif advanced_opts:
|
||||||
|
client_cert = None
|
||||||
|
if cert or key:
|
||||||
|
client_cert = (cert, key)
|
||||||
|
return TLSConfig(
|
||||||
|
client_cert=client_cert, verify=verify, ca_cert=ca_cert,
|
||||||
|
assert_hostname=(
|
||||||
|
hostname or not options.get('--skip-hostname-check', False)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def docker_client(version=None, tls_config=None, host=None):
|
||||||
"""
|
"""
|
||||||
Returns a docker-py client configured using environment variables
|
Returns a docker-py client configured using environment variables
|
||||||
according to the same logic as the official Docker client.
|
according to the same logic as the official Docker client.
|
||||||
@ -31,6 +59,11 @@ def docker_client(version=None):
|
|||||||
"and DOCKER_CERT_PATH are set correctly.\n"
|
"and DOCKER_CERT_PATH are set correctly.\n"
|
||||||
"You might need to run `eval \"$(docker-machine env default)\"`")
|
"You might need to run `eval \"$(docker-machine env default)\"`")
|
||||||
|
|
||||||
|
if host:
|
||||||
|
kwargs['base_url'] = host
|
||||||
|
if tls_config:
|
||||||
|
kwargs['tls'] = tls_config
|
||||||
|
|
||||||
if version:
|
if version:
|
||||||
kwargs['version'] = version
|
kwargs['version'] = version
|
||||||
|
|
||||||
|
@ -145,10 +145,20 @@ class TopLevelCommand(object):
|
|||||||
docker-compose -h|--help
|
docker-compose -h|--help
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-f, --file FILE Specify an alternate compose file (default: docker-compose.yml)
|
-f, --file FILE Specify an alternate compose file (default: docker-compose.yml)
|
||||||
-p, --project-name NAME Specify an alternate project name (default: directory name)
|
-p, --project-name NAME Specify an alternate project name (default: directory name)
|
||||||
--verbose Show more output
|
--verbose Show more output
|
||||||
-v, --version Print version and exit
|
-v, --version Print version and exit
|
||||||
|
-H, --host HOST Daemon socket to connect to
|
||||||
|
|
||||||
|
--tls Use TLS; implied by --tlsverify
|
||||||
|
--tlscacert CA_PATH Trust certs signed only by this CA
|
||||||
|
--tlscert CLIENT_CERT_PATH Path to TLS certificate file
|
||||||
|
--tlskey TLS_KEY_PATH Path to TLS key file
|
||||||
|
--tlsverify Use TLS and verify the remote
|
||||||
|
--skip-hostname-check Don't check the daemon's hostname against the name specified
|
||||||
|
in the client certificate (for example if your docker host
|
||||||
|
is an IP address)
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
build Build or rebuild services
|
build Build or rebuild services
|
||||||
|
@ -3,7 +3,7 @@ cached-property==1.2.0
|
|||||||
dockerpty==0.4.1
|
dockerpty==0.4.1
|
||||||
docopt==0.6.1
|
docopt==0.6.1
|
||||||
enum34==1.0.4
|
enum34==1.0.4
|
||||||
git+https://github.com/docker/docker-py.git@8c4546f8c8f52bb2923834783a17beb5bb89a724#egg=docker-py
|
git+https://github.com/docker/docker-py.git@5c1c42397cf0fdb74182df2d69822b82df8f2a6a#egg=docker-py
|
||||||
jsonschema==2.5.1
|
jsonschema==2.5.1
|
||||||
requests==2.7.0
|
requests==2.7.0
|
||||||
six==1.7.3
|
six==1.7.3
|
||||||
|
0
tests/fixtures/tls/ca.pem
vendored
Normal file
0
tests/fixtures/tls/ca.pem
vendored
Normal file
0
tests/fixtures/tls/cert.pem
vendored
Normal file
0
tests/fixtures/tls/cert.pem
vendored
Normal file
0
tests/fixtures/tls/key.key
vendored
Normal file
0
tests/fixtures/tls/key.key
vendored
Normal file
@ -3,7 +3,11 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from compose.cli import docker_client
|
import docker
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from compose.cli.docker_client import docker_client
|
||||||
|
from compose.cli.docker_client import tls_config_from_options
|
||||||
from tests import mock
|
from tests import mock
|
||||||
from tests import unittest
|
from tests import unittest
|
||||||
|
|
||||||
@ -13,10 +17,89 @@ class DockerClientTestCase(unittest.TestCase):
|
|||||||
def test_docker_client_no_home(self):
|
def test_docker_client_no_home(self):
|
||||||
with mock.patch.dict(os.environ):
|
with mock.patch.dict(os.environ):
|
||||||
del os.environ['HOME']
|
del os.environ['HOME']
|
||||||
docker_client.docker_client()
|
docker_client()
|
||||||
|
|
||||||
def test_docker_client_with_custom_timeout(self):
|
def test_docker_client_with_custom_timeout(self):
|
||||||
timeout = 300
|
timeout = 300
|
||||||
with mock.patch('compose.cli.docker_client.HTTP_TIMEOUT', 300):
|
with mock.patch('compose.cli.docker_client.HTTP_TIMEOUT', 300):
|
||||||
client = docker_client.docker_client()
|
client = docker_client()
|
||||||
self.assertEqual(client.timeout, int(timeout))
|
self.assertEqual(client.timeout, int(timeout))
|
||||||
|
|
||||||
|
|
||||||
|
class TLSConfigTestCase(unittest.TestCase):
|
||||||
|
ca_cert = 'tests/fixtures/tls/ca.pem'
|
||||||
|
client_cert = 'tests/fixtures/tls/cert.pem'
|
||||||
|
key = 'tests/fixtures/tls/key.key'
|
||||||
|
|
||||||
|
def test_simple_tls(self):
|
||||||
|
options = {'--tls': True}
|
||||||
|
result = tls_config_from_options(options)
|
||||||
|
assert result is True
|
||||||
|
|
||||||
|
def test_tls_ca_cert(self):
|
||||||
|
options = {
|
||||||
|
'--tlscacert': self.ca_cert, '--tlsverify': True
|
||||||
|
}
|
||||||
|
result = tls_config_from_options(options)
|
||||||
|
assert isinstance(result, docker.tls.TLSConfig)
|
||||||
|
assert result.ca_cert == options['--tlscacert']
|
||||||
|
assert result.verify is True
|
||||||
|
|
||||||
|
def test_tls_ca_cert_explicit(self):
|
||||||
|
options = {
|
||||||
|
'--tlscacert': self.ca_cert, '--tls': True,
|
||||||
|
'--tlsverify': True
|
||||||
|
}
|
||||||
|
result = tls_config_from_options(options)
|
||||||
|
assert isinstance(result, docker.tls.TLSConfig)
|
||||||
|
assert result.ca_cert == options['--tlscacert']
|
||||||
|
assert result.verify is True
|
||||||
|
|
||||||
|
def test_tls_client_cert(self):
|
||||||
|
options = {
|
||||||
|
'--tlscert': self.client_cert, '--tlskey': self.key
|
||||||
|
}
|
||||||
|
result = tls_config_from_options(options)
|
||||||
|
assert isinstance(result, docker.tls.TLSConfig)
|
||||||
|
assert result.cert == (options['--tlscert'], options['--tlskey'])
|
||||||
|
|
||||||
|
def test_tls_client_cert_explicit(self):
|
||||||
|
options = {
|
||||||
|
'--tlscert': self.client_cert, '--tlskey': self.key,
|
||||||
|
'--tls': True
|
||||||
|
}
|
||||||
|
result = tls_config_from_options(options)
|
||||||
|
assert isinstance(result, docker.tls.TLSConfig)
|
||||||
|
assert result.cert == (options['--tlscert'], options['--tlskey'])
|
||||||
|
|
||||||
|
def test_tls_client_and_ca(self):
|
||||||
|
options = {
|
||||||
|
'--tlscert': self.client_cert, '--tlskey': self.key,
|
||||||
|
'--tlsverify': True, '--tlscacert': self.ca_cert
|
||||||
|
}
|
||||||
|
result = tls_config_from_options(options)
|
||||||
|
assert isinstance(result, docker.tls.TLSConfig)
|
||||||
|
assert result.cert == (options['--tlscert'], options['--tlskey'])
|
||||||
|
assert result.ca_cert == options['--tlscacert']
|
||||||
|
assert result.verify is True
|
||||||
|
|
||||||
|
def test_tls_client_and_ca_explicit(self):
|
||||||
|
options = {
|
||||||
|
'--tlscert': self.client_cert, '--tlskey': self.key,
|
||||||
|
'--tlsverify': True, '--tlscacert': self.ca_cert,
|
||||||
|
'--tls': True
|
||||||
|
}
|
||||||
|
result = tls_config_from_options(options)
|
||||||
|
assert isinstance(result, docker.tls.TLSConfig)
|
||||||
|
assert result.cert == (options['--tlscert'], options['--tlskey'])
|
||||||
|
assert result.ca_cert == options['--tlscacert']
|
||||||
|
assert result.verify is True
|
||||||
|
|
||||||
|
def test_tls_client_missing_key(self):
|
||||||
|
options = {'--tlscert': self.client_cert}
|
||||||
|
with pytest.raises(docker.errors.TLSParameterError):
|
||||||
|
tls_config_from_options(options)
|
||||||
|
|
||||||
|
options = {'--tlskey': self.key}
|
||||||
|
with pytest.raises(docker.errors.TLSParameterError):
|
||||||
|
tls_config_from_options(options)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user