Skip to content

Commit b170034

Browse files
committed
fix(suite): resolve PR comments
1 parent a5070aa commit b170034

11 files changed

Lines changed: 524 additions & 584 deletions

File tree

netmito/src/api/suites.rs

Lines changed: 12 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,10 @@ use uuid::Uuid;
88

99
use crate::{
1010
config::InfraPool,
11-
entity::task_suite_agent::SuiteAgentSelectionType,
1211
error::ApiError,
1312
schema::{
14-
CancelSuiteResp, CancelTaskSuiteParam, CreateTaskSuiteReq, CreateTaskSuiteResp,
15-
ResetSuiteAgentResp, SuiteAgentReq, SuiteAgentResp, TaskSuiteQueryResp, TaskSuitesQueryReq,
16-
TaskSuitesQueryResp,
13+
CancelTaskSuiteParam, CreateTaskSuiteReq, CreateTaskSuiteResp, SuiteAgentSelectionReq,
14+
SuiteAgentSelectionResp, TaskSuiteQueryResp, TaskSuitesQueryReq, TaskSuitesQueryResp,
1715
},
1816
service::{
1917
self,
@@ -27,9 +25,7 @@ pub fn suites_router(st: InfraPool) -> Router<InfraPool> {
2725
.route("/query", post(query_suites))
2826
.route("/{uuid}", get(get_suite_details).delete(cancel_suite))
2927
.route("/{uuid}/close", post(close_suite))
30-
.route("/{uuid}/agents/include", post(include_suite_agents))
31-
.route("/{uuid}/agents/exclude", post(exclude_suite_agents))
32-
.route("/{uuid}/agents/reset", post(reset_suite_agent))
28+
.route("/{uuid}/agents/selection", post(select_suite_agents))
3329
.route_layer(middleware::from_fn_with_state(
3430
st.clone(),
3531
user_auth_middleware,
@@ -98,57 +94,20 @@ pub async fn cancel_suite(
9894
State(pool): State<InfraPool>,
9995
Path(uuid): Path<Uuid>,
10096
Query(param): Query<CancelTaskSuiteParam>,
101-
) -> Result<Json<CancelSuiteResp>, ApiError> {
102-
let resp =
103-
service::suite::user_cancel_task_suite(u.id, &pool, uuid, param.op.unwrap_or_default())
104-
.await
105-
.map_err(map_service_error)?;
106-
Ok(Json(resp))
107-
}
108-
109-
pub async fn include_suite_agents(
110-
Extension(u): Extension<AuthUser>,
111-
State(pool): State<InfraPool>,
112-
Path(uuid): Path<Uuid>,
113-
Json(req): Json<SuiteAgentReq>,
114-
) -> Result<Json<SuiteAgentResp>, ApiError> {
115-
let resp = service::suite::user_set_suite_agent(
116-
u.id,
117-
&pool,
118-
uuid,
119-
req.agent_uuid,
120-
SuiteAgentSelectionType::UserIncluded,
121-
)
122-
.await
123-
.map_err(map_service_error)?;
124-
Ok(Json(resp))
125-
}
126-
127-
pub async fn exclude_suite_agents(
128-
Extension(u): Extension<AuthUser>,
129-
State(pool): State<InfraPool>,
130-
Path(uuid): Path<Uuid>,
131-
Json(req): Json<SuiteAgentReq>,
132-
) -> Result<Json<SuiteAgentResp>, ApiError> {
133-
let resp = service::suite::user_set_suite_agent(
134-
u.id,
135-
&pool,
136-
uuid,
137-
req.agent_uuid,
138-
SuiteAgentSelectionType::UserExcluded,
139-
)
140-
.await
141-
.map_err(map_service_error)?;
142-
Ok(Json(resp))
97+
) -> Result<(), ApiError> {
98+
service::suite::user_cancel_task_suite(u.id, &pool, uuid, param.op.unwrap_or_default())
99+
.await
100+
.map_err(map_service_error)?;
101+
Ok(())
143102
}
144103

145-
pub async fn reset_suite_agent(
104+
pub async fn select_suite_agents(
146105
Extension(u): Extension<AuthUser>,
147106
State(pool): State<InfraPool>,
148107
Path(uuid): Path<Uuid>,
149-
Json(req): Json<SuiteAgentReq>,
150-
) -> Result<Json<ResetSuiteAgentResp>, ApiError> {
151-
let resp = service::suite::user_reset_suite_agent(u.id, &pool, uuid, req.agent_uuid)
108+
Json(req): Json<SuiteAgentSelectionReq>,
109+
) -> Result<Json<SuiteAgentSelectionResp>, ApiError> {
110+
let resp = service::suite::user_add_agents_to_suite(u.id, &pool, uuid, req)
152111
.await
153112
.map_err(map_service_error)?;
154113
Ok(Json(resp))

netmito/src/client/http.rs

Lines changed: 12 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::collections::HashMap;
12
use std::path::PathBuf;
23

34
use figment::value::magic::RelativePathBuf;
@@ -1394,7 +1395,7 @@ impl MitoHttpClient {
13941395
&mut self,
13951396
uuid: Uuid,
13961397
force: bool,
1397-
) -> crate::error::Result<CancelSuiteResp> {
1398+
) -> crate::error::Result<()> {
13981399
self.url.set_path(&format!("suites/{uuid}"));
13991400
if force {
14001401
self.url.set_query(Some("op=force"));
@@ -1409,61 +1410,32 @@ impl MitoHttpClient {
14091410
// Clear the query so it does not leak into later requests reusing self.url.
14101411
self.url.set_query(None);
14111412
if resp.status().is_success() {
1412-
let resp = resp
1413-
.json::<CancelSuiteResp>()
1414-
.await
1415-
.map_err(RequestError::from)?;
1416-
Ok(resp)
1417-
} else {
1418-
Err(get_error_from_resp(resp).await.into())
1419-
}
1420-
}
1421-
1422-
/// Include (`include == true`) or exclude an agent on a suite.
1423-
pub async fn set_suite_agent(
1424-
&mut self,
1425-
uuid: Uuid,
1426-
include: bool,
1427-
req: SuiteAgentReq,
1428-
) -> crate::error::Result<SuiteAgentResp> {
1429-
let op = if include { "include" } else { "exclude" };
1430-
self.url.set_path(&format!("suites/{uuid}/agents/{op}"));
1431-
let resp = self
1432-
.http_client
1433-
.post(self.url.as_str())
1434-
.bearer_auth(&self.credential)
1435-
.json(&req)
1436-
.send()
1437-
.await
1438-
.map_err(map_reqwest_err)?;
1439-
if resp.status().is_success() {
1440-
let resp = resp
1441-
.json::<SuiteAgentResp>()
1442-
.await
1443-
.map_err(RequestError::from)?;
1444-
Ok(resp)
1413+
Ok(())
14451414
} else {
14461415
Err(get_error_from_resp(resp).await.into())
14471416
}
14481417
}
14491418

1450-
pub async fn reset_suite_agent(
1419+
/// Batch-set agent selection overrides on a suite. Each entry pins (`Include`),
1420+
/// blocks (`Exclude`), or clears the override for (`Match`) one agent.
1421+
pub async fn select_suite_agents(
14511422
&mut self,
14521423
uuid: Uuid,
1453-
req: SuiteAgentReq,
1454-
) -> crate::error::Result<ResetSuiteAgentResp> {
1455-
self.url.set_path(&format!("suites/{uuid}/agents/reset"));
1424+
selection: HashMap<Uuid, SuiteAgentSelectionAction>,
1425+
) -> crate::error::Result<SuiteAgentSelectionResp> {
1426+
self.url
1427+
.set_path(&format!("suites/{uuid}/agents/selection"));
14561428
let resp = self
14571429
.http_client
14581430
.post(self.url.as_str())
14591431
.bearer_auth(&self.credential)
1460-
.json(&req)
1432+
.json(&SuiteAgentSelectionReq { selection })
14611433
.send()
14621434
.await
14631435
.map_err(map_reqwest_err)?;
14641436
if resp.status().is_success() {
14651437
let resp = resp
1466-
.json::<ResetSuiteAgentResp>()
1438+
.json::<SuiteAgentSelectionResp>()
14671439
.await
14681440
.map_err(RequestError::from)?;
14691441
Ok(resp)

netmito/src/client/interactive.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ pub(crate) fn output_group_info(info: &GroupQueryInfo) {
229229
}
230230
}
231231

232-
pub(crate) fn output_suite_list_info(info: &TaskSuiteInfo) {
232+
pub(crate) fn output_suite_info(info: &TaskSuiteInfo) {
233233
tracing::info!("Suite UUID: {}", info.uuid);
234234
if let Some(ref name) = info.name {
235235
tracing::info!("Name: {}", name);

netmito/src/client/mod.rs

Lines changed: 40 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{io::Write, process::Stdio};
1+
use std::{collections::HashMap, io::Write, process::Stdio};
22

33
use clap_repl::ReadCommandOutput;
44
use http::MitoHttpClient;
@@ -824,36 +824,32 @@ impl MitoClient {
824824
self.http_client.close_task_suite(args.uuid).await
825825
}
826826

827-
pub async fn suites_cancel(
828-
&mut self,
829-
args: CancelSuiteArgs,
830-
) -> crate::error::Result<CancelSuiteResp> {
827+
pub async fn suites_cancel(&mut self, args: CancelSuiteArgs) -> crate::error::Result<()> {
831828
self.http_client
832829
.cancel_task_suite(args.uuid, args.force)
833830
.await
834831
}
835832

836-
pub async fn suites_set_agent(
833+
/// Apply a single-agent selection override, reporting the per-agent outcome. Backed
834+
/// by the batch endpoint with a one-entry request.
835+
async fn suites_select_agent(
837836
&mut self,
838837
args: SuiteAgentArgs,
839-
include: bool,
840-
) -> crate::error::Result<SuiteAgentResp> {
841-
let req = SuiteAgentReq {
842-
agent_uuid: args.agent,
843-
};
844-
self.http_client
845-
.set_suite_agent(args.uuid, include, req)
846-
.await
847-
}
848-
849-
pub async fn suites_reset_agent(
850-
&mut self,
851-
args: SuiteAgentArgs,
852-
) -> crate::error::Result<ResetSuiteAgentResp> {
853-
let req = SuiteAgentReq {
854-
agent_uuid: args.agent,
855-
};
856-
self.http_client.reset_suite_agent(args.uuid, req).await
838+
action: SuiteAgentSelectionAction,
839+
) {
840+
let (suite, agent) = (args.uuid, args.agent);
841+
let selection = HashMap::from([(agent, action)]);
842+
match self.http_client.select_suite_agents(suite, selection).await {
843+
Ok(resp) => match resp.failed.get(&agent) {
844+
Some(err) => {
845+
tracing::error!(
846+
"Failed to apply {action:?} to agent {agent} on suite {suite}: {err:?}"
847+
)
848+
}
849+
None => tracing::info!("Applied {action:?} to agent {agent} on suite {suite}"),
850+
},
851+
Err(e) => tracing::error!("{}", e),
852+
}
857853
}
858854

859855
pub async fn tasks_batch_cancel(
@@ -2016,9 +2012,16 @@ impl MitoClient {
20162012
if !counted {
20172013
for suite in resp.suites {
20182014
if verbose {
2019-
output_suite_list_info(&suite);
2015+
output_suite_info(&suite);
20202016
} else {
2021-
tracing::info!("{} ({})", suite.uuid, suite.state);
2017+
tracing::info!(
2018+
"{}, {} ({}), Tasks (incomplete/total): {} / {}",
2019+
suite.uuid,
2020+
suite.name.unwrap_or("[no name]".to_string()),
2021+
suite.state,
2022+
suite.incomplete_tasks,
2023+
suite.total_tasks
2024+
);
20222025
}
20232026
}
20242027
}
@@ -2030,7 +2033,7 @@ impl MitoClient {
20302033
}
20312034
SuitesCommands::Get(args) => match self.suites_get(args).await {
20322035
Ok(resp) => {
2033-
output_parsed_suite_info(&resp.info, &resp.assigned_agents);
2036+
output_parsed_suite_info(&resp.info, &resp.eligible_agents);
20342037
}
20352038
Err(e) => {
20362039
tracing::error!("{}", e);
@@ -2045,58 +2048,25 @@ impl MitoClient {
20452048
}
20462049
},
20472050
SuitesCommands::Cancel(args) => match self.suites_cancel(args).await {
2048-
Ok(resp) => {
2049-
tracing::info!(
2050-
"Suite cancelled; {} tasks cancelled",
2051-
resp.cancelled_task_count
2052-
);
2051+
Ok(_) => {
2052+
tracing::info!("Suite cancelled");
20532053
}
20542054
Err(e) => {
20552055
tracing::error!("{}", e);
20562056
}
20572057
},
20582058
SuitesCommands::IncludeAgent(args) => {
2059-
match self.suites_set_agent(args, true).await {
2060-
Ok(resp) => {
2061-
tracing::info!(
2062-
"Agent {} set to {:?} on suite {}",
2063-
resp.agent_uuid,
2064-
resp.selection,
2065-
resp.suite_uuid
2066-
);
2067-
}
2068-
Err(e) => {
2069-
tracing::error!("{}", e);
2070-
}
2071-
}
2059+
self.suites_select_agent(args, SuiteAgentSelectionAction::Include)
2060+
.await;
20722061
}
20732062
SuitesCommands::ExcludeAgent(args) => {
2074-
match self.suites_set_agent(args, false).await {
2075-
Ok(resp) => {
2076-
tracing::info!(
2077-
"Agent {} set to {:?} on suite {}",
2078-
resp.agent_uuid,
2079-
resp.selection,
2080-
resp.suite_uuid
2081-
);
2082-
}
2083-
Err(e) => {
2084-
tracing::error!("{}", e);
2085-
}
2086-
}
2063+
self.suites_select_agent(args, SuiteAgentSelectionAction::Exclude)
2064+
.await;
2065+
}
2066+
SuitesCommands::ResetAgent(args) => {
2067+
self.suites_select_agent(args, SuiteAgentSelectionAction::Match)
2068+
.await;
20872069
}
2088-
SuitesCommands::ResetAgent(args) => match self.suites_reset_agent(args).await {
2089-
Ok(resp) => {
2090-
if resp.reset {
2091-
tracing::info!("Reset the agent to the tag-match default");
2092-
} else {
2093-
tracing::info!("No manual override to reset");
2094-
}
2095-
}
2096-
Err(e) => {
2097-
tracing::error!("{}", e);
2098-
}
2099-
},
21002070
},
21012071
ClientCommand::Quit => {
21022072
return false;

netmito/src/config/client/tasks.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub struct SubmitTaskArgs {
5050
#[arg(short = 'g', long = "group")]
5151
pub group_name: Option<String>,
5252
/// The UUID of the task suite to submit this task to. When set, the task is executed
53-
/// by the suite's agents instead of traditional workers, and inherits the suite's tags.
53+
/// by the suite's agents instead of workers
5454
#[arg(long = "suite")]
5555
pub suite_uuid: Option<Uuid>,
5656
/// The tags of the task, used to filter workers to execute the task

0 commit comments

Comments
 (0)