Skip to content

Commit 3e21c8e

Browse files
fregataaclaudelablup-octodog
authored
refactor(BA-5073): Apply RBAC Creator pattern to DeploymentRevision (#10016)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: octodog <mu001@lablup.com>
1 parent 2a47dd4 commit 3e21c8e

10 files changed

Lines changed: 68 additions & 24 deletions

File tree

changes/10016.enhance.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Apply RBAC Creator pattern to auto sub-entity (BA-5073)

docs/manager/graphql-reference/supergraph.graphql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10492,6 +10492,7 @@ enum RBACElementType
1049210492
NOTIFICATION_RULE @join__enumValue(graph: STRAWBERRY)
1049310493
DEPLOYMENT_TOKEN @join__enumValue(graph: STRAWBERRY)
1049410494
DEPLOYMENT_POLICY @join__enumValue(graph: STRAWBERRY)
10495+
DEPLOYMENT_REVISION @join__enumValue(graph: STRAWBERRY)
1049510496
ARTIFACT_REVISION @join__enumValue(graph: STRAWBERRY)
1049610497
}
1049710498

docs/manager/graphql-reference/v2-schema.graphql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6079,6 +6079,7 @@ enum RBACElementType {
60796079
NOTIFICATION_RULE
60806080
DEPLOYMENT_TOKEN
60816081
DEPLOYMENT_POLICY
6082+
DEPLOYMENT_REVISION
60826083
ARTIFACT_REVISION
60836084
}
60846085

src/ai/backend/common/data/permission/types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,7 @@ class RBACElementType(enum.StrEnum):
391391
# === Auto sub-entities with direct GET APIs ===
392392
DEPLOYMENT_TOKEN = "deployment:token"
393393
DEPLOYMENT_POLICY = "deployment:policy"
394+
DEPLOYMENT_REVISION = "deployment:revision"
394395

395396
# === Entity-level scopes (for entity-scope permissions) ===
396397
ARTIFACT_REVISION = "artifact_revision"

src/ai/backend/manager/api/gql/rbac/types/entity.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ async def entity(
167167
| RBACElementType.ROUTING
168168
| RBACElementType.DEPLOYMENT_TOKEN
169169
| RBACElementType.DEPLOYMENT_POLICY
170+
| RBACElementType.DEPLOYMENT_REVISION
170171
):
171172
return None
172173

src/ai/backend/manager/api/gql/rbac/types/permission.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ class RBACElementTypeGQL(StrEnum):
8787
# Auto sub-entities with direct GET APIs
8888
DEPLOYMENT_TOKEN = "deployment:token"
8989
DEPLOYMENT_POLICY = "deployment:policy"
90+
DEPLOYMENT_REVISION = "deployment:revision"
9091

9192
# Entity-level scopes
9293
ARTIFACT_REVISION = "artifact_revision"
@@ -266,6 +267,7 @@ async def scope(
266267
| RBACElementType.ROUTING
267268
| RBACElementType.DEPLOYMENT_TOKEN
268269
| RBACElementType.DEPLOYMENT_POLICY
270+
| RBACElementType.DEPLOYMENT_REVISION
269271
):
270272
return None
271273

