mod context;
mod control_plane;
mod disk_reclaim;
mod dns_service;
mod host_mount;
mod kubernetes_lb;
mod kubernetes_proxy;
mod machine_dns;
mod machine_mount;
#[cfg(target_os = "macos")]
mod mdns;
mod nfs_mount;
mod power;
mod recovery;
mod sandbox_cleanup;
mod self_setup;
mod services;
mod shutdown;
mod ssh_agent;
mod ssh_service;
mod startup;
use std::sync::Arc;
use anyhow::Result;
use arcbox_api::SetupState;
use arcbox_constants::container_network::ContainerNetwork;
use arcbox_constants::paths::ArcboxProfile;
use arcbox_logging::LogConfig;
use clap::Parser;
#[derive(Debug, Parser)]
#[command(name = "arcbox-daemon")]
#[command(author, version, about, long_about = None)]
pub struct DaemonArgs {
#[arg(long)]
pub profile: Option<ArcboxProfile>,
#[arg(long)]
pub socket: Option<std::path::PathBuf>,
#[arg(long)]
pub grpc_socket: Option<std::path::PathBuf>,
#[arg(long)]
pub data_dir: Option<std::path::PathBuf>,
#[arg(long)]
pub dns_domain: Option<arcbox_helper::validate::Domain>,
#[arg(long)]
pub dns_port: Option<u16>,
#[arg(long)]
pub install_dns_resolver: bool,
#[arg(long)]
pub kubernetes_port: Option<u16>,
#[arg(long)]
pub kubernetes_context: Option<String>,
#[arg(long)]
pub ssh_port: Option<u16>,
#[arg(long)]
pub container_cidr: Option<ContainerNetwork>,
#[arg(long)]
pub kernel: Option<std::path::PathBuf>,
#[arg(long)]
pub docker_integration: bool,
#[arg(long)]
pub no_linux_vm: bool,
#[arg(long)]
pub foreground: bool,
#[arg(long)]
pub guest_docker_vsock_port: Option<u32>,
#[arg(long)]
pub no_mount_nfs: bool,
}
fn main() -> Result<()> {
let args = DaemonArgs::parse();
let profile = args
.profile
.unwrap_or_else(ArcboxProfile::from_env_or_default);
validate_docker_integration(profile, args.docker_integration)?;
let _sentry_guard = sentry::init(sentry::ClientOptions {
dsn: sentry_dsn().and_then(|s| s.parse().ok()),
release: Some(env!("CARGO_PKG_VERSION").into()),
environment: sentry_environment().map(Into::into),
traces_sample_rate: 0.2,
sample_rate: 1.0,
attach_stacktrace: true,
..Default::default()
});
let data_dir = startup::resolve_data_dir(profile, args.data_dir.as_ref());
let log_guard = arcbox_logging::init_with_sentry(LogConfig {
log_dir: data_dir.join(arcbox_constants::paths::host::LOG),
file_name: arcbox_constants::paths::host::DAEMON_LOG.to_string(),
default_filter: "arcbox=info,arcbox_daemon=info,guest_serial=info,guest_console=info"
.to_string(),
foreground: args.foreground,
..LogConfig::default()
});
tracing::info!(
version = env!("CARGO_PKG_VERSION"),
build = env!("ARCBOX_BUILD_SHA"),
profile = ?profile,
"arcbox-daemon starting"
);
let result = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.expect("Failed to build tokio runtime")
.block_on(run(args));
if let Err(ref e) = result {
let error: &(dyn std::error::Error + Send + Sync + 'static) = e.as_ref();
sentry::capture_error(error);
tracing::error!("Daemon exited with error: {e:?}");
}
log_guard.flush();
result
}
fn validate_docker_integration(profile: ArcboxProfile, enabled: bool) -> Result<()> {
anyhow::ensure!(
!enabled || profile == ArcboxProfile::Production,
"--docker-integration is production-only because it changes the global Docker current context; use the development instance's Docker context explicitly"
);
Ok(())
}
fn sentry_dsn() -> Option<String> {
std::env::var("ARCBOX_DAEMON_SENTRY_DSN")
.or_else(|_| std::env::var("SENTRY_DSN"))
.ok()
}
fn sentry_environment() -> Option<String> {
std::env::var("ARCBOX_DAEMON_SENTRY_ENVIRONMENT")
.or_else(|_| std::env::var("SENTRY_ENVIRONMENT"))
.ok()
}
async fn run(args: DaemonArgs) -> Result<()> {
let handles = context::StartupHandles::new(Arc::new(SetupState::new()));
let ready = tokio::select! {
ready = start(args, handles.clone()) => match ready {
Ok(ready) => ready,
Err(e) => {
handles.setup_state.set_failed(&format!("{e:#}"));
tokio::time::sleep(std::time::Duration::from_millis(200)).await;
shutdown::abort_failed_startup(&handles).await;
return Err(e);
}
},
() = shutdown::wait_for_signal() => {
return shutdown::interrupt_startup(&handles).await;
}
};
shutdown::run(ready.ctx, ready.handles).await
}
async fn start(args: DaemonArgs, handles: context::StartupHandles) -> Result<startup::ReadyDaemon> {
startup::Startup::from_args(args, handles)
.prepare_host()
.await?
.acquire_daemon_lease()
.await?
.start_control_plane()
.await?
.release_stale_resources()
.await?
.prepare_assets()
.await?
.boot_runtime()
.await?
.start_runtime_services()
.await?
.mark_ready()
.await
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn development_cannot_mutate_the_global_docker_context() {
assert!(validate_docker_integration(ArcboxProfile::Development, true).is_err());
assert!(validate_docker_integration(ArcboxProfile::Development, false).is_ok());
assert!(validate_docker_integration(ArcboxProfile::Production, true).is_ok());
}
}