opentf-toolkit-nightly 0.57.0.dev1044__py3-none-any.whl → 0.57.0.dev1057__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.
- opentf/commons/pubsub.py +34 -30
- opentf/toolkit/__init__.py +47 -14
- opentf/toolkit/core.py +1 -2
- {opentf_toolkit_nightly-0.57.0.dev1044.dist-info → opentf_toolkit_nightly-0.57.0.dev1057.dist-info}/METADATA +9 -9
- {opentf_toolkit_nightly-0.57.0.dev1044.dist-info → opentf_toolkit_nightly-0.57.0.dev1057.dist-info}/RECORD +8 -8
- {opentf_toolkit_nightly-0.57.0.dev1044.dist-info → opentf_toolkit_nightly-0.57.0.dev1057.dist-info}/WHEEL +1 -1
- {opentf_toolkit_nightly-0.57.0.dev1044.dist-info → opentf_toolkit_nightly-0.57.0.dev1057.dist-info}/LICENSE +0 -0
- {opentf_toolkit_nightly-0.57.0.dev1044.dist-info → opentf_toolkit_nightly-0.57.0.dev1057.dist-info}/top_level.txt +0 -0
opentf/commons/pubsub.py
CHANGED
|
@@ -108,14 +108,14 @@ def _do(req, path: str, eventbus: Dict[str, Any], **kwargs) -> Response:
|
|
|
108
108
|
)
|
|
109
109
|
|
|
110
110
|
|
|
111
|
-
def _dispatch_events(dispatch_queue: Queue, app) -> None:
|
|
111
|
+
def _dispatch_events(dispatch_queue: Queue, fn, app) -> None:
|
|
112
112
|
"""Async event dispatch thread handler."""
|
|
113
113
|
delay = 0
|
|
114
114
|
while True:
|
|
115
115
|
try:
|
|
116
116
|
publication = dispatch_queue.get()
|
|
117
117
|
try:
|
|
118
|
-
|
|
118
|
+
fn(publication, app.config['CONTEXT'])
|
|
119
119
|
delay = 0
|
|
120
120
|
except Exception:
|
|
121
121
|
dispatch_queue.put(publication)
|
|
@@ -125,34 +125,6 @@ def _dispatch_events(dispatch_queue: Queue, app) -> None:
|
|
|
125
125
|
app.logger.error(f'Internal error while dispatching publication: {err}.')
|
|
126
126
|
|
|
127
127
|
|
|
128
|
-
def make_dispatchqueue(app) -> Queue:
|
|
129
|
-
"""Make an asynchronous dispatch queue.
|
|
130
|
-
|
|
131
|
-
Handles publication failures by waiting for an increasing delay and
|
|
132
|
-
re-attempting publication.
|
|
133
|
-
|
|
134
|
-
The delay is at most 60 seconds.
|
|
135
|
-
|
|
136
|
-
# Required parameters
|
|
137
|
-
|
|
138
|
-
- app: a flask app
|
|
139
|
-
|
|
140
|
-
# Returned value
|
|
141
|
-
|
|
142
|
-
A _queue_. Events pushed to this queue will be published.
|
|
143
|
-
"""
|
|
144
|
-
queue = Queue()
|
|
145
|
-
app.logger.debug('Starting events dispatch thread.')
|
|
146
|
-
try:
|
|
147
|
-
threading.Thread(
|
|
148
|
-
target=_dispatch_events, args=[queue, app], daemon=True
|
|
149
|
-
).start()
|
|
150
|
-
return queue
|
|
151
|
-
except Exception as err:
|
|
152
|
-
app.logger.error('Cound not start events dispatch thread: %s.', str(err))
|
|
153
|
-
sys.exit(2)
|
|
154
|
-
|
|
155
|
-
|
|
156
128
|
def subscribe(
|
|
157
129
|
kind: Optional[str],
|
|
158
130
|
target: str,
|
|
@@ -262,3 +234,35 @@ def publish(publication: Any, context: Dict[str, Any]) -> Response:
|
|
|
262
234
|
if isinstance(publication, dict) and 'metadata' in publication:
|
|
263
235
|
publication['metadata']['creationTimestamp'] = datetime.now().isoformat()
|
|
264
236
|
return _do(post, '/publications', context['eventbus'], json=publication)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def make_dispatchqueue(app, fn=publish) -> Queue:
|
|
240
|
+
"""Make an asynchronous dispatch queue.
|
|
241
|
+
|
|
242
|
+
Handles publication failures by waiting for an increasing delay and
|
|
243
|
+
re-attempting publication.
|
|
244
|
+
|
|
245
|
+
The delay is at most 60 seconds.
|
|
246
|
+
|
|
247
|
+
# Required parameters
|
|
248
|
+
|
|
249
|
+
- app: a flask app
|
|
250
|
+
|
|
251
|
+
# Optional parameters
|
|
252
|
+
|
|
253
|
+
- fn: a function (`publish` by default)
|
|
254
|
+
|
|
255
|
+
# Returned value
|
|
256
|
+
|
|
257
|
+
A _queue_. Events pushed to this queue will be published.
|
|
258
|
+
"""
|
|
259
|
+
queue = Queue()
|
|
260
|
+
app.logger.debug('Starting events dispatch thread.')
|
|
261
|
+
try:
|
|
262
|
+
threading.Thread(
|
|
263
|
+
target=_dispatch_events, args=[queue, fn, app], daemon=True
|
|
264
|
+
).start()
|
|
265
|
+
return queue
|
|
266
|
+
except Exception as err:
|
|
267
|
+
app.logger.error('Cound not start events dispatch thread: %s.', str(err))
|
|
268
|
+
sys.exit(2)
|
opentf/toolkit/__init__.py
CHANGED
|
@@ -40,6 +40,7 @@ from opentf.commons import (
|
|
|
40
40
|
CHANNEL_HOOKS,
|
|
41
41
|
validate_schema,
|
|
42
42
|
make_status_response,
|
|
43
|
+
make_dispatchqueue,
|
|
43
44
|
)
|
|
44
45
|
from opentf.toolkit import core
|
|
45
46
|
|
|
@@ -51,6 +52,7 @@ KIND_KEY = '__kind key__'
|
|
|
51
52
|
INPUTS_KEY = '__inputs key__'
|
|
52
53
|
WATCHEDFILES_KEY = '__watched files__'
|
|
53
54
|
WATCHEDFILES_EVENT_KEY = '__watched files event__'
|
|
55
|
+
DISPATCHQUEUE_KEY = '__dispatch queue__'
|
|
54
56
|
|
|
55
57
|
WATCHDOG_POLLING_DELAY_SECONDS = 30
|
|
56
58
|
WATCHDOG_POLLING_DELAY_KEY = 'watchdog_polling_delay_seconds'
|
|
@@ -296,22 +298,26 @@ def _dispatch_providercommand(plugin, handler: Handler, body: Dict[str, Any]) ->
|
|
|
296
298
|
def _dispatch_executioncommand(_, handler: Handler, body: Dict[str, Any]):
|
|
297
299
|
"""Channel plugin dispatcher."""
|
|
298
300
|
try:
|
|
299
|
-
|
|
301
|
+
handler(body)
|
|
300
302
|
except Exception as err:
|
|
301
|
-
|
|
302
|
-
core.publish_error(msg)
|
|
303
|
-
return make_status_response('InternalError', msg)
|
|
303
|
+
core.publish_error(f'Unexpected execution error: {err}.')
|
|
304
304
|
|
|
305
305
|
|
|
306
|
-
def _dispatch_generatorcommand(
|
|
306
|
+
def _dispatch_generatorcommand(plugin, handler: Handler, body: Dict[str, Any]):
|
|
307
307
|
"""Generator plugin dispatcher."""
|
|
308
308
|
try:
|
|
309
|
+
labels = body['metadata'].get('labels', {})
|
|
310
|
+
plugin.logger.debug(
|
|
311
|
+
'Calling generator %s (%s/%s@%s).',
|
|
312
|
+
handler.__name__,
|
|
313
|
+
labels.get('opentestfactory.org/categoryPrefix', '_'),
|
|
314
|
+
labels.get('opentestfactory.org/category', '_'),
|
|
315
|
+
labels.get('opentestfactory.org/categoryVersion', '_'),
|
|
316
|
+
)
|
|
309
317
|
inputs: Dict[str, Any] = body.get('with', {})
|
|
310
318
|
core.publish_generatorresult(handler(inputs))
|
|
311
319
|
except Exception as err:
|
|
312
|
-
|
|
313
|
-
core.publish_error(msg)
|
|
314
|
-
return make_status_response('InternalError', msg)
|
|
320
|
+
core.publish_error(f'Unexpected execution error: {err}.')
|
|
315
321
|
|
|
316
322
|
|
|
317
323
|
########################################################################
|
|
@@ -544,6 +550,7 @@ def make_plugin(
|
|
|
544
550
|
descriptor=None,
|
|
545
551
|
schema=None,
|
|
546
552
|
configfile=None,
|
|
553
|
+
args: Optional[Any] = None,
|
|
547
554
|
):
|
|
548
555
|
"""Create and return a new plugin service.
|
|
549
556
|
|
|
@@ -559,6 +566,11 @@ def make_plugin(
|
|
|
559
566
|
- Add publication handler
|
|
560
567
|
- Create service (not started)
|
|
561
568
|
|
|
569
|
+
Some 'optional' parameters are required for some publin types:
|
|
570
|
+
|
|
571
|
+
`args` is required for channel handlers. It must be a list of one
|
|
572
|
+
element that implements the `__contains__` interface.
|
|
573
|
+
|
|
562
574
|
# Required parameters
|
|
563
575
|
|
|
564
576
|
- name: a string
|
|
@@ -572,6 +584,7 @@ def make_plugin(
|
|
|
572
584
|
by default)
|
|
573
585
|
- schema: a string or None (None by default)
|
|
574
586
|
- configfile: a string or None (None by default)
|
|
587
|
+
- args: a list or None (None by default)
|
|
575
588
|
|
|
576
589
|
# Raised exceptions
|
|
577
590
|
|
|
@@ -589,11 +602,25 @@ def make_plugin(
|
|
|
589
602
|
except Exception as err:
|
|
590
603
|
return make_status_response('BadRequest', f'Could not parse body: {err}.')
|
|
591
604
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
605
|
+
if channel:
|
|
606
|
+
try:
|
|
607
|
+
channel_id = body['metadata'].get('channel_id')
|
|
608
|
+
if channel_id and channel_id not in args[0]:
|
|
609
|
+
return make_status_response(
|
|
610
|
+
'OK', 'Job not handled by this channel plugin.'
|
|
611
|
+
)
|
|
612
|
+
except KeyError:
|
|
613
|
+
return make_status_response(
|
|
614
|
+
'BadRequest',
|
|
615
|
+
f'Not a valid {kind} request: Missing metadata section',
|
|
616
|
+
)
|
|
617
|
+
|
|
618
|
+
if request.remote_addr != '127.0.0.1':
|
|
619
|
+
valid, extra = validate_schema(kind, body)
|
|
620
|
+
if not valid:
|
|
621
|
+
return make_status_response(
|
|
622
|
+
'BadRequest', f'Not a valid {kind} request: {extra}.'
|
|
623
|
+
)
|
|
597
624
|
|
|
598
625
|
if workflow_id := body.get('metadata', {}).get('workflow_id'):
|
|
599
626
|
g.workflow_id = workflow_id
|
|
@@ -608,7 +635,7 @@ def make_plugin(
|
|
|
608
635
|
elif provider:
|
|
609
636
|
_dispatch_providercommand(plugin, provider, body)
|
|
610
637
|
elif channel:
|
|
611
|
-
|
|
638
|
+
_dispatch_executioncommand(plugin, channel, body)
|
|
612
639
|
elif generator:
|
|
613
640
|
_dispatch_generatorcommand(plugin, generator, body)
|
|
614
641
|
else:
|
|
@@ -624,6 +651,10 @@ def make_plugin(
|
|
|
624
651
|
raise ValueError(
|
|
625
652
|
"'descriptor', if specified, must be a dictionary or a list of dictionaries."
|
|
626
653
|
)
|
|
654
|
+
if channel and (not isinstance(args, list) or len(args) != 1):
|
|
655
|
+
raise ValueError(
|
|
656
|
+
"'args' is required for channel plugins and must be a list of one element."
|
|
657
|
+
)
|
|
627
658
|
|
|
628
659
|
kind = (
|
|
629
660
|
EXECUTIONCOMMAND
|
|
@@ -645,8 +676,10 @@ def make_plugin(
|
|
|
645
676
|
|
|
646
677
|
if kind == PROVIDERCOMMAND:
|
|
647
678
|
_maybe_add_hook_watcher(plugin, schema)
|
|
679
|
+
plugin.config[DISPATCHQUEUE_KEY] = make_dispatchqueue(plugin)
|
|
648
680
|
elif kind == EXECUTIONCOMMAND:
|
|
649
681
|
_maybe_add_hook_watcher(plugin, CHANNEL_HOOKS)
|
|
682
|
+
plugin.config[DISPATCHQUEUE_KEY] = make_dispatchqueue(plugin)
|
|
650
683
|
|
|
651
684
|
core.register_defaultplugin(plugin)
|
|
652
685
|
|
opentf/toolkit/core.py
CHANGED
|
@@ -27,7 +27,6 @@ from opentf.commons import (
|
|
|
27
27
|
GENERATORRESULT,
|
|
28
28
|
PROVIDERRESULT,
|
|
29
29
|
EXECUTIONERROR,
|
|
30
|
-
publish,
|
|
31
30
|
make_event,
|
|
32
31
|
make_uuid,
|
|
33
32
|
)
|
|
@@ -114,7 +113,7 @@ class ExecutionError(Exception):
|
|
|
114
113
|
|
|
115
114
|
def publish_event(event) -> None:
|
|
116
115
|
"""Publish event."""
|
|
117
|
-
|
|
116
|
+
_getplugin().config['__dispatch queue__'].put(event)
|
|
118
117
|
|
|
119
118
|
|
|
120
119
|
def publish_error(error_details) -> None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: opentf-toolkit-nightly
|
|
3
|
-
Version: 0.57.0.
|
|
3
|
+
Version: 0.57.0.dev1057
|
|
4
4
|
Summary: OpenTestFactory Orchestrator Toolkit
|
|
5
5
|
Home-page: https://gitlab.com/henixdevelopment/open-source/opentestfactory/python-toolkit
|
|
6
6
|
Author: Martin Lafaix
|
|
@@ -16,14 +16,14 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
|
16
16
|
Requires-Python: >= 3.8.0
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
18
|
License-File: LICENSE
|
|
19
|
-
Requires-Dist: requests
|
|
20
|
-
Requires-Dist: PyJWT[crypto]
|
|
21
|
-
Requires-Dist: PyYAML
|
|
22
|
-
Requires-Dist: Flask
|
|
23
|
-
Requires-Dist: jsonschema
|
|
24
|
-
Requires-Dist: toposort
|
|
25
|
-
Requires-Dist: waitress
|
|
26
|
-
Requires-Dist: paste
|
|
19
|
+
Requires-Dist: requests>=2.31
|
|
20
|
+
Requires-Dist: PyJWT[crypto]<2.9,>=2.7
|
|
21
|
+
Requires-Dist: PyYAML>=6
|
|
22
|
+
Requires-Dist: Flask>=2.2.3
|
|
23
|
+
Requires-Dist: jsonschema>=4.17
|
|
24
|
+
Requires-Dist: toposort>=1.10
|
|
25
|
+
Requires-Dist: waitress>=2.1.2
|
|
26
|
+
Requires-Dist: paste>=3.5.2
|
|
27
27
|
|
|
28
28
|
# opentf-toolkit
|
|
29
29
|
|
|
@@ -3,7 +3,7 @@ opentf/commons/auth.py,sha256=bM2Z3kxm2Wku1lKXaRAIg37LHvXWAXIZIqjplDfN2P8,15899
|
|
|
3
3
|
opentf/commons/config.py,sha256=dyus4K5Zdmcftc3Y9Z1YRkzA1KwiRLHoeAlg2_A49QM,7876
|
|
4
4
|
opentf/commons/datasources.py,sha256=4ye-TMtaE88O8GVcWx-FtKXOC8aIZLteR6wfIr7Do8U,25232
|
|
5
5
|
opentf/commons/expressions.py,sha256=jM_YKXVOFhvOE2aE2IuacuvxhIsOYTFs2oQkpcbWR6g,19645
|
|
6
|
-
opentf/commons/pubsub.py,sha256=
|
|
6
|
+
opentf/commons/pubsub.py,sha256=DVrSara5FRfNdPBwXKUkTobqGki0RPDehylTEFcJnFc,7341
|
|
7
7
|
opentf/commons/schemas.py,sha256=YSCvlmqc7satt-OqIoYXnmhOyo9h8wIpNyKaBAY4u9c,4039
|
|
8
8
|
opentf/commons/selectors.py,sha256=DEpLgRAr5HXSpSYI4liXP2hLUTvOSexFa9Vfa1xIQTk,7134
|
|
9
9
|
opentf/schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -46,11 +46,11 @@ opentf/schemas/opentestfactory.org/v1beta1/Workflow.json,sha256=QZ8mM9PhzsI9gTmw
|
|
|
46
46
|
opentf/schemas/opentestfactory.org/v1beta2/ServiceConfig.json,sha256=rEvK2YWL5lG94_qYgR_GnLWNsaQhaQ-2kuZdWJr5NnY,3517
|
|
47
47
|
opentf/scripts/launch_java_service.sh,sha256=S0jAaCuv2sZy0Gf2NGBuPX-eD531rcM-b0fNyhmzSjw,2423
|
|
48
48
|
opentf/scripts/startup.py,sha256=Da2zo93pBWbdRmj-wgekgLcF94rpNc3ZkbvR8R0w8XY,21279
|
|
49
|
-
opentf/toolkit/__init__.py,sha256=
|
|
49
|
+
opentf/toolkit/__init__.py,sha256=4UbExlqRO8Ew7GYRrMdEDruMIB0zTLSsoVCKfW3vPnQ,23488
|
|
50
50
|
opentf/toolkit/channels.py,sha256=6xcVKHUK2FdyVKIQmPQbakngfVuQDzCcD_lInOdKpro,17171
|
|
51
|
-
opentf/toolkit/core.py,sha256=
|
|
52
|
-
opentf_toolkit_nightly-0.57.0.
|
|
53
|
-
opentf_toolkit_nightly-0.57.0.
|
|
54
|
-
opentf_toolkit_nightly-0.57.0.
|
|
55
|
-
opentf_toolkit_nightly-0.57.0.
|
|
56
|
-
opentf_toolkit_nightly-0.57.0.
|
|
51
|
+
opentf/toolkit/core.py,sha256=GdmEJ0ikdMdpViEpR4jP-viqfvBUHnpiFCOXwLGThxg,9606
|
|
52
|
+
opentf_toolkit_nightly-0.57.0.dev1057.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
53
|
+
opentf_toolkit_nightly-0.57.0.dev1057.dist-info/METADATA,sha256=Rfyt1TuD1vqvhdTcSAs1G_3Iujzd6P8StHAFXiE9dYo,1943
|
|
54
|
+
opentf_toolkit_nightly-0.57.0.dev1057.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
|
|
55
|
+
opentf_toolkit_nightly-0.57.0.dev1057.dist-info/top_level.txt,sha256=_gPuE6GTT6UNXy1DjtmQSfCcZb_qYA2vWmjg7a30AGk,7
|
|
56
|
+
opentf_toolkit_nightly-0.57.0.dev1057.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|