summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoffrey F <joffrey@docker.com>2017-11-02 18:46:25 -0700
committerJoffrey F <joffrey@docker.com>2017-11-03 15:33:45 -0700
commit37c135060d71020ed095efdd2206548786bd2563 (patch)
tree797ac66d4c9314a550578c76cf06212d86844b2c
parent1ce93ac6e75f0aaf83aededcc165e1e9fe49c52e (diff)
downloaddocker-py-37c135060d71020ed095efdd2206548786bd2563.tar.gz
Update SwarmSpec to include new parametersupdate_swarm_spec
Signed-off-by: Joffrey F <joffrey@docker.com>
-rw-r--r--docker/api/swarm.py29
-rw-r--r--docker/models/swarm.py17
-rw-r--r--docker/types/swarm.py92
-rw-r--r--docs/api.rst2
-rw-r--r--tests/integration/api_swarm_test.py38
5 files changed, 160 insertions, 18 deletions
diff --git a/docker/api/swarm.py b/docker/api/swarm.py
index 4fa0c4a..576fd79 100644
--- a/docker/api/swarm.py
+++ b/docker/api/swarm.py
@@ -9,8 +9,8 @@ class SwarmApiMixin(object):
def create_swarm_spec(self, *args, **kwargs):
"""
- Create a ``docker.types.SwarmSpec`` instance that can be used as the
- ``swarm_spec`` argument in
+ Create a :py:class:`docker.types.SwarmSpec` instance that can be used
+ as the ``swarm_spec`` argument in
:py:meth:`~docker.api.swarm.SwarmApiMixin.init_swarm`.
Args:
@@ -29,13 +29,25 @@ class SwarmApiMixin(object):
dispatcher_heartbeat_period (int): The delay for an agent to send
a heartbeat to the dispatcher.
node_cert_expiry (int): Automatic expiry for nodes certificates.
- external_ca (dict): Configuration for forwarding signing requests
- to an external certificate authority. Use
- ``docker.types.SwarmExternalCA``.
+ external_cas (:py:class:`list`): Configuration for forwarding
+ signing requests to an external certificate authority. Use
+ a list of :py:class:`docker.types.SwarmExternalCA`.
name (string): Swarm's name
+ labels (dict): User-defined key/value metadata.
+ signing_ca_cert (str): The desired signing CA certificate for all
+ swarm node TLS leaf certificates, in PEM format.
+ signing_ca_key (str): The desired signing CA key for all swarm
+ node TLS leaf certificates, in PEM format.
+ ca_force_rotate (int): An integer whose purpose is to force swarm
+ to generate a new signing CA certificate and key, if none have
+ been specified.
+ autolock_managers (boolean): If set, generate a key and use it to
+ lock data stored on the managers.
+ log_driver (DriverConfig): The default log driver to use for tasks
+ created in the orchestrator.
Returns:
- ``docker.types.SwarmSpec`` instance.
+ :py:class:`docker.types.SwarmSpec`
Raises:
:py:class:`docker.errors.APIError`
@@ -51,7 +63,10 @@ class SwarmApiMixin(object):
force_new_cluster=False, swarm_spec=spec
)
"""
- return types.SwarmSpec(*args, **kwargs)
+ ext_ca = kwargs.pop('external_ca', None)
+ if ext_ca:
+ kwargs['external_cas'] = [ext_ca]
+ return types.SwarmSpec(self._version, *args, **kwargs)
@utils.minimum_version('1.24')
def init_swarm(self, advertise_addr=None, listen_addr='0.0.0.0:2377',
diff --git a/docker/models/swarm.py b/docker/models/swarm.py
index df3afd3..5a253c5 100644
--- a/docker/models/swarm.py
+++ b/docker/models/swarm.py
@@ -1,6 +1,5 @@
from docker.api import APIClient
from docker.errors import APIError
-from docker.types import SwarmSpec
from .resource import Model
@@ -72,6 +71,18 @@ class Swarm(Model):
to an external certificate authority. Use
``docker.types.SwarmExternalCA``.
name (string): Swarm's name
+ labels (dict): User-defined key/value metadata.
+ signing_ca_cert (str): The desired signing CA certificate for all
+ swarm node TLS leaf certificates, in PEM format.
+ signing_ca_key (str): The desired signing CA key for all swarm
+ node TLS leaf certificates, in PEM format.
+ ca_force_rotate (int): An integer whose purpose is to force swarm
+ to generate a new signing CA certificate and key, if none have
+ been specified.
+ autolock_managers (boolean): If set, generate a key and use it to
+ lock data stored on the managers.
+ log_driver (DriverConfig): The default log driver to use for tasks
+ created in the orchestrator.
Returns:
``True`` if the request went through.
@@ -94,7 +105,7 @@ class Swarm(Model):
'listen_addr': listen_addr,
'force_new_cluster': force_new_cluster
}
- init_kwargs['swarm_spec'] = SwarmSpec(**kwargs)
+ init_kwargs['swarm_spec'] = self.client.api.create_swarm_spec(**kwargs)
self.client.api.init_swarm(**init_kwargs)
self.reload()
@@ -143,7 +154,7 @@ class Swarm(Model):
return self.client.api.update_swarm(
version=self.version,
- swarm_spec=SwarmSpec(**kwargs),
+ swarm_spec=self.client.api.create_swarm_spec(**kwargs),
rotate_worker_token=rotate_worker_token,
rotate_manager_token=rotate_manager_token
)
diff --git a/docker/types/swarm.py b/docker/types/swarm.py
index 49beaa1..9687a82 100644
--- a/docker/types/swarm.py
+++ b/docker/types/swarm.py
@@ -1,9 +1,21 @@
+from ..errors import InvalidVersion
+from ..utils import version_lt
+
+
class SwarmSpec(dict):
- def __init__(self, task_history_retention_limit=None,
+ """
+ Describe a Swarm's configuration and options. Use
+ :py:meth:`~docker.api.swarm.SwarmApiMixin.create_swarm_spec`
+ to instantiate.
+ """
+ def __init__(self, version, task_history_retention_limit=None,
snapshot_interval=None, keep_old_snapshots=None,
log_entries_for_slow_followers=None, heartbeat_tick=None,
election_tick=None, dispatcher_heartbeat_period=None,
- node_cert_expiry=None, external_ca=None, name=None):
+ node_cert_expiry=None, external_cas=None, name=None,
+ labels=None, signing_ca_cert=None, signing_ca_key=None,
+ ca_force_rotate=None, autolock_managers=None,
+ log_driver=None):
if task_history_retention_limit is not None:
self['Orchestration'] = {
'TaskHistoryRetentionLimit': task_history_retention_limit
@@ -26,18 +38,82 @@ class SwarmSpec(dict):
'HeartbeatPeriod': dispatcher_heartbeat_period
}
- if node_cert_expiry or external_ca:
- self['CAConfig'] = {
- 'NodeCertExpiry': node_cert_expiry,
- 'ExternalCA': external_ca
- }
+ ca_config = {}
+ if node_cert_expiry is not None:
+ ca_config['NodeCertExpiry'] = node_cert_expiry
+ if external_cas:
+ if version_lt(version, '1.25'):
+ if len(external_cas) > 1:
+ raise InvalidVersion(
+ 'Support for multiple external CAs is not available '
+ 'for API version < 1.25'
+ )
+ ca_config['ExternalCA'] = external_cas[0]
+ else:
+ ca_config['ExternalCAs'] = external_cas
+ if signing_ca_key:
+ if version_lt(version, '1.30'):
+ raise InvalidVersion(
+ 'signing_ca_key is not supported in API version < 1.30'
+ )
+ ca_config['SigningCAKey'] = signing_ca_key
+ if signing_ca_cert:
+ if version_lt(version, '1.30'):
+ raise InvalidVersion(
+ 'signing_ca_cert is not supported in API version < 1.30'
+ )
+ ca_config['SigningCACert'] = signing_ca_cert
+ if ca_force_rotate is not None:
+ if version_lt(version, '1.30'):
+ raise InvalidVersion(
+ 'force_rotate is not supported in API version < 1.30'
+ )
+ ca_config['ForceRotate'] = ca_force_rotate
+ if ca_config:
+ self['CAConfig'] = ca_config
+
+ if autolock_managers is not None:
+ if version_lt(version, '1.25'):
+ raise InvalidVersion(
+ 'autolock_managers is not supported in API version < 1.25'
+ )
+
+ self['EncryptionConfig'] = {'AutoLockManagers': autolock_managers}
+
+ if log_driver is not None:
+ if version_lt(version, '1.25'):
+ raise InvalidVersion(
+ 'log_driver is not supported in API version < 1.25'
+ )
+
+ self['TaskDefaults'] = {'LogDriver': log_driver}
if name is not None:
self['Name'] = name
+ if labels is not None:
+ self['Labels'] = labels
class SwarmExternalCA(dict):
- def __init__(self, url, protocol=None, options=None):
+ """
+ Configuration for forwarding signing requests to an external
+ certificate authority.
+
+ Args:
+ url (string): URL where certificate signing requests should be
+ sent.
+ protocol (string): Protocol for communication with the external CA.
+ options (dict): An object with key/value pairs that are interpreted
+ as protocol-specific options for the external CA driver.
+ ca_cert (string): The root CA certificate (in PEM format) this
+ external CA uses to issue TLS certificates (assumed to be to
+ the current swarm root CA certificate if not provided).
+
+
+
+ """
+ def __init__(self, url, protocol=None, options=None, ca_cert=None):
self['URL'] = url
self['Protocol'] = protocol
self['Options'] = options
+ self['CACert'] = ca_cert
diff --git a/docs/api.rst b/docs/api.rst
index 18993ad..ff466a1 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -147,5 +147,7 @@ Configuration types
.. autoclass:: RestartPolicy
.. autoclass:: SecretReference
.. autoclass:: ServiceMode
+.. autoclass:: SwarmExternalCA
+.. autoclass:: SwarmSpec(*args, **kwargs)
.. autoclass:: TaskTemplate
.. autoclass:: UpdateConfig
diff --git a/tests/integration/api_swarm_test.py b/tests/integration/api_swarm_test.py
index 666c689..34b0879 100644
--- a/tests/integration/api_swarm_test.py
+++ b/tests/integration/api_swarm_test.py
@@ -45,6 +45,44 @@ class SwarmTest(BaseAPIIntegrationTest):
assert swarm_info['Spec']['Raft']['SnapshotInterval'] == 5000
assert swarm_info['Spec']['Raft']['LogEntriesForSlowFollowers'] == 1200
+ @requires_api_version('1.30')
+ def test_init_swarm_with_ca_config(self):
+ spec = self.client.create_swarm_spec(
+ node_cert_expiry=7776000000000000, ca_force_rotate=6000000000000
+ )
+
+ assert self.init_swarm(swarm_spec=spec)
+ swarm_info = self.client.inspect_swarm()
+ assert swarm_info['Spec']['CAConfig']['NodeCertExpiry'] == (
+ spec['CAConfig']['NodeCertExpiry']
+ )
+ assert swarm_info['Spec']['CAConfig']['ForceRotate'] == (
+ spec['CAConfig']['ForceRotate']
+ )
+
+ @requires_api_version('1.25')
+ def test_init_swarm_with_autolock_managers(self):
+ spec = self.client.create_swarm_spec(autolock_managers=True)
+ assert self.init_swarm(swarm_spec=spec)
+ swarm_info = self.client.inspect_swarm()
+
+ assert (
+ swarm_info['Spec']['EncryptionConfig']['AutoLockManagers'] is True
+ )
+
+ @requires_api_version('1.25')
+ @pytest.mark.xfail(
+ reason="This doesn't seem to be taken into account by the engine"
+ )
+ def test_init_swarm_with_log_driver(self):
+ spec = {'TaskDefaults': {'LogDriver': {'Name': 'syslog'}}}
+ assert self.init_swarm(swarm_spec=spec)
+ swarm_info = self.client.inspect_swarm()
+
+ assert swarm_info['Spec']['TaskDefaults']['LogDriver']['Name'] == (
+ 'syslog'
+ )
+
@requires_api_version('1.24')
def test_leave_swarm(self):
assert self.init_swarm()