Skip to content

Commit 4f81b83

Browse files
committed
refactor: move already bound node check to the select_exit_node_local function
1 parent d87ac53 commit 4f81b83

File tree

3 files changed

+75
-38
lines changed

3 files changed

+75
-38
lines changed

src/daemon.rs

Lines changed: 15 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,16 @@ async fn select_exit_node_local(
213213
return Err(ReconcileError::NoAvailableExitNodes);
214214
}
215215
};
216+
217+
let already_bound_exit_node =
218+
crate::util::get_svc_bound_exit_node(ctx.clone(), service).await?;
219+
220+
if let Some(node) = already_bound_exit_node {
221+
info!("Service already bound to an exit node, using that now");
222+
*lock = Some((std::time::Instant::now(), node.get_host()));
223+
return Ok(node);
224+
}
225+
216226
// if service has label with exit node name, use that and error if not found
217227
let exit_node_selection = {
218228
if let Some(exit_node_name) = service
@@ -408,42 +418,14 @@ async fn reconcile_svcs(obj: Arc<Service>, ctx: Arc<Context>) -> Result<Action,
408418
let services: Api<Service> = Api::namespaced(ctx.client.clone(), &obj.namespace().unwrap());
409419
let nodes: Api<ExitNode> = Api::all(ctx.client.clone());
410420

411-
// --- Let's skip reconciling services whose exit node IP addresses still exit in the cluster
412-
// only list IP addresses of exit nodes
413-
let nodes_by_ip: BTreeMap<String, ExitNode> = nodes
414-
.list(&ListParams::default().timeout(30))
415-
.await?
416-
.items
417-
.into_iter()
418-
.filter_map(|node| {
419-
let host = node.get_host();
420-
if let Some(_status) = &node.status {
421-
Some((host, node))
422-
} else {
423-
None
424-
}
425-
})
426-
.collect();
427-
428421
let mut svc = services.get_status(&obj.name_any()).await?;
429422

430-
let svc_lb_ip = svc
431-
.status
432-
.as_ref()
433-
.and_then(|status| status.load_balancer.as_ref())
434-
.and_then(|lb| lb.ingress.as_ref())
435-
.and_then(|ingress| ingress.first())
436-
.and_then(|ingress| ingress.ip.clone())
437-
.unwrap_or_default();
438-
439-
let existing_bound_node = nodes_by_ip.get(&svc_lb_ip);
440-
441423
let obj = svc.clone();
442424

443425
let node_list = nodes.list(&ListParams::default().timeout(30)).await?;
444426

445427
// Find service binding of svc name/namespace?
446-
let existing_node = node_list.iter().find(|node| {
428+
let named_exit_node = node_list.iter().find(|node| {
447429
node.metadata
448430
.annotations
449431
.as_ref()
@@ -453,9 +435,11 @@ async fn reconcile_svcs(obj: Arc<Service>, ctx: Arc<Context>) -> Result<Action,
453435

454436
// XXX: Exit node manifest generation starts here
455437
let node = {
456-
if let Some(node) = existing_node {
438+
if let Some(node) = named_exit_node {
439+
info!("Service explicitly set to use a named exit node, using that now");
457440
node.clone()
458441
} else if check_service_managed(&obj).await {
442+
info!("Service is managed by a cloud provider, Resolving exit node...");
459443
// Remove attached exit node if the service was managed by a cloud provider and when it is removed
460444
let mut exit_node = exit_node_for_service(ctx.clone(), &obj).await?;
461445

@@ -466,15 +450,8 @@ async fn reconcile_svcs(obj: Arc<Service>, ctx: Arc<Context>) -> Result<Action,
466450
}
467451

468452
exit_node
469-
}
470-
// If a service *specifically* chooses a named exit node, use that one
471-
// Allows support for multiple services to use the same exit node
472-
473-
// Else, use the first available exit node
474-
// Fails if there's no empty exit node available
475-
else if let Some(node) = existing_bound_node {
476-
node.clone()
477453
} else {
454+
info!("Selecting an exit node for the service");
478455
select_exit_node_local(&ctx, &obj).await?
479456
}
480457
};

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ pub mod daemon;
33
pub mod deployment;
44
pub mod error;
55
pub mod ops;
6+
pub mod util;

src/util.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
use std::{collections::BTreeMap, sync::Arc};
2+
3+
use k8s_openapi::api::core::v1::Service;
4+
use kube::{api::ListParams, Api};
5+
6+
use crate::{daemon::Context, ops::ExitNode};
7+
use color_eyre::Result;
8+
9+
/// Fetch exit nodes from the Kubernetes API in a map keyed by IP address
10+
/// This is useful for quickly looking up exit nodes by IP address
11+
///
12+
/// # Arguments
13+
///
14+
/// * `ctx` - A shared context object
15+
/// * `namespace` - An optional namespace to filter exit nodes by. If None, all namespaces are looked up
16+
pub async fn get_exit_nodes_by_ip(
17+
ctx: Arc<Context>,
18+
namespace: Option<&str>,
19+
) -> Result<BTreeMap<String, ExitNode>> {
20+
let exit_node_api: Api<ExitNode> = {
21+
if let Some(namespace) = namespace {
22+
Api::namespaced(ctx.client.clone(), namespace)
23+
} else {
24+
Api::all(ctx.client.clone())
25+
}
26+
};
27+
Ok(exit_node_api
28+
.list(&ListParams::default().timeout(30))
29+
.await?
30+
.items
31+
.into_iter()
32+
.filter_map(|node| {
33+
let host = node.get_host();
34+
if let Some(_status) = &node.status {
35+
Some((host, node))
36+
} else {
37+
None
38+
}
39+
})
40+
.collect())
41+
}
42+
43+
pub fn get_svc_lb_ip(svc: &Service) -> Option<String> {
44+
svc.status.as_ref().and_then(|status| {
45+
status
46+
.load_balancer
47+
.as_ref()
48+
.and_then(|lb| lb.ingress.as_ref())
49+
.and_then(|ingress| ingress.first())
50+
.and_then(|ingress| ingress.ip.as_ref())
51+
.cloned()
52+
})
53+
}
54+
55+
pub async fn get_svc_bound_exit_node(ctx: Arc<Context>, svc: &Service) -> Result<Option<ExitNode>> {
56+
let exit_nodes = get_exit_nodes_by_ip(ctx, None).await?;
57+
let svc_lb_ip = get_svc_lb_ip(svc);
58+
Ok(svc_lb_ip.and_then(|ip| exit_nodes.get(&ip).cloned()))
59+
}

0 commit comments

Comments
 (0)