bpf_oci/
lib.rs

1//!  SPDX-License-Identifier: MIT
2//!
3//! Copyright (c) 2023, eunomia-bpf
4//! All rights reserved.
5//!
6
7use std::collections::HashMap;
8
9pub use oci_distribution;
10use oci_distribution::{
11    annotations,
12    client::{Config, ImageLayer},
13    manifest,
14    secrets::RegistryAuth,
15    Client, Reference,
16};
17
18pub mod auth;
19
20#[cfg(test)]
21pub(crate) mod tests;
22
23use anyhow::{anyhow, bail, Context, Result};
24/// Pull a wasm image from the registry
25/// Use the authencation info provided by `auth`
26/// Provide your own client if you want to customization
27pub async fn pull_wasm_image(
28    reference: &Reference,
29    auth: &RegistryAuth,
30    client: Option<&mut Client>,
31) -> Result<Vec<u8>> {
32    let mut local_client = Client::default();
33    let client = client.unwrap_or(&mut local_client);
34    let out = client
35        .pull(reference, auth, vec![manifest::WASM_LAYER_MEDIA_TYPE])
36        .await
37        .with_context(|| anyhow!("Failed to pull wasm image"))?
38        .layers
39        .into_iter()
40        .next()
41        .map(|v| v.data)
42        .with_context(|| anyhow!("Data not found from the image"))?;
43    Ok(out)
44}
45
46pub async fn push_wasm_image(
47    auth: &RegistryAuth,
48    reference: &Reference,
49    annotations: Option<HashMap<String, String>>,
50    module: &[u8],
51    client: Option<&mut Client>,
52) -> Result<()> {
53    let mut local_client = Client::default();
54    let client = client.unwrap_or(&mut local_client);
55    let layers = vec![ImageLayer::new(
56        module.to_vec(),
57        manifest::WASM_LAYER_MEDIA_TYPE.to_string(),
58        None,
59    )];
60    let config = Config {
61        annotations: None,
62        data: b"{}".to_vec(),
63        media_type: manifest::WASM_CONFIG_MEDIA_TYPE.to_string(),
64    };
65    let image_manifest = manifest::OciImageManifest::build(&layers, &config, annotations);
66    client
67        .push(reference, &layers, config, auth, Some(image_manifest))
68        .await
69        .with_context(|| anyhow!("Failed to push image"))?;
70
71    Ok(())
72}
73/// Parse annotations like key=value into HashMap
74pub fn parse_annotations<T: AsRef<str>>(input: &[T]) -> anyhow::Result<HashMap<String, String>> {
75    let mut annotations_map = HashMap::default();
76    for ent in input.iter() {
77        if let Some((key, value)) = ent.as_ref().split_once('=') {
78            annotations_map.insert(key.into(), value.into());
79        } else {
80            bail!("Annotations should be like `key=value`");
81        }
82    }
83    Ok(annotations_map)
84}
85/// Parse annotations like key=value into HashMap
86/// Will insert ORG_OPENCONTAINERS_IMAGE_TITLE if not provided
87pub fn parse_annotations_and_insert_image_title<T: AsRef<str>>(
88    input: &[T],
89    title: String,
90) -> anyhow::Result<HashMap<String, String>> {
91    let mut ret = parse_annotations(input)?;
92    if !ret.contains_key(&annotations::ORG_OPENCONTAINERS_IMAGE_TITLE.to_owned()) {
93        ret.insert(
94            annotations::ORG_OPENCONTAINERS_IMAGE_TITLE.to_string(),
95            title,
96        );
97    }
98    Ok(ret)
99}