clnrm_core/config/
services.rs1use crate::error::{CleanroomError, Result};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7#[derive(Debug, Deserialize, Serialize, Clone)]
9pub struct ServiceConfig {
10 pub r#type: String,
12 pub plugin: String,
14 pub image: Option<String>,
16 #[serde(default)]
19 pub args: Option<Vec<String>>,
20 pub env: Option<HashMap<String, String>>,
22 pub ports: Option<Vec<u16>>,
24 pub volumes: Option<Vec<VolumeConfig>>,
26 pub health_check: Option<HealthCheckConfig>,
28 pub username: Option<String>,
30 pub password: Option<String>,
32 pub strict: Option<bool>,
34 pub wait_for_span: Option<String>,
37 pub wait_for_span_timeout_secs: Option<u64>,
39}
40
41#[derive(Debug, Deserialize, Serialize, Clone)]
43pub struct VolumeConfig {
44 pub host_path: String,
46 pub container_path: String,
48 pub read_only: Option<bool>,
50}
51
52impl VolumeConfig {
53 pub fn validate(&self) -> Result<()> {
62 use std::path::Path;
63
64 if self.host_path.trim().is_empty() {
66 return Err(CleanroomError::validation_error(
67 "Volume host path cannot be empty",
68 ));
69 }
70
71 if self.container_path.trim().is_empty() {
73 return Err(CleanroomError::validation_error(
74 "Volume container path cannot be empty",
75 ));
76 }
77
78 let host_path = Path::new(&self.host_path);
80 if !host_path.is_absolute() {
81 return Err(CleanroomError::validation_error(format!(
82 "Volume host path must be absolute: {}",
83 self.host_path
84 )));
85 }
86
87 let container_path = Path::new(&self.container_path);
89 if !container_path.is_absolute() {
90 return Err(CleanroomError::validation_error(format!(
91 "Volume container path must be absolute: {}",
92 self.container_path
93 )));
94 }
95
96 Ok(())
97 }
98
99 pub fn to_volume_mount(&self) -> Result<crate::backend::volume::VolumeMount> {
104 use crate::backend::volume::VolumeMount;
105 VolumeMount::from_config(self)
106 }
107}
108
109#[derive(Debug, Deserialize, Serialize, Clone)]
111pub struct HealthCheckConfig {
112 pub cmd: Vec<String>,
114 pub interval: Option<u64>,
116 pub timeout: Option<u64>,
118 pub retries: Option<u32>,
120}
121
122impl ServiceConfig {
123 pub fn validate(&self) -> Result<()> {
125 if self.r#type.trim().is_empty() {
126 return Err(CleanroomError::validation_error(
127 "Service type cannot be empty",
128 ));
129 }
130
131 if self.plugin.trim().is_empty() {
132 return Err(CleanroomError::validation_error(
133 "Service plugin cannot be empty",
134 ));
135 }
136
137 if let Some(ref image) = self.image {
138 if image.trim().is_empty() {
139 return Err(CleanroomError::validation_error(
140 "Service image cannot be empty",
141 ));
142 }
143 } else if self.r#type != "network_service" && self.r#type != "ollama" {
144 return Err(CleanroomError::validation_error(
146 "Service image is required for container-based services",
147 ));
148 }
149
150 if let Some(ref volumes) = self.volumes {
152 for (i, volume) in volumes.iter().enumerate() {
153 volume.validate().map_err(|e| {
154 CleanroomError::validation_error(format!("Volume {}: {}", i, e))
155 })?;
156 }
157 }
158
159 Ok(())
160 }
161}