mirror of https://github.com/docker/compose.git
Prevent `docker-compose scale` to be used with a v2.2 config file
Signed-off-by: Joffrey F <joffrey@docker.com>
This commit is contained in:
parent
457c16a7b1
commit
ef40e3c6b9
|
@ -26,6 +26,7 @@ from ..config import resolve_build_args
|
||||||
from ..config.environment import Environment
|
from ..config.environment import Environment
|
||||||
from ..config.serialize import serialize_config
|
from ..config.serialize import serialize_config
|
||||||
from ..config.types import VolumeSpec
|
from ..config.types import VolumeSpec
|
||||||
|
from ..const import COMPOSEFILE_V2_2 as V2_2
|
||||||
from ..const import IS_WINDOWS_PLATFORM
|
from ..const import IS_WINDOWS_PLATFORM
|
||||||
from ..errors import StreamParseError
|
from ..errors import StreamParseError
|
||||||
from ..progress_stream import StreamOutputError
|
from ..progress_stream import StreamOutputError
|
||||||
|
@ -771,6 +772,12 @@ class TopLevelCommand(object):
|
||||||
"""
|
"""
|
||||||
timeout = timeout_from_opts(options)
|
timeout = timeout_from_opts(options)
|
||||||
|
|
||||||
|
if self.project.config_version == V2_2:
|
||||||
|
raise UserError(
|
||||||
|
'The scale command is incompatible with the v2.2 format. '
|
||||||
|
'Use the up command with the --scale flag instead.'
|
||||||
|
)
|
||||||
|
|
||||||
for service_name, num in parse_scale_args(options['SERVICE=NUM']).items():
|
for service_name, num in parse_scale_args(options['SERVICE=NUM']).items():
|
||||||
self.project.get_service(service_name).scale(num, timeout=timeout)
|
self.project.get_service(service_name).scale(num, timeout=timeout)
|
||||||
|
|
||||||
|
|
|
@ -57,12 +57,13 @@ class Project(object):
|
||||||
"""
|
"""
|
||||||
A collection of services.
|
A collection of services.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name, services, client, networks=None, volumes=None):
|
def __init__(self, name, services, client, networks=None, volumes=None, config_version=None):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.services = services
|
self.services = services
|
||||||
self.client = client
|
self.client = client
|
||||||
self.volumes = volumes or ProjectVolumes({})
|
self.volumes = volumes or ProjectVolumes({})
|
||||||
self.networks = networks or ProjectNetworks({}, False)
|
self.networks = networks or ProjectNetworks({}, False)
|
||||||
|
self.config_version = config_version
|
||||||
|
|
||||||
def labels(self, one_off=OneOffFilter.exclude):
|
def labels(self, one_off=OneOffFilter.exclude):
|
||||||
labels = ['{0}={1}'.format(LABEL_PROJECT, self.name)]
|
labels = ['{0}={1}'.format(LABEL_PROJECT, self.name)]
|
||||||
|
@ -82,7 +83,7 @@ class Project(object):
|
||||||
networks,
|
networks,
|
||||||
use_networking)
|
use_networking)
|
||||||
volumes = ProjectVolumes.from_config(name, config_data, client)
|
volumes = ProjectVolumes.from_config(name, config_data, client)
|
||||||
project = cls(name, [], client, project_networks, volumes)
|
project = cls(name, [], client, project_networks, volumes, config_data.version)
|
||||||
|
|
||||||
for service_dict in config_data.services:
|
for service_dict in config_data.services:
|
||||||
service_dict = dict(service_dict)
|
service_dict = dict(service_dict)
|
||||||
|
|
|
@ -383,8 +383,8 @@ class Service(object):
|
||||||
lambda n: self.get_container_name(n),
|
lambda n: self.get_container_name(n),
|
||||||
"Creating"
|
"Creating"
|
||||||
)
|
)
|
||||||
if errors:
|
for error in errors.values():
|
||||||
raise OperationFailedError(errors.values()[0])
|
raise OperationFailedError(error)
|
||||||
|
|
||||||
return containers
|
return containers
|
||||||
|
|
||||||
|
@ -404,8 +404,9 @@ class Service(object):
|
||||||
lambda c: c.name,
|
lambda c: c.name,
|
||||||
"Recreating"
|
"Recreating"
|
||||||
)
|
)
|
||||||
if errors:
|
for error in errors.values():
|
||||||
raise OperationFailedError(errors.values()[0])
|
raise OperationFailedError(error)
|
||||||
|
|
||||||
if len(containers) < scale:
|
if len(containers) < scale:
|
||||||
containers.extend(self._execute_convergence_create(
|
containers.extend(self._execute_convergence_create(
|
||||||
scale - len(containers), detached, start
|
scale - len(containers), detached, start
|
||||||
|
@ -424,8 +425,8 @@ class Service(object):
|
||||||
"Starting"
|
"Starting"
|
||||||
)
|
)
|
||||||
|
|
||||||
if errors:
|
for error in errors.values():
|
||||||
raise OperationFailedError(errors.values()[0])
|
raise OperationFailedError(error)
|
||||||
|
|
||||||
if len(containers) < scale:
|
if len(containers) < scale:
|
||||||
containers.extend(self._execute_convergence_create(
|
containers.extend(self._execute_convergence_create(
|
||||||
|
|
|
@ -1866,9 +1866,27 @@ class CLITestCase(DockerClientTestCase):
|
||||||
self.assertEqual(len(project.get_service('simple').containers()), 0)
|
self.assertEqual(len(project.get_service('simple').containers()), 0)
|
||||||
self.assertEqual(len(project.get_service('another').containers()), 0)
|
self.assertEqual(len(project.get_service('another').containers()), 0)
|
||||||
|
|
||||||
def test_up_scale(self):
|
def test_scale_v2_2(self):
|
||||||
|
self.base_dir = 'tests/fixtures/scale'
|
||||||
|
result = self.dispatch(['scale', 'web=1'], returncode=1)
|
||||||
|
assert 'incompatible with the v2.2 format' in result.stderr
|
||||||
|
|
||||||
|
def test_up_scale_scale_up(self):
|
||||||
self.base_dir = 'tests/fixtures/scale'
|
self.base_dir = 'tests/fixtures/scale'
|
||||||
project = self.project
|
project = self.project
|
||||||
|
|
||||||
|
self.dispatch(['up', '-d'])
|
||||||
|
assert len(project.get_service('web').containers()) == 2
|
||||||
|
assert len(project.get_service('db').containers()) == 1
|
||||||
|
|
||||||
|
self.dispatch(['up', '-d', '--scale', 'web=3'])
|
||||||
|
assert len(project.get_service('web').containers()) == 3
|
||||||
|
assert len(project.get_service('db').containers()) == 1
|
||||||
|
|
||||||
|
def test_up_scale_scale_down(self):
|
||||||
|
self.base_dir = 'tests/fixtures/scale'
|
||||||
|
project = self.project
|
||||||
|
|
||||||
self.dispatch(['up', '-d'])
|
self.dispatch(['up', '-d'])
|
||||||
assert len(project.get_service('web').containers()) == 2
|
assert len(project.get_service('web').containers()) == 2
|
||||||
assert len(project.get_service('db').containers()) == 1
|
assert len(project.get_service('db').containers()) == 1
|
||||||
|
@ -1877,13 +1895,21 @@ class CLITestCase(DockerClientTestCase):
|
||||||
assert len(project.get_service('web').containers()) == 1
|
assert len(project.get_service('web').containers()) == 1
|
||||||
assert len(project.get_service('db').containers()) == 1
|
assert len(project.get_service('db').containers()) == 1
|
||||||
|
|
||||||
self.dispatch(['up', '-d', '--scale', 'web=3'])
|
def test_up_scale_reset(self):
|
||||||
|
self.base_dir = 'tests/fixtures/scale'
|
||||||
|
project = self.project
|
||||||
|
|
||||||
|
self.dispatch(['up', '-d', '--scale', 'web=3', '--scale', 'db=3'])
|
||||||
assert len(project.get_service('web').containers()) == 3
|
assert len(project.get_service('web').containers()) == 3
|
||||||
|
assert len(project.get_service('db').containers()) == 3
|
||||||
|
|
||||||
|
self.dispatch(['up', '-d'])
|
||||||
|
assert len(project.get_service('web').containers()) == 2
|
||||||
assert len(project.get_service('db').containers()) == 1
|
assert len(project.get_service('db').containers()) == 1
|
||||||
|
|
||||||
self.dispatch(['up', '-d', '--scale', 'web=1', '--scale', 'db=2'])
|
def test_up_scale_to_zero(self):
|
||||||
assert len(project.get_service('web').containers()) == 1
|
self.base_dir = 'tests/fixtures/scale'
|
||||||
assert len(project.get_service('db').containers()) == 2
|
project = self.project
|
||||||
|
|
||||||
self.dispatch(['up', '-d'])
|
self.dispatch(['up', '-d'])
|
||||||
assert len(project.get_service('web').containers()) == 2
|
assert len(project.get_service('web').containers()) == 2
|
||||||
|
|
|
@ -565,12 +565,12 @@ class ProjectTest(DockerClientTestCase):
|
||||||
self.assertEqual(len(service.containers()), 3)
|
self.assertEqual(len(service.containers()), 3)
|
||||||
project.up()
|
project.up()
|
||||||
service = project.get_service('web')
|
service = project.get_service('web')
|
||||||
self.assertEqual(len(service.containers()), 3)
|
self.assertEqual(len(service.containers()), 1)
|
||||||
service.scale(1)
|
service.scale(1)
|
||||||
self.assertEqual(len(service.containers()), 1)
|
self.assertEqual(len(service.containers()), 1)
|
||||||
project.up()
|
project.up(scale_override={'web': 3})
|
||||||
service = project.get_service('web')
|
service = project.get_service('web')
|
||||||
self.assertEqual(len(service.containers()), 1)
|
self.assertEqual(len(service.containers()), 3)
|
||||||
# does scale=0 ,makes any sense? after recreating at least 1 container is running
|
# does scale=0 ,makes any sense? after recreating at least 1 container is running
|
||||||
service.scale(0)
|
service.scale(0)
|
||||||
project.up()
|
project.up()
|
||||||
|
|
|
@ -26,6 +26,7 @@ from compose.const import LABEL_PROJECT
|
||||||
from compose.const import LABEL_SERVICE
|
from compose.const import LABEL_SERVICE
|
||||||
from compose.const import LABEL_VERSION
|
from compose.const import LABEL_VERSION
|
||||||
from compose.container import Container
|
from compose.container import Container
|
||||||
|
from compose.errors import OperationFailedError
|
||||||
from compose.project import OneOffFilter
|
from compose.project import OneOffFilter
|
||||||
from compose.service import ConvergencePlan
|
from compose.service import ConvergencePlan
|
||||||
from compose.service import ConvergenceStrategy
|
from compose.service import ConvergenceStrategy
|
||||||
|
@ -777,15 +778,15 @@ class ServiceTest(DockerClientTestCase):
|
||||||
message="testing",
|
message="testing",
|
||||||
response={},
|
response={},
|
||||||
explanation="Boom")):
|
explanation="Boom")):
|
||||||
|
|
||||||
with mock.patch('sys.stderr', new_callable=StringIO) as mock_stderr:
|
with mock.patch('sys.stderr', new_callable=StringIO) as mock_stderr:
|
||||||
service.scale(3)
|
with pytest.raises(OperationFailedError):
|
||||||
|
service.scale(3)
|
||||||
|
|
||||||
self.assertEqual(len(service.containers()), 1)
|
assert len(service.containers()) == 1
|
||||||
self.assertTrue(service.containers()[0].is_running)
|
assert service.containers()[0].is_running
|
||||||
self.assertIn(
|
assert (
|
||||||
"ERROR: for composetest_web_2 Cannot create container for service web: Boom",
|
"ERROR: for composetest_web_2 Cannot create container for service"
|
||||||
mock_stderr.getvalue()
|
" web: Boom" in mock_stderr.getvalue()
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_scale_with_unexpected_exception(self):
|
def test_scale_with_unexpected_exception(self):
|
||||||
|
@ -837,7 +838,8 @@ class ServiceTest(DockerClientTestCase):
|
||||||
service = self.create_service('app', container_name='custom-container')
|
service = self.create_service('app', container_name='custom-container')
|
||||||
self.assertEqual(service.custom_container_name, 'custom-container')
|
self.assertEqual(service.custom_container_name, 'custom-container')
|
||||||
|
|
||||||
service.scale(3)
|
with pytest.raises(OperationFailedError):
|
||||||
|
service.scale(3)
|
||||||
|
|
||||||
captured_output = mock_log.warn.call_args[0][0]
|
captured_output = mock_log.warn.call_args[0][0]
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue