From 0bba28a458f9e7219cc4e55be959f9c3962e99d9 Mon Sep 17 00:00:00 2001 From: peterschmidt85 Date: Fri, 12 Sep 2025 13:01:21 +0200 Subject: [PATCH 1/3] [Internal] Extend project creation with a customizable config that can be used in hooks --- src/dstack/_internal/server/services/projects.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/dstack/_internal/server/services/projects.py b/src/dstack/_internal/server/services/projects.py index f5b5acd407..6d8540abf6 100644 --- a/src/dstack/_internal/server/services/projects.py +++ b/src/dstack/_internal/server/services/projects.py @@ -13,6 +13,7 @@ ) from dstack._internal.core.backends.models import BackendInfo from dstack._internal.core.errors import ForbiddenError, ResourceExistsError, ServerClientError +from dstack._internal.core.models.common import CoreModel from dstack._internal.core.models.projects import Member, MemberPermissions, Project from dstack._internal.core.models.runs import RunStatus from dstack._internal.core.models.users import GlobalRole, ProjectRole @@ -115,11 +116,20 @@ async def get_project_by_name( return project_model_to_project(project_model) +class ProjectConfig(CoreModel): + """ + This class can be inherited to extend the project creation configuration passed to the hooks. + """ + + pass + + async def create_project( session: AsyncSession, user: UserModel, project_name: str, is_public: bool = False, + config: Optional[ProjectConfig] = None, ) -> Project: user_permissions = users.get_user_permissions(user) if not user_permissions.can_create_projects: @@ -147,7 +157,7 @@ async def create_project( session=session, project_name=project_name ) for hook in _CREATE_PROJECT_HOOKS: - await hook(session, project_model) + await hook(session, project_model, config) # a hook may change project session.expire(project_model) project_model = await get_project_model_by_name_or_error( @@ -609,7 +619,9 @@ def get_member_permissions(member_model: MemberModel) -> MemberPermissions: _CREATE_PROJECT_HOOKS = [] -def register_create_project_hook(func: Callable[[AsyncSession, ProjectModel], Awaitable[None]]): +def register_create_project_hook( + func: Callable[[AsyncSession, ProjectModel, Optional[ProjectConfig]], Awaitable[None]], +): _CREATE_PROJECT_HOOKS.append(func) From 639b8616595b4a5ebe23a99fdebb872671e81f70 Mon Sep 17 00:00:00 2001 From: peterschmidt85 Date: Mon, 15 Sep 2025 09:07:10 +0200 Subject: [PATCH 2/3] [Internal] Extend project creation with a customizable config that can be used in hooks Review feedback --- src/dstack/_internal/server/services/projects.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dstack/_internal/server/services/projects.py b/src/dstack/_internal/server/services/projects.py index 6d8540abf6..25eb8edb06 100644 --- a/src/dstack/_internal/server/services/projects.py +++ b/src/dstack/_internal/server/services/projects.py @@ -116,7 +116,7 @@ async def get_project_by_name( return project_model_to_project(project_model) -class ProjectConfig(CoreModel): +class ProjectHookConfig(CoreModel): """ This class can be inherited to extend the project creation configuration passed to the hooks. """ @@ -129,7 +129,7 @@ async def create_project( user: UserModel, project_name: str, is_public: bool = False, - config: Optional[ProjectConfig] = None, + config: Optional[ProjectHookConfig] = None, ) -> Project: user_permissions = users.get_user_permissions(user) if not user_permissions.can_create_projects: @@ -620,7 +620,7 @@ def get_member_permissions(member_model: MemberModel) -> MemberPermissions: def register_create_project_hook( - func: Callable[[AsyncSession, ProjectModel, Optional[ProjectConfig]], Awaitable[None]], + func: Callable[[AsyncSession, ProjectModel, Optional[ProjectHookConfig]], Awaitable[None]], ): _CREATE_PROJECT_HOOKS.append(func) From 92c2c4b1df7821f08b4dacf99cbe7da0f607da35 Mon Sep 17 00:00:00 2001 From: peterschmidt85 Date: Mon, 15 Sep 2025 09:28:09 +0200 Subject: [PATCH 3/3] [Internal] Extend project creation with a customizable config that can be used in hooks Review feedback (part 2) --- src/dstack/_internal/core/models/projects.py | 8 ++++++++ src/dstack/_internal/server/services/projects.py | 16 ++++++---------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/dstack/_internal/core/models/projects.py b/src/dstack/_internal/core/models/projects.py index c6ab64c658..9748ece1ae 100644 --- a/src/dstack/_internal/core/models/projects.py +++ b/src/dstack/_internal/core/models/projects.py @@ -26,3 +26,11 @@ class Project(CoreModel): backends: List[BackendInfo] members: List[Member] is_public: bool = False + + +class ProjectHookConfig(CoreModel): + """ + This class can be inherited to extend the project creation configuration passed to the hooks. + """ + + pass diff --git a/src/dstack/_internal/server/services/projects.py b/src/dstack/_internal/server/services/projects.py index 25eb8edb06..330fcceb49 100644 --- a/src/dstack/_internal/server/services/projects.py +++ b/src/dstack/_internal/server/services/projects.py @@ -13,8 +13,12 @@ ) from dstack._internal.core.backends.models import BackendInfo from dstack._internal.core.errors import ForbiddenError, ResourceExistsError, ServerClientError -from dstack._internal.core.models.common import CoreModel -from dstack._internal.core.models.projects import Member, MemberPermissions, Project +from dstack._internal.core.models.projects import ( + Member, + MemberPermissions, + Project, + ProjectHookConfig, +) from dstack._internal.core.models.runs import RunStatus from dstack._internal.core.models.users import GlobalRole, ProjectRole from dstack._internal.server.models import ( @@ -116,14 +120,6 @@ async def get_project_by_name( return project_model_to_project(project_model) -class ProjectHookConfig(CoreModel): - """ - This class can be inherited to extend the project creation configuration passed to the hooks. - """ - - pass - - async def create_project( session: AsyncSession, user: UserModel,