feat: Create RunDriver (#196)
This will be used for running containers for various tasks. There will be a way to take all output from the process and a way to display output from a running container like our builds have.
This commit is contained in:
parent
1a348f8137
commit
784be9869a
16 changed files with 510 additions and 98 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -304,6 +304,7 @@ dependencies = [
|
||||||
"serde_yaml 0.9.34+deprecated",
|
"serde_yaml 0.9.34+deprecated",
|
||||||
"signal-hook",
|
"signal-hook",
|
||||||
"syntect",
|
"syntect",
|
||||||
|
"tempdir",
|
||||||
"typed-builder",
|
"typed-builder",
|
||||||
"which",
|
"which",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ once_cell = "1"
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
serde_yaml = "0.9"
|
serde_yaml = "0.9"
|
||||||
|
tempdir = "0.3"
|
||||||
typed-builder = "0.18"
|
typed-builder = "0.18"
|
||||||
uuid = { version = "1", features = ["v4"] }
|
uuid = { version = "1", features = ["v4"] }
|
||||||
|
|
||||||
|
|
@ -69,7 +70,6 @@ rayon = { version = "1.10.0", optional = true }
|
||||||
requestty = { version = "0.5", features = ["macros", "termion"] }
|
requestty = { version = "0.5", features = ["macros", "termion"] }
|
||||||
semver = { version = "1", features = ["serde"] }
|
semver = { version = "1", features = ["serde"] }
|
||||||
shadow-rs = "0.26"
|
shadow-rs = "0.26"
|
||||||
tempdir = "0.3"
|
|
||||||
urlencoding = "2"
|
urlencoding = "2"
|
||||||
users = "0.11"
|
users = "0.11"
|
||||||
|
|
||||||
|
|
@ -85,6 +85,7 @@ once_cell.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
serde_yaml.workspace = true
|
serde_yaml.workspace = true
|
||||||
|
tempdir.workspace = true
|
||||||
typed-builder.workspace = true
|
typed-builder.workspace = true
|
||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -123,17 +123,6 @@ pub enum CommandArgs {
|
||||||
|
|
||||||
#[derive(Default, Clone, Copy, Debug, TypedBuilder, Args)]
|
#[derive(Default, Clone, Copy, Debug, TypedBuilder, Args)]
|
||||||
pub struct DriverArgs {
|
pub struct DriverArgs {
|
||||||
/// Runs all instructions inside one layer of the final image.
|
|
||||||
///
|
|
||||||
/// WARN: This doesn't work with the
|
|
||||||
/// docker driver as it has been deprecated.
|
|
||||||
///
|
|
||||||
/// NOTE: Squash has a performance benefit for
|
|
||||||
/// podman and buildah when running inside a container.
|
|
||||||
#[arg(short, long)]
|
|
||||||
#[builder(default)]
|
|
||||||
squash: bool,
|
|
||||||
|
|
||||||
/// Select which driver to use to build
|
/// Select which driver to use to build
|
||||||
/// your image.
|
/// your image.
|
||||||
#[builder(default)]
|
#[builder(default)]
|
||||||
|
|
@ -146,3 +135,15 @@ pub struct DriverArgs {
|
||||||
#[arg(short = 'I', long)]
|
#[arg(short = 'I', long)]
|
||||||
inspect_driver: Option<InspectDriverType>,
|
inspect_driver: Option<InspectDriverType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use clap::CommandFactory;
|
||||||
|
|
||||||
|
use super::BlueBuildArgs;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cli() {
|
||||||
|
BlueBuildArgs::command().debug_assert();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,17 @@ pub struct BuildCommand {
|
||||||
#[builder(default)]
|
#[builder(default)]
|
||||||
no_sign: bool,
|
no_sign: bool,
|
||||||
|
|
||||||
|
/// Runs all instructions inside one layer of the final image.
|
||||||
|
///
|
||||||
|
/// WARN: This doesn't work with the
|
||||||
|
/// docker driver as it has been deprecated.
|
||||||
|
///
|
||||||
|
/// NOTE: Squash has a performance benefit for
|
||||||
|
/// podman and buildah when running inside a container.
|
||||||
|
#[arg(short, long)]
|
||||||
|
#[builder(default)]
|
||||||
|
squash: bool,
|
||||||
|
|
||||||
#[clap(flatten)]
|
#[clap(flatten)]
|
||||||
#[builder(default)]
|
#[builder(default)]
|
||||||
drivers: DriverArgs,
|
drivers: DriverArgs,
|
||||||
|
|
@ -134,7 +145,7 @@ impl BlueBuildCommand for BuildCommand {
|
||||||
.build_driver(self.drivers.build_driver)
|
.build_driver(self.drivers.build_driver)
|
||||||
.inspect_driver(self.drivers.inspect_driver)
|
.inspect_driver(self.drivers.inspect_driver)
|
||||||
.build()
|
.build()
|
||||||
.init()?;
|
.init();
|
||||||
|
|
||||||
self.update_gitignore()?;
|
self.update_gitignore()?;
|
||||||
|
|
||||||
|
|
@ -171,7 +182,7 @@ impl BlueBuildCommand for BuildCommand {
|
||||||
GenerateCommand::builder()
|
GenerateCommand::builder()
|
||||||
.output(generate_containerfile_path(recipe)?)
|
.output(generate_containerfile_path(recipe)?)
|
||||||
.recipe(recipe)
|
.recipe(recipe)
|
||||||
.drivers(DriverArgs::builder().squash(self.drivers.squash).build())
|
.drivers(self.drivers)
|
||||||
.build()
|
.build()
|
||||||
.try_run()
|
.try_run()
|
||||||
})?;
|
})?;
|
||||||
|
|
@ -195,7 +206,7 @@ impl BlueBuildCommand for BuildCommand {
|
||||||
GenerateCommand::builder()
|
GenerateCommand::builder()
|
||||||
.output(generate_containerfile_path(&recipe_path)?)
|
.output(generate_containerfile_path(&recipe_path)?)
|
||||||
.recipe(&recipe_path)
|
.recipe(&recipe_path)
|
||||||
.drivers(DriverArgs::builder().squash(self.drivers.squash).build())
|
.drivers(self.drivers)
|
||||||
.build()
|
.build()
|
||||||
.try_run()?;
|
.try_run()?;
|
||||||
|
|
||||||
|
|
@ -227,7 +238,7 @@ impl BuildCommand {
|
||||||
archive_dir.to_string_lossy().trim_end_matches('/'),
|
archive_dir.to_string_lossy().trim_end_matches('/'),
|
||||||
recipe.name.to_lowercase().replace('/', "_"),
|
recipe.name.to_lowercase().replace('/', "_"),
|
||||||
))
|
))
|
||||||
.squash(self.drivers.squash)
|
.squash(self.squash)
|
||||||
.build()
|
.build()
|
||||||
} else {
|
} else {
|
||||||
BuildTagPushOpts::builder()
|
BuildTagPushOpts::builder()
|
||||||
|
|
@ -238,7 +249,7 @@ impl BuildCommand {
|
||||||
.no_retry_push(self.no_retry_push)
|
.no_retry_push(self.no_retry_push)
|
||||||
.retry_count(self.retry_count)
|
.retry_count(self.retry_count)
|
||||||
.compression(self.compression_format)
|
.compression(self.compression_format)
|
||||||
.squash(self.drivers.squash)
|
.squash(self.squash)
|
||||||
.build()
|
.build()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -273,7 +284,7 @@ impl BuildCommand {
|
||||||
archive_dir.to_string_lossy().trim_end_matches('/'),
|
archive_dir.to_string_lossy().trim_end_matches('/'),
|
||||||
recipe.name.to_lowercase().replace('/', "_"),
|
recipe.name.to_lowercase().replace('/', "_"),
|
||||||
))
|
))
|
||||||
.squash(self.drivers.squash)
|
.squash(self.squash)
|
||||||
.build()
|
.build()
|
||||||
} else {
|
} else {
|
||||||
BuildTagPushOpts::builder()
|
BuildTagPushOpts::builder()
|
||||||
|
|
@ -284,7 +295,7 @@ impl BuildCommand {
|
||||||
.no_retry_push(self.no_retry_push)
|
.no_retry_push(self.no_retry_push)
|
||||||
.retry_count(self.retry_count)
|
.retry_count(self.retry_count)
|
||||||
.compression(self.compression_format)
|
.compression(self.compression_format)
|
||||||
.squash(self.drivers.squash)
|
.squash(self.squash)
|
||||||
.build()
|
.build()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ impl BlueBuildCommand for GenerateCommand {
|
||||||
.build_driver(self.drivers.build_driver)
|
.build_driver(self.drivers.build_driver)
|
||||||
.inspect_driver(self.drivers.inspect_driver)
|
.inspect_driver(self.drivers.inspect_driver)
|
||||||
.build()
|
.build()
|
||||||
.init()?;
|
.init();
|
||||||
|
|
||||||
self.template_file()
|
self.template_file()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ impl BlueBuildCommand for SwitchCommand {
|
||||||
.build_driver(self.drivers.build_driver)
|
.build_driver(self.drivers.build_driver)
|
||||||
.inspect_driver(self.drivers.inspect_driver)
|
.inspect_driver(self.drivers.inspect_driver)
|
||||||
.build()
|
.build()
|
||||||
.init()?;
|
.init();
|
||||||
|
|
||||||
let status = RpmOstreeStatus::try_new()?;
|
let status = RpmOstreeStatus::try_new()?;
|
||||||
trace!("{status:?}");
|
trace!("{status:?}");
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
use std::{env, sync::Mutex};
|
use std::{env, sync::Mutex};
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
|
||||||
use blue_build_utils::constants::{
|
use blue_build_utils::constants::{
|
||||||
CI_REGISTRY, CI_REGISTRY_PASSWORD, CI_REGISTRY_USER, GITHUB_ACTIONS, GITHUB_ACTOR, GITHUB_TOKEN,
|
CI_REGISTRY, CI_REGISTRY_PASSWORD, CI_REGISTRY_USER, GITHUB_ACTIONS, GITHUB_ACTOR, GITHUB_TOKEN,
|
||||||
};
|
};
|
||||||
|
|
@ -107,29 +106,23 @@ static ENV_CREDENTIALS: Lazy<Option<Credentials>> = Lazy::new(|| {
|
||||||
/// any strategy that requires credentials as
|
/// any strategy that requires credentials as
|
||||||
/// the environment credentials are lazy allocated.
|
/// the environment credentials are lazy allocated.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Panics
|
||||||
/// Will error if it can't lock the mutex.
|
/// Will panic if it can't lock the mutex.
|
||||||
pub fn set_user_creds(
|
pub fn set_user_creds(
|
||||||
username: Option<&String>,
|
username: Option<&String>,
|
||||||
password: Option<&String>,
|
password: Option<&String>,
|
||||||
registry: Option<&String>,
|
registry: Option<&String>,
|
||||||
) -> Result<()> {
|
) {
|
||||||
trace!("credentials::set({username:?}, password, {registry:?})");
|
trace!("credentials::set({username:?}, password, {registry:?})");
|
||||||
let mut creds_lock = USER_CREDS
|
let mut creds_lock = USER_CREDS.lock().expect("Must lock USER_CREDS");
|
||||||
.lock()
|
|
||||||
.map_err(|e| anyhow!("Failed to set credentials: {e}"))?;
|
|
||||||
creds_lock.username = username.map(ToOwned::to_owned);
|
creds_lock.username = username.map(ToOwned::to_owned);
|
||||||
creds_lock.password = password.map(ToOwned::to_owned);
|
creds_lock.password = password.map(ToOwned::to_owned);
|
||||||
creds_lock.registry = registry.map(ToOwned::to_owned);
|
creds_lock.registry = registry.map(ToOwned::to_owned);
|
||||||
drop(creds_lock);
|
drop(creds_lock);
|
||||||
let _ = ENV_CREDENTIALS.as_ref();
|
let _ = ENV_CREDENTIALS.as_ref();
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the credentials for the current set of actions.
|
/// Get the credentials for the current set of actions.
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// Will error if there aren't any credentials available.
|
|
||||||
pub fn get() -> Option<&'static Credentials> {
|
pub fn get() -> Option<&'static Credentials> {
|
||||||
trace!("credentials::get()");
|
trace!("credentials::get()");
|
||||||
ENV_CREDENTIALS.as_ref()
|
ENV_CREDENTIALS.as_ref()
|
||||||
|
|
|
||||||
151
src/drivers.rs
151
src/drivers.rs
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::{hash_map::Entry, HashMap},
|
collections::{hash_map::Entry, HashMap},
|
||||||
|
process::{ExitStatus, Output},
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -23,10 +24,10 @@ use crate::{credentials, image_metadata::ImageMetadata};
|
||||||
use self::{
|
use self::{
|
||||||
buildah_driver::BuildahDriver,
|
buildah_driver::BuildahDriver,
|
||||||
docker_driver::DockerDriver,
|
docker_driver::DockerDriver,
|
||||||
opts::{BuildOpts, BuildTagPushOpts, GetMetadataOpts, PushOpts, TagOpts},
|
opts::{BuildOpts, BuildTagPushOpts, GetMetadataOpts, PushOpts, RunOpts, TagOpts},
|
||||||
podman_driver::PodmanDriver,
|
podman_driver::PodmanDriver,
|
||||||
skopeo_driver::SkopeoDriver,
|
skopeo_driver::SkopeoDriver,
|
||||||
types::{BuildDriverType, InspectDriverType},
|
types::{BuildDriverType, InspectDriverType, RunDriverType},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod buildah_driver;
|
mod buildah_driver;
|
||||||
|
|
@ -36,10 +37,11 @@ mod podman_driver;
|
||||||
mod skopeo_driver;
|
mod skopeo_driver;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
static INIT: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
|
static INIT: Lazy<Mutex<bool>> = Lazy::new(|| Mutex::new(false));
|
||||||
static SELECTED_BUILD_DRIVER: Lazy<Mutex<Option<BuildDriverType>>> = Lazy::new(|| Mutex::new(None));
|
static SELECTED_BUILD_DRIVER: Lazy<Mutex<Option<BuildDriverType>>> = Lazy::new(|| Mutex::new(None));
|
||||||
static SELECTED_INSPECT_DRIVER: Lazy<Mutex<Option<InspectDriverType>>> =
|
static SELECTED_INSPECT_DRIVER: Lazy<Mutex<Option<InspectDriverType>>> =
|
||||||
Lazy::new(|| Mutex::new(None));
|
Lazy::new(|| Mutex::new(None));
|
||||||
|
static SELECTED_RUN_DRIVER: Lazy<Mutex<Option<RunDriverType>>> = Lazy::new(|| Mutex::new(None));
|
||||||
|
|
||||||
/// Stores the build driver.
|
/// Stores the build driver.
|
||||||
///
|
///
|
||||||
|
|
@ -75,7 +77,7 @@ static BUILD_DRIVER: Lazy<Arc<dyn BuildDriver>> = Lazy::new(|| {
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// This will cause a panic if a build strategy could
|
/// This will cause a panic if a inspect strategy could
|
||||||
/// not be determined.
|
/// not be determined.
|
||||||
static INSPECT_DRIVER: Lazy<Arc<dyn InspectDriver>> = Lazy::new(|| {
|
static INSPECT_DRIVER: Lazy<Arc<dyn InspectDriver>> = Lazy::new(|| {
|
||||||
let driver = SELECTED_INSPECT_DRIVER.lock().unwrap();
|
let driver = SELECTED_INSPECT_DRIVER.lock().unwrap();
|
||||||
|
|
@ -91,6 +93,30 @@ static INSPECT_DRIVER: Lazy<Arc<dyn InspectDriver>> = Lazy::new(|| {
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// Stores the run driver.
|
||||||
|
///
|
||||||
|
/// This will, on load, find the best way to run containers in the
|
||||||
|
/// current environment. Once that strategy is determined,
|
||||||
|
/// it will be available for any part of the program to call
|
||||||
|
/// on to perform inspections.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// This will cause a panic if a run strategy could
|
||||||
|
/// not be determined.
|
||||||
|
static RUN_DRIVER: Lazy<Arc<dyn RunDriver>> = Lazy::new(|| {
|
||||||
|
let driver = SELECTED_RUN_DRIVER.lock().unwrap();
|
||||||
|
driver.map_or_else(
|
||||||
|
|| panic!("Driver needs to be initialized"),
|
||||||
|
|driver| -> Arc<dyn RunDriver> {
|
||||||
|
match driver {
|
||||||
|
RunDriverType::Podman => Arc::new(PodmanDriver),
|
||||||
|
RunDriverType::Docker => Arc::new(DockerDriver),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
/// UUID used to mark the current builds
|
/// UUID used to mark the current builds
|
||||||
static BUILD_ID: Lazy<Uuid> = Lazy::new(Uuid::new_v4);
|
static BUILD_ID: Lazy<Uuid> = Lazy::new(Uuid::new_v4);
|
||||||
|
|
||||||
|
|
@ -218,6 +244,20 @@ pub trait BuildDriver: Sync + Send {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait RunDriver: Sync + Send {
|
||||||
|
/// Run a container to perform an action.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Will error if there is an issue running the container.
|
||||||
|
fn run(&self, opts: &RunOpts) -> std::io::Result<ExitStatus>;
|
||||||
|
|
||||||
|
/// Run a container to perform an action and capturing output.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// Will error if there is an issue running the container.
|
||||||
|
fn run_output(&self, opts: &RunOpts) -> std::io::Result<Output>;
|
||||||
|
}
|
||||||
|
|
||||||
/// Allows agnostic inspection of images.
|
/// Allows agnostic inspection of images.
|
||||||
pub trait InspectDriver: Sync + Send {
|
pub trait InspectDriver: Sync + Send {
|
||||||
/// Gets the metadata on an image tag.
|
/// Gets the metadata on an image tag.
|
||||||
|
|
@ -243,6 +283,9 @@ pub struct Driver<'a> {
|
||||||
|
|
||||||
#[builder(default)]
|
#[builder(default)]
|
||||||
inspect_driver: Option<InspectDriverType>,
|
inspect_driver: Option<InspectDriverType>,
|
||||||
|
|
||||||
|
#[builder(default)]
|
||||||
|
run_driver: Option<RunDriverType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Driver<'_> {
|
impl Driver<'_> {
|
||||||
|
|
@ -252,35 +295,46 @@ impl Driver<'_> {
|
||||||
/// you will want to run init before trying to use any of
|
/// you will want to run init before trying to use any of
|
||||||
/// the strategies.
|
/// the strategies.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Panics
|
||||||
/// Will error if it is unable to set the user credentials.
|
/// Will panic if it is unable to initialize drivers.
|
||||||
pub fn init(self) -> Result<()> {
|
pub fn init(self) {
|
||||||
trace!("Driver::init()");
|
trace!("Driver::init()");
|
||||||
let init = INIT.lock().map_err(|e| anyhow!("{e}"))?;
|
let mut initialized = INIT.lock().expect("Must lock INIT");
|
||||||
credentials::set_user_creds(self.username, self.password, self.registry)?;
|
|
||||||
|
|
||||||
let mut build_driver = SELECTED_BUILD_DRIVER.lock().map_err(|e| anyhow!("{e}"))?;
|
if !*initialized {
|
||||||
let mut inspect_driver = SELECTED_INSPECT_DRIVER.lock().map_err(|e| anyhow!("{e}"))?;
|
credentials::set_user_creds(self.username, self.password, self.registry);
|
||||||
|
|
||||||
*build_driver = Some(match self.build_driver {
|
let mut build_driver = SELECTED_BUILD_DRIVER.lock().expect("Must lock BuildDriver");
|
||||||
None => Self::determine_build_driver()?,
|
let mut inspect_driver = SELECTED_INSPECT_DRIVER
|
||||||
Some(driver) => driver,
|
.lock()
|
||||||
});
|
.expect("Must lock InspectDriver");
|
||||||
trace!("Build driver set to {:?}", *build_driver);
|
let mut run_driver = SELECTED_RUN_DRIVER.lock().expect("Must lock RunDriver");
|
||||||
drop(build_driver);
|
|
||||||
let _ = Self::get_build_driver();
|
|
||||||
|
|
||||||
*inspect_driver = Some(match self.inspect_driver {
|
*build_driver = Some(
|
||||||
None => Self::determine_inspect_driver()?,
|
self.build_driver
|
||||||
Some(driver) => driver,
|
.map_or_else(Self::determine_build_driver, |driver| driver),
|
||||||
});
|
);
|
||||||
trace!("Inspect driver set to {:?}", *inspect_driver);
|
trace!("Build driver set to {:?}", *build_driver);
|
||||||
drop(inspect_driver);
|
drop(build_driver);
|
||||||
let _ = Self::get_inspection_driver();
|
let _ = Self::get_build_driver();
|
||||||
|
|
||||||
drop(init);
|
*inspect_driver = Some(
|
||||||
|
self.inspect_driver
|
||||||
|
.map_or_else(Self::determine_inspect_driver, |driver| driver),
|
||||||
|
);
|
||||||
|
trace!("Inspect driver set to {:?}", *inspect_driver);
|
||||||
|
drop(inspect_driver);
|
||||||
|
let _ = Self::get_inspection_driver();
|
||||||
|
|
||||||
Ok(())
|
*run_driver = Some(
|
||||||
|
self.run_driver
|
||||||
|
.map_or_else(Self::determine_run_driver, |driver| driver),
|
||||||
|
);
|
||||||
|
drop(run_driver);
|
||||||
|
let _ = Self::get_run_driver();
|
||||||
|
|
||||||
|
*initialized = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the current build's UUID
|
/// Gets the current build's UUID
|
||||||
|
|
@ -302,6 +356,11 @@ impl Driver<'_> {
|
||||||
INSPECT_DRIVER.clone()
|
INSPECT_DRIVER.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_run_driver() -> Arc<dyn RunDriver> {
|
||||||
|
trace!("Driver::get_run_driver()");
|
||||||
|
RUN_DRIVER.clone()
|
||||||
|
}
|
||||||
|
|
||||||
/// Retrieve the `os_version` for an image.
|
/// Retrieve the `os_version` for an image.
|
||||||
///
|
///
|
||||||
/// This gets cached for faster resolution if it's required
|
/// This gets cached for faster resolution if it's required
|
||||||
|
|
@ -352,10 +411,10 @@ impl Driver<'_> {
|
||||||
Ok(os_version)
|
Ok(os_version)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn determine_inspect_driver() -> Result<InspectDriverType> {
|
fn determine_inspect_driver() -> InspectDriverType {
|
||||||
trace!("Driver::determine_inspect_driver()");
|
trace!("Driver::determine_inspect_driver()");
|
||||||
|
|
||||||
Ok(match (
|
match (
|
||||||
blue_build_utils::check_command_exists("skopeo"),
|
blue_build_utils::check_command_exists("skopeo"),
|
||||||
blue_build_utils::check_command_exists("docker"),
|
blue_build_utils::check_command_exists("docker"),
|
||||||
blue_build_utils::check_command_exists("podman"),
|
blue_build_utils::check_command_exists("podman"),
|
||||||
|
|
@ -363,14 +422,14 @@ impl Driver<'_> {
|
||||||
(Ok(_skopeo), _, _) => InspectDriverType::Skopeo,
|
(Ok(_skopeo), _, _) => InspectDriverType::Skopeo,
|
||||||
(_, Ok(_docker), _) => InspectDriverType::Docker,
|
(_, Ok(_docker), _) => InspectDriverType::Docker,
|
||||||
(_, _, Ok(_podman)) => InspectDriverType::Podman,
|
(_, _, Ok(_podman)) => InspectDriverType::Podman,
|
||||||
_ => bail!("Could not determine inspection strategy. You need either skopeo, docker, or podman"),
|
_ => panic!("Could not determine inspection strategy. You need either skopeo, docker, or podman"),
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn determine_build_driver() -> Result<BuildDriverType> {
|
fn determine_build_driver() -> BuildDriverType {
|
||||||
trace!("Driver::determine_build_driver()");
|
trace!("Driver::determine_build_driver()");
|
||||||
|
|
||||||
Ok(match (
|
match (
|
||||||
blue_build_utils::check_command_exists("docker"),
|
blue_build_utils::check_command_exists("docker"),
|
||||||
blue_build_utils::check_command_exists("podman"),
|
blue_build_utils::check_command_exists("podman"),
|
||||||
blue_build_utils::check_command_exists("buildah"),
|
blue_build_utils::check_command_exists("buildah"),
|
||||||
|
|
@ -384,12 +443,34 @@ impl Driver<'_> {
|
||||||
(_, _, Ok(_buildah)) if BuildahDriver::is_supported_version() => {
|
(_, _, Ok(_buildah)) if BuildahDriver::is_supported_version() => {
|
||||||
BuildDriverType::Buildah
|
BuildDriverType::Buildah
|
||||||
}
|
}
|
||||||
_ => bail!(
|
_ => panic!(
|
||||||
"Could not determine strategy, need either docker version {}, podman version {}, or buildah version {} to continue",
|
"Could not determine strategy, need either docker version {}, podman version {}, or buildah version {} to continue",
|
||||||
DockerDriver::VERSION_REQ,
|
DockerDriver::VERSION_REQ,
|
||||||
PodmanDriver::VERSION_REQ,
|
PodmanDriver::VERSION_REQ,
|
||||||
BuildahDriver::VERSION_REQ,
|
BuildahDriver::VERSION_REQ,
|
||||||
),
|
),
|
||||||
})
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn determine_run_driver() -> RunDriverType {
|
||||||
|
trace!("Driver::determine_run_driver()");
|
||||||
|
|
||||||
|
match (
|
||||||
|
blue_build_utils::check_command_exists("docker"),
|
||||||
|
blue_build_utils::check_command_exists("podman"),
|
||||||
|
) {
|
||||||
|
(Ok(_docker), _) if DockerDriver::is_supported_version() => RunDriverType::Docker,
|
||||||
|
(_, Ok(_podman)) if PodmanDriver::is_supported_version() => RunDriverType::Podman,
|
||||||
|
_ => panic!(
|
||||||
|
"{}{}{}{}",
|
||||||
|
"Could not determine strategy, ",
|
||||||
|
format_args!("need either docker version {}, ", DockerDriver::VERSION_REQ),
|
||||||
|
format_args!("podman version {}, ", PodmanDriver::VERSION_REQ),
|
||||||
|
format_args!(
|
||||||
|
"or buildah version {} to continue",
|
||||||
|
BuildahDriver::VERSION_REQ
|
||||||
|
),
|
||||||
|
),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,32 @@
|
||||||
use std::{env, process::Command, sync::Mutex, time::Duration};
|
use std::{
|
||||||
|
env,
|
||||||
|
path::Path,
|
||||||
|
process::{Command, ExitStatus},
|
||||||
|
sync::Mutex,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, Result};
|
use anyhow::{anyhow, bail, Result};
|
||||||
use blue_build_utils::{
|
use blue_build_utils::{
|
||||||
constants::{BB_BUILDKIT_CACHE_GHA, CONTAINER_FILE, DOCKER_HOST, SKOPEO_IMAGE},
|
constants::{BB_BUILDKIT_CACHE_GHA, CONTAINER_FILE, DOCKER_HOST, SKOPEO_IMAGE},
|
||||||
logging::{CommandLogging, Logger},
|
logging::{CommandLogging, Logger},
|
||||||
|
signal_handler::{add_cid, remove_cid, ContainerId},
|
||||||
};
|
};
|
||||||
use indicatif::{ProgressBar, ProgressStyle};
|
use indicatif::{ProgressBar, ProgressStyle};
|
||||||
use log::{info, trace, warn};
|
use log::{info, trace, warn};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use tempdir::TempDir;
|
||||||
|
|
||||||
use crate::{credentials::Credentials, image_metadata::ImageMetadata};
|
use crate::{
|
||||||
|
credentials::Credentials, drivers::types::RunDriverType, image_metadata::ImageMetadata,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
credentials,
|
credentials,
|
||||||
opts::{BuildOpts, BuildTagPushOpts, GetMetadataOpts, PushOpts, TagOpts},
|
opts::{BuildOpts, BuildTagPushOpts, GetMetadataOpts, PushOpts, RunOpts, TagOpts},
|
||||||
BuildDriver, DriverVersion, InspectDriver,
|
BuildDriver, DriverVersion, InspectDriver, RunDriver,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
|
@ -308,14 +318,12 @@ impl InspectDriver for DockerDriver {
|
||||||
);
|
);
|
||||||
progress.enable_steady_tick(Duration::from_millis(100));
|
progress.enable_steady_tick(Duration::from_millis(100));
|
||||||
|
|
||||||
trace!("docker run {SKOPEO_IMAGE} inspect {url}");
|
let output = self.run_output(
|
||||||
let output = Command::new("docker")
|
&RunOpts::builder()
|
||||||
.arg("run")
|
.image(SKOPEO_IMAGE)
|
||||||
.arg("--rm")
|
.args(&["inspect".to_string(), url.clone()])
|
||||||
.arg(SKOPEO_IMAGE)
|
.build(),
|
||||||
.arg("inspect")
|
)?;
|
||||||
.arg(&url)
|
|
||||||
.output()?;
|
|
||||||
|
|
||||||
progress.finish();
|
progress.finish();
|
||||||
Logger::multi_progress().remove(&progress);
|
Logger::multi_progress().remove(&progress);
|
||||||
|
|
@ -329,3 +337,78 @@ impl InspectDriver for DockerDriver {
|
||||||
Ok(serde_json::from_slice(&output.stdout)?)
|
Ok(serde_json::from_slice(&output.stdout)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RunDriver for DockerDriver {
|
||||||
|
fn run(&self, opts: &RunOpts) -> std::io::Result<ExitStatus> {
|
||||||
|
trace!("DockerDriver::run({opts:#?})");
|
||||||
|
|
||||||
|
let cid_path = TempDir::new("docker")?;
|
||||||
|
let cid_file = cid_path.path().join("cid");
|
||||||
|
let cid = ContainerId::new(&cid_file, RunDriverType::Docker, false);
|
||||||
|
|
||||||
|
add_cid(&cid);
|
||||||
|
|
||||||
|
let status = docker_run(opts, &cid_file)
|
||||||
|
.status_image_ref_progress(opts.image.as_ref(), "Running container")?;
|
||||||
|
|
||||||
|
remove_cid(&cid);
|
||||||
|
|
||||||
|
Ok(status)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_output(&self, opts: &RunOpts) -> std::io::Result<std::process::Output> {
|
||||||
|
trace!("DockerDriver::run({opts:#?})");
|
||||||
|
|
||||||
|
let cid_path = TempDir::new("docker")?;
|
||||||
|
let cid_file = cid_path.path().join("cid");
|
||||||
|
let cid = ContainerId::new(&cid_file, RunDriverType::Docker, false);
|
||||||
|
|
||||||
|
add_cid(&cid);
|
||||||
|
|
||||||
|
let output = docker_run(opts, &cid_file).output()?;
|
||||||
|
|
||||||
|
remove_cid(&cid);
|
||||||
|
|
||||||
|
Ok(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn docker_run(opts: &RunOpts, cid_file: &Path) -> Command {
|
||||||
|
let mut command = Command::new("docker");
|
||||||
|
|
||||||
|
command
|
||||||
|
.arg("run")
|
||||||
|
.arg(format!("--cidfile={}", cid_file.display()));
|
||||||
|
|
||||||
|
if opts.privileged {
|
||||||
|
command.arg("--privileged");
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.remove {
|
||||||
|
command.arg("--rm");
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.pull {
|
||||||
|
command.arg("--pull=always");
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.volumes.iter().for_each(|volume| {
|
||||||
|
command.arg("--volume");
|
||||||
|
command.arg(format!(
|
||||||
|
"{}:{}",
|
||||||
|
volume.path_or_vol_name, volume.container_path,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
opts.env_vars.iter().for_each(|env| {
|
||||||
|
command.arg("--env");
|
||||||
|
command.arg(format!("{}={}", env.key, env.value));
|
||||||
|
});
|
||||||
|
|
||||||
|
command.arg(opts.image.as_ref());
|
||||||
|
|
||||||
|
command.args(opts.args.iter());
|
||||||
|
|
||||||
|
trace!("{command:?}");
|
||||||
|
command
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,11 @@ use clap::ValueEnum;
|
||||||
|
|
||||||
pub use build::*;
|
pub use build::*;
|
||||||
pub use inspect::*;
|
pub use inspect::*;
|
||||||
|
pub use run::*;
|
||||||
|
|
||||||
mod build;
|
mod build;
|
||||||
mod inspect;
|
mod inspect;
|
||||||
|
mod run;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Default, ValueEnum)]
|
#[derive(Debug, Copy, Clone, Default, ValueEnum)]
|
||||||
pub enum CompressionType {
|
pub enum CompressionType {
|
||||||
|
|
|
||||||
45
src/drivers/opts/run.rs
Normal file
45
src/drivers/opts/run.rs
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
use typed_builder::TypedBuilder;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, TypedBuilder)]
|
||||||
|
pub struct RunOpts<'a> {
|
||||||
|
#[builder(default, setter(into))]
|
||||||
|
pub image: Cow<'a, str>,
|
||||||
|
|
||||||
|
#[builder(default, setter(into))]
|
||||||
|
pub args: Cow<'a, [String]>,
|
||||||
|
|
||||||
|
#[builder(default, setter(into))]
|
||||||
|
pub env_vars: Cow<'a, [RunOptsEnv<'a>]>,
|
||||||
|
|
||||||
|
#[builder(default, setter(into))]
|
||||||
|
pub volumes: Cow<'a, [RunOptsVolume<'a>]>,
|
||||||
|
|
||||||
|
#[builder(default)]
|
||||||
|
pub privileged: bool,
|
||||||
|
|
||||||
|
#[builder(default)]
|
||||||
|
pub pull: bool,
|
||||||
|
|
||||||
|
#[builder(default)]
|
||||||
|
pub remove: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, TypedBuilder)]
|
||||||
|
pub struct RunOptsVolume<'a> {
|
||||||
|
#[builder(setter(into))]
|
||||||
|
pub path_or_vol_name: Cow<'a, str>,
|
||||||
|
|
||||||
|
#[builder(setter(into))]
|
||||||
|
pub container_path: Cow<'a, str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, TypedBuilder)]
|
||||||
|
pub struct RunOptsEnv<'a> {
|
||||||
|
#[builder(setter(into))]
|
||||||
|
pub key: Cow<'a, str>,
|
||||||
|
|
||||||
|
#[builder(setter(into))]
|
||||||
|
pub value: Cow<'a, str>,
|
||||||
|
}
|
||||||
|
|
@ -1,21 +1,30 @@
|
||||||
use std::{process::Command, time::Duration};
|
use std::{
|
||||||
|
path::Path,
|
||||||
|
process::{Command, ExitStatus},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use blue_build_utils::{
|
use blue_build_utils::{
|
||||||
constants::SKOPEO_IMAGE,
|
constants::SKOPEO_IMAGE,
|
||||||
logging::{CommandLogging, Logger},
|
logging::{CommandLogging, Logger},
|
||||||
|
signal_handler::{add_cid, remove_cid, ContainerId},
|
||||||
};
|
};
|
||||||
|
use colored::Colorize;
|
||||||
use indicatif::{ProgressBar, ProgressStyle};
|
use indicatif::{ProgressBar, ProgressStyle};
|
||||||
use log::{debug, error, info, trace};
|
use log::{debug, error, info, trace, warn};
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use tempdir::TempDir;
|
||||||
|
|
||||||
use crate::{credentials::Credentials, image_metadata::ImageMetadata};
|
use crate::{
|
||||||
|
credentials::Credentials, drivers::types::RunDriverType, image_metadata::ImageMetadata,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
credentials,
|
credentials,
|
||||||
opts::{BuildOpts, GetMetadataOpts, PushOpts, TagOpts},
|
opts::{BuildOpts, GetMetadataOpts, PushOpts, RunOpts, TagOpts},
|
||||||
BuildDriver, DriverVersion, InspectDriver,
|
BuildDriver, DriverVersion, InspectDriver, RunDriver,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
|
@ -170,14 +179,12 @@ impl InspectDriver for PodmanDriver {
|
||||||
);
|
);
|
||||||
progress.enable_steady_tick(Duration::from_millis(100));
|
progress.enable_steady_tick(Duration::from_millis(100));
|
||||||
|
|
||||||
trace!("podman run {SKOPEO_IMAGE} inspect {url}");
|
let output = self.run_output(
|
||||||
let output = Command::new("podman")
|
&RunOpts::builder()
|
||||||
.arg("run")
|
.image(SKOPEO_IMAGE)
|
||||||
.arg("--rm")
|
.args(&["inspect".to_string(), url.clone()])
|
||||||
.arg(SKOPEO_IMAGE)
|
.build(),
|
||||||
.arg("inspect")
|
)?;
|
||||||
.arg(&url)
|
|
||||||
.output()?;
|
|
||||||
|
|
||||||
progress.finish();
|
progress.finish();
|
||||||
Logger::multi_progress().remove(&progress);
|
Logger::multi_progress().remove(&progress);
|
||||||
|
|
@ -190,3 +197,91 @@ impl InspectDriver for PodmanDriver {
|
||||||
Ok(serde_json::from_slice(&output.stdout)?)
|
Ok(serde_json::from_slice(&output.stdout)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RunDriver for PodmanDriver {
|
||||||
|
fn run(&self, opts: &RunOpts) -> std::io::Result<ExitStatus> {
|
||||||
|
trace!("PodmanDriver::run({opts:#?})");
|
||||||
|
|
||||||
|
let cid_path = TempDir::new("podman")?;
|
||||||
|
let cid_file = cid_path.path().join("cid");
|
||||||
|
|
||||||
|
let cid = ContainerId::new(&cid_file, RunDriverType::Podman, opts.privileged);
|
||||||
|
|
||||||
|
add_cid(&cid);
|
||||||
|
|
||||||
|
let status = podman_run(opts, &cid_file)
|
||||||
|
.status_image_ref_progress(opts.image.as_ref(), "Running container")?;
|
||||||
|
|
||||||
|
remove_cid(&cid);
|
||||||
|
|
||||||
|
Ok(status)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_output(&self, opts: &RunOpts) -> std::io::Result<std::process::Output> {
|
||||||
|
trace!("PodmanDriver::run_output({opts:#?})");
|
||||||
|
|
||||||
|
let cid_path = TempDir::new("podman")?;
|
||||||
|
let cid_file = cid_path.path().join("cid");
|
||||||
|
|
||||||
|
let cid = ContainerId::new(&cid_file, RunDriverType::Podman, opts.privileged);
|
||||||
|
|
||||||
|
add_cid(&cid);
|
||||||
|
|
||||||
|
let output = podman_run(opts, &cid_file).output()?;
|
||||||
|
|
||||||
|
remove_cid(&cid);
|
||||||
|
|
||||||
|
Ok(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn podman_run(opts: &RunOpts, cid_file: &Path) -> Command {
|
||||||
|
let mut command = if opts.privileged {
|
||||||
|
warn!(
|
||||||
|
"Running 'podman' in privileged mode requires '{}'",
|
||||||
|
"sudo".bold().red()
|
||||||
|
);
|
||||||
|
Command::new("sudo")
|
||||||
|
} else {
|
||||||
|
Command::new("podman")
|
||||||
|
};
|
||||||
|
|
||||||
|
if opts.privileged {
|
||||||
|
command.arg("podman");
|
||||||
|
}
|
||||||
|
|
||||||
|
command
|
||||||
|
.arg("run")
|
||||||
|
.arg(format!("--cidfile={}", cid_file.display()));
|
||||||
|
|
||||||
|
if opts.privileged {
|
||||||
|
command.arg("--privileged");
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.remove {
|
||||||
|
command.arg("--rm");
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.pull {
|
||||||
|
command.arg("--pull=always");
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.volumes.iter().for_each(|volume| {
|
||||||
|
command.arg("--volume");
|
||||||
|
command.arg(format!(
|
||||||
|
"{}:{}",
|
||||||
|
volume.path_or_vol_name, volume.container_path,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
|
||||||
|
opts.env_vars.iter().for_each(|env| {
|
||||||
|
command.arg("--env");
|
||||||
|
command.arg(format!("{}={}", env.key, env.value));
|
||||||
|
});
|
||||||
|
|
||||||
|
command.arg(opts.image.as_ref());
|
||||||
|
command.args(opts.args.iter());
|
||||||
|
|
||||||
|
trace!("{command:?}");
|
||||||
|
command
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,3 +13,18 @@ pub enum BuildDriverType {
|
||||||
Podman,
|
Podman,
|
||||||
Docker,
|
Docker,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, ValueEnum)]
|
||||||
|
pub enum RunDriverType {
|
||||||
|
Podman,
|
||||||
|
Docker,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RunDriverType> for String {
|
||||||
|
fn from(value: RunDriverType) -> Self {
|
||||||
|
match value {
|
||||||
|
RunDriverType::Podman => "podman".to_string(),
|
||||||
|
RunDriverType::Docker => "docker".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ indicatif.workspace = true
|
||||||
indicatif-log-bridge.workspace = true
|
indicatif-log-bridge.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
once_cell.workspace = true
|
once_cell.workspace = true
|
||||||
|
tempdir.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_yaml.workspace = true
|
serde_yaml.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use std::{
|
||||||
fs::OpenOptions,
|
fs::OpenOptions,
|
||||||
io::{BufRead, BufReader, Result, Write as IoWrite},
|
io::{BufRead, BufReader, Result, Write as IoWrite},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
process::{Command, ExitStatus},
|
process::{Command, ExitStatus, Stdio},
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
thread,
|
thread,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
|
|
@ -199,7 +199,9 @@ impl CommandLogging for Command {
|
||||||
let log_prefix = Arc::new(log_header(short_name));
|
let log_prefix = Arc::new(log_header(short_name));
|
||||||
let (reader, writer) = os_pipe::pipe()?;
|
let (reader, writer) = os_pipe::pipe()?;
|
||||||
|
|
||||||
self.stdout(writer.try_clone()?).stderr(writer);
|
self.stdout(writer.try_clone()?)
|
||||||
|
.stderr(writer)
|
||||||
|
.stdin(Stdio::piped());
|
||||||
|
|
||||||
let progress = Logger::multi_progress()
|
let progress = Logger::multi_progress()
|
||||||
.add(ProgressBar::new_spinner().with_message(format!("{} {name}", message.as_ref())));
|
.add(ProgressBar::new_spinner().with_message(format!("{} {name}", message.as_ref())));
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
use std::{
|
use std::{
|
||||||
process,
|
fs,
|
||||||
|
path::PathBuf,
|
||||||
|
process::{self, Command},
|
||||||
sync::{atomic::AtomicBool, Arc, Mutex},
|
sync::{atomic::AtomicBool, Arc, Mutex},
|
||||||
thread,
|
thread,
|
||||||
};
|
};
|
||||||
|
|
||||||
use log::{error, trace, warn};
|
use log::{debug, error, trace, warn};
|
||||||
use nix::{
|
use nix::{
|
||||||
libc::{SIGABRT, SIGCONT, SIGHUP, SIGTSTP},
|
libc::{SIGABRT, SIGCONT, SIGHUP, SIGTSTP},
|
||||||
sys::signal::{kill, Signal},
|
sys::signal::{kill, Signal},
|
||||||
|
|
@ -20,7 +22,31 @@ use signal_hook::{
|
||||||
|
|
||||||
use crate::logging::Logger;
|
use crate::logging::Logger;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct ContainerId {
|
||||||
|
cid_path: PathBuf,
|
||||||
|
requires_sudo: bool,
|
||||||
|
crt: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContainerId {
|
||||||
|
pub fn new<P, S>(cid_path: P, container_runtime: S, requires_sudo: bool) -> Self
|
||||||
|
where
|
||||||
|
P: Into<PathBuf>,
|
||||||
|
S: Into<String>,
|
||||||
|
{
|
||||||
|
let cid_path = cid_path.into();
|
||||||
|
let crt = container_runtime.into();
|
||||||
|
Self {
|
||||||
|
cid_path,
|
||||||
|
requires_sudo,
|
||||||
|
crt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static PID_LIST: Lazy<Arc<Mutex<Vec<i32>>>> = Lazy::new(|| Arc::new(Mutex::new(vec![])));
|
static PID_LIST: Lazy<Arc<Mutex<Vec<i32>>>> = Lazy::new(|| Arc::new(Mutex::new(vec![])));
|
||||||
|
static CID_LIST: Lazy<Arc<Mutex<Vec<ContainerId>>>> = Lazy::new(|| Arc::new(Mutex::new(vec![])));
|
||||||
|
|
||||||
/// Initialize Ctrl-C handler. This should be done at the start
|
/// Initialize Ctrl-C handler. This should be done at the start
|
||||||
/// of a binary.
|
/// of a binary.
|
||||||
|
|
@ -57,10 +83,36 @@ where
|
||||||
warn!("Received termination signal, cleaning up...");
|
warn!("Received termination signal, cleaning up...");
|
||||||
trace!("{info:#?}");
|
trace!("{info:#?}");
|
||||||
|
|
||||||
Logger::multi_progress().clear().unwrap();
|
Logger::multi_progress()
|
||||||
|
.clear()
|
||||||
|
.expect("Should clear multi_progress");
|
||||||
|
|
||||||
send_signal_processes(termsig);
|
send_signal_processes(termsig);
|
||||||
|
|
||||||
|
let cid_list = CID_LIST.clone();
|
||||||
|
let cid_list = cid_list.lock().expect("Should lock mutex");
|
||||||
|
cid_list.iter().for_each(|cid| {
|
||||||
|
if let Ok(id) = fs::read_to_string(&cid.cid_path) {
|
||||||
|
let id = id.trim();
|
||||||
|
debug!("Killing container {id}");
|
||||||
|
|
||||||
|
let status = if cid.requires_sudo {
|
||||||
|
Command::new("sudo")
|
||||||
|
.arg(&cid.crt)
|
||||||
|
.arg("stop")
|
||||||
|
.arg(id)
|
||||||
|
.status()
|
||||||
|
} else {
|
||||||
|
Command::new(&cid.crt).arg("stop").arg(id).status()
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = status {
|
||||||
|
error!("Failed to kill container {id}: Error {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
drop(cid_list);
|
||||||
|
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
SIGTSTP => {
|
SIGTSTP => {
|
||||||
|
|
@ -86,8 +138,12 @@ where
|
||||||
fn send_signal_processes(sig: i32) {
|
fn send_signal_processes(sig: i32) {
|
||||||
let pid_list = PID_LIST.clone();
|
let pid_list = PID_LIST.clone();
|
||||||
let pid_list = pid_list.lock().expect("Should lock mutex");
|
let pid_list = pid_list.lock().expect("Should lock mutex");
|
||||||
|
|
||||||
pid_list.iter().for_each(|pid| {
|
pid_list.iter().for_each(|pid| {
|
||||||
if let Err(e) = kill(Pid::from_raw(*pid), Signal::try_from(sig).unwrap()) {
|
if let Err(e) = kill(
|
||||||
|
Pid::from_raw(*pid),
|
||||||
|
Signal::try_from(sig).expect("Should be valid signal"),
|
||||||
|
) {
|
||||||
error!("Failed to kill process {pid}: Error {e}");
|
error!("Failed to kill process {pid}: Error {e}");
|
||||||
} else {
|
} else {
|
||||||
trace!("Killed process {pid}");
|
trace!("Killed process {pid}");
|
||||||
|
|
@ -130,3 +186,28 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a cid to the list to kill when the program
|
||||||
|
/// recieves a kill signal.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
/// Will panic if the mutex cannot be locked.
|
||||||
|
pub fn add_cid(cid: &ContainerId) {
|
||||||
|
let mut cid_list = CID_LIST.lock().expect("Should lock cid_list");
|
||||||
|
|
||||||
|
if !cid_list.contains(cid) {
|
||||||
|
cid_list.push(cid.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a cid from the list of pids to kill.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
/// Will panic if the mutex cannot be locked.
|
||||||
|
pub fn remove_cid(cid: &ContainerId) {
|
||||||
|
let mut cid_list = CID_LIST.lock().expect("Should lock cid_list");
|
||||||
|
|
||||||
|
if let Some(index) = cid_list.iter().position(|val| *val == *cid) {
|
||||||
|
cid_list.swap_remove(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue