Skip to content

Commit 3e617fe

Browse files
authored
refactor: Single-source active/dead set definitions of ContainerStatus (#11213)
1 parent cc195bf commit 3e617fe

8 files changed

Lines changed: 30 additions & 34 deletions

File tree

changes/11213.fix.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Single-source active/dead flag set definitions of `ContainerStatus` to prevent potential mismatch in future code edits

src/ai/backend/agent/agent.py

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -276,17 +276,8 @@
276276

277277
_sentinel = Sentinel.TOKEN
278278

279-
ACTIVE_STATUS_SET = frozenset([
280-
ContainerStatus.RUNNING,
281-
ContainerStatus.RESTARTING,
282-
ContainerStatus.PAUSED,
283-
])
284-
285-
DEAD_STATUS_SET = frozenset([
286-
ContainerStatus.EXITED,
287-
ContainerStatus.DEAD,
288-
ContainerStatus.REMOVING,
289-
])
279+
ACTIVE_STATUS_SET = ContainerStatus.active_set()
280+
DEAD_STATUS_SET = ContainerStatus.dead_set()
290281

291282
COMMIT_STATUS_EXPIRE: Final[int] = 13
292283
EVENT_DISPATCHER_CONSUMER_GROUP: Final = "agent"

src/ai/backend/common/types.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import math
99
import numbers
1010
import textwrap
11-
import uuid
1211
from abc import ABC, ABCMeta, abstractmethod
1312
from collections import UserDict, UserString, defaultdict, namedtuple
1413
from collections.abc import AsyncIterator, Iterable, Mapping, Sequence
@@ -27,7 +26,6 @@
2726
NewType,
2827
NotRequired,
2928
Self,
30-
TypeAlias,
3129
TypedDict,
3230
TypeVar,
3331
cast,
@@ -474,6 +472,7 @@ def _validate_slot_name(v: Any) -> SlotName:
474472

475473

476474
class ContainerStatus(enum.StrEnum):
475+
CREATED = "created"
477476
RUNNING = "running"
478477
RESTARTING = "restarting"
479478
PAUSED = "paused"
@@ -780,7 +779,7 @@ class VFolderMountOptions:
780779
class VFolderMountRequest:
781780
"""A single vfolder mount request combining reference, destination path, and options."""
782781

783-
ref: str | uuid.UUID # vfolder name (with optional /subpath) or UUID
782+
ref: str | UUID # vfolder name (with optional /subpath) or UUID
784783
dst_path: str | None = None # custom mount destination path
785784
options: VFolderMountOptions = attrs.Factory(VFolderMountOptions)
786785

@@ -1394,7 +1393,7 @@ def as_trafaret(cls) -> t.Trafaret:
13941393
raise NotImplementedError
13951394

13961395

1397-
VolumeID: TypeAlias = uuid.UUID
1396+
VolumeID = NewType("VolumeID", UUID)
13981397

13991398

14001399
@attrs.define(slots=True, frozen=True)

src/ai/backend/storage/services/service.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,11 @@ async def get_volumes(self) -> list[VolumeMeta]:
126126
volumes = self._volume_pool.list_volumes()
127127
return [
128128
VolumeMeta(
129-
volume_id=uuid.UUID(volume_id),
129+
volume_id=VolumeID(uuid.UUID(volume_id)),
130130
backend=info.backend,
131131
path=info.path,
132132
fsprefix=info.fsprefix,
133-
capabilities=await self._get_capabilities(uuid.UUID(volume_id)),
133+
capabilities=await self._get_capabilities(VolumeID(uuid.UUID(volume_id))),
134134
)
135135
for volume_id, info in volumes.items()
136136
]

src/ai/backend/storage/volumes/pool.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import logging
4+
import uuid
45
from collections.abc import AsyncIterator, Mapping
56
from contextlib import asynccontextmanager as actxmgr
67
from pathlib import Path
@@ -82,7 +83,7 @@ async def create(
8283
volumes_by_name: dict[str, AbstractVolume] = {}
8384
for raw_volume_id, config in local_config.volume.items():
8485
try:
85-
volume_id = VolumeID(raw_volume_id)
86+
volume_id = VolumeID(uuid.UUID(raw_volume_id))
8687
except (ValueError, TypeError):
8788
volumes_by_name[raw_volume_id] = await cls._init_volume(
8889
config,

tests/unit/storage-proxy/vfolder/test_handler.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from aiohttp import web
1111
from aiohttp.web import StreamResponse
1212

13-
from ai.backend.common.types import QuotaConfig, QuotaScopeID, QuotaScopeType, VFolderID
13+
from ai.backend.common.types import QuotaConfig, QuotaScopeID, QuotaScopeType, VFolderID, VolumeID
1414
from ai.backend.storage.api.vfolder.handler import VFolderHandler, VFolderServiceProtocol
1515
from ai.backend.storage.volumes.types import (
1616
QuotaScopeKey,
@@ -27,7 +27,7 @@
2727

2828
class MockVFolderService(VFolderServiceProtocol):
2929
@override
30-
async def get_volume(self, volume_id: uuid.UUID) -> VolumeMeta:
30+
async def get_volume(self, volume_id: VolumeID) -> VolumeMeta:
3131
return VolumeMeta(
3232
volume_id=volume_id,
3333
backend="vfs",
@@ -39,8 +39,8 @@ async def get_volume(self, volume_id: uuid.UUID) -> VolumeMeta:
3939
@override
4040
async def get_volumes(self) -> list[VolumeMeta]:
4141
volumes = {
42-
UUID1: {"backend": "vfs", "path": "/mnt/volume1", "fsprefix": "vfs-test-1"},
43-
UUID2: {"backend": "nfs", "path": "/mnt/volume2", "fsprefix": "nfs-test-2"},
42+
VolumeID(UUID1): {"backend": "vfs", "path": "/mnt/volume1", "fsprefix": "vfs-test-1"},
43+
VolumeID(UUID2): {"backend": "nfs", "path": "/mnt/volume2", "fsprefix": "nfs-test-2"},
4444
}
4545
return [
4646
VolumeMeta(

tests/unit/storage-proxy/vfolder/test_pool.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import uuid
12
from pathlib import Path, PurePath
23
from unittest.mock import AsyncMock, MagicMock
34

@@ -53,7 +54,7 @@ def mock_storage_plugin_ctx() -> AsyncMock:
5354

5455
async def test_get_volume(mock_volume: AsyncMock, mock_storage_plugin_ctx: AsyncMock) -> None:
5556
# Create a VolumePool with mocked volumes
56-
volume_id = VolumeID("550e8400-e29b-41d4-a716-446655440000")
57+
volume_id = VolumeID(uuid.UUID("550e8400-e29b-41d4-a716-446655440000"))
5758
volumes = {volume_id: mock_volume}
5859
volumes_by_name = {"test_volume": mock_volume}
5960

@@ -72,7 +73,7 @@ async def test_get_volume(mock_volume: AsyncMock, mock_storage_plugin_ctx: Async
7273
assert volume is mock_volume
7374

7475
# Test get_volume with invalid volume ID
75-
invalid_id = VolumeID("00000000-0000-0000-0000-000000000000")
76+
invalid_id = VolumeID(uuid.UUID("00000000-0000-0000-0000-000000000000"))
7677
with pytest.raises(InvalidVolumeError):
7778
async with pool.get_volume(invalid_id) as volume:
7879
pass

tests/unit/storage-proxy/vfolder/test_service.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import pytest
66
from aiohttp import web
77

8-
from ai.backend.common.types import QuotaConfig, QuotaScopeID, QuotaScopeType, VFolderID
8+
from ai.backend.common.types import QuotaConfig, QuotaScopeID, QuotaScopeType, VFolderID, VolumeID
99
from ai.backend.storage.errors import VFolderNotFoundError
1010
from ai.backend.storage.services.service import VolumeService
1111
from ai.backend.storage.services.service import log as service_log
@@ -20,6 +20,9 @@
2020
UUID = uuid.UUID("12345678-1234-5678-1234-567812345678")
2121
UUID1 = uuid.UUID("12345678-1234-5678-1234-567812345679")
2222
UUID2 = uuid.UUID("12345678-1234-5678-1234-567812345680")
23+
VOLUME_ID = VolumeID(UUID)
24+
VOLUME_ID1 = VolumeID(UUID1)
25+
VOLUME_ID2 = VolumeID(UUID2)
2326

2427

2528
@pytest.fixture
@@ -46,7 +49,7 @@ async def test_get_volume(
4649

4750
mock_volume_pool.get_volume_info.return_value = mock_volume_info
4851

49-
volume_id = UUID
52+
volume_id = VOLUME_ID
5053
result = await mock_service.get_volume(volume_id)
5154

5255
mock_log.assert_called_once_with(service_log, "get_volume", volume_id)
@@ -108,7 +111,7 @@ async def test_create_quota_scope(
108111

109112
quota_scope_id = QuotaScopeID(scope_type=QuotaScopeType.USER, scope_id=UUID)
110113
quota_scope_key = QuotaScopeKey(
111-
volume_id=uuid.UUID("12345678-1234-5678-1234-567812345678"),
114+
volume_id=VOLUME_ID,
112115
quota_scope_id=quota_scope_id,
113116
)
114117
options = QuotaConfig(limit_bytes=1024 * 1024 * 1024)
@@ -132,7 +135,7 @@ async def test_get_quota_scope(
132135
mock_volume_pool.get_volume.return_value.__aenter__.return_value = mock_volume
133136

134137
quota_scope_id = QuotaScopeID(scope_type=QuotaScopeType.USER, scope_id=UUID)
135-
quota_scope_key = QuotaScopeKey(volume_id=UUID, quota_scope_id=quota_scope_id)
138+
quota_scope_key = QuotaScopeKey(volume_id=VOLUME_ID, quota_scope_id=quota_scope_id)
136139

137140
result = await mock_service.get_quota_scope(quota_scope_key)
138141

@@ -155,7 +158,7 @@ async def test_update_quota_scope(
155158
mock_volume_pool.get_volume.return_value.__aenter__.return_value = mock_volume
156159

157160
quota_scope_id = QuotaScopeID(scope_type=QuotaScopeType.USER, scope_id=UUID)
158-
quota_scope_key = QuotaScopeKey(volume_id=UUID, quota_scope_id=quota_scope_id)
161+
quota_scope_key = QuotaScopeKey(volume_id=VOLUME_ID, quota_scope_id=quota_scope_id)
159162
options = QuotaConfig(limit_bytes=2000)
160163

161164
await mock_service.update_quota_scope(quota_scope_key, options)
@@ -180,7 +183,7 @@ async def test_delete_quota_scope(
180183
mock_volume_pool.get_volume.return_value.__aenter__.return_value = mock_volume
181184

182185
quota_scope_id = QuotaScopeID(scope_type=QuotaScopeType.USER, scope_id=UUID)
183-
quota_scope_key = QuotaScopeKey(volume_id=UUID, quota_scope_id=quota_scope_id)
186+
quota_scope_key = QuotaScopeKey(volume_id=VOLUME_ID, quota_scope_id=quota_scope_id)
184187

185188
await mock_service.delete_quota_scope(quota_scope_key)
186189

@@ -203,7 +206,7 @@ async def test_create_vfolder(
203206
quota_scope_id=QuotaScopeID(scope_type=QuotaScopeType.USER, scope_id=UUID),
204207
folder_id=UUID,
205208
)
206-
vfolder_key = VFolderKey(volume_id=UUID, vfolder_id=vfolder_id)
209+
vfolder_key = VFolderKey(volume_id=VOLUME_ID, vfolder_id=vfolder_id)
207210

208211
await mock_service.create_vfolder(vfolder_key)
209212

@@ -228,7 +231,7 @@ async def test_clone_vfolder(
228231
quota_scope_id=QuotaScopeID(scope_type=QuotaScopeType.USER, scope_id=UUID1),
229232
folder_id=UUID2,
230233
)
231-
vfolder_key = VFolderKey(volume_id=UUID, vfolder_id=src_vfolder_id)
234+
vfolder_key = VFolderKey(volume_id=VOLUME_ID, vfolder_id=src_vfolder_id)
232235

233236
await mock_service.clone_vfolder(vfolder_key, dst_vfolder_id)
234237

@@ -260,7 +263,7 @@ async def test_get_vfolder_info(
260263
quota_scope_id=QuotaScopeID(scope_type=QuotaScopeType.USER, scope_id=UUID),
261264
folder_id=UUID,
262265
)
263-
vfolder_key = VFolderKey(volume_id=UUID, vfolder_id=vfolder_id)
266+
vfolder_key = VFolderKey(volume_id=VOLUME_ID, vfolder_id=vfolder_id)
264267
subpath = "test_subpath"
265268

266269
result = await mock_service.get_vfolder_info(vfolder_key, subpath)
@@ -291,7 +294,7 @@ async def test_delete_vfolder(
291294
quota_scope_id=QuotaScopeID(scope_type=QuotaScopeType.USER, scope_id=UUID),
292295
folder_id=UUID,
293296
)
294-
vfolder_key = VFolderKey(volume_id=UUID, vfolder_id=vfolder_id)
297+
vfolder_key = VFolderKey(volume_id=VOLUME_ID, vfolder_id=vfolder_id)
295298

296299
with pytest.raises(web.HTTPGone, match="VFolder not found"):
297300
await mock_service.delete_vfolder(vfolder_key)

0 commit comments

Comments
 (0)