Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Add users_to_add and users_to_remove fields to replace user update mode #1705

Draft
wants to merge 33 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
28bf838
revert functioned description and deprecated msg
fregataa Nov 8, 2023
55d74b8
impl user add/delete group mutation
fregataa Nov 8, 2023
b0cd2a4
replace input type of ID from UUID to String
fregataa Nov 8, 2023
e1528e8
keep the modify function alive
fregataa Nov 8, 2023
4f19cbc
change mutation name typo
fregataa Nov 8, 2023
00a9b3e
Merge branch 'main' into fix/refactor-group-graphql
fregataa Nov 14, 2023
175a5fd
Merge branch 'main' into fix/refactor-group-graphql
fregataa Nov 14, 2023
c75db1e
impl user add/remove in ModifyGroup mutation and revert description m…
fregataa Nov 14, 2023
fb68733
Merge branch 'main' into fix/refactor-group-graphql
fregataa Nov 23, 2023
b0c85d3
add news fragment
fregataa Nov 24, 2023
0760819
change added versions of some fields
fregataa Nov 24, 2023
fed0bfe
update news fragment
fregataa Nov 24, 2023
b446de3
Merge branch 'main' into fix/refactor-group-graphql
fregataa Jan 3, 2024
b5c76cc
fix wrong code and change naming
fregataa Jan 3, 2024
84a8f13
Merge branch 'main' into fix/refactor-group-graphql
fregataa Apr 18, 2024
7d8c88f
update version notation
fregataa Apr 18, 2024
20b3787
remove use of deprecated fields
fregataa Apr 18, 2024
c6f9116
little change of error msg
fregataa Apr 18, 2024
999736b
chore: update GraphQL schema dump
fregataa Apr 18, 2024
f62c10e
Merge branch 'main' into fix/refactor-group-graphql
fregataa Apr 23, 2024
a4f5e04
Merge remote-tracking branch 'origin/fix/refactor-group-graphql' into…
fregataa Apr 23, 2024
278d878
update gql fields description
fregataa Apr 23, 2024
0820ce5
chore: update GraphQL schema dump
fregataa Apr 23, 2024
81c8cdb
Merge branch 'main' into fix/refactor-group-graphql
fregataa May 1, 2024
f4ee3bb
Merge remote-tracking branch 'origin/fix/refactor-group-graphql' into…
fregataa May 1, 2024
f39454a
Merge branch 'main' into fix/refactor-group-graphql
fregataa May 8, 2024
4352885
rename fields
fregataa May 8, 2024
a9c05ed
chore: update GraphQL schema dump
fregataa May 8, 2024
4a17f83
Merge branch 'main' into fix/refactor-group-graphql
fregataa May 9, 2024
66a6880
minor update for pythonic code
fregataa May 9, 2024
62c65a7
Merge remote-tracking branch 'origin/fix/refactor-group-graphql' into…
fregataa May 9, 2024
9e068fd
update news fragment
fregataa May 10, 2024
5621850
handle duplicate user associating to group
fregataa May 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion src/ai/backend/manager/models/gql.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,15 @@
from .base import DataLoaderManager, privileged_query, scoped_query
from .domain import CreateDomain, DeleteDomain, Domain, ModifyDomain, PurgeDomain
from .endpoint import Endpoint, EndpointList, EndpointToken, EndpointTokenList
from .group import CreateGroup, DeleteGroup, Group, ModifyGroup, PurgeGroup
from .group import (
AddUserToGroup,
CreateGroup,
DeleteGroup,
DeleteUserFromGroup,
Group,
ModifyGroup,
PurgeGroup,
)
from .image import (
AliasImage,
ClearImages,
Expand Down Expand Up @@ -170,6 +178,8 @@ class Mutations(graphene.ObjectType):
modify_group = ModifyGroup.Field()
delete_group = DeleteGroup.Field()
purge_group = PurgeGroup.Field()
add_user_to_group = AddUserToGroup.Field()
delete_user_from_group = DeleteUserFromGroup.Field()

# super-admin only
create_user = CreateUser.Field()
Expand Down
100 changes: 96 additions & 4 deletions src/ai/backend/manager/models/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,13 +393,40 @@ class ModifyGroupInput(graphene.InputObjectType):
is_active = graphene.Boolean(required=False)
domain_name = graphene.String(required=False)
total_resource_slots = graphene.JSONString(required=False)
user_update_mode = graphene.String(required=False)
user_uuids = graphene.List(lambda: graphene.String, required=False)
user_update_mode = graphene.String(
deprecation_reason=(
"Deprecated since 24.03.0. Recommend to use AddUsersToGroup, DeleteUsersFromGroup"
" mutation"
)
)
user_uuids = graphene.List(
lambda: graphene.String,
deprecation_reason=(
"Deprecated since 24.03.0. Recommend to use AddUsersToGroup, DeleteUsersFromGroup"
" mutation"
),
)
allowed_vfolder_hosts = graphene.JSONString(required=False)
integration_id = graphene.String(required=False)
resource_policy = graphene.String(required=False)


class AddUsersToGroupInput(graphene.InputObjectType):
user_ids = graphene.List(
lambda: graphene.String,
required=True,
description="Added since 24.03.0. ID array of the users to be added to the group.",
)


class DeleteUsersFromGroupInput(graphene.InputObjectType):
user_ids = graphene.List(
lambda: graphene.String,
required=True,
description="Added since 24.03.0. ID array of the users to be deleted from the group.",
)


class CreateGroup(graphene.Mutation):
allowed_roles = (UserRole.ADMIN, UserRole.SUPERADMIN)

Expand Down Expand Up @@ -496,8 +523,6 @@ async def mutate(

async def _do_mutate() -> ModifyGroup:
async with graph_ctx.db.begin() as conn:
# TODO: refactor user addition/removal in groups as separate mutations
# (to apply since 21.09)
if props.user_update_mode == "add":
values = [{"user_id": uuid, "group_id": gid} for uuid in props.user_uuids]
await conn.execute(
Expand Down Expand Up @@ -531,6 +556,73 @@ async def _do_mutate() -> ModifyGroup:
return cls(ok=False, msg=f"unexpected error: {e}", group=None)


class AddUserToGroup(graphene.Mutation):
allowed_roles = (UserRole.ADMIN, UserRole.SUPERADMIN)

class Arguments:
gid = graphene.UUID(required=True)
props = AddUsersToGroupInput(required=True)

ok = graphene.Boolean()
msg = graphene.String()

@classmethod
async def mutate(
cls,
root,
info: graphene.ResolveInfo,
gid: uuid.UUID,
props: AddUsersToGroupInput,
) -> AddUserToGroup:
graph_ctx: GraphQueryContext = info.context
data: dict[str, Any] = {}
set_if_set(props, data, "user_ids")
if not props.user_ids:
return cls(ok=False, msg="empty user id array")

insert_query = sa.insert(AssocGroupUserRow).values(
[
{
"user_id": uuid.UUID(uid),
"group_id": gid,
}
for uid in props.user_ids
]
)
return await simple_db_mutate_returning_item(cls, graph_ctx, insert_query, item_cls=Group)


class DeleteUserFromGroup(graphene.Mutation):
fregataa marked this conversation as resolved.
Show resolved Hide resolved
allowed_roles = (UserRole.ADMIN, UserRole.SUPERADMIN)

class Arguments:
gid = graphene.UUID(required=True)
props = DeleteUsersFromGroupInput(required=True)

ok = graphene.Boolean()
msg = graphene.String()

@classmethod
async def mutate(
cls,
root,
info: graphene.ResolveInfo,
gid: uuid.UUID,
props: DeleteUsersFromGroupInput,
) -> DeleteUserFromGroup:
graph_ctx: GraphQueryContext = info.context
data: dict[str, Any] = {}
set_if_set(props, data, "user_ids")
if not props.user_ids:
return cls(ok=False, msg="empty user id array")

delete_query = sa.insert(AssocGroupUserRow).where(
(AssocGroupUserRow.group_id == gid)
& (AssocGroupUserRow.user_id.in_([uuid.UUID(uid) for uid in props.user_ids]))
)
return await simple_db_mutate_returning_item(cls, graph_ctx, delete_query, item_cls=Group)


class DeleteGroup(graphene.Mutation):
"""
Instead of deleting the group, just mark it as inactive.
Expand Down
111 changes: 59 additions & 52 deletions src/ai/backend/manager/models/resource_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
)
from .keypair import keypairs
from .user import UserRole
from .utils import deprecation_reason_msg, description_msg

if TYPE_CHECKING:
from .gql import GraphQueryContext
Expand Down Expand Up @@ -58,34 +57,6 @@
)


def user_max_vfolder_count(required: bool = False):
return graphene.Int(
required=required,
description=description_msg("24.03.1", "Limitation of the number of user vfolders."),
)


def user_max_quota_scope_size(required: bool = False):
return BigInt(
required=required,
description=description_msg("24.03.1", "Limitation of the quota size of user vfolders."),
)


def project_max_vfolder_count(required: bool = False):
return graphene.Int(
required=required,
description=description_msg("24.03.1", "Limitation of the number of project vfolders."),
)


def project_max_quota_scope_size(required: bool = False):
return BigInt(
required=required,
description=description_msg("24.03.1", "Limitation of the quota size of project vfolders."),
)


keypair_resource_policies = sa.Table(
"keypair_resource_policies",
mapper_registry.metadata,
Expand Down Expand Up @@ -172,9 +143,9 @@ class KeyPairResourcePolicy(graphene.ObjectType):
idle_timeout = BigInt()
allowed_vfolder_hosts = graphene.JSONString()

max_vfolder_count = graphene.Int(deprecation_reason=deprecation_reason_msg("23.09.4"))
max_vfolder_size = BigInt(deprecation_reason=deprecation_reason_msg("23.09.4"))
max_quota_scope_size = BigInt(deprecation_reason=deprecation_reason_msg("23.09.4"))
max_vfolder_count = graphene.Int(deprecation_reason="Deprecated since 23.09.4")
max_vfolder_size = BigInt(deprecation_reason="Deprecated since 23.09.4")
max_quota_scope_size = BigInt(deprecation_reason="Deprecated since 23.09.4")

@classmethod
def from_row(
Expand Down Expand Up @@ -326,15 +297,15 @@ class CreateKeyPairResourcePolicyInput(graphene.InputObjectType):
allowed_vfolder_hosts = graphene.JSONString(required=False)
max_vfolder_count = graphene.Int(
required=False,
deprecation_reason=deprecation_reason_msg("23.09.4"),
deprecation_reason="Deprecated since 23.09.4",
)
max_vfolder_size = BigInt(
required=False,
deprecation_reason=deprecation_reason_msg("23.09.4"),
deprecation_reason="Deprecated since 23.09.4",
)
max_quota_scope_size = BigInt(
required=False,
deprecation_reason=deprecation_reason_msg("23.09.4"),
deprecation_reason="Deprecated since 23.09.4",
)


Expand All @@ -349,15 +320,15 @@ class ModifyKeyPairResourcePolicyInput(graphene.InputObjectType):
allowed_vfolder_hosts = graphene.JSONString(required=False)
max_vfolder_count = graphene.Int(
required=False,
deprecation_reason=deprecation_reason_msg("23.09.4"),
deprecation_reason="Deprecated since 23.09.4",
)
max_vfolder_size = BigInt(
required=False,
deprecation_reason=deprecation_reason_msg("23.09.4"),
deprecation_reason="Deprecated since 23.09.4",
)
max_quota_scope_size = BigInt(
required=False,
deprecation_reason=deprecation_reason_msg("23.09.4"),
deprecation_reason="Deprecated since 23.09.4",
)


Expand Down Expand Up @@ -471,9 +442,15 @@ class UserResourcePolicy(graphene.ObjectType):
id = graphene.ID(required=True)
name = graphene.String(required=True)
created_at = GQLDateTime(required=True)
max_vfolder_count = user_max_vfolder_count()
max_quota_scope_size = user_max_quota_scope_size()
max_vfolder_size = BigInt(deprecation_reason=deprecation_reason_msg("23.09.1"))
max_vfolder_count = graphene.Int(
required=False,
description="Added since 24.03.1. Limitation of the number of user vfolders.",
)
max_quota_scope_size = BigInt(
required=False,
description="Added since 24.03.1. Limitation of the quota size of user vfolders.",
)
max_vfolder_size = BigInt(deprecation_reason="Deprecated since 23.09.1")

@classmethod
def from_row(
Expand Down Expand Up @@ -545,13 +522,25 @@ async def batch_load_by_user(


class CreateUserResourcePolicyInput(graphene.InputObjectType):
max_vfolder_count = user_max_vfolder_count()
max_quota_scope_size = user_max_quota_scope_size()
max_vfolder_count = graphene.Int(
required=False,
description="Added since 24.03.1. Limitation of the number of user vfolders.",
)
max_quota_scope_size = BigInt(
required=False,
description="Added since 24.03.1. Limitation of the quota size of user vfolders.",
)


class ModifyUserResourcePolicyInput(graphene.InputObjectType):
max_vfolder_count = user_max_vfolder_count()
max_quota_scope_size = user_max_quota_scope_size()
max_vfolder_count = graphene.Int(
required=False,
description="Added since 24.03.1. Limitation of the number of user vfolders.",
)
max_quota_scope_size = BigInt(
required=False,
description="Added since 24.03.1. Limitation of the quota size of user vfolders.",
)


class CreateUserResourcePolicy(graphene.Mutation):
Expand Down Expand Up @@ -648,9 +637,15 @@ class ProjectResourcePolicy(graphene.ObjectType):
id = graphene.ID(required=True)
name = graphene.String(required=True)
created_at = GQLDateTime(required=True)
max_vfolder_count = project_max_vfolder_count()
max_quota_scope_size = project_max_quota_scope_size()
max_vfolder_size = BigInt(deprecation_reason=deprecation_reason_msg("23.09.1"))
max_vfolder_count = graphene.Int(
required=False,
description="Added since 24.03.1. Limitation of the number of project vfolders.",
)
max_quota_scope_size = BigInt(
required=False,
description="Added since 24.03.1. Limitation of the quota size of project vfolders.",
)
max_vfolder_size = BigInt(deprecation_reason="Deprecated since 23.09.1")

@classmethod
def from_row(
Expand Down Expand Up @@ -723,13 +718,25 @@ async def batch_load_by_project(


class CreateProjectResourcePolicyInput(graphene.InputObjectType):
max_vfolder_count = project_max_vfolder_count()
max_quota_scope_size = project_max_quota_scope_size()
max_vfolder_count = graphene.Int(
required=False,
description="Added since 24.03.1. Limitation of the number of project vfolders.",
)
max_quota_scope_size = BigInt(
required=False,
description="Added since 24.03.1. Limitation of the quota size of project vfolders.",
)


class ModifyProjectResourcePolicyInput(graphene.InputObjectType):
max_vfolder_count = project_max_vfolder_count()
max_quota_scope_size = project_max_quota_scope_size()
max_vfolder_count = graphene.Int(
required=False,
description="Added since 24.03.1. Limitation of the number of project vfolders.",
)
max_quota_scope_size = BigInt(
required=False,
description="Added since 24.03.1. Limitation of the quota size of project vfolders.",
)


class CreateProjectResourcePolicy(graphene.Mutation):
Expand Down
14 changes: 0 additions & 14 deletions src/ai/backend/manager/models/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,17 +362,3 @@ def agg_to_array(column: sa.Column) -> sa.sql.functions.Function:

def is_db_retry_error(e: Exception) -> bool:
return isinstance(e, DBAPIError) and getattr(e.orig, "pgcode", None) == "40001"


def description_msg(version: str, detail: str | None = None) -> str:
val = f"Added since {version}."
if detail:
val = f"{val} {detail}"
return val


def deprecation_reason_msg(version: str, detail: str | None = None) -> str:
val = f"Deprecated since {version}."
if detail:
val = f"{val} {detail}"
return val
Loading