wasmer_deploy_schema/schema/
app_config.rs

1//! User-facing app.yaml file config: [`AppConfigV1`].
2
3use std::collections::HashMap;
4
5use anyhow::{bail, Context};
6use bytesize::ByteSize;
7
8use super::StringWebcIdent;
9
10/// User-facing app.yaml config file for apps.
11///
12/// NOTE: only used by the backend, Edge itself does not use this format, and
13/// uses [`super::AppVersionV1Spec`] instead.
14#[derive(serde::Serialize, serde::Deserialize, schemars::JsonSchema, Clone, Debug)]
15pub struct AppConfigV1 {
16    /// Name of the app.
17    pub name: String,
18
19    /// App id assigned by the backend.
20    ///
21    /// This will get populated once the app has been deployed.
22    ///
23    /// This id is also used to map to the existing app during deployments.
24    // #[serde(skip_serializing_if = "Option::is_none")]
25    // pub description: Option<String>,
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub app_id: Option<String>,
28
29    /// The package to execute.
30    pub package: StringWebcIdent,
31
32    /// Environment variables.
33    #[serde(default, skip_serializing_if = "HashMap::is_empty")]
34    pub env: HashMap<String, String>,
35
36    /// CLI arguments passed to the runner.
37    /// Only applicable for runners that accept CLI arguments.
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub cli_args: Option<Vec<String>>,
40
41    #[serde(skip_serializing_if = "Option::is_none")]
42    pub capabilities: Option<AppConfigCapabilityMapV1>,
43}
44
45impl AppConfigV1 {
46    pub const KIND: &'static str = "wasmer.io/App.v0";
47    pub const CANONICAL_FILE_NAME: &'static str = "app.yaml";
48
49    pub fn to_yaml_value(self) -> Result<serde_yaml::Value, serde_yaml::Error> {
50        // Need to do an annoying type dance to both insert the kind field
51        // and also insert kind at the top.
52        let obj = match serde_yaml::to_value(self)? {
53            serde_yaml::Value::Mapping(m) => m,
54            _ => unreachable!(),
55        };
56        let mut m = serde_yaml::Mapping::new();
57        m.insert("kind".into(), Self::KIND.into());
58        for (k, v) in obj.into_iter() {
59            m.insert(k, v);
60        }
61        Ok(m.into())
62    }
63
64    pub fn to_yaml(self) -> Result<String, serde_yaml::Error> {
65        serde_yaml::to_string(&self.to_yaml_value()?)
66    }
67
68    pub fn parse_yaml(value: &str) -> Result<Self, anyhow::Error> {
69        let raw = serde_yaml::from_str::<serde_yaml::Value>(value).context("invalid yaml")?;
70        let kind = raw
71            .get("kind")
72            .context("invalid app config: no 'kind' field found")?
73            .as_str()
74            .context("invalid app config: 'kind' field is not a string")?;
75        match kind {
76            Self::KIND => {}
77            other => {
78                bail!(
79                    "invalid app config: unspported kind '{}', expected {}",
80                    other,
81                    Self::KIND
82                );
83            }
84        }
85
86        let data = serde_yaml::from_value(raw).context("could not deserialize app config")?;
87        Ok(data)
88    }
89}
90
91/// Restricted version of [`super::CapabilityMapV1`], with only a select subset
92/// of settings.
93#[derive(serde::Serialize, serde::Deserialize, schemars::JsonSchema, Clone, Debug)]
94pub struct AppConfigCapabilityMapV1 {
95    /// Instance memory settings.
96    #[serde(skip_serializing_if = "Option::is_none")]
97    pub memory: Option<AppConfigCapabilityMemoryV1>,
98}
99
100/// Memory capability settings.
101///
102/// NOTE: this is kept separate from the [`super::CapabilityMemoryV1`] struct
103/// to have separation between the high-level app.yaml and the more internal
104/// App entity.
105#[derive(serde::Serialize, serde::Deserialize, schemars::JsonSchema, Clone, Debug)]
106pub struct AppConfigCapabilityMemoryV1 {
107    /// Memory limit for an instance.
108    ///
109    /// Format: [digit][unit], where unit is Mb/Gb/MiB/GiB,...
110    #[schemars(with = "Option<String>")]
111    #[serde(skip_serializing_if = "Option::is_none")]
112    pub limit: Option<ByteSize>,
113}