Ignore containers that don't have a name

If a container is in the process of being removed, or removal has
failed, it can sometimes appear in the output of GET /containers/json
but not have a 'Name' key. In that case, rather than crashing, we can
ignore it.

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
This commit is contained in:
Aanand Prasad 2015-08-10 16:00:45 +01:00
parent 6cb8e512f2
commit dfa4bf4452
6 changed files with 64 additions and 11 deletions

View File

@ -22,10 +22,14 @@ class Container(object):
""" """
Construct a container object from the output of GET /containers/json. Construct a container object from the output of GET /containers/json.
""" """
name = get_container_name(dictionary)
if name is None:
return None
new_dictionary = { new_dictionary = {
'Id': dictionary['Id'], 'Id': dictionary['Id'],
'Image': dictionary['Image'], 'Image': dictionary['Image'],
'Name': '/' + get_container_name(dictionary), 'Name': '/' + name,
} }
return cls(client, new_dictionary, **kwargs) return cls(client, new_dictionary, **kwargs)

View File

@ -310,11 +310,11 @@ class Project(object):
else: else:
service_names = self.service_names service_names = self.service_names
containers = [ containers = filter(None, [
Container.from_ps(self.client, container) Container.from_ps(self.client, container)
for container in self.client.containers( for container in self.client.containers(
all=stopped, all=stopped,
filters={'label': self.labels(one_off=one_off)})] filters={'label': self.labels(one_off=one_off)})])
def matches_service_names(container): def matches_service_names(container):
return container.labels.get(LABEL_SERVICE) in service_names return container.labels.get(LABEL_SERVICE) in service_names

View File

@ -101,11 +101,11 @@ class Service(object):
self.options = options self.options = options
def containers(self, stopped=False, one_off=False): def containers(self, stopped=False, one_off=False):
containers = [ containers = filter(None, [
Container.from_ps(self.client, container) Container.from_ps(self.client, container)
for container in self.client.containers( for container in self.client.containers(
all=stopped, all=stopped,
filters={'label': self.labels(one_off=one_off)})] filters={'label': self.labels(one_off=one_off)})])
if not containers: if not containers:
check_for_legacy_containers( check_for_legacy_containers(
@ -494,12 +494,13 @@ class Service(object):
# TODO: this would benefit from github.com/docker/docker/pull/11943 # TODO: this would benefit from github.com/docker/docker/pull/11943
# to remove the need to inspect every container # to remove the need to inspect every container
def _next_container_number(self, one_off=False): def _next_container_number(self, one_off=False):
numbers = [ containers = filter(None, [
Container.from_ps(self.client, container).number Container.from_ps(self.client, container)
for container in self.client.containers( for container in self.client.containers(
all=True, all=True,
filters={'label': self.labels(one_off=one_off)}) filters={'label': self.labels(one_off=one_off)})
] ])
numbers = [c.number for c in containers]
return 1 if not numbers else max(numbers) + 1 return 1 if not numbers else max(numbers) + 1
def _get_links(self, link_to_self): def _get_links(self, link_to_self):

View File

@ -65,7 +65,7 @@ class UtilitiesTestCase(unittest.TestCase):
legacy.is_valid_name("composetest_web_lol_1", one_off=True), legacy.is_valid_name("composetest_web_lol_1", one_off=True),
) )
def test_get_legacy_containers_no_labels(self): def test_get_legacy_containers(self):
client = Mock() client = Mock()
client.containers.return_value = [ client.containers.return_value = [
{ {
@ -74,12 +74,23 @@ class UtilitiesTestCase(unittest.TestCase):
"Name": "composetest_web_1", "Name": "composetest_web_1",
"Labels": None, "Labels": None,
}, },
{
"Id": "ghi789",
"Image": "def456",
"Name": None,
"Labels": None,
},
{
"Id": "jkl012",
"Image": "def456",
"Labels": None,
},
] ]
containers = list(legacy.get_legacy_containers( containers = legacy.get_legacy_containers(client, "composetest", ["web"])
client, "composetest", ["web"]))
self.assertEqual(len(containers), 1) self.assertEqual(len(containers), 1)
self.assertEqual(containers[0].id, 'abc123')
class LegacyTestCase(DockerClientTestCase): class LegacyTestCase(DockerClientTestCase):

View File

@ -3,6 +3,7 @@ from .. import unittest
from compose.service import Service from compose.service import Service
from compose.project import Project from compose.project import Project
from compose.container import Container from compose.container import Container
from compose.const import LABEL_SERVICE
import mock import mock
import docker import docker
@ -260,3 +261,27 @@ class ProjectTest(unittest.TestCase):
service = project.get_service('test') service = project.get_service('test')
self.assertEqual(service._get_net(), 'container:' + container_name) self.assertEqual(service._get_net(), 'container:' + container_name)
def test_container_without_name(self):
self.mock_client.containers.return_value = [
{'Image': 'busybox:latest', 'Id': '1', 'Name': '1'},
{'Image': 'busybox:latest', 'Id': '2', 'Name': None},
{'Image': 'busybox:latest', 'Id': '3'},
]
self.mock_client.inspect_container.return_value = {
'Id': '1',
'Config': {
'Labels': {
LABEL_SERVICE: 'web',
},
},
}
project = Project.from_dicts(
'test',
[{
'name': 'web',
'image': 'busybox:latest',
}],
self.mock_client,
)
self.assertEqual([c.id for c in project.containers()], ['1'])

View File

@ -76,6 +76,18 @@ class ServiceTest(unittest.TestCase):
all=False, all=False,
filters={'label': expected_labels}) filters={'label': expected_labels})
def test_container_without_name(self):
self.mock_client.containers.return_value = [
{'Image': 'foo', 'Id': '1', 'Name': '1'},
{'Image': 'foo', 'Id': '2', 'Name': None},
{'Image': 'foo', 'Id': '3'},
]
service = Service('db', self.mock_client, 'myproject', image='foo')
self.assertEqual([c.id for c in service.containers()], ['1'])
self.assertEqual(service._next_container_number(), 2)
self.assertEqual(service.get_container(1).id, '1')
def test_get_volumes_from_container(self): def test_get_volumes_from_container(self):
container_id = 'aabbccddee' container_id = 'aabbccddee'
service = Service( service = Service(