From e941c51b84ba3f9ce905e8cb0c587a56876fae48 Mon Sep 17 00:00:00 2001 From: Barry Shapira Date: Tue, 11 Dec 2018 22:06:59 -0800 Subject: [PATCH 1/7] Added arguments to creeate a swarm with a custom address pool and subnet size. Signed-off-by: Barry Shapira --- docker/api/swarm.py | 9 +++++++++ docker/models/swarm.py | 10 ++++++++++ tests/integration/api_swarm_test.py | 23 +++++++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/docker/api/swarm.py b/docker/api/swarm.py index 04595da139..bec3efdf23 100644 --- a/docker/api/swarm.py +++ b/docker/api/swarm.py @@ -82,6 +82,7 @@ def get_unlock_key(self): @utils.minimum_version('1.24') def init_swarm(self, advertise_addr=None, listen_addr='0.0.0.0:2377', + default_addr_pool=[], subnet_size=24, force_new_cluster=False, swarm_spec=None): """ Initialize a new Swarm using the current connected engine as the first @@ -102,6 +103,12 @@ def init_swarm(self, advertise_addr=None, listen_addr='0.0.0.0:2377', or an interface followed by a port number, like ``eth0:4567``. If the port number is omitted, the default swarm listening port is used. Default: '0.0.0.0:2377' + default_addr_pool (list of strings): Default Address Pool specifies + default subnet pools for global scope networks. Each pool + should be specified as a CIDR block, like '10.0.0.0/16'. + Default: [] + subnet_size (int): SubnetSize specifies the subnet size of the + networks created from the default subnet pool. Default: 24 force_new_cluster (bool): Force creating a new Swarm, even if already part of one. Default: False swarm_spec (dict): Configuration settings of the new Swarm. Use @@ -122,6 +129,8 @@ def init_swarm(self, advertise_addr=None, listen_addr='0.0.0.0:2377', data = { 'AdvertiseAddr': advertise_addr, 'ListenAddr': listen_addr, + 'DefaultAddrPool': default_addr_pool, + 'SubnetSize': subnet_size, 'ForceNewCluster': force_new_cluster, 'Spec': swarm_spec, } diff --git a/docker/models/swarm.py b/docker/models/swarm.py index 7396e730d7..cdefec6ac8 100644 --- a/docker/models/swarm.py +++ b/docker/models/swarm.py @@ -34,6 +34,7 @@ def get_unlock_key(self): get_unlock_key.__doc__ = APIClient.get_unlock_key.__doc__ def init(self, advertise_addr=None, listen_addr='0.0.0.0:2377', + default_addr_pool=[], subnet_size=24, force_new_cluster=False, **kwargs): """ Initialize a new swarm on this Engine. @@ -54,6 +55,12 @@ def init(self, advertise_addr=None, listen_addr='0.0.0.0:2377', or an interface followed by a port number, like ``eth0:4567``. If the port number is omitted, the default swarm listening port is used. Default: ``0.0.0.0:2377`` + default_addr_pool (list of str): Default Address Pool specifies + default subnet pools for global scope networks. Each pool + should be specified as a CIDR block, like '10.0.0.0/16'. + Default: [] + subnet_size (int): SubnetSize specifies the subnet size of the + networks created from the default subnet pool. Default: 24 force_new_cluster (bool): Force creating a new Swarm, even if already part of one. Default: False task_history_retention_limit (int): Maximum number of tasks @@ -99,6 +106,7 @@ def init(self, advertise_addr=None, listen_addr='0.0.0.0:2377', >>> client.swarm.init( advertise_addr='eth0', listen_addr='0.0.0.0:5000', + default_addr_pool=['10.20.0.0/16], subnet_size=24, force_new_cluster=False, snapshot_interval=5000, log_entries_for_slow_followers=1200 ) @@ -107,6 +115,8 @@ def init(self, advertise_addr=None, listen_addr='0.0.0.0:2377', init_kwargs = { 'advertise_addr': advertise_addr, 'listen_addr': listen_addr, + 'default_addr_pool': default_addr_pool, + 'subnet_size': subnet_size, 'force_new_cluster': force_new_cluster } init_kwargs['swarm_spec'] = self.client.api.create_swarm_spec(**kwargs) diff --git a/tests/integration/api_swarm_test.py b/tests/integration/api_swarm_test.py index b58dabc639..5ef651d2ad 100644 --- a/tests/integration/api_swarm_test.py +++ b/tests/integration/api_swarm_test.py @@ -35,6 +35,29 @@ def test_init_swarm_force_new_cluster(self): version_2 = self.client.inspect_swarm()['Version']['Index'] assert version_2 != version_1 + @requires_api_version('1.39') + def test_init_swarm_custom_addr_pool(self): + assert self.init_swarm() + results_1 = self.client.inspect_swarm() + assert results_1['DefaultAddrPool'] is None + assert results_1['SubnetSize'] == 24 + + assert self.init_swarm(default_addr_pool=['2.0.0.0/16'], + force_new_cluster=True) + results_2 = self.client.inspect_swarm() + assert set(results_2['DefaultAddrPool']) == ( + {'2.0.0.0/16'} + ) + assert results_2['SubnetSize'] == 24 + + assert self.init_swarm(default_addr_pool=['2.0.0.0/16', '3.0.0.0/16'], + subnet_size=28, force_new_cluster=True) + results_3 = self.client.inspect_swarm() + assert set(results_3['DefaultAddrPool']) == ( + {'2.0.0.0/16', '3.0.0.0/16'} + ) + assert results_3['SubnetSize'] == 28 + @requires_api_version('1.24') def test_init_already_in_cluster(self): assert self.init_swarm() From b7d93f62d98b3457a229d6c51a2573b309ed035f Mon Sep 17 00:00:00 2001 From: Barry Shapira Date: Fri, 14 Dec 2018 07:30:55 +0000 Subject: [PATCH 2/7] Check API version before setting swarm addr pool. Also corrected a documentation error: the default API version from constants is currently 1.35, not 1.30 as was sometimes listed. Signed-off-by: Barry Shapira --- docker/api/client.py | 2 +- docker/api/swarm.py | 27 ++++++++++++++++++++++++--- docker/client.py | 6 +++--- docker/constants.py | 3 +++ docker/models/swarm.py | 6 +++--- tests/integration/api_swarm_test.py | 23 ++++++++++++++--------- 6 files changed, 48 insertions(+), 19 deletions(-) diff --git a/docker/api/client.py b/docker/api/client.py index 265dfdcef5..e1d0ccc1d3 100644 --- a/docker/api/client.py +++ b/docker/api/client.py @@ -81,7 +81,7 @@ class APIClient( base_url (str): URL to the Docker server. For example, ``unix:///var/run/docker.sock`` or ``tcp://127.0.0.1:1234``. version (str): The version of the API to use. Set to ``auto`` to - automatically detect the server's version. Default: ``1.30`` + automatically detect the server's version. Default: ``1.35`` timeout (int): Default timeout for API calls, in seconds. tls (bool or :py:class:`~docker.tls.TLSConfig`): Enable TLS. Pass ``True`` to enable it with default options, or pass a diff --git a/docker/api/swarm.py b/docker/api/swarm.py index bec3efdf23..4a39782aaa 100644 --- a/docker/api/swarm.py +++ b/docker/api/swarm.py @@ -1,5 +1,6 @@ import logging from six.moves import http_client +from ..constants import DEFAULT_SWARM_ADDR_POOL, DEFAULT_SWARM_SUBNET_SIZE from .. import errors from .. import types from .. import utils @@ -82,7 +83,7 @@ def get_unlock_key(self): @utils.minimum_version('1.24') def init_swarm(self, advertise_addr=None, listen_addr='0.0.0.0:2377', - default_addr_pool=[], subnet_size=24, + default_addr_pool=None, subnet_size=None, force_new_cluster=False, swarm_spec=None): """ Initialize a new Swarm using the current connected engine as the first @@ -106,9 +107,9 @@ def init_swarm(self, advertise_addr=None, listen_addr='0.0.0.0:2377', default_addr_pool (list of strings): Default Address Pool specifies default subnet pools for global scope networks. Each pool should be specified as a CIDR block, like '10.0.0.0/16'. - Default: [] + Default: None subnet_size (int): SubnetSize specifies the subnet size of the - networks created from the default subnet pool. Default: 24 + networks created from the default subnet pool. Default: None force_new_cluster (bool): Force creating a new Swarm, even if already part of one. Default: False swarm_spec (dict): Configuration settings of the new Swarm. Use @@ -124,8 +125,28 @@ def init_swarm(self, advertise_addr=None, listen_addr='0.0.0.0:2377', """ url = self._url('/swarm/init') + if swarm_spec is not None and not isinstance(swarm_spec, dict): raise TypeError('swarm_spec must be a dictionary') + + if default_addr_pool is not None: + if utils.version_lt(self._version, '1.39'): + raise errors.InvalidVersion( + 'Address pool is only available for API version >= 1.39' + ) + # subnet_size becomes 0 if not set with default_addr_pool + if subnet_size is None: + subnet_size = DEFAULT_SWARM_SUBNET_SIZE + + if subnet_size is not None: + if utils.version_lt(self._version, '1.39'): + raise errors.InvalidVersion( + 'Subnet size is only available for API version >= 1.39' + ) + # subnet_size is ignored if set without default_addr_pool + if default_addr_pool is None: + default_addr_pool = DEFAULT_SWARM_ADDR_POOL + data = { 'AdvertiseAddr': advertise_addr, 'ListenAddr': listen_addr, diff --git a/docker/client.py b/docker/client.py index 8d4a52b2ef..b9ae063b7c 100644 --- a/docker/client.py +++ b/docker/client.py @@ -26,7 +26,7 @@ class DockerClient(object): base_url (str): URL to the Docker server. For example, ``unix:///var/run/docker.sock`` or ``tcp://127.0.0.1:1234``. version (str): The version of the API to use. Set to ``auto`` to - automatically detect the server's version. Default: ``1.30`` + automatically detect the server's version. Default: ``1.35`` timeout (int): Default timeout for API calls, in seconds. tls (bool or :py:class:`~docker.tls.TLSConfig`): Enable TLS. Pass ``True`` to enable it with default options, or pass a @@ -51,7 +51,7 @@ def from_env(cls, **kwargs): The URL to the Docker host. - .. envvar:: DOCKER_TLS_VERIFY + .. envvar:: DOCKE R_TLS_VERIFY Verify the host against a CA certificate. @@ -62,7 +62,7 @@ def from_env(cls, **kwargs): Args: version (str): The version of the API to use. Set to ``auto`` to - automatically detect the server's version. Default: ``1.30`` + automatically detect the server's version. Default: ``1.35`` timeout (int): Default timeout for API calls, in seconds. ssl_version (int): A valid `SSL version`_. assert_hostname (bool): Verify the hostname of the server. diff --git a/docker/constants.py b/docker/constants.py index 1ab11ec051..5fc92056cb 100644 --- a/docker/constants.py +++ b/docker/constants.py @@ -19,3 +19,6 @@ DEFAULT_USER_AGENT = "docker-sdk-python/{0}".format(version) DEFAULT_NUM_POOLS = 25 DEFAULT_DATA_CHUNK_SIZE = 1024 * 2048 + +DEFAULT_SWARM_ADDR_POOL = ['10.0.0.0/8'] +DEFAULT_SWARM_SUBNET_SIZE = 24 diff --git a/docker/models/swarm.py b/docker/models/swarm.py index cdefec6ac8..637fedbad5 100644 --- a/docker/models/swarm.py +++ b/docker/models/swarm.py @@ -34,7 +34,7 @@ def get_unlock_key(self): get_unlock_key.__doc__ = APIClient.get_unlock_key.__doc__ def init(self, advertise_addr=None, listen_addr='0.0.0.0:2377', - default_addr_pool=[], subnet_size=24, + default_addr_pool=None, subnet_size=None, force_new_cluster=False, **kwargs): """ Initialize a new swarm on this Engine. @@ -58,9 +58,9 @@ def init(self, advertise_addr=None, listen_addr='0.0.0.0:2377', default_addr_pool (list of str): Default Address Pool specifies default subnet pools for global scope networks. Each pool should be specified as a CIDR block, like '10.0.0.0/16'. - Default: [] + Default: None subnet_size (int): SubnetSize specifies the subnet size of the - networks created from the default subnet pool. Default: 24 + networks created from the default subnet pool. Default: None force_new_cluster (bool): Force creating a new Swarm, even if already part of one. Default: False task_history_retention_limit (int): Maximum number of tasks diff --git a/tests/integration/api_swarm_test.py b/tests/integration/api_swarm_test.py index 5ef651d2ad..41fae578be 100644 --- a/tests/integration/api_swarm_test.py +++ b/tests/integration/api_swarm_test.py @@ -37,26 +37,31 @@ def test_init_swarm_force_new_cluster(self): @requires_api_version('1.39') def test_init_swarm_custom_addr_pool(self): + # test defaults assert self.init_swarm() results_1 = self.client.inspect_swarm() - assert results_1['DefaultAddrPool'] is None + assert set(results_1['DefaultAddrPool']) == {'10.0.0.0/8'} assert results_1['SubnetSize'] == 24 - + # test addr pool alone assert self.init_swarm(default_addr_pool=['2.0.0.0/16'], force_new_cluster=True) results_2 = self.client.inspect_swarm() - assert set(results_2['DefaultAddrPool']) == ( - {'2.0.0.0/16'} - ) + assert set(results_2['DefaultAddrPool']) == {'2.0.0.0/16'} assert results_2['SubnetSize'] == 24 - + # test subnet size alone + assert self.init_swarm(subnet_size=26, + force_new_cluster=True) + results_3 = self.client.inspect_swarm() + assert set(results_3['DefaultAddrPool']) == {'10.0.0.0/8'} + assert results_3['SubnetSize'] == 26 + # test both arguments together assert self.init_swarm(default_addr_pool=['2.0.0.0/16', '3.0.0.0/16'], subnet_size=28, force_new_cluster=True) - results_3 = self.client.inspect_swarm() - assert set(results_3['DefaultAddrPool']) == ( + results_4 = self.client.inspect_swarm() + assert set(results_4['DefaultAddrPool']) == ( {'2.0.0.0/16', '3.0.0.0/16'} ) - assert results_3['SubnetSize'] == 28 + assert results_4['SubnetSize'] == 28 @requires_api_version('1.24') def test_init_already_in_cluster(self): From 6e21b83aaa6861a026204bce8279d98fea92ecc1 Mon Sep 17 00:00:00 2001 From: Barry Shapira Date: Fri, 14 Dec 2018 08:30:48 -0800 Subject: [PATCH 3/7] Removed accidental whitespace. Signed-off-by: Barry Shapira --- docker/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/client.py b/docker/client.py index b9ae063b7c..99ae1962c4 100644 --- a/docker/client.py +++ b/docker/client.py @@ -51,7 +51,7 @@ def from_env(cls, **kwargs): The URL to the Docker host. - .. envvar:: DOCKE R_TLS_VERIFY + .. envvar:: DOCKER_TLS_VERIFY Verify the host against a CA certificate. From 422d10107f03e8fb73d3fed0d67af6c954335b06 Mon Sep 17 00:00:00 2001 From: Barry Shapira Date: Thu, 3 Jan 2019 17:31:06 -0800 Subject: [PATCH 4/7] Split monolithic integration tests into individual tests. The integration tests require restarting the swarm once for each test. I had done so manually with self.init_swarm(force_new_cluster=True) but that wasn't resetting the swarm state correctly. The usual test teardown procedure cleans up correctly. Signed-off-by: Barry Shapira --- tests/integration/api_swarm_test.py | 49 +++++++++++++++-------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/tests/integration/api_swarm_test.py b/tests/integration/api_swarm_test.py index 41fae578be..37f5fa7959 100644 --- a/tests/integration/api_swarm_test.py +++ b/tests/integration/api_swarm_test.py @@ -36,32 +36,33 @@ def test_init_swarm_force_new_cluster(self): assert version_2 != version_1 @requires_api_version('1.39') - def test_init_swarm_custom_addr_pool(self): - # test defaults + def test_init_swarm_custom_addr_pool_defaults(self): assert self.init_swarm() - results_1 = self.client.inspect_swarm() - assert set(results_1['DefaultAddrPool']) == {'10.0.0.0/8'} - assert results_1['SubnetSize'] == 24 - # test addr pool alone - assert self.init_swarm(default_addr_pool=['2.0.0.0/16'], - force_new_cluster=True) - results_2 = self.client.inspect_swarm() - assert set(results_2['DefaultAddrPool']) == {'2.0.0.0/16'} - assert results_2['SubnetSize'] == 24 - # test subnet size alone - assert self.init_swarm(subnet_size=26, - force_new_cluster=True) - results_3 = self.client.inspect_swarm() - assert set(results_3['DefaultAddrPool']) == {'10.0.0.0/8'} - assert results_3['SubnetSize'] == 26 - # test both arguments together + results = self.client.inspect_swarm() + assert set(results['DefaultAddrPool']) == {'10.0.0.0/8'} + assert results['SubnetSize'] == 24 + + @requires_api_version('1.39') + def test_init_swarm_custom_addr_pool_only_pool(self): + assert self.init_swarm(default_addr_pool=['2.0.0.0/16']) + results = self.client.inspect_swarm() + assert set(results['DefaultAddrPool']) == {'2.0.0.0/16'} + assert results['SubnetSize'] == 24 + + @requires_api_version('1.39') + def test_init_swarm_custom_addr_pool_only_subnet_size(self): + assert self.init_swarm(subnet_size=26) + results = self.client.inspect_swarm() + assert set(results['DefaultAddrPool']) == {'10.0.0.0/8'} + assert results['SubnetSize'] == 26 + + @requires_api_version('1.39') + def test_init_swarm_custom_addr_pool_both_args(self): assert self.init_swarm(default_addr_pool=['2.0.0.0/16', '3.0.0.0/16'], - subnet_size=28, force_new_cluster=True) - results_4 = self.client.inspect_swarm() - assert set(results_4['DefaultAddrPool']) == ( - {'2.0.0.0/16', '3.0.0.0/16'} - ) - assert results_4['SubnetSize'] == 28 + subnet_size=28) + results = self.client.inspect_swarm() + assert set(results['DefaultAddrPool']) == {'2.0.0.0/16', '3.0.0.0/16'} + assert results['SubnetSize'] == 28 @requires_api_version('1.24') def test_init_already_in_cluster(self): From 59d0a8bd0a8f9f1d4a14fc2822a9df29184be95a Mon Sep 17 00:00:00 2001 From: bluikko <14869000+bluikko@users.noreply.github.com> Date: Fri, 22 Feb 2019 16:41:02 +0700 Subject: [PATCH 5/7] fixes requested in PR2201 review Following https://github.com/docker/docker-py/pull/2201#pullrequestreview-192571911 --- docker/api/swarm.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/api/swarm.py b/docker/api/swarm.py index 4a39782aaa..4dc3d3590e 100644 --- a/docker/api/swarm.py +++ b/docker/api/swarm.py @@ -83,8 +83,8 @@ def get_unlock_key(self): @utils.minimum_version('1.24') def init_swarm(self, advertise_addr=None, listen_addr='0.0.0.0:2377', - default_addr_pool=None, subnet_size=None, - force_new_cluster=False, swarm_spec=None): + force_new_cluster=False, swarm_spec=None, + default_addr_pool=None, subnet_size=None): """ Initialize a new Swarm using the current connected engine as the first node. @@ -106,7 +106,7 @@ def init_swarm(self, advertise_addr=None, listen_addr='0.0.0.0:2377', is used. Default: '0.0.0.0:2377' default_addr_pool (list of strings): Default Address Pool specifies default subnet pools for global scope networks. Each pool - should be specified as a CIDR block, like '10.0.0.0/16'. + should be specified as a CIDR block, like '10.0.0.0/8'. Default: None subnet_size (int): SubnetSize specifies the subnet size of the networks created from the default subnet pool. Default: None From a210c31a369b5bd7cf9ba57d1c273a39dd09ac97 Mon Sep 17 00:00:00 2001 From: bluikko <14869000+bluikko@users.noreply.github.com> Date: Fri, 22 Feb 2019 16:48:37 +0700 Subject: [PATCH 6/7] fixes requested in PR2201 review Following https://github.com/docker/docker-py/pull/2201 --- docker/models/swarm.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docker/models/swarm.py b/docker/models/swarm.py index 637fedbad5..ff3d4026f1 100644 --- a/docker/models/swarm.py +++ b/docker/models/swarm.py @@ -34,8 +34,8 @@ def get_unlock_key(self): get_unlock_key.__doc__ = APIClient.get_unlock_key.__doc__ def init(self, advertise_addr=None, listen_addr='0.0.0.0:2377', - default_addr_pool=None, subnet_size=None, - force_new_cluster=False, **kwargs): + force_new_cluster=False, default_addr_pool=None, + subnet_size=None, **kwargs): """ Initialize a new swarm on this Engine. @@ -55,14 +55,14 @@ def init(self, advertise_addr=None, listen_addr='0.0.0.0:2377', or an interface followed by a port number, like ``eth0:4567``. If the port number is omitted, the default swarm listening port is used. Default: ``0.0.0.0:2377`` + force_new_cluster (bool): Force creating a new Swarm, even if + already part of one. Default: False default_addr_pool (list of str): Default Address Pool specifies default subnet pools for global scope networks. Each pool - should be specified as a CIDR block, like '10.0.0.0/16'. + should be specified as a CIDR block, like '10.0.0.0/8'. Default: None subnet_size (int): SubnetSize specifies the subnet size of the networks created from the default subnet pool. Default: None - force_new_cluster (bool): Force creating a new Swarm, even if - already part of one. Default: False task_history_retention_limit (int): Maximum number of tasks history stored. snapshot_interval (int): Number of logs entries between snapshot. @@ -106,8 +106,8 @@ def init(self, advertise_addr=None, listen_addr='0.0.0.0:2377', >>> client.swarm.init( advertise_addr='eth0', listen_addr='0.0.0.0:5000', - default_addr_pool=['10.20.0.0/16], subnet_size=24, - force_new_cluster=False, snapshot_interval=5000, + force_new_cluster=False, default_addr_pool=['10.20.0.0/16], + subnet_size=24, snapshot_interval=5000, log_entries_for_slow_followers=1200 ) @@ -115,9 +115,9 @@ def init(self, advertise_addr=None, listen_addr='0.0.0.0:2377', init_kwargs = { 'advertise_addr': advertise_addr, 'listen_addr': listen_addr, + 'force_new_cluster': force_new_cluster, 'default_addr_pool': default_addr_pool, - 'subnet_size': subnet_size, - 'force_new_cluster': force_new_cluster + 'subnet_size': subnet_size } init_kwargs['swarm_spec'] = self.client.api.create_swarm_spec(**kwargs) self.client.api.init_swarm(**init_kwargs) From dc28041e559fe0e6f4b8dd49775b6ffa07482623 Mon Sep 17 00:00:00 2001 From: bluikko <14869000+bluikko@users.noreply.github.com> Date: Sat, 23 Feb 2019 12:57:24 +0700 Subject: [PATCH 7/7] remove erraneous newline --- docker/api/swarm.py | 1 - 1 file changed, 1 deletion(-) diff --git a/docker/api/swarm.py b/docker/api/swarm.py index 4dc3d3590e..5d88e660db 100644 --- a/docker/api/swarm.py +++ b/docker/api/swarm.py @@ -125,7 +125,6 @@ def init_swarm(self, advertise_addr=None, listen_addr='0.0.0.0:2377', """ url = self._url('/swarm/init') - if swarm_spec is not None and not isinstance(swarm_spec, dict): raise TypeError('swarm_spec must be a dictionary')