photoncloud-monorepo/chainfire/crates/chainfire-server/src/main.rs
centra 5c6eb04a46 T036: Add VM cluster deployment configs for nixos-anywhere
- netboot-base.nix with SSH key auth
- Launch scripts for node01/02/03
- Node configuration.nix and disko.nix
- Nix modules for first-boot automation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-11 09:59:19 +09:00

170 lines
5 KiB
Rust

//! Chainfire distributed KVS server
use anyhow::Result;
use chainfire_server::config::ServerConfig;
use clap::Parser;
use metrics_exporter_prometheus::PrometheusBuilder;
use std::path::PathBuf;
use tracing::info;
/// Chainfire distributed Key-Value Store
#[derive(Parser, Debug)]
#[command(name = "chainfire")]
#[command(author, version, about, long_about = None)]
struct Args {
/// Configuration file path
#[arg(short, long, default_value = "chainfire.toml")]
config: PathBuf,
/// Node ID (overrides config)
#[arg(long)]
node_id: Option<u64>,
/// Data directory (overrides config)
#[arg(long)]
data_dir: Option<PathBuf>,
/// API listen address (overrides config)
#[arg(long)]
api_addr: Option<String>,
/// Raft listen address (overrides config)
#[arg(long)]
raft_addr: Option<String>,
/// Gossip listen address (overrides config)
#[arg(long)]
gossip_addr: Option<String>,
/// Initial cluster members for bootstrap (comma-separated node_id=addr pairs)
#[arg(long)]
initial_cluster: Option<String>,
/// Enable verbose logging
#[arg(short, long)]
verbose: bool,
/// Metrics port for Prometheus scraping
#[arg(long, default_value = "9091")]
metrics_port: u16,
}
#[tokio::main]
async fn main() -> Result<()> {
let args = Args::parse();
// Initialize logging
let filter = if args.verbose {
"chainfire=debug,tower_http=debug"
} else {
"chainfire=info"
};
tracing_subscriber::fmt()
.with_env_filter(filter)
.with_target(true)
.init();
info!("Chainfire v{}", env!("CARGO_PKG_VERSION"));
// Initialize Prometheus metrics exporter
let metrics_addr = format!("0.0.0.0:{}", args.metrics_port);
let builder = PrometheusBuilder::new();
builder
.with_http_listener(metrics_addr.parse::<std::net::SocketAddr>()?)
.install()
.expect("Failed to install Prometheus metrics exporter");
info!(
"Prometheus metrics available at http://{}/metrics",
metrics_addr
);
// Register chainfire metrics
metrics::describe_counter!(
"chainfire_kv_requests_total",
"Total number of KV requests by operation type"
);
metrics::describe_counter!(
"chainfire_kv_bytes_read",
"Total bytes read from KV store"
);
metrics::describe_counter!(
"chainfire_kv_bytes_written",
"Total bytes written to KV store"
);
metrics::describe_histogram!(
"chainfire_kv_request_duration_seconds",
"KV request duration in seconds"
);
metrics::describe_gauge!(
"chainfire_raft_term",
"Current Raft term"
);
metrics::describe_gauge!(
"chainfire_raft_is_leader",
"Whether this node is the Raft leader (1=yes, 0=no)"
);
metrics::describe_counter!(
"chainfire_watch_events_total",
"Total number of watch events emitted"
);
use config::{Config as Cfg, Environment, File, FileFormat};
use toml; // Import toml for serializing defaults
// ... (rest of existing imports)
// Load configuration using config-rs
let mut settings = Cfg::builder()
// Layer 1: Application defaults. Serialize ServerConfig::default() into TOML.
.add_source(File::from_str(
toml::to_string(&ServerConfig::default())?.as_str(),
FileFormat::Toml,
))
// Layer 2: Environment variables (e.g., CHAINFIRE_NODE__ID, CHAINFIRE_NETWORK__API_ADDR)
.add_source(
Environment::with_prefix("CHAINFIRE")
.separator("__") // Use double underscore for nested fields
);
// Layer 3: Configuration file (if specified)
if args.config.exists() {
info!("Loading config from file: {}", args.config.display());
settings = settings.add_source(File::from(args.config.as_path()));
} else {
info!("Config file not found, using defaults and environment variables.");
}
let mut config: ServerConfig = settings
.build()?
.try_deserialize()?;
// Apply command line overrides (Layer 4: highest precedence)
if let Some(node_id) = args.node_id {
config.node.id = node_id;
}
if let Some(data_dir) = args.data_dir {
config.storage.data_dir = data_dir;
}
if let Some(api_addr) = args.api_addr {
config.network.api_addr = api_addr.parse()?;
}
if let Some(raft_addr) = args.raft_addr {
config.network.raft_addr = raft_addr.parse()?;
}
if let Some(gossip_addr) = args.gossip_addr {
config.network.gossip_addr = gossip_addr.parse()?;
}
info!(node_id = config.node.id, "Starting node");
info!(api_addr = %config.network.api_addr, "API address");
info!(raft_addr = %config.network.raft_addr, "Raft address");
info!(gossip_addr = %config.network.gossip_addr, "Gossip address");
// Start the server
let server = chainfire_server::server::Server::new(config).await?;
server.run().await?;
Ok(())
}