bug fixes

This commit is contained in:
Yan Ka, Chiu
2023-07-14 14:41:56 -04:00
parent 44024994a0
commit e1124b0ec9
11 changed files with 199 additions and 47 deletions

View File

@@ -548,7 +548,6 @@ impl Session {
// if there is none, just register this manifest
let manifest_variant = self.query_manifest(tag).await?;
let manifest_list = match manifest_variant {
None => todo!(),
Some(ManifestVariant::List(mut manifest_list)) => {
let mut manifests = manifest_list
.manifests

View File

@@ -26,6 +26,7 @@ mod util;
use crate::util::*;
use clap::{Parser, Subcommand};
use sha2::{Digest, Sha256};
use std::fs::File;
use std::io::{Read, Write};
use std::process::Command;
@@ -216,18 +217,21 @@ fn zfs_dataset_get_mountpoint(dataset: &String) -> Result<Option<String>, std::i
}
pub fn do_create(args: CreateArgs) -> Result<(), std::io::Error> {
let mut output: Box<dyn Write> = match args.file.as_str() {
let output: Box<dyn Write> = match args.file.as_str() {
"-" => Box::new(std::io::stdout()),
path => Box::new(File::create(path)?),
};
output = match args.compression {
CompressionType::Zstd => Box::new(ZstdEncoder::new(output, 3)?.auto_finish()),
let sha256 = std::rc::Rc::new(std::cell::RefCell::new(Sha256::new()));
let handle = DigestSink::<Box<dyn Write>>::new(output, sha256.clone());
let mut output: Box<dyn Write> = match args.compression {
CompressionType::Zstd => Box::new(ZstdEncoder::new(handle, 3)?.auto_finish()),
CompressionType::Gzip => Box::new(flate2::write::GzEncoder::new(
output,
handle,
flate2::Compression::default(),
)),
_ => output,
_ => Box::new(handle),
};
if args.zfs_diff {
@@ -301,7 +305,7 @@ pub fn do_create(args: CreateArgs) -> Result<(), std::io::Error> {
&removing,
&mut output,
args.write_to_stderr,
)
)?;
} else {
create_tar(
args.without_oci,
@@ -311,8 +315,18 @@ pub fn do_create(args: CreateArgs) -> Result<(), std::io::Error> {
&args.remove,
&mut output,
args.write_to_stderr,
)
)?;
}
drop(output);
let digest: [u8; 32] = sha256.borrow().clone().finalize().into();
if !args.write_to_stderr {
println!("sha256:{}", hex(digest));
} else {
eprintln!("sha256:{}", hex(digest));
}
Ok(())
}
pub fn do_extract(args: ExtractArgs) -> Result<(), std::io::Error> {
@@ -377,6 +391,7 @@ fn create_tar<W: Write>(
.arg("-T-") //.args(paths)
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::null())
.spawn()?;
let mut child_stdin = child.stdin.take().unwrap();

View File

@@ -298,9 +298,10 @@ pub struct Summary {
}
pub fn remove_path(p: std::path::PathBuf) -> std::io::Result<()> {
/*
eprintln!("enter remove path");
log::warn!("removing: {p:?}");
*/
let path = std::path::PathBuf::from(&p);
if path.exists() {
if path.is_dir() {
@@ -308,10 +309,10 @@ pub fn remove_path(p: std::path::PathBuf) -> std::io::Result<()> {
} else {
_ = std::fs::remove_file(path);
}
eprintln!("exit remove path");
// eprintln!("exit remove path");
Ok(())
} else {
eprintln!("exit remove path");
// eprintln!("exit remove path");
Ok(())
}

View File

@@ -90,6 +90,33 @@ impl<R: Read> Read for DigestReaderHandle<R> {
}
}
pub struct DigestSink<W: Write> {
sink: W,
digest: Rc<RefCell<Sha256>>,
}
//pub struct DigestSinkHandle<W: Write>(pub Rc<RefCell<DigestSink<W>>>);
impl<T: Write> DigestSink<T> {
pub fn new<W: Write>(sink: W, digest: Rc<RefCell<Sha256>>) -> DigestSink<W> {
DigestSink {
sink,
digest,
}
}
}
impl<W: Write> Write for DigestSink<W> {
fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
let size = self.sink.write(buf)?;
self.digest.borrow_mut().update(&buf[..size]);
Ok(size)
}
fn flush(&mut self) -> Result<(), std::io::Error> {
self.sink.flush()
}
}
pub struct DigestWriter<W: Write> {
sink: W,
digest: Sha256,
@@ -110,11 +137,12 @@ impl<T: Write> DigestWriter<T> {
impl<W: Write> Write for DigestWriter<W> {
fn write(&mut self, buf: &[u8]) -> Result<usize, std::io::Error> {
self.digest.update(buf);
self.sink.write(buf)
let size = self.sink.write(buf)?;
self.digest.update(&buf[..size]);
Ok(size)
}
fn flush(&mut self) -> Result<(), std::io::Error> {
Ok(())
self.sink.flush()
}
}

View File

@@ -156,6 +156,16 @@ impl std::fmt::Display for Var {
}
}
impl FromStr for Var {
type Err = std::io::Error;
fn from_str(s: &str) -> Result<Var, Self::Err> {
Var::new(s).ok_or(std::io::Error::new(
std::io::ErrorKind::Other,
"must conform to IEEE Std 1003.1-2001",
))
}
}
impl Var {
pub fn as_str(&self) -> &str {
self.0.as_str()

View File

@@ -21,6 +21,7 @@
// 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.
pub mod add_env;
pub mod copy;
pub mod from;
pub mod run;
@@ -33,14 +34,14 @@ use anyhow::Context;
use std::collections::HashMap;
use varutil::string_interpolation::{InterpolatedString, Var};
use xc::models::jail_image::{JailConfig, SpecialMount};
use xc::models::{EntryPoint, MountSpec, SystemVPropValue};
use xc::models::{EntryPoint, EnvSpec, MountSpec, SystemVPropValue};
pub(crate) trait Directive: Sized {
fn from_action(action: &Action) -> Result<Self, anyhow::Error>;
fn run_in_context(&self, context: &mut JailContext) -> Result<(), anyhow::Error>;
}
#[derive(Clone)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum ConfigMod {
Allow(Vec<String>),
ReplaceAllow(Vec<String>),
@@ -55,6 +56,7 @@ pub(crate) enum ConfigMod {
Volume(String, MountSpec),
Mount(String, String),
SysV(Vec<String>),
AddEnv(Var, EnvSpec),
}
impl ConfigMod {
@@ -62,6 +64,9 @@ impl ConfigMod {
match self {
Self::NoInit => config.init = Vec::new(),
Self::NoDeinit => config.deinit = Vec::new(),
Self::AddEnv(variable, spec) => {
config.envs.insert(variable.clone(), spec.clone());
}
Self::Allow(allows) => {
for allow in allows.iter() {
if let Some(param) = allow.strip_prefix('-') {
@@ -232,10 +237,7 @@ impl Directive for ConfigMod {
curr = args_iter.next();
}
let arg0 = curr
.as_ref()
.clone()
.context("entry point requires one variable")?;
let arg0 = curr.as_ref().context("entry point requires one variable")?;
Ok(ConfigMod::EntryPoint(entry_point, arg0.to_string(), envs))
}
"CMD" => {
@@ -270,3 +272,52 @@ impl Directive for ConfigMod {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_directive_entrypoint_no_env() {
let action = Action {
directive_name: "ENTRYPOINT".to_string(),
directive_args: HashMap::new(),
args: vec!["arg0".to_string()],
heredoc: None,
};
let config_mod = ConfigMod::from_action(&action).expect("cannot parse");
assert_eq!(
config_mod,
ConfigMod::EntryPoint("main".to_string(), "arg0".to_string(), HashMap::new())
);
}
#[test]
fn test_parse_directive_entrypoint_with_env() {
let action = Action {
directive_name: "ENTRYPOINT".to_string(),
directive_args: HashMap::new(),
args: vec![
"A=BCD".to_string(),
"PATH=/usr/bin:/usr/sbin".to_string(),
"arg0".to_string(),
],
heredoc: None,
};
let config_mod = ConfigMod::from_action(&action).expect("cannot parse");
let mut expect_hashmap = HashMap::new();
expect_hashmap.insert(
Var::new("A").unwrap(),
InterpolatedString::new("BCD").unwrap(),
);
expect_hashmap.insert(
Var::new("PATH").unwrap(),
InterpolatedString::new("/usr/bin:/usr/sbin").unwrap(),
);
assert_eq!(
config_mod,
ConfigMod::EntryPoint("main".to_string(), "arg0".to_string(), expect_hashmap)
);
}
}

View File

@@ -255,6 +255,7 @@ fn main() -> Result<(), ActionError> {
empty_dns,
output_inplace,
} => {
use crate::jailfile::directives::add_env::*;
use crate::jailfile::directives::copy::*;
use crate::jailfile::directives::from::*;
use crate::jailfile::directives::run::*;
@@ -311,19 +312,25 @@ fn main() -> Result<(), ActionError> {
let mut context = JailContext::new(conn, dns, net_req, output_inplace);
for action in actions.iter() {
if action.directive_name == "RUN" {
let directive = RunDirective::from_action(action)?;
directive.run_in_context(&mut context)?;
} else if action.directive_name == "FROM" {
macro_rules! do_directive {
($name:expr, $tpe:ty) => {
if action.directive_name == $name {
let directive = <$tpe>::from_action(action)?;
directive.run_in_context(&mut context)?;
continue;
}
};
}
do_directive!("RUN", RunDirective);
do_directive!("COPY", CopyDirective);
do_directive!("VOLUME", VolumeDirective);
do_directive!("ADDENV", AddEnvDirective);
if action.directive_name == "FROM" {
let directive = FromDirective::from_action(action)?;
directive.run_in_context(&mut context)?;
std::thread::sleep_ms(500);
} 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())
{

View File

@@ -369,25 +369,32 @@ impl ProcessRunner {
cmd.jwork_dir(work_dir);
}
let spawn_info = match &exec.output_mode {
let spawn_info_result = match &exec.output_mode {
StdioMode::Terminal => {
let socket_path = format!("/var/run/xc.{}.{}", self.container.id, id);
let log_path = format!("/var/log/xc.{}.{}.log", self.container.id, id);
spawn_process_pty(cmd, &log_path, &socket_path)?
spawn_process_pty(cmd, &log_path, &socket_path)
}
StdioMode::Files { stdout, stderr } => spawn_process_files(&mut cmd, stdout, stderr)?,
StdioMode::Files { stdout, stderr } => spawn_process_files(&mut cmd, stdout, stderr),
StdioMode::Inherit => {
let out_path = format!("/var/log/xc.{}.{}.out.log", self.container.id, id);
let err_path = format!("/var/log/xc.{}.{}.err.log", self.container.id, id);
spawn_process_files(&mut cmd, &Some(out_path), &Some(err_path))?
spawn_process_files(&mut cmd, &Some(out_path), &Some(err_path))
}
StdioMode::Forward {
stdin,
stdout,
stderr,
} => spawn_process_forward(&mut cmd, *stdin, *stdout, *stderr)?,
} => spawn_process_forward(&mut cmd, *stdin, *stdout, *stderr),
};
let spawn_info = spawn_info_result.map_err(|error| {
if let Some(n) = notify.clone() {
n.notify_waiters();
}
error
})?;
let pid = spawn_info.pid;
tx.send_if_modified(|status| {

View File

@@ -198,6 +198,12 @@ impl Default for JailImage {
}
impl JailImage {
pub fn os(&self) -> &str {
&self.0.os
}
pub fn architecture(&self) -> &str {
&self.0.architecture
}
pub fn chain_id(&self) -> Option<ChainId> {
if self.0.rootfs.diff_ids.is_empty() {
None

View File

@@ -294,10 +294,18 @@ impl ServerContext {
let output = child.wait_with_output()?;
let (diff_id, _digest) = {
let mut results = std::str::from_utf8(&output.stdout).unwrap().trim().lines();
let diff_id = results.next().expect("unexpected output");
let digest = results.next().expect("unexpected output");
eprintln!("diff_id: {diff_id}");
(
OciDigest::from_str(diff_id).unwrap(),
OciDigest::from_str(digest).unwrap()
)
};
zfs.destroy(format!("{running_dataset}@{commit_id}"), true, true, true)?;
let diff_id = std::str::from_utf8(&output.stderr).unwrap().trim();
Ok(OciDigest::from_str(diff_id)?)
Ok(diff_id)
}
pub(crate) async fn do_commit(
@@ -357,14 +365,19 @@ impl ServerContext {
.output()
.expect("fail to spawn ocitar");
let diff_id = {
let diff_id = std::str::from_utf8(&output.stdout).unwrap().trim();
let (diff_id, digest) = {
let mut results = std::str::from_utf8(&output.stdout).unwrap().trim().lines();
let diff_id = results.next().expect("unexpected output");
let digest = results.next().expect("unexpected output");
eprintln!("diff_id: {diff_id}");
eprintln!("rename: {temp_file} -> {}/{diff_id}", config.layers_dir);
std::fs::rename(temp_file, format!("{}/{diff_id}", config.layers_dir))?;
OciDigest::from_str(diff_id).unwrap()
eprintln!("rename: {temp_file} -> {}/{digest}", config.layers_dir);
std::fs::rename(temp_file, format!("{}/{digest}", config.layers_dir))?;
(
OciDigest::from_str(diff_id).unwrap(),
OciDigest::from_str(digest).unwrap()
)
};
//
chain_id.consume_diff_id(oci_util::digest::DigestAlgorithm::Sha256, &diff_id);
let new_name = format!("{}/{chain_id}", config.image_dataset);
zfs.rename(&dst_dataset, new_name)?;
@@ -377,7 +390,7 @@ impl ServerContext {
.register_and_tag_manifest(name, tag, &manifest)
.await;
context.map_diff_id(&diff_id, &diff_id, "plain").await?;
context.map_diff_id(&diff_id, &digest, "zstd").await?;
Ok(commit_id)
}

View File

@@ -30,6 +30,7 @@ use oci_util::digest::OciDigest;
use oci_util::distribution::client::*;
use oci_util::image_reference::ImageReference;
use oci_util::models::Descriptor;
use oci_util::models::Platform;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use thiserror::Error;
@@ -267,9 +268,23 @@ pub async fn push_image(
state.push_manifest = true;
Ok(())
});
session.register_manifest(&tag, &manifest).await?;
debug!("Registering manifest: {manifest:#?}");
let arch = record.manifest.architecture();
let arch_tag = format!("{tag}-{arch}");
let platform = Platform {
os: record.manifest.os().to_string(),
architecture: arch.to_string(),
os_version: None,
os_features: Vec::new(),
variant: None,
features: Vec::new(),
};
let descriptor = session.register_manifest(&arch_tag, &manifest).await?;
session
.merge_manifest_list(&descriptor, &platform, &tag)
.await?;
_ = emitter.use_try(|state| {
state.done = true;
Ok(())