zenml-nightly 0.80.0.dev20250326__py3-none-any.whl → 0.80.0.dev20250328__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.
- zenml/VERSION +1 -1
- zenml/cli/project.py +41 -4
- zenml/client.py +7 -2
- zenml/config/global_config.py +15 -15
- zenml/integrations/databricks/__init__.py +6 -3
- zenml/integrations/deepchecks/__init__.py +5 -2
- zenml/integrations/evidently/__init__.py +5 -2
- zenml/integrations/facets/__init__.py +5 -2
- zenml/integrations/feast/__init__.py +4 -2
- zenml/integrations/great_expectations/__init__.py +4 -2
- zenml/integrations/huggingface/__init__.py +4 -2
- zenml/integrations/integration.py +6 -1
- zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator.py +1 -1
- zenml/integrations/langchain/materializers/openai_embedding_materializer.py +5 -9
- zenml/integrations/langchain/materializers/vector_store_materializer.py +3 -7
- zenml/integrations/llama_index/materializers/document_materializer.py +2 -6
- zenml/integrations/llama_index/materializers/gpt_index_materializer.py +2 -7
- zenml/integrations/mlflow/__init__.py +15 -4
- zenml/integrations/seldon/__init__.py +4 -2
- zenml/integrations/tensorboard/__init__.py +3 -1
- zenml/integrations/tensorflow/__init__.py +4 -3
- zenml/integrations/whylogs/__init__.py +4 -2
- zenml/models/v2/core/user.py +17 -0
- zenml/utils/requirements_utils.py +12 -3
- zenml/zen_server/routers/users_endpoints.py +169 -173
- zenml/zen_server/template_execution/utils.py +14 -5
- zenml/zen_stores/base_zen_store.py +46 -14
- zenml/zen_stores/migrations/versions/1f1105204aaf_add_user_default_project.py +51 -0
- zenml/zen_stores/rest_zen_store.py +1 -3
- zenml/zen_stores/schemas/user_schemas.py +11 -0
- zenml/zen_stores/sql_zen_store.py +1 -5
- {zenml_nightly-0.80.0.dev20250326.dist-info → zenml_nightly-0.80.0.dev20250328.dist-info}/METADATA +1 -1
- {zenml_nightly-0.80.0.dev20250326.dist-info → zenml_nightly-0.80.0.dev20250328.dist-info}/RECORD +36 -35
- {zenml_nightly-0.80.0.dev20250326.dist-info → zenml_nightly-0.80.0.dev20250328.dist-info}/LICENSE +0 -0
- {zenml_nightly-0.80.0.dev20250326.dist-info → zenml_nightly-0.80.0.dev20250328.dist-info}/WHEEL +0 -0
- {zenml_nightly-0.80.0.dev20250326.dist-info → zenml_nightly-0.80.0.dev20250328.dist-info}/entry_points.txt +0 -0
@@ -50,11 +50,13 @@ class WhylogsIntegration(Integration):
|
|
50
50
|
return [WhylogsDataValidatorFlavor]
|
51
51
|
|
52
52
|
@classmethod
|
53
|
-
def get_requirements(cls, target_os: Optional[str] = None
|
53
|
+
def get_requirements(cls, target_os: Optional[str] = None, python_version: Optional[str] = None
|
54
|
+
) -> List[str]:
|
54
55
|
"""Method to get the requirements for the integration.
|
55
56
|
|
56
57
|
Args:
|
57
58
|
target_os: The target operating system to get the requirements for.
|
59
|
+
python_version: The Python version to use for the requirements.
|
58
60
|
|
59
61
|
Returns:
|
60
62
|
A list of requirements.
|
@@ -62,6 +64,6 @@ class WhylogsIntegration(Integration):
|
|
62
64
|
from zenml.integrations.pandas import PandasIntegration
|
63
65
|
|
64
66
|
return cls.REQUIREMENTS + \
|
65
|
-
PandasIntegration.get_requirements(target_os=target_os)
|
67
|
+
PandasIntegration.get_requirements(target_os=target_os, python_version=python_version)
|
66
68
|
|
67
69
|
|
zenml/models/v2/core/user.py
CHANGED
@@ -203,6 +203,10 @@ class UserUpdate(UserBase, BaseUpdate):
|
|
203
203
|
"accounts. Required when updating the password.",
|
204
204
|
max_length=STR_FIELD_MAX_LENGTH,
|
205
205
|
)
|
206
|
+
default_project_id: Optional[UUID] = Field(
|
207
|
+
default=None,
|
208
|
+
title="The default project ID for the user.",
|
209
|
+
)
|
206
210
|
|
207
211
|
@model_validator(mode="after")
|
208
212
|
def user_email_updates(self) -> "UserUpdate":
|
@@ -279,6 +283,10 @@ class UserResponseBody(BaseDatedResponseBody):
|
|
279
283
|
is_admin: bool = Field(
|
280
284
|
title="Whether the account is an administrator.",
|
281
285
|
)
|
286
|
+
default_project_id: Optional[UUID] = Field(
|
287
|
+
default=None,
|
288
|
+
title="The default project ID for the user.",
|
289
|
+
)
|
282
290
|
|
283
291
|
|
284
292
|
class UserResponseMetadata(BaseResponseMetadata):
|
@@ -422,6 +430,15 @@ class UserResponse(
|
|
422
430
|
"""
|
423
431
|
return self.get_metadata().user_metadata
|
424
432
|
|
433
|
+
@property
|
434
|
+
def default_project_id(self) -> Optional[UUID]:
|
435
|
+
"""The `default_project_id` property.
|
436
|
+
|
437
|
+
Returns:
|
438
|
+
the value of the property.
|
439
|
+
"""
|
440
|
+
return self.get_body().default_project_id
|
441
|
+
|
425
442
|
# Helper methods
|
426
443
|
@classmethod
|
427
444
|
def _get_crypt_context(cls) -> "CryptContext":
|
@@ -13,7 +13,7 @@
|
|
13
13
|
# permissions and limitations under the License.
|
14
14
|
"""Requirement utils."""
|
15
15
|
|
16
|
-
from typing import TYPE_CHECKING, List, Set, Tuple
|
16
|
+
from typing import TYPE_CHECKING, List, Optional, Set, Tuple
|
17
17
|
|
18
18
|
from zenml.integrations.utils import get_integration_for_module
|
19
19
|
|
@@ -23,11 +23,13 @@ if TYPE_CHECKING:
|
|
23
23
|
|
24
24
|
def get_requirements_for_stack(
|
25
25
|
stack: "StackResponse",
|
26
|
+
python_version: Optional[str] = None,
|
26
27
|
) -> Tuple[List[str], List[str]]:
|
27
28
|
"""Get requirements for a stack model.
|
28
29
|
|
29
30
|
Args:
|
30
31
|
stack: The stack for which to get the requirements.
|
32
|
+
python_version: The Python version to use for the requirements.
|
31
33
|
|
32
34
|
Returns:
|
33
35
|
Tuple of PyPI and APT requirements of the stack.
|
@@ -41,7 +43,10 @@ def get_requirements_for_stack(
|
|
41
43
|
(
|
42
44
|
component_pypi_requirements,
|
43
45
|
component_apt_packages,
|
44
|
-
) = get_requirements_for_component(
|
46
|
+
) = get_requirements_for_component(
|
47
|
+
component=component,
|
48
|
+
python_version=python_version,
|
49
|
+
)
|
45
50
|
pypi_requirements = pypi_requirements.union(
|
46
51
|
component_pypi_requirements
|
47
52
|
)
|
@@ -52,11 +57,13 @@ def get_requirements_for_stack(
|
|
52
57
|
|
53
58
|
def get_requirements_for_component(
|
54
59
|
component: "ComponentResponse",
|
60
|
+
python_version: Optional[str] = None,
|
55
61
|
) -> Tuple[List[str], List[str]]:
|
56
62
|
"""Get requirements for a component model.
|
57
63
|
|
58
64
|
Args:
|
59
65
|
component: The component for which to get the requirements.
|
66
|
+
python_version: The Python version to use for the requirements.
|
60
67
|
|
61
68
|
Returns:
|
62
69
|
Tuple of PyPI and APT requirements of the component.
|
@@ -66,6 +73,8 @@ def get_requirements_for_component(
|
|
66
73
|
)
|
67
74
|
|
68
75
|
if integration:
|
69
|
-
return integration.get_requirements(
|
76
|
+
return integration.get_requirements(
|
77
|
+
python_version=python_version
|
78
|
+
), integration.APT_PACKAGES
|
70
79
|
else:
|
71
80
|
return [], []
|
@@ -65,6 +65,11 @@ from zenml.zen_server.utils import (
|
|
65
65
|
|
66
66
|
logger = get_logger(__name__)
|
67
67
|
|
68
|
+
pass_change_limiter = RequestLimiter(
|
69
|
+
day_limit=server_config().login_rate_limit_day,
|
70
|
+
minute_limit=server_config().login_rate_limit_minute,
|
71
|
+
)
|
72
|
+
|
68
73
|
router = APIRouter(
|
69
74
|
prefix=API + VERSION_1 + USERS,
|
70
75
|
tags=["users"],
|
@@ -223,179 +228,6 @@ def get_user(
|
|
223
228
|
# When the auth scheme is set to EXTERNAL, users cannot be updated via the
|
224
229
|
# API.
|
225
230
|
if server_config().auth_scheme != AuthScheme.EXTERNAL:
|
226
|
-
pass_change_limiter = RequestLimiter(
|
227
|
-
day_limit=server_config().login_rate_limit_day,
|
228
|
-
minute_limit=server_config().login_rate_limit_minute,
|
229
|
-
)
|
230
|
-
|
231
|
-
@router.put(
|
232
|
-
"/{user_name_or_id}",
|
233
|
-
responses={
|
234
|
-
401: error_response,
|
235
|
-
404: error_response,
|
236
|
-
422: error_response,
|
237
|
-
},
|
238
|
-
)
|
239
|
-
@handle_exceptions
|
240
|
-
def update_user(
|
241
|
-
user_name_or_id: Union[str, UUID],
|
242
|
-
user_update: UserUpdate,
|
243
|
-
request: Request,
|
244
|
-
auth_context: AuthContext = Security(authorize),
|
245
|
-
) -> UserResponse:
|
246
|
-
"""Updates a specific user.
|
247
|
-
|
248
|
-
Args:
|
249
|
-
user_name_or_id: Name or ID of the user.
|
250
|
-
user_update: the user to use for the update.
|
251
|
-
request: The request object.
|
252
|
-
auth_context: Authentication context.
|
253
|
-
|
254
|
-
Returns:
|
255
|
-
The updated user.
|
256
|
-
|
257
|
-
Raises:
|
258
|
-
IllegalOperationError: if the user tries change admin status,
|
259
|
-
while not an admin, if the user tries to change the password
|
260
|
-
of another user, or if the user tries to change their own
|
261
|
-
password without providing the old password or providing
|
262
|
-
an incorrect old password.
|
263
|
-
"""
|
264
|
-
user = zen_store().get_user(user_name_or_id)
|
265
|
-
|
266
|
-
# Use a separate object to compute the update that will be applied to
|
267
|
-
# the user to avoid giving the API requester direct control over the
|
268
|
-
# user attributes that are updated.
|
269
|
-
#
|
270
|
-
# Exclude attributes that cannot be updated through this endpoint:
|
271
|
-
#
|
272
|
-
# - activation_token
|
273
|
-
# - external_user_id
|
274
|
-
# - old_password
|
275
|
-
#
|
276
|
-
# Exclude things that are not always safe to update and need to be
|
277
|
-
# validated first:
|
278
|
-
#
|
279
|
-
# - admin
|
280
|
-
# - active
|
281
|
-
# - password
|
282
|
-
# - email_opted_in + email
|
283
|
-
#
|
284
|
-
safe_user_update = user_update.create_copy(
|
285
|
-
exclude={
|
286
|
-
"activation_token",
|
287
|
-
"external_user_id",
|
288
|
-
"is_admin",
|
289
|
-
"active",
|
290
|
-
"password",
|
291
|
-
"old_password",
|
292
|
-
"email_opted_in",
|
293
|
-
"email",
|
294
|
-
},
|
295
|
-
)
|
296
|
-
|
297
|
-
if user.id != auth_context.user.id:
|
298
|
-
verify_admin_status_if_no_rbac(
|
299
|
-
auth_context.user.is_admin, "update other user account"
|
300
|
-
)
|
301
|
-
# verify_permission_for_model(
|
302
|
-
# user,
|
303
|
-
# action=Action.UPDATE,
|
304
|
-
# )
|
305
|
-
|
306
|
-
# Validate a password change
|
307
|
-
if user_update.password is not None:
|
308
|
-
if user.id != auth_context.user.id:
|
309
|
-
raise IllegalOperationError(
|
310
|
-
"Users cannot change the password of other users. Use the "
|
311
|
-
"account deactivation and activation flow instead."
|
312
|
-
)
|
313
|
-
|
314
|
-
# If the user is updating their own password, we need to verify
|
315
|
-
# the old password
|
316
|
-
if user_update.old_password is None:
|
317
|
-
raise IllegalOperationError(
|
318
|
-
"The current password must be supplied when changing the "
|
319
|
-
"password."
|
320
|
-
)
|
321
|
-
|
322
|
-
with pass_change_limiter.limit_failed_requests(request):
|
323
|
-
auth_user = zen_store().get_auth_user(user_name_or_id)
|
324
|
-
if not UserAuthModel.verify_password(
|
325
|
-
user_update.old_password, auth_user
|
326
|
-
):
|
327
|
-
raise IllegalOperationError(
|
328
|
-
"The current password is incorrect."
|
329
|
-
)
|
330
|
-
|
331
|
-
# Accept the password update
|
332
|
-
safe_user_update.password = user_update.password
|
333
|
-
|
334
|
-
# Validate an admin status change
|
335
|
-
if (
|
336
|
-
user_update.is_admin is not None
|
337
|
-
and user.is_admin != user_update.is_admin
|
338
|
-
):
|
339
|
-
if user.id == auth_context.user.id:
|
340
|
-
raise IllegalOperationError(
|
341
|
-
"Cannot change the admin status of your own user account."
|
342
|
-
)
|
343
|
-
|
344
|
-
if (
|
345
|
-
user.id != auth_context.user.id
|
346
|
-
and not auth_context.user.is_admin
|
347
|
-
):
|
348
|
-
raise IllegalOperationError(
|
349
|
-
"Only admins are allowed to change the admin status of "
|
350
|
-
"other user accounts."
|
351
|
-
)
|
352
|
-
|
353
|
-
# Accept the admin status update
|
354
|
-
safe_user_update.is_admin = user_update.is_admin
|
355
|
-
|
356
|
-
# Validate an active status change
|
357
|
-
if (
|
358
|
-
user_update.active is not None
|
359
|
-
and user.active != user_update.active
|
360
|
-
):
|
361
|
-
if user.id == auth_context.user.id:
|
362
|
-
raise IllegalOperationError(
|
363
|
-
"Cannot change the active status of your own user account."
|
364
|
-
)
|
365
|
-
|
366
|
-
if (
|
367
|
-
user.id != auth_context.user.id
|
368
|
-
and not auth_context.user.is_admin
|
369
|
-
):
|
370
|
-
raise IllegalOperationError(
|
371
|
-
"Only admins are allowed to change the active status of "
|
372
|
-
"other user accounts."
|
373
|
-
)
|
374
|
-
|
375
|
-
# Accept the admin status update
|
376
|
-
safe_user_update.is_admin = user_update.is_admin
|
377
|
-
|
378
|
-
# Validate changes to private user account information
|
379
|
-
if (
|
380
|
-
user_update.email_opted_in is not None
|
381
|
-
or user_update.email is not None
|
382
|
-
):
|
383
|
-
if user.id != auth_context.user.id:
|
384
|
-
raise IllegalOperationError(
|
385
|
-
"Cannot change the private user account information for "
|
386
|
-
"another user account."
|
387
|
-
)
|
388
|
-
|
389
|
-
# Accept the private user account information update
|
390
|
-
if safe_user_update.email_opted_in is not None:
|
391
|
-
safe_user_update.email_opted_in = user_update.email_opted_in
|
392
|
-
safe_user_update.email = user_update.email
|
393
|
-
|
394
|
-
updated_user = zen_store().update_user(
|
395
|
-
user_id=user.id,
|
396
|
-
user_update=safe_user_update,
|
397
|
-
)
|
398
|
-
return dehydrate_response_model(updated_user)
|
399
231
|
|
400
232
|
@activation_router.put(
|
401
233
|
"/{user_name_or_id}" + ACTIVATE,
|
@@ -599,6 +431,170 @@ if server_config().auth_scheme != AuthScheme.EXTERNAL:
|
|
599
431
|
)
|
600
432
|
|
601
433
|
|
434
|
+
@router.put(
|
435
|
+
"/{user_name_or_id}",
|
436
|
+
responses={
|
437
|
+
401: error_response,
|
438
|
+
404: error_response,
|
439
|
+
422: error_response,
|
440
|
+
},
|
441
|
+
)
|
442
|
+
@handle_exceptions
|
443
|
+
def update_user(
|
444
|
+
user_name_or_id: Union[str, UUID],
|
445
|
+
user_update: UserUpdate,
|
446
|
+
request: Request,
|
447
|
+
auth_context: AuthContext = Security(authorize),
|
448
|
+
) -> UserResponse:
|
449
|
+
"""Updates a specific user.
|
450
|
+
|
451
|
+
Args:
|
452
|
+
user_name_or_id: Name or ID of the user.
|
453
|
+
user_update: the user to use for the update.
|
454
|
+
request: The request object.
|
455
|
+
auth_context: Authentication context.
|
456
|
+
|
457
|
+
Returns:
|
458
|
+
The updated user.
|
459
|
+
|
460
|
+
Raises:
|
461
|
+
IllegalOperationError: if the user tries change admin status,
|
462
|
+
while not an admin, if the user tries to change the password
|
463
|
+
of another user, or if the user tries to change their own
|
464
|
+
password without providing the old password or providing
|
465
|
+
an incorrect old password.
|
466
|
+
"""
|
467
|
+
if server_config().auth_scheme == AuthScheme.EXTERNAL:
|
468
|
+
# For external auth, we only allow updating the default project
|
469
|
+
user_update = UserUpdate(
|
470
|
+
default_project_id=user_update.default_project_id,
|
471
|
+
)
|
472
|
+
|
473
|
+
user = zen_store().get_user(user_name_or_id)
|
474
|
+
|
475
|
+
# Use a separate object to compute the update that will be applied to
|
476
|
+
# the user to avoid giving the API requester direct control over the
|
477
|
+
# user attributes that are updated.
|
478
|
+
#
|
479
|
+
# Exclude attributes that cannot be updated through this endpoint:
|
480
|
+
#
|
481
|
+
# - activation_token
|
482
|
+
# - external_user_id
|
483
|
+
# - old_password
|
484
|
+
#
|
485
|
+
# Exclude things that are not always safe to update and need to be
|
486
|
+
# validated first:
|
487
|
+
#
|
488
|
+
# - admin
|
489
|
+
# - active
|
490
|
+
# - password
|
491
|
+
# - email_opted_in + email
|
492
|
+
#
|
493
|
+
safe_user_update = user_update.create_copy(
|
494
|
+
exclude={
|
495
|
+
"activation_token",
|
496
|
+
"external_user_id",
|
497
|
+
"is_admin",
|
498
|
+
"active",
|
499
|
+
"password",
|
500
|
+
"old_password",
|
501
|
+
"email_opted_in",
|
502
|
+
"email",
|
503
|
+
},
|
504
|
+
)
|
505
|
+
|
506
|
+
if user.id != auth_context.user.id:
|
507
|
+
verify_admin_status_if_no_rbac(
|
508
|
+
auth_context.user.is_admin, "update other user account"
|
509
|
+
)
|
510
|
+
# verify_permission_for_model(
|
511
|
+
# user,
|
512
|
+
# action=Action.UPDATE,
|
513
|
+
# )
|
514
|
+
|
515
|
+
# Validate a password change
|
516
|
+
if user_update.password is not None:
|
517
|
+
if user.id != auth_context.user.id:
|
518
|
+
raise IllegalOperationError(
|
519
|
+
"Users cannot change the password of other users. Use the "
|
520
|
+
"account deactivation and activation flow instead."
|
521
|
+
)
|
522
|
+
|
523
|
+
# If the user is updating their own password, we need to verify
|
524
|
+
# the old password
|
525
|
+
if user_update.old_password is None:
|
526
|
+
raise IllegalOperationError(
|
527
|
+
"The current password must be supplied when changing the "
|
528
|
+
"password."
|
529
|
+
)
|
530
|
+
|
531
|
+
with pass_change_limiter.limit_failed_requests(request):
|
532
|
+
auth_user = zen_store().get_auth_user(user_name_or_id)
|
533
|
+
if not UserAuthModel.verify_password(
|
534
|
+
user_update.old_password, auth_user
|
535
|
+
):
|
536
|
+
raise IllegalOperationError(
|
537
|
+
"The current password is incorrect."
|
538
|
+
)
|
539
|
+
|
540
|
+
# Accept the password update
|
541
|
+
safe_user_update.password = user_update.password
|
542
|
+
|
543
|
+
# Validate an admin status change
|
544
|
+
if (
|
545
|
+
user_update.is_admin is not None
|
546
|
+
and user.is_admin != user_update.is_admin
|
547
|
+
):
|
548
|
+
if user.id == auth_context.user.id:
|
549
|
+
raise IllegalOperationError(
|
550
|
+
"Cannot change the admin status of your own user account."
|
551
|
+
)
|
552
|
+
|
553
|
+
if user.id != auth_context.user.id and not auth_context.user.is_admin:
|
554
|
+
raise IllegalOperationError(
|
555
|
+
"Only admins are allowed to change the admin status of "
|
556
|
+
"other user accounts."
|
557
|
+
)
|
558
|
+
|
559
|
+
# Accept the admin status update
|
560
|
+
safe_user_update.is_admin = user_update.is_admin
|
561
|
+
|
562
|
+
# Validate an active status change
|
563
|
+
if user_update.active is not None and user.active != user_update.active:
|
564
|
+
if user.id == auth_context.user.id:
|
565
|
+
raise IllegalOperationError(
|
566
|
+
"Cannot change the active status of your own user account."
|
567
|
+
)
|
568
|
+
|
569
|
+
if user.id != auth_context.user.id and not auth_context.user.is_admin:
|
570
|
+
raise IllegalOperationError(
|
571
|
+
"Only admins are allowed to change the active status of "
|
572
|
+
"other user accounts."
|
573
|
+
)
|
574
|
+
|
575
|
+
# Accept the admin status update
|
576
|
+
safe_user_update.is_admin = user_update.is_admin
|
577
|
+
|
578
|
+
# Validate changes to private user account information
|
579
|
+
if user_update.email_opted_in is not None or user_update.email is not None:
|
580
|
+
if user.id != auth_context.user.id:
|
581
|
+
raise IllegalOperationError(
|
582
|
+
"Cannot change the private user account information for "
|
583
|
+
"another user account."
|
584
|
+
)
|
585
|
+
|
586
|
+
# Accept the private user account information update
|
587
|
+
if safe_user_update.email_opted_in is not None:
|
588
|
+
safe_user_update.email_opted_in = user_update.email_opted_in
|
589
|
+
safe_user_update.email = user_update.email
|
590
|
+
|
591
|
+
updated_user = zen_store().update_user(
|
592
|
+
user_id=user.id,
|
593
|
+
user_update=safe_user_update,
|
594
|
+
)
|
595
|
+
return dehydrate_response_model(updated_user)
|
596
|
+
|
597
|
+
|
602
598
|
@current_user_router.get(
|
603
599
|
"/current-user",
|
604
600
|
responses={401: error_response, 404: error_response, 422: error_response},
|
@@ -149,11 +149,6 @@ def run_template(
|
|
149
149
|
)
|
150
150
|
|
151
151
|
def _task() -> None:
|
152
|
-
(
|
153
|
-
pypi_requirements,
|
154
|
-
apt_packages,
|
155
|
-
) = requirements_utils.get_requirements_for_stack(stack=stack)
|
156
|
-
|
157
152
|
if build.python_version:
|
158
153
|
version_info = version.parse(build.python_version)
|
159
154
|
python_version = f"{version_info.major}.{version_info.minor}"
|
@@ -162,6 +157,13 @@ def run_template(
|
|
162
157
|
f"{sys.version_info.major}.{sys.version_info.minor}"
|
163
158
|
)
|
164
159
|
|
160
|
+
(
|
161
|
+
pypi_requirements,
|
162
|
+
apt_packages,
|
163
|
+
) = requirements_utils.get_requirements_for_stack(
|
164
|
+
stack=stack, python_version=python_version
|
165
|
+
)
|
166
|
+
|
165
167
|
dockerfile = generate_dockerfile(
|
166
168
|
pypi_requirements=pypi_requirements,
|
167
169
|
apt_packages=apt_packages,
|
@@ -169,6 +171,10 @@ def run_template(
|
|
169
171
|
python_version=python_version,
|
170
172
|
)
|
171
173
|
|
174
|
+
# building a docker image with requirements and apt packages from the
|
175
|
+
# stack only (no code). Ideally, only orchestrator requirements should
|
176
|
+
# be added to the docker image, but we have to instantiate the entire
|
177
|
+
# stack to get the orchestrator to run pipelines.
|
172
178
|
image_hash = generate_image_hash(dockerfile=dockerfile)
|
173
179
|
|
174
180
|
runner_image = workload_manager().build_and_push_image(
|
@@ -182,6 +188,9 @@ def run_template(
|
|
182
188
|
workload_id=new_deployment.id,
|
183
189
|
message="Starting pipeline run.",
|
184
190
|
)
|
191
|
+
|
192
|
+
# could do this same thing with a step operator, but we need some
|
193
|
+
# minor changes to the abstract interface to support that.
|
185
194
|
workload_manager().run(
|
186
195
|
workload_id=new_deployment.id,
|
187
196
|
image=runner_image,
|
@@ -22,7 +22,6 @@ from typing import (
|
|
22
22
|
Optional,
|
23
23
|
Tuple,
|
24
24
|
Type,
|
25
|
-
Union,
|
26
25
|
)
|
27
26
|
from uuid import UUID
|
28
27
|
|
@@ -46,6 +45,7 @@ from zenml.enums import (
|
|
46
45
|
from zenml.exceptions import IllegalOperationError
|
47
46
|
from zenml.logger import get_logger
|
48
47
|
from zenml.models import (
|
48
|
+
ProjectFilter,
|
49
49
|
ProjectResponse,
|
50
50
|
ServerDatabaseType,
|
51
51
|
ServerDeploymentType,
|
@@ -291,7 +291,7 @@ class BaseZenStore(
|
|
291
291
|
|
292
292
|
def validate_active_config(
|
293
293
|
self,
|
294
|
-
|
294
|
+
active_project_id: Optional[UUID] = None,
|
295
295
|
active_stack_id: Optional[UUID] = None,
|
296
296
|
config_name: str = "",
|
297
297
|
) -> Tuple[Optional[ProjectResponse], StackResponse]:
|
@@ -306,7 +306,7 @@ class BaseZenStore(
|
|
306
306
|
stack will be returned in their stead.
|
307
307
|
|
308
308
|
Args:
|
309
|
-
|
309
|
+
active_project_id: The ID of the active project.
|
310
310
|
active_stack_id: The ID of the active stack.
|
311
311
|
config_name: The name of the configuration to validate (used in the
|
312
312
|
displayed logs/messages).
|
@@ -316,30 +316,62 @@ class BaseZenStore(
|
|
316
316
|
"""
|
317
317
|
active_project: Optional[ProjectResponse] = None
|
318
318
|
|
319
|
-
if
|
319
|
+
if active_project_id:
|
320
320
|
try:
|
321
|
-
active_project = self.get_project(
|
321
|
+
active_project = self.get_project(active_project_id)
|
322
322
|
except (KeyError, IllegalOperationError):
|
323
|
-
|
323
|
+
active_project_id = None
|
324
324
|
logger.warning(
|
325
325
|
f"The current {config_name} active project is no longer "
|
326
326
|
f"available."
|
327
327
|
)
|
328
328
|
|
329
|
+
if active_project is None:
|
330
|
+
user = self.get_user()
|
331
|
+
if user.default_project_id:
|
332
|
+
try:
|
333
|
+
active_project = self.get_project(user.default_project_id)
|
334
|
+
except (KeyError, IllegalOperationError):
|
335
|
+
logger.warning(
|
336
|
+
"The default project %s for the active user is no longer "
|
337
|
+
"available.",
|
338
|
+
user.default_project_id,
|
339
|
+
)
|
340
|
+
else:
|
341
|
+
logger.info(
|
342
|
+
f"Setting the {config_name} active project "
|
343
|
+
f"to '{active_project.name}'."
|
344
|
+
)
|
345
|
+
|
329
346
|
if active_project is None:
|
330
347
|
try:
|
331
|
-
|
332
|
-
|
348
|
+
projects = self.list_projects(
|
349
|
+
project_filter_model=ProjectFilter()
|
350
|
+
)
|
351
|
+
except Exception:
|
352
|
+
# There was some failure, we force the user to set the active
|
353
|
+
# project manually
|
333
354
|
logger.warning(
|
334
355
|
"An active project is not set. Please set the active "
|
335
|
-
"project by running `zenml project set "
|
336
|
-
"<project-name>`."
|
356
|
+
"project by running `zenml project set <NAME>`."
|
337
357
|
)
|
338
358
|
else:
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
359
|
+
if len(projects) == 0:
|
360
|
+
logger.warning(
|
361
|
+
"No available projects. Please create a project by "
|
362
|
+
"running `zenml project register <NAME> --set`."
|
363
|
+
)
|
364
|
+
elif len(projects) == 1:
|
365
|
+
active_project = projects.items[0]
|
366
|
+
logger.info(
|
367
|
+
f"Setting the {config_name} active project "
|
368
|
+
f"to '{active_project.name}'."
|
369
|
+
)
|
370
|
+
else:
|
371
|
+
logger.warning(
|
372
|
+
"Multiple projects are available. Please set the "
|
373
|
+
"active project by running `zenml project set <NAME>`."
|
374
|
+
)
|
343
375
|
|
344
376
|
active_stack: StackResponse
|
345
377
|
|
@@ -0,0 +1,51 @@
|
|
1
|
+
"""Add user default project [1f1105204aaf].
|
2
|
+
|
3
|
+
Revision ID: 1f1105204aaf
|
4
|
+
Revises: 0.80.0
|
5
|
+
Create Date: 2025-03-25 11:11:13.480745
|
6
|
+
|
7
|
+
"""
|
8
|
+
|
9
|
+
import sqlalchemy as sa
|
10
|
+
import sqlmodel
|
11
|
+
from alembic import op
|
12
|
+
|
13
|
+
# revision identifiers, used by Alembic.
|
14
|
+
revision = "1f1105204aaf"
|
15
|
+
down_revision = "0.80.0"
|
16
|
+
branch_labels = None
|
17
|
+
depends_on = None
|
18
|
+
|
19
|
+
|
20
|
+
def upgrade() -> None:
|
21
|
+
"""Upgrade database schema and/or data, creating a new revision."""
|
22
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
23
|
+
with op.batch_alter_table("user", schema=None) as batch_op:
|
24
|
+
batch_op.add_column(
|
25
|
+
sa.Column(
|
26
|
+
"default_project_id",
|
27
|
+
sqlmodel.sql.sqltypes.GUID(),
|
28
|
+
nullable=True,
|
29
|
+
)
|
30
|
+
)
|
31
|
+
batch_op.create_foreign_key(
|
32
|
+
"fk_user_default_project_id_project",
|
33
|
+
"project",
|
34
|
+
["default_project_id"],
|
35
|
+
["id"],
|
36
|
+
ondelete="SET NULL",
|
37
|
+
)
|
38
|
+
|
39
|
+
# ### end Alembic commands ###
|
40
|
+
|
41
|
+
|
42
|
+
def downgrade() -> None:
|
43
|
+
"""Downgrade database schema and/or data back to the previous revision."""
|
44
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
45
|
+
with op.batch_alter_table("user", schema=None) as batch_op:
|
46
|
+
batch_op.drop_constraint(
|
47
|
+
"fk_user_default_project_id_project", type_="foreignkey"
|
48
|
+
)
|
49
|
+
batch_op.drop_column("default_project_id")
|
50
|
+
|
51
|
+
# ### end Alembic commands ###
|