@@ -19,6 +19,10 @@ def raise_version_error(param, min_version):
1919 if 'Monitor' in update_config :
2020 raise_version_error ('UpdateConfig.monitor' , '1.25' )
2121
22+ if utils .version_lt (version , '1.29' ):
23+ if 'Order' in update_config :
24+ raise_version_error ('UpdateConfig.order' , '1.29' )
25+
2226 if task_template is not None :
2327 if 'ForceUpdate' in task_template and utils .version_lt (
2428 version , '1.25' ):
@@ -62,6 +66,21 @@ def raise_version_error(param, min_version):
6266 raise_version_error ('ContainerSpec.privileges' , '1.30' )
6367
6468
69+ def _merge_task_template (current , override ):
70+ merged = current .copy ()
71+ if override is not None :
72+ for ts_key , ts_value in override .items ():
73+ if ts_key == 'ContainerSpec' :
74+ if 'ContainerSpec' not in merged :
75+ merged ['ContainerSpec' ] = {}
76+ for cs_key , cs_value in override ['ContainerSpec' ].items ():
77+ if cs_value is not None :
78+ merged ['ContainerSpec' ][cs_key ] = cs_value
79+ elif ts_value is not None :
80+ merged [ts_key ] = ts_value
81+ return merged
82+
83+
6584class ServiceApiMixin (object ):
6685 @utils .minimum_version ('1.24' )
6786 def create_service (
@@ -306,7 +325,7 @@ def tasks(self, filters=None):
306325 def update_service (self , service , version , task_template = None , name = None ,
307326 labels = None , mode = None , update_config = None ,
308327 networks = None , endpoint_config = None ,
309- endpoint_spec = None ):
328+ endpoint_spec = None , fetch_current_spec = False ):
310329 """
311330 Update a service.
312331
@@ -328,6 +347,8 @@ def update_service(self, service, version, task_template=None, name=None,
328347 the service to. Default: ``None``.
329348 endpoint_spec (EndpointSpec): Properties that can be configured to
330349 access and load balance a service. Default: ``None``.
350+ fetch_current_spec (boolean): Use the undefined settings from the
351+ current specification of the service. Default: ``False``
331352
332353 Returns:
333354 ``True`` if successful.
@@ -345,32 +366,64 @@ def update_service(self, service, version, task_template=None, name=None,
345366
346367 _check_api_features (self ._version , task_template , update_config )
347368
369+ if fetch_current_spec :
370+ inspect_defaults = True
371+ if utils .version_lt (self ._version , '1.29' ):
372+ inspect_defaults = None
373+ current = self .inspect_service (
374+ service , insert_defaults = inspect_defaults
375+ )['Spec' ]
376+
377+ else :
378+ current = {}
379+
348380 url = self ._url ('/services/{0}/update' , service )
349381 data = {}
350382 headers = {}
351- if name is not None :
352- data ['Name' ] = name
353- if labels is not None :
354- data ['Labels' ] = labels
383+
384+ data ['Name' ] = current .get ('Name' ) if name is None else name
385+
386+ data ['Labels' ] = current .get ('Labels' ) if labels is None else labels
387+
355388 if mode is not None :
356389 if not isinstance (mode , dict ):
357390 mode = ServiceMode (mode )
358391 data ['Mode' ] = mode
359- if task_template is not None :
360- image = task_template .get ('ContainerSpec' , {}).get ('Image' , None )
361- if image is not None :
362- registry , repo_name = auth .resolve_repository_name (image )
363- auth_header = auth .get_config_header (self , registry )
364- if auth_header :
365- headers ['X-Registry-Auth' ] = auth_header
366- data ['TaskTemplate' ] = task_template
392+ else :
393+ data ['Mode' ] = current .get ('Mode' )
394+
395+ data ['TaskTemplate' ] = _merge_task_template (
396+ current .get ('TaskTemplate' , {}), task_template
397+ )
398+
399+ container_spec = data ['TaskTemplate' ].get ('ContainerSpec' , {})
400+ image = container_spec .get ('Image' , None )
401+ if image is not None :
402+ registry , repo_name = auth .resolve_repository_name (image )
403+ auth_header = auth .get_config_header (self , registry )
404+ if auth_header :
405+ headers ['X-Registry-Auth' ] = auth_header
406+
367407 if update_config is not None :
368408 data ['UpdateConfig' ] = update_config
409+ else :
410+ data ['UpdateConfig' ] = current .get ('UpdateConfig' )
369411
370412 if networks is not None :
371- data ['Networks' ] = utils .convert_service_networks (networks )
413+ converted_networks = utils .convert_service_networks (networks )
414+ data ['TaskTemplate' ]['Networks' ] = converted_networks
415+ elif data ['TaskTemplate' ].get ('Networks' ) is None :
416+ current_task_template = current .get ('TaskTemplate' , {})
417+ current_networks = current_task_template .get ('Networks' )
418+ if current_networks is None :
419+ current_networks = current .get ('Networks' )
420+ if current_networks is not None :
421+ data ['TaskTemplate' ]['Networks' ] = current_networks
422+
372423 if endpoint_spec is not None :
373424 data ['EndpointSpec' ] = endpoint_spec
425+ else :
426+ data ['EndpointSpec' ] = current .get ('EndpointSpec' )
374427
375428 resp = self ._post_json (
376429 url , data = data , params = {'version' : version }, headers = headers
0 commit comments