mirror of https://github.com/docker/docs.git
Add docker-compose config subcommand.
Signed-off-by: Daniel Nephin <dnephin@docker.com>
This commit is contained in:
parent
ecf2dd11f9
commit
437f3f8adb
|
@ -46,7 +46,7 @@ def friendly_error_message():
|
||||||
def project_from_options(base_dir, options):
|
def project_from_options(base_dir, options):
|
||||||
return get_project(
|
return get_project(
|
||||||
base_dir,
|
base_dir,
|
||||||
get_config_path(options.get('--file')),
|
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'),
|
||||||
use_networking=options.get('--x-networking'),
|
use_networking=options.get('--x-networking'),
|
||||||
|
@ -54,7 +54,8 @@ def project_from_options(base_dir, options):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_config_path(file_option):
|
def get_config_path_from_options(options):
|
||||||
|
file_option = options.get('--file')
|
||||||
if file_option:
|
if file_option:
|
||||||
return file_option
|
return file_option
|
||||||
|
|
||||||
|
|
|
@ -8,10 +8,12 @@ import sys
|
||||||
from inspect import getdoc
|
from inspect import getdoc
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
|
|
||||||
|
import yaml
|
||||||
from docker.errors import APIError
|
from docker.errors import APIError
|
||||||
from requests.exceptions import ReadTimeout
|
from requests.exceptions import ReadTimeout
|
||||||
|
|
||||||
from .. import __version__
|
from .. import __version__
|
||||||
|
from ..config import config
|
||||||
from ..config import ConfigurationError
|
from ..config import ConfigurationError
|
||||||
from ..config import parse_environment
|
from ..config import parse_environment
|
||||||
from ..const import DEFAULT_TIMEOUT
|
from ..const import DEFAULT_TIMEOUT
|
||||||
|
@ -23,6 +25,7 @@ from ..service import BuildError
|
||||||
from ..service import ConvergenceStrategy
|
from ..service import ConvergenceStrategy
|
||||||
from ..service import NeedsBuildError
|
from ..service import NeedsBuildError
|
||||||
from .command import friendly_error_message
|
from .command import friendly_error_message
|
||||||
|
from .command import get_config_path_from_options
|
||||||
from .command import project_from_options
|
from .command import project_from_options
|
||||||
from .docopt_command import DocoptCommand
|
from .docopt_command import DocoptCommand
|
||||||
from .docopt_command import NoSuchCommand
|
from .docopt_command import NoSuchCommand
|
||||||
|
@ -126,6 +129,7 @@ class TopLevelCommand(DocoptCommand):
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
build Build or rebuild services
|
build Build or rebuild services
|
||||||
|
config Validate and view the compose file
|
||||||
help Get help on a command
|
help Get help on a command
|
||||||
kill Kill containers
|
kill Kill containers
|
||||||
logs View output from containers
|
logs View output from containers
|
||||||
|
@ -158,6 +162,10 @@ class TopLevelCommand(DocoptCommand):
|
||||||
handler(None, command_options)
|
handler(None, command_options)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if options['COMMAND'] == 'config':
|
||||||
|
handler(options, command_options)
|
||||||
|
return
|
||||||
|
|
||||||
project = project_from_options(self.base_dir, options)
|
project = project_from_options(self.base_dir, options)
|
||||||
with friendly_error_message():
|
with friendly_error_message():
|
||||||
handler(project, command_options)
|
handler(project, command_options)
|
||||||
|
@ -183,6 +191,36 @@ class TopLevelCommand(DocoptCommand):
|
||||||
pull=bool(options.get('--pull', False)),
|
pull=bool(options.get('--pull', False)),
|
||||||
force_rm=bool(options.get('--force-rm', False)))
|
force_rm=bool(options.get('--force-rm', False)))
|
||||||
|
|
||||||
|
def config(self, config_options, options):
|
||||||
|
"""
|
||||||
|
Validate and view the compose file.
|
||||||
|
|
||||||
|
Usage: config [options]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-q, --quiet Only validate the configuration, don't print
|
||||||
|
anything.
|
||||||
|
--services Print the service names, one per line.
|
||||||
|
|
||||||
|
"""
|
||||||
|
config_path = get_config_path_from_options(config_options)
|
||||||
|
compose_config = config.load(config.find(self.base_dir, config_path))
|
||||||
|
|
||||||
|
if options['--quiet']:
|
||||||
|
return
|
||||||
|
|
||||||
|
if options['--services']:
|
||||||
|
print('\n'.join(service['name'] for service in compose_config))
|
||||||
|
return
|
||||||
|
|
||||||
|
compose_config = dict(
|
||||||
|
(service.pop('name'), service) for service in compose_config)
|
||||||
|
print(yaml.dump(
|
||||||
|
compose_config,
|
||||||
|
default_flow_style=False,
|
||||||
|
indent=2,
|
||||||
|
width=80))
|
||||||
|
|
||||||
def help(self, project, options):
|
def help(self, project, options):
|
||||||
"""
|
"""
|
||||||
Get help on a command.
|
Get help on a command.
|
||||||
|
|
|
@ -7,6 +7,7 @@ import subprocess
|
||||||
import time
|
import time
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
|
from textwrap import dedent
|
||||||
|
|
||||||
from docker import errors
|
from docker import errors
|
||||||
|
|
||||||
|
@ -90,10 +91,11 @@ class CLITestCase(DockerClientTestCase):
|
||||||
self.base_dir = 'tests/fixtures/simple-composefile'
|
self.base_dir = 'tests/fixtures/simple-composefile'
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
self.project.kill()
|
if self.base_dir:
|
||||||
self.project.remove_stopped()
|
self.project.kill()
|
||||||
for container in self.project.containers(stopped=True, one_off=True):
|
self.project.remove_stopped()
|
||||||
container.remove(force=True)
|
for container in self.project.containers(stopped=True, one_off=True):
|
||||||
|
container.remove(force=True)
|
||||||
super(CLITestCase, self).tearDown()
|
super(CLITestCase, self).tearDown()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -109,13 +111,39 @@ class CLITestCase(DockerClientTestCase):
|
||||||
return wait_on_process(proc, returncode=returncode)
|
return wait_on_process(proc, returncode=returncode)
|
||||||
|
|
||||||
def test_help(self):
|
def test_help(self):
|
||||||
old_base_dir = self.base_dir
|
|
||||||
self.base_dir = 'tests/fixtures/no-composefile'
|
self.base_dir = 'tests/fixtures/no-composefile'
|
||||||
result = self.dispatch(['help', 'up'], returncode=1)
|
result = self.dispatch(['help', 'up'], returncode=1)
|
||||||
assert 'Usage: up [options] [SERVICE...]' in result.stderr
|
assert 'Usage: up [options] [SERVICE...]' in result.stderr
|
||||||
# self.project.kill() fails during teardown
|
# Prevent tearDown from trying to create a project
|
||||||
# unless there is a composefile.
|
self.base_dir = None
|
||||||
self.base_dir = old_base_dir
|
|
||||||
|
def test_config_list_services(self):
|
||||||
|
result = self.dispatch(['config', '--services'])
|
||||||
|
assert set(result.stdout.rstrip().split('\n')) == {'simple', 'another'}
|
||||||
|
|
||||||
|
def test_config_quiet_with_error(self):
|
||||||
|
self.base_dir = None
|
||||||
|
result = self.dispatch([
|
||||||
|
'-f', 'tests/fixtures/invalid-composefile/invalid.yml',
|
||||||
|
'config', '-q'
|
||||||
|
], returncode=1)
|
||||||
|
assert "'notaservice' doesn't have any configuration" in result.stderr
|
||||||
|
|
||||||
|
def test_config_quiet(self):
|
||||||
|
assert self.dispatch(['config', '-q']).stdout == ''
|
||||||
|
|
||||||
|
def test_config_default(self):
|
||||||
|
result = self.dispatch(['config'])
|
||||||
|
assert dedent("""
|
||||||
|
simple:
|
||||||
|
command: top
|
||||||
|
image: busybox:latest
|
||||||
|
""").lstrip() in result.stdout
|
||||||
|
assert dedent("""
|
||||||
|
another:
|
||||||
|
command: top
|
||||||
|
image: busybox:latest
|
||||||
|
""").lstrip() in result.stdout
|
||||||
|
|
||||||
def test_ps(self):
|
def test_ps(self):
|
||||||
self.project.get_service('simple').create_container()
|
self.project.get_service('simple').create_container()
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
notaservice: oops
|
||||||
|
|
||||||
|
web:
|
||||||
|
image: 'alpine:edge'
|
Loading…
Reference in New Issue