Add CLI option to skip ipv4 or ipv6 (#58)

closes #7
This commit is contained in:
Kaan Barmore-Genç 2022-11-13 15:36:21 -05:00 committed by GitHub
parent 84bef554b0
commit 5755aedc2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 122 additions and 10 deletions

View File

@ -5,10 +5,15 @@ use serde::Deserialize;
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
fn default_types() -> Vec<String> {
DEFAULT_TYPES.iter().map(|v| v.to_string()).collect()
}
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct Entry { pub struct Entry {
pub name: String, pub name: String,
types: Option<Vec<String>>, #[serde(default = "default_types")]
types: Vec<String>,
fqdn: Option<String>, fqdn: Option<String>,
ttl: Option<u32>, ttl: Option<u32>,
} }
@ -54,11 +59,7 @@ impl Config {
} }
pub fn types<'e>(entry: &'e Entry) -> Vec<&'e str> { pub fn types<'e>(entry: &'e Entry) -> Vec<&'e str> {
entry entry.types.iter().map(|t| t.as_str()).collect()
.types
.as_ref()
.and_then(|ts| Some(ts.iter().map(|t| t.as_str()).collect()))
.unwrap_or_else(|| DEFAULT_TYPES.to_vec())
} }
} }
@ -68,7 +69,7 @@ fn load_config_from<P: std::convert::AsRef<std::path::Path>>(path: P) -> anyhow:
} }
pub fn load_config(opts: &opts::Opts) -> anyhow::Result<Config> { pub fn load_config(opts: &opts::Opts) -> anyhow::Result<Config> {
match &opts.config { let mut config = match &opts.config {
Some(config_path) => load_config_from(&config_path), Some(config_path) => load_config_from(&config_path),
None => { None => {
let confpath = ProjectDirs::from("me", "kaangenc", "gandi-dynamic-dns") let confpath = ProjectDirs::from("me", "kaangenc", "gandi-dynamic-dns")
@ -85,7 +86,23 @@ pub fn load_config(opts: &opts::Opts) -> anyhow::Result<Config> {
load_config_from(path) load_config_from(path)
}) })
} }
}?;
// Filter out any types skipped in CLI opts
if opts.skip_ipv4 || opts.skip_ipv6 {
config.entry = config
.entry
.into_iter()
.map(|mut entry| {
entry.types = entry
.types
.into_iter()
.filter(|v| (v == "A" && !opts.skip_ipv4) || (v == "AAAA" && !opts.skip_ipv6))
.collect();
entry
})
.collect();
} }
Ok(config)
} }
pub fn validate_config(config: &Config) -> anyhow::Result<()> { pub fn validate_config(config: &Config) -> anyhow::Result<()> {
@ -117,6 +134,9 @@ fqdn = "example.com"
api_key = "xxx" api_key = "xxx"
ttl = 300 ttl = 300
[[entry]]
name = "www"
[[entry]] [[entry]]
name = "@" name = "@"
"#, "#,
@ -125,14 +145,18 @@ name = "@"
let opts = Opts { let opts = Opts {
config: Some(temp.to_string_lossy().to_string()), config: Some(temp.to_string_lossy().to_string()),
..Opts::default()
}; };
let conf = load_config(&opts).expect("Failed to load config file"); let conf = load_config(&opts).expect("Failed to load config file");
assert_eq!(conf.fqdn, "example.com"); assert_eq!(conf.fqdn, "example.com");
assert_eq!(conf.api_key, "xxx"); assert_eq!(conf.api_key, "xxx");
assert_eq!(conf.ttl, 300); assert_eq!(conf.ttl, 300);
assert_eq!(conf.entry.len(), 1); assert_eq!(conf.entry.len(), 2);
assert_eq!(conf.entry[0].name, "@"); assert_eq!(conf.entry[0].name, "www");
assert_eq!(conf.entry[0].types, vec!["A".to_string()]);
assert_eq!(conf.entry[1].name, "@");
assert_eq!(conf.entry[1].types, vec!["A".to_string()]);
// default // default
assert_eq!(conf.ip_source, IPSourceName::Ipify); assert_eq!(conf.ip_source, IPSourceName::Ipify);
} }
@ -161,6 +185,7 @@ name = "@"
let opts = Opts { let opts = Opts {
config: Some(temp.to_string_lossy().to_string()), config: Some(temp.to_string_lossy().to_string()),
..Opts::default()
}; };
let conf = load_config(&opts).expect("Failed to load config file"); let conf = load_config(&opts).expect("Failed to load config file");
@ -172,4 +197,80 @@ name = "@"
assert_eq!(conf.entry[1].name, "@"); assert_eq!(conf.entry[1].name, "@");
assert_eq!(conf.ip_source, IPSourceName::Icanhazip); assert_eq!(conf.ip_source, IPSourceName::Icanhazip);
} }
#[test]
fn load_config_skip_ipv4_with_opts() {
let mut temp = temp_dir().join("gandi-live-dns-test");
fs::create_dir_all(&temp).expect("Failed to create test dir");
temp.push("test-3.toml");
fs::write(
&temp,
r#"
fqdn = "example.com"
api_key = "yyy"
[[entry]]
name = "www"
types = ["A", "AAAA"]
[[entry]]
name = "@"
types = ["A", "AAAA"]
"#,
)
.expect("Failed to write test config file");
let opts = Opts {
config: Some(temp.to_string_lossy().to_string()),
skip_ipv4: true,
..Opts::default()
};
let conf = load_config(&opts).expect("Failed to load config file");
assert_eq!(conf.fqdn, "example.com");
assert_eq!(conf.api_key, "yyy");
assert_eq!(conf.entry.len(), 2);
assert_eq!(conf.entry[0].name, "www");
assert_eq!(conf.entry[0].types, vec!["AAAA".to_string()]);
assert_eq!(conf.entry[1].name, "@");
assert_eq!(conf.entry[1].types, vec!["AAAA".to_string()]);
}
#[test]
fn load_config_skip_ipv6_with_opts() {
let mut temp = temp_dir().join("gandi-live-dns-test");
fs::create_dir_all(&temp).expect("Failed to create test dir");
temp.push("test-4.toml");
fs::write(
&temp,
r#"
fqdn = "example.com"
api_key = "yyy"
[[entry]]
name = "www"
types = ["A", "AAAA"]
[[entry]]
name = "@"
types = ["A", "AAAA"]
"#,
)
.expect("Failed to write test config file");
let opts = Opts {
config: Some(temp.to_string_lossy().to_string()),
skip_ipv6: true,
..Opts::default()
};
let conf = load_config(&opts).expect("Failed to load config file");
assert_eq!(conf.fqdn, "example.com");
assert_eq!(conf.api_key, "yyy");
assert_eq!(conf.entry.len(), 2);
assert_eq!(conf.entry[0].name, "www");
assert_eq!(conf.entry[0].types, vec!["A".to_string()]);
assert_eq!(conf.entry[1].name, "@");
assert_eq!(conf.entry[1].types, vec!["A".to_string()]);
}
} }

