mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-05-13 17:35:37 +00:00
Honor credential reusable flag (#2157)
- propagate reusable through credential storage, CLI, RPC, routing, and tests - enforce reusable=false owner election with current topology - preserve proof-backed groups when refreshing credential ACL groups
This commit is contained in:
@@ -23,12 +23,12 @@ use rstest::rstest;
|
||||
|
||||
/// Prepare network namespaces for credential tests
|
||||
/// Topology:
|
||||
/// br_a (10.1.1.0/24): ns_adm (10.1.1.1), ns_c1 (10.1.1.2), ns_c2 (10.1.1.3), ns_c3 (10.1.1.4)
|
||||
/// br_a (10.1.1.0/24): ns_adm (10.1.1.1), ns_c1 (10.1.1.2), ns_c2 (10.1.1.3), ns_c3 (10.1.1.4), ns_c4 (10.1.1.5)
|
||||
/// br_b (10.1.2.0/24): ns_adm2 (10.1.2.1) - for multi-admin tests
|
||||
/// Note: Using short names (max 15 chars for veth interfaces)
|
||||
pub fn prepare_credential_network() {
|
||||
// Clean up any existing namespaces
|
||||
for ns in ["ns_adm", "ns_c1", "ns_c2", "ns_c3", "ns_adm2"] {
|
||||
for ns in ["ns_adm", "ns_c1", "ns_c2", "ns_c3", "ns_c4", "ns_adm2"] {
|
||||
del_netns(ns);
|
||||
}
|
||||
|
||||
@@ -61,6 +61,10 @@ pub fn prepare_credential_network() {
|
||||
create_netns("ns_c3", "10.1.1.4/24", "fd11::4/64");
|
||||
add_ns_to_bridge("br_a", "ns_c3");
|
||||
|
||||
// Create ns_c4 for multi-admin credential tests (needs 5 nodes)
|
||||
create_netns("ns_c4", "10.1.1.5/24", "fd11::5/64");
|
||||
add_ns_to_bridge("br_a", "ns_c4");
|
||||
|
||||
// Create bridge br_b for second admin (multi-admin tests)
|
||||
let _ = std::process::Command::new("ip")
|
||||
.args(["link", "del", "br_b"])
|
||||
@@ -80,6 +84,38 @@ pub fn prepare_credential_network() {
|
||||
add_ns_to_bridge("br_b", "ns_adm2");
|
||||
}
|
||||
|
||||
fn credential_private_key_from_secret(credential_secret: &str) -> x25519_dalek::StaticSecret {
|
||||
use base64::Engine as _;
|
||||
|
||||
let privkey_bytes: [u8; 32] = base64::prelude::BASE64_STANDARD
|
||||
.decode(credential_secret)
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
x25519_dalek::StaticSecret::from(privkey_bytes)
|
||||
}
|
||||
|
||||
fn build_credential_config(
|
||||
network_name: String,
|
||||
private_key: &x25519_dalek::StaticSecret,
|
||||
inst_name: &str,
|
||||
ns: Option<&str>,
|
||||
ipv4: &str,
|
||||
ipv6: &str,
|
||||
) -> TomlConfigLoader {
|
||||
let config = TomlConfigLoader::default();
|
||||
config.set_inst_name(inst_name.to_owned());
|
||||
config.set_netns(ns.map(|s| s.to_owned()));
|
||||
config.set_ipv4(Some(ipv4.parse().unwrap()));
|
||||
config.set_ipv6(Some(ipv6.parse().unwrap()));
|
||||
config.set_listeners(vec![]);
|
||||
config.set_network_identity(NetworkIdentity::new_credential(network_name));
|
||||
config.set_secure_mode(Some(generate_secure_mode_config_with_key(private_key)));
|
||||
|
||||
config
|
||||
}
|
||||
|
||||
/// Helper: Create credential node config with generated credential
|
||||
async fn create_credential_config(
|
||||
admin_inst: &Instance,
|
||||
@@ -88,39 +124,41 @@ async fn create_credential_config(
|
||||
ipv4: &str,
|
||||
ipv6: &str,
|
||||
) -> TomlConfigLoader {
|
||||
use base64::Engine as _;
|
||||
|
||||
// Generate credential on admin
|
||||
let (_cred_id, cred_secret) = admin_inst
|
||||
.get_global_ctx()
|
||||
.get_credential_manager()
|
||||
.generate_credential(vec![], false, vec![], Duration::from_secs(3600));
|
||||
|
||||
// Decode private key
|
||||
let privkey_bytes: [u8; 32] = base64::prelude::BASE64_STANDARD
|
||||
.decode(&cred_secret)
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let private = x25519_dalek::StaticSecret::from(privkey_bytes);
|
||||
|
||||
// Create config
|
||||
let config = TomlConfigLoader::default();
|
||||
config.set_inst_name(inst_name.to_owned());
|
||||
config.set_netns(ns.map(|s| s.to_owned()));
|
||||
config.set_ipv4(Some(ipv4.parse().unwrap()));
|
||||
config.set_ipv6(Some(ipv6.parse().unwrap()));
|
||||
config.set_listeners(vec![]);
|
||||
config.set_network_identity(NetworkIdentity::new_credential(
|
||||
build_credential_config(
|
||||
admin_inst
|
||||
.get_global_ctx()
|
||||
.get_network_identity()
|
||||
.network_name
|
||||
.clone(),
|
||||
));
|
||||
config.set_secure_mode(Some(generate_secure_mode_config_with_key(&private)));
|
||||
&credential_private_key_from_secret(&cred_secret),
|
||||
inst_name,
|
||||
ns,
|
||||
ipv4,
|
||||
ipv6,
|
||||
)
|
||||
}
|
||||
|
||||
config
|
||||
fn create_credential_config_from_secret(
|
||||
network_name: String,
|
||||
credential_secret: &str,
|
||||
inst_name: &str,
|
||||
ns: Option<&str>,
|
||||
ipv4: &str,
|
||||
ipv6: &str,
|
||||
) -> TomlConfigLoader {
|
||||
build_credential_config(
|
||||
network_name,
|
||||
&credential_private_key_from_secret(credential_secret),
|
||||
inst_name,
|
||||
ns,
|
||||
ipv4,
|
||||
ipv6,
|
||||
)
|
||||
}
|
||||
|
||||
/// Helper: Create credential node config with a random, unknown key
|
||||
@@ -132,17 +170,7 @@ fn create_unknown_credential_config(
|
||||
ipv6: &str,
|
||||
) -> TomlConfigLoader {
|
||||
let random_private = x25519_dalek::StaticSecret::random_from_rng(rand::rngs::OsRng);
|
||||
|
||||
let config = TomlConfigLoader::default();
|
||||
config.set_inst_name(inst_name.to_owned());
|
||||
config.set_netns(ns.map(|s| s.to_owned()));
|
||||
config.set_ipv4(Some(ipv4.parse().unwrap()));
|
||||
config.set_ipv6(Some(ipv6.parse().unwrap()));
|
||||
config.set_listeners(vec![]);
|
||||
config.set_network_identity(NetworkIdentity::new_credential(network_name));
|
||||
config.set_secure_mode(Some(generate_secure_mode_config_with_key(&random_private)));
|
||||
|
||||
config
|
||||
build_credential_config(network_name, &random_private, inst_name, ns, ipv4, ipv6)
|
||||
}
|
||||
|
||||
/// Helper: Create admin node config
|
||||
@@ -200,33 +228,22 @@ fn create_generated_credential_config(
|
||||
ipv4: &str,
|
||||
ipv6: &str,
|
||||
) -> (TomlConfigLoader, String) {
|
||||
use base64::Engine as _;
|
||||
|
||||
let (cred_id, cred_secret) = admin_inst
|
||||
.get_global_ctx()
|
||||
.get_credential_manager()
|
||||
.generate_credential(vec![], false, vec![], Duration::from_secs(3600));
|
||||
let privkey_bytes: [u8; 32] = base64::prelude::BASE64_STANDARD
|
||||
.decode(&cred_secret)
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let private = x25519_dalek::StaticSecret::from(privkey_bytes);
|
||||
|
||||
let config = TomlConfigLoader::default();
|
||||
config.set_inst_name(inst_name.to_owned());
|
||||
config.set_netns(ns.map(|s| s.to_owned()));
|
||||
config.set_ipv4(Some(ipv4.parse().unwrap()));
|
||||
config.set_ipv6(Some(ipv6.parse().unwrap()));
|
||||
config.set_listeners(vec![]);
|
||||
config.set_network_identity(NetworkIdentity::new_credential(
|
||||
let config = build_credential_config(
|
||||
admin_inst
|
||||
.get_global_ctx()
|
||||
.get_network_identity()
|
||||
.network_name
|
||||
.clone(),
|
||||
));
|
||||
config.set_secure_mode(Some(generate_secure_mode_config_with_key(&private)));
|
||||
&credential_private_key_from_secret(&cred_secret),
|
||||
inst_name,
|
||||
ns,
|
||||
ipv4,
|
||||
ipv6,
|
||||
);
|
||||
|
||||
(config, cred_id)
|
||||
}
|
||||
@@ -311,6 +328,68 @@ async fn assert_shared_visibility_stable(
|
||||
}
|
||||
}
|
||||
|
||||
async fn wait_stable_single_visible_peer_on_admins(
|
||||
admin_a_inst: &Instance,
|
||||
admin_c_inst: &Instance,
|
||||
peer_a_id: u32,
|
||||
peer_b_id: u32,
|
||||
timeout: Duration,
|
||||
) -> u32 {
|
||||
let start = std::time::Instant::now();
|
||||
let mut stable_winner = None;
|
||||
let mut stable_samples = 0;
|
||||
|
||||
loop {
|
||||
let admin_a_routes = admin_a_inst.get_peer_manager().list_routes().await;
|
||||
let admin_c_routes = admin_c_inst.get_peer_manager().list_routes().await;
|
||||
|
||||
let admin_a_has_a = admin_a_routes.iter().any(|r| r.peer_id == peer_a_id);
|
||||
let admin_a_has_b = admin_a_routes.iter().any(|r| r.peer_id == peer_b_id);
|
||||
let admin_c_has_a = admin_c_routes.iter().any(|r| r.peer_id == peer_a_id);
|
||||
let admin_c_has_b = admin_c_routes.iter().any(|r| r.peer_id == peer_b_id);
|
||||
|
||||
let current_winner = if admin_a_has_a && admin_c_has_a && !admin_a_has_b && !admin_c_has_b {
|
||||
Some(peer_a_id)
|
||||
} else if admin_a_has_b && admin_c_has_b && !admin_a_has_a && !admin_c_has_a {
|
||||
Some(peer_b_id)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
println!(
|
||||
"single-visible routes: a={:?} c={:?} winner={:?}",
|
||||
admin_a_routes.iter().map(|r| r.peer_id).collect::<Vec<_>>(),
|
||||
admin_c_routes.iter().map(|r| r.peer_id).collect::<Vec<_>>(),
|
||||
current_winner
|
||||
);
|
||||
|
||||
match current_winner {
|
||||
Some(winner) if stable_winner == Some(winner) => stable_samples += 1,
|
||||
Some(winner) => {
|
||||
stable_winner = Some(winner);
|
||||
stable_samples = 1;
|
||||
}
|
||||
None => {
|
||||
stable_winner = None;
|
||||
stable_samples = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if stable_samples >= 3 {
|
||||
return stable_winner.unwrap();
|
||||
}
|
||||
|
||||
assert!(
|
||||
start.elapsed() < timeout,
|
||||
"timed out waiting for a stable single visible peer on both admins: a={:?} c={:?}",
|
||||
admin_a_routes.iter().map(|r| r.peer_id).collect::<Vec<_>>(),
|
||||
admin_c_routes.iter().map(|r| r.peer_id).collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Test 1: Basic credential node connectivity
|
||||
/// Topology: Admin ← Credential
|
||||
/// Verifies that a credential node can connect to an admin node and appears in routes
|
||||
@@ -884,6 +963,161 @@ async fn credential_revocation_propagates() {
|
||||
drop_insts(vec![admin_inst, cred_inst]).await;
|
||||
}
|
||||
|
||||
/// Test: A non-reusable credential only allows one peer at a time.
|
||||
#[tokio::test]
|
||||
#[serial_test::serial]
|
||||
async fn credential_non_reusable_allows_only_one_peer() {
|
||||
prepare_credential_network();
|
||||
|
||||
let admin_config = create_admin_config("admin", Some("ns_adm"), "10.144.144.1", "fd00::1/64");
|
||||
let mut admin_inst = Instance::new(admin_config);
|
||||
admin_inst.run().await.unwrap();
|
||||
|
||||
let (_cred_id, cred_secret) = admin_inst
|
||||
.get_global_ctx()
|
||||
.get_credential_manager()
|
||||
.generate_credential_with_options(
|
||||
vec![],
|
||||
false,
|
||||
vec![],
|
||||
Duration::from_secs(3600),
|
||||
None,
|
||||
false,
|
||||
);
|
||||
|
||||
let network_name = admin_inst
|
||||
.get_global_ctx()
|
||||
.get_network_identity()
|
||||
.network_name
|
||||
.clone();
|
||||
let cred1_config = create_credential_config_from_secret(
|
||||
network_name.clone(),
|
||||
&cred_secret,
|
||||
"cred1_single",
|
||||
Some("ns_c1"),
|
||||
"10.144.144.2",
|
||||
"fd00::2/64",
|
||||
);
|
||||
let cred2_config = create_credential_config_from_secret(
|
||||
network_name,
|
||||
&cred_secret,
|
||||
"cred2_single",
|
||||
Some("ns_c2"),
|
||||
"10.144.144.3",
|
||||
"fd00::3/64",
|
||||
);
|
||||
|
||||
let mut cred1_inst = Some(Instance::new(cred1_config));
|
||||
cred1_inst.as_mut().unwrap().run().await.unwrap();
|
||||
cred1_inst
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get_conn_manager()
|
||||
.add_connector(TcpTunnelConnector::new(
|
||||
"tcp://10.1.1.1:11010".parse().unwrap(),
|
||||
));
|
||||
|
||||
let cred1_peer_id = cred1_inst.as_ref().unwrap().peer_id();
|
||||
wait_for_condition(
|
||||
|| async {
|
||||
admin_inst
|
||||
.get_peer_manager()
|
||||
.list_routes()
|
||||
.await
|
||||
.iter()
|
||||
.any(|r| r.peer_id == cred1_peer_id)
|
||||
},
|
||||
Duration::from_secs(10),
|
||||
)
|
||||
.await;
|
||||
wait_ping_reachability("ns_adm", "10.144.144.2", true, Duration::from_secs(10)).await;
|
||||
|
||||
let mut cred2_inst = Some(Instance::new(cred2_config));
|
||||
cred2_inst.as_mut().unwrap().run().await.unwrap();
|
||||
cred2_inst
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get_conn_manager()
|
||||
.add_connector(TcpTunnelConnector::new(
|
||||
"tcp://10.1.1.1:11010".parse().unwrap(),
|
||||
));
|
||||
|
||||
let cred2_peer_id = cred2_inst.as_ref().unwrap().peer_id();
|
||||
tokio::time::sleep(Duration::from_secs(3)).await;
|
||||
|
||||
// The non-reusable credential owner is elected by lowest peer_id, so either cred1 or cred2
|
||||
// may win. Determine the winner and loser dynamically.
|
||||
let admin_routes = admin_inst.get_peer_manager().list_routes().await;
|
||||
let (winner_peer_id, winner_ip, winner_inst, loser_peer_id, loser_ip, loser_inst) =
|
||||
if admin_routes.iter().any(|r| r.peer_id == cred1_peer_id) {
|
||||
(
|
||||
cred1_peer_id,
|
||||
"10.144.144.2",
|
||||
&mut cred1_inst,
|
||||
cred2_peer_id,
|
||||
"10.144.144.3",
|
||||
&mut cred2_inst,
|
||||
)
|
||||
} else if admin_routes.iter().any(|r| r.peer_id == cred2_peer_id) {
|
||||
(
|
||||
cred2_peer_id,
|
||||
"10.144.144.3",
|
||||
&mut cred2_inst,
|
||||
cred1_peer_id,
|
||||
"10.144.144.2",
|
||||
&mut cred1_inst,
|
||||
)
|
||||
} else {
|
||||
panic!(
|
||||
"neither credential peer is present in routes: {:?}",
|
||||
admin_routes.iter().map(|r| r.peer_id).collect::<Vec<_>>()
|
||||
);
|
||||
};
|
||||
|
||||
for _ in 0..5 {
|
||||
let admin_routes = admin_inst.get_peer_manager().list_routes().await;
|
||||
assert!(
|
||||
admin_routes.iter().any(|r| r.peer_id == winner_peer_id),
|
||||
"winning credential peer should remain present: {:?}",
|
||||
admin_routes.iter().map(|r| r.peer_id).collect::<Vec<_>>()
|
||||
);
|
||||
assert!(
|
||||
ping_test("ns_adm", winner_ip, None).await,
|
||||
"admin should still reach the winning credential peer"
|
||||
);
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
|
||||
for _ in 0..5 {
|
||||
let admin_routes = admin_inst.get_peer_manager().list_routes().await;
|
||||
assert!(
|
||||
!admin_routes.iter().any(|r| r.peer_id == loser_peer_id),
|
||||
"losing credential peer should not appear in routes: {:?}",
|
||||
admin_routes.iter().map(|r| r.peer_id).collect::<Vec<_>>()
|
||||
);
|
||||
assert!(
|
||||
!ping_test("ns_adm", loser_ip, None).await,
|
||||
"admin should not reach the losing credential peer"
|
||||
);
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
|
||||
drop_insts(vec![winner_inst.take().unwrap()]).await;
|
||||
|
||||
wait_for_condition(
|
||||
|| async {
|
||||
let routes = admin_inst.get_peer_manager().list_routes().await;
|
||||
!routes.iter().any(|r| r.peer_id == winner_peer_id)
|
||||
&& routes.iter().any(|r| r.peer_id == loser_peer_id)
|
||||
},
|
||||
Duration::from_secs(20),
|
||||
)
|
||||
.await;
|
||||
wait_ping_reachability("ns_adm", loser_ip, true, Duration::from_secs(20)).await;
|
||||
|
||||
drop_insts(vec![admin_inst, loser_inst.take().unwrap()]).await;
|
||||
}
|
||||
|
||||
/// Test 4: Unknown credential (not in trusted list) is rejected
|
||||
/// Topology: Admin
|
||||
/// Verifies that credential nodes with unknown/random keys cannot connect
|
||||
@@ -1226,3 +1460,217 @@ async fn credential_admin_shared_admin_credential_connectivity(
|
||||
|
||||
drop_insts(vec![admin_a_inst, shared_b_inst, admin_c_inst, cred_d_inst]).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial_test::serial]
|
||||
async fn credential_non_reusable_across_two_admins_allows_only_one_peer() {
|
||||
prepare_credential_network();
|
||||
|
||||
let admin_a_config =
|
||||
create_admin_config("admin_a", Some("ns_adm"), "10.144.144.1", "fd00::1/64");
|
||||
let mut admin_a_inst = Instance::new(admin_a_config);
|
||||
admin_a_inst.run().await.unwrap();
|
||||
|
||||
let shared_b_config =
|
||||
create_shared_config("shared_b", Some("ns_c1"), "10.144.144.2", "fd00::2/64");
|
||||
let mut shared_b_inst = Instance::new(shared_b_config);
|
||||
shared_b_inst.run().await.unwrap();
|
||||
|
||||
let admin_c_config =
|
||||
create_admin_config("admin_c", Some("ns_c3"), "10.144.144.4", "fd00::4/64");
|
||||
let mut admin_c_inst = Instance::new(admin_c_config);
|
||||
admin_c_inst.run().await.unwrap();
|
||||
|
||||
admin_a_inst
|
||||
.get_conn_manager()
|
||||
.add_connector(TcpTunnelConnector::new(
|
||||
"tcp://10.1.1.2:11010".parse().unwrap(),
|
||||
));
|
||||
admin_c_inst
|
||||
.get_conn_manager()
|
||||
.add_connector(TcpTunnelConnector::new(
|
||||
"tcp://10.1.1.2:11010".parse().unwrap(),
|
||||
));
|
||||
|
||||
let admin_c_peer_id = admin_c_inst.peer_id();
|
||||
wait_for_condition(
|
||||
|| async {
|
||||
let a_routes = admin_a_inst.get_peer_manager().list_routes().await;
|
||||
let c_routes = admin_c_inst.get_peer_manager().list_routes().await;
|
||||
a_routes.iter().any(|r| r.peer_id == admin_c_peer_id)
|
||||
|| c_routes.iter().any(|r| r.peer_id == admin_a_inst.peer_id())
|
||||
},
|
||||
Duration::from_secs(10),
|
||||
)
|
||||
.await;
|
||||
|
||||
let (_cred_id, cred_secret) = admin_a_inst
|
||||
.get_global_ctx()
|
||||
.get_credential_manager()
|
||||
.generate_credential_with_options(
|
||||
vec![],
|
||||
false,
|
||||
vec![],
|
||||
Duration::from_secs(3600),
|
||||
None,
|
||||
false,
|
||||
);
|
||||
admin_a_inst
|
||||
.get_global_ctx()
|
||||
.issue_event(GlobalCtxEvent::CredentialChanged);
|
||||
|
||||
let network_name = admin_a_inst
|
||||
.get_global_ctx()
|
||||
.get_network_identity()
|
||||
.network_name
|
||||
.clone();
|
||||
let cred_left_config = create_credential_config_from_secret(
|
||||
network_name.clone(),
|
||||
&cred_secret,
|
||||
"cred_left",
|
||||
Some("ns_c2"),
|
||||
"10.144.144.5",
|
||||
"fd00::5/64",
|
||||
);
|
||||
let cred_right_config = create_credential_config_from_secret(
|
||||
network_name,
|
||||
&cred_secret,
|
||||
"cred_right",
|
||||
Some("ns_c4"),
|
||||
"10.144.144.6",
|
||||
"fd00::6/64",
|
||||
);
|
||||
|
||||
let mut cred_left_inst = Some(Instance::new(cred_left_config));
|
||||
cred_left_inst.as_mut().unwrap().run().await.unwrap();
|
||||
cred_left_inst
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get_conn_manager()
|
||||
.add_connector(TcpTunnelConnector::new(
|
||||
"tcp://10.1.1.1:11010".parse().unwrap(),
|
||||
));
|
||||
|
||||
let mut cred_right_inst = Some(Instance::new(cred_right_config));
|
||||
cred_right_inst.as_mut().unwrap().run().await.unwrap();
|
||||
cred_right_inst
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get_conn_manager()
|
||||
.add_connector(TcpTunnelConnector::new(
|
||||
"tcp://10.1.1.4:11010".parse().unwrap(),
|
||||
));
|
||||
|
||||
let cred_left_peer_id = cred_left_inst.as_ref().unwrap().peer_id();
|
||||
let cred_right_peer_id = cred_right_inst.as_ref().unwrap().peer_id();
|
||||
let winner_peer_id = wait_stable_single_visible_peer_on_admins(
|
||||
&admin_a_inst,
|
||||
&admin_c_inst,
|
||||
cred_left_peer_id,
|
||||
cred_right_peer_id,
|
||||
Duration::from_secs(60),
|
||||
)
|
||||
.await;
|
||||
|
||||
let (winner_peer_id, winner_ip, loser_peer_id, loser_ip) =
|
||||
if winner_peer_id == cred_left_peer_id {
|
||||
(
|
||||
cred_left_peer_id,
|
||||
"10.144.144.5",
|
||||
cred_right_peer_id,
|
||||
"10.144.144.6",
|
||||
)
|
||||
} else {
|
||||
(
|
||||
cred_right_peer_id,
|
||||
"10.144.144.6",
|
||||
cred_left_peer_id,
|
||||
"10.144.144.5",
|
||||
)
|
||||
};
|
||||
|
||||
wait_ping_reachability("ns_adm", winner_ip, true, Duration::from_secs(20)).await;
|
||||
wait_ping_reachability("ns_c3", winner_ip, true, Duration::from_secs(20)).await;
|
||||
wait_ping_reachability("ns_adm", loser_ip, false, Duration::from_secs(5)).await;
|
||||
wait_ping_reachability("ns_c3", loser_ip, false, Duration::from_secs(5)).await;
|
||||
|
||||
assert_shared_visibility_stable(
|
||||
&admin_a_inst,
|
||||
&admin_c_inst,
|
||||
winner_peer_id,
|
||||
winner_ip,
|
||||
true,
|
||||
"winning credential",
|
||||
)
|
||||
.await;
|
||||
assert_shared_visibility_stable(
|
||||
&admin_a_inst,
|
||||
&admin_c_inst,
|
||||
loser_peer_id,
|
||||
loser_ip,
|
||||
false,
|
||||
"losing credential",
|
||||
)
|
||||
.await;
|
||||
|
||||
if winner_peer_id == cred_left_peer_id {
|
||||
drop_insts(vec![cred_left_inst.take().unwrap()]).await;
|
||||
} else {
|
||||
drop_insts(vec![cred_right_inst.take().unwrap()]).await;
|
||||
}
|
||||
|
||||
wait_for_condition(
|
||||
|| async {
|
||||
let admin_a_routes = admin_a_inst.get_peer_manager().list_routes().await;
|
||||
let admin_c_routes = admin_c_inst.get_peer_manager().list_routes().await;
|
||||
admin_a_routes.iter().any(|r| r.peer_id == loser_peer_id)
|
||||
&& admin_c_routes.iter().any(|r| r.peer_id == loser_peer_id)
|
||||
&& !admin_a_routes.iter().any(|r| r.peer_id == winner_peer_id)
|
||||
&& !admin_c_routes.iter().any(|r| r.peer_id == winner_peer_id)
|
||||
},
|
||||
Duration::from_secs(60),
|
||||
)
|
||||
.await;
|
||||
|
||||
wait_ping_reachability("ns_adm", loser_ip, true, Duration::from_secs(20)).await;
|
||||
wait_ping_reachability("ns_c3", loser_ip, true, Duration::from_secs(20)).await;
|
||||
wait_ping_reachability("ns_adm", winner_ip, false, Duration::from_secs(5)).await;
|
||||
wait_ping_reachability("ns_c3", winner_ip, false, Duration::from_secs(5)).await;
|
||||
|
||||
assert_shared_visibility_stable(
|
||||
&admin_a_inst,
|
||||
&admin_c_inst,
|
||||
loser_peer_id,
|
||||
loser_ip,
|
||||
true,
|
||||
"failover credential",
|
||||
)
|
||||
.await;
|
||||
assert_shared_visibility_stable(
|
||||
&admin_a_inst,
|
||||
&admin_c_inst,
|
||||
winner_peer_id,
|
||||
winner_ip,
|
||||
false,
|
||||
"removed winning credential",
|
||||
)
|
||||
.await;
|
||||
|
||||
if winner_peer_id == cred_left_peer_id {
|
||||
drop_insts(vec![
|
||||
admin_a_inst,
|
||||
shared_b_inst,
|
||||
admin_c_inst,
|
||||
cred_right_inst.take().unwrap(),
|
||||
])
|
||||
.await;
|
||||
} else {
|
||||
drop_insts(vec![
|
||||
admin_a_inst,
|
||||
shared_b_inst,
|
||||
admin_c_inst,
|
||||
cred_left_inst.take().unwrap(),
|
||||
])
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user