mirror of
https://github.com/SeriousBug/gandi-live-dns-rust
synced 2024-11-01 06:37:24 -05:00
Compare commits
No commits in common. "35d60f0b294381a9dba19ee24b39bb3a14769985" and "84bef554b09298a8cf8fa40fb398c181319adc45" have entirely different histories.
35d60f0b29
...
84bef554b0
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
|
@ -2,7 +2,7 @@ name: test
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- "main"
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- "*"
|
- "*"
|
||||||
|
|
516
Cargo.lock
generated
516
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
17
Cargo.toml
17
Cargo.toml
|
@ -1,12 +1,8 @@
|
||||||
[package]
|
[package]
|
||||||
name = "gandi-live-dns"
|
name = "gandi-live-dns"
|
||||||
description = "Automatically updates your IP address in Gandi's Live DNS. Makes it possible to use Gandi as a dynamic DNS system."
|
version = "1.4.0"
|
||||||
version = "1.5.0"
|
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["Kaan Barmore-Genç <kaan@bgenc.net>"]
|
authors = ["Kaan Barmore-Genç <kaan@bgenc.net>"]
|
||||||
license = "MIT"
|
|
||||||
readme = "Readme.md"
|
|
||||||
repository = "https://github.com/SeriousBug/gandi-live-dns-rust"
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
strip = "symbols"
|
strip = "symbols"
|
||||||
|
@ -21,7 +17,7 @@ toml = "0.5"
|
||||||
json = "0.12"
|
json = "0.12"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
directories = "4.0"
|
directories = "4.0"
|
||||||
clap = { version = "4.0", features = [
|
clap = { version = "3.2", features = [
|
||||||
"derive",
|
"derive",
|
||||||
"cargo",
|
"cargo",
|
||||||
"unicode",
|
"unicode",
|
||||||
|
@ -30,14 +26,11 @@ clap = { version = "4.0", features = [
|
||||||
tokio = { version = "1.20", features = ["full"] }
|
tokio = { version = "1.20", features = ["full"] }
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
governor = "0.5"
|
governor = "0.4"
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
die-exit-2 = "0.4"
|
# TODO: Relies on a yet-unreleased interface. Switch to an actual crate release once available
|
||||||
|
die-exit = { git = "https://github.com/Xavientois/die.git", rev = "31d3801f4e21654b0b28430987b1e21fc7728676" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
httpmock = "0.6"
|
httpmock = "0.6"
|
||||||
regex = "1.6"
|
regex = "1.6"
|
||||||
|
|
||||||
[dev-dependencies.die-exit-2]
|
|
||||||
version = "0.4"
|
|
||||||
features = ["test"]
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
## gandi-live-dns-rust
|
## gandi-live-dns-rust
|
||||||
|
|
||||||
[![tests](https://img.shields.io/github/workflow/status/SeriousBug/gandi-live-dns-rust/test?label=tests)](https://github.com/SeriousBug/gandi-live-dns-rust/actions/workflows/test.yml) [![Test coverage report](https://img.shields.io/codecov/c/github/SeriousBug/gandi-live-dns-rust)](https://codecov.io/gh/SeriousBug/gandi-live-dns-rust) [![lint checks](https://img.shields.io/github/workflow/status/SeriousBug/gandi-live-dns-rust/lint%20checks?label=lints)](https://github.com/SeriousBug/gandi-live-dns-rust/actions/workflows/lint.yml) [![Releases](https://img.shields.io/github/v/release/SeriousBug/gandi-live-dns-rust?include_prereleases)](https://github.com/SeriousBug/gandi-live-dns-rust/releases) [![Docker Image Size](https://img.shields.io/docker/image-size/seriousbug/gandi-live-dns-rust)](https://hub.docker.com/r/seriousbug/gandi-live-dns-rust) [![MIT license](https://img.shields.io/github/license/SeriousBug/gandi-live-dns-rust)](https://github.com/SeriousBug/gandi-live-dns-rust/blob/master/LICENSE.txt)
|
[![tests](https://img.shields.io/github/workflow/status/SeriousBug/gandi-live-dns-rust/test?label=tests)](https://github.com/SeriousBug/gandi-live-dns-rust/actions/workflows/test.yml) [![lint checks](https://img.shields.io/github/workflow/status/SeriousBug/gandi-live-dns-rust/lint%20checks?label=lints)](https://github.com/SeriousBug/gandi-live-dns-rust/actions/workflows/lint.yml) [![Releases](https://img.shields.io/github/v/release/SeriousBug/gandi-live-dns-rust?include_prereleases)](https://github.com/SeriousBug/gandi-live-dns-rust/releases) [![Docker Image Size](https://img.shields.io/docker/image-size/seriousbug/gandi-live-dns-rust)](https://hub.docker.com/r/seriousbug/gandi-live-dns-rust) [![MIT license](https://img.shields.io/github/license/SeriousBug/gandi-live-dns-rust)](https://github.com/SeriousBug/gandi-live-dns-rust/blob/master/LICENSE.txt)
|
||||||
|
|
||||||
A program that can set the IP addresses for configured DNS entries in
|
A program that can set the IP addresses for configured DNS entries in
|
||||||
[Gandi](https://gandi.net)'s domain configuration. Thanks to Gandi's
|
[Gandi](https://gandi.net)'s domain configuration. Thanks to Gandi's
|
||||||
|
@ -59,12 +59,6 @@ arm64, armv6, and armv7 platforms. Follow the steps below to use these images.
|
||||||
> has a full path to the config file (`$(pwd)/gandi.toml` part). Otherwise
|
> has a full path to the config file (`$(pwd)/gandi.toml` part). Otherwise
|
||||||
> Docker will create a directory.
|
> Docker will create a directory.
|
||||||
|
|
||||||
### From source
|
|
||||||
|
|
||||||
This package is also published on `crates.io` as
|
|
||||||
[gandi-live-dns](https://crates.io/crates/gandi-live-dns). If you would like to
|
|
||||||
build it from source and you have a working rust install, you can use `cargo install gandi-live-dns` to build and install it.
|
|
||||||
|
|
||||||
## Automation
|
## Automation
|
||||||
|
|
||||||
The `Packaging` folder contains a Systemd service and timer, which you can use
|
The `Packaging` folder contains a Systemd service and timer, which you can use
|
||||||
|
|
|
@ -44,24 +44,25 @@ declare -A DOCKER_TARGETS=(
|
||||||
)
|
)
|
||||||
|
|
||||||
# Get the version number
|
# Get the version number
|
||||||
VERSION=$(sed -nr 's/^version *= *"([0-9.]+)"/\1/p' Cargo.toml | head --lines=1)
|
VERSION=$(sed -nr 's/^version *= *"([0-9.]+)"/\1/p' Cargo.toml)
|
||||||
|
|
||||||
# Make the builds
|
# Make the builds
|
||||||
for target in "${!TARGETS[@]}"; do
|
for target in "${!TARGETS[@]}"; do
|
||||||
echo Building "${target}"
|
echo Building "${target}"
|
||||||
cross build -j $(($(nproc) / 2)) --release --target "${target}"
|
cross build -j $(($(nproc) / 2)) --release --target "${target}"
|
||||||
if [[ "${target}" =~ .*"windows".* ]]; then
|
if [[ "${target}" =~ .*"windows".* ]] ; then
|
||||||
zip -j "gandi-live-dns.${VERSION}.${TARGETS[${target}]}.zip" target/"${target}"/release/gandi-live-dns.exe 1>/dev/null
|
zip -j "gandi-live-dns.${VERSION}.${TARGETS[${target}]}.zip" target/"${target}"/release/gandi-live-dns.exe 1>/dev/null
|
||||||
else
|
else
|
||||||
tar -acf "gandi-live-dns.${VERSION}.${TARGETS[${target}]}.tar.xz" -C "target/${target}/release/" "gandi-live-dns"
|
tar -acf "gandi-live-dns.${VERSION}.${TARGETS[${target}]}.tar.xz" -C "target/${target}/release/" "gandi-live-dns"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
if [[ "$#" -ge 2 && "$1" = "--no-docker" ]]; then
|
if [[ "$#" -ge 2 && "$1" = "--no-docker" ]] ; then
|
||||||
echo "Exiting without releasing to docker"
|
echo "Exiting without releasing to docker"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
# Copy files into place so Docker can get them easily
|
# Copy files into place so Docker can get them easily
|
||||||
cd Docker
|
cd Docker
|
||||||
echo Building Docker images
|
echo Building Docker images
|
||||||
|
|
119
src/config.rs
119
src/config.rs
|
@ -5,15 +5,10 @@ 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,
|
||||||
#[serde(default = "default_types")]
|
types: Option<Vec<String>>,
|
||||||
types: Vec<String>,
|
|
||||||
fqdn: Option<String>,
|
fqdn: Option<String>,
|
||||||
ttl: Option<u32>,
|
ttl: Option<u32>,
|
||||||
}
|
}
|
||||||
|
@ -59,7 +54,11 @@ impl Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn types<'e>(entry: &'e Entry) -> Vec<&'e str> {
|
pub fn types<'e>(entry: &'e Entry) -> Vec<&'e str> {
|
||||||
entry.types.iter().map(|t| t.as_str()).collect()
|
entry
|
||||||
|
.types
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|ts| Some(ts.iter().map(|t| t.as_str()).collect()))
|
||||||
|
.unwrap_or_else(|| DEFAULT_TYPES.to_vec())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +68,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> {
|
||||||
let mut config = match &opts.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")
|
||||||
|
@ -86,23 +85,7 @@ 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<()> {
|
||||||
|
@ -134,9 +117,6 @@ fqdn = "example.com"
|
||||||
api_key = "xxx"
|
api_key = "xxx"
|
||||||
ttl = 300
|
ttl = 300
|
||||||
|
|
||||||
[[entry]]
|
|
||||||
name = "www"
|
|
||||||
|
|
||||||
[[entry]]
|
[[entry]]
|
||||||
name = "@"
|
name = "@"
|
||||||
"#,
|
"#,
|
||||||
|
@ -145,18 +125,14 @@ 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(), 2);
|
assert_eq!(conf.entry.len(), 1);
|
||||||
assert_eq!(conf.entry[0].name, "www");
|
assert_eq!(conf.entry[0].name, "@");
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
@ -185,7 +161,6 @@ 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");
|
||||||
|
|
||||||
|
@ -197,80 +172,4 @@ 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()]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ mod config;
|
||||||
mod gandi;
|
mod gandi;
|
||||||
mod ip_source;
|
mod ip_source;
|
||||||
mod opts;
|
mod opts;
|
||||||
use die_exit_2::*;
|
use die_exit::*;
|
||||||
use governor;
|
use governor;
|
||||||
|
|
||||||
/// 30 requests per minute, see https://api.gandi.net/docs/reference/
|
/// 30 requests per minute, see https://api.gandi.net/docs/reference/
|
||||||
|
@ -179,7 +179,6 @@ 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)
|
||||||
|
|
12
src/opts.rs
12
src/opts.rs
|
@ -1,20 +1,10 @@
|
||||||
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, Default)]
|
#[derive(Parser, Debug)]
|
||||||
#[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,
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue