zenml-nightly 0.73.0.dev20250204__py3-none-any.whl → 0.73.0.dev20250206__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 (46) hide show
  1. zenml/VERSION +1 -1
  2. zenml/cli/utils.py +42 -53
  3. zenml/client.py +6 -2
  4. zenml/constants.py +1 -0
  5. zenml/integrations/gcp/image_builders/gcp_image_builder.py +5 -8
  6. zenml/models/__init__.py +4 -2
  7. zenml/models/v2/base/filter.py +34 -11
  8. zenml/models/v2/base/scoped.py +4 -4
  9. zenml/models/v2/core/artifact.py +3 -3
  10. zenml/models/v2/core/artifact_version.py +22 -5
  11. zenml/models/v2/core/model.py +13 -3
  12. zenml/models/v2/core/model_version.py +13 -3
  13. zenml/models/v2/core/pipeline.py +11 -4
  14. zenml/models/v2/core/pipeline_run.py +20 -7
  15. zenml/models/v2/core/run_template.py +13 -3
  16. zenml/models/v2/core/step_run.py +9 -2
  17. zenml/pipelines/pipeline_definition.py +28 -12
  18. zenml/stack/stack.py +5 -0
  19. zenml/zen_stores/schemas/artifact_schemas.py +31 -4
  20. zenml/zen_stores/schemas/model_schemas.py +31 -6
  21. zenml/zen_stores/schemas/pipeline_run_schemas.py +1 -1
  22. zenml/zen_stores/schemas/pipeline_schemas.py +35 -8
  23. zenml/zen_stores/schemas/run_template_schemas.py +42 -14
  24. zenml/zen_stores/sql_zen_store.py +22 -56
  25. {zenml_nightly-0.73.0.dev20250204.dist-info → zenml_nightly-0.73.0.dev20250206.dist-info}/METADATA +1 -1
  26. {zenml_nightly-0.73.0.dev20250204.dist-info → zenml_nightly-0.73.0.dev20250206.dist-info}/RECORD +29 -46
  27. zenml/zen_server/deploy/helm/.helmignore +0 -23
  28. zenml/zen_server/deploy/helm/Chart.yaml +0 -12
  29. zenml/zen_server/deploy/helm/README.md +0 -50
  30. zenml/zen_server/deploy/helm/templates/NOTES.txt +0 -52
  31. zenml/zen_server/deploy/helm/templates/_environment.tpl +0 -511
  32. zenml/zen_server/deploy/helm/templates/_helpers.tpl +0 -70
  33. zenml/zen_server/deploy/helm/templates/cert-secret.yaml +0 -45
  34. zenml/zen_server/deploy/helm/templates/hpa.yaml +0 -32
  35. zenml/zen_server/deploy/helm/templates/server-db-job.yaml +0 -121
  36. zenml/zen_server/deploy/helm/templates/server-db-pvc.yaml +0 -25
  37. zenml/zen_server/deploy/helm/templates/server-deployment.yaml +0 -132
  38. zenml/zen_server/deploy/helm/templates/server-ingress.yaml +0 -59
  39. zenml/zen_server/deploy/helm/templates/server-secret.yaml +0 -60
  40. zenml/zen_server/deploy/helm/templates/server-service.yaml +0 -15
  41. zenml/zen_server/deploy/helm/templates/serviceaccount.yaml +0 -27
  42. zenml/zen_server/deploy/helm/templates/tests/test-connection.yaml +0 -15
  43. zenml/zen_server/deploy/helm/values.yaml +0 -1008
  44. {zenml_nightly-0.73.0.dev20250204.dist-info → zenml_nightly-0.73.0.dev20250206.dist-info}/LICENSE +0 -0
  45. {zenml_nightly-0.73.0.dev20250204.dist-info → zenml_nightly-0.73.0.dev20250206.dist-info}/WHEEL +0 -0
  46. {zenml_nightly-0.73.0.dev20250204.dist-info → zenml_nightly-0.73.0.dev20250206.dist-info}/entry_points.txt +0 -0
