wip
This commit is contained in:
commit
48887e988d
9 changed files with 1923 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/target
|
1747
Cargo.lock
generated
Normal file
1747
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
2
Cargo.toml
Normal file
2
Cargo.toml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
workspace.resolver = "2"
|
||||||
|
workspace.members = ["crates/*"]
|
55
README.md
Normal file
55
README.md
Normal 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
13
crates/server/Cargo.toml
Normal 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"
|
5
crates/server/src/config.rs
Normal file
5
crates/server/src/config.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
// Local server config
|
||||||
|
|
||||||
|
// Cluster config
|
||||||
|
|
||||||
|
struct ClusterConfig {}
|
37
crates/server/src/main.rs
Normal file
37
crates/server/src/main.rs
Normal 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
62
crates/server/src/raft.rs
Normal 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
1
rustfmt.toml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
tab_spaces = 2
|
Loading…
Reference in a new issue