girder-worker 5.0.11.dev22__tar.gz → 5.0.11.dev33__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.
Files changed (99) hide show
  1. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/PKG-INFO +1 -1
  2. girder_worker-5.0.11.dev33/girder_plugin_worker/__init__.py +38 -0
  3. girder_worker-5.0.11.dev33/girder_plugin_worker/api/worker.py +40 -0
  4. girder_worker-5.0.11.dev33/girder_plugin_worker/constants.py +11 -0
  5. girder_worker-5.0.11.dev33/girder_plugin_worker/event_handlers.py +134 -0
  6. girder_worker-5.0.11.dev33/girder_plugin_worker/status.py +67 -0
  7. girder_worker-5.0.11.dev33/girder_plugin_worker/utils.py +155 -0
  8. girder_worker-5.0.11.dev33/girder_plugin_worker/web_client/JobStatus.js +50 -0
  9. girder_worker-5.0.11.dev33/girder_plugin_worker/web_client/main.js +4 -0
  10. girder_worker-5.0.11.dev33/girder_plugin_worker/web_client/package-lock.json +2989 -0
  11. girder_worker-5.0.11.dev33/girder_plugin_worker/web_client/package.json +28 -0
  12. girder_worker-5.0.11.dev33/girder_plugin_worker/web_client/routes.js +16 -0
  13. girder_worker-5.0.11.dev33/girder_plugin_worker/web_client/stylesheets/jobDetailsWidget.styl +9 -0
  14. girder_worker-5.0.11.dev33/girder_plugin_worker/web_client/stylesheets/taskStatusView.styl +65 -0
  15. girder_worker-5.0.11.dev33/girder_plugin_worker/web_client/templates/configView.pug +20 -0
  16. girder_worker-5.0.11.dev33/girder_plugin_worker/web_client/templates/taskStatusView.pug +102 -0
  17. girder_worker-5.0.11.dev33/girder_plugin_worker/web_client/tsconfig.json +13 -0
  18. girder_worker-5.0.11.dev33/girder_plugin_worker/web_client/views/ConfigView.js +84 -0
  19. girder_worker-5.0.11.dev33/girder_plugin_worker/web_client/views/taskStatusView.js +106 -0
  20. girder_worker-5.0.11.dev33/girder_plugin_worker/web_client/vite-env.d.ts +6 -0
  21. girder_worker-5.0.11.dev33/girder_plugin_worker/web_client/vite.config.ts +42 -0
  22. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker.egg-info/PKG-INFO +1 -1
  23. girder_worker-5.0.11.dev33/girder_worker.egg-info/SOURCES.txt +1242 -0
  24. girder_worker-5.0.11.dev33/girder_worker.egg-info/scm_file_list.json +1178 -0
  25. girder_worker-5.0.11.dev33/girder_worker.egg-info/scm_version.json +8 -0
  26. girder_worker-5.0.11.dev33/girder_worker.egg-info/top_level.txt +2 -0
  27. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/setup.py +5 -26
  28. girder_worker-5.0.11.dev22/girder_worker/.gitignore +0 -1
  29. girder_worker-5.0.11.dev22/girder_worker/utils/tests/fake_dir/file2.txt +0 -0
  30. girder_worker-5.0.11.dev22/girder_worker/utils/tests/fake_dir/subdir/file3.txt +0 -0
  31. girder_worker-5.0.11.dev22/girder_worker.egg-info/SOURCES.txt +0 -73
  32. girder_worker-5.0.11.dev22/girder_worker.egg-info/top_level.txt +0 -1
  33. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/MANIFEST.in +0 -0
  34. {girder_worker-5.0.11.dev22/girder_worker/_test_plugins → girder_worker-5.0.11.dev33/girder_plugin_worker/api}/__init__.py +0 -0
  35. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/__init__.py +0 -0
  36. {girder_worker-5.0.11.dev22/girder_worker/utils/tests → girder_worker-5.0.11.dev33/girder_worker/_test_plugins}/__init__.py +0 -0
  37. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/_test_plugins/plugins.py +0 -0
  38. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/_test_plugins/tasks.py +0 -0
  39. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/app.py +0 -0
  40. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/celeryconfig.py +0 -0
  41. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/context/__init__.py +0 -0
  42. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/context/girder_context.py +0 -0
  43. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/context/nongirder_context.py +0 -0
  44. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/docker/__init__.py +0 -0
  45. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/docker/io/__init__.py +0 -0
  46. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/docker/io/girder.py +0 -0
  47. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/docker/nvidia.py +0 -0
  48. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/docker/stream_adapter.py +0 -0
  49. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/docker/tasks/__init__.py +0 -0
  50. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/docker/transforms/__init__.py +0 -0
  51. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/docker/transforms/girder.py +0 -0
  52. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/docker/utils.py +0 -0
  53. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/entrypoint.py +0 -0
  54. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/log_utils.py +0 -0
  55. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/task.py +0 -0
  56. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/__init__.py +0 -0
  57. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/decorators.py +0 -0
  58. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/tee.py +0 -0
  59. {girder_worker-5.0.11.dev22/girder_worker/utils/tests/contrib → girder_worker-5.0.11.dev33/girder_worker/utils/tests}/__init__.py +0 -0
  60. {girder_worker-5.0.11.dev22/girder_worker/utils/transforms → girder_worker-5.0.11.dev33/girder_worker/utils/tests/contrib}/__init__.py +0 -0
  61. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/tests/contrib/girder_io_test.py +0 -0
  62. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/tests/decorators_test.py +0 -0
  63. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/tests/girder_io_test.py +0 -0
  64. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/tests/tee_test.py +0 -0
  65. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/tests/transform_test.py +0 -0
  66. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/tests/types_test.py +0 -0
  67. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/transform.py +0 -0
  68. {girder_worker-5.0.11.dev22/girder_worker/utils/transforms/contrib → girder_worker-5.0.11.dev33/girder_worker/utils/transforms}/__init__.py +0 -0
  69. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/transforms/common.py +0 -0
  70. /girder_worker-5.0.11.dev22/girder_worker/utils/tests/fake_dir/file1.txt → /girder_worker-5.0.11.dev33/girder_worker/utils/transforms/contrib/__init__.py +0 -0
  71. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/transforms/contrib/girder_io.py +0 -0
  72. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/transforms/girder_io.py +0 -0
  73. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/types/__init__.py +0 -0
  74. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/types/base.py +0 -0
  75. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/types/boolean.py +0 -0
  76. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/types/choice.py +0 -0
  77. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/types/color.py +0 -0
  78. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/types/girder.py +0 -0
  79. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/types/girder_folder.py +0 -0
  80. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/types/girder_item.py +0 -0
  81. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/types/integer.py +0 -0
  82. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/types/number.py +0 -0
  83. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/types/number_choice.py +0 -0
  84. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/types/number_multichoice.py +0 -0
  85. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/types/number_vector.py +0 -0
  86. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/types/range.py +0 -0
  87. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/types/string.py +0 -0
  88. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/types/string_choice.py +0 -0
  89. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/types/string_multichoice.py +0 -0
  90. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/types/string_vector.py +0 -0
  91. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/utils/types/vector.py +0 -0
  92. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker/worker.dist.cfg +0 -0
  93. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker.egg-info/dependency_links.txt +0 -0
  94. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker.egg-info/entry_points.txt +0 -0
  95. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker.egg-info/not-zip-safe +0 -0
  96. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/girder_worker.egg-info/requires.txt +0 -0
  97. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/pyproject.toml +0 -0
  98. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/requirements.in +0 -0
  99. {girder_worker-5.0.11.dev22 → girder_worker-5.0.11.dev33}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: girder-worker
