opentf-toolkit-nightly 0.57.0.dev1071__py3-none-any.whl → 0.57.0.dev1074__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/schemas.py CHANGED
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2023 Henix, Henix.fr
1
+ # Copyright (c) Henix, Henix.fr
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@ import json
20
20
  import logging
21
21
  import os
22
22
 
23
- from jsonschema import validate, ValidationError
23
+ from jsonschema import Draft201909Validator, ValidationError
24
24
 
25
25
  import opentf.schemas
26
26
 
@@ -41,10 +41,10 @@ WORKFLOWCANCELED = 'opentestfactory.org/v1alpha1/WorkflowCanceled'
41
41
  WORKFLOWRESULT = 'opentestfactory.org/v1alpha1/WorkflowResult'
42
42
 
43
43
  GENERATORCOMMAND = 'opentestfactory.org/v1alpha1/GeneratorCommand'
44
- GENERATORRESULT = 'opentestfactory.org/v1beta1/GeneratorResult'
44
+ GENERATORRESULT = 'opentestfactory.org/v1/GeneratorResult'
45
45
 
46
46
  PROVIDERCOMMAND = 'opentestfactory.org/v1beta1/ProviderCommand'
47
- PROVIDERRESULT = 'opentestfactory.org/v1beta1/ProviderResult'
47
+ PROVIDERRESULT = 'opentestfactory.org/v1/ProviderResult'
48
48
 
49
49
  EXECUTIONCOMMAND = 'opentestfactory.org/v1beta1/ExecutionCommand'
50
50
  EXECUTIONRESULT = 'opentestfactory.org/v1alpha1/ExecutionResult'
@@ -67,6 +67,7 @@ INSIGHT_COLLECTOR = 'opentestfactory.org/v1alpha1/InsightCollector'
67
67
  # JSON Schema Helpers
68
68
 
69
69
  _schemas = {}
70
+ _validators = {}
70
71
 
71
72
  SCHEMAS_ROOT_DIRECTORY = list(opentf.schemas.__path__)[0]
72
73
 
@@ -101,6 +102,12 @@ def get_schema(name: str) -> Dict[str, Any]:
101
102
  return _schemas[name]
102
103
 
103
104
 
105
+ def _validator(schema: str):
106
+ if schema not in _validators:
107
+ _validators[schema] = Draft201909Validator(get_schema(schema))
108
+ return _validators[schema]
109
+
110
+
104
111
  def validate_schema(schema, instance) -> Tuple[bool, Any]:
105
112
  """Return True if instance validates schema.
106
113
 
@@ -120,7 +127,7 @@ def validate_schema(schema, instance) -> Tuple[bool, Any]:
120
127
  is logged and raised again.
121
128
  """
122
129
  try:
123
- validate(schema=get_schema(schema), instance=instance)
130
+ _validator(schema).validate(instance=instance)
124
131
  except ValidationError as err:
125
132
  return False, err
126
133
  return True, None
