Page MenuHomePhabricator

No OneTemporary

diff --git a/crates/api/Cargo.toml b/crates/api/Cargo.toml
new file mode 100644
index 0000000..7f731ef
--- /dev/null
+++ b/crates/api/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "api"
+version = "0.1.0"
+license.workspace = true
+edition.workspace = true
+authors.workspace = true
+
+[dependencies]
+log.workspace = true
+thiserror.workspace = true
+tracing.workspace = true
+tokio.workspace = true
+libcollar.workspace = true
+store = { path = "../store" }
+tower-http = { version = "0.6.4", features = ["trace"] }
+axum = "0.8.4"
diff --git a/crates/api/src/lib.rs b/crates/api/src/lib.rs
new file mode 100644
index 0000000..a768ab8
--- /dev/null
+++ b/crates/api/src/lib.rs
@@ -0,0 +1,36 @@
+use std::sync::Arc;
+
+use tower_http::trace::TraceLayer;
+
+use axum::{
+ routing::get,
+ extract::{State, Request, Json, Path, Extension, Query},
+ routing::post,
+};
+
+pub use axum::serve;
+
+struct AppState {
+ collar: libcollar::State,
+}
+
+pub fn app(collar: libcollar::State) -> axum::Router {
+ let state = Arc::new(AppState { collar });
+
+ // build our application with a single route
+ let app = axum::Router::new()
+ .route("/", get(|| async { "collared." }))
+ .route("/ng/eiface/{name}", post(post_ng_eiface))
+ .with_state(state)
+ .layer(tower_http::trace::TraceLayer::new_for_http());
+
+ app
+}
+
+async fn post_ng_eiface(State(state): State<Arc<AppState>>, Path(name): Path<String>) -> Result<String, String> {
+ let fname = name.clone();
+ let result = state.collar.leash().gated(move || {
+ Ok(libcollar::ng::new_eiface(&name))
+ }).await;
+ Ok(format!("sup {} => {:?}", &fname, result))
+}
diff --git a/crates/libcollar/Cargo.toml b/crates/libcollar/Cargo.toml
new file mode 100644
index 0000000..2fbc331
--- /dev/null
+++ b/crates/libcollar/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "libcollar"
+version = "0.1.0"
+license.workspace = true
+edition.workspace = true
+authors.workspace = true
+
+[dependencies]
+log.workspace = true
+thiserror.workspace = true
+tracing.workspace = true
+tokio.workspace = true
+store.workspace = true
+serde.workspace = true
+ifconfig = { path = "../ifconfig" }
+ng = { path = "../ng" }
diff --git a/src/error.rs b/crates/libcollar/src/error.rs
similarity index 100%
rename from src/error.rs
rename to crates/libcollar/src/error.rs
diff --git a/crates/libcollar/src/jail/jail.rs b/crates/libcollar/src/jail/jail.rs
new file mode 100644
index 0000000..1cd2474
--- /dev/null
+++ b/crates/libcollar/src/jail/jail.rs
@@ -0,0 +1,5 @@
+#[derive(Debug, Deserialize)]
+pub struct Jail {
+ store_id: u64,
+ name: String
+}
diff --git a/crates/libcollar/src/jail/mod.rs b/crates/libcollar/src/jail/mod.rs
new file mode 100644
index 0000000..483ab07
--- /dev/null
+++ b/crates/libcollar/src/jail/mod.rs
@@ -0,0 +1,2 @@
+mod jail;
+pub use jail;
diff --git a/crates/libcollar/src/lib.rs b/crates/libcollar/src/lib.rs
new file mode 100644
index 0000000..8f510d2
--- /dev/null
+++ b/crates/libcollar/src/lib.rs
@@ -0,0 +1,6 @@
+mod error;
+pub mod net;
+pub use error::{Error, Result};
+pub use ng;
+mod libcollar;
+pub use libcollar::*;
diff --git a/crates/libcollar/src/libcollar.rs b/crates/libcollar/src/libcollar.rs
new file mode 100644
index 0000000..e245bf5
--- /dev/null
+++ b/crates/libcollar/src/libcollar.rs
@@ -0,0 +1,17 @@
+use crate::Result;
+
+pub struct State {
+ leash: net::Leash,
+}
+
+pub fn new() -> Result<State> {
+ Ok(State {
+ leash: net::Leash::new(),
+ })
+}
+
+impl State {
+ pub fn leash(&self) -> &net::Leash {
+ &self.leash
+ }
+}
diff --git a/src/lib.rs b/crates/libcollar/src/net/leash.rs
similarity index 95%
rename from src/lib.rs
rename to crates/libcollar/src/net/leash.rs
index 40b40a4..fce5a5b 100644
--- a/src/lib.rs
+++ b/crates/libcollar/src/net/leash.rs
@@ -1,45 +1,43 @@
-pub mod error;
-
use std::sync::Arc;
use tokio::sync::Mutex;
-use crate::error::{Error, Result};
+use crate::{Error, Result};
pub struct NetworkLeash {
lock: Arc<Mutex<()>>
}
impl NetworkLeash {
pub fn new() -> Self {
tracing::trace!("Creating new NetworkLeash");
Self {
lock: Arc::new(Mutex::new(())),
}
}
pub async fn with_interface<F, R>(&self, name: &str, operation: F) -> Result<R>
where
F: FnOnce(&mut ifconfig::Iface) -> Result<R> + Send + 'static,
R: Send + 'static,
{
let _guard = self.lock.lock().await;
let name = name.to_string();
tokio::task::spawn_blocking(move || {
let mut iface = ifconfig::Iface::new(&name)?;
operation(&mut iface)
}).await?
}
pub async fn gated<F, R>(&self, operation: F) -> Result<R>
where
F: FnOnce() -> Result<R> + Send + 'static,
R: Send + 'static,
{
let _guard = self.lock.lock().await;
tokio::task::spawn_blocking(move || {
operation()
}).await?
}
}
diff --git a/crates/libcollar/src/net/mod.rs b/crates/libcollar/src/net/mod.rs
new file mode 100644
index 0000000..f27d84d
--- /dev/null
+++ b/crates/libcollar/src/net/mod.rs
@@ -0,0 +1,2 @@
+mod leash;
+pub use leash::NetworkLeash as Leash;
diff --git a/crates/libcollar/src/net/network.rs b/crates/libcollar/src/net/network.rs
new file mode 100644
index 0000000..d81246c
--- /dev/null
+++ b/crates/libcollar/src/net/network.rs
@@ -0,0 +1,5 @@
+pub struct Network {
+ store_id: u64,
+ name: String,
+ handler: String,
+}
diff --git a/crates/store/Cargo.toml b/crates/store/Cargo.toml
new file mode 100644
index 0000000..e58282d
--- /dev/null
+++ b/crates/store/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "store"
+version = "0.1.0"
+license.workspace = true
+edition.workspace = true
+authors.workspace = true
+
+[dependencies]
+log.workspace = true
+thiserror.workspace = true
+sled = "0.34.7"
diff --git a/crates/store/src/error.rs b/crates/store/src/error.rs
new file mode 100644
index 0000000..e43e59c
--- /dev/null
+++ b/crates/store/src/error.rs
@@ -0,0 +1,7 @@
+#[derive(thiserror::Error, Debug)]
+pub enum Error {
+ #[error(transparent)]
+ StoreError(#[from] sled::Error)
+}
+
+pub type Result<T, E = Error> = ::std::result::Result<T, E>;
diff --git a/crates/store/src/lib.rs b/crates/store/src/lib.rs
new file mode 100644
index 0000000..d6e4129
--- /dev/null
+++ b/crates/store/src/lib.rs
@@ -0,0 +1,8 @@
+mod error;
+mod store;
+
+pub use error::{Result, Error};
+pub use store::Store;
+pub use store::open;
+
+mod namespace;
diff --git a/crates/store/src/namespace.rs b/crates/store/src/namespace.rs
new file mode 100644
index 0000000..b9b849c
--- /dev/null
+++ b/crates/store/src/namespace.rs
@@ -0,0 +1,142 @@
+// This module implements "namespaces" which are buckets of unique values
+// that maps to keys elsewhere in storage.
+//
+// the `names` tree is a k/v of `name` -> `id` where `id` is a u64 from `generate_id`.
+// the `spaces` tree is a k/v of `id` -> `name` where `id` is a u64 the `names` tree id and `name` is a str.
+use crate::Result;
+
+#[derive(Debug, Clone)]
+pub struct NamespaceStore {
+ names: sled::Tree,
+ spaces: sled::Tree,
+}
+
+impl NamespaceStore {
+ pub(crate) fn open(db: &sled::Db) -> Result<Self> {
+ Ok(NamespaceStore {
+ names: db.open_tree("namespaces/1/n")?,
+ spaces: db.open_tree("namespaces/1/s")?,
+ })
+ }
+
+ // define a namespace.
+ // inserts a key `namespace` into `names`, where the value is a random u64.
+ pub fn define(&self, namespace: &str) -> Result<()> {
+ self.names.transaction(|db| {
+ match db.get(namespace)? {
+ Some(_) => Ok(()),
+ None => {
+ let id = db.generate_id()?;
+ db.insert(namespace, &id.to_be_bytes())?;
+ Ok(())
+ }
+ }
+ })?;
+ Ok(())
+ }
+
+ pub fn resolve(&self, namespace: &str, key: &str) -> Result<bool> {
+ let result = (&self.names, &self.spaces).transaction(|(names, spaces)| {
+ // Get ID of the namespace from `names`
+ let namespace_id = match names.get(namespace)? {
+ Some(id_bytes) => {
+ let id_array: [u8; 8] = id_bytes.as_ref().try_into()
+ .map_err(|_| sled::transaction::ConflictableTransactionError::Abort(()))?;
+ u64::from_be_bytes(id_array)
+ }
+ None => return Ok(false), // namespace doesn't exist
+ };
+
+ // Create composite key: namespace_id + key
+ let mut composite_key = Vec::new();
+ composite_key.extend_from_slice(&namespace_id.to_be_bytes());
+ composite_key.extend_from_slice(key.as_bytes());
+
+ // Check if key exists in spaces
+ Ok(spaces.get(&composite_key)?.is_some())
+ })?;
+ Ok(result)
+ }
+
+ pub fn reserve(&self, namespace: &str, key: &str, value: &str) -> Result<bool> {
+ let result = (&self.names, &self.spaces).transaction(|(names, spaces)| {
+ // Get ID of the namespace from `names`
+ let namespace_id = match names.get(namespace)? {
+ Some(id_bytes) => {
+ let id_array: [u8; 8] = id_bytes.as_ref().try_into()
+ .map_err(|_| sled::transaction::ConflictableTransactionError::Abort(()))?;
+ u64::from_be_bytes(id_array)
+ }
+ None => return Ok(false), // namespace doesn't exist
+ };
+
+ // Create composite key: namespace_id + key
+ let mut composite_key = Vec::new();
+ composite_key.extend_from_slice(&namespace_id.to_be_bytes());
+ composite_key.extend_from_slice(key.as_bytes());
+
+ // Check if key already exists
+ if spaces.get(&composite_key)?.is_some() {
+ return Ok(false); // key already exists
+ }
+
+ // Insert the key-value pair
+ spaces.insert(&composite_key, value.as_bytes())?;
+ Ok(true)
+ })?;
+ Ok(result)
+ }
+
+ pub fn get(&self, namespace: &str, key: &str) -> Result<Option<String>> {
+ let result = (&self.names, &self.spaces).transaction(|(names, spaces)| {
+ // Get ID of the namespace from `names`
+ let namespace_id = match names.get(namespace)? {
+ Some(id_bytes) => {
+ let id_array: [u8; 8] = id_bytes.as_ref().try_into()
+ .map_err(|_| sled::transaction::ConflictableTransactionError::Abort(()))?;
+ u64::from_be_bytes(id_array)
+ }
+ None => return Ok(None), // namespace doesn't exist
+ };
+
+ // Create composite key: namespace_id + key
+ let mut composite_key = Vec::new();
+ composite_key.extend_from_slice(&namespace_id.to_be_bytes());
+ composite_key.extend_from_slice(key.as_bytes());
+
+ // Get value from spaces
+ match spaces.get(&composite_key)? {
+ Some(value_bytes) => {
+ let value = String::from_utf8(value_bytes.to_vec())
+ .map_err(|_| sled::transaction::ConflictableTransactionError::Abort(()))?;
+ Ok(Some(value))
+ }
+ None => Ok(None),
+ }
+ })?;
+ Ok(result)
+ }
+
+ pub fn remove(&self, namespace: &str, key: &str) -> Result<bool> {
+ let result = (&self.names, &self.spaces).transaction(|(names, spaces)| {
+ // Get ID of the namespace from `names`
+ let namespace_id = match names.get(namespace)? {
+ Some(id_bytes) => {
+ let id_array: [u8; 8] = id_bytes.as_ref().try_into()
+ .map_err(|_| sled::transaction::ConflictableTransactionError::Abort(()))?;
+ u64::from_be_bytes(id_array)
+ }
+ None => return Ok(false), // namespace doesn't exist
+ };
+
+ // Create composite key: namespace_id + key
+ let mut composite_key = Vec::new();
+ composite_key.extend_from_slice(&namespace_id.to_be_bytes());
+ composite_key.extend_from_slice(key.as_bytes());
+
+ // Remove the key-value pair
+ Ok(spaces.remove(&composite_key)?.is_some())
+ })?;
+ Ok(result)
+ }
+}
\ No newline at end of file
diff --git a/crates/store/src/store.rs b/crates/store/src/store.rs
new file mode 100644
index 0000000..ab9d6c2
--- /dev/null
+++ b/crates/store/src/store.rs
@@ -0,0 +1,42 @@
+use crate::Result;
+
+#[derive(Debug, Clone)]
+pub struct Db {
+ prefix: String,
+ db: sled::Db
+}
+
+#[derive(Debug, Clone)]
+pub struct Store {
+ db: Db,
+ namespaces: namespaces::NamespaceStore,
+}
+
+impl Db {
+ pub fn open(path: String, prefix: String) -> Result<Self> {
+ let db = sled::open(path)?;
+ Ok(Db { prefix, db })
+ }
+
+ pub fn open_tree(&self, name: &str) -> Result<sled::Tree> {
+ self.db.open_tree(self.tree_path(name))
+ }
+
+ pub fn tree_path(&self, name: &str) -> String {
+ format!("t1/{}/{}", self.prefix, name)
+ }
+}
+
+pub fn open() -> Result<Store> {
+ let db = Db::open("libcollar_store".to_string(), "bonefire".to_string())?;
+ Ok(Store {
+ db: db,
+ namespaces: crate::namespaces::open(&db)?,
+ })
+}
+
+impl Store {
+ fn tree_path(&self, name: &str) -> String {
+ make_tree_path(self.prefix, name)
+ }
+}
diff --git a/src/bin/collar-ng.rs b/src/bin/collar-ng.rs
index 1d08d62..e0e134c 100644
--- a/src/bin/collar-ng.rs
+++ b/src/bin/collar-ng.rs
@@ -1,68 +1,68 @@
use std::env::args;
use env_logger::Env;
use log::error;
-use ng;
+use libcollar::ng;
fn main() {
let env = Env::default()
.filter_or("LOG_LEVEL", "trace")
.write_style_or("LOG_STYLE", "always");
env_logger::init_from_env(env);
let mut argv = args();
let prog = argv.next().unwrap();
match argv.next() {
None => println!("Usage: {} <command> (args)", prog),
Some(str) => match str.as_str() {
"new-eiface" => {
match argv.next() {
None => println!("Usage: {} new-eiface <name>", prog),
Some(name) => match ng::new_eiface(&name) {
Ok(s) => println!("ok: {:#?}", s),
Err(e) => error!("error: {:?}", e),
}
}
},
"new-eiface-bridge" => {
match argv.next() {
None => println!("Usage: {} new-bridge <bridge> <name>", prog),
Some(bridge) => match argv.next() {
None => println!("Usage: {} new-bridge <bridge> <name>", prog),
Some(name) => match ng::new_bridge(&bridge, &name) {
Ok(s) => println!("ok: {}", s),
Err(e) => error!("error: {:?}", e)
}
}
}
}
"join-eiface-bridge" => {
match argv.next() {
None => println!("Usage: {} bridge-eiface <bridge> <name>", prog),
Some(bridge) => match argv.next() {
None => println!("Usage: {} bridge-eiface <bridge> <name>", prog),
Some(name) => match ng::bridge_eiface(&bridge, &name) {
Ok(s) => println!("ok: {}", s),
Err(e) => error!("error: {:?}", e)
}
}
}
}
"bridge" => {
match argv.next() {
None => println!("Usage: {} <path>", prog),
Some(path) => match ng::go(&path) {
Ok(s) => println!("{}", s),
Err(e) => error!("error: {:?}", e)
}
}
}
_ => println!("Unknown action: {}", str)
}
}
}
diff --git a/src/bin/collared.rs b/src/bin/collared.rs
index 62580e8..7b6fbbb 100644
--- a/src/bin/collared.rs
+++ b/src/bin/collared.rs
@@ -1,53 +1,27 @@
use std::env::args;
+use libcollar;
+use api::app;
use tokio::net::TcpListener;
-use tower_http::trace::TraceLayer;
-use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
-
-use std::sync::Arc;
-
-use axum::{
- routing::get,
- extract::{State, Request, Json, Path, Extension, Query},
- routing::post,
-};
-
-struct AppState {
- leash: collar::NetworkLeash,
-}
+use tracing_subscriber::{filter::EnvFilter, layer::SubscriberExt, util::SubscriberInitExt};
#[tokio::main]
async fn main() {
tracing_subscriber::registry()
.with(
- tracing_subscriber::filter::EnvFilter::try_from_default_env().unwrap_or_else(|_| {
- "collar=trace,tower_http=debug,axum::rejection=trace"
+ EnvFilter::try_from_default_env().unwrap_or_else(|_| {
+ "collar=trace,libcollar=trace,ng=trace,ifconfig=trace,api=trace,store=trace,tower_http=debug,axum::rejection=trace"
.into()
}),
)
.with(tracing_subscriber::fmt::layer())
.init();
- let leash = collar::NetworkLeash::new();
- let state = Arc::new(AppState { leash });
-
- // build our application with a single route
- let app = axum::Router::new()
- .route("/", get(|| async { "collared." }))
- .route("/ng/eiface/{name}", post(post_ng_eiface))
- .with_state(state)
- .layer(tower_http::trace::TraceLayer::new_for_http());
+ let app = app(libcollar::new().expect("Failed to initialize libcollar"));
- // run our app with hyper, listening globally on port 3000
- let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
+ let listen = "0.0.0.0:3000";
+ let listener = TcpListener::bind(listen).await.expect("Failed to bind listener");
tracing::info!("listening on {}", listener.local_addr().unwrap());
- axum::serve(listener, app).await.unwrap();
-}
-async fn post_ng_eiface(State(state): State<Arc<AppState>>, Path(name): Path<String>) -> Result<String, String> {
- let fname = name.clone();
- let result = state.leash.gated(move || {
- Ok(ng::new_eiface(&name))
- }).await;
- Ok(format!("sup {} => {:?}", &fname, result))
+ api::serve(listener, app).await.unwrap();
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Jun 8, 12:16 PM (18 h, 33 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
47577
Default Alt Text
(18 KB)

Event Timeline