add a Client image

This commit is contained in:
Jorge Aparicio 2024-02-23 13:21:07 +01:00
parent c0b681e0a3
commit 98cb9ddaae
8 changed files with 85 additions and 58 deletions

View File

@ -1,10 +1,10 @@
use core::str::FromStr;
use std::net::Ipv4Addr;
use crate::container::{Container, Network};
use crate::container::{Container, Image, Network};
use crate::record::{Record, RecordType};
use crate::trust_anchor::TrustAnchor;
use crate::{Error, Implementation, Result, FQDN};
use crate::{Error, Result, FQDN};
pub struct Client {
inner: Container,
@ -13,7 +13,7 @@ pub struct Client {
impl Client {
pub fn new(network: &Network) -> Result<Self> {
Ok(Self {
inner: Container::run(&Implementation::Unbound, network)?,
inner: Container::run(&Image::Client, network)?,
})
}

View File

@ -1,17 +1,17 @@
mod network;
use core::str;
use core::{fmt, str};
use std::net::Ipv4Addr;
use std::process::{self, ChildStdout, ExitStatus};
use std::process::{Command, Stdio};
use std::sync::atomic::AtomicUsize;
use std::sync::{atomic, Arc};
use std::sync::{atomic, Arc, Once};
use std::{env, fs};
use tempfile::{NamedTempFile, TempDir};
pub use crate::container::network::Network;
use crate::{Error, Implementation, Result};
use crate::{Error, Implementation, Repository, Result};
#[derive(Clone)]
pub struct Container {
@ -20,16 +20,72 @@ pub struct Container {
const PACKAGE_NAME: &str = env!("CARGO_PKG_NAME");
#[derive(Clone)]
pub enum Image {
Client,
Hickory(Repository<'static>),
Unbound,
}
impl Image {
fn dockerfile(&self) -> &'static str {
match self {
Self::Unbound => include_str!("docker/unbound.Dockerfile"),
Self::Hickory { .. } => include_str!("docker/hickory.Dockerfile"),
Self::Client => include_str!("docker/client.Dockerfile"),
}
}
fn once(&self) -> &'static Once {
match self {
Self::Client { .. } => {
static CLIENT_ONCE: Once = Once::new();
&CLIENT_ONCE
}
Self::Hickory { .. } => {
static HICKORY_ONCE: Once = Once::new();
&HICKORY_ONCE
}
Self::Unbound { .. } => {
static UNBOUND_ONCE: Once = Once::new();
&UNBOUND_ONCE
}
}
}
}
impl From<Implementation> for Image {
fn from(implementation: Implementation) -> Self {
match implementation {
Implementation::Unbound => Self::Unbound,
Implementation::Hickory(repo) => Self::Hickory(repo),
}
}
}
impl fmt::Display for Image {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::Client => "client",
Self::Hickory { .. } => "hickory",
Self::Unbound => "unbound",
};
f.write_str(s)
}
}
impl Container {
/// Starts the container in a "parked" state
pub fn run(implementation: &Implementation, network: &Network) -> Result<Self> {
pub fn run(image: &Image, network: &Network) -> Result<Self> {
// TODO make this configurable and support hickory & bind
let dockerfile = implementation.dockerfile();
let dockerfile = image.dockerfile();
let docker_build_dir = TempDir::new()?;
let docker_build_dir = docker_build_dir.path();
fs::write(docker_build_dir.join("Dockerfile"), dockerfile)?;
let image_tag = format!("{PACKAGE_NAME}-{implementation}");
let image_tag = format!("{PACKAGE_NAME}-{image}");
let mut command = Command::new("docker");
command
@ -37,13 +93,13 @@ impl Container {
.arg(&image_tag)
.arg(docker_build_dir);
let repo = if let Implementation::Hickory(repo) = implementation {
let repo = if let Image::Hickory(repo) = image {
Some(repo)
} else {
None
};
implementation.once().call_once(|| {
image.once().call_once(|| {
if let Some(repo) = repo {
let mut cp_r = Command::new("git");
cp_r.args([
@ -66,7 +122,7 @@ impl Container {
let mut command = Command::new("docker");
let pid = process::id();
let count = container_count();
let name = format!("{PACKAGE_NAME}-{implementation}-{pid}-{count}");
let name = format!("{PACKAGE_NAME}-{image}-{pid}-{count}");
command
.args([
"run",
@ -334,7 +390,7 @@ mod tests {
#[test]
fn run_works() -> Result<()> {
let network = Network::new()?;
let container = Container::run(&Implementation::Unbound, &network)?;
let container = Container::run(&Image::Client, &network)?;
let output = container.output(&["true"])?;
assert!(output.status.success());
@ -345,7 +401,7 @@ mod tests {
#[test]
fn ipv4_addr_works() -> Result<()> {
let network = Network::new()?;
let container = Container::run(&Implementation::Unbound, &network)?;
let container = Container::run(&Image::Client, &network)?;
let ipv4_addr = container.ipv4_addr();
let output = container.output(&["ping", "-c1", &format!("{ipv4_addr}")])?;
@ -357,7 +413,7 @@ mod tests {
#[test]
fn cp_works() -> Result<()> {
let network = Network::new()?;
let container = Container::run(&Implementation::Unbound, &network)?;
let container = Container::run(&Image::Client, &network)?;
let path = "/tmp/somefile";
let contents = "hello";

View File

@ -113,7 +113,7 @@ fn network_count() -> usize {
#[cfg(test)]
mod tests {
use crate::{container::Container, Implementation};
use crate::container::{Container, Image};
use super::*;
@ -146,7 +146,7 @@ mod tests {
let network = Network::new().expect("Failed to create network");
let network_name = network.name().to_string();
let container =
Container::run(&Implementation::Unbound, &network).expect("Failed to start container");
Container::run(&Image::Client, &network).expect("Failed to start container");
assert!(exists_network(&network_name));
drop(network);

View File

@ -0,0 +1,8 @@
FROM debian:bookworm-slim
# dnsutils = dig & delv
# iputils-ping = ping
RUN apt-get update && \
apt-get install -y \
dnsutils \
iputils-ping

View File

@ -1,12 +1,8 @@
FROM debian:bookworm-slim
# dnsutils = dig & delv
# iputils-ping = ping
# ldns-utils = ldns-{key2ds,keygen,signzone}
RUN apt-get update && \
apt-get install -y \
dnsutils \
iputils-ping \
ldnsutils \
nsd \
tshark \

View File

@ -1,9 +1,7 @@
//! A test framework for all things DNS
use core::fmt;
use std::borrow::Cow;
use std::path::Path;
use std::sync::Once;
use url::Url;
@ -57,45 +55,12 @@ pub fn Repository(input: impl Into<Cow<'static, str>>) -> Repository<'static> {
Repository { inner: input }
}
impl Implementation {
fn dockerfile(&self) -> &'static str {
match self {
Implementation::Unbound => include_str!("docker/unbound.Dockerfile"),
Implementation::Hickory { .. } => include_str!("docker/hickory.Dockerfile"),
}
}
fn once(&self) -> &'static Once {
match self {
Implementation::Unbound => {
static UNBOUND_ONCE: Once = Once::new();
&UNBOUND_ONCE
}
Implementation::Hickory { .. } => {
static HICKORY_ONCE: Once = Once::new();
&HICKORY_ONCE
}
}
}
}
impl Default for Implementation {
fn default() -> Self {
Self::Unbound
}
}
impl fmt::Display for Implementation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Implementation::Unbound => "unbound",
Implementation::Hickory { .. } => "hickory",
};
f.write_str(s)
}
}
pub fn subject() -> Implementation {
if let Ok(subject) = std::env::var("DNS_TEST_SUBJECT") {
if subject == "unbound" {

View File

@ -47,8 +47,9 @@ impl<'a> NameServer<'a, Stopped> {
nameserver: nameserver.clone(),
});
let image = implementation.into();
Ok(Self {
container: Container::run(&Implementation::Unbound, network)?,
container: Container::run(&image, network)?,
zone_file,
state: Stopped,
})

View File

@ -33,7 +33,8 @@ impl Resolver {
"must configure at least one local root server"
);
let container = Container::run(&implementation, network)?;
let image = implementation.clone().into();
let container = Container::run(&image, network)?;
let mut hints = String::new();
for root in roots {