zenml-nightly 0.84.0.dev20250718__py3-none-any.whl → 0.84.0.dev20250719__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/pipeline.py +33 -0
- zenml/client.py +85 -5
- zenml/integrations/gcp/step_operators/vertex_step_operator.py +9 -6
- zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator.py +83 -13
- zenml/models/v2/core/schedule.py +1 -0
- zenml/orchestrators/base_orchestrator.py +61 -1
- zenml/zen_server/rbac/models.py +1 -0
- zenml/zen_server/rbac/utils.py +6 -0
- zenml/zen_server/routers/schedule_endpoints.py +22 -10
- zenml/zen_stores/schemas/schedule_schema.py +3 -0
- {zenml_nightly-0.84.0.dev20250718.dist-info → zenml_nightly-0.84.0.dev20250719.dist-info}/METADATA +1 -1
- {zenml_nightly-0.84.0.dev20250718.dist-info → zenml_nightly-0.84.0.dev20250719.dist-info}/RECORD +16 -16
- {zenml_nightly-0.84.0.dev20250718.dist-info → zenml_nightly-0.84.0.dev20250719.dist-info}/LICENSE +0 -0
- {zenml_nightly-0.84.0.dev20250718.dist-info → zenml_nightly-0.84.0.dev20250719.dist-info}/WHEEL +0 -0
- {zenml_nightly-0.84.0.dev20250718.dist-info → zenml_nightly-0.84.0.dev20250719.dist-info}/entry_points.txt +0 -0
zenml/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.84.0.
|
1
|
+
0.84.0.dev20250719
|
zenml/cli/pipeline.py
CHANGED
@@ -451,6 +451,39 @@ def list_schedules(**kwargs: Any) -> None:
|
|
451
451
|
)
|
452
452
|
|
453
453
|
|
454
|
+
@schedule.command("update", help="Update a pipeline schedule.")
|
455
|
+
@click.argument("schedule_name_or_id", type=str, required=True)
|
456
|
+
@click.option(
|
457
|
+
"--cron-expression",
|
458
|
+
"-c",
|
459
|
+
type=str,
|
460
|
+
required=False,
|
461
|
+
help="The cron expression to update the schedule with.",
|
462
|
+
)
|
463
|
+
def update_schedule(
|
464
|
+
schedule_name_or_id: str, cron_expression: Optional[str] = None
|
465
|
+
) -> None:
|
466
|
+
"""Update a pipeline schedule.
|
467
|
+
|
468
|
+
Args:
|
469
|
+
schedule_name_or_id: The name or ID of the schedule to update.
|
470
|
+
cron_expression: The cron expression to update the schedule with.
|
471
|
+
"""
|
472
|
+
if not cron_expression:
|
473
|
+
cli_utils.declare("No schedule update requested.")
|
474
|
+
return
|
475
|
+
|
476
|
+
try:
|
477
|
+
Client().update_schedule(
|
478
|
+
name_id_or_prefix=schedule_name_or_id,
|
479
|
+
cron_expression=cron_expression,
|
480
|
+
)
|
481
|
+
except Exception as e:
|
482
|
+
cli_utils.error(str(e))
|
483
|
+
else:
|
484
|
+
cli_utils.declare(f"Updated schedule '{schedule_name_or_id}'.")
|
485
|
+
|
486
|
+
|
454
487
|
@schedule.command("delete", help="Delete a pipeline schedule.")
|
455
488
|
@click.argument("schedule_name_or_id", type=str, required=True)
|
456
489
|
@click.option(
|
zenml/client.py
CHANGED
@@ -148,6 +148,7 @@ from zenml.models import (
|
|
148
148
|
RunTemplateUpdate,
|
149
149
|
ScheduleFilter,
|
150
150
|
ScheduleResponse,
|
151
|
+
ScheduleUpdate,
|
151
152
|
SecretFilter,
|
152
153
|
SecretRequest,
|
153
154
|
SecretResponse,
|
@@ -201,6 +202,7 @@ from zenml.utils.uuid_utils import is_valid_uuid
|
|
201
202
|
|
202
203
|
if TYPE_CHECKING:
|
203
204
|
from zenml.metadata.metadata_types import MetadataType, MetadataTypeEnum
|
205
|
+
from zenml.orchestrators import BaseOrchestrator
|
204
206
|
from zenml.service_connectors.service_connector import ServiceConnector
|
205
207
|
from zenml.services.service import ServiceConfig
|
206
208
|
from zenml.stack import Stack
|
@@ -3806,6 +3808,74 @@ class Client(metaclass=ClientMetaClass):
|
|
3806
3808
|
hydrate=hydrate,
|
3807
3809
|
)
|
3808
3810
|
|
3811
|
+
def _get_orchestrator_for_schedule(
|
3812
|
+
self, schedule: ScheduleResponse
|
3813
|
+
) -> Optional["BaseOrchestrator"]:
|
3814
|
+
"""Get the orchestrator for a schedule.
|
3815
|
+
|
3816
|
+
Args:
|
3817
|
+
schedule: The schedule to get the orchestrator for.
|
3818
|
+
|
3819
|
+
Returns:
|
3820
|
+
The orchestrator for the schedule.
|
3821
|
+
"""
|
3822
|
+
from zenml.orchestrators import BaseOrchestrator
|
3823
|
+
|
3824
|
+
if not schedule.orchestrator_id:
|
3825
|
+
return None
|
3826
|
+
|
3827
|
+
try:
|
3828
|
+
orchestrator_model = self.get_stack_component(
|
3829
|
+
component_type=StackComponentType.ORCHESTRATOR,
|
3830
|
+
name_id_or_prefix=schedule.orchestrator_id,
|
3831
|
+
)
|
3832
|
+
except KeyError:
|
3833
|
+
return None
|
3834
|
+
|
3835
|
+
return cast(
|
3836
|
+
BaseOrchestrator, BaseOrchestrator.from_model(orchestrator_model)
|
3837
|
+
)
|
3838
|
+
|
3839
|
+
def update_schedule(
|
3840
|
+
self,
|
3841
|
+
name_id_or_prefix: Union[str, UUID],
|
3842
|
+
cron_expression: Optional[str] = None,
|
3843
|
+
) -> ScheduleResponse:
|
3844
|
+
"""Update a schedule.
|
3845
|
+
|
3846
|
+
Args:
|
3847
|
+
name_id_or_prefix: The name, id or prefix of the schedule to update.
|
3848
|
+
cron_expression: The new cron expression for the schedule.
|
3849
|
+
|
3850
|
+
Returns:
|
3851
|
+
The updated schedule.
|
3852
|
+
"""
|
3853
|
+
schedule = self.get_schedule(
|
3854
|
+
name_id_or_prefix=name_id_or_prefix,
|
3855
|
+
allow_name_prefix_match=False,
|
3856
|
+
project=self.active_project.id,
|
3857
|
+
)
|
3858
|
+
|
3859
|
+
orchestrator = self._get_orchestrator_for_schedule(schedule)
|
3860
|
+
if not orchestrator:
|
3861
|
+
logger.warning(
|
3862
|
+
"Unable to find orchestrator for schedule, skipping update."
|
3863
|
+
)
|
3864
|
+
return schedule
|
3865
|
+
elif not orchestrator.supports_schedule_updates:
|
3866
|
+
logger.warning(
|
3867
|
+
"Orchestrator does not support schedule updates, skipping "
|
3868
|
+
"update."
|
3869
|
+
)
|
3870
|
+
return schedule
|
3871
|
+
|
3872
|
+
update = ScheduleUpdate(cron_expression=cron_expression)
|
3873
|
+
orchestrator.update_schedule(schedule, update)
|
3874
|
+
return self.zen_store.update_schedule(
|
3875
|
+
schedule_id=schedule.id,
|
3876
|
+
schedule_update=update,
|
3877
|
+
)
|
3878
|
+
|
3809
3879
|
def delete_schedule(
|
3810
3880
|
self,
|
3811
3881
|
name_id_or_prefix: Union[str, UUID],
|
@@ -3823,11 +3893,21 @@ class Client(metaclass=ClientMetaClass):
|
|
3823
3893
|
allow_name_prefix_match=False,
|
3824
3894
|
project=project,
|
3825
3895
|
)
|
3826
|
-
|
3827
|
-
|
3828
|
-
|
3829
|
-
|
3830
|
-
|
3896
|
+
|
3897
|
+
orchestrator = self._get_orchestrator_for_schedule(schedule)
|
3898
|
+
if not orchestrator:
|
3899
|
+
logger.warning(
|
3900
|
+
"Unable to find orchestrator for schedule. Will only delete "
|
3901
|
+
"the schedule reference from ZenML."
|
3902
|
+
)
|
3903
|
+
elif not orchestrator.supports_schedule_deletion:
|
3904
|
+
logger.warning(
|
3905
|
+
"Orchestrator does not support schedule deletion. Will only "
|
3906
|
+
"delete the schedule reference from ZenML."
|
3907
|
+
)
|
3908
|
+
else:
|
3909
|
+
orchestrator.delete_schedule(schedule)
|
3910
|
+
|
3831
3911
|
self.zen_store.delete_schedule(schedule_id=schedule.id)
|
3832
3912
|
|
3833
3913
|
# ----------------------------- Pipeline runs ------------------------------
|
@@ -306,6 +306,14 @@ class VertexStepOperator(BaseStepOperator, GoogleCredentialsMixin):
|
|
306
306
|
|
307
307
|
while response.state not in VERTEX_JOB_STATES_COMPLETED:
|
308
308
|
time.sleep(POLLING_INTERVAL_IN_SECONDS)
|
309
|
+
if self.connector_has_expired():
|
310
|
+
logger.warning("Connector has expired. Recreating client...")
|
311
|
+
# This call will refresh the credentials if they expired.
|
312
|
+
credentials, project_id = self._get_authentication()
|
313
|
+
# Recreate the Python API client.
|
314
|
+
client = aiplatform.gapic.JobServiceClient(
|
315
|
+
credentials=credentials, client_options=client_options
|
316
|
+
)
|
309
317
|
try:
|
310
318
|
response = client.get_custom_job(name=job_id)
|
311
319
|
retry_count = 0
|
@@ -318,12 +326,7 @@ class VertexStepOperator(BaseStepOperator, GoogleCredentialsMixin):
|
|
318
326
|
f"Error encountered when polling job "
|
319
327
|
f"{job_id}: {err}\nRetrying...",
|
320
328
|
)
|
321
|
-
|
322
|
-
credentials, project_id = self._get_authentication()
|
323
|
-
# Recreate the Python API client.
|
324
|
-
client = aiplatform.gapic.JobServiceClient(
|
325
|
-
credentials=credentials, client_options=client_options
|
326
|
-
)
|
329
|
+
continue
|
327
330
|
else:
|
328
331
|
logger.exception(
|
329
332
|
"Request failed after %s retries.",
|
@@ -65,6 +65,7 @@ from zenml.integrations.kubernetes.orchestrators.manifest_utils import (
|
|
65
65
|
from zenml.integrations.kubernetes.pod_settings import KubernetesPodSettings
|
66
66
|
from zenml.logger import get_logger
|
67
67
|
from zenml.metadata.metadata_types import MetadataType
|
68
|
+
from zenml.models.v2.core.schedule import ScheduleUpdate
|
68
69
|
from zenml.orchestrators import ContainerizedOrchestrator, SubmissionResult
|
69
70
|
from zenml.orchestrators.utils import get_orchestrator_run_name
|
70
71
|
from zenml.stack import StackValidator
|
@@ -74,6 +75,7 @@ if TYPE_CHECKING:
|
|
74
75
|
PipelineDeploymentBase,
|
75
76
|
PipelineDeploymentResponse,
|
76
77
|
PipelineRunResponse,
|
78
|
+
ScheduleResponse,
|
77
79
|
)
|
78
80
|
from zenml.stack import Stack
|
79
81
|
|
@@ -81,6 +83,7 @@ logger = get_logger(__name__)
|
|
81
83
|
|
82
84
|
ENV_ZENML_KUBERNETES_RUN_ID = "ZENML_KUBERNETES_RUN_ID"
|
83
85
|
KUBERNETES_SECRET_TOKEN_KEY_NAME = "zenml_api_token"
|
86
|
+
KUBERNETES_CRON_JOB_METADATA_KEY = "cron_job_name"
|
84
87
|
|
85
88
|
|
86
89
|
class KubernetesOrchestrator(ContainerizedOrchestrator):
|
@@ -434,6 +437,7 @@ class KubernetesOrchestrator(ContainerizedOrchestrator):
|
|
434
437
|
|
435
438
|
Raises:
|
436
439
|
RuntimeError: If a schedule without cron expression is given.
|
440
|
+
Exception: If the orchestrator pod fails to start.
|
437
441
|
|
438
442
|
Returns:
|
439
443
|
Optional submission result.
|
@@ -576,7 +580,7 @@ class KubernetesOrchestrator(ContainerizedOrchestrator):
|
|
576
580
|
labels=orchestrator_pod_labels,
|
577
581
|
)
|
578
582
|
|
579
|
-
self._k8s_batch_api.create_namespaced_cron_job(
|
583
|
+
cron_job = self._k8s_batch_api.create_namespaced_cron_job(
|
580
584
|
body=cron_job_manifest,
|
581
585
|
namespace=self.config.kubernetes_namespace,
|
582
586
|
)
|
@@ -584,7 +588,11 @@ class KubernetesOrchestrator(ContainerizedOrchestrator):
|
|
584
588
|
f"Scheduling Kubernetes run `{pod_name}` with CRON expression "
|
585
589
|
f'`"{cron_expression}"`.'
|
586
590
|
)
|
587
|
-
return
|
591
|
+
return SubmissionResult(
|
592
|
+
metadata={
|
593
|
+
KUBERNETES_CRON_JOB_METADATA_KEY: cron_job.metadata.name,
|
594
|
+
}
|
595
|
+
)
|
588
596
|
else:
|
589
597
|
# Create and run the orchestrator pod.
|
590
598
|
pod_manifest = build_pod_manifest(
|
@@ -601,17 +609,34 @@ class KubernetesOrchestrator(ContainerizedOrchestrator):
|
|
601
609
|
termination_grace_period_seconds=settings.pod_stop_grace_period,
|
602
610
|
)
|
603
611
|
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
612
|
+
try:
|
613
|
+
kube_utils.create_and_wait_for_pod_to_start(
|
614
|
+
core_api=self._k8s_core_api,
|
615
|
+
pod_display_name="Kubernetes orchestrator pod",
|
616
|
+
pod_name=pod_name,
|
617
|
+
pod_manifest=pod_manifest,
|
618
|
+
namespace=self.config.kubernetes_namespace,
|
619
|
+
startup_max_retries=settings.pod_failure_max_retries,
|
620
|
+
startup_failure_delay=settings.pod_failure_retry_delay,
|
621
|
+
startup_failure_backoff=settings.pod_failure_backoff,
|
622
|
+
startup_timeout=settings.pod_startup_timeout,
|
623
|
+
)
|
624
|
+
except Exception as e:
|
625
|
+
if self.config.pass_zenml_token_as_secret:
|
626
|
+
secret_name = self.get_token_secret_name(deployment.id)
|
627
|
+
try:
|
628
|
+
kube_utils.delete_secret(
|
629
|
+
core_api=self._k8s_core_api,
|
630
|
+
namespace=self.config.kubernetes_namespace,
|
631
|
+
secret_name=secret_name,
|
632
|
+
)
|
633
|
+
except Exception as cleanup_error:
|
634
|
+
logger.error(
|
635
|
+
"Error cleaning up secret %s: %s",
|
636
|
+
secret_name,
|
637
|
+
cleanup_error,
|
638
|
+
)
|
639
|
+
raise e
|
615
640
|
|
616
641
|
metadata: Dict[str, MetadataType] = {
|
617
642
|
METADATA_ORCHESTRATOR_RUN_ID: pod_name,
|
@@ -980,3 +1005,48 @@ class KubernetesOrchestrator(ContainerizedOrchestrator):
|
|
980
1005
|
return {
|
981
1006
|
METADATA_ORCHESTRATOR_RUN_ID: self.get_orchestrator_run_id(),
|
982
1007
|
}
|
1008
|
+
|
1009
|
+
def update_schedule(
|
1010
|
+
self, schedule: "ScheduleResponse", update: ScheduleUpdate
|
1011
|
+
) -> None:
|
1012
|
+
"""Updates a schedule.
|
1013
|
+
|
1014
|
+
Args:
|
1015
|
+
schedule: The schedule to update.
|
1016
|
+
update: The update to apply to the schedule.
|
1017
|
+
|
1018
|
+
Raises:
|
1019
|
+
RuntimeError: If the cron job name is not found.
|
1020
|
+
"""
|
1021
|
+
cron_job_name = schedule.run_metadata.get(
|
1022
|
+
KUBERNETES_CRON_JOB_METADATA_KEY
|
1023
|
+
)
|
1024
|
+
if not cron_job_name:
|
1025
|
+
raise RuntimeError("Unable to find cron job name for schedule.")
|
1026
|
+
|
1027
|
+
if update.cron_expression:
|
1028
|
+
self._k8s_batch_api.patch_namespaced_cron_job(
|
1029
|
+
name=cron_job_name,
|
1030
|
+
namespace=self.config.kubernetes_namespace,
|
1031
|
+
body={"spec": {"schedule": update.cron_expression}},
|
1032
|
+
)
|
1033
|
+
|
1034
|
+
def delete_schedule(self, schedule: "ScheduleResponse") -> None:
|
1035
|
+
"""Deletes a schedule.
|
1036
|
+
|
1037
|
+
Args:
|
1038
|
+
schedule: The schedule to delete.
|
1039
|
+
|
1040
|
+
Raises:
|
1041
|
+
RuntimeError: If the cron job name is not found.
|
1042
|
+
"""
|
1043
|
+
cron_job_name = schedule.run_metadata.get(
|
1044
|
+
KUBERNETES_CRON_JOB_METADATA_KEY
|
1045
|
+
)
|
1046
|
+
if not cron_job_name:
|
1047
|
+
raise RuntimeError("Unable to find cron job name for schedule.")
|
1048
|
+
|
1049
|
+
self._k8s_batch_api.delete_namespaced_cron_job(
|
1050
|
+
name=cron_job_name,
|
1051
|
+
namespace=self.config.kubernetes_namespace,
|
1052
|
+
)
|
zenml/models/v2/core/schedule.py
CHANGED
@@ -50,7 +50,12 @@ from zenml.utils.pydantic_utils import before_validator_handler
|
|
50
50
|
|
51
51
|
if TYPE_CHECKING:
|
52
52
|
from zenml.config.step_configurations import Step
|
53
|
-
from zenml.models import
|
53
|
+
from zenml.models import (
|
54
|
+
PipelineDeploymentResponse,
|
55
|
+
PipelineRunResponse,
|
56
|
+
ScheduleResponse,
|
57
|
+
ScheduleUpdate,
|
58
|
+
)
|
54
59
|
|
55
60
|
logger = get_logger(__name__)
|
56
61
|
|
@@ -522,6 +527,61 @@ class BaseOrchestrator(StackComponent, ABC):
|
|
522
527
|
f"'{self.__class__.__name__}' orchestrator."
|
523
528
|
)
|
524
529
|
|
530
|
+
@property
|
531
|
+
def supports_schedule_updates(self) -> bool:
|
532
|
+
"""Whether the orchestrator supports updating schedules.
|
533
|
+
|
534
|
+
Returns:
|
535
|
+
Whether the orchestrator supports updating schedules.
|
536
|
+
"""
|
537
|
+
return (
|
538
|
+
getattr(self.update_schedule, "__func__", None)
|
539
|
+
is not BaseOrchestrator.update_schedule
|
540
|
+
)
|
541
|
+
|
542
|
+
@property
|
543
|
+
def supports_schedule_deletion(self) -> bool:
|
544
|
+
"""Whether the orchestrator supports deleting schedules.
|
545
|
+
|
546
|
+
Returns:
|
547
|
+
Whether the orchestrator supports deleting schedules.
|
548
|
+
"""
|
549
|
+
return (
|
550
|
+
getattr(self.delete_schedule, "__func__", None)
|
551
|
+
is not BaseOrchestrator.delete_schedule
|
552
|
+
)
|
553
|
+
|
554
|
+
def update_schedule(
|
555
|
+
self, schedule: "ScheduleResponse", update: "ScheduleUpdate"
|
556
|
+
) -> None:
|
557
|
+
"""Updates a schedule.
|
558
|
+
|
559
|
+
Args:
|
560
|
+
schedule: The schedule to update.
|
561
|
+
update: The update to apply to the schedule.
|
562
|
+
|
563
|
+
Raises:
|
564
|
+
NotImplementedError: If the functionality is not implemented.
|
565
|
+
"""
|
566
|
+
raise NotImplementedError(
|
567
|
+
"Schedule updating is not implemented for the "
|
568
|
+
f"'{self.__class__.__name__}' orchestrator."
|
569
|
+
)
|
570
|
+
|
571
|
+
def delete_schedule(self, schedule: "ScheduleResponse") -> None:
|
572
|
+
"""Deletes a schedule.
|
573
|
+
|
574
|
+
Args:
|
575
|
+
schedule: The schedule to delete.
|
576
|
+
|
577
|
+
Raises:
|
578
|
+
NotImplementedError: If the functionality is not implemented.
|
579
|
+
"""
|
580
|
+
raise NotImplementedError(
|
581
|
+
"Schedule deletion is not implemented for the "
|
582
|
+
f"'{self.__class__.__name__}' orchestrator."
|
583
|
+
)
|
584
|
+
|
525
585
|
|
526
586
|
class BaseOrchestratorFlavor(Flavor):
|
527
587
|
"""Base orchestrator flavor class."""
|
zenml/zen_server/rbac/models.py
CHANGED
zenml/zen_server/rbac/utils.py
CHANGED
@@ -461,6 +461,8 @@ def get_resource_type_for_model(
|
|
461
461
|
RunMetadataRequest,
|
462
462
|
RunTemplateRequest,
|
463
463
|
RunTemplateResponse,
|
464
|
+
ScheduleRequest,
|
465
|
+
ScheduleResponse,
|
464
466
|
SecretRequest,
|
465
467
|
SecretResponse,
|
466
468
|
ServiceAccountRequest,
|
@@ -512,6 +514,8 @@ def get_resource_type_for_model(
|
|
512
514
|
RunMetadataRequest: ResourceType.RUN_METADATA,
|
513
515
|
RunTemplateRequest: ResourceType.RUN_TEMPLATE,
|
514
516
|
RunTemplateResponse: ResourceType.RUN_TEMPLATE,
|
517
|
+
ScheduleRequest: ResourceType.SCHEDULE,
|
518
|
+
ScheduleResponse: ResourceType.SCHEDULE,
|
515
519
|
SecretRequest: ResourceType.SECRET,
|
516
520
|
SecretResponse: ResourceType.SECRET,
|
517
521
|
ServiceAccountRequest: ResourceType.SERVICE_ACCOUNT,
|
@@ -649,6 +653,7 @@ def get_schema_for_resource_type(
|
|
649
653
|
PipelineSchema,
|
650
654
|
RunMetadataSchema,
|
651
655
|
RunTemplateSchema,
|
656
|
+
ScheduleSchema,
|
652
657
|
SecretSchema,
|
653
658
|
ServiceConnectorSchema,
|
654
659
|
ServiceSchema,
|
@@ -681,6 +686,7 @@ def get_schema_for_resource_type(
|
|
681
686
|
ResourceType.PIPELINE_BUILD: PipelineBuildSchema,
|
682
687
|
ResourceType.RUN_TEMPLATE: RunTemplateSchema,
|
683
688
|
ResourceType.RUN_METADATA: RunMetadataSchema,
|
689
|
+
ResourceType.SCHEDULE: ScheduleSchema,
|
684
690
|
# ResourceType.USER: UserSchema,
|
685
691
|
ResourceType.ACTION: ActionSchema,
|
686
692
|
ResourceType.EVENT_SOURCE: EventSourceSchema,
|
@@ -30,7 +30,12 @@ from zenml.zen_server.auth import AuthContext, authorize
|
|
30
30
|
from zenml.zen_server.exceptions import error_response
|
31
31
|
from zenml.zen_server.rbac.endpoint_utils import (
|
32
32
|
verify_permissions_and_create_entity,
|
33
|
+
verify_permissions_and_delete_entity,
|
34
|
+
verify_permissions_and_get_entity,
|
35
|
+
verify_permissions_and_list_entities,
|
36
|
+
verify_permissions_and_update_entity,
|
33
37
|
)
|
38
|
+
from zenml.zen_server.rbac.models import ResourceType
|
34
39
|
from zenml.zen_server.routers.projects_endpoints import workspace_router
|
35
40
|
from zenml.zen_server.utils import (
|
36
41
|
async_fastapi_endpoint_wrapper,
|
@@ -77,8 +82,6 @@ def create_schedule(
|
|
77
82
|
project = zen_store().get_project(project_name_or_id)
|
78
83
|
schedule.project = project.id
|
79
84
|
|
80
|
-
# NOTE: no RBAC is enforced currently for schedules, but we're
|
81
|
-
# keeping the RBAC checks here for consistency
|
82
85
|
return verify_permissions_and_create_entity(
|
83
86
|
request_model=schedule,
|
84
87
|
create_method=zen_store().create_schedule,
|
@@ -121,8 +124,10 @@ def list_schedules(
|
|
121
124
|
if project_name_or_id:
|
122
125
|
schedule_filter_model.project = project_name_or_id
|
123
126
|
|
124
|
-
return
|
125
|
-
|
127
|
+
return verify_permissions_and_list_entities(
|
128
|
+
filter_model=schedule_filter_model,
|
129
|
+
resource_type=ResourceType.SCHEDULE,
|
130
|
+
list_method=zen_store().list_schedules,
|
126
131
|
hydrate=hydrate,
|
127
132
|
)
|
128
133
|
|
@@ -147,8 +152,9 @@ def get_schedule(
|
|
147
152
|
Returns:
|
148
153
|
A specific schedule object.
|
149
154
|
"""
|
150
|
-
return
|
151
|
-
|
155
|
+
return verify_permissions_and_get_entity(
|
156
|
+
id=schedule_id,
|
157
|
+
get_method=zen_store().get_schedule,
|
152
158
|
hydrate=hydrate,
|
153
159
|
)
|
154
160
|
|
@@ -172,9 +178,11 @@ def update_schedule(
|
|
172
178
|
Returns:
|
173
179
|
The updated schedule object.
|
174
180
|
"""
|
175
|
-
return
|
176
|
-
|
177
|
-
|
181
|
+
return verify_permissions_and_update_entity(
|
182
|
+
id=schedule_id,
|
183
|
+
update_model=schedule_update,
|
184
|
+
get_method=zen_store().get_schedule,
|
185
|
+
update_method=zen_store().update_schedule,
|
178
186
|
)
|
179
187
|
|
180
188
|
|
@@ -192,4 +200,8 @@ def delete_schedule(
|
|
192
200
|
Args:
|
193
201
|
schedule_id: ID of the schedule to delete.
|
194
202
|
"""
|
195
|
-
|
203
|
+
verify_permissions_and_delete_entity(
|
204
|
+
id=schedule_id,
|
205
|
+
get_method=zen_store().get_schedule,
|
206
|
+
delete_method=zen_store().delete_schedule,
|
207
|
+
)
|
@@ -202,6 +202,9 @@ class ScheduleSchema(NamedSchema, RunMetadataInterface, table=True):
|
|
202
202
|
if schedule_update.name is not None:
|
203
203
|
self.name = schedule_update.name
|
204
204
|
|
205
|
+
if schedule_update.cron_expression:
|
206
|
+
self.cron_expression = schedule_update.cron_expression
|
207
|
+
|
205
208
|
self.updated = utc_now()
|
206
209
|
return self
|
207
210
|
|
{zenml_nightly-0.84.0.dev20250718.dist-info → zenml_nightly-0.84.0.dev20250719.dist-info}/RECORD
RENAMED
@@ -1,5 +1,5 @@
|
|
1
1
|
zenml/README.md,sha256=827dekbOWAs1BpW7VF1a4d7EbwPbjwccX-2zdXBENZo,1777
|
2
|
-
zenml/VERSION,sha256=
|
2
|
+
zenml/VERSION,sha256=yjGFrz90XGxPchLqDA7W1CXkoa7C9RMiL5PhpTeqB3E,19
|
3
3
|
zenml/__init__.py,sha256=r7JUg2SVDf_dPhS7iU6vudKusEqK4ics7_jFMZhq0o4,2731
|
4
4
|
zenml/actions/__init__.py,sha256=mrt6wPo73iKRxK754_NqsGyJ3buW7RnVeIGXr1xEw8Y,681
|
5
5
|
zenml/actions/base_action.py,sha256=UcaHev6BTuLDwuswnyaPjdA8AgUqB5xPZ-lRtuvf2FU,25553
|
@@ -41,7 +41,7 @@ zenml/cli/integration.py,sha256=Rq37R-QiSvaFOMmlA0YJTwDti4slLp-hEf05vbWxkFk,1734
|
|
41
41
|
zenml/cli/login.py,sha256=0h1ChK0Nh0ppnErmlaz6JMXLOHsVmbIdYVCVFheCwTA,43295
|
42
42
|
zenml/cli/model.py,sha256=_6la1czUXPBWBkiZLS6iwGHMy2z4vJLX0Z2Ho2zV48M,21921
|
43
43
|
zenml/cli/model_registry.py,sha256=zzxWXXFhKu2B1Wp0u7prKVnN1ftM-JdGdQwlD-5G-QM,20786
|
44
|
-
zenml/cli/pipeline.py,sha256=
|
44
|
+
zenml/cli/pipeline.py,sha256=8voH4r3nqwpC4_az1j-cJdLUZsEaP0o9vL1T8rNsssM,22194
|
45
45
|
zenml/cli/project.py,sha256=Co2Spcgyw6hm4YIquxYJgIhwE2sTCuV0EehzXHmh2xM,6574
|
46
46
|
zenml/cli/secret.py,sha256=zwt07v0xoIf_dLf-qY5CFdbKepBEuwmXD2HIMcLe_xU,20164
|
47
47
|
zenml/cli/served_model.py,sha256=3w1UcAbg6Geu37fr7ej1_81GBCt3fF7j3Ge799YE4Mc,14974
|
@@ -55,7 +55,7 @@ zenml/cli/text_utils.py,sha256=bY1GIjoULt1cW2FyrPlMoAXNS2R7cSOjDFEZQqrpVQ8,3553
|
|
55
55
|
zenml/cli/user_management.py,sha256=sNnhaUxH-cHecbZBR1L0mEU0TnLNZHzI6ZBCUSQa7OY,13078
|
56
56
|
zenml/cli/utils.py,sha256=9sKbKFZ3lEtJ9za0VrsRHLU4Q6Ls8e-tIBiB_Go38hI,86889
|
57
57
|
zenml/cli/version.py,sha256=nm1iSU_1V6-MUwpMKeXcwFhLYGUMLswvQL67cEuCpxA,3635
|
58
|
-
zenml/client.py,sha256=
|
58
|
+
zenml/client.py,sha256=yyLVCjw1KAYJj5CUJiEpz2lNR86oquxZvuvL5abbsxI,296101
|
59
59
|
zenml/client_lazy_loader.py,sha256=oyxKvBWVB7k2pHMavdhNEOfR2Vk4IS3XUu43SBzDPsI,7152
|
60
60
|
zenml/code_repositories/__init__.py,sha256=W5bDfzAG8OXIKZSV1L-VHuzMcSCYa9qzTdPb3jqfyYw,920
|
61
61
|
zenml/code_repositories/base_code_repository.py,sha256=Id6VjbUu8N3ZpNvBGhOgbahtoMiCAtYXed3G7YQ_iAc,5225
|
@@ -273,7 +273,7 @@ zenml/integrations/gcp/orchestrators/vertex_orchestrator.py,sha256=xkxyYZGUzo89-
|
|
273
273
|
zenml/integrations/gcp/service_connectors/__init__.py,sha256=fdydawaor8KAtMYvRZieiTuA1i5QATxXXgI-yV1lsn8,788
|
274
274
|
zenml/integrations/gcp/service_connectors/gcp_service_connector.py,sha256=9u-vEHbmSyN5IGwYI8v39TcFZg5ObgkxlbwSPz-e5zE,95018
|
275
275
|
zenml/integrations/gcp/step_operators/__init__.py,sha256=iPkob2LtPIQ-OHszhbNz_ojhoovL6SprmTx37It4EJ8,808
|
276
|
-
zenml/integrations/gcp/step_operators/vertex_step_operator.py,sha256=
|
276
|
+
zenml/integrations/gcp/step_operators/vertex_step_operator.py,sha256=RPdawIfc538Umd3bAxsUHnEucJdrUWKmFaA3a_CVKcw,13698
|
277
277
|
zenml/integrations/gcp/vertex_custom_job_parameters.py,sha256=B5RLkw7KDOi4ZfWHFnC6TGLphXMzToMjROxszCEAS9c,3676
|
278
278
|
zenml/integrations/github/__init__.py,sha256=movoEHoX8SpY0t6mBS5D64enxG6lPkpYJIEQ9jElHLA,1427
|
279
279
|
zenml/integrations/github/code_repositories/__init__.py,sha256=ub_hSE2ks2mZB1aeHRjQYz7QIRQIgOw2s080IIqJaGs,817
|
@@ -338,7 +338,7 @@ zenml/integrations/kubernetes/flavors/kubernetes_orchestrator_flavor.py,sha256=M
|
|
338
338
|
zenml/integrations/kubernetes/flavors/kubernetes_step_operator_flavor.py,sha256=Vp7kY9t2TWdWRBNSf_sJKPL5dZnhuHnskPhQ7KBuGPY,6384
|
339
339
|
zenml/integrations/kubernetes/orchestrators/__init__.py,sha256=TJID3OTieZBox36WpQpzD0jdVRA_aZVcs_bNtfXS8ik,811
|
340
340
|
zenml/integrations/kubernetes/orchestrators/kube_utils.py,sha256=ReqiXBfmtdmFcRU0WIOPfkZO57n-AFGdo-WweNr60VQ,24698
|
341
|
-
zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator.py,sha256=
|
341
|
+
zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator.py,sha256=Agx_Thj6nByAd5p61HTf0p6SRDPOcYKSSl-_Km2EdfU,41316
|
342
342
|
zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator_entrypoint.py,sha256=9swZf_e_rmtQZyQoBMzaweV_w9Tp_iBCmShJBHuveSM,19605
|
343
343
|
zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator_entrypoint_configuration.py,sha256=QOwQnWCfB-t_BQ2eOZN0SakurGUd0GTMCSdUlREhk6I,2324
|
344
344
|
zenml/integrations/kubernetes/orchestrators/manifest_utils.py,sha256=52Ba6MZ4lCo6CpnW0IjnFOJQNpPoVgbvIqR-x32ubew,16085
|
@@ -653,7 +653,7 @@ zenml/models/v2/core/pipeline_run.py,sha256=lGMzt2AS9-5kHFZa6MBDfx68LLJu4ohMRGht
|
|
653
653
|
zenml/models/v2/core/project.py,sha256=fNNO8Tg5OhSzmFf2t6g4SpUzGWC96oHhUccVyWytvIE,5627
|
654
654
|
zenml/models/v2/core/run_metadata.py,sha256=hRGQa_sk99uDSab3EyyOQhefypVpiQDCH3oAtblREDk,2432
|
655
655
|
zenml/models/v2/core/run_template.py,sha256=6jdH1son7kpvFv-vtaOL2nXMATtVpqk_7a2xOVv_7cc,14097
|
656
|
-
zenml/models/v2/core/schedule.py,sha256=
|
656
|
+
zenml/models/v2/core/schedule.py,sha256=b0ywURfW36agoHpiSdbN0CDbMYG-WNGetNZNLQnMMrg,10418
|
657
657
|
zenml/models/v2/core/secret.py,sha256=t2SneCxe60OSvWznWPl2Z-VerUbBsTlm8SVKwaw6PlM,9464
|
658
658
|
zenml/models/v2/core/server_settings.py,sha256=NNNsYM2AwfsPmA2kfTMwqxn0__9WDoii52n2DNxrcq0,6263
|
659
659
|
zenml/models/v2/core/service.py,sha256=PeI036PIVG0zX5EiYPsx7h5LTRC8hlmfdeClKff-IaU,16106
|
@@ -682,7 +682,7 @@ zenml/models/v2/misc/statistics.py,sha256=ajce9rHC2bBylLzOmLfcOBSbTnJD67Y8n5YKCY
|
|
682
682
|
zenml/models/v2/misc/tag.py,sha256=jUoz7wqMpDFlIUmvj4PVq8NYJJB7TMCcdRfW-DAABCU,974
|
683
683
|
zenml/models/v2/misc/user_auth.py,sha256=1-yafNA9qK4wL8ToROjaklTVI7Mj9va0t80_4wm7w3U,6988
|
684
684
|
zenml/orchestrators/__init__.py,sha256=Nhmc6U-q6c8TEH1Jb5l8XaKnc4KmLNspDpvvV5TcvzQ,2007
|
685
|
-
zenml/orchestrators/base_orchestrator.py,sha256=
|
685
|
+
zenml/orchestrators/base_orchestrator.py,sha256=m9qsavtb4fQv1pie8cnuQlOVxu_p2qJQ8X7jukijFZ4,21445
|
686
686
|
zenml/orchestrators/cache_utils.py,sha256=QkmTs-ANfXve9_QzTqgGlyulZDEWOngoTcsiSjG5aA8,5906
|
687
687
|
zenml/orchestrators/containerized_orchestrator.py,sha256=-umxlNlQfezks1GLoi0SyXtXXo9dkC4Re9nA0fh_avw,3858
|
688
688
|
zenml/orchestrators/dag_runner.py,sha256=6pDloHKuGvOTaABJ8G8OJ94f_8uPfeN-M0Nc5sFjHwA,10708
|
@@ -1038,10 +1038,10 @@ zenml/zen_server/middleware.py,sha256=4NQqQNh-qOKXDYGHXVkTwomfa1jA1M2yAxjkBXeaXN
|
|
1038
1038
|
zenml/zen_server/rate_limit.py,sha256=rOg5H_6rOGQ_qiSCtMKPREmL1LL3bZyn4czKILtImJg,6057
|
1039
1039
|
zenml/zen_server/rbac/__init__.py,sha256=nACbn_G7nZt_AWM3zeFL0FCmELvQnvaOFMwvTG3-6ZE,637
|
1040
1040
|
zenml/zen_server/rbac/endpoint_utils.py,sha256=wPwP-7Vbxbrb8KfY5vO8xA45TrUAUQeM5a0iA1xbXkQ,11582
|
1041
|
-
zenml/zen_server/rbac/models.py,sha256=
|
1041
|
+
zenml/zen_server/rbac/models.py,sha256=zl42U7YpZZ4TUgoWJci_haRxvcZ9VbjykYdKtQcm984,4959
|
1042
1042
|
zenml/zen_server/rbac/rbac_interface.py,sha256=VPMNnUIPcqJWDngITX6cfaPlM6zJwzCApXAnT_oaxSQ,3431
|
1043
1043
|
zenml/zen_server/rbac/rbac_sql_zen_store.py,sha256=ZBjAbCUlhMsp8GZiIkULl2ztJ8K2Wf-0QdSxzQXwovM,5930
|
1044
|
-
zenml/zen_server/rbac/utils.py,sha256=
|
1044
|
+
zenml/zen_server/rbac/utils.py,sha256=7oECbNT9_cLNecG_srSTsVZWcqqt-n2b1tXhuSTJQpk,24746
|
1045
1045
|
zenml/zen_server/rbac/zenml_cloud_rbac.py,sha256=fQRQsHYZsifjAAYAZALqiSB8ZjqIxjF1FfnXcyU7HF8,6093
|
1046
1046
|
zenml/zen_server/request_management.py,sha256=nIpCdHAs26jtxRUKgQ3UQ8Jgrx2s0YHs9fofXBHWnjg,21373
|
1047
1047
|
zenml/zen_server/routers/__init__.py,sha256=ViyAhWL-ogHxE9wBvB_iMcur5H1NRVrzXkpogVY7FBA,641
|
@@ -1064,7 +1064,7 @@ zenml/zen_server/routers/projects_endpoints.py,sha256=uL7oK8_0-H2nhjvPYHfYPvJOU4
|
|
1064
1064
|
zenml/zen_server/routers/run_metadata_endpoints.py,sha256=hUatO7c6LUBHpMDZYgDqiUbVUSBpt6-ZxaSC9eAaF9I,3688
|
1065
1065
|
zenml/zen_server/routers/run_templates_endpoints.py,sha256=zUO8bTDQj7fwtQbnxPvkcdWHuhljpIK3YRMzCyp-jZc,8614
|
1066
1066
|
zenml/zen_server/routers/runs_endpoints.py,sha256=N63sOlpTgsmMapCBuVV1mlbd3pVhYfr-Kv7P_MNDK78,14313
|
1067
|
-
zenml/zen_server/routers/schedule_endpoints.py,sha256=
|
1067
|
+
zenml/zen_server/routers/schedule_endpoints.py,sha256=sEgfnFwNyNCe-0y77oQ1dHWYJ7Hdzq2yn6yqEpXOTc8,6304
|
1068
1068
|
zenml/zen_server/routers/secrets_endpoints.py,sha256=VgepInQHZXt-d8IAinftHs1n9il6hTD1qca6w5Pnnsk,9302
|
1069
1069
|
zenml/zen_server/routers/server_endpoints.py,sha256=Aci-7TB5VZylmasEYc2tVpG7Npgtp6Dj56nrmy7mMmc,8133
|
1070
1070
|
zenml/zen_server/routers/service_accounts_endpoints.py,sha256=q-vFzzYYorqaOPUnESQU06h2I-BaZvIyPxCAhQ4gLPw,12086
|
@@ -1323,7 +1323,7 @@ zenml/zen_stores/schemas/pipeline_schemas.py,sha256=xgioTeBuFFFDOJi5eESx2j-8mW55
|
|
1323
1323
|
zenml/zen_stores/schemas/project_schemas.py,sha256=X2GClPNQz0COsEZX8xI-I8Sm68Hb6f20Obm24mQyLS0,6013
|
1324
1324
|
zenml/zen_stores/schemas/run_metadata_schemas.py,sha256=G94rT4ldluMSnf9rm7R_9rw_GlgaAyq72ptkHl0gHeg,3605
|
1325
1325
|
zenml/zen_stores/schemas/run_template_schemas.py,sha256=dmF0N5dXc-lIEdMBPvobHS0f6NROGbMBm6OH-CGr2lo,12359
|
1326
|
-
zenml/zen_stores/schemas/schedule_schema.py,sha256=
|
1326
|
+
zenml/zen_stores/schemas/schedule_schema.py,sha256=M_xq8jyeHcwtjfpmEk3oSdCTGVWRnZVtrRtOOQn1zPw,9073
|
1327
1327
|
zenml/zen_stores/schemas/schema_utils.py,sha256=Xahifq2fJ5szXCM00ZZ6461Div9Suatzl6sy9hVhPkk,3612
|
1328
1328
|
zenml/zen_stores/schemas/secret_schemas.py,sha256=gFm5X6-FJDHk7Eix6_3Xbevvne0qyUMGLIXxcNUHQSY,10741
|
1329
1329
|
zenml/zen_stores/schemas/server_settings_schemas.py,sha256=usBE-idRrmK-LeLN0zDtCRCGP51YTnyKfIx5GZ0_ATg,4275
|
@@ -1347,8 +1347,8 @@ zenml/zen_stores/secrets_stores/sql_secrets_store.py,sha256=LPFW757WCJLP1S8vrvjs
|
|
1347
1347
|
zenml/zen_stores/sql_zen_store.py,sha256=cXcKBBRKiwM2GtwHzmJb6tiN5NhGWo9n8SWnL1b4_WE,491150
|
1348
1348
|
zenml/zen_stores/template_utils.py,sha256=iCXrXpqzVTY7roqop4Eh9J7DmLW6PQeILZexmw_l3b8,10074
|
1349
1349
|
zenml/zen_stores/zen_store_interface.py,sha256=weiSULdI9AsbCE10a5TcwtybX-BJs9hKhjPJnTapWv4,93023
|
1350
|
-
zenml_nightly-0.84.0.
|
1351
|
-
zenml_nightly-0.84.0.
|
1352
|
-
zenml_nightly-0.84.0.
|
1353
|
-
zenml_nightly-0.84.0.
|
1354
|
-
zenml_nightly-0.84.0.
|
1350
|
+
zenml_nightly-0.84.0.dev20250719.dist-info/LICENSE,sha256=wbnfEnXnafPbqwANHkV6LUsPKOtdpsd-SNw37rogLtc,11359
|
1351
|
+
zenml_nightly-0.84.0.dev20250719.dist-info/METADATA,sha256=zfJh2gUijnWxKLO5XTcZvcVWfUPxxHS5jjZ4nUjfPg4,24296
|
1352
|
+
zenml_nightly-0.84.0.dev20250719.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
1353
|
+
zenml_nightly-0.84.0.dev20250719.dist-info/entry_points.txt,sha256=QK3ETQE0YswAM2mWypNMOv8TLtr7EjnqAFq1br_jEFE,43
|
1354
|
+
zenml_nightly-0.84.0.dev20250719.dist-info/RECORD,,
|
{zenml_nightly-0.84.0.dev20250718.dist-info → zenml_nightly-0.84.0.dev20250719.dist-info}/LICENSE
RENAMED
File without changes
|
{zenml_nightly-0.84.0.dev20250718.dist-info → zenml_nightly-0.84.0.dev20250719.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|