1use axum::{Json, extract::Path, http::StatusCode, response::IntoResponse};
13
14use super::{ErrorResponse, RequestCtx, SiteHeader, to_handler_error};
15use crate::server::audit::AuditNote;
16use crate::service;
17
18pub use manta_shared::types::api::hw_cluster::{
19 AddHwComponentRequest, ApplyHwConfigurationRequest, DeleteHwComponentRequest,
20 HwClusterMode,
21};
22
23#[utoipa::path(post, path = "/hardware-clusters/{target}/members", tag = "hardware",
25 params(("target" = String, Path, description = "Target cluster name"), SiteHeader),
26 request_body = AddHwComponentRequest,
27 security(("bearerAuth" = [])),
28 responses(
29 (status = 200, description = "Members added or preview", body = serde_json::Value),
31 (status = 401, description = "Unauthorized", body = ErrorResponse),
32 (status = 500, description = "Internal error", body = ErrorResponse),
33 )
34)]
35#[tracing::instrument(skip_all)]
36pub async fn add_hw_component(
37 ctx: RequestCtx,
38 note: AuditNote,
39 Path(target): Path<String>,
40 Json(body): Json<AddHwComponentRequest>,
41) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
42 note
43 .action("hw_cluster.members.add")
44 .target("group", &target)
45 .target("group", &body.parent_cluster)
46 .detail("parent_cluster", &body.parent_cluster)
47 .detail("pattern", &body.pattern)
48 .detail("create_group", body.create_hsm_group)
49 .dry_run(body.dry_run);
50
51 tracing::info!(
52 "add_hw_component target={} parent={} dry_run={}",
53 target,
54 body.parent_cluster,
55 body.dry_run
56 );
57 let infra = ctx.infra();
58
59 service::authorization::validate_user_group_access(
60 &infra, &ctx.token, &target,
61 )
62 .await
63 .map_err(to_handler_error)?;
64 service::authorization::validate_user_group_access(
65 &infra,
66 &ctx.token,
67 &body.parent_cluster,
68 )
69 .await
70 .map_err(to_handler_error)?;
71
72 let result = crate::service::hw_cluster::add_hw_component(
73 &infra,
74 &ctx.token,
75 &target,
76 &body.parent_cluster,
77 &body.pattern,
78 body.dry_run,
79 body.create_hsm_group,
80 )
81 .await
82 .map_err(to_handler_error)?;
83
84 Ok(Json(serde_json::json!({
85 "dry_run": body.dry_run,
86 "nodes_moved": result.nodes_moved,
87 "target_cluster": target,
88 "target_nodes": result.target_nodes,
89 "parent_cluster": body.parent_cluster,
90 "parent_nodes": result.parent_nodes,
91 })))
92}
93
94#[utoipa::path(delete, path = "/hardware-clusters/{target}/members", tag = "hardware",
100 params(("target" = String, Path, description = "Target cluster name"), SiteHeader),
101 request_body = DeleteHwComponentRequest,
102 security(("bearerAuth" = [])),
103 responses(
104 (status = 200, description = "Members removed or preview", body = serde_json::Value),
106 (status = 401, description = "Unauthorized", body = ErrorResponse),
107 (status = 500, description = "Internal error", body = ErrorResponse),
108 )
109)]
110#[tracing::instrument(skip_all)]
111pub async fn delete_hw_component(
112 ctx: RequestCtx,
113 note: AuditNote,
114 Path(target): Path<String>,
115 Json(body): Json<DeleteHwComponentRequest>,
116) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
117 note
118 .action("hw_cluster.members.delete")
119 .target("group", &target)
120 .target("group", &body.parent_cluster)
121 .detail("parent_cluster", &body.parent_cluster)
122 .detail("pattern", &body.pattern)
123 .detail("delete_group", body.delete_hsm_group)
124 .dry_run(body.dry_run);
125
126 tracing::info!(
127 "delete_hw_component target={} parent={} dry_run={}",
128 target,
129 body.parent_cluster,
130 body.dry_run
131 );
132 let infra = ctx.infra();
133
134 service::authorization::validate_user_group_access(
135 &infra, &ctx.token, &target,
136 )
137 .await
138 .map_err(to_handler_error)?;
139 service::authorization::validate_user_group_access(
140 &infra,
141 &ctx.token,
142 &body.parent_cluster,
143 )
144 .await
145 .map_err(to_handler_error)?;
146
147 let result = crate::service::hw_cluster::delete_hw_component(
148 &infra,
149 &ctx.token,
150 &target,
151 &body.parent_cluster,
152 &body.pattern,
153 body.dry_run,
154 body.delete_hsm_group,
155 )
156 .await
157 .map_err(to_handler_error)?;
158
159 Ok(Json(serde_json::json!({
160 "dry_run": body.dry_run,
161 "nodes_moved": result.nodes_moved,
162 "target_cluster": target,
163 "target_nodes": result.target_nodes,
164 "parent_cluster": body.parent_cluster,
165 "parent_nodes": result.parent_nodes,
166 })))
167}
168
169#[utoipa::path(post, path = "/hardware-clusters/{target}/configuration", tag = "hardware",
175 params(("target" = String, Path, description = "Target cluster name"), SiteHeader),
176 request_body = ApplyHwConfigurationRequest,
177 security(("bearerAuth" = [])),
178 responses(
179 (status = 200, description = "Configuration applied or preview", body = serde_json::Value),
181 (status = 401, description = "Unauthorized", body = ErrorResponse),
182 (status = 500, description = "Internal error", body = ErrorResponse),
183 )
184)]
185#[tracing::instrument(skip_all)]
186pub async fn apply_hw_configuration(
187 ctx: RequestCtx,
188 note: AuditNote,
189 Path(target): Path<String>,
190 Json(body): Json<ApplyHwConfigurationRequest>,
191) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
192 note
193 .action("hw_cluster.configuration.apply")
194 .target("group", &target)
195 .target("group", &body.parent_cluster)
196 .detail("parent_cluster", &body.parent_cluster)
197 .detail("pattern", &body.pattern)
198 .detail("mode", body.mode)
199 .detail("create_target_group", body.create_target_hsm_group)
200 .detail(
201 "delete_empty_parent_group",
202 body.delete_empty_parent_hsm_group,
203 )
204 .dry_run(body.dry_run);
205
206 tracing::info!(
207 "apply_hw_configuration target={} parent={} dry_run={}",
208 target,
209 body.parent_cluster,
210 body.dry_run
211 );
212 let infra = ctx.infra();
213
214 service::authorization::validate_user_group_access(
215 &infra, &ctx.token, &target,
216 )
217 .await
218 .map_err(to_handler_error)?;
219 service::authorization::validate_user_group_access(
220 &infra,
221 &ctx.token,
222 &body.parent_cluster,
223 )
224 .await
225 .map_err(to_handler_error)?;
226
227 let result = crate::service::hw_cluster::apply_hw_configuration(
228 &infra,
229 &ctx.token,
230 crate::service::hw_cluster::ApplyHwConfigurationParams {
231 mode: body.mode,
232 target_group_name: &target,
233 parent_group_name: &body.parent_cluster,
234 pattern: &body.pattern,
235 dryrun: body.dry_run,
236 create_target_group: body.create_target_hsm_group,
237 delete_empty_parent_group: body.delete_empty_parent_hsm_group,
238 },
239 )
240 .await
241 .map_err(to_handler_error)?;
242
243 Ok(Json(serde_json::json!({
244 "dry_run": body.dry_run,
245 "target_cluster": target,
246 "target_nodes": result.target_nodes,
247 "parent_cluster": body.parent_cluster,
248 "parent_nodes": result.parent_nodes,
249 })))
250}