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 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
- publish(publication, app.config['CONTEXT'])
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)
@@ -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
- return handler(body)
301
+ handler(body)
300
302
  except Exception as err:
301
- msg = f'Unexpected execution error: {err}.'
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(_, handler: Handler, body: Dict[str, Any]):
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
- msg = f'Unexpected execution error: {err}.'
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
- valid, extra = validate_schema(kind, body)
593
- if not valid:
594
- return make_status_response(
595
- 'BadRequest', f'Not a valid {kind} request: {extra}.'
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
- return _dispatch_executioncommand(plugin, channel, body)
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
- publish(event, context=_getplugin().config['CONTEXT'])
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.dev1044
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 >=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
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=Y3vOeGNcI4_-uYwBy2grxmn1Oq5r89tyRZZX3mjgiAA,7254
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=FLjU1HzD3M4xyLV3uUrec4RdVDyTcpvMGRnZOZtfXfc,22037
49
+ opentf/toolkit/__init__.py,sha256=4UbExlqRO8Ew7GYRrMdEDruMIB0zTLSsoVCKfW3vPnQ,23488
50
50
  opentf/toolkit/channels.py,sha256=6xcVKHUK2FdyVKIQmPQbakngfVuQDzCcD_lInOdKpro,17171
51
- opentf/toolkit/core.py,sha256=Uc5cRwyi6bs7WVmgvQLTvEa6bXjZ3KfCKWHSdIeUy98,9621
52
- opentf_toolkit_nightly-0.57.0.dev1044.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
53
- opentf_toolkit_nightly-0.57.0.dev1044.dist-info/METADATA,sha256=88E1Hd8UjdibvTKeeNB6rW67PGj4k0p6qaG_Tg5SBcs,1951
54
- opentf_toolkit_nightly-0.57.0.dev1044.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
55
- opentf_toolkit_nightly-0.57.0.dev1044.dist-info/top_level.txt,sha256=_gPuE6GTT6UNXy1DjtmQSfCcZb_qYA2vWmjg7a30AGk,7
56
- opentf_toolkit_nightly-0.57.0.dev1044.dist-info/RECORD,,
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: bdist_wheel (0.44.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5