ya_runtime_api/
deploy.rs

1use std::convert::TryFrom;
2use std::io::Cursor;
3use std::net::{AddrParseError, Ipv4Addr, SocketAddr};
4use std::path::PathBuf;
5
6use serde::{Deserialize, Serialize};
7
8#[derive(Serialize, Deserialize, Debug)]
9#[serde(rename_all = "camelCase")]
10pub struct DeployResult {
11    pub valid: Result<String, String>,
12    #[serde(default)]
13    pub vols: Vec<ContainerVolume>,
14    #[serde(default)]
15    pub start_mode: StartMode,
16}
17
18#[derive(Serialize, Deserialize, Debug, Clone)]
19#[serde(rename_all = "camelCase")]
20pub struct ContainerVolume {
21    pub name: String,
22    pub path: String,
23}
24
25#[non_exhaustive]
26#[derive(Serialize, Deserialize, Debug, Clone)]
27pub enum ContainerEndpoint {
28    UnixStream(PathBuf),
29    UnixDatagram(PathBuf),
30    UdpDatagram(SocketAddr),
31    TcpListener(SocketAddr),
32    TcpStream(SocketAddr),
33}
34
35impl std::fmt::Display for ContainerEndpoint {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        match self {
38            Self::UnixStream(path) | Self::UnixDatagram(path) => write!(f, "{}", path.display()),
39            Self::UdpDatagram(addr) | Self::TcpListener(addr) | Self::TcpStream(addr) => {
40                write!(f, "{}", addr)
41            }
42        }
43    }
44}
45
46impl From<ContainerEndpoint> for PathBuf {
47    fn from(e: ContainerEndpoint) -> Self {
48        match e {
49            ContainerEndpoint::UnixStream(p) | ContainerEndpoint::UnixDatagram(p) => p,
50            ContainerEndpoint::UdpDatagram(a) => PathBuf::from(format!("udp://{}", a)),
51            ContainerEndpoint::TcpListener(a) => PathBuf::from(format!("tcp-connect://{}", a)),
52            ContainerEndpoint::TcpStream(a) => PathBuf::from(format!("tcp-listen://{}", a)),
53        }
54    }
55}
56
57impl<'a> TryFrom<&'a crate::server::NetworkEndpoint> for ContainerEndpoint {
58    type Error = String;
59
60    fn try_from(endpoint: &'a crate::server::NetworkEndpoint) -> Result<Self, Self::Error> {
61        match endpoint {
62            crate::server::NetworkEndpoint::UnixStream(s) => Ok(Self::UnixStream(PathBuf::from(s))),
63            crate::server::NetworkEndpoint::UnixDatagram(s) => {
64                Ok(Self::UnixDatagram(PathBuf::from(s)))
65            }
66            crate::server::NetworkEndpoint::UdpDatagram(s) => {
67                Ok(Self::UdpDatagram(to_socket_addr(s)?))
68            }
69            crate::server::NetworkEndpoint::TcpListener(s) => {
70                Ok(Self::TcpListener(to_socket_addr(s)?))
71            }
72            crate::server::NetworkEndpoint::TcpStream(s) => Ok(Self::TcpStream(to_socket_addr(s)?)),
73        }
74    }
75}
76
77impl TryFrom<url::Url> for ContainerEndpoint {
78    type Error = String;
79
80    fn try_from(url: url::Url) -> Result<Self, Self::Error> {
81        match url.scheme() {
82            "unix" => {
83                let url = url.to_string().replacen("unix://", "", 1);
84                Ok(Self::UnixStream(PathBuf::from(url)))
85            }
86            "udp" => {
87                let url = url.to_string().replacen("udp://", "", 1);
88                let addr: SocketAddr = url.parse().map_err(|e: AddrParseError| e.to_string())?;
89                Ok(Self::UdpDatagram(addr))
90            }
91            "tcp-connect" => {
92                let url = url.to_string().replacen("tcp-connect://", "", 1);
93                let addr: SocketAddr = url.parse().map_err(|e: AddrParseError| e.to_string())?;
94                Ok(Self::TcpStream(addr))
95            }
96            "tcp-listen" => Ok(Self::TcpListener(SocketAddr::new(
97                Ipv4Addr::new(127, 0, 0, 1).into(),
98                0,
99            ))),
100            scheme => Err(format!("Unknown scheme: {scheme}")),
101        }
102    }
103}
104
105fn to_socket_addr(s: &str) -> Result<SocketAddr, String> {
106    s.parse().map_err(|e: AddrParseError| e.to_string())
107}
108
109#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
110#[serde(rename_all = "camelCase")]
111pub enum StartMode {
112    Empty,
113    Blocking,
114}
115
116impl Default for StartMode {
117    fn default() -> Self {
118        StartMode::Empty
119    }
120}
121
122impl DeployResult {
123    pub fn from_bytes(bytes: impl AsRef<[u8]>) -> anyhow::Result<DeployResult> {
124        let b: &[u8] = bytes.as_ref();
125        if b.is_empty() {
126            log::warn!("empty descriptor");
127            let vols = if cfg!(feature = "compat-deployment") {
128                vec![ContainerVolume {
129                    name: ".".to_string(),
130                    path: "".to_string(),
131                }]
132            } else {
133                Default::default()
134            };
135
136            return Ok(DeployResult {
137                valid: Ok(Default::default()),
138                vols,
139                start_mode: Default::default(),
140            });
141        }
142        if let Some(idx) = b.iter().position(|&ch| ch == b'{') {
143            let b = &b[idx..];
144            Ok(serde_json::from_reader(Cursor::new(b))?)
145        } else {
146            let text = String::from_utf8_lossy(b);
147            anyhow::bail!("invalid deploy response: {}", text);
148        }
149    }
150}
151
152#[cfg(test)]
153mod test {
154    use super::DeployResult;
155
156    fn parse_bytes<T: AsRef<[u8]>>(b: T) -> DeployResult {
157        let result = DeployResult::from_bytes(b).unwrap();
158        eprintln!("result={:?}", result);
159        result
160    }
161
162    #[test]
163    fn test_wasi_deploy() {
164        parse_bytes(
165            r#"{
166            "valid": {"Ok": "success"}
167        }"#,
168        );
169        parse_bytes(
170            r#"{
171            "valid": {"Err": "bad image format"}
172        }"#,
173        );
174        parse_bytes(
175            r#"{
176            "valid": {"Ok": "success"},
177            "vols": [
178                {"name": "vol-9a0c1c4a", "path": "/in"},
179                {"name": "vol-a68672e0", "path": "/out"}
180            ]
181        }"#,
182        );
183    }
184}