toil 6.1.0a1__py3-none-any.whl → 8.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- toil/__init__.py +122 -315
- toil/batchSystems/__init__.py +1 -0
- toil/batchSystems/abstractBatchSystem.py +173 -89
- toil/batchSystems/abstractGridEngineBatchSystem.py +272 -148
- toil/batchSystems/awsBatch.py +244 -135
- toil/batchSystems/cleanup_support.py +26 -16
- toil/batchSystems/contained_executor.py +31 -28
- toil/batchSystems/gridengine.py +86 -50
- toil/batchSystems/htcondor.py +166 -89
- toil/batchSystems/kubernetes.py +632 -382
- toil/batchSystems/local_support.py +20 -15
- toil/batchSystems/lsf.py +134 -81
- toil/batchSystems/lsfHelper.py +13 -11
- toil/batchSystems/mesos/__init__.py +41 -29
- toil/batchSystems/mesos/batchSystem.py +290 -151
- toil/batchSystems/mesos/executor.py +79 -50
- toil/batchSystems/mesos/test/__init__.py +31 -23
- toil/batchSystems/options.py +46 -28
- toil/batchSystems/registry.py +53 -19
- toil/batchSystems/singleMachine.py +296 -125
- toil/batchSystems/slurm.py +603 -138
- toil/batchSystems/torque.py +47 -33
- toil/bus.py +186 -76
- toil/common.py +664 -368
- toil/cwl/__init__.py +1 -1
- toil/cwl/cwltoil.py +1136 -483
- toil/cwl/utils.py +17 -22
- toil/deferred.py +63 -42
- toil/exceptions.py +5 -3
- toil/fileStores/__init__.py +5 -5
- toil/fileStores/abstractFileStore.py +140 -60
- toil/fileStores/cachingFileStore.py +717 -269
- toil/fileStores/nonCachingFileStore.py +116 -87
- toil/job.py +1225 -368
- toil/jobStores/abstractJobStore.py +416 -266
- toil/jobStores/aws/jobStore.py +863 -477
- toil/jobStores/aws/utils.py +201 -120
- toil/jobStores/conftest.py +3 -2
- toil/jobStores/fileJobStore.py +292 -154
- toil/jobStores/googleJobStore.py +140 -74
- toil/jobStores/utils.py +36 -15
- toil/leader.py +668 -272
- toil/lib/accelerators.py +115 -18
- toil/lib/aws/__init__.py +74 -31
- toil/lib/aws/ami.py +122 -87
- toil/lib/aws/iam.py +284 -108
- toil/lib/aws/s3.py +31 -0
- toil/lib/aws/session.py +214 -39
- toil/lib/aws/utils.py +287 -231
- toil/lib/bioio.py +13 -5
- toil/lib/compatibility.py +11 -6
- toil/lib/conversions.py +104 -47
- toil/lib/docker.py +131 -103
- toil/lib/ec2.py +361 -199
- toil/lib/ec2nodes.py +174 -106
- toil/lib/encryption/_dummy.py +5 -3
- toil/lib/encryption/_nacl.py +10 -6
- toil/lib/encryption/conftest.py +1 -0
- toil/lib/exceptions.py +26 -7
- toil/lib/expando.py +5 -3
- toil/lib/ftp_utils.py +217 -0
- toil/lib/generatedEC2Lists.py +127 -19
- toil/lib/humanize.py +6 -2
- toil/lib/integration.py +341 -0
- toil/lib/io.py +141 -15
- toil/lib/iterables.py +4 -2
- toil/lib/memoize.py +12 -8
- toil/lib/misc.py +66 -21
- toil/lib/objects.py +2 -2
- toil/lib/resources.py +68 -15
- toil/lib/retry.py +126 -81
- toil/lib/threading.py +299 -82
- toil/lib/throttle.py +16 -15
- toil/options/common.py +843 -409
- toil/options/cwl.py +175 -90
- toil/options/runner.py +50 -0
- toil/options/wdl.py +73 -17
- toil/provisioners/__init__.py +117 -46
- toil/provisioners/abstractProvisioner.py +332 -157
- toil/provisioners/aws/__init__.py +70 -33
- toil/provisioners/aws/awsProvisioner.py +1145 -715
- toil/provisioners/clusterScaler.py +541 -279
- toil/provisioners/gceProvisioner.py +282 -179
- toil/provisioners/node.py +155 -79
- toil/realtimeLogger.py +34 -22
- toil/resource.py +137 -75
- toil/server/app.py +128 -62
- toil/server/celery_app.py +3 -1
- toil/server/cli/wes_cwl_runner.py +82 -53
- toil/server/utils.py +54 -28
- toil/server/wes/abstract_backend.py +64 -26
- toil/server/wes/amazon_wes_utils.py +21 -15
- toil/server/wes/tasks.py +121 -63
- toil/server/wes/toil_backend.py +142 -107
- toil/server/wsgi_app.py +4 -3
- toil/serviceManager.py +58 -22
- toil/statsAndLogging.py +224 -70
- toil/test/__init__.py +282 -183
- toil/test/batchSystems/batchSystemTest.py +460 -210
- toil/test/batchSystems/batch_system_plugin_test.py +90 -0
- toil/test/batchSystems/test_gridengine.py +173 -0
- toil/test/batchSystems/test_lsf_helper.py +67 -58
- toil/test/batchSystems/test_slurm.py +110 -49
- toil/test/cactus/__init__.py +0 -0
- toil/test/cactus/test_cactus_integration.py +56 -0
- toil/test/cwl/cwlTest.py +496 -287
- toil/test/cwl/measure_default_memory.cwl +12 -0
- toil/test/cwl/not_run_required_input.cwl +29 -0
- toil/test/cwl/scatter_duplicate_outputs.cwl +40 -0
- toil/test/cwl/seqtk_seq.cwl +1 -1
- toil/test/docs/scriptsTest.py +69 -46
- toil/test/jobStores/jobStoreTest.py +427 -264
- toil/test/lib/aws/test_iam.py +118 -50
- toil/test/lib/aws/test_s3.py +16 -9
- toil/test/lib/aws/test_utils.py +5 -6
- toil/test/lib/dockerTest.py +118 -141
- toil/test/lib/test_conversions.py +113 -115
- toil/test/lib/test_ec2.py +58 -50
- toil/test/lib/test_integration.py +104 -0
- toil/test/lib/test_misc.py +12 -5
- toil/test/mesos/MesosDataStructuresTest.py +23 -10
- toil/test/mesos/helloWorld.py +7 -6
- toil/test/mesos/stress.py +25 -20
- toil/test/options/__init__.py +13 -0
- toil/test/options/options.py +42 -0
- toil/test/provisioners/aws/awsProvisionerTest.py +320 -150
- toil/test/provisioners/clusterScalerTest.py +440 -250
- toil/test/provisioners/clusterTest.py +166 -44
- toil/test/provisioners/gceProvisionerTest.py +174 -100
- toil/test/provisioners/provisionerTest.py +25 -13
- toil/test/provisioners/restartScript.py +5 -4
- toil/test/server/serverTest.py +188 -141
- toil/test/sort/restart_sort.py +137 -68
- toil/test/sort/sort.py +134 -66
- toil/test/sort/sortTest.py +91 -49
- toil/test/src/autoDeploymentTest.py +141 -101
- toil/test/src/busTest.py +20 -18
- toil/test/src/checkpointTest.py +8 -2
- toil/test/src/deferredFunctionTest.py +49 -35
- toil/test/src/dockerCheckTest.py +32 -24
- toil/test/src/environmentTest.py +135 -0
- toil/test/src/fileStoreTest.py +539 -272
- toil/test/src/helloWorldTest.py +7 -4
- toil/test/src/importExportFileTest.py +61 -31
- toil/test/src/jobDescriptionTest.py +46 -21
- toil/test/src/jobEncapsulationTest.py +2 -0
- toil/test/src/jobFileStoreTest.py +74 -50
- toil/test/src/jobServiceTest.py +187 -73
- toil/test/src/jobTest.py +121 -71
- toil/test/src/miscTests.py +19 -18
- toil/test/src/promisedRequirementTest.py +82 -36
- toil/test/src/promisesTest.py +7 -6
- toil/test/src/realtimeLoggerTest.py +10 -6
- toil/test/src/regularLogTest.py +71 -37
- toil/test/src/resourceTest.py +80 -49
- toil/test/src/restartDAGTest.py +36 -22
- toil/test/src/resumabilityTest.py +9 -2
- toil/test/src/retainTempDirTest.py +45 -14
- toil/test/src/systemTest.py +12 -8
- toil/test/src/threadingTest.py +44 -25
- toil/test/src/toilContextManagerTest.py +10 -7
- toil/test/src/userDefinedJobArgTypeTest.py +8 -5
- toil/test/src/workerTest.py +73 -23
- toil/test/utils/toilDebugTest.py +103 -33
- toil/test/utils/toilKillTest.py +4 -5
- toil/test/utils/utilsTest.py +245 -106
- toil/test/wdl/wdltoil_test.py +818 -149
- toil/test/wdl/wdltoil_test_kubernetes.py +91 -0
- toil/toilState.py +120 -35
- toil/utils/toilConfig.py +13 -4
- toil/utils/toilDebugFile.py +44 -27
- toil/utils/toilDebugJob.py +214 -27
- toil/utils/toilDestroyCluster.py +11 -6
- toil/utils/toilKill.py +8 -3
- toil/utils/toilLaunchCluster.py +256 -140
- toil/utils/toilMain.py +37 -16
- toil/utils/toilRsyncCluster.py +32 -14
- toil/utils/toilSshCluster.py +49 -22
- toil/utils/toilStats.py +356 -273
- toil/utils/toilStatus.py +292 -139
- toil/utils/toilUpdateEC2Instances.py +3 -1
- toil/version.py +12 -12
- toil/wdl/utils.py +5 -5
- toil/wdl/wdltoil.py +3913 -1033
- toil/worker.py +367 -184
- {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/LICENSE +25 -0
- toil-8.0.0.dist-info/METADATA +173 -0
- toil-8.0.0.dist-info/RECORD +253 -0
- {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/WHEEL +1 -1
- toil-6.1.0a1.dist-info/METADATA +0 -125
- toil-6.1.0a1.dist-info/RECORD +0 -237
- {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/entry_points.txt +0 -0
- {toil-6.1.0a1.dist-info → toil-8.0.0.dist-info}/top_level.txt +0 -0
|
@@ -19,22 +19,37 @@ import time
|
|
|
19
19
|
from abc import abstractmethod
|
|
20
20
|
from inspect import getsource
|
|
21
21
|
from textwrap import dedent
|
|
22
|
+
from typing import TYPE_CHECKING, Optional
|
|
22
23
|
from uuid import uuid4
|
|
23
24
|
|
|
24
25
|
import pytest
|
|
25
26
|
|
|
26
27
|
from toil.provisioners import cluster_factory
|
|
27
28
|
from toil.provisioners.aws.awsProvisioner import AWSProvisioner
|
|
28
|
-
from toil.test import (
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
from toil.test import (
|
|
30
|
+
ToilTest,
|
|
31
|
+
integrative,
|
|
32
|
+
needs_aws_ec2,
|
|
33
|
+
needs_fetchable_appliance,
|
|
34
|
+
needs_mesos,
|
|
35
|
+
slow,
|
|
36
|
+
timeLimit,
|
|
37
|
+
)
|
|
35
38
|
from toil.test.provisioners.clusterTest import AbstractClusterTest
|
|
36
39
|
from toil.version import exactPython
|
|
37
40
|
|
|
41
|
+
if TYPE_CHECKING:
|
|
42
|
+
from mypy_boto3_ec2 import EC2Client
|
|
43
|
+
from mypy_boto3_ec2.type_defs import (
|
|
44
|
+
DescribeVolumesResultTypeDef,
|
|
45
|
+
EbsInstanceBlockDeviceTypeDef,
|
|
46
|
+
FilterTypeDef,
|
|
47
|
+
InstanceBlockDeviceMappingTypeDef,
|
|
48
|
+
InstanceTypeDef,
|
|
49
|
+
VolumeTypeDef,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
38
53
|
log = logging.getLogger(__name__)
|
|
39
54
|
|
|
40
55
|
|
|
@@ -46,11 +61,13 @@ class AWSProvisionerBenchTest(ToilTest):
|
|
|
46
61
|
# Needs to talk to EC2 for image discovery
|
|
47
62
|
@needs_aws_ec2
|
|
48
63
|
def test_AMI_finding(self):
|
|
49
|
-
for zone in [
|
|
50
|
-
provisioner = AWSProvisioner(
|
|
64
|
+
for zone in ["us-west-2a", "eu-central-1a", "sa-east-1b"]:
|
|
65
|
+
provisioner = AWSProvisioner(
|
|
66
|
+
"fakename", "mesos", zone, 10000, None, None, enable_fuse=False
|
|
67
|
+
)
|
|
51
68
|
ami = provisioner._discoverAMI()
|
|
52
69
|
# Make sure we got an AMI and it looks plausible
|
|
53
|
-
assert
|
|
70
|
+
assert ami.startswith("ami-")
|
|
54
71
|
|
|
55
72
|
@needs_aws_ec2
|
|
56
73
|
def test_read_write_global_files(self):
|
|
@@ -58,8 +75,16 @@ class AWSProvisionerBenchTest(ToilTest):
|
|
|
58
75
|
Make sure the `_write_file_to_cloud()` and `_read_file_from_cloud()`
|
|
59
76
|
functions of the AWS provisioner work as intended.
|
|
60
77
|
"""
|
|
61
|
-
provisioner = AWSProvisioner(
|
|
62
|
-
|
|
78
|
+
provisioner = AWSProvisioner(
|
|
79
|
+
f"aws-provisioner-test-{uuid4()}",
|
|
80
|
+
"mesos",
|
|
81
|
+
"us-west-2a",
|
|
82
|
+
50,
|
|
83
|
+
None,
|
|
84
|
+
None,
|
|
85
|
+
enable_fuse=False,
|
|
86
|
+
)
|
|
87
|
+
key = "config/test.txt"
|
|
63
88
|
contents = b"Hello, this is a test."
|
|
64
89
|
|
|
65
90
|
try:
|
|
@@ -80,23 +105,23 @@ class AbstractAWSAutoscaleTest(AbstractClusterTest):
|
|
|
80
105
|
def __init__(self, methodName):
|
|
81
106
|
super().__init__(methodName=methodName)
|
|
82
107
|
self.instanceTypes = ["m5a.large"]
|
|
83
|
-
self.clusterName =
|
|
84
|
-
self.numWorkers = [
|
|
108
|
+
self.clusterName = "aws-provisioner-test-" + str(uuid4())
|
|
109
|
+
self.numWorkers = ["2"]
|
|
85
110
|
self.numSamples = 2
|
|
86
111
|
self.spotBid = 0.15
|
|
87
112
|
# We can't dump our user script right in /tmp or /home, because hot
|
|
88
113
|
# deploy refuses to zip up those whole directories. So we make sure to
|
|
89
114
|
# have a subdirectory to upload the script to.
|
|
90
|
-
self.scriptDir =
|
|
115
|
+
self.scriptDir = "/tmp/t"
|
|
91
116
|
# Where should we put our virtualenv?
|
|
92
|
-
self.venvDir =
|
|
117
|
+
self.venvDir = "/tmp/venv"
|
|
93
118
|
# Where should we put our data to work on?
|
|
94
119
|
# Must exist in the Toil container; the leader will try to rsync to it
|
|
95
120
|
# (for the SSE key) and not create it.
|
|
96
|
-
self.dataDir =
|
|
121
|
+
self.dataDir = "/tmp"
|
|
97
122
|
# What filename should we use for our script (without path)?
|
|
98
123
|
# Can be changed by derived tests.
|
|
99
|
-
self.scriptName =
|
|
124
|
+
self.scriptName = "test_script.py"
|
|
100
125
|
|
|
101
126
|
def script(self):
|
|
102
127
|
"""
|
|
@@ -111,17 +136,37 @@ class AbstractAWSAutoscaleTest(AbstractClusterTest):
|
|
|
111
136
|
return os.path.join(self.dataDir, filename)
|
|
112
137
|
|
|
113
138
|
def rsyncUtil(self, src, dest):
|
|
114
|
-
subprocess.check_call(
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
139
|
+
subprocess.check_call(
|
|
140
|
+
[
|
|
141
|
+
"toil",
|
|
142
|
+
"rsync-cluster",
|
|
143
|
+
"--insecure",
|
|
144
|
+
"-p=aws",
|
|
145
|
+
"-z",
|
|
146
|
+
self.zone,
|
|
147
|
+
self.clusterName,
|
|
148
|
+
]
|
|
149
|
+
+ [src, dest]
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
def getRootVolID(self) -> str:
|
|
153
|
+
instances: list["InstanceTypeDef"] = self.cluster._get_nodes_in_cluster_boto3()
|
|
154
|
+
instances.sort(key=lambda x: x.get("LaunchTime"))
|
|
155
|
+
leader: "InstanceTypeDef" = instances[0] # assume leader was launched first
|
|
156
|
+
|
|
157
|
+
bdm: Optional[list["InstanceBlockDeviceMappingTypeDef"]] = leader.get(
|
|
158
|
+
"BlockDeviceMappings"
|
|
159
|
+
)
|
|
160
|
+
assert bdm is not None
|
|
161
|
+
root_block_device: Optional["EbsInstanceBlockDeviceTypeDef"] = None
|
|
162
|
+
for device in bdm:
|
|
163
|
+
if device["DeviceName"] == "/dev/xvda":
|
|
164
|
+
root_block_device = device["Ebs"]
|
|
165
|
+
assert (
|
|
166
|
+
root_block_device is not None
|
|
167
|
+
) # There should be a device named "/dev/xvda"
|
|
168
|
+
assert root_block_device.get("VolumeId") is not None
|
|
169
|
+
return root_block_device["VolumeId"]
|
|
125
170
|
|
|
126
171
|
@abstractmethod
|
|
127
172
|
def _getScript(self):
|
|
@@ -132,18 +177,19 @@ class AbstractAWSAutoscaleTest(AbstractClusterTest):
|
|
|
132
177
|
"""
|
|
133
178
|
Helper method for _getScript to inject a script file at the configured script path, from text.
|
|
134
179
|
"""
|
|
135
|
-
cluster = cluster_factory(
|
|
180
|
+
cluster = cluster_factory(
|
|
181
|
+
provisioner="aws", zone=self.zone, clusterName=self.clusterName
|
|
182
|
+
)
|
|
136
183
|
leader = cluster.getLeader()
|
|
137
184
|
|
|
138
|
-
self.sshUtil([
|
|
185
|
+
self.sshUtil(["mkdir", "-p", self.scriptDir])
|
|
139
186
|
|
|
140
|
-
with tempfile.NamedTemporaryFile(mode=
|
|
187
|
+
with tempfile.NamedTemporaryFile(mode="w") as t:
|
|
141
188
|
# use appliance ssh method instead of sshutil so we can specify input param
|
|
142
189
|
t.write(content)
|
|
143
190
|
# This works to make writes visible on non-Windows
|
|
144
191
|
t.flush()
|
|
145
|
-
leader.injectFile(t.name, self.script(),
|
|
146
|
-
|
|
192
|
+
leader.injectFile(t.name, self.script(), "toil_leader")
|
|
147
193
|
|
|
148
194
|
@abstractmethod
|
|
149
195
|
def _runScript(self, toilOptions):
|
|
@@ -162,51 +208,60 @@ class AbstractAWSAutoscaleTest(AbstractClusterTest):
|
|
|
162
208
|
self.launchCluster()
|
|
163
209
|
# get the leader so we know the IP address - we don't need to wait since create cluster
|
|
164
210
|
# already insures the leader is running
|
|
165
|
-
self.cluster = cluster_factory(
|
|
211
|
+
self.cluster = cluster_factory(
|
|
212
|
+
provisioner="aws", zone=self.zone, clusterName=self.clusterName
|
|
213
|
+
)
|
|
166
214
|
self.leader = self.cluster.getLeader()
|
|
167
|
-
self.sshUtil([
|
|
168
|
-
self.sshUtil([
|
|
215
|
+
self.sshUtil(["mkdir", "-p", self.scriptDir])
|
|
216
|
+
self.sshUtil(["mkdir", "-p", self.dataDir])
|
|
169
217
|
|
|
170
218
|
assert len(self.cluster._getRoleNames()) == 1
|
|
171
219
|
# --never-download prevents silent upgrades to pip, wheel and setuptools
|
|
172
|
-
venv_command = [
|
|
220
|
+
venv_command = [
|
|
221
|
+
"virtualenv",
|
|
222
|
+
"--system-site-packages",
|
|
223
|
+
"--python",
|
|
224
|
+
exactPython,
|
|
225
|
+
"--never-download",
|
|
226
|
+
self.venvDir,
|
|
227
|
+
]
|
|
173
228
|
self.sshUtil(venv_command)
|
|
174
229
|
|
|
175
|
-
log.info(
|
|
230
|
+
log.info("Set up script...")
|
|
176
231
|
self._getScript()
|
|
177
232
|
|
|
178
|
-
toilOptions = [
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
233
|
+
toilOptions = [
|
|
234
|
+
self.jobStore,
|
|
235
|
+
"--workDir=/var/lib/toil",
|
|
236
|
+
"--clean=always",
|
|
237
|
+
"--retryCount=2",
|
|
238
|
+
"--logDebug",
|
|
239
|
+
"--logFile=" + os.path.join(self.scriptDir, "sort.log"),
|
|
240
|
+
]
|
|
185
241
|
|
|
186
242
|
if preemptibleJobs:
|
|
187
|
-
toilOptions.extend([
|
|
243
|
+
toilOptions.extend(["--defaultPreemptible"])
|
|
188
244
|
|
|
189
|
-
log.info(
|
|
245
|
+
log.info("Run script...")
|
|
190
246
|
self._runScript(toilOptions)
|
|
191
247
|
|
|
192
248
|
assert len(self.cluster._getRoleNames()) == 1
|
|
193
249
|
|
|
194
|
-
from boto.exception import EC2ResponseError
|
|
195
250
|
volumeID = self.getRootVolID()
|
|
196
251
|
self.cluster.destroyCluster()
|
|
252
|
+
boto3_ec2: "EC2Client" = self.aws.client(region=self.region, service_name="ec2")
|
|
253
|
+
volume_filter: "FilterTypeDef" = {"Name": "volume-id", "Values": [volumeID]}
|
|
254
|
+
volumes: Optional[list["VolumeTypeDef"]] = None
|
|
197
255
|
for attempt in range(6):
|
|
198
256
|
# https://github.com/BD2KGenomics/toil/issues/1567
|
|
199
257
|
# retry this for up to 1 minute until the volume disappears
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
raise
|
|
208
|
-
else:
|
|
209
|
-
self.fail('Volume with ID %s was not cleaned up properly' % volumeID)
|
|
258
|
+
volumes = boto3_ec2.describe_volumes(Filters=[volume_filter])["Volumes"]
|
|
259
|
+
if len(volumes) == 0:
|
|
260
|
+
# None are left, so they have been properly deleted
|
|
261
|
+
break
|
|
262
|
+
time.sleep(10)
|
|
263
|
+
if volumes is None or len(volumes) > 0:
|
|
264
|
+
self.fail("Volume with ID %s was not cleaned up properly" % volumeID)
|
|
210
265
|
|
|
211
266
|
assert len(self.cluster._getRoleNames()) == 0
|
|
212
267
|
|
|
@@ -217,66 +272,86 @@ class AbstractAWSAutoscaleTest(AbstractClusterTest):
|
|
|
217
272
|
class AWSAutoscaleTest(AbstractAWSAutoscaleTest):
|
|
218
273
|
def __init__(self, name):
|
|
219
274
|
super().__init__(name)
|
|
220
|
-
self.clusterName =
|
|
275
|
+
self.clusterName = "provisioner-test-" + str(uuid4())
|
|
221
276
|
self.requestedLeaderStorage = 80
|
|
222
|
-
self.scriptName =
|
|
277
|
+
self.scriptName = "sort.py"
|
|
223
278
|
|
|
224
279
|
def setUp(self):
|
|
225
280
|
super().setUp()
|
|
226
|
-
self.jobStore = f
|
|
281
|
+
self.jobStore = f"aws:{self.awsRegion()}:autoscale-{uuid4()}"
|
|
227
282
|
|
|
228
283
|
def _getScript(self):
|
|
229
284
|
fileToSort = os.path.join(os.getcwd(), str(uuid4()))
|
|
230
|
-
with open(fileToSort,
|
|
285
|
+
with open(fileToSort, "w") as f:
|
|
231
286
|
# Fixme: making this file larger causes the test to hang
|
|
232
|
-
f.write(
|
|
233
|
-
self.rsyncUtil(
|
|
234
|
-
|
|
287
|
+
f.write("01234567890123456789012345678901")
|
|
288
|
+
self.rsyncUtil(
|
|
289
|
+
os.path.join(self._projectRootPath(), "src/toil/test/sort/sort.py"),
|
|
290
|
+
":" + self.script(),
|
|
291
|
+
)
|
|
292
|
+
self.rsyncUtil(fileToSort, ":" + self.data("sortFile"))
|
|
235
293
|
os.unlink(fileToSort)
|
|
236
294
|
|
|
237
295
|
def _runScript(self, toilOptions):
|
|
238
|
-
toilOptions.extend(
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
296
|
+
toilOptions.extend(
|
|
297
|
+
[
|
|
298
|
+
"--provisioner=aws",
|
|
299
|
+
"--batchSystem=mesos",
|
|
300
|
+
"--nodeTypes=" + ",".join(self.instanceTypes),
|
|
301
|
+
"--maxNodes=" + ",".join(self.numWorkers),
|
|
302
|
+
]
|
|
303
|
+
)
|
|
304
|
+
runCommand = [
|
|
305
|
+
self.python(),
|
|
306
|
+
self.script(),
|
|
307
|
+
"--fileToSort=" + self.data("sortFile"),
|
|
308
|
+
"--sseKey=" + self.data("sortFile"),
|
|
309
|
+
]
|
|
242
310
|
runCommand.extend(toilOptions)
|
|
243
311
|
self.sshUtil(runCommand)
|
|
244
312
|
|
|
245
313
|
def launchCluster(self):
|
|
246
314
|
# add arguments to test that we can specify leader storage
|
|
247
|
-
self.createClusterUtil(
|
|
315
|
+
self.createClusterUtil(
|
|
316
|
+
args=["--leaderStorage", str(self.requestedLeaderStorage)]
|
|
317
|
+
)
|
|
248
318
|
|
|
249
|
-
def getRootVolID(self):
|
|
319
|
+
def getRootVolID(self) -> str:
|
|
250
320
|
"""
|
|
251
321
|
Adds in test to check that EBS volume is build with adequate size.
|
|
252
322
|
Otherwise is functionally equivalent to parent.
|
|
253
323
|
:return: volumeID
|
|
254
324
|
"""
|
|
255
325
|
volumeID = super().getRootVolID()
|
|
256
|
-
|
|
326
|
+
boto3_ec2: "EC2Client" = self.aws.client(region=self.region, service_name="ec2")
|
|
327
|
+
volume_filter: "FilterTypeDef" = {"Name": "volume-id", "Values": [volumeID]}
|
|
328
|
+
volumes: "DescribeVolumesResultTypeDef" = boto3_ec2.describe_volumes(
|
|
329
|
+
Filters=[volume_filter]
|
|
330
|
+
)
|
|
331
|
+
root_volume: "VolumeTypeDef" = volumes["Volumes"][0] # should be first
|
|
257
332
|
# test that the leader is given adequate storage
|
|
258
|
-
self.assertGreaterEqual(
|
|
333
|
+
self.assertGreaterEqual(root_volume["Size"], self.requestedLeaderStorage)
|
|
259
334
|
return volumeID
|
|
260
335
|
|
|
261
336
|
@integrative
|
|
262
337
|
@needs_aws_ec2
|
|
263
338
|
def testAutoScale(self):
|
|
264
339
|
self.instanceTypes = ["m5a.large"]
|
|
265
|
-
self.numWorkers = [
|
|
340
|
+
self.numWorkers = ["2"]
|
|
266
341
|
self._test()
|
|
267
342
|
|
|
268
343
|
@integrative
|
|
269
344
|
@needs_aws_ec2
|
|
270
345
|
def testSpotAutoScale(self):
|
|
271
346
|
self.instanceTypes = ["m5a.large:%f" % self.spotBid]
|
|
272
|
-
self.numWorkers = [
|
|
347
|
+
self.numWorkers = ["2"]
|
|
273
348
|
self._test(preemptibleJobs=True)
|
|
274
349
|
|
|
275
350
|
@integrative
|
|
276
351
|
@needs_aws_ec2
|
|
277
352
|
def testSpotAutoScaleBalancingTypes(self):
|
|
278
353
|
self.instanceTypes = ["m5.large/m5a.large:%f" % self.spotBid]
|
|
279
|
-
self.numWorkers = [
|
|
354
|
+
self.numWorkers = ["2"]
|
|
280
355
|
self._test(preemptibleJobs=True)
|
|
281
356
|
|
|
282
357
|
|
|
@@ -285,26 +360,36 @@ class AWSAutoscaleTest(AbstractAWSAutoscaleTest):
|
|
|
285
360
|
@pytest.mark.timeout(2400)
|
|
286
361
|
class AWSStaticAutoscaleTest(AWSAutoscaleTest):
|
|
287
362
|
"""Runs the tests on a statically provisioned cluster with autoscaling enabled."""
|
|
363
|
+
|
|
288
364
|
def __init__(self, name):
|
|
289
365
|
super().__init__(name)
|
|
290
366
|
self.requestedNodeStorage = 20
|
|
291
367
|
|
|
292
368
|
def launchCluster(self):
|
|
293
|
-
from boto.ec2.blockdevicemapping import BlockDeviceType
|
|
294
|
-
|
|
295
369
|
from toil.lib.ec2 import wait_instances_running
|
|
296
|
-
self.createClusterUtil(args=['--leaderStorage', str(self.requestedLeaderStorage),
|
|
297
|
-
'--nodeTypes', ",".join(self.instanceTypes),
|
|
298
|
-
'-w', ",".join(self.numWorkers),
|
|
299
|
-
'--nodeStorage', str(self.requestedLeaderStorage)])
|
|
300
370
|
|
|
301
|
-
self.
|
|
371
|
+
self.createClusterUtil(
|
|
372
|
+
args=[
|
|
373
|
+
"--leaderStorage",
|
|
374
|
+
str(self.requestedLeaderStorage),
|
|
375
|
+
"--nodeTypes",
|
|
376
|
+
",".join(self.instanceTypes),
|
|
377
|
+
"-w",
|
|
378
|
+
",".join(self.numWorkers),
|
|
379
|
+
"--nodeStorage",
|
|
380
|
+
str(self.requestedLeaderStorage),
|
|
381
|
+
]
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
self.cluster = cluster_factory(
|
|
385
|
+
provisioner="aws", zone=self.zone, clusterName=self.clusterName
|
|
386
|
+
)
|
|
302
387
|
# We need to wait a little bit here because the workers might not be
|
|
303
388
|
# visible to EC2 read requests immediately after the create returns,
|
|
304
389
|
# which is the last thing that starting the cluster does.
|
|
305
390
|
time.sleep(10)
|
|
306
|
-
nodes = self.cluster.
|
|
307
|
-
nodes.sort(key=lambda x: x.
|
|
391
|
+
nodes: list["InstanceTypeDef"] = self.cluster._get_nodes_in_cluster_boto3()
|
|
392
|
+
nodes.sort(key=lambda x: x.get("LaunchTime"))
|
|
308
393
|
# assuming that leader is first
|
|
309
394
|
workers = nodes[1:]
|
|
310
395
|
# test that two worker nodes were created
|
|
@@ -312,18 +397,49 @@ class AWSStaticAutoscaleTest(AWSAutoscaleTest):
|
|
|
312
397
|
# test that workers have expected storage size
|
|
313
398
|
# just use the first worker
|
|
314
399
|
worker = workers[0]
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
400
|
+
boto3_ec2: "EC2Client" = self.aws.client(region=self.region, service_name="ec2")
|
|
401
|
+
|
|
402
|
+
worker: "InstanceTypeDef" = next(wait_instances_running(boto3_ec2, [worker]))
|
|
403
|
+
|
|
404
|
+
bdm: Optional[list["InstanceBlockDeviceMappingTypeDef"]] = worker.get(
|
|
405
|
+
"BlockDeviceMappings"
|
|
406
|
+
)
|
|
407
|
+
assert bdm is not None
|
|
408
|
+
root_block_device: Optional["EbsInstanceBlockDeviceTypeDef"] = None
|
|
409
|
+
for device in bdm:
|
|
410
|
+
if device["DeviceName"] == "/dev/xvda":
|
|
411
|
+
root_block_device = device["Ebs"]
|
|
412
|
+
assert root_block_device is not None
|
|
413
|
+
assert (
|
|
414
|
+
root_block_device.get("VolumeId") is not None
|
|
415
|
+
) # TypedDicts cannot have runtime type checks
|
|
416
|
+
|
|
417
|
+
volume_filter: "FilterTypeDef" = {
|
|
418
|
+
"Name": "volume-id",
|
|
419
|
+
"Values": [root_block_device["VolumeId"]],
|
|
420
|
+
}
|
|
421
|
+
root_volume: "VolumeTypeDef" = boto3_ec2.describe_volumes(
|
|
422
|
+
Filters=[volume_filter]
|
|
423
|
+
)["Volumes"][
|
|
424
|
+
0
|
|
425
|
+
] # should be first
|
|
426
|
+
self.assertGreaterEqual(root_volume.get("Size"), self.requestedNodeStorage)
|
|
320
427
|
|
|
321
428
|
def _runScript(self, toilOptions):
|
|
322
429
|
# Autoscale even though we have static nodes
|
|
323
|
-
toilOptions.extend(
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
430
|
+
toilOptions.extend(
|
|
431
|
+
[
|
|
432
|
+
"--provisioner=aws",
|
|
433
|
+
"--batchSystem=mesos",
|
|
434
|
+
"--nodeTypes=" + ",".join(self.instanceTypes),
|
|
435
|
+
"--maxNodes=" + ",".join(self.numWorkers),
|
|
436
|
+
]
|
|
437
|
+
)
|
|
438
|
+
runCommand = [
|
|
439
|
+
self.python(),
|
|
440
|
+
self.script(),
|
|
441
|
+
"--fileToSort=" + self.data("sortFile"),
|
|
442
|
+
]
|
|
327
443
|
runCommand.extend(toilOptions)
|
|
328
444
|
self.sshUtil(runCommand)
|
|
329
445
|
|
|
@@ -332,26 +448,39 @@ class AWSStaticAutoscaleTest(AWSAutoscaleTest):
|
|
|
332
448
|
@pytest.mark.timeout(1200)
|
|
333
449
|
class AWSManagedAutoscaleTest(AWSAutoscaleTest):
|
|
334
450
|
"""Runs the tests on a self-scaling Kubernetes cluster."""
|
|
451
|
+
|
|
335
452
|
def __init__(self, name):
|
|
336
453
|
super().__init__(name)
|
|
337
454
|
self.requestedNodeStorage = 20
|
|
338
455
|
|
|
339
456
|
def launchCluster(self):
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
457
|
+
self.createClusterUtil(
|
|
458
|
+
args=[
|
|
459
|
+
"--leaderStorage",
|
|
460
|
+
str(self.requestedLeaderStorage),
|
|
461
|
+
"--nodeTypes",
|
|
462
|
+
",".join(self.instanceTypes),
|
|
463
|
+
"--workers",
|
|
464
|
+
",".join([f"0-{c}" for c in self.numWorkers]),
|
|
465
|
+
"--nodeStorage",
|
|
466
|
+
str(self.requestedLeaderStorage),
|
|
467
|
+
"--clusterType",
|
|
468
|
+
"kubernetes",
|
|
469
|
+
]
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
self.cluster = cluster_factory(
|
|
473
|
+
provisioner="aws", zone=self.zone, clusterName=self.clusterName
|
|
474
|
+
)
|
|
350
475
|
|
|
351
476
|
def _runScript(self, toilOptions):
|
|
352
477
|
# Don't use the provisioner, and use Kubernetes instead of Mesos
|
|
353
|
-
toilOptions.extend([
|
|
354
|
-
runCommand = [
|
|
478
|
+
toilOptions.extend(["--batchSystem=kubernetes"])
|
|
479
|
+
runCommand = [
|
|
480
|
+
self.python(),
|
|
481
|
+
self.script(),
|
|
482
|
+
"--fileToSort=" + self.data("sortFile"),
|
|
483
|
+
]
|
|
355
484
|
runCommand.extend(toilOptions)
|
|
356
485
|
self.sshUtil(runCommand)
|
|
357
486
|
|
|
@@ -362,37 +491,51 @@ class AWSManagedAutoscaleTest(AWSAutoscaleTest):
|
|
|
362
491
|
class AWSAutoscaleTestMultipleNodeTypes(AbstractAWSAutoscaleTest):
|
|
363
492
|
def __init__(self, name):
|
|
364
493
|
super().__init__(name)
|
|
365
|
-
self.clusterName =
|
|
494
|
+
self.clusterName = "provisioner-test-" + str(uuid4())
|
|
366
495
|
|
|
367
496
|
def setUp(self):
|
|
368
497
|
super().setUp()
|
|
369
|
-
self.jobStore = f
|
|
498
|
+
self.jobStore = f"aws:{self.awsRegion()}:autoscale-{uuid4()}"
|
|
370
499
|
|
|
371
500
|
def _getScript(self):
|
|
372
|
-
sseKeyFile = os.path.join(os.getcwd(),
|
|
373
|
-
with open(sseKeyFile,
|
|
374
|
-
f.write(
|
|
375
|
-
self.rsyncUtil(
|
|
376
|
-
|
|
501
|
+
sseKeyFile = os.path.join(os.getcwd(), "keyFile")
|
|
502
|
+
with open(sseKeyFile, "w") as f:
|
|
503
|
+
f.write("01234567890123456789012345678901")
|
|
504
|
+
self.rsyncUtil(
|
|
505
|
+
os.path.join(self._projectRootPath(), "src/toil/test/sort/sort.py"),
|
|
506
|
+
":" + self.script(),
|
|
507
|
+
)
|
|
508
|
+
self.rsyncUtil(sseKeyFile, ":" + self.data("keyFile"))
|
|
377
509
|
os.unlink(sseKeyFile)
|
|
378
510
|
|
|
379
511
|
def _runScript(self, toilOptions):
|
|
380
512
|
# Set memory requirements so that sort jobs can be run
|
|
381
513
|
# on small instances, but merge jobs must be run on large
|
|
382
514
|
# instances
|
|
383
|
-
toilOptions.extend(
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
515
|
+
toilOptions.extend(
|
|
516
|
+
[
|
|
517
|
+
"--provisioner=aws",
|
|
518
|
+
"--batchSystem=mesos",
|
|
519
|
+
"--nodeTypes=" + ",".join(self.instanceTypes),
|
|
520
|
+
"--maxNodes=" + ",".join(self.numWorkers),
|
|
521
|
+
]
|
|
522
|
+
)
|
|
523
|
+
runCommand = [
|
|
524
|
+
self.python(),
|
|
525
|
+
self.script(),
|
|
526
|
+
"--fileToSort=/home/s3am/bin/asadmin",
|
|
527
|
+
"--sortMemory=0.6G",
|
|
528
|
+
"--mergeMemory=3.0G",
|
|
529
|
+
]
|
|
387
530
|
runCommand.extend(toilOptions)
|
|
388
|
-
runCommand.append(
|
|
531
|
+
runCommand.append("--sseKey=" + self.data("keyFile"))
|
|
389
532
|
self.sshUtil(runCommand)
|
|
390
533
|
|
|
391
534
|
@integrative
|
|
392
535
|
@needs_aws_ec2
|
|
393
536
|
def testAutoScale(self):
|
|
394
537
|
self.instanceTypes = ["t2.small", "m5a.large"]
|
|
395
|
-
self.numWorkers = [
|
|
538
|
+
self.numWorkers = ["2", "1"]
|
|
396
539
|
self._test()
|
|
397
540
|
|
|
398
541
|
|
|
@@ -401,16 +544,17 @@ class AWSAutoscaleTestMultipleNodeTypes(AbstractAWSAutoscaleTest):
|
|
|
401
544
|
@pytest.mark.timeout(1200)
|
|
402
545
|
class AWSRestartTest(AbstractAWSAutoscaleTest):
|
|
403
546
|
"""This test insures autoscaling works on a restarted Toil run."""
|
|
547
|
+
|
|
404
548
|
def __init__(self, name):
|
|
405
549
|
super().__init__(name)
|
|
406
|
-
self.clusterName =
|
|
407
|
-
self.scriptName =
|
|
550
|
+
self.clusterName = "restart-test-" + str(uuid4())
|
|
551
|
+
self.scriptName = "restartScript.py"
|
|
408
552
|
|
|
409
553
|
def setUp(self):
|
|
410
554
|
super().setUp()
|
|
411
|
-
self.instanceTypes = [
|
|
412
|
-
self.numWorkers = [
|
|
413
|
-
self.jobStore = f
|
|
555
|
+
self.instanceTypes = ["t2.small"]
|
|
556
|
+
self.numWorkers = ["1"]
|
|
557
|
+
self.jobStore = f"aws:{self.awsRegion()}:restart-{uuid4()}"
|
|
414
558
|
|
|
415
559
|
def _getScript(self):
|
|
416
560
|
def restartScript():
|
|
@@ -421,38 +565,56 @@ class AWSRestartTest(AbstractAWSAutoscaleTest):
|
|
|
421
565
|
from toil.job import Job
|
|
422
566
|
|
|
423
567
|
def f0(job):
|
|
424
|
-
if
|
|
425
|
-
raise RuntimeError(
|
|
568
|
+
if "FAIL" in os.environ:
|
|
569
|
+
raise RuntimeError("failed on purpose")
|
|
426
570
|
|
|
427
|
-
if __name__ ==
|
|
571
|
+
if __name__ == "__main__":
|
|
428
572
|
parser = ArgumentParser()
|
|
429
573
|
Job.Runner.addToilOptions(parser)
|
|
430
574
|
options = parser.parse_args()
|
|
431
|
-
rootJob = Job.wrapJobFn(f0, cores=0.5, memory=
|
|
575
|
+
rootJob = Job.wrapJobFn(f0, cores=0.5, memory="50 M", disk="50 M")
|
|
432
576
|
Job.Runner.startToil(rootJob, options)
|
|
433
577
|
|
|
434
|
-
script = dedent(
|
|
578
|
+
script = dedent("\n".join(getsource(restartScript).split("\n")[1:]))
|
|
435
579
|
self.putScript(script)
|
|
436
580
|
|
|
437
581
|
def _runScript(self, toilOptions):
|
|
438
582
|
# Use the provisioner in the workflow
|
|
439
|
-
toilOptions.extend(
|
|
440
|
-
|
|
441
|
-
|
|
583
|
+
toilOptions.extend(
|
|
584
|
+
[
|
|
585
|
+
"--provisioner=aws",
|
|
586
|
+
"--batchSystem=mesos",
|
|
587
|
+
"--nodeTypes=" + ",".join(self.instanceTypes),
|
|
588
|
+
"--maxNodes=" + ",".join(self.numWorkers),
|
|
589
|
+
]
|
|
590
|
+
)
|
|
442
591
|
# clean = onSuccess
|
|
443
|
-
disallowedOptions = [
|
|
444
|
-
newOptions = [
|
|
592
|
+
disallowedOptions = ["--clean=always", "--retryCount=2"]
|
|
593
|
+
newOptions = [
|
|
594
|
+
option for option in toilOptions if option not in disallowedOptions
|
|
595
|
+
]
|
|
445
596
|
try:
|
|
446
597
|
# include a default memory - on restart the minimum memory requirement is the default, usually 2 GB
|
|
447
|
-
command = [
|
|
598
|
+
command = [
|
|
599
|
+
self.python(),
|
|
600
|
+
self.script(),
|
|
601
|
+
"--setEnv",
|
|
602
|
+
"FAIL=true",
|
|
603
|
+
"--defaultMemory=50000000",
|
|
604
|
+
]
|
|
448
605
|
command.extend(newOptions)
|
|
449
606
|
self.sshUtil(command)
|
|
450
607
|
except subprocess.CalledProcessError:
|
|
451
608
|
pass
|
|
452
609
|
else:
|
|
453
|
-
self.fail(
|
|
610
|
+
self.fail("Command succeeded when we expected failure")
|
|
454
611
|
with timeLimit(600):
|
|
455
|
-
command = [
|
|
612
|
+
command = [
|
|
613
|
+
self.python(),
|
|
614
|
+
self.script(),
|
|
615
|
+
"--restart",
|
|
616
|
+
"--defaultMemory=50000000",
|
|
617
|
+
]
|
|
456
618
|
command.extend(toilOptions)
|
|
457
619
|
self.sshUtil(command)
|
|
458
620
|
|
|
@@ -466,14 +628,17 @@ class AWSRestartTest(AbstractAWSAutoscaleTest):
|
|
|
466
628
|
class PreemptibleDeficitCompensationTest(AbstractAWSAutoscaleTest):
|
|
467
629
|
def __init__(self, name):
|
|
468
630
|
super().__init__(name)
|
|
469
|
-
self.clusterName =
|
|
470
|
-
self.scriptName =
|
|
631
|
+
self.clusterName = "deficit-test-" + str(uuid4())
|
|
632
|
+
self.scriptName = "userScript.py"
|
|
471
633
|
|
|
472
634
|
def setUp(self):
|
|
473
635
|
super().setUp()
|
|
474
|
-
self.instanceTypes = [
|
|
475
|
-
|
|
476
|
-
|
|
636
|
+
self.instanceTypes = [
|
|
637
|
+
"m5a.large:0.01",
|
|
638
|
+
"m5a.large",
|
|
639
|
+
] # instance needs to be available on the spot market
|
|
640
|
+
self.numWorkers = ["1", "1"]
|
|
641
|
+
self.jobStore = f"aws:{self.awsRegion()}:deficit-{uuid4()}"
|
|
477
642
|
|
|
478
643
|
def test(self):
|
|
479
644
|
self._test(preemptibleJobs=True)
|
|
@@ -490,10 +655,10 @@ class PreemptibleDeficitCompensationTest(AbstractAWSAutoscaleTest):
|
|
|
490
655
|
# we will observe a deficit of preemptible nodes that the non-preemptible scaler will
|
|
491
656
|
# compensate for by spinning up non-preemptible nodes instead.
|
|
492
657
|
#
|
|
493
|
-
def job(job, disk=
|
|
658
|
+
def job(job, disk="10M", cores=1, memory="10M", preemptible=True):
|
|
494
659
|
pass
|
|
495
660
|
|
|
496
|
-
if __name__ ==
|
|
661
|
+
if __name__ == "__main__":
|
|
497
662
|
options = Job.Runner.getDefaultArgumentParser().parse_args()
|
|
498
663
|
with Toil(options) as toil:
|
|
499
664
|
if toil.config.restart:
|
|
@@ -501,14 +666,19 @@ class PreemptibleDeficitCompensationTest(AbstractAWSAutoscaleTest):
|
|
|
501
666
|
else:
|
|
502
667
|
toil.start(Job.wrapJobFn(job))
|
|
503
668
|
|
|
504
|
-
script = dedent(
|
|
669
|
+
script = dedent("\n".join(getsource(userScript).split("\n")[1:]))
|
|
505
670
|
self.putScript(script)
|
|
506
671
|
|
|
507
672
|
def _runScript(self, toilOptions):
|
|
508
|
-
toilOptions.extend(
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
673
|
+
toilOptions.extend(
|
|
674
|
+
[
|
|
675
|
+
"--provisioner=aws",
|
|
676
|
+
"--batchSystem=mesos",
|
|
677
|
+
"--nodeTypes=" + ",".join(self.instanceTypes),
|
|
678
|
+
"--maxNodes=" + ",".join(self.numWorkers),
|
|
679
|
+
]
|
|
680
|
+
)
|
|
681
|
+
toilOptions.extend(["--preemptibleCompensation=1.0"])
|
|
512
682
|
command = [self.python(), self.script()]
|
|
513
683
|
command.extend(toilOptions)
|
|
514
684
|
self.sshUtil(command)
|