mirror of
https://github.com/michael-yuji/xc.git
synced 2026-03-17 22:35:43 +01:00
minor image format refactoring
This commit is contained in:
@@ -168,6 +168,8 @@ pub struct OciInnerConfig {
|
||||
pub volumes: Option<Set<String>>,
|
||||
pub exposed_ports: Option<Set<String>>,
|
||||
pub user: Option<String>,
|
||||
pub labels: Option<HashMap<String, String>>,
|
||||
pub stop_signal: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Serialize, Deserialize, Clone, Debug)]
|
||||
|
||||
@@ -46,6 +46,9 @@ pub(crate) enum ImageAction {
|
||||
Show {
|
||||
image_id: String,
|
||||
},
|
||||
Describe {
|
||||
image_id: ImageReference,
|
||||
},
|
||||
GetConfig {
|
||||
image_id: String,
|
||||
},
|
||||
@@ -83,6 +86,10 @@ pub(crate) enum PatchActions {
|
||||
name: Option<String>,
|
||||
/// mount point in the container
|
||||
mount_point: PathBuf,
|
||||
|
||||
#[clap(long = "required", action)]
|
||||
required: bool,
|
||||
|
||||
/// the image
|
||||
image_reference: ImageReference,
|
||||
},
|
||||
@@ -164,6 +171,7 @@ pub(crate) fn use_image_action(
|
||||
hints,
|
||||
name,
|
||||
mount_point,
|
||||
required,
|
||||
image_reference,
|
||||
read_only,
|
||||
} => {
|
||||
@@ -185,6 +193,7 @@ pub(crate) fn use_image_action(
|
||||
read_only,
|
||||
volume_hints,
|
||||
destination,
|
||||
required,
|
||||
};
|
||||
config.mounts.insert(key, mountspec);
|
||||
})?;
|
||||
@@ -310,6 +319,47 @@ pub(crate) fn use_image_action(
|
||||
}
|
||||
}
|
||||
}
|
||||
ImageAction::Describe { image_id } => {
|
||||
let image_name = image_id.name.to_string();
|
||||
let tag = image_id.tag.to_string();
|
||||
let reqt = DescribeImageRequest { image_name, tag };
|
||||
let res = do_describe_image(conn, reqt)?;
|
||||
match res {
|
||||
Err(e) => eprintln!("{e:#?}"),
|
||||
Ok(res) => {
|
||||
let image = &res.jail_image;
|
||||
let config = image.jail_config();
|
||||
println!("\n{image_id}");
|
||||
println!(" Envs:");
|
||||
for (key, value) in config.envs.iter() {
|
||||
println!(" {key}:");
|
||||
println!(" Required: {}", value.required);
|
||||
println!(
|
||||
" Description: {}",
|
||||
value.description.clone().unwrap_or_default()
|
||||
);
|
||||
}
|
||||
println!(" Ports:");
|
||||
for (port, value) in config.ports.iter() {
|
||||
println!(" {port}: {value}");
|
||||
}
|
||||
println!(" Volumes:");
|
||||
for (name, spec) in config.mounts.iter() {
|
||||
println!(" {name}:");
|
||||
println!(" Mount Point: {}", spec.destination);
|
||||
println!(" Required: {}", spec.required);
|
||||
println!(" Read-Only: {}", spec.read_only);
|
||||
if !spec.volume_hints.is_empty() {
|
||||
println!(" Hints:");
|
||||
for (key, value) in spec.volume_hints.iter() {
|
||||
let desc = serde_json::to_string(value).unwrap();
|
||||
println!(" {key}: {desc}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ImageAction::GetConfig { image_id } => {
|
||||
let (image_name, tag) = image_id.rsplit_once(':').expect("invalid image id");
|
||||
let reqt = DescribeImageRequest {
|
||||
|
||||
@@ -24,13 +24,14 @@
|
||||
pub mod copy;
|
||||
pub mod from;
|
||||
pub mod run;
|
||||
pub mod volume;
|
||||
|
||||
use super::JailContext;
|
||||
use crate::jailfile::parse::Action;
|
||||
|
||||
use anyhow::Context;
|
||||
use xc::models::jail_image::{JailConfig, SpecialMount};
|
||||
use xc::models::SystemVPropValue;
|
||||
use xc::models::{MountSpec, SystemVPropValue};
|
||||
|
||||
pub(crate) trait Directive: Sized {
|
||||
fn from_action(action: &Action) -> Result<Self, anyhow::Error>;
|
||||
@@ -48,7 +49,7 @@ pub(crate) enum ConfigMod {
|
||||
NoDeinit,
|
||||
Cmd,
|
||||
Expose,
|
||||
Volume,
|
||||
Volume(String, MountSpec),
|
||||
Mount(String, String),
|
||||
SysV(Vec<String>),
|
||||
}
|
||||
@@ -100,6 +101,9 @@ impl ConfigMod {
|
||||
}
|
||||
}
|
||||
}
|
||||
Self::Volume(name, mount_spec) => {
|
||||
config.mounts.insert(name.clone(), mount_spec.clone());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ use crate::channel::{use_channel_action, ChannelAction};
|
||||
use crate::error::ActionError;
|
||||
use crate::format::{BindMount, EnvPair, IpWant, PublishSpec};
|
||||
use crate::image::{use_image_action, ImageAction};
|
||||
use crate::jailfile::directives::volume::VolumeDirective;
|
||||
use crate::network::{use_network_action, NetworkAction};
|
||||
use crate::redirect::{use_rdr_action, RdrAction};
|
||||
|
||||
@@ -314,6 +315,9 @@ fn main() -> Result<(), ActionError> {
|
||||
} else if action.directive_name == "COPY" {
|
||||
let directive = CopyDirective::from_action(action)?;
|
||||
directive.run_in_context(&mut context)?;
|
||||
} else if action.directive_name == "VOLUME" {
|
||||
let directive = VolumeDirective::from_action(action)?;
|
||||
directive.run_in_context(&mut context)?;
|
||||
} else if ConfigMod::implemented_directives()
|
||||
.contains(&action.directive_name.as_str())
|
||||
{
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
use crate::models::network::{NetProto, PortNum};
|
||||
use pest::Parser;
|
||||
use pest_derive::Parser;
|
||||
use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Parser)]
|
||||
@@ -35,12 +36,61 @@ expose = { (portrange | portnum) ~ ("/" ~ net_proto)? }
|
||||
"#]
|
||||
struct RuleParser;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, std::hash::Hash)]
|
||||
pub struct Expose {
|
||||
pub port: PortNum,
|
||||
pub proto: Option<NetProto>,
|
||||
}
|
||||
|
||||
impl Serialize for Expose {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(self.to_string().as_str())
|
||||
}
|
||||
}
|
||||
|
||||
struct ExposeVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for ExposeVisitor {
|
||||
type Value = Expose;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("(PORT | PORTRANGE)(/ PROTO)?")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
value.parse().map_err(|e| E::custom("{e:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Expose {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Expose, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_str(ExposeVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Expose {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self.port {
|
||||
PortNum::Single(n) => write!(f, "{n}")?,
|
||||
PortNum::Range(s, n) => write!(f, "{s}-{n}")?,
|
||||
};
|
||||
match &self.proto {
|
||||
Some(proto) => write!(f, "/{proto}")?,
|
||||
None => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Expose {
|
||||
type Err = anyhow::Error;
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
// SUCH DAMAGE.
|
||||
|
||||
use crate::format::docker_compat::Expose;
|
||||
use crate::util::default_on_missing;
|
||||
|
||||
use super::cmd_arg::CmdArg;
|
||||
use super::exec::Exec;
|
||||
use super::MountSpec;
|
||||
@@ -45,16 +49,12 @@ pub struct JailConfig {
|
||||
|
||||
pub original_oci_config: Option<OciConfig>,
|
||||
|
||||
/// If this jail require vnet to run
|
||||
/// is vnet required
|
||||
#[serde(default)]
|
||||
pub vnet: bool,
|
||||
|
||||
/// The mappings between the alias and variable name, for example, a jail can expect an
|
||||
/// interface myext0 to exists, and map the variable as $MY_EXT0
|
||||
pub nics: Option<HashMap<Var, String>>,
|
||||
|
||||
/// Required ports
|
||||
pub ports: HashMap<u16, String>,
|
||||
pub ports: HashMap<Expose, String>,
|
||||
|
||||
pub devfs_rules: Vec<InterpolatedString>,
|
||||
|
||||
@@ -87,6 +87,9 @@ pub struct JailConfig {
|
||||
|
||||
#[serde(default)]
|
||||
pub linux: bool,
|
||||
|
||||
#[serde(default, deserialize_with = "default_on_missing")]
|
||||
pub labels: HashMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Serialize, Deserialize, Clone, Debug)]
|
||||
@@ -169,11 +172,30 @@ impl JailConfig {
|
||||
})
|
||||
.unwrap_or_else(HashMap::new);
|
||||
|
||||
let ports = config
|
||||
.config
|
||||
.as_ref()
|
||||
.and_then(|config| config.exposed_ports.as_ref())
|
||||
.map(|set| {
|
||||
let mut ports = HashMap::new();
|
||||
for port in set.0.iter() {
|
||||
if let Ok(expose) = port.parse::<Expose>() {
|
||||
ports.insert(expose, "".to_string());
|
||||
}
|
||||
}
|
||||
ports
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
let labels = config
|
||||
.config
|
||||
.as_ref()
|
||||
.and_then(|config| config.labels.clone())
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut meta = JailConfig {
|
||||
secure_level: 0,
|
||||
vnet: false,
|
||||
nics: None,
|
||||
ports: HashMap::new(),
|
||||
devfs_rules: Vec::new(),
|
||||
allow: Vec::new(),
|
||||
sysv_msg: SystemVPropValue::New,
|
||||
@@ -184,8 +206,10 @@ impl JailConfig {
|
||||
init: Vec::new(),
|
||||
deinit: Vec::new(),
|
||||
mounts,
|
||||
linux: true,
|
||||
linux: config.os != "FreeBSD",
|
||||
original_oci_config: Some(config.clone()),
|
||||
ports,
|
||||
labels,
|
||||
..JailConfig::default()
|
||||
};
|
||||
|
||||
@@ -237,14 +261,10 @@ impl JailConfig {
|
||||
|
||||
let layers = config.rootfs.diff_ids;
|
||||
|
||||
Some(meta.to_image(layers))
|
||||
let mut image = meta.to_image(layers);
|
||||
|
||||
image.0.os = config.os;
|
||||
|
||||
Some(image)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum Satis {
|
||||
MissingCap(String),
|
||||
MissingEnv(String, Option<String>),
|
||||
PreconditionFailure(String),
|
||||
Ok,
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ pub mod exec;
|
||||
pub mod jail_image;
|
||||
pub mod network;
|
||||
|
||||
use crate::util::default_on_missing;
|
||||
|
||||
use cmd_arg::CmdArg;
|
||||
use exec::ResolvedExec;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -39,6 +41,8 @@ pub struct MountSpec {
|
||||
pub destination: String,
|
||||
pub volume_hints: HashMap<String, Value>,
|
||||
pub read_only: bool,
|
||||
#[serde(default, deserialize_with = "default_on_missing")]
|
||||
pub required: bool,
|
||||
}
|
||||
|
||||
impl MountSpec {
|
||||
@@ -48,6 +52,7 @@ impl MountSpec {
|
||||
destination: destination.to_string(),
|
||||
volume_hints: HashMap::new(),
|
||||
read_only: false,
|
||||
required: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ impl std::fmt::Display for IpAssign {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, std::hash::Hash)]
|
||||
pub enum PortNum {
|
||||
Single(u16),
|
||||
/// Represents a range of port number by a starting number and length
|
||||
@@ -77,7 +77,7 @@ impl std::fmt::Display for PortNum {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, std::hash::Hash)]
|
||||
pub enum NetProto {
|
||||
Tcp,
|
||||
Udp,
|
||||
|
||||
@@ -21,12 +21,23 @@
|
||||
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
// SUCH DAMAGE.
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use std::collections::VecDeque;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
use sysctl::{Ctl, Sysctl};
|
||||
|
||||
pub fn default_on_missing<'de, D, T: Default + serde::Deserialize<'de>>(
|
||||
deserializer: D,
|
||||
) -> Result<T, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let opt = Option::deserialize(deserializer)?;
|
||||
Ok(opt.unwrap_or_default())
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum PathComp {
|
||||
RootDir,
|
||||
|
||||
@@ -213,6 +213,13 @@ impl InstantiateBlueprint {
|
||||
for req in request.ipreq.iter() {
|
||||
match network_manager.allocate(vnet, req, id) {
|
||||
Ok((alloc, router)) => {
|
||||
if !existing_ifaces.contains(&alloc.interface) {
|
||||
precondition_failure!(
|
||||
ENOENT,
|
||||
"missing network interface {}",
|
||||
&alloc.interface
|
||||
);
|
||||
}
|
||||
if let Some(router) = router {
|
||||
if default_router.is_none() {
|
||||
default_router = Some(router);
|
||||
|
||||
Reference in New Issue
Block a user