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
use crate::{is_false, is_zero};
use derive_setters::*;
use warpgate_api::{ExecCommandInput, VirtualPath, api_enum, api_struct, api_unit_enum};
api_unit_enum!(
/// Types of caching strategies.
pub enum CacheStrategy {
/// Cache in-memory for the current process.
#[default]
Memory,
/// Cache on disk using a hashed fingerprint,
/// which is useful for caching across processes.
Hash,
}
);
api_struct!(
/// A command to be executed on the host machine, with optional caching and retrying.
#[derive(Setters)]
#[serde(default)]
pub struct ExecCommand {
/// When enabled, failed command executions will
/// not abort the moon process, and allow it to
/// continue running.
#[serde(skip_serializing_if = "is_false")]
#[setters(bool)]
pub allow_failure: bool,
/// Cache the command, either in-memory or on disk.
/// If not provided, the command will not be cached.
#[serde(skip_serializing_if = "Option::is_none")]
#[setters(strip_option)]
pub cache: Option<CacheStrategy>,
/// The command parameters.
#[setters(skip)]
pub command: ExecCommandInput,
/// List of additional inputs to gather when generating
/// the cache key/hash.
#[serde(skip_serializing_if = "Vec::is_empty")]
pub inputs: Vec<CacheInput>,
/// Checkpoint label to print to the console. If not
/// provided, will default to the command + arguments.
#[serde(skip_serializing_if = "Option::is_none")]
#[setters(into, strip_option)]
pub label: Option<String>,
/// Execute the command in parallel.
#[serde(skip_serializing_if = "is_false")]
#[setters(bool)]
pub parallel: bool,
/// A count of how many times to retry the command
/// if it fails to execute.
#[serde(skip_serializing_if = "is_zero")]
pub retry_count: u8,
}
);
impl ExecCommand {
/// Create a new command with the provided input.
pub fn new(command: ExecCommandInput) -> Self {
Self {
allow_failure: false,
cache: None,
command,
inputs: vec![],
label: None,
parallel: false,
retry_count: 0,
}
}
/// Return the label, or the command + arguments.
pub fn get_label(&self) -> String {
self.label.clone().unwrap_or_else(|| {
format!("{} {}", self.command.command, self.command.args.join(" "))
.trim()
.into()
})
}
}
impl From<ExecCommandInput> for ExecCommand {
fn from(input: ExecCommandInput) -> Self {
Self::new(input)
}
}
api_enum!(
/// Types of inputs that can be cached.
#[serde(tag = "type", content = "value", rename_all = "kebab-case")]
pub enum CacheInput {
/// Environment variable.
EnvVar(String),
/// SHA256 file hash.
FileHash(VirtualPath),
/// File size in bytes.
FileSize(VirtualPath),
/// File modified or created at timestamp.
FileTimestamp(VirtualPath),
}
);
impl CacheInput {
/// Return the virtual path of the cache input, if applicable.
pub fn get_virtual_path(&self) -> Option<&VirtualPath> {
match self {
CacheInput::FileHash(path)
| CacheInput::FileSize(path)
| CacheInput::FileTimestamp(path) => Some(path),
_ => None,
}
}
/// Set the virtual path of the cache input, if applicable.
pub fn set_virtual_path(&mut self, new_path: VirtualPath) {
match self {
CacheInput::FileHash(path)
| CacheInput::FileSize(path)
| CacheInput::FileTimestamp(path) => *path = new_path,
_ => {}
}
}
}