zenml-nightly 0.61.0.dev20240711__py3-none-any.whl → 0.61.0.dev20240713__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/stack.py +220 -71
- zenml/constants.py +6 -0
- zenml/enums.py +16 -0
- zenml/integrations/gcp/service_connectors/gcp_service_connector.py +54 -6
- zenml/logging/step_logging.py +34 -35
- zenml/models/__init__.py +2 -0
- zenml/models/v2/core/server_settings.py +0 -20
- zenml/models/v2/misc/stack_deployment.py +20 -0
- zenml/orchestrators/step_launcher.py +1 -0
- zenml/stack_deployments/aws_stack_deployment.py +56 -91
- zenml/stack_deployments/gcp_stack_deployment.py +260 -0
- zenml/stack_deployments/stack_deployment.py +103 -25
- zenml/stack_deployments/utils.py +4 -0
- zenml/zen_server/routers/devices_endpoints.py +4 -1
- zenml/zen_server/routers/server_endpoints.py +29 -2
- zenml/zen_server/routers/stack_deployment_endpoints.py +34 -20
- zenml/zen_stores/migrations/versions/b4fca5241eea_migrate_onboarding_state.py +167 -0
- zenml/zen_stores/rest_zen_store.py +45 -21
- zenml/zen_stores/schemas/server_settings_schemas.py +23 -11
- zenml/zen_stores/sql_zen_store.py +117 -19
- zenml/zen_stores/zen_store_interface.py +6 -5
- {zenml_nightly-0.61.0.dev20240711.dist-info → zenml_nightly-0.61.0.dev20240713.dist-info}/METADATA +1 -1
- {zenml_nightly-0.61.0.dev20240711.dist-info → zenml_nightly-0.61.0.dev20240713.dist-info}/RECORD +27 -25
- {zenml_nightly-0.61.0.dev20240711.dist-info → zenml_nightly-0.61.0.dev20240713.dist-info}/LICENSE +0 -0
- {zenml_nightly-0.61.0.dev20240711.dist-info → zenml_nightly-0.61.0.dev20240713.dist-info}/WHEEL +0 -0
- {zenml_nightly-0.61.0.dev20240711.dist-info → zenml_nightly-0.61.0.dev20240713.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,167 @@
|
|
1
|
+
"""Migrate onboarding state [b4fca5241eea].
|
2
|
+
|
3
|
+
Revision ID: b4fca5241eea
|
4
|
+
Revises: 0.61.0
|
5
|
+
Create Date: 2024-06-20 15:01:22.414801
|
6
|
+
|
7
|
+
"""
|
8
|
+
|
9
|
+
import json
|
10
|
+
from typing import Dict, List
|
11
|
+
|
12
|
+
import sqlalchemy as sa
|
13
|
+
from alembic import op
|
14
|
+
|
15
|
+
# revision identifiers, used by Alembic.
|
16
|
+
revision = "b4fca5241eea"
|
17
|
+
down_revision = "0.61.0"
|
18
|
+
branch_labels = None
|
19
|
+
depends_on = None
|
20
|
+
|
21
|
+
|
22
|
+
ONBOARDING_KEY_MAPPING = {
|
23
|
+
"connect_zenml": ["device_verified"],
|
24
|
+
"run_first_pipeline": ["pipeline_run", "starter_setup_completed"],
|
25
|
+
"run_remote_pipeline": [
|
26
|
+
"production_setup_completed",
|
27
|
+
],
|
28
|
+
}
|
29
|
+
|
30
|
+
|
31
|
+
def upgrade() -> None:
|
32
|
+
"""Upgrade database schema and/or data, creating a new revision."""
|
33
|
+
connection = op.get_bind()
|
34
|
+
|
35
|
+
meta = sa.MetaData()
|
36
|
+
meta.reflect(only=("server_settings",), bind=connection)
|
37
|
+
|
38
|
+
server_settings_table = sa.Table("server_settings", meta)
|
39
|
+
|
40
|
+
existing_onboarding_state = connection.execute(
|
41
|
+
sa.select(server_settings_table.c.onboarding_state)
|
42
|
+
).scalar_one_or_none()
|
43
|
+
|
44
|
+
new_state = []
|
45
|
+
|
46
|
+
if existing_onboarding_state:
|
47
|
+
# There was already an existing onboarding state in the DB
|
48
|
+
# -> Migrate to the new server keys
|
49
|
+
state = json.loads(existing_onboarding_state)
|
50
|
+
|
51
|
+
if isinstance(state, Dict):
|
52
|
+
for key in state.keys():
|
53
|
+
if key in ONBOARDING_KEY_MAPPING:
|
54
|
+
new_state.extend(ONBOARDING_KEY_MAPPING[key])
|
55
|
+
elif isinstance(state, List):
|
56
|
+
# Somehow the state is already converted, probably shouldn't happen
|
57
|
+
return
|
58
|
+
|
59
|
+
# We now query the DB and complete all onboarding steps that we can detect
|
60
|
+
# from the database
|
61
|
+
meta = sa.MetaData()
|
62
|
+
meta.reflect(
|
63
|
+
only=(
|
64
|
+
"pipeline_run",
|
65
|
+
"stack_component",
|
66
|
+
"stack",
|
67
|
+
"stack_composition",
|
68
|
+
"pipeline_deployment",
|
69
|
+
),
|
70
|
+
bind=connection,
|
71
|
+
)
|
72
|
+
|
73
|
+
pipeline_run_table = sa.Table("pipeline_run", meta)
|
74
|
+
stack_component_table = sa.Table("stack_component", meta)
|
75
|
+
stack_table = sa.Table("stack", meta)
|
76
|
+
stack_composition_table = sa.Table("stack_composition", meta)
|
77
|
+
pipeline_deployment_table = sa.Table("pipeline_deployment", meta)
|
78
|
+
|
79
|
+
pipeline_run_count = connection.execute(
|
80
|
+
sa.select(sa.func.count(pipeline_run_table.c.id))
|
81
|
+
).scalar()
|
82
|
+
if pipeline_run_count and pipeline_run_count > 0:
|
83
|
+
new_state.extend(ONBOARDING_KEY_MAPPING["run_first_pipeline"])
|
84
|
+
|
85
|
+
stack_with_remote_orchestrator_count = connection.execute(
|
86
|
+
sa.select(sa.func.count(stack_table.c.id))
|
87
|
+
.where(stack_composition_table.c.stack_id == stack_table.c.id)
|
88
|
+
.where(
|
89
|
+
stack_composition_table.c.component_id
|
90
|
+
== stack_component_table.c.id
|
91
|
+
)
|
92
|
+
.where(
|
93
|
+
stack_component_table.c.flavor.not_in(["local", "local_docker"])
|
94
|
+
)
|
95
|
+
.where(stack_component_table.c.type == "orchestrator")
|
96
|
+
).scalar()
|
97
|
+
if (
|
98
|
+
stack_with_remote_orchestrator_count
|
99
|
+
and stack_with_remote_orchestrator_count > 0
|
100
|
+
):
|
101
|
+
new_state.append("stack_with_remote_orchestrator_created")
|
102
|
+
|
103
|
+
pipeline_run_with_remote_artifact_store_count = connection.execute(
|
104
|
+
sa.select(sa.func.count(pipeline_run_table.c.id))
|
105
|
+
.where(
|
106
|
+
pipeline_run_table.c.deployment_id
|
107
|
+
== pipeline_deployment_table.c.id
|
108
|
+
)
|
109
|
+
.where(pipeline_deployment_table.c.stack_id == stack_table.c.id)
|
110
|
+
.where(stack_composition_table.c.stack_id == stack_table.c.id)
|
111
|
+
.where(
|
112
|
+
stack_composition_table.c.component_id
|
113
|
+
== stack_component_table.c.id
|
114
|
+
)
|
115
|
+
.where(stack_component_table.c.flavor != "local")
|
116
|
+
.where(stack_component_table.c.type == "artifact_store")
|
117
|
+
).scalar()
|
118
|
+
if (
|
119
|
+
pipeline_run_with_remote_artifact_store_count
|
120
|
+
and pipeline_run_with_remote_artifact_store_count > 0
|
121
|
+
):
|
122
|
+
new_state.append("production_setup_completed")
|
123
|
+
|
124
|
+
pipeline_run_with_remote_orchestrator_count = connection.execute(
|
125
|
+
sa.select(sa.func.count(pipeline_run_table.c.id))
|
126
|
+
.where(
|
127
|
+
pipeline_run_table.c.deployment_id
|
128
|
+
== pipeline_deployment_table.c.id
|
129
|
+
)
|
130
|
+
.where(pipeline_deployment_table.c.stack_id == stack_table.c.id)
|
131
|
+
.where(stack_composition_table.c.stack_id == stack_table.c.id)
|
132
|
+
.where(
|
133
|
+
stack_composition_table.c.component_id
|
134
|
+
== stack_component_table.c.id
|
135
|
+
)
|
136
|
+
.where(
|
137
|
+
stack_component_table.c.flavor.not_in(["local", "local_docker"])
|
138
|
+
)
|
139
|
+
.where(stack_component_table.c.type == "orchestrator")
|
140
|
+
).scalar()
|
141
|
+
if (
|
142
|
+
pipeline_run_with_remote_orchestrator_count
|
143
|
+
and pipeline_run_with_remote_orchestrator_count > 0
|
144
|
+
):
|
145
|
+
new_state.append("pipeline_run_with_remote_orchestrator")
|
146
|
+
new_state.append("production_setup_completed")
|
147
|
+
|
148
|
+
if new_state:
|
149
|
+
# If any of the items are finished, we also complete the initial
|
150
|
+
# onboarding step which is not explicitly tracked in the database
|
151
|
+
new_state.append("device_verified")
|
152
|
+
|
153
|
+
# Remove duplicate keys
|
154
|
+
new_state = list(set(new_state))
|
155
|
+
|
156
|
+
connection.execute(
|
157
|
+
sa.update(server_settings_table).values(
|
158
|
+
onboarding_state=json.dumps(new_state)
|
159
|
+
)
|
160
|
+
)
|
161
|
+
|
162
|
+
|
163
|
+
def downgrade() -> None:
|
164
|
+
"""Downgrade database schema and/or data back to the previous revision."""
|
165
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
166
|
+
pass
|
167
|
+
# ### end Alembic commands ###
|
@@ -58,6 +58,7 @@ from zenml.constants import (
|
|
58
58
|
ARTIFACTS,
|
59
59
|
CODE_REFERENCES,
|
60
60
|
CODE_REPOSITORIES,
|
61
|
+
CONFIG,
|
61
62
|
CURRENT_USER,
|
62
63
|
DEACTIVATE,
|
63
64
|
DEFAULT_HTTP_TIMEOUT,
|
@@ -91,6 +92,7 @@ from zenml.constants import (
|
|
91
92
|
SERVICE_CONNECTOR_RESOURCES,
|
92
93
|
SERVICE_CONNECTOR_TYPES,
|
93
94
|
SERVICE_CONNECTOR_VERIFY,
|
95
|
+
SERVICE_CONNECTOR_VERIFY_REQUEST_TIMEOUT,
|
94
96
|
SERVICE_CONNECTORS,
|
95
97
|
SERVICES,
|
96
98
|
STACK,
|
@@ -101,7 +103,6 @@ from zenml.constants import (
|
|
101
103
|
TAGS,
|
102
104
|
TRIGGER_EXECUTIONS,
|
103
105
|
TRIGGERS,
|
104
|
-
URL,
|
105
106
|
USERS,
|
106
107
|
VERSION_1,
|
107
108
|
WORKSPACES,
|
@@ -216,6 +217,7 @@ from zenml.models import (
|
|
216
217
|
ServiceRequest,
|
217
218
|
ServiceResponse,
|
218
219
|
ServiceUpdate,
|
220
|
+
StackDeploymentConfig,
|
219
221
|
StackDeploymentInfo,
|
220
222
|
StackFilter,
|
221
223
|
StackRequest,
|
@@ -2476,6 +2478,10 @@ class RestZenStore(BaseZenStore):
|
|
2476
2478
|
f"{SERVICE_CONNECTORS}{SERVICE_CONNECTOR_VERIFY}",
|
2477
2479
|
body=service_connector,
|
2478
2480
|
params={"list_resources": list_resources},
|
2481
|
+
timeout=max(
|
2482
|
+
self.config.http_timeout,
|
2483
|
+
SERVICE_CONNECTOR_VERIFY_REQUEST_TIMEOUT,
|
2484
|
+
),
|
2479
2485
|
)
|
2480
2486
|
|
2481
2487
|
resources = ServiceConnectorResourcesModel.model_validate(
|
@@ -2513,6 +2519,10 @@ class RestZenStore(BaseZenStore):
|
|
2513
2519
|
response_body = self.put(
|
2514
2520
|
f"{SERVICE_CONNECTORS}/{str(service_connector_id)}{SERVICE_CONNECTOR_VERIFY}",
|
2515
2521
|
params=params,
|
2522
|
+
timeout=max(
|
2523
|
+
self.config.http_timeout,
|
2524
|
+
SERVICE_CONNECTOR_VERIFY_REQUEST_TIMEOUT,
|
2525
|
+
),
|
2516
2526
|
)
|
2517
2527
|
|
2518
2528
|
resources = ServiceConnectorResourcesModel.model_validate(
|
@@ -2859,13 +2869,13 @@ class RestZenStore(BaseZenStore):
|
|
2859
2869
|
)
|
2860
2870
|
return StackDeploymentInfo.model_validate(body)
|
2861
2871
|
|
2862
|
-
def
|
2872
|
+
def get_stack_deployment_config(
|
2863
2873
|
self,
|
2864
2874
|
provider: StackDeploymentProvider,
|
2865
2875
|
stack_name: str,
|
2866
2876
|
location: Optional[str] = None,
|
2867
|
-
) ->
|
2868
|
-
"""Return the URL to deploy the ZenML stack
|
2877
|
+
) -> StackDeploymentConfig:
|
2878
|
+
"""Return the cloud provider console URL and configuration needed to deploy the ZenML stack.
|
2869
2879
|
|
2870
2880
|
Args:
|
2871
2881
|
provider: The stack deployment provider.
|
@@ -2873,11 +2883,8 @@ class RestZenStore(BaseZenStore):
|
|
2873
2883
|
location: The location where the stack should be deployed.
|
2874
2884
|
|
2875
2885
|
Returns:
|
2876
|
-
The
|
2877
|
-
|
2878
|
-
|
2879
|
-
Raises:
|
2880
|
-
ValueError: If the response body is not as expected.
|
2886
|
+
The cloud provider console URL and configuration needed to deploy
|
2887
|
+
the ZenML stack to the specified cloud provider.
|
2881
2888
|
"""
|
2882
2889
|
params = {
|
2883
2890
|
"provider": provider.value,
|
@@ -2885,14 +2892,8 @@ class RestZenStore(BaseZenStore):
|
|
2885
2892
|
}
|
2886
2893
|
if location:
|
2887
2894
|
params["location"] = location
|
2888
|
-
body = self.get(f"{STACK_DEPLOYMENT}{
|
2889
|
-
|
2890
|
-
if not isinstance(body, list) or len(body) != 2:
|
2891
|
-
raise ValueError(
|
2892
|
-
"Bad response body received from the stack deployment URL "
|
2893
|
-
"endpoint."
|
2894
|
-
)
|
2895
|
-
return body[0], body[1]
|
2895
|
+
body = self.get(f"{STACK_DEPLOYMENT}{CONFIG}", params=params)
|
2896
|
+
return StackDeploymentConfig.model_validate(body)
|
2896
2897
|
|
2897
2898
|
def get_stack_deployment_stack(
|
2898
2899
|
self,
|
@@ -4081,6 +4082,7 @@ class RestZenStore(BaseZenStore):
|
|
4081
4082
|
method: str,
|
4082
4083
|
url: str,
|
4083
4084
|
params: Optional[Dict[str, Any]] = None,
|
4085
|
+
timeout: Optional[int] = None,
|
4084
4086
|
**kwargs: Any,
|
4085
4087
|
) -> Json:
|
4086
4088
|
"""Make a request to the REST API.
|
@@ -4089,6 +4091,7 @@ class RestZenStore(BaseZenStore):
|
|
4089
4091
|
method: The HTTP method to use.
|
4090
4092
|
url: The URL to request.
|
4091
4093
|
params: The query parameters to pass to the endpoint.
|
4094
|
+
timeout: The request timeout in seconds.
|
4092
4095
|
kwargs: Additional keyword arguments to pass to the request.
|
4093
4096
|
|
4094
4097
|
Returns:
|
@@ -4111,7 +4114,7 @@ class RestZenStore(BaseZenStore):
|
|
4111
4114
|
url,
|
4112
4115
|
params=params,
|
4113
4116
|
verify=self.config.verify_ssl,
|
4114
|
-
timeout=self.config.http_timeout,
|
4117
|
+
timeout=timeout or self.config.http_timeout,
|
4115
4118
|
**kwargs,
|
4116
4119
|
)
|
4117
4120
|
)
|
@@ -4140,13 +4143,18 @@ class RestZenStore(BaseZenStore):
|
|
4140
4143
|
raise
|
4141
4144
|
|
4142
4145
|
def get(
|
4143
|
-
self,
|
4146
|
+
self,
|
4147
|
+
path: str,
|
4148
|
+
params: Optional[Dict[str, Any]] = None,
|
4149
|
+
timeout: Optional[int] = None,
|
4150
|
+
**kwargs: Any,
|
4144
4151
|
) -> Json:
|
4145
4152
|
"""Make a GET request to the given endpoint path.
|
4146
4153
|
|
4147
4154
|
Args:
|
4148
4155
|
path: The path to the endpoint.
|
4149
4156
|
params: The query parameters to pass to the endpoint.
|
4157
|
+
timeout: The request timeout in seconds.
|
4150
4158
|
kwargs: Additional keyword arguments to pass to the request.
|
4151
4159
|
|
4152
4160
|
Returns:
|
@@ -4154,17 +4162,26 @@ class RestZenStore(BaseZenStore):
|
|
4154
4162
|
"""
|
4155
4163
|
logger.debug(f"Sending GET request to {path}...")
|
4156
4164
|
return self._request(
|
4157
|
-
"GET",
|
4165
|
+
"GET",
|
4166
|
+
self.url + API + VERSION_1 + path,
|
4167
|
+
params=params,
|
4168
|
+
timeout=timeout,
|
4169
|
+
**kwargs,
|
4158
4170
|
)
|
4159
4171
|
|
4160
4172
|
def delete(
|
4161
|
-
self,
|
4173
|
+
self,
|
4174
|
+
path: str,
|
4175
|
+
params: Optional[Dict[str, Any]] = None,
|
4176
|
+
timeout: Optional[int] = None,
|
4177
|
+
**kwargs: Any,
|
4162
4178
|
) -> Json:
|
4163
4179
|
"""Make a DELETE request to the given endpoint path.
|
4164
4180
|
|
4165
4181
|
Args:
|
4166
4182
|
path: The path to the endpoint.
|
4167
4183
|
params: The query parameters to pass to the endpoint.
|
4184
|
+
timeout: The request timeout in seconds.
|
4168
4185
|
kwargs: Additional keyword arguments to pass to the request.
|
4169
4186
|
|
4170
4187
|
Returns:
|
@@ -4175,6 +4192,7 @@ class RestZenStore(BaseZenStore):
|
|
4175
4192
|
"DELETE",
|
4176
4193
|
self.url + API + VERSION_1 + path,
|
4177
4194
|
params=params,
|
4195
|
+
timeout=timeout,
|
4178
4196
|
**kwargs,
|
4179
4197
|
)
|
4180
4198
|
|
@@ -4183,6 +4201,7 @@ class RestZenStore(BaseZenStore):
|
|
4183
4201
|
path: str,
|
4184
4202
|
body: BaseModel,
|
4185
4203
|
params: Optional[Dict[str, Any]] = None,
|
4204
|
+
timeout: Optional[int] = None,
|
4186
4205
|
**kwargs: Any,
|
4187
4206
|
) -> Json:
|
4188
4207
|
"""Make a POST request to the given endpoint path.
|
@@ -4191,6 +4210,7 @@ class RestZenStore(BaseZenStore):
|
|
4191
4210
|
path: The path to the endpoint.
|
4192
4211
|
body: The body to send.
|
4193
4212
|
params: The query parameters to pass to the endpoint.
|
4213
|
+
timeout: The request timeout in seconds.
|
4194
4214
|
kwargs: Additional keyword arguments to pass to the request.
|
4195
4215
|
|
4196
4216
|
Returns:
|
@@ -4202,6 +4222,7 @@ class RestZenStore(BaseZenStore):
|
|
4202
4222
|
self.url + API + VERSION_1 + path,
|
4203
4223
|
data=body.model_dump_json(),
|
4204
4224
|
params=params,
|
4225
|
+
timeout=timeout,
|
4205
4226
|
**kwargs,
|
4206
4227
|
)
|
4207
4228
|
|
@@ -4210,6 +4231,7 @@ class RestZenStore(BaseZenStore):
|
|
4210
4231
|
path: str,
|
4211
4232
|
body: Optional[BaseModel] = None,
|
4212
4233
|
params: Optional[Dict[str, Any]] = None,
|
4234
|
+
timeout: Optional[int] = None,
|
4213
4235
|
**kwargs: Any,
|
4214
4236
|
) -> Json:
|
4215
4237
|
"""Make a PUT request to the given endpoint path.
|
@@ -4218,6 +4240,7 @@ class RestZenStore(BaseZenStore):
|
|
4218
4240
|
path: The path to the endpoint.
|
4219
4241
|
body: The body to send.
|
4220
4242
|
params: The query parameters to pass to the endpoint.
|
4243
|
+
timeout: The request timeout in seconds.
|
4221
4244
|
kwargs: Additional keyword arguments to pass to the request.
|
4222
4245
|
|
4223
4246
|
Returns:
|
@@ -4230,6 +4253,7 @@ class RestZenStore(BaseZenStore):
|
|
4230
4253
|
self.url + API + VERSION_1 + path,
|
4231
4254
|
data=data,
|
4232
4255
|
params=params,
|
4256
|
+
timeout=timeout,
|
4233
4257
|
**kwargs,
|
4234
4258
|
)
|
4235
4259
|
|
@@ -15,7 +15,7 @@
|
|
15
15
|
|
16
16
|
import json
|
17
17
|
from datetime import datetime
|
18
|
-
from typing import Any, Optional
|
18
|
+
from typing import Any, Optional, Set
|
19
19
|
from uuid import UUID
|
20
20
|
|
21
21
|
from sqlmodel import Field, SQLModel
|
@@ -59,16 +59,33 @@ class ServerSettingsSchema(SQLModel, table=True):
|
|
59
59
|
for field, value in settings_update.model_dump(
|
60
60
|
exclude_unset=True
|
61
61
|
).items():
|
62
|
-
if field
|
63
|
-
if value is not None:
|
64
|
-
self.onboarding_state = json.dumps(value)
|
65
|
-
elif hasattr(self, field):
|
62
|
+
if hasattr(self, field):
|
66
63
|
setattr(self, field, value)
|
67
64
|
|
68
65
|
self.updated = datetime.utcnow()
|
69
66
|
|
70
67
|
return self
|
71
68
|
|
69
|
+
def update_onboarding_state(
|
70
|
+
self, completed_steps: Set[str]
|
71
|
+
) -> "ServerSettingsSchema":
|
72
|
+
"""Update the onboarding state.
|
73
|
+
|
74
|
+
Args:
|
75
|
+
completed_steps: Newly completed onboarding steps.
|
76
|
+
|
77
|
+
Returns:
|
78
|
+
The updated schema.
|
79
|
+
"""
|
80
|
+
old_state = set(
|
81
|
+
json.loads(self.onboarding_state) if self.onboarding_state else []
|
82
|
+
)
|
83
|
+
new_state = old_state.union(completed_steps)
|
84
|
+
self.onboarding_state = json.dumps(list(new_state))
|
85
|
+
self.updated = datetime.utcnow()
|
86
|
+
|
87
|
+
return self
|
88
|
+
|
72
89
|
def to_model(
|
73
90
|
self,
|
74
91
|
include_metadata: bool = False,
|
@@ -82,7 +99,6 @@ class ServerSettingsSchema(SQLModel, table=True):
|
|
82
99
|
include_resources: Whether the resources will be filled.
|
83
100
|
**kwargs: Keyword arguments to allow schema specific logic
|
84
101
|
|
85
|
-
|
86
102
|
Returns:
|
87
103
|
The created `SettingsResponse`.
|
88
104
|
"""
|
@@ -101,11 +117,7 @@ class ServerSettingsSchema(SQLModel, table=True):
|
|
101
117
|
resources = None
|
102
118
|
|
103
119
|
if include_metadata:
|
104
|
-
metadata = ServerSettingsResponseMetadata(
|
105
|
-
onboarding_state=json.loads(self.onboarding_state)
|
106
|
-
if self.onboarding_state
|
107
|
-
else {},
|
108
|
-
)
|
120
|
+
metadata = ServerSettingsResponseMetadata()
|
109
121
|
|
110
122
|
if include_resources:
|
111
123
|
resources = ServerSettingsResponseResources()
|
@@ -33,6 +33,7 @@ from typing import (
|
|
33
33
|
NoReturn,
|
34
34
|
Optional,
|
35
35
|
Sequence,
|
36
|
+
Set,
|
36
37
|
Tuple,
|
37
38
|
Type,
|
38
39
|
TypeVar,
|
@@ -107,6 +108,7 @@ from zenml.enums import (
|
|
107
108
|
ExecutionStatus,
|
108
109
|
LoggingLevels,
|
109
110
|
ModelStages,
|
111
|
+
OnboardingStep,
|
110
112
|
SecretScope,
|
111
113
|
SecretsStoreType,
|
112
114
|
SorterOps,
|
@@ -245,6 +247,7 @@ from zenml.models import (
|
|
245
247
|
ServiceRequest,
|
246
248
|
ServiceResponse,
|
247
249
|
ServiceUpdate,
|
250
|
+
StackDeploymentConfig,
|
248
251
|
StackDeploymentInfo,
|
249
252
|
StackFilter,
|
250
253
|
StackRequest,
|
@@ -796,6 +799,7 @@ class SqlZenStore(BaseZenStore):
|
|
796
799
|
_secrets_store: Optional[BaseSecretsStore] = None
|
797
800
|
_backup_secrets_store: Optional[BaseSecretsStore] = None
|
798
801
|
_should_send_user_enriched_events: bool = False
|
802
|
+
_cached_onboarding_state: Optional[Set[str]] = None
|
799
803
|
|
800
804
|
@property
|
801
805
|
def secrets_store(self) -> "BaseSecretsStore":
|
@@ -1552,12 +1556,18 @@ class SqlZenStore(BaseZenStore):
|
|
1552
1556
|
revisions_afterwards = self.alembic.current_revisions()
|
1553
1557
|
|
1554
1558
|
if current_revisions != revisions_afterwards:
|
1555
|
-
|
1556
|
-
current_revisions
|
1557
|
-
|
1558
|
-
|
1559
|
-
|
1560
|
-
|
1559
|
+
try:
|
1560
|
+
if current_revisions and version.parse(
|
1561
|
+
current_revisions[0]
|
1562
|
+
) < version.parse("0.57.1"):
|
1563
|
+
# We want to send the missing user enriched events for users
|
1564
|
+
# which were created pre 0.57.1 and only on one upgrade
|
1565
|
+
self._should_send_user_enriched_events = True
|
1566
|
+
except version.InvalidVersion:
|
1567
|
+
# This can happen if the database is not currently
|
1568
|
+
# stamped with an official ZenML version (e.g. in
|
1569
|
+
# development environments).
|
1570
|
+
pass
|
1561
1571
|
|
1562
1572
|
self._sync_flavors()
|
1563
1573
|
|
@@ -1681,6 +1691,60 @@ class SqlZenStore(BaseZenStore):
|
|
1681
1691
|
|
1682
1692
|
return settings.to_model(include_metadata=True)
|
1683
1693
|
|
1694
|
+
def get_onboarding_state(self) -> List[str]:
|
1695
|
+
"""Get the server onboarding state.
|
1696
|
+
|
1697
|
+
Returns:
|
1698
|
+
The server onboarding state.
|
1699
|
+
"""
|
1700
|
+
with Session(self.engine) as session:
|
1701
|
+
settings = self._get_server_settings(session=session)
|
1702
|
+
if settings.onboarding_state:
|
1703
|
+
self._cached_onboarding_state = set(
|
1704
|
+
json.loads(settings.onboarding_state)
|
1705
|
+
)
|
1706
|
+
return list(self._cached_onboarding_state)
|
1707
|
+
else:
|
1708
|
+
return []
|
1709
|
+
|
1710
|
+
def _update_onboarding_state(
|
1711
|
+
self, completed_steps: Set[str], session: Session
|
1712
|
+
) -> None:
|
1713
|
+
"""Update the server onboarding state.
|
1714
|
+
|
1715
|
+
Args:
|
1716
|
+
completed_steps: Newly completed onboarding steps.
|
1717
|
+
session: DB session.
|
1718
|
+
"""
|
1719
|
+
if self._cached_onboarding_state and completed_steps.issubset(
|
1720
|
+
self._cached_onboarding_state
|
1721
|
+
):
|
1722
|
+
# All the onboarding steps are already completed, no need to query
|
1723
|
+
# the DB
|
1724
|
+
return
|
1725
|
+
|
1726
|
+
settings = self._get_server_settings(session=session)
|
1727
|
+
settings.update_onboarding_state(completed_steps=completed_steps)
|
1728
|
+
session.add(settings)
|
1729
|
+
session.commit()
|
1730
|
+
session.refresh(settings)
|
1731
|
+
|
1732
|
+
assert settings.onboarding_state
|
1733
|
+
self._cached_onboarding_state = set(
|
1734
|
+
json.loads(settings.onboarding_state)
|
1735
|
+
)
|
1736
|
+
|
1737
|
+
def update_onboarding_state(self, completed_steps: Set[str]) -> None:
|
1738
|
+
"""Update the server onboarding state.
|
1739
|
+
|
1740
|
+
Args:
|
1741
|
+
completed_steps: Newly completed onboarding steps.
|
1742
|
+
"""
|
1743
|
+
with Session(self.engine) as session:
|
1744
|
+
self._update_onboarding_state(
|
1745
|
+
completed_steps=completed_steps, session=session
|
1746
|
+
)
|
1747
|
+
|
1684
1748
|
def activate_server(
|
1685
1749
|
self, request: ServerActivationRequest
|
1686
1750
|
) -> Optional[UserResponse]:
|
@@ -6149,6 +6213,7 @@ class SqlZenStore(BaseZenStore):
|
|
6149
6213
|
|
6150
6214
|
connector = new_service_connector.to_model(include_metadata=True)
|
6151
6215
|
self._populate_connector_type(connector)
|
6216
|
+
|
6152
6217
|
return connector
|
6153
6218
|
|
6154
6219
|
def get_service_connector(
|
@@ -6924,6 +6989,16 @@ class SqlZenStore(BaseZenStore):
|
|
6924
6989
|
session.commit()
|
6925
6990
|
session.refresh(new_stack_schema)
|
6926
6991
|
|
6992
|
+
for component in defined_components:
|
6993
|
+
if component.type == StackComponentType.ORCHESTRATOR:
|
6994
|
+
if component.flavor not in {"local", "local_docker"}:
|
6995
|
+
self._update_onboarding_state(
|
6996
|
+
completed_steps={
|
6997
|
+
OnboardingStep.STACK_WITH_REMOTE_ORCHESTRATOR_CREATED
|
6998
|
+
},
|
6999
|
+
session=session,
|
7000
|
+
)
|
7001
|
+
|
6927
7002
|
return new_stack_schema.to_model(include_metadata=True)
|
6928
7003
|
|
6929
7004
|
def create_full_stack(self, full_stack: FullStackRequest) -> StackResponse:
|
@@ -7000,7 +7075,6 @@ class SqlZenStore(BaseZenStore):
|
|
7000
7075
|
|
7001
7076
|
# Stack Components
|
7002
7077
|
components_mapping: Dict[StackComponentType, List[UUID]] = {}
|
7003
|
-
|
7004
7078
|
for (
|
7005
7079
|
component_type,
|
7006
7080
|
component_info,
|
@@ -7012,6 +7086,20 @@ class SqlZenStore(BaseZenStore):
|
|
7012
7086
|
)
|
7013
7087
|
# Create a new component
|
7014
7088
|
else:
|
7089
|
+
flavor_list = self.list_flavors(
|
7090
|
+
flavor_filter_model=FlavorFilter(
|
7091
|
+
name=component_info.flavor,
|
7092
|
+
type=component_type,
|
7093
|
+
)
|
7094
|
+
)
|
7095
|
+
if not len(flavor_list):
|
7096
|
+
raise ValueError(
|
7097
|
+
f"Flavor '{component_info.flavor}' not found "
|
7098
|
+
f"for component type '{component_type}'."
|
7099
|
+
)
|
7100
|
+
|
7101
|
+
flavor_model = flavor_list[0]
|
7102
|
+
|
7015
7103
|
component_name = full_stack.name
|
7016
7104
|
while True:
|
7017
7105
|
try:
|
@@ -7039,15 +7127,6 @@ class SqlZenStore(BaseZenStore):
|
|
7039
7127
|
service_connector = service_connectors[
|
7040
7128
|
component_info.service_connector_index
|
7041
7129
|
]
|
7042
|
-
flavor_list = self.list_flavors(
|
7043
|
-
flavor_filter_model=FlavorFilter(
|
7044
|
-
name=component_info.flavor,
|
7045
|
-
type=component_type,
|
7046
|
-
)
|
7047
|
-
)
|
7048
|
-
assert len(flavor_list) == 1
|
7049
|
-
|
7050
|
-
flavor_model = flavor_list[0]
|
7051
7130
|
|
7052
7131
|
requirements = flavor_model.connector_requirements
|
7053
7132
|
|
@@ -7434,13 +7513,13 @@ class SqlZenStore(BaseZenStore):
|
|
7434
7513
|
"Stack deployments are not supported by local ZenML deployments."
|
7435
7514
|
)
|
7436
7515
|
|
7437
|
-
def
|
7516
|
+
def get_stack_deployment_config(
|
7438
7517
|
self,
|
7439
7518
|
provider: StackDeploymentProvider,
|
7440
7519
|
stack_name: str,
|
7441
7520
|
location: Optional[str] = None,
|
7442
|
-
) ->
|
7443
|
-
"""Return the URL to deploy the ZenML stack
|
7521
|
+
) -> StackDeploymentConfig:
|
7522
|
+
"""Return the cloud provider console URL and configuration needed to deploy the ZenML stack.
|
7444
7523
|
|
7445
7524
|
Args:
|
7446
7525
|
provider: The stack deployment provider.
|
@@ -7954,6 +8033,25 @@ class SqlZenStore(BaseZenStore):
|
|
7954
8033
|
"duration_seconds": duration_seconds,
|
7955
8034
|
**stack_metadata,
|
7956
8035
|
}
|
8036
|
+
|
8037
|
+
completed_onboarding_steps: Set[str] = {
|
8038
|
+
OnboardingStep.PIPELINE_RUN,
|
8039
|
+
OnboardingStep.STARTER_SETUP_COMPLETED,
|
8040
|
+
}
|
8041
|
+
if stack_metadata["orchestrator"] not in {
|
8042
|
+
"local",
|
8043
|
+
"local_docker",
|
8044
|
+
}:
|
8045
|
+
completed_onboarding_steps.update(
|
8046
|
+
{
|
8047
|
+
OnboardingStep.PIPELINE_RUN_WITH_REMOTE_ORCHESTRATOR,
|
8048
|
+
OnboardingStep.PRODUCTION_SETUP_COMPLETED,
|
8049
|
+
}
|
8050
|
+
)
|
8051
|
+
|
8052
|
+
self._update_onboarding_state(
|
8053
|
+
completed_steps=completed_onboarding_steps, session=session
|
8054
|
+
)
|
7957
8055
|
pipeline_run.update(run_update)
|
7958
8056
|
session.add(pipeline_run)
|
7959
8057
|
|