Skip to main content

docker_config/
config.rs

1#![warn(missing_docs)]
2
3//! 配置管理
4//!
5//! 负责读取和管理 Docker 全局配置、配置和密钥。
6
7use std::collections::HashMap;
8use std::default::Default;
9use std::fs;
10use std::path::Path;
11use std::time::SystemTime;
12
13use docker_types::{
14    ConfigInfo, DockerConfig, EndpointConfig, EndpointInfo, EndpointStatus, EndpointType,
15    LogConfig, ResourceLimits, DockerError, SecretInfo,
16};
17
18/// 结果类型
19pub type Result<T> = std::result::Result<T, DockerError>;
20
21/// 配置管理器
22pub struct ConfigManager {
23    /// 配置文件路径
24    config_path: String,
25    /// 配置存储目录
26    configs_dir: String,
27    /// 密钥存储目录
28    secrets_dir: String,
29    /// 端点存储目录
30    endpoints_dir: String,
31}
32
33impl ConfigManager {
34    /// 创建新的配置管理器
35    pub fn new() -> Result<Self> {
36        // 确定配置文件路径
37        let config_path = Self::get_config_path()?;
38        let data_dir = Self::get_default_data_dir();
39        let configs_dir = format!("{}/configs", data_dir);
40        let secrets_dir = format!("{}/secrets", data_dir);
41        let endpoints_dir = format!("{}/endpoints", data_dir);
42
43        // 确保配置、密钥和端点目录存在
44        fs::create_dir_all(&configs_dir).map_err(|e| DockerError::io_error("create_dir_all", e.to_string()))?;
45        fs::create_dir_all(&secrets_dir).map_err(|e| DockerError::io_error("create_dir_all", e.to_string()))?;
46        fs::create_dir_all(&endpoints_dir).map_err(|e| DockerError::io_error("create_dir_all", e.to_string()))?;
47
48        Ok(Self {
49            config_path,
50            configs_dir,
51            secrets_dir,
52            endpoints_dir,
53        })
54    }
55
56    /// 获取配置文件路径
57    fn get_config_path() -> Result<String> {
58        // 在不同操作系统上的默认配置路径
59        #[cfg(target_os = "windows")]
60        let default_path = "C:\\ProgramData\\rusty-docker\\config.toml";
61
62        #[cfg(target_os = "linux")]
63        let default_path = "/etc/rusty-docker/config.toml";
64
65        #[cfg(target_os = "macos")]
66        let default_path = "/usr/local/etc/rusty-docker/config.toml";
67
68        // 检查配置文件是否存在
69        if Path::new(default_path).exists() {
70            Ok(default_path.to_string())
71        } else {
72            // 如果配置文件不存在,创建默认配置
73            Self::create_default_config(default_path)?;
74            Ok(default_path.to_string())
75        }
76    }
77
78    /// 创建默认配置文件
79    fn create_default_config(path: &str) -> Result<()> {
80        // 确保目录存在
81        if let Some(dir) = Path::new(path).parent() {
82            fs::create_dir_all(dir).map_err(|e| DockerError::io_error("create_dir_all", e.to_string()))?;
83        }
84
85        // 创建默认配置
86        let default_config = Self::get_default_config();
87        let config_content = toml::to_string(&default_config).map_err(|e| DockerError::parse_error("DockerConfig", e.to_string()))?;
88
89        // 写入配置文件
90        fs::write(path, config_content).map_err(|e| DockerError::io_error("write", e.to_string()))?;
91        Ok(())
92    }
93
94    /// 获取默认配置
95    fn get_default_config() -> DockerConfig {
96        DockerConfig {
97            data_dir: Self::get_default_data_dir(),
98            image_dir: format!("{}/images", Self::get_default_data_dir()),
99            container_dir: format!("{}/containers", Self::get_default_data_dir()),
100            network_dir: format!("{}/networks", Self::get_default_data_dir()),
101            default_network: "default".to_string(),
102            default_resources: ResourceLimits {
103                cpu_limit: 1.0,
104                memory_limit: 512,
105                storage_limit: 10,
106                network_limit: 10,
107            },
108            log_config: LogConfig {
109                log_level: "info".to_string(),
110                log_file: format!("{}/logs/docker.log", Self::get_default_data_dir()),
111                max_log_size: 100,
112            },
113        }
114    }
115
116    /// 获取默认数据目录
117    fn get_default_data_dir() -> String {
118        #[cfg(target_os = "windows")]
119        return "C:\\ProgramData\\rusty-docker\\data".to_string();
120
121        #[cfg(target_os = "linux")]
122        return "/var/lib/rusty-docker".to_string();
123
124        #[cfg(target_os = "macos")]
125        return "/usr/local/var/rusty-docker".to_string();
126    }
127
128    /// 获取配置
129    pub fn get_config(&self) -> Result<DockerConfig> {
130        // 读取配置文件
131        let config_content = fs::read_to_string(&self.config_path).map_err(|e| DockerError::io_error("read_to_string", e.to_string()))?;
132
133        // 解析配置文件
134        let config: DockerConfig = toml::from_str(&config_content).map_err(|e| DockerError::parse_error("DockerConfig", e.to_string()))?;
135
136        Ok(config)
137    }
138
139    /// 保存配置
140    pub fn save_config(&self, config: &DockerConfig) -> Result<()> {
141        // 序列化配置
142        let config_content = toml::to_string(config).map_err(|e| DockerError::parse_error("DockerConfig", e.to_string()))?;
143
144        // 写入配置文件
145        fs::write(&self.config_path, config_content).map_err(|e| DockerError::io_error("write", e.to_string()))?;
146        Ok(())
147    }
148
149    /// 生成唯一 ID
150    fn generate_id(data: &str) -> String {
151        use sha2::{Digest, Sha256};
152        let mut hasher = Sha256::new();
153        hasher.update(data);
154        hasher.update(
155            SystemTime::now()
156                .duration_since(SystemTime::UNIX_EPOCH)
157                .unwrap()
158                .as_secs()
159                .to_string(),
160        );
161        format!("{:x}", hasher.finalize())
162    }
163
164    /// 创建配置
165    pub fn create_config(
166        &self,
167        name: &str,
168        data: &str,
169        labels: HashMap<String, String>,
170    ) -> Result<ConfigInfo> {
171        let id = Self::generate_id(&format!("{}{}", name, data));
172        let config_info = ConfigInfo {
173            id: id.clone(),
174            name: name.to_string(),
175            data: data.to_string(),
176            created_at: SystemTime::now(),
177            labels,
178        };
179
180        // 序列化配置信息
181        let config_content = toml::to_string(&config_info).map_err(|e| DockerError::parse_error("ConfigInfo", e.to_string()))?;
182
183        // 写入配置文件
184        let config_path = format!("{}/{}.toml", self.configs_dir, id);
185        fs::write(&config_path, config_content).map_err(|e| DockerError::io_error("write", e.to_string()))?;
186
187        Ok(config_info)
188    }
189
190    /// 更新配置
191    pub fn update_config(
192        &self,
193        config_id: &str,
194        data: &str,
195        labels: HashMap<String, String>,
196    ) -> Result<ConfigInfo> {
197        // 检查配置是否存在
198        let config_path = format!("{}/{}.toml", self.configs_dir, config_id);
199        if !Path::new(&config_path).exists() {
200            return Err(DockerError::not_found("config", format!(
201                "Config {} not found",
202                config_id
203            )));
204        }
205
206        // 读取现有配置
207        let config_content = fs::read_to_string(&config_path).map_err(|e| DockerError::io_error("read_to_string", e.to_string()))?;
208        let mut config_info: ConfigInfo = toml::from_str(&config_content).map_err(|e| DockerError::parse_error("ConfigInfo", e.to_string()))?;
209
210        // 更新配置
211        config_info.data = data.to_string();
212        config_info.labels = labels;
213
214        // 序列化并保存
215        let updated_content = toml::to_string(&config_info).map_err(|e| DockerError::parse_error("ConfigInfo", e.to_string()))?;
216        fs::write(&config_path, updated_content).map_err(|e| DockerError::io_error("write", e.to_string()))?;
217
218        Ok(config_info)
219    }
220
221    /// 删除配置
222    pub fn delete_config(&self, config_id: &str) -> Result<()> {
223        let config_path = format!("{}/{}.toml", self.configs_dir, config_id);
224        if !Path::new(&config_path).exists() {
225            return Err(DockerError::not_found("config", format!(
226                "Config {} not found",
227                config_id
228            )));
229        }
230
231        fs::remove_file(&config_path).map_err(|e| DockerError::io_error("remove_file", e.to_string()))?;
232        Ok(())
233    }
234
235    /// 获取配置详细信息
236    pub fn get_config_info(&self, config_id: &str) -> Result<ConfigInfo> {
237        let config_path = format!("{}/{}.toml", self.configs_dir, config_id);
238        if !Path::new(&config_path).exists() {
239            return Err(DockerError::not_found("config", format!(
240                "Config {} not found",
241                config_id
242            )));
243        }
244
245        let config_content = fs::read_to_string(&config_path).map_err(|e| DockerError::io_error("read_to_string", e.to_string()))?;
246        let config_info: ConfigInfo = toml::from_str(&config_content).map_err(|e| DockerError::parse_error("ConfigInfo", e.to_string()))?;
247
248        Ok(config_info)
249    }
250
251    /// 列出所有配置
252    pub fn list_configs(&self) -> Result<Vec<ConfigInfo>> {
253        let mut configs = Vec::new();
254
255        // 遍历配置目录
256        for entry in fs::read_dir(&self.configs_dir).map_err(|e| DockerError::io_error("read_dir", e.to_string()))? {
257            let entry = entry.map_err(|e| DockerError::io_error("entry", e.to_string()))?;
258            let path = entry.path();
259
260            if path.is_file() && path.extension().unwrap_or_default() == "toml" {
261                let config_content = fs::read_to_string(&path).map_err(|e| DockerError::io_error("read_to_string", e.to_string()))?;
262                let config_info: ConfigInfo = toml::from_str(&config_content).map_err(|e| DockerError::parse_error("ConfigInfo", e.to_string()))?;
263                configs.push(config_info);
264            }
265        }
266
267        Ok(configs)
268    }
269
270    /// 创建密钥
271    pub fn create_secret(
272        &self,
273        name: &str,
274        data: &str,
275        labels: HashMap<String, String>,
276    ) -> Result<SecretInfo> {
277        let id = Self::generate_id(&format!("{}{}", name, data));
278        let digest = Self::generate_id(data);
279
280        let secret_info = SecretInfo {
281            id: id.clone(),
282            name: name.to_string(),
283            created_at: SystemTime::now(),
284            labels,
285            digest,
286        };
287
288        // 序列化密钥信息
289        let secret_content = toml::to_string(&secret_info).map_err(|e| DockerError::parse_error("SecretInfo", e.to_string()))?;
290
291        // 写入密钥文件
292        let secret_path = format!("{}/{}.toml", self.secrets_dir, id);
293        fs::write(&secret_path, secret_content).map_err(|e| DockerError::io_error("write", e.to_string()))?;
294
295        // 写入密钥数据(实际应用中应该加密存储)
296        let data_path = format!("{}/{}.data", self.secrets_dir, id);
297        fs::write(&data_path, data).map_err(|e| DockerError::io_error("write", e.to_string()))?;
298
299        Ok(secret_info)
300    }
301
302    /// 删除密钥
303    pub fn delete_secret(&self, secret_id: &str) -> Result<()> {
304        let secret_path = format!("{}/{}.toml", self.secrets_dir, secret_id);
305        let data_path = format!("{}/{}.data", self.secrets_dir, secret_id);
306
307        if !Path::new(&secret_path).exists() {
308            return Err(DockerError::not_found("secret", format!(
309                "Secret {} not found",
310                secret_id
311            )));
312        }
313
314        // 删除密钥文件和数据文件
315        fs::remove_file(&secret_path).map_err(|e| DockerError::io_error("remove_file", e.to_string()))?;
316        if Path::new(&data_path).exists() {
317            fs::remove_file(&data_path).map_err(|e| DockerError::io_error("remove_file", e.to_string()))?;
318        }
319
320        Ok(())
321    }
322
323    /// 获取密钥详细信息
324    pub fn get_secret_info(&self, secret_id: &str) -> Result<SecretInfo> {
325        let secret_path = format!("{}/{}.toml", self.secrets_dir, secret_id);
326        if !Path::new(&secret_path).exists() {
327            return Err(DockerError::not_found("secret", format!(
328                "Secret {} not found",
329                secret_id
330            )));
331        }
332
333        let secret_content = fs::read_to_string(&secret_path).map_err(|e| DockerError::io_error("read_to_string", e.to_string()))?;
334        let secret_info: SecretInfo = toml::from_str(&secret_content).map_err(|e| DockerError::parse_error("SecretInfo", e.to_string()))?;
335
336        Ok(secret_info)
337    }
338
339    /// 列出所有密钥
340    pub fn list_secrets(&self) -> Result<Vec<SecretInfo>> {
341        let mut secrets = Vec::new();
342
343        // 遍历密钥目录
344        for entry in fs::read_dir(&self.secrets_dir).map_err(|e| DockerError::io_error("read_dir", e.to_string()))? {
345            let entry = entry.map_err(|e| DockerError::io_error("entry", e.to_string()))?;
346            let path = entry.path();
347
348            if path.is_file() && path.extension().unwrap_or_default() == "toml" {
349                let secret_content = fs::read_to_string(&path).map_err(|e| DockerError::io_error("read_to_string", e.to_string()))?;
350                let secret_info: SecretInfo = toml::from_str(&secret_content).map_err(|e| DockerError::parse_error("SecretInfo", e.to_string()))?;
351                secrets.push(secret_info);
352            }
353        }
354
355        Ok(secrets)
356    }
357
358    /// 创建端点
359    pub fn create_endpoint(
360        &self,
361        name: &str,
362        endpoint_type: EndpointType,
363        url: &str,
364        use_tls: bool,
365        tls_cert_path: Option<String>,
366        tls_key_path: Option<String>,
367        tls_ca_path: Option<String>,
368        auth_token: Option<String>,
369        labels: HashMap<String, String>,
370    ) -> Result<EndpointInfo> {
371        let endpoint_type_str = match endpoint_type {
372            EndpointType::Local => "local",
373            EndpointType::Remote => "remote",
374            EndpointType::Cloud => "cloud",
375        };
376        let id = Self::generate_id(&format!("{}{}{}", name, url, endpoint_type_str));
377        let config = EndpointConfig {
378            id: id.clone(),
379            name: name.to_string(),
380            endpoint_type,
381            url: url.to_string(),
382            use_tls,
383            tls_cert_path,
384            tls_key_path,
385            tls_ca_path,
386            auth_token,
387            labels,
388        };
389
390        let endpoint_info = EndpointInfo {
391            config,
392            status: EndpointStatus::Disconnected,
393            created_at: SystemTime::now(),
394            last_connected_at: None,
395            connection_info: None,
396        };
397
398        // 序列化端点信息
399        let endpoint_content = toml::to_string(&endpoint_info).map_err(|e| DockerError::parse_error("EndpointInfo", e.to_string()))?;
400
401        // 写入端点文件
402        let endpoint_path = format!("{}/{}.toml", self.endpoints_dir, id);
403        fs::write(&endpoint_path, endpoint_content).map_err(|e| DockerError::io_error("write", e.to_string()))?;
404
405        Ok(endpoint_info)
406    }
407
408    /// 更新端点
409    pub fn update_endpoint(
410        &self,
411        endpoint_id: &str,
412        name: &str,
413        url: &str,
414        use_tls: bool,
415        tls_cert_path: Option<String>,
416        tls_key_path: Option<String>,
417        tls_ca_path: Option<String>,
418        auth_token: Option<String>,
419        labels: HashMap<String, String>,
420    ) -> Result<EndpointInfo> {
421        // 检查端点是否存在
422        let endpoint_path = format!("{}/{}.toml", self.endpoints_dir, endpoint_id);
423        if !Path::new(&endpoint_path).exists() {
424            return Err(DockerError::not_found("endpoint", format!(
425                "Endpoint {} not found",
426                endpoint_id
427            )));
428        }
429
430        // 读取现有端点
431        let endpoint_content = fs::read_to_string(&endpoint_path).map_err(|e| DockerError::io_error("read_to_string", e.to_string()))?;
432        let mut endpoint_info: EndpointInfo = toml::from_str(&endpoint_content).map_err(|e| DockerError::parse_error("EndpointInfo", e.to_string()))?;
433
434        // 更新端点配置
435        endpoint_info.config.name = name.to_string();
436        endpoint_info.config.url = url.to_string();
437        endpoint_info.config.use_tls = use_tls;
438        endpoint_info.config.tls_cert_path = tls_cert_path;
439        endpoint_info.config.tls_key_path = tls_key_path;
440        endpoint_info.config.tls_ca_path = tls_ca_path;
441        endpoint_info.config.auth_token = auth_token;
442        endpoint_info.config.labels = labels;
443
444        // 序列化并保存
445        let updated_content = toml::to_string(&endpoint_info).map_err(|e| DockerError::parse_error("EndpointInfo", e.to_string()))?;
446        fs::write(&endpoint_path, updated_content).map_err(|e| DockerError::io_error("write", e.to_string()))?;
447
448        Ok(endpoint_info)
449    }
450
451    /// 删除端点
452    pub fn delete_endpoint(&self, endpoint_id: &str) -> Result<()> {
453        let endpoint_path = format!("{}/{}.toml", self.endpoints_dir, endpoint_id);
454        if !Path::new(&endpoint_path).exists() {
455            return Err(DockerError::not_found("endpoint", format!(
456                "Endpoint {} not found",
457                endpoint_id
458            )));
459        }
460
461        fs::remove_file(&endpoint_path).map_err(|e| DockerError::io_error("remove_file", e.to_string()))?;
462        Ok(())
463    }
464
465    /// 获取端点详细信息
466    pub fn get_endpoint_info(&self, endpoint_id: &str) -> Result<EndpointInfo> {
467        let endpoint_path = format!("{}/{}.toml", self.endpoints_dir, endpoint_id);
468        if !Path::new(&endpoint_path).exists() {
469            return Err(DockerError::not_found("endpoint", format!(
470                "Endpoint {} not found",
471                endpoint_id
472            )));
473        }
474
475        let endpoint_content = fs::read_to_string(&endpoint_path).map_err(|e| DockerError::io_error("read_to_string", e.to_string()))?;
476        let endpoint_info: EndpointInfo = toml::from_str(&endpoint_content).map_err(|e| DockerError::parse_error("EndpointInfo", e.to_string()))?;
477
478        Ok(endpoint_info)
479    }
480
481    /// 列出所有端点
482    pub fn list_endpoints(&self) -> Result<Vec<EndpointInfo>> {
483        let mut endpoints = Vec::new();
484
485        // 遍历端点目录
486        for entry in fs::read_dir(&self.endpoints_dir).map_err(|e| DockerError::io_error("read_dir", e.to_string()))? {
487            let entry = entry.map_err(|e| DockerError::io_error("entry", e.to_string()))?;
488            let path = entry.path();
489
490            if path.is_file() && path.extension().unwrap_or_default() == "toml" {
491                let endpoint_content = fs::read_to_string(&path).map_err(|e| DockerError::io_error("read_to_string", e.to_string()))?;
492                let endpoint_info: EndpointInfo = toml::from_str(&endpoint_content).map_err(|e| DockerError::parse_error("EndpointInfo", e.to_string()))?;
493                endpoints.push(endpoint_info);
494            }
495        }
496
497        Ok(endpoints)
498    }
499
500    /// 测试端点连接
501    pub fn test_endpoint_connection(&self, endpoint_id: &str) -> Result<EndpointStatus> {
502        // 这里应该实现实际的连接测试逻辑
503        // 目前只是模拟连接成功
504        Ok(EndpointStatus::Connected)
505    }
506}
507
508impl Default for ConfigManager {
509    fn default() -> Self {
510        Self::new().expect("Failed to create config manager")
511    }
512}