Skip to content

Commit 7aaa6b1

Browse files
committed
unit test inbound
1 parent 2b2d3ff commit 7aaa6b1

File tree

2 files changed

+191
-5
lines changed

2 files changed

+191
-5
lines changed

src/proxy.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -629,11 +629,7 @@ mod tests {
629629
workload::gatewayaddress::Destination,
630630
},
631631
};
632-
use std::{
633-
collections::HashMap,
634-
net::{Ipv4Addr, SocketAddrV4},
635-
sync::RwLock,
636-
};
632+
use std::{collections::HashMap, net::Ipv4Addr, sync::RwLock};
637633

638634
#[tokio::test]
639635
async fn check_gateway() {

src/proxy/inbound.rs

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,3 +579,193 @@ impl crate::tls::ServerCertProvider for InboundCertProvider {
579579
}
580580
}
581581

582+
#[cfg(test)]
583+
mod tests {
584+
use super::Inbound;
585+
586+
use std::{
587+
net::SocketAddr,
588+
sync::{Arc, RwLock},
589+
};
590+
591+
use crate::{
592+
rbac::Connection,
593+
state::{
594+
self,
595+
service::{endpoint_uid, Endpoint, Service},
596+
workload::{
597+
gatewayaddress::Destination, GatewayAddress, NamespacedHostname, NetworkAddress,
598+
Protocol, Workload,
599+
},
600+
DemandProxyState,
601+
},
602+
test_helpers,
603+
};
604+
605+
use hickory_resolver::config::{ResolverConfig, ResolverOpts};
606+
use test_case::test_case;
607+
608+
const CLIENT_POD_IP: &str = "10.0.0.1";
609+
610+
const SERVER_POD_IP: &str = "10.0.0.2";
611+
const SERVER_SVC_IP: &str = "10.10.0.1";
612+
613+
const WAYPOINT_POD_IP: &str = "10.0.0.3";
614+
const WAYPOINT_SVC_IP: &str = "10.10.0.2";
615+
616+
const HBONE_TARGET_PORT: u16 = 8080;
617+
618+
// Regular zTunnel workload traffic inbound
619+
#[test_case(Waypoint::None, SERVER_POD_IP, SERVER_POD_IP , Some((SERVER_POD_IP, HBONE_TARGET_PORT)); "to workload no waypoint")]
620+
// Waypoint is referenced directly by Pod IP
621+
#[test_case(Waypoint::Workload(WAYPOINT_POD_IP), WAYPOINT_POD_IP, SERVER_POD_IP , Some((WAYPOINT_POD_IP, HBONE_TARGET_PORT)); "to workload with waypoint pod")]
622+
#[test_case(Waypoint::Workload(WAYPOINT_SVC_IP), WAYPOINT_POD_IP, SERVER_POD_IP , Some((WAYPOINT_POD_IP, HBONE_TARGET_PORT)); "to workload with waypoint svc")]
623+
// Waypoint is referenced through it's service VIP
624+
#[test_case(Waypoint::Service(WAYPOINT_POD_IP), WAYPOINT_POD_IP, SERVER_SVC_IP , Some((WAYPOINT_POD_IP, HBONE_TARGET_PORT)); "to service with waypoint pod")]
625+
#[test_case(Waypoint::Service(WAYPOINT_SVC_IP), WAYPOINT_POD_IP, SERVER_SVC_IP , Some((WAYPOINT_POD_IP, HBONE_TARGET_PORT)); "to service with waypoint svc")]
626+
// Error cases
627+
#[test_case(Waypoint::None, SERVER_POD_IP, CLIENT_POD_IP, None; "to server ip mismatch" )]
628+
#[tokio::test]
629+
async fn test_find_inbound_upstream<'a>(
630+
target_waypoint: Waypoint<'a>,
631+
connection_dst: &str,
632+
hbone_dst: &str,
633+
want: Option<(&str, u16)>,
634+
) {
635+
let state = test_state(target_waypoint).expect("state setup");
636+
let conn = Connection {
637+
src_identity: None,
638+
src_ip: CLIENT_POD_IP.parse().unwrap(),
639+
dst_network: "".to_string(),
640+
dst: format!("{connection_dst}:15008").parse().unwrap(),
641+
};
642+
let res = Inbound::find_inbound_upstream(
643+
state,
644+
&conn,
645+
format!("{hbone_dst}:{HBONE_TARGET_PORT}").parse().unwrap(),
646+
)
647+
.await;
648+
649+
match want {
650+
Some((ip, port)) => {
651+
let got_addr = res.expect("found upstream").0;
652+
assert_eq!(got_addr, SocketAddr::new(ip.parse().unwrap(), port))
653+
}
654+
None => {
655+
res.expect_err("did not find upstream");
656+
}
657+
}
658+
}
659+
660+
fn test_state(server_waypoint: Waypoint) -> anyhow::Result<state::DemandProxyState> {
661+
let mut state = state::ProxyState::default();
662+
663+
let services = vec![
664+
("waypoint", WAYPOINT_SVC_IP, WAYPOINT_POD_IP, Waypoint::None),
665+
("server", SERVER_SVC_IP, SERVER_POD_IP, server_waypoint),
666+
]
667+
.into_iter()
668+
.map(|(name, vip, ep_ip, waypoint)| {
669+
let ep_uid = format!("cluster1//v1/Pod/default/{name}");
670+
let ep_addr = Some(NetworkAddress {
671+
address: ep_ip.parse().unwrap(),
672+
network: "".to_string(),
673+
});
674+
Service {
675+
name: name.to_string(),
676+
namespace: "default".to_string(),
677+
hostname: format!("{name}.default.svc.cluster.local"),
678+
vips: vec![NetworkAddress {
679+
address: vip.parse().unwrap(),
680+
network: "".to_string(),
681+
}],
682+
ports: std::collections::HashMap::new(),
683+
endpoints: vec![(
684+
endpoint_uid(&ep_uid, ep_addr.as_ref()),
685+
Endpoint {
686+
workload_uid: ep_uid,
687+
service: NamespacedHostname {
688+
hostname: format!("{name}.default.svc.cluster.local"),
689+
namespace: "default".to_string(),
690+
},
691+
address: ep_addr,
692+
port: std::collections::HashMap::new(),
693+
},
694+
)]
695+
.into_iter()
696+
.collect(),
697+
subject_alt_names: vec![format!("{name}.default.svc.cluster.local")],
698+
waypoint: waypoint.service_attached(),
699+
}
700+
});
701+
702+
let workloads = vec![
703+
("waypoint", WAYPOINT_POD_IP, Waypoint::None),
704+
("client", CLIENT_POD_IP, Waypoint::None),
705+
("server", SERVER_POD_IP, server_waypoint),
706+
]
707+
.into_iter()
708+
.map(|(name, ip, waypoint): (&str, &str, Waypoint)| Workload {
709+
workload_ips: vec![ip.parse().unwrap()],
710+
waypoint: waypoint.workload_attached(),
711+
protocol: Protocol::HBONE,
712+
uid: format!("cluster1//v1/Pod/default/{name}"),
713+
name: format!("workload-{name}"),
714+
namespace: "default".to_string(),
715+
service_account: format!("service-account-{name}"),
716+
..test_helpers::test_default_workload()
717+
});
718+
719+
for svc in services {
720+
state.services.insert(svc);
721+
}
722+
for wl in workloads {
723+
state.workloads.insert(wl)?;
724+
}
725+
726+
Ok(DemandProxyState::new(
727+
Arc::new(RwLock::new(state)),
728+
None,
729+
ResolverConfig::default(),
730+
ResolverOpts::default(),
731+
))
732+
}
733+
734+
// tells the test if we're using workload-attached or svc-attached waypoints
735+
#[derive(Copy, Clone)]
736+
enum Waypoint<'a> {
737+
None,
738+
Service(&'a str),
739+
Workload(&'a str),
740+
}
741+
742+
impl<'a> Waypoint<'a> {
743+
fn service_attached(&self) -> Option<GatewayAddress> {
744+
let Waypoint::Service(s) = self else {
745+
return None;
746+
};
747+
Some(GatewayAddress {
748+
destination: Destination::Address(NetworkAddress {
749+
network: "".to_string(),
750+
address: s.parse().expect("a valid waypoint IP"),
751+
}),
752+
hbone_mtls_port: 15008,
753+
hbone_single_tls_port: None,
754+
})
755+
}
756+
757+
fn workload_attached(&self) -> Option<GatewayAddress> {
758+
let Waypoint::Workload(s) = self else {
759+
return None;
760+
};
761+
Some(GatewayAddress {
762+
destination: Destination::Address(NetworkAddress {
763+
network: "".to_string(),
764+
address: s.parse().expect("a valid waypoint IP"),
765+
}),
766+
hbone_mtls_port: 15008,
767+
hbone_single_tls_port: None,
768+
})
769+
}
770+
}
771+
}

0 commit comments

Comments
 (0)