improve webclient (#2151)

This commit is contained in:
KKRainbow
2026-04-23 13:44:18 +08:00
committed by GitHub
parent 263f4c3bc9
commit 958b246f05
19 changed files with 1585 additions and 188 deletions
+76 -40
View File
@@ -1,10 +1,23 @@
use std::{collections::HashSet, sync::Arc};
use crate::{
common::config::{ConfigFileControl, ConfigFilePermission, ConfigLoader},
common::config::{ConfigFileControl, ConfigFilePermission, ConfigLoader, ConfigSource},
instance_manager::NetworkInstanceManager,
proto::{
api::{config::GetConfigRequest, manage::*},
api::{
config::GetConfigRequest,
manage::{
CollectNetworkInfoRequest, CollectNetworkInfoResponse,
DeleteNetworkInstanceRequest, DeleteNetworkInstanceResponse,
GetNetworkInstanceConfigRequest, GetNetworkInstanceConfigResponse,
ListNetworkInstanceMetaRequest, ListNetworkInstanceMetaResponse,
ListNetworkInstanceRequest, ListNetworkInstanceResponse,
NetworkInstanceRunningInfoMap, NetworkMeta, RetainNetworkInstanceRequest,
RetainNetworkInstanceResponse, RunNetworkInstanceRequest,
RunNetworkInstanceResponse, ValidateConfigRequest, ValidateConfigResponse,
WebClientService,
},
},
rpc_types::{self, controller::BaseController},
},
web_client::WebClientHooks,
@@ -44,53 +57,64 @@ impl WebClientService for InstanceManageRpcService {
return Err(anyhow::anyhow!("config is required").into());
}
let cfg = req.config.unwrap().gen_config()?;
let id = cfg.get_id();
let mut effective_id = cfg.get_id();
if let Some(inst_id) = req.inst_id {
cfg.set_id(inst_id.into());
effective_id = inst_id.into();
cfg.set_id(effective_id);
}
let requested_source = ConfigSource::from_rpc(req.source);
let resp = RunNetworkInstanceResponse {
inst_id: Some(id.into()),
inst_id: Some(effective_id.into()),
};
let mut control = if let Some(control) = self.manager.get_instance_config_control(&id) {
let error_msg = self
.manager
.get_network_info(&id)
.await
.and_then(|i| i.error_msg)
.unwrap_or_default();
let mut control =
if let Some(control) = self.manager.get_instance_config_control(&effective_id) {
let existing_source = self
.manager
.get_instance_network_config_source(&effective_id);
let error_msg = self
.manager
.get_network_info(&effective_id)
.await
.and_then(|i| i.error_msg)
.unwrap_or_default();
if !req.overwrite && error_msg.is_empty() {
return Ok(resp);
}
if control.is_read_only() {
return Err(
anyhow::anyhow!("instance {} is read-only, cannot be overwritten", id).into(),
);
}
if let Some(path) = control.path.as_ref() {
let real_control = ConfigFileControl::from_path(path.clone()).await;
if real_control.is_read_only() {
if !req.overwrite && error_msg.is_empty() {
return Ok(resp);
}
if control.is_read_only() {
return Err(anyhow::anyhow!(
"config file {} is read-only, cannot be overwritten",
path.display()
"instance {} is read-only, cannot be overwritten",
effective_id
)
.into());
}
}
self.manager.delete_network_instance(vec![id])?;
if let Some(path) = control.path.as_ref() {
let real_control = ConfigFileControl::from_path(path.clone()).await;
if real_control.is_read_only() {
return Err(anyhow::anyhow!(
"config file {} is read-only, cannot be overwritten",
path.display()
)
.into());
}
}
control.clone()
} else if let Some(config_dir) = self.manager.get_config_dir() {
ConfigFileControl::new(
Some(config_dir.join(format!("{}.toml", id))),
ConfigFilePermission::default(),
)
} else {
ConfigFileControl::new(None, ConfigFilePermission::default())
};
self.manager.delete_network_instance(vec![effective_id])?;
cfg.set_network_config_source(requested_source.or(existing_source));
control.clone()
} else if let Some(config_dir) = self.manager.get_config_dir() {
cfg.set_network_config_source(requested_source);
ConfigFileControl::new(
Some(config_dir.join(format!("{}.toml", effective_id))),
ConfigFilePermission::default(),
)
} else {
cfg.set_network_config_source(requested_source);
ConfigFileControl::new(None, ConfigFilePermission::default())
};
if !control.is_read_only()
&& let Some(config_file) = control.path.as_ref()
@@ -109,9 +133,9 @@ impl WebClientService for InstanceManageRpcService {
}
self.manager.run_network_instance(cfg, true, control)?;
println!("instance {} started", id);
println!("instance {} started", effective_id);
if let Err(e) = self.hooks.post_run_network_instance(&id).await {
if let Err(e) = self.hooks.post_run_network_instance(&effective_id).await {
tracing::warn!("post-run hook failed: {}", e);
}
@@ -261,7 +285,14 @@ impl WebClientService for InstanceManageRpcService {
.get_config(BaseController::default(), GetConfigRequest::default())
.await?
.config;
Ok(GetNetworkInstanceConfigResponse { config })
Ok(GetNetworkInstanceConfigResponse {
config,
source: self
.manager
.get_instance_network_config_source(&inst_id)
.unwrap_or(ConfigSource::User)
.to_rpc(),
})
}
async fn list_network_instance_meta(
@@ -286,6 +317,11 @@ impl WebClientService for InstanceManageRpcService {
network_name,
config_permission: control.permission.into(),
instance_name,
source: self
.manager
.get_instance_network_config_source(&inst_id)
.unwrap_or(ConfigSource::User)
.to_rpc(),
};
metas.push(meta);
}
+64 -11
View File
@@ -1,7 +1,18 @@
use async_trait::async_trait;
use uuid::Uuid;
use crate::proto::{api::manage::*, rpc_types::controller::BaseController};
use crate::{
common::config::ConfigSource,
proto::{
api::manage::{
CollectNetworkInfoRequest, CollectNetworkInfoResponse, DeleteNetworkInstanceRequest,
GetNetworkInstanceConfigRequest, ListNetworkInstanceMetaRequest,
ListNetworkInstanceRequest, NetworkConfig, NetworkMeta, RunNetworkInstanceRequest,
ValidateConfigRequest, ValidateConfigResponse, WebClientService,
},
rpc_types::controller::BaseController,
},
};
#[async_trait]
pub trait RemoteClientManager<T, C, E>
@@ -52,6 +63,7 @@ where
inst_id: None,
config: Some(config.clone()),
overwrite: true,
source: ConfigSource::User.to_rpc(),
},
)
.await?;
@@ -62,6 +74,7 @@ where
identify,
resp.inst_id.unwrap_or_default().into(),
config,
ConfigSource::User,
)
.await
.map_err(RemoteClientError::PersistentError)?;
@@ -162,13 +175,18 @@ where
.get_rpc_client(identify.clone())
.ok_or(RemoteClientError::ClientNotFound)?;
let cfg = self
.handle_get_network_config(identify.clone(), inst_id)
let (cfg, source) = self
.handle_get_network_config_with_source(identify.clone(), inst_id)
.await?;
if disabled {
self.get_storage()
.insert_or_update_user_network_config(identify.clone(), inst_id, cfg.clone())
.insert_or_update_user_network_config(
identify.clone(),
inst_id,
cfg.clone(),
source,
)
.await
.map_err(RemoteClientError::PersistentError)?;
@@ -188,6 +206,7 @@ where
inst_id: Some(inst_id.into()),
config: Some(cfg),
overwrite: true,
source: source.to_rpc(),
},
)
.await?;
@@ -230,8 +249,8 @@ where
if metas.contains_key(&instance_id) {
continue;
}
let config = self
.handle_get_network_config(identify.clone(), instance_id)
let (config, source) = self
.handle_get_network_config_with_source(identify.clone(), instance_id)
.await?;
let network_name = config.network_name.unwrap_or_default();
metas.insert(
@@ -241,6 +260,7 @@ where
network_name: network_name.clone(),
config_permission: 0,
instance_name: network_name,
source: source.to_rpc(),
},
);
}
@@ -255,7 +275,12 @@ where
config: NetworkConfig,
) -> Result<(), RemoteClientError<E>> {
self.get_storage()
.insert_or_update_user_network_config(identify.clone(), inst_id, config)
.insert_or_update_user_network_config(
identify.clone(),
inst_id,
config,
ConfigSource::User,
)
.await
.map_err(RemoteClientError::PersistentError)?;
self.get_storage()
@@ -270,6 +295,16 @@ where
identify: T,
inst_id: uuid::Uuid,
) -> Result<NetworkConfig, RemoteClientError<E>> {
self.handle_get_network_config_with_source(identify, inst_id)
.await
.map(|(config, _)| config)
}
async fn handle_get_network_config_with_source(
&self,
identify: T,
inst_id: uuid::Uuid,
) -> Result<(NetworkConfig, ConfigSource), RemoteClientError<E>> {
if let Some(client) = self.get_rpc_client(identify.clone())
&& let Ok(resp) = client
.get_network_instance_config(
@@ -281,7 +316,17 @@ where
.await
&& let Some(config) = resp.config
{
return Ok(config);
let source = if let Some(source) = ConfigSource::from_rpc(resp.source) {
source
} else {
self.get_storage()
.get_network_config(identify.clone(), &inst_id.to_string())
.await
.map_err(RemoteClientError::PersistentError)?
.map(|cfg| cfg.get_runtime_network_config_source())
.unwrap_or(ConfigSource::User)
};
return Ok((config, source));
}
let inst_id = inst_id.to_string();
@@ -296,9 +341,12 @@ where
inst_id
)))?;
Ok(db_row
.get_network_config()
.map_err(RemoteClientError::PersistentError)?)
Ok((
db_row
.get_network_config()
.map_err(RemoteClientError::PersistentError)?,
db_row.get_runtime_network_config_source(),
))
}
}
@@ -336,6 +384,10 @@ pub struct GetNetworkMetasResponse {
pub trait PersistentConfig<E> {
fn get_network_inst_id(&self) -> &str;
fn get_network_config(&self) -> Result<NetworkConfig, E>;
fn get_network_config_source(&self) -> ConfigSource;
fn get_runtime_network_config_source(&self) -> ConfigSource {
self.get_network_config_source()
}
}
#[async_trait]
@@ -348,6 +400,7 @@ where
identify: T,
network_inst_id: Uuid,
network_config: NetworkConfig,
source: ConfigSource,
) -> Result<(), E>;
async fn delete_network_configs(&self, identify: T, network_inst_ids: &[Uuid])