3
- Version: 5.0.11.dev22
3
+ Version: 5.0.11.dev33
4
4
  Summary: Batch execution engine built on celery.
5
5
  Home-page: https://github.com/girder/girder_worker
6
6
  Author: Kitware, Inc.
@@ -0,0 +1,38 @@
1
+ import logging
2
+ from pathlib import Path
3
+
4
+ from girder_jobs.models.job import Job
5
+
6
+ from girder import events
7
+ from girder.constants import AccessType
8
+ from girder.plugin import GirderPlugin, getPlugin, registerPluginStaticContent
9
+
10
+ from . import event_handlers
11
+ from .api.worker import Worker
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class WorkerPlugin(GirderPlugin):
17
+ DISPLAY_NAME = 'Worker'
18
+
19
+ def load(self, info):
20
+ getPlugin('jobs').load(info)
21
+
22
+ info['apiRoot'].worker = Worker()
23
+
24
+ registerPluginStaticContent(
25
+ plugin='worker',
26
+ js=['/girder-plugin-worker.umd.cjs'],
27
+ css=['/style.css'],
28
+ staticDir=Path(__file__).parent / 'web_client' / 'dist',
29
+ tree=info['serverRoot'],
30
+ )
31
+
32
+ events.bind('jobs.schedule', 'worker', event_handlers.schedule)
33
+ events.bind('jobs.status.validate', 'worker', event_handlers.validateJobStatus)
34
+ events.bind('jobs.status.validTransitions', 'worker', event_handlers.validTransitions)
35
+ events.bind('jobs.cancel', 'worker', event_handlers.cancel)
36
+ events.bind('model.job.save.after', 'worker', event_handlers.attachJobInfoSpec)
37
+ events.bind('model.job.save', 'worker', event_handlers.attachParentJob)
38
+ Job().exposeFields(AccessType.SITE_ADMIN, {'celeryTaskId', 'celeryQueue'})
@@ -0,0 +1,40 @@
1
+ import logging
2
+
3
+ import celery
4
+ from girder_worker.app import app
5
+
6
+ from girder.api import access
7
+ from girder.api.describe import Description, autoDescribeRoute
8
+ from girder.api.rest import Resource
9
+ from girder.constants import TokenScope
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ class Worker(Resource):
15
+ def __init__(self):
16
+ super().__init__()
17
+ self.resourceName = 'worker'
18
+ self.route('GET', ('status',), self.getWorkerStatus)
19
+
20
+ @autoDescribeRoute(
21
+ Description('Get worker status and task information.')
22
+ .notes('Return -1 if the broker is inaccessible.')
23
+ )
24
+ @access.user(scope=TokenScope.DATA_READ)
25
+ def getWorkerStatus(self):
26
+ result = {}
27
+ conn = app.connection_for_read()
28
+ try:
29
+ conn.ensure_connection(max_retries=1)
30
+ except celery.exceptions.OperationalError:
31
+ logger.exception(f'Broker ({app.conf.broker_url}) is inaccessible.')
32
+ return -1
33
+
34
+ status = app.control.inspect()
35
+ result['report'] = status.report()
36
+ result['stats'] = status.stats()
37
+ result['ping'] = status.ping()
38
+ result['active'] = status.active() or {}
39
+ result['reserved'] = status.reserved() or {}
40
+ return result
@@ -0,0 +1,11 @@
1
+ # The path that will be mounted in docker containers for data IO
2
+ DOCKER_DATA_VOLUME = '/mnt/girder_worker/data'
3
+
4
+ # The path that will be mounted in docker containers for utility scripts
5
+ DOCKER_SCRIPTS_VOLUME = '/mnt/girder_worker/scripts'
6
+
7
+
8
+ # Settings where plugin information is stored
9
+ class PluginSettings:
10
+ API_URL = 'worker.api_url'
11
+ DIRECT_PATH = 'worker.direct_path'
@@ -0,0 +1,134 @@
1
+ import logging
2
+
3
+ from celery.result import AsyncResult
4
+ from girder_jobs.constants import JobStatus
5
+ from girder_jobs.models.job import Job
6
+ from girder_worker.app import app
7
+
8
+ from girder.exceptions import ValidationException
9
+ from girder.utility import setting_utilities
10
+
11
+ from .constants import PluginSettings
12
+ from .status import CustomJobStatus
13
+ from .utils import getWorkerApiUrl, jobInfoSpec
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ @setting_utilities.validator({
19
+ PluginSettings.API_URL
20
+ })
21
+ def validateApiUrl(doc):
22
+ val = doc['value']
23
+ if val and not val.startswith('http://') and not val.startswith('https://'):
24
+ raise ValidationException('API URL must start with http:// or https://.', 'value')
25
+
26
+
27
+ @setting_utilities.validator(PluginSettings.DIRECT_PATH)
28
+ def _validateAutoCompute(doc):
29
+ if not isinstance(doc['value'], bool):
30
+ raise ValidationException('The direct path setting must be true or false.')
31
+
32
+
33
+ def validateJobStatus(event):
34
+ """Allow our custom job status values."""
35
+ if CustomJobStatus.isValid(event.info):
36
+ event.preventDefault().addResponse(True)
37
+
38
+
39
+ def validTransitions(event):
40
+ """Allow our custom job transitions."""
41
+ states = None
42
+ if event.info['job']['handler'] == 'worker_handler':
43
+ states = CustomJobStatus.validTransitionsWorker(event.info['status'])
44
+ elif event.info['job']['handler'] == 'celery_handler':
45
+ states = CustomJobStatus.validTransitionsCelery(event.info['status'])
46
+ if states is not None:
47
+ event.preventDefault().addResponse(states)
48
+
49
+
50
+ def schedule(event):
51
+ """
52
+ This is bound to the "jobs.schedule" event, and will be triggered any time
53
+ a job is scheduled. This handler will process any job that has the
54
+ handler field set to "worker_handler".
55
+ """
56
+ job = event.info
57
+ if job['handler'] == 'worker_handler':
58
+ task = job.get('celeryTaskName', 'girder_worker.run')
59
+ queue_name = job.get('celeryQueue')
60
+
61
+ if queue_name == 'local':
62
+ from girder.tasks import ensure_local_worker_available
63
+ ensure_local_worker_available()
64
+ # Set the job status to queued
65
+ Job().updateJob(job, status=JobStatus.QUEUED)
66
+
67
+ # Send the task to celery
68
+ asyncResult = app.send_task(
69
+ task, job['args'], job['kwargs'], queue=queue_name, headers={
70
+ 'jobInfoSpec': jobInfoSpec(job, job.get('token', None)),
71
+ 'apiUrl': getWorkerApiUrl()
72
+ })
73
+
74
+ # Record the task ID from celery.
75
+ Job().updateJob(job, otherFields={
76
+ 'celeryTaskId': asyncResult.task_id
77
+ })
78
+
79
+ # Stop event propagation since we have taken care of scheduling.
80
+ event.stopPropagation()
81
+
82
+
83
+ def cancel(event):
84
+ """
85
+ This is bound to the "jobs.cancel" event, and will be triggered any time
86
+ a job is canceled. This handler will process any job that has the
87
+ handler field set to "worker_handler".
88
+ """
89
+ job = event.info
90
+ if job['handler'] in ['worker_handler', 'celery_handler']:
91
+ # Stop event propagation and prevent default, we are using a custom state
92
+ event.stopPropagation().preventDefault()
93
+
94
+ celeryTaskId = job.get('celeryTaskId')
95
+
96
+ if celeryTaskId is None:
97
+ msg = ("Unable to cancel Celery task. Job '%s' doesn't have a Celery task id."
98
+ % job['_id'])
99
+ logger.warning(msg)
100
+ return
101
+
102
+ should_revoke = False
103
+ if job['status'] == JobStatus.INACTIVE:
104
+ # Move inactive jobs directly to canceled state
105
+ Job().updateJob(job, status=JobStatus.CANCELED)
106
+ should_revoke = True
107
+
108
+ elif job['status'] not in [CustomJobStatus.CANCELING, JobStatus.CANCELED,
109
+ JobStatus.SUCCESS, JobStatus.ERROR]:
110
+ # Give active jobs a chance to be canceled by their runner
111
+ Job().updateJob(job, status=CustomJobStatus.CANCELING)
112
+ should_revoke = True
113
+
114
+ if should_revoke:
115
+ # Send the revoke request.
116
+ asyncResult = AsyncResult(celeryTaskId, app=app)
117
+ asyncResult.revoke()
118
+
119
+
120
+ def attachParentJob(event):
121
+ """Attach parentJob before a model is saved."""
122
+ job = event.info
123
+ if job.get('celeryParentTaskId'):
124
+ celeryParentTaskId = job['celeryParentTaskId']
125
+ parentJob = Job().findOne({'celeryTaskId': celeryParentTaskId})
126
+ event.info['parentId'] = parentJob['_id']
127
+
128
+
129
+ def attachJobInfoSpec(event):
130
+ """Attach jobInfoSpec after a model is saved."""
131
+ job = event.info
132
+ # Local jobs have a module key
133
+ if not job.get('module'):
134
+ Job().updateJob(job, otherFields={'jobInfoSpec': jobInfoSpec(job)})
@@ -0,0 +1,67 @@
1
+ from girder_jobs.constants import JobStatus
2
+
3
+
4
+ class CustomJobStatus:
5
+ """The custom job status flags for the worker."""
6
+
7
+ FETCHING_INPUT = 820
8
+ CONVERTING_INPUT = 821
9
+ CONVERTING_OUTPUT = 822
10
+ PUSHING_OUTPUT = 823
11
+ CANCELING = 824
12
+
13
+ # valid transitions for worker scheduled jobs
14
+ valid_worker_transitions = {
15
+ JobStatus.QUEUED: [JobStatus.INACTIVE],
16
+ JobStatus.RUNNING: [JobStatus.QUEUED, FETCHING_INPUT],
17
+ FETCHING_INPUT: [JobStatus.RUNNING],
18
+ CONVERTING_INPUT: [JobStatus.RUNNING, FETCHING_INPUT],
19
+ CONVERTING_OUTPUT: [JobStatus.RUNNING],
20
+ PUSHING_OUTPUT: [JobStatus.RUNNING, CONVERTING_OUTPUT],
21
+ CANCELING: [JobStatus.INACTIVE, JobStatus.QUEUED, JobStatus.RUNNING],
22
+ JobStatus.ERROR: [FETCHING_INPUT, CONVERTING_INPUT, CONVERTING_OUTPUT,
23
+ PUSHING_OUTPUT, CANCELING, JobStatus.QUEUED,
24
+ JobStatus.RUNNING],
25
+ # The last two are allowed for revoke called from outside Girder
26
+ JobStatus.CANCELED: [CANCELING, JobStatus.QUEUED, JobStatus.RUNNING],
27
+ JobStatus.SUCCESS: [JobStatus.RUNNING, PUSHING_OUTPUT]
28
+ }
29
+
30
+ # valid transitions for celery scheduled jobs
31
+ # N.B. We have the extra worker input/output states defined here for when
32
+ # we are running girder_worker.run as a regular celery task
33
+ valid_celery_transitions = {
34
+ JobStatus.QUEUED: [JobStatus.INACTIVE],
35
+ # Note celery tasks can jump straight from INACTIVE to RUNNING
36
+ JobStatus.RUNNING: [JobStatus.INACTIVE, JobStatus.QUEUED,
37
+ FETCHING_INPUT],
38
+ FETCHING_INPUT: [JobStatus.RUNNING],
39
+ CONVERTING_INPUT: [JobStatus.RUNNING, FETCHING_INPUT],
40
+ CONVERTING_OUTPUT: [JobStatus.RUNNING],
41
+ PUSHING_OUTPUT: [JobStatus.RUNNING, CONVERTING_OUTPUT],
42
+ CANCELING: [JobStatus.INACTIVE, JobStatus.QUEUED, JobStatus.RUNNING],
43
+ JobStatus.ERROR: [FETCHING_INPUT, CONVERTING_INPUT, CONVERTING_OUTPUT,
44
+ PUSHING_OUTPUT, CANCELING, JobStatus.QUEUED,
45
+ JobStatus.RUNNING],
46
+ JobStatus.CANCELED: [CANCELING, JobStatus.INACTIVE, JobStatus.QUEUED,
47
+ JobStatus.RUNNING],
48
+ JobStatus.SUCCESS: [JobStatus.RUNNING, PUSHING_OUTPUT]
49
+ }
50
+
51
+ @classmethod
52
+ def isValid(cls, status):
53
+ return status in (
54
+ cls.FETCHING_INPUT,
55
+ cls.CONVERTING_INPUT,
56
+ cls.CONVERTING_OUTPUT,
57
+ cls.PUSHING_OUTPUT,
58
+ cls.CANCELING
59
+ )
60
+
61
+ @classmethod
62
+ def validTransitionsWorker(cls, status):
63
+ return cls.valid_worker_transitions.get(status)
64
+
65
+ @classmethod
66
+ def validTransitionsCelery(cls, status):
67
+ return cls.valid_celery_transitions.get(status)
@@ -0,0 +1,155 @@
1
+ ###############################################################################
2
+ # Copyright Kitware Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 ( the "License" );
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ ###############################################################################
16
+
17
+ from girder_jobs.models.job import Job
18
+
19
+ from girder.api.rest import getApiUrl
20
+ from girder.exceptions import FilePathException
21
+ from girder.models.file import File
22
+ from girder.models.setting import Setting
23
+
24
+ from .constants import PluginSettings
25
+
26
+
27
+ def getWorkerApiUrl():
28
+ """
29
+ Return the API base URL to which the worker should callback to
30
+ write output information back to the server. This is controlled
31
+ via a system setting, and the default is to use the core server
32
+ root setting.
33
+ """
34
+ apiUrl = Setting().get(PluginSettings.API_URL)
35
+ return apiUrl or getApiUrl()
36
+
37
+
38
+ def girderInputSpec(resource, resourceType='file', name=None, token=None,
39
+ dataType='string', dataFormat='text', fetchParent=False):
40
+ """
41
+ Downstream plugins that are building Girder worker jobs that use Girder IO
42
+ should use this to generate the input specs more easily.
43
+
44
+ :param resource: The resource document to be downloaded at runtime.
45
+ :type resource: dict
46
+ :param resourceType: The resource type to download for the input. Should
47
+ be "folder", "item", or "file".
48
+ :type resourceType: str
49
+ :param name: The name of the resource to download. If not passed, uses
50
+ the "name" field of the resource document.
51
+ :type name: str or None
52
+ :param token: The Girder token document or raw token string to use to
53
+ authenticate when downloading. Pass `None` for anonymous downloads.
54
+ :type token: dict, str, or None
55
+ :param dataType: The worker `type` field.
56
+ :type dataType: str
57
+ :param dataFormat: The worker `format` field.
58
+ :type dataFormat: str
59
+ :param fetchParent: Whether to fetch the whole parent resource of the
60
+ specified resource as a side effect.
61
+ :type fetchParent: bool
62
+ """
63
+ if isinstance(token, dict):
64
+ token = token['_id']
65
+
66
+ result = {
67
+ 'mode': 'girder',
68
+ 'api_url': getWorkerApiUrl(),
69
+ 'token': token,
70
+ 'id': str(resource['_id']),
71
+ 'name': name or resource['name'],
72
+ 'resource_type': resourceType,
73
+ 'type': dataType,
74
+ 'format': dataFormat,
75
+ 'fetch_parent': fetchParent
76
+ }
77
+
78
+ if resourceType == 'file' and not fetchParent and Setting().get(PluginSettings.DIRECT_PATH):
79
+ # If we are adding a file and it exists on the local filesystem include
80
+ # that location. This can permit the user of the specification to
81
+ # access the file directly instead of downloading the file.
82
+ try:
83
+ result['direct_path'] = File().getLocalFilePath(resource)
84
+ except FilePathException:
85
+ pass
86
+ return result
87
+
88
+
89
+ def girderOutputSpec(parent, token, parentType='folder', name=None,
90
+ dataType='string', dataFormat='text', reference=None):
91
+ """
92
+ Downstream plugins that are building worker jobs that use Girder IO
93
+ should use this to generate the output specs more easily.
94
+
95
+ :param parent: The parent to upload the data into (an item or folder).
96
+ :type parent: dict
97
+ :param token: The Girder token document or raw token string to use to
98
+ authenticate when uploading.
99
+ :type token: dict or str
100
+ :param parentType: The type of the parent object ("item" or "folder").
101
+ :type parentType: str
102
+ :param name: Name of the resource to use when uploading. Required if
103
+ the output target type is "memory". If the target is "filepath", uses
104
+ the basename of the file being uploaded by default.
105
+ :type name: str or None
106
+ :param dataType: The worker `type` field.
107
+ :type dataType: str
108
+ :param dataFormat: The worker `format` field.
109
+ :type dataFormat: str
110
+ :param reference: Optional "reference" string to pass back to the server
111
+ during the upload. This can be used to attach arbitrary data to this
112
+ for tracking purposes, e.g., referring back to related inputs. Bind to
113
+ the "data.process" event to hook into the upload and inspect references.
114
+ :type reference: str
115
+ """
116
+ if isinstance(token, dict):
117
+ token = token['_id']
118
+
119
+ return {
120
+ 'mode': 'girder',
121
+ 'api_url': getWorkerApiUrl(),
122
+ 'token': token,
123
+ 'name': name,
124
+ 'parent_id': str(parent['_id']),
125
+ 'parent_type': parentType,
126
+ 'type': dataType,
127
+ 'format': dataFormat,
128
+ 'reference': reference
129
+ }
130
+
131
+
132
+ def jobInfoSpec(job, token=None, logPrint=True):
133
+ """
134
+ Build the jobInfo specification for a task to write status and log output
135
+ back to a Girder job.
136
+
137
+ :param job: The job document representing the worker task.
138
+ :type job: dict
139
+ :param token: The token to use. Creates a job token if not passed.
140
+ :type token: str or dict
141
+ :param logPrint: Whether standard output from the job should be
142
+ """
143
+ if token is None:
144
+ token = Job().createJobToken(job)
145
+
146
+ if isinstance(token, dict):
147
+ token = token['_id']
148
+
149
+ return {
150
+ 'method': 'PUT',
151
+ 'url': '/'.join((getWorkerApiUrl(), 'job', str(job['_id']))),
152
+ 'reference': str(job['_id']),
153
+ 'headers': {'Girder-Token': token},
154
+ 'logPrint': logPrint
155
+ }
@@ -0,0 +1,50 @@
1
+ const events = girder.events;
2
+
3
+ // g:appload.before runs after all plugin static files have been loaded
4
+ events.on('g:appload.before', () => {
5
+ const JobStatus = girder.plugins.jobs.JobStatus;
6
+
7
+ JobStatus.registerStatus({
8
+ WORKER_FETCHING_INPUT: {
9
+ value: 820,
10
+ text: 'Fetching input',
11
+ icon: 'icon-download',
12
+ color: '#89d2e2'
13
+ },
14
+ WORKER_CONVERTING_INPUT: {
15
+ value: 821,
16
+ text: 'Converting input',
17
+ icon: 'icon-shuffle',
18
+ color: '#92f5b5'
19
+ },
20
+ WORKER_CONVERTING_OUTPUT: {
21
+ value: 822,
22
+ text: 'Converting output',
23
+ icon: 'icon-shuffle',
24
+ color: '#92f5b5'
25
+ },
26
+ WORKER_PUSHING_OUTPUT: {
27
+ value: 823,
28
+ text: 'Pushing output',
29
+ icon: 'icon-upload',
30
+ color: '#89d2e2'
31
+ },
32
+ WORKER_CANCELING: {
33
+ value: 824,
34
+ text: 'Canceling',
35
+ icon: 'icon-spin3 animate-spin',
36
+ color: '#f89406'
37
+ }
38
+ });
39
+
40
+ const jobPluginIsCancelable = JobStatus.isCancelable;
41
+ JobStatus.isCancelable = function (job) {
42
+ const handler = job.get('handler');
43
+ if (handler === 'worker_handler' || handler === 'celery_handler') {
44
+ return [JobStatus.CANCELED, JobStatus.WORKER_CANCELING,
45
+ JobStatus.SUCCESS, JobStatus.ERROR].indexOf(job.get('status')) === -1;
46
+ }
47
+
48
+ return jobPluginIsCancelable(job);
49
+ };
50
+ });
@@ -0,0 +1,4 @@
1
+ import './routes';
2
+ import './stylesheets/jobDetailsWidget.styl';
3
+ import './stylesheets/taskStatusView.styl';
4
+ import './JobStatus';