greentic_runner_host/
lib.rs1#![deny(unsafe_code)]
2use std::path::PathBuf;
10use std::sync::Arc;
11use std::time::Duration;
12
13use crate::secrets::SecretsBackend;
14use anyhow::{Context, Result, anyhow};
15use runner_core::env::PackConfig;
16use tokio::signal;
17
18pub mod boot;
19pub mod config;
20pub mod engine;
21pub mod http;
22pub mod imports;
23pub mod ingress;
24pub mod pack;
25pub mod routing;
26pub mod runner;
27pub mod runtime;
28pub mod runtime_wasmtime;
29pub mod secrets;
30pub mod storage;
31pub mod telemetry;
32pub mod verify;
33pub mod wasi;
34pub mod watcher;
35
36mod activity;
37mod host;
38pub mod oauth;
39
40pub use activity::{Activity, ActivityKind};
41pub use config::HostConfig;
42pub use host::TelemetryCfg;
43pub use host::{HostBuilder, RunnerHost, TenantHandle};
44pub use wasi::{PreopenSpec, RunnerWasiPolicy};
45
46pub use greentic_types::{EnvId, FlowId, PackId, TenantCtx, TenantId};
47
48pub use http::auth::AdminAuth;
49pub use routing::RoutingConfig;
50use routing::TenantRouting;
51pub use runner::HostServer;
52
53#[derive(Clone)]
55pub struct RunnerConfig {
56 pub bindings: Vec<PathBuf>,
57 pub pack: PackConfig,
58 pub port: u16,
59 pub refresh_interval: Duration,
60 pub routing: RoutingConfig,
61 pub admin: AdminAuth,
62 pub telemetry: Option<TelemetryCfg>,
63 pub secrets_backend: SecretsBackend,
64 pub wasi_policy: RunnerWasiPolicy,
65}
66
67impl RunnerConfig {
68 pub fn from_env(bindings: Vec<PathBuf>) -> Result<Self> {
70 if bindings.is_empty() {
71 anyhow::bail!("at least one bindings file is required");
72 }
73 let pack = PackConfig::from_env()?;
74 let refresh = parse_refresh_interval(std::env::var("PACK_REFRESH_INTERVAL").ok())?;
75 let port = std::env::var("PORT")
76 .ok()
77 .and_then(|value| value.parse().ok())
78 .unwrap_or(8080);
79 let routing = RoutingConfig::from_env();
80 let admin = AdminAuth::from_env();
81 let secrets_backend = SecretsBackend::from_env(std::env::var("SECRETS_BACKEND").ok())?;
82 Ok(Self {
83 bindings,
84 pack,
85 port,
86 refresh_interval: refresh,
87 routing,
88 admin,
89 telemetry: None,
90 secrets_backend,
91 wasi_policy: RunnerWasiPolicy::default(),
92 })
93 }
94
95 pub fn with_port(mut self, port: u16) -> Self {
97 self.port = port;
98 self
99 }
100
101 pub fn with_wasi_policy(mut self, policy: RunnerWasiPolicy) -> Self {
102 self.wasi_policy = policy;
103 self
104 }
105}
106
107fn parse_refresh_interval(value: Option<String>) -> Result<Duration> {
108 let raw = value.unwrap_or_else(|| "30s".into());
109 humantime::parse_duration(&raw).map_err(|err| anyhow!("invalid PACK_REFRESH_INTERVAL: {err}"))
110}
111
112pub async fn run(cfg: RunnerConfig) -> Result<()> {
114 let RunnerConfig {
115 bindings,
116 pack,
117 port,
118 refresh_interval,
119 routing,
120 admin,
121 telemetry,
122 secrets_backend,
123 wasi_policy,
124 } = cfg;
125 #[cfg(not(feature = "telemetry"))]
126 let _ = telemetry;
127
128 let mut builder = HostBuilder::new();
129 for path in &bindings {
130 let host_config = HostConfig::load_from_path(path)
131 .with_context(|| format!("failed to load host bindings {}", path.display()))?;
132 builder = builder.with_config(host_config);
133 }
134 #[cfg(feature = "telemetry")]
135 if let Some(telemetry) = telemetry.clone() {
136 builder = builder.with_telemetry(telemetry);
137 }
138 builder = builder
139 .with_wasi_policy(wasi_policy.clone())
140 .with_secrets_manager(
141 secrets_backend
142 .build_manager()
143 .context("failed to initialise secrets backend")?,
144 );
145
146 let host = Arc::new(builder.build()?);
147 host.start().await?;
148
149 let (watcher, reload_handle) =
150 watcher::start_pack_watcher(Arc::clone(&host), pack.clone(), refresh_interval).await?;
151
152 let routing = TenantRouting::new(routing.clone());
153 let server = HostServer::new(
154 port,
155 host.active_packs(),
156 routing,
157 host.health_state(),
158 Some(reload_handle),
159 admin.clone(),
160 )?;
161
162 tokio::select! {
163 result = server.serve() => {
164 result?;
165 }
166 _ = signal::ctrl_c() => {
167 tracing::info!("received shutdown signal");
168 }
169 }
170
171 drop(watcher);
172 host.stop().await?;
173 Ok(())
174}