Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API Routes for Modules #528

Closed
wants to merge 29 commits into from
Closed
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
95760b0
Panorama config
trueleo Sep 26, 2023
cecb5d1
Registration for external modules
trueleo Oct 4, 2023
c2f2906
fix
trueleo Oct 4, 2023
c3fd833
- save registration in parseable json
aldrinjenson Oct 5, 2023
b7edd96
Fix lock drop and pass all headers
trueleo Oct 5, 2023
eb2b8ec
filter out authorization header passed to module
aldrinjenson Oct 5, 2023
e0928c6
Add deregister for modules
aldrinjenson Oct 6, 2023
9744ac7
update registration flow based on new spec
aldrinjenson Oct 9, 2023
9bdfafc
Put module config in stream json
trueleo Oct 9, 2023
bb7b9cf
Fix
trueleo Oct 9, 2023
ea0a575
Fix
trueleo Oct 9, 2023
63de18a
add semver checking
aldrinjenson Oct 9, 2023
f99003c
Fix
trueleo Oct 9, 2023
4e6b260
Fix
trueleo Oct 9, 2023
2612dd4
Use id from path
trueleo Oct 9, 2023
668fd7c
pass in content type for config
aldrinjenson Oct 9, 2023
bea613c
fix issue with tail to proxy to module_path
aldrinjenson Oct 9, 2023
c10259a
revert change for dynami path
aldrinjenson Oct 9, 2023
d174a32
Merge branch 'main' into panorama_config
nitisht Oct 10, 2023
7f9215c
deepsource fix
aldrinjenson Oct 10, 2023
7029ada
Refactor
trueleo Oct 10, 2023
0311963
Validate Version
trueleo Oct 10, 2023
1ac4089
Update routes to accept module_name in path
aldrinjenson Oct 11, 2023
19fb1f8
Handle edge case when stream does not exist
aldrinjenson Oct 11, 2023
0743567
load registration into module registry upon startup.
aldrinjenson Oct 11, 2023
f8948e8
deepsource fix
aldrinjenson Oct 11, 2023
c380e7b
Update server/Cargo.toml
nitisht Oct 12, 2023
88f8dfb
add license
aldrinjenson Oct 12, 2023
6a21995
update workflow to test modules route with panorama
aldrinjenson Oct 16, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ reqwest = { version = "0.11.18", default_features = false, features = [
] }
rustls = "0.20"
rustls-pemfile = "1.0"
semver = "1.0"
semver = { version = "1.0", features = ["serde"] }
nitisht marked this conversation as resolved.
Show resolved Hide resolved
serde = { version = "1.0", features = ["rc"] }
serde_json = "1.0"
static-files = "0.2"
Expand Down
104 changes: 104 additions & 0 deletions server/src/external_service.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use std::{
aldrinjenson marked this conversation as resolved.
Show resolved Hide resolved
collections::HashMap,
sync::{Arc, RwLock},
};

use once_cell::sync::Lazy;
use semver::Version;

pub static MODULE_REGISTRY: Lazy<Arc<RwLock<ModuleRegistry>>> = Lazy::new(Arc::default);

#[derive(Debug, Default)]
pub struct ModuleRegistry {
inner: HashMap<String, Registration>,
}

impl ModuleRegistry {
pub fn register(&mut self, module: Registration) {
self.inner.insert(module.id.clone(), module);
}

pub fn registrations(&self) -> impl Iterator<Item = &Registration> {
self.inner.values()
}

pub fn get(&self, id: &str) -> Option<&Registration> {
self.inner.get(id)
}
pub fn deregister(&mut self, module_id: &str) {
self.inner.remove(module_id);
}
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash)]
pub struct StreamConfig {
pub path: String,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash)]
#[serde(rename_all = "camelCase")]
pub struct Registration {
pub id: String,
pub version: String,
pub url: url::Url,
pub username: String,
pub password: String,
pub stream_config: StreamConfig,
pub routes: Vec<Route>,
}

impl Registration {
pub fn get_module_path(&self, path: &str, method: &http::Method) -> Option<String> {
self.routes
.iter()
.find(|x| x.server_path == path && method.eq(&x.method))
.map(|route| route.module_path.clone())
}

pub fn set_version(&mut self, version: &str) -> Result<(), String> {
if let Some(version) = version.strip_prefix('v') {
if Version::parse(version).is_err() {
return Err("Invalid SemVer format".to_string());
}
self.version = version.to_string();
Ok(())
} else {
Err("Module version must start with 'v'".to_string())
}
}
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash)]
pub struct DeRegistration {
pub id: String,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash)]
#[serde(rename_all = "camelCase")]
pub struct Route {
pub server_path: String,
pub module_path: String,
pub method: Method,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash)]
#[serde(rename_all = "UPPERCASE")]
#[allow(clippy::upper_case_acronyms)]
pub enum Method {
GET,
PUT,
POST,
DELETE,
}

impl PartialEq<Method> for http::Method {
fn eq(&self, other: &Method) -> bool {
matches!(
(self, other),
(&http::Method::GET, &Method::GET)
| (&http::Method::PUT, &Method::PUT)
| (&http::Method::POST, &Method::POST)
| (&http::Method::DELETE, &Method::DELETE)
)
}
}
19 changes: 18 additions & 1 deletion server/src/handlers/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@ use openid::Discovered;
use rustls::{Certificate, PrivateKey, ServerConfig};
use rustls_pemfile::{certs, pkcs8_private_keys};

use crate::option::CONFIG;
use crate::rbac::role::Action;
use crate::{external_service::MODULE_REGISTRY, option::CONFIG};

use self::middleware::{DisAllowRootUser, RouteExt};

mod about;
mod external;
mod health_check;
mod ingest;
mod llm;
Expand Down Expand Up @@ -286,6 +287,21 @@ pub fn configure_routes(
oauth_api = oauth_api.app_data(web::Data::from(client))
}

let external_services = web::scope("modules")
.service(resource("").route(web::get().to(external::get)))
.service(
resource("/register")
.route(web::put().to(external::register))
.route(web::delete().to(external::deregister)),
)
.service(
resource("{module}/config/{logstream}")
.route(web::get().to(external::get_config))
.route(web::put().to(external::put_config)),
)
.service(resource("{module}/{tail}*").to(external::router))
.app_data(web::Data::from(Arc::clone(&*MODULE_REGISTRY)));

// Deny request if username is same as the env variable P_USERNAME.
cfg.service(
// Base path "{url}/api/v1"
Expand Down Expand Up @@ -329,6 +345,7 @@ pub fn configure_routes(
.service(user_api)
.service(llm_query_api)
.service(oauth_api)
.service(external_services)
.service(role_api),
)
// GET "/" ==> Serve the static frontend directory
Expand Down
Loading
Loading