toil 5.11.0__tar.gz → 5.12.0__tar.gz
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-5.11.0/src/toil.egg-info → toil-5.12.0}/PKG-INFO +1 -1
- toil-5.12.0/requirements-aws.txt +4 -0
- {toil-5.11.0 → toil-5.12.0}/requirements-cwl.txt +1 -1
- {toil-5.11.0 → toil-5.12.0}/requirements-dev.txt +1 -1
- {toil-5.11.0 → toil-5.12.0}/requirements-google.txt +1 -0
- {toil-5.11.0 → toil-5.12.0}/requirements-wdl.txt +1 -1
- {toil-5.11.0 → toil-5.12.0}/setup.cfg +2 -1
- {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/singleMachine.py +1 -1
- {toil-5.11.0 → toil-5.12.0}/src/toil/common.py +35 -7
- {toil-5.11.0 → toil-5.12.0}/src/toil/cwl/__init__.py +8 -4
- {toil-5.11.0 → toil-5.12.0}/src/toil/cwl/cwltoil.py +15 -10
- {toil-5.11.0 → toil-5.12.0}/src/toil/fileStores/abstractFileStore.py +8 -1
- {toil-5.11.0 → toil-5.12.0}/src/toil/jobStores/abstractJobStore.py +34 -17
- {toil-5.11.0 → toil-5.12.0}/src/toil/jobStores/googleJobStore.py +47 -16
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/accelerators.py +35 -3
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/aws/iam.py +54 -13
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/aws/session.py +1 -1
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/docker.py +32 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/server/wsgi_app.py +1 -1
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/__init__.py +26 -4
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/jobStores/jobStoreTest.py +8 -8
- toil-5.12.0/src/toil/test/lib/aws/test_iam.py +139 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/provisioners/gceProvisionerTest.py +11 -6
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/sort/sortTest.py +6 -3
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/fileStoreTest.py +5 -3
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/wdl/wdltoil_test.py +89 -8
- toil-5.12.0/src/toil/version.py +14 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/wdl/wdltoil.py +91 -10
- {toil-5.11.0 → toil-5.12.0/src/toil.egg-info}/PKG-INFO +1 -1
- {toil-5.11.0 → toil-5.12.0}/src/toil.egg-info/requires.txt +12 -6
- toil-5.11.0/requirements-aws.txt +0 -2
- toil-5.11.0/src/toil/test/lib/aws/test_iam.py +0 -54
- toil-5.11.0/src/toil/version.py +0 -12
- {toil-5.11.0 → toil-5.12.0}/LICENSE +0 -0
- {toil-5.11.0 → toil-5.12.0}/MANIFEST.in +0 -0
- {toil-5.11.0 → toil-5.12.0}/README.rst +0 -0
- {toil-5.11.0 → toil-5.12.0}/requirements-encryption.txt +0 -0
- {toil-5.11.0 → toil-5.12.0}/requirements-htcondor.txt +0 -0
- {toil-5.11.0 → toil-5.12.0}/requirements-kubernetes.txt +0 -0
- {toil-5.11.0 → toil-5.12.0}/requirements-mesos.txt +0 -0
- {toil-5.11.0 → toil-5.12.0}/requirements-server.txt +0 -0
- {toil-5.11.0 → toil-5.12.0}/requirements.txt +0 -0
- {toil-5.11.0 → toil-5.12.0}/setup.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/abstractBatchSystem.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/abstractGridEngineBatchSystem.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/awsBatch.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/cleanup_support.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/contained_executor.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/gridengine.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/htcondor.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/kubernetes.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/local_support.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/lsf.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/lsfHelper.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/mesos/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/mesos/batchSystem.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/mesos/conftest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/mesos/executor.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/mesos/test/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/options.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/parasol.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/registry.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/slurm.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/tes.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/batchSystems/torque.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/bus.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/cwl/conftest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/cwl/utils.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/deferred.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/exceptions.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/fileStores/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/fileStores/cachingFileStore.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/fileStores/nonCachingFileStore.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/job.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/jobStores/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/jobStores/aws/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/jobStores/aws/jobStore.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/jobStores/aws/utils.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/jobStores/conftest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/jobStores/fileJobStore.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/jobStores/utils.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/leader.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/aws/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/aws/ami.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/aws/utils.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/bioio.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/compatibility.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/conversions.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/ec2.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/ec2nodes.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/encryption/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/encryption/_dummy.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/encryption/_nacl.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/encryption/conftest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/exceptions.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/expando.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/generatedEC2Lists.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/humanize.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/io.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/iterables.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/memoize.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/misc.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/objects.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/resources.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/retry.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/threading.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/lib/throttle.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/provisioners/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/provisioners/abstractProvisioner.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/provisioners/aws/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/provisioners/aws/awsProvisioner.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/provisioners/clusterScaler.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/provisioners/gceProvisioner.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/provisioners/node.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/realtimeLogger.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/resource.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/server/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/server/api_spec/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/server/api_spec/workflow_execution_service.swagger.yaml +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/server/app.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/server/celery_app.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/server/cli/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/server/cli/wes_cwl_runner.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/server/utils.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/server/wes/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/server/wes/abstract_backend.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/server/wes/amazon_wes_utils.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/server/wes/tasks.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/server/wes/toil_backend.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/serviceManager.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/statsAndLogging.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/batchSystems/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/batchSystems/batchSystemTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/batchSystems/parasolTestSupport.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/batchSystems/test_lsf_helper.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/batchSystems/test_slurm.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/alwaysfails.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/colon_test_output.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/colon_test_output_job.yaml +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/conditional_wf.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/conditional_wf.yaml +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/conftest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/cwlTest.py +1 -1
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/directory_from_directory.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/download.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/download_directory.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/download_subdirectory.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/echo-stderr.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/echo-stdout-log-dir.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/echo.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/echo_string.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/echo_string_scatter_capture_stdout.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/file_from_directory.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/load_contents.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/mpi_simple.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/nvidia_smi.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/revsort.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/revsort2.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/revtool.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/revtool2.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/s3_secondary_file.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/seqtk_seq.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/sorttool.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/stream.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/test_filename_conflict_detection.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/test_filename_conflict_detection_at_root.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/cwl/test_filename_conflict_resolution.cwl +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/docs/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/docs/scriptsTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/jobStores/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/lib/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/lib/aws/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/lib/aws/test_s3.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/lib/aws/test_utils.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/lib/dockerTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/lib/test_conversions.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/lib/test_ec2.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/lib/test_misc.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/mesos/MesosDataStructuresTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/mesos/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/mesos/helloWorld.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/mesos/stress.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/provisioners/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/provisioners/aws/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/provisioners/aws/awsProvisionerTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/provisioners/clusterScalerTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/provisioners/clusterTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/provisioners/provisionerTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/provisioners/restartScript.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/server/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/server/serverTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/sort/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/sort/restart_sort.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/sort/sort.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/autoDeploymentTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/busTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/checkpointTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/deferredFunctionTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/dockerCheckTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/helloWorldTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/importExportFileTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/jobDescriptionTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/jobEncapsulationTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/jobFileStoreTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/jobServiceTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/jobTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/miscTests.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/promisedRequirementTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/promisesTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/realtimeLoggerTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/regularLogTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/resourceTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/restartDAGTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/resumabilityTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/retainTempDirTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/systemTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/threadingTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/toilContextManagerTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/userDefinedJobArgTypeTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/src/workerTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/utils/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/utils/toilDebugTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/utils/toilKillTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/utils/utilsTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/wdl/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/wdl/builtinTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/wdl/conftest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/test/wdl/toilwdlTest.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/toilState.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/utils/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/utils/toilClean.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/utils/toilDebugFile.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/utils/toilDebugJob.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/utils/toilDestroyCluster.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/utils/toilKill.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/utils/toilLaunchCluster.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/utils/toilMain.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/utils/toilRsyncCluster.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/utils/toilServer.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/utils/toilSshCluster.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/utils/toilStats.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/utils/toilStatus.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/utils/toilUpdateEC2Instances.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/wdl/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/wdl/toilwdl.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/wdl/utils.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/wdl/versions/__init__.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/wdl/versions/dev.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/wdl/versions/draft2.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/wdl/versions/v1.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/wdl/wdl_analysis.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/wdl/wdl_functions.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/wdl/wdl_synthesis.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/wdl/wdl_types.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil/worker.py +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil.egg-info/SOURCES.txt +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil.egg-info/dependency_links.txt +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil.egg-info/entry_points.txt +0 -0
- {toil-5.11.0 → toil-5.12.0}/src/toil.egg-info/top_level.txt +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
miniwdl==1.
|
|
1
|
+
miniwdl==1.10.0
|
|
2
2
|
wdlparse==0.1.0
|
|
@@ -601,7 +601,7 @@ class SingleMachineBatchSystem(BatchSystemSupport):
|
|
|
601
601
|
if job_accelerators:
|
|
602
602
|
# Try and find some accelerators to use.
|
|
603
603
|
# Start with all the accelerators that are free right now
|
|
604
|
-
accelerator_set : ResourceSet = self.resource_sources[
|
|
604
|
+
accelerator_set : ResourceSet = self.resource_sources[-1]
|
|
605
605
|
snapshot = accelerator_set.get_free_snapshot()
|
|
606
606
|
# And build a plan of the ones we want
|
|
607
607
|
accelerators_needed, problem = self._identify_sufficient_accelerators(job_accelerators, snapshot)
|
|
@@ -528,7 +528,7 @@ def addOptions(parser: ArgumentParser, config: Optional[Config] = None, jobstore
|
|
|
528
528
|
core_options.add_argument("--workDir", dest="workDir", default=None,
|
|
529
529
|
help="Absolute path to directory where temporary files generated during the Toil "
|
|
530
530
|
"run should be placed. Standard output and error from batch system jobs "
|
|
531
|
-
"(unless --noStdOutErr) will be placed in this directory. A cache directory "
|
|
531
|
+
"(unless --noStdOutErr is set) will be placed in this directory. A cache directory "
|
|
532
532
|
"may be placed in this directory. Temp files and folders will be placed in a "
|
|
533
533
|
"directory toil-<workflowID> within workDir. The workflowID is generated by "
|
|
534
534
|
"Toil and will be reported in the workflow logs. Default is determined by the "
|
|
@@ -1266,27 +1266,55 @@ class Toil(ContextManager["Toil"]):
|
|
|
1266
1266
|
def import_file(self,
|
|
1267
1267
|
src_uri: str,
|
|
1268
1268
|
shared_file_name: str,
|
|
1269
|
-
symlink: bool = True
|
|
1269
|
+
symlink: bool = True,
|
|
1270
|
+
check_existence: bool = True) -> None: ...
|
|
1270
1271
|
|
|
1271
1272
|
@overload
|
|
1272
1273
|
def import_file(self,
|
|
1273
1274
|
src_uri: str,
|
|
1274
1275
|
shared_file_name: None = None,
|
|
1275
|
-
symlink: bool = True
|
|
1276
|
+
symlink: bool = True,
|
|
1277
|
+
check_existence: bool = True) -> FileID: ...
|
|
1276
1278
|
|
|
1277
1279
|
def import_file(self,
|
|
1278
1280
|
src_uri: str,
|
|
1279
1281
|
shared_file_name: Optional[str] = None,
|
|
1280
|
-
symlink: bool = True
|
|
1282
|
+
symlink: bool = True,
|
|
1283
|
+
check_existence: bool = True) -> Optional[FileID]:
|
|
1281
1284
|
"""
|
|
1282
1285
|
Import the file at the given URL into the job store.
|
|
1283
1286
|
|
|
1287
|
+
By default, returns None if the file does not exist.
|
|
1288
|
+
|
|
1289
|
+
:param check_existence: If true, raise FileNotFoundError if the file
|
|
1290
|
+
does not exist. If false, return None when the file does not
|
|
1291
|
+
exist.
|
|
1292
|
+
|
|
1284
1293
|
See :func:`toil.jobStores.abstractJobStore.AbstractJobStore.importFile` for a
|
|
1285
1294
|
full description
|
|
1286
1295
|
"""
|
|
1287
1296
|
self._assertContextManagerUsed()
|
|
1288
|
-
|
|
1289
|
-
|
|
1297
|
+
full_uri = self.normalize_uri(src_uri, check_existence=check_existence)
|
|
1298
|
+
try:
|
|
1299
|
+
imported = self._jobStore.import_file(full_uri, shared_file_name=shared_file_name, symlink=symlink)
|
|
1300
|
+
except FileNotFoundError:
|
|
1301
|
+
# TODO: I thought we refactored the different job store import
|
|
1302
|
+
# methods to not raise and instead return None, but that looks to
|
|
1303
|
+
# not currently be the case.
|
|
1304
|
+
if check_existence:
|
|
1305
|
+
raise
|
|
1306
|
+
else:
|
|
1307
|
+
# So translate the raise-based API if needed.
|
|
1308
|
+
# TODO: If check_existence is false but a shared file name is
|
|
1309
|
+
# specified, we have no way to report the lack of file
|
|
1310
|
+
# existence, since we also return None on success!
|
|
1311
|
+
return None
|
|
1312
|
+
if imported is None and shared_file_name is None and check_existence:
|
|
1313
|
+
# We need to protect the caller from missing files.
|
|
1314
|
+
# We think a file was missing, and we got None becasuse of it.
|
|
1315
|
+
# We didn't get None instead because of usign a shared file name.
|
|
1316
|
+
raise FileNotFoundError(f'Could not find file {src_uri}')
|
|
1317
|
+
return imported
|
|
1290
1318
|
|
|
1291
1319
|
@deprecated(new_function_name='export_file')
|
|
1292
1320
|
def exportFile(self, jobStoreFileID: FileID, dstUrl: str) -> None:
|
|
@@ -1308,7 +1336,7 @@ class Toil(ContextManager["Toil"]):
|
|
|
1308
1336
|
"""
|
|
1309
1337
|
Given a URI, if it has no scheme, prepend "file:".
|
|
1310
1338
|
|
|
1311
|
-
:param check_existence: If set, raise
|
|
1339
|
+
:param check_existence: If set, raise FileNotFoundError if a URI points to
|
|
1312
1340
|
a local file that does not exist.
|
|
1313
1341
|
"""
|
|
1314
1342
|
if urlparse(uri).scheme == 'file':
|
|
@@ -15,15 +15,17 @@ import logging
|
|
|
15
15
|
from functools import lru_cache
|
|
16
16
|
|
|
17
17
|
from pkg_resources import DistributionNotFound, get_distribution
|
|
18
|
+
|
|
18
19
|
try:
|
|
19
20
|
# Setuptools 66+ will raise this if any package on the system has a version that isn't PEP440.
|
|
20
21
|
# See https://github.com/pypa/setuptools/issues/3772
|
|
21
|
-
from setuptools.extern.packaging.version import InvalidVersion
|
|
22
|
+
from setuptools.extern.packaging.version import InvalidVersion # type: ignore
|
|
22
23
|
except ImportError:
|
|
23
24
|
# It's not clear that this exception is really part fo the public API, so fake it.
|
|
24
|
-
class InvalidVersion(Exception):
|
|
25
|
+
class InvalidVersion(Exception): # type: ignore
|
|
25
26
|
pass
|
|
26
27
|
|
|
28
|
+
|
|
27
29
|
from toil.version import cwltool_version
|
|
28
30
|
|
|
29
31
|
logger = logging.getLogger(__name__)
|
|
@@ -47,8 +49,10 @@ def check_cwltool_version() -> None:
|
|
|
47
49
|
except DistributionNotFound:
|
|
48
50
|
logger.debug("cwltool is not installed.")
|
|
49
51
|
except InvalidVersion as e:
|
|
50
|
-
logger.warning(
|
|
51
|
-
|
|
52
|
+
logger.warning(
|
|
53
|
+
f"Could not determine the installed version of cwltool because a package "
|
|
54
|
+
f"with an unacceptable version is installed: {e}"
|
|
55
|
+
)
|
|
52
56
|
|
|
53
57
|
|
|
54
58
|
check_cwltool_version()
|
|
@@ -60,10 +60,10 @@ import cwl_utils.expression
|
|
|
60
60
|
import cwltool.builder
|
|
61
61
|
import cwltool.command_line_tool
|
|
62
62
|
import cwltool.context
|
|
63
|
+
import cwltool.cwlprov
|
|
63
64
|
import cwltool.job
|
|
64
65
|
import cwltool.load_tool
|
|
65
66
|
import cwltool.main
|
|
66
|
-
import cwltool.provenance
|
|
67
67
|
import cwltool.resolver
|
|
68
68
|
import schema_salad.ref_resolver
|
|
69
69
|
from cwltool.loghandler import _logger as cwllogger
|
|
@@ -85,9 +85,9 @@ from cwltool.software_requirements import (
|
|
|
85
85
|
)
|
|
86
86
|
from cwltool.stdfsaccess import StdFsAccess, abspath
|
|
87
87
|
from cwltool.utils import (
|
|
88
|
-
DirectoryType,
|
|
89
88
|
CWLObjectType,
|
|
90
89
|
CWLOutputType,
|
|
90
|
+
DirectoryType,
|
|
91
91
|
adjustDirObjs,
|
|
92
92
|
aslist,
|
|
93
93
|
downloadHttpFile,
|
|
@@ -110,6 +110,7 @@ from toil.cwl.utils import (
|
|
|
110
110
|
download_structure,
|
|
111
111
|
visit_cwl_class_and_reduce,
|
|
112
112
|
)
|
|
113
|
+
from toil.exceptions import FailedJobsException
|
|
113
114
|
from toil.fileStores import FileID
|
|
114
115
|
from toil.fileStores.abstractFileStore import AbstractFileStore
|
|
115
116
|
from toil.job import AcceleratorRequirement, Job, Promise, Promised, unwrap
|
|
@@ -119,8 +120,6 @@ from toil.jobStores.utils import JobStoreUnavailableException, generate_locator
|
|
|
119
120
|
from toil.lib.threading import ExceptionalThread
|
|
120
121
|
from toil.statsAndLogging import DEFAULT_LOGLEVEL
|
|
121
122
|
from toil.version import baseVersion
|
|
122
|
-
from toil.exceptions import FailedJobsException
|
|
123
|
-
|
|
124
123
|
|
|
125
124
|
logger = logging.getLogger(__name__)
|
|
126
125
|
|
|
@@ -2015,7 +2014,8 @@ def toilStageFiles(
|
|
|
2015
2014
|
f.close()
|
|
2016
2015
|
# Import it and pack up the file ID so we can turn around and export it.
|
|
2017
2016
|
file_id_or_contents = (
|
|
2018
|
-
"toilfile:"
|
|
2017
|
+
"toilfile:"
|
|
2018
|
+
+ toil.import_file(f.name, symlink=False).pack()
|
|
2019
2019
|
)
|
|
2020
2020
|
|
|
2021
2021
|
if file_id_or_contents.startswith("toildir:"):
|
|
@@ -3625,7 +3625,7 @@ def main(args: Optional[List[str]] = None, stdout: TextIO = sys.stdout) -> int:
|
|
|
3625
3625
|
loading_context = cwltool.main.setup_loadingContext(None, runtime_context, options)
|
|
3626
3626
|
|
|
3627
3627
|
if options.provenance:
|
|
3628
|
-
research_obj = cwltool.
|
|
3628
|
+
research_obj = cwltool.cwlprov.ro.ResearchObject(
|
|
3629
3629
|
temp_prefix_ro=options.tmp_outdir_prefix,
|
|
3630
3630
|
orcid=options.orcid,
|
|
3631
3631
|
full_name=options.cwl_full_name,
|
|
@@ -3708,8 +3708,9 @@ def main(args: Optional[List[str]] = None, stdout: TextIO = sys.stdout) -> int:
|
|
|
3708
3708
|
document_loader = loading_context.loader
|
|
3709
3709
|
|
|
3710
3710
|
if options.provenance and runtime_context.research_obj:
|
|
3711
|
-
|
|
3712
|
-
|
|
3711
|
+
cwltool.cwlprov.writablebagfile.packed_workflow(
|
|
3712
|
+
runtime_context.research_obj,
|
|
3713
|
+
cwltool.main.print_pack(loading_context, uri),
|
|
3713
3714
|
)
|
|
3714
3715
|
|
|
3715
3716
|
try:
|
|
@@ -3879,7 +3880,9 @@ def main(args: Optional[List[str]] = None, stdout: TextIO = sys.stdout) -> int:
|
|
|
3879
3880
|
toilStageFiles(toil, outobj, outdir, destBucket=options.destBucket)
|
|
3880
3881
|
|
|
3881
3882
|
if runtime_context.research_obj is not None:
|
|
3882
|
-
|
|
3883
|
+
cwltool.cwlprov.writablebagfile.create_job(
|
|
3884
|
+
runtime_context.research_obj, outobj, True
|
|
3885
|
+
)
|
|
3883
3886
|
|
|
3884
3887
|
def remove_at_id(doc: Any) -> None:
|
|
3885
3888
|
if isinstance(doc, MutableMapping):
|
|
@@ -3906,7 +3909,9 @@ def main(args: Optional[List[str]] = None, stdout: TextIO = sys.stdout) -> int:
|
|
|
3906
3909
|
workflowobj, document_loader, uri
|
|
3907
3910
|
)
|
|
3908
3911
|
runtime_context.research_obj.generate_snapshot(prov_dependencies)
|
|
3909
|
-
|
|
3912
|
+
cwltool.cwlprov.writablebagfile.close_ro(
|
|
3913
|
+
runtime_context.research_obj, options.provenance
|
|
3914
|
+
)
|
|
3910
3915
|
|
|
3911
3916
|
if not options.destBucket and options.compute_checksum:
|
|
3912
3917
|
visit_class(
|
|
@@ -346,7 +346,14 @@ class AbstractFileStore(ABC):
|
|
|
346
346
|
# For each access record
|
|
347
347
|
if len(item) == 2:
|
|
348
348
|
# If it has a name, dump wit the name
|
|
349
|
-
|
|
349
|
+
file_id, dest_path = item
|
|
350
|
+
if os.path.exists(dest_path):
|
|
351
|
+
if os.path.islink(dest_path):
|
|
352
|
+
logger.warning('Symlinked file \'%s\' to path \'%s\'', file_id, dest_path)
|
|
353
|
+
else:
|
|
354
|
+
logger.warning('Downloaded file \'%s\' to path \'%s\'', file_id, dest_path)
|
|
355
|
+
else:
|
|
356
|
+
logger.warning('Downloaded file \'%s\' to path \'%s\' (gone!)', file_id, dest_path)
|
|
350
357
|
else:
|
|
351
358
|
# Otherwise dump without the name
|
|
352
359
|
logger.warning('Streamed file \'%s\'', *item)
|
|
@@ -43,11 +43,10 @@ else:
|
|
|
43
43
|
from typing_extensions import Literal
|
|
44
44
|
|
|
45
45
|
from urllib.parse import ParseResult, urlparse
|
|
46
|
+
from urllib.error import HTTPError
|
|
46
47
|
from urllib.request import urlopen
|
|
47
48
|
from uuid import uuid4
|
|
48
49
|
|
|
49
|
-
from requests.exceptions import HTTPError
|
|
50
|
-
|
|
51
50
|
from toil.common import Config, getNodeID, safeUnpickleFromStream
|
|
52
51
|
from toil.fileStores import FileID
|
|
53
52
|
from toil.job import (CheckpointJobDescription,
|
|
@@ -420,6 +419,8 @@ class AbstractJobStore(ABC):
|
|
|
420
419
|
- 'gs'
|
|
421
420
|
e.g. gs://bucket/file
|
|
422
421
|
|
|
422
|
+
Raises FileNotFoundError if the file does not exist.
|
|
423
|
+
|
|
423
424
|
:param str src_uri: URL that points to a file or object in the storage mechanism of a
|
|
424
425
|
supported URL scheme e.g. a blob in an AWS s3 bucket. It must be a file, not a
|
|
425
426
|
directory or prefix.
|
|
@@ -453,6 +454,8 @@ class AbstractJobStore(ABC):
|
|
|
453
454
|
asks the other job store class for a stream and writes that stream as either a regular or
|
|
454
455
|
a shared file.
|
|
455
456
|
|
|
457
|
+
Raises FileNotFoundError if the file does not exist.
|
|
458
|
+
|
|
456
459
|
:param AbstractJobStore otherCls: The concrete subclass of AbstractJobStore that supports
|
|
457
460
|
reading from the given URL and getting the file size from the URL.
|
|
458
461
|
|
|
@@ -587,6 +590,8 @@ class AbstractJobStore(ABC):
|
|
|
587
590
|
"""
|
|
588
591
|
Read the given URL and write its content into the given writable stream.
|
|
589
592
|
|
|
593
|
+
Raises FileNotFoundError if the URL doesn't exist.
|
|
594
|
+
|
|
590
595
|
:return: The size of the file in bytes and whether the executable permission bit is set
|
|
591
596
|
:rtype: Tuple[int, bool]
|
|
592
597
|
"""
|
|
@@ -618,8 +623,12 @@ class AbstractJobStore(ABC):
|
|
|
618
623
|
Reads the contents of the object at the specified location and writes it to the given
|
|
619
624
|
writable stream.
|
|
620
625
|
|
|
626
|
+
Raises FileNotFoundError if the URL doesn't exist.
|
|
627
|
+
|
|
621
628
|
Refer to :func:`~AbstractJobStore.importFile` documentation for currently supported URL schemes.
|
|
622
629
|
|
|
630
|
+
Raises FileNotFoundError if the thing at the URL is not found.
|
|
631
|
+
|
|
623
632
|
:param ParseResult url: URL that points to a file or object in the storage
|
|
624
633
|
mechanism of a supported URL scheme e.g. a blob in an AWS s3 bucket.
|
|
625
634
|
|
|
@@ -635,7 +644,7 @@ class AbstractJobStore(ABC):
|
|
|
635
644
|
def _write_to_url(cls, readable: Union[IO[bytes], IO[str]], url: ParseResult, executable: bool = False) -> None:
|
|
636
645
|
"""
|
|
637
646
|
Reads the contents of the given readable stream and writes it to the object at the
|
|
638
|
-
specified location.
|
|
647
|
+
specified location. Raises FileNotFoundError if the URL doesn't exist..
|
|
639
648
|
|
|
640
649
|
Refer to AbstractJobStore.importFile documentation for currently supported URL schemes.
|
|
641
650
|
|
|
@@ -1707,20 +1716,28 @@ class JobStoreSupport(AbstractJobStore, metaclass=ABCMeta):
|
|
|
1707
1716
|
# We can only retry on errors that happen as responses to the request.
|
|
1708
1717
|
# If we start getting file data, and the connection drops, we fail.
|
|
1709
1718
|
# So we don't have to worry about writing the start of the file twice.
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1719
|
+
try:
|
|
1720
|
+
with closing(urlopen(url.geturl())) as readable:
|
|
1721
|
+
# Make something to count the bytes we get
|
|
1722
|
+
# We need to put the actual count in a container so our
|
|
1723
|
+
# nested function can modify it without creating its own
|
|
1724
|
+
# local with the same name.
|
|
1725
|
+
size = [0]
|
|
1726
|
+
def count(l: int) -> None:
|
|
1727
|
+
size[0] += l
|
|
1728
|
+
counter = WriteWatchingStream(writable)
|
|
1729
|
+
counter.onWrite(count)
|
|
1730
|
+
|
|
1731
|
+
# Do the download
|
|
1732
|
+
shutil.copyfileobj(readable, counter)
|
|
1733
|
+
return size[0], False
|
|
1734
|
+
except HTTPError as e:
|
|
1735
|
+
if e.code == 404:
|
|
1736
|
+
# Translate into a FileNotFoundError for detecting
|
|
1737
|
+
# un-importable files
|
|
1738
|
+
raise FileNotFoundError(str(url)) from e
|
|
1739
|
+
else:
|
|
1740
|
+
raise
|
|
1724
1741
|
|
|
1725
1742
|
@classmethod
|
|
1726
1743
|
def _get_is_directory(cls, url: ParseResult) -> bool:
|
|
@@ -27,6 +27,7 @@ from google.api_core.exceptions import (GoogleAPICallError,
|
|
|
27
27
|
InternalServerError,
|
|
28
28
|
ServiceUnavailable)
|
|
29
29
|
from google.cloud import exceptions, storage
|
|
30
|
+
from google.auth.exceptions import DefaultCredentialsError
|
|
30
31
|
|
|
31
32
|
from toil.jobStores.abstractJobStore import (AbstractJobStore,
|
|
32
33
|
JobStoreExistsException,
|
|
@@ -112,25 +113,54 @@ class GoogleJobStore(AbstractJobStore):
|
|
|
112
113
|
self.readStatsBaseID = self.statsReadPrefix+self.statsBaseID
|
|
113
114
|
|
|
114
115
|
self.sseKey = None
|
|
116
|
+
self.storageClient = self.create_client()
|
|
115
117
|
|
|
116
|
-
# Determine if we have an override environment variable for our credentials.
|
|
117
|
-
# We don't pull out the filename; we just see if a name is there.
|
|
118
|
-
self.credentialsFromEnvironment = bool(os.getenv('GOOGLE_APPLICATION_CREDENTIALS', False))
|
|
119
118
|
|
|
120
|
-
|
|
119
|
+
@classmethod
|
|
120
|
+
def create_client(cls) -> storage.Client:
|
|
121
|
+
"""
|
|
122
|
+
Produce a client for Google Sotrage with the highest level of access we can get.
|
|
123
|
+
|
|
124
|
+
Fall back to anonymous access if no project is available, unlike the
|
|
125
|
+
Google Storage module's behavior.
|
|
126
|
+
|
|
127
|
+
Warn if GOOGLE_APPLICATION_CREDENTIALS is set but not actually present.
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
# Determine if we have an override environment variable for our credentials.
|
|
131
|
+
# We get the path to check existence, but Google Storage works out what
|
|
132
|
+
# to use later by looking at the environment again.
|
|
133
|
+
credentials_path: Optional[str] = os.getenv('GOOGLE_APPLICATION_CREDENTIALS', None)
|
|
134
|
+
if credentials_path is not None and not os.path.exists(credentials_path):
|
|
121
135
|
# If the file is missing, complain.
|
|
122
136
|
# This variable holds a file name and not any sensitive data itself.
|
|
123
137
|
log.warning("File '%s' from GOOGLE_APPLICATION_CREDENTIALS is unavailable! "
|
|
124
138
|
"We may not be able to authenticate!",
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
139
|
+
credentials_path)
|
|
140
|
+
|
|
141
|
+
if credentials_path is None and os.path.exists(cls.nodeServiceAccountJson):
|
|
142
|
+
try:
|
|
143
|
+
# load credentials from a particular file on GCE nodes if an override path is not set
|
|
144
|
+
return storage.Client.from_service_account_json(cls.nodeServiceAccountJson)
|
|
145
|
+
except OSError:
|
|
146
|
+
# Probably we don't have permission to use the file.
|
|
147
|
+
log.warning("File '%s' exists but didn't work to authenticate!",
|
|
148
|
+
cls.nodeServiceAccountJson)
|
|
149
|
+
pass
|
|
150
|
+
|
|
151
|
+
# Either a filename is specified, or our fallback file isn't there.
|
|
152
|
+
try:
|
|
132
153
|
# See if Google can work out how to authenticate.
|
|
133
|
-
|
|
154
|
+
return storage.Client()
|
|
155
|
+
except (DefaultCredentialsError, EnvironmentError):
|
|
156
|
+
# Depending on which Google codepath or module version (???)
|
|
157
|
+
# realizes we have no credentials, we can get an EnvironemntError,
|
|
158
|
+
# or the new DefaultCredentialsError we are supposedly specced to
|
|
159
|
+
# get.
|
|
160
|
+
|
|
161
|
+
# Google can't find credentials, fall back to being anonymous.
|
|
162
|
+
# This is likely to happen all the time so don't warn.
|
|
163
|
+
return storage.Client.create_anonymous_client()
|
|
134
164
|
|
|
135
165
|
|
|
136
166
|
@google_retry
|
|
@@ -244,10 +274,11 @@ class GoogleJobStore(AbstractJobStore):
|
|
|
244
274
|
|
|
245
275
|
env = {}
|
|
246
276
|
|
|
247
|
-
|
|
277
|
+
credentials_path: Optional[str] = os.getenv('GOOGLE_APPLICATION_CREDENTIALS', None)
|
|
278
|
+
if credentials_path is not None:
|
|
248
279
|
# Send along the environment variable that points to the credentials file.
|
|
249
280
|
# It must be available in the same place on all nodes.
|
|
250
|
-
env['GOOGLE_APPLICATION_CREDENTIALS'] =
|
|
281
|
+
env['GOOGLE_APPLICATION_CREDENTIALS'] = credentials_path
|
|
251
282
|
|
|
252
283
|
return env
|
|
253
284
|
|
|
@@ -351,8 +382,8 @@ class GoogleJobStore(AbstractJobStore):
|
|
|
351
382
|
if fileName.startswith('/'):
|
|
352
383
|
fileName = fileName[1:]
|
|
353
384
|
|
|
354
|
-
storageClient =
|
|
355
|
-
bucket = storageClient.
|
|
385
|
+
storageClient = cls.create_client()
|
|
386
|
+
bucket = storageClient.bucket(bucket_name=bucketName)
|
|
356
387
|
blob = bucket.blob(compat_bytes(fileName))
|
|
357
388
|
|
|
358
389
|
if exists:
|
|
@@ -14,8 +14,9 @@
|
|
|
14
14
|
|
|
15
15
|
"""Accelerator (i.e. GPU) utilities for Toil"""
|
|
16
16
|
|
|
17
|
+
import os
|
|
17
18
|
import subprocess
|
|
18
|
-
from typing import Dict, List, Optional, Set
|
|
19
|
+
from typing import Dict, List, Optional, Set, Union
|
|
19
20
|
from xml.dom import minidom
|
|
20
21
|
|
|
21
22
|
from toil.job import AcceleratorRequirement
|
|
@@ -37,6 +38,37 @@ def have_working_nvidia_smi() -> bool:
|
|
|
37
38
|
return False
|
|
38
39
|
return True
|
|
39
40
|
|
|
41
|
+
@memoize
|
|
42
|
+
def get_host_accelerator_numbers() -> List[int]:
|
|
43
|
+
"""
|
|
44
|
+
Work out what accelerator is what.
|
|
45
|
+
|
|
46
|
+
For each accelerator visible to us, returns the host-side (for example,
|
|
47
|
+
outside-of-Slurm-job) number for that accelerator. It is often the same as
|
|
48
|
+
the apparent number.
|
|
49
|
+
|
|
50
|
+
Can be used with Docker's --gpus='"device=#,#,#"' option to forward the
|
|
51
|
+
right GPUs as seen from a Docker daemon.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
for number_list_var in ['SLURM_STEP_GPUS', 'SLURM_JOB_GPUS', 'CUDA_VISIBLE_DEVICES', 'NVIDIA_VISIBLE_DEVICES']:
|
|
55
|
+
# Any of these can have a list of GPU numbers, but the CUDA/NVIDIA ones
|
|
56
|
+
# also support a system of GPU GUIDs that we don't support.
|
|
57
|
+
# TODO: If Slurm confinement is set we ignore any attempt to further
|
|
58
|
+
# limit us with the app-level variables. Does that make sense? Writing
|
|
59
|
+
# code to translate through would be hard and probably not actually
|
|
60
|
+
# useful.
|
|
61
|
+
if number_list_var in os.environ:
|
|
62
|
+
device_string = os.environ[number_list_var]
|
|
63
|
+
# Parse all the numbers we have
|
|
64
|
+
device_numbers = [int(part) for part in device_string.split(',') if part.isnumeric()]
|
|
65
|
+
if len(device_numbers) > 0:
|
|
66
|
+
# We found some numbers, so use those
|
|
67
|
+
return device_numbers
|
|
68
|
+
|
|
69
|
+
# If we don't see a set of limits we understand, say we have all nvidia GPUs
|
|
70
|
+
return list(range(count_nvidia_gpus()))
|
|
71
|
+
|
|
40
72
|
@memoize
|
|
41
73
|
def have_working_nvidia_docker_runtime() -> bool:
|
|
42
74
|
"""
|
|
@@ -45,7 +77,7 @@ def have_working_nvidia_docker_runtime() -> bool:
|
|
|
45
77
|
try:
|
|
46
78
|
# The runtime injects nvidia-smi; it doesn't seem to have to be in the image we use here
|
|
47
79
|
subprocess.check_call(['docker', 'run', '--rm', '--runtime', 'nvidia', '--gpus', 'all', 'ubuntu:20.04', 'nvidia-smi'])
|
|
48
|
-
except subprocess.CalledProcessError:
|
|
80
|
+
except (FileNotFoundError, subprocess.CalledProcessError):
|
|
49
81
|
return False
|
|
50
82
|
return True
|
|
51
83
|
|
|
@@ -83,7 +115,7 @@ def get_individual_local_accelerators() -> List[AcceleratorRequirement]:
|
|
|
83
115
|
# For now we only know abput nvidia GPUs
|
|
84
116
|
return [{'kind': 'gpu', 'brand': 'nvidia', 'api': 'cuda', 'count': 1} for _ in range(count_nvidia_gpus())]
|
|
85
117
|
|
|
86
|
-
def get_restrictive_environment_for_local_accelerators(accelerator_numbers : Set[int]) -> Dict[str, str]:
|
|
118
|
+
def get_restrictive_environment_for_local_accelerators(accelerator_numbers : Union[Set[int], List[int]]) -> Dict[str, str]:
|
|
87
119
|
"""
|
|
88
120
|
Get environment variables which can be applied to a process to restrict it
|
|
89
121
|
to using only the given accelerator numbers.
|
|
@@ -3,7 +3,7 @@ import json
|
|
|
3
3
|
import logging
|
|
4
4
|
from collections import defaultdict
|
|
5
5
|
from functools import lru_cache
|
|
6
|
-
from typing import Any, Dict, List, Optional, Set, cast
|
|
6
|
+
from typing import Any, Dict, List, Optional, Set, cast, Union, Sequence
|
|
7
7
|
|
|
8
8
|
import boto3
|
|
9
9
|
from mypy_boto3_iam import IAMClient
|
|
@@ -80,8 +80,6 @@ def add_to_action_collection(a: AllowedActionCollection, b: AllowedActionCollect
|
|
|
80
80
|
return to_return
|
|
81
81
|
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
85
83
|
def policy_permissions_allow(given_permissions: AllowedActionCollection, required_permissions: List[str] = []) -> bool:
|
|
86
84
|
"""
|
|
87
85
|
Check whether given set of actions are a subset of another given set of actions, returns true if they are
|
|
@@ -189,7 +187,23 @@ def allowed_actions_roles(iam: IAMClient, policy_names: List[str], role_name: st
|
|
|
189
187
|
|
|
190
188
|
return allowed_actions
|
|
191
189
|
|
|
192
|
-
|
|
190
|
+
|
|
191
|
+
def collect_policy_actions(policy_documents: Sequence[Union[str, Dict[str, Any]]]) -> AllowedActionCollection:
|
|
192
|
+
"""
|
|
193
|
+
Collect all of the actions allowed by the given policy documents into one AllowedActionCollection.
|
|
194
|
+
"""
|
|
195
|
+
allowed_actions: AllowedActionCollection = init_action_collection()
|
|
196
|
+
for policy_str in policy_documents:
|
|
197
|
+
# sometimes a string is returned from the api, so convert to a dictionary
|
|
198
|
+
if isinstance(policy_str, dict):
|
|
199
|
+
policy_dict = policy_str
|
|
200
|
+
else:
|
|
201
|
+
policy_dict = json.loads(policy_str)
|
|
202
|
+
allowed_actions = add_to_action_collection(allowed_actions, get_actions_from_policy_document(policy_dict))
|
|
203
|
+
return allowed_actions
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def allowed_actions_user(iam: IAMClient, policy_names: List[str], user_name: str) -> AllowedActionCollection:
|
|
193
207
|
"""
|
|
194
208
|
Gets all allowed actions for a user given by user_name, returns a dictionary, keyed by resource,
|
|
195
209
|
with a list of permissions allowed for each given resource.
|
|
@@ -198,17 +212,34 @@ def allowed_actions_users(iam: IAMClient, policy_names: List[str], user_name: st
|
|
|
198
212
|
:param policy_names: Name of policy document associated with a user
|
|
199
213
|
:param user_name: Name of user to get associated policies
|
|
200
214
|
"""
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
for policy_name in policy_names:
|
|
204
|
-
user_policy = iam.get_user_policy(
|
|
215
|
+
user_policies = [
|
|
216
|
+
iam.get_user_policy(
|
|
205
217
|
UserName=user_name,
|
|
206
218
|
PolicyName=policy_name
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
|
|
219
|
+
)["PolicyDocument"]
|
|
220
|
+
for policy_name in policy_names
|
|
221
|
+
]
|
|
222
|
+
return collect_policy_actions(user_policies)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def allowed_actions_group(iam: IAMClient, policy_names: List[str], group_name: str) -> AllowedActionCollection:
|
|
226
|
+
"""
|
|
227
|
+
Gets all allowed actions for a group given by group_name, returns a dictionary, keyed by resource,
|
|
228
|
+
with a list of permissions allowed for each given resource.
|
|
229
|
+
|
|
230
|
+
:param iam: IAM client to use
|
|
231
|
+
:param policy_names: Name of policy document associated with a user
|
|
232
|
+
:param group_name: Name of group to get associated policies
|
|
233
|
+
"""
|
|
234
|
+
group_policies = [
|
|
235
|
+
iam.get_group_policy(
|
|
236
|
+
GroupName=group_name,
|
|
237
|
+
PolicyName=policy_name
|
|
238
|
+
)["PolicyDocument"]
|
|
239
|
+
for policy_name in policy_names
|
|
240
|
+
]
|
|
241
|
+
return collect_policy_actions(group_policies)
|
|
210
242
|
|
|
211
|
-
return allowed_actions
|
|
212
243
|
|
|
213
244
|
def get_policy_permissions(region: str) -> AllowedActionCollection:
|
|
214
245
|
"""
|
|
@@ -229,9 +260,19 @@ def get_policy_permissions(region: str) -> AllowedActionCollection:
|
|
|
229
260
|
attached_policies = iam.list_attached_user_policies(UserName=user['User']['UserName'])
|
|
230
261
|
user_attached_policies = allowed_actions_attached(iam, attached_policies['AttachedPolicies'])
|
|
231
262
|
allowed_actions = add_to_action_collection(allowed_actions, user_attached_policies)
|
|
232
|
-
user_inline_policies =
|
|
263
|
+
user_inline_policies = allowed_actions_user(iam, list_policies['PolicyNames'], user['User']['UserName'])
|
|
233
264
|
allowed_actions = add_to_action_collection(allowed_actions, user_inline_policies)
|
|
234
265
|
|
|
266
|
+
# grab group policies associated with the user
|
|
267
|
+
groups = iam.list_groups_for_user(UserName=user['User']['UserName'])
|
|
268
|
+
for group in groups["Groups"]:
|
|
269
|
+
list_policies = iam.list_group_policies(GroupName=group['GroupName'])
|
|
270
|
+
attached_policies = iam.list_attached_group_policies(GroupName=group['GroupName'])
|
|
271
|
+
group_attached_policies = allowed_actions_attached(iam, attached_policies['AttachedPolicies'])
|
|
272
|
+
allowed_actions = add_to_action_collection(allowed_actions, group_attached_policies)
|
|
273
|
+
group_inline_policies = allowed_actions_group(iam, list_policies['PolicyNames'], group['GroupName'])
|
|
274
|
+
allowed_actions = add_to_action_collection(allowed_actions, group_inline_policies)
|
|
275
|
+
|
|
235
276
|
except:
|
|
236
277
|
# If not successful, we check the role associated with an instance profile
|
|
237
278
|
# and grab the role's associated permissions
|