1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use crate::error::*;
use oci_spec::image::*;
use std::{
    fs,
    io::{Read, Seek},
    path::*,
};

use crate::{digest::Digest, image::*, ImageName};

/// Handler for oci-archive format
///
/// oci-archive consists of several manifests i.e. several container.
pub struct Archive<'buf, W: Read + Seek> {
    archive: Option<tar::Archive<&'buf mut W>>,
}

impl<'buf, W: Read + Seek> Archive<'buf, W> {
    pub fn new(buf: &'buf mut W) -> Self {
        Self {
            archive: Some(tar::Archive::new(buf)),
        }
    }

    pub fn entries(&mut self) -> Result<tar::Entries<&'buf mut W>> {
        let raw = self
            .archive
            .take()
            .expect("This never becomes None except in this function");
        let inner = raw.into_inner();
        inner.rewind()?;
        self.archive = Some(tar::Archive::new(inner));
        Ok(self
            .archive
            .as_mut()
            .expect("This never becomes None except in this function")
            .entries_with_seek()?)
    }

    pub fn get_manifests(&mut self) -> Result<Vec<(ImageName, ImageManifest)>> {
        let index = self.get_index()?;
        index
            .manifests()
            .iter()
            .map(|manifest| {
                let annotations = annotations::flat::Annotations::from_map(
                    manifest.annotations().clone().unwrap_or_default(),
                )?;
                let image_name = annotations.ref_name.ok_or(Error::MissingManifestName)?;
                let image_name = ImageName::parse(&image_name)?;
                let digest = Digest::new(manifest.digest())?;
                let manifest = self.get_manifest(&digest)?;
                Ok((image_name, manifest))
            })
            .collect()
    }

    pub fn get_index(&mut self) -> Result<ImageIndex> {
        for entry in self.entries()? {
            let mut entry = entry?;
            if entry.path()?.as_os_str() == "index.json" {
                let mut out = Vec::new();
                entry.read_to_end(&mut out)?;
                return Ok(ImageIndex::from_reader(&*out)?);
            }
        }
        Err(Error::MissingIndex)
    }

    pub fn get_blob(&mut self, digest: &Digest) -> Result<tar::Entry<&'buf mut W>> {
        for entry in self.entries()? {
            let entry = entry?;
            if entry.path()? == digest.as_path() {
                return Ok(entry);
            }
        }
        Err(Error::UnknownDigest(digest.clone()))
    }

    pub fn get_manifest(&mut self, digest: &Digest) -> Result<ImageManifest> {
        let entry = self.get_blob(digest)?;
        Ok(ImageManifest::from_reader(entry)?)
    }

    pub fn get_config(&mut self, digest: &Digest) -> Result<ImageConfiguration> {
        let entry = self.get_blob(digest)?;
        Ok(ImageConfiguration::from_reader(entry)?)
    }

    pub fn unpack_layer(&mut self, layer: &Descriptor, dest: &Path) -> Result<()> {
        let digest = Digest::new(layer.digest())?;
        let blob = self.get_blob(&digest)?;
        match layer.media_type() {
            MediaType::ImageLayerGzip => {
                let buf = flate2::read::GzDecoder::new(blob);
                tar::Archive::new(buf).unpack(dest)?;
                Ok(())
            }
            _ => unimplemented!("Unsupported layer type"),
        }
    }
}

/// Load oci-archive into local storage
pub fn load(input: &Path) -> Result<()> {
    let mut f = fs::File::open(input)?;
    let mut ar = Archive::new(&mut f);
    for (image_name, manifest) in ar.get_manifests()? {
        let dest = crate::local::image_dir(&image_name)?;
        if dest.exists() {
            log::warn!(
                "Local image aleady exists, skip loading: {}",
                dest.display()
            );
            continue;
        }
        log::info!("Create local image: {}", dest.display());
        fs::create_dir_all(&dest)?;
        for layer in manifest.layers() {
            ar.unpack_layer(layer, &dest)?;
        }
    }
    Ok(())
}