mirror of https://github.com/docker/compose.git
Resolves #369, add verbose output on --verbose flag
Signed-off-by: Daniel Nephin <dnephin@gmail.com>
This commit is contained in:
parent
f2bf7f9e0d
commit
df7c2cc43f
|
@ -12,9 +12,10 @@ from ..packages import six
|
|||
from ..project import Project
|
||||
from ..service import ConfigError
|
||||
from .docopt_command import DocoptCommand
|
||||
from .formatter import Formatter
|
||||
from .utils import cached_property, docker_url, call_silently, is_mac, is_ubuntu
|
||||
from .utils import docker_url, call_silently, is_mac, is_ubuntu
|
||||
from . import verbose_proxy
|
||||
from . import errors
|
||||
from .. import __version__
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -22,10 +23,6 @@ log = logging.getLogger(__name__)
|
|||
class Command(DocoptCommand):
|
||||
base_dir = '.'
|
||||
|
||||
def __init__(self):
|
||||
self._yaml_path = os.environ.get('FIG_FILE', None)
|
||||
self.explicit_project_name = None
|
||||
|
||||
def dispatch(self, *args, **kwargs):
|
||||
try:
|
||||
super(Command, self).dispatch(*args, **kwargs)
|
||||
|
@ -40,60 +37,70 @@ class Command(DocoptCommand):
|
|||
elif call_silently(['which', 'docker-osx']) == 0:
|
||||
raise errors.ConnectionErrorDockerOSX()
|
||||
else:
|
||||
raise errors.ConnectionErrorGeneric(self.client.base_url)
|
||||
raise errors.ConnectionErrorGeneric(self.get_client().base_url)
|
||||
|
||||
def perform_command(self, options, *args, **kwargs):
|
||||
if options['--file'] is not None:
|
||||
self.yaml_path = os.path.join(self.base_dir, options['--file'])
|
||||
if options['--project-name'] is not None:
|
||||
self.explicit_project_name = options['--project-name']
|
||||
return super(Command, self).perform_command(options, *args, **kwargs)
|
||||
def perform_command(self, options, handler, command_options):
|
||||
explicit_config_path = options.get('--file') or os.environ.get('FIG_FILE')
|
||||
project = self.get_project(
|
||||
self.get_config_path(explicit_config_path),
|
||||
project_name=options.get('--project-name'),
|
||||
verbose=options.get('--verbose'))
|
||||
|
||||
@cached_property
|
||||
def client(self):
|
||||
return Client(docker_url())
|
||||
handler(project, command_options)
|
||||
|
||||
@cached_property
|
||||
def project(self):
|
||||
def get_client(self, verbose=False):
|
||||
client = Client(docker_url())
|
||||
if verbose:
|
||||
version_info = six.iteritems(client.version())
|
||||
log.info("Fig version %s", __version__)
|
||||
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_config(self, config_path):
|
||||
try:
|
||||
config = yaml.safe_load(open(self.yaml_path))
|
||||
with open(config_path, 'r') as fh:
|
||||
return yaml.safe_load(fh)
|
||||
except IOError as e:
|
||||
if e.errno == errno.ENOENT:
|
||||
raise errors.FigFileNotFound(os.path.basename(e.filename))
|
||||
raise errors.UserError(six.text_type(e))
|
||||
|
||||
def get_project(self, config_path, project_name=None, verbose=False):
|
||||
try:
|
||||
return Project.from_config(self.project_name, config, self.client)
|
||||
return Project.from_config(
|
||||
self.get_project_name(config_path, project_name),
|
||||
self.get_config(config_path),
|
||||
self.get_client(verbose=verbose))
|
||||
except ConfigError as e:
|
||||
raise errors.UserError(six.text_type(e))
|
||||
|
||||
@cached_property
|
||||
def project_name(self):
|
||||
project = os.path.basename(os.path.dirname(os.path.abspath(self.yaml_path)))
|
||||
if self.explicit_project_name is not None:
|
||||
project = self.explicit_project_name
|
||||
project = re.sub(r'[^a-zA-Z0-9]', '', project)
|
||||
if not project:
|
||||
project = 'default'
|
||||
return project
|
||||
def get_project_name(self, config_path, project_name=None):
|
||||
def normalize_name(name):
|
||||
return re.sub(r'[^a-zA-Z0-9]', '', name)
|
||||
|
||||
@cached_property
|
||||
def formatter(self):
|
||||
return Formatter()
|
||||
if project_name is not None:
|
||||
return normalize_name(project_name)
|
||||
|
||||
@cached_property
|
||||
def yaml_path(self):
|
||||
if self._yaml_path is not None:
|
||||
return self._yaml_path
|
||||
elif os.path.exists(os.path.join(self.base_dir, 'fig.yaml')):
|
||||
project = os.path.basename(os.path.dirname(os.path.abspath(config_path)))
|
||||
if project:
|
||||
return normalize_name(project)
|
||||
|
||||
log.warning("Fig just read the file 'fig.yaml' on startup, rather than 'fig.yml'")
|
||||
log.warning("Please be aware that fig.yml the expected extension in most cases, and using .yaml can cause compatibility issues in future")
|
||||
return 'default'
|
||||
|
||||
def get_config_path(self, file_path=None):
|
||||
if file_path:
|
||||
return os.path.join(self.base_dir, file_path)
|
||||
|
||||
if os.path.exists(os.path.join(self.base_dir, 'fig.yaml')):
|
||||
log.warning("Fig just read the file 'fig.yaml' on startup, rather "
|
||||
"than 'fig.yml'")
|
||||
log.warning("Please be aware that fig.yml the expected extension "
|
||||
"in most cases, and using .yaml can cause compatibility "
|
||||
"issues in future")
|
||||
|
||||
return os.path.join(self.base_dir, 'fig.yaml')
|
||||
else:
|
||||
return os.path.join(self.base_dir, 'fig.yml')
|
||||
|
||||
@yaml_path.setter
|
||||
def yaml_path(self, value):
|
||||
self._yaml_path = value
|
||||
return os.path.join(self.base_dir, 'fig.yml')
|
||||
|
|
|
@ -23,7 +23,7 @@ class DocoptCommand(object):
|
|||
def dispatch(self, argv, global_options):
|
||||
self.perform_command(*self.parse(argv, global_options))
|
||||
|
||||
def perform_command(self, options, command, handler, command_options):
|
||||
def perform_command(self, options, handler, command_options):
|
||||
handler(command_options)
|
||||
|
||||
def parse(self, argv, global_options):
|
||||
|
@ -43,7 +43,7 @@ class DocoptCommand(object):
|
|||
raise NoSuchCommand(command, self)
|
||||
|
||||
command_options = docopt_full_help(docstring, options['ARGS'], options_first=True)
|
||||
return (options, command, handler, command_options)
|
||||
return options, handler, command_options
|
||||
|
||||
|
||||
class NoSuchCommand(Exception):
|
||||
|
|
|
@ -98,7 +98,7 @@ class TopLevelCommand(Command):
|
|||
options['version'] = "fig %s" % __version__
|
||||
return options
|
||||
|
||||
def build(self, options):
|
||||
def build(self, project, options):
|
||||
"""
|
||||
Build or rebuild services.
|
||||
|
||||
|
@ -112,9 +112,9 @@ class TopLevelCommand(Command):
|
|||
--no-cache Do not use cache when building the image.
|
||||
"""
|
||||
no_cache = bool(options.get('--no-cache', False))
|
||||
self.project.build(service_names=options['SERVICE'], no_cache=no_cache)
|
||||
project.build(service_names=options['SERVICE'], no_cache=no_cache)
|
||||
|
||||
def help(self, options):
|
||||
def help(self, project, options):
|
||||
"""
|
||||
Get help on a command.
|
||||
|
||||
|
@ -125,15 +125,15 @@ class TopLevelCommand(Command):
|
|||
raise NoSuchCommand(command, self)
|
||||
raise SystemExit(getdoc(getattr(self, command)))
|
||||
|
||||
def kill(self, options):
|
||||
def kill(self, project, options):
|
||||
"""
|
||||
Force stop service containers.
|
||||
|
||||
Usage: kill [SERVICE...]
|
||||
"""
|
||||
self.project.kill(service_names=options['SERVICE'])
|
||||
project.kill(service_names=options['SERVICE'])
|
||||
|
||||
def logs(self, options):
|
||||
def logs(self, project, options):
|
||||
"""
|
||||
View output from containers.
|
||||
|
||||
|
@ -142,14 +142,13 @@ class TopLevelCommand(Command):
|
|||
Options:
|
||||
--no-color Produce monochrome output.
|
||||
"""
|
||||
containers = self.project.containers(service_names=options['SERVICE'], stopped=True)
|
||||
containers = project.containers(service_names=options['SERVICE'], stopped=True)
|
||||
|
||||
monochrome = options['--no-color']
|
||||
|
||||
print("Attaching to", list_containers(containers))
|
||||
LogPrinter(containers, attach_params={'logs': True}, monochrome=monochrome).run()
|
||||
|
||||
def ps(self, options):
|
||||
def ps(self, project, options):
|
||||
"""
|
||||
List containers.
|
||||
|
||||
|
@ -158,7 +157,7 @@ class TopLevelCommand(Command):
|
|||
Options:
|
||||
-q Only display IDs
|
||||
"""
|
||||
containers = self.project.containers(service_names=options['SERVICE'], stopped=True) + self.project.containers(service_names=options['SERVICE'], one_off=True)
|
||||
containers = project.containers(service_names=options['SERVICE'], stopped=True) + project.containers(service_names=options['SERVICE'], one_off=True)
|
||||
|
||||
if options['-q']:
|
||||
for container in containers:
|
||||
|
@ -183,7 +182,7 @@ class TopLevelCommand(Command):
|
|||
])
|
||||
print(Formatter().table(headers, rows))
|
||||
|
||||
def rm(self, options):
|
||||
def rm(self, project, options):
|
||||
"""
|
||||
Remove stopped service containers.
|
||||
|
||||
|
@ -193,21 +192,21 @@ class TopLevelCommand(Command):
|
|||
--force Don't ask to confirm removal
|
||||
-v Remove volumes associated with containers
|
||||
"""
|
||||
all_containers = self.project.containers(service_names=options['SERVICE'], stopped=True)
|
||||
all_containers = project.containers(service_names=options['SERVICE'], stopped=True)
|
||||
stopped_containers = [c for c in all_containers if not c.is_running]
|
||||
|
||||
if len(stopped_containers) > 0:
|
||||
print("Going to remove", list_containers(stopped_containers))
|
||||
if options.get('--force') \
|
||||
or yesno("Are you sure? [yN] ", default=False):
|
||||
self.project.remove_stopped(
|
||||
project.remove_stopped(
|
||||
service_names=options['SERVICE'],
|
||||
v=options.get('-v', False)
|
||||
)
|
||||
else:
|
||||
print("No stopped containers")
|
||||
|
||||
def run(self, options):
|
||||
def run(self, project, options):
|
||||
"""
|
||||
Run a one-off command on a service.
|
||||
|
||||
|
@ -229,14 +228,13 @@ class TopLevelCommand(Command):
|
|||
--rm Remove container after run. Ignored in detached mode.
|
||||
--no-deps Don't start linked services.
|
||||
"""
|
||||
|
||||
service = self.project.get_service(options['SERVICE'])
|
||||
service = project.get_service(options['SERVICE'])
|
||||
|
||||
if not options['--no-deps']:
|
||||
deps = service.get_linked_names()
|
||||
|
||||
if len(deps) > 0:
|
||||
self.project.up(
|
||||
project.up(
|
||||
service_names=deps,
|
||||
start_links=True,
|
||||
recreate=False,
|
||||
|
@ -262,14 +260,14 @@ class TopLevelCommand(Command):
|
|||
print(container.name)
|
||||
else:
|
||||
service.start_container(container, ports=None, one_off=True)
|
||||
dockerpty.start(self.client, container.id)
|
||||
dockerpty.start(project.client, container.id)
|
||||
exit_code = container.wait()
|
||||
if options['--rm']:
|
||||
log.info("Removing %s..." % container.name)
|
||||
self.client.remove_container(container.id)
|
||||
project.client.remove_container(container.id)
|
||||
sys.exit(exit_code)
|
||||
|
||||
def scale(self, options):
|
||||
def scale(self, project, options):
|
||||
"""
|
||||
Set number of containers to run for a service.
|
||||
|
||||
|
@ -290,19 +288,24 @@ class TopLevelCommand(Command):
|
|||
raise UserError('Number of containers for service "%s" is not a '
|
||||
'number' % service_name)
|
||||
try:
|
||||
self.project.get_service(service_name).scale(num)
|
||||
project.get_service(service_name).scale(num)
|
||||
except CannotBeScaledError:
|
||||
raise UserError('Service "%s" cannot be scaled because it specifies a port on the host. If multiple containers for this service were created, the port would clash.\n\nRemove the ":" from the port definition in fig.yml so Docker can choose a random port for each container.' % service_name)
|
||||
raise UserError(
|
||||
'Service "%s" cannot be scaled because it specifies a port '
|
||||
'on the host. If multiple containers for this service were '
|
||||
'created, the port would clash.\n\nRemove the ":" from the '
|
||||
'port definition in fig.yml so Docker can choose a random '
|
||||
'port for each container.' % service_name)
|
||||
|
||||
def start(self, options):
|
||||
def start(self, project, options):
|
||||
"""
|
||||
Start existing containers.
|
||||
|
||||
Usage: start [SERVICE...]
|
||||
"""
|
||||
self.project.start(service_names=options['SERVICE'])
|
||||
project.start(service_names=options['SERVICE'])
|
||||
|
||||
def stop(self, options):
|
||||
def stop(self, project, options):
|
||||
"""
|
||||
Stop running containers without removing them.
|
||||
|
||||
|
@ -310,9 +313,9 @@ class TopLevelCommand(Command):
|
|||
|
||||
Usage: stop [SERVICE...]
|
||||
"""
|
||||
self.project.stop(service_names=options['SERVICE'])
|
||||
project.stop(service_names=options['SERVICE'])
|
||||
|
||||
def up(self, options):
|
||||
def up(self, project, options):
|
||||
"""
|
||||
Build, (re)create, start and attach to containers for a service.
|
||||
|
||||
|
@ -343,13 +346,13 @@ class TopLevelCommand(Command):
|
|||
recreate = not options['--no-recreate']
|
||||
service_names = options['SERVICE']
|
||||
|
||||
self.project.up(
|
||||
project.up(
|
||||
service_names=service_names,
|
||||
start_links=start_links,
|
||||
recreate=recreate
|
||||
)
|
||||
|
||||
to_attach = [c for s in self.project.get_services(service_names) for c in s.containers()]
|
||||
to_attach = [c for s in project.get_services(service_names) for c in s.containers()]
|
||||
|
||||
if not detached:
|
||||
print("Attaching to", list_containers(to_attach))
|
||||
|
@ -359,12 +362,12 @@ class TopLevelCommand(Command):
|
|||
log_printer.run()
|
||||
finally:
|
||||
def handler(signal, frame):
|
||||
self.project.kill(service_names=service_names)
|
||||
project.kill(service_names=service_names)
|
||||
sys.exit(0)
|
||||
signal.signal(signal.SIGINT, handler)
|
||||
|
||||
print("Gracefully stopping... (press Ctrl+C again to force)")
|
||||
self.project.stop(service_names=service_names)
|
||||
project.stop(service_names=service_names)
|
||||
|
||||
|
||||
def list_containers(containers):
|
||||
|
|
|
@ -7,25 +7,6 @@ import subprocess
|
|||
import platform
|
||||
|
||||
|
||||
def cached_property(f):
|
||||
"""
|
||||
returns a cached property that is calculated by function f
|
||||
http://code.activestate.com/recipes/576563-cached-property/
|
||||
"""
|
||||
def get(self):
|
||||
try:
|
||||
return self._property_cache[f]
|
||||
except AttributeError:
|
||||
self._property_cache = {}
|
||||
x = self._property_cache[f] = f(self)
|
||||
return x
|
||||
except KeyError:
|
||||
x = self._property_cache[f] = f(self)
|
||||
return x
|
||||
|
||||
return property(get)
|
||||
|
||||
|
||||
def yesno(prompt, default=None):
|
||||
"""
|
||||
Prompt the user for a yes or no.
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
|
||||
import functools
|
||||
from itertools import chain
|
||||
import logging
|
||||
import pprint
|
||||
|
||||
from fig.packages import six
|
||||
|
||||
|
||||
def format_call(args, kwargs):
|
||||
args = (repr(a) for a in args)
|
||||
kwargs = ("{0!s}={1!r}".format(*item) for item in six.iteritems(kwargs))
|
||||
return "({0})".format(", ".join(chain(args, kwargs)))
|
||||
|
||||
|
||||
def format_return(result, max_lines):
|
||||
if isinstance(result, (list, tuple, set)):
|
||||
return "({0} with {1} items)".format(type(result).__name__, len(result))
|
||||
|
||||
if result:
|
||||
lines = pprint.pformat(result).split('\n')
|
||||
extra = '\n...' if len(lines) > max_lines else ''
|
||||
return '\n'.join(lines[:max_lines]) + extra
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class VerboseProxy(object):
|
||||
"""Proxy all function calls to another class and log method name, arguments
|
||||
and return values for each call.
|
||||
"""
|
||||
|
||||
def __init__(self, obj_name, obj, log_name=None, max_lines=10):
|
||||
self.obj_name = obj_name
|
||||
self.obj = obj
|
||||
self.max_lines = max_lines
|
||||
self.log = logging.getLogger(log_name or __name__)
|
||||
|
||||
def __getattr__(self, name):
|
||||
attr = getattr(self.obj, name)
|
||||
|
||||
if not six.callable(attr):
|
||||
return attr
|
||||
|
||||
return functools.partial(self.proxy_callable, name)
|
||||
|
||||
def proxy_callable(self, call_name, *args, **kwargs):
|
||||
self.log.info("%s %s <- %s",
|
||||
self.obj_name,
|
||||
call_name,
|
||||
format_call(args, kwargs))
|
||||
|
||||
result = getattr(self.obj, call_name)(*args, **kwargs)
|
||||
self.log.info("%s %s -> %s",
|
||||
self.obj_name,
|
||||
call_name,
|
||||
format_return(result, self.max_lines))
|
||||
return result
|
|
@ -5,6 +5,7 @@ from fig.cli.main import TopLevelCommand
|
|||
from fig.packages.six import StringIO
|
||||
import sys
|
||||
|
||||
|
||||
class CLITestCase(DockerClientTestCase):
|
||||
def setUp(self):
|
||||
super(CLITestCase, self).setUp()
|
||||
|
@ -15,12 +16,16 @@ class CLITestCase(DockerClientTestCase):
|
|||
|
||||
def tearDown(self):
|
||||
sys.exit = self.old_sys_exit
|
||||
self.command.project.kill()
|
||||
self.command.project.remove_stopped()
|
||||
self.project.kill()
|
||||
self.project.remove_stopped()
|
||||
|
||||
@property
|
||||
def project(self):
|
||||
return self.command.get_project(self.command.get_config_path())
|
||||
|
||||
@patch('sys.stdout', new_callable=StringIO)
|
||||
def test_ps(self, mock_stdout):
|
||||
self.command.project.get_service('simple').create_container()
|
||||
self.project.get_service('simple').create_container()
|
||||
self.command.dispatch(['ps'], None)
|
||||
self.assertIn('simplefigfile_simple_1', mock_stdout.getvalue())
|
||||
|
||||
|
@ -64,17 +69,17 @@ class CLITestCase(DockerClientTestCase):
|
|||
|
||||
def test_up(self):
|
||||
self.command.dispatch(['up', '-d'], None)
|
||||
service = self.command.project.get_service('simple')
|
||||
another = self.command.project.get_service('another')
|
||||
service = self.project.get_service('simple')
|
||||
another = self.project.get_service('another')
|
||||
self.assertEqual(len(service.containers()), 1)
|
||||
self.assertEqual(len(another.containers()), 1)
|
||||
|
||||
def test_up_with_links(self):
|
||||
self.command.base_dir = 'tests/fixtures/links-figfile'
|
||||
self.command.dispatch(['up', '-d', 'web'], None)
|
||||
web = self.command.project.get_service('web')
|
||||
db = self.command.project.get_service('db')
|
||||
console = self.command.project.get_service('console')
|
||||
web = self.project.get_service('web')
|
||||
db = self.project.get_service('db')
|
||||
console = self.project.get_service('console')
|
||||
self.assertEqual(len(web.containers()), 1)
|
||||
self.assertEqual(len(db.containers()), 1)
|
||||
self.assertEqual(len(console.containers()), 0)
|
||||
|
@ -82,16 +87,16 @@ class CLITestCase(DockerClientTestCase):
|
|||
def test_up_with_no_deps(self):
|
||||
self.command.base_dir = 'tests/fixtures/links-figfile'
|
||||
self.command.dispatch(['up', '-d', '--no-deps', 'web'], None)
|
||||
web = self.command.project.get_service('web')
|
||||
db = self.command.project.get_service('db')
|
||||
console = self.command.project.get_service('console')
|
||||
web = self.project.get_service('web')
|
||||
db = self.project.get_service('db')
|
||||
console = self.project.get_service('console')
|
||||
self.assertEqual(len(web.containers()), 1)
|
||||
self.assertEqual(len(db.containers()), 0)
|
||||
self.assertEqual(len(console.containers()), 0)
|
||||
|
||||
def test_up_with_recreate(self):
|
||||
self.command.dispatch(['up', '-d'], None)
|
||||
service = self.command.project.get_service('simple')
|
||||
service = self.project.get_service('simple')
|
||||
self.assertEqual(len(service.containers()), 1)
|
||||
|
||||
old_ids = [c.id for c in service.containers()]
|
||||
|
@ -105,7 +110,7 @@ class CLITestCase(DockerClientTestCase):
|
|||
|
||||
def test_up_with_keep_old(self):
|
||||
self.command.dispatch(['up', '-d'], None)
|
||||
service = self.command.project.get_service('simple')
|
||||
service = self.project.get_service('simple')
|
||||
self.assertEqual(len(service.containers()), 1)
|
||||
|
||||
old_ids = [c.id for c in service.containers()]
|
||||
|
@ -117,19 +122,18 @@ class CLITestCase(DockerClientTestCase):
|
|||
|
||||
self.assertEqual(old_ids, new_ids)
|
||||
|
||||
|
||||
@patch('dockerpty.start')
|
||||
def test_run_service_without_links(self, mock_stdout):
|
||||
self.command.base_dir = 'tests/fixtures/links-figfile'
|
||||
self.command.dispatch(['run', 'console', '/bin/true'], None)
|
||||
self.assertEqual(len(self.command.project.containers()), 0)
|
||||
self.assertEqual(len(self.project.containers()), 0)
|
||||
|
||||
@patch('dockerpty.start')
|
||||
def test_run_service_with_links(self, __):
|
||||
self.command.base_dir = 'tests/fixtures/links-figfile'
|
||||
self.command.dispatch(['run', 'web', '/bin/true'], None)
|
||||
db = self.command.project.get_service('db')
|
||||
console = self.command.project.get_service('console')
|
||||
db = self.project.get_service('db')
|
||||
console = self.project.get_service('console')
|
||||
self.assertEqual(len(db.containers()), 1)
|
||||
self.assertEqual(len(console.containers()), 0)
|
||||
|
||||
|
@ -137,14 +141,14 @@ class CLITestCase(DockerClientTestCase):
|
|||
def test_run_with_no_deps(self, __):
|
||||
self.command.base_dir = 'tests/fixtures/links-figfile'
|
||||
self.command.dispatch(['run', '--no-deps', 'web', '/bin/true'], None)
|
||||
db = self.command.project.get_service('db')
|
||||
db = self.project.get_service('db')
|
||||
self.assertEqual(len(db.containers()), 0)
|
||||
|
||||
@patch('dockerpty.start')
|
||||
def test_run_does_not_recreate_linked_containers(self, __):
|
||||
self.command.base_dir = 'tests/fixtures/links-figfile'
|
||||
self.command.dispatch(['up', '-d', 'db'], None)
|
||||
db = self.command.project.get_service('db')
|
||||
db = self.project.get_service('db')
|
||||
self.assertEqual(len(db.containers()), 1)
|
||||
|
||||
old_ids = [c.id for c in db.containers()]
|
||||
|
@ -161,11 +165,11 @@ class CLITestCase(DockerClientTestCase):
|
|||
self.command.base_dir = 'tests/fixtures/commands-figfile'
|
||||
self.client.build('tests/fixtures/simple-dockerfile', tag='figtest_test')
|
||||
|
||||
for c in self.command.project.containers(stopped=True, one_off=True):
|
||||
for c in self.project.containers(stopped=True, one_off=True):
|
||||
c.remove()
|
||||
|
||||
self.command.dispatch(['run', 'implicit'], None)
|
||||
service = self.command.project.get_service('implicit')
|
||||
service = self.project.get_service('implicit')
|
||||
containers = service.containers(stopped=True, one_off=True)
|
||||
self.assertEqual(
|
||||
[c.human_readable_command for c in containers],
|
||||
|
@ -173,7 +177,7 @@ class CLITestCase(DockerClientTestCase):
|
|||
)
|
||||
|
||||
self.command.dispatch(['run', 'explicit'], None)
|
||||
service = self.command.project.get_service('explicit')
|
||||
service = self.project.get_service('explicit')
|
||||
containers = service.containers(stopped=True, one_off=True)
|
||||
self.assertEqual(
|
||||
[c.human_readable_command for c in containers],
|
||||
|
@ -181,7 +185,7 @@ class CLITestCase(DockerClientTestCase):
|
|||
)
|
||||
|
||||
def test_rm(self):
|
||||
service = self.command.project.get_service('simple')
|
||||
service = self.project.get_service('simple')
|
||||
service.create_container()
|
||||
service.kill()
|
||||
self.assertEqual(len(service.containers(stopped=True)), 1)
|
||||
|
@ -189,24 +193,23 @@ class CLITestCase(DockerClientTestCase):
|
|||
self.assertEqual(len(service.containers(stopped=True)), 0)
|
||||
|
||||
def test_scale(self):
|
||||
project = self.command.project
|
||||
project = self.project
|
||||
|
||||
self.command.scale({'SERVICE=NUM': ['simple=1']})
|
||||
self.command.scale(project, {'SERVICE=NUM': ['simple=1']})
|
||||
self.assertEqual(len(project.get_service('simple').containers()), 1)
|
||||
|
||||
self.command.scale({'SERVICE=NUM': ['simple=3', 'another=2']})
|
||||
self.command.scale(project, {'SERVICE=NUM': ['simple=3', 'another=2']})
|
||||
self.assertEqual(len(project.get_service('simple').containers()), 3)
|
||||
self.assertEqual(len(project.get_service('another').containers()), 2)
|
||||
|
||||
self.command.scale({'SERVICE=NUM': ['simple=1', 'another=1']})
|
||||
self.command.scale(project, {'SERVICE=NUM': ['simple=1', 'another=1']})
|
||||
self.assertEqual(len(project.get_service('simple').containers()), 1)
|
||||
self.assertEqual(len(project.get_service('another').containers()), 1)
|
||||
|
||||
self.command.scale({'SERVICE=NUM': ['simple=1', 'another=1']})
|
||||
self.command.scale(project, {'SERVICE=NUM': ['simple=1', 'another=1']})
|
||||
self.assertEqual(len(project.get_service('simple').containers()), 1)
|
||||
self.assertEqual(len(project.get_service('another').containers()), 1)
|
||||
|
||||
self.command.scale({'SERVICE=NUM': ['simple=0', 'another=0']})
|
||||
self.command.scale(project, {'SERVICE=NUM': ['simple=0', 'another=0']})
|
||||
self.assertEqual(len(project.get_service('simple').containers()), 0)
|
||||
self.assertEqual(len(project.get_service('another').containers()), 0)
|
||||
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
from __future__ import unicode_literals
|
||||
from __future__ import absolute_import
|
||||
from tests import unittest
|
||||
|
||||
from fig.cli import verbose_proxy
|
||||
|
||||
|
||||
class VerboseProxy(unittest.TestCase):
|
||||
|
||||
def test_format_call(self):
|
||||
expected = "(u'arg1', True, key=u'value')"
|
||||
actual = verbose_proxy.format_call(
|
||||
("arg1", True),
|
||||
{'key': 'value'})
|
||||
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_format_return_sequence(self):
|
||||
expected = "(list with 10 items)"
|
||||
actual = verbose_proxy.format_return(list(range(10)), 2)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_format_return(self):
|
||||
expected = "{u'Id': u'ok'}"
|
||||
actual = verbose_proxy.format_return({'Id': 'ok'}, 2)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_format_return_no_result(self):
|
||||
actual = verbose_proxy.format_return(None, 2)
|
||||
self.assertEqual(None, actual)
|
|
@ -4,6 +4,8 @@ import logging
|
|||
import os
|
||||
from .. import unittest
|
||||
|
||||
import mock
|
||||
|
||||
from fig.cli import main
|
||||
from fig.cli.main import TopLevelCommand
|
||||
from fig.packages.six import StringIO
|
||||
|
@ -16,24 +18,37 @@ class CLITestCase(unittest.TestCase):
|
|||
try:
|
||||
os.chdir('tests/fixtures/simple-figfile')
|
||||
command = TopLevelCommand()
|
||||
self.assertEquals('simplefigfile', command.project_name)
|
||||
project_name = command.get_project_name(command.get_config_path())
|
||||
self.assertEquals('simplefigfile', project_name)
|
||||
finally:
|
||||
os.chdir(cwd)
|
||||
|
||||
def test_project_name_with_explicit_base_dir(self):
|
||||
command = TopLevelCommand()
|
||||
command.base_dir = 'tests/fixtures/simple-figfile'
|
||||
self.assertEquals('simplefigfile', command.project_name)
|
||||
project_name = command.get_project_name(command.get_config_path())
|
||||
self.assertEquals('simplefigfile', project_name)
|
||||
|
||||
def test_project_name_with_explicit_project_name(self):
|
||||
command = TopLevelCommand()
|
||||
command.explicit_project_name = 'explicit-project-name'
|
||||
self.assertEquals('explicitprojectname', command.project_name)
|
||||
name = 'explicit-project-name'
|
||||
project_name = command.get_project_name(None, project_name=name)
|
||||
self.assertEquals('explicitprojectname', project_name)
|
||||
|
||||
def test_yaml_filename_check(self):
|
||||
command = TopLevelCommand()
|
||||
command.base_dir = 'tests/fixtures/longer-filename-figfile'
|
||||
self.assertTrue(command.project.get_service('definedinyamlnotyml'))
|
||||
with mock.patch('fig.cli.command.log', autospec=True) as mock_log:
|
||||
self.assertTrue(command.get_config_path())
|
||||
self.assertEqual(mock_log.warning.call_count, 2)
|
||||
|
||||
def test_get_project(self):
|
||||
command = TopLevelCommand()
|
||||
command.base_dir = 'tests/fixtures/longer-filename-figfile'
|
||||
project = command.get_project(command.get_config_path())
|
||||
self.assertEqual(project.name, 'longerfilenamefigfile')
|
||||
self.assertTrue(project.client)
|
||||
self.assertTrue(project.services)
|
||||
|
||||
def test_help(self):
|
||||
command = TopLevelCommand()
|
||||
|
|
Loading…
Reference in New Issue