-
Notifications
You must be signed in to change notification settings - Fork 89
/
instance.rs
233 lines (194 loc) · 6.83 KB
/
instance.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
//! Abstractions for running/managing a wasm/wasi instance.
use std::path::{Path, PathBuf};
use std::time::Duration;
use chrono::{DateTime, Utc};
use super::error::Error;
use super::sync::WaitableCell;
use crate::sys::signals::*;
/// Generic options builder for creating a wasm instance.
/// This is passed to the `Instance::new` method.
#[derive(Clone)]
pub struct InstanceConfig<Engine: Send + Sync + Clone> {
/// The WASI engine to use.
/// This should be cheap to clone.
engine: Engine,
/// Optional stdin named pipe path.
stdin: PathBuf,
/// Optional stdout named pipe path.
stdout: PathBuf,
/// Optional stderr named pipe path.
stderr: PathBuf,
/// Path to the OCI bundle directory.
bundle: PathBuf,
/// Namespace for containerd
namespace: String,
// /// GRPC address back to main containerd
containerd_address: String,
}
impl<Engine: Send + Sync + Clone> InstanceConfig<Engine> {
pub fn new(
engine: Engine,
namespace: impl AsRef<str>,
containerd_address: impl AsRef<str>,
) -> Self {
let namespace = namespace.as_ref().to_string();
let containerd_address = containerd_address.as_ref().to_string();
Self {
engine,
namespace,
containerd_address,
stdin: PathBuf::default(),
stdout: PathBuf::default(),
stderr: PathBuf::default(),
bundle: PathBuf::default(),
}
}
/// set the stdin path for the instance
pub fn set_stdin(&mut self, stdin: impl AsRef<Path>) -> &mut Self {
self.stdin = stdin.as_ref().to_path_buf();
self
}
/// get the stdin path for the instance
pub fn get_stdin(&self) -> &Path {
&self.stdin
}
/// set the stdout path for the instance
pub fn set_stdout(&mut self, stdout: impl AsRef<Path>) -> &mut Self {
self.stdout = stdout.as_ref().to_path_buf();
self
}
/// get the stdout path for the instance
pub fn get_stdout(&self) -> &Path {
&self.stdout
}
/// set the stderr path for the instance
pub fn set_stderr(&mut self, stderr: impl AsRef<Path>) -> &mut Self {
self.stderr = stderr.as_ref().to_path_buf();
self
}
/// get the stderr path for the instance
pub fn get_stderr(&self) -> &Path {
&self.stderr
}
/// set the OCI bundle path for the instance
pub fn set_bundle(&mut self, bundle: impl AsRef<Path>) -> &mut Self {
self.bundle = bundle.as_ref().to_path_buf();
self
}
/// get the OCI bundle path for the instance
pub fn get_bundle(&self) -> &Path {
&self.bundle
}
/// get the wasm engine for the instance
pub fn get_engine(&self) -> Engine {
self.engine.clone()
}
/// get the namespace for the instance
pub fn get_namespace(&self) -> String {
self.namespace.clone()
}
/// get the containerd address for the instance
pub fn get_containerd_address(&self) -> String {
self.containerd_address.clone()
}
}
/// Represents a WASI module(s).
/// Instance is a trait that gets implemented by consumers of this library.
/// This trait requires that any type implementing it is `'static`, similar to `std::any::Any`.
/// This means that the type cannot contain a non-`'static` reference.
pub trait Instance: 'static {
/// The WASI engine type
type Engine: Send + Sync + Clone;
/// Create a new instance
fn new(id: String, cfg: Option<&InstanceConfig<Self::Engine>>) -> Result<Self, Error>
where
Self: Sized;
/// Start the instance
/// The returned value should be a unique ID (such as a PID) for the instance.
/// Nothing internally should be using this ID, but it is returned to containerd where a user may want to use it.
fn start(&self) -> Result<u32, Error>;
/// Send a signal to the instance
fn kill(&self, signal: u32) -> Result<(), Error>;
/// Delete any reference to the instance
/// This is called after the instance has exited.
fn delete(&self) -> Result<(), Error>;
/// Waits for the instance to finish and retunrs its exit code
/// This is a blocking call.
#[cfg_attr(feature = "tracing", tracing::instrument(skip(self), parent = tracing::Span::current(), level = "Info"))]
fn wait(&self) -> (u32, DateTime<Utc>) {
self.wait_timeout(None).unwrap()
}
/// Waits for the instance to finish and retunrs its exit code
/// Returns None if the timeout is reached before the instance has finished.
/// This is a blocking call.
fn wait_timeout(&self, t: impl Into<Option<Duration>>) -> Option<(u32, DateTime<Utc>)>;
}
/// This is used for the "pause" container with cri and is a no-op instance implementation.
pub struct Nop {
/// Since we are faking the container, we need to keep track of the "exit" code/time
/// We'll just mark it as exited when kill is called.
exit_code: WaitableCell<(u32, DateTime<Utc>)>,
}
impl Instance for Nop {
type Engine = ();
fn new(_id: String, _cfg: Option<&InstanceConfig<Self::Engine>>) -> Result<Self, Error> {
Ok(Nop {
exit_code: WaitableCell::new(),
})
}
fn start(&self) -> Result<u32, Error> {
Ok(std::process::id())
}
fn kill(&self, signal: u32) -> Result<(), Error> {
let code = match signal as i32 {
SIGKILL => 137,
SIGINT | SIGTERM => 0,
s => {
return Err(Error::InvalidArgument(format!("unsupported signal: {}", s)));
}
};
let _ = self.exit_code.set((code, Utc::now()));
Ok(())
}
fn delete(&self) -> Result<(), Error> {
Ok(())
}
fn wait_timeout(&self, t: impl Into<Option<Duration>>) -> Option<(u32, DateTime<Utc>)> {
self.exit_code.wait_timeout(t).copied()
}
}
#[cfg(test)]
mod noptests {
use std::time::Duration;
use super::*;
#[test]
fn test_nop_kill_sigkill() -> Result<(), Error> {
let nop = Nop::new("".to_string(), None)?;
nop.kill(SIGKILL as u32)?;
let ec = nop.wait_timeout(Duration::from_secs(3)).unwrap();
assert_eq!(ec.0, 137);
Ok(())
}
#[test]
fn test_nop_kill_sigterm() -> Result<(), Error> {
let nop = Nop::new("".to_string(), None)?;
nop.kill(SIGTERM as u32)?;
let ec = nop.wait_timeout(Duration::from_secs(3)).unwrap();
assert_eq!(ec.0, 0);
Ok(())
}
#[test]
fn test_nop_kill_sigint() -> Result<(), Error> {
let nop = Nop::new("".to_string(), None)?;
nop.kill(SIGINT as u32)?;
let ec = nop.wait_timeout(Duration::from_secs(3)).unwrap();
assert_eq!(ec.0, 0);
Ok(())
}
#[test]
fn test_nop_delete_after_create() -> Result<(), Error> {
let nop = Nop::new("".to_string(), None)?;
nop.delete()?;
Ok(())
}
}