Migrate containers in dependency order

This fixes a bug where migration would fail with an error if a
downstream container was migrated before its upstream dependencies, due
to `check_for_legacy_containers()` being implicitly called when we fetch
`links`, `volumes_from` or `net` dependencies.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
This commit is contained in:
Aanand Prasad 2015-05-21 16:09:06 +01:00
parent 276fee105b
commit 7da8e6be3b
2 changed files with 32 additions and 29 deletions

View File

@ -35,15 +35,15 @@ def check_for_legacy_containers(
and warn the user that those containers may need to be migrated to and warn the user that those containers may need to be migrated to
using labels, so that compose can find them. using labels, so that compose can find them.
""" """
names = list(get_legacy_container_names( containers = list(get_legacy_containers(
client, client,
project, project,
services, services,
stopped=stopped, stopped=stopped,
one_off=one_off)) one_off=one_off))
if names: if containers:
raise LegacyContainersError(names) raise LegacyContainersError([c.name for c in containers])
class LegacyContainersError(Exception): class LegacyContainersError(Exception):
@ -61,8 +61,8 @@ class LegacyContainersError(Exception):
__str__ = __unicode__ __str__ = __unicode__
def add_labels(project, container, name): def add_labels(project, container):
project_name, service_name, one_off, number = NAME_RE.match(name).groups() 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: if project_name != project.name or service_name not in project.service_names:
return return
service = project.get_service(service_name) service = project.get_service(service_name)
@ -72,26 +72,31 @@ def add_labels(project, container, name):
def migrate_project_to_labels(project): def migrate_project_to_labels(project):
log.info("Running migration to labels for project %s", project.name) log.info("Running migration to labels for project %s", project.name)
client = project.client containers = get_legacy_containers(
for container in client.containers(all=True): project.client,
name = get_container_name(container) project.name,
if not is_valid_name(name): project.service_names,
continue stopped=True,
add_labels(project, Container.from_ps(client, container), name) one_off=False)
for container in containers:
add_labels(project, container)
def get_legacy_container_names( def get_legacy_containers(
client, client,
project, project,
services, services,
stopped=False, stopped=False,
one_off=False): one_off=False):
for container in client.containers(all=stopped): containers = client.containers(all=stopped)
name = get_container_name(container)
for service in services: for service in services:
for container in containers:
name = get_container_name(container)
if has_container(project, service, name, one_off=one_off): if has_container(project, service, name, one_off=one_off):
yield name yield Container.from_ps(client, container)
def has_container(project, service, name, one_off=False): def has_container(project, service, name, one_off=False):

View File

@ -8,20 +8,21 @@ class ProjectTest(DockerClientTestCase):
def setUp(self): def setUp(self):
super(ProjectTest, self).setUp() super(ProjectTest, self).setUp()
self.services = [ db = self.create_service('db')
self.create_service('web'), web = self.create_service('web', links=[(db, 'db')])
self.create_service('db'), nginx = self.create_service('nginx', links=[(web, 'web')])
]
self.services = [db, web, nginx]
self.project = Project('composetest', self.services, self.client) self.project = Project('composetest', self.services, self.client)
# Create a legacy container for each service # Create a legacy container for each service
for service in self.services: for service in self.services:
service.ensure_image_exists() service.ensure_image_exists()
self.client.create_container( container = self.client.create_container(
name='{}_{}_1'.format(self.project.name, service.name), name='{}_{}_1'.format(self.project.name, service.name),
**service.options **service.options
) )
self.client.start(container)
# Create a single one-off legacy container # Create a single one-off legacy container
self.client.create_container( self.client.create_container(
@ -29,11 +30,8 @@ class ProjectTest(DockerClientTestCase):
**self.services[0].options **self.services[0].options
) )
def get_names(self, **kwargs): def get_legacy_containers(self, **kwargs):
if 'stopped' not in kwargs: return list(legacy.get_legacy_containers(
kwargs['stopped'] = True
return list(legacy.get_legacy_container_names(
self.client, self.client,
self.project.name, self.project.name,
[s.name for s in self.services], [s.name for s in self.services],
@ -41,10 +39,10 @@ class ProjectTest(DockerClientTestCase):
)) ))
def test_get_legacy_container_names(self): def test_get_legacy_container_names(self):
self.assertEqual(len(self.get_names()), len(self.services)) self.assertEqual(len(self.get_legacy_containers()), len(self.services))
def test_get_legacy_container_names_one_off(self): def test_get_legacy_container_names_one_off(self):
self.assertEqual(len(self.get_names(one_off=True)), 1) self.assertEqual(len(self.get_legacy_containers(stopped=True, one_off=True)), 1)
def test_migration_to_labels(self): def test_migration_to_labels(self):
with self.assertRaises(legacy.LegacyContainersError) as cm: with self.assertRaises(legacy.LegacyContainersError) as cm:
@ -52,7 +50,7 @@ class ProjectTest(DockerClientTestCase):
self.assertEqual( self.assertEqual(
set(cm.exception.names), set(cm.exception.names),
set(['composetest_web_1', 'composetest_db_1']), set(['composetest_db_1', 'composetest_web_1', 'composetest_nginx_1']),
) )
legacy.migrate_project_to_labels(self.project) legacy.migrate_project_to_labels(self.project)