Skip to content

Commit d15658c

Browse files
author
Alan Christie
committed
Now contains the schema (and a validation function)
1 parent dd3c1ca commit d15658c

File tree

5 files changed

+236
-2
lines changed

5 files changed

+236
-2
lines changed

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
include LICENSE
2+
include decoder/schema.yaml

README.rst

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ The following encoding/decoding formats are supported: -
1818

1919
- jinja2 (3.0)
2020

21+
The package also provides a ``validate_job_schema()`` function,
22+
which can (should) be used to validate the Job definition against the
23+
built-in schema.
24+
2125
.. _jinja2: https://jinja.palletsprojects.com/en/3.0.x/
2226

2327
Installation (Python)
@@ -28,9 +32,14 @@ there::
2832

2933
pip install im-data-manager-job-decoder
3034

31-
Once installed you can access the protocol buffers with:
35+
Once installed you can validate the definition (expected to be a dictionary
36+
formed from the definition YAML file) with:
3237

3338
>>> from decoder import decoder
39+
>>> error = decoder.validate_job_schema(job_defintion)
40+
41+
And run the decoder with:
42+
3443
>>> decoded, success = decoder.decode(text, variables, 'command', decoder.TextEncoding.JINJA2_3_0)
3544

3645
.. _PyPI: https://pypi.org/project/im-data-manager-job-decoder

