almost done!

This commit is contained in:
Kaan Genc 2021-12-24 20:20:31 -05:00
parent cab2ecbdf1
commit 093b99d69d
6 changed files with 176 additions and 28 deletions

View file

@ -1,6 +1,7 @@
{ {
"cSpell.words": [ "cSpell.words": [
"gandi", "gandi",
"rrset",
"structopt" "structopt"
] ]
} }

98
Cargo.lock generated
View file

@ -156,42 +156,92 @@ dependencies = [
] ]
[[package]] [[package]]
name = "futures-channel" name = "futures"
version = "0.3.18" version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fc8cd39e3dbf865f7340dce6a2d401d24fd37c6fe6c4f0ee0de8bfca2252d27" checksum = "28560757fe2bb34e79f907794bb6b22ae8b0e5c669b638a1132f2592b19035b4"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3dda0b6588335f360afc675d0564c17a77a2bda81ca178a4b6081bd86c7f0b"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-sink",
] ]
[[package]] [[package]]
name = "futures-core" name = "futures-core"
version = "0.3.18" version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "629316e42fe7c2a0b9a65b47d159ceaa5453ab14e8f0a3c5eedbb8cd55b4a445" checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7"
[[package]] [[package]]
name = "futures-sink" name = "futures-executor"
version = "0.3.18" version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "996c6442437b62d21a32cd9906f9c41e7dc1e19a9579843fad948696769305af" checksum = "29d6d2ff5bb10fb95c85b8ce46538a2e5f5e7fdc755623a7d4529ab8a4ed9d2a"
[[package]]
name = "futures-task"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dabf1872aaab32c886832f2276d2f5399887e2bd613698a02359e4ea83f8de12"
[[package]]
name = "futures-util"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d22213122356472061ac0f1ab2cee28d2bac8491410fd68c2af53d1cedb83e"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-task", "futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f9d34af5a1aac6fb380f735fe510746c38067c5bf16c7fd250280503c971b2"
[[package]]
name = "futures-macro"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbd947adfffb0efc70599b3ddcf7b5597bb5fa9e245eb99f62b3a5f7bb8bd3c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3055baccb68d74ff6480350f8d6eb8fcfa3aa11bdc1a1ae3afdd0514617d508"
[[package]]
name = "futures-task"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72"
[[package]]
name = "futures-util"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite", "pin-project-lite",
"pin-utils", "pin-utils",
"slab",
] ]
[[package]] [[package]]
@ -199,6 +249,8 @@ name = "gandi-rust-dns-updater"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"directories", "directories",
"futures",
"json",
"reqwest", "reqwest",
"serde", "serde",
"structopt", "structopt",
@ -382,6 +434,12 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "json"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd"
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"

View file

@ -9,7 +9,9 @@ edition = "2021"
reqwest = { version = "0.11.7", features = ["json"] } reqwest = { version = "0.11.7", features = ["json"] }
toml = "0.5.8" toml = "0.5.8"
json = "0.12.4"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
directories = "4.0.1" directories = "4.0.1"
structopt = "0.3.25" structopt = "0.3.25"
tokio = { version = "1.14.0", features = ["full"] } tokio = { version = "1.14.0", features = ["full"] }
futures = "0.3.17"

View file

@ -1,2 +1,26 @@
fqdn = "my-website.example.com" # Set the domain that you want to configure.
fqdn = "example.com"
# The API key to use. To get your API key, log in to Gandi, click on your user
# on the top right and click settings. Then select the "Security" tab, and
# generate an API key under "Production API Key". Paste the key here.
#
# Make sure this file config file is not readable by your user! Anyone who can
# read this key can change your domain configuration, transfer your domains, or
# otherwise do things that will cause you to be charged money.
api_key = "xxxxxxxxxxxxxxxxxxxxxxxx" api_key = "xxxxxxxxxxxxxxxxxxxxxxxx"
# For every domain or subdomain you want to update, create an entry below.
[[entry]]
# Updates A (IPv4) entry for example.com
name = "@"
[[entry]]
# Updates both A (IPv4) and AAA (IPv6) entries for other.example.com
name = "other"
types = ["A", "AAA"]
[[entry]]
# Updates A for some.example.net
name = "some"
fqdn = "example.net" # Overrides top level setting

View file

@ -5,10 +5,32 @@ use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
use crate::opts; use crate::opts;
#[derive(Deserialize, Debug)]
pub struct Entry {
pub name: String,
types: Option<Vec<String>>,
fqdn: Option<String>,
}
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct Config { pub struct Config {
pub fqdn: String, fqdn: String,
pub api_key: String, pub api_key: String,
pub entry: Vec<Entry>,
}
const DEFAULT_TYPES: Vec<&str> = vec!["A"];
impl Config {
pub fn fqdn<'c>(entry: &'c Entry, config: &'c Config) -> &'c str {
return entry.fqdn.as_ref().unwrap_or(&config.fqdn).as_str();
}
pub fn types<'e>(entry: &'e Entry) -> Vec<&'e str> {
return entry.types.as_ref().and_then(
|ts| Some(ts.iter().map(|t| t.as_str()).collect())
).unwrap_or(DEFAULT_TYPES);
}
} }
pub fn load_config(file: PathBuf) -> Result<Config, Box<dyn Error>> { pub fn load_config(file: PathBuf) -> Result<Config, Box<dyn Error>> {
@ -19,6 +41,17 @@ pub fn load_config(file: PathBuf) -> Result<Config, Box<dyn Error>> {
return Ok(config); return Ok(config);
} }
pub fn validate_config(config: &Config) -> Result<(), String> {
for entry in &config.entry {
for entry_type in Config::types(&entry) {
if entry_type != "A" && entry_type != "AAA" {
return Err(format!("Entry {} has invalid type {}", entry.name, entry_type));
}
}
}
return Ok(());
}
pub fn config_path(opts: &opts::Opts) -> PathBuf { pub fn config_path(opts: &opts::Opts) -> PathBuf {
return opts return opts
.config .config

View file

@ -1,14 +1,21 @@
use std::collections::HashMap;
use crate::config::Config;
use reqwest::{header, Client, ClientBuilder}; use reqwest::{header, Client, ClientBuilder};
use std::error::Error; use std::error::Error;
use structopt::StructOpt; use structopt::StructOpt;
use tokio; use tokio;
use futures;
mod config; mod config;
mod opts; mod opts;
fn gandi_api(fqdn: &str) -> String { fn gandi_api_get(fqdn: &str) -> String {
return format!("https://api.gandi.net/v5/livedns/domains/{}/records", fqdn); return format!("https://api.gandi.net/v5/livedns/domains/{}/records", fqdn);
} }
fn gandi_api_url(fqdn: &str, rrset_name: &str, rrset_type: &str) -> String {
return format!(" https://api.gandi.net/v5/livedns/domains/{}/records/{}/{}", fqdn, rrset_name, rrset_type);
}
fn api_client(api_key: &str) -> Result<Client, Box<dyn Error>> { fn api_client(api_key: &str) -> Result<Client, Box<dyn Error>> {
let client_builder = ClientBuilder::new(); let client_builder = ClientBuilder::new();
@ -29,13 +36,36 @@ async fn main() -> Result<(), Box<dyn Error>> {
let conf_path = config::config_path(&opts); let conf_path = config::config_path(&opts);
println!("Loading config from {:#?}", conf_path); println!("Loading config from {:#?}", conf_path);
let conf = config::load_config(conf_path)?; let conf = config::load_config(conf_path)?;
println!("Checking domain {:#?}", conf.fqdn); config::validate_config(&conf)?;
let url = gandi_api(&conf.fqdn);
let client = api_client(&conf.api_key)?; let client = api_client(&conf.api_key)?;
let out = client.get(url).send().await?; let ipv4 = String::from("173.89.215.91");
println!("Output: {:#?}", out); let ipv6 = String::from("2603:6011:be07:302:79f4:50dd:6abe:be38");
println!("Output: {:#?}", out.json().await?);
let mut results = Vec::new();
for entry in &conf.entry {
for entry_type in Config::types(entry) {
let fqdn = Config::fqdn(&entry, &conf);
let url = gandi_api_url(fqdn, entry.name.as_str(), entry_type);
let ip = if entry_type.eq("A") { ipv4.as_str() } else { ipv6.as_str() };
let mut map = HashMap::new();
map.insert("rrset_values", ip);
let req = client.put(url).json(&map);
let task = tokio::task::spawn(async move {
let response = req.send().await?;
return (response.status(), response.text().await?);
});
results.push(task);
}
}
let results = futures::future::try_join_all(results).await?;
for (status, body) in results {
println!("{} - {}", status, body);
}
return Ok(()); return Ok(());
} }