This commit is contained in:
Michael Zhang 2024-11-25 19:30:41 -06:00
commit 48887e988d
9 changed files with 1923 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

1747
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

2
Cargo.toml Normal file
View file

@ -0,0 +1,2 @@
workspace.resolver = "2"
workspace.members = ["crates/*"]

55
README.md Normal file
View file

@ -0,0 +1,55 @@
Michael Zhang Web Services (MZWS)
===
Welcome to MZWS!
Design Principles
---
* Onboarding should be simple.
Download a binary / run a script, then add more servers / storage / as necessary.
Configuration should be incremental, in that a basic setup will work and more features can be added with more configuration.
Requirements for setup
---
* Just a single machine: host services locally
* With an internal IP: serve to that IP
* With a public IP: automatic ACME certificates
* With more nodes: automatic consensus across the cluster
* With more storage: automatic CRUSH for S3
Features
---
Services:
* Object storage
* Managed static websites
* Managed DB
Features:
* Manage collections of services
* ^Programmatically
Technical docs
---
### Raft docs
* A node initially has no raft startup
Overall state consists of:
* A list of nodes, along with their UUIDs
* Which nodes are exit nodes, i.e hooked up to which public ports
* The exit nodes will receive requests and forward them to others
* TODO: For now, just a single load balancer. In the future, have others?
* Which nodes are available to run Docker services, and which services are running on which hosts
### Assumptions
* All nodes can reach each other directly.
This is either via public internet or a private net like Tailscale.
Bastion systems are not supported.

13
crates/server/Cargo.toml Normal file
View file

@ -0,0 +1,13 @@
[package]
name = "server"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0.93"
askama = { version = "0.12.1", features = ["with-axum"] }
axum = { version = "0.7.9", features = ["http2", "macros", "ws"] }
futures = "0.3.31"
raft = { version = "0.7.0", default-features = false, features = ["prost-codec"] }
tokio = { version = "1.41.1", features = ["full"] }
turmoil = "0.6.4"

View file

@ -0,0 +1,5 @@
// Local server config
// Cluster config
struct ClusterConfig {}

37
crates/server/src/main.rs Normal file
View file

@ -0,0 +1,37 @@
use anyhow::Result;
use axum::{extract::State, routing::get, Router};
use raft::{ITransport, TokioTransport};
use crate::raft::RaftManager;
mod config;
mod raft;
#[derive(Clone)]
struct AppState<T> {
raft_manager: RaftManager<T>,
}
#[tokio::main]
async fn main() -> Result<()> {
let transport = TokioTransport {};
let app_state = AppState {
raft_manager: RaftManager::new(transport),
};
let app = Router::new()
.route("/", get(|| async { "Hello, World! 1" }))
.route("/_admin/raft/status", get(raft_status))
.route("/_admin/raft/connect", get(|| async { "ope" }))
.with_state(app_state);
let listener = tokio::net::TcpListener::bind("0.0.0.0:7771").await?;
axum::serve(listener, app).await?;
Ok(())
}
async fn raft_status<T: ITransport>(s: State<AppState<T>>) -> String {
let status = s.raft_manager.status().await.unwrap();
format!("healthy: {:?}", status.healthy)
}

62
crates/server/src/raft.rs Normal file
View file

@ -0,0 +1,62 @@
use std::collections::HashMap;
use anyhow::Result;
use raft::{storage::MemStorage, RawNode};
/// T = Transport
#[derive(Clone)]
pub struct RaftManager<T> {
known_servers: HashMap<String, String>,
this_node: RawNode<MemStorage>,
transport: T,
}
pub struct RaftStatus {
/// Single-bit whether or not we are in a healthy status
pub healthy: bool,
}
impl<T: ITransport> RaftManager<T> {
pub fn new(transport: T) -> Self {
RaftManager {
known_servers: Default::default(),
transport,
}
}
pub async fn status(&self) -> Result<RaftStatus> {
Ok(RaftStatus { healthy: false })
}
}
pub trait ITransport {
type TcpListener: ITcpListener;
type TcpStream: ITcpStream;
}
pub trait ITcpStream {}
pub trait ITcpListener {}
#[derive(Clone)]
pub struct TokioTransport {}
impl ITransport for TokioTransport {
type TcpStream = tokio::net::TcpStream;
type TcpListener = tokio::net::TcpListener;
}
impl ITcpStream for tokio::net::TcpStream {}
impl ITcpListener for tokio::net::TcpListener {}
struct TestTransport {}
impl ITransport for TestTransport {
type TcpStream = turmoil::net::TcpStream;
type TcpListener = turmoil::net::TcpListener;
}
impl ITcpStream for turmoil::net::TcpStream {}
impl ITcpListener for turmoil::net::TcpListener {}

1
rustfmt.toml Normal file
View file

@ -0,0 +1 @@
tab_spaces = 2