decoder/decoder.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,52 @@
55
given a 'template' string and a 'dictionary' of parameters and values.
66
"""
77
import enum
8-
from typing import Dict, Optional, Tuple
8+
import os
9+
from typing import Any, Dict, Optional, Tuple
10+
11+
import jsonschema
12+
import yaml
913

1014
# The decoding engine implementations.
1115
# The modules are expected to be called 'decode_<TextEncoding.lower()>'
1216
from . import decode_jinja2_3_0
1317

18+
# The (built-in) Job Definition schema...
19+
# from the same directory as us.
20+
_SCHEMA_FILE: str = os.path.join(os.path.dirname(__file__), 'schema.yaml')
21+
22+
# Load the schema YAML file now.
23+
# This must work as the file is installed along with this module.
24+
_JOB_SCHEMA: Dict[str, Any] = {}
25+
assert os.path.isfile(_SCHEMA_FILE)
26+
with open(_SCHEMA_FILE, 'r', encoding='utf8') as schema_file:
27+
_JOB_SCHEMA = yaml.load(schema_file, Loader=yaml.FullLoader)
28+
assert _JOB_SCHEMA
29+
1430

1531
class TextEncoding(enum.Enum):
1632
"""A general text encoding format, used initially for Job text fields.
1733
"""
1834
JINJA2_3_0 = 1 # Encoding that complies with Jinja2 v3.0.x
1935

2036

37+
def validate_job_schema(job_definition: Dict[str, Any]) -> Optional[str]:
38+
"""Checks the Job Definition (a preloaded job-definition dictionary)
39+
against the built-in schema. If there's an error the error text is
40+
returned, otherwise None.
41+
"""
42+
assert job_definition
43+
44+
# Validate the Job Definition against our schema
45+
try:
46+
jsonschema.validate(job_definition, schema=_JOB_SCHEMA)
47+
except jsonschema.ValidationError as ex:
48+
return str(ex.message)
49+
50+
# OK if we get here
51+
return None
52+
53+
2154
def decode(template_text: str,
2255
variable_map: Optional[Dict[str, str]],
2356
subject: str,

decoder/schema.yaml

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
---
2+
# The JSONSchema for the 'JobDefinition' (JD) YAML files.
3+
#
4+
# See https://json-schema.org/understanding-json-schema/index.html
5+
6+
$schema: http://json-schema.org/draft-07/schema#
7+
8+
title: Data Manager Job Definition
9+
description: >-
10+
Defines one or more jobs that can be executed
11+
by the Data manager Job Operator
12+
13+
type: object
14+
properties:
15+
kind:
16+
const: DataManagerJobDefinition
17+
kind-version:
18+
enum:
19+
- '2021.1'
20+
name:
21+
type: string
22+
maxLength: 80
23+
description:
24+
tyep: string
25+
collection:
26+
type: string
27+
minLength: 1
28+
maxLength: 80
29+
pattern: '^[a-z]{1}[a-z0-9-]*$'
30+
repository-url:
31+
type: string
32+
maxlength: 2048
33+
format: uri
34+
repository-tag:
35+
type: string
36+
minLength: 1
37+
maxLength: 24
38+
jobs:
39+
$ref: '#/definitions/job-identity'
40+
required:
41+
- kind
42+
- kind-version
43+
- collection
44+
- repository-url
45+
- repository-tag
46+
- jobs
47+
48+
# Sub-object definitions ------------------------------------------------------
49+
# Things like the Job structure, Image structure etc.
50+
51+
definitions:
52+
53+
# A Job.
54+
# Consists of an identity (i.e. 'filter-molecules')
55+
# followed by a Job object.
56+
job-identity:
57+
type: object
58+
patternProperties:
59+
'^[a-z]{1}[a-z0-9-]{0,79}$':
60+
$ref: '#/definitions/job'
61+
additionalProperties: false
62+
minProperties: 1
63+
64+
# An individual Job
65+
job:
66+
type: object
67+
properties:
68+
name:
69+
type: string
70+
minLength: 1
71+
maxLength: 80
72+
description:
73+
type: string
74+
version:
75+
type: string
76+
minLength: 1
77+
maxLength: 24
78+
category:
79+
type: string
80+
doc-url:
81+
type: string
82+
keywords:
83+
type: array
84+
items:
85+
type: string
86+
image:
87+
$ref: '#/definitions/image'
88+
command-encoding:
89+
const: JINJA2_3_0
90+
command:
91+
type: string
92+
minLength: 1
93+
maxLength: 4096
94+
required:
95+
- version
96+
- image
97+
- command
98+
- name
99+
100+
# A Job container image
101+
# The 'type' is optional and is used to indicate
102+
# a single job _image_ (the default) or a workflow image like _nextflow_
103+
image:
104+
type: object
105+
additionalProperties: false
106+
properties:
107+
name:
108+
type: string
109+
minLength: 1
110+
maxLenght: 120
111+
tag:
112+
type: string
113+
minLength: 1
114+
maxLength: 24
115+
project-directory:
116+
type: string
117+
minLength: 1
118+
maxLength: 255
119+
pattern: '^(/[a-zA-Z0-9_-]+)+$'
120+
working-directory:
121+
type: string
122+
minLength: 1
123+
maxLength: 255
124+
pattern: '^(/[a-zA-Z0-9_-]+)+$'
125+
type:
126+
type: string
127+
enum:
128+
- simple
129+
- nextflow
130+
default: simple
131+
pull-secret:
132+
$ref: '#/definitions/rfc-1035-name'
133+
environment:
134+
$ref: '#/definitions/env-var-name'
135+
required:
136+
- name
137+
- tag
138+
- project-directory
139+
140+
# Image environment definitions.
141+
environment:
142+
type: array
143+
items:
144+
anyOf:
145+
- $ref: '#/definitions/environment-value-from-api-token'
146+
147+
# An Image environment from an 'api-token'.
148+
# Roles is an optional list of API token realm roles where, for now,
149+
# we limit the number in the list to a maximum 1.
150+
environment-value-from-api-token:
151+
type: object
152+
additionalProperties: false
153+
properties:
154+
name:
155+
$ref: '#/definitions/environment-name'
156+
value-from:
157+
type: object
158+
properties:
159+
api-token:
160+
type: object
161+
properties:
162+
roles:
163+
type: array
164+
items:
165+
type: string
166+
pattern: '^[a-z]{1,}[a-z-_]{0,}$'
167+
minItems: 0
168+
maxItems: 1
169+
uniqueItems: true
170+
required:
171+
- api-token
172+
required:
173+
- name
174+
- value-from
175+
176+
# The pattern for Image environment names.
177+
# Classic linux/shell,
178+
# i.e. letters, digits and '_' and must begin letter or '_'
179+
env-var-name:
180+
type: string
181+
minLength: 1
182+
pattern: '^[a-zA-Z_]{1,}[a-zA-Z0-9_]{0,}$'
183+
184+
# The pattern for RFC 1035 label names.
185+
rfc-1035-name:
186+
type: string
187+
minLength: 1
188+
maxLength: 63
189+
pattern: '^[a-z]([a-z0-9-]*[a-z0-9])?$'

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1+
jsonschema == 3.2.0
12
jinja2 == 3.0.3
3+
pyyaml == 5.4.1

0 commit comments

Comments
 (0)