zenml-nightly 0.68.1.dev20241111__py3-none-any.whl → 0.70.0.dev20241114__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.
- README.md +1 -1
- RELEASE_NOTES.md +77 -0
- zenml/VERSION +1 -1
- zenml/__init__.py +2 -0
- zenml/artifacts/utils.py +5 -1
- zenml/cli/base.py +1 -1
- zenml/client.py +9 -2
- zenml/config/server_config.py +17 -1
- zenml/constants.py +1 -7
- zenml/enums.py +8 -0
- zenml/exceptions.py +4 -0
- zenml/integrations/aws/orchestrators/sagemaker_orchestrator.py +1 -14
- zenml/integrations/lightning/orchestrators/lightning_orchestrator.py +29 -9
- zenml/model/model.py +16 -62
- zenml/model/utils.py +5 -0
- zenml/models/v2/base/filter.py +121 -8
- zenml/models/v2/core/artifact_version.py +23 -0
- zenml/models/v2/core/model_version.py +23 -0
- zenml/models/v2/core/pipeline_run.py +22 -1
- zenml/models/v2/core/step_run.py +22 -0
- zenml/orchestrators/base_orchestrator.py +12 -1
- zenml/orchestrators/step_launcher.py +2 -1
- zenml/orchestrators/utils.py +45 -26
- zenml/stack_deployments/aws_stack_deployment.py +23 -6
- zenml/stack_deployments/azure_stack_deployment.py +28 -5
- zenml/stack_deployments/gcp_stack_deployment.py +25 -8
- zenml/stack_deployments/stack_deployment.py +3 -5
- zenml/steps/utils.py +5 -0
- zenml/utils/metadata_utils.py +335 -0
- zenml/zen_server/auth.py +221 -3
- zenml/zen_server/cache.py +208 -0
- zenml/zen_server/dashboard/assets/{404-DT4QRUqN.js → 404-NVXKFp-x.js} +1 -1
- zenml/zen_server/dashboard/assets/{@radix-DP6vWzyx.js → @radix-DeK6qiuw.js} +1 -1
- zenml/zen_server/dashboard/assets/{@react-router-BMhZulnd.js → @react-router-B3Z5rLr2.js} +1 -1
- zenml/zen_server/dashboard/assets/{@reactflow-8U9qNlMR.js → @reactflow-CK0KJUen.js} +2 -2
- zenml/zen_server/dashboard/assets/{@tanstack-BUCbhJyH.js → @tanstack-DT5WLu9C.js} +1 -1
- zenml/zen_server/dashboard/assets/AlertDialogDropdownItem-DezXKmDf.js +1 -0
- zenml/zen_server/dashboard/assets/{CodeSnippet-CqybNv0k.js → CodeSnippet-JzR8CEtw.js} +2 -2
- zenml/zen_server/dashboard/assets/{CollapsibleCard-0r_8G2Lj.js → CollapsibleCard-DQW_ktMO.js} +1 -1
- zenml/zen_server/dashboard/assets/{Commands-BDjgBQKi.js → Commands-DL2kwkRd.js} +1 -1
- zenml/zen_server/dashboard/assets/ComponentBadge-D_g62Wv8.js +1 -0
- zenml/zen_server/dashboard/assets/{CopyButton-C745BrKi.js → CopyButton-LNcWaa14.js} +1 -1
- zenml/zen_server/dashboard/assets/{CsvVizualization-PpAq0CeZ.js → CsvVizualization-DknpE5ej.js} +5 -5
- zenml/zen_server/dashboard/assets/{DialogItem-DcVCZEew.js → DialogItem-Bxf8FuAT.js} +1 -1
- zenml/zen_server/dashboard/assets/{DisplayDate-BeXgUG_C.js → DisplayDate-CDMUcQHS.js} +1 -1
- zenml/zen_server/dashboard/assets/{EmptyState-DeK7H4pr.js → EmptyState-BzdlCwp3.js} +1 -1
- zenml/zen_server/dashboard/assets/{Error-BMlzibXj.js → Error-DYflYyps.js} +1 -1
- zenml/zen_server/dashboard/assets/ExecutionStatus-C7zyIQKZ.js +1 -0
- zenml/zen_server/dashboard/assets/{Helpbox-BLf40fLV.js → Helpbox-oYSGpLqd.js} +1 -1
- zenml/zen_server/dashboard/assets/{Infobox-BwisKifi.js → Infobox-Cx4xGoXR.js} +1 -1
- zenml/zen_server/dashboard/assets/{InlineAvatar-jEgodSgX.js → InlineAvatar-DiGOWNKF.js} +1 -1
- zenml/zen_server/dashboard/assets/{Lock-3lLt1ih0.js → Lock-CYYy18Mm.js} +1 -1
- zenml/zen_server/dashboard/assets/{MarkdownVisualization-8O9kTr-2.js → MarkdownVisualization-ylXaAxev.js} +1 -1
- zenml/zen_server/dashboard/assets/NestedCollapsible-DYbgyKxK.js +1 -0
- zenml/zen_server/dashboard/assets/{NumberBox-T9eELfLZ.js → NumberBox-Dtp3J6g5.js} +1 -1
- zenml/zen_server/dashboard/assets/Partials-03iZf8-N.js +1 -0
- zenml/zen_server/dashboard/assets/{PasswordChecker-CW0kqY0W.js → PasswordChecker-B0nadgh6.js} +1 -1
- zenml/zen_server/dashboard/assets/ProBadge-D_EB8HNo.js +1 -0
- zenml/zen_server/dashboard/assets/ProCta-DqNS4v3x.js +1 -0
- zenml/zen_server/dashboard/assets/ProviderIcon-Bki2aw8w.js +1 -0
- zenml/zen_server/dashboard/assets/{ProviderRadio-BROY1700.js → ProviderRadio-8f43sPD4.js} +1 -1
- zenml/zen_server/dashboard/assets/RunSelector-DkPiIiNr.js +1 -0
- zenml/zen_server/dashboard/assets/RunsBody-07YEO7qI.js +1 -0
- zenml/zen_server/dashboard/assets/SearchField-lp1KgU4e.js +1 -0
- zenml/zen_server/dashboard/assets/{SecretTooltip-C_qByGWB.js → SecretTooltip-CgnbyeOx.js} +1 -1
- zenml/zen_server/dashboard/assets/{SetPassword-7pRB00El.js → SetPassword-CpP418A2.js} +1 -1
- zenml/zen_server/dashboard/assets/StackList-WvuKQusZ.js +1 -0
- zenml/zen_server/dashboard/assets/Tabs-BktHkCJJ.js +1 -0
- zenml/zen_server/dashboard/assets/Tick-BlMoIlJT.js +1 -0
- zenml/zen_server/dashboard/assets/{UpdatePasswordSchemas-DckMEkFf.js → UpdatePasswordSchemas-Sc0A0pP-.js} +1 -1
- zenml/zen_server/dashboard/assets/{UsageReason-DVceN14P.js → UsageReason-YYduL4fj.js} +1 -1
- zenml/zen_server/dashboard/assets/{WizardFooter-CW0Cvd70.js → WizardFooter-dgmizSJC.js} +1 -1
- zenml/zen_server/dashboard/assets/all-pipeline-runs-query-D-c2G6lV.js +1 -0
- zenml/zen_server/dashboard/assets/check-DloQpStc.js +1 -0
- zenml/zen_server/dashboard/assets/{check-circle-Dwxliy1Z.js → check-circle-jNbX5-sR.js} +1 -1
- zenml/zen_server/dashboard/assets/{chevron-down-8wLBS5pQ.js → chevron-down-6JyMkfjR.js} +1 -1
- zenml/zen_server/dashboard/assets/{chevron-right-double-DoD8iXWM.js → chevron-right-double-D7ojK9Co.js} +1 -1
- zenml/zen_server/dashboard/assets/{code-browser-CZUQs3Wa.js → code-browser-CUFUIHfp.js} +1 -1
- zenml/zen_server/dashboard/assets/{copy-CaSMXwiU.js → copy-C8XQA2Ug.js} +1 -1
- zenml/zen_server/dashboard/assets/create-stack-DM_JPgef.js +1 -0
- zenml/zen_server/dashboard/assets/delete-run-CJdh1P_h.js +1 -0
- zenml/zen_server/dashboard/assets/{docker-BFNgg-z3.js → docker-BdA9vrnW.js} +1 -1
- zenml/zen_server/dashboard/assets/{dots-horizontal-DK5Duzx4.js → dots-horizontal-otGBOSDJ.js} +1 -1
- zenml/zen_server/dashboard/assets/{form-schemas-1AyOCx90.js → form-schemas-K6FYKjwa.js} +1 -1
- zenml/zen_server/dashboard/assets/{gcp-7M2Yf3ZK.js → gcp-CFtm4BA7.js} +1 -1
- zenml/zen_server/dashboard/assets/{help-Dam461dC.js → help-Cc9bBIJH.js} +1 -1
- zenml/zen_server/dashboard/assets/index-B1mVPYxf.js +1 -0
- zenml/zen_server/dashboard/assets/index-BAkC7FXi.js +1 -0
- zenml/zen_server/dashboard/assets/{index-QQb7wQEC.js → index-CCOPpudF.js} +8 -8
- zenml/zen_server/dashboard/assets/index-CEV4Cvaf.js +1 -0
- zenml/zen_server/dashboard/assets/index-DlGvJQPn.css +1 -0
- zenml/zen_server/dashboard/assets/{index-BVJ8n2-j.js → index-Uu49AX48.js} +1 -1
- zenml/zen_server/dashboard/assets/{index.esm-cuVep_NJ.js → index.esm-Dy6Z9Ung.js} +1 -1
- zenml/zen_server/dashboard/assets/{kubernetes--g7r02Zu.js → kubernetes-B2wmAJ1d.js} +1 -1
- zenml/zen_server/dashboard/assets/{layout-DCSYN7-C.js → layout-BtHBmE4w.js} +1 -1
- zenml/zen_server/dashboard/assets/{link-external-CBEk6kEG.js → link-external-b9AXw_sW.js} +1 -1
- zenml/zen_server/dashboard/assets/{login-mutation-DTcAFP1l.js → login-mutation-hf-lK87O.js} +1 -1
- zenml/zen_server/dashboard/assets/{logs-D5bdJGur.js → logs-WMSM52RF.js} +1 -1
- zenml/zen_server/dashboard/assets/{not-found-Cc-JkRH2.js → not-found-BGirLjU-.js} +1 -1
- zenml/zen_server/dashboard/assets/{package-Cs35Szwh.js → package-C6uypY4h.js} +1 -1
- zenml/zen_server/dashboard/assets/page-0JE_-Ec1.js +1 -0
- zenml/zen_server/dashboard/assets/{page-DH_Z7iW1.js → page-6m6yHHlE.js} +1 -1
- zenml/zen_server/dashboard/assets/page-BDigxVpo.js +1 -0
- zenml/zen_server/dashboard/assets/page-BR68V0V1.js +1 -0
- zenml/zen_server/dashboard/assets/page-BRLpxOt0.js +1 -0
- zenml/zen_server/dashboard/assets/{page-BQQKaabe.js → page-BU7huvKw.js} +3 -3
- zenml/zen_server/dashboard/assets/page-BvqLv2Ky.js +1 -0
- zenml/zen_server/dashboard/assets/page-C00YAkaB.js +1 -0
- zenml/zen_server/dashboard/assets/{page-N4qoPHKb.js → page-CD-DcWoy.js} +1 -1
- zenml/zen_server/dashboard/assets/page-COXXJj1k.js +1 -0
- zenml/zen_server/dashboard/assets/page-CbpvrsDL.js +1 -0
- zenml/zen_server/dashboard/assets/page-CdMWnQak.js +1 -0
- zenml/zen_server/dashboard/assets/{page-ClUVkl-O.js → page-CjGdWY13.js} +1 -1
- zenml/zen_server/dashboard/assets/page-CwxrFarU.js +1 -0
- zenml/zen_server/dashboard/assets/{page-DLixvR-7.js → page-D01JhjQB.js} +1 -1
- zenml/zen_server/dashboard/assets/page-D6uU2ax4.js +1 -0
- zenml/zen_server/dashboard/assets/page-D7S3aCbF.js +1 -0
- zenml/zen_server/dashboard/assets/{page-9yplj5JT.js → page-DLC-bNBP.js} +1 -1
- zenml/zen_server/dashboard/assets/page-DXSTpqRD.js +1 -0
- zenml/zen_server/dashboard/assets/{page-DzpVUZ8f.js → page-DakHVWXF.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-DIOXwhiD.js → page-Df-Fw0aq.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-B-y2XKIc.js → page-DfbXf_8s.js} +1 -1
- zenml/zen_server/dashboard/assets/page-DjRJCGb3.js +1 -0
- zenml/zen_server/dashboard/assets/{page-C0N5q3l7.js → page-Djikxq_S.js} +1 -1
- zenml/zen_server/dashboard/assets/page-Dnovpa0i.js +3 -0
- zenml/zen_server/dashboard/assets/page-Dot3LPmL.js +1 -0
- zenml/zen_server/dashboard/assets/page-Vcxara9U.js +1 -0
- zenml/zen_server/dashboard/assets/page-Xynx4btY.js +14 -0
- zenml/zen_server/dashboard/assets/page-YpKAqVSa.js +1 -0
- zenml/zen_server/dashboard/assets/page-yYC9OI-E.js +1 -0
- zenml/zen_server/dashboard/assets/{persist-DNb5cdrU.js → persist-Coz7ZWvz.js} +1 -1
- zenml/zen_server/dashboard/assets/{persist-CP0JmYZ4.js → persist-GjC8PZoC.js} +1 -1
- zenml/zen_server/dashboard/assets/{plus-C9IxgN2M.js → plus-tf1V2hTJ.js} +1 -1
- zenml/zen_server/dashboard/assets/{refresh-BVu22P_C.js → refresh-BjOeWlEq.js} +1 -1
- zenml/zen_server/dashboard/assets/{rocket-CONEmRmB.js → rocket-DjT2cDvG.js} +1 -1
- zenml/zen_server/dashboard/assets/sharedSchema-CQb14VSr.js +14 -0
- zenml/zen_server/dashboard/assets/stack-detail-query-OPEW-cDJ.js +1 -0
- zenml/zen_server/dashboard/assets/{tick-circle-CM1ZScbQ.js → tick-circle-BEX_Tp4v.js} +1 -1
- zenml/zen_server/dashboard/assets/{trash-DkJHMOg7.js → trash-arLUMWMS.js} +1 -1
- zenml/zen_server/dashboard/assets/{update-server-settings-mutation-DsU8cNVl.js → update-server-settings-mutation-LwuQfHYn.js} +1 -1
- zenml/zen_server/dashboard/assets/upgrade-form-CwRHBuXB.webp +0 -0
- zenml/zen_server/dashboard/assets/url-CkvKAnwF.js +1 -0
- zenml/zen_server/dashboard/assets/{zod-D89GC_vc.js → zod-BwEbpOxH.js} +1 -1
- zenml/zen_server/dashboard/index.html +7 -7
- zenml/zen_server/deploy/helm/Chart.yaml +1 -1
- zenml/zen_server/deploy/helm/README.md +2 -2
- zenml/zen_server/exceptions.py +2 -0
- zenml/zen_server/jwt.py +30 -13
- zenml/zen_server/routers/auth_endpoints.py +134 -102
- zenml/zen_server/routers/logs_endpoints.py +66 -0
- zenml/zen_server/template_execution/utils.py +14 -16
- zenml/zen_server/utils.py +27 -0
- zenml/zen_server/zen_server_api.py +6 -1
- zenml/zen_stores/migrations/versions/0.70.0_release.py +23 -0
- zenml/zen_stores/migrations/versions/904464ea4041_add_pipeline_model_run_unique_constraints.py +192 -0
- zenml/zen_stores/rest_zen_store.py +13 -10
- zenml/zen_stores/schemas/model_schemas.py +25 -1
- zenml/zen_stores/schemas/pipeline_run_schemas.py +5 -0
- zenml/zen_stores/schemas/pipeline_schemas.py +8 -2
- zenml/zen_stores/sql_zen_store.py +215 -121
- {zenml_nightly-0.68.1.dev20241111.dist-info → zenml_nightly-0.70.0.dev20241114.dist-info}/METADATA +2 -2
- {zenml_nightly-0.68.1.dev20241111.dist-info → zenml_nightly-0.70.0.dev20241114.dist-info}/RECORD +165 -151
- zenml/zen_server/dashboard/assets/AlertDialogDropdownItem-C6N2rGrB.js +0 -1
- zenml/zen_server/dashboard/assets/ComponentBadge-DUiEYJHu.js +0 -1
- zenml/zen_server/dashboard/assets/ComponentFallbackDialog-BFoH5K4V.js +0 -1
- zenml/zen_server/dashboard/assets/ComponentIcon-CAIoUis2.js +0 -1
- zenml/zen_server/dashboard/assets/Partials-YPBB3V4q.js +0 -1
- zenml/zen_server/dashboard/assets/ProviderIcon-Bb3Xha5A.js +0 -1
- zenml/zen_server/dashboard/assets/RunSelector-DCiL3M1c.js +0 -1
- zenml/zen_server/dashboard/assets/SearchField-DfUiGFVd.js +0 -1
- zenml/zen_server/dashboard/assets/Tick-CykQFPj2.js +0 -1
- zenml/zen_server/dashboard/assets/cloud-only-B-s_HMDm.js +0 -1
- zenml/zen_server/dashboard/assets/codespaces-BitYDX9d.gif +0 -0
- zenml/zen_server/dashboard/assets/create-stack-CEmaPZ4c.js +0 -1
- zenml/zen_server/dashboard/assets/delete-run-D-LKbGyz.js +0 -1
- zenml/zen_server/dashboard/assets/index-Bpmj40BI.js +0 -1
- zenml/zen_server/dashboard/assets/index-CbU4Ln_E.css +0 -1
- zenml/zen_server/dashboard/assets/index-DKPhqP2B.js +0 -1
- zenml/zen_server/dashboard/assets/page-BBpOxVcY.js +0 -1
- zenml/zen_server/dashboard/assets/page-BRInM1Lg.js +0 -1
- zenml/zen_server/dashboard/assets/page-BjjlMk7s.js +0 -1
- zenml/zen_server/dashboard/assets/page-Bvd7YH2A.js +0 -1
- zenml/zen_server/dashboard/assets/page-CT3Nep8W.js +0 -1
- zenml/zen_server/dashboard/assets/page-C_f47pBf.js +0 -1
- zenml/zen_server/dashboard/assets/page-Cmv8C_yM.js +0 -3
- zenml/zen_server/dashboard/assets/page-CyN2bdWG.js +0 -1
- zenml/zen_server/dashboard/assets/page-CzzXH4fs.js +0 -1
- zenml/zen_server/dashboard/assets/page-DTlGjgnG.js +0 -1
- zenml/zen_server/dashboard/assets/page-Dbpl86h0.js +0 -1
- zenml/zen_server/dashboard/assets/page-Ddgy6kDS.js +0 -1
- zenml/zen_server/dashboard/assets/page-DtCAfBLy.js +0 -9
- zenml/zen_server/dashboard/assets/page-Dx16z7nA.js +0 -1
- zenml/zen_server/dashboard/assets/page-McUyYbo1.js +0 -1
- zenml/zen_server/dashboard/assets/page-T1P3RyAR.js +0 -1
- zenml/zen_server/dashboard/assets/page-bKaULTGG.js +0 -1
- zenml/zen_server/dashboard/assets/page-sbXUJy9t.js +0 -1
- zenml/zen_server/dashboard/assets/sharedSchema-TMLu-nYQ.js +0 -14
- zenml/zen_server/dashboard/assets/stack-detail-query-xmYxSsUY.js +0 -1
- zenml/zen_server/dashboard/assets/url-D5le3J4q.js +0 -1
- {zenml_nightly-0.68.1.dev20241111.dist-info → zenml_nightly-0.70.0.dev20241114.dist-info}/LICENSE +0 -0
- {zenml_nightly-0.68.1.dev20241111.dist-info → zenml_nightly-0.70.0.dev20241114.dist-info}/WHEEL +0 -0
- {zenml_nightly-0.68.1.dev20241111.dist-info → zenml_nightly-0.70.0.dev20241114.dist-info}/entry_points.txt +0 -0
@@ -70,11 +70,12 @@ and are aware of any potential costs:
|
|
70
70
|
|
71
71
|
- A GCS bucket registered as a [ZenML artifact store](https://docs.zenml.io/stack-components/artifact-stores/gcp).
|
72
72
|
- A Google Artifact Registry registered as a [ZenML container registry](https://docs.zenml.io/stack-components/container-registries/gcp).
|
73
|
-
- Vertex AI registered as a [ZenML orchestrator](https://docs.zenml.io/stack-components/orchestrators/vertex)
|
73
|
+
- Vertex AI registered as a [ZenML orchestrator](https://docs.zenml.io/stack-components/orchestrators/vertex)
|
74
|
+
and as a [ZenML step operator](https://docs.zenml.io/stack-components/step-operators/vertex).
|
74
75
|
- GCP Cloud Build registered as a [ZenML image builder](https://docs.zenml.io/stack-components/image-builders/gcp).
|
75
76
|
- A GCP Service Account with the minimum necessary permissions to access the
|
76
77
|
above resources.
|
77
|
-
-
|
78
|
+
- A GCP Service Account access key used to give access to ZenML to connect to
|
78
79
|
the above resources through a [ZenML service connector](https://docs.zenml.io/how-to/auth-management/gcp-service-connector).
|
79
80
|
|
80
81
|
The Deployment Manager deployment will automatically create a GCP Service
|
@@ -259,14 +260,30 @@ GCP project and to clean up the resources created by the stack by using
|
|
259
260
|
)
|
260
261
|
|
261
262
|
if self.deployment_type == STACK_DEPLOYMENT_TERRAFORM:
|
262
|
-
config = f"""
|
263
|
+
config = f"""terraform {{
|
264
|
+
required_providers {{
|
265
|
+
google = {{
|
266
|
+
source = "hashicorp/google"
|
267
|
+
}}
|
268
|
+
zenml = {{
|
269
|
+
source = "zenml-io/zenml"
|
270
|
+
}}
|
271
|
+
}}
|
272
|
+
}}
|
273
|
+
|
274
|
+
provider "google" {{
|
275
|
+
region = "{self.location or "europe-west3"}"
|
276
|
+
project = your GCP project name
|
277
|
+
}}
|
278
|
+
|
279
|
+
provider "zenml" {{
|
280
|
+
server_url = "{self.zenml_server_url}"
|
281
|
+
api_token = "{self.zenml_server_api_token}"
|
282
|
+
}}
|
283
|
+
|
284
|
+
module "zenml_stack" {{
|
263
285
|
source = "zenml-io/zenml-stack/gcp"
|
264
286
|
|
265
|
-
project_id = "my-gcp-project"
|
266
|
-
region = "{self.location or "europe-west3"}"
|
267
|
-
zenml_server_url = "{self.zenml_server_url}"
|
268
|
-
zenml_api_key = ""
|
269
|
-
zenml_api_token = "{self.zenml_server_api_token}"
|
270
287
|
zenml_stack_name = "{self.stack_name}"
|
271
288
|
zenml_stack_deployment = "{self.deployment_type}"
|
272
289
|
}}
|
@@ -219,16 +219,14 @@ class ZenMLCloudStackDeployment(BaseModel):
|
|
219
219
|
if stack.labels.get("zenml:deployment") != self.deployment_type:
|
220
220
|
continue
|
221
221
|
|
222
|
-
|
223
|
-
StackComponentType.ARTIFACT_STORE
|
224
|
-
][0]
|
222
|
+
orchestrator = stack.components[StackComponentType.ORCHESTRATOR][0]
|
225
223
|
|
226
|
-
if not
|
224
|
+
if not orchestrator.connector:
|
227
225
|
continue
|
228
226
|
|
229
227
|
return DeployedStack(
|
230
228
|
stack=stack,
|
231
|
-
service_connector=
|
229
|
+
service_connector=orchestrator.connector,
|
232
230
|
)
|
233
231
|
|
234
232
|
return None
|
zenml/steps/utils.py
CHANGED
@@ -442,6 +442,11 @@ def log_step_metadata(
|
|
442
442
|
from within a step or if no pipeline name or ID is provided and
|
443
443
|
the function is not called from within a step.
|
444
444
|
"""
|
445
|
+
logger.warning(
|
446
|
+
"The `log_step_metadata` function is deprecated and will soon be "
|
447
|
+
"removed. Please use `log_metadata` instead."
|
448
|
+
)
|
449
|
+
|
445
450
|
step_context = None
|
446
451
|
if not step_name:
|
447
452
|
with contextlib.suppress(RuntimeError):
|
@@ -0,0 +1,335 @@
|
|
1
|
+
# Copyright (c) ZenML GmbH 2024. All Rights Reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at:
|
6
|
+
#
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
12
|
+
# or implied. See the License for the specific language governing
|
13
|
+
# permissions and limitations under the License.
|
14
|
+
"""Utility functions to handle metadata for ZenML entities."""
|
15
|
+
|
16
|
+
import contextlib
|
17
|
+
from typing import Dict, Optional, Union, overload
|
18
|
+
from uuid import UUID
|
19
|
+
|
20
|
+
from zenml.client import Client
|
21
|
+
from zenml.enums import MetadataResourceTypes
|
22
|
+
from zenml.logger import get_logger
|
23
|
+
from zenml.metadata.metadata_types import MetadataType
|
24
|
+
from zenml.steps.step_context import get_step_context
|
25
|
+
|
26
|
+
logger = get_logger(__name__)
|
27
|
+
|
28
|
+
|
29
|
+
@overload
|
30
|
+
def log_metadata(metadata: Dict[str, MetadataType]) -> None: ...
|
31
|
+
|
32
|
+
|
33
|
+
@overload
|
34
|
+
def log_metadata(
|
35
|
+
*,
|
36
|
+
metadata: Dict[str, MetadataType],
|
37
|
+
artifact_version_id: UUID,
|
38
|
+
) -> None: ...
|
39
|
+
|
40
|
+
|
41
|
+
@overload
|
42
|
+
def log_metadata(
|
43
|
+
*,
|
44
|
+
metadata: Dict[str, MetadataType],
|
45
|
+
artifact_name: str,
|
46
|
+
artifact_version: Optional[str] = None,
|
47
|
+
) -> None: ...
|
48
|
+
|
49
|
+
|
50
|
+
@overload
|
51
|
+
def log_metadata(
|
52
|
+
*,
|
53
|
+
metadata: Dict[str, MetadataType],
|
54
|
+
model_version_id: UUID,
|
55
|
+
) -> None: ...
|
56
|
+
|
57
|
+
|
58
|
+
@overload
|
59
|
+
def log_metadata(
|
60
|
+
*,
|
61
|
+
metadata: Dict[str, MetadataType],
|
62
|
+
model_name: str,
|
63
|
+
model_version: str,
|
64
|
+
) -> None: ...
|
65
|
+
|
66
|
+
|
67
|
+
@overload
|
68
|
+
def log_metadata(
|
69
|
+
*,
|
70
|
+
metadata: Dict[str, MetadataType],
|
71
|
+
step_id: UUID,
|
72
|
+
) -> None: ...
|
73
|
+
|
74
|
+
|
75
|
+
@overload
|
76
|
+
def log_metadata(
|
77
|
+
*,
|
78
|
+
metadata: Dict[str, MetadataType],
|
79
|
+
run_id_name_or_prefix: Union[UUID, str],
|
80
|
+
) -> None: ...
|
81
|
+
|
82
|
+
|
83
|
+
@overload
|
84
|
+
def log_metadata(
|
85
|
+
*,
|
86
|
+
metadata: Dict[str, MetadataType],
|
87
|
+
step_name: str,
|
88
|
+
run_id_name_or_prefix: Union[UUID, str],
|
89
|
+
) -> None: ...
|
90
|
+
|
91
|
+
|
92
|
+
def log_metadata(
|
93
|
+
metadata: Dict[str, MetadataType],
|
94
|
+
# Parameters to manually log metadata for steps and runs
|
95
|
+
step_id: Optional[UUID] = None,
|
96
|
+
step_name: Optional[str] = None,
|
97
|
+
run_id_name_or_prefix: Optional[Union[UUID, str]] = None,
|
98
|
+
# Parameters to manually log metadata for artifacts
|
99
|
+
artifact_version_id: Optional[UUID] = None,
|
100
|
+
artifact_name: Optional[str] = None,
|
101
|
+
artifact_version: Optional[str] = None,
|
102
|
+
# Parameters to manually log metadata for models
|
103
|
+
model_version_id: Optional[UUID] = None,
|
104
|
+
model_name: Optional[str] = None,
|
105
|
+
model_version: Optional[str] = None,
|
106
|
+
) -> None:
|
107
|
+
"""Logs metadata for various resource types in a generalized way.
|
108
|
+
|
109
|
+
Args:
|
110
|
+
metadata: The metadata to log.
|
111
|
+
step_id: The ID of the step.
|
112
|
+
step_name: The name of the step.
|
113
|
+
run_id_name_or_prefix: The id, name or prefix of the run
|
114
|
+
artifact_version_id: The ID of the artifact version
|
115
|
+
artifact_name: The name of the artifact.
|
116
|
+
artifact_version: The version of the artifact.
|
117
|
+
model_version_id: The ID of the model version.
|
118
|
+
model_name: The name of the model.
|
119
|
+
model_version: The version of the model
|
120
|
+
|
121
|
+
Raises:
|
122
|
+
ValueError: If no identifiers are provided and the function is not
|
123
|
+
called from within a step.
|
124
|
+
"""
|
125
|
+
client = Client()
|
126
|
+
|
127
|
+
# If a step name is provided, we need a run_id_name_or_prefix and will log
|
128
|
+
# metadata for the steps pipeline and model accordingly.
|
129
|
+
if step_name is not None and run_id_name_or_prefix is not None:
|
130
|
+
run_model = client.get_pipeline_run(
|
131
|
+
name_id_or_prefix=run_id_name_or_prefix
|
132
|
+
)
|
133
|
+
step_model = run_model.steps[step_name]
|
134
|
+
|
135
|
+
client.create_run_metadata(
|
136
|
+
metadata=metadata,
|
137
|
+
resource_id=run_model.id,
|
138
|
+
resource_type=MetadataResourceTypes.PIPELINE_RUN,
|
139
|
+
)
|
140
|
+
client.create_run_metadata(
|
141
|
+
metadata=metadata,
|
142
|
+
resource_id=step_model.id,
|
143
|
+
resource_type=MetadataResourceTypes.STEP_RUN,
|
144
|
+
)
|
145
|
+
if step_model.model_version:
|
146
|
+
client.create_run_metadata(
|
147
|
+
metadata=metadata,
|
148
|
+
resource_id=step_model.model_version.id,
|
149
|
+
resource_type=MetadataResourceTypes.MODEL_VERSION,
|
150
|
+
)
|
151
|
+
|
152
|
+
# If a step is identified by id, fetch it directly through the client,
|
153
|
+
# follow a similar procedure and log metadata for its pipeline and model
|
154
|
+
# as well.
|
155
|
+
elif step_id is not None:
|
156
|
+
step_model = client.get_run_step(step_run_id=step_id)
|
157
|
+
client.create_run_metadata(
|
158
|
+
metadata=metadata,
|
159
|
+
resource_id=step_model.pipeline_run_id,
|
160
|
+
resource_type=MetadataResourceTypes.PIPELINE_RUN,
|
161
|
+
)
|
162
|
+
client.create_run_metadata(
|
163
|
+
metadata=metadata,
|
164
|
+
resource_id=step_model.id,
|
165
|
+
resource_type=MetadataResourceTypes.STEP_RUN,
|
166
|
+
)
|
167
|
+
if step_model.model_version:
|
168
|
+
client.create_run_metadata(
|
169
|
+
metadata=metadata,
|
170
|
+
resource_id=step_model.model_version.id,
|
171
|
+
resource_type=MetadataResourceTypes.MODEL_VERSION,
|
172
|
+
)
|
173
|
+
|
174
|
+
# If a pipeline run id is identified, we need to log metadata to it and its
|
175
|
+
# model as well.
|
176
|
+
elif run_id_name_or_prefix is not None:
|
177
|
+
run_model = client.get_pipeline_run(
|
178
|
+
name_id_or_prefix=run_id_name_or_prefix
|
179
|
+
)
|
180
|
+
client.create_run_metadata(
|
181
|
+
metadata=metadata,
|
182
|
+
resource_id=run_model.id,
|
183
|
+
resource_type=MetadataResourceTypes.PIPELINE_RUN,
|
184
|
+
)
|
185
|
+
if run_model.model_version:
|
186
|
+
client.create_run_metadata(
|
187
|
+
metadata=metadata,
|
188
|
+
resource_id=run_model.model_version.id,
|
189
|
+
resource_type=MetadataResourceTypes.MODEL_VERSION,
|
190
|
+
)
|
191
|
+
|
192
|
+
# If the user provides a model name and version, we use to model abstraction
|
193
|
+
# to fetch the model version and attach the corresponding metadata to it.
|
194
|
+
elif model_name is not None and model_version is not None:
|
195
|
+
from zenml import Model
|
196
|
+
|
197
|
+
mv = Model(name=model_name, version=model_version)
|
198
|
+
client.create_run_metadata(
|
199
|
+
metadata=metadata,
|
200
|
+
resource_id=mv.id,
|
201
|
+
resource_type=MetadataResourceTypes.MODEL_VERSION,
|
202
|
+
)
|
203
|
+
|
204
|
+
# If the user provides a model version id, we use the client to fetch it and
|
205
|
+
# attach the metadata to it.
|
206
|
+
elif model_version_id is not None:
|
207
|
+
model_version_id = client.get_model_version(
|
208
|
+
model_version_name_or_number_or_id=model_version_id
|
209
|
+
).id
|
210
|
+
client.create_run_metadata(
|
211
|
+
metadata=metadata,
|
212
|
+
resource_id=model_version_id,
|
213
|
+
resource_type=MetadataResourceTypes.MODEL_VERSION,
|
214
|
+
)
|
215
|
+
|
216
|
+
# If the user provides an artifact name, there are three possibilities. If
|
217
|
+
# an artifact version is also provided with the name, we use both to fetch
|
218
|
+
# the artifact version and use it to log the metadata. If no version is
|
219
|
+
# provided, if the function is called within a step we search the artifacts
|
220
|
+
# of the step if not we fetch the latest version and attach the metadata
|
221
|
+
# to the latest version.
|
222
|
+
elif artifact_name is not None:
|
223
|
+
if artifact_version:
|
224
|
+
artifact_version_model = client.get_artifact_version(
|
225
|
+
name_id_or_prefix=artifact_name, version=artifact_version
|
226
|
+
)
|
227
|
+
client.create_run_metadata(
|
228
|
+
metadata=metadata,
|
229
|
+
resource_id=artifact_version_model.id,
|
230
|
+
resource_type=MetadataResourceTypes.ARTIFACT_VERSION,
|
231
|
+
)
|
232
|
+
else:
|
233
|
+
step_context = None
|
234
|
+
with contextlib.suppress(RuntimeError):
|
235
|
+
step_context = get_step_context()
|
236
|
+
|
237
|
+
if step_context:
|
238
|
+
step_context.add_output_metadata(
|
239
|
+
metadata=metadata, output_name=artifact_name
|
240
|
+
)
|
241
|
+
else:
|
242
|
+
artifact_version_model = client.get_artifact_version(
|
243
|
+
name_id_or_prefix=artifact_name
|
244
|
+
)
|
245
|
+
client.create_run_metadata(
|
246
|
+
metadata=metadata,
|
247
|
+
resource_id=artifact_version_model.id,
|
248
|
+
resource_type=MetadataResourceTypes.ARTIFACT_VERSION,
|
249
|
+
)
|
250
|
+
|
251
|
+
# If the user directly provides an artifact_version_id, we use the client to
|
252
|
+
# fetch is and attach the metadata accordingly.
|
253
|
+
elif artifact_version_id is not None:
|
254
|
+
artifact_version_model = client.get_artifact_version(
|
255
|
+
name_id_or_prefix=artifact_version_id,
|
256
|
+
)
|
257
|
+
client.create_run_metadata(
|
258
|
+
metadata=metadata,
|
259
|
+
resource_id=artifact_version_model.id,
|
260
|
+
resource_type=MetadataResourceTypes.ARTIFACT_VERSION,
|
261
|
+
)
|
262
|
+
|
263
|
+
# If every additional value is None, that means we are calling it bare bones
|
264
|
+
# and this call needs to happen during a step execution. We will use the
|
265
|
+
# step context to fetch the step, run and possibly the model version and
|
266
|
+
# attach the metadata accordingly.
|
267
|
+
elif all(
|
268
|
+
v is None
|
269
|
+
for v in [
|
270
|
+
step_id,
|
271
|
+
step_name,
|
272
|
+
run_id_name_or_prefix,
|
273
|
+
artifact_version_id,
|
274
|
+
artifact_name,
|
275
|
+
artifact_version,
|
276
|
+
model_version_id,
|
277
|
+
model_name,
|
278
|
+
model_version,
|
279
|
+
]
|
280
|
+
):
|
281
|
+
try:
|
282
|
+
step_context = get_step_context()
|
283
|
+
except RuntimeError:
|
284
|
+
raise ValueError(
|
285
|
+
"You are calling 'log_metadata()' outside of a step execution. "
|
286
|
+
"If you would like to add metadata to a ZenML entity outside "
|
287
|
+
"of the step execution, please provide the required "
|
288
|
+
"identifiers."
|
289
|
+
)
|
290
|
+
client.create_run_metadata(
|
291
|
+
metadata=metadata,
|
292
|
+
resource_id=step_context.pipeline_run.id,
|
293
|
+
resource_type=MetadataResourceTypes.PIPELINE_RUN,
|
294
|
+
)
|
295
|
+
client.create_run_metadata(
|
296
|
+
metadata=metadata,
|
297
|
+
resource_id=step_context.step_run.id,
|
298
|
+
resource_type=MetadataResourceTypes.STEP_RUN,
|
299
|
+
)
|
300
|
+
if step_context.model_version:
|
301
|
+
client.create_run_metadata(
|
302
|
+
metadata=metadata,
|
303
|
+
resource_id=step_context.model_version.id,
|
304
|
+
resource_type=MetadataResourceTypes.MODEL_VERSION,
|
305
|
+
)
|
306
|
+
|
307
|
+
else:
|
308
|
+
raise ValueError(
|
309
|
+
"""
|
310
|
+
Unsupported way to call the `log_metadata`. Possible combinations "
|
311
|
+
include:
|
312
|
+
|
313
|
+
# Inside a step
|
314
|
+
# Logs the metadata to the step, its run and possibly its model
|
315
|
+
log_metadata(metadata={})
|
316
|
+
|
317
|
+
# Manually logging for a step
|
318
|
+
# Logs the metadata to the step, its run and possibly its model
|
319
|
+
log_metadata(metadata={}, step_name=..., run_id_name_or_prefix=...)
|
320
|
+
log_metadata(metadata={}, step_id=...)
|
321
|
+
|
322
|
+
# Manually logging for a run
|
323
|
+
# Logs the metadata to the run, possibly its model
|
324
|
+
log_metadata(metadata={}, run_id_name_or_prefix=...)
|
325
|
+
|
326
|
+
# Manually logging for a model
|
327
|
+
log_metadata(metadata={}, model_name=..., model_version=...)
|
328
|
+
log_metadata(metadata={}, model_version_id=...)
|
329
|
+
|
330
|
+
# Manually logging for an artifact
|
331
|
+
log_metadata(metadata={}, artifact_name=...) # inside a step
|
332
|
+
log_metadata(metadata={}, artifact_name=..., artifact_version=...)
|
333
|
+
log_metadata(metadata={}, artifact_version_id=...)
|
334
|
+
"""
|
335
|
+
)
|
zenml/zen_server/auth.py
CHANGED
@@ -14,13 +14,13 @@
|
|
14
14
|
"""Authentication module for ZenML server."""
|
15
15
|
|
16
16
|
from contextvars import ContextVar
|
17
|
-
from datetime import datetime
|
17
|
+
from datetime import datetime, timedelta
|
18
18
|
from typing import Callable, Optional, Union
|
19
19
|
from urllib.parse import urlencode
|
20
20
|
from uuid import UUID
|
21
21
|
|
22
22
|
import requests
|
23
|
-
from fastapi import Depends
|
23
|
+
from fastapi import Depends, Response
|
24
24
|
from fastapi.security import (
|
25
25
|
HTTPBasic,
|
26
26
|
HTTPBasicCredentials,
|
@@ -37,7 +37,7 @@ from zenml.constants import (
|
|
37
37
|
LOGIN,
|
38
38
|
VERSION_1,
|
39
39
|
)
|
40
|
-
from zenml.enums import AuthScheme, OAuthDeviceStatus
|
40
|
+
from zenml.enums import AuthScheme, ExecutionStatus, OAuthDeviceStatus
|
41
41
|
from zenml.exceptions import (
|
42
42
|
AuthorizationException,
|
43
43
|
CredentialsNotValid,
|
@@ -51,11 +51,13 @@ from zenml.models import (
|
|
51
51
|
ExternalUserModel,
|
52
52
|
OAuthDeviceInternalResponse,
|
53
53
|
OAuthDeviceInternalUpdate,
|
54
|
+
OAuthTokenResponse,
|
54
55
|
UserAuthModel,
|
55
56
|
UserRequest,
|
56
57
|
UserResponse,
|
57
58
|
UserUpdate,
|
58
59
|
)
|
60
|
+
from zenml.zen_server.cache import cache_result
|
59
61
|
from zenml.zen_server.exceptions import http_exception_from_error
|
60
62
|
from zenml.zen_server.jwt import JWTToken
|
61
63
|
from zenml.zen_server.utils import server_config, zen_store
|
@@ -329,6 +331,148 @@ def authenticate_credentials(
|
|
329
331
|
),
|
330
332
|
)
|
331
333
|
|
334
|
+
if decoded_token.schedule_id:
|
335
|
+
# If the token contains a schedule ID, we need to check if the
|
336
|
+
# schedule still exists in the database. We use a cached version
|
337
|
+
# of the schedule active status to avoid unnecessary database
|
338
|
+
# queries.
|
339
|
+
|
340
|
+
@cache_result(expiry=30)
|
341
|
+
def get_schedule_active(schedule_id: UUID) -> Optional[bool]:
|
342
|
+
"""Get the active status of a schedule.
|
343
|
+
|
344
|
+
Args:
|
345
|
+
schedule_id: The schedule ID.
|
346
|
+
|
347
|
+
Returns:
|
348
|
+
The schedule active status or None if the schedule does not
|
349
|
+
exist.
|
350
|
+
"""
|
351
|
+
try:
|
352
|
+
schedule = zen_store().get_schedule(
|
353
|
+
schedule_id, hydrate=False
|
354
|
+
)
|
355
|
+
except KeyError:
|
356
|
+
return False
|
357
|
+
|
358
|
+
return schedule.active
|
359
|
+
|
360
|
+
schedule_active = get_schedule_active(decoded_token.schedule_id)
|
361
|
+
if schedule_active is None:
|
362
|
+
error = (
|
363
|
+
f"Authentication error: error retrieving token schedule "
|
364
|
+
f"{decoded_token.schedule_id}"
|
365
|
+
)
|
366
|
+
logger.error(error)
|
367
|
+
raise CredentialsNotValid(error)
|
368
|
+
|
369
|
+
if not schedule_active:
|
370
|
+
error = (
|
371
|
+
f"Authentication error: schedule {decoded_token.schedule_id} "
|
372
|
+
"is not active"
|
373
|
+
)
|
374
|
+
logger.error(error)
|
375
|
+
raise CredentialsNotValid(error)
|
376
|
+
|
377
|
+
if decoded_token.pipeline_run_id:
|
378
|
+
# If the token contains a pipeline run ID, we need to check if the
|
379
|
+
# pipeline run exists in the database and the pipeline run has
|
380
|
+
# not concluded. We use a cached version of the pipeline run status
|
381
|
+
# to avoid unnecessary database queries.
|
382
|
+
|
383
|
+
@cache_result(expiry=30)
|
384
|
+
def get_pipeline_run_status(
|
385
|
+
pipeline_run_id: UUID,
|
386
|
+
) -> Optional[ExecutionStatus]:
|
387
|
+
"""Get the status of a pipeline run.
|
388
|
+
|
389
|
+
Args:
|
390
|
+
pipeline_run_id: The pipeline run ID.
|
391
|
+
|
392
|
+
Returns:
|
393
|
+
The pipeline run status or None if the pipeline run does not
|
394
|
+
exist.
|
395
|
+
"""
|
396
|
+
try:
|
397
|
+
pipeline_run = zen_store().get_run(
|
398
|
+
pipeline_run_id, hydrate=False
|
399
|
+
)
|
400
|
+
except KeyError:
|
401
|
+
return None
|
402
|
+
|
403
|
+
return pipeline_run.status
|
404
|
+
|
405
|
+
pipeline_run_status = get_pipeline_run_status(
|
406
|
+
decoded_token.pipeline_run_id
|
407
|
+
)
|
408
|
+
if pipeline_run_status is None:
|
409
|
+
error = (
|
410
|
+
f"Authentication error: error retrieving token pipeline run "
|
411
|
+
f"{decoded_token.pipeline_run_id}"
|
412
|
+
)
|
413
|
+
logger.error(error)
|
414
|
+
raise CredentialsNotValid(error)
|
415
|
+
|
416
|
+
if pipeline_run_status in [
|
417
|
+
ExecutionStatus.FAILED,
|
418
|
+
ExecutionStatus.COMPLETED,
|
419
|
+
]:
|
420
|
+
error = (
|
421
|
+
f"The execution of pipeline run "
|
422
|
+
f"{decoded_token.pipeline_run_id} has already concluded and "
|
423
|
+
"API tokens scoped to it are no longer valid."
|
424
|
+
)
|
425
|
+
logger.error(error)
|
426
|
+
raise CredentialsNotValid(error)
|
427
|
+
|
428
|
+
if decoded_token.step_run_id:
|
429
|
+
# If the token contains a step run ID, we need to check if the
|
430
|
+
# step run exists in the database and the step run has not concluded.
|
431
|
+
# We use a cached version of the step run status to avoid unnecessary
|
432
|
+
# database queries.
|
433
|
+
|
434
|
+
@cache_result(expiry=30)
|
435
|
+
def get_step_run_status(
|
436
|
+
step_run_id: UUID,
|
437
|
+
) -> Optional[ExecutionStatus]:
|
438
|
+
"""Get the status of a step run.
|
439
|
+
|
440
|
+
Args:
|
441
|
+
step_run_id: The step run ID.
|
442
|
+
|
443
|
+
Returns:
|
444
|
+
The step run status or None if the step run does not exist.
|
445
|
+
"""
|
446
|
+
try:
|
447
|
+
step_run = zen_store().get_run_step(
|
448
|
+
step_run_id, hydrate=False
|
449
|
+
)
|
450
|
+
except KeyError:
|
451
|
+
return None
|
452
|
+
|
453
|
+
return step_run.status
|
454
|
+
|
455
|
+
step_run_status = get_step_run_status(decoded_token.step_run_id)
|
456
|
+
if step_run_status is None:
|
457
|
+
error = (
|
458
|
+
f"Authentication error: error retrieving token step run "
|
459
|
+
f"{decoded_token.step_run_id}"
|
460
|
+
)
|
461
|
+
logger.error(error)
|
462
|
+
raise CredentialsNotValid(error)
|
463
|
+
|
464
|
+
if step_run_status in [
|
465
|
+
ExecutionStatus.FAILED,
|
466
|
+
ExecutionStatus.COMPLETED,
|
467
|
+
]:
|
468
|
+
error = (
|
469
|
+
f"The execution of step run "
|
470
|
+
f"{decoded_token.step_run_id} has already concluded and "
|
471
|
+
"API tokens scoped to it are no longer valid."
|
472
|
+
)
|
473
|
+
logger.error(error)
|
474
|
+
raise CredentialsNotValid(error)
|
475
|
+
|
332
476
|
auth_context = AuthContext(
|
333
477
|
user=user_model,
|
334
478
|
access_token=decoded_token,
|
@@ -660,6 +804,80 @@ def authenticate_api_key(
|
|
660
804
|
return AuthContext(user=user_model, api_key=internal_api_key)
|
661
805
|
|
662
806
|
|
807
|
+
def generate_access_token(
|
808
|
+
user_id: UUID,
|
809
|
+
response: Optional[Response] = None,
|
810
|
+
device: Optional[OAuthDeviceInternalResponse] = None,
|
811
|
+
api_key: Optional[APIKeyInternalResponse] = None,
|
812
|
+
expires_in: Optional[int] = None,
|
813
|
+
schedule_id: Optional[UUID] = None,
|
814
|
+
pipeline_run_id: Optional[UUID] = None,
|
815
|
+
step_run_id: Optional[UUID] = None,
|
816
|
+
) -> OAuthTokenResponse:
|
817
|
+
"""Generates an access token for the given user.
|
818
|
+
|
819
|
+
Args:
|
820
|
+
user_id: The ID of the user.
|
821
|
+
response: The FastAPI response object.
|
822
|
+
device: The device used for authentication.
|
823
|
+
api_key: The service account API key used for authentication.
|
824
|
+
expires_in: The number of seconds until the token expires.
|
825
|
+
schedule_id: The ID of the schedule to scope the token to.
|
826
|
+
pipeline_run_id: The ID of the pipeline run to scope the token to.
|
827
|
+
step_run_id: The ID of the step run to scope the token to.
|
828
|
+
|
829
|
+
Returns:
|
830
|
+
An authentication response with an access token.
|
831
|
+
"""
|
832
|
+
config = server_config()
|
833
|
+
|
834
|
+
# If the expiration time is not supplied, the JWT tokens are set to expire
|
835
|
+
# according to the values configured in the server config. Device tokens are
|
836
|
+
# handled separately from regular user tokens.
|
837
|
+
expires: Optional[datetime] = None
|
838
|
+
if expires_in:
|
839
|
+
expires = datetime.utcnow() + timedelta(seconds=expires_in)
|
840
|
+
elif device:
|
841
|
+
# If a device was used for authentication, the token will expire
|
842
|
+
# at the same time as the device.
|
843
|
+
expires = device.expires
|
844
|
+
if expires:
|
845
|
+
expires_in = max(
|
846
|
+
int(expires.timestamp() - datetime.utcnow().timestamp()), 0
|
847
|
+
)
|
848
|
+
elif config.jwt_token_expire_minutes:
|
849
|
+
expires = datetime.utcnow() + timedelta(
|
850
|
+
minutes=config.jwt_token_expire_minutes
|
851
|
+
)
|
852
|
+
expires_in = config.jwt_token_expire_minutes * 60
|
853
|
+
|
854
|
+
access_token = JWTToken(
|
855
|
+
user_id=user_id,
|
856
|
+
device_id=device.id if device else None,
|
857
|
+
api_key_id=api_key.id if api_key else None,
|
858
|
+
schedule_id=schedule_id,
|
859
|
+
pipeline_run_id=pipeline_run_id,
|
860
|
+
step_run_id=step_run_id,
|
861
|
+
).encode(expires=expires)
|
862
|
+
|
863
|
+
if not device and response:
|
864
|
+
# Also set the access token as an HTTP only cookie in the response
|
865
|
+
response.set_cookie(
|
866
|
+
key=config.get_auth_cookie_name(),
|
867
|
+
value=access_token,
|
868
|
+
httponly=True,
|
869
|
+
samesite="lax",
|
870
|
+
max_age=config.jwt_token_expire_minutes * 60
|
871
|
+
if config.jwt_token_expire_minutes
|
872
|
+
else None,
|
873
|
+
domain=config.auth_cookie_domain,
|
874
|
+
)
|
875
|
+
|
876
|
+
return OAuthTokenResponse(
|
877
|
+
access_token=access_token, expires_in=expires_in, token_type="bearer"
|
878
|
+
)
|
879
|
+
|
880
|
+
|
663
881
|
def http_authentication(
|
664
882
|
credentials: HTTPBasicCredentials = Depends(HTTPBasic()),
|
665
883
|
) -> AuthContext:
|