scalable-pypeline 1.1.5__py2.py3-none-any.whl → 1.2.1__py2.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.
- pypeline/__init__.py +1 -1
- pypeline/celery.py +22 -86
- pypeline/constants.py +3 -3
- pypeline/flask/api/pipelines.py +16 -3
- pypeline/flask/api/utils.py +2 -3
- pypeline/pipeline/__init__.py +0 -0
- pypeline/pipeline/chained_task.py +70 -0
- pypeline/pipeline/generator.py +254 -0
- pypeline/pipeline_config_schema.py +46 -12
- pypeline/sermos_yaml.py +8 -303
- pypeline/utils/task_utils.py +22 -273
- {scalable_pypeline-1.1.5.dist-info → scalable_pypeline-1.2.1.dist-info}/METADATA +7 -5
- {scalable_pypeline-1.1.5.dist-info → scalable_pypeline-1.2.1.dist-info}/RECORD +17 -14
- {scalable_pypeline-1.1.5.dist-info → scalable_pypeline-1.2.1.dist-info}/LICENSE +0 -0
- {scalable_pypeline-1.1.5.dist-info → scalable_pypeline-1.2.1.dist-info}/WHEEL +0 -0
- {scalable_pypeline-1.1.5.dist-info → scalable_pypeline-1.2.1.dist-info}/entry_points.txt +0 -0
- {scalable_pypeline-1.1.5.dist-info → scalable_pypeline-1.2.1.dist-info}/top_level.txt +0 -0
pypeline/sermos_yaml.py
CHANGED
@@ -3,59 +3,11 @@ managed deployments through Sermos.ai. If self-hosting, safely disregard this
|
|
3
3
|
yaml format, no `sermos.yaml` is required for your application.
|
4
4
|
|
5
5
|
If using, a basic file may look like::
|
6
|
-
|
7
|
-
imageConfig:
|
8
|
-
- name: base-image
|
9
|
-
installCommand: sermos-demo-client[core]
|
10
|
-
- name: public-api-image
|
11
|
-
repositoryUrl: myregistry/public-api-image
|
12
|
-
|
13
|
-
environmentVariables:
|
14
|
-
- name: GLOBAL_ENV_VAR
|
15
|
-
value: globally-available-env-var
|
16
|
-
|
17
6
|
serviceConfig:
|
18
|
-
- name: base-worker
|
19
|
-
serviceType: celery-worker
|
20
|
-
imageName: base-image
|
21
|
-
queue: default-queue
|
22
|
-
registeredTasks:
|
23
|
-
- handler: sermos_demo_client.workers.demo_worker.demo_task
|
24
|
-
|
25
|
-
|
26
|
-
imageConfig:
|
27
|
-
- name: base-image
|
28
|
-
installCommand: sermos-demo-client
|
29
|
-
sourceSshUrl: git@gitlab.com:sermos/sermos-demo-client.git
|
30
|
-
baseImage: registry.gitlab.com/sermos/sermos-tools:0.3.0
|
31
|
-
|
32
|
-
serviceConfig:
|
33
|
-
- name: demo-api
|
34
|
-
serviceType: external
|
35
|
-
serviceId: ${SERVICE_ID_API} # Rendered using `sermos deploy` if available in the environment.
|
36
|
-
imageName: base-image
|
37
|
-
command: gunicorn --log-level info -k gevent -b 0.0.0.0:5000 sermos_demo_client.app:create_app()
|
38
|
-
port: 5000
|
39
|
-
replicaCount: 1
|
40
|
-
cpuLimit: 0.5
|
41
|
-
memoryLimit: 0.5
|
42
|
-
environmentVariables:
|
43
|
-
- name: FLASK_SECRET_KEY
|
44
|
-
value: ${FLASK_SECRET_KEY}
|
45
7
|
- name: sermos-worker
|
46
|
-
serviceType: celery-worker
|
47
|
-
serviceId: ${SERVICE_ID_WORKER}
|
48
|
-
imageName: base-image
|
49
|
-
command: celery -A sermos_demo_client.celery worker --without-gossip --without-mingle -c '1' -l INFO --queue default-task-queue
|
50
|
-
replicaCount: 1
|
51
|
-
cpuLimit: 0.5
|
52
|
-
memoryLimit: 0.5
|
53
8
|
registeredTasks:
|
54
9
|
- handler: sermos_demo_client.workers.demo_worker.demo_worker_task
|
55
10
|
- handler: sermos_demo_client.workers.demo_worker.demo_model_task
|
56
|
-
environmentVariables:
|
57
|
-
- name: WORKER_NAME
|
58
|
-
value: sermos-worker
|
59
11
|
|
60
12
|
pipelines:
|
61
13
|
demo-pipeline:
|
@@ -124,77 +76,11 @@ class MissingSermosConfig(Exception):
|
|
124
76
|
pass
|
125
77
|
|
126
78
|
|
127
|
-
class InvalidImageConfig(Exception):
|
128
|
-
pass
|
129
|
-
|
130
|
-
|
131
79
|
class ExcludeUnknownSchema(Schema):
|
132
80
|
class Meta:
|
133
81
|
unknown = EXCLUDE
|
134
82
|
|
135
83
|
|
136
|
-
class EnvironmentVariableSchema(ExcludeUnknownSchema):
|
137
|
-
""" A single environment variables (singular)
|
138
|
-
"""
|
139
|
-
name = fields.String(required=True,
|
140
|
-
description="Environment variable name.",
|
141
|
-
example="MY_ENV_VAR")
|
142
|
-
value = fields.String(required=True,
|
143
|
-
description="Environment variable value.",
|
144
|
-
example="my special value")
|
145
|
-
|
146
|
-
|
147
|
-
class EnvironmentVariablesSchema(Schema):
|
148
|
-
""" Multiple environment variables (plural)
|
149
|
-
"""
|
150
|
-
environmentVariables = fields.List(
|
151
|
-
fields.Nested(EnvironmentVariableSchema, required=True),
|
152
|
-
description="List of name/value environment variable pairs available "
|
153
|
-
"to the scope of this service.",
|
154
|
-
required=False)
|
155
|
-
|
156
|
-
|
157
|
-
class ServiceRequestsSchema(Schema):
|
158
|
-
|
159
|
-
replicaCount = fields.Integer(
|
160
|
-
required=False,
|
161
|
-
description="Baseline (min) scale of this service to have available.",
|
162
|
-
default=1,
|
163
|
-
example=1)
|
164
|
-
cpuRequest = fields.Float(
|
165
|
-
required=False,
|
166
|
-
description="Requested CPUs to be available for each replica.",
|
167
|
-
default=0.5,
|
168
|
-
example="0.5")
|
169
|
-
memoryRequest = fields.Float(
|
170
|
-
required=False,
|
171
|
-
description="Requested memory (in GB) to be available for each replica.",
|
172
|
-
default=0.5,
|
173
|
-
example="0.5 (means half of 1 GB)")
|
174
|
-
ephemeralStorageRequest = fields.Float(
|
175
|
-
required=False,
|
176
|
-
description="Requested ephemeral storage (in GB) to "
|
177
|
-
"be available for each replica.",
|
178
|
-
default=8,
|
179
|
-
example="2 (means 2 GB)")
|
180
|
-
cpuLimit = fields.Float(
|
181
|
-
required=False,
|
182
|
-
description="Maximum CPUs to be available for each replica.",
|
183
|
-
default=0.5,
|
184
|
-
example="0.5")
|
185
|
-
memoryLimit = fields.Float(
|
186
|
-
required=False,
|
187
|
-
description="Maximum memory (in GB) to be available for each replica.",
|
188
|
-
default=0.5,
|
189
|
-
example="0.5 (means half of 1 GB)")
|
190
|
-
ephemeralStorageLimit = fields.Float(
|
191
|
-
required=False,
|
192
|
-
description="Maximum ephemeral storage (in GB) to "
|
193
|
-
"be available for each replica.",
|
194
|
-
default=8,
|
195
|
-
example="2 (means 2 GB)")
|
196
|
-
|
197
|
-
|
198
84
|
class NameSchema(Schema):
|
199
85
|
""" Validated name string field.
|
200
86
|
"""
|
@@ -216,98 +102,6 @@ class NameSchema(Schema):
|
|
216
102
|
return item
|
217
103
|
|
218
104
|
|
219
|
-
class SermosImageConfigSchema(NameSchema):
|
220
|
-
installCommand = fields.String(
|
221
|
-
required=False,
|
222
|
-
description="The pip install command to use when Sermos is "
|
223
|
-
"responsible for building your image.",
|
224
|
-
example="sermos-client-pkg[core,special_feature]")
|
225
|
-
sourceSshUrl = fields.String(
|
226
|
-
required=False,
|
227
|
-
description="The source code ssh url to use when Sermos is "
|
228
|
-
"responsible for building your image.",
|
229
|
-
example="git@github.com:myorg/sermos-client.git")
|
230
|
-
baseImage = fields.String(
|
231
|
-
required=False,
|
232
|
-
description="The Docker base image to use as the starting point when "
|
233
|
-
"Sermos is responsible for building your image",
|
234
|
-
example="rhoai/sermos:latest")
|
235
|
-
repositoryUrl = fields.String(
|
236
|
-
required=False,
|
237
|
-
description="Deprecated - Use imageUri instead.")
|
238
|
-
repositoryUser = fields.String(
|
239
|
-
required=False,
|
240
|
-
description="Deprecated - Use registryUser instead.",)
|
241
|
-
repositoryPassword = fields.String(
|
242
|
-
required=False,
|
243
|
-
description="Deprecated - Use registryPassword instead.")
|
244
|
-
imageUri = fields.String(
|
245
|
-
required=False,
|
246
|
-
description="The Docker image uri when using an image not built by "
|
247
|
-
"Sermos. Tag is optional, if excluded, `latest` is used.",
|
248
|
-
example="rhoai/custom-image ; rhoai/custom-image:v0.0.0")
|
249
|
-
registryDomain = fields.String(
|
250
|
-
required=False,
|
251
|
-
description="Registry domain for using a pre-built image.",
|
252
|
-
example="index.docker.io")
|
253
|
-
registryUser = fields.String(
|
254
|
-
required=False,
|
255
|
-
description="Optional registry username if provided registryDomain"
|
256
|
-
"is a private registry.",
|
257
|
-
example="rhoai")
|
258
|
-
registryPassword = fields.String(
|
259
|
-
required=False,
|
260
|
-
description="Optional registry password if provided registryDomain "
|
261
|
-
"is a private registry. NOTE: Strongly recommended to use environment "
|
262
|
-
"variable interpolation in your sermos.yaml file, do not commit "
|
263
|
-
"unencrypted secrets to a git repository. "
|
264
|
-
"e.g. repositoryPassword: ${DOCKER_REPOSITORY_PASSWORD}",
|
265
|
-
example="abc123")
|
266
|
-
|
267
|
-
|
268
|
-
class SermosSharedConfigSchema(Schema):
|
269
|
-
""" Attributes shared across internal and external service types
|
270
|
-
"""
|
271
|
-
command = fields.String(
|
272
|
-
required=False,
|
273
|
-
_required=True,
|
274
|
-
description="Command to be run as container CMD.",
|
275
|
-
example="gunicorn -b 0.0.0.0:5000 package.app:create_app()")
|
276
|
-
port = fields.Integer(
|
277
|
-
required=False,
|
278
|
-
_required=True,
|
279
|
-
description="Port (and targetPort) to direct traffic.",
|
280
|
-
example=5000)
|
281
|
-
|
282
|
-
|
283
|
-
class SermosExternalConfigSchema(SermosSharedConfigSchema):
|
284
|
-
""" Attributes required for serviceType: external
|
285
|
-
|
286
|
-
Note: Schema lists these are not required in order for this to be used
|
287
|
-
as a mixin to SermosServiceConfigSchema. The validation is done
|
288
|
-
programmatically based on serviceType.
|
289
|
-
"""
|
290
|
-
serviceId = fields.String(
|
291
|
-
required=False,
|
292
|
-
_required=True,
|
293
|
-
description="The serviceId provided by Sermos. Find in admin console.",
|
294
|
-
example="dry-gorge-8018")
|
295
|
-
|
296
|
-
|
297
|
-
class SermosInternalSchema(SermosSharedConfigSchema):
|
298
|
-
""" Attributes required for serviceType: internal
|
299
|
-
|
300
|
-
Note: Schema lists these are not required in order for this to be used
|
301
|
-
as a mixin to SermosServiceConfigSchema. The validation is done
|
302
|
-
programmatically based on serviceType.
|
303
|
-
"""
|
304
|
-
protocol = fields.String(required=False,
|
305
|
-
_required=True,
|
306
|
-
description="Protocol to use.",
|
307
|
-
example="TCP",
|
308
|
-
validate=OneOf(['TCP', 'UDP']))
|
309
|
-
|
310
|
-
|
311
105
|
class SermosRegisteredTaskDetailConfigSchema(Schema):
|
312
106
|
handler = fields.String(
|
313
107
|
required=True,
|
@@ -321,18 +115,9 @@ class SermosRegisteredTaskDetailConfigSchema(Schema):
|
|
321
115
|
|
322
116
|
|
323
117
|
class SermosCeleryWorkerConfigSchema(Schema):
|
324
|
-
""" Attributes
|
325
|
-
|
326
|
-
Note: Schema lists these are not required in order for this to be used
|
327
|
-
as a mixin to SermosServiceConfigSchema. The validation is done
|
328
|
-
programmatically based on serviceType.
|
118
|
+
""" Attributes for a celery worker. This worker will run all of the
|
119
|
+
pipelines and scheduled tasks.
|
329
120
|
"""
|
330
|
-
command = fields.String(
|
331
|
-
required=False,
|
332
|
-
_required=True,
|
333
|
-
description="Command to be run as container CMD.",
|
334
|
-
example="celery -A mypkg.celery.celery worker --queue my-queue")
|
335
|
-
|
336
121
|
registeredTasks = fields.List(
|
337
122
|
fields.Nested(SermosRegisteredTaskDetailConfigSchema, required=True),
|
338
123
|
required=False,
|
@@ -341,47 +126,17 @@ class SermosCeleryWorkerConfigSchema(Schema):
|
|
341
126
|
)
|
342
127
|
|
343
128
|
|
344
|
-
|
345
|
-
service_types = {
|
346
|
-
'external': SermosExternalConfigSchema,
|
347
|
-
'internal': SermosInternalSchema,
|
348
|
-
'celery-worker': SermosCeleryWorkerConfigSchema
|
349
|
-
}
|
350
|
-
|
351
|
-
|
352
|
-
class SermosServiceConfigSchema(ExcludeUnknownSchema, ServiceRequestsSchema,
|
353
|
-
EnvironmentVariablesSchema,
|
354
|
-
SermosExternalConfigSchema,
|
355
|
-
SermosInternalSchema,
|
129
|
+
class SermosServiceConfigSchema(ExcludeUnknownSchema,
|
356
130
|
SermosCeleryWorkerConfigSchema, NameSchema):
|
357
|
-
""" Base service config object definition for workers
|
358
|
-
services.
|
131
|
+
""" Base service config object definition for workers.
|
359
132
|
"""
|
360
|
-
|
361
|
-
required=True,
|
362
|
-
description="Specify the name of the base image to use for this "
|
363
|
-
"service.",
|
364
|
-
example="custom-worker-name")
|
365
|
-
|
366
|
-
serviceType = fields.String(required=True,
|
367
|
-
description="Name of the worker.",
|
368
|
-
example="useful_worker",
|
369
|
-
validate=OneOf(service_types.keys()))
|
133
|
+
pass
|
370
134
|
|
371
135
|
|
372
|
-
class SermosYamlSchema(ExcludeUnknownSchema
|
136
|
+
class SermosYamlSchema(ExcludeUnknownSchema):
|
373
137
|
""" The primary `sermos.yaml` file schema. This defines all available
|
374
138
|
properties in a valid Sermos configuration file.
|
375
139
|
"""
|
376
|
-
|
377
|
-
imageConfig = fields.List(fields.Nested(SermosImageConfigSchema,
|
378
|
-
required=True),
|
379
|
-
required=True,
|
380
|
-
description="List of available base images. At "
|
381
|
-
"least one image must be defined. The 'name' "
|
382
|
-
"of the image is used in each service defined "
|
383
|
-
"in `serviceConfig`")
|
384
|
-
|
385
140
|
serviceConfig = fields.List(
|
386
141
|
fields.Nested(SermosServiceConfigSchema,
|
387
142
|
required=True,
|
@@ -414,17 +169,12 @@ class SermosYamlSchema(ExcludeUnknownSchema, EnvironmentVariablesSchema):
|
|
414
169
|
Nested fields that are not required are not validated by Marshmallow
|
415
170
|
by default. Do a single level down of validation for now.
|
416
171
|
|
417
|
-
Each serviceType has attributes that are required but are listed
|
418
|
-
as not required in the marshmallow schema. Validate here.
|
419
|
-
|
420
172
|
imageConfig can provide *either* an install command for Sermos
|
421
173
|
to use to build the image for customer *or* a Docker repository
|
422
174
|
for Sermos to pull.
|
423
175
|
"""
|
424
176
|
# Vaidate nested
|
425
177
|
key_schema_pairs = (
|
426
|
-
('imageConfig', SermosImageConfigSchema),
|
427
|
-
('environmentVariables', EnvironmentVariableSchema),
|
428
178
|
('serviceConfig', SermosServiceConfigSchema),
|
429
179
|
)
|
430
180
|
for k_s in key_schema_pairs:
|
@@ -440,10 +190,8 @@ class SermosYamlSchema(ExcludeUnknownSchema, EnvironmentVariablesSchema):
|
|
440
190
|
# required in order to use them as mixins for a generic service object,
|
441
191
|
# however, they ARE required, so validate here using the custom
|
442
192
|
# metadata property `_required`. Default to value of `required`.
|
443
|
-
service_image_names = []
|
444
193
|
for service in data.get('serviceConfig'):
|
445
|
-
|
446
|
-
schema = service_types[service_type]
|
194
|
+
schema = SermosCeleryWorkerConfigSchema
|
447
195
|
for field in schema().fields:
|
448
196
|
try:
|
449
197
|
if schema().fields[field].metadata.get(
|
@@ -452,43 +200,7 @@ class SermosYamlSchema(ExcludeUnknownSchema, EnvironmentVariablesSchema):
|
|
452
200
|
assert field in service
|
453
201
|
except AssertionError:
|
454
202
|
raise ValidationError(
|
455
|
-
f"`{field}` missing in
|
456
|
-
|
457
|
-
service_image_names.append(service['imageName'])
|
458
|
-
|
459
|
-
# Validate imageConfig
|
460
|
-
image_names = []
|
461
|
-
for image in data.get('imageConfig'):
|
462
|
-
image_names.append(image['name'])
|
463
|
-
try:
|
464
|
-
if image.get('installCommand', None) is not None:
|
465
|
-
assert image.get('repositoryUrl', None) is None
|
466
|
-
if image.get('repositoryUrl', None) is not None:
|
467
|
-
assert image.get('installCommand', None) is None
|
468
|
-
except AssertionError:
|
469
|
-
raise InvalidImageConfig(
|
470
|
-
"Each imageConfig may have *either* an installCommand "
|
471
|
-
"*or* a repositoryUrl but not both. Review "
|
472
|
-
f"image `{image['name']}`")
|
473
|
-
|
474
|
-
if image.get('installCommand', None) is not None:
|
475
|
-
if image.get('sourceSshUrl') is None:
|
476
|
-
raise InvalidImageConfig(
|
477
|
-
"Each imageConfig must have a sourceSshUrl if "
|
478
|
-
"installCommand is specified")
|
479
|
-
if image.get('baseImage') is None:
|
480
|
-
raise InvalidImageConfig(
|
481
|
-
"Each imageConfig must have a baseImage if "
|
482
|
-
"installCommand is specified")
|
483
|
-
|
484
|
-
# Verify each imageName referenced in each service exists in imageConfig
|
485
|
-
try:
|
486
|
-
assert set(service_image_names).issubset(image_names)
|
487
|
-
except AssertionError:
|
488
|
-
raise InvalidImageConfig(
|
489
|
-
"Mismatched imageName in at least one "
|
490
|
-
f"service ({service_image_names}) compared to images available "
|
491
|
-
f"in imageConfig ({image_names})")
|
203
|
+
f"`{field}` missing in worker definition.")
|
492
204
|
|
493
205
|
# Validate unique pipeline ids
|
494
206
|
if 'pipelines' in data:
|
@@ -612,11 +324,6 @@ def parse_config_file(sermos_yaml: str):
|
|
612
324
|
.format(e.messages)
|
613
325
|
logger.error(msg)
|
614
326
|
raise InvalidSermosConfig(msg)
|
615
|
-
except InvalidImageConfig as e:
|
616
|
-
msg = "Invalid imageConfig configuration due to {}"\
|
617
|
-
.format(e)
|
618
|
-
logger.error(msg)
|
619
|
-
raise InvalidImageConfig(msg)
|
620
327
|
except Exception as e:
|
621
328
|
msg = "Invalid Sermos configuration, likely due to invalid "\
|
622
329
|
"YAML formatting ..."
|
@@ -680,8 +387,6 @@ def load_sermos_config(pkg_name: str = None,
|
|
680
387
|
sermos_config = parse_config_file(sermos_yaml)
|
681
388
|
except InvalidSermosConfig as e:
|
682
389
|
raise
|
683
|
-
except InvalidImageConfig as e:
|
684
|
-
raise
|
685
390
|
except FileNotFoundError as e:
|
686
391
|
msg = "Sermos config file could not be found at path {} ...".format(
|
687
392
|
sermos_config_path)
|