Skip to main content

orchestrator_config/config/
mod.rs

1//! Configuration structures for the orchestrator.
2
3mod agent;
4mod defaults;
5mod dynamic_items;
6mod env_store;
7mod execution;
8mod execution_profile;
9mod invariant;
10mod item_isolation;
11mod item_select;
12mod observability;
13mod pipeline;
14mod prehook;
15mod runner;
16mod safety;
17mod spawn;
18mod step;
19mod step_conventions;
20mod step_template;
21mod store_backend_provider;
22mod store_io;
23mod trigger;
24mod workflow;
25mod workflow_store;
26
27pub use agent::*;
28pub use defaults::*;
29pub use dynamic_items::*;
30pub use env_store::*;
31pub use execution::*;
32pub use execution_profile::*;
33pub use invariant::*;
34pub use item_isolation::*;
35pub use item_select::*;
36pub use observability::*;
37pub use pipeline::*;
38pub use prehook::*;
39pub use runner::*;
40pub use safety::*;
41pub use spawn::*;
42pub use step::*;
43pub use step_conventions::*;
44pub use step_template::*;
45pub use store_backend_provider::*;
46pub use store_io::*;
47pub use trigger::*;
48pub use workflow::*;
49pub use workflow_store::*;
50
51use crate::crd_types::{CustomResource, CustomResourceDefinition};
52use crate::resource_store::ResourceStore;
53use serde::{Deserialize, Serialize};
54use std::collections::HashMap;
55
56/// Builtin project identifier used when callers omit an explicit project.
57pub const DEFAULT_PROJECT_ID: &str = "default";
58
59/// Main orchestrator configuration
60#[derive(Debug, Clone, Default, Serialize, Deserialize)]
61pub struct OrchestratorConfig {
62    /// Project-scoped builtin resources keyed by project identifier.
63    #[serde(default)]
64    pub projects: HashMap<String, ProjectConfig>,
65    /// Registered custom resource definitions keyed by CRD kind.
66    #[serde(default)]
67    pub custom_resource_definitions: HashMap<String, CustomResourceDefinition>,
68    /// Custom resource instances (CRD-defined resources).
69    #[serde(default)]
70    pub custom_resources: HashMap<String, CustomResource>,
71    /// Unified resource store — stores all resources (builtin + custom CRD instances).
72    #[serde(default)]
73    pub resource_store: ResourceStore,
74}
75
76impl OrchestratorConfig {
77    /// Resolves a caller-supplied project ID to the effective project namespace.
78    pub fn effective_project_id<'a>(&'a self, project_id: Option<&'a str>) -> &'a str {
79        project_id
80            .filter(|value| !value.trim().is_empty())
81            .unwrap_or(DEFAULT_PROJECT_ID)
82    }
83
84    /// Returns the project config for the effective project ID.
85    pub fn project(&self, project_id: Option<&str>) -> Option<&ProjectConfig> {
86        self.projects.get(self.effective_project_id(project_id))
87    }
88
89    /// Returns a mutable project config for the effective project ID.
90    pub fn project_mut(&mut self, project_id: Option<&str>) -> Option<&mut ProjectConfig> {
91        let project_id = self.effective_project_id(project_id).to_string();
92        self.projects.get_mut(&project_id)
93    }
94
95    /// Returns the builtin default project, if present.
96    pub fn default_project(&self) -> Option<&ProjectConfig> {
97        self.project(Some(DEFAULT_PROJECT_ID))
98    }
99
100    /// Ensures the effective project exists and returns a mutable reference to it.
101    pub fn ensure_project(&mut self, project_id: Option<&str>) -> &mut ProjectConfig {
102        let project_id = self.effective_project_id(project_id).to_string();
103        self.projects.entry(project_id).or_default()
104    }
105}
106
107/// Persisted metadata for declarative resources.
108#[derive(Debug, Clone, Serialize, Deserialize, Default)]
109pub struct ResourceMetadataStore {
110    /// Persisted metadata for workspace resources.
111    #[serde(default)]
112    pub workspaces: HashMap<String, ResourceStoredMetadata>,
113    /// Persisted metadata for agent resources.
114    #[serde(default)]
115    pub agents: HashMap<String, ResourceStoredMetadata>,
116    /// Persisted metadata for workflow resources.
117    #[serde(default)]
118    pub workflows: HashMap<String, ResourceStoredMetadata>,
119}
120
121/// Labels and annotations persisted independently from resource specs.
122#[derive(Debug, Clone, Serialize, Deserialize, Default)]
123pub struct ResourceStoredMetadata {
124    /// Labels stored independently from the resource spec.
125    #[serde(default, skip_serializing_if = "Option::is_none")]
126    pub labels: Option<HashMap<String, String>>,
127    /// Annotations stored independently from the resource spec.
128    #[serde(default, skip_serializing_if = "Option::is_none")]
129    pub annotations: Option<HashMap<String, String>>,
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135
136    #[test]
137    fn test_orchestrator_config_default() {
138        let cfg = OrchestratorConfig::default();
139        assert!(cfg.projects.is_empty());
140    }
141
142    #[test]
143    fn test_orchestrator_config_serde_round_trip() {
144        let cfg = OrchestratorConfig::default();
145        let json = serde_json::to_string(&cfg).expect("config should serialize");
146        let cfg2: OrchestratorConfig =
147            serde_json::from_str(&json).expect("config should deserialize");
148        assert_eq!(cfg2.projects.len(), cfg.projects.len());
149        assert!(cfg2.projects.is_empty());
150    }
151
152    #[test]
153    fn test_default_project() {
154        assert_eq!(DEFAULT_PROJECT_ID, "default");
155    }
156}