@@ -0,0 +1,484 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2019-09/schema#",
3
+ "title": "JSON SCHEMA for opentestfactory.org/v1 GeneratorResult manifests",
4
+ "type": "object",
5
+ "properties": {
6
+ "apiVersion": {
7
+ "union": [
8
+ "opentestfactory.org/v1alpha1",
9
+ "opentestfactory.org/v1beta1",
10
+ "opentestfactory.org/v1"
11
+ ]
12
+ },
13
+ "kind": {
14
+ "const": "GeneratorResult"
15
+ },
16
+ "metadata": {
17
+ "type": "object",
18
+ "properties": {
19
+ "name": {
20
+ "type": "string"
21
+ },
22
+ "namespace": {
23
+ "type": "string",
24
+ "pattern": "^[a-z0-9][a-z0-9-]*$"
25
+ },
26
+ "workflow_id": {
27
+ "type": "string"
28
+ },
29
+ "job_id": {
30
+ "type": "string"
31
+ },
32
+ "job_origin": {
33
+ "type": "array",
34
+ "items": {
35
+ "type": "string"
36
+ }
37
+ },
38
+ "labels": {
39
+ "type": "object",
40
+ "patternProperties": {
41
+ "^([a-zA-Z0-9-.]+/)?[a-zA-Z0-9]([a-zA-Z0-9._-]*[a-zA-Z0-9])?$": {
42
+ "type": "string"
43
+ }
44
+ },
45
+ "minProperties": 1,
46
+ "additionalProperties": false
47
+ }
48
+ },
49
+ "additionalProperties": true,
50
+ "required": [
51
+ "name",
52
+ "workflow_id",
53
+ "job_id",
54
+ "job_origin"
55
+ ]
56
+ },
57
+ "jobs": {
58
+ "$ref": "#/definitions/jobs"
59
+ },
60
+ "outputs": {
61
+ "$ref": "#/definitions/outputs"
62
+ }
63
+ },
64
+ "required": [
65
+ "apiVersion",
66
+ "kind",
67
+ "metadata",
68
+ "jobs"
69
+ ],
70
+ "additionalProperties": false,
71
+ "definitions": {
72
+ "defaults": {
73
+ "type": "object",
74
+ "propertyNames": {
75
+ "pattern": "^[a-zA-Z][a-zA-Z0-9-]*$"
76
+ }
77
+ },
78
+ "job-generator": {
79
+ "type": "object",
80
+ "properties": {
81
+ "name": {
82
+ "type": "string"
83
+ },
84
+ "if": {
85
+ "type": "string"
86
+ },
87
+ "concurrency": {
88
+ "$ref": "#/definitions/concurrency"
89
+ },
90
+ "runs-on": {
91
+ "$ref": "#/definitions/runs-on"
92
+ },
93
+ "needs": {
94
+ "$ref": "#/definitions/needs"
95
+ },
96
+ "defaults": {
97
+ "$ref": "#/definitions/defaults"
98
+ },
99
+ "variables": {
100
+ "$ref": "#/definitions/variables"
101
+ },
102
+ "timeout-minutes": {
103
+ "$ref": "#/definitions/number-expression"
104
+ },
105
+ "strategy": {
106
+ "$ref": "#/definitions/strategy"
107
+ },
108
+ "jobs": {
109
+ "$ref": "#/definitions/jobs"
110
+ },
111
+ "generator": {
112
+ "type": "string"
113
+ },
114
+ "uses": {
115
+ "type": "string"
116
+ },
117
+ "with": {
118
+ "$ref": "#/definitions/with"
119
+ },
120
+ "outputs": {
121
+ "$ref": "#/definitions/outputs"
122
+ },
123
+ "continue-on-error": {
124
+ "$ref": "#/definitions/boolean-expression"
125
+ }
126
+ },
127
+ "oneOf": [
128
+ {
129
+ "required": [
130
+ "generator"
131
+ ]
132
+ },
133
+ {
134
+ "required": [
135
+ "uses"
136
+ ]
137
+ },
138
+ {
139
+ "required": [
140
+ "jobs"
141
+ ]
142
+ }
143
+ ],
144
+ "additionalProperties": false
145
+ },
146
+ "job-steps": {
147
+ "type": "object",
148
+ "properties": {
149
+ "name": {
150
+ "type": "string"
151
+ },
152
+ "if": {
153
+ "type": "string"
154
+ },
155
+ "concurrency": {
156
+ "$ref": "#/definitions/concurrency"
157
+ },
158
+ "runs-on": {
159
+ "$ref": "#/definitions/runs-on"
160
+ },
161
+ "needs": {
162
+ "$ref": "#/definitions/needs"
163
+ },
164
+ "defaults": {
165
+ "$ref": "#/definitions/defaults"
166
+ },
167
+ "variables": {
168
+ "$ref": "#/definitions/variables"
169
+ },
170
+ "timeout-minutes": {
171
+ "$ref": "#/definitions/number-expression"
172
+ },
173
+ "strategy": {
174
+ "$ref": "#/definitions/strategy"
175
+ },
176
+ "steps": {
177
+ "type": "array",
178
+ "minItems": 1,
179
+ "items": {
180
+ "anyOf": [
181
+ {
182
+ "type": "object",
183
+ "properties": {
184
+ "name": {
185
+ "type": "string"
186
+ },
187
+ "id": {
188
+ "type": "string"
189
+ },
190
+ "if": {
191
+ "type": "string"
192
+ },
193
+ "uses": {
194
+ "type": "string"
195
+ },
196
+ "with": {
197
+ "$ref": "#/definitions/with"
198
+ },
199
+ "variables": {
200
+ "$ref": "#/definitions/variables"
201
+ },
202
+ "timeout-minutes": {
203
+ "$ref": "#/definitions/number-expression"
204
+ },
205
+ "continue-on-error": {
206
+ "$ref": "#/definitions/boolean-expression"
207
+ },
208
+ "working-directory": {
209
+ "type": "string"
210
+ }
211
+ },
212
+ "required": [
213
+ "uses"
214
+ ],
215
+ "additionalProperties": false
216
+ },
217
+ {
218
+ "type": "object",
219
+ "properties": {
220
+ "name": {
221
+ "type": "string"
222
+ },
223
+ "id": {
224
+ "type": "string"
225
+ },
226
+ "if": {
227
+ "type": "string"
228
+ },
229
+ "run": {
230
+ "type": "string"
231
+ },
232
+ "shell": {
233
+ "type": "string"
234
+ },
235
+ "variables": {
236
+ "$ref": "#/definitions/variables"
237
+ },
238
+ "timeout-minutes": {
239
+ "$ref": "#/definitions/number-expression"
240
+ },
241
+ "continue-on-error": {
242
+ "$ref": "#/definitions/boolean-expression"
243
+ },
244
+ "working-directory": {
245
+ "type": "string"
246
+ }
247
+ },
248
+ "required": [
249
+ "run"
250
+ ],
251
+ "additionalProperties": false
252
+ }
253
+ ]
254
+ }
255
+ },
256
+ "outputs": {
257
+ "$ref": "#/definitions/outputs"
258
+ },
259
+ "continue-on-error": {
260
+ "$ref": "#/definitions/boolean-expression"
261
+ }
262
+ },
263
+ "required": [
264
+ "steps"
265
+ ],
266
+ "additionalProperties": false
267
+ },
268
+ "needs": {
269
+ "anyOf": [
270
+ {
271
+ "type": "string",
272
+ "pattern": "(^[a-zA-Z_][a-zA-Z0-9_-]*$)|(^\\$\\{\\{.*\\}\\}$)"
273
+ },
274
+ {
275
+ "type": "array",
276
+ "minItems": 1,
277
+ "items": {
278
+ "type": "string",
279
+ "pattern": "(^[a-zA-Z_][a-zA-Z0-9_-]*$)|(^\\$\\{\\{.*\\}\\}$)"
280
+ }
281
+ }
282
+ ]
283
+ },
284
+ "number-expression": {
285
+ "anyOf": [
286
+ {
287
+ "type": "number"
288
+ },
289
+ {
290
+ "type": "string",
291
+ "pattern": "^\\$\\{\\{.*\\}\\}$"
292
+ }
293
+ ]
294
+ },
295
+ "boolean-expression": {
296
+ "anyOf": [
297
+ {
298
+ "type": "boolean"
299
+ },
300
+ {
301
+ "type": "string",
302
+ "pattern": "^\\$\\{\\{.*\\}\\}$"
303
+ }
304
+ ]
305
+ },
306
+ "identifier-expression": {
307
+ "type": "string",
308
+ "pattern": "(^[a-zA-Z][a-zA-Z0-9-]*$)|(^\\$\\{\\{.*\\}\\}$)"
309
+ },
310
+ "runs-on": {
311
+ "anyOf": [
312
+ {
313
+ "$ref": "#/definitions/identifier-expression"
314
+ },
315
+ {
316
+ "type": "array",
317
+ "minItems": 1,
318
+ "items": {
319
+ "$ref": "#/definitions/identifier-expression"
320
+ }
321
+ },
322
+ {
323
+ "type": "object"
324
+ }
325
+ ]
326
+ },
327
+ "variables": {
328
+ "type": "object",
329
+ "patternProperties": {
330
+ "^[a-zA-Z0-9_]+$": {
331
+ "oneOf": [
332
+ {
333
+ "type": "string"
334
+ },
335
+ {
336
+ "type": "object",
337
+ "properties": {
338
+ "value": {
339
+ "type": "string"
340
+ },
341
+ "verbatim": {
342
+ "type": "boolean"
343
+ }
344
+ },
345
+ "required": [
346
+ "value"
347
+ ],
348
+ "additionalProperties": false
349
+ }
350
+ ]
351
+ }
352
+ },
353
+ "minProperties": 1
354
+ },
355
+ "strategy": {
356
+ "type": "object",
357
+ "properties": {
358
+ "max-parallel": {
359
+ "$ref": "#/definitions/number-expression"
360
+ },
361
+ "fail-fast": {
362
+ "$ref": "#/definitions/boolean-expression"
363
+ },
364
+ "matrix": {
365
+ "type": "object",
366
+ "properties": {
367
+ "exclude": {
368
+ "type": "array",
369
+ "minItems": 1,
370
+ "items": {
371
+ "type": "object",
372
+ "patternProperties": {
373
+ "^[a-zA-Z][a-zA-Z0-9-]*$": {
374
+ "type": "string"
375
+ }
376
+ }
377
+ }
378
+ },
379
+ "include": {
380
+ "type": "array",
381
+ "minItems": 1,
382
+ "items": {
383
+ "type": "object",
384
+ "patternProperties": {
385
+ "^[a-zA-Z][a-zA-Z0-9-]*$": {
386
+ "type": "string"
387
+ }
388
+ }
389
+ }
390
+ }
391
+ },
392
+ "patternProperties": {
393
+ "^[a-zA-Z][a-zA-Z0-9-]*$": {
394
+ "type": "array",
395
+ "minItems": 1,
396
+ "items": {
397
+ "oneOf": [
398
+ {
399
+ "type": "string"
400
+ },
401
+ {
402
+ "type": "number"
403
+ },
404
+ {
405
+ "type": "object",
406
+ "patternProperties": {
407
+ "^[a-zA-Z][a-zA-Z0-9-]*$": {
408
+ "type": "string"
409
+ }
410
+ }
411
+ }
412
+ ]
413
+ }
414
+ }
415
+ }
416
+ }
417
+ },
418
+ "minProperties": 1,
419
+ "additionalProperties": false
420
+ },
421
+ "concurrency": {
422
+ "type": "object",
423
+ "properties": {
424
+ "group": {
425
+ "type": "string"
426
+ },
427
+ "cancel-in-progress": {
428
+ "$ref": "#/definitions/boolean-expression"
429
+ }
430
+ },
431
+ "additionalProperties": false,
432
+ "required": [
433
+ "group"
434
+ ]
435
+ },
436
+ "jobs": {
437
+ "type": "object",
438
+ "patternProperties": {
439
+ "^[a-zA-Z_][a-zA-Z0-9_-]*$": {
440
+ "oneOf": [
441
+ {
442
+ "$ref": "#/definitions/job-generator"
443
+ },
444
+ {
445
+ "$ref": "#/definitions/job-steps"
446
+ }
447
+ ]
448
+ }
449
+ },
450
+ "minProperties": 1
451
+ },
452
+ "with": {
453
+ "type": "object"
454
+ },
455
+ "outputs": {
456
+ "type": "object",
457
+ "patternProperties": {
458
+ "^[a-zA-Z_][a-zA-Z0-9_-]*$": {
459
+ "oneOf": [
460
+ {
461
+ "type": "object",
462
+ "properties": {
463
+ "description": {
464
+ "type": "string"
465
+ },
466
+ "value": {
467
+ "type": "string"
468
+ }
469
+ },
470
+ "required": [
471
+ "value"
472
+ ],
473
+ "additionalProperties": false
474
+ },
475
+ {
476
+ "type": "string"
477
+ }
478
+ ]
479
+ }
480
+ },
481
+ "minProperties": 1
482
+ }
483
+ }
484
+ }
@@ -0,0 +1,297 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2019-09/schema#",
3
+ "title": "JSON SCHEMA for opentestfactory.org/v1 ProviderResult manifests",
4
+ "type": "object",
5
+ "properties": {
6
+ "apiVersion": {
7
+ "enum": [
8
+ "opentestfactory.org/v1alpha1",
9
+ "opentestfactory.org/v1beta1",
10
+ "opentestfactory.org/v1"
11
+ ]
12
+ },
13
+ "kind": {
14
+ "const": "ProviderResult"
15
+ },
16
+ "metadata": {
17
+ "type": "object",
18
+ "properties": {
19
+ "name": {
20
+ "type": "string"
21
+ },
22
+ "namespace": {
23
+ "type": "string",
24
+ "pattern": "^[a-z0-9][a-z0-9-]*$"
25
+ },
26
+ "workflow_id": {
27
+ "type": "string"
28
+ },
29
+ "job_id": {
30
+ "type": "string"
31
+ },
32
+ "job_origin": {
33
+ "type": "array",
34
+ "items": {
35
+ "type": "string"
36
+ }
37
+ },
38
+ "step_id": {
39
+ "type": "string"
40
+ },
41
+ "step_origin": {
42
+ "type": "array",
43
+ "items": {
44
+ "type": "string"
45
+ }
46
+ },
47
+ "labels": {
48
+ "type": "object",
49
+ "patternProperties": {
50
+ "^([a-zA-Z0-9-.]+/)?[a-zA-Z0-9]([a-zA-Z0-9._-]*[a-zA-Z0-9])?$": {
51
+ "type": "string"
52
+ }
53
+ },
54
+ "minProperties": 1,
55
+ "additionalProperties": false
56
+ }
57
+ },
58
+ "additionalProperties": true,
59
+ "required": [
60
+ "name",
61
+ "workflow_id",
62
+ "job_id",
63
+ "job_origin",
64
+ "step_id",
65
+ "step_origin"
66
+ ]
67
+ },
68
+ "hooks": {
69
+ "$ref": "#/definitions/hooks"
70
+ },
71
+ "steps": {
72
+ "type": "array",
73
+ "minItems": 0,
74
+ "items": {
75
+ "anyOf": [
76
+ {
77
+ "type": "object",
78
+ "properties": {
79
+ "name": {
80
+ "type": "string"
81
+ },
82
+ "id": {
83
+ "type": "string"
84
+ },
85
+ "if": {
86
+ "type": "string"
87
+ },
88
+ "uses": {
89
+ "type": "string"
90
+ },
91
+ "with": {
92
+ "$ref": "#/definitions/with"
93
+ },
94
+ "variables": {
95
+ "$ref": "#/definitions/variables"
96
+ },
97
+ "timeout-minutes": {
98
+ "$ref": "#/definitions/number-expression"
99
+ },
100
+ "continue-on-error": {
101
+ "$ref": "#/definitions/boolean-expression"
102
+ },
103
+ "working-directory": {
104
+ "type": "string"
105
+ }
106
+ },
107
+ "required": [
108
+ "uses"
109
+ ],
110
+ "additionalProperties": false
111
+ },
112
+ {
113
+ "type": "object",
114
+ "properties": {
115
+ "name": {
116
+ "type": "string"
117
+ },
118
+ "id": {
119
+ "type": "string"
120
+ },
121
+ "if": {
122
+ "type": "string"
123
+ },
124
+ "run": {
125
+ "type": "string"
126
+ },
127
+ "shell": {
128
+ "type": "string"
129
+ },
130
+ "variables": {
131
+ "$ref": "#/definitions/variables"
132
+ },
133
+ "timeout-minutes": {
134
+ "$ref": "#/definitions/number-expression"
135
+ },
136
+ "continue-on-error": {
137
+ "$ref": "#/definitions/boolean-expression"
138
+ },
139
+ "working-directory": {
140
+ "type": "string"
141
+ }
142
+ },
143
+ "required": [
144
+ "run"
145
+ ],
146
+ "additionalProperties": false
147
+ }
148
+ ]
149
+ }
150
+ },
151
+ "outputs": {
152
+ "$ref": "#/definitions/outputs"
153
+ }
154
+ },
155
+ "required": [
156
+ "apiVersion",
157
+ "kind",
158
+ "metadata",
159
+ "steps"
160
+ ],
161
+ "additionalProperties": false,
162
+ "definitions": {
163
+ "number-expression": {
164
+ "anyOf": [
165
+ {
166
+ "type": "number"
167
+ },
168
+ {
169
+ "type": "string",
170
+ "pattern": "^\\$\\{\\{.*\\}\\}$"
171
+ }
172
+ ]
173
+ },
174
+ "boolean-expression": {
175
+ "anyOf": [
176
+ {
177
+ "type": "boolean"
178
+ },
179
+ {
180
+ "type": "string",
181
+ "pattern": "^\\$\\{\\{.*\\}\\}$"
182
+ }
183
+ ]
184
+ },
185
+ "variables": {
186
+ "type": "object",
187
+ "patternProperties": {
188
+ "^[a-zA-Z0-9_]+$": {
189
+ "oneOf": [
190
+ {
191
+ "type": "string"
192
+ },
193
+ {
194
+ "type": "object",
195
+ "properties": {
196
+ "value": {
197
+ "type": "string"
198
+ },
199
+ "verbatim": {
200
+ "type": "boolean"
201
+ }
202
+ },
203
+ "required": [
204
+ "value"
205
+ ],
206
+ "additionalProperties": false
207
+ }
208
+ ]
209
+ }
210
+ },
211
+ "minProperties": 1
212
+ },
213
+ "hooks": {
214
+ "type": "array",
215
+ "minItems": 1,
216
+ "items": {
217
+ "type": "object",
218
+ "properties": {
219
+ "name": {
220
+ "type": "string"
221
+ },
222
+ "if": {
223
+ "type": "string"
224
+ },
225
+ "events": {
226
+ "type": "array",
227
+ "minItems": 1
228
+ },
229
+ "before": {
230
+ "type": "array",
231
+ "minItems": 1
232
+ },
233
+ "after": {
234
+ "type": "array",
235
+ "minItems": 1
236
+ }
237
+ },
238
+ "anyOf": [
239
+ {
240
+ "required": [
241
+ "name",
242
+ "events",
243
+ "before"
244
+ ]
245
+ },
246
+ {
247
+ "required": [
248
+ "name",
249
+ "events",
250
+ "after",
251
+ "before"
252
+ ]
253
+ },
254
+ {
255
+ "required": [
256
+ "name",
257
+ "events",
258
+ "after"
259
+ ]
260
+ }
261
+ ],
262
+ "additionalProperties": false
263
+ }
264
+ },
265
+ "with": {
266
+ "type": "object"
267
+ },
268
+ "outputs": {
269
+ "type": "object",
270
+ "patternProperties": {
271
+ "^[a-zA-Z_][a-zA-Z0-9_-]*$": {
272
+ "oneOf": [
273
+ {
274
+ "type": "object",
275
+ "properties": {
276
+ "description": {
277
+ "type": "string"
278
+ },
279
+ "value": {
280
+ "type": "string"
281
+ }
282
+ },
283
+ "required": [
284
+ "value"
285
+ ],
286
+ "additionalProperties": false
287
+ },
288
+ {
289
+ "type": "string"
290
+ }
291
+ ]
292
+ }
293
+ },
294
+ "minProperties": 1
295
+ }
296
+ }
297
+ }
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2021-2023 Henix, Henix.fr
1
+ # Copyright (c) Henix, Henix.fr
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
14
14
 
15
15
  """A toolkit for creating OpenTestFactory plugins."""
16
16
 
17
- from typing import Any, Callable, Dict, Optional
17
+ from typing import Any, Callable, Dict, Optional, Tuple
18
18
 
19
19
  import os
20
20
  import threading
@@ -50,6 +50,7 @@ from opentf.toolkit import core
50
50
  SUBSCRIPTION_KEY = '__subscription uuid__'
51
51
  KIND_KEY = '__kind key__'
52
52
  INPUTS_KEY = '__inputs key__'
53
+ OUTPUTS_KEY = '__outputs key__'
53
54
  WATCHEDFILES_KEY = '__watched files__'
54
55
  WATCHEDFILES_EVENT_KEY = '__watched files event__'
55
56
  DISPATCHQUEUE_KEY = '__dispatch queue__'
@@ -91,6 +92,33 @@ def _normalize_inputs(inputs: Dict[str, Any]) -> None:
91
92
  inputs[normalized] = inputs.pop(key)
92
93
 
93
94
 
95
+ def _get_pcv(
96
+ labels: Dict[str, str], default: Optional[str] = None
97
+ ) -> Tuple[Optional[str], Optional[str], Optional[str]]:
98
+ """Extract prefix, category, version from labels."""
99
+ prefix = labels.get('opentestfactory.org/categoryPrefix', default)
100
+ category = labels.get('opentestfactory.org/category', default)
101
+ version = labels.get('opentestfactory.org/categoryVersion', default) or None
102
+ return prefix, category, version
103
+
104
+
105
+ def _maybe_get_item(cache: Dict[Any, Any], labels: Dict[str, str]) -> Optional[Any]:
106
+ """Get most relevant item from cache if it exists."""
107
+ prefix, category, version = _get_pcv(labels)
108
+
109
+ for keys in (
110
+ (prefix, category, version),
111
+ (None, category, version),
112
+ (prefix, category, None),
113
+ (None, category, None),
114
+ (prefix, None, None),
115
+ ):
116
+ if (entry := cache.get(keys)) is not None:
117
+ return entry
118
+
119
+ return None
120
+
121
+
94
122
  def _ensure_inputs_match(
95
123
  plugin, labels: Dict[str, str], inputs: Dict[str, Any]
96
124
  ) -> None:
@@ -105,20 +133,7 @@ def _ensure_inputs_match(
105
133
  or if an unexpected entry is found.
106
134
  """
107
135
  cache = plugin.config['CONTEXT'][INPUTS_KEY]
108
- prefix = labels.get('opentestfactory.org/categoryPrefix')
109
- category = labels.get('opentestfactory.org/category')
110
- version = labels.get('opentestfactory.org/categoryVersion') or None
111
-
112
- for keys in (
113
- (prefix, category, version),
114
- (None, category, version),
115
- (prefix, category, None),
116
- (None, category, None),
117
- (prefix, None, None),
118
- ):
119
- if (entry := cache.get(keys)) is not None:
120
- break
121
- else:
136
+ if (entry := _maybe_get_item(cache, labels)) is None:
122
137
  return
123
138
 
124
139
  declaration, additional_inputs = entry
@@ -159,9 +174,7 @@ def _get_target(
159
174
 
160
175
  `category[@vn]` is more specific than `prefix`.
161
176
  """
162
- prefix = labels['opentestfactory.org/categoryPrefix']
163
- category = labels['opentestfactory.org/category']
164
- version = labels.get('opentestfactory.org/categoryVersion')
177
+ prefix, category, version = _get_pcv(labels)
165
178
 
166
179
  for template in (f'{prefix}/{category}', category):
167
180
  if version:
@@ -282,13 +295,12 @@ def _dispatch_providercommand(plugin, handler: Handler, body: Dict[str, Any]) ->
282
295
  plugin.logger.debug(
283
296
  'Calling provider function %s (%s/%s@%s).',
284
297
  handler.__name__,
285
- labels.get('opentestfactory.org/categoryPrefix', '_'),
286
- labels.get('opentestfactory.org/category', '_'),
287
- labels.get('opentestfactory.org/categoryVersion', '_'),
298
+ *_get_pcv(labels, default='_'),
288
299
  )
289
300
  inputs: Dict[str, Any] = body['step'].get('with', {})
290
301
  _ensure_inputs_match(plugin, labels, inputs)
291
- core.publish_providerresult(handler(inputs))
302
+ outputs = _maybe_get_item(plugin.config['CONTEXT'][OUTPUTS_KEY], labels)
303
+ core.publish_providerresult(handler(inputs), outputs)
292
304
  except core.ExecutionError as err:
293
305
  core.publish_error(str(err))
294
306
  except Exception as err:
@@ -310,12 +322,14 @@ def _dispatch_generatorcommand(plugin, handler: Handler, body: Dict[str, Any]):
310
322
  plugin.logger.debug(
311
323
  'Calling generator %s (%s/%s@%s).',
312
324
  handler.__name__,
313
- labels.get('opentestfactory.org/categoryPrefix', '_'),
314
- labels.get('opentestfactory.org/category', '_'),
315
- labels.get('opentestfactory.org/categoryVersion', '_'),
325
+ *_get_pcv(labels, default='_'),
316
326
  )
317
327
  inputs: Dict[str, Any] = body.get('with', {})
318
- core.publish_generatorresult(handler(inputs))
328
+ _ensure_inputs_match(plugin, labels, inputs)
329
+ outputs = _maybe_get_item(plugin.config['CONTEXT'][OUTPUTS_KEY], labels)
330
+ core.publish_generatorresult(handler(inputs), outputs)
331
+ except core.ExecutionError as err:
332
+ core.publish_error(str(err))
319
333
  except Exception as err:
320
334
  core.publish_error(f'Unexpected execution error: {err}.')
321
335
 
@@ -464,6 +478,10 @@ def _subscribe(
464
478
  manifest.get('inputs', {}),
465
479
  manifest.get('additionalInputs'),
466
480
  )
481
+ context[OUTPUTS_KEY][(cat_prefix, cat, cat_version)] = {
482
+ k: v['value'] if isinstance(v, dict) else v
483
+ for k, v in manifest.get('outputs', {}).items()
484
+ }
467
485
  return subscribe(kind=kind, target='inbox', app=plugin, labels=labels)
468
486
 
469
487
 
@@ -479,7 +497,8 @@ def run_plugin(plugin):
479
497
  context = plugin.config['CONTEXT']
480
498
  context[SUBSCRIPTION_KEY] = []
481
499
  context[INPUTS_KEY] = {}
482
- if context[KIND_KEY] == PROVIDERCOMMAND:
500
+ context[OUTPUTS_KEY] = {}
501
+ if context[KIND_KEY] in (PROVIDERCOMMAND, GENERATORCOMMAND):
483
502
  for manifest in plugin.config['DESCRIPTOR']:
484
503
  metadata = manifest.get('metadata', {})
485
504
  if metadata.get('name', '').lower() != plugin.name.lower():
@@ -502,37 +521,6 @@ def run_plugin(plugin):
502
521
  context[SUBSCRIPTION_KEY].append(
503
522
  subscribe(kind=EXECUTIONCOMMAND, target='inbox', app=plugin)
504
523
  )
505
- elif context[KIND_KEY] == GENERATORCOMMAND:
506
- for manifest in plugin.config['DESCRIPTOR']:
507
- metadata = manifest.get('metadata', {})
508
- if metadata.get('name', '').lower() != plugin.name.lower():
509
- continue
510
- if 'action' not in metadata:
511
- continue
512
- for event in manifest.get('events', []):
513
- cat_prefix = event.get('categoryPrefix')
514
- cat = event.get('category')
515
- if cat or cat_prefix:
516
- cat_version = event.get('categoryVersion')
517
- labels = {}
518
- if cat is not None:
519
- labels['opentestfactory.org/category'] = cat
520
- if cat_prefix is not None:
521
- labels['opentestfactory.org/categoryPrefix'] = cat_prefix
522
- if cat_version is not None:
523
- labels['opentestfactory.org/categoryVersion'] = cat_version
524
- context[SUBSCRIPTION_KEY].append(
525
- subscribe(
526
- kind=GENERATORCOMMAND,
527
- target='inbox',
528
- labels=labels,
529
- app=plugin,
530
- )
531
- )
532
- else:
533
- plugin.logger.warning(
534
- "At least one of 'category', 'categoryPrefix' required, ignoring."
535
- )
536
524
  run_app(plugin)
537
525
  finally:
538
526
  for subscription_id in plugin.config['CONTEXT'][SUBSCRIPTION_KEY]:
@@ -566,10 +554,10 @@ def make_plugin(
566
554
  - Add publication handler
567
555
  - Create service (not started)
568
556
 
569
- Some 'optional' parameters are required for some publin types:
557
+ Some 'optional' parameters are required for some plugin types:
570
558
 
571
559
  `args` is required for channel handlers. It must be a list of one
572
- element that implements the `__contains__` interface.
560
+ element that implements the `__contains__` protocol.
573
561
 
574
562
  # Required parameters
575
563
 
@@ -615,12 +603,11 @@ def make_plugin(
615
603
  f'Not a valid {kind} request: Missing metadata section',
616
604
  )
617
605
 
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
- )
606
+ valid, extra = validate_schema(kind, body)
607
+ if not valid:
608
+ return make_status_response(
609
+ 'BadRequest', f'Not a valid {kind} request: {extra}.'
610
+ )
624
611
 
625
612
  if workflow_id := body.get('metadata', {}).get('workflow_id'):
626
613
  g.workflow_id = workflow_id
@@ -677,6 +664,8 @@ def make_plugin(
677
664
  if kind == PROVIDERCOMMAND:
678
665
  _maybe_add_hook_watcher(plugin, schema)
679
666
  plugin.config[DISPATCHQUEUE_KEY] = make_dispatchqueue(plugin)
667
+ elif kind == GENERATORCOMMAND:
668
+ plugin.config[DISPATCHQUEUE_KEY] = make_dispatchqueue(plugin)
680
669
  elif kind == EXECUTIONCOMMAND:
681
670
  _maybe_add_hook_watcher(plugin, CHANNEL_HOOKS)
682
671
  plugin.config[DISPATCHQUEUE_KEY] = make_dispatchqueue(plugin)
opentf/toolkit/core.py CHANGED
@@ -126,7 +126,7 @@ def publish_error(error_details) -> None:
126
126
  publish_event(error)
127
127
 
128
128
 
129
- def publish_providerresult(steps: Iterable) -> None:
129
+ def publish_providerresult(steps: Iterable, outputs: Optional[Dict[str, Any]]) -> None:
130
130
  """Publish ProviderResult event."""
131
131
  command = make_event(
132
132
  PROVIDERRESULT,
@@ -135,18 +135,24 @@ def publish_providerresult(steps: Iterable) -> None:
135
135
  )
136
136
  if hooks := _getplugin().config['CONFIG'].get('hooks'):
137
137
  command['hooks'] = hooks
138
+ if outputs:
139
+ command['outputs'] = outputs
138
140
  for step in command['steps']:
139
141
  step.setdefault('id', make_uuid())
140
142
  publish_event(command)
141
143
 
142
144
 
143
- def publish_generatorresult(jobs: Dict[str, Any]) -> None:
145
+ def publish_generatorresult(
146
+ jobs: Dict[str, Any], outputs: Optional[Dict[str, Any]]
147
+ ) -> None:
144
148
  """Publish GeneratorResult event."""
145
149
  command = make_event(
146
150
  GENERATORRESULT,
147
151
  metadata=_getbody()['metadata'],
148
152
  jobs={k: v.copy() for k, v in jobs.items()},
149
153
  )
154
+ if outputs:
155
+ command['outputs'] = outputs
150
156
  publish_event(command)
151
157
 
152
158
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: opentf-toolkit-nightly
3
- Version: 0.57.0.dev1071
3
+ Version: 0.57.0.dev1074
4
4
  Summary: OpenTestFactory Orchestrator Toolkit
5
5
  Home-page: https://gitlab.com/henixdevelopment/open-source/opentestfactory/python-toolkit
6
6
  Author: Martin Lafaix
@@ -19,8 +19,8 @@ License-File: LICENSE
19
19
  Requires-Dist: requests>=2.31
20
20
  Requires-Dist: PyJWT[crypto]<2.9,>=2.7
21
21
  Requires-Dist: PyYAML>=6
22
- Requires-Dist: Flask>=2.2.3
23
- Requires-Dist: jsonschema>=4.17
22
+ Requires-Dist: Flask<3,>=2.2.3
23
+ Requires-Dist: jsonschema>=4.23
24
24
  Requires-Dist: toposort>=1.10
25
25
  Requires-Dist: waitress>=2.1.2
26
26
  Requires-Dist: paste>=3.5.2
@@ -4,10 +4,12 @@ opentf/commons/config.py,sha256=dyus4K5Zdmcftc3Y9Z1YRkzA1KwiRLHoeAlg2_A49QM,7876
4
4
  opentf/commons/datasources.py,sha256=LjIjZbf08u1VllPN4fDss0OAg-_7gtRqgpIZ2tLuiHo,26807
5
5
  opentf/commons/expressions.py,sha256=jM_YKXVOFhvOE2aE2IuacuvxhIsOYTFs2oQkpcbWR6g,19645
6
6
  opentf/commons/pubsub.py,sha256=DVrSara5FRfNdPBwXKUkTobqGki0RPDehylTEFcJnFc,7341
7
- opentf/commons/schemas.py,sha256=YSCvlmqc7satt-OqIoYXnmhOyo9h8wIpNyKaBAY4u9c,4039
7
+ opentf/commons/schemas.py,sha256=P6jyIJR5Zw2L7vH3_jhpTiJAHHwjE6MJUmva0_41B3E,4212
8
8
  opentf/commons/selectors.py,sha256=DEpLgRAr5HXSpSYI4liXP2hLUTvOSexFa9Vfa1xIQTk,7134
9
9
  opentf/schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  opentf/schemas/abac.opentestfactory.org/v1alpha1/Policy.json,sha256=JXsfNAPSEYggeyaDutSQBeG38o4Bmcr70dPLWWeqIh8,2105
11
+ opentf/schemas/opentestfactory.org/v1/GeneratorResult.json,sha256=neoFocJGkVTQQPtnG5xnbqNLX8ivFBJbanbbPweId7s,16608
12
+ opentf/schemas/opentestfactory.org/v1/ProviderResult.json,sha256=Ej4zhCE3rCqCGKcaeAoIHwSJTV_7fw-rAxhJ52qA-Gs,9641
11
13
  opentf/schemas/opentestfactory.org/v1/Workflow.json,sha256=mpWxJfP3aV3sYzVxxWOivD55Top4J5WXTTKBzp7gjIw,22486
12
14
  opentf/schemas/opentestfactory.org/v1alpha1/AgentRegistration.json,sha256=NQykqU-lKE8LtBhBiFUcpVJq00MRG6dZsoM1xedx6uQ,1230
13
15
  opentf/schemas/opentestfactory.org/v1alpha1/AllureCollectorOutput.json,sha256=-L9DDWA0A4x54bPMn4m6Qwi2tf2nHvzIPFOElTjaVck,1366
@@ -46,11 +48,11 @@ opentf/schemas/opentestfactory.org/v1beta1/Workflow.json,sha256=QZ8mM9PhzsI9gTmw
46
48
  opentf/schemas/opentestfactory.org/v1beta2/ServiceConfig.json,sha256=rEvK2YWL5lG94_qYgR_GnLWNsaQhaQ-2kuZdWJr5NnY,3517
47
49
  opentf/scripts/launch_java_service.sh,sha256=S0jAaCuv2sZy0Gf2NGBuPX-eD531rcM-b0fNyhmzSjw,2423
48
50
  opentf/scripts/startup.py,sha256=Da2zo93pBWbdRmj-wgekgLcF94rpNc3ZkbvR8R0w8XY,21279
49
- opentf/toolkit/__init__.py,sha256=4UbExlqRO8Ew7GYRrMdEDruMIB0zTLSsoVCKfW3vPnQ,23488
51
+ opentf/toolkit/__init__.py,sha256=nbllYXON3Rzd-hU7Cred7r4hPaCPbfSJmoY7cQ6RShc,22589
50
52
  opentf/toolkit/channels.py,sha256=6xcVKHUK2FdyVKIQmPQbakngfVuQDzCcD_lInOdKpro,17171
51
- opentf/toolkit/core.py,sha256=GdmEJ0ikdMdpViEpR4jP-viqfvBUHnpiFCOXwLGThxg,9606
52
- opentf_toolkit_nightly-0.57.0.dev1071.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
53
- opentf_toolkit_nightly-0.57.0.dev1071.dist-info/METADATA,sha256=C46QttPC75AZio20HOPCH_uxLwdUoKhC-jNIIFKfgP4,1943
54
- opentf_toolkit_nightly-0.57.0.dev1071.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
55
- opentf_toolkit_nightly-0.57.0.dev1071.dist-info/top_level.txt,sha256=_gPuE6GTT6UNXy1DjtmQSfCcZb_qYA2vWmjg7a30AGk,7
56
- opentf_toolkit_nightly-0.57.0.dev1071.dist-info/RECORD,,
53
+ opentf/toolkit/core.py,sha256=KN6-z8Hmty1thZ-c0mTbcqkF6q1nNrtE8A3F6-wjX4g,9788
54
+ opentf_toolkit_nightly-0.57.0.dev1074.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
55
+ opentf_toolkit_nightly-0.57.0.dev1074.dist-info/METADATA,sha256=bE5iD0Xm2FAWCYQHF8Y2Wvwy_4dZm45uEpnNEI7BWLk,1946
56
+ opentf_toolkit_nightly-0.57.0.dev1074.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
57
+ opentf_toolkit_nightly-0.57.0.dev1074.dist-info/top_level.txt,sha256=_gPuE6GTT6UNXy1DjtmQSfCcZb_qYA2vWmjg7a30AGk,7
58
+ opentf_toolkit_nightly-0.57.0.dev1074.dist-info/RECORD,,