use axum::{Json, extract::Query, http::StatusCode, response::IntoResponse};
use serde::Deserialize;
use utoipa::ToSchema;
use super::{
ErrorResponse, RequestCtx, SiteHeader, serialize_or_500, to_handler_error,
};
use crate::service;
pub use manta_shared::types::api::queries::BootParametersQuery;
#[utoipa::path(get, path = "/boot-parameters", tag = "boot-parameters",
params(BootParametersQuery, SiteHeader),
security(("bearerAuth" = [])),
responses(
(status = 200, description = "Boot parameters", body = Vec<manta_backend_dispatcher::types::bss::BootParameters>),
(status = 401, description = "Unauthorized", body = ErrorResponse),
(status = 500, description = "Internal error", body = ErrorResponse),
)
)]
#[tracing::instrument(skip_all)]
pub async fn get_boot_parameters(
ctx: RequestCtx,
Query(q): Query<BootParametersQuery>,
) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
let infra = ctx.infra();
let params = service::boot_parameters::GetBootParametersParams {
group_name: q.hsm_group,
host_expression: q.nodes,
settings_group_name: None,
};
let boot_params =
service::boot_parameters::get_boot_parameters(&infra, &ctx.token, ¶ms)
.await
.map_err(to_handler_error)?;
Ok(Json(boot_params))
}
#[derive(Deserialize, ToSchema)]
pub struct DeleteBootParametersRequest {
pub hosts: Vec<String>,
}
#[utoipa::path(delete, path = "/boot-parameters", tag = "boot-parameters",
params(SiteHeader),
request_body = DeleteBootParametersRequest,
security(("bearerAuth" = [])),
responses(
(status = 204, description = "Boot parameters removed"),
(status = 400, description = "Bad request", body = ErrorResponse),
(status = 401, description = "Unauthorized", body = ErrorResponse),
(status = 500, description = "Internal error", body = ErrorResponse),
)
)]
#[tracing::instrument(skip_all)]
pub async fn delete_boot_parameters(
ctx: RequestCtx,
Json(body): Json<DeleteBootParametersRequest>,
) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
if body.hosts.is_empty() {
return Err((
StatusCode::BAD_REQUEST,
Json(ErrorResponse {
error: "hosts list must not be empty".to_string(),
}),
));
}
tracing::info!("delete_boot_parameters hosts={:?}", body.hosts);
let infra = ctx.infra();
service::boot_parameters::delete_boot_parameters(
&infra, &ctx.token, body.hosts,
)
.await
.map_err(to_handler_error)?;
Ok(StatusCode::NO_CONTENT)
}
#[utoipa::path(post, path = "/boot-parameters", tag = "boot-parameters",
params(SiteHeader),
request_body = manta_backend_dispatcher::types::bss::BootParameters,
security(("bearerAuth" = [])),
responses(
(status = 201, description = "Boot parameters created", body = manta_shared::types::api::responses::CreatedResponse),
(status = 401, description = "Unauthorized", body = ErrorResponse),
(status = 500, description = "Internal error", body = ErrorResponse),
)
)]
#[tracing::instrument(skip_all)]
pub async fn add_boot_parameters(
ctx: RequestCtx,
Json(boot_params): Json<
::manta_backend_dispatcher::types::bss::BootParameters,
>,
) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
tracing::info!("add_boot_parameters");
let infra = ctx.infra();
service::boot_parameters::add_boot_parameters(
&infra,
&ctx.token,
&boot_params,
)
.await
.map_err(to_handler_error)?;
Ok((
StatusCode::CREATED,
Json(serde_json::json!({ "created": true })),
))
}
#[utoipa::path(put, path = "/boot-parameters", tag = "boot-parameters",
params(SiteHeader),
request_body = crate::service::boot_parameters::UpdateBootParametersParams,
security(("bearerAuth" = [])),
responses(
(status = 204, description = "Boot parameters updated"),
(status = 400, description = "Bad request", body = ErrorResponse),
(status = 401, description = "Unauthorized", body = ErrorResponse),
(status = 500, description = "Internal error", body = ErrorResponse),
)
)]
#[tracing::instrument(skip_all)]
pub async fn update_boot_parameters(
ctx: RequestCtx,
Json(params): Json<service::boot_parameters::UpdateBootParametersParams>,
) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
tracing::info!("update_boot_parameters");
let infra = ctx.infra();
service::boot_parameters::update_boot_parameters(&infra, &ctx.token, params)
.await
.map_err(to_handler_error)?;
Ok(StatusCode::NO_CONTENT)
}
pub use manta_shared::types::api::boot_parameters::ApplyBootConfigRequest;
#[utoipa::path(post, path = "/boot-config", tag = "boot-parameters",
params(SiteHeader),
request_body = ApplyBootConfigRequest,
security(("bearerAuth" = [])),
responses(
// dry_run/real result union — kept as Value until the union shape is formalised
(status = 200, description = "Boot config applied or preview", body = serde_json::Value),
(status = 400, description = "Bad request", body = ErrorResponse),
(status = 401, description = "Unauthorized", body = ErrorResponse),
(status = 500, description = "Internal error", body = ErrorResponse),
)
)]
#[tracing::instrument(skip_all)]
pub async fn apply_boot_config(
ctx: RequestCtx,
Json(body): Json<ApplyBootConfigRequest>,
) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
tracing::info!(
"apply_boot_config hosts={} dry_run={}",
body.hosts_expression,
body.dry_run
);
let infra = ctx.infra();
let changeset = service::boot_parameters::prepare_boot_config(
&infra,
&ctx.token,
&body.hosts_expression,
body.boot_image_id.as_deref(),
body.boot_image_configuration.as_deref(),
body.kernel_parameters.as_deref(),
)
.await
.map_err(to_handler_error)?;
if body.dry_run {
return Ok((StatusCode::OK, Json(serialize_or_500(&changeset)?)));
}
service::boot_parameters::persist_boot_config(
&infra,
&ctx.token,
&changeset,
body.runtime_configuration.as_deref(),
)
.await
.map_err(to_handler_error)?;
Ok((
StatusCode::OK,
Json(serde_json::json!({
"applied": true,
"nodes": changeset.xname_vec,
"need_restart": changeset.need_restart,
})),
))
}