opentf-toolkit-nightly 0.62.0.dev1347__py3-none-any.whl → 0.62.0.dev1370__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/__init__.py +15 -178
- opentf/commons/config.py +1 -1
- opentf/commons/schemas.py +296 -5
- opentf/schemas/config.opentestfactory.org/v1/Descriptor.json +323 -0
- opentf/schemas/opentestfactory.org/v1/Workflow.json +0 -1
- opentf/toolkit/__init__.py +25 -30
- {opentf_toolkit_nightly-0.62.0.dev1347.dist-info → opentf_toolkit_nightly-0.62.0.dev1370.dist-info}/METADATA +1 -1
- {opentf_toolkit_nightly-0.62.0.dev1347.dist-info → opentf_toolkit_nightly-0.62.0.dev1370.dist-info}/RECORD +11 -10
- {opentf_toolkit_nightly-0.62.0.dev1347.dist-info → opentf_toolkit_nightly-0.62.0.dev1370.dist-info}/WHEEL +0 -0
- {opentf_toolkit_nightly-0.62.0.dev1347.dist-info → opentf_toolkit_nightly-0.62.0.dev1370.dist-info}/licenses/LICENSE +0 -0
- {opentf_toolkit_nightly-0.62.0.dev1347.dist-info → opentf_toolkit_nightly-0.62.0.dev1370.dist-info}/top_level.txt +0 -0
opentf/commons/__init__.py
CHANGED
|
@@ -27,8 +27,6 @@ import jwt
|
|
|
27
27
|
|
|
28
28
|
from flask import Flask, current_app, make_response, request, g, Response
|
|
29
29
|
|
|
30
|
-
from toposort import toposort, CircularDependencyError
|
|
31
|
-
|
|
32
30
|
from .auth import (
|
|
33
31
|
initialize_authn_authz,
|
|
34
32
|
get_user_accessible_namespaces,
|
|
@@ -220,6 +218,10 @@ def _get_contextparameter_spec(app: Flask, name: str) -> Optional[Dict[str, Any]
|
|
|
220
218
|
parameters = app.config[PARAMETERS_KEY]
|
|
221
219
|
try:
|
|
222
220
|
spec = get_named(name, parameters)
|
|
221
|
+
if spec.get('type') == 'int':
|
|
222
|
+
spec['type'] = 'number'
|
|
223
|
+
if spec.get('type') == 'bool':
|
|
224
|
+
spec['type'] = 'boolean'
|
|
223
225
|
except ValueError:
|
|
224
226
|
spec = None
|
|
225
227
|
return spec
|
|
@@ -429,7 +431,7 @@ def make_app(
|
|
|
429
431
|
|
|
430
432
|
`CONFIG` is a dictionary, the complete config file. `CONTEXT` is a
|
|
431
433
|
subset of `CONFIG`, the current entry in `CONFIG['context']`. It is
|
|
432
|
-
also a dictionary. `DESCRIPTOR` is the service
|
|
434
|
+
also a dictionary. `DESCRIPTOR` is the service descriptors.
|
|
433
435
|
|
|
434
436
|
# Raised Exception
|
|
435
437
|
|
|
@@ -466,6 +468,12 @@ def make_app(
|
|
|
466
468
|
app.config['DESCRIPTOR'] = (
|
|
467
469
|
descriptor if isinstance(descriptor, list) else [descriptor]
|
|
468
470
|
)
|
|
471
|
+
try:
|
|
472
|
+
validate_descriptors(app.config['DESCRIPTOR'])
|
|
473
|
+
except ValueError as err:
|
|
474
|
+
app.logger.error('Invalid descriptor: %s', err)
|
|
475
|
+
sys.exit(2)
|
|
476
|
+
|
|
469
477
|
app.route('/health', methods=['GET'])(lambda: 'OK')
|
|
470
478
|
app.before_request(_make_authenticator(context))
|
|
471
479
|
app.after_request(_add_securityheaders)
|
|
@@ -566,11 +574,10 @@ def get_context_parameter(
|
|
|
566
574
|
)
|
|
567
575
|
|
|
568
576
|
if spec:
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
_fatal(f'Context parameter "{name}" not an integer: {err}.')
|
|
577
|
+
try:
|
|
578
|
+
val = validate_value(spec, val)
|
|
579
|
+
except ValueError as err:
|
|
580
|
+
_fatal(f'Context parameter "{name}": {err}')
|
|
574
581
|
desc = spec['descriptiveName'][0].upper() + spec['descriptiveName'][1:]
|
|
575
582
|
if 'minValue' in spec and val < spec['minValue']:
|
|
576
583
|
_fatal(f'{desc} must be greater than {spec["minValue"]-1}.')
|
|
@@ -746,173 +753,3 @@ def annotate_response(
|
|
|
746
753
|
if seen := {k for k in processed if request.args.get(k) is not None}:
|
|
747
754
|
response.headers['X-Processed-Query'] = ','.join(seen)
|
|
748
755
|
return response
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
########################################################################
|
|
752
|
-
# Pipelines Helpers
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
def validate_pipeline(
|
|
756
|
-
workflow: Dict[str, Any],
|
|
757
|
-
) -> Tuple[bool, Union[str, List[List[str]]]]:
|
|
758
|
-
"""Validate workflow jobs, looking for circular dependencies.
|
|
759
|
-
|
|
760
|
-
# Required parameters
|
|
761
|
-
|
|
762
|
-
- workflow: a dictionary
|
|
763
|
-
|
|
764
|
-
# Returned value
|
|
765
|
-
|
|
766
|
-
A (`bool`, extra) pair.
|
|
767
|
-
|
|
768
|
-
If there is a dependency on an non-existing job, returns
|
|
769
|
-
`(False, description (a string))`.
|
|
770
|
-
|
|
771
|
-
If there are circular dependencies in the workflow jobs, returns
|
|
772
|
-
`(False, description (a string))`.
|
|
773
|
-
|
|
774
|
-
If there are no circular dependencies, returns `(True, jobs)` where
|
|
775
|
-
`jobs` is an ordered list of job names lists. Each item in the
|
|
776
|
-
returned list is a set of jobs that can run in parallel.
|
|
777
|
-
"""
|
|
778
|
-
try:
|
|
779
|
-
jobs = {}
|
|
780
|
-
for job_name, job_definition in workflow['jobs'].items():
|
|
781
|
-
if needs := job_definition.get('needs'):
|
|
782
|
-
if isinstance(needs, list):
|
|
783
|
-
jobs[job_name] = set(needs)
|
|
784
|
-
else:
|
|
785
|
-
jobs[job_name] = {needs}
|
|
786
|
-
else:
|
|
787
|
-
jobs[job_name] = set()
|
|
788
|
-
for src, dependencies in jobs.items():
|
|
789
|
-
for dep in dependencies:
|
|
790
|
-
if dep not in jobs:
|
|
791
|
-
return (
|
|
792
|
-
False,
|
|
793
|
-
f"Job '{src}' has a dependency on job '{dep}' which does not exist.",
|
|
794
|
-
)
|
|
795
|
-
|
|
796
|
-
return True, [list(items) for items in toposort(jobs)]
|
|
797
|
-
except CircularDependencyError as err:
|
|
798
|
-
return False, str(err)
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
def _normalize_inputs(inputs: Dict[str, Any]) -> None:
|
|
802
|
-
"""Normalize inputs.
|
|
803
|
-
|
|
804
|
-
The 'normalized' form for inputs is with `-` separators, not `_`.
|
|
805
|
-
|
|
806
|
-
Non-normalized inputs are removed from the dictionary.
|
|
807
|
-
|
|
808
|
-
# Raised exceptions
|
|
809
|
-
|
|
810
|
-
A _ValueError_ exception is raised if an input is provided twice, in
|
|
811
|
-
a normalized as well as a non-normalized form.
|
|
812
|
-
"""
|
|
813
|
-
for key in inputs.copy():
|
|
814
|
-
if '_' in key:
|
|
815
|
-
normalized = key.replace('_', '-')
|
|
816
|
-
if normalized in inputs:
|
|
817
|
-
raise ValueError(
|
|
818
|
-
f'Both "{key}" and "{normalized}" specified in inputs.'
|
|
819
|
-
)
|
|
820
|
-
inputs[normalized] = inputs.pop(key)
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
def _set_default(inputs: Dict[str, Any], key: str, definition: Dict[str, Any]) -> None:
|
|
824
|
-
if (default := definition.get('default')) is not None:
|
|
825
|
-
inputs[key] = default
|
|
826
|
-
elif type_ := definition.get('type'):
|
|
827
|
-
if type_ == 'string':
|
|
828
|
-
inputs[key] = ''
|
|
829
|
-
elif type_ == 'number':
|
|
830
|
-
inputs[key] = 0
|
|
831
|
-
elif type_ == 'boolean':
|
|
832
|
-
inputs[key] = False
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
def validate_inputs(
|
|
836
|
-
declaration: Dict[str, Dict[str, Any]],
|
|
837
|
-
inputs: Dict[str, Any],
|
|
838
|
-
additional_inputs: bool = False,
|
|
839
|
-
normalize: bool = True,
|
|
840
|
-
) -> None:
|
|
841
|
-
"""Validate inputs.
|
|
842
|
-
|
|
843
|
-
Default values are filled in `inputs` as appropriate.
|
|
844
|
-
|
|
845
|
-
Input names are normalized to use hyphens instead of underscores
|
|
846
|
-
by default (declaration is expected to be normalized).
|
|
847
|
-
|
|
848
|
-
If `normalize` is set, non-normalized inputs are removed from the
|
|
849
|
-
dictionary.
|
|
850
|
-
|
|
851
|
-
Choices values are validated.
|
|
852
|
-
|
|
853
|
-
Types are enforced if declared as `boolean`, `number`, or `string`.
|
|
854
|
-
Conversion is performed if needed.
|
|
855
|
-
|
|
856
|
-
# Required parameters
|
|
857
|
-
|
|
858
|
-
- declaration: a dictionary
|
|
859
|
-
- inputs: a dictionary
|
|
860
|
-
|
|
861
|
-
# Optional parameters
|
|
862
|
-
|
|
863
|
-
- additional_inputs: a boolean (False by default)
|
|
864
|
-
- normalize: a boolean (True by default)
|
|
865
|
-
|
|
866
|
-
# Raised exceptions
|
|
867
|
-
|
|
868
|
-
A _ValueError_ exception is raised if inputs do not match
|
|
869
|
-
declaration.
|
|
870
|
-
"""
|
|
871
|
-
if normalize and not any(key.startswith('{') for key in declaration):
|
|
872
|
-
_normalize_inputs(inputs)
|
|
873
|
-
|
|
874
|
-
for key, definition in declaration.items():
|
|
875
|
-
if key.startswith('{'):
|
|
876
|
-
continue
|
|
877
|
-
if key not in inputs:
|
|
878
|
-
if definition.get('required', False):
|
|
879
|
-
raise ValueError(f'Mandatory input "{key}" not provided.')
|
|
880
|
-
_set_default(inputs, key, definition)
|
|
881
|
-
continue
|
|
882
|
-
val = inputs.get(key)
|
|
883
|
-
type_ = definition.get('type')
|
|
884
|
-
if type_ == 'choice':
|
|
885
|
-
if val not in definition['options']:
|
|
886
|
-
allowed = '", "'.join(sorted(definition['options']))
|
|
887
|
-
raise ValueError(
|
|
888
|
-
f'Invalid value "{val}" for input "{key}". Allowed values: "{allowed}".'
|
|
889
|
-
)
|
|
890
|
-
elif type_ == 'boolean' and not isinstance(val, bool):
|
|
891
|
-
if isinstance(val, str) and val.lower() in ('true', 'false'):
|
|
892
|
-
inputs[key] = val.lower() == 'true'
|
|
893
|
-
continue
|
|
894
|
-
raise ValueError(
|
|
895
|
-
f'Invalid value "{val}" for input "{key}". Allowed values: "true", "false".'
|
|
896
|
-
)
|
|
897
|
-
elif type_ == 'number' and not isinstance(val, (int, float)):
|
|
898
|
-
try:
|
|
899
|
-
inputs[key] = int(val)
|
|
900
|
-
except ValueError:
|
|
901
|
-
try:
|
|
902
|
-
inputs[key] = float(val)
|
|
903
|
-
except ValueError:
|
|
904
|
-
raise ValueError(
|
|
905
|
-
f'Invalid value "{val}" for input "{key}". Expected a number.'
|
|
906
|
-
)
|
|
907
|
-
elif type_ == 'string' and not isinstance(val, str):
|
|
908
|
-
inputs[key] = str(val)
|
|
909
|
-
|
|
910
|
-
if additional_inputs:
|
|
911
|
-
return
|
|
912
|
-
|
|
913
|
-
if unexpected := set(inputs) - set(declaration):
|
|
914
|
-
allowed = '", "'.join(sorted(declaration))
|
|
915
|
-
unexpected = '", "'.join(sorted(unexpected))
|
|
916
|
-
raise ValueError(
|
|
917
|
-
f'Unexpected inputs "{unexpected}" found. Allowed inputs: "{allowed}".'
|
|
918
|
-
)
|
opentf/commons/config.py
CHANGED
|
@@ -220,7 +220,7 @@ def read_config(
|
|
|
220
220
|
else:
|
|
221
221
|
configfile = altconfig or configfile
|
|
222
222
|
try:
|
|
223
|
-
config = read_and_validate(
|
|
223
|
+
config = read_and_validate(schema or SERVICECONFIG, configfile)
|
|
224
224
|
except (ValueError, OSError) as err:
|
|
225
225
|
raise ConfigError(f'Could not read configfile "{configfile}": {err}.')
|
|
226
226
|
|
opentf/commons/schemas.py
CHANGED
|
@@ -12,17 +12,19 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
-
"""Helpers for the OpenTestFactory schemas."""
|
|
15
|
+
"""Helpers for the OpenTestFactory schemas and validation."""
|
|
16
16
|
|
|
17
|
-
from typing import Any, Dict, Optional, Tuple
|
|
17
|
+
from typing import Any, Dict, Iterable, List, Optional, Tuple, Union
|
|
18
18
|
|
|
19
19
|
import json
|
|
20
20
|
import logging
|
|
21
21
|
import os
|
|
22
22
|
|
|
23
23
|
from jsonschema import Draft201909Validator, ValidationError
|
|
24
|
+
from toposort import toposort, CircularDependencyError
|
|
24
25
|
from yaml import safe_load
|
|
25
26
|
|
|
27
|
+
|
|
26
28
|
import opentf.schemas
|
|
27
29
|
|
|
28
30
|
|
|
@@ -65,6 +67,8 @@ RETENTION_POLICY = 'opentestfactory.org/v1alpha1/RetentionPolicy'
|
|
|
65
67
|
TRACKER_PUBLISHER = 'opentestfactory.org/v1alpha1/TrackerPublisher'
|
|
66
68
|
INSIGHT_COLLECTOR = 'opentestfactory.org/v1alpha1/InsightCollector'
|
|
67
69
|
|
|
70
|
+
PLUGIN_DESCRIPTOR = 'config.opentestfactory.org/v1/Descriptor'
|
|
71
|
+
|
|
68
72
|
|
|
69
73
|
########################################################################
|
|
70
74
|
# JSON Schema Helpers
|
|
@@ -133,17 +137,17 @@ def validate_schema(
|
|
|
133
137
|
return True, None
|
|
134
138
|
|
|
135
139
|
|
|
136
|
-
def read_and_validate(
|
|
140
|
+
def read_and_validate(schema: str, filename: str) -> Dict[str, Any]:
|
|
137
141
|
"""Read and validate a JSON or YAML file.
|
|
138
142
|
|
|
139
143
|
# Required parameters
|
|
140
144
|
|
|
145
|
+
- schema: a string, the schema name (its kind)
|
|
141
146
|
- filename: a string, the file name
|
|
142
|
-
- schema: a string, the schema to validate the file content
|
|
143
147
|
|
|
144
148
|
# Returned value
|
|
145
149
|
|
|
146
|
-
A dictionary, the
|
|
150
|
+
A dictionary, the valid content.
|
|
147
151
|
|
|
148
152
|
# Raised exceptions
|
|
149
153
|
|
|
@@ -161,3 +165,290 @@ def read_and_validate(filename: str, schema: str) -> Dict[str, Any]:
|
|
|
161
165
|
if not valid:
|
|
162
166
|
raise ValueError(f'Invalid content: {extra}.')
|
|
163
167
|
return config
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
########################################################################
|
|
171
|
+
# Pipelines Helpers
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def validate_pipeline(
|
|
175
|
+
workflow: Dict[str, Any],
|
|
176
|
+
) -> Tuple[bool, Union[str, List[List[str]]]]:
|
|
177
|
+
"""Validate workflow jobs, looking for circular dependencies.
|
|
178
|
+
|
|
179
|
+
# Required parameters
|
|
180
|
+
|
|
181
|
+
- workflow: a dictionary
|
|
182
|
+
|
|
183
|
+
# Returned value
|
|
184
|
+
|
|
185
|
+
A (`bool`, extra) pair.
|
|
186
|
+
|
|
187
|
+
If there is a dependency on an non-existing job, returns
|
|
188
|
+
`(False, description (a string))`.
|
|
189
|
+
|
|
190
|
+
If there are circular dependencies in the workflow jobs, returns
|
|
191
|
+
`(False, description (a string))`.
|
|
192
|
+
|
|
193
|
+
If there are no circular dependencies, returns `(True, jobs)` where
|
|
194
|
+
`jobs` is an ordered list of job names lists. Each item in the
|
|
195
|
+
returned list is a set of jobs that can run in parallel.
|
|
196
|
+
"""
|
|
197
|
+
jobs = {}
|
|
198
|
+
for job_name, job_definition in workflow['jobs'].items():
|
|
199
|
+
if needs := job_definition.get('needs'):
|
|
200
|
+
if isinstance(needs, list):
|
|
201
|
+
jobs[job_name] = set(needs)
|
|
202
|
+
else:
|
|
203
|
+
jobs[job_name] = {needs}
|
|
204
|
+
else:
|
|
205
|
+
jobs[job_name] = set()
|
|
206
|
+
for src, dependencies in jobs.items():
|
|
207
|
+
for dep in dependencies:
|
|
208
|
+
if dep not in jobs:
|
|
209
|
+
return (
|
|
210
|
+
False,
|
|
211
|
+
f'Job "{src}" has a dependency on job "{dep}" which does not exist.',
|
|
212
|
+
)
|
|
213
|
+
try:
|
|
214
|
+
return True, [list(items) for items in toposort(jobs)]
|
|
215
|
+
except CircularDependencyError as err:
|
|
216
|
+
return False, str(err)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def validate_workflow(workflow: Dict[str, Any]) -> List[List[str]]:
|
|
220
|
+
"""Validate workflow.
|
|
221
|
+
|
|
222
|
+
# Required parameters
|
|
223
|
+
|
|
224
|
+
- workflow: a dictionary
|
|
225
|
+
|
|
226
|
+
# Returned value
|
|
227
|
+
|
|
228
|
+
An ordered list of job names lists. Each item in the returned list
|
|
229
|
+
is a set of jobs that can run in parallel.
|
|
230
|
+
|
|
231
|
+
# Raised exceptions
|
|
232
|
+
|
|
233
|
+
A _ValueError_ exception is raised if the workflow is not a valid.
|
|
234
|
+
"""
|
|
235
|
+
valid, extra = validate_schema(WORKFLOW, workflow)
|
|
236
|
+
if not valid:
|
|
237
|
+
raise ValueError(extra)
|
|
238
|
+
|
|
239
|
+
if declaration := workflow.get('inputs'):
|
|
240
|
+
try:
|
|
241
|
+
_validate_defaults(declaration)
|
|
242
|
+
except ValueError as err:
|
|
243
|
+
raise ValueError(
|
|
244
|
+
f'Invalid "inputs" section, default value for {err}'
|
|
245
|
+
) from None
|
|
246
|
+
|
|
247
|
+
valid, extra = validate_pipeline(workflow)
|
|
248
|
+
if not valid:
|
|
249
|
+
raise ValueError(extra)
|
|
250
|
+
|
|
251
|
+
return extra
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
# Inputs Helpers
|
|
255
|
+
|
|
256
|
+
INPUTSTYPE_VALIDATION = {'string': (str,), 'boolean': (bool,), 'number': (int, float)}
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def _normalize_inputs(inputs: Dict[str, Any]) -> None:
|
|
260
|
+
"""Normalize inputs.
|
|
261
|
+
|
|
262
|
+
The 'normalized' form for inputs is with `-` separators, not `_`.
|
|
263
|
+
|
|
264
|
+
Non-normalized inputs are removed from the dictionary.
|
|
265
|
+
|
|
266
|
+
# Raised exceptions
|
|
267
|
+
|
|
268
|
+
A _ValueError_ exception is raised if an input is provided twice, in
|
|
269
|
+
a normalized as well as a non-normalized form.
|
|
270
|
+
"""
|
|
271
|
+
for key in inputs.copy():
|
|
272
|
+
if '_' not in key:
|
|
273
|
+
continue
|
|
274
|
+
normalized = key.replace('_', '-')
|
|
275
|
+
if normalized in inputs:
|
|
276
|
+
raise ValueError(f'Both "{key}" and "{normalized}" specified in inputs.')
|
|
277
|
+
inputs[normalized] = inputs.pop(key)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def _set_default(inputs: Dict[str, Any], key: str, spec: Dict[str, Any]) -> None:
|
|
281
|
+
if (default := spec.get('default')) is not None:
|
|
282
|
+
inputs[key] = default
|
|
283
|
+
elif type_ := spec.get('type'):
|
|
284
|
+
if type_ == 'string':
|
|
285
|
+
inputs[key] = ''
|
|
286
|
+
elif type_ == 'number':
|
|
287
|
+
inputs[key] = 0
|
|
288
|
+
elif type_ == 'boolean':
|
|
289
|
+
inputs[key] = False
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def validate_value(spec: Dict[str, Any], val: Any) -> Any:
|
|
293
|
+
"""Validate a value.
|
|
294
|
+
|
|
295
|
+
# Required parameters
|
|
296
|
+
|
|
297
|
+
- spec: a dictionary
|
|
298
|
+
- val: the value to validate
|
|
299
|
+
|
|
300
|
+
# Returned value
|
|
301
|
+
|
|
302
|
+
The validated value, converted to the expected type if appropriate.
|
|
303
|
+
|
|
304
|
+
# Raised exceptions
|
|
305
|
+
|
|
306
|
+
A _ValueError_ exception is raised if the validation fails.
|
|
307
|
+
"""
|
|
308
|
+
type_ = spec.get('type')
|
|
309
|
+
|
|
310
|
+
if type_ == 'choice':
|
|
311
|
+
if val in spec['options']:
|
|
312
|
+
return val
|
|
313
|
+
allowed = '", "'.join(sorted(spec['options']))
|
|
314
|
+
raise ValueError(f'Invalid value "{val}". Allowed values: "{allowed}".')
|
|
315
|
+
|
|
316
|
+
if isinstance(val, INPUTSTYPE_VALIDATION.get(type_, object)):
|
|
317
|
+
return val
|
|
318
|
+
|
|
319
|
+
if type_ == 'boolean':
|
|
320
|
+
if isinstance(val, str) and val.lower() in ('true', 'false'):
|
|
321
|
+
return val.lower() == 'true'
|
|
322
|
+
raise ValueError(f'Invalid value "{val}". Allowed values: "true", "false".')
|
|
323
|
+
if type_ == 'number':
|
|
324
|
+
for cast in (int, float):
|
|
325
|
+
try:
|
|
326
|
+
return cast(val)
|
|
327
|
+
except (ValueError, TypeError):
|
|
328
|
+
pass
|
|
329
|
+
raise ValueError(f'Invalid value "{val}". Expected a number.')
|
|
330
|
+
if type_ == 'string':
|
|
331
|
+
return str(val)
|
|
332
|
+
|
|
333
|
+
raise ValueError(f'Invalid value "{val}". Expected a {type_}.')
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def validate_inputs(
|
|
337
|
+
declaration: Dict[str, Dict[str, Any]],
|
|
338
|
+
inputs: Dict[str, Any],
|
|
339
|
+
additional_inputs: bool = False,
|
|
340
|
+
normalize: bool = True,
|
|
341
|
+
) -> None:
|
|
342
|
+
"""Validate inputs.
|
|
343
|
+
|
|
344
|
+
Default values are filled in `inputs` as appropriate.
|
|
345
|
+
|
|
346
|
+
Input names are normalized to use hyphens instead of underscores
|
|
347
|
+
by default (declaration is expected to be normalized in this case).
|
|
348
|
+
|
|
349
|
+
If `normalize` is set, non-normalized inputs are removed from the
|
|
350
|
+
dictionary.
|
|
351
|
+
|
|
352
|
+
If declaration contains a pattern (a key starting with '{'),
|
|
353
|
+
normalization is ignored.
|
|
354
|
+
|
|
355
|
+
Choices values are validated.
|
|
356
|
+
|
|
357
|
+
Types are enforced if declared as `boolean`, `number`, or `string`.
|
|
358
|
+
Conversion is performed if needed.
|
|
359
|
+
|
|
360
|
+
# Required parameters
|
|
361
|
+
|
|
362
|
+
- declaration: a dictionary
|
|
363
|
+
- inputs: a dictionary
|
|
364
|
+
|
|
365
|
+
# Optional parameters
|
|
366
|
+
|
|
367
|
+
- additional_inputs: a boolean (False by default)
|
|
368
|
+
- normalize: a boolean (True by default)
|
|
369
|
+
|
|
370
|
+
# Raised exceptions
|
|
371
|
+
|
|
372
|
+
A _ValueError_ exception is raised if inputs do not match
|
|
373
|
+
declaration.
|
|
374
|
+
"""
|
|
375
|
+
if normalize and not any(key.startswith('{') for key in declaration):
|
|
376
|
+
_normalize_inputs(inputs)
|
|
377
|
+
|
|
378
|
+
for key, spec in declaration.items():
|
|
379
|
+
if key.startswith('{'):
|
|
380
|
+
continue
|
|
381
|
+
if key not in inputs:
|
|
382
|
+
if spec.get('required'):
|
|
383
|
+
raise ValueError(f'Mandatory input "{key}" not provided.')
|
|
384
|
+
_set_default(inputs, key, spec)
|
|
385
|
+
continue
|
|
386
|
+
try:
|
|
387
|
+
inputs[key] = validate_value(spec, inputs[key])
|
|
388
|
+
except ValueError as err:
|
|
389
|
+
raise ValueError(f'Input "{key}": {err}') from None
|
|
390
|
+
|
|
391
|
+
if additional_inputs:
|
|
392
|
+
return
|
|
393
|
+
|
|
394
|
+
if unexpected := set(inputs) - set(declaration):
|
|
395
|
+
allowed = '", "'.join(sorted(declaration))
|
|
396
|
+
unexpected = '", "'.join(sorted(unexpected))
|
|
397
|
+
raise ValueError(
|
|
398
|
+
f'Unexpected inputs "{unexpected}" found. Allowed inputs: "{allowed}".'
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def _validate_defaults(declaration: Dict[str, Any]) -> None:
|
|
403
|
+
"""Validate defaults.
|
|
404
|
+
|
|
405
|
+
Ensure `default`, if specified, is of the correct type and range.
|
|
406
|
+
"""
|
|
407
|
+
for key, spec in declaration.items():
|
|
408
|
+
type_ = spec.get('type')
|
|
409
|
+
if not (type_ and 'default' in spec):
|
|
410
|
+
continue
|
|
411
|
+
default = spec['default']
|
|
412
|
+
if expected := INPUTSTYPE_VALIDATION.get(type_):
|
|
413
|
+
if not isinstance(default, expected):
|
|
414
|
+
raise ValueError(f'"{key}" must be a {type_}, got {repr(default)}.')
|
|
415
|
+
continue
|
|
416
|
+
if type_ == 'choice' and default not in spec.get('options'):
|
|
417
|
+
allowed = '", "'.join(sorted(spec.get('options')))
|
|
418
|
+
raise ValueError(
|
|
419
|
+
f'"{key}" must be one of "{allowed}", got {repr(default)}.'
|
|
420
|
+
)
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def validate_descriptors(descriptors: Iterable[Dict[str, Any]]) -> None:
|
|
424
|
+
"""Validate descriptors.
|
|
425
|
+
|
|
426
|
+
Validate descriptors against PLUGIN_DESCRIPTOR schema for plugins.
|
|
427
|
+
|
|
428
|
+
If applicable, `default` values are checked against the input's
|
|
429
|
+
`type`.
|
|
430
|
+
|
|
431
|
+
# Required parameters
|
|
432
|
+
|
|
433
|
+
- descriptors: a series of manifests
|
|
434
|
+
|
|
435
|
+
# Raised exceptions
|
|
436
|
+
|
|
437
|
+
A _ValueError_ exception is raised if a descriptor is invalid.
|
|
438
|
+
"""
|
|
439
|
+
for manifest in descriptors:
|
|
440
|
+
try:
|
|
441
|
+
metadata = manifest['metadata']
|
|
442
|
+
what = f'{metadata["name"].lower()} {metadata.get("action", "")}'.strip()
|
|
443
|
+
except Exception as err:
|
|
444
|
+
raise ValueError(f'Missing "metadata.name" section: {err}.') from None
|
|
445
|
+
valid, extra = validate_schema(PLUGIN_DESCRIPTOR, manifest)
|
|
446
|
+
if not valid:
|
|
447
|
+
raise ValueError(f'"{what}": {extra}')
|
|
448
|
+
if declaration := manifest.get('inputs'):
|
|
449
|
+
try:
|
|
450
|
+
_validate_defaults(declaration)
|
|
451
|
+
except ValueError as err:
|
|
452
|
+
raise ValueError(
|
|
453
|
+
f'"inputs" section for "{what}", default value for {err}'
|
|
454
|
+
) from None
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2019-09/schema#",
|
|
3
|
+
"title": "JSON SCHEMA for opentestfactory inputs",
|
|
4
|
+
"description": "An events/inputs/outputs part in a plugin descriptor.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"apiVersion": {
|
|
8
|
+
"type": "string"
|
|
9
|
+
},
|
|
10
|
+
"kind": {
|
|
11
|
+
"type": "string"
|
|
12
|
+
},
|
|
13
|
+
"metadata": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"properties": {
|
|
16
|
+
"name": {
|
|
17
|
+
"type": "string"
|
|
18
|
+
},
|
|
19
|
+
"title": {
|
|
20
|
+
"type": "string"
|
|
21
|
+
},
|
|
22
|
+
"action": {
|
|
23
|
+
"type": "string"
|
|
24
|
+
},
|
|
25
|
+
"description": {
|
|
26
|
+
"type": "string"
|
|
27
|
+
},
|
|
28
|
+
"note": {
|
|
29
|
+
"type": "string"
|
|
30
|
+
},
|
|
31
|
+
"license": {
|
|
32
|
+
"type": "string"
|
|
33
|
+
},
|
|
34
|
+
"hooks": {
|
|
35
|
+
"const": "default"
|
|
36
|
+
},
|
|
37
|
+
"variables": {
|
|
38
|
+
"const": "default"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"required": [
|
|
42
|
+
"name"
|
|
43
|
+
],
|
|
44
|
+
"additionalProperties": true
|
|
45
|
+
},
|
|
46
|
+
"cmd": {
|
|
47
|
+
"type": "string"
|
|
48
|
+
},
|
|
49
|
+
"spec": {
|
|
50
|
+
"type": "object",
|
|
51
|
+
"properties": {
|
|
52
|
+
"contextParameters": {
|
|
53
|
+
"$ref": "#/definitions/contextparameters"
|
|
54
|
+
},
|
|
55
|
+
"variables": {
|
|
56
|
+
"$ref": "#/definitions/variables"
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"patternProperties": {
|
|
60
|
+
"^.*\\..*$": {
|
|
61
|
+
"type": [
|
|
62
|
+
"string",
|
|
63
|
+
"array",
|
|
64
|
+
"object"
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"required": [],
|
|
69
|
+
"additionalProperties": false
|
|
70
|
+
},
|
|
71
|
+
"events": {
|
|
72
|
+
"type": "array",
|
|
73
|
+
"minItems": 1
|
|
74
|
+
},
|
|
75
|
+
"inputs": {
|
|
76
|
+
"$ref": "#/definitions/inputs"
|
|
77
|
+
},
|
|
78
|
+
"additionalInputs": {
|
|
79
|
+
"type": "boolean"
|
|
80
|
+
},
|
|
81
|
+
"outputs": {
|
|
82
|
+
"$ref": "#/definitions/outputs"
|
|
83
|
+
},
|
|
84
|
+
"reports": {
|
|
85
|
+
"type": "object"
|
|
86
|
+
},
|
|
87
|
+
"branding": {
|
|
88
|
+
"type": "object"
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"required": [
|
|
92
|
+
"metadata"
|
|
93
|
+
],
|
|
94
|
+
"allOf": [
|
|
95
|
+
{
|
|
96
|
+
"if": {
|
|
97
|
+
"properties": {
|
|
98
|
+
"metadata": {
|
|
99
|
+
"properties": {
|
|
100
|
+
"action": {
|
|
101
|
+
"type": "string"
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"required": [
|
|
105
|
+
"action"
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
"then": {
|
|
111
|
+
"required": [
|
|
112
|
+
"events"
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"if": {
|
|
118
|
+
"properties": {
|
|
119
|
+
"additionalInputs": {
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
"required": [
|
|
123
|
+
"additionalInputs"
|
|
124
|
+
]
|
|
125
|
+
},
|
|
126
|
+
"then": {
|
|
127
|
+
"required": [
|
|
128
|
+
"inputs"
|
|
129
|
+
]
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
],
|
|
133
|
+
"additionalProperties": false,
|
|
134
|
+
"definitions": {
|
|
135
|
+
"contextparameters": {
|
|
136
|
+
"type": "array",
|
|
137
|
+
"minItems": 1,
|
|
138
|
+
"items": {
|
|
139
|
+
"type": "object",
|
|
140
|
+
"properties": {
|
|
141
|
+
"name": {
|
|
142
|
+
"type": "string"
|
|
143
|
+
},
|
|
144
|
+
"descriptiveName": {
|
|
145
|
+
"type": "string"
|
|
146
|
+
},
|
|
147
|
+
"description": {
|
|
148
|
+
"type": "string"
|
|
149
|
+
},
|
|
150
|
+
"deprecatedNames": {
|
|
151
|
+
"type": "array",
|
|
152
|
+
"minItems": 1,
|
|
153
|
+
"items": {
|
|
154
|
+
"type": "string"
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
"type": {
|
|
158
|
+
"enum": [
|
|
159
|
+
"string",
|
|
160
|
+
"number",
|
|
161
|
+
"boolean",
|
|
162
|
+
"choice",
|
|
163
|
+
"int",
|
|
164
|
+
"bool"
|
|
165
|
+
]
|
|
166
|
+
},
|
|
167
|
+
"options": {
|
|
168
|
+
"type": "array",
|
|
169
|
+
"minItems": 1,
|
|
170
|
+
"items": {
|
|
171
|
+
"type": "string"
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
"default": {},
|
|
175
|
+
"minValue": { "type": "number" },
|
|
176
|
+
"maxValue": { "type": "number" },
|
|
177
|
+
"shared": {
|
|
178
|
+
"type": "boolean"
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
"required": [
|
|
182
|
+
"name",
|
|
183
|
+
"descriptiveName"
|
|
184
|
+
],
|
|
185
|
+
"allOf": [
|
|
186
|
+
{
|
|
187
|
+
"$ref": "#/definitions/choice"
|
|
188
|
+
}
|
|
189
|
+
],
|
|
190
|
+
"additionalProperties": false
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
"variables": {
|
|
194
|
+
"type": "object",
|
|
195
|
+
"minProperties": 1,
|
|
196
|
+
"patternProperties": {
|
|
197
|
+
"^[a-zA-Z0-9_]+$": {
|
|
198
|
+
"type": "object",
|
|
199
|
+
"properties": {
|
|
200
|
+
"descriptiveName": {
|
|
201
|
+
"type": "string"
|
|
202
|
+
},
|
|
203
|
+
"description": {
|
|
204
|
+
"type": "string"
|
|
205
|
+
},
|
|
206
|
+
"default": {
|
|
207
|
+
"type": "string"
|
|
208
|
+
},
|
|
209
|
+
"required": {
|
|
210
|
+
"type": "boolean"
|
|
211
|
+
},
|
|
212
|
+
"masked": {
|
|
213
|
+
"type": "boolean"
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
"required": [
|
|
217
|
+
"descriptiveName"
|
|
218
|
+
],
|
|
219
|
+
"additionalProperties": false
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
"inputs": {
|
|
224
|
+
"type": "object",
|
|
225
|
+
"minProperties": 0,
|
|
226
|
+
"patternProperties": {
|
|
227
|
+
"^[a-zA-Z-][a-zA-Z0-9-]*$|^{.*": {
|
|
228
|
+
"type": "object",
|
|
229
|
+
"properties": {
|
|
230
|
+
"description": {
|
|
231
|
+
"type": "string"
|
|
232
|
+
},
|
|
233
|
+
"required": {
|
|
234
|
+
"type": "boolean"
|
|
235
|
+
},
|
|
236
|
+
"default": {},
|
|
237
|
+
"type": {
|
|
238
|
+
"enum": [
|
|
239
|
+
"string",
|
|
240
|
+
"number",
|
|
241
|
+
"boolean",
|
|
242
|
+
"choice"
|
|
243
|
+
]
|
|
244
|
+
},
|
|
245
|
+
"options": {
|
|
246
|
+
"type": "array",
|
|
247
|
+
"minItems": 1,
|
|
248
|
+
"items": {
|
|
249
|
+
"type": "string"
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
"depreciationMessage": {
|
|
253
|
+
"type": "string"
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
"required": [
|
|
257
|
+
"description"
|
|
258
|
+
],
|
|
259
|
+
"allOf": [
|
|
260
|
+
{
|
|
261
|
+
"$ref": "#/definitions/choice"
|
|
262
|
+
}
|
|
263
|
+
],
|
|
264
|
+
"additionalProperties": false
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
"additionalProperties": false
|
|
268
|
+
},
|
|
269
|
+
"outputs": {
|
|
270
|
+
"type": "object",
|
|
271
|
+
"minProperties": 1,
|
|
272
|
+
"patternProperties": {
|
|
273
|
+
"^[a-zA-Z_][a-zA-Z0-9_-]*$": {
|
|
274
|
+
"oneOf": [
|
|
275
|
+
{
|
|
276
|
+
"type": "object",
|
|
277
|
+
"properties": {
|
|
278
|
+
"description": {
|
|
279
|
+
"type": "string"
|
|
280
|
+
},
|
|
281
|
+
"value": {
|
|
282
|
+
"type": "string"
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
"required": [
|
|
286
|
+
"value"
|
|
287
|
+
],
|
|
288
|
+
"additionalProperties": false
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
"type": "string"
|
|
292
|
+
}
|
|
293
|
+
]
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
"additionalProperties": false
|
|
297
|
+
},
|
|
298
|
+
"choice": {
|
|
299
|
+
"if": {
|
|
300
|
+
"properties": {
|
|
301
|
+
"type": {
|
|
302
|
+
"const": "choice"
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
"required": [
|
|
306
|
+
"type"
|
|
307
|
+
]
|
|
308
|
+
},
|
|
309
|
+
"then": {
|
|
310
|
+
"required": [
|
|
311
|
+
"options"
|
|
312
|
+
]
|
|
313
|
+
},
|
|
314
|
+
"else": {
|
|
315
|
+
"not": {
|
|
316
|
+
"required": [
|
|
317
|
+
"options"
|
|
318
|
+
]
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
opentf/toolkit/__init__.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright (c) Henix, Henix.fr
|
|
1
|
+
# Copyright (c) 2021 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, List, Optional
|
|
18
18
|
|
|
19
19
|
import os
|
|
20
20
|
import threading
|
|
@@ -34,13 +34,16 @@ from opentf.commons import (
|
|
|
34
34
|
subscribe,
|
|
35
35
|
unsubscribe,
|
|
36
36
|
EXECUTIONCOMMAND,
|
|
37
|
+
EXECUTIONRESULT,
|
|
37
38
|
PROVIDERCOMMAND,
|
|
38
39
|
PROVIDERCONFIG,
|
|
39
40
|
GENERATORCOMMAND,
|
|
40
41
|
SERVICECONFIG,
|
|
41
42
|
CHANNEL_HOOKS,
|
|
43
|
+
PLUGIN_DESCRIPTOR,
|
|
42
44
|
make_dispatchqueue,
|
|
43
45
|
make_status_response,
|
|
46
|
+
validate_descriptors,
|
|
44
47
|
validate_inputs,
|
|
45
48
|
validate_schema,
|
|
46
49
|
)
|
|
@@ -465,23 +468,10 @@ def _subscribe(
|
|
|
465
468
|
manifest.get('inputs', {}),
|
|
466
469
|
manifest.get('additionalInputs'),
|
|
467
470
|
)
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
}
|
|
473
|
-
except KeyError as err:
|
|
474
|
-
plugin.logger.error(
|
|
475
|
-
'Invalid descriptor "outputs" section, could not find key %s in: %s.',
|
|
476
|
-
err,
|
|
477
|
-
manifest.get('outputs', {}),
|
|
478
|
-
)
|
|
479
|
-
sys.exit(2)
|
|
480
|
-
except Exception as err:
|
|
481
|
-
plugin.logger.error(
|
|
482
|
-
'Invalid descriptor "outputs" section, got %s while parsing outputs.', err
|
|
483
|
-
)
|
|
484
|
-
sys.exit(2)
|
|
471
|
+
context[OUTPUTS_KEY][(cat_prefix, cat, cat_version)] = {
|
|
472
|
+
k: v['value'] if isinstance(v, dict) else v
|
|
473
|
+
for k, v in manifest.get('outputs', {}).items()
|
|
474
|
+
}
|
|
485
475
|
return subscribe(kind=kind, target='inbox', app=plugin, labels=labels)
|
|
486
476
|
|
|
487
477
|
|
|
@@ -574,14 +564,14 @@ def make_plugin(
|
|
|
574
564
|
- configfile: a string or None (None by default)
|
|
575
565
|
- args: a list or None (None by default)
|
|
576
566
|
|
|
567
|
+
# Returned value
|
|
568
|
+
|
|
569
|
+
A plugin service (not started).
|
|
570
|
+
|
|
577
571
|
# Raised exceptions
|
|
578
572
|
|
|
579
573
|
A _ValueError_ exception is raised if the provided parameters are
|
|
580
574
|
invalid.
|
|
581
|
-
|
|
582
|
-
# Returned value
|
|
583
|
-
|
|
584
|
-
A plugin service (not started).
|
|
585
575
|
"""
|
|
586
576
|
|
|
587
577
|
def process_inbox():
|
|
@@ -600,7 +590,7 @@ def make_plugin(
|
|
|
600
590
|
except KeyError:
|
|
601
591
|
return make_status_response(
|
|
602
592
|
'BadRequest',
|
|
603
|
-
f'Not a valid {kind} request: Missing metadata section',
|
|
593
|
+
f'Not a valid {kind} request: Missing "metadata" section',
|
|
604
594
|
)
|
|
605
595
|
|
|
606
596
|
valid, extra = validate_schema(kind, body)
|
|
@@ -643,11 +633,6 @@ def make_plugin(
|
|
|
643
633
|
'"args" is required for channel plugins and must be a list of one element.'
|
|
644
634
|
)
|
|
645
635
|
|
|
646
|
-
kind = (
|
|
647
|
-
EXECUTIONCOMMAND
|
|
648
|
-
if channel
|
|
649
|
-
else GENERATORCOMMAND if generator else PROVIDERCOMMAND
|
|
650
|
-
)
|
|
651
636
|
if not schema:
|
|
652
637
|
schema = SERVICECONFIG if generator else PROVIDERCONFIG
|
|
653
638
|
|
|
@@ -658,8 +643,18 @@ def make_plugin(
|
|
|
658
643
|
schema=schema,
|
|
659
644
|
descriptor=descriptor if descriptor is not None else 'plugin.yaml',
|
|
660
645
|
)
|
|
661
|
-
|
|
646
|
+
|
|
647
|
+
if channel:
|
|
648
|
+
kind = EXECUTIONCOMMAND
|
|
649
|
+
elif generator:
|
|
650
|
+
kind = GENERATORCOMMAND
|
|
651
|
+
elif publisher:
|
|
652
|
+
kind = EXECUTIONRESULT
|
|
653
|
+
else:
|
|
654
|
+
kind = PROVIDERCOMMAND
|
|
655
|
+
|
|
662
656
|
plugin.config['CONTEXT'][KIND_KEY] = kind
|
|
657
|
+
plugin.route('/inbox', methods=['POST'])(process_inbox)
|
|
663
658
|
|
|
664
659
|
if kind == PROVIDERCOMMAND:
|
|
665
660
|
_maybe_add_hook_watcher(plugin, schema)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opentf-toolkit-nightly
|
|
3
|
-
Version: 0.62.0.
|
|
3
|
+
Version: 0.62.0.dev1370
|
|
4
4
|
Summary: OpenTestFactory Orchestrator Toolkit
|
|
5
5
|
Home-page: https://gitlab.com/henixdevelopment/open-source/opentestfactory/python-toolkit
|
|
6
6
|
Author: Martin Lafaix
|
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
opentf/commons/__init__.py,sha256=
|
|
1
|
+
opentf/commons/__init__.py,sha256=N8GrBU1oL4sDUB4iVF5kPVB__uBlhe4FwD8E5ITV2pI,23011
|
|
2
2
|
opentf/commons/auth.py,sha256=yUmAoZPk9Aru2UVT5xSjH96u9DOKPk17AeL1_12mjBM,16399
|
|
3
|
-
opentf/commons/config.py,sha256=
|
|
3
|
+
opentf/commons/config.py,sha256=WiXoxuWQUebHrWAIY1fkxpwKb17uCnG29_8cZ2CaCcE,10454
|
|
4
4
|
opentf/commons/exceptions.py,sha256=7dhUXO8iyAbqVwlUKxZhgRzGqVcb7LkG39hFlm-VxIA,2407
|
|
5
5
|
opentf/commons/expressions.py,sha256=jM_YKXVOFhvOE2aE2IuacuvxhIsOYTFs2oQkpcbWR6g,19645
|
|
6
6
|
opentf/commons/meta.py,sha256=ygSO3mE2d-Ux62abzK1wYk86noT4R5Tumd90nyZo0MU,3322
|
|
7
7
|
opentf/commons/pubsub.py,sha256=M0bvajR9raUP-xe5mfRjdrweZyHQw1_Qsy56gS-Sck4,7676
|
|
8
|
-
opentf/commons/schemas.py,sha256=
|
|
8
|
+
opentf/commons/schemas.py,sha256=LT8SlkUcJGtqbnUUDT0U1KsQqTEXjl-ShMny332DkMQ,14042
|
|
9
9
|
opentf/commons/selectors.py,sha256=2mmnvfZ13KizBQLsIvHXPU0Qtf6hkIvJpYdejNRszUs,7203
|
|
10
10
|
opentf/schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
11
|
opentf/schemas/abac.opentestfactory.org/v1alpha1/Policy.json,sha256=JXsfNAPSEYggeyaDutSQBeG38o4Bmcr70dPLWWeqIh8,2105
|
|
12
|
+
opentf/schemas/config.opentestfactory.org/v1/Descriptor.json,sha256=V0ttnb2V9Yz5vWcVbtrSm0yylYp9eTyzEicuxRxjZu0,9541
|
|
12
13
|
opentf/schemas/opentestfactory.org/v1/ExecutionCommand.json,sha256=UtmAlTaYdPSZDeQnx-ocX8IonyEPWoNmgDkHwQwQxWE,4126
|
|
13
14
|
opentf/schemas/opentestfactory.org/v1/ExecutionResult.json,sha256=u0n-WYXNX9d0cEyu2D4tegU35k2Gf3eA6PGs9Uz4QDg,3275
|
|
14
15
|
opentf/schemas/opentestfactory.org/v1/GeneratorResult.json,sha256=sOwieyDWi6UZR7X29R9IjOR87ruSRQ4U6CM2_yT81Zk,16607
|
|
15
16
|
opentf/schemas/opentestfactory.org/v1/ProviderCommand.json,sha256=EU4kvEKxlqpH2IkLZ1HdBealuG6C0YzPkAtI7dNrfHg,4427
|
|
16
17
|
opentf/schemas/opentestfactory.org/v1/ProviderResult.json,sha256=Ej4zhCE3rCqCGKcaeAoIHwSJTV_7fw-rAxhJ52qA-Gs,9641
|
|
17
18
|
opentf/schemas/opentestfactory.org/v1/Subscription.json,sha256=hNaCUW3qSqqa1LY01Ao7kQ8FJhkjkwMrrPf4xihgSt4,7150
|
|
18
|
-
opentf/schemas/opentestfactory.org/v1/Workflow.json,sha256=
|
|
19
|
+
opentf/schemas/opentestfactory.org/v1/Workflow.json,sha256=5SoyzTHvCxx7ygSK0A8y66VJwNfk2tQkoFuxbvook6Y,24184
|
|
19
20
|
opentf/schemas/opentestfactory.org/v1/WorkflowCanceled.json,sha256=BCWxnm3rt4VWh9GXkIhdNY9JYohFWxi9Wg3JjXIavaU,1694
|
|
20
21
|
opentf/schemas/opentestfactory.org/v1/WorkflowCancellation.json,sha256=HAtc8xjrr-WwXwIck9B86BrHELkCb1oPYhTsRzHMMOE,1993
|
|
21
22
|
opentf/schemas/opentestfactory.org/v1/WorkflowCompleted.json,sha256=KtC9-oeaBNM3D60SFpmYnLMtqewUAGRoOJfVzb7CdHg,1635
|
|
@@ -56,11 +57,11 @@ opentf/schemas/opentestfactory.org/v1beta1/Workflow.json,sha256=QZ8mM9PhzsI9gTmw
|
|
|
56
57
|
opentf/schemas/opentestfactory.org/v1beta2/ServiceConfig.json,sha256=rEvK2YWL5lG94_qYgR_GnLWNsaQhaQ-2kuZdWJr5NnY,3517
|
|
57
58
|
opentf/scripts/launch_java_service.sh,sha256=S0jAaCuv2sZy0Gf2NGBuPX-eD531rcM-b0fNyhmzSjw,2423
|
|
58
59
|
opentf/scripts/startup.py,sha256=AcVXU2auPvqMb_6OpGzkVqrpgYV6vz7x_Rnv8YbAEkk,23114
|
|
59
|
-
opentf/toolkit/__init__.py,sha256=
|
|
60
|
+
opentf/toolkit/__init__.py,sha256=EBTZJ3srbzrLDd4fCmEN5C4NnoZ2kKhpE1aj6oMPtPs,22041
|
|
60
61
|
opentf/toolkit/channels.py,sha256=6qKSsAgq_oJpuDRiKqVUz-EAjdfikcCG3SFAGmKZdhQ,25551
|
|
61
62
|
opentf/toolkit/core.py,sha256=8REPRzLWrN50B2VMz8yO-AClgOOt8MsqYdafDw2my48,9608
|
|
62
|
-
opentf_toolkit_nightly-0.62.0.
|
|
63
|
-
opentf_toolkit_nightly-0.62.0.
|
|
64
|
-
opentf_toolkit_nightly-0.62.0.
|
|
65
|
-
opentf_toolkit_nightly-0.62.0.
|
|
66
|
-
opentf_toolkit_nightly-0.62.0.
|
|
63
|
+
opentf_toolkit_nightly-0.62.0.dev1370.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
64
|
+
opentf_toolkit_nightly-0.62.0.dev1370.dist-info/METADATA,sha256=PBI7Oca_P0qepFNUHfghv_WhMcudi860YzIxk93M9JQ,2214
|
|
65
|
+
opentf_toolkit_nightly-0.62.0.dev1370.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
66
|
+
opentf_toolkit_nightly-0.62.0.dev1370.dist-info/top_level.txt,sha256=_gPuE6GTT6UNXy1DjtmQSfCcZb_qYA2vWmjg7a30AGk,7
|
|
67
|
+
opentf_toolkit_nightly-0.62.0.dev1370.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|