oras/
lib.rs

1//! OCI Registry As Storage (ORAS) provides a mechanism for storing arbitrary
2//! artifacts in registries that support the
3//! [Open Container Initiative](https://opencontainers.org) distribution
4//! specification.
5//! 
6//! More information about the ORAS project may be found at
7//! [github.com/deislabs/oras](https://github.com/deislabs/oras).
8//! 
9//! This crate is not affiliated with deislabs.
10#![deny(missing_docs)]
11#![warn(rust_2018_idioms)]
12
13use oci_distribution::{Reference, Client as OCIClient};
14use oci_distribution::manifest::OciManifest;
15use std::convert::{TryFrom, TryInto};
16
17/// Connects to an OCI registry and pushes or pulls artifacts with custom metadata types.
18#[derive(Default)]
19pub struct Client {
20    oci_client: OCIClient,
21}
22
23impl Client {
24    /// Allocates a new `Client`.
25    pub fn new(oci_client: OCIClient) -> Self {
26        Self{
27            oci_client,
28        }
29    }
30
31    /// Push an object to an OCI registry.
32    pub async fn push<T>(&mut self, _reference: &Reference, _object: T) -> anyhow::Result<OciManifest>
33    where T: TryInto<std::vec::Vec<u8>, Error = anyhow::Error> + Send {
34        unimplemented!()
35    }
36
37    /// Pull an object from an OCI registry.
38    pub async fn pull<T>(&mut self, reference: &Reference) -> anyhow::Result<(T, OciManifest)>
39    where T: TryFrom<std::vec::Vec<u8>, Error = anyhow::Error> + Send {
40        let data = self.oci_client.pull_image(&reference).await?;
41
42        let result = T::try_from(data.content)?;
43
44        // TODO(jlegrone): set manifest fields instead of using default()
45        let mut manifest = OciManifest::default();
46        manifest.config.digest = data.digest.expect("digest should always be known after pull");
47
48        Ok((result, manifest))
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use oci_distribution::Reference;
55    use std::convert::TryFrom;
56    use std::vec::Vec;
57
58    #[derive(Debug)]
59    struct TestObject {
60        content: String,
61    }
62
63    impl TryFrom<Vec<u8>> for TestObject {
64        type Error = anyhow::Error;
65
66        fn try_from(from: Vec<u8>) -> anyhow::Result<Self> {
67            Ok(Self{
68                content: std::str::from_utf8(&from)?.to_owned(),
69            })
70        }
71    }
72
73    #[tokio::test]
74    async fn test_pull_image() {
75        let test_ref = std::env::var("TEST_ORAS_REF").expect("TEST_ORAS_REF env var set");
76        let image = Reference::try_from(test_ref).expect("failed to parse reference");
77
78        let (result, desc) = crate::Client::default().pull::<TestObject>(&image).await.expect("failed to pull manifest");
79
80        assert_eq!("sha256:f29dba55022eec8c0ce1cbfaaed45f2352ab3fbbb1cdcd5ea30ca3513deb70c9", desc.config.digest);
81        assert_eq!(
82            "{\"description\":\"\",\"invocationImages\":null,\"name\":\"\",\"schemaVersion\":\"\",\"version\":\"v1\"}".to_owned(),
83            result.content,
84        );
85    }
86}