You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The current CRUD handler interface in storage_proxy suffers from code duplication and readability issues, making maintenance challenging.
To improve code quality, the interface needs to be refactored for better modularity and scalability, enabling easier integration of new features and updates.
Required Features
Define CRUD handler class and methods for storage-proxy
Apply handler’s method to new routes in storage/api/manager.py, client.py
Implementation Detail (interface)
Use typing.Protocol to define a formal interface for CRUD handlers, enabling better type checking and enforcing consistency across handler implementations.
Refactor duplicated CRUD logic into standalone utility functions to improve reusability and reduce redundancy across handlers.
Use dependency injection to pass RootContext to handlers dynamically, ensuring they receive the necessary context during initialization.
storage/api/vfolder/types.py
importuuidfrompathlibimportPath, PurePath, PurePosixPathfromtypingimportAny, Mapping, TypeAliasfrompydanticimportAliasChoices, Field, model_validatorfrompydanticimportBaseModelasPydanticBaseModelfromai.backend.common.typesimportBinarySize, QuotaConfig, QuotaScopeID, VFolderIDfromai.backend.storage.typesimportCapacityUsage, TreeUsage__all__= (
"VolumeIDModel",
"VolumeInfoModel",
"VolumeInfoListModel",
"VFolderIDModel",
"VFolderInfoRequestModel",
"VFolderInfoModel",
"VFolderCloneModel",
"QuotaIDModel",
"QuotaScopeInfoModel",
"QuotaConfigModel",
)
classBaseModel(PydanticBaseModel):
"""Base model for all models in this module"""model_config= {"arbitrary_types_allowed": True}
VolumeID: TypeAlias=uuid.UUID# Common fields for VolumeID and VFolderIDVOLUME_ID_FIELD=Field(
...,
validation_alias=AliasChoices(
"vid", "volumeid", "volume_id", "VolumeID", "Volume_Id", "Volumeid"
),
)
VFOLDER_ID_FIELD=Field(
...,
validation_alias=AliasChoices(
"vfid", "vfolderid", "vfolder_id", "VFolderID", "VFolder_Id", "VFolderid"
),
)
QUOTA_SCOPE_ID_FIELD=Field(
...,
validation_alias=AliasChoices(
"qsid",
"quotascopeid",
"quota_scope_id",
"QuotaScopeID",
"Quota_Scope_Id",
"QuotaScopeid",
"Quota_ScopeID",
"Quota_Scopeid",
"quotaScopeID",
"quotaScopeid",
),
)
classVolumeIDModel(BaseModel):
volume_id: VolumeID=VOLUME_ID_FIELD@model_validator(mode="before")defvalidate_all_fields(cls, values: dict[str, Any]) ->dict[str, Any]:
if"volume_id"invaluesandnotisinstance(values["volume_id"], uuid.UUID):
raiseValueError("volume_id must be a UUID")
returnvaluesclassVolumeInfoModel(BaseModel):
"""For `get_volume`, `get_volumes` requests"""volume_id: VolumeID=VOLUME_ID_FIELDbackend: str=Field(...)
path: Path=Field(...)
fsprefix: PurePath|Nonecapabilities: list[str] =Field(...)
options: Mapping[str, Any] |None@model_validator(mode="before")defvalidate_all_fields(cls, values: dict[str, Any]) ->dict[str, Any]:
if"volume_id"invaluesandnotisinstance(values["volume_id"], uuid.UUID):
raiseValueError("volume_id must be a UUID")
if"backend"invaluesandnotisinstance(values["backend"], str):
raiseValueError("backend must be a string")
if"path"invaluesandnotisinstance(values["path"], Path):
raiseValueError("path must be a Path object")
ifvalues.get("fsprefix") isnotNoneandnotisinstance(values["fsprefix"], PurePath):
raiseValueError("fsprefix must be a PurePath or None")
if"capabilities"invaluesandnotisinstance(values["capabilities"], list):
raiseValueError("capabilities must be a list of strings")
ifvalues.get("options") isnotNoneandnotisinstance(values["options"], Mapping):
raiseValueError("options must be a mapping or None")
returnvaluesclassVolumeInfoListModel(BaseModel):
"""For `get_volumes` response"""volumes: dict[VolumeID, VolumeInfoModel] =Field(...)
@model_validator(mode="before")defvalidate_all_fields(cls, values: dict[str, Any]) ->dict[str, Any]:
if"volumes"invaluesandnotisinstance(values["volumes"], dict):
raiseValueError("volumes must be a dictionary")
fork, vinvalues.get("volumes", {}).items():
ifnotisinstance(k, uuid.UUID):
raiseValueError("keys in volumes must be UUIDs")
ifnotisinstance(v, VolumeInfoModel):
raiseValueError("values in volumes must be VolumeInfoModel instances")
returnvaluesclassVFolderIDModel(BaseModel):
volume_id: VolumeID=VOLUME_ID_FIELDvfolder_id: VFolderID=VFOLDER_ID_FIELD@model_validator(mode="before")defvalidate_all_fields(cls, values: dict[str, Any]) ->dict[str, Any]:
if"volume_id"invaluesandnotisinstance(values["volume_id"], uuid.UUID):
raiseValueError("volume_id must be a UUID")
if"vfolder_id"invaluesandnotisinstance(values["vfolder_id"], VFolderID):
raiseValueError("vfolder_id must be a VFolderID")
returnvaluesclassVFolderInfoRequestModel(BaseModel):
"""For `get_vfolder_info` request"""volume_id: VolumeID=VOLUME_ID_FIELDvfolder_id: VFolderID=VFOLDER_ID_FIELDsubpath: PurePosixPath=Field(...)
@model_validator(mode="before")defvalidate_all_fields(cls, values: dict[str, Any]) ->dict[str, Any]:
if"volume_id"invaluesandnotisinstance(values["volume_id"], uuid.UUID):
raiseValueError("volume_id must be a UUID")
if"vfolder_id"invaluesandnotisinstance(values["vfolder_id"], VFolderID):
raiseValueError("vfolder_id must be a VFolderID")
if"subpath"invaluesandnotisinstance(values["subpath"], PurePosixPath):
raiseValueError("subpath must be a PurePosixPath")
returnvaluesclassVFolderInfoModel(BaseModel):
"""For `get_vfolder_info` response"""vfolder_mount: Path=Field(...)
vfolder_metadata: bytes=Field(...) # 실제로 쓰이는지 확인 필요vfolder_usage: TreeUsage=Field(...)
vfolder_used_bytes: BinarySize=Field(...)
vfolder_fs_usage: CapacityUsage=Field(...)
@model_validator(mode="before")defvalidate_all_fields(cls, values: dict[str, Any]) ->dict[str, Any]:
if"vfolder_mount"invaluesandnotisinstance(values["vfolder_mount"], Path):
raiseValueError("vfolder_mount must be a Path object")
if"vfolder_metadata"invaluesandnotisinstance(values["vfolder_metadata"], bytes):
raiseValueError("vfolder_metadata must be bytes")
if"vfolder_usage"invaluesandnotisinstance(values["vfolder_usage"], TreeUsage):
raiseValueError("vfolder_usage must be a TreeUsage object")
if"vfolder_used_bytes"invaluesandnotisinstance(
values["vfolder_used_bytes"], BinarySize
):
raiseValueError("vfolder_used_bytes must be a BinarySize object")
if"vfolder_fs_usage"invaluesandnotisinstance(
values["vfolder_fs_usage"], CapacityUsage
):
raiseValueError("vfolder_fs_usage must be a CapacityUsage object")
returnvaluesclassVFolderCloneModel(BaseModel):
volume_id: VolumeID=VOLUME_ID_FIELD# source volumesrc_vfolder_id: VFolderID=Field(
...,
validation_alias=AliasChoices(
"src_vfid",
"src_vfolderid",
"src_vfolder_id",
"source",
"src",
"src_vfolderid",
"source_vfid",
"source_vfolderid",
"source_vfolder_id",
"SrcVfid",
"SrcVfolderid",
"Source",
"Src",
"SrcVfolderid",
"SourceVfid",
"SourceVfolderid",
),
)
dst_vfolder_id: VFolderID=Field(
...,
validation_alias=AliasChoices(
"dst_vfid",
"dst_vfolderid",
"destination",
"dst",
"dst_vfolderid",
"dst_vfolder_id",
"destination_vfid",
"destination_vfolderid",
"destination_vfolder_id",
"DstVfid",
"DstVfolderid",
"Destination",
"Dst",
"DstVfolderid",
"DestinationVfid",
"DestinationVfolderid",
),
)
@model_validator(mode="before")defvalidate_all_fields(cls, values: dict[str, Any]) ->dict[str, Any]:
if"volume_id"invaluesandnotisinstance(values["volume_id"], uuid.UUID):
raiseValueError("volume_id must be a UUID")
if"src_vfolder_id"invaluesandnotisinstance(values["src_vfolder_id"], VFolderID):
raiseValueError("src_vfolder_id must be a VFolderID")
if"dst_vfolder_id"invaluesandnotisinstance(values["dst_vfolder_id"], VFolderID):
raiseValueError("dst_vfolder_id must be a VFolderID")
returnvaluesclassQuotaIDModel(BaseModel):
volume_id: VolumeID=VOLUME_ID_FIELDquota_scope_id: QuotaScopeID=QUOTA_SCOPE_ID_FIELD@model_validator(mode="before")defvalidate_all_fields(cls, values: dict[str, Any]) ->dict[str, Any]:
if"volume_id"invaluesandnotisinstance(values["volume_id"], uuid.UUID):
raiseValueError("volume_id must be a UUID")
if"quota_scope_id"invaluesandnotisinstance(values["quota_scope_id"], QuotaScopeID):
raiseValueError("quota_scope_id must be a QuotaScopeID")
returnvaluesclassQuotaScopeInfoModel(BaseModel):
used_bytes: int|Nonelimit_bytes: int|None@model_validator(mode="before")defvalidate_all_fields(cls, values: dict[str, Any]) ->dict[str, Any]:
forfieldin ["used_bytes", "limit_bytes"]:
value=values.get(field)
ifvalueisNone:
values[field] =0elifnotisinstance(value, int):
raiseValueError(f"{field} must be an integer or None")
returnvaluesclassQuotaConfigModel(BaseModel):
volume_id: VolumeID=VOLUME_ID_FIELDquota_scope_id: QuotaScopeID=QUOTA_SCOPE_ID_FIELDoptions: QuotaConfig|None@model_validator(mode="before")defvalidate_all_fields(cls, values: dict[str, Any]) ->dict[str, Any]:
if"volume_id"invaluesandnotisinstance(values["volume_id"], uuid.UUID):
raiseValueError("volume_id must be a UUID")
if"quota_scope_id"invaluesandnotisinstance(values["quota_scope_id"], QuotaScopeID):
raiseValueError("quota_scope_id must be a QuotaScopeID")
ifvalues.get("options") isnotNoneandnotisinstance(values["options"], QuotaConfig):
raiseValueError("options must be a QuotaConfig or None")
returnvalues
HyeockJinKim
changed the title
Implement CRUD handlers for storage-proxy
Implement volume and vfolder CRUD handlers for storage-proxy (manager and client)
Jan 15, 2025
HyeockJinKim
changed the title
Implement volume and vfolder CRUD handlers for storage-proxy (manager and client)
Add Interface for volume and vfolder CRUD handlers in storage-proxy
Jan 15, 2025
HyeockJinKim
changed the title
Add Interface for volume and vfolder CRUD handlers in storage-proxy
Add a new handler with an empty interface to handle VFolder CRUD API in storage-proxy
Jan 15, 2025
HyeockJinKim
changed the title
Add a new handler with an empty interface to handle VFolder CRUD API in storage-proxy
Add the skeleton interface of VFolder CRUD APIs using the new layered architecture in storage-proxy
Jan 15, 2025
Motivation
Required Features
storage/api/manager.py, client.py
Implementation Detail (interface)
typing.Protocol
to define a formal interface for CRUD handlers, enabling better type checking and enforcing consistency across handler implementations.storage/api/vfolder/types.py
storage/api/vfolder/manager_service.py
storage/api/vfolder/manager_handler.py
Testing Scenarios
The text was updated successfully, but these errors were encountered: