This repository has been archived by the owner on Aug 2, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: Don't allow empty values in allowed session types and scan all a…
…llowed scaling groups (#542) * Improve the failure messages and rewrite the exclusion logic. * Add a new test case to demonstrate how to write test cases with mocking. Co-authored-by: Joongi Kim <[email protected]>
- Loading branch information
1 parent
b947549
commit d04d3c1
Showing
4 changed files
with
191 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Disallow empty values in `allowed_session_types` of scheduler options and fix the predicate check to scan all scaling groups |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
from __future__ import annotations | ||
|
||
from unittest import mock | ||
from unittest.mock import MagicMock | ||
|
||
import pytest | ||
|
||
from ai.backend.common.types import SessionTypes | ||
from ai.backend.manager.scheduler.predicates import check_scaling_group | ||
|
||
|
||
@pytest.mark.asyncio | ||
@mock.patch('ai.backend.manager.scheduler.predicates.execute_with_retry') | ||
async def test_allowed_session_types_check(mock_query): | ||
mock_query.return_value = [ | ||
{ | ||
'name': 'a', | ||
'scheduler_opts': { | ||
'allowed_session_types': ['batch'], | ||
}, | ||
}, | ||
{ | ||
'name': 'b', | ||
'scheduler_opts': { | ||
'allowed_session_types': ['interactive'], | ||
}, | ||
}, | ||
{ | ||
'name': 'c', | ||
'scheduler_opts': { | ||
'allowed_session_types': ['batch', 'interactive'], | ||
}, | ||
}, | ||
] | ||
mock_conn = MagicMock() | ||
mock_sched_ctx = MagicMock() | ||
mock_sess_ctx = MagicMock() | ||
|
||
# Preferred scaling group with one match in allowed sgroups | ||
|
||
mock_sess_ctx.session_type = SessionTypes.BATCH | ||
mock_sess_ctx.scaling_group = 'a' | ||
mock_sess_ctx.target_sgroup_names = [] | ||
result = await check_scaling_group(mock_conn, mock_sched_ctx, mock_sess_ctx) | ||
assert result.passed | ||
assert mock_sess_ctx.target_sgroup_names == ['a'] | ||
|
||
mock_sess_ctx.session_type = SessionTypes.BATCH | ||
mock_sess_ctx.scaling_group = 'b' | ||
mock_sess_ctx.target_sgroup_names = [] | ||
result = await check_scaling_group(mock_conn, mock_sched_ctx, mock_sess_ctx) | ||
assert not result.passed | ||
assert result.message is not None | ||
assert "does not accept" in result.message | ||
assert mock_sess_ctx.target_sgroup_names == [] | ||
|
||
mock_sess_ctx.session_type = SessionTypes.BATCH | ||
mock_sess_ctx.scaling_group = 'c' | ||
mock_sess_ctx.target_sgroup_names = [] | ||
result = await check_scaling_group(mock_conn, mock_sched_ctx, mock_sess_ctx) | ||
assert result.passed | ||
assert mock_sess_ctx.target_sgroup_names == ['c'] | ||
|
||
mock_sess_ctx.session_type = SessionTypes.INTERACTIVE | ||
mock_sess_ctx.scaling_group = 'a' | ||
mock_sess_ctx.target_sgroup_names = [] | ||
result = await check_scaling_group(mock_conn, mock_sched_ctx, mock_sess_ctx) | ||
assert not result.passed | ||
assert result.message is not None | ||
assert "does not accept" in result.message | ||
assert mock_sess_ctx.target_sgroup_names == [] | ||
|
||
mock_sess_ctx.session_type = SessionTypes.INTERACTIVE | ||
mock_sess_ctx.scaling_group = 'b' | ||
mock_sess_ctx.target_sgroup_names = [] | ||
result = await check_scaling_group(mock_conn, mock_sched_ctx, mock_sess_ctx) | ||
assert result.passed | ||
assert mock_sess_ctx.target_sgroup_names == ['b'] | ||
|
||
mock_sess_ctx.session_type = SessionTypes.INTERACTIVE | ||
mock_sess_ctx.scaling_group = 'c' | ||
mock_sess_ctx.target_sgroup_names = [] | ||
result = await check_scaling_group(mock_conn, mock_sched_ctx, mock_sess_ctx) | ||
assert result.passed | ||
assert mock_sess_ctx.target_sgroup_names == ['c'] | ||
|
||
# Non-existent/disallowed preferred scaling group | ||
|
||
mock_sess_ctx.session_type = SessionTypes.INTERACTIVE | ||
mock_sess_ctx.scaling_group = 'x' | ||
mock_sess_ctx.target_sgroup_names = [] | ||
result = await check_scaling_group(mock_conn, mock_sched_ctx, mock_sess_ctx) | ||
assert not result.passed | ||
assert result.message is not None | ||
assert "do not have access" in result.message | ||
assert mock_sess_ctx.target_sgroup_names == [] | ||
|
||
# No preferred scaling group with partially matching allowed sgroups | ||
|
||
mock_sess_ctx.session_type = SessionTypes.BATCH | ||
mock_sess_ctx.scaling_group = None | ||
mock_sess_ctx.target_sgroup_names = [] | ||
result = await check_scaling_group(mock_conn, mock_sched_ctx, mock_sess_ctx) | ||
assert result.passed | ||
assert mock_sess_ctx.target_sgroup_names == ['a', 'c'] | ||
|
||
mock_sess_ctx.session_type = SessionTypes.INTERACTIVE | ||
mock_sess_ctx.scaling_group = None | ||
mock_sess_ctx.target_sgroup_names = [] | ||
result = await check_scaling_group(mock_conn, mock_sched_ctx, mock_sess_ctx) | ||
assert result.passed | ||
assert mock_sess_ctx.target_sgroup_names == ['b', 'c'] | ||
|
||
# No preferred scaling group with an empty list of allowed sgroups | ||
|
||
mock_query.return_value = [] | ||
|
||
mock_sess_ctx.session_type = SessionTypes.BATCH | ||
mock_sess_ctx.scaling_group = 'x' | ||
mock_sess_ctx.target_sgroup_names = [] | ||
result = await check_scaling_group(mock_conn, mock_sched_ctx, mock_sess_ctx) | ||
assert not result.passed | ||
assert result.message is not None | ||
assert "do not have any" in result.message | ||
assert mock_sess_ctx.target_sgroup_names == [] | ||
|
||
mock_sess_ctx.session_type = SessionTypes.INTERACTIVE | ||
mock_sess_ctx.scaling_group = 'x' | ||
mock_sess_ctx.target_sgroup_names = [] | ||
result = await check_scaling_group(mock_conn, mock_sched_ctx, mock_sess_ctx) | ||
assert not result.passed | ||
assert result.message is not None | ||
assert "do not have any" in result.message | ||
assert mock_sess_ctx.target_sgroup_names == [] | ||
|
||
# No preferred scaling group with a non-empty list of allowed sgroups | ||
|
||
mock_query.return_value = [ | ||
{ | ||
'name': 'a', | ||
'scheduler_opts': { | ||
'allowed_session_types': ['batch'], | ||
}, | ||
}, | ||
] | ||
|
||
mock_sess_ctx.session_type = SessionTypes.BATCH | ||
mock_sess_ctx.scaling_group = None | ||
mock_sess_ctx.target_sgroup_names = [] | ||
result = await check_scaling_group(mock_conn, mock_sched_ctx, mock_sess_ctx) | ||
assert result.passed | ||
assert mock_sess_ctx.target_sgroup_names == ['a'] | ||
|
||
mock_sess_ctx.session_type = SessionTypes.INTERACTIVE | ||
mock_sess_ctx.scaling_group = None | ||
mock_sess_ctx.target_sgroup_names = [] | ||
result = await check_scaling_group(mock_conn, mock_sched_ctx, mock_sess_ctx) | ||
assert not result.passed | ||
assert result.message is not None | ||
assert "No scaling groups accept" in result.message | ||
assert mock_sess_ctx.target_sgroup_names == [] |