-
Notifications
You must be signed in to change notification settings - Fork 165
feat(BA-3486): Implement ModifyScalingGroup Action
#7522
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
Conversation
ModifyScalingGroup Action
tests/manager/repositories/scaling_group/test_scaling_group_repository.py
Fixed
Show fixed
Hide fixed
tests/manager/repositories/scaling_group/test_scaling_group_repository.py
Fixed
Show fixed
Hide fixed
tests/manager/repositories/scaling_group/test_scaling_group_repository.py
Fixed
Show fixed
Hide fixed
06dee4b to
92db966
Compare
tests/unit/manager/repositories/scaling_group/test_scaling_group_repository.py
Fixed
Show fixed
Hide fixed
| assert result.metadata.description == "Updated description" | ||
| assert result.status.is_active is False | ||
| assert result.status.is_public is False | ||
| assert result.network.wsproxy_addr == "http://new-wsproxy:5000" |
Check warning
Code scanning / devskim
An HTTP-based URL without TLS was detected. Warning test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR implements the ModifyScalingGroup action following the repository pattern, enabling partial updates to scaling groups using the Updater pattern with OptionalState and TriState for nullable and non-nullable fields respectively.
- Adds
ModifyScalingGroupActionand corresponding action result classes - Implements
ScalingGroupUpdaterSpecwith support for partial field updates - Adds repository and service layer methods for updating scaling groups
- Includes comprehensive unit tests at both repository and service layers
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/ai/backend/manager/services/scaling_group/actions/modify.py | New action class for modifying scaling groups with updater pattern |
| src/ai/backend/manager/repositories/scaling_group/updaters.py | New updater specification defining updateable fields and their state handling |
| src/ai/backend/manager/repositories/scaling_group/db_source/db_source.py | Implements database update operation with error handling for non-existent groups |
| src/ai/backend/manager/repositories/scaling_group/repository.py | Adds repository method wrapper for update operations |
| src/ai/backend/manager/services/scaling_group/service.py | Adds service layer method for modify operation |
| src/ai/backend/manager/services/scaling_group/processors.py | Registers modify action processor in the processor package |
| tests/unit/manager/services/scaling_group/test_scaling_group_service.py | Service layer tests including success and not-found error cases |
| tests/unit/manager/repositories/scaling_group/test_scaling_group_repository.py | Repository layer tests covering database update operations and error conditions |
| changes/7522.feature.md | Changelog entry documenting the new feature |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/ai/backend/manager/repositories/scaling_group/db_source/db_source.py
Outdated
Show resolved
Hide resolved
| def _create_scaling_group_updater( | ||
| self, | ||
| name: str, | ||
| description: Optional[TriState[str]] = None, | ||
| is_active: Optional[OptionalState[bool]] = None, | ||
| is_public: Optional[OptionalState[bool]] = None, | ||
| wsproxy_addr: Optional[TriState[str]] = None, | ||
| wsproxy_api_token: Optional[TriState[str]] = None, | ||
| driver: Optional[OptionalState[str]] = None, | ||
| driver_opts: Optional[OptionalState[Mapping[str, Any]]] = None, | ||
| scheduler: Optional[OptionalState[str]] = None, | ||
| scheduler_opts: Optional[OptionalState[ScalingGroupOpts]] = None, | ||
| use_host_network: Optional[OptionalState[bool]] = None, | ||
| ) -> Updater[ScalingGroupRow]: | ||
| """Create a ScalingGroupUpdaterSpec with the given parameters.""" | ||
| spec = ScalingGroupUpdaterSpec( | ||
| description=description if description is not None else TriState.nop(), | ||
| is_active=is_active if is_active is not None else OptionalState.nop(), | ||
| is_public=is_public if is_public is not None else OptionalState.nop(), | ||
| wsproxy_addr=wsproxy_addr if wsproxy_addr is not None else TriState.nop(), | ||
| wsproxy_api_token=( | ||
| wsproxy_api_token if wsproxy_api_token is not None else TriState.nop() | ||
| ), | ||
| driver=driver if driver is not None else OptionalState.nop(), | ||
| driver_opts=driver_opts if driver_opts is not None else OptionalState.nop(), | ||
| scheduler=scheduler if scheduler is not None else OptionalState.nop(), | ||
| scheduler_opts=scheduler_opts if scheduler_opts is not None else OptionalState.nop(), | ||
| use_host_network=( | ||
| use_host_network if use_host_network is not None else OptionalState.nop() | ||
| ), | ||
| ) | ||
| return Updater(spec=spec, pk_value=name) |
Copilot
AI
Dec 31, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This helper method is duplicated between the service test file and the repository test file. The implementation is identical in both locations (lines 340-371 here and lines 82-113 in test_scaling_group_repository.py). Consider extracting this into a shared test utility module to reduce code duplication and ensure consistency across tests.
9552aac to
4af738e
Compare
d97146e to
bb08812
Compare
| new_scheduler_opts = ScalingGroupOpts( | ||
| allowed_session_types=[SessionTypes.BATCH], | ||
| ) | ||
| updater = self._create_scaling_group_updater( | ||
| name=sample_scaling_group_for_update, | ||
| description=TriState.update("Updated description"), | ||
| is_active=OptionalState.update(False), | ||
| is_public=OptionalState.update(False), | ||
| wsproxy_addr=TriState.update("http://new-wsproxy:5000"), | ||
| wsproxy_api_token=TriState.update("new-token"), | ||
| driver=OptionalState.update("docker"), | ||
| driver_opts=OptionalState.update({"new_opt": "value"}), | ||
| scheduler=OptionalState.update("drf"), | ||
| scheduler_opts=OptionalState.update(new_scheduler_opts), | ||
| use_host_network=OptionalState.update(True), | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it can be provided as a fixture
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we extract the inputs into fixtures, it becomes hard to see at a glance what the test is actually verifying, so I think applying fixtures here does not seem like a good idea.
| def _create_scaling_group_updater( | ||
| self, | ||
| name: str, | ||
| description: Optional[TriState[str]] = None, | ||
| is_active: Optional[OptionalState[bool]] = None, | ||
| is_public: Optional[OptionalState[bool]] = None, | ||
| wsproxy_addr: Optional[TriState[str]] = None, | ||
| wsproxy_api_token: Optional[TriState[str]] = None, | ||
| driver: Optional[OptionalState[str]] = None, | ||
| driver_opts: Optional[OptionalState[Mapping[str, Any]]] = None, | ||
| scheduler: Optional[OptionalState[str]] = None, | ||
| scheduler_opts: Optional[OptionalState[ScalingGroupOpts]] = None, | ||
| use_host_network: Optional[OptionalState[bool]] = None, | ||
| ) -> Updater[ScalingGroupRow]: | ||
| """Create a ScalingGroupUpdaterSpec with the given parameters.""" | ||
| # Build sub-specs only if any of their fields are provided | ||
| status_spec: ScalingGroupStatusUpdaterSpec | None = None | ||
| if is_active is not None or is_public is not None: | ||
| status_spec = ScalingGroupStatusUpdaterSpec( | ||
| is_active=is_active if is_active is not None else OptionalState.nop(), | ||
| is_public=is_public if is_public is not None else OptionalState.nop(), | ||
| ) | ||
|
|
||
| metadata_spec: ScalingGroupMetadataUpdaterSpec | None = None | ||
| if description is not None: | ||
| metadata_spec = ScalingGroupMetadataUpdaterSpec( | ||
| description=description, | ||
| ) | ||
|
|
||
| network_spec: ScalingGroupNetworkConfigUpdaterSpec | None = None | ||
| if ( | ||
| wsproxy_addr is not None | ||
| or wsproxy_api_token is not None | ||
| or use_host_network is not None | ||
| ): | ||
| network_spec = ScalingGroupNetworkConfigUpdaterSpec( | ||
| wsproxy_addr=wsproxy_addr if wsproxy_addr is not None else TriState.nop(), | ||
| wsproxy_api_token=( | ||
| wsproxy_api_token if wsproxy_api_token is not None else TriState.nop() | ||
| ), | ||
| use_host_network=( | ||
| use_host_network if use_host_network is not None else OptionalState.nop() | ||
| ), | ||
| ) | ||
|
|
||
| driver_spec: ScalingGroupDriverConfigUpdaterSpec | None = None | ||
| if driver is not None or driver_opts is not None: | ||
| driver_spec = ScalingGroupDriverConfigUpdaterSpec( | ||
| driver=driver if driver is not None else OptionalState.nop(), | ||
| driver_opts=driver_opts if driver_opts is not None else OptionalState.nop(), | ||
| ) | ||
|
|
||
| scheduler_spec: ScalingGroupSchedulerConfigUpdaterSpec | None = None | ||
| if scheduler is not None or scheduler_opts is not None: | ||
| scheduler_spec = ScalingGroupSchedulerConfigUpdaterSpec( | ||
| scheduler=scheduler if scheduler is not None else OptionalState.nop(), | ||
| scheduler_opts=scheduler_opts | ||
| if scheduler_opts is not None | ||
| else OptionalState.nop(), | ||
| ) | ||
|
|
||
| spec = ScalingGroupUpdaterSpec( | ||
| status=status_spec, | ||
| metadata=metadata_spec, | ||
| network=network_spec, | ||
| driver=driver_spec, | ||
| scheduler=scheduler_spec, | ||
| ) | ||
| return Updater(spec=spec, pk_value=name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don’t think we need this method.
resolves #7486 (BA-3486)
Checklist: (if applicable)
ai.backend.testdocsdirectory