ya_runtime_wasi/
deploy.rs

1use crate::manifest::WasmImage;
2
3use std::{
4    borrow::Cow,
5    path::{Path, PathBuf},
6    {fs, io},
7};
8
9use anyhow::{Context, Result};
10use serde::{Deserialize, Serialize};
11use uuid::Uuid;
12
13/// Represents deployed Yagna Wasm image with set up volumes inside the
14/// container.
15///
16/// A handle to the deployed image can be obtained after [`ya_runtime_wasi::deploy`]
17/// command was executed, however, then the image will not have been yet validated. To
18/// obtain a handle to a validated image you must run [`ya_runtime_wasi::start`] first.
19///
20/// [`ya_runtime_wasi::deploy`]: fn.deploy.html
21/// [`ya_runtime_wasi::start`]: fn.start.html
22///
23/// ## Example
24///
25/// ```rust,no_run
26/// use std::path::Path;
27/// use ya_runtime_wasi::{deploy, DeployFile, start};
28///
29/// deploy(Path::new("workspace"), Path::new("package.zip")).unwrap();
30/// let not_validated = DeployFile::load(Path::new("workspace")).unwrap();
31///
32/// start(Path::new("workspace")).unwrap();
33/// let validated = DeployFile::load(Path::new("workspace")).unwrap();
34/// ```
35#[derive(Serialize, Deserialize)]
36pub struct DeployFile {
37    image_path: PathBuf,
38    vols: Vec<ContainerVolume>,
39}
40
41impl DeployFile {
42    fn for_image(image: &WasmImage) -> Result<Self> {
43        let image_path = image.path().to_owned();
44        let vols = image
45            .manifest
46            .mount_points
47            .iter()
48            .map(|mount_point| ContainerVolume {
49                name: format!("vol-{}", Uuid::new_v4()),
50                path: absolute_path(mount_point.path()).into(),
51            })
52            .collect();
53        Ok(DeployFile { image_path, vols })
54    }
55
56    /// Loads deployed image from workspace where [`ya_runtime_wasi::deploy`] was executed.
57    ///
58    /// [`ya_runtime_wasi::deploy`]: fn.deploy.html
59    pub fn load(work_dir: impl AsRef<Path>) -> Result<Self> {
60        let deploy_file = deploy_path(work_dir.as_ref());
61        let reader = io::BufReader::new(fs::File::open(&deploy_file).with_context(|| {
62            format!(
63                "Can't read deploy file {}. Did you run deploy command?",
64                deploy_file.display()
65            )
66        })?);
67        let deploy = serde_json::from_reader(reader)?;
68
69        Ok(deploy)
70    }
71
72    pub(crate) fn save(&self, work_dir: impl AsRef<Path>) -> Result<()> {
73        let deploy_file = deploy_path(work_dir.as_ref());
74        fs::write(&deploy_file, serde_json::to_vec(&self)?)?;
75        Ok(())
76    }
77
78    pub(crate) fn create_dirs(&self, work_dir: impl AsRef<Path>) -> Result<()> {
79        let work_dir = work_dir.as_ref();
80        for vol in &self.vols {
81            fs::create_dir(work_dir.join(&vol.name))?;
82        }
83        Ok(())
84    }
85
86    /// Returns path to the deployed image.
87    pub fn image_path(&self) -> &Path {
88        &self.image_path
89    }
90
91    /// Returns an iterator over mapped container volumes.
92    pub fn vols(&self) -> impl Iterator<Item = &ContainerVolume> {
93        self.vols.iter()
94    }
95}
96
97fn deploy_path(work_dir: &Path) -> PathBuf {
98    work_dir.join("deploy.json")
99}
100
101fn absolute_path(path: &str) -> Cow<'_, str> {
102    if path.starts_with('/') {
103        Cow::Borrowed(path)
104    } else {
105        Cow::Owned(format!("/{}", path))
106    }
107}
108
109/// Represents name and path to the mapped volume in the container.
110#[derive(Serialize, Deserialize, Debug, Clone)]
111#[serde(rename_all = "camelCase")]
112pub struct ContainerVolume {
113    /// Volume name
114    pub name: String,
115
116    /// Path to the volume inside the container
117    pub path: String,
118}
119
120/// Deploys the Wasm image into the workspace.
121///
122/// Takes path to workdir and path to the Wasm image as arguments.
123///
124/// ## Example
125///
126/// ```rust,no_run
127/// use std::path::Path;
128/// use ya_runtime_wasi::deploy;
129///
130/// deploy(Path::new("workspace"), Path::new("package.zig")).unwrap();
131/// ```
132pub fn deploy(workdir: impl AsRef<Path>, path: impl AsRef<Path>) -> Result<()> {
133    let workdir = workdir.as_ref();
134    let path = path.as_ref();
135
136    let image = WasmImage::new(&path)
137        .with_context(|| format!("Can't read image file {}.", path.display()))?;
138    let deploy_file = DeployFile::for_image(&image)?;
139    deploy_file.save(workdir)?;
140    deploy_file.create_dirs(workdir)?;
141
142    log::info!("Deploy completed");
143    log::info!("Volumes = {:#?}", deploy_file.vols);
144
145    Ok(())
146}