zenml-nightly 0.68.0.dev20241027__py3-none-any.whl → 0.68.1.dev20241101__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- README.md +17 -11
- RELEASE_NOTES.md +9 -0
- zenml/VERSION +1 -1
- zenml/__init__.py +1 -1
- zenml/analytics/context.py +16 -1
- zenml/analytics/utils.py +18 -7
- zenml/artifacts/utils.py +40 -216
- zenml/cli/__init__.py +63 -90
- zenml/cli/base.py +3 -3
- zenml/cli/login.py +951 -0
- zenml/cli/server.py +462 -353
- zenml/cli/service_accounts.py +4 -4
- zenml/cli/stack.py +77 -2
- zenml/cli/stack_components.py +5 -16
- zenml/cli/user_management.py +0 -12
- zenml/cli/utils.py +24 -77
- zenml/client.py +46 -14
- zenml/config/compiler.py +1 -0
- zenml/config/global_config.py +9 -0
- zenml/config/pipeline_configurations.py +2 -1
- zenml/config/pipeline_run_configuration.py +2 -1
- zenml/constants.py +3 -9
- zenml/enums.py +1 -1
- zenml/exceptions.py +11 -0
- zenml/integrations/github/code_repositories/github_code_repository.py +1 -1
- zenml/login/__init__.py +16 -0
- zenml/login/credentials.py +346 -0
- zenml/login/credentials_store.py +603 -0
- zenml/login/pro/__init__.py +16 -0
- zenml/login/pro/client.py +496 -0
- zenml/login/pro/constants.py +34 -0
- zenml/login/pro/models.py +25 -0
- zenml/login/pro/organization/__init__.py +14 -0
- zenml/login/pro/organization/client.py +79 -0
- zenml/login/pro/organization/models.py +32 -0
- zenml/login/pro/tenant/__init__.py +14 -0
- zenml/login/pro/tenant/client.py +92 -0
- zenml/login/pro/tenant/models.py +174 -0
- zenml/login/pro/utils.py +121 -0
- zenml/{cli → login}/web_login.py +64 -28
- zenml/materializers/base_materializer.py +43 -9
- zenml/materializers/built_in_materializer.py +1 -1
- zenml/metadata/metadata_types.py +49 -0
- zenml/model/model.py +0 -38
- zenml/models/__init__.py +3 -0
- zenml/models/v2/base/base.py +12 -8
- zenml/models/v2/base/filter.py +9 -0
- zenml/models/v2/core/artifact_version.py +49 -10
- zenml/models/v2/core/component.py +54 -19
- zenml/models/v2/core/flavor.py +13 -13
- zenml/models/v2/core/model.py +3 -1
- zenml/models/v2/core/model_version.py +3 -5
- zenml/models/v2/core/model_version_artifact.py +3 -1
- zenml/models/v2/core/model_version_pipeline_run.py +3 -1
- zenml/models/v2/core/pipeline.py +3 -1
- zenml/models/v2/core/pipeline_run.py +23 -1
- zenml/models/v2/core/run_template.py +3 -1
- zenml/models/v2/core/stack.py +7 -3
- zenml/models/v2/core/step_run.py +43 -2
- zenml/models/v2/misc/auth_models.py +11 -2
- zenml/models/v2/misc/server_models.py +2 -0
- zenml/orchestrators/base_orchestrator.py +8 -4
- zenml/orchestrators/step_launcher.py +1 -0
- zenml/orchestrators/step_run_utils.py +10 -2
- zenml/orchestrators/step_runner.py +67 -55
- zenml/orchestrators/utils.py +45 -22
- zenml/pipelines/pipeline_decorator.py +5 -0
- zenml/pipelines/pipeline_definition.py +206 -160
- zenml/pipelines/run_utils.py +11 -10
- zenml/services/local/local_daemon_entrypoint.py +4 -4
- zenml/services/service.py +2 -2
- zenml/stack/stack.py +2 -6
- zenml/stack/stack_component.py +2 -7
- zenml/stack/utils.py +26 -14
- zenml/steps/base_step.py +8 -2
- zenml/steps/step_context.py +0 -3
- zenml/steps/step_invocation.py +14 -5
- zenml/steps/utils.py +1 -0
- zenml/utils/materializer_utils.py +1 -1
- zenml/utils/requirements_utils.py +71 -0
- zenml/utils/singleton.py +15 -3
- zenml/utils/source_utils.py +39 -2
- zenml/utils/visualization_utils.py +1 -1
- zenml/zen_server/auth.py +44 -39
- zenml/zen_server/deploy/__init__.py +7 -7
- zenml/zen_server/deploy/base_provider.py +46 -73
- zenml/zen_server/deploy/{local → daemon}/__init__.py +3 -3
- zenml/zen_server/deploy/{local/local_provider.py → daemon/daemon_provider.py} +44 -63
- zenml/zen_server/deploy/{local/local_zen_server.py → daemon/daemon_zen_server.py} +50 -22
- zenml/zen_server/deploy/deployer.py +90 -171
- zenml/zen_server/deploy/deployment.py +20 -12
- zenml/zen_server/deploy/docker/docker_provider.py +9 -28
- zenml/zen_server/deploy/docker/docker_zen_server.py +19 -3
- zenml/zen_server/deploy/helm/Chart.yaml +1 -1
- zenml/zen_server/deploy/helm/README.md +2 -2
- zenml/zen_server/exceptions.py +11 -0
- zenml/zen_server/jwt.py +9 -9
- zenml/zen_server/routers/auth_endpoints.py +30 -8
- zenml/zen_server/routers/stack_components_endpoints.py +1 -1
- zenml/zen_server/routers/workspaces_endpoints.py +1 -1
- zenml/zen_server/template_execution/runner_entrypoint_configuration.py +7 -4
- zenml/zen_server/template_execution/utils.py +6 -61
- zenml/zen_server/utils.py +64 -36
- zenml/zen_stores/base_zen_store.py +4 -49
- zenml/zen_stores/migrations/versions/0.68.1_release.py +23 -0
- zenml/zen_stores/migrations/versions/c22561cbb3a9_add_artifact_unique_constraints.py +86 -0
- zenml/zen_stores/rest_zen_store.py +325 -147
- zenml/zen_stores/schemas/api_key_schemas.py +9 -4
- zenml/zen_stores/schemas/artifact_schemas.py +21 -2
- zenml/zen_stores/schemas/artifact_visualization_schemas.py +1 -1
- zenml/zen_stores/schemas/component_schemas.py +49 -6
- zenml/zen_stores/schemas/device_schemas.py +9 -4
- zenml/zen_stores/schemas/flavor_schemas.py +1 -1
- zenml/zen_stores/schemas/model_schemas.py +1 -1
- zenml/zen_stores/schemas/service_schemas.py +1 -1
- zenml/zen_stores/schemas/step_run_schemas.py +1 -1
- zenml/zen_stores/schemas/trigger_schemas.py +1 -1
- zenml/zen_stores/sql_zen_store.py +393 -140
- zenml/zen_stores/template_utils.py +3 -1
- {zenml_nightly-0.68.0.dev20241027.dist-info → zenml_nightly-0.68.1.dev20241101.dist-info}/METADATA +18 -12
- {zenml_nightly-0.68.0.dev20241027.dist-info → zenml_nightly-0.68.1.dev20241101.dist-info}/RECORD +124 -107
- zenml/api.py +0 -60
- {zenml_nightly-0.68.0.dev20241027.dist-info → zenml_nightly-0.68.1.dev20241101.dist-info}/LICENSE +0 -0
- {zenml_nightly-0.68.0.dev20241027.dist-info → zenml_nightly-0.68.1.dev20241101.dist-info}/WHEEL +0 -0
- {zenml_nightly-0.68.0.dev20241027.dist-info → zenml_nightly-0.68.1.dev20241101.dist-info}/entry_points.txt +0 -0
@@ -21,19 +21,27 @@ from zenml.enums import ServerProviderType
|
|
21
21
|
from zenml.services.service_status import ServiceState
|
22
22
|
|
23
23
|
|
24
|
-
class
|
25
|
-
"""Generic server deployment configuration.
|
24
|
+
class LocalServerDeploymentConfig(BaseModel):
|
25
|
+
"""Generic local server deployment configuration.
|
26
26
|
|
27
|
-
All server deployment configurations should inherit from this class
|
28
|
-
handle extra attributes as provider specific attributes.
|
27
|
+
All local server deployment configurations should inherit from this class
|
28
|
+
and handle extra attributes as provider specific attributes.
|
29
29
|
|
30
30
|
Attributes:
|
31
|
-
name: Name of the server deployment.
|
32
31
|
provider: The server provider type.
|
33
32
|
"""
|
34
33
|
|
35
|
-
name: str
|
36
34
|
provider: ServerProviderType
|
35
|
+
|
36
|
+
@property
|
37
|
+
def url(self) -> Optional[str]:
|
38
|
+
"""Get the configured server URL.
|
39
|
+
|
40
|
+
Returns:
|
41
|
+
The configured server URL.
|
42
|
+
"""
|
43
|
+
return None
|
44
|
+
|
37
45
|
model_config = ConfigDict(
|
38
46
|
# Validate attributes when assigning them. We need to set this in order
|
39
47
|
# to have a mix of mutable and immutable attributes
|
@@ -44,13 +52,13 @@ class ServerDeploymentConfig(BaseModel):
|
|
44
52
|
)
|
45
53
|
|
46
54
|
|
47
|
-
class
|
48
|
-
"""
|
55
|
+
class LocalServerDeploymentStatus(BaseModel):
|
56
|
+
"""Local server deployment status.
|
49
57
|
|
50
58
|
Ideally this should convey the following information:
|
51
59
|
|
52
60
|
* whether the server's deployment is managed by this client (i.e. if
|
53
|
-
the server was deployed with `zenml
|
61
|
+
the server was deployed with `zenml login --local`)
|
54
62
|
* for a managed deployment, the status of the deployment/tear-down, e.g.
|
55
63
|
not deployed, deploying, running, deleting, deployment timeout/error,
|
56
64
|
tear-down timeout/error etc.
|
@@ -72,7 +80,7 @@ class ServerDeploymentStatus(BaseModel):
|
|
72
80
|
ca_crt: Optional[str] = None
|
73
81
|
|
74
82
|
|
75
|
-
class
|
83
|
+
class LocalServerDeployment(BaseModel):
|
76
84
|
"""Server deployment.
|
77
85
|
|
78
86
|
Attributes:
|
@@ -80,8 +88,8 @@ class ServerDeployment(BaseModel):
|
|
80
88
|
status: The server deployment status.
|
81
89
|
"""
|
82
90
|
|
83
|
-
config:
|
84
|
-
status: Optional[
|
91
|
+
config: LocalServerDeploymentConfig
|
92
|
+
status: Optional[LocalServerDeploymentStatus] = None
|
85
93
|
|
86
94
|
@property
|
87
95
|
def is_running(self) -> bool:
|
@@ -14,7 +14,7 @@
|
|
14
14
|
"""Zen Server docker deployer implementation."""
|
15
15
|
|
16
16
|
import shutil
|
17
|
-
from typing import ClassVar,
|
17
|
+
from typing import ClassVar, Optional, Tuple, Type, cast
|
18
18
|
from uuid import uuid4
|
19
19
|
|
20
20
|
from zenml.enums import ServerProviderType
|
@@ -31,7 +31,7 @@ from zenml.services import (
|
|
31
31
|
ServiceEndpointProtocol,
|
32
32
|
)
|
33
33
|
from zenml.zen_server.deploy.base_provider import BaseServerProvider
|
34
|
-
from zenml.zen_server.deploy.deployment import
|
34
|
+
from zenml.zen_server.deploy.deployment import LocalServerDeploymentConfig
|
35
35
|
from zenml.zen_server.deploy.docker.docker_zen_server import (
|
36
36
|
DOCKER_ZENML_SERVER_DEFAULT_TIMEOUT,
|
37
37
|
ZEN_SERVER_HEALTHCHECK_URL_PATH,
|
@@ -47,14 +47,14 @@ class DockerServerProvider(BaseServerProvider):
|
|
47
47
|
"""Docker ZenML server provider."""
|
48
48
|
|
49
49
|
TYPE: ClassVar[ServerProviderType] = ServerProviderType.DOCKER
|
50
|
-
CONFIG_TYPE: ClassVar[Type[
|
50
|
+
CONFIG_TYPE: ClassVar[Type[LocalServerDeploymentConfig]] = (
|
51
51
|
DockerServerDeploymentConfig
|
52
52
|
)
|
53
53
|
|
54
54
|
@classmethod
|
55
55
|
def _get_service_configuration(
|
56
56
|
cls,
|
57
|
-
server_config:
|
57
|
+
server_config: LocalServerDeploymentConfig,
|
58
58
|
) -> Tuple[
|
59
59
|
ServiceConfig,
|
60
60
|
ServiceEndpointConfig,
|
@@ -75,7 +75,7 @@ class DockerServerProvider(BaseServerProvider):
|
|
75
75
|
root_runtime_path=DockerZenServer.config_path(),
|
76
76
|
singleton=True,
|
77
77
|
image=server_config.image,
|
78
|
-
name=
|
78
|
+
name=ServerProviderType.DOCKER.value,
|
79
79
|
server=server_config,
|
80
80
|
),
|
81
81
|
ContainerServiceEndpointConfig(
|
@@ -91,7 +91,7 @@ class DockerServerProvider(BaseServerProvider):
|
|
91
91
|
|
92
92
|
def _create_service(
|
93
93
|
self,
|
94
|
-
config:
|
94
|
+
config: LocalServerDeploymentConfig,
|
95
95
|
timeout: Optional[int] = None,
|
96
96
|
) -> BaseService:
|
97
97
|
"""Create, start and return the docker ZenML server deployment service.
|
@@ -142,7 +142,7 @@ class DockerServerProvider(BaseServerProvider):
|
|
142
142
|
def _update_service(
|
143
143
|
self,
|
144
144
|
service: BaseService,
|
145
|
-
config:
|
145
|
+
config: LocalServerDeploymentConfig,
|
146
146
|
timeout: Optional[int] = None,
|
147
147
|
) -> BaseService:
|
148
148
|
"""Update the docker ZenML server deployment service.
|
@@ -244,12 +244,9 @@ class DockerServerProvider(BaseServerProvider):
|
|
244
244
|
service.stop(timeout)
|
245
245
|
shutil.rmtree(DockerZenServer.config_path())
|
246
246
|
|
247
|
-
def _get_service(self
|
247
|
+
def _get_service(self) -> BaseService:
|
248
248
|
"""Get the docker ZenML server deployment service.
|
249
249
|
|
250
|
-
Args:
|
251
|
-
server_name: The server deployment name.
|
252
|
-
|
253
250
|
Returns:
|
254
251
|
The service instance.
|
255
252
|
|
@@ -260,27 +257,11 @@ class DockerServerProvider(BaseServerProvider):
|
|
260
257
|
if service is None:
|
261
258
|
raise KeyError("The docker ZenML server is not deployed.")
|
262
259
|
|
263
|
-
if service.config.name != server_name:
|
264
|
-
raise KeyError(
|
265
|
-
"The docker ZenML server is deployed but with a different name."
|
266
|
-
)
|
267
|
-
|
268
260
|
return service
|
269
261
|
|
270
|
-
def _list_services(self) -> List[BaseService]:
|
271
|
-
"""Get all service instances for all deployed ZenML servers.
|
272
|
-
|
273
|
-
Returns:
|
274
|
-
A list of service instances.
|
275
|
-
"""
|
276
|
-
service = DockerZenServer.get_service()
|
277
|
-
if service:
|
278
|
-
return [service]
|
279
|
-
return []
|
280
|
-
|
281
262
|
def _get_deployment_config(
|
282
263
|
self, service: BaseService
|
283
|
-
) ->
|
264
|
+
) -> LocalServerDeploymentConfig:
|
284
265
|
"""Recreate the server deployment configuration from a service instance.
|
285
266
|
|
286
267
|
Args:
|
@@ -22,16 +22,20 @@ import zenml
|
|
22
22
|
from zenml.config.global_config import GlobalConfiguration
|
23
23
|
from zenml.config.store_config import StoreConfiguration
|
24
24
|
from zenml.constants import (
|
25
|
+
DEFAULT_LOCAL_SERVICE_IP_ADDRESS,
|
25
26
|
ENV_ZENML_ANALYTICS_OPT_IN,
|
26
27
|
ENV_ZENML_CONFIG_PATH,
|
27
28
|
ENV_ZENML_DISABLE_DATABASE_MIGRATION,
|
28
29
|
ENV_ZENML_LOCAL_STORES_PATH,
|
30
|
+
ENV_ZENML_SERVER,
|
31
|
+
ENV_ZENML_SERVER_AUTH_SCHEME,
|
29
32
|
ENV_ZENML_SERVER_AUTO_ACTIVATE,
|
30
33
|
ENV_ZENML_SERVER_DEPLOYMENT_TYPE,
|
34
|
+
ENV_ZENML_USER_ID,
|
31
35
|
LOCAL_STORES_DIRECTORY_NAME,
|
32
36
|
ZEN_SERVER_ENTRYPOINT,
|
33
37
|
)
|
34
|
-
from zenml.enums import StoreType
|
38
|
+
from zenml.enums import AuthScheme, StoreType
|
35
39
|
from zenml.logger import get_logger
|
36
40
|
from zenml.models import ServerDeploymentType
|
37
41
|
from zenml.services import (
|
@@ -46,7 +50,7 @@ from zenml.services.container.container_service import (
|
|
46
50
|
SERVICE_CONTAINER_PATH,
|
47
51
|
)
|
48
52
|
from zenml.utils.io_utils import get_global_config_directory
|
49
|
-
from zenml.zen_server.deploy.deployment import
|
53
|
+
from zenml.zen_server.deploy.deployment import LocalServerDeploymentConfig
|
50
54
|
|
51
55
|
logger = get_logger(__name__)
|
52
56
|
|
@@ -57,7 +61,7 @@ DOCKER_ZENML_SERVER_DEFAULT_IMAGE = (
|
|
57
61
|
DOCKER_ZENML_SERVER_DEFAULT_TIMEOUT = 60
|
58
62
|
|
59
63
|
|
60
|
-
class DockerServerDeploymentConfig(
|
64
|
+
class DockerServerDeploymentConfig(LocalServerDeploymentConfig):
|
61
65
|
"""Docker server deployment configuration.
|
62
66
|
|
63
67
|
Attributes:
|
@@ -69,6 +73,15 @@ class DockerServerDeploymentConfig(ServerDeploymentConfig):
|
|
69
73
|
image: str = DOCKER_ZENML_SERVER_DEFAULT_IMAGE
|
70
74
|
store: Optional[StoreConfiguration] = None
|
71
75
|
|
76
|
+
@property
|
77
|
+
def url(self) -> Optional[str]:
|
78
|
+
"""Get the configured server URL.
|
79
|
+
|
80
|
+
Returns:
|
81
|
+
The configured server URL.
|
82
|
+
"""
|
83
|
+
return f"http://{DEFAULT_LOCAL_SERVICE_IP_ADDRESS}:{self.port}"
|
84
|
+
|
72
85
|
model_config = ConfigDict(extra="forbid")
|
73
86
|
|
74
87
|
|
@@ -155,12 +168,15 @@ class DockerZenServer(ContainerService):
|
|
155
168
|
gc = GlobalConfiguration()
|
156
169
|
|
157
170
|
cmd, env = super()._get_container_cmd()
|
171
|
+
env[ENV_ZENML_SERVER] = "true"
|
158
172
|
env[ENV_ZENML_CONFIG_PATH] = os.path.join(
|
159
173
|
SERVICE_CONTAINER_PATH,
|
160
174
|
SERVICE_CONTAINER_GLOBAL_CONFIG_DIR,
|
161
175
|
)
|
176
|
+
env[ENV_ZENML_SERVER_AUTH_SCHEME] = AuthScheme.NO_AUTH.value
|
162
177
|
env[ENV_ZENML_SERVER_DEPLOYMENT_TYPE] = ServerDeploymentType.DOCKER
|
163
178
|
env[ENV_ZENML_ANALYTICS_OPT_IN] = str(gc.analytics_opt_in)
|
179
|
+
env[ENV_ZENML_USER_ID] = str(gc.user_id)
|
164
180
|
|
165
181
|
# Set the local stores path to the same path used by the client (mounted
|
166
182
|
# in the container by the super class). This ensures that the server's
|
@@ -20,8 +20,8 @@ ZenML is an open-source MLOps framework designed to help you create robust, main
|
|
20
20
|
To install the ZenML chart directly from Amazon ECR, use the following command:
|
21
21
|
|
22
22
|
```bash
|
23
|
-
# example command for version 0.68.
|
24
|
-
helm install my-zenml oci://public.ecr.aws/zenml/zenml --version 0.68.
|
23
|
+
# example command for version 0.68.1
|
24
|
+
helm install my-zenml oci://public.ecr.aws/zenml/zenml --version 0.68.1
|
25
25
|
```
|
26
26
|
|
27
27
|
Note: Ensure you have OCI support enabled in your Helm client and that you are authenticated with Amazon ECR.
|
zenml/zen_server/exceptions.py
CHANGED
@@ -20,6 +20,7 @@ from pydantic import BaseModel
|
|
20
20
|
|
21
21
|
from zenml.exceptions import (
|
22
22
|
AuthorizationException,
|
23
|
+
CredentialsNotValid,
|
23
24
|
DoesNotExistException,
|
24
25
|
DuplicateRunNameError,
|
25
26
|
EntityExistsError,
|
@@ -78,6 +79,7 @@ REST_API_EXCEPTIONS: List[Tuple[Type[Exception], int]] = [
|
|
78
79
|
# 403 Forbidden
|
79
80
|
(IllegalOperationError, 403),
|
80
81
|
# 401 Unauthorized
|
82
|
+
(CredentialsNotValid, 401),
|
81
83
|
(AuthorizationException, 401),
|
82
84
|
# 402 Payment required
|
83
85
|
(SubscriptionUpgradeRequiredError, 402),
|
@@ -253,4 +255,13 @@ def exception_from_response(
|
|
253
255
|
|
254
256
|
exception = default_exc
|
255
257
|
|
258
|
+
# There is one special case where we want to return a specific exception:
|
259
|
+
# 401 Unauthorized exceptions thrown directly by FastAPI in the course of
|
260
|
+
# authentication are interpreted as AuthorizationException, but we want to
|
261
|
+
# return CredentialsNotValid instead.
|
262
|
+
if response.status_code == 401:
|
263
|
+
if not isinstance(exception(), CredentialsNotValid):
|
264
|
+
if response.headers.get("WWW-Authenticate"):
|
265
|
+
return CredentialsNotValid(exc_msg)
|
266
|
+
|
256
267
|
return exception(exc_msg)
|
zenml/zen_server/jwt.py
CHANGED
@@ -25,7 +25,7 @@ from uuid import UUID
|
|
25
25
|
import jwt
|
26
26
|
from pydantic import BaseModel
|
27
27
|
|
28
|
-
from zenml.exceptions import
|
28
|
+
from zenml.exceptions import CredentialsNotValid
|
29
29
|
from zenml.logger import get_logger
|
30
30
|
from zenml.zen_server.utils import server_config
|
31
31
|
|
@@ -71,7 +71,7 @@ class JWTToken(BaseModel):
|
|
71
71
|
The decoded JWT access token.
|
72
72
|
|
73
73
|
Raises:
|
74
|
-
|
74
|
+
CredentialsNotValid: If the token is invalid.
|
75
75
|
"""
|
76
76
|
config = server_config()
|
77
77
|
|
@@ -87,18 +87,18 @@ class JWTToken(BaseModel):
|
|
87
87
|
)
|
88
88
|
claims = cast(Dict[str, Any], claims_data)
|
89
89
|
except jwt.PyJWTError as e:
|
90
|
-
raise
|
90
|
+
raise CredentialsNotValid(f"Invalid JWT token: {e}") from e
|
91
91
|
|
92
92
|
subject: str = claims.pop("sub", "")
|
93
93
|
if not subject:
|
94
|
-
raise
|
94
|
+
raise CredentialsNotValid(
|
95
95
|
"Invalid JWT token: the subject claim is missing"
|
96
96
|
)
|
97
97
|
|
98
98
|
try:
|
99
99
|
user_id = UUID(subject)
|
100
100
|
except ValueError:
|
101
|
-
raise
|
101
|
+
raise CredentialsNotValid(
|
102
102
|
"Invalid JWT token: the subject claim is not a valid UUID"
|
103
103
|
)
|
104
104
|
|
@@ -107,7 +107,7 @@ class JWTToken(BaseModel):
|
|
107
107
|
try:
|
108
108
|
device_id = UUID(claims.pop("device_id"))
|
109
109
|
except ValueError:
|
110
|
-
raise
|
110
|
+
raise CredentialsNotValid(
|
111
111
|
"Invalid JWT token: the device_id claim is not a valid "
|
112
112
|
"UUID"
|
113
113
|
)
|
@@ -117,7 +117,7 @@ class JWTToken(BaseModel):
|
|
117
117
|
try:
|
118
118
|
api_key_id = UUID(claims.pop("api_key_id"))
|
119
119
|
except ValueError:
|
120
|
-
raise
|
120
|
+
raise CredentialsNotValid(
|
121
121
|
"Invalid JWT token: the api_key_id claim is not a valid "
|
122
122
|
"UUID"
|
123
123
|
)
|
@@ -127,7 +127,7 @@ class JWTToken(BaseModel):
|
|
127
127
|
try:
|
128
128
|
pipeline_id = UUID(claims.pop("pipeline_id"))
|
129
129
|
except ValueError:
|
130
|
-
raise
|
130
|
+
raise CredentialsNotValid(
|
131
131
|
"Invalid JWT token: the pipeline_id claim is not a valid "
|
132
132
|
"UUID"
|
133
133
|
)
|
@@ -137,7 +137,7 @@ class JWTToken(BaseModel):
|
|
137
137
|
try:
|
138
138
|
schedule_id = UUID(claims.pop("schedule_id"))
|
139
139
|
except ValueError:
|
140
|
-
raise
|
140
|
+
raise CredentialsNotValid(
|
141
141
|
"Invalid JWT token: the schedule_id claim is not a valid "
|
142
142
|
"UUID"
|
143
143
|
)
|
@@ -116,6 +116,8 @@ class OAuthLoginRequestForm:
|
|
116
116
|
Raises:
|
117
117
|
HTTPException: If the request is invalid.
|
118
118
|
"""
|
119
|
+
config = server_config()
|
120
|
+
|
119
121
|
if not grant_type:
|
120
122
|
# Detect the grant type from the form data
|
121
123
|
if username is not None:
|
@@ -124,8 +126,21 @@ class OAuthLoginRequestForm:
|
|
124
126
|
self.grant_type = OAuthGrantTypes.ZENML_API_KEY
|
125
127
|
elif device_code:
|
126
128
|
self.grant_type = OAuthGrantTypes.OAUTH_DEVICE_CODE
|
127
|
-
|
129
|
+
elif config.auth_scheme == AuthScheme.EXTERNAL:
|
128
130
|
self.grant_type = OAuthGrantTypes.ZENML_EXTERNAL
|
131
|
+
elif config.auth_scheme in [
|
132
|
+
AuthScheme.OAUTH2_PASSWORD_BEARER,
|
133
|
+
AuthScheme.NO_AUTH,
|
134
|
+
AuthScheme.HTTP_BASIC,
|
135
|
+
]:
|
136
|
+
# For no auth and basic HTTP auth schemes, we also allow the
|
137
|
+
# password grant type to be used for backwards compatibility
|
138
|
+
self.grant_type = OAuthGrantTypes.OAUTH_PASSWORD
|
139
|
+
else:
|
140
|
+
raise HTTPException(
|
141
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
142
|
+
detail="Invalid request: grant type is required.",
|
143
|
+
)
|
129
144
|
else:
|
130
145
|
if grant_type not in OAuthGrantTypes.values():
|
131
146
|
logger.info(
|
@@ -137,10 +152,15 @@ class OAuthLoginRequestForm:
|
|
137
152
|
)
|
138
153
|
self.grant_type = OAuthGrantTypes(grant_type)
|
139
154
|
|
140
|
-
config = server_config()
|
141
|
-
|
142
155
|
if self.grant_type == OAuthGrantTypes.OAUTH_PASSWORD:
|
143
|
-
|
156
|
+
# For the no auth and basic HTTP auth schemes, we also allow the
|
157
|
+
# password grant type to be used for compatibility with other
|
158
|
+
# auth schemes
|
159
|
+
if config.auth_scheme not in [
|
160
|
+
AuthScheme.OAUTH2_PASSWORD_BEARER,
|
161
|
+
AuthScheme.NO_AUTH,
|
162
|
+
AuthScheme.HTTP_BASIC,
|
163
|
+
]:
|
144
164
|
logger.info(
|
145
165
|
f"Request with unsupported grant type: {self.grant_type}"
|
146
166
|
)
|
@@ -280,6 +300,7 @@ def token(
|
|
280
300
|
Raises:
|
281
301
|
ValueError: If the grant type is invalid.
|
282
302
|
"""
|
303
|
+
config = server_config()
|
283
304
|
if auth_form_data.grant_type == OAuthGrantTypes.OAUTH_PASSWORD:
|
284
305
|
auth_context = authenticate_credentials(
|
285
306
|
user_name_or_id=auth_form_data.username,
|
@@ -297,8 +318,6 @@ def token(
|
|
297
318
|
)
|
298
319
|
|
299
320
|
elif auth_form_data.grant_type == OAuthGrantTypes.ZENML_EXTERNAL:
|
300
|
-
config = server_config()
|
301
|
-
|
302
321
|
assert config.external_cookie_name is not None
|
303
322
|
assert config.external_login_url is not None
|
304
323
|
|
@@ -399,8 +418,11 @@ def device_authorization(
|
|
399
418
|
config = server_config()
|
400
419
|
store = zen_store()
|
401
420
|
|
402
|
-
|
403
|
-
|
421
|
+
try:
|
422
|
+
# Use this opportunity to delete expired devices
|
423
|
+
store.delete_expired_authorized_devices()
|
424
|
+
except Exception:
|
425
|
+
logger.exception("Failed to delete expired devices")
|
404
426
|
|
405
427
|
# Fetch additional details about the client from the user-agent header
|
406
428
|
user_agent_header = request.headers.get("User-Agent")
|
@@ -141,7 +141,7 @@ def update_stack_component(
|
|
141
141
|
existing_component = zen_store().get_stack_component(component_id)
|
142
142
|
validate_stack_component_config(
|
143
143
|
configuration_dict=component_update.configuration,
|
144
|
-
|
144
|
+
flavor=existing_component.flavor_name,
|
145
145
|
component_type=existing_component.type,
|
146
146
|
zen_store=zen_store(),
|
147
147
|
# We allow custom flavors to fail import on the server side.
|
@@ -453,7 +453,7 @@ def create_stack_component(
|
|
453
453
|
|
454
454
|
validate_stack_component_config(
|
455
455
|
configuration_dict=component.configuration,
|
456
|
-
|
456
|
+
flavor=component.flavor,
|
457
457
|
component_type=component.type,
|
458
458
|
zen_store=zen_store(),
|
459
459
|
# We allow custom flavors to fail import on the server side.
|
@@ -17,9 +17,7 @@ from zenml.client import Client
|
|
17
17
|
from zenml.entrypoints.base_entrypoint_configuration import (
|
18
18
|
BaseEntrypointConfiguration,
|
19
19
|
)
|
20
|
-
from zenml.pipelines.run_utils import
|
21
|
-
deploy_pipeline,
|
22
|
-
)
|
20
|
+
from zenml.pipelines.run_utils import deploy_pipeline, get_placeholder_run
|
23
21
|
|
24
22
|
|
25
23
|
class RunnerEntrypointConfiguration(BaseEntrypointConfiguration):
|
@@ -36,4 +34,9 @@ class RunnerEntrypointConfiguration(BaseEntrypointConfiguration):
|
|
36
34
|
stack = Client().active_stack
|
37
35
|
assert deployment.stack and stack.id == deployment.stack.id
|
38
36
|
|
39
|
-
|
37
|
+
placeholder_run = get_placeholder_run(deployment_id=deployment.id)
|
38
|
+
deploy_pipeline(
|
39
|
+
deployment=deployment,
|
40
|
+
stack=stack,
|
41
|
+
placeholder_run=placeholder_run,
|
42
|
+
)
|
@@ -3,7 +3,7 @@
|
|
3
3
|
import copy
|
4
4
|
import hashlib
|
5
5
|
import sys
|
6
|
-
from typing import Any, Dict, List, Optional
|
6
|
+
from typing import Any, Dict, List, Optional
|
7
7
|
from uuid import UUID
|
8
8
|
|
9
9
|
from fastapi import BackgroundTasks
|
@@ -22,11 +22,9 @@ from zenml.constants import (
|
|
22
22
|
ENV_ZENML_ACTIVE_WORKSPACE_ID,
|
23
23
|
)
|
24
24
|
from zenml.enums import ExecutionStatus, StackComponentType, StoreType
|
25
|
-
from zenml.integrations.utils import get_integration_for_module
|
26
25
|
from zenml.logger import get_logger
|
27
26
|
from zenml.models import (
|
28
27
|
CodeReferenceRequest,
|
29
|
-
ComponentResponse,
|
30
28
|
FlavorFilter,
|
31
29
|
PipelineDeploymentRequest,
|
32
30
|
PipelineDeploymentResponse,
|
@@ -43,7 +41,7 @@ from zenml.pipelines.run_utils import (
|
|
43
41
|
validate_stack_is_runnable_from_server,
|
44
42
|
)
|
45
43
|
from zenml.stack.flavor import Flavor
|
46
|
-
from zenml.utils import dict_utils, settings_utils
|
44
|
+
from zenml.utils import dict_utils, requirements_utils, settings_utils
|
47
45
|
from zenml.zen_server.auth import AuthContext
|
48
46
|
from zenml.zen_server.template_execution.runner_entrypoint_configuration import (
|
49
47
|
RunnerEntrypointConfiguration,
|
@@ -151,8 +149,8 @@ def run_template(
|
|
151
149
|
assert placeholder_run
|
152
150
|
|
153
151
|
def _task() -> None:
|
154
|
-
pypi_requirements, apt_packages =
|
155
|
-
stack=stack
|
152
|
+
pypi_requirements, apt_packages = (
|
153
|
+
requirements_utils.get_requirements_for_stack(stack=stack)
|
156
154
|
)
|
157
155
|
|
158
156
|
if build.python_version:
|
@@ -248,7 +246,7 @@ def ensure_async_orchestrator(
|
|
248
246
|
"""
|
249
247
|
orchestrator = stack.components[StackComponentType.ORCHESTRATOR][0]
|
250
248
|
flavors = zen_store().list_flavors(
|
251
|
-
FlavorFilter(name=orchestrator.
|
249
|
+
FlavorFilter(name=orchestrator.flavor_name, type=orchestrator.type)
|
252
250
|
)
|
253
251
|
flavor = Flavor.from_model(flavors[0])
|
254
252
|
|
@@ -266,59 +264,6 @@ def ensure_async_orchestrator(
|
|
266
264
|
)
|
267
265
|
|
268
266
|
|
269
|
-
def get_requirements_for_stack(
|
270
|
-
stack: StackResponse,
|
271
|
-
) -> Tuple[List[str], List[str]]:
|
272
|
-
"""Get requirements for a stack model.
|
273
|
-
|
274
|
-
Args:
|
275
|
-
stack: The stack for which to get the requirements.
|
276
|
-
|
277
|
-
Returns:
|
278
|
-
Tuple of PyPI and APT requirements of the stack.
|
279
|
-
"""
|
280
|
-
pypi_requirements: Set[str] = set()
|
281
|
-
apt_packages: Set[str] = set()
|
282
|
-
|
283
|
-
for component_list in stack.components.values():
|
284
|
-
assert len(component_list) == 1
|
285
|
-
component = component_list[0]
|
286
|
-
(
|
287
|
-
component_pypi_requirements,
|
288
|
-
component_apt_packages,
|
289
|
-
) = get_requirements_for_component(component=component)
|
290
|
-
pypi_requirements = pypi_requirements.union(
|
291
|
-
component_pypi_requirements
|
292
|
-
)
|
293
|
-
apt_packages = apt_packages.union(component_apt_packages)
|
294
|
-
|
295
|
-
return sorted(pypi_requirements), sorted(apt_packages)
|
296
|
-
|
297
|
-
|
298
|
-
def get_requirements_for_component(
|
299
|
-
component: ComponentResponse,
|
300
|
-
) -> Tuple[List[str], List[str]]:
|
301
|
-
"""Get requirements for a component model.
|
302
|
-
|
303
|
-
Args:
|
304
|
-
component: The component for which to get the requirements.
|
305
|
-
|
306
|
-
Returns:
|
307
|
-
Tuple of PyPI and APT requirements of the component.
|
308
|
-
"""
|
309
|
-
flavors = zen_store().list_flavors(
|
310
|
-
FlavorFilter(name=component.flavor, type=component.type)
|
311
|
-
)
|
312
|
-
assert len(flavors) == 1
|
313
|
-
flavor_source = flavors[0].source
|
314
|
-
integration = get_integration_for_module(module_name=flavor_source)
|
315
|
-
|
316
|
-
if integration:
|
317
|
-
return integration.get_requirements(), integration.APT_PACKAGES
|
318
|
-
else:
|
319
|
-
return [], []
|
320
|
-
|
321
|
-
|
322
267
|
def generate_image_hash(dockerfile: str) -> str:
|
323
268
|
"""Generate a hash of the Dockerfile.
|
324
269
|
|
@@ -512,7 +457,7 @@ def get_pipeline_run_analytics_metadata(
|
|
512
457
|
own_stack = stack_creator and stack_creator.id == deployment.user.id
|
513
458
|
|
514
459
|
stack_metadata = {
|
515
|
-
component_type.value: component_list[0].
|
460
|
+
component_type.value: component_list[0].flavor_name
|
516
461
|
for component_type, component_list in stack.components.items()
|
517
462
|
}
|
518
463
|
|