Skip to content

Commit

Permalink
Add endpoint to access internal app IP addresses and ports
Browse files Browse the repository at this point in the history
  • Loading branch information
twizmwazin committed Jan 22, 2025
1 parent ad0331a commit 999578a
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 3 deletions.
28 changes: 27 additions & 1 deletion src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use poem_openapi::{param::Path, payload::Json, ApiResponse, OpenApi};

use crate::{
backend::{AppControllerBackend, BackendError},
types::{App, AppConfig, AppId, AppStatus},
types::{App, AppConfig, AppId, AppStatus, SocketAddr},
};

pub struct Api<B: AppControllerBackend + 'static>(B);
Expand Down Expand Up @@ -85,6 +85,19 @@ enum GetAllAppsResponse {
InternalError(Json<String>),
}

#[derive(ApiResponse)]
enum GetAppAddrResponse {
/// The address and port of the app were found successfully.
#[oai(status = 200)]
Ok(Json<SocketAddr>),
/// The app could not be found.
#[oai(status = 404)]
NotFound,
/// The app could not be found because of an internal error.
#[oai(status = 500)]
InternalError,
}

#[OpenApi]
impl<B: AppControllerBackend> Api<B> {
/// Create new app
Expand Down Expand Up @@ -148,4 +161,17 @@ impl<B: AppControllerBackend> Api<B> {
Err(err) => GetAllAppsResponse::InternalError(Json(err.to_string())),
}
}

/// Get internal app address
#[oai(path = "/app/:id/addr", method = "get")]
async fn get_app_addr(&self, id: Path<AppId>) -> GetAppAddrResponse {
match self.0.get_app_addr(id.0).await {
Ok((addr, port)) => GetAppAddrResponse::Ok(Json(SocketAddr {
ip: addr.to_string(),
port,
})),
Err(BackendError::NotFound) => GetAppAddrResponse::NotFound,
Err(BackendError::InternalError(_)) => GetAppAddrResponse::InternalError,
}
}
}
8 changes: 7 additions & 1 deletion src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub use kubernetes::KubernetesBackend;
#[cfg(test)]
pub use mock::MockBackend;

use std::future::Future;
use std::{future::Future, net::IpAddr};

use crate::types::{App, AppConfig, AppId, AppStatus};

Expand All @@ -34,4 +34,10 @@ pub trait AppControllerBackend: Send + Sync {

/// Get all apps currently managed by the controller.
fn get_all_apps(&self) -> impl Future<Output = Result<Vec<App>, BackendError>> + Send;

/// Get the address and port of the app with the given ID.
fn get_app_addr(
&self,
id: AppId,
) -> impl Future<Output = Result<(IpAddr, u16), BackendError>> + Send;
}
30 changes: 29 additions & 1 deletion src/backend/kubernetes.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{collections::BTreeMap, str::FromStr};
use std::{collections::BTreeMap, net::IpAddr, str::FromStr};

use crate::types::{App, AppConfig, AppId, AppStatus, InteractionModel};
use k8s_openapi::{
Expand Down Expand Up @@ -308,4 +308,32 @@ impl AppControllerBackend for KubernetesBackend {

Ok(apps)
}

async fn get_app_addr(&self, id: AppId) -> Result<(IpAddr, u16), BackendError> {
let service_api: Api<Service> = Api::default_namespaced(self.client.clone());
let list_params = ListParams::default().labels(&format!("app-controller-id={}", id));
let list = service_api.list(&list_params).await?;

// There should be only one service with the given ID.
let service = list
.items
.into_iter()
.next()
.ok_or(BackendError::NotFound)?;

// Get the IP address of the service.
let raw = service
.spec
.ok_or(BackendError::InternalError(
"Missing spec in service".to_string(),
))?
.cluster_ip
.ok_or(BackendError::InternalError(
"Missing cluster IP in service".to_string(),
))?;

IpAddr::from_str(&raw)
.map(|ip| (ip, 5911))
.map_err(|e| BackendError::InternalError(e.to_string()))
}
}
5 changes: 5 additions & 0 deletions src/backend/mock.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::{
collections::HashMap,
net::IpAddr,
sync::{
atomic::{AtomicU64, Ordering},
RwLock,
Expand Down Expand Up @@ -80,4 +81,8 @@ impl AppControllerBackend for MockBackend {
async fn get_all_apps(&self) -> Result<Vec<App>, BackendError> {
Ok(self.apps.read().unwrap().values().cloned().collect())
}

async fn get_app_addr(&self, _id: AppId) -> Result<(IpAddr, u16), BackendError> {
todo!()
}
}
10 changes: 10 additions & 0 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,13 @@ pub struct App {
/// Current status of the app.
pub config: AppConfig,
}

pub type IpAddr = String;

/// Address and port of a app.
#[derive(Debug, Clone, Object)]
#[oai(read_only_all = true)]
pub struct SocketAddr {
pub ip: IpAddr,
pub port: u16,
}

0 comments on commit 999578a

Please sign in to comment.