From 73fbd01cfe7c1e8ad7d297d7f7cfb8704aeb501d Mon Sep 17 00:00:00 2001
From: Aanand Prasad <aanand.prasad@gmail.com>
Date: Thu, 14 Jan 2016 13:39:44 +0000
Subject: [PATCH] Support the 'external' option for networks

Signed-off-by: Aanand Prasad <aanand.prasad@gmail.com>
---
 compose/network.py                            | 25 +++++++++++++++++--
 compose/project.py                            |  4 ++-
 tests/acceptance/cli_test.py                  | 23 +++++++++++++++++
 tests/fixtures/networks/external-networks.yml | 16 ++++++++++++
 4 files changed, 65 insertions(+), 3 deletions(-)
 create mode 100644 tests/fixtures/networks/external-networks.yml

diff --git a/compose/network.py b/compose/network.py
index a8f7e918d..b2ba2e9b7 100644
--- a/compose/network.py
+++ b/compose/network.py
@@ -11,16 +11,35 @@ from .config import ConfigurationError
 log = logging.getLogger(__name__)
 
 
-# TODO: support external networks
 class Network(object):
-    def __init__(self, client, project, name, driver=None, driver_opts=None):
+    def __init__(self, client, project, name, driver=None, driver_opts=None,
+                 external_name=None):
         self.client = client
         self.project = project
         self.name = name
         self.driver = driver
         self.driver_opts = driver_opts
+        self.external_name = external_name
 
     def ensure(self):
+        if self.external_name:
+            try:
+                self.inspect()
+                log.debug(
+                    'Network {0} declared as external. No new '
+                    'network will be created.'.format(self.name)
+                )
+            except NotFound:
+                raise ConfigurationError(
+                    'Network {name} declared as external, but could'
+                    ' not be found. Please create the network manually'
+                    ' using `{command} {name}` and try again.'.format(
+                        name=self.external_name,
+                        command='docker network create'
+                    )
+                )
+            return
+
         try:
             data = self.inspect()
             if self.driver and data['Driver'] != self.driver:
@@ -55,4 +74,6 @@ class Network(object):
 
     @property
     def full_name(self):
+        if self.external_name:
+            return self.external_name
         return '{0}_{1}'.format(self.project, self.name)
diff --git a/compose/project.py b/compose/project.py
index 1b5d2eb94..6a171d514 100644
--- a/compose/project.py
+++ b/compose/project.py
@@ -64,7 +64,9 @@ class Project(object):
                 custom_networks.append(
                     Network(
                         client=client, project=name, name=network_name,
-                        driver=data.get('driver'), driver_opts=data.get('driver_opts')
+                        driver=data.get('driver'),
+                        driver_opts=data.get('driver_opts'),
+                        external_name=data.get('external_name'),
                     )
                 )
 
diff --git a/tests/acceptance/cli_test.py b/tests/acceptance/cli_test.py
index 90c507698..a7d5dbaae 100644
--- a/tests/acceptance/cli_test.py
+++ b/tests/acceptance/cli_test.py
@@ -456,6 +456,29 @@ class CLITestCase(DockerClientTestCase):
             assert container.get('NetworkSettings.Networks').keys() == [name]
             assert container.get('HostConfig.NetworkMode') == name
 
+    def test_up_external_networks(self):
+        filename = 'external-networks.yml'
+
+        self.base_dir = 'tests/fixtures/networks'
+        self._project = get_project(self.base_dir, [filename])
+
+        result = self.dispatch(['-f', filename, 'up', '-d'], returncode=1)
+        assert 'declared as external, but could not be found' in result.stderr
+
+        networks = [
+            n['Name'] for n in self.client.networks()
+            if n['Name'].startswith('{}_'.format(self.project.name))
+        ]
+        assert not networks
+
+        network_names = ['{}_{}'.format(self.project.name, n) for n in ['foo', 'bar']]
+        for name in network_names:
+            self.client.create_network(name)
+
+        self.dispatch(['-f', filename, 'up', '-d'])
+        container = self.project.containers()[0]
+        assert sorted(container.get('NetworkSettings.Networks').keys()) == sorted(network_names)
+
     def test_up_no_services(self):
         self.base_dir = 'tests/fixtures/no-services'
         self.dispatch(['up', '-d'], None)
diff --git a/tests/fixtures/networks/external-networks.yml b/tests/fixtures/networks/external-networks.yml
new file mode 100644
index 000000000..644e3dda9
--- /dev/null
+++ b/tests/fixtures/networks/external-networks.yml
@@ -0,0 +1,16 @@
+version: 2
+
+services:
+  web:
+    image: busybox
+    command: top
+    networks:
+      - networks_foo
+      - bar
+
+networks:
+  networks_foo:
+    external: true
+  bar:
+    external:
+      name: networks_bar