View File

@ -179,6 +179,7 @@ mod tests {
let opts = Opts { let opts = Opts {
config: Some(temp.to_string_lossy().to_string()), config: Some(temp.to_string_lossy().to_string()),
..Opts::default()
}; };
let conf = config::load_config(&opts).expect("Failed to load config"); let conf = config::load_config(&opts).expect("Failed to load config");
run::<IPSourceMock>(server.base_url().as_str(), conf) run::<IPSourceMock>(server.base_url().as_str(), conf)

View File

@ -1,10 +1,20 @@
use clap::Parser; use clap::Parser;
/// A tool to automatically update DNS entries on Gandi, using it as a dynamic DNS system. /// A tool to automatically update DNS entries on Gandi, using it as a dynamic DNS system.
#[derive(Parser, Debug)] #[derive(Parser, Debug, Default)]
#[clap(author, version, about, long_about = None, name = "gandi-live-dns")] #[clap(author, version, about, long_about = None, name = "gandi-live-dns")]
pub struct Opts { pub struct Opts {
/// The path to the configuration file. /// The path to the configuration file.
#[clap(long)] #[clap(long)]
pub config: Option<String>, pub config: Option<String>,
/// Skip IPv4 updates.
///
/// If enabled, any IPv4 (A) records in the configuration file are ignored.
#[clap(action, long)]
pub skip_ipv4: bool,
/// Skip IPv4 updates.
///
/// If enabled, any IPv6 (AAAA) records in the configuration file are ignored.
#[clap(action, long)]
pub skip_ipv6: bool,
} }