@@ -29,6 +29,7 @@ class TaskTemplate(dict):
2929 force_update (int): A counter that triggers an update even if no
3030 relevant parameters have been changed.
3131 """
32+
3233 def __init__ (self , container_spec , resources = None , restart_policy = None ,
3334 placement = None , log_driver = None , networks = None ,
3435 force_update = None ):
@@ -115,6 +116,7 @@ class ContainerSpec(dict):
115116 cap_drop (:py:class:`list`): A list of kernel capabilities to drop from
116117 the default set for the container.
117118 """
119+
118120 def __init__ (self , image , command = None , args = None , hostname = None , env = None ,
119121 workdir = None , user = None , labels = None , mounts = None ,
120122 stop_grace_period = None , secrets = None , tty = None , groups = None ,
@@ -231,6 +233,7 @@ class Mount(dict):
231233 tmpfs_size (int or string): The size for the tmpfs mount in bytes.
232234 tmpfs_mode (int): The permission mode for the tmpfs mount.
233235 """
236+
234237 def __init__ (self , target , source , type = 'volume' , read_only = False ,
235238 consistency = None , propagation = None , no_copy = False ,
236239 labels = None , driver_config = None , tmpfs_size = None ,
@@ -331,6 +334,7 @@ class Resources(dict):
331334 ``{ resource_name: resource_value }``. Alternatively, a list of
332335 of resource specifications as defined by the Engine API.
333336 """
337+
334338 def __init__ (self , cpu_limit = None , mem_limit = None , cpu_reservation = None ,
335339 mem_reservation = None , generic_resources = None ):
336340 limits = {}
@@ -401,6 +405,7 @@ class UpdateConfig(dict):
401405 order (string): Specifies the order of operations when rolling out an
402406 updated task. Either ``start-first`` or ``stop-first`` are accepted.
403407 """
408+
404409 def __init__ (self , parallelism = 0 , delay = None , failure_action = 'continue' ,
405410 monitor = None , max_failure_ratio = None , order = None ):
406411 self ['Parallelism' ] = parallelism
@@ -512,6 +517,7 @@ class DriverConfig(dict):
512517 name (string): Name of the driver to use.
513518 options (dict): Driver-specific options. Default: ``None``.
514519 """
520+
515521 def __init__ (self , name , options = None ):
516522 self ['Name' ] = name
517523 if options :
@@ -533,6 +539,7 @@ class EndpointSpec(dict):
533539 is ``(target_port [, protocol [, publish_mode]])``.
534540 Ports can only be provided if the ``vip`` resolution mode is used.
535541 """
542+
536543 def __init__ (self , mode = None , ports = None ):
537544 if ports :
538545 self ['Ports' ] = convert_service_ports (ports )
@@ -575,37 +582,70 @@ def convert_service_ports(ports):
575582
576583class ServiceMode (dict ):
577584 """
578- Indicate whether a service should be deployed as a replicated or global
579- service, and associated parameters
585+ Indicate whether a service or a job should be deployed as a replicated
586+ or global service, and associated parameters
580587
581588 Args:
582- mode (string): Can be either ``replicated`` or ``global``
589+ mode (string): Can be either ``replicated``, ``global``,
590+ ``replicated-job`` or ``global-job``
583591 replicas (int): Number of replicas. For replicated services only.
592+ concurrency (int): Number of concurrent jobs. For replicated job
593+ services only.
584594 """
585- def __init__ ( self , mode , replicas = None ):
586- if mode not in ( 'replicated' , 'global' ):
587- raise errors . InvalidArgument (
588- 'mode must be either "replicated" or " global"'
589- )
590- if mode != 'replicated' and replicas is not None :
595+
596+ def __init__ ( self , mode , replicas = None , concurrency = None ):
597+ replicated_modes = ( 'replicated' , 'replicated-job' )
598+ supported_modes = replicated_modes + ( 'global' , ' global-job' )
599+
600+ if mode not in supported_modes :
591601 raise errors .InvalidArgument (
592- 'replicas can only be used for replicated mode'
602+ 'mode must be either "replicated", "global", "replicated-job"'
603+ ' or "global-job"'
593604 )
594- self [mode ] = {}
605+
606+ if mode not in replicated_modes :
607+ if replicas is not None :
608+ raise errors .InvalidArgument (
609+ 'replicas can only be used for "replicated" or'
610+ ' "replicated-job" mode'
611+ )
612+
613+ if concurrency is not None :
614+ raise errors .InvalidArgument (
615+ 'concurrency can only be used for "replicated-job" mode'
616+ )
617+
618+ service_mode = self ._convert_mode (mode )
619+ self .mode = service_mode
620+ self [service_mode ] = {}
621+
595622 if replicas is not None :
596- self [mode ]['Replicas' ] = replicas
623+ if mode == 'replicated' :
624+ self [service_mode ]['Replicas' ] = replicas
597625
598- @property
599- def mode (self ):
600- if 'global' in self :
601- return 'global'
602- return 'replicated'
626+ if mode == 'replicated-job' :
627+ self [service_mode ]['MaxConcurrent' ] = concurrency or 1
628+ self [service_mode ]['TotalCompletions' ] = replicas
629+
630+ @staticmethod
631+ def _convert_mode (original_mode ):
632+ if original_mode == 'global-job' :
633+ return 'GlobalJob'
634+
635+ if original_mode == 'replicated-job' :
636+ return 'ReplicatedJob'
637+
638+ return original_mode
603639
604640 @property
605641 def replicas (self ):
606- if self .mode != 'replicated' :
607- return None
608- return self ['replicated' ].get ('Replicas' )
642+ if 'replicated' in self :
643+ return self ['replicated' ].get ('Replicas' )
644+
645+ if 'ReplicatedJob' in self :
646+ return self ['ReplicatedJob' ].get ('TotalCompletions' )
647+
648+ return None
609649
610650
611651class SecretReference (dict ):
@@ -679,6 +719,7 @@ class Placement(dict):
679719 platforms (:py:class:`list` of tuple): A list of platforms
680720 expressed as ``(arch, os)`` tuples
681721 """
722+
682723 def __init__ (self , constraints = None , preferences = None , platforms = None ,
683724 maxreplicas = None ):
684725 if constraints is not None :
@@ -711,6 +752,7 @@ class PlacementPreference(dict):
711752 the scheduler will try to spread tasks evenly over groups of
712753 nodes identified by this label.
713754 """
755+
714756 def __init__ (self , strategy , descriptor ):
715757 if strategy != 'spread' :
716758 raise errors .InvalidArgument (
@@ -732,6 +774,7 @@ class DNSConfig(dict):
732774 options (:py:class:`list`): A list of internal resolver variables
733775 to be modified (e.g., ``debug``, ``ndots:3``, etc.).
734776 """
777+
735778 def __init__ (self , nameservers = None , search = None , options = None ):
736779 self ['Nameservers' ] = nameservers
737780 self ['Search' ] = search
@@ -762,6 +805,7 @@ class Privileges(dict):
762805 selinux_type (string): SELinux type label
763806 selinux_level (string): SELinux level label
764807 """
808+
765809 def __init__ (self , credentialspec_file = None , credentialspec_registry = None ,
766810 selinux_disable = None , selinux_user = None , selinux_role = None ,
767811 selinux_type = None , selinux_level = None ):
@@ -804,6 +848,7 @@ class NetworkAttachmentConfig(dict):
804848 options (:py:class:`dict`): Driver attachment options for the
805849 network target.
806850 """
851+
807852 def __init__ (self , target , aliases = None , options = None ):
808853 self ['Target' ] = target
809854 self ['Aliases' ] = aliases
0 commit comments