initial Container API
This commit is contained in:
parent
e6691ffc40
commit
3c50ca911a
150
Cargo.lock
generated
150
Cargo.lock
generated
|
@ -2,6 +2,156 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "dnssec-tests"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.153"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"redox_syscall",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||
|
|
|
@ -7,3 +7,4 @@ license = "MIT or Apache 2.0"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
tempfile = "3.9.0"
|
||||
|
|
203
src/lib.rs
203
src/lib.rs
|
@ -1,14 +1,207 @@
|
|||
pub fn add(left: usize, right: usize) -> usize {
|
||||
left + right
|
||||
use core::fmt;
|
||||
use std::process::Output;
|
||||
use std::sync::atomic;
|
||||
use std::{
|
||||
fs,
|
||||
path::Path,
|
||||
process::{Command, ExitStatus, Stdio},
|
||||
sync::atomic::AtomicUsize,
|
||||
};
|
||||
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
pub type Error = Box<dyn std::error::Error>;
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
|
||||
pub struct Container {
|
||||
id: String,
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl Container {
|
||||
/// Starts the container in a "parked" state
|
||||
pub fn run(image: Image) -> Result<Self> {
|
||||
static COUNT: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
let image_tag = format!("dnssec-tests-{image}");
|
||||
|
||||
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
|
||||
let dockerfile_path = manifest_dir
|
||||
.join("docker")
|
||||
.join(format!("{image}.Dockerfile"));
|
||||
let docker_dir_path = manifest_dir.join("docker");
|
||||
dbg!(&image_tag);
|
||||
|
||||
let mut command = Command::new("docker");
|
||||
command
|
||||
.args(&["build", "-t"])
|
||||
.arg(&image_tag)
|
||||
.arg("-f")
|
||||
.arg(dockerfile_path)
|
||||
.arg(docker_dir_path);
|
||||
let status = command.status()?;
|
||||
|
||||
if !status.success() {
|
||||
return Err(format!("`{command:?}` failed").into());
|
||||
}
|
||||
|
||||
// run container based on image
|
||||
// `docker run --rm -it $IMAGE sleep infinity`
|
||||
|
||||
let mut command = Command::new("docker");
|
||||
let container_name = format!("{image}-{}", COUNT.fetch_add(1, atomic::Ordering::Relaxed));
|
||||
command.args(&["run", "--rm", "--detach", "--name", &container_name]);
|
||||
let output = command
|
||||
.arg("-it")
|
||||
.arg(image_tag)
|
||||
.args(["sleep", "infinity"])
|
||||
.output()?;
|
||||
|
||||
if !output.status.success() {
|
||||
return Err(format!("`{command:?}` failed").into());
|
||||
}
|
||||
|
||||
let id = core::str::from_utf8(&output.stdout)?.trim().to_string();
|
||||
dbg!(&id);
|
||||
|
||||
Ok(Self {
|
||||
id,
|
||||
name: container_name,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn cp(&self, path_in_container: &str, file_contents: &str) -> Result<()> {
|
||||
let mut temp_file = NamedTempFile::new()?;
|
||||
fs::write(&mut temp_file, file_contents)?;
|
||||
|
||||
let src_path = temp_file.path().display().to_string();
|
||||
let dest_path = format!("{}:{path_in_container}", self.id);
|
||||
|
||||
let mut command = Command::new("docker");
|
||||
command.args(["cp", &src_path, &dest_path]);
|
||||
|
||||
let status = command.status()?;
|
||||
if !status.success() {
|
||||
return Err(format!("`{command:?}` failed").into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn exec(&self, cmd: &[&str]) -> Result<Output> {
|
||||
let mut command = Command::new("docker");
|
||||
command.args(&["exec", "-t", &self.id]).args(cmd);
|
||||
|
||||
let output = command.output()?;
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
pub fn ip_addr(&self) -> Result<String> {
|
||||
let mut command = Command::new("docker");
|
||||
command
|
||||
.args(&[
|
||||
"inspect",
|
||||
"-f",
|
||||
"{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}",
|
||||
])
|
||||
.arg(&self.id);
|
||||
|
||||
let output = command.output()?;
|
||||
if !output.status.success() {
|
||||
return Err(format!("`{command:?}` failed").into());
|
||||
}
|
||||
|
||||
let ip_addr = core::str::from_utf8(&output.stdout)?.trim().to_string();
|
||||
dbg!(&ip_addr);
|
||||
|
||||
Ok(ip_addr)
|
||||
}
|
||||
}
|
||||
|
||||
// ensure the container gets deleted
|
||||
impl Drop for Container {
|
||||
fn drop(&mut self) {
|
||||
// running this to completion would block the current thread for several seconds so just
|
||||
// fire and forget
|
||||
let _ = Command::new("docker")
|
||||
.args(["rm", "-f", &self.id])
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.status();
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Image {
|
||||
Nsd, // for ROOT, TLD, DOMAIN
|
||||
Unbound,
|
||||
Client,
|
||||
}
|
||||
|
||||
impl fmt::Display for Image {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let name = match self {
|
||||
Image::Nsd => "nsd",
|
||||
Image::Unbound => "unbound",
|
||||
Image::Client => "client",
|
||||
};
|
||||
f.write_str(name)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = add(2, 2);
|
||||
assert_eq!(result, 4);
|
||||
fn run_works() -> Result<()> {
|
||||
let container = Container::run(Image::Client)?;
|
||||
|
||||
let output = container.exec(&["true"])?;
|
||||
assert!(output.status.success());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ip_addr_works() -> Result<()> {
|
||||
let container = Container::run(Image::Client)?;
|
||||
|
||||
let ip_addr = container.ip_addr()?;
|
||||
assert!(ip_addr.parse::<Ipv4Addr>().is_ok());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cp_works() -> Result<()> {
|
||||
let container = Container::run(Image::Client)?;
|
||||
|
||||
let path = "/tmp/somefile";
|
||||
let contents = "hello";
|
||||
container.cp(path, contents)?;
|
||||
|
||||
let output = container.exec(&["cat", path])?;
|
||||
dbg!(&output);
|
||||
|
||||
assert!(output.status.success());
|
||||
|
||||
assert_eq!(contents, core::str::from_utf8(&output.stdout)?);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[ignore = "TODO"]
|
||||
#[test]
|
||||
fn tld_setup() -> Result<()> {
|
||||
let container = Container::run(Image::Nsd)?;
|
||||
|
||||
container.cp("/etc/nsd/zones/main.zone", "TODO")?;
|
||||
|
||||
container.exec(&["nsd", "-d"])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user