opentf-toolkit-nightly 0.57.0.dev1057__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/datasources.py +67 -19
- opentf/commons/schemas.py +12 -5
- opentf/schemas/opentestfactory.org/v1/GeneratorResult.json +484 -0
- opentf/schemas/opentestfactory.org/v1/ProviderResult.json +297 -0
- opentf/toolkit/__init__.py +56 -67
- opentf/toolkit/core.py +8 -2
- {opentf_toolkit_nightly-0.57.0.dev1057.dist-info → opentf_toolkit_nightly-0.57.0.dev1074.dist-info}/METADATA +3 -3
- {opentf_toolkit_nightly-0.57.0.dev1057.dist-info → opentf_toolkit_nightly-0.57.0.dev1074.dist-info}/RECORD +11 -9
- {opentf_toolkit_nightly-0.57.0.dev1057.dist-info → opentf_toolkit_nightly-0.57.0.dev1074.dist-info}/LICENSE +0 -0
- {opentf_toolkit_nightly-0.57.0.dev1057.dist-info → opentf_toolkit_nightly-0.57.0.dev1074.dist-info}/WHEEL +0 -0
- {opentf_toolkit_nightly-0.57.0.dev1057.dist-info → opentf_toolkit_nightly-0.57.0.dev1074.dist-info}/top_level.txt +0 -0
opentf/commons/datasources.py
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
"""Datasources (testcases, tags and jobs) retrieval helpers"""
|
|
16
16
|
|
|
17
|
-
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple
|
|
17
|
+
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union
|
|
18
18
|
|
|
19
19
|
from datetime import datetime
|
|
20
20
|
|
|
@@ -22,6 +22,7 @@ from datetime import datetime
|
|
|
22
22
|
from flask import current_app
|
|
23
23
|
|
|
24
24
|
from opentf.commons.expressions import evaluate_bool
|
|
25
|
+
from opentf.commons.selectors import match_selectors
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
########################################################################
|
|
@@ -99,9 +100,11 @@ def parse_testcase_name(full_name: str) -> Tuple[str, str]:
|
|
|
99
100
|
## Datasource: Testcases
|
|
100
101
|
|
|
101
102
|
|
|
102
|
-
def in_scope(expr: str, contexts: Dict[str, Any]) -> bool:
|
|
103
|
+
def in_scope(expr: Union[str, bool], contexts: Dict[str, Any]) -> bool:
|
|
103
104
|
"""Safely evaluate datasource scope."""
|
|
104
105
|
try:
|
|
106
|
+
if isinstance(expr, bool):
|
|
107
|
+
return expr
|
|
105
108
|
return evaluate_bool(expr, contexts)
|
|
106
109
|
except ValueError as err:
|
|
107
110
|
raise ValueError(f'Invalid conditional {expr}: {err}.')
|
|
@@ -399,9 +402,7 @@ def _get_testresult_labels(
|
|
|
399
402
|
|
|
400
403
|
|
|
401
404
|
def _make_testcase_from_testresult(
|
|
402
|
-
item: Dict[str, Any],
|
|
403
|
-
labels: Dict[str, Any],
|
|
404
|
-
scope: str,
|
|
405
|
+
item: Dict[str, Any], labels: Dict[str, Any], scope: Union[str, bool]
|
|
405
406
|
) -> Dict[str, Any]:
|
|
406
407
|
suite_name, testcase_name = parse_testcase_name(item['name'])
|
|
407
408
|
item_data = {
|
|
@@ -434,8 +435,55 @@ def _make_testcase_from_testresult(
|
|
|
434
435
|
return testcase
|
|
435
436
|
|
|
436
437
|
|
|
438
|
+
def _get_max_count(state: Dict[str, Any]) -> int:
|
|
439
|
+
if state['reset']:
|
|
440
|
+
return state['per_page'] * state['page']
|
|
441
|
+
return state['per_page']
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def _extract_testcases(
|
|
445
|
+
testresults: List[Dict[str, Any]],
|
|
446
|
+
state: Dict[str, Any],
|
|
447
|
+
scope: Union[str, bool],
|
|
448
|
+
events: List[Dict[str, Any]],
|
|
449
|
+
) -> Dict[str, Dict[str, Any]]:
|
|
450
|
+
testcases = {}
|
|
451
|
+
items = 0
|
|
452
|
+
for i, testresult in enumerate(
|
|
453
|
+
testresults[state['last_notification_used'] :],
|
|
454
|
+
start=state['last_notification_used'],
|
|
455
|
+
):
|
|
456
|
+
if i == state['last_notification_used']:
|
|
457
|
+
last_testresult_used = state['last_testresult_used']
|
|
458
|
+
else:
|
|
459
|
+
last_testresult_used = 0
|
|
460
|
+
execution_id = testresult['metadata']['attachment_origin'][0]
|
|
461
|
+
labels = _get_testresult_labels(execution_id, events)
|
|
462
|
+
if not labels:
|
|
463
|
+
continue
|
|
464
|
+
for j, item in enumerate(
|
|
465
|
+
testresult['spec']['testResults'][last_testresult_used:],
|
|
466
|
+
start=last_testresult_used,
|
|
467
|
+
):
|
|
468
|
+
testcase = _make_testcase_from_testresult(item, labels, scope)
|
|
469
|
+
if not testcase:
|
|
470
|
+
continue
|
|
471
|
+
if not match_selectors(testcase, state['fieldselector']):
|
|
472
|
+
continue
|
|
473
|
+
testcases[item['id']] = testcase
|
|
474
|
+
items += 1
|
|
475
|
+
if items > _get_max_count(state):
|
|
476
|
+
state['last_notification_used'] = i
|
|
477
|
+
state['last_testresult_used'] = j
|
|
478
|
+
return testcases
|
|
479
|
+
|
|
480
|
+
state['last_notification_used'] = i + 1
|
|
481
|
+
state['last_testresult_used'] = 0
|
|
482
|
+
return testcases
|
|
483
|
+
|
|
484
|
+
|
|
437
485
|
def get_testcases(
|
|
438
|
-
events: List[Dict[str, Any]], scope: str =
|
|
486
|
+
events: List[Dict[str, Any]], scope: Union[str, bool] = True, state=None
|
|
439
487
|
) -> Dict[str, Dict[str, Any]]:
|
|
440
488
|
"""Extract metadata for each test result.
|
|
441
489
|
|
|
@@ -489,6 +537,9 @@ def get_testcases(
|
|
|
489
537
|
A _ValueError_ exception is raised if there were no test results in
|
|
490
538
|
`events` or some scope errors occured retrieving test results.
|
|
491
539
|
"""
|
|
540
|
+
if not state:
|
|
541
|
+
raise ValueError('No workflow cache state received from observer.')
|
|
542
|
+
|
|
492
543
|
if _uses_inception(events):
|
|
493
544
|
testresults = _get_inception_testresults(events)
|
|
494
545
|
else:
|
|
@@ -497,17 +548,10 @@ def get_testcases(
|
|
|
497
548
|
if not testresults:
|
|
498
549
|
raise ValueError('No test results in events.')
|
|
499
550
|
|
|
500
|
-
testcases =
|
|
501
|
-
for testresult in testresults:
|
|
502
|
-
execution_id = testresult['metadata']['attachment_origin'][0]
|
|
503
|
-
labels = _get_testresult_labels(execution_id, events)
|
|
504
|
-
if not labels:
|
|
505
|
-
continue
|
|
506
|
-
for item in testresult['spec']['testResults']:
|
|
507
|
-
if testcase := _make_testcase_from_testresult(item, labels, scope):
|
|
508
|
-
testcases[item['id']] = testcase
|
|
551
|
+
testcases = _extract_testcases(testresults, state, scope, events)
|
|
509
552
|
if not testcases:
|
|
510
553
|
raise ValueError(f'No test cases matching scope `{scope}`.')
|
|
554
|
+
|
|
511
555
|
return testcases
|
|
512
556
|
|
|
513
557
|
|
|
@@ -538,7 +582,9 @@ def _make_tag_datasource(tag: str, parent: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
538
582
|
}
|
|
539
583
|
|
|
540
584
|
|
|
541
|
-
def get_tags(
|
|
585
|
+
def get_tags(
|
|
586
|
+
events: List[Dict[str, Any]], scope: Union[str, bool] = True, state=None
|
|
587
|
+
) -> Dict[str, Any]:
|
|
542
588
|
"""Extract metadata for each execution environment tag.
|
|
543
589
|
|
|
544
590
|
# Required parameters:
|
|
@@ -574,7 +620,7 @@ def get_tags(events: List[Dict[str, Any]], scope: str = 'true') -> Dict[str, Any
|
|
|
574
620
|
'No job events found in workflow. Cannot extract data for tags.'
|
|
575
621
|
)
|
|
576
622
|
try:
|
|
577
|
-
testcases = get_testcases(events, scope)
|
|
623
|
+
testcases = get_testcases(events, scope, state)
|
|
578
624
|
except ValueError as err:
|
|
579
625
|
if str(err).startswith('[SCOPE ERROR] '):
|
|
580
626
|
raise ValueError(str(err))
|
|
@@ -711,7 +757,9 @@ def _make_job_datasource(
|
|
|
711
757
|
}
|
|
712
758
|
|
|
713
759
|
|
|
714
|
-
def get_jobs(
|
|
760
|
+
def get_jobs(
|
|
761
|
+
events: List[Dict[str, Any]], scope: Union[str, bool] = True, state=None
|
|
762
|
+
) -> Dict[str, Any]:
|
|
715
763
|
"""Extract metadata for each job.
|
|
716
764
|
|
|
717
765
|
# Required parameters:
|
|
@@ -764,7 +812,7 @@ def get_jobs(events: List[Dict[str, Any]], scope: str = 'true') -> Dict[str, Any
|
|
|
764
812
|
)
|
|
765
813
|
|
|
766
814
|
try:
|
|
767
|
-
testcases = get_testcases(events, scope)
|
|
815
|
+
testcases = get_testcases(events, scope, state)
|
|
768
816
|
except ValueError as err:
|
|
769
817
|
if str(err).startswith('[SCOPE ERROR] '):
|
|
770
818
|
raise ValueError(str(err))
|
opentf/commons/schemas.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c)
|
|
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
|
|
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/
|
|
44
|
+
GENERATORRESULT = 'opentestfactory.org/v1/GeneratorResult'
|
|
45
45
|
|
|
46
46
|
PROVIDERCOMMAND = 'opentestfactory.org/v1beta1/ProviderCommand'
|
|
47
|
-
PROVIDERRESULT = 'opentestfactory.org/
|
|
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
|
-
|
|
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
|
+
}
|
opentf/toolkit/__init__.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c)
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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__`
|
|
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
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
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(
|
|
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.
|
|
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
|
|
23
|
-
Requires-Dist: jsonschema>=4.
|
|
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
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
opentf/commons/__init__.py,sha256=Uq-7WvkMoBiF3C1KnhwIL4LCKpT8EvomnuG4MBYpIhs,21994
|
|
2
2
|
opentf/commons/auth.py,sha256=bM2Z3kxm2Wku1lKXaRAIg37LHvXWAXIZIqjplDfN2P8,15899
|
|
3
3
|
opentf/commons/config.py,sha256=dyus4K5Zdmcftc3Y9Z1YRkzA1KwiRLHoeAlg2_A49QM,7876
|
|
4
|
-
opentf/commons/datasources.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
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.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|