migration to sqlite!
This commit is contained in:
@@ -18,3 +18,5 @@ target/
|
||||
#.idea/
|
||||
|
||||
.env
|
||||
|
||||
.codex
|
||||
|
||||
Generated
+716
-7
File diff suppressed because it is too large
Load Diff
@@ -125,7 +125,7 @@ Example:
|
||||
data_dir = "/var/lib/wakey-control-plane"
|
||||
bind = "0.0.0.0:8080"
|
||||
public_url = "https://cp.example.com"
|
||||
state_file = "state.db"
|
||||
state_file = "state.sqlite3"
|
||||
pid_file = "wakey-control-plane.pid"
|
||||
ui_dist_dir = "/opt/wakey/ui/dist"
|
||||
command_timeout_ms = 30000
|
||||
@@ -140,9 +140,14 @@ json_logs = false
|
||||
If `telemetry.otlp_endpoint` is omitted, logs still work normally and only local
|
||||
structured logs are emitted.
|
||||
|
||||
State is persisted in an embedded `sled` database (default
|
||||
`/var/lib/wakey-control-plane/state.db`).
|
||||
State is persisted in an embedded SQLite database (default
|
||||
`/var/lib/wakey-control-plane/state.sqlite3`).
|
||||
Relative paths in config are resolved under `data_dir`.
|
||||
Legacy sled state can be imported explicitly:
|
||||
|
||||
```sh
|
||||
wakey-control-plane import-sled-state --from-sled-state /var/lib/wakey-control-plane/state.db --to-state-file /var/lib/wakey-control-plane/state.sqlite3
|
||||
```
|
||||
|
||||
Enroll tokens are now expiring and revocable. Issuance returns `expires_at_unix`.
|
||||
Expired tokens are rejected on enroll and can be garbage-collected periodically
|
||||
|
||||
@@ -20,6 +20,14 @@ reqwest = { version = "0.13", default-features = false, features = [
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
sled = "0.34"
|
||||
sqlx = { version = "0.8", features = [
|
||||
"runtime-tokio",
|
||||
"sqlite",
|
||||
"macros",
|
||||
"migrate",
|
||||
"uuid",
|
||||
"json",
|
||||
] }
|
||||
toml = "1.1"
|
||||
tokio = { version = "1", features = [
|
||||
"macros",
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
CREATE TABLE meta (
|
||||
key TEXT PRIMARY KEY,
|
||||
value BLOB NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE enroll_tokens (
|
||||
token TEXT PRIMARY KEY,
|
||||
expires_at_unix INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE agents (
|
||||
agent_id TEXT PRIMARY KEY,
|
||||
agent_token TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE agent_meta (
|
||||
agent_id TEXT PRIMARY KEY REFERENCES agents(agent_id) ON DELETE CASCADE,
|
||||
nickname TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE audit_events (
|
||||
event_key TEXT PRIMARY KEY,
|
||||
event_json TEXT NOT NULL,
|
||||
ts_unix INTEGER NOT NULL,
|
||||
agent_id TEXT,
|
||||
request_id TEXT,
|
||||
event_type TEXT NOT NULL,
|
||||
outcome TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX audit_events_ts_unix_idx ON audit_events(ts_unix);
|
||||
CREATE INDEX audit_events_agent_id_idx ON audit_events(agent_id);
|
||||
CREATE INDEX audit_events_request_id_idx ON audit_events(request_id);
|
||||
CREATE INDEX audit_events_event_type_outcome_idx ON audit_events(event_type, outcome);
|
||||
|
||||
CREATE TABLE active_alerts (
|
||||
alert_id TEXT PRIMARY KEY,
|
||||
alert_json TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE alert_transitions (
|
||||
transition_key TEXT PRIMARY KEY,
|
||||
transition_json TEXT NOT NULL,
|
||||
ts_unix INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX alert_transitions_ts_unix_idx ON alert_transitions(ts_unix);
|
||||
@@ -43,6 +43,8 @@ pub enum Command {
|
||||
RevokeAgent(RevokeAgentArgs),
|
||||
/// Print state backend stats.
|
||||
StateStats(StateStatsArgs),
|
||||
/// Import a legacy sled state directory into a SQLite state file.
|
||||
ImportSledState(ImportSledStateArgs),
|
||||
/// Send SIGHUP to an already-running daemon.
|
||||
Reload(ReloadArgs),
|
||||
}
|
||||
@@ -236,6 +238,18 @@ pub struct StateStatsArgs {
|
||||
pub target: AdminTargetArgs,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct ImportSledStateArgs {
|
||||
#[arg(long)]
|
||||
pub from_sled_state: PathBuf,
|
||||
|
||||
#[arg(long)]
|
||||
pub to_state_file: PathBuf,
|
||||
|
||||
#[arg(long)]
|
||||
pub force: bool,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct ReloadArgs {
|
||||
#[arg(long, default_value = DEFAULT_PID_FILE)]
|
||||
|
||||
@@ -40,7 +40,7 @@ pub fn write_init_config(args: &InitConfigArgs) -> Result<Option<PathBuf>> {
|
||||
let state_file_raw = args
|
||||
.state_file
|
||||
.clone()
|
||||
.unwrap_or_else(|| PathBuf::from("state.db"));
|
||||
.unwrap_or_else(|| PathBuf::from("state.sqlite3"));
|
||||
let state_file = resolve_path(&data_dir, state_file_raw);
|
||||
|
||||
let pid_file_raw = args
|
||||
|
||||
@@ -49,7 +49,7 @@ impl DaemonConfig {
|
||||
.state_file
|
||||
.clone()
|
||||
.or(file.state_file)
|
||||
.unwrap_or_else(|| PathBuf::from("state.db"));
|
||||
.unwrap_or_else(|| PathBuf::from("state.sqlite3"));
|
||||
let state_file = resolve_path(&data_dir, state_file_raw);
|
||||
|
||||
let command_timeout = Duration::from_millis(
|
||||
@@ -255,7 +255,7 @@ fn resolve_state_access(
|
||||
&data_dir,
|
||||
cli_state_file
|
||||
.or(file.state_file)
|
||||
.unwrap_or_else(|| PathBuf::from("state.db")),
|
||||
.unwrap_or_else(|| PathBuf::from("state.sqlite3")),
|
||||
);
|
||||
|
||||
let resolved_public_url = cli_public_url
|
||||
@@ -288,8 +288,11 @@ mod tests {
|
||||
fn resolve_path_joins_relative_path() {
|
||||
let out = resolve_path(
|
||||
Path::new("/var/lib/wakey-control-plane"),
|
||||
PathBuf::from("state.db"),
|
||||
PathBuf::from("state.sqlite3"),
|
||||
);
|
||||
assert_eq!(
|
||||
out,
|
||||
PathBuf::from("/var/lib/wakey-control-plane/state.sqlite3")
|
||||
);
|
||||
assert_eq!(out, PathBuf::from("/var/lib/wakey-control-plane/state.db"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,10 @@ async fn main() -> Result<()> {
|
||||
tracing::init(cli.verbose, &config::TelemetryConfig::default())?;
|
||||
runtime::state_stats(args).await
|
||||
}
|
||||
Command::ImportSledState(args) => {
|
||||
tracing::init(cli.verbose, &config::TelemetryConfig::default())?;
|
||||
runtime::import_sled_state(args).await
|
||||
}
|
||||
Command::Reload(args) => {
|
||||
tracing::init(cli.verbose, &config::TelemetryConfig::default())?;
|
||||
runtime::reload_daemon(&args.pid_file)
|
||||
|
||||
@@ -4,8 +4,8 @@ use anyhow::{Context, Result};
|
||||
|
||||
use crate::api;
|
||||
use crate::cli::{
|
||||
IssueEnrollTokenArgs, ListEnrollTokensArgs, RevokeAgentArgs, RevokeEnrollTokenArgs,
|
||||
StateStatsArgs,
|
||||
ImportSledStateArgs, IssueEnrollTokenArgs, ListEnrollTokensArgs, RevokeAgentArgs,
|
||||
RevokeEnrollTokenArgs, StateStatsArgs,
|
||||
};
|
||||
use crate::config;
|
||||
use crate::state;
|
||||
@@ -276,3 +276,11 @@ pub async fn state_stats(args: StateStatsArgs) -> Result<()> {
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn import_sled_state(args: ImportSledStateArgs) -> Result<()> {
|
||||
state::Store::import_sled_state(&args.from_sled_state, &args.to_state_file, args.force).await?;
|
||||
println!("from_sled_state={}", args.from_sled_state.display());
|
||||
println!("to_state_file={}", args.to_state_file.display());
|
||||
println!("imported=true");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -26,7 +26,9 @@ use crate::ws;
|
||||
mod admin;
|
||||
mod process;
|
||||
pub use admin::revoke_agent;
|
||||
pub use admin::{issue_enroll_token, list_enroll_tokens, revoke_enroll_token, state_stats};
|
||||
pub use admin::{
|
||||
import_sled_state, issue_enroll_token, list_enroll_tokens, revoke_enroll_token, state_stats,
|
||||
};
|
||||
pub use process::reload_daemon;
|
||||
use process::{remove_pid_file, write_pid_file};
|
||||
|
||||
@@ -62,6 +64,7 @@ fn public_api_routes(ui_dist_dir: std::path::PathBuf) -> Router<AppState> {
|
||||
|
||||
Router::new()
|
||||
.route("/ui", get(|| async { Redirect::temporary("/ui/") }))
|
||||
.route("/", get(|| async { Redirect::temporary("/ui/") })) // same as caddyfile
|
||||
.nest_service(
|
||||
"/ui/",
|
||||
get_service(ServeDir::new(ui_dist_dir).not_found_service(ServeFile::new(index_file))),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user