src/ai/backend/manager/repositories/deployment/db_source/db_source.py

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Database source implementation for deployment repository."""
22

3+
import dataclasses
34
import uuid
45
from collections import Counter, defaultdict
56
from collections.abc import AsyncIterator, Mapping, Sequence
@@ -2037,7 +2038,7 @@ async def get_latest_revision_number(
20372038

20382039
async def create_revision(
20392040
self,
2040-
creator: Creator[DeploymentRevisionRow],
2041+
creator: RBACEntityCreator[DeploymentRevisionRow],
20412042
) -> ModelRevisionData:
20422043
"""Create a new deployment revision for an endpoint.
20432044
@@ -2052,16 +2053,12 @@ async def create_revision(
20522053
This requires adding a `revision_history_limit` column to EndpointRow.
20532054
"""
20542055
async with self._begin_session_read_committed() as db_sess:
2055-
spec = cast(DeploymentRevisionCreatorSpec, creator.spec)
2056-
2057-
row = spec.build_row()
2058-
db_sess.add(row)
2059-
await db_sess.flush()
2060-
return row.to_data()
2056+
rbac_result = await execute_rbac_entity_creator(db_sess, creator)
2057+
return rbac_result.row.to_data()
20612058

20622059
async def create_revision_with_next_number(
20632060
self,
2064-
creator: Creator[DeploymentRevisionRow],
2061+
creator: RBACEntityCreator[DeploymentRevisionRow],
20652062
endpoint_id: uuid.UUID,
20662063
) -> ModelRevisionData:
20672064
"""Atomically read the latest revision number and create a new revision.
@@ -2089,14 +2086,14 @@ async def create_revision_with_next_number(
20892086
)
20902087
result = await db_sess.execute(max_query)
20912088
latest_revision_number = result.scalar()
2092-
next_revision_number = (latest_revision_number or 0) + 1
2089+
next_number = (latest_revision_number or 0) + 1
20932090

20942091
spec = cast(DeploymentRevisionCreatorSpec, creator.spec)
2095-
spec = spec.with_revision_number(next_revision_number)
2096-
row = spec.build_row()
2097-
db_sess.add(row)
2098-
await db_sess.flush()
2099-
return row.to_data()
2092+
updated_creator = dataclasses.replace(
2093+
creator, spec=spec.with_revision_number(next_number)
2094+
)
2095+
rbac_result = await execute_rbac_entity_creator(db_sess, updated_creator)
2096+
return rbac_result.row.to_data()
21002097

21012098
async def get_revision(
21022099
self,

src/ai/backend/manager/repositories/deployment/repository.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,15 +1057,15 @@ async def get_default_architecture_from_scaling_group(
10571057
@deployment_repository_resilience.apply()
10581058
async def create_revision(
10591059
self,
1060-
creator: Creator[DeploymentRevisionRow],
1060+
creator: RBACEntityCreator[DeploymentRevisionRow],
10611061
) -> ModelRevisionData:
10621062
"""Create a new deployment revision."""
10631063
return await self._db_source.create_revision(creator)
10641064

10651065
@deployment_repository_resilience.apply()
10661066
async def create_revision_with_next_number(
10671067
self,
1068-
creator: Creator[DeploymentRevisionRow],
1068+
creator: RBACEntityCreator[DeploymentRevisionRow],
10691069
endpoint_id: uuid.UUID,
10701070
) -> ModelRevisionData:
10711071
"""Atomically read the latest revision number and create a new revision.

src/ai/backend/manager/services/deployment/service.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,7 @@
3737
from ai.backend.manager.data.permission.types import RBACElementRef
3838
from ai.backend.manager.errors.service import RoutingNotFound
3939
from ai.backend.manager.models.deployment_policy import DeploymentPolicyRow
40-
from ai.backend.manager.models.deployment_revision import DeploymentRevisionRow
4140
from ai.backend.manager.models.endpoint import EndpointRow, EndpointTokenRow
42-
from ai.backend.manager.repositories.base import Creator
4341
from ai.backend.manager.repositories.base.rbac.entity_creator import RBACEntityCreator
4442
from ai.backend.manager.repositories.base.upserter import Upserter
4543
from ai.backend.manager.repositories.deployment import DeploymentRepository
@@ -611,7 +609,14 @@ async def _build_revision(
611609
# TODO: Convert merged_creator.mounts.extra_mounts (list[MountInfo]) to Sequence[VFolderMount] instead of discarding.
612610
extra_mounts=(),
613611
)
614-
creator: Creator[DeploymentRevisionRow] = Creator(spec=spec)
612+
creator = RBACEntityCreator(
613+
spec=spec,
614+
element_type=RBACElementType.DEPLOYMENT_REVISION,
615+
scope_ref=RBACElementRef(
616+
element_type=RBACElementType.MODEL_DEPLOYMENT,
617+
element_id=str(deployment_id),
618+
),
619+
)
615620
return await self._deployment_repository.create_revision_with_next_number(
616621
creator, deployment_id
617622
)

tests/unit/manager/repositories/deployment/test_deployment_repository.py

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1198,6 +1198,7 @@ async def db_with_cleanup(
11981198
ImageRow,
11991199
EndpointRow,
12001200
EntityFieldRow, # DeploymentRevisionRow relationship dependency
1201+
AssociationScopesEntitiesRow, # RBACEntityCreator dependency
12011202
DeploymentRevisionRow,
12021203
DeploymentPolicyRow,
12031204
],
@@ -1455,7 +1456,16 @@ async def test_revision_data(
14551456
runtime_variant=RuntimeVariant.CUSTOM,
14561457
extra_mounts=[],
14571458
)
1458-
return await deployment_repository.create_revision(Creator(spec=spec))
1459+
return await deployment_repository.create_revision(
1460+
RBACEntityCreator(
1461+
spec=spec,
1462+
element_type=RBACElementType.DEPLOYMENT_REVISION,
1463+
scope_ref=RBACElementRef(
1464+
element_type=RBACElementType.MODEL_DEPLOYMENT,
1465+
element_id=str(test_endpoint_id),
1466+
),
1467+
)
1468+
)
14591469

14601470
@pytest.fixture
14611471
async def test_multiple_revisions(
@@ -1488,7 +1498,16 @@ async def test_multiple_revisions(
14881498
runtime_variant=RuntimeVariant.CUSTOM,
14891499
extra_mounts=[],
14901500
)
1491-
revision = await deployment_repository.create_revision(Creator(spec=spec))
1501+
revision = await deployment_repository.create_revision(
1502+
RBACEntityCreator(
1503+
spec=spec,
1504+
element_type=RBACElementType.DEPLOYMENT_REVISION,
1505+
scope_ref=RBACElementRef(
1506+
element_type=RBACElementType.MODEL_DEPLOYMENT,
1507+
element_id=str(test_endpoint_id),
1508+
),
1509+
)
1510+
)
14921511
revisions.append(revision)
14931512
return revisions
14941513

@@ -1523,7 +1542,16 @@ async def test_five_revisions(
15231542
runtime_variant=RuntimeVariant.CUSTOM,
15241543
extra_mounts=[],
15251544
)
1526-
revision = await deployment_repository.create_revision(Creator(spec=spec))
1545+
revision = await deployment_repository.create_revision(
1546+
RBACEntityCreator(
1547+
spec=spec,
1548+
element_type=RBACElementType.DEPLOYMENT_REVISION,
1549+
scope_ref=RBACElementRef(
1550+
element_type=RBACElementType.MODEL_DEPLOYMENT,
1551+
element_id=str(test_endpoint_id),
1552+
),
1553+
)
1554+
)
15271555
revisions.append(revision)
15281556
return revisions
15291557

@@ -1534,7 +1562,7 @@ async def test_create_revision(
15341562
test_image_id: uuid.UUID,
15351563
test_scaling_group_name: str,
15361564
) -> None:
1537-
"""Test creating a deployment revision using Creator."""
1565+
"""Test creating a deployment revision using RBACEntityCreator."""
15381566
spec = DeploymentRevisionCreatorSpec(
15391567
endpoint_id=test_endpoint_id,
15401568
revision_number=1,
@@ -1555,7 +1583,14 @@ async def test_create_revision(
15551583
runtime_variant=RuntimeVariant.CUSTOM,
15561584
extra_mounts=[],
15571585
)
1558-
creator = Creator(spec=spec)
1586+
creator = RBACEntityCreator(
1587+
spec=spec,
1588+
element_type=RBACElementType.DEPLOYMENT_REVISION,
1589+
scope_ref=RBACElementRef(
1590+
element_type=RBACElementType.MODEL_DEPLOYMENT,
1591+
element_id=str(test_endpoint_id),
1592+
),
1593+
)
15591594

15601595
result = await deployment_repository.create_revision(creator)
15611596

0 commit comments

Comments
 (0)