oci_util/container/
mod.rs

1use crate::distribution::pull::pull;
2use crate::filesystem::FileSystem;
3use crate::image::config::ConfigFile;
4use crate::image::layer::tar_file::TarFileTy;
5use crate::image::manifest::Manifest;
6use crate::image::Repositories;
7use crate::util::DigestPre;
8use anyhow::{anyhow, Error, Result};
9use log::{debug, info, warn};
10use oci_distribution::secrets::RegistryAuth;
11use oci_distribution::Reference;
12use std::fs::File;
13use std::path::PathBuf;
14
15///
16/// 初始化镜像
17/// 如果本地不存在该镜像,则先pull再初始化。
18pub async fn init(image: &Reference, auth: &RegistryAuth, force: bool) -> Result<Container> {
19    // 判断是否已存在该容器:
20    // 读取config
21    // 读取layer
22    // 初始化容器的文件系统
23    let repo = Repositories::init()?;
24    let manifest_digest = match repo.image_digest(&image) {
25        Some(digest) => digest.get_digest()?,
26        None => {
27            info!("本地未找到镜像{:?},先拉取镜像!", image);
28            pull(image, auth).await?
29        }
30    };
31    let path = FileSystem.container()?.join(&manifest_digest);
32
33    let manifest = Manifest::load(manifest_digest.as_str())?.to_oci_manifest()?;
34
35    let config = ConfigFile::load(&manifest.config.digest.get_digest()?)?;
36
37    let container = Container { path, config };
38    if force {
39        container.clear()?;
40    } else {
41        if container.exists() {
42            debug!("local disk had a container");
43            return Ok(container);
44        }
45    }
46    container.init()?;
47    Ok(container)
48}
49
50pub struct Container {
51    pub config: ConfigFile,
52    pub path: PathBuf,
53}
54
55impl Container {
56    pub fn cmd(&self) -> PathBuf {
57        let path = self.path.join(self.config.cmd.as_str());
58        debug!("base={:?} path={:?}", self.path, path);
59        path
60    }
61    pub fn exists(&self) -> bool {
62        self.path.exists()
63    }
64    pub fn clear(&self) -> Result<()> {
65        if self.exists() {
66            std::fs::remove_dir_all(&self.path)?;
67        }
68        Ok(())
69    }
70    pub fn init(&self) -> Result<()> {
71        let config = &self.config;
72        std::fs::create_dir_all(&self.path)?;
73        for copy in config.rootf.diff_ids.as_slice() {
74            debug!("read layer {:?}", copy);
75            let file = std::fs::File::open(FileSystem.layer_blobs()?.join(copy.get_digest()?))?;
76            let mut archive = tar::Archive::new(file);
77            let entries = archive.entries().unwrap();
78            for (_index, item) in entries.enumerate() {
79                if let Ok(item) = item {
80                    if let Some(path) = item.path()?.to_str().and_then(|x| Some(x.to_string())) {
81                        let tar_file: TarFileTy = path.into();
82                        apply_tar_file(tar_file, &self.path, item)?;
83                    } else {
84                        warn!("archive.entries.item has not path")
85                    }
86                } else {
87                    warn!("archive.entries.item fail")
88                }
89            }
90        }
91        Ok(())
92    }
93}
94
95pub fn apply_tar_file(
96    tar_file_ty: TarFileTy,
97    base: &PathBuf,
98    mut item: tar::Entry<File>,
99) -> Result<()> {
100    debug!("{:?}", tar_file_ty);
101    match tar_file_ty {
102        TarFileTy::Delete(file) => {
103            let target_path = base.join(&file);
104            debug!("target: {:?}", target_path);
105            if item.header().entry_type().is_file() {
106                std::fs::remove_file(&target_path)?;
107            } else if item.header().entry_type().is_dir() {
108                std::fs::remove_dir_all(&target_path)?;
109            } else {
110                warn!("暂不支持其他文件类型: {:?}", item.header().entry_type());
111            }
112        }
113        TarFileTy::Update(file) => {
114            let target_path = base.join(&file);
115            debug!("target: {:?}", target_path,);
116            if item.header().entry_type().is_file() {
117                let mut file = std::fs::File::create(target_path)?;
118                // let mut data = Vec::with_capacity(item.size() as usize);
119                // let read_size = item.read_to_end(&mut data)?;
120                std::io::copy(&mut item, &mut file)?;
121            } else if item.header().entry_type().is_dir() {
122                std::fs::create_dir_all(target_path)?;
123            } else {
124                warn!("暂不支持其他文件类型: {:?}", item.header().entry_type());
125            }
126        }
127    }
128    Ok(())
129}
130
131impl TryFrom<&Reference> for Container {
132    type Error = Error;
133
134    fn try_from(image: &Reference) -> std::result::Result<Self, Self::Error> {
135        let repo = Repositories::init()?;
136        let config_digest = repo
137            .image_digest(&image)
138            .ok_or(anyhow!("本地未找到镜像{:?}", image))?;
139        let path = FileSystem.container()?.join(&config_digest);
140        let config = ConfigFile::load(&config_digest)?;
141        Ok(Self { path, config })
142    }
143}