Repository: docker/docker-py

Issue:
Swarm tests fail if host has multiple network interfaces
```
__________________ ERROR at setup of ServiceTest.test_create ___________________
tests/integration/models_services_test.py:14: in setUpClass
    client.swarm.init(listen_addr=helpers.swarm_listen_addr())
docker/models/swarm.py:96: in init
    self.client.api.init_swarm(**init_kwargs)
docker/utils/decorators.py:35: in wrapper
    return f(self, *args, **kwargs)
docker/api/swarm.py:102: in init_swarm
    self._raise_for_status(response)
docker/api/client.py:208: in _raise_for_status
    raise create_api_error_from_http_exception(e)
docker/errors.py:30: in create_api_error_from_http_exception
    raise cls(e, response=response, explanation=explanation)
E   APIError: 500 Server Error: Internal Server Error for url: http://172.17.0.1:12375/v1.24/swarm/init ("could not choose an IP address to advertise since this system has multiple addresses on different interfaces (10.0.2.15 on enp0s3 and 192.168.48.11 on enp0s8)")
```



Discussion:
Thank you for the report. We mostly run our tests inside containers (`make integration-dind`) but it's still worth looking into for sure.
In model/swarm.py, the client.api.init_swarm(**init_kwargs) method is being called without advertise_addr. That's where the problem is. If there is multiple interface advertise_addr  (or interface) should be given.

```
init_kwargs = {}
print kwargs
    for arg in ['advertise_addr', 'listen_addr', 'force_new_cluster']:
        print arg
        if arg in kwargs:
            print arg
            init_kwargs[arg] = kwargs[arg]
            print kwargs[arg]
            del kwargs[arg]
init_kwargs['swarm_spec'] = SwarmSpec(**kwargs)
print init_kwargs
self.client.api.init_swarm(**init_kwargs)
self.reload()
```

I solved it locally, by just adding 2 parameters:
```
self.client.api.init_swarm(advertise_addr, listen_addr, **init_kwargs)
```
@saifulhoque26 it seems to be tricky to code this for arbitrary test environments.

Perhaps the test should be skipped if multiple interfaces are present?

Resolving patch:
diff --git a/docker/models/swarm.py b/docker/models/swarm.py
--- a/docker/models/swarm.py
+++ b/docker/models/swarm.py
@@ -29,7 +29,7 @@ def version(self):
         return self.attrs.get('Version').get('Index')
 
     def init(self, advertise_addr=None, listen_addr='0.0.0.0:2377',
-             force_new_cluster=False, swarm_spec=None, **kwargs):
+             force_new_cluster=False, **kwargs):
         """
         Initialize a new swarm on this Engine.
 
@@ -87,11 +87,11 @@ def init(self, advertise_addr=None, listen_addr='0.0.0.0:2377',
             )
 
         """
-        init_kwargs = {}
-        for arg in ['advertise_addr', 'listen_addr', 'force_new_cluster']:
-            if arg in kwargs:
-                init_kwargs[arg] = kwargs[arg]
-                del kwargs[arg]
+        init_kwargs = {
+            'advertise_addr': advertise_addr,
+            'listen_addr': listen_addr,
+            'force_new_cluster': force_new_cluster
+        }
         init_kwargs['swarm_spec'] = SwarmSpec(**kwargs)
         self.client.api.init_swarm(**init_kwargs)
         self.reload()


Test patch:
diff --git a/tests/integration/models_nodes_test.py b/tests/integration/models_nodes_test.py
--- a/tests/integration/models_nodes_test.py
+++ b/tests/integration/models_nodes_test.py
@@ -14,7 +14,7 @@ def tearDown(self):
 
     def test_list_get_update(self):
         client = docker.from_env()
-        client.swarm.init(listen_addr=helpers.swarm_listen_addr())
+        client.swarm.init('eth0', listen_addr=helpers.swarm_listen_addr())
         nodes = client.nodes.list()
         assert len(nodes) == 1
         assert nodes[0].attrs['Spec']['Role'] == 'manager'
diff --git a/tests/integration/models_services_test.py b/tests/integration/models_services_test.py
--- a/tests/integration/models_services_test.py
+++ b/tests/integration/models_services_test.py
@@ -11,7 +11,7 @@ class ServiceTest(unittest.TestCase):
     def setUpClass(cls):
         client = docker.from_env()
         helpers.force_leave_swarm(client)
-        client.swarm.init(listen_addr=helpers.swarm_listen_addr())
+        client.swarm.init('eth0', listen_addr=helpers.swarm_listen_addr())
 
     @classmethod
     def tearDownClass(cls):
diff --git a/tests/integration/models_swarm_test.py b/tests/integration/models_swarm_test.py
--- a/tests/integration/models_swarm_test.py
+++ b/tests/integration/models_swarm_test.py
@@ -15,7 +15,8 @@ def tearDown(self):
     def test_init_update_leave(self):
         client = docker.from_env()
         client.swarm.init(
-            snapshot_interval=5000, listen_addr=helpers.swarm_listen_addr()
+            advertise_addr='eth0', snapshot_interval=5000,
+            listen_addr=helpers.swarm_listen_addr()
         )
         assert client.swarm.attrs['Spec']['Raft']['SnapshotInterval'] == 5000
         client.swarm.update(snapshot_interval=10000)