1pub mod auth;
6pub mod autoscale_controller;
7pub mod bundle;
8pub mod capability;
9pub mod cdi;
10pub mod cgroups_stats;
11pub mod container_supervisor;
12pub mod cron_scheduler;
13pub mod dependency;
14pub mod env;
15pub mod error;
16pub mod gpu_detector;
17pub mod gpu_metrics;
18pub mod gpu_sharing;
19pub mod health;
20pub mod init;
21pub mod job;
22pub mod kv;
23pub mod metrics_providers;
24pub mod overlay_manager;
25pub mod proxy_manager;
26pub mod runtime;
27pub mod runtimes;
28pub mod service;
29pub mod stabilization;
30pub mod storage_manager;
31pub mod worker_client;
32
33#[cfg(target_os = "windows")]
34pub mod windows;
35
36pub use auth::{ContainerTokenSink, DeploymentDigestSink};
37pub use autoscale_controller::{has_adaptive_scaling, AutoscaleController};
38pub use bundle::*;
39pub use container_supervisor::{
40 ContainerSupervisor, SupervisedContainer, SupervisedState, SupervisorConfig, SupervisorEvent,
41};
42pub use cron_scheduler::{CronJobInfo, CronScheduler};
43pub use dependency::{
44 DependencyConditionChecker, DependencyError, DependencyGraph, DependencyNode, DependencyWaiter,
45 WaitResult,
46};
47pub use env::{
48 resolve_env_value, resolve_env_vars, resolve_env_with_secrets, EnvResolutionError, ResolvedEnv,
49};
50pub use error::*;
51pub use gpu_detector::{detect_gpus, GpuInfo};
52pub use health::*;
53pub use init::{BackoffConfig, InitOrchestrator};
54pub use job::{
55 JobExecution, JobExecutionId, JobExecutor, JobExecutorConfig, JobStatus, JobTrigger,
56};
57pub use kv::{
58 global_kv, set_global_kv, KvBackend, KvEntry, KvError, KvEvent, KvEventKind, KvStore,
59};
60pub use metrics_providers::{
61 LockedServiceManagerContainerProvider, RuntimeStatsProvider, ServiceManagerContainerProvider,
62};
63pub use overlay_manager::{make_interface_name, OverlayManager};
64pub use proxy_manager::{ProxyManager, ProxyManagerConfig};
65pub use runtime::*;
66pub use runtimes::{create_runtime_for_image, detect_image_artifact_type};
67
68#[cfg(all(target_os = "linux", feature = "youki-runtime"))]
70pub use runtimes::{YoukiConfig, YoukiRuntime};
71
72#[cfg(feature = "docker")]
73pub use runtimes::DockerRuntime;
74
75#[cfg(feature = "wasm")]
76pub use runtimes::{WasmConfig, WasmRuntime};
77
78#[cfg(target_os = "macos")]
79pub use runtimes::macos_sandbox::SandboxRuntime;
80#[cfg(target_os = "macos")]
81pub use runtimes::macos_vm::VmRuntime;
82
83pub use service::*;
84pub use stabilization::{
85 wait_for_stabilization, ServiceHealthSummary, StabilizationOutcome, StabilizationResult,
86};
87pub use storage_manager::{StorageError, StorageManager, VolumeInfo};
88pub use worker_client::{
89 WorkerClientError, WorkerClientImpl, WorkerIdentity, WorkerStatusProvider,
90};
91
92#[cfg(target_os = "macos")]
93use std::path::PathBuf;
94use std::sync::Arc;
95
96#[cfg(target_os = "macos")]
102#[derive(Debug, Clone)]
103pub struct MacSandboxConfig {
104 pub data_dir: PathBuf,
106 pub log_dir: PathBuf,
108 pub gpu_access: bool,
110 pub keychain_access_allowed: bool,
116 pub overlay_cidr: Option<String>,
120}
121
122#[cfg(target_os = "macos")]
127#[must_use]
128pub fn keychain_access_allowed_from_env() -> bool {
129 std::env::var("ZLAYER_ALLOW_KEYCHAIN_ACCESS")
130 .ok()
131 .is_some_and(|v| {
132 matches!(
133 v.trim().to_ascii_lowercase().as_str(),
134 "1" | "true" | "yes" | "on"
135 )
136 })
137}
138
139#[cfg(target_os = "macos")]
140impl Default for MacSandboxConfig {
141 fn default() -> Self {
142 let dirs = zlayer_paths::ZLayerDirs::system_default();
143 Self {
144 data_dir: dirs.data_dir().to_path_buf(),
145 log_dir: dirs.logs(),
146 gpu_access: true,
147 keychain_access_allowed: false,
148 overlay_cidr: Some(zlayer_overlay::DEFAULT_OVERLAY_CIDR.to_string()),
149 }
150 }
151}
152
153#[derive(Debug, Clone, Default)]
155pub enum RuntimeConfig {
156 #[default]
165 Auto,
166 Mock,
168 #[cfg(all(target_os = "linux", feature = "youki-runtime"))]
170 Youki(YoukiConfig),
171 #[cfg(feature = "docker")]
173 Docker,
174 #[cfg(feature = "wasm")]
176 Wasm(WasmConfig),
177 #[cfg(target_os = "macos")]
179 MacSandbox(MacSandboxConfig),
180 #[cfg(target_os = "macos")]
182 MacVm,
183 #[cfg(target_os = "macos")]
186 MacVz,
187 #[cfg(target_os = "windows")]
193 #[deprecated(
194 note = "Wsl2 is deprecated in favor of Hcs (native Windows containers via the \
195 Host Compute Service). This variant is preserved for one release and \
196 currently aliases to Hcs with a default config at dispatch time."
197 )]
198 Wsl2,
199 #[cfg(target_os = "windows")]
205 Hcs(crate::runtimes::hcs::HcsConfig),
206}
207
208#[cfg(feature = "docker")]
216pub async fn is_docker_available() -> bool {
217 use bollard::Docker;
218
219 match Docker::connect_with_local_defaults() {
220 Ok(docker) => match docker.ping().await {
221 Ok(_) => {
222 tracing::debug!("Docker daemon is available");
223 true
224 }
225 Err(e) => {
226 tracing::debug!(error = %e, "Docker daemon ping failed");
227 false
228 }
229 },
230 Err(e) => {
231 tracing::debug!(error = %e, "Failed to connect to Docker daemon");
232 false
233 }
234 }
235}
236
237#[cfg(not(feature = "docker"))]
239#[allow(clippy::unused_async)]
240pub async fn is_docker_available() -> bool {
241 false
242}
243
244#[cfg(feature = "wasm")]
261#[must_use]
262pub fn is_wasm_available() -> bool {
263 true
264}
265
266#[cfg(not(feature = "wasm"))]
268#[must_use]
269pub fn is_wasm_available() -> bool {
270 false
271}
272
273#[allow(clippy::too_many_lines)]
303pub async fn create_runtime(
304 config: RuntimeConfig,
305 auth_ctx: Option<ContainerAuthContext>,
306) -> Result<Arc<dyn Runtime + Send + Sync>> {
307 match config {
308 RuntimeConfig::Auto => create_auto_runtime(auth_ctx).await,
309 RuntimeConfig::Mock => Ok(Arc::new(MockRuntime::new())),
310 #[cfg(all(target_os = "linux", feature = "youki-runtime"))]
311 RuntimeConfig::Youki(youki_config) => {
312 let runtime = YoukiRuntime::new(youki_config, auth_ctx).await?;
313 Ok(Arc::new(runtime))
314 }
315 #[cfg(feature = "docker")]
316 RuntimeConfig::Docker => {
317 let runtime = DockerRuntime::new(auth_ctx).await?;
318 Ok(Arc::new(runtime))
319 }
320 #[cfg(feature = "wasm")]
321 RuntimeConfig::Wasm(wasm_config) => {
322 let runtime = WasmRuntime::new(wasm_config, auth_ctx).await?;
323 Ok(Arc::new(runtime))
324 }
325 #[cfg(target_os = "macos")]
326 RuntimeConfig::MacSandbox(config) => {
327 let primary: Arc<dyn Runtime> = Arc::new(runtimes::macos_sandbox::SandboxRuntime::new(
328 config,
329 auth_ctx.clone(),
330 )?);
331 let delegate: Option<Arc<dyn Runtime>> = match runtimes::macos_vm::VmRuntime::new(
332 auth_ctx.clone(),
333 ) {
334 Ok(rt) => {
335 tracing::info!(
336 "macOS VM (libkrun) delegate available — Linux containers will execute in a micro-VM"
337 );
338 Some(Arc::new(rt))
339 }
340 Err(e) => {
341 tracing::warn!(
342 error = %e,
343 "macOS VM delegate unavailable; node will only run mac-native containers"
344 );
345 None
346 }
347 };
348 let vz_linux: Option<Arc<dyn Runtime>> =
351 runtimes::macos_vz_linux::VzLinuxRuntime::new(auth_ctx.clone())
352 .map(|rt| Arc::new(rt) as Arc<dyn Runtime>)
353 .ok();
354 let vz: Option<Arc<dyn Runtime>> = match runtimes::macos_vz::VzRuntime::new(auth_ctx) {
356 Ok(rt) => Some(Arc::new(rt)),
357 Err(e) => {
358 tracing::warn!(error = %e, "macOS VZ delegate unavailable");
359 None
360 }
361 };
362 let data_dir = zlayer_paths::ZLayerDirs::default_data_dir();
376 let os_inspect_cache_paths = vec![
377 data_dir
378 .join("vz")
379 .join("linux")
380 .join("images")
381 .join("blobs.redb"),
382 data_dir.join("images").join("blobs.redb"),
383 ];
384 Ok(Arc::new(
385 runtimes::composite::CompositeRuntime::new(primary, delegate)
386 .with_vz_delegate(vz)
387 .with_vz_linux_delegate(vz_linux)
388 .with_os_inspect_cache_paths(os_inspect_cache_paths),
389 ))
390 }
391 #[cfg(target_os = "macos")]
392 RuntimeConfig::MacVm => Ok(Arc::new(runtimes::macos_vm::VmRuntime::new(auth_ctx)?)),
393 #[cfg(target_os = "macos")]
394 RuntimeConfig::MacVz => Ok(Arc::new(runtimes::macos_vz::VzRuntime::new(auth_ctx)?)),
395 #[cfg(target_os = "windows")]
396 #[allow(deprecated)]
397 RuntimeConfig::Wsl2 => {
398 tracing::warn!(
399 "RuntimeConfig::Wsl2 is deprecated; treating as RuntimeConfig::Hcs with default config"
400 );
401 Box::pin(create_runtime(
402 RuntimeConfig::Hcs(crate::runtimes::hcs::HcsConfig::default()),
403 auth_ctx,
404 ))
405 .await
406 }
407 #[cfg(target_os = "windows")]
408 RuntimeConfig::Hcs(hcs_config) => {
409 let primary: Arc<dyn Runtime> = Arc::new(
410 crate::runtimes::hcs::HcsRuntime::new(hcs_config, auth_ctx.clone()).await?,
411 );
412
413 #[cfg(feature = "wsl")]
414 let delegate: Option<Arc<dyn Runtime>> =
415 match runtimes::wsl2_delegate::Wsl2DelegateRuntime::try_new(auth_ctx.clone()).await
416 {
417 Ok(Some(rt)) => {
418 tracing::info!(
419 "WSL2 delegate runtime available — Linux containers will execute inside the zlayer distro"
420 );
421 Some(Arc::new(rt))
422 }
423 Ok(None) => {
424 tracing::info!(
425 "WSL2 not available; node will only run Windows-image containers"
426 );
427 None
428 }
429 Err(e) => {
430 tracing::warn!(
431 error = %e,
432 "WSL2 delegate setup failed; node will only run Windows-image containers"
433 );
434 None
435 }
436 };
437 #[cfg(not(feature = "wsl"))]
438 let delegate: Option<Arc<dyn Runtime>> = None;
439
440 Ok(Arc::new(runtimes::composite::CompositeRuntime::new(
441 primary, delegate,
442 )))
443 }
444 }
445}
446
447#[cfg_attr(
456 not(all(target_os = "linux", feature = "youki-runtime")),
457 allow(clippy::unused_async)
458)]
459#[cfg_attr(
460 not(any(
461 all(target_os = "linux", feature = "youki-runtime"),
462 target_os = "macos",
463 feature = "docker"
464 )),
465 allow(unused_variables)
466)]
467#[allow(clippy::too_many_lines)]
468async fn create_auto_runtime(
469 auth_ctx: Option<ContainerAuthContext>,
470) -> Result<Arc<dyn Runtime + Send + Sync>> {
471 tracing::info!("Auto-selecting container runtime");
472
473 #[cfg(all(target_os = "linux", feature = "youki-runtime"))]
475 {
476 match YoukiRuntime::new(YoukiConfig::default(), auth_ctx.clone()).await {
477 Ok(runtime) => {
478 tracing::info!("Using bundled libcontainer runtime (Linux-native, no daemon)");
479 return Ok(Arc::new(runtime));
480 }
481 Err(e) => {
482 tracing::warn!(error = %e, "Failed to initialize libcontainer runtime, trying Docker");
483 }
484 }
485 }
486
487 #[cfg(target_os = "macos")]
493 {
494 let sandbox_config = MacSandboxConfig {
500 keychain_access_allowed: keychain_access_allowed_from_env(),
501 ..MacSandboxConfig::default()
502 };
503 let primary: Option<Arc<dyn Runtime>> =
504 match runtimes::macos_sandbox::SandboxRuntime::new(sandbox_config, auth_ctx.clone()) {
505 Ok(rt) => Some(Arc::new(rt)),
506 Err(e) => {
507 tracing::warn!("macOS sandbox runtime unavailable: {e}");
508 None
509 }
510 };
511 let delegate: Option<Arc<dyn Runtime>> = match runtimes::macos_vm::VmRuntime::new(
512 auth_ctx.clone(),
513 ) {
514 Ok(rt) => {
515 tracing::info!(
516 "macOS VM (libkrun) delegate available — Linux containers will execute in a micro-VM"
517 );
518 Some(Arc::new(rt))
519 }
520 Err(e) => {
521 tracing::warn!("macOS VM runtime (libkrun) unavailable: {e}");
522 None
523 }
524 };
525 let vz: Option<Arc<dyn Runtime>> = runtimes::macos_vz::VzRuntime::new(auth_ctx.clone())
528 .map(|rt| Arc::new(rt) as Arc<dyn Runtime>)
529 .ok();
530 let vz_linux: Option<Arc<dyn Runtime>> =
532 runtimes::macos_vz_linux::VzLinuxRuntime::new(auth_ctx.clone())
533 .map(|rt| Arc::new(rt) as Arc<dyn Runtime>)
534 .ok();
535
536 if let Some(p) = primary {
537 let data_dir = zlayer_paths::ZLayerDirs::default_data_dir();
544 let os_inspect_cache_paths = vec![
545 data_dir
546 .join("vz")
547 .join("linux")
548 .join("images")
549 .join("blobs.redb"),
550 data_dir.join("images").join("blobs.redb"),
551 ];
552 return Ok(Arc::new(
553 runtimes::composite::CompositeRuntime::new(p, delegate)
554 .with_vz_delegate(vz)
555 .with_vz_linux_delegate(vz_linux)
556 .with_os_inspect_cache_paths(os_inspect_cache_paths),
557 ));
558 }
559 if let Some(d) = delegate {
563 return Ok(d);
564 }
565 }
566
567 #[cfg(target_os = "windows")]
573 {
574 let primary: Option<Arc<dyn Runtime>> = match crate::runtimes::hcs::HcsRuntime::new(
575 crate::runtimes::hcs::HcsConfig::default(),
576 auth_ctx.clone(),
577 )
578 .await
579 {
580 Ok(rt) => {
581 tracing::info!(
582 "Using native Windows HCS runtime (no Docker Desktop / WSL2 required)"
583 );
584 Some(Arc::new(rt))
585 }
586 Err(e) => {
587 tracing::warn!(error = %e, "HCS runtime unavailable, falling back to Docker");
588 None
589 }
590 };
591
592 #[cfg(feature = "wsl")]
593 let delegate: Option<Arc<dyn Runtime>> =
594 match runtimes::wsl2_delegate::Wsl2DelegateRuntime::try_new(auth_ctx.clone()).await {
595 Ok(Some(rt)) => {
596 tracing::info!(
597 "WSL2 delegate runtime available — Linux containers will execute inside the zlayer distro"
598 );
599 Some(Arc::new(rt))
600 }
601 Ok(None) => {
602 tracing::info!(
603 "WSL2 not available; node will only run Windows-image containers"
604 );
605 None
606 }
607 Err(e) => {
608 tracing::warn!(
609 error = %e,
610 "WSL2 delegate setup failed; node will only run Windows-image containers"
611 );
612 None
613 }
614 };
615 #[cfg(not(feature = "wsl"))]
616 let delegate: Option<Arc<dyn Runtime>> = None;
617
618 if let Some(p) = primary {
619 return Ok(Arc::new(runtimes::composite::CompositeRuntime::new(
620 p, delegate,
621 )));
622 }
623 }
624
625 #[cfg(feature = "docker")]
627 {
628 if is_docker_available().await {
629 tracing::info!("Selected Docker runtime");
630 let runtime = DockerRuntime::new(auth_ctx).await?;
631 return Ok(Arc::new(runtime));
632 }
633 tracing::debug!("Docker daemon not available");
634 }
635
636 #[cfg(all(target_os = "linux", feature = "docker"))]
638 {
639 Err(AgentError::Configuration(
640 "Bundled libcontainer runtime failed to initialize and Docker daemon is not available."
641 .to_string(),
642 ))
643 }
644
645 #[cfg(all(target_os = "linux", not(feature = "docker")))]
646 {
647 Err(AgentError::Configuration(
648 "Bundled libcontainer runtime failed to initialize. Enable the 'docker' feature for an alternative."
649 .to_string(),
650 ))
651 }
652
653 #[cfg(all(not(target_os = "linux"), feature = "docker"))]
654 {
655 Err(AgentError::Configuration(
656 "No container runtime available. Start the Docker daemon.".to_string(),
657 ))
658 }
659
660 #[cfg(all(not(target_os = "linux"), not(feature = "docker")))]
661 {
662 Err(AgentError::Configuration(
663 "No container runtime available. Enable the 'docker' feature and start the Docker daemon.".to_string(),
664 ))
665 }
666}