clone-shim/src/specification.rs

159 lines
3.7 KiB
Rust
Raw Normal View History

2022-03-01 11:29:13 +00:00
use log::debug;
2022-02-13 23:52:41 +00:00
use crate::Error;
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;
use ipnetwork::{Ipv4Network, Ipv6Network};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
pub struct Specification {
pub entrypoints: HashMap<String, Entrypoint>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Entrypoint {
2022-02-28 20:42:44 +00:00
#[serde(default)]
2022-02-13 23:52:41 +00:00
pub trigger: Trigger,
2022-02-28 20:42:44 +00:00
#[serde(default = "Arg::default_vec")]
2022-02-13 23:52:41 +00:00
pub args: Vec<Arg>,
}
#[derive(Serialize, Deserialize, Debug)]
pub enum Trigger {
Startup,
Pipe(String),
}
2022-02-28 20:42:44 +00:00
impl Default for Trigger {
fn default() -> Self {
Self::Startup
}
}
2022-03-01 11:29:13 +00:00
#[derive(Serialize, Deserialize, PartialEq, Debug)]
2022-02-28 20:42:44 +00:00
// #[serde(tag = "type")]
2022-02-13 23:52:41 +00:00
pub enum Arg {
/// The binary name, or argv[0], of the original program start
BinaryName,
/// The name of this entrypoint
Entrypoint,
/// A chosen end of a named pipe
Pipe(Pipe),
2022-03-01 11:29:13 +00:00
/// The value of a pipe trigger
/// NOTE: Only valid if the trigger is of type Pipe(...)
PipeTrigger,
/// A TCP Listener
TcpListener { port: u16 },
2022-02-13 23:52:41 +00:00
/// The rest of argv[1..], 0 or more arguments
Trailing,
}
2022-02-28 20:42:44 +00:00
impl Arg {
fn default_vec() -> Vec<Arg> {
vec![Arg::BinaryName]
}
}
2022-03-01 11:29:13 +00:00
#[derive(Serialize, Deserialize, PartialEq, Debug)]
2022-02-13 23:52:41 +00:00
pub enum Pipe {
Rx(String),
Tx(String),
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Hash, Debug)]
2022-03-01 11:29:13 +00:00
pub enum Permission {
2022-02-13 23:52:41 +00:00
Filesystem {
host_path: PathBuf,
final_path: PathBuf,
},
Network {
network: Network,
},
2022-03-01 11:29:13 +00:00
PropagateFiles,
2022-02-13 23:52:41 +00:00
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Hash, Debug)]
pub enum Network {
InternetV4,
InternetV6,
PrivateV4(Ipv4Network),
PrivateV6(Ipv6Network),
}
impl Specification {
pub fn pipes(&self) -> (Vec<&str>, Vec<&str>) {
let mut read = Vec::new();
let mut write = Vec::new();
2022-02-28 20:42:44 +00:00
for entry in self.entrypoints.values() {
2022-02-13 23:52:41 +00:00
match &entry.trigger {
Trigger::Startup => {}
Trigger::Pipe(s) => read.push(s.as_str()),
}
for arg in &entry.args {
2022-03-01 11:29:13 +00:00
if let Arg::Pipe(p) = arg {
match p {
2022-02-13 23:52:41 +00:00
Pipe::Rx(s) => read.push(s.as_str()),
Pipe::Tx(s) => write.push(s.as_str()),
2022-03-01 11:29:13 +00:00
}
2022-02-13 23:52:41 +00:00
}
}
}
2022-03-01 11:29:13 +00:00
debug!("read pipes: {:?}", &read);
debug!("write pipes: {:?}", &write);
2022-02-13 23:52:41 +00:00
(read, write)
}
pub fn validate(&self) -> Result<(), Error> {
// validate pipes match
let (read, write) = self.pipes();
let mut read_set = HashSet::with_capacity(read.len());
for pipe in read {
2022-03-01 11:29:13 +00:00
if !read_set.insert(pipe) {
2022-02-13 23:52:41 +00:00
return Err(Error::TooManyPipes(pipe.to_string()));
}
}
let mut write_set = HashSet::with_capacity(write.len());
for pipe in write {
2022-03-01 11:29:13 +00:00
if !write_set.insert(pipe) {
2022-02-13 23:52:41 +00:00
return Err(Error::TooManyPipes(pipe.to_string()));
}
}
for pipe in read_set {
if !write_set.remove(pipe) {
return Err(Error::ReadOnlyPipe(pipe.to_string()));
}
}
2022-02-28 20:42:44 +00:00
if let Some(pipe) = write_set.into_iter().next() {
2022-02-13 23:52:41 +00:00
return Err(Error::WriteOnlyPipe(pipe.to_string()));
}
2022-03-01 11:29:13 +00:00
// validate pipe trigger arguments make sense
for entrypoint in self.entrypoints.values() {
if entrypoint.args.contains(&Arg::PipeTrigger) {
match entrypoint.trigger {
Trigger::Pipe(_) => {}
_ => return Err(Error::BadPipeTrigger),
}
}
}
2022-02-13 23:52:41 +00:00
Ok(())
}
}