@@ -29,6 +29,7 @@ class TaskTemplate(dict):
29
29
force_update (int): A counter that triggers an update even if no
30
30
relevant parameters have been changed.
31
31
"""
32
+
32
33
def __init__ (self , container_spec , resources = None , restart_policy = None ,
33
34
placement = None , log_driver = None , networks = None ,
34
35
force_update = None ):
@@ -115,6 +116,7 @@ class ContainerSpec(dict):
115
116
cap_drop (:py:class:`list`): A list of kernel capabilities to drop from
116
117
the default set for the container.
117
118
"""
119
+
118
120
def __init__ (self , image , command = None , args = None , hostname = None , env = None ,
119
121
workdir = None , user = None , labels = None , mounts = None ,
120
122
stop_grace_period = None , secrets = None , tty = None , groups = None ,
@@ -231,6 +233,7 @@ class Mount(dict):
231
233
tmpfs_size (int or string): The size for the tmpfs mount in bytes.
232
234
tmpfs_mode (int): The permission mode for the tmpfs mount.
233
235
"""
236
+
234
237
def __init__ (self , target , source , type = 'volume' , read_only = False ,
235
238
consistency = None , propagation = None , no_copy = False ,
236
239
labels = None , driver_config = None , tmpfs_size = None ,
@@ -331,6 +334,7 @@ class Resources(dict):
331
334
``{ resource_name: resource_value }``. Alternatively, a list of
332
335
of resource specifications as defined by the Engine API.
333
336
"""
337
+
334
338
def __init__ (self , cpu_limit = None , mem_limit = None , cpu_reservation = None ,
335
339
mem_reservation = None , generic_resources = None ):
336
340
limits = {}
@@ -401,6 +405,7 @@ class UpdateConfig(dict):
401
405
order (string): Specifies the order of operations when rolling out an
402
406
updated task. Either ``start-first`` or ``stop-first`` are accepted.
403
407
"""
408
+
404
409
def __init__ (self , parallelism = 0 , delay = None , failure_action = 'continue' ,
405
410
monitor = None , max_failure_ratio = None , order = None ):
406
411
self ['Parallelism' ] = parallelism
@@ -512,6 +517,7 @@ class DriverConfig(dict):
512
517
name (string): Name of the driver to use.
513
518
options (dict): Driver-specific options. Default: ``None``.
514
519
"""
520
+
515
521
def __init__ (self , name , options = None ):
516
522
self ['Name' ] = name
517
523
if options :
@@ -533,6 +539,7 @@ class EndpointSpec(dict):
533
539
is ``(target_port [, protocol [, publish_mode]])``.
534
540
Ports can only be provided if the ``vip`` resolution mode is used.
535
541
"""
542
+
536
543
def __init__ (self , mode = None , ports = None ):
537
544
if ports :
538
545
self ['Ports' ] = convert_service_ports (ports )
@@ -575,37 +582,70 @@ def convert_service_ports(ports):
575
582
576
583
class ServiceMode (dict ):
577
584
"""
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
580
587
581
588
Args:
582
- mode (string): Can be either ``replicated`` or ``global``
589
+ mode (string): Can be either ``replicated``, ``global``,
590
+ ``replicated-job`` or ``global-job``
583
591
replicas (int): Number of replicas. For replicated services only.
592
+ concurrency (int): Number of concurrent jobs. For replicated job
593
+ services only.
584
594
"""
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 :
591
601
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"'
593
604
)
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
+
595
622
if replicas is not None :
596
- self [mode ]['Replicas' ] = replicas
623
+ if mode == 'replicated' :
624
+ self [service_mode ]['Replicas' ] = replicas
597
625
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
603
639
604
640
@property
605
641
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
609
649
610
650
611
651
class SecretReference (dict ):
@@ -679,6 +719,7 @@ class Placement(dict):
679
719
platforms (:py:class:`list` of tuple): A list of platforms
680
720
expressed as ``(arch, os)`` tuples
681
721
"""
722
+
682
723
def __init__ (self , constraints = None , preferences = None , platforms = None ,
683
724
maxreplicas = None ):
684
725
if constraints is not None :
@@ -711,6 +752,7 @@ class PlacementPreference(dict):
711
752
the scheduler will try to spread tasks evenly over groups of
712
753
nodes identified by this label.
713
754
"""
755
+
714
756
def __init__ (self , strategy , descriptor ):
715
757
if strategy != 'spread' :
716
758
raise errors .InvalidArgument (
@@ -732,6 +774,7 @@ class DNSConfig(dict):
732
774
options (:py:class:`list`): A list of internal resolver variables
733
775
to be modified (e.g., ``debug``, ``ndots:3``, etc.).
734
776
"""
777
+
735
778
def __init__ (self , nameservers = None , search = None , options = None ):
736
779
self ['Nameservers' ] = nameservers
737
780
self ['Search' ] = search
@@ -762,6 +805,7 @@ class Privileges(dict):
762
805
selinux_type (string): SELinux type label
763
806
selinux_level (string): SELinux level label
764
807
"""
808
+
765
809
def __init__ (self , credentialspec_file = None , credentialspec_registry = None ,
766
810
selinux_disable = None , selinux_user = None , selinux_role = None ,
767
811
selinux_type = None , selinux_level = None ):
@@ -804,6 +848,7 @@ class NetworkAttachmentConfig(dict):
804
848
options (:py:class:`dict`): Driver attachment options for the
805
849
network target.
806
850
"""
851
+
807
852
def __init__ (self , target , aliases = None , options = None ):
808
853
self ['Target' ] = target
809
854
self ['Aliases' ] = aliases
0 commit comments