Skip to content
8 changes: 8 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,14 @@ jobs:
toolchain: ${{ matrix.rust }}
cache: false

# Install the `set_listen_pid` helper.
- name: Install `set_listen_pid`
run: cargo install --path crates/tests --bin set_listen_pid

# Install `dnst` for `dnst keyset`.
- name: Install `dnst`
run: cargo install --locked --bin dnst --git https://github.com/nlnetlabs/dnst dnst

# TODO: Restore a cache of dependencies and 'target'.

# Build and run the test suite.
Expand Down
41 changes: 41 additions & 0 deletions Cargo.lock

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

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ members = [
"crates/cfg",
"crates/policy-file",
"crates/zonedata",

"crates/tests",
]
default-members = [".", "crates/cli"]

Expand Down Expand Up @@ -258,6 +260,7 @@ workspace = true
[dev-dependencies]
assert-json-diff = "2.0"

cascade-tests = { path = "crates/tests" }

# --- Packaging ----------------------------------------------------------------

Expand Down
1 change: 1 addition & 0 deletions crates/api/src/dep.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//! Re-export dependencies for API consumers

pub use domain;
pub use serde;
6 changes: 6 additions & 0 deletions crates/cfg/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ edition.workspace = true
readme = "README.md"


[features]
default = ["args"]
args = ["dep:clap"]


# --- Dependencies -------------------------------------------------------------

[dependencies]
Expand All @@ -28,6 +33,7 @@ features = ["serde1"]

# 'clap' is used for defining and parsing command-line arguments.
[dependencies.clap]
optional = true
workspace = true
default-features = false

Expand Down
11 changes: 8 additions & 3 deletions crates/cfg/src/file/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//! The configuration file.

use std::{fmt, sync::Arc};
use std::{fmt, io, sync::Arc};

use camino::Utf8Path;
use serde::Deserialize;
use serde::{Deserialize, Serialize};

use super::Config;

Expand All @@ -12,7 +12,7 @@ pub mod v1;
//----------- Spec -------------------------------------------------------------

/// A configuration file.
#[derive(Clone, Debug, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", tag = "version")]
pub enum Spec {
/// The version 1 format.
Expand All @@ -36,6 +36,11 @@ impl Spec {
let text = std::fs::read_to_string(path)?;
Ok(toml::from_str(&text)?)
}

/// Save the configuration file, formatting in a compact notation.
pub fn save_compact(&self, path: &Utf8Path) -> io::Result<()> {
std::fs::write(path, toml::to_string(self).unwrap())
}
}

//----------- FileError --------------------------------------------------------
Expand Down
86 changes: 73 additions & 13 deletions crates/cfg/src/file/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use std::{fmt, net::SocketAddr, num::IntErrorKind, str::FromStr};

use camino::Utf8Path;
use serde::Deserialize;
use serde::{Deserialize, Serialize};

use crate::{
Config, DaemonConfig, GroupId, KeyManagerConfig, LoaderConfig, LogLevel, LogTarget,
Expand All @@ -13,7 +13,7 @@ use crate::{
//----------- Spec -------------------------------------------------------------

/// A configuration file.
#[derive(Clone, Debug, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", deny_unknown_fields, default)]
pub struct Spec {
/// The directory storing policy files.
Expand Down Expand Up @@ -135,7 +135,7 @@ impl Spec {
//----------- RemoteControlSpec ----------------------------------------------

/// Remote control configuration for Cascade.
#[derive(Clone, Debug, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", deny_unknown_fields, default)]
pub struct RemoteControlSpec {
/// Where to serve our HTTP API from, e.g. for the Cascade client.
Expand Down Expand Up @@ -175,7 +175,7 @@ impl RemoteControlSpec {
//----------- DaemonSpec -------------------------------------------------------

/// Configuring the Cascade daemon.
#[derive(Clone, Debug, Default, Deserialize)]
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", deny_unknown_fields, default)]
pub struct DaemonSpec {
/// The minimum severity of messages to log.
Expand Down Expand Up @@ -210,7 +210,7 @@ impl DaemonSpec {
//----------- LogLevelSpec -----------------------------------------------------

/// A severity level for logging.
#[derive(Copy, Clone, Debug, Deserialize)]
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum LogLevelSpec {
/// A function or variable was interacted with, for debugging.
Expand Down Expand Up @@ -251,7 +251,7 @@ impl LogLevelSpec {
//----------- LogTargetSpec ----------------------------------------------------

/// A logging target.
#[derive(Clone, Debug, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", deny_unknown_fields, tag = "type")]
pub enum LogTargetSpec {
/// Append logs to a file.
Expand Down Expand Up @@ -324,6 +324,23 @@ impl<'de> Deserialize<'de> for IdentitySpec {
}
}

//--- Serialization

impl fmt::Display for IdentitySpec {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}", self.user, self.group)
}
}

impl Serialize for IdentitySpec {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.to_string().serialize(serializer)
}
}

//--- Conversion

impl IdentitySpec {
Expand Down Expand Up @@ -363,6 +380,17 @@ impl FromStr for UserIdSpec {
}
}

//--- Serialization

impl fmt::Display for UserIdSpec {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Numeric(v) => write!(f, "{v}"),
Self::Named(v) => write!(f, "{v}"),
}
}
}

//--- Conversion

impl UserIdSpec {
Expand Down Expand Up @@ -405,6 +433,17 @@ impl FromStr for GroupIdSpec {
}
}

//--- Serialization

impl fmt::Display for GroupIdSpec {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Numeric(v) => write!(f, "{v}"),
Self::Named(v) => write!(f, "{v}"),
}
}
}

//--- Conversion

impl GroupIdSpec {
Expand All @@ -420,7 +459,7 @@ impl GroupIdSpec {
//----------- LoaderSpec -------------------------------------------------------

/// Configuring how zones are loaded.
#[derive(Clone, Debug, Default, Deserialize)]
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", deny_unknown_fields, default)]
pub struct LoaderSpec {
/// Configuring whether and how loaded zones are reviewed.
Expand All @@ -439,7 +478,7 @@ impl LoaderSpec {
//----------- SignerSpec -------------------------------------------------------

/// Configuring the zone signer.
#[derive(Clone, Debug, Default, Deserialize)]
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", deny_unknown_fields, default)]
pub struct SignerSpec {
/// Configuring whether and how signed zones are reviewed.
Expand All @@ -458,7 +497,7 @@ impl SignerSpec {
//----------- ReviewSpec -------------------------------------------------------

/// Configuring whether and how zones are reviewed.
#[derive(Clone, Debug, Default, Deserialize)]
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", deny_unknown_fields, default)]
pub struct ReviewSpec {
/// Where to serve zones for review.
Expand All @@ -480,7 +519,7 @@ impl ReviewSpec {
//----------- KeyManagerSpec ---------------------------------------------------

/// Configuring DNSSEC key management.
#[derive(Clone, Debug, Default, Deserialize)]
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", deny_unknown_fields, default)]
pub struct KeyManagerSpec {}

Expand All @@ -496,7 +535,7 @@ impl KeyManagerSpec {
//----------- ServerSpec -------------------------------------------------------

/// Configuring how zones are published.
#[derive(Clone, Debug, Default, Deserialize)]
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", deny_unknown_fields, default)]
pub struct ServerSpec {
/// Where to serve zones.
Expand All @@ -518,7 +557,7 @@ impl ServerSpec {
//----------- SocketSpec -------------------------------------------------------

/// Configuration for serving / listening on a network socket.
#[derive(Clone, Debug, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged, expecting = "a URI string or an inline table")]
pub enum SocketSpec {
/// A simple socket specification.
Expand Down Expand Up @@ -553,7 +592,7 @@ pub enum SimpleSocketSpec {
}

/// A complex [`SocketSpec`] as a table.
#[derive(Clone, Debug, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case", deny_unknown_fields, tag = "type")]
pub enum ComplexSocketSpec {
/// Listen exclusively over UDP.
Expand Down Expand Up @@ -612,6 +651,27 @@ impl<'de> Deserialize<'de> for SimpleSocketSpec {
}
}

//--- Serialization

impl fmt::Display for SimpleSocketSpec {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SimpleSocketSpec::UDP { addr } => write!(f, "udp://{addr}"),
SimpleSocketSpec::TCP { addr } => write!(f, "tcp://{addr}"),
SimpleSocketSpec::TCPUDP { addr } => write!(f, "{addr}"),
}
}
}

impl Serialize for SimpleSocketSpec {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.to_string().serialize(serializer)
}
}

//--- Conversion

impl SocketSpec {
Expand Down
Loading
Loading