@@ -33,12 +33,13 @@ from zenml.constants import STR_FIELD_MAX_LENGTH, TEXT_FIELD_MAX_LENGTH
33
33
  from zenml.enums import ExecutionStatus
34
34
  from zenml.models.v2.base.base import BaseUpdate
35
35
  from zenml.models.v2.base.scoped import (
36
+ TaggableFilter,
37
+ WorkspaceScopedFilter,
36
38
  WorkspaceScopedRequest,
37
39
  WorkspaceScopedResponse,
38
40
  WorkspaceScopedResponseBody,
39
41
  WorkspaceScopedResponseMetadata,
40
42
  WorkspaceScopedResponseResources,
41
- WorkspaceScopedTaggableFilter,
42
43
  )
43
44
  from zenml.models.v2.core.code_reference import (
44
45
  CodeReferenceResponse,
@@ -307,11 +308,12 @@ class RunTemplateResponse(
307
308
  # ------------------ Filter Model ------------------
308
309
 
309
310
 
310
- class RunTemplateFilter(WorkspaceScopedTaggableFilter):
311
+ class RunTemplateFilter(WorkspaceScopedFilter, TaggableFilter):
311
312
  """Model for filtering of run templates."""
312
313
 
313
314
  FILTER_EXCLUDE_FIELDS: ClassVar[List[str]] = [
314
- *WorkspaceScopedTaggableFilter.FILTER_EXCLUDE_FIELDS,
315
+ *WorkspaceScopedFilter.FILTER_EXCLUDE_FIELDS,
316
+ *TaggableFilter.FILTER_EXCLUDE_FIELDS,
315
317
  "code_repository_id",
316
318
  "stack_id",
317
319
  "build_id",
@@ -320,6 +322,14 @@ class RunTemplateFilter(WorkspaceScopedTaggableFilter):
320
322
  "pipeline",
321
323
  "stack",
322
324
  ]
325
+ CUSTOM_SORTING_OPTIONS = [
326
+ *WorkspaceScopedFilter.CUSTOM_SORTING_OPTIONS,
327
+ *TaggableFilter.CUSTOM_SORTING_OPTIONS,
328
+ ]
329
+ CLI_EXCLUDE_FIELDS = [
330
+ *WorkspaceScopedFilter.CLI_EXCLUDE_FIELDS,
331
+ *TaggableFilter.CLI_EXCLUDE_FIELDS,
332
+ ]
323
333
 
324
334
  name: Optional[str] = Field(
325
335
  default=None,
@@ -16,6 +16,7 @@
16
16
  from datetime import datetime
17
17
  from typing import (
18
18
  TYPE_CHECKING,
19
+ Any,
19
20
  ClassVar,
20
21
  Dict,
21
22
  List,
@@ -574,7 +575,7 @@ class StepRunFilter(WorkspaceScopedFilter):
574
575
  default=None,
575
576
  description="Name/ID of the model associated with the step run.",
576
577
  )
577
- run_metadata: Optional[Dict[str, str]] = Field(
578
+ run_metadata: Optional[Dict[str, Any]] = Field(
578
579
  default=None,
579
580
  description="The run_metadata to filter the step runs by.",
580
581
  )
@@ -619,13 +620,19 @@ class StepRunFilter(WorkspaceScopedFilter):
619
620
  additional_filter = and_(
620
621
  RunMetadataResourceSchema.resource_id == StepRunSchema.id,
621
622
  RunMetadataResourceSchema.resource_type
622
- == MetadataResourceTypes.STEP_RUN,
623
+ == MetadataResourceTypes.STEP_RUN.value,
623
624
  RunMetadataResourceSchema.run_metadata_id
624
625
  == RunMetadataSchema.id,
626
+ self.generate_custom_query_conditions_for_column(
627
+ value=key,
628
+ table=RunMetadataSchema,
629
+ column="key",
630
+ ),
625
631
  self.generate_custom_query_conditions_for_column(
626
632
  value=value,
627
633
  table=RunMetadataSchema,
628
634
  column="value",
635
+ json_encode_value=True,
629
636
  ),
630
637
  )
631
638
  custom_filters.append(additional_filter)
@@ -407,17 +407,31 @@ class Pipeline:
407
407
  return self
408
408
 
409
409
  @property
410
- def requires_parameters(self) -> bool:
411
- """If the pipeline entrypoint requires parameters.
410
+ def required_parameters(self) -> List[str]:
411
+ """List of required parameters for the pipeline entrypoint.
412
412
 
413
413
  Returns:
414
- If the pipeline entrypoint requires parameters.
414
+ List of required parameters for the pipeline entrypoint.
415
415
  """
416
416
  signature = inspect.signature(self.entrypoint, follow_wrapped=True)
417
- return any(
418
- parameter.default is inspect.Parameter.empty
417
+ return [
418
+ parameter.name
419
419
  for parameter in signature.parameters.values()
420
- )
420
+ if parameter.default is inspect.Parameter.empty
421
+ ]
422
+
423
+ @property
424
+ def missing_parameters(self) -> List[str]:
425
+ """List of missing parameters for the pipeline entrypoint.
426
+
427
+ Returns:
428
+ List of missing parameters for the pipeline entrypoint.
429
+ """
430
+ available_parameters = set(self.configuration.parameters or {})
431
+ if params_from_file := self._from_config_file.get("parameters", None):
432
+ available_parameters.update(params_from_file)
433
+
434
+ return list(set(self.required_parameters) - available_parameters)
421
435
 
422
436
  @property
423
437
  def is_prepared(self) -> bool:
@@ -1412,7 +1426,7 @@ To avoid this consider setting pipeline parameters only in one place (config or
1412
1426
  except ValidationError as e:
1413
1427
  raise ValueError(
1414
1428
  "Invalid or missing pipeline function entrypoint arguments. "
1415
- "Only JSON serializable inputs are allowed as pipeline inputs."
1429
+ "Only JSON serializable inputs are allowed as pipeline inputs. "
1416
1430
  "Check out the pydantic error above for more details."
1417
1431
  ) from e
1418
1432
 
@@ -1427,15 +1441,17 @@ To avoid this consider setting pipeline parameters only in one place (config or
1427
1441
  requires parameters.
1428
1442
  """
1429
1443
  if not self.is_prepared:
1430
- if self.requires_parameters:
1444
+ if missing_parameters := self.missing_parameters:
1431
1445
  raise RuntimeError(
1432
1446
  f"Failed while trying to prepare pipeline {self.name}. "
1433
1447
  "The entrypoint function of the pipeline requires "
1434
- "arguments. Please prepare the pipeline by calling "
1435
- "`pipeline_instance.prepare(...)` and try again."
1448
+ "arguments which have not been configured yet: "
1449
+ f"{missing_parameters}. Please provide those parameters by "
1450
+ "calling `pipeline_instance.configure(parameters=...)` or "
1451
+ "by calling `pipeline_instance.prepare(...)` and try again."
1436
1452
  )
1437
- else:
1438
- self.prepare()
1453
+
1454
+ self.prepare()
1439
1455
 
1440
1456
  def create_run_template(
1441
1457
  self, name: str, **kwargs: Any
zenml/stack/stack.py CHANGED
@@ -37,6 +37,7 @@ from zenml.config.global_config import GlobalConfiguration
37
37
  from zenml.constants import (
38
38
  ENV_ZENML_SECRET_VALIDATION_LEVEL,
39
39
  ENV_ZENML_SKIP_IMAGE_BUILDER_DEFAULT,
40
+ ENV_ZENML_SKIP_STACK_VALIDATION,
40
41
  handle_bool_env_var,
41
42
  )
42
43
  from zenml.enums import SecretValidationLevel, StackComponentType
@@ -706,6 +707,10 @@ class Stack:
706
707
  if a secret for a component is missing. Otherwise, only a
707
708
  warning will be logged.
708
709
  """
710
+ if handle_bool_env_var(ENV_ZENML_SKIP_STACK_VALIDATION, default=False):
711
+ logger.debug("Skipping stack validation.")
712
+ return
713
+
709
714
  self.validate_image_builder()
710
715
  for component in self.components.values():
711
716
  if component.validator:
@@ -18,7 +18,8 @@ from uuid import UUID
18
18
 
19
19
  from pydantic import ValidationError
20
20
  from sqlalchemy import TEXT, Column, UniqueConstraint
21
- from sqlmodel import Field, Relationship
21
+ from sqlalchemy.orm import object_session
22
+ from sqlmodel import Field, Relationship, desc, select
22
23
 
23
24
  from zenml.config.source import Source
24
25
  from zenml.enums import (
@@ -90,6 +91,32 @@ class ArtifactSchema(NamedSchema, table=True):
90
91
  ),
91
92
  )
92
93
 
94
+ @property
95
+ def latest_version(self) -> Optional["ArtifactVersionSchema"]:
96
+ """Fetch the latest version for this artifact.
97
+
98
+ Raises:
99
+ RuntimeError: If no session for the schema exists.
100
+
101
+ Returns:
102
+ The latest version for this artifact.
103
+ """
104
+ if session := object_session(self):
105
+ return (
106
+ session.execute(
107
+ select(ArtifactVersionSchema)
108
+ .where(ArtifactVersionSchema.artifact_id == self.id)
109
+ .order_by(desc(ArtifactVersionSchema.created))
110
+ .limit(1)
111
+ )
112
+ .scalars()
113
+ .one_or_none()
114
+ )
115
+ else:
116
+ raise RuntimeError(
117
+ "Missing DB session to fetch latest version for artifact."
118
+ )
119
+
93
120
  @classmethod
94
121
  def from_request(
95
122
  cls,
@@ -127,9 +154,9 @@ class ArtifactSchema(NamedSchema, table=True):
127
154
  The created `ArtifactResponse`.
128
155
  """
129
156
  latest_id, latest_name = None, None
130
- if self.versions:
131
- latest_version = max(self.versions, key=lambda x: x.created)
132
- latest_id, latest_name = latest_version.id, latest_version.version
157
+ if latest_version := self.latest_version:
158
+ latest_id = latest_version.id
159
+ latest_name = latest_version.version
133
160
 
134
161
  # Create the body of the model
135
162
  body = ArtifactResponseBody(
@@ -24,7 +24,8 @@ from sqlalchemy import (
24
24
  Column,
25
25
  UniqueConstraint,
26
26
  )
27
- from sqlmodel import Field, Relationship
27
+ from sqlalchemy.orm import object_session
28
+ from sqlmodel import Field, Relationship, desc, select
28
29
 
29
30
  from zenml.enums import (
30
31
  ArtifactType,
@@ -126,6 +127,32 @@ class ModelSchema(NamedSchema, table=True):
126
127
  sa_relationship_kwargs={"cascade": "delete"},
127
128
  )
128
129
 
130
+ @property
131
+ def latest_version(self) -> Optional["ModelVersionSchema"]:
132
+ """Fetch the latest version for this model.
133
+
134
+ Raises:
135
+ RuntimeError: If no session for the schema exists.
136
+
137
+ Returns:
138
+ The latest version for this model.
139
+ """
140
+ if session := object_session(self):
141
+ return (
142
+ session.execute(
143
+ select(ModelVersionSchema)
144
+ .where(ModelVersionSchema.model_id == self.id)
145
+ .order_by(desc(ModelVersionSchema.number))
146
+ .limit(1)
147
+ )
148
+ .scalars()
149
+ .one_or_none()
150
+ )
151
+ else:
152
+ raise RuntimeError(
153
+ "Missing DB session to fetch latest version for model."
154
+ )
155
+
129
156
  @classmethod
130
157
  def from_request(cls, model_request: ModelRequest) -> "ModelSchema":
131
158
  """Convert an `ModelRequest` to an `ModelSchema`.
@@ -169,11 +196,9 @@ class ModelSchema(NamedSchema, table=True):
169
196
  """
170
197
  tags = [tag.to_model() for tag in self.tags]
171
198
 
172
- if self.model_versions:
173
- version_numbers = [mv.number for mv in self.model_versions]
174
- latest_version_idx = version_numbers.index(max(version_numbers))
175
- latest_version_name = self.model_versions[latest_version_idx].name
176
- latest_version_id = self.model_versions[latest_version_idx].id
199
+ if latest_version := self.latest_version:
200
+ latest_version_name = latest_version.name
201
+ latest_version_id = latest_version.id
177
202
  else:
178
203
  latest_version_name = None
179
204
  latest_version_id = None
@@ -208,7 +208,7 @@ class PipelineRunSchema(NamedSchema, RunMetadataInterface, table=True):
208
208
  stack: Optional["StackSchema"] = Relationship()
209
209
  build: Optional["PipelineBuildSchema"] = Relationship()
210
210
  schedule: Optional["ScheduleSchema"] = Relationship()
211
- pipeline: Optional["PipelineSchema"] = Relationship(back_populates="runs")
211
+ pipeline: Optional["PipelineSchema"] = Relationship()
212
212
  trigger_execution: Optional["TriggerExecutionSchema"] = Relationship()
213
213
 
214
214
  services: List["ServiceSchema"] = Relationship(
@@ -17,7 +17,8 @@ from typing import TYPE_CHECKING, Any, List, Optional
17
17
  from uuid import UUID
18
18
 
19
19
  from sqlalchemy import TEXT, Column, UniqueConstraint
20
- from sqlmodel import Field, Relationship
20
+ from sqlalchemy.orm import object_session
21
+ from sqlmodel import Field, Relationship, desc, select
21
22
 
22
23
  from zenml.enums import TaggableResourceTypes
23
24
  from zenml.models import (
@@ -85,10 +86,6 @@ class PipelineSchema(NamedSchema, table=True):
85
86
  schedules: List["ScheduleSchema"] = Relationship(
86
87
  back_populates="pipeline",
87
88
  )
88
- runs: List["PipelineRunSchema"] = Relationship(
89
- back_populates="pipeline",
90
- sa_relationship_kwargs={"order_by": "PipelineRunSchema.created"},
91
- )
92
89
  builds: List["PipelineBuildSchema"] = Relationship(
93
90
  back_populates="pipeline"
94
91
  )
@@ -105,6 +102,34 @@ class PipelineSchema(NamedSchema, table=True):
105
102
  ),
106
103
  )
107
104
 
105
+ @property
106
+ def latest_run(self) -> Optional["PipelineRunSchema"]:
107
+ """Fetch the latest run for this pipeline.
108
+
109
+ Raises:
110
+ RuntimeError: If no session for the schema exists.
111
+
112
+ Returns:
113
+ The latest run for this pipeline.
114
+ """
115
+ from zenml.zen_stores.schemas import PipelineRunSchema
116
+
117
+ if session := object_session(self):
118
+ return (
119
+ session.execute(
120
+ select(PipelineRunSchema)
121
+ .where(PipelineRunSchema.pipeline_id == self.id)
122
+ .order_by(desc(PipelineRunSchema.created))
123
+ .limit(1)
124
+ )
125
+ .scalars()
126
+ .one_or_none()
127
+ )
128
+ else:
129
+ raise RuntimeError(
130
+ "Missing DB session to fetch latest run for pipeline."
131
+ )
132
+
108
133
  @classmethod
109
134
  def from_request(
110
135
  cls,
@@ -141,10 +166,12 @@ class PipelineSchema(NamedSchema, table=True):
141
166
  Returns:
142
167
  The created PipelineResponse.
143
168
  """
169
+ latest_run = self.latest_run
170
+
144
171
  body = PipelineResponseBody(
145
172
  user=self.user.to_model() if self.user else None,
146
- latest_run_id=self.runs[-1].id if self.runs else None,
147
- latest_run_status=self.runs[-1].status if self.runs else None,
173
+ latest_run_id=latest_run.id if latest_run else None,
174
+ latest_run_status=latest_run.status if latest_run else None,
148
175
  created=self.created,
149
176
  updated=self.updated,
150
177
  )
@@ -158,7 +185,7 @@ class PipelineSchema(NamedSchema, table=True):
158
185
 
159
186
  resources = None
160
187
  if include_resources:
161
- latest_run_user = self.runs[-1].user if self.runs else None
188
+ latest_run_user = latest_run.user if latest_run else None
162
189
 
163
190
  resources = PipelineResponseResources(
164
191
  latest_run_user=latest_run_user.to_model()
@@ -18,7 +18,8 @@ from uuid import UUID
18
18
 
19
19
  from sqlalchemy import Column, String, UniqueConstraint
20
20
  from sqlalchemy.dialects.mysql import MEDIUMTEXT
21
- from sqlmodel import Field, Relationship
21
+ from sqlalchemy.orm import object_session
22
+ from sqlmodel import Field, Relationship, col, desc, select
22
23
 
23
24
  from zenml.constants import MEDIUMTEXT_MAX_LENGTH
24
25
  from zenml.enums import TaggableResourceTypes
@@ -99,17 +100,6 @@ class RunTemplateSchema(BaseSchema, table=True):
99
100
  }
100
101
  )
101
102
 
102
- runs: List["PipelineRunSchema"] = Relationship(
103
- sa_relationship_kwargs={
104
- "primaryjoin": "RunTemplateSchema.id==PipelineDeploymentSchema.template_id",
105
- "secondaryjoin": "PipelineDeploymentSchema.id==PipelineRunSchema.deployment_id",
106
- "secondary": "pipeline_deployment",
107
- "cascade": "delete",
108
- "viewonly": True,
109
- "order_by": "PipelineRunSchema.created",
110
- }
111
- )
112
-
113
103
  tags: List["TagSchema"] = Relationship(
114
104
  sa_relationship_kwargs=dict(
115
105
  primaryjoin=f"and_(foreign(TagResourceSchema.resource_type)=='{TaggableResourceTypes.RUN_TEMPLATE.value}', foreign(TagResourceSchema.resource_id)==RunTemplateSchema.id)",
@@ -120,6 +110,42 @@ class RunTemplateSchema(BaseSchema, table=True):
120
110
  ),
121
111
  )
122
112
 
113
+ @property
114
+ def latest_run(self) -> Optional["PipelineRunSchema"]:
115
+ """Fetch the latest run for this template.
116
+
117
+ Raises:
118
+ RuntimeError: If no session for the schema exists.
119
+
120
+ Returns:
121
+ The latest run for this template.
122
+ """
123
+ from zenml.zen_stores.schemas import (
124
+ PipelineDeploymentSchema,
125
+ PipelineRunSchema,
126
+ )
127
+
128
+ if session := object_session(self):
129
+ return (
130
+ session.execute(
131
+ select(PipelineRunSchema)
132
+ .join(
133
+ PipelineDeploymentSchema,
134
+ col(PipelineDeploymentSchema.id)
135
+ == col(PipelineRunSchema.deployment_id),
136
+ )
137
+ .where(PipelineDeploymentSchema.template_id == self.id)
138
+ .order_by(desc(PipelineRunSchema.created))
139
+ .limit(1)
140
+ )
141
+ .scalars()
142
+ .one_or_none()
143
+ )
144
+ else:
145
+ raise RuntimeError(
146
+ "Missing DB session to fetch latest run for template."
147
+ )
148
+
123
149
  @classmethod
124
150
  def from_request(
125
151
  cls,
@@ -184,13 +210,15 @@ class RunTemplateSchema(BaseSchema, table=True):
184
210
  ):
185
211
  runnable = True
186
212
 
213
+ latest_run = self.latest_run
214
+
187
215
  body = RunTemplateResponseBody(
188
216
  user=self.user.to_model() if self.user else None,
189
217
  created=self.created,
190
218
  updated=self.updated,
191
219
  runnable=runnable,
192
- latest_run_id=self.runs[-1].id if self.runs else None,
193
- latest_run_status=self.runs[-1].status if self.runs else None,
220
+ latest_run_id=latest_run.id if latest_run else None,
221
+ latest_run_status=latest_run.status if latest_run else None,
194
222
  )
195
223
 
196
224
  metadata = None
@@ -303,7 +303,6 @@ from zenml.utils.enum_utils import StrEnum
303
303
  from zenml.utils.networking_utils import (
304
304
  replace_localhost_with_internal_hostname,
305
305
  )
306
- from zenml.utils.pydantic_utils import before_validator_handler
307
306
  from zenml.utils.secret_utils import PlainSerializedSecretStr
308
307
  from zenml.utils.string_utils import (
309
308
  format_name_template,
@@ -434,6 +433,7 @@ class SqlZenStoreConfiguration(StoreConfiguration):
434
433
  be created automatically on first access.
435
434
  username: The database username.
436
435
  password: The database password.
436
+ ssl: Whether to use SSL.
437
437
  ssl_ca: certificate authority certificate. Required for SSL
438
438
  enabled authentication if the CA certificate is not part of the
439
439
  certificates shipped by the operating system.
@@ -463,6 +463,7 @@ class SqlZenStoreConfiguration(StoreConfiguration):
463
463
  database: Optional[str] = None
464
464
  username: Optional[PlainSerializedSecretStr] = None
465
465
  password: Optional[PlainSerializedSecretStr] = None
466
+ ssl: bool = False
466
467
  ssl_ca: Optional[PlainSerializedSecretStr] = None
467
468
  ssl_cert: Optional[PlainSerializedSecretStr] = None
468
469
  ssl_key: Optional[PlainSerializedSecretStr] = None
@@ -499,35 +500,6 @@ class SqlZenStoreConfiguration(StoreConfiguration):
499
500
 
500
501
  return secrets_store
501
502
 
502
- @model_validator(mode="before")
503
- @classmethod
504
- @before_validator_handler
505
- def _remove_grpc_attributes(cls, data: Dict[str, Any]) -> Dict[str, Any]:
506
- """Removes old GRPC attributes.
507
-
508
- Args:
509
- data: All model attribute values.
510
-
511
- Returns:
512
- The model attribute values
513
- """
514
- grpc_attribute_keys = [
515
- "grpc_metadata_host",
516
- "grpc_metadata_port",
517
- "grpc_metadata_ssl_ca",
518
- "grpc_metadata_ssl_key",
519
- "grpc_metadata_ssl_cert",
520
- ]
521
- grpc_values = [data.pop(key, None) for key in grpc_attribute_keys]
522
- if any(grpc_values):
523
- logger.warning(
524
- "The GRPC attributes %s are unused and will be removed soon. "
525
- "Please remove them from SQLZenStore configuration. This will "
526
- "become an error in future versions of ZenML."
527
- )
528
-
529
- return data
530
-
531
503
  @model_validator(mode="after")
532
504
  def _validate_backup_strategy(self) -> "SqlZenStoreConfiguration":
533
505
  """Validate the backup strategy.
@@ -641,15 +613,21 @@ class SqlZenStoreConfiguration(StoreConfiguration):
641
613
  return None
642
614
 
643
615
  for k, v in sql_url.query.items():
644
- if k == "ssl_ca":
616
+ if k == "ssl":
617
+ if r := _get_query_result(v):
618
+ self.ssl = is_true_string_value(r)
619
+ elif k == "ssl_ca":
645
620
  if r := _get_query_result(v):
646
621
  self.ssl_ca = PlainSerializedSecretStr(r)
622
+ self.ssl = True
647
623
  elif k == "ssl_cert":
648
624
  if r := _get_query_result(v):
649
625
  self.ssl_cert = PlainSerializedSecretStr(r)
626
+ self.ssl = True
650
627
  elif k == "ssl_key":
651
628
  if r := _get_query_result(v):
652
629
  self.ssl_key = PlainSerializedSecretStr(r)
630
+ self.ssl = True
653
631
  elif k == "ssl_verify_server_cert":
654
632
  if r := _get_query_result(v):
655
633
  if is_true_string_value(r):
@@ -659,7 +637,7 @@ class SqlZenStoreConfiguration(StoreConfiguration):
659
637
  else:
660
638
  raise ValueError(
661
639
  "Invalid MySQL URL query parameter `%s`: The "
662
- "parameter must be one of: ssl_ca, ssl_cert, "
640
+ "parameter must be one of: ssl, ssl_ca, ssl_cert, "
663
641
  "ssl_key, or ssl_verify_server_cert.",
664
642
  k,
665
643
  )
@@ -728,15 +706,6 @@ class SqlZenStoreConfiguration(StoreConfiguration):
728
706
  """
729
707
  return make_url(url).drivername in SQLDatabaseDriver.values()
730
708
 
731
- def expand_certificates(self) -> None:
732
- """Expands the certificates in the verify_ssl field."""
733
- # Load the certificate values back into the configuration
734
- for key in ["ssl_key", "ssl_ca", "ssl_cert"]:
735
- file_path = getattr(self, key, None)
736
- if file_path and os.path.isfile(file_path.get_secret_value()):
737
- with open(file_path, "r") as f:
738
- setattr(self, key, f.read())
739
-
740
709
  def get_sqlalchemy_config(
741
710
  self,
742
711
  database: Optional[str] = None,
@@ -789,22 +758,19 @@ class SqlZenStoreConfiguration(StoreConfiguration):
789
758
  sqlalchemy_ssl_args: Dict[str, Any] = {}
790
759
 
791
760
  # Handle SSL params
792
- for key in ["ssl_key", "ssl_ca", "ssl_cert"]:
793
- ssl_setting = getattr(self, key)
794
- if not ssl_setting:
795
- continue
796
- if not os.path.isfile(ssl_setting.get_secret_value()):
797
- logger.warning(
798
- f"Database SSL setting `{key}` is not a file. "
761
+ if self.ssl:
762
+ sqlalchemy_ssl_args["ssl"] = True
763
+ for key in ["ssl_key", "ssl_ca", "ssl_cert"]:
764
+ ssl_setting = getattr(self, key)
765
+ if not ssl_setting:
766
+ continue
767
+ if not os.path.isfile(ssl_setting.get_secret_value()):
768
+ logger.warning(
769
+ f"Database SSL setting `{key}` is not a file. "
770
+ )
771
+ sqlalchemy_ssl_args[key.removeprefix("ssl_")] = (
772
+ ssl_setting.get_secret_value()
799
773
  )
800
- sqlalchemy_ssl_args[key.lstrip("ssl_")] = (
801
- ssl_setting.get_secret_value()
802
- )
803
- sqlalchemy_ssl_args[key.removeprefix("ssl_")] = (
804
- ssl_setting.get_secret_value()
805
- )
806
-
807
- if len(sqlalchemy_ssl_args) > 0:
808
774
  sqlalchemy_ssl_args["check_hostname"] = (
809
775
  self.ssl_verify_server_cert
810
776
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: zenml-nightly
3
- Version: 0.73.0.dev20250204
3
+ Version: 0.73.0.dev20250206
4
4
  Summary: ZenML: Write production-ready ML code.
5
5
  License: Apache-2.0
6
6
  Keywords: machine learning,production,pipeline,mlops,devops