zenml-nightly 0.83.1.dev20250624__py3-none-any.whl → 0.83.1.dev20250626__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/base.py +3 -2
- zenml/cli/login.py +21 -3
- zenml/cli/service_connectors.py +5 -12
- zenml/cli/stack.py +1 -5
- zenml/cli/utils.py +8 -52
- zenml/client.py +32 -40
- zenml/config/__init__.py +13 -2
- zenml/constants.py +0 -1
- zenml/exceptions.py +16 -0
- zenml/integrations/airflow/orchestrators/airflow_orchestrator.py +15 -6
- zenml/integrations/aws/container_registries/aws_container_registry.py +3 -1
- zenml/integrations/aws/orchestrators/sagemaker_orchestrator.py +54 -58
- zenml/integrations/azure/orchestrators/azureml_orchestrator.py +28 -19
- zenml/integrations/databricks/orchestrators/databricks_orchestrator.py +19 -63
- zenml/integrations/databricks/orchestrators/databricks_orchestrator_entrypoint_config.py +8 -3
- zenml/integrations/gcp/orchestrators/vertex_orchestrator.py +36 -61
- zenml/integrations/hyperai/orchestrators/hyperai_orchestrator.py +19 -22
- zenml/integrations/integration.py +23 -58
- zenml/integrations/kubeflow/orchestrators/kubeflow_orchestrator.py +28 -31
- zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator.py +33 -20
- zenml/integrations/lightning/orchestrators/lightning_orchestrator.py +25 -100
- zenml/integrations/skypilot/orchestrators/skypilot_base_vm_orchestrator.py +19 -8
- zenml/integrations/skypilot/utils.py +17 -13
- zenml/integrations/tekton/orchestrators/tekton_orchestrator.py +28 -12
- zenml/models/__init__.py +2 -0
- zenml/models/v2/core/service_connector.py +178 -108
- zenml/models/v2/core/step_run.py +1 -0
- zenml/orchestrators/__init__.py +2 -0
- zenml/orchestrators/base_orchestrator.py +137 -66
- zenml/orchestrators/input_utils.py +5 -13
- zenml/orchestrators/local/local_orchestrator.py +19 -9
- zenml/orchestrators/local_docker/local_docker_orchestrator.py +15 -5
- zenml/orchestrators/publish_utils.py +24 -0
- zenml/orchestrators/step_run_utils.py +1 -2
- zenml/pipelines/run_utils.py +12 -7
- zenml/service_connectors/service_connector.py +11 -61
- zenml/service_connectors/service_connector_utils.py +4 -2
- zenml/step_operators/step_operator_entrypoint_configuration.py +1 -1
- zenml/utils/package_utils.py +111 -1
- zenml/zen_server/routers/service_connectors_endpoints.py +7 -22
- zenml/zen_stores/migrations/versions/5bb25e95849c_add_internal_secrets.py +62 -0
- zenml/zen_stores/rest_zen_store.py +204 -132
- zenml/zen_stores/schemas/secret_schemas.py +5 -0
- zenml/zen_stores/schemas/service_connector_schemas.py +16 -14
- zenml/zen_stores/secrets_stores/service_connector_secrets_store.py +4 -1
- zenml/zen_stores/sql_zen_store.py +241 -119
- zenml/zen_stores/zen_store_interface.py +9 -1
- {zenml_nightly-0.83.1.dev20250624.dist-info → zenml_nightly-0.83.1.dev20250626.dist-info}/METADATA +1 -1
- {zenml_nightly-0.83.1.dev20250624.dist-info → zenml_nightly-0.83.1.dev20250626.dist-info}/RECORD +53 -53
- zenml/utils/integration_utils.py +0 -34
- {zenml_nightly-0.83.1.dev20250624.dist-info → zenml_nightly-0.83.1.dev20250626.dist-info}/LICENSE +0 -0
- {zenml_nightly-0.83.1.dev20250624.dist-info → zenml_nightly-0.83.1.dev20250626.dist-info}/WHEEL +0 -0
- {zenml_nightly-0.83.1.dev20250624.dist-info → zenml_nightly-0.83.1.dev20250626.dist-info}/entry_points.txt +0 -0
@@ -18,6 +18,7 @@ import re
|
|
18
18
|
import time
|
19
19
|
from datetime import datetime
|
20
20
|
from pathlib import Path
|
21
|
+
from threading import RLock
|
21
22
|
from typing import (
|
22
23
|
Any,
|
23
24
|
ClassVar,
|
@@ -39,6 +40,7 @@ from pydantic import (
|
|
39
40
|
BaseModel,
|
40
41
|
ConfigDict,
|
41
42
|
Field,
|
43
|
+
PrivateAttr,
|
42
44
|
ValidationError,
|
43
45
|
field_validator,
|
44
46
|
model_validator,
|
@@ -272,6 +274,7 @@ from zenml.utils.networking_utils import (
|
|
272
274
|
replace_localhost_with_internal_hostname,
|
273
275
|
)
|
274
276
|
from zenml.utils.pydantic_utils import before_validator_handler
|
277
|
+
from zenml.utils.time_utils import utc_now
|
275
278
|
from zenml.zen_server.exceptions import exception_from_response
|
276
279
|
from zenml.zen_stores.base_zen_store import BaseZenStore
|
277
280
|
|
@@ -440,6 +443,8 @@ class RestZenStore(BaseZenStore):
|
|
440
443
|
_api_token: Optional[APIToken] = None
|
441
444
|
_session: Optional[requests.Session] = None
|
442
445
|
_server_info: Optional[ServerModel] = None
|
446
|
+
_session_lock: RLock = PrivateAttr(default_factory=RLock)
|
447
|
+
_last_authenticated: Optional[datetime] = None
|
443
448
|
|
444
449
|
# ====================================
|
445
450
|
# ZenML Store interface implementation
|
@@ -2408,10 +2413,21 @@ class RestZenStore(BaseZenStore):
|
|
2408
2413
|
response_model=ServiceConnectorResponse,
|
2409
2414
|
)
|
2410
2415
|
self._populate_connector_type(connector_model)
|
2416
|
+
# Call this to properly split the secrets from the configuration
|
2417
|
+
try:
|
2418
|
+
connector_model.validate_configuration()
|
2419
|
+
except ValueError as e:
|
2420
|
+
logger.error(
|
2421
|
+
f"Error validating connector configuration for "
|
2422
|
+
f"{connector_model.name}: {e}"
|
2423
|
+
)
|
2411
2424
|
return connector_model
|
2412
2425
|
|
2413
2426
|
def get_service_connector(
|
2414
|
-
self,
|
2427
|
+
self,
|
2428
|
+
service_connector_id: UUID,
|
2429
|
+
hydrate: bool = True,
|
2430
|
+
expand_secrets: bool = False,
|
2415
2431
|
) -> ServiceConnectorResponse:
|
2416
2432
|
"""Gets a specific service connector.
|
2417
2433
|
|
@@ -2419,6 +2435,8 @@ class RestZenStore(BaseZenStore):
|
|
2419
2435
|
service_connector_id: The ID of the service connector to get.
|
2420
2436
|
hydrate: Flag deciding whether to hydrate the output model(s)
|
2421
2437
|
by including metadata fields in the response.
|
2438
|
+
expand_secrets: Flag deciding whether to include the secrets
|
2439
|
+
associated with the service connector.
|
2422
2440
|
|
2423
2441
|
Returns:
|
2424
2442
|
The requested service connector, if it was found.
|
@@ -2427,15 +2445,25 @@ class RestZenStore(BaseZenStore):
|
|
2427
2445
|
resource_id=service_connector_id,
|
2428
2446
|
route=SERVICE_CONNECTORS,
|
2429
2447
|
response_model=ServiceConnectorResponse,
|
2430
|
-
params={"
|
2448
|
+
params={"hydrate": hydrate, "expand_secrets": expand_secrets},
|
2431
2449
|
)
|
2432
2450
|
self._populate_connector_type(connector_model)
|
2451
|
+
if expand_secrets:
|
2452
|
+
try:
|
2453
|
+
# Call this to properly split the secrets from the configuration
|
2454
|
+
connector_model.validate_configuration()
|
2455
|
+
except ValueError as e:
|
2456
|
+
logger.error(
|
2457
|
+
f"Error validating connector configuration for "
|
2458
|
+
f"{connector_model.name}: {e}"
|
2459
|
+
)
|
2433
2460
|
return connector_model
|
2434
2461
|
|
2435
2462
|
def list_service_connectors(
|
2436
2463
|
self,
|
2437
2464
|
filter_model: ServiceConnectorFilter,
|
2438
2465
|
hydrate: bool = False,
|
2466
|
+
expand_secrets: bool = False,
|
2439
2467
|
) -> Page[ServiceConnectorResponse]:
|
2440
2468
|
"""List all service connectors.
|
2441
2469
|
|
@@ -2444,6 +2472,8 @@ class RestZenStore(BaseZenStore):
|
|
2444
2472
|
params.
|
2445
2473
|
hydrate: Flag deciding whether to hydrate the output model(s)
|
2446
2474
|
by including metadata fields in the response.
|
2475
|
+
expand_secrets: Flag deciding whether to include the secrets
|
2476
|
+
associated with the service connector.
|
2447
2477
|
|
2448
2478
|
Returns:
|
2449
2479
|
A page of all service connectors.
|
@@ -2452,9 +2482,19 @@ class RestZenStore(BaseZenStore):
|
|
2452
2482
|
route=SERVICE_CONNECTORS,
|
2453
2483
|
response_model=ServiceConnectorResponse,
|
2454
2484
|
filter_model=filter_model,
|
2455
|
-
params={"
|
2485
|
+
params={"hydrate": hydrate, "expand_secrets": expand_secrets},
|
2456
2486
|
)
|
2457
2487
|
self._populate_connector_type(*connector_models.items)
|
2488
|
+
if expand_secrets:
|
2489
|
+
# Call this to properly split the secrets from the configuration
|
2490
|
+
for connector_model in connector_models.items:
|
2491
|
+
try:
|
2492
|
+
connector_model.validate_configuration()
|
2493
|
+
except ValueError as e:
|
2494
|
+
logger.error(
|
2495
|
+
f"Error validating connector configuration for "
|
2496
|
+
f"{connector_model.name}: {e}"
|
2497
|
+
)
|
2458
2498
|
return connector_models
|
2459
2499
|
|
2460
2500
|
def update_service_connector(
|
@@ -2494,6 +2534,14 @@ class RestZenStore(BaseZenStore):
|
|
2494
2534
|
route=SERVICE_CONNECTORS,
|
2495
2535
|
)
|
2496
2536
|
self._populate_connector_type(connector_model)
|
2537
|
+
# Call this to properly split the secrets from the configuration
|
2538
|
+
try:
|
2539
|
+
connector_model.validate_configuration()
|
2540
|
+
except ValueError as e:
|
2541
|
+
logger.error(
|
2542
|
+
f"Error validating connector configuration for "
|
2543
|
+
f"{connector_model.name}: {e}"
|
2544
|
+
)
|
2497
2545
|
return connector_model
|
2498
2546
|
|
2499
2547
|
def delete_service_connector(self, service_connector_id: UUID) -> None:
|
@@ -2660,6 +2708,14 @@ class RestZenStore(BaseZenStore):
|
|
2660
2708
|
|
2661
2709
|
connector = ServiceConnectorResponse.model_validate(response_body)
|
2662
2710
|
self._populate_connector_type(connector)
|
2711
|
+
# Call this to properly split the secrets from the configuration
|
2712
|
+
try:
|
2713
|
+
connector.validate_configuration()
|
2714
|
+
except ValueError as e:
|
2715
|
+
logger.error(
|
2716
|
+
f"Error validating connector configuration for connector client "
|
2717
|
+
f"{connector.name}: {e}"
|
2718
|
+
)
|
2663
2719
|
return connector
|
2664
2720
|
|
2665
2721
|
def list_service_connector_resources(
|
@@ -2706,7 +2762,9 @@ class RestZenStore(BaseZenStore):
|
|
2706
2762
|
|
2707
2763
|
# Retrieve the resource list locally
|
2708
2764
|
assert resources.id is not None
|
2709
|
-
connector = self.get_service_connector(
|
2765
|
+
connector = self.get_service_connector(
|
2766
|
+
resources.id, expand_secrets=True
|
2767
|
+
)
|
2710
2768
|
connector_instance = (
|
2711
2769
|
service_connector_registry.instantiate_connector(
|
2712
2770
|
model=connector
|
@@ -4157,7 +4215,7 @@ class RestZenStore(BaseZenStore):
|
|
4157
4215
|
elif token.expired:
|
4158
4216
|
raise CredentialsNotValid(
|
4159
4217
|
"Your authentication to the current server has expired. "
|
4160
|
-
"Please log in again using 'zenml login
|
4218
|
+
"Please log in again using 'zenml login "
|
4161
4219
|
f"{self.url}'."
|
4162
4220
|
)
|
4163
4221
|
|
@@ -4203,74 +4261,79 @@ class RestZenStore(BaseZenStore):
|
|
4203
4261
|
Returns:
|
4204
4262
|
A requests session.
|
4205
4263
|
"""
|
4206
|
-
|
4207
|
-
|
4208
|
-
|
4209
|
-
|
4210
|
-
|
4211
|
-
urllib3.
|
4212
|
-
|
4264
|
+
with self._session_lock:
|
4265
|
+
if self._session is None:
|
4266
|
+
# We only need to initialize the session once over the lifetime
|
4267
|
+
# of the client. We can swap the token out when it expires.
|
4268
|
+
if self.config.verify_ssl is False:
|
4269
|
+
urllib3.disable_warnings(
|
4270
|
+
urllib3.exceptions.InsecureRequestWarning
|
4271
|
+
)
|
4213
4272
|
|
4214
|
-
|
4215
|
-
|
4216
|
-
|
4217
|
-
|
4218
|
-
|
4219
|
-
|
4220
|
-
|
4221
|
-
|
4222
|
-
|
4223
|
-
|
4224
|
-
|
4225
|
-
|
4226
|
-
|
4227
|
-
|
4228
|
-
|
4229
|
-
|
4230
|
-
|
4231
|
-
|
4232
|
-
|
4233
|
-
|
4234
|
-
|
4235
|
-
|
4236
|
-
|
4237
|
-
|
4238
|
-
|
4239
|
-
|
4240
|
-
|
4241
|
-
|
4242
|
-
|
4243
|
-
|
4244
|
-
|
4245
|
-
|
4246
|
-
|
4247
|
-
|
4248
|
-
|
4249
|
-
|
4250
|
-
|
4251
|
-
|
4252
|
-
|
4253
|
-
|
4254
|
-
|
4255
|
-
|
4256
|
-
|
4257
|
-
|
4258
|
-
|
4259
|
-
|
4260
|
-
|
4261
|
-
|
4262
|
-
|
4263
|
-
|
4264
|
-
|
4265
|
-
|
4266
|
-
|
4267
|
-
|
4268
|
-
|
4273
|
+
self._session = requests.Session()
|
4274
|
+
# Retries are triggered for all HTTP methods (GET, HEAD, POST, PUT,
|
4275
|
+
# PATCH, OPTIONS and DELETE) on specific HTTP status codes:
|
4276
|
+
#
|
4277
|
+
# 408: Request Timeout.
|
4278
|
+
# 429: Too Many Requests.
|
4279
|
+
# 502: Bad Gateway.
|
4280
|
+
# 503: Service Unavailable.
|
4281
|
+
# 504: Gateway Timeout
|
4282
|
+
#
|
4283
|
+
# This also handles connection level errors, if a connection attempt
|
4284
|
+
# fails due to transient issues like:
|
4285
|
+
#
|
4286
|
+
# DNS resolution errors.
|
4287
|
+
# Connection timeouts.
|
4288
|
+
# Network disruptions.
|
4289
|
+
#
|
4290
|
+
# Additional errors retried:
|
4291
|
+
#
|
4292
|
+
# Read Timeouts: If the server does not send a response within
|
4293
|
+
# the timeout period.
|
4294
|
+
# Connection Refused: If the server refuses the connection.
|
4295
|
+
#
|
4296
|
+
retries = Retry(
|
4297
|
+
connect=5,
|
4298
|
+
read=8,
|
4299
|
+
redirect=3,
|
4300
|
+
status=10,
|
4301
|
+
allowed_methods=[
|
4302
|
+
"HEAD",
|
4303
|
+
"GET",
|
4304
|
+
"POST",
|
4305
|
+
"PUT",
|
4306
|
+
"PATCH",
|
4307
|
+
"DELETE",
|
4308
|
+
"OPTIONS",
|
4309
|
+
],
|
4310
|
+
status_forcelist=[
|
4311
|
+
408, # Request Timeout
|
4312
|
+
429, # Too Many Requests
|
4313
|
+
502, # Bad Gateway
|
4314
|
+
503, # Service Unavailable
|
4315
|
+
504, # Gateway Timeout
|
4316
|
+
],
|
4317
|
+
other=3,
|
4318
|
+
backoff_factor=1,
|
4319
|
+
)
|
4320
|
+
self._session.mount(
|
4321
|
+
"https://", HTTPAdapter(max_retries=retries)
|
4322
|
+
)
|
4323
|
+
self._session.mount(
|
4324
|
+
"http://", HTTPAdapter(max_retries=retries)
|
4325
|
+
)
|
4326
|
+
self._session.verify = self.config.verify_ssl
|
4327
|
+
# Use a custom user agent to identify the ZenML client in the server
|
4328
|
+
# logs.
|
4329
|
+
self._session.headers.update(
|
4330
|
+
{"User-Agent": "zenml/" + zenml.__version__}
|
4331
|
+
)
|
4269
4332
|
|
4270
|
-
|
4271
|
-
|
4272
|
-
|
4273
|
-
|
4333
|
+
# Note that we return an unauthenticated session here. An API token
|
4334
|
+
# is only fetched and set in the authorization header when and if it is
|
4335
|
+
# needed.
|
4336
|
+
return self._session
|
4274
4337
|
|
4275
4338
|
def authenticate(self, force: bool = False) -> None:
|
4276
4339
|
"""Authenticate or re-authenticate to the ZenML server.
|
@@ -4325,6 +4388,7 @@ class RestZenStore(BaseZenStore):
|
|
4325
4388
|
{"Authorization": "Bearer " + new_api_token}
|
4326
4389
|
)
|
4327
4390
|
logger.debug(f"Authenticated to {self.url}")
|
4391
|
+
self._last_authenticated = utc_now()
|
4328
4392
|
|
4329
4393
|
@staticmethod
|
4330
4394
|
def _handle_response(response: requests.Response) -> Json:
|
@@ -4391,9 +4455,9 @@ class RestZenStore(BaseZenStore):
|
|
4391
4455
|
CredentialsNotValid: if the request fails due to invalid
|
4392
4456
|
client credentials.
|
4393
4457
|
"""
|
4394
|
-
|
4395
|
-
|
4396
|
-
|
4458
|
+
request_headers = {
|
4459
|
+
source_context.name: source_context.get().value,
|
4460
|
+
}
|
4397
4461
|
|
4398
4462
|
# If the server replies with a credentials validation (401 Unauthorized)
|
4399
4463
|
# error, we (re-)authenticate and retry the request here in the
|
@@ -4414,19 +4478,21 @@ class RestZenStore(BaseZenStore):
|
|
4414
4478
|
while True:
|
4415
4479
|
# Add a request ID to the request headers
|
4416
4480
|
request_id = str(uuid4())[:8]
|
4417
|
-
|
4481
|
+
request_headers["X-Request-ID"] = request_id
|
4418
4482
|
# Add an idempotency key to the request headers to ensure that
|
4419
4483
|
# requests are idempotent.
|
4420
|
-
|
4484
|
+
request_headers["Idempotency-Key"] = str(uuid4())
|
4421
4485
|
|
4422
4486
|
start_time = time.time()
|
4423
4487
|
logger.debug(f"[{request_id}] {method} {path} started...")
|
4424
4488
|
status_code = "failed"
|
4489
|
+
last_authenticated = self._last_authenticated
|
4425
4490
|
|
4426
4491
|
try:
|
4427
4492
|
response = self.session.request(
|
4428
4493
|
method,
|
4429
4494
|
url,
|
4495
|
+
headers=request_headers,
|
4430
4496
|
params=params if params else {},
|
4431
4497
|
verify=self.config.verify_ssl,
|
4432
4498
|
timeout=timeout or self.config.http_timeout,
|
@@ -4442,62 +4508,68 @@ class RestZenStore(BaseZenStore):
|
|
4442
4508
|
# authenticated at all.
|
4443
4509
|
credentials_store = get_credentials_store()
|
4444
4510
|
|
4445
|
-
|
4446
|
-
|
4447
|
-
|
4448
|
-
|
4449
|
-
|
4450
|
-
|
4451
|
-
|
4452
|
-
|
4453
|
-
|
4454
|
-
|
4455
|
-
|
4456
|
-
|
4457
|
-
|
4458
|
-
|
4459
|
-
|
4460
|
-
|
4461
|
-
|
4462
|
-
|
4463
|
-
|
4464
|
-
|
4465
|
-
|
4466
|
-
|
4467
|
-
|
4468
|
-
|
4469
|
-
|
4470
|
-
|
4471
|
-
|
4472
|
-
|
4473
|
-
|
4474
|
-
|
4475
|
-
|
4476
|
-
|
4477
|
-
|
4478
|
-
|
4479
|
-
|
4480
|
-
|
4481
|
-
|
4482
|
-
|
4483
|
-
|
4484
|
-
|
4485
|
-
|
4486
|
-
|
4487
|
-
|
4488
|
-
|
4489
|
-
|
4490
|
-
|
4491
|
-
|
4492
|
-
|
4493
|
-
|
4494
|
-
|
4495
|
-
|
4496
|
-
|
4497
|
-
|
4498
|
-
|
4499
|
-
|
4500
|
-
|
4511
|
+
with self._session_lock:
|
4512
|
+
if self._last_authenticated != last_authenticated:
|
4513
|
+
# Another thread has re-authenticated since the last
|
4514
|
+
# request. We simply retry the request.
|
4515
|
+
continue
|
4516
|
+
|
4517
|
+
if self._api_token is None:
|
4518
|
+
# The last request was not authenticated with an API
|
4519
|
+
# token at all. We authenticate here and then try the
|
4520
|
+
# request again, this time with a valid API token in the
|
4521
|
+
# header.
|
4522
|
+
logger.debug(
|
4523
|
+
f"[{request_id}] The last request was not "
|
4524
|
+
f"authenticated: {e}\n"
|
4525
|
+
"Re-authenticating and retrying..."
|
4526
|
+
)
|
4527
|
+
self.authenticate()
|
4528
|
+
elif not credentials_store.can_login(self.url):
|
4529
|
+
# The request failed either because we're not
|
4530
|
+
# authenticated or our current credentials are not valid
|
4531
|
+
# anymore.
|
4532
|
+
logger.error(
|
4533
|
+
"The current token is no longer valid, and "
|
4534
|
+
"it is not possible to generate a new token using the "
|
4535
|
+
"configured credentials. Please run "
|
4536
|
+
f"`zenml login {self.url}` to "
|
4537
|
+
"re-authenticate to the server or authenticate using "
|
4538
|
+
"an API key. See "
|
4539
|
+
"https://docs.zenml.io/deploying-zenml/connecting-to-zenml/connect-with-a-service-account "
|
4540
|
+
"for more information."
|
4541
|
+
)
|
4542
|
+
# Clear the current token from the credentials store to
|
4543
|
+
# force a new authentication flow next time.
|
4544
|
+
get_credentials_store().clear_token(self.url)
|
4545
|
+
raise e
|
4546
|
+
elif not re_authenticated:
|
4547
|
+
# The last request was authenticated with an API token
|
4548
|
+
# that was rejected by the server. We attempt a
|
4549
|
+
# re-authentication here and then retry the request.
|
4550
|
+
logger.debug(
|
4551
|
+
f"[{request_id}] The last request was authenticated "
|
4552
|
+
"with an API token that was rejected by the server: "
|
4553
|
+
f"{e}\n"
|
4554
|
+
"Re-authenticating and retrying..."
|
4555
|
+
)
|
4556
|
+
re_authenticated = True
|
4557
|
+
self.authenticate(
|
4558
|
+
# Ignore the current token and force a re-authentication
|
4559
|
+
force=True
|
4560
|
+
)
|
4561
|
+
else:
|
4562
|
+
# The last request was made after re-authenticating but
|
4563
|
+
# still failed. Bailing out.
|
4564
|
+
logger.debug(
|
4565
|
+
f"[{request_id}] The last request failed after "
|
4566
|
+
"re-authenticating: {e}\n"
|
4567
|
+
"Bailing out..."
|
4568
|
+
)
|
4569
|
+
raise CredentialsNotValid(
|
4570
|
+
"The current credentials are no longer valid. Please "
|
4571
|
+
"log in again using 'zenml login'."
|
4572
|
+
) from e
|
4501
4573
|
finally:
|
4502
4574
|
end_time = time.time()
|
4503
4575
|
duration = (end_time - start_time) * 1000
|
@@ -67,6 +67,8 @@ class SecretSchema(NamedSchema, table=True):
|
|
67
67
|
|
68
68
|
private: bool
|
69
69
|
|
70
|
+
internal: bool = Field(default=False)
|
71
|
+
|
70
72
|
values: Optional[bytes] = Field(sa_column=Column(TEXT, nullable=True))
|
71
73
|
|
72
74
|
user_id: UUID = build_foreign_key_field(
|
@@ -191,11 +193,13 @@ class SecretSchema(NamedSchema, table=True):
|
|
191
193
|
def from_request(
|
192
194
|
cls,
|
193
195
|
secret: SecretRequest,
|
196
|
+
internal: bool = False,
|
194
197
|
) -> "SecretSchema":
|
195
198
|
"""Create a `SecretSchema` from a `SecretRequest`.
|
196
199
|
|
197
200
|
Args:
|
198
201
|
secret: The `SecretRequest` from which to create the schema.
|
202
|
+
internal: Whether the secret is internal.
|
199
203
|
|
200
204
|
Returns:
|
201
205
|
The created `SecretSchema`.
|
@@ -209,6 +213,7 @@ class SecretSchema(NamedSchema, table=True):
|
|
209
213
|
# SQL secret store will call `store_secret_values` to store the
|
210
214
|
# values separately if SQL is used as the secrets store.
|
211
215
|
values=None,
|
216
|
+
internal=internal,
|
212
217
|
)
|
213
218
|
|
214
219
|
def update(
|
@@ -25,6 +25,7 @@ from sqlalchemy.sql.base import ExecutableOption
|
|
25
25
|
from sqlmodel import Field, Relationship
|
26
26
|
|
27
27
|
from zenml.models import (
|
28
|
+
ServiceConnectorConfiguration,
|
28
29
|
ServiceConnectorRequest,
|
29
30
|
ServiceConnectorResponse,
|
30
31
|
ServiceConnectorResponseBody,
|
@@ -168,6 +169,7 @@ class ServiceConnectorSchema(NamedSchema, table=True):
|
|
168
169
|
The created `ServiceConnectorSchema`.
|
169
170
|
"""
|
170
171
|
assert connector_request.user is not None, "User must be set."
|
172
|
+
configuration = connector_request.configuration.non_secrets
|
171
173
|
return cls(
|
172
174
|
user_id=connector_request.user,
|
173
175
|
name=connector_request.name,
|
@@ -180,9 +182,9 @@ class ServiceConnectorSchema(NamedSchema, table=True):
|
|
180
182
|
resource_id=connector_request.resource_id,
|
181
183
|
supports_instances=connector_request.supports_instances,
|
182
184
|
configuration=base64.b64encode(
|
183
|
-
json.dumps(
|
185
|
+
json.dumps(configuration).encode("utf-8")
|
184
186
|
)
|
185
|
-
if
|
187
|
+
if configuration
|
186
188
|
else None,
|
187
189
|
secret_id=secret_id,
|
188
190
|
expires_at=connector_request.expires_at,
|
@@ -226,15 +228,16 @@ class ServiceConnectorSchema(NamedSchema, table=True):
|
|
226
228
|
self.expiration_seconds = None
|
227
229
|
continue
|
228
230
|
if field == "configuration":
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
231
|
+
if connector_update.configuration is not None:
|
232
|
+
configuration = connector_update.configuration.non_secrets
|
233
|
+
if configuration is not None:
|
234
|
+
self.configuration = (
|
235
|
+
base64.b64encode(
|
236
|
+
json.dumps(configuration).encode("utf-8")
|
237
|
+
)
|
238
|
+
if configuration
|
239
|
+
else None
|
233
240
|
)
|
234
|
-
)
|
235
|
-
if connector_update.configuration
|
236
|
-
else None
|
237
|
-
)
|
238
241
|
elif field == "resource_types":
|
239
242
|
self.resource_types = base64.b64encode(
|
240
243
|
json.dumps(connector_update.resource_types).encode("utf-8")
|
@@ -286,12 +289,11 @@ class ServiceConnectorSchema(NamedSchema, table=True):
|
|
286
289
|
metadata = None
|
287
290
|
if include_metadata:
|
288
291
|
metadata = ServiceConnectorResponseMetadata(
|
289
|
-
configuration=
|
290
|
-
base64.b64decode(self.configuration).decode()
|
292
|
+
configuration=ServiceConnectorConfiguration(
|
293
|
+
**json.loads(base64.b64decode(self.configuration).decode())
|
291
294
|
)
|
292
295
|
if self.configuration
|
293
|
-
else
|
294
|
-
secret_id=self.secret_id,
|
296
|
+
else ServiceConnectorConfiguration(),
|
295
297
|
expiration_seconds=self.expiration_seconds,
|
296
298
|
labels=self.labels_dict,
|
297
299
|
)
|
@@ -29,6 +29,7 @@ from pydantic import Field, model_validator
|
|
29
29
|
from zenml.config.secrets_store_config import SecretsStoreConfiguration
|
30
30
|
from zenml.logger import get_logger
|
31
31
|
from zenml.models import (
|
32
|
+
ServiceConnectorConfiguration,
|
32
33
|
ServiceConnectorRequest,
|
33
34
|
)
|
34
35
|
from zenml.service_connectors.service_connector import ServiceConnector
|
@@ -133,7 +134,9 @@ class ServiceConnectorSecretsStore(BaseSecretsStore):
|
|
133
134
|
connector_type=self.SERVICE_CONNECTOR_TYPE,
|
134
135
|
resource_types=[self.SERVICE_CONNECTOR_RESOURCE_TYPE],
|
135
136
|
auth_method=self.config.auth_method,
|
136
|
-
configuration=
|
137
|
+
configuration=ServiceConnectorConfiguration(
|
138
|
+
**self.config.auth_config
|
139
|
+
),
|
137
140
|
)
|
138
141
|
base_connector = service_connector_registry.instantiate_connector(
|
139
142
|
model=request
|