Skip to content
This repository has been archived by the owner on Jun 7, 2024. It is now read-only.

Commit

Permalink
Merge pull request #14 from japaric/ja-hickory-source
Browse files Browse the repository at this point in the history
support building hickory from a local/remote git source
  • Loading branch information
japaric authored Feb 20, 2024
2 parents 3707bdf + 709a840 commit 9d3d405
Show file tree
Hide file tree
Showing 10 changed files with 183 additions and 30 deletions.
11 changes: 9 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ on:
branches: [main]
merge_group:

env:
HICKORY_REV: a3669bd80f3f7b97f0c301c15f1cba6368d97b63
DNS_TEST_VERBOSE_DOCKER_BUILD: 1

jobs:
ci:
name: Continuous Integration
Expand All @@ -27,12 +31,15 @@ jobs:
run: cargo test -p conformance-tests -- --include-ignored

- name: Run tests against hickory
run: DNS_TEST_SUBJECT=hickory cargo test -p conformance-tests
run: |
git clone https://github.com/hickory-dns/hickory-dns /tmp/hickory
( cd /tmp/hickory && git reset --hard ${{ env.HICKORY_REV }} )
DNS_TEST_SUBJECT="hickory /tmp/hickory" cargo test -p conformance-tests
- name: Check that ignored tests fail with hickory
run: |
tmpfile="$(mktemp)"
DNS_TEST_SUBJECT=hickory cargo test -p conformance-tests -- --ignored | tee "$tmpfile"
DNS_TEST_SUBJECT="hickory /tmp/hickory" cargo test -p conformance-tests -- --ignored | tee "$tmpfile"
grep 'test result: FAILED. 0 passed' "$tmpfile" || ( echo "expected ALL tests to fail but at least one passed; the passing tests must be un-#[ignore]-d" && exit 1 )
- name: Check that code is formatted
Expand Down
67 changes: 67 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/dns-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ serde = { version = "1.0.196", features = ["derive"] }
serde_json = "1.0.113"
serde_with = "3.6.1"
tempfile = "3.9.0"
url = "2.5.0"

[lib]
doctest = false
2 changes: 1 addition & 1 deletion packages/dns-test/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(&Implementation::Unbound, network)?,
})
}

Expand Down
59 changes: 47 additions & 12 deletions packages/dns-test/src/container.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
mod network;

use core::str;
use std::fs;
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::{env, fs};

use tempfile::{NamedTempFile, TempDir};

Expand All @@ -22,7 +22,7 @@ const PACKAGE_NAME: &str = env!("CARGO_PKG_NAME");

impl Container {
/// Starts the container in a "parked" state
pub fn run(implementation: Implementation, network: &Network) -> Result<Self> {
pub fn run(implementation: &Implementation, network: &Network) -> Result<Self> {
// TODO make this configurable and support hickory & bind
let dockerfile = implementation.dockerfile();
let docker_build_dir = TempDir::new()?;
Expand All @@ -37,14 +37,30 @@ impl Container {
.arg(&image_tag)
.arg(docker_build_dir);

let repo = if let Implementation::Hickory(repo) = implementation {
Some(repo)
} else {
None
};

implementation.once().call_once(|| {
let output = command.output().unwrap();
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
output.status.success(),
"--- STDOUT ---\n{stdout}\n--- STDERR ---\n{stderr}"
);
if let Some(repo) = repo {
let mut cp_r = Command::new("git");
cp_r.args([
"clone",
"--depth",
"1",
repo.as_str(),
&docker_build_dir.join("src").display().to_string(),
]);

exec_or_panic(&mut cp_r, false);
}

fs::write(docker_build_dir.join(".dockerignore"), "src/.git")
.expect("could not create .dockerignore file");

exec_or_panic(&mut command, verbose_docker_build());
});

let mut command = Command::new("docker");
Expand Down Expand Up @@ -171,6 +187,25 @@ impl Container {
}
}

fn verbose_docker_build() -> bool {
env::var("DNS_TEST_VERBOSE_DOCKER_BUILD").as_deref() == Ok("1")
}

fn exec_or_panic(command: &mut Command, verbose: bool) {
if verbose {
let status = command.status().unwrap();
assert!(status.success());
} else {
let output = command.output().unwrap();
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
output.status.success(),
"--- STDOUT ---\n{stdout}\n--- STDERR ---\n{stderr}"
);
}
}

fn container_count() -> usize {
static COUNT: AtomicUsize = AtomicUsize::new(0);

Expand Down Expand Up @@ -299,7 +334,7 @@ mod tests {
#[test]
fn run_works() -> Result<()> {
let network = Network::new()?;
let container = Container::run(Implementation::Unbound, &network)?;
let container = Container::run(&Implementation::Unbound, &network)?;

let output = container.output(&["true"])?;
assert!(output.status.success());
Expand All @@ -310,7 +345,7 @@ mod tests {
#[test]
fn ipv4_addr_works() -> Result<()> {
let network = Network::new()?;
let container = Container::run(Implementation::Unbound, &network)?;
let container = Container::run(&Implementation::Unbound, &network)?;
let ipv4_addr = container.ipv4_addr();

let output = container.output(&["ping", "-c1", &format!("{ipv4_addr}")])?;
Expand All @@ -322,7 +357,7 @@ mod tests {
#[test]
fn cp_works() -> Result<()> {
let network = Network::new()?;
let container = Container::run(Implementation::Unbound, &network)?;
let container = Container::run(&Implementation::Unbound, &network)?;

let path = "/tmp/somefile";
let contents = "hello";
Expand Down
2 changes: 1 addition & 1 deletion packages/dns-test/src/container/network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(&Implementation::Unbound, &network).expect("Failed to start container");

assert!(exists_network(&network_name));
drop(network);
Expand Down
6 changes: 5 additions & 1 deletion packages/dns-test/src/docker/hickory.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,9 @@ RUN apt-get update && \
apt-get install -y \
tshark

RUN cargo install hickory-dns --version 0.24.0 --features recursor --debug
# `dns-test` will invoke `docker build` from a temporary directory that contains
# a clone of the hickory repository. `./src` here refers to that clone; not to
# any directory inside the `dns-test` repository
COPY ./src /usr/src/hickory
RUN cargo install --path /usr/src/hickory/bin --features recursor --debug
env RUST_LOG=debug
57 changes: 48 additions & 9 deletions packages/dns-test/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
//! 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;

pub use crate::container::Network;
pub use crate::fqdn::FQDN;
pub use crate::resolver::Resolver;
Expand All @@ -21,17 +25,43 @@ mod trust_anchor;
pub mod tshark;
pub mod zone_file;

#[derive(Clone, Copy)]
#[derive(Clone)]
pub enum Implementation {
Unbound,
Hickory,
Hickory(Repository<'static>),
}

#[derive(Clone)]
pub struct Repository<'a> {
inner: Cow<'a, str>,
}

impl Repository<'_> {
fn as_str(&self) -> &str {
&self.inner
}
}

/// checks that `input` looks like a valid repository which can be either local or remote
///
/// # Panics
///
/// this function panics if `input` is not a local `Path` that exists or a well-formed URL
#[allow(non_snake_case)]
pub fn Repository(input: impl Into<Cow<'static, str>>) -> Repository<'static> {
let input = input.into();
assert!(
Path::new(&*input).exists() || Url::parse(&input).is_ok(),
"{input} is not a valid repository"
);
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"),
Implementation::Hickory { .. } => include_str!("docker/hickory.Dockerfile"),
}
}

Expand All @@ -41,7 +71,8 @@ impl Implementation {
static UNBOUND_ONCE: Once = Once::new();
&UNBOUND_ONCE
}
Implementation::Hickory => {

Implementation::Hickory { .. } => {
static HICKORY_ONCE: Once = Once::new();
&HICKORY_ONCE
}
Expand All @@ -59,18 +90,26 @@ impl fmt::Display for Implementation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Implementation::Unbound => "unbound",
Implementation::Hickory => "hickory",
Implementation::Hickory { .. } => "hickory",
};
f.write_str(s)
}
}

pub fn subject() -> Implementation {
if let Ok(subject) = std::env::var("DNS_TEST_SUBJECT") {
match subject.as_str() {
"hickory" => Implementation::Hickory,
"unbound" => Implementation::Unbound,
_ => panic!("unknown implementation: {subject}"),
if subject == "unbound" {
return Implementation::Unbound;
}

if subject.starts_with("hickory") {
if let Some(url) = subject.strip_prefix("hickory ") {
Implementation::Hickory(Repository(url.to_string()))
} else {
panic!("the syntax of DNS_TEST_SUBJECT is 'hickory $URL', e.g. 'hickory /tmp/hickory' or 'hickory https://github.com/owner/repo'")
}
} else {
panic!("unknown implementation: {subject}")
}
} else {
Implementation::default()
Expand Down
2 changes: 1 addition & 1 deletion packages/dns-test/src/name_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ impl<'a> NameServer<'a, Stopped> {
});

Ok(Self {
container: Container::run(Implementation::Unbound, network)?,
container: Container::run(&Implementation::Unbound, network)?,
zone_file,
state: Stopped,
})
Expand Down
Loading

0 comments on commit 9d3d405

Please sign in to comment.