From a21f9993b3acf20efafad70b02da81debfd37830 Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Fri, 27 Nov 2015 12:02:13 -0500 Subject: [PATCH] Remove migrate-to-labels. Signed-off-by: Daniel Nephin --- compose/cli/main.py | 47 +----- compose/legacy.py | 182 --------------------- compose/project.py | 8 - compose/service.py | 12 +- contrib/completion/bash/docker-compose | 10 -- contrib/completion/zsh/_docker-compose | 5 - docs/install.md | 2 +- docs/reference/docker-compose.md | 1 - tests/integration/legacy_test.py | 218 ------------------------- tests/unit/cli_test.py | 6 - 10 files changed, 7 insertions(+), 484 deletions(-) delete mode 100644 compose/legacy.py delete mode 100644 tests/integration/legacy_test.py diff --git a/compose/cli/main.py b/compose/cli/main.py index 9fef8d041..90b030177 100644 --- a/compose/cli/main.py +++ b/compose/cli/main.py @@ -12,7 +12,6 @@ from docker.errors import APIError from requests.exceptions import ReadTimeout from .. import __version__ -from .. import legacy from ..config import ConfigurationError from ..config import parse_environment from ..const import DEFAULT_TIMEOUT @@ -55,7 +54,7 @@ def main(): except KeyboardInterrupt: log.error("\nAborting.") sys.exit(1) - except (UserError, NoSuchService, ConfigurationError, legacy.LegacyError) as e: + except (UserError, NoSuchService, ConfigurationError) as e: log.error(e.msg) sys.exit(1) except NoSuchCommand as e: @@ -147,9 +146,7 @@ class TopLevelCommand(DocoptCommand): stop Stop services unpause Unpause services up Create and start containers - migrate-to-labels Recreate containers to add labels version Show the Docker-Compose version information - """ base_dir = '.' @@ -550,32 +547,6 @@ class TopLevelCommand(DocoptCommand): log_printer = build_log_printer(to_attach, service_names, monochrome) attach_to_logs(project, log_printer, service_names, timeout) - def migrate_to_labels(self, project, _options): - """ - Recreate containers to add labels - - If you're coming from Compose 1.2 or earlier, you'll need to remove or - migrate your existing containers after upgrading Compose. This is - because, as of version 1.3, Compose uses Docker labels to keep track - of containers, and so they need to be recreated with labels added. - - If Compose detects containers that were created without labels, it - will refuse to run so that you don't end up with two sets of them. If - you want to keep using your existing containers (for example, because - they have data volumes you want to preserve) you can migrate them with - the following command: - - docker-compose migrate-to-labels - - Alternatively, if you're not worried about keeping them, you can - remove them - Compose will just create new ones. - - docker rm -f myapp_web_1 myapp_db_1 ... - - Usage: migrate-to-labels - """ - legacy.migrate_project_to_labels(project) - def version(self, project, options): """ Show version informations @@ -618,18 +589,10 @@ def run_one_off_container(container_options, project, service, options): if project.use_networking: project.ensure_network_exists() - try: - container = service.create_container( - quiet=True, - one_off=True, - **container_options) - except APIError: - legacy.check_for_legacy_containers( - project.client, - project.name, - [service.name], - allow_one_off=False) - raise + container = service.create_container( + quiet=True, + one_off=True, + **container_options) if options['-d']: container.start() diff --git a/compose/legacy.py b/compose/legacy.py deleted file mode 100644 index 541624178..000000000 --- a/compose/legacy.py +++ /dev/null @@ -1,182 +0,0 @@ -import logging -import re - -from .const import LABEL_VERSION -from .container import Container -from .container import get_container_name - - -log = logging.getLogger(__name__) - - -# TODO: remove this section when migrate_project_to_labels is removed -NAME_RE = re.compile(r'^([^_]+)_([^_]+)_(run_)?(\d+)$') - -ERROR_MESSAGE_FORMAT = """ -Compose found the following containers without labels: - -{names_list} - -As of Compose 1.3.0, containers are identified with labels instead of naming -convention. If you want to continue using these containers, run: - - $ docker-compose migrate-to-labels - -Alternatively, remove them: - - $ docker rm -f {rm_args} -""" - -ONE_OFF_ADDENDUM_FORMAT = """ -You should also remove your one-off containers: - - $ docker rm -f {rm_args} -""" - -ONE_OFF_ERROR_MESSAGE_FORMAT = """ -Compose found the following containers without labels: - -{names_list} - -As of Compose 1.3.0, containers are identified with labels instead of naming convention. - -Remove them before continuing: - - $ docker rm -f {rm_args} -""" - - -def check_for_legacy_containers( - client, - project, - services, - allow_one_off=True): - """Check if there are containers named using the old naming convention - and warn the user that those containers may need to be migrated to - using labels, so that compose can find them. - """ - containers = get_legacy_containers(client, project, services, one_off=False) - - if containers: - one_off_containers = get_legacy_containers(client, project, services, one_off=True) - - raise LegacyContainersError( - [c.name for c in containers], - [c.name for c in one_off_containers], - ) - - if not allow_one_off: - one_off_containers = get_legacy_containers(client, project, services, one_off=True) - - if one_off_containers: - raise LegacyOneOffContainersError( - [c.name for c in one_off_containers], - ) - - -class LegacyError(Exception): - def __unicode__(self): - return self.msg - - __str__ = __unicode__ - - -class LegacyContainersError(LegacyError): - def __init__(self, names, one_off_names): - self.names = names - self.one_off_names = one_off_names - - self.msg = ERROR_MESSAGE_FORMAT.format( - names_list="\n".join(" {}".format(name) for name in names), - rm_args=" ".join(names), - ) - - if one_off_names: - self.msg += ONE_OFF_ADDENDUM_FORMAT.format(rm_args=" ".join(one_off_names)) - - -class LegacyOneOffContainersError(LegacyError): - def __init__(self, one_off_names): - self.one_off_names = one_off_names - - self.msg = ONE_OFF_ERROR_MESSAGE_FORMAT.format( - names_list="\n".join(" {}".format(name) for name in one_off_names), - rm_args=" ".join(one_off_names), - ) - - -def add_labels(project, container): - project_name, service_name, one_off, number = NAME_RE.match(container.name).groups() - if project_name != project.name or service_name not in project.service_names: - return - service = project.get_service(service_name) - service.recreate_container(container) - - -def migrate_project_to_labels(project): - log.info("Running migration to labels for project %s", project.name) - - containers = get_legacy_containers( - project.client, - project.name, - project.service_names, - one_off=False, - ) - - for container in containers: - add_labels(project, container) - - -def get_legacy_containers( - client, - project, - services, - one_off=False): - - return list(_get_legacy_containers_iter( - client, - project, - services, - one_off=one_off, - )) - - -def _get_legacy_containers_iter( - client, - project, - services, - one_off=False): - - containers = client.containers(all=True) - - for service in services: - for container in containers: - if LABEL_VERSION in (container.get('Labels') or {}): - continue - - name = get_container_name(container) - if has_container(project, service, name, one_off=one_off): - yield Container.from_ps(client, container) - - -def has_container(project, service, name, one_off=False): - if not name or not is_valid_name(name, one_off): - return False - container_project, container_service, _container_number = parse_name(name) - return container_project == project and container_service == service - - -def is_valid_name(name, one_off=False): - match = NAME_RE.match(name) - if match is None: - return False - if one_off: - return match.group(3) == 'run_' - else: - return match.group(3) is None - - -def parse_name(name): - match = NAME_RE.match(name) - (project, service_name, _, suffix) = match.groups() - return (project, service_name, int(suffix)) diff --git a/compose/project.py b/compose/project.py index 30e81693c..af40d820a 100644 --- a/compose/project.py +++ b/compose/project.py @@ -15,7 +15,6 @@ from .const import LABEL_ONE_OFF from .const import LABEL_PROJECT from .const import LABEL_SERVICE from .container import Container -from .legacy import check_for_legacy_containers from .service import ContainerNet from .service import ConvergenceStrategy from .service import Net @@ -287,13 +286,6 @@ class Project(object): def matches_service_names(container): return container.labels.get(LABEL_SERVICE) in service_names - if not containers: - check_for_legacy_containers( - self.client, - self.name, - self.service_names, - ) - return [c for c in containers if matches_service_names(c)] def get_network(self): diff --git a/compose/service.py b/compose/service.py index 08d563e93..0b9a2aa3b 100644 --- a/compose/service.py +++ b/compose/service.py @@ -26,7 +26,6 @@ from .const import LABEL_PROJECT from .const import LABEL_SERVICE from .const import LABEL_VERSION from .container import Container -from .legacy import check_for_legacy_containers from .parallel import parallel_execute from .parallel import parallel_remove from .parallel import parallel_start @@ -122,21 +121,12 @@ class Service(object): def containers(self, stopped=False, one_off=False, filters={}): filters.update({'label': self.labels(one_off=one_off)}) - containers = list(filter(None, [ + return list(filter(None, [ Container.from_ps(self.client, container) for container in self.client.containers( all=stopped, filters=filters)])) - if not containers: - check_for_legacy_containers( - self.client, - self.project, - [self.name], - ) - - return containers - def get_container(self, number=1): """Return a :class:`compose.container.Container` for this service. The container must be active, and match `number`. diff --git a/contrib/completion/bash/docker-compose b/contrib/completion/bash/docker-compose index b4f4387f3..c22e6abc3 100644 --- a/contrib/completion/bash/docker-compose +++ b/contrib/completion/bash/docker-compose @@ -164,15 +164,6 @@ _docker_compose_logs() { } -_docker_compose_migrate_to_labels() { - case "$cur" in - -*) - COMPREPLY=( $( compgen -W "--help" -- "$cur" ) ) - ;; - esac -} - - _docker_compose_pause() { case "$cur" in -*) @@ -385,7 +376,6 @@ _docker_compose() { help kill logs - migrate-to-labels pause port ps diff --git a/contrib/completion/zsh/_docker-compose b/contrib/completion/zsh/_docker-compose index 08d5150d9..0b50b5358 100644 --- a/contrib/completion/zsh/_docker-compose +++ b/contrib/completion/zsh/_docker-compose @@ -212,11 +212,6 @@ __docker-compose_subcommand() { '--no-color[Produce monochrome output.]' \ '*:services:__docker-compose_services_all' && ret=0 ;; - (migrate-to-labels) - _arguments -A '-*' \ - $opts_help \ - '(-):Recreate containers to add labels' && ret=0 - ;; (pause) _arguments \ $opts_help \ diff --git a/docs/install.md b/docs/install.md index 861954b4b..cc15cec1b 100644 --- a/docs/install.md +++ b/docs/install.md @@ -98,7 +98,7 @@ be recreated with labels added. If Compose detects containers that were created without labels, it will refuse to run so that you don't end up with two sets of them. If you want to keep using your existing containers (for example, because they have data volumes you want -to preserve) you can migrate them with the following command: +to preserve) you can use compose 1.5.x to migrate them with the following command: $ docker-compose migrate-to-labels diff --git a/docs/reference/docker-compose.md b/docs/reference/docker-compose.md index 8712072e5..c19e42848 100644 --- a/docs/reference/docker-compose.md +++ b/docs/reference/docker-compose.md @@ -40,7 +40,6 @@ Commands: stop Stop services unpause Unpause services up Create and start containers - migrate-to-labels Recreate containers to add labels version Show the Docker-Compose version information ``` diff --git a/tests/integration/legacy_test.py b/tests/integration/legacy_test.py deleted file mode 100644 index 3465d57f4..000000000 --- a/tests/integration/legacy_test.py +++ /dev/null @@ -1,218 +0,0 @@ -import unittest - -from docker.errors import APIError - -from .. import mock -from .testcases import DockerClientTestCase -from compose import legacy -from compose.project import Project - - -class UtilitiesTestCase(unittest.TestCase): - def test_has_container(self): - self.assertTrue( - legacy.has_container("composetest", "web", "composetest_web_1", one_off=False), - ) - self.assertFalse( - legacy.has_container("composetest", "web", "composetest_web_run_1", one_off=False), - ) - - def test_has_container_one_off(self): - self.assertFalse( - legacy.has_container("composetest", "web", "composetest_web_1", one_off=True), - ) - self.assertTrue( - legacy.has_container("composetest", "web", "composetest_web_run_1", one_off=True), - ) - - def test_has_container_different_project(self): - self.assertFalse( - legacy.has_container("composetest", "web", "otherapp_web_1", one_off=False), - ) - self.assertFalse( - legacy.has_container("composetest", "web", "otherapp_web_run_1", one_off=True), - ) - - def test_has_container_different_service(self): - self.assertFalse( - legacy.has_container("composetest", "web", "composetest_db_1", one_off=False), - ) - self.assertFalse( - legacy.has_container("composetest", "web", "composetest_db_run_1", one_off=True), - ) - - def test_is_valid_name(self): - self.assertTrue( - legacy.is_valid_name("composetest_web_1", one_off=False), - ) - self.assertFalse( - legacy.is_valid_name("composetest_web_run_1", one_off=False), - ) - - def test_is_valid_name_one_off(self): - self.assertFalse( - legacy.is_valid_name("composetest_web_1", one_off=True), - ) - self.assertTrue( - legacy.is_valid_name("composetest_web_run_1", one_off=True), - ) - - def test_is_valid_name_invalid(self): - self.assertFalse( - legacy.is_valid_name("foo"), - ) - self.assertFalse( - legacy.is_valid_name("composetest_web_lol_1", one_off=True), - ) - - def test_get_legacy_containers(self): - client = mock.Mock() - client.containers.return_value = [ - { - "Id": "abc123", - "Image": "def456", - "Name": "composetest_web_1", - "Labels": None, - }, - { - "Id": "ghi789", - "Image": "def456", - "Name": None, - "Labels": None, - }, - { - "Id": "jkl012", - "Image": "def456", - "Labels": None, - }, - ] - - containers = legacy.get_legacy_containers(client, "composetest", ["web"]) - - self.assertEqual(len(containers), 1) - self.assertEqual(containers[0].id, 'abc123') - - -class LegacyTestCase(DockerClientTestCase): - - def setUp(self): - super(LegacyTestCase, self).setUp() - self.containers = [] - - db = self.create_service('db') - web = self.create_service('web', links=[(db, 'db')]) - nginx = self.create_service('nginx', links=[(web, 'web')]) - - self.services = [db, web, nginx] - self.project = Project('composetest', self.services, self.client) - - # Create a legacy container for each service - for service in self.services: - service.ensure_image_exists() - container = self.client.create_container( - name='{}_{}_1'.format(self.project.name, service.name), - **service.options - ) - self.client.start(container) - self.containers.append(container) - - # Create a single one-off legacy container - self.containers.append(self.client.create_container( - name='{}_{}_run_1'.format(self.project.name, db.name), - **self.services[0].options - )) - - def tearDown(self): - super(LegacyTestCase, self).tearDown() - for container in self.containers: - try: - self.client.kill(container) - except APIError: - pass - try: - self.client.remove_container(container) - except APIError: - pass - - def get_legacy_containers(self, **kwargs): - return legacy.get_legacy_containers( - self.client, - self.project.name, - [s.name for s in self.services], - **kwargs - ) - - def test_get_legacy_container_names(self): - self.assertEqual(len(self.get_legacy_containers()), len(self.services)) - - def test_get_legacy_container_names_one_off(self): - self.assertEqual(len(self.get_legacy_containers(one_off=True)), 1) - - def test_migration_to_labels(self): - # Trying to get the container list raises an exception - - with self.assertRaises(legacy.LegacyContainersError) as cm: - self.project.containers(stopped=True) - - self.assertEqual( - set(cm.exception.names), - set(['composetest_db_1', 'composetest_web_1', 'composetest_nginx_1']), - ) - - self.assertEqual( - set(cm.exception.one_off_names), - set(['composetest_db_run_1']), - ) - - # Migrate the containers - - legacy.migrate_project_to_labels(self.project) - - # Getting the list no longer raises an exception - - containers = self.project.containers(stopped=True) - self.assertEqual(len(containers), len(self.services)) - - def test_migration_one_off(self): - # We've already migrated - - legacy.migrate_project_to_labels(self.project) - - # Trying to create a one-off container results in a Docker API error - - with self.assertRaises(APIError) as cm: - self.project.get_service('db').create_container(one_off=True) - - # Checking for legacy one-off containers raises an exception - - with self.assertRaises(legacy.LegacyOneOffContainersError) as cm: - legacy.check_for_legacy_containers( - self.client, - self.project.name, - ['db'], - allow_one_off=False, - ) - - self.assertEqual( - set(cm.exception.one_off_names), - set(['composetest_db_run_1']), - ) - - # Remove the old one-off container - - c = self.client.inspect_container('composetest_db_run_1') - self.client.remove_container(c) - - # Checking no longer raises an exception - - legacy.check_for_legacy_containers( - self.client, - self.project.name, - ['db'], - allow_one_off=False, - ) - - # Creating a one-off container no longer results in an API error - - self.project.get_service('db').create_container(one_off=True) - self.assertIsInstance(self.client.inspect_container('composetest_db_run_1'), dict) diff --git a/tests/unit/cli_test.py b/tests/unit/cli_test.py index 23dc42629..2473a3406 100644 --- a/tests/unit/cli_test.py +++ b/tests/unit/cli_test.py @@ -74,12 +74,6 @@ class CLITestCase(unittest.TestCase): self.assertIn('Usage: up', str(ctx.exception)) - def test_command_help_dashes(self): - with self.assertRaises(SystemExit) as ctx: - TopLevelCommand().dispatch(['help', 'migrate-to-labels'], None) - - self.assertIn('Usage: migrate-to-labels', str(ctx.exception)) - def test_command_help_nonexistent(self): with self.assertRaises(NoSuchCommand): TopLevelCommand().dispatch(['help', 'nonexistent'], None)