mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-09-03 01:25:37 +00:00
feat: stabilize mobile runtime and VPN portal (#2536)
This commit is contained in:
@@ -568,15 +568,15 @@ mod tests {
|
||||
fn vpn_portal_client_changes_produce_hot_patches() {
|
||||
let current = config_with_vpn_portal(
|
||||
vec![
|
||||
portal_client("alice", "10.144.144.4"),
|
||||
portal_client("carol", "10.144.144.6"),
|
||||
portal_client("alice", "10.144.144.4/24"),
|
||||
portal_client("carol", "10.144.144.6/24"),
|
||||
],
|
||||
"0.0.0.0:22121",
|
||||
);
|
||||
let desired = config_with_vpn_portal(
|
||||
vec![
|
||||
portal_client("bob", "10.144.144.5"),
|
||||
portal_client("carol", "10.144.144.7"),
|
||||
portal_client("bob", "10.144.144.5/24"),
|
||||
portal_client("carol", "10.144.144.7/24"),
|
||||
],
|
||||
"0.0.0.0:22121",
|
||||
);
|
||||
@@ -595,16 +595,24 @@ mod tests {
|
||||
],
|
||||
"removals must precede additions; changed clients are remove+add"
|
||||
);
|
||||
let added = patch
|
||||
.vpn_portal_clients
|
||||
.iter()
|
||||
.filter(|item| item.action == ConfigPatchAction::Add as i32)
|
||||
.filter_map(|item| item.client.as_ref())
|
||||
.map(|client| client.virtual_ip.as_str())
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(added, ["10.144.144.5/24", "10.144.144.7/24"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn vpn_portal_client_no_op_produces_empty_patch_section() {
|
||||
let current = config_with_vpn_portal(
|
||||
vec![portal_client("alice", "10.144.144.4")],
|
||||
vec![portal_client("alice", "10.144.144.4/24")],
|
||||
"0.0.0.0:22121",
|
||||
);
|
||||
let desired = config_with_vpn_portal(
|
||||
vec![portal_client("alice", "10.144.144.4")],
|
||||
vec![portal_client("alice", "10.144.144.4/24")],
|
||||
"0.0.0.0:22121",
|
||||
);
|
||||
|
||||
@@ -617,11 +625,11 @@ mod tests {
|
||||
#[test]
|
||||
fn vpn_portal_listener_identity_change_requires_recreate() {
|
||||
let current = config_with_vpn_portal(
|
||||
vec![portal_client("alice", "10.144.144.4")],
|
||||
vec![portal_client("alice", "10.144.144.4/24")],
|
||||
"0.0.0.0:22121",
|
||||
);
|
||||
let desired = config_with_vpn_portal(
|
||||
vec![portal_client("alice", "10.144.144.4")],
|
||||
vec![portal_client("alice", "10.144.144.4/24")],
|
||||
"0.0.0.0:22122",
|
||||
);
|
||||
assert!(
|
||||
@@ -652,7 +660,7 @@ mod tests {
|
||||
fn vpn_portal_enable_or_disable_requires_recreate() {
|
||||
let without_portal = config_with_port_forwards(Vec::new());
|
||||
let with_portal = config_with_vpn_portal(
|
||||
vec![portal_client("alice", "10.144.144.4")],
|
||||
vec![portal_client("alice", "10.144.144.4/24")],
|
||||
"0.0.0.0:22121",
|
||||
);
|
||||
|
||||
|
||||
@@ -1087,19 +1087,33 @@ async fn mark_config_revision_applied_if_current(
|
||||
let Some(data) = session_data.upgrade() else {
|
||||
return RoundStatus::Stop;
|
||||
};
|
||||
let mut data = data.write().await;
|
||||
if !SessionRpcService::runtime_heartbeat_is_current_locked(&data, &round.req) {
|
||||
return RoundStatus::Ready(());
|
||||
let notify = {
|
||||
let mut data = data.write().await;
|
||||
if !SessionRpcService::runtime_heartbeat_is_current_locked(&data, &round.req) {
|
||||
return RoundStatus::Ready(());
|
||||
}
|
||||
if data.runtime_config_epoch != round.runtime_config_epoch {
|
||||
return RoundStatus::Ready(());
|
||||
}
|
||||
record_applied_config_revision(&mut data, round.target_config_revision.clone())
|
||||
};
|
||||
if let Some(notify) = notify {
|
||||
notify.notify_one();
|
||||
}
|
||||
if data.runtime_config_epoch != round.runtime_config_epoch {
|
||||
return RoundStatus::Ready(());
|
||||
}
|
||||
data.applied_config_revision = round.target_config_revision.clone();
|
||||
data.pending_managed_config_delta = None;
|
||||
|
||||
RoundStatus::Ready(())
|
||||
}
|
||||
|
||||
fn record_applied_config_revision(
|
||||
data: &mut SessionData,
|
||||
revision: Option<String>,
|
||||
) -> Option<std::sync::Arc<tokio::sync::Notify>> {
|
||||
let changed = data.applied_config_revision != revision;
|
||||
data.applied_config_revision = revision;
|
||||
data.pending_managed_config_delta = None;
|
||||
changed.then(|| SessionRpcService::mark_webhook_validation_dirty_locked(data))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use easytier::proto::api::manage::{NetworkingMethod, PortForwardConfig};
|
||||
@@ -1128,6 +1142,52 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn newly_applied_revision_wakes_webhook_validation() {
|
||||
let storage =
|
||||
crate::client_manager::storage::Storage::new(crate::db::Db::memory_db().await);
|
||||
let mut data = SessionData::new(
|
||||
storage.weak_ref(),
|
||||
url::Url::parse("http://127.0.0.1").unwrap(),
|
||||
None,
|
||||
std::sync::Arc::new(crate::FeatureFlags::default()),
|
||||
std::sync::Arc::new(crate::webhook::WebhookConfig::new(
|
||||
None, None, None, None, None,
|
||||
)),
|
||||
);
|
||||
|
||||
let notify = record_applied_config_revision(&mut data, Some("rev-applied".to_string()))
|
||||
.expect("new applied revision should wake validation");
|
||||
assert_eq!(data.applied_config_revision.as_deref(), Some("rev-applied"));
|
||||
assert!(data.webhook_validation_dirty);
|
||||
|
||||
notify.notify_one();
|
||||
tokio::time::timeout(std::time::Duration::from_millis(100), notify.notified())
|
||||
.await
|
||||
.expect("validation worker was not notified");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn unchanged_applied_revision_does_not_add_validation_work() {
|
||||
let storage =
|
||||
crate::client_manager::storage::Storage::new(crate::db::Db::memory_db().await);
|
||||
let mut data = SessionData::new(
|
||||
storage.weak_ref(),
|
||||
url::Url::parse("http://127.0.0.1").unwrap(),
|
||||
None,
|
||||
std::sync::Arc::new(crate::FeatureFlags::default()),
|
||||
std::sync::Arc::new(crate::webhook::WebhookConfig::new(
|
||||
None, None, None, None, None,
|
||||
)),
|
||||
);
|
||||
data.applied_config_revision = Some("rev-applied".to_string());
|
||||
|
||||
assert!(
|
||||
record_applied_config_revision(&mut data, Some("rev-applied".to_string())).is_none()
|
||||
);
|
||||
assert!(!data.webhook_validation_dirty);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn patch_delete_requires_runtime_to_remove_every_requested_instance() {
|
||||
let deleted_id = uuid::Uuid::new_v4();
|
||||
|
||||
Reference in New Issue
Block a user