xref: /DADK/dadk-user/src/executor/cache.rs (revision eaa67f3cf8881c221a744937c6318444b068a801)
173779f3dSLoGin use std::{
273779f3dSLoGin     path::PathBuf,
373779f3dSLoGin     sync::{Arc, Once},
473779f3dSLoGin };
573779f3dSLoGin 
673779f3dSLoGin use log::info;
773779f3dSLoGin 
873779f3dSLoGin use crate::{
973779f3dSLoGin     parser::{
1073779f3dSLoGin         task::{CodeSource, DADKTask, TaskType},
1173779f3dSLoGin         task_log::TaskLog,
1273779f3dSLoGin     },
1373779f3dSLoGin     scheduler::SchedEntity,
14*eaa67f3cSLoGin     utils::{lazy_init::Lazy, path::abs_path},
1573779f3dSLoGin };
1673779f3dSLoGin 
1773779f3dSLoGin use super::ExecutorError;
1873779f3dSLoGin 
1973779f3dSLoGin pub static CACHE_ROOT: Lazy<PathBuf> = Lazy::new();
2073779f3dSLoGin 
2173779f3dSLoGin /// # 初始化缓存根目录
2273779f3dSLoGin ///
2373779f3dSLoGin /// ## 参数
2473779f3dSLoGin ///
2573779f3dSLoGin /// - `path` 缓存根目录的路径
cache_root_init(path: Option<PathBuf>) -> Result<(), ExecutorError>2673779f3dSLoGin pub fn cache_root_init(path: Option<PathBuf>) -> Result<(), ExecutorError> {
2773779f3dSLoGin     let cache_root: String;
2873779f3dSLoGin     if path.is_none() {
2973779f3dSLoGin         // 查询环境变量,是否有设置缓存根目录
3073779f3dSLoGin         let env = std::env::var("DADK_CACHE_ROOT");
3173779f3dSLoGin         if env.is_ok() {
3273779f3dSLoGin             cache_root = env.unwrap();
3373779f3dSLoGin         } else {
3473779f3dSLoGin             // 如果没有设置环境变量,则使用默认值
3573779f3dSLoGin             // 默认值为当前目录下的.cache目录
3673779f3dSLoGin             let cwd = std::env::current_dir().map_err(|e| ExecutorError::IoError(e.to_string()))?;
3773779f3dSLoGin             let cwd = cwd.to_str();
3873779f3dSLoGin 
3973779f3dSLoGin             if cwd.is_none() {
4073779f3dSLoGin                 return Err(ExecutorError::IoError(
4173779f3dSLoGin                     std::io::Error::new(
4273779f3dSLoGin                         std::io::ErrorKind::Other,
4373779f3dSLoGin                         "Current dir is not a valid unicode string",
4473779f3dSLoGin                     )
4573779f3dSLoGin                     .to_string(),
4673779f3dSLoGin                 ));
4773779f3dSLoGin             }
4873779f3dSLoGin             let cwd = cwd.unwrap();
4973779f3dSLoGin 
5073779f3dSLoGin             cache_root = format!("{}/dadk_cache", cwd);
5173779f3dSLoGin         }
5273779f3dSLoGin     } else {
5373779f3dSLoGin         // 如果有设置缓存根目录,则使用设置的值
5473779f3dSLoGin         let path = path.unwrap();
5573779f3dSLoGin         let x = path.to_str().ok_or(ExecutorError::IoError(
5673779f3dSLoGin             std::io::Error::new(
5773779f3dSLoGin                 std::io::ErrorKind::Other,
5873779f3dSLoGin                 "Cache root dir is not a valid unicode string",
5973779f3dSLoGin             )
6073779f3dSLoGin             .to_string(),
6173779f3dSLoGin         ))?;
6273779f3dSLoGin         cache_root = x.to_string();
6373779f3dSLoGin     }
6473779f3dSLoGin 
6573779f3dSLoGin     let cache_root = PathBuf::from(cache_root);
6673779f3dSLoGin 
6773779f3dSLoGin     // 如果缓存根目录不存在,则创建
6873779f3dSLoGin     if !cache_root.exists() {
6973779f3dSLoGin         info!("Cache root dir not exists, create it: {:?}", cache_root);
7073779f3dSLoGin         std::fs::create_dir_all(&cache_root).map_err(|e| ExecutorError::IoError(e.to_string()))?;
7173779f3dSLoGin     } else if !cache_root.is_dir() {
7273779f3dSLoGin         // 如果缓存根目录不是目录,则报错
7373779f3dSLoGin         return Err(ExecutorError::IoError(
7473779f3dSLoGin             std::io::Error::new(
7573779f3dSLoGin                 std::io::ErrorKind::NotADirectory,
7673779f3dSLoGin                 format!("Cache root dir is not a directory: {:?}", cache_root),
7773779f3dSLoGin             )
7873779f3dSLoGin             .to_string(),
7973779f3dSLoGin         ));
8073779f3dSLoGin     }
8173779f3dSLoGin 
8273779f3dSLoGin     // 初始化缓存根目录
8373779f3dSLoGin     static CACHE_ROOT_INIT_ONCE: Once = Once::new();
8473779f3dSLoGin     CACHE_ROOT_INIT_ONCE.call_once(|| CACHE_ROOT.init(cache_root));
8573779f3dSLoGin 
8673779f3dSLoGin     // 设置环境变量
8773779f3dSLoGin     std::env::set_var("DADK_CACHE_ROOT", CACHE_ROOT.get().to_str().unwrap());
8873779f3dSLoGin     info!("Cache root dir: {:?}", CACHE_ROOT.get());
8973779f3dSLoGin     return Ok(());
9073779f3dSLoGin }
9173779f3dSLoGin 
9273779f3dSLoGin #[derive(Debug, Clone, Copy)]
9373779f3dSLoGin pub enum CacheDirType {
9473779f3dSLoGin     /// 构建缓存目录
9573779f3dSLoGin     Build,
9673779f3dSLoGin     /// 源码缓存目录
9773779f3dSLoGin     Source,
9873779f3dSLoGin     /// 每个任务执行数据缓存目录
9973779f3dSLoGin     TaskData,
10073779f3dSLoGin }
10173779f3dSLoGin 
10273779f3dSLoGin #[derive(Debug, Clone)]
10373779f3dSLoGin pub struct CacheDir {
10473779f3dSLoGin     #[allow(dead_code)]
10573779f3dSLoGin     entity: Arc<SchedEntity>,
10673779f3dSLoGin     pub path: PathBuf,
10773779f3dSLoGin     pub cache_type: CacheDirType,
10873779f3dSLoGin }
10973779f3dSLoGin 
11073779f3dSLoGin impl CacheDir {
11173779f3dSLoGin     pub const DADK_BUILD_CACHE_DIR_ENV_KEY_PREFIX: &'static str = "DADK_BUILD_CACHE_DIR";
11273779f3dSLoGin     pub const DADK_SOURCE_CACHE_DIR_ENV_KEY_PREFIX: &'static str = "DADK_SOURCE_CACHE_DIR";
new(entity: Arc<SchedEntity>, cache_type: CacheDirType) -> Result<Self, ExecutorError>11373779f3dSLoGin     pub fn new(entity: Arc<SchedEntity>, cache_type: CacheDirType) -> Result<Self, ExecutorError> {
11473779f3dSLoGin         let task = entity.task();
11573779f3dSLoGin         let path = Self::get_path(&task, cache_type);
11673779f3dSLoGin 
11773779f3dSLoGin         let result = Self {
11873779f3dSLoGin             entity,
11973779f3dSLoGin             path,
12073779f3dSLoGin             cache_type,
12173779f3dSLoGin         };
12273779f3dSLoGin 
12373779f3dSLoGin         result.create()?;
12473779f3dSLoGin 
12573779f3dSLoGin         return Ok(result);
12673779f3dSLoGin     }
12773779f3dSLoGin 
get_path(task: &DADKTask, cache_type: CacheDirType) -> PathBuf12873779f3dSLoGin     fn get_path(task: &DADKTask, cache_type: CacheDirType) -> PathBuf {
12973779f3dSLoGin         let cache_root = CACHE_ROOT.get();
13073779f3dSLoGin         let name_version = task.name_version();
13173779f3dSLoGin         let cache_dir = match cache_type {
13273779f3dSLoGin             CacheDirType::Build => {
13373779f3dSLoGin                 format!("{}/build/{}", cache_root.to_str().unwrap(), name_version)
13473779f3dSLoGin             }
13573779f3dSLoGin             CacheDirType::Source => {
13673779f3dSLoGin                 format!("{}/source/{}", cache_root.to_str().unwrap(), name_version)
13773779f3dSLoGin             }
13873779f3dSLoGin             CacheDirType::TaskData => {
13973779f3dSLoGin                 format!(
14073779f3dSLoGin                     "{}/task_data/{}",
14173779f3dSLoGin                     cache_root.to_str().unwrap(),
14273779f3dSLoGin                     name_version
14373779f3dSLoGin                 )
14473779f3dSLoGin             }
14573779f3dSLoGin         };
146*eaa67f3cSLoGin         abs_path(&PathBuf::from(cache_dir))
14773779f3dSLoGin     }
14873779f3dSLoGin 
14973779f3dSLoGin     pub fn build_dir(entity: Arc<SchedEntity>) -> Result<PathBuf, ExecutorError> {
build_dir(entity: Arc<SchedEntity>) -> Result<PathBuf, ExecutorError>15073779f3dSLoGin         return Ok(Self::new(entity.clone(), CacheDirType::Build)?.path);
15173779f3dSLoGin     }
15273779f3dSLoGin 
15373779f3dSLoGin     pub fn source_dir(entity: Arc<SchedEntity>) -> Result<PathBuf, ExecutorError> {
source_dir(entity: Arc<SchedEntity>) -> Result<PathBuf, ExecutorError>15473779f3dSLoGin         return Ok(Self::new(entity.clone(), CacheDirType::Source)?.path);
15573779f3dSLoGin     }
15673779f3dSLoGin 
15773779f3dSLoGin     pub fn build_dir_env_key(entity: &Arc<SchedEntity>) -> Result<String, ExecutorError> {
build_dir_env_key(entity: &Arc<SchedEntity>) -> Result<String, ExecutorError>15873779f3dSLoGin         let name_version_env = entity.task().name_version_env();
15973779f3dSLoGin         return Ok(format!(
16073779f3dSLoGin             "{}_{}",
16173779f3dSLoGin             Self::DADK_BUILD_CACHE_DIR_ENV_KEY_PREFIX,
16273779f3dSLoGin             name_version_env
16373779f3dSLoGin         ));
16473779f3dSLoGin     }
16573779f3dSLoGin 
16673779f3dSLoGin     pub fn source_dir_env_key(entity: &Arc<SchedEntity>) -> Result<String, ExecutorError> {
source_dir_env_key(entity: &Arc<SchedEntity>) -> Result<String, ExecutorError>16773779f3dSLoGin         let name_version_env = entity.task().name_version_env();
16873779f3dSLoGin         return Ok(format!(
16973779f3dSLoGin             "{}_{}",
17073779f3dSLoGin             Self::DADK_SOURCE_CACHE_DIR_ENV_KEY_PREFIX,
17173779f3dSLoGin             name_version_env
17273779f3dSLoGin         ));
17373779f3dSLoGin     }
17473779f3dSLoGin 
17573779f3dSLoGin     pub fn need_source_cache(entity: &Arc<SchedEntity>) -> bool {
need_source_cache(entity: &Arc<SchedEntity>) -> bool17673779f3dSLoGin         let task_type = &entity.task().task_type;
17773779f3dSLoGin 
17873779f3dSLoGin         if let TaskType::BuildFromSource(cs) = task_type {
17973779f3dSLoGin             match cs {
18073779f3dSLoGin                 CodeSource::Git(_) | CodeSource::Archive(_) => {
18173779f3dSLoGin                     return true;
18273779f3dSLoGin                 }
18373779f3dSLoGin                 CodeSource::Local(_) => {
18473779f3dSLoGin                     return false;
18573779f3dSLoGin                 }
18673779f3dSLoGin             }
18773779f3dSLoGin         } else if let TaskType::InstallFromPrebuilt(ps) = task_type {
18873779f3dSLoGin             match ps {
18973779f3dSLoGin                 crate::parser::task::PrebuiltSource::Archive(_) => return false,
19073779f3dSLoGin                 crate::parser::task::PrebuiltSource::Local(_) => return false,
19173779f3dSLoGin             }
19273779f3dSLoGin         }
19373779f3dSLoGin         unimplemented!("Not fully implemented task type: {:?}", task_type);
19473779f3dSLoGin     }
19573779f3dSLoGin 
19673779f3dSLoGin     pub fn create(&self) -> Result<(), ExecutorError> {
create(&self) -> Result<(), ExecutorError>19773779f3dSLoGin         if !self.path.exists() {
19873779f3dSLoGin             info!("Cache dir not exists, create it: {:?}", self.path);
19973779f3dSLoGin             std::fs::create_dir_all(&self.path)
20073779f3dSLoGin                 .map_err(|e| ExecutorError::IoError(e.to_string()))?;
20173779f3dSLoGin             info!("Cache dir: [{:?}] created.", self.path);
20273779f3dSLoGin         } else if !self.path.is_dir() {
20373779f3dSLoGin             // 如果路径类别不是目录,则报错
20473779f3dSLoGin             return Err(ExecutorError::IoError(
20573779f3dSLoGin                 std::io::Error::new(
20673779f3dSLoGin                     std::io::ErrorKind::NotADirectory,
20773779f3dSLoGin                     format!("Cache dir is not a directory: {:?}", self.path),
20873779f3dSLoGin                 )
20973779f3dSLoGin                 .to_string(),
21073779f3dSLoGin             ));
21173779f3dSLoGin         }
21273779f3dSLoGin 
21373779f3dSLoGin         return Ok(());
21473779f3dSLoGin     }
21573779f3dSLoGin 
21673779f3dSLoGin     /// 判断缓存目录是否为空
21773779f3dSLoGin     pub fn is_empty(&self) -> Result<bool, ExecutorError> {
is_empty(&self) -> Result<bool, ExecutorError>21873779f3dSLoGin         let x = self
21973779f3dSLoGin             .path
22073779f3dSLoGin             .read_dir()
22173779f3dSLoGin             .map_err(|e| ExecutorError::IoError(e.to_string()))?;
22273779f3dSLoGin         for _ in x {
22373779f3dSLoGin             return Ok(false);
22473779f3dSLoGin         }
22573779f3dSLoGin 
22673779f3dSLoGin         return Ok(true);
22773779f3dSLoGin     }
22873779f3dSLoGin 
22973779f3dSLoGin     /// # 递归删除自身目录
23073779f3dSLoGin     /// 递归删除自身目录,如果目录不存在,则忽略
23173779f3dSLoGin     ///
23273779f3dSLoGin     /// 请注意,这会删除整个目录,包括目录下的所有文件和子目录
23373779f3dSLoGin     pub fn remove_self_recursive(&self) -> Result<(), ExecutorError> {
remove_self_recursive(&self) -> Result<(), ExecutorError>23473779f3dSLoGin         let path = &self.path;
23573779f3dSLoGin         if path.exists() {
23673779f3dSLoGin             std::fs::remove_dir_all(path).map_err(|e| ExecutorError::IoError(e.to_string()))?;
23773779f3dSLoGin         }
23873779f3dSLoGin         return Ok(());
23973779f3dSLoGin     }
24073779f3dSLoGin }
24173779f3dSLoGin 
24273779f3dSLoGin #[derive(Debug, Clone)]
24373779f3dSLoGin pub struct TaskDataDir {
24473779f3dSLoGin     dir: CacheDir,
24573779f3dSLoGin }
24673779f3dSLoGin 
24773779f3dSLoGin impl TaskDataDir {
24873779f3dSLoGin     const TASK_LOG_FILE_NAME: &'static str = "task_log.toml";
24973779f3dSLoGin     pub fn new(entity: Arc<SchedEntity>) -> Result<Self, ExecutorError> {
new(entity: Arc<SchedEntity>) -> Result<Self, ExecutorError>25073779f3dSLoGin         let dir = CacheDir::new(entity.clone(), CacheDirType::TaskData)?;
25173779f3dSLoGin         return Ok(Self { dir });
25273779f3dSLoGin     }
25373779f3dSLoGin 
25473779f3dSLoGin     /// # 获取任务日志
25573779f3dSLoGin     pub fn task_log(&self) -> TaskLog {
task_log(&self) -> TaskLog25673779f3dSLoGin         let path = self.dir.path.join(Self::TASK_LOG_FILE_NAME);
25773779f3dSLoGin         if path.exists() {
25873779f3dSLoGin             let content = std::fs::read_to_string(&path).unwrap();
25973779f3dSLoGin             let task_log: TaskLog = toml::from_str(&content).unwrap();
26073779f3dSLoGin             return task_log;
26173779f3dSLoGin         } else {
26273779f3dSLoGin             return TaskLog::new();
26373779f3dSLoGin         }
26473779f3dSLoGin     }
26573779f3dSLoGin 
26673779f3dSLoGin     /// # 设置任务日志
26773779f3dSLoGin     pub fn save_task_log(&self, task_log: &TaskLog) -> Result<(), ExecutorError> {
save_task_log(&self, task_log: &TaskLog) -> Result<(), ExecutorError>26873779f3dSLoGin         let path = self.dir.path.join(Self::TASK_LOG_FILE_NAME);
26973779f3dSLoGin         let content = toml::to_string(task_log).unwrap();
27073779f3dSLoGin         std::fs::write(&path, content).map_err(|e| ExecutorError::IoError(e.to_string()))?;
27173779f3dSLoGin         return Ok(());
27273779f3dSLoGin     }
27373779f3dSLoGin }
274