diff --git a/xc-bin/src/image.rs b/xc-bin/src/image.rs index 0d39929..49cf350 100644 --- a/xc-bin/src/image.rs +++ b/xc-bin/src/image.rs @@ -21,9 +21,11 @@ // 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 clap::Subcommand; +use clap::{Parser, Subcommand}; use oci_util::image_reference::ImageReference; use std::os::unix::net::UnixStream; +use varutil::string_interpolation::Var; +use xc::models::{jail_image::JailConfig, EnvSpec}; use xcd::ipc::*; #[derive(Subcommand, Debug)] @@ -37,13 +39,60 @@ pub(crate) enum ImageAction { Show { image_id: String, }, - GetMeta { + GetConfig { image_id: String, }, - ReplaceMeta { + SetConfig { image_id: String, meta_path: String, }, + #[clap(subcommand)] + Patch(PatchActions), +} + +#[derive(Parser, Debug)] +pub(crate) enum PatchActions { + AddEnv { + #[clap(long, action)] + required: bool, + #[clap(short = 'd', long = "description")] + description: Option, + env: String, + image_reference: ImageReference, + }, +} + +fn patch_image( + conn: &mut UnixStream, + image_reference: &ImageReference, + f: F, +) -> Result<(), crate::ActionError> +where + F: FnOnce(&mut JailConfig), +{ + let image_name = &image_reference.name; + let tag = &image_reference.tag; + let reqt = DescribeImageRequest { + image_name: image_name.to_string(), + tag: tag.to_string(), + }; + let res = do_describe_image(conn, reqt)?; + match res { + Err(e) => { + eprintln!("{e:#?}"); + } + Ok(res) => { + let mut config = res.jail_image.jail_config(); + f(&mut config); + let req = SetConfigRequest { + name: image_name.to_string(), + tag: tag.to_string(), + config, + }; + _ = do_replace_meta(conn, req)?; + } + } + Ok(()) } pub(crate) fn use_image_action( @@ -51,6 +100,25 @@ pub(crate) fn use_image_action( action: ImageAction, ) -> Result<(), crate::ActionError> { match action { + ImageAction::Patch(patch) => match patch { + PatchActions::AddEnv { + required, + description, + env, + image_reference, + } => { + patch_image(conn, &image_reference, |config| { + let env_var = Var::new(env).expect("invalid environment variable name"); + config.envs.insert( + env_var, + EnvSpec { + description, + required, + }, + ); + })?; + } + }, ImageAction::Import { image_id, path, @@ -99,7 +167,7 @@ pub(crate) fn use_image_action( } } } - ImageAction::GetMeta { image_id } => { + ImageAction::GetConfig { image_id } => { let (image_name, tag) = image_id.rsplit_once(':').expect("invalid image id"); let reqt = DescribeImageRequest { image_name: image_name.to_string(), @@ -116,13 +184,13 @@ pub(crate) fn use_image_action( } } } - ImageAction::ReplaceMeta { + ImageAction::SetConfig { image_id, meta_path, } => { let (name, tag) = image_id.rsplit_once(':').expect("invalid image id"); - let meta: xc::models::jail_image::JailConfig = if meta_path == *"-" { + let config: xc::models::jail_image::JailConfig = if meta_path == *"-" { //let input = std::io::read_to_string(std::io::stdin())?; serde_json::from_reader(std::io::stdin()).unwrap() } else { @@ -133,10 +201,10 @@ pub(crate) fn use_image_action( serde_json::from_reader(meta_file).unwrap() }; - let req = ReplaceMetaRequest { + let req = SetConfigRequest { name: name.to_string(), tag: tag.to_string(), - meta, + config, }; let manifest = do_replace_meta(conn, req)?; // let manifest = request(conn, "replace_meta", req)?; diff --git a/xc-bin/src/main.rs b/xc-bin/src/main.rs index 49135f2..7b601f6 100644 --- a/xc-bin/src/main.rs +++ b/xc-bin/src/main.rs @@ -27,12 +27,14 @@ mod error; mod format; mod image; mod network; +mod redirect; 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::network::{use_network_action, NetworkAction}; +use crate::redirect::{use_rdr_action, RdrAction}; use clap::Parser; use freebsd::event::{eventfd, EventFdNotify}; @@ -42,7 +44,6 @@ use oci_util::digest::OciDigest; use oci_util::image_reference::ImageReference; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; -use std::io::{stdin, Read}; use std::os::fd::{AsRawFd, IntoRawFd}; use std::os::unix::net::UnixStream; use term_table::homogeneous::{TableLayout, TableSource, Title}; @@ -134,11 +135,8 @@ enum Action { image_reference: ImageReference, new_image_reference: ImageReference, }, - Rdr { - #[clap(long = "publish", short = 'p', multiple_occurrences = true)] - publish: Vec, - name: String, - }, + #[clap(subcommand)] + Rdr(RdrAction), Run { #[clap(long, default_value_t, action)] no_clean: bool, @@ -246,8 +244,8 @@ fn main() -> Result<(), ActionError> { eprintln!("{res:#?}"); } Action::Kill { name } => { - let req = DestroyContainerRequest { name }; - let res = do_destroy_container(&mut conn, req)?.unwrap(); + let req = KillContainerRequest { name }; + let res = do_kill_container(&mut conn, req)?.unwrap(); eprintln!("{res:#?}"); } Action::Link { name } => { @@ -284,7 +282,6 @@ fn main() -> Result<(), ActionError> { } let password = password.unwrap_or_else(|| { - let mut password = String::new(); print!("Enter password: \n"); rpassword::read_password().unwrap() }); @@ -435,18 +432,8 @@ fn main() -> Result<(), ActionError> { } } } - - Action::Rdr { name, publish } => { - for expose in publish.iter() { - let redirection = expose.to_host_spec(); - let request = DoRdr { - name: name.clone(), - redirection, - }; - if let Ok(response) = do_rdr_container(&mut conn, request)? { - eprintln!("{response:#?}"); - } - } + Action::Rdr(rdr) => { + _ = use_rdr_action(&mut conn, rdr); } Action::Run { image_reference, diff --git a/xc-bin/src/redirect.rs b/xc-bin/src/redirect.rs new file mode 100644 index 0000000..7fd217d --- /dev/null +++ b/xc-bin/src/redirect.rs @@ -0,0 +1,78 @@ +// Copyright (c) 2023 Yan Ka, Chiu. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions, and the following disclaimer, +// without modification, immediately at the beginning of the file. +// 2. The name of the author may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR +// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// 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 clap::Parser; +use std::os::unix::net::UnixStream; +use xcd::ipc::*; + +use crate::format::PublishSpec; + +#[derive(Parser, Debug)] +pub(crate) enum RdrAction { + Add { + #[clap(long = "publish", short = 'p', multiple_occurrences = true)] + publish: Vec, + name: String, + }, + List { + name: String, + #[clap(short = 'H', action)] + without_header: bool, + }, +} + +pub(crate) fn use_rdr_action( + conn: &mut UnixStream, + action: RdrAction, +) -> Result<(), crate::ActionError> { + match action { + RdrAction::Add { name, publish } => { + for expose in publish.iter() { + let redirection = expose.to_host_spec(); + let request = DoRdr { + name: name.clone(), + redirection, + }; + if let Ok(response) = do_rdr_container(conn, request)? { + eprintln!("{response:#?}"); + } + } + } + RdrAction::List { + name, + without_header, + } => { + let response = do_list_site_rdr(conn, ContainerRdrList { name })?; + if let Ok(response) = response { + if !without_header { + let count = response.len(); + println!("{count} redirection(s)"); + } + for rdr in response.iter() { + println!("{}", rdr.to_pf_rule()); + } + } + } + } + Ok(()) +} diff --git a/xcd/src/image/pull.rs b/xcd/src/image/pull.rs index 89d4402..3fe21fd 100644 --- a/xcd/src/image/pull.rs +++ b/xcd/src/image/pull.rs @@ -31,7 +31,6 @@ use freebsd::fs::zfs::ZfsHandle; use oci_util::digest::OciDigest; use oci_util::distribution::client::*; use oci_util::image_reference::ImageReference; -use oci_util::image_reference::ImageTag; use oci_util::layer::ChainId; use oci_util::models::ImageManifest; use serde::{Deserialize, Serialize};