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