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.
Files changed (54) hide show
  1. zenml/VERSION +1 -1
  2. zenml/cli/base.py +3 -2
  3. zenml/cli/login.py +21 -3
  4. zenml/cli/service_connectors.py +5 -12
  5. zenml/cli/stack.py +1 -5
  6. zenml/cli/utils.py +8 -52
  7. zenml/client.py +32 -40
  8. zenml/config/__init__.py +13 -2
  9. zenml/constants.py +0 -1
  10. zenml/exceptions.py +16 -0
  11. zenml/integrations/airflow/orchestrators/airflow_orchestrator.py +15 -6
  12. zenml/integrations/aws/container_registries/aws_container_registry.py +3 -1
  13. zenml/integrations/aws/orchestrators/sagemaker_orchestrator.py +54 -58
  14. zenml/integrations/azure/orchestrators/azureml_orchestrator.py +28 -19
  15. zenml/integrations/databricks/orchestrators/databricks_orchestrator.py +19 -63
  16. zenml/integrations/databricks/orchestrators/databricks_orchestrator_entrypoint_config.py +8 -3
  17. zenml/integrations/gcp/orchestrators/vertex_orchestrator.py +36 -61
  18. zenml/integrations/hyperai/orchestrators/hyperai_orchestrator.py +19 -22
  19. zenml/integrations/integration.py +23 -58
  20. zenml/integrations/kubeflow/orchestrators/kubeflow_orchestrator.py +28 -31
  21. zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator.py +33 -20
  22. zenml/integrations/lightning/orchestrators/lightning_orchestrator.py +25 -100
  23. zenml/integrations/skypilot/orchestrators/skypilot_base_vm_orchestrator.py +19 -8
  24. zenml/integrations/skypilot/utils.py +17 -13
  25. zenml/integrations/tekton/orchestrators/tekton_orchestrator.py +28 -12
  26. zenml/models/__init__.py +2 -0
  27. zenml/models/v2/core/service_connector.py +178 -108
  28. zenml/models/v2/core/step_run.py +1 -0
  29. zenml/orchestrators/__init__.py +2 -0
  30. zenml/orchestrators/base_orchestrator.py +137 -66
  31. zenml/orchestrators/input_utils.py +5 -13
  32. zenml/orchestrators/local/local_orchestrator.py +19 -9
  33. zenml/orchestrators/local_docker/local_docker_orchestrator.py +15 -5
  34. zenml/orchestrators/publish_utils.py +24 -0
  35. zenml/orchestrators/step_run_utils.py +1 -2
  36. zenml/pipelines/run_utils.py +12 -7
  37. zenml/service_connectors/service_connector.py +11 -61
  38. zenml/service_connectors/service_connector_utils.py +4 -2
  39. zenml/step_operators/step_operator_entrypoint_configuration.py +1 -1
  40. zenml/utils/package_utils.py +111 -1
  41. zenml/zen_server/routers/service_connectors_endpoints.py +7 -22
  42. zenml/zen_stores/migrations/versions/5bb25e95849c_add_internal_secrets.py +62 -0
  43. zenml/zen_stores/rest_zen_store.py +204 -132
  44. zenml/zen_stores/schemas/secret_schemas.py +5 -0
  45. zenml/zen_stores/schemas/service_connector_schemas.py +16 -14
  46. zenml/zen_stores/secrets_stores/service_connector_secrets_store.py +4 -1
  47. zenml/zen_stores/sql_zen_store.py +241 -119
  48. zenml/zen_stores/zen_store_interface.py +9 -1
  49. {zenml_nightly-0.83.1.dev20250624.dist-info → zenml_nightly-0.83.1.dev20250626.dist-info}/METADATA +1 -1
  50. {zenml_nightly-0.83.1.dev20250624.dist-info → zenml_nightly-0.83.1.dev20250626.dist-info}/RECORD +53 -53
  51. zenml/utils/integration_utils.py +0 -34
  52. {zenml_nightly-0.83.1.dev20250624.dist-info → zenml_nightly-0.83.1.dev20250626.dist-info}/LICENSE +0 -0
  53. {zenml_nightly-0.83.1.dev20250624.dist-info → zenml_nightly-0.83.1.dev20250626.dist-info}/WHEEL +0 -0
  54. {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, service_connector_id: UUID, hydrate: bool = True
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={"expand_secrets": False, "hydrate": hydrate},
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={"expand_secrets": False, "hydrate": hydrate},
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(resources.id)
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 --url "
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
- if self._session is None:
4207
- # We only need to initialize the session once over the lifetime
4208
- # of the client. We can swap the token out when it expires.
4209
- if self.config.verify_ssl is False:
4210
- urllib3.disable_warnings(
4211
- urllib3.exceptions.InsecureRequestWarning
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
- self._session = requests.Session()
4215
- # Retries are triggered for all HTTP methods (GET, HEAD, POST, PUT,
4216
- # PATCH, OPTIONS and DELETE) on specific HTTP status codes:
4217
- #
4218
- # 408: Request Timeout.
4219
- # 429: Too Many Requests.
4220
- # 502: Bad Gateway.
4221
- # 503: Service Unavailable.
4222
- # 504: Gateway Timeout
4223
- #
4224
- # This also handles connection level errors, if a connection attempt
4225
- # fails due to transient issues like:
4226
- #
4227
- # DNS resolution errors.
4228
- # Connection timeouts.
4229
- # Network disruptions.
4230
- #
4231
- # Additional errors retried:
4232
- #
4233
- # Read Timeouts: If the server does not send a response within
4234
- # the timeout period.
4235
- # Connection Refused: If the server refuses the connection.
4236
- #
4237
- retries = Retry(
4238
- connect=5,
4239
- read=8,
4240
- redirect=3,
4241
- status=10,
4242
- allowed_methods=[
4243
- "HEAD",
4244
- "GET",
4245
- "POST",
4246
- "PUT",
4247
- "PATCH",
4248
- "DELETE",
4249
- "OPTIONS",
4250
- ],
4251
- status_forcelist=[
4252
- 408, # Request Timeout
4253
- 429, # Too Many Requests
4254
- 502, # Bad Gateway
4255
- 503, # Service Unavailable
4256
- 504, # Gateway Timeout
4257
- ],
4258
- other=3,
4259
- backoff_factor=1,
4260
- )
4261
- self._session.mount("https://", HTTPAdapter(max_retries=retries))
4262
- self._session.mount("http://", HTTPAdapter(max_retries=retries))
4263
- self._session.verify = self.config.verify_ssl
4264
- # Use a custom user agent to identify the ZenML client in the server
4265
- # logs.
4266
- self._session.headers.update(
4267
- {"User-Agent": "zenml/" + zenml.__version__}
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
- # Note that we return an unauthenticated session here. An API token
4271
- # is only fetched and set in the authorization header when and if it is
4272
- # needed.
4273
- return self._session
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
- self.session.headers.update(
4395
- {source_context.name: source_context.get().value}
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
- self.session.headers.update({"X-Request-ID": request_id})
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
- self.session.headers.update({"Idempotency-Key": str(uuid4())})
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
- if self._api_token is None:
4446
- # The last request was not authenticated with an API
4447
- # token at all. We authenticate here and then try the
4448
- # request again, this time with a valid API token in the
4449
- # header.
4450
- logger.debug(
4451
- f"[{request_id}] The last request was not "
4452
- f"authenticated: {e}\n"
4453
- "Re-authenticating and retrying..."
4454
- )
4455
- self.authenticate()
4456
- elif not credentials_store.can_login(self.url):
4457
- # The request failed either because we're not
4458
- # authenticated or our current credentials are not valid
4459
- # anymore.
4460
- logger.error(
4461
- "The current token is no longer valid, and "
4462
- "it is not possible to generate a new token using the "
4463
- "configured credentials. Please run "
4464
- f"`zenml login {self.url}` to "
4465
- "re-authenticate to the server or authenticate using "
4466
- "an API key. See "
4467
- "https://docs.zenml.io/deploying-zenml/connecting-to-zenml/connect-with-a-service-account "
4468
- "for more information."
4469
- )
4470
- # Clear the current token from the credentials store to
4471
- # force a new authentication flow next time.
4472
- get_credentials_store().clear_token(self.url)
4473
- raise e
4474
- elif not re_authenticated:
4475
- # The last request was authenticated with an API token
4476
- # that was rejected by the server. We attempt a
4477
- # re-authentication here and then retry the request.
4478
- logger.debug(
4479
- f"[{request_id}] The last request was authenticated "
4480
- "with an API token that was rejected by the server: "
4481
- f"{e}\n"
4482
- "Re-authenticating and retrying..."
4483
- )
4484
- re_authenticated = True
4485
- self.authenticate(
4486
- # Ignore the current token and force a re-authentication
4487
- force=True
4488
- )
4489
- else:
4490
- # The last request was made after re-authenticating but
4491
- # still failed. Bailing out.
4492
- logger.debug(
4493
- f"[{request_id}] The last request failed after "
4494
- "re-authenticating: {e}\n"
4495
- "Bailing out..."
4496
- )
4497
- raise CredentialsNotValid(
4498
- "The current credentials are no longer valid. Please "
4499
- "log in again using 'zenml login'."
4500
- ) from e
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(connector_request.configuration).encode("utf-8")
185
+ json.dumps(configuration).encode("utf-8")
184
186
  )
185
- if connector_request.configuration
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
- self.configuration = (
230
- base64.b64encode(
231
- json.dumps(connector_update.configuration).encode(
232
- "utf-8"
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=json.loads(
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=self.config.auth_config,
137
+ configuration=ServiceConnectorConfiguration(
138
+ **self.config.auth_config
139
+ ),
137
140
  )
138
141
  base_connector = service_connector_registry.instantiate_connector(
139
142
  model=request