Skip to main content

harn_cli/commands/orchestrator/harness/
config.rs

1//! Public configuration and error types for the in-process orchestrator harness.
2
3use std::net::SocketAddr;
4use std::path::PathBuf;
5use std::sync::Arc;
6use std::time::Duration;
7
8use harn_vm::clock::{Clock, RealClock};
9
10use super::super::errors::OrchestratorError;
11use super::super::role::OrchestratorRole;
12use super::super::tls::TlsFiles;
13
14// ── Public config ─────────────────────────────────────────────────────────────
15
16/// Drain limits applied during graceful shutdown.
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub struct DrainConfig {
19    pub max_items: usize,
20    pub deadline: Duration,
21}
22
23impl Default for DrainConfig {
24    fn default() -> Self {
25        Self {
26            max_items: crate::package::default_orchestrator_drain_max_items(),
27            deadline: Duration::from_secs(
28                crate::package::default_orchestrator_drain_deadline_seconds(),
29            ),
30        }
31    }
32}
33
34/// Topic-pump concurrency limit.
35#[derive(Clone, Copy, Debug, PartialEq, Eq)]
36pub struct PumpConfig {
37    pub max_outstanding: usize,
38}
39
40impl Default for PumpConfig {
41    fn default() -> Self {
42        Self {
43            max_outstanding: crate::package::default_orchestrator_pump_max_outstanding(),
44        }
45    }
46}
47
48/// Configuration for `OrchestratorHarness::start`.
49#[derive(Clone, Debug)]
50pub struct OrchestratorConfig {
51    pub manifest_path: PathBuf,
52    pub state_dir: PathBuf,
53    pub bind: SocketAddr,
54    pub role: OrchestratorRole,
55    pub watch_manifest: bool,
56    pub mcp: bool,
57    pub mcp_path: String,
58    pub mcp_sse_path: String,
59    pub mcp_messages_path: String,
60    pub tls: Option<TlsFiles>,
61    pub shutdown_timeout: Duration,
62    pub drain: DrainConfig,
63    pub pump: PumpConfig,
64    /// When `Some`, installs an observability tracing subscriber.  Tests
65    /// should leave this `None` to avoid conflicts with the test runtime.
66    pub log_format: Option<harn_vm::observability::otel::LogFormat>,
67    /// Time + sleep substrate. Defaults to [`RealClock`]; tests inject
68    /// [`harn_vm::clock::PausedClock`] via [`OrchestratorConfig::with_clock`].
69    pub clock: Arc<dyn Clock>,
70}
71
72impl OrchestratorConfig {
73    /// Minimal defaults suitable for integration tests.
74    pub fn for_test(manifest_path: PathBuf, state_dir: PathBuf) -> Self {
75        Self {
76            manifest_path,
77            state_dir,
78            bind: "127.0.0.1:0".parse().unwrap(),
79            role: OrchestratorRole::SingleTenant,
80            watch_manifest: false,
81            mcp: false,
82            mcp_path: "/mcp".to_string(),
83            mcp_sse_path: "/sse".to_string(),
84            mcp_messages_path: "/messages".to_string(),
85            tls: None,
86            shutdown_timeout: Duration::from_secs(5),
87            drain: DrainConfig::default(),
88            pump: PumpConfig::default(),
89            log_format: None,
90            clock: RealClock::arc(),
91        }
92    }
93
94    /// Inject a custom [`Clock`]. Production callers leave the default
95    /// [`RealClock`]; harness-driven tests pass a
96    /// [`harn_vm::clock::PausedClock`] so cron and the dispatcher run on
97    /// virtual time.
98    pub fn with_clock(mut self, clock: Arc<dyn Clock>) -> Self {
99        self.clock = clock;
100        self
101    }
102}
103
104// ── Public harness types ──────────────────────────────────────────────────────
105
106/// Summary returned by `OrchestratorHarness::shutdown`.
107#[derive(Debug)]
108pub struct ShutdownReport {
109    #[allow(dead_code)]
110    pub timed_out: bool,
111}
112
113/// Error type for harness operations.
114#[derive(Debug)]
115pub struct HarnessError(pub String);
116
117impl std::fmt::Display for HarnessError {
118    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119        f.write_str(&self.0)
120    }
121}
122
123impl From<OrchestratorError> for HarnessError {
124    fn from(error: OrchestratorError) -> Self {
125        HarnessError(error.to_string())
126    }
127}
128
129impl From<String> for HarnessError {
130    fn from(s: String) -> Self {
131        HarnessError(s)
132    }
133}