From 90d13d5fd2bb057d9d7a38673cea8b1e0a319c60 Mon Sep 17 00:00:00 2001 From: Peter Yu Date: Sat, 9 Nov 2013 16:36:56 +0000 Subject: [PATCH] links implemented. use v1.6 by default now --- docker/client.py | 16 ++++-- tests/fake_api.py | 4 +- tests/integration_test.py | 88 ++++++++++++++++++++++++++++++ tests/test.py | 109 +++++++++++++++++++++++++------------- 4 files changed, 173 insertions(+), 44 deletions(-) diff --git a/docker/client.py b/docker/client.py index 7c838ee5..7bf5d9ba 100644 --- a/docker/client.py +++ b/docker/client.py @@ -62,7 +62,7 @@ class APIError(requests.exceptions.HTTPError): class Client(requests.Session): - def __init__(self, base_url="unix://var/run/docker.sock", version="1.4"): + def __init__(self, base_url="unix://var/run/docker.sock", version="1.6"): super(Client, self).__init__() if base_url.startswith('unix:///'): base_url = base_url.replace('unix:/', 'unix:') @@ -278,7 +278,7 @@ class Client(requests.Session): 'since': since, 'before': before } - u = self._url("/containers/ps") + u = self._url("/containers/json") res = self._result(self.get(u, params=params), True) if quiet: return [{'Id': x['Id']} for x in res] @@ -524,10 +524,10 @@ class Client(requests.Session): else: return self._result(self._post_json(u, authcfg, stream=False)) - def remove_container(self, container, v=False): + def remove_container(self, container, v=False, link=False): if isinstance(container, dict): container = container.get('Id') - params = {'v': v} + params = {'v': v, 'link': link} res = self.delete(self._url("/containers/" + container), params=params) self._raise_for_status(res) @@ -548,7 +548,7 @@ class Client(requests.Session): params={'term': term}), True) def start(self, container, binds=None, port_bindings=None, lxc_conf=None, - publish_all_ports=False): + publish_all_ports=False, links=None): if isinstance(container, dict): container = container.get('Id') @@ -572,6 +572,12 @@ class Client(requests.Session): start_config['PublishAllPorts'] = publish_all_ports + if links: + formatted_links = [ + '{0}:{1}'.format(path, alias) for path, alias in links.items() + ] + start_config['Links'] = formatted_links + url = self._url("/containers/{0}/start".format(container)) res = self._post_json(url, start_config) self._raise_for_status(res) diff --git a/tests/fake_api.py b/tests/fake_api.py index 1c611daa..6d115663 100644 --- a/tests/fake_api.py +++ b/tests/fake_api.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -CURRENT_VERSION = 'v1.4' +CURRENT_VERSION = 'v1.6' FAKE_CONTAINER_ID = '3cc2351ab11b' FAKE_IMAGE_ID = 'e9aa60c60128' @@ -170,7 +170,7 @@ fake_responses = { get_fake_search, '{1}/{0}/images/json'.format(CURRENT_VERSION, prefix): get_fake_images, - '{1}/{0}/containers/ps'.format(CURRENT_VERSION, prefix): + '{1}/{0}/containers/json'.format(CURRENT_VERSION, prefix): get_fake_containers, '{1}/{0}/containers/3cc2351ab11b/start'.format(CURRENT_VERSION, prefix): post_fake_start_container, diff --git a/tests/integration_test.py b/tests/integration_test.py index 4db21808..6f303dde 100644 --- a/tests/integration_test.py +++ b/tests/integration_test.py @@ -435,6 +435,94 @@ class TestRemoveContainerWithDictInsteadOfId(BaseTestCase): res = [x for x in containers if 'Id' in x and x['Id'].startswith(id)] self.assertEqual(len(res), 0) + +class TestStartContainerWithLinks(BaseTestCase): + def runTest(self): + res0 = self.client.create_container( + 'busybox', 'cat', + detach=True, stdin_open=True, + environment={'FOO': '1'}) + + container1_id = res0['Id'] + self.tmp_containers.append(container1_id) + + self.client.start(container1_id) + + res1 = self.client.create_container( + 'busybox', 'cat', + detach=True, stdin_open=True, + environment={'FOO': '1'}) + + container2_id = res1['Id'] + self.tmp_containers.append(container2_id) + + self.client.start(container2_id) + + # we don't want the first / + link_path1 = self.client.inspect_container(container1_id)['Name'][1:] + link_alias1 = 'mylink1' + link_env_prefix1 = link_alias1.upper() + + link_path2 = self.client.inspect_container(container2_id)['Name'][1:] + link_alias2 = 'mylink2' + link_env_prefix2 = link_alias2.upper() + + res2 = self.client.create_container('busybox', 'env') + container3_id = res2['Id'] + self.tmp_containers.append(container3_id) + self.client.start( + container3_id, + links={link_path1: link_alias1, link_path2: link_alias2} + ) + self.assertEqual(self.client.wait(container3_id), 0) + + logs = self.client.logs(container3_id) + self.assertIn('{0}_NAME='.format(link_env_prefix1), logs) + self.assertIn('{0}_ENV_FOO=1'.format(link_env_prefix1), logs) + self.assertIn('{0}_NAME='.format(link_env_prefix2), logs) + self.assertIn('{0}_ENV_FOO=1'.format(link_env_prefix2), logs) + +################# +## LINKS TESTS ## +################# + + +class TestRemoveLink(BaseTestCase): + def runTest(self): + # Create containers + container1 = self.client.create_container( + 'busybox', 'cat', detach=True, stdin_open=True) + container1_id = container1['Id'] + self.tmp_containers.append(container1_id) + self.client.start(container1_id) + + # Create Link + # we don't want the first / + link_path = self.client.inspect_container(container1_id)['Name'][1:] + link_alias = 'mylink' + + container2 = self.client.create_container('busybox', 'cat') + container2_id = container2['Id'] + self.tmp_containers.append(container2_id) + self.client.start(container2_id, links={link_path: link_alias}) + + # Remove link + linked_name = self.client.inspect_container(container2_id)['Name'][1:] + link_name = '%s/%s' % (linked_name, link_alias) + self.client.remove_container(link_name, link=True) + + # Link is gone + containers = self.client.containers(all=True) + retrieved = [x for x in containers if link_name in x['Names']] + self.assertEqual(len(retrieved), 0) + + # Containers are still there + retrieved = [ + x for x in containers if x['Id'].startswith(container1_id) + or x['Id'].startswith(container2_id) + ] + self.assertEqual(len(retrieved), 2) + ################## ## IMAGES TESTS ## ################## diff --git a/tests/test.py b/tests/test.py index 3a62f7f2..0ed066bc 100644 --- a/tests/test.py +++ b/tests/test.py @@ -76,7 +76,7 @@ class DockerClientTest(unittest.TestCase): self.fail('Command should not raise exception: {0}'.format(e)) fake_request.assert_called_with( - 'unix://var/run/docker.sock/v1.4/version' + 'unix://var/run/docker.sock/v1.6/version' ) def test_info(self): @@ -85,7 +85,7 @@ class DockerClientTest(unittest.TestCase): except Exception as e: self.fail('Command should not raise exception: {0}'.format(e)) - fake_request.assert_called_with('unix://var/run/docker.sock/v1.4/info') + fake_request.assert_called_with('unix://var/run/docker.sock/v1.6/info') def test_search(self): try: @@ -94,7 +94,7 @@ class DockerClientTest(unittest.TestCase): self.fail('Command should not raise exception: {0}'.format(e)) fake_request.assert_called_with( - 'unix://var/run/docker.sock/v1.4/images/search', + 'unix://var/run/docker.sock/v1.6/images/search', params={'term': 'busybox'} ) @@ -108,7 +108,7 @@ class DockerClientTest(unittest.TestCase): except Exception as e: self.fail('Command should not raise exception: {0}'.format(e)) fake_request.assert_called_with( - 'unix://var/run/docker.sock/v1.4/images/json', + 'unix://var/run/docker.sock/v1.6/images/json', params={'filter': None, 'only_ids': 0, 'all': 1} ) @@ -119,7 +119,7 @@ class DockerClientTest(unittest.TestCase): self.fail('Command should not raise exception: {0}'.format(e)) fake_request.assert_called_with( - 'unix://var/run/docker.sock/v1.4/images/json', + 'unix://var/run/docker.sock/v1.6/images/json', params={'filter': None, 'only_ids': 1, 'all': 0} ) @@ -130,7 +130,7 @@ class DockerClientTest(unittest.TestCase): self.fail('Command should not raise exception: {0}'.format(e)) fake_request.assert_called_with( - 'unix://var/run/docker.sock/v1.4/containers/ps', + 'unix://var/run/docker.sock/v1.6/containers/json', params={ 'all': 1, 'since': None, @@ -152,7 +152,7 @@ class DockerClientTest(unittest.TestCase): args = fake_request.call_args self.assertEqual(args[0][0], - 'unix://var/run/docker.sock/v1.4/containers/create') + 'unix://var/run/docker.sock/v1.6/containers/create') self.assertEqual(json.loads(args[0][1]), json.loads(''' {"Tty": false, "Image": "busybox", "Cmd": ["true"], @@ -174,7 +174,7 @@ class DockerClientTest(unittest.TestCase): args = fake_request.call_args self.assertEqual(args[0][0], - 'unix://var/run/docker.sock/v1.4/containers/create') + 'unix://var/run/docker.sock/v1.6/containers/create') self.assertEqual(json.loads(args[0][1]), json.loads(''' {"Tty": false, "Image": "busybox", @@ -193,7 +193,7 @@ class DockerClientTest(unittest.TestCase): args = fake_request.call_args self.assertEqual(args[0][0], - 'unix://var/run/docker.sock/v1.4/containers/create') + 'unix://var/run/docker.sock/v1.6/containers/create') self.assertEqual(json.loads(args[0][1]), json.loads(''' {"Tty": false, "Image": "busybox", "Cmd": ["true"], @@ -212,7 +212,7 @@ class DockerClientTest(unittest.TestCase): args = fake_request.call_args self.assertEqual(args[0][0], - 'unix://var/run/docker.sock/v1.4/containers/create') + 'unix://var/run/docker.sock/v1.6/containers/create') self.assertEqual(json.loads(args[0][1]), json.loads(''' {"Tty": false, "Image": "busybox", "Cmd": ["true"], @@ -230,7 +230,7 @@ class DockerClientTest(unittest.TestCase): self.fail('Command should not raise exception: {0}'.format(e)) fake_request.assert_called_with( - 'unix://var/run/docker.sock/v1.4/containers/3cc2351ab11b/start', + 'unix://var/run/docker.sock/v1.6/containers/3cc2351ab11b/start', '{"PublishAllPorts": false}', headers={'Content-Type': 'application/json'} ) @@ -244,7 +244,7 @@ class DockerClientTest(unittest.TestCase): except Exception as e: self.fail('Command should not raise exception: {0}'.format(e)) args = fake_request.call_args - self.assertEqual(args[0][0], 'unix://var/run/docker.sock/v1.4/' + self.assertEqual(args[0][0], 'unix://var/run/docker.sock/v1.6/' 'containers/3cc2351ab11b/start') self.assertEqual( json.loads(args[0][1]), @@ -263,7 +263,7 @@ class DockerClientTest(unittest.TestCase): except Exception as e: self.fail('Command should not raise exception: {0}'.format(e)) args = fake_request.call_args - self.assertEqual(args[0][0], 'unix://var/run/docker.sock/v1.4/' + self.assertEqual(args[0][0], 'unix://var/run/docker.sock/v1.6/' 'containers/3cc2351ab11b/start') self.assertEqual( json.loads(args[0][1]), @@ -283,7 +283,7 @@ class DockerClientTest(unittest.TestCase): self.fail('Command should not raise exception: {0}'.format(e)) args = fake_request.call_args - self.assertEqual(args[0][0], 'unix://var/run/docker.sock/v1.4/' + self.assertEqual(args[0][0], 'unix://var/run/docker.sock/v1.6/' 'containers/3cc2351ab11b/start') self.assertEqual(json.loads(args[0][1]), {"Binds": ["/tmp:/mnt"], "PublishAllPorts": False}) @@ -296,11 +296,35 @@ class DockerClientTest(unittest.TestCase): except Exception as e: self.fail('Command should not raise exception: {0}'.format(e)) fake_request.assert_called_with( - 'unix://var/run/docker.sock/v1.4/containers/3cc2351ab11b/start', + 'unix://var/run/docker.sock/v1.6/containers/3cc2351ab11b/start', '{"PublishAllPorts": false}', headers={'Content-Type': 'application/json'} ) + def test_start_container_with_links(self): + try: + self.client.start( + fake_api.FAKE_CONTAINER_ID, + links={'link1': 'alias1', 'link2': 'alias2'} + ) + except Exception as e: + self.fail('Command should not raise exception: {0}'.format(e)) + + args = fake_request.call_args + self.assertEqual(args[0][0], 'unix://var/run/docker.sock/v1.6/' + 'containers/3cc2351ab11b/start') + params = json.loads(args[0][1]) + self.assertEqual(sorted(["Links", "PublishAllPorts"]), + sorted(params.keys())) + self.assertEqual(params["PublishAllPorts"], False) + # We have to do this since the order is not guaranteed + links = params["Links"] + self.assertTrue(len(["link1:alias1", "link2:alias2"]) == len(links)) + self.assertTrue( + sorted(["link1:alias1", "link2:alias2"]) == sorted(links)) + self.assertEqual(args[1]['headers'], + {'Content-Type': 'application/json'}) + def test_wait(self): try: self.client.wait(fake_api.FAKE_CONTAINER_ID) @@ -308,7 +332,7 @@ class DockerClientTest(unittest.TestCase): self.fail('Command should not raise exception: {0}'.format(e)) fake_request.assert_called_with( - 'unix://var/run/docker.sock/v1.4/containers/3cc2351ab11b/wait', + 'unix://var/run/docker.sock/v1.6/containers/3cc2351ab11b/wait', None, timeout=None ) @@ -320,7 +344,7 @@ class DockerClientTest(unittest.TestCase): self.fail('Command should not raise exception: {0}'.format(e)) fake_request.assert_called_with( - 'unix://var/run/docker.sock/v1.4/containers/3cc2351ab11b/wait', + 'unix://var/run/docker.sock/v1.6/containers/3cc2351ab11b/wait', None, timeout=None ) @@ -332,7 +356,7 @@ class DockerClientTest(unittest.TestCase): self.fail('Command should not raise exception: {0}'.format(e)) fake_request.assert_called_with( - 'unix://var/run/docker.sock/v1.4/containers/3cc2351ab11b/attach', + 'unix://var/run/docker.sock/v1.6/containers/3cc2351ab11b/attach', None, params={'logs': 1, 'stderr': 1, 'stdout': 1} ) @@ -344,7 +368,7 @@ class DockerClientTest(unittest.TestCase): self.fail('Command should not raise exception: {0}'.format(e)) fake_request.assert_called_with( - 'unix://var/run/docker.sock/v1.4/containers/3cc2351ab11b/attach', + 'unix://var/run/docker.sock/v1.6/containers/3cc2351ab11b/attach', None, params={'logs': 1, 'stderr': 1, 'stdout': 1} ) @@ -356,7 +380,7 @@ class DockerClientTest(unittest.TestCase): self.fail('Command should not raise exception: {0}'.format(e)) fake_request.assert_called_with( - 'unix://var/run/docker.sock/v1.4/containers/3cc2351ab11b/changes') + 'unix://var/run/docker.sock/v1.6/containers/3cc2351ab11b/changes') def test_diff_with_dict_instead_of_id(self): try: @@ -365,7 +389,7 @@ class DockerClientTest(unittest.TestCase): self.fail('Command should not raise exception: {0}'.format(e)) fake_request.assert_called_with( - 'unix://var/run/docker.sock/v1.4/containers/3cc2351ab11b/changes') + 'unix://var/run/docker.sock/v1.6/containers/3cc2351ab11b/changes') def test_stop_container(self): try: @@ -374,7 +398,7 @@ class DockerClientTest(unittest.TestCase): self.fail('Command should not raise exception: {0}'.format(e)) fake_request.assert_called_with( - 'unix://var/run/docker.sock/v1.4/containers/3cc2351ab11b/stop', + 'unix://var/run/docker.sock/v1.6/containers/3cc2351ab11b/stop', None, params={'t': 2} ) @@ -386,7 +410,7 @@ class DockerClientTest(unittest.TestCase): self.fail('Command should not raise exception: {0}'.format(e)) fake_request.assert_called_with( - 'unix://var/run/docker.sock/v1.4/containers/3cc2351ab11b/stop', + 'unix://var/run/docker.sock/v1.6/containers/3cc2351ab11b/stop', None, params={'t': 2} ) @@ -398,7 +422,7 @@ class DockerClientTest(unittest.TestCase): self.fail('Command should not raise exception: {0}'.format(e)) fake_request.assert_called_with( - 'unix://var/run/docker.sock/v1.4/containers/3cc2351ab11b/kill', + 'unix://var/run/docker.sock/v1.6/containers/3cc2351ab11b/kill', None ) @@ -409,7 +433,7 @@ class DockerClientTest(unittest.TestCase): self.fail('Command should not raise exception: {0}'.format(e)) fake_request.assert_called_with( - 'unix://var/run/docker.sock/v1.4/containers/3cc2351ab11b/kill', + 'unix://var/run/docker.sock/v1.6/containers/3cc2351ab11b/kill', None ) @@ -420,7 +444,7 @@ class DockerClientTest(unittest.TestCase): self.fail('Command should not raise exception : {0}'.format(e)) fake_request.assert_called_with( - 'unix://var/run/docker.sock/v1.4/containers/3cc2351ab11b/restart', + 'unix://var/run/docker.sock/v1.6/containers/3cc2351ab11b/restart', None, params={'t': 2} ) @@ -432,7 +456,7 @@ class DockerClientTest(unittest.TestCase): self.fail('Command should not raise exception: {0}'.format(e)) fake_request.assert_called_with( - 'unix://var/run/docker.sock/v1.4/containers/3cc2351ab11b/restart', + 'unix://var/run/docker.sock/v1.6/containers/3cc2351ab11b/restart', None, params={'t': 2} ) @@ -444,8 +468,8 @@ class DockerClientTest(unittest.TestCase): self.fail('Command should not raise exception: {0}'.format(e)) fake_request.assert_called_with( - 'unix://var/run/docker.sock/v1.4/containers/3cc2351ab11b', - params={'v': False} + 'unix://var/run/docker.sock/v1.6/containers/3cc2351ab11b', + params={'v': False, 'link': False} ) def test_remove_container_with_dict_instead_of_id(self): @@ -455,8 +479,19 @@ class DockerClientTest(unittest.TestCase): self.fail('Command should not raise exception: {0}'.format(e)) fake_request.assert_called_with( - 'unix://var/run/docker.sock/v1.4/containers/3cc2351ab11b', - params={'v': False} + 'unix://var/run/docker.sock/v1.6/containers/3cc2351ab11b', + params={'v': False, 'link': False} + ) + + def test_remove_link(self): + try: + self.client.remove_container(fake_api.FAKE_CONTAINER_ID, link=True) + except Exception as e: + self.fail('Command should not raise exception: {0}'.format(e)) + + fake_request.assert_called_with( + 'unix://var/run/docker.sock/v1.6/containers/3cc2351ab11b', + params={'v': False, 'link': True} ) ################## @@ -470,7 +505,7 @@ class DockerClientTest(unittest.TestCase): self.fail('Command should not raise exception: {0}'.format(e)) fake_request.assert_called_with( - 'unix://var/run/docker.sock/v1.4/images/create', + 'unix://var/run/docker.sock/v1.6/images/create', headers={}, params={'tag': None, 'fromImage': 'joffrey/test001'}, stream=False @@ -483,7 +518,7 @@ class DockerClientTest(unittest.TestCase): self.fail('Command should not raise exception: {0}'.format(e)) fake_request.assert_called_with( - 'unix://var/run/docker.sock/v1.4/images/create', + 'unix://var/run/docker.sock/v1.6/images/create', headers={}, params={'tag': None, 'fromImage': 'joffrey/test001'}, stream=True @@ -496,7 +531,7 @@ class DockerClientTest(unittest.TestCase): self.fail('Command should not raise exception: {0}'.format(e)) fake_request.assert_called_with( - 'unix://var/run/docker.sock/v1.4/commit', + 'unix://var/run/docker.sock/v1.6/commit', '{}', headers={'Content-Type': 'application/json'}, params={ @@ -515,7 +550,7 @@ class DockerClientTest(unittest.TestCase): self.fail('Command should not raise exception: {0}'.format(e)) fake_request.assert_called_with( - 'unix://var/run/docker.sock/v1.4/images/e9aa60c60128' + 'unix://var/run/docker.sock/v1.6/images/e9aa60c60128' ) ################# @@ -559,9 +594,9 @@ class DockerClientTest(unittest.TestCase): cfg = docker.auth.load_config(folder) self.assertTrue(cfg is not None) self.assertTrue('Configs' in cfg) - self.assertEquals(cfg['Configs'], {}) + self.assertEqual(cfg['Configs'], {}) self.assertTrue('rootPath' in cfg) - self.assertEquals(cfg['rootPath'], folder) + self.assertEqual(cfg['rootPath'], folder) def test_load_config(self): folder = tempfile.mkdtemp()