add nproc, update rollback, s3 (WIP)

This commit is contained in:
Pakin 2025-09-03 14:39:52 +07:00
parent 6d215292bd
commit 73d5cdd171
8 changed files with 1962 additions and 59 deletions

View file

@ -1,12 +1,14 @@
use aws_sdk_s3::operation::list_objects_v2::ListObjectsV2Output;
use axum::{
Router,
body::Body,
extract::{Path, Query, State},
http::StatusCode,
response::{IntoResponse, Json, Response},
routing::{get, post},
routing::{delete, get, post, put},
};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use std::{collections::HashMap, sync::Arc};
use tokio::sync::Mutex;
use tower::ServiceBuilder;
use tower_http::{
@ -30,6 +32,8 @@ pub struct ApiServer {
discovery: Arc<ContainerDiscovery>,
storage: Arc<Storage>,
log_manager: Arc<LogManager>,
s3_client: aws_sdk_s3::Client,
garage_endpoint: String,
}
#[derive(Clone)]
@ -38,6 +42,10 @@ pub struct AppState {
pub discovery: Arc<ContainerDiscovery>,
pub storage: Arc<Storage>,
pub log_manager: Arc<LogManager>,
// garage
pub s3_client: aws_sdk_s3::Client,
pub garage_endpoint: String,
}
impl ApiServer {
@ -47,6 +55,8 @@ impl ApiServer {
discovery: Arc<ContainerDiscovery>,
storage: Arc<Storage>,
log_manager: Arc<LogManager>,
s3_client: aws_sdk_s3::Client,
garage_endpoint: String,
) -> Self {
Self {
port,
@ -54,6 +64,8 @@ impl ApiServer {
discovery,
storage,
log_manager,
s3_client,
garage_endpoint,
}
}
@ -63,6 +75,8 @@ impl ApiServer {
discovery: self.discovery,
storage: self.storage,
log_manager: self.log_manager.clone(),
s3_client: self.s3_client,
garage_endpoint: self.garage_endpoint,
};
let app = Router::new()
@ -74,6 +88,7 @@ impl ApiServer {
.route("/api/containers/{id}", get(get_container))
.route("/api/containers/{id}/update", post(trigger_update))
.route("/api/containers/{id}/force-update", post(force_update))
.route("/api/containers/{id}/rollback", post(force_rollback))
// Discovery endpoints
.route("/api/discovery/scan", post(force_discovery))
.route("/api/discovery/containers", get(get_discovered_containers))
@ -84,6 +99,14 @@ impl ApiServer {
.route("/api/logs/dates", get(get_log_dates))
// Bulk operations
.route("/api/bulk/update-check", post(bulk_update_check))
// TODO: communicate with garage s3
// .route("/api/s3/{bucket}", get(handler))
// .route("/api/s3/{bucket}", put(handler))
// .route("/api/s3/{bucket}", delete(handler))
// .route("/api/s3/{bucket}/{key}", get(handler))
// .route("/api/s3/{bucket}/{key}", put(handler))
// .route("/api/s3/{bucket}/{key}", delete(handler))
// TODO: Online installations
.with_state(state)
.layer(
ServiceBuilder::new()
@ -214,8 +237,8 @@ async fn system_status(State(state): State<AppState>) -> Json<ApiResponse<System
};
let status = SystemStatusResponse {
service: "Docker Update Manager".to_string(),
version: "1.0.0".to_string(),
service: "Silserv".to_string(),
version: "0.1.1".to_string(),
managed_containers: containers.len(),
discovered_containers: discovered.len(),
active_updates,
@ -335,6 +358,33 @@ async fn force_discovery(
Ok(Json(ApiResponse::success(response)))
}
async fn force_rollback(
Path(id): Path<String>,
State(state): State<AppState>,
) -> Result<Json<ApiResponse<RollbackResponse>>, ApiError> {
// find images with format of <name>-backup-<timestamp>
{
let mgr = state.update_manager.lock().await;
let container = mgr.get_container(&id).await;
match container {
Ok(mut c) => {
mgr.attempt_rollback(&mut c).await?;
}
Err(err) => {
error!("Failed to get container: {}", err);
return Ok(Json(ApiResponse::error(err.to_string())));
}
}
}
let success = RollbackResponse {
message: "Rollback completed successfully".to_string(),
};
Ok(Json(ApiResponse::success(success)))
}
async fn get_discovered_containers(
State(state): State<AppState>,
) -> Result<Json<ApiResponse<Vec<String>>>, ApiError> {
@ -459,6 +509,55 @@ async fn bulk_update_check(
Ok(Json(ApiResponse::success(response)))
}
// get object list
async fn list_objects(
State(state): State<AppState>,
Path(bucket): Path<String>,
Query(params): Query<std::collections::HashMap<String, String>>,
) -> Result<Json<ApiResponse<serde_json::Value>>, ApiError> {
let mut list_request = state.s3_client.list_objects_v2().bucket(&bucket);
if let Some(prefix) = params.get("prefix") {
list_request = list_request.prefix(prefix);
}
let response: ListObjectsV2Output = list_request.send().await?;
if response.contents.is_some() {
let mapped = response
.contents()
.iter()
.map(|obj| {
let mut hashmap = HashMap::new();
hashmap.insert("Key".to_string(), obj.key().unwrap().to_string());
hashmap.insert("Size".to_string(), obj.size().unwrap().to_string());
hashmap.insert(
"LastModified".to_string(),
obj.last_modified().unwrap().to_string(),
);
hashmap.insert("ETag".to_string(), obj.e_tag().unwrap().to_string());
hashmap.insert(
"StorageClass".to_string(),
obj.storage_class().unwrap().to_string(),
);
hashmap.insert(
"Owner".to_string(),
obj.owner().unwrap().id().unwrap().to_string(),
);
hashmap
})
.collect::<Vec<HashMap<String, String>>>();
// let mut response = Response::builder()
// .status(StatusCode::OK)
// .body(Body::new(mapped))
// .unwrap();
Ok(Json(ApiResponse::success(serde_json::json!(mapped))))
} else {
Ok(Json(ApiResponse::success(serde_json::json!({}))))
}
}
// Request/Response types
#[derive(Debug, Deserialize)]
@ -546,6 +645,11 @@ struct BulkUpdateResult {
error: Option<String>,
}
#[derive(Debug, Serialize)]
struct RollbackResponse {
message: String,
}
// Error handling
#[derive(Debug)]