diff --git a/fig/container.py b/fig/container.py index 71f5baa2f1..a4042c868b 100644 --- a/fig/container.py +++ b/fig/container.py @@ -67,7 +67,7 @@ def number(self): @property def ports(self): self.inspect_if_not_inspected() - return self.dictionary['NetworkSettings']['Ports'] or {} + return self.get('NetworkSettings.Ports') or {} @property def human_readable_ports(self): @@ -82,33 +82,35 @@ def format_port(private, public): @property def human_readable_state(self): - self.inspect_if_not_inspected() - if self.dictionary['State']['Running']: - if self.dictionary['State'].get('Ghost'): - return 'Ghost' - else: - return 'Up' + if self.is_running: + return 'Ghost' if self.get('State.Ghost') else 'Up' else: - return 'Exit %s' % self.dictionary['State']['ExitCode'] + return 'Exit %s' % self.get('State.ExitCode') @property def human_readable_command(self): - self.inspect_if_not_inspected() - if self.dictionary['Config']['Cmd']: - return ' '.join(self.dictionary['Config']['Cmd']) - else: - return '' + return ' '.join(self.get('Config.Cmd') or '') @property def environment(self): - self.inspect_if_not_inspected() - return dict(var.split("=", 1) - for var in self.dictionary.get('Config', {}).get('Env', [])) + return dict(var.split("=", 1) for var in self.get('Config.Env') or []) @property def is_running(self): + return self.get('State.Running') + + def get(self, key): + """Return a value from the container or None if the value is not set. + + :param key: a string using dotted notation for nested dictionary + lookups + """ self.inspect_if_not_inspected() - return self.dictionary['State']['Running'] + + def get_value(dictionary, key): + return (dictionary or {}).get(key) + + return reduce(get_value, key.split('.'), self.dictionary) def get_local_port(self, port, protocol='tcp'): port = self.ports.get("%s/%s" % (port, protocol)) diff --git a/fig/service.py b/fig/service.py index 29ad348740..d13ba0c359 100644 --- a/fig/service.py +++ b/fig/service.py @@ -200,6 +200,10 @@ def recreate_containers(self, **override_options): return tuples def recreate_container(self, container, **override_options): + """Recreate a container. An intermediate container is created so that + the new container has the same name, while still supporting + `volumes-from` the original container. + """ try: container.stop() except APIError as e: diff --git a/tests/integration/project_test.py b/tests/integration/project_test.py index a9123af85a..ce08724581 100644 --- a/tests/integration/project_test.py +++ b/tests/integration/project_test.py @@ -94,7 +94,7 @@ def test_project_up(self): def test_project_up_recreates_containers(self): web = self.create_service('web') - db = self.create_service('db', volumes=['/var/db']) + db = self.create_service('db', volumes=['/etc']) project = Project('figtest', [web, db], self.client) project.start() self.assertEqual(len(project.containers()), 0) @@ -102,15 +102,14 @@ def test_project_up_recreates_containers(self): project.up(['db']) self.assertEqual(len(project.containers()), 1) old_db_id = project.containers()[0].id - db_volume_path = project.containers()[0].inspect()['Volumes']['/var/db'] + db_volume_path = project.containers()[0].get('Volumes./etc') project.up() self.assertEqual(len(project.containers()), 2) db_container = [c for c in project.containers() if 'db' in c.name][0] self.assertNotEqual(db_container.id, old_db_id) - self.assertEqual(db_container.inspect()['Volumes']['/var/db'], - db_volume_path) + self.assertEqual(db_container.get('Volumes./etc'), db_volume_path) project.kill() project.remove_stopped() diff --git a/tests/integration/service_test.py b/tests/integration/service_test.py index 16b1d97030..e2431f48e8 100644 --- a/tests/integration/service_test.py +++ b/tests/integration/service_test.py @@ -107,14 +107,16 @@ def test_create_container_with_volumes_from(self): host_service = self.create_service('host', volumes_from=[volume_service, volume_container_2]) host_container = host_service.create_container() host_service.start_container(host_container) - self.assertIn(volume_container_1.id, host_container.inspect()['HostConfig']['VolumesFrom']) - self.assertIn(volume_container_2.id, host_container.inspect()['HostConfig']['VolumesFrom']) + self.assertIn(volume_container_1.id, + host_container.get('HostConfig.VolumesFrom')) + self.assertIn(volume_container_2.id, + host_container.get('HostConfig.VolumesFrom')) def test_recreate_containers(self): service = self.create_service( 'db', environment={'FOO': '1'}, - volumes=['/var/db'], + volumes=['/etc'], entrypoint=['sleep'], command=['300'] ) @@ -124,7 +126,7 @@ def test_recreate_containers(self): self.assertIn('FOO=1', old_container.dictionary['Config']['Env']) self.assertEqual(old_container.name, 'figtest_db_1') service.start_container(old_container) - volume_path = old_container.inspect()['Volumes']['/var/db'] + volume_path = old_container.inspect()['Volumes']['/etc'] num_containers_before = len(self.client.containers(all=True)) @@ -140,7 +142,7 @@ def test_recreate_containers(self): self.assertEqual(new_container.dictionary['Config']['Cmd'], ['300']) self.assertIn('FOO=2', new_container.dictionary['Config']['Env']) self.assertEqual(new_container.name, 'figtest_db_1') - self.assertEqual(new_container.inspect()['Volumes']['/var/db'], volume_path) + self.assertEqual(new_container.inspect()['Volumes']['/etc'], volume_path) self.assertIn(intermediate_container.id, new_container.dictionary['HostConfig']['VolumesFrom']) self.assertEqual(len(self.client.containers(all=True)), num_containers_before) @@ -340,18 +342,18 @@ def test_scale_sets_ports(self): def test_network_mode_none(self): service = self.create_service('web', net='none') - container = service.start_container().inspect() - self.assertEqual(container['HostConfig']['NetworkMode'], 'none') + container = service.start_container() + self.assertEqual(container.get('HostConfig.NetworkMode'), 'none') def test_network_mode_bridged(self): service = self.create_service('web', net='bridge') - container = service.start_container().inspect() - self.assertEqual(container['HostConfig']['NetworkMode'], 'bridge') + container = service.start_container() + self.assertEqual(container.get('HostConfig.NetworkMode'), 'bridge') def test_network_mode_host(self): service = self.create_service('web', net='host') - container = service.start_container().inspect() - self.assertEqual(container['HostConfig']['NetworkMode'], 'host') + container = service.start_container() + self.assertEqual(container.get('HostConfig.NetworkMode'), 'host') def test_dns_single_value(self): service = self.create_service('web', dns='8.8.8.8') diff --git a/tests/unit/container_test.py b/tests/unit/container_test.py index db37e09365..18f7944eb3 100644 --- a/tests/unit/container_test.py +++ b/tests/unit/container_test.py @@ -105,3 +105,15 @@ def test_get_local_port(self): self.assertEqual( container.get_local_port(45454, protocol='tcp'), '0.0.0.0:49197') + + def test_get(self): + container = Container(None, { + "Status":"Up 8 seconds", + "HostConfig": { + "VolumesFrom": ["volume_id",] + }, + }, has_been_inspected=True) + + self.assertEqual(container.get('Status'), "Up 8 seconds") + self.assertEqual(container.get('HostConfig.VolumesFrom'), ["volume_id",]) + self.assertEqual(container.get('Foo.Bar.DoesNotExist'), None)