xref: /DADK/dadk-user/src/executor/cache.rs (revision c6f35e8aa5fda2a3004763828fb91c7762df6087)
1 use std::{
2     path::PathBuf,
3     sync::{Arc, Once},
4 };
5 
6 use log::info;
7 
8 use crate::{
9     parser::{
10         task::{CodeSource, DADKTask, TaskType},
11         task_log::TaskLog,
12     },
13     scheduler::SchedEntity,
14     utils::{lazy_init::Lazy, path::abs_path},
15 };
16 
17 use super::ExecutorError;
18 
19 pub static CACHE_ROOT: Lazy<PathBuf> = Lazy::new();
20 
21 /// # 初始化缓存根目录
22 ///
23 /// ## 参数
24 ///
25 /// - `path` 缓存根目录的路径
26 pub fn cache_root_init(path: Option<PathBuf>) -> Result<(), ExecutorError> {
27     let cache_root: String;
28     if path.is_none() {
29         // 查询环境变量,是否有设置缓存根目录
30         let env = std::env::var("DADK_CACHE_ROOT");
31         if env.is_ok() {
32             cache_root = env.unwrap();
33         } else {
34             // 如果没有设置环境变量,则使用默认值
35             // 默认值为当前目录下的.cache目录
36             let cwd = std::env::current_dir().map_err(|e| ExecutorError::IoError(e.to_string()))?;
37             let cwd = cwd.to_str();
38 
39             if cwd.is_none() {
40                 return Err(ExecutorError::IoError(
41                     std::io::Error::new(
42                         std::io::ErrorKind::Other,
43                         "Current dir is not a valid unicode string",
44                     )
45                     .to_string(),
46                 ));
47             }
48             let cwd = cwd.unwrap();
49 
50             cache_root = format!("{}/dadk_cache", cwd);
51         }
52     } else {
53         // 如果有设置缓存根目录,则使用设置的值
54         let path = path.unwrap();
55         let x = path.to_str().ok_or(ExecutorError::IoError(
56             std::io::Error::new(
57                 std::io::ErrorKind::Other,
58                 "Cache root dir is not a valid unicode string",
59             )
60             .to_string(),
61         ))?;
62         cache_root = x.to_string();
63     }
64 
65     let cache_root = PathBuf::from(cache_root);
66 
67     // 如果缓存根目录不存在,则创建
68     if !cache_root.exists() {
69         info!("Cache root dir not exists, create it: {:?}", cache_root);
70         std::fs::create_dir_all(&cache_root).map_err(|e| ExecutorError::IoError(e.to_string()))?;
71     } else if !cache_root.is_dir() {
72         // 如果缓存根目录不是目录,则报错
73         return Err(ExecutorError::IoError(
74             std::io::Error::new(
75                 std::io::ErrorKind::NotADirectory,
76                 format!("Cache root dir is not a directory: {:?}", cache_root),
77             )
78             .to_string(),
79         ));
80     }
81 
82     // 初始化缓存根目录
83     static CACHE_ROOT_INIT_ONCE: Once = Once::new();
84     CACHE_ROOT_INIT_ONCE.call_once(|| CACHE_ROOT.init(cache_root));
85 
86     // 设置环境变量
87     std::env::set_var("DADK_CACHE_ROOT", CACHE_ROOT.get().to_str().unwrap());
88     info!("Cache root dir: {:?}", CACHE_ROOT.get());
89     return Ok(());
90 }
91 
92 #[derive(Debug, Clone, Copy)]
93 pub enum CacheDirType {
94     /// 构建缓存目录
95     Build,
96     /// 源码缓存目录
97     Source,
98     /// 每个任务执行数据缓存目录
99     TaskData,
100 }
101 
102 #[derive(Debug, Clone)]
103 pub struct CacheDir {
104     #[allow(dead_code)]
105     entity: Arc<SchedEntity>,
106     pub path: PathBuf,
107     pub cache_type: CacheDirType,
108 }
109 
110 impl CacheDir {
111     pub const DADK_BUILD_CACHE_DIR_ENV_KEY_PREFIX: &'static str = "DADK_BUILD_CACHE_DIR";
112     pub const DADK_SOURCE_CACHE_DIR_ENV_KEY_PREFIX: &'static str = "DADK_SOURCE_CACHE_DIR";
113     pub fn new(entity: Arc<SchedEntity>, cache_type: CacheDirType) -> Result<Self, ExecutorError> {
114         let task = entity.task();
115         let path = Self::get_path(&task, cache_type);
116 
117         let result = Self {
118             entity,
119             path,
120             cache_type,
121         };
122 
123         result.create()?;
124 
125         return Ok(result);
126     }
127 
128     fn get_path(task: &DADKTask, cache_type: CacheDirType) -> PathBuf {
129         let cache_root = CACHE_ROOT.get();
130         let name_version = task.name_version();
131         let cache_dir = match cache_type {
132             CacheDirType::Build => {
133                 format!("{}/build/{}", cache_root.to_str().unwrap(), name_version)
134             }
135             CacheDirType::Source => {
136                 format!("{}/source/{}", cache_root.to_str().unwrap(), name_version)
137             }
138             CacheDirType::TaskData => {
139                 format!(
140                     "{}/task_data/{}",
141                     cache_root.to_str().unwrap(),
142                     name_version
143                 )
144             }
145         };
146         abs_path(&PathBuf::from(cache_dir))
147     }
148 
149     pub fn build_dir(entity: Arc<SchedEntity>) -> Result<PathBuf, ExecutorError> {
150         return Ok(Self::new(entity.clone(), CacheDirType::Build)?.path);
151     }
152 
153     pub fn source_dir(entity: Arc<SchedEntity>) -> Result<PathBuf, ExecutorError> {
154         return Ok(Self::new(entity.clone(), CacheDirType::Source)?.path);
155     }
156 
157     pub fn build_dir_env_key(entity: &Arc<SchedEntity>) -> Result<String, ExecutorError> {
158         let name_version_env = entity.task().name_version_env();
159         return Ok(format!(
160             "{}_{}",
161             Self::DADK_BUILD_CACHE_DIR_ENV_KEY_PREFIX,
162             name_version_env
163         ));
164     }
165 
166     pub fn source_dir_env_key(entity: &Arc<SchedEntity>) -> Result<String, ExecutorError> {
167         let name_version_env = entity.task().name_version_env();
168         return Ok(format!(
169             "{}_{}",
170             Self::DADK_SOURCE_CACHE_DIR_ENV_KEY_PREFIX,
171             name_version_env
172         ));
173     }
174 
175     pub fn need_source_cache(entity: &Arc<SchedEntity>) -> bool {
176         let task_type = &entity.task().task_type;
177 
178         if let TaskType::BuildFromSource(cs) = task_type {
179             match cs {
180                 CodeSource::Git(_) | CodeSource::Archive(_) => {
181                     return true;
182                 }
183                 CodeSource::Local(_) => {
184                     return false;
185                 }
186             }
187         } else if let TaskType::InstallFromPrebuilt(ps) = task_type {
188             match ps {
189                 crate::parser::task::PrebuiltSource::Archive(_) => return false,
190                 crate::parser::task::PrebuiltSource::Local(_) => return false,
191             }
192         }
193         unimplemented!("Not fully implemented task type: {:?}", task_type);
194     }
195 
196     pub fn create(&self) -> Result<(), ExecutorError> {
197         if !self.path.exists() {
198             info!("Cache dir not exists, create it: {:?}", self.path);
199             std::fs::create_dir_all(&self.path)
200                 .map_err(|e| ExecutorError::IoError(e.to_string()))?;
201             info!("Cache dir: [{:?}] created.", self.path);
202         } else if !self.path.is_dir() {
203             // 如果路径类别不是目录,则报错
204             return Err(ExecutorError::IoError(
205                 std::io::Error::new(
206                     std::io::ErrorKind::NotADirectory,
207                     format!("Cache dir is not a directory: {:?}", self.path),
208                 )
209                 .to_string(),
210             ));
211         }
212 
213         return Ok(());
214     }
215 
216     /// 判断缓存目录是否为空
217     pub fn is_empty(&self) -> Result<bool, ExecutorError> {
218         let x = self
219             .path
220             .read_dir()
221             .map_err(|e| ExecutorError::IoError(e.to_string()))?;
222         for _ in x {
223             return Ok(false);
224         }
225 
226         return Ok(true);
227     }
228 
229     /// # 递归删除自身目录
230     /// 递归删除自身目录,如果目录不存在,则忽略
231     ///
232     /// 请注意,这会删除整个目录,包括目录下的所有文件和子目录
233     pub fn remove_self_recursive(&self) -> Result<(), ExecutorError> {
234         let path = &self.path;
235         if path.exists() {
236             std::fs::remove_dir_all(path).map_err(|e| ExecutorError::IoError(e.to_string()))?;
237         }
238         return Ok(());
239     }
240 }
241 
242 #[derive(Debug, Clone)]
243 pub struct TaskDataDir {
244     dir: CacheDir,
245 }
246 
247 impl TaskDataDir {
248     const TASK_LOG_FILE_NAME: &'static str = "task_log.toml";
249     pub fn new(entity: Arc<SchedEntity>) -> Result<Self, ExecutorError> {
250         let dir = CacheDir::new(entity.clone(), CacheDirType::TaskData)?;
251         return Ok(Self { dir });
252     }
253 
254     /// # 获取任务日志
255     pub fn task_log(&self) -> TaskLog {
256         let path = self.dir.path.join(Self::TASK_LOG_FILE_NAME);
257         if path.exists() {
258             let content = std::fs::read_to_string(&path).unwrap();
259             let task_log: TaskLog = toml::from_str(&content).unwrap();
260             return task_log;
261         } else {
262             return TaskLog::new();
263         }
264     }
265 
266     /// # 设置任务日志
267     pub fn save_task_log(&self, task_log: &TaskLog) -> Result<(), ExecutorError> {
268         let path = self.dir.path.join(Self::TASK_LOG_FILE_NAME);
269         let content = toml::to_string(task_log).unwrap();
270         std::fs::write(&path, content).map_err(|e| ExecutorError::IoError(e.to_string()))?;
271         return Ok(());
272     }
273 }
274