Compare commits

..

No commits in common. "master" and "1.6.0" have entirely different histories.

17 changed files with 355 additions and 930 deletions

View file

@ -1,25 +0,0 @@
{
"files": [
"README.md"
],
"imageSize": 100,
"commit": false,
"commitConvention": "angular",
"contributors": [
{
"login": "jannikac",
"name": "jannikac",
"avatar_url": "https://avatars.githubusercontent.com/u/21014142?v=4",
"profile": "https://github.com/jannikac",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,
"skipCi": true,
"repoType": "github",
"repoHost": "https://github.com",
"projectName": "gandi-live-dns-rust",
"projectOwner": "SeriousBug"
}

View file

@ -1,3 +0,0 @@
# assert_eq!(..., true) or false is a lot clearer when testing functions that
# return booleans.
bool_assert_comparison = false

View file

@ -1,4 +0,0 @@
ignore:
# These are tested, but the tests hit external services which is not
# necessarily smart to do in CI, so they get skipped.
- "src/ip_source"

443
Cargo.lock generated
View file

@ -13,9 +13,9 @@ dependencies = [
[[package]] [[package]]
name = "anyhow" name = "anyhow"
version = "1.0.69" version = "1.0.67"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" checksum = "7724808837b77f4b4de9d283820f9d98bcf496d5692934b857a2399d31ff22e6"
[[package]] [[package]]
name = "ascii-canvas" name = "ascii-canvas"
@ -168,9 +168,9 @@ checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524"
[[package]] [[package]]
name = "async-trait" name = "async-trait"
version = "0.1.64" version = "0.1.60"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" checksum = "677d1d8ab452a3936018a687b20e6f7cf5363d713b732b8884001317b0e48aa3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -179,9 +179,9 @@ dependencies = [
[[package]] [[package]]
name = "atomic-waker" name = "atomic-waker"
version = "1.1.0" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "debc29dde2e69f9e47506b525f639ed42300fc014a3e007832592448fa8e4599" checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a"
[[package]] [[package]]
name = "atty" name = "atty"
@ -206,12 +206,6 @@ version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "base64"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
[[package]] [[package]]
name = "basic-cookies" name = "basic-cookies"
version = "0.1.4" version = "0.1.4"
@ -260,15 +254,15 @@ dependencies = [
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.12.0" version = "3.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.4.0" version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
[[package]] [[package]]
name = "castaway" name = "castaway"
@ -278,9 +272,9 @@ checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.79" version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
@ -290,9 +284,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.1.4" version = "4.0.29"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"clap_derive", "clap_derive",
@ -308,9 +302,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.1.0" version = "4.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro-error", "proc-macro-error",
@ -321,18 +315,18 @@ dependencies = [
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.3.1" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
dependencies = [ dependencies = [
"os_str_bytes", "os_str_bytes",
] ]
[[package]] [[package]]
name = "concurrent-queue" name = "concurrent-queue"
version = "2.1.0" version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c278839b831783b70278b14df4d45e1beb1aad306c07bb796637de9a0e323e8e" checksum = "bd7bef69dc86e3c610e4e7aed41035e2a7ed12e72dd7530f61327a6579a4390b"
dependencies = [ dependencies = [
"crossbeam-utils", "crossbeam-utils",
] ]
@ -407,10 +401,10 @@ dependencies = [
] ]
[[package]] [[package]]
name = "die-exit" name = "die-exit-2"
version = "0.4.0" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38c7de4151fc657b3ef1c90aebd13b25d089166f1f8c1d0a7950ebdfe6499f49" checksum = "3e9ac3d64c794ef4965e2efc9f9eb3c815f40201a25fe47ee19c0801ec428b7c"
[[package]] [[package]]
name = "diff" name = "diff"
@ -461,9 +455,9 @@ dependencies = [
[[package]] [[package]]
name = "either" name = "either"
version = "1.8.1" version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[package]] [[package]]
name = "ena" name = "ena"
@ -476,9 +470,9 @@ dependencies = [
[[package]] [[package]]
name = "encoding_rs" name = "encoding_rs"
version = "0.8.32" version = "0.8.31"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
] ]
@ -542,9 +536,9 @@ dependencies = [
[[package]] [[package]]
name = "futures" name = "futures"
version = "0.3.26" version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84" checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
dependencies = [ dependencies = [
"futures-channel", "futures-channel",
"futures-core", "futures-core",
@ -557,9 +551,9 @@ dependencies = [
[[package]] [[package]]
name = "futures-channel" name = "futures-channel"
version = "0.3.26" version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-sink", "futures-sink",
@ -567,15 +561,15 @@ dependencies = [
[[package]] [[package]]
name = "futures-core" name = "futures-core"
version = "0.3.26" version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
[[package]] [[package]]
name = "futures-executor" name = "futures-executor"
version = "0.3.26" version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e" checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-task", "futures-task",
@ -584,9 +578,9 @@ dependencies = [
[[package]] [[package]]
name = "futures-io" name = "futures-io"
version = "0.3.26" version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
[[package]] [[package]]
name = "futures-lite" name = "futures-lite"
@ -605,9 +599,9 @@ dependencies = [
[[package]] [[package]]
name = "futures-macro" name = "futures-macro"
version = "0.3.26" version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70" checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -616,15 +610,15 @@ dependencies = [
[[package]] [[package]]
name = "futures-sink" name = "futures-sink"
version = "0.3.26" version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
[[package]] [[package]]
name = "futures-task" name = "futures-task"
version = "0.3.26" version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
[[package]] [[package]]
name = "futures-timer" name = "futures-timer"
@ -634,9 +628,9 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c"
[[package]] [[package]]
name = "futures-util" name = "futures-util"
version = "0.3.26" version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
dependencies = [ dependencies = [
"futures-channel", "futures-channel",
"futures-core", "futures-core",
@ -652,22 +646,20 @@ dependencies = [
[[package]] [[package]]
name = "gandi-live-dns" name = "gandi-live-dns"
version = "1.8.0" version = "1.5.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
"clap", "clap",
"die-exit", "die-exit-2",
"directories", "directories",
"futures", "futures",
"governor", "governor",
"httpmock", "httpmock",
"json", "json",
"lazy_static",
"regex", "regex",
"reqwest", "reqwest",
"serde", "serde",
"thiserror",
"tokio", "tokio",
"toml", "toml",
] ]
@ -685,9 +677,9 @@ dependencies = [
[[package]] [[package]]
name = "gloo-timers" name = "gloo-timers"
version = "0.2.6" version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" checksum = "98c4a8d6391675c6b2ee1a6c8d06e8e2d03605c44cec1270675985a4c2a5500b"
dependencies = [ dependencies = [
"futures-channel", "futures-channel",
"futures-core", "futures-core",
@ -740,9 +732,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.4.1" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
@ -762,12 +754,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "hermit-abi"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]] [[package]]
name = "http" name = "http"
version = "0.2.8" version = "0.2.8"
@ -811,7 +797,7 @@ dependencies = [
"assert-json-diff", "assert-json-diff",
"async-object-pool", "async-object-pool",
"async-trait", "async-trait",
"base64 0.13.1", "base64",
"basic-cookies", "basic-cookies",
"crossbeam-utils", "crossbeam-utils",
"form_urlencoded", "form_urlencoded",
@ -832,9 +818,9 @@ dependencies = [
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "0.14.24" version = "0.14.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-channel", "futures-channel",
@ -898,30 +884,30 @@ dependencies = [
[[package]] [[package]]
name = "io-lifetimes" name = "io-lifetimes"
version = "1.0.5" version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.45.0", "windows-sys 0.42.0",
] ]
[[package]] [[package]]
name = "ipnet" name = "ipnet"
version = "2.7.1" version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" checksum = "11b0d96e660696543b251e58030cf9787df56da39dab19ad60eae7353040917e"
[[package]] [[package]]
name = "is-terminal" name = "is-terminal"
version = "0.4.3" version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330"
dependencies = [ dependencies = [
"hermit-abi 0.3.1", "hermit-abi 0.2.6",
"io-lifetimes", "io-lifetimes",
"rustix", "rustix",
"windows-sys 0.45.0", "windows-sys 0.42.0",
] ]
[[package]] [[package]]
@ -968,9 +954,9 @@ checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.61" version = "0.3.60"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
dependencies = [ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
@ -1036,9 +1022,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.139" version = "0.2.138"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
[[package]] [[package]]
name = "libnghttp2-sys" name = "libnghttp2-sys"
@ -1133,15 +1119,6 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c"
[[package]]
name = "nom8"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "nonzero_ext" name = "nonzero_ext"
version = "0.3.0" version = "0.3.0"
@ -1150,19 +1127,19 @@ checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21"
[[package]] [[package]]
name = "num_cpus" name = "num_cpus"
version = "1.15.0" version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
dependencies = [ dependencies = [
"hermit-abi 0.2.6", "hermit-abi 0.1.19",
"libc", "libc",
] ]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.17.0" version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
[[package]] [[package]]
name = "openssl-probe" name = "openssl-probe"
@ -1172,9 +1149,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]] [[package]]
name = "openssl-sys" name = "openssl-sys"
version = "0.9.80" version = "0.9.79"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" checksum = "5454462c0eced1e97f2ec09036abc8da362e66802f66fd20f86854d9d8cbcbc4"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"cc", "cc",
@ -1207,15 +1184,15 @@ dependencies = [
[[package]] [[package]]
name = "parking_lot_core" name = "parking_lot_core"
version = "0.9.7" version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"redox_syscall", "redox_syscall",
"smallvec", "smallvec",
"windows-sys 0.45.0", "windows-sys 0.42.0",
] ]
[[package]] [[package]]
@ -1226,9 +1203,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
[[package]] [[package]]
name = "petgraph" name = "petgraph"
version = "0.6.3" version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143"
dependencies = [ dependencies = [
"fixedbitset", "fixedbitset",
"indexmap", "indexmap",
@ -1339,9 +1316,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.51" version = "1.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" checksum = "e9d89e5dba24725ae5678020bf8f1357a9aa7ff10736b551adbcd3f8d17d766f"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@ -1364,9 +1341,9 @@ dependencies = [
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.23" version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" checksum = "556d0f47a940e895261e77dc200d5eadfc6ef644c179c6f5edfc105e3a2292c8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -1403,9 +1380,9 @@ dependencies = [
[[package]] [[package]]
name = "raw-cpuid" name = "raw-cpuid"
version = "10.6.1" version = "10.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c307f7aacdbab3f0adee67d52739a1d71112cc068d6fab169ddeb18e48877fad" checksum = "a6823ea29436221176fe662da99998ad3b4db2c7f31e7b6f5fe43adccd6320bb"
dependencies = [ dependencies = [
"bitflags", "bitflags",
] ]
@ -1432,9 +1409,9 @@ dependencies = [
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.7.1" version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@ -1449,11 +1426,11 @@ checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
[[package]] [[package]]
name = "reqwest" name = "reqwest"
version = "0.11.14" version = "0.11.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c"
dependencies = [ dependencies = [
"base64 0.21.0", "base64",
"bytes", "bytes",
"encoding_rs", "encoding_rs",
"futures-core", "futures-core",
@ -1503,23 +1480,23 @@ dependencies = [
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.36.8" version = "0.36.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"errno", "errno",
"io-lifetimes", "io-lifetimes",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys",
"windows-sys 0.45.0", "windows-sys 0.42.0",
] ]
[[package]] [[package]]
name = "rustls" name = "rustls"
version = "0.20.8" version = "0.20.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c"
dependencies = [ dependencies = [
"log", "log",
"ring", "ring",
@ -1529,11 +1506,11 @@ dependencies = [
[[package]] [[package]]
name = "rustls-pemfile" name = "rustls-pemfile"
version = "1.0.2" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55"
dependencies = [ dependencies = [
"base64 0.21.0", "base64",
] ]
[[package]] [[package]]
@ -1550,11 +1527,12 @@ checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
[[package]] [[package]]
name = "schannel" name = "schannel"
version = "0.1.21" version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
dependencies = [ dependencies = [
"windows-sys 0.42.0", "lazy_static",
"windows-sys 0.36.1",
] ]
[[package]] [[package]]
@ -1575,18 +1553,18 @@ dependencies = [
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.152" version = "1.0.151"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.152" version = "1.0.151"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1595,9 +1573,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.93" version = "1.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" checksum = "8778cc0b528968fe72abec38b5db5a20a70d148116cd9325d2bc5f5180ca3faf"
dependencies = [ dependencies = [
"itoa", "itoa",
"ryu", "ryu",
@ -1614,15 +1592,6 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "serde_spanned"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "serde_urlencoded" name = "serde_urlencoded"
version = "0.7.1" version = "0.7.1"
@ -1637,9 +1606,9 @@ dependencies = [
[[package]] [[package]]
name = "signal-hook" name = "signal-hook"
version = "0.3.15" version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9" checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d"
dependencies = [ dependencies = [
"libc", "libc",
"signal-hook-registry", "signal-hook-registry",
@ -1647,9 +1616,9 @@ dependencies = [
[[package]] [[package]]
name = "signal-hook-registry" name = "signal-hook-registry"
version = "1.4.1" version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
dependencies = [ dependencies = [
"libc", "libc",
] ]
@ -1729,9 +1698,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.107" version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" checksum = "09ee3a69cd2c7e06684677e5629b3878b253af05e4714964204279c6bc02cf0b"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1751,9 +1720,9 @@ dependencies = [
[[package]] [[package]]
name = "termcolor" name = "termcolor"
version = "1.2.0" version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [ dependencies = [
"winapi-util", "winapi-util",
] ]
@ -1808,15 +1777,15 @@ dependencies = [
[[package]] [[package]]
name = "tinyvec_macros" name = "tinyvec_macros"
version = "0.1.1" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.25.0" version = "1.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af" checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"bytes", "bytes",
@ -1856,9 +1825,9 @@ dependencies = [
[[package]] [[package]]
name = "tokio-util" name = "tokio-util"
version = "0.7.7" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-core", "futures-core",
@ -1870,36 +1839,11 @@ dependencies = [
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.7.2" version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7afcae9e3f0fe2c370fd4657108972cbb2fa9db1b9f84849cefd80741b01cb6" checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f"
dependencies = [ dependencies = [
"serde", "serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.19.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e6a7712b49e1775fb9a7b998de6635b299237f48b404dde71704f2e0e7f37e5"
dependencies = [
"indexmap",
"nom8",
"serde",
"serde_spanned",
"toml_datetime",
] ]
[[package]] [[package]]
@ -1953,9 +1897,9 @@ dependencies = [
[[package]] [[package]]
name = "try-lock" name = "try-lock"
version = "0.2.4" version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
[[package]] [[package]]
name = "unicase" name = "unicase"
@ -1968,9 +1912,9 @@ dependencies = [
[[package]] [[package]]
name = "unicode-bidi" name = "unicode-bidi"
version = "0.3.10" version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
@ -2068,9 +2012,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.84" version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"wasm-bindgen-macro", "wasm-bindgen-macro",
@ -2078,9 +2022,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-backend" name = "wasm-bindgen-backend"
version = "0.2.84" version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"log", "log",
@ -2093,9 +2037,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-futures" name = "wasm-bindgen-futures"
version = "0.4.34" version = "0.4.33"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"js-sys", "js-sys",
@ -2105,9 +2049,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.84" version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
dependencies = [ dependencies = [
"quote", "quote",
"wasm-bindgen-macro-support", "wasm-bindgen-macro-support",
@ -2115,9 +2059,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro-support" name = "wasm-bindgen-macro-support"
version = "0.2.84" version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2128,15 +2072,15 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-shared" name = "wasm-bindgen-shared"
version = "0.2.84" version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.61" version = "0.3.60"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",
@ -2201,6 +2145,19 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
dependencies = [
"windows_aarch64_msvc 0.36.1",
"windows_i686_gnu 0.36.1",
"windows_i686_msvc 0.36.1",
"windows_x86_64_gnu 0.36.1",
"windows_x86_64_msvc 0.36.1",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.42.0" version = "0.42.0"
@ -2208,79 +2165,85 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [ dependencies = [
"windows_aarch64_gnullvm", "windows_aarch64_gnullvm",
"windows_aarch64_msvc", "windows_aarch64_msvc 0.42.0",
"windows_i686_gnu", "windows_i686_gnu 0.42.0",
"windows_i686_msvc", "windows_i686_msvc 0.42.0",
"windows_x86_64_gnu", "windows_x86_64_gnu 0.42.0",
"windows_x86_64_gnullvm", "windows_x86_64_gnullvm",
"windows_x86_64_msvc", "windows_x86_64_msvc 0.42.0",
]
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
] ]
[[package]] [[package]]
name = "windows_aarch64_gnullvm" name = "windows_aarch64_gnullvm"
version = "0.42.1" version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_msvc"
version = "0.42.1" version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
[[package]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.42.1" version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
[[package]]
name = "windows_i686_gnu"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.42.1" version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
[[package]]
name = "windows_i686_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.42.1" version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
[[package]] [[package]]
name = "windows_x86_64_gnullvm" name = "windows_x86_64_gnullvm"
version = "0.42.1" version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
[[package]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.42.1" version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
[[package]] [[package]]
name = "winreg" name = "winreg"

View file

@ -1,11 +1,11 @@
[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." description = "Automatically updates your IP address in Gandi's Live DNS. Makes it possible to use Gandi as a dynamic DNS system."
version = "1.8.0" version = "1.6.0"
edition = "2021" edition = "2021"
authors = ["Kaan Barmore-Genç <kaan@bgenc.net>"] authors = ["Kaan Barmore-Genç <kaan@bgenc.net>"]
license = "MIT" license = "MIT"
readme = "README.md" readme = "Readme.md"
repository = "https://github.com/SeriousBug/gandi-live-dns-rust" repository = "https://github.com/SeriousBug/gandi-live-dns-rust"
[profile.release] [profile.release]
@ -17,7 +17,7 @@ reqwest = { version = "0.11", default-features = false, features = [
"json", "json",
"rustls-tls", "rustls-tls",
] } ] }
toml = "0.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"
@ -32,14 +32,12 @@ futures = "0.3"
anyhow = "1.0" anyhow = "1.0"
governor = "0.5" governor = "0.5"
async-trait = "0.1" async-trait = "0.1"
die-exit = "0.4" die-exit-2 = "0.4"
thiserror = "1.0.38"
[dev-dependencies] [dev-dependencies]
httpmock = "0.6" httpmock = "0.6"
regex = "1.6" regex = "1.6"
lazy_static = "1.4.0"
[dev-dependencies.die-exit] [dev-dependencies.die-exit-2]
version = "0.4" version = "0.4"
features = ["test"] features = ["test"]

View file

@ -1,14 +1,6 @@
## Gandi Live Dns Rust <!-- omit in toc --> ## gandi-live-dns-rust
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section --> [![tests](https://img.shields.io/github/actions/workflow/status/SeriousBug/gandi-live-dns-rust/test.yml?label=tests&branch=master)](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/actions/workflow/status/SeriousBug/gandi-live-dns-rust/lint.yml?label=lints&branch=master)](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)
[![Contributors](https://img.shields.io/badge/all_contributors-1-orange.svg?label=contributors)](#contributors) <!-- ALL-CONTRIBUTORS-BADGE:END -->
[![tests](https://img.shields.io/github/actions/workflow/status/SeriousBug/gandi-live-dns-rust/test.yml?label=tests&branch=master)](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/actions/workflow/status/SeriousBug/gandi-live-dns-rust/lint.yml?label=lints&branch=master)](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
@ -21,23 +13,6 @@ program can update both IPv4 and IPv6 addresses for one or more domains and
subdomains. It can be used as a one-shot tool managed with a systemd timer subdomains. It can be used as a one-shot tool managed with a systemd timer
or cron, or a long-running process that reschedules itself. or cron, or a long-running process that reschedules itself.
## Table of Contents <!-- omit in toc -->
- [Usage](#usage)
- [System packages](#system-packages)
- [Prebuilt binaries](#prebuilt-binaries)
- [With docker](#with-docker)
- [From source](#from-source)
- [Automation](#automation)
- [By running as a background process](#by-running-as-a-background-process)
- [Skipped updates](#skipped-updates)
- [With a Systemd timer](#with-a-systemd-timer)
- [Development](#development)
- [Local builds](#local-builds)
- [Making a release](#making-a-release)
- [Alternatives](#alternatives)
- [Contributors](#contributors)
## Usage ## Usage
The Gandi Live DNS API is rate limited at 30 requests per minute. This program The Gandi Live DNS API is rate limited at 30 requests per minute. This program
@ -69,14 +44,8 @@ Download the latest version from the releases page, extract it from the archive,
### With docker ### With docker
Container images are available on both Github Packages and Docker Hub. Use the [seriousbug/gandi-live-dns-rust](https://hub.docker.com/r/seriousbug/gandi-live-dns-rust) Docker images, which are available for x86_64,
- [ghcr.io/seriousbug/gandi-live-dns-rust](https://github.com/users/seriousbug/packages/container/package/gandi-live-dns-rust) arm64, armv6, and armv7 platforms. Follow the steps below to use these images.
- [docker.io/seriousbug/gandi-live-dns-rust](https://hub.docker.com/r/seriousbug/gandi-live-dns-rust)
The container images are built multi-arch, with support for x86_64, arm64,
armv7, and armv6 platforms. Follow the steps below to use them. You can use
`seriousbug/gandi-live-dns-rust` directly which will default to Docker Hub,
otherwise add `ghcr.io` in the examples below to use Github Packages.
- Create a file `gandi.toml`, then copy and paste the contents of [`example.toml`](https://raw.githubusercontent.com/SeriousBug/gandi-live-dns-rust/master/example.toml) - Create a file `gandi.toml`, then copy and paste the contents of [`example.toml`](https://raw.githubusercontent.com/SeriousBug/gandi-live-dns-rust/master/example.toml)
- Follow the instructions in the example config to get your API key and put it in the config - Follow the instructions in the example config to get your API key and put it in the config
@ -115,23 +84,15 @@ docker run --rm -it -v $(pwd)/gandi.toml:/gandi.toml:ro seriousbug/gandi-live-dn
Or with a `docker-compose.yml` file, add it in the arguments: Or with a `docker-compose.yml` file, add it in the arguments:
```yml ```yml
gandi-live-dns: gandi-live-dns:
image: seriousbug/gandi-live-dns-rust:latest image: seriousbug/gandi-live-dns-rust:latest
restart: always restart: always
volumes: volumes:
- ./gandi.toml:/gandi.toml:ro - ./gandi.toml:/gandi.toml:ro
# Repeat the update every day # Repeat the update every day
command: --repeat=86400 command: --repeat=86400
``` ```
#### Skipped updates
In background process mode, the tool will avoid sending an update to Gandi if
your IP address has not changed since the last update. This only works so long
as the tool continues to run, it will send an update when restarted even if your
IP address has not changed. You can also override this behavior by adding
`always_update = true` to the top of your config file.
### With a Systemd timer ### With a Systemd timer
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
@ -167,8 +128,7 @@ Docker with `docker login`. Then follow these steps:
- Create a release on Github - Create a release on Github
- Make sure to create a tag for the release version on `master` - Make sure to create a tag for the release version on `master`
- Upload the binary archives to the Github release - Upload the binary archives to the Github release
- Update the AUR version - Update the AUR version manually
- Run `cargo publish` to update the crates.io version
## Alternatives ## Alternatives
@ -176,28 +136,3 @@ Docker with `docker login`. Then follow these steps:
- [ Adam Vigneaux's Bash based updater, with a docker image](https://github.com/AdamVig/gandi-dynamic-dns) - [ Adam Vigneaux's Bash based updater, with a docker image](https://github.com/AdamVig/gandi-dynamic-dns)
- [Yago Riveiro's Python based updater](https://github.com/yriveiro/giu) - [Yago Riveiro's Python based updater](https://github.com/yriveiro/giu)
- [ Maxime Le Conte des Floris' Go based updater](https://github.com/mlcdf/dyndns) - [ Maxime Le Conte des Floris' Go based updater](https://github.com/mlcdf/dyndns)
## Contributors
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tbody>
<tr>
<td align="center"><a href="https://github.com/jannikac"><img src="https://avatars.githubusercontent.com/u/21014142?v=4?s=100" width="100px;" alt="jannikac"/><br /><sub><b>jannikac</b></sub></a><br /><a href="https://github.com/SeriousBug/gandi-live-dns-rust/commits?author=jannikac" title="Code">💻</a></td>
</tr>
</tbody>
</table>
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->

View file

@ -19,9 +19,8 @@ ttl = 300
# Ipify is used by default. If you want to change it, uncomment the one you want # Ipify is used by default. If you want to change it, uncomment the one you want
# to use. # to use.
# #
#ip_source = "Ipify" # An open source and public service. https://github.com/rdegges/ipify-api # ip_source = "Ipify"
#ip_source = "Icanhazip" # A free service, currently run by Cloudflare. https://major.io/2021/06/06/a-new-future-for-icanhazip/ # ip_source = "Icanhazip"
#ip_source = "SeeIP" # A free service, run by UNVIO, LLC. https://seeip.org/
# For every domain or subdomain you want to update, create an entry below. # For every domain or subdomain you want to update, create an entry below.

View file

@ -3,7 +3,9 @@
# Make sure `cross` is installed. # Make sure `cross` is installed.
# You'll also need `sed`, a relatively recent version of `tar`, and `7z`. # You'll also need `sed`, a relatively recent version of `tar`, and `7z`.
# #
DOCKER="docker" # This script runs does `sudo docker` to build and push the release to docker.
# If you have rootless docker set up, remove sudo from this variable.
DOCKER="sudo docker"
# #
shopt -s extglob shopt -s extglob
# Trap errors and interrupts # Trap errors and interrupts
@ -47,9 +49,6 @@ VERSION=$(sed -nr 's/^version *= *"([0-9.]+)"/\1/p' Cargo.toml | head --lines=1)
# Make the builds # Make the builds
for target in "${!TARGETS[@]}"; do for target in "${!TARGETS[@]}"; do
echo Building "${target}" echo Building "${target}"
# Keeping the cached builds seem to be breaking things when going between targets
# This wouldn't be a problem if these were running in a matrix on the CI...
rm -rf target/release/
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
@ -77,6 +76,4 @@ ${DOCKER} buildx build . \
--file "Dockerfile" \ --file "Dockerfile" \
--tag "seriousbug/gandi-live-dns-rust:latest" \ --tag "seriousbug/gandi-live-dns-rust:latest" \
--tag "seriousbug/gandi-live-dns-rust:${VERSION}" \ --tag "seriousbug/gandi-live-dns-rust:${VERSION}" \
--tag "ghcr.io/seriousbug/gandi-live-dns-rust:latest" \
--tag "ghcr.io/seriousbug/gandi-live-dns-rust:${VERSION}" \
--push --push

View file

@ -1,26 +1,13 @@
use crate::opts; use crate::opts;
use directories::ProjectDirs; use directories::ProjectDirs;
use serde::Deserialize; use serde::Deserialize;
use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
use std::{fs, io};
use thiserror::Error;
fn default_types() -> Vec<String> { fn default_types() -> Vec<String> {
DEFAULT_TYPES.iter().map(|v| v.to_string()).collect() DEFAULT_TYPES.iter().map(|v| v.to_string()).collect()
} }
#[derive(Error, Debug)]
pub enum ConfigError {
#[error("Failed to read config file: {0} ")]
Io(#[from] io::Error),
#[error("Failed to parse config file: {0}")]
Parse(#[from] toml::de::Error),
#[error("Entry '{0}' has invalid type '{1}'")]
Validation(String, String),
#[error("Can't find config directory")]
ConfigNotFound(),
}
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct Entry { pub struct Entry {
pub name: String, pub name: String,
@ -38,7 +25,6 @@ fn default_ttl() -> u32 {
pub enum IPSourceName { pub enum IPSourceName {
Ipify, Ipify,
Icanhazip, Icanhazip,
SeeIP,
} }
impl Default for IPSourceName { impl Default for IPSourceName {
@ -58,8 +44,6 @@ pub struct Config {
pub entry: Vec<Entry>, pub entry: Vec<Entry>,
#[serde(default = "default_ttl")] #[serde(default = "default_ttl")]
pub ttl: u32, pub ttl: u32,
#[serde(default)]
pub always_update: bool,
} }
const DEFAULT_TYPES: &[&str] = &["A"]; const DEFAULT_TYPES: &[&str] = &["A"];
@ -78,20 +62,18 @@ impl Config {
} }
} }
fn load_config_from<P: std::convert::AsRef<std::path::Path>>( fn load_config_from<P: std::convert::AsRef<std::path::Path>>(path: P) -> anyhow::Result<Config> {
path: P,
) -> Result<Config, ConfigError> {
let contents = fs::read_to_string(path)?; let contents = fs::read_to_string(path)?;
Ok(toml::from_str(&contents)?) Ok(toml::from_str(&contents)?)
} }
pub fn load_config(opts: &opts::Opts) -> Result<Config, ConfigError> { pub fn load_config(opts: &opts::Opts) -> anyhow::Result<Config> {
let mut 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")
.map(|dir| PathBuf::from(dir.config_dir()).join("config.toml")) .map(|dir| PathBuf::from(dir.config_dir()).join("config.toml"))
.ok_or(ConfigError::ConfigNotFound()); .ok_or_else(|| anyhow::anyhow!("Can't find config directory"));
confpath confpath
.and_then(|path| { .and_then(|path| {
println!("Checking for config: {}", path.to_string_lossy()); println!("Checking for config: {}", path.to_string_lossy());
@ -120,14 +102,11 @@ pub fn load_config(opts: &opts::Opts) -> Result<Config, ConfigError> {
Ok(config) Ok(config)
} }
pub fn validate_config(config: &Config) -> Result<(), ConfigError> { pub fn validate_config(config: &Config) -> anyhow::Result<()> {
for entry in &config.entry { for entry in &config.entry {
for entry_type in Config::types(entry) { for entry_type in Config::types(entry) {
if entry_type != "A" && entry_type != "AAAA" { if entry_type != "A" && entry_type != "AAAA" {
return Err(ConfigError::Validation( anyhow::bail!("Entry {} has invalid type {}", entry.name, entry_type);
entry.name.clone(),
entry_type.to_string(),
));
} }
} }
} }
@ -177,7 +156,6 @@ name = "@"
assert_eq!(conf.entry[1].types, vec!["A".to_string()]); 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);
assert_eq!(conf.always_update, false);
} }
#[test] #[test]
@ -192,7 +170,6 @@ fqdn = "example.com"
api_key = "yyy" api_key = "yyy"
ttl = 1200 ttl = 1200
ip_source = "Icanhazip" ip_source = "Icanhazip"
always_update = true
[[entry]] [[entry]]
name = "www" name = "www"
@ -216,7 +193,6 @@ name = "@"
assert_eq!(conf.entry[0].name, "www"); assert_eq!(conf.entry[0].name, "www");
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);
assert_eq!(conf.always_update, true);
} }
#[test] #[test]

View file

@ -2,5 +2,5 @@ The IP sources. These are APIs that we can query to get the IP address of the
current service. current service.
The tests under this directory are all marked to be skipped, the tests hit the The tests under this directory are all marked to be skipped, the tests hit the
actual APIs and can be flaky in CI. Make sure to run the tests manually if you actual APIs and can be flakey in CI. Make sure to run the tests manually if you
have to modify the code. have to modify the code.

View file

@ -1,9 +0,0 @@
use async_trait::async_trait;
use crate::ClientError;
#[async_trait]
pub trait IPSource {
async fn get_ipv4(&self) -> Result<String, ClientError>;
async fn get_ipv6(&self) -> Result<String, ClientError>;
}

View file

@ -1,12 +1,10 @@
use async_trait::async_trait; use async_trait::async_trait;
use crate::ClientError; use super::ip_source::IPSource;
use super::common::IPSource; pub(crate) struct IPSourceIcanhazip {}
pub(crate) struct IPSourceIcanhazip; async fn get_ip(api_url: &str) -> anyhow::Result<String> {
async fn get_ip(api_url: &str) -> Result<String, ClientError> {
let response = reqwest::get(api_url).await?; let response = reqwest::get(api_url).await?;
let text = response.text().await?; let text = response.text().await?;
Ok(text) Ok(text)
@ -14,14 +12,14 @@ async fn get_ip(api_url: &str) -> Result<String, ClientError> {
#[async_trait] #[async_trait]
impl IPSource for IPSourceIcanhazip { impl IPSource for IPSourceIcanhazip {
async fn get_ipv4(&self) -> Result<String, ClientError> { async fn get_ipv4() -> anyhow::Result<String> {
Ok(get_ip("https://ipv4.icanhazip.com") Ok(get_ip("https://ipv4.icanhazip.com")
.await? .await?
// icanazip puts a newline at the end // icanazip puts a newline at the end
.trim() .trim()
.to_string()) .to_string())
} }
async fn get_ipv6(&self) -> Result<String, ClientError> { async fn get_ipv6() -> anyhow::Result<String> {
Ok(get_ip("https://ipv6.icanhazip.com") Ok(get_ip("https://ipv6.icanhazip.com")
.await? .await?
// icanazip puts a newline at the end // icanazip puts a newline at the end
@ -34,15 +32,13 @@ impl IPSource for IPSourceIcanhazip {
mod tests { mod tests {
use regex::Regex; use regex::Regex;
use crate::ip_source::common::IPSource; use super::IPSource;
use super::IPSourceIcanhazip; use super::IPSourceIcanhazip;
#[tokio::test] #[tokio::test]
#[ignore] #[ignore]
async fn ipv4_test() { async fn ipv4_test() {
let ipv4 = IPSourceIcanhazip let ipv4 = IPSourceIcanhazip::get_ipv4()
.get_ipv4()
.await .await
.expect("Failed to get the IP address"); .expect("Failed to get the IP address");
assert!(Regex::new(r"^\d+[.]\d+[.]\d+[.]\d+$") assert!(Regex::new(r"^\d+[.]\d+[.]\d+[.]\d+$")
@ -53,8 +49,7 @@ mod tests {
#[tokio::test] #[tokio::test]
#[ignore] #[ignore]
async fn ipv6_test() { async fn ipv6_test() {
let ipv6 = IPSourceIcanhazip let ipv6 = IPSourceIcanhazip::get_ipv6()
.get_ipv6()
.await .await
.expect("Failed to get the IP address"); .expect("Failed to get the IP address");
assert!(Regex::new(r"^([0-9a-fA-F]*:){7}[0-9a-fA-F]*$") assert!(Regex::new(r"^([0-9a-fA-F]*:){7}[0-9a-fA-F]*$")

View file

@ -0,0 +1,7 @@
use async_trait::async_trait;
#[async_trait]
pub trait IPSource {
async fn get_ipv4() -> anyhow::Result<String>;
async fn get_ipv6() -> anyhow::Result<String>;
}

View file

@ -1,12 +1,10 @@
use async_trait::async_trait; use async_trait::async_trait;
use crate::ClientError; use super::ip_source::IPSource;
use super::common::IPSource; pub(crate) struct IPSourceIpify {}
pub(crate) struct IPSourceIpify; async fn get_ip(api_url: &str) -> anyhow::Result<String> {
async fn get_ip(api_url: &str) -> Result<String, ClientError> {
let response = reqwest::get(api_url).await?; let response = reqwest::get(api_url).await?;
let text = response.text().await?; let text = response.text().await?;
Ok(text) Ok(text)
@ -14,10 +12,10 @@ async fn get_ip(api_url: &str) -> Result<String, ClientError> {
#[async_trait] #[async_trait]
impl IPSource for IPSourceIpify { impl IPSource for IPSourceIpify {
async fn get_ipv4(&self) -> Result<String, ClientError> { async fn get_ipv4() -> anyhow::Result<String> {
get_ip("https://api.ipify.org").await get_ip("https://api.ipify.org").await
} }
async fn get_ipv6(&self) -> Result<String, ClientError> { async fn get_ipv6() -> anyhow::Result<String> {
get_ip("https://api6.ipify.org").await get_ip("https://api6.ipify.org").await
} }
} }
@ -32,8 +30,7 @@ mod tests {
#[tokio::test] #[tokio::test]
#[ignore] #[ignore]
async fn ipv4_test() { async fn ipv4_test() {
let ipv4 = IPSourceIpify let ipv4 = IPSourceIpify::get_ipv4()
.get_ipv4()
.await .await
.expect("Failed to get the IP address"); .expect("Failed to get the IP address");
assert!(Regex::new(r"^\d+[.]\d+[.]\d+[.]\d+$") assert!(Regex::new(r"^\d+[.]\d+[.]\d+[.]\d+$")
@ -44,8 +41,7 @@ mod tests {
#[tokio::test] #[tokio::test]
#[ignore] #[ignore]
async fn ipv6_test() { async fn ipv6_test() {
let ipv6 = IPSourceIpify let ipv6 = IPSourceIpify::get_ipv6()
.get_ipv6()
.await .await
.expect("Failed to get the IP address"); .expect("Failed to get the IP address");
assert!(Regex::new(r"^([0-9a-fA-F]*:){7}[0-9a-fA-F]*$") assert!(Regex::new(r"^([0-9a-fA-F]*:){7}[0-9a-fA-F]*$")

View file

@ -1,4 +1,3 @@
pub(crate) mod common;
pub(crate) mod icanhazip; pub(crate) mod icanhazip;
pub(crate) mod ip_source;
pub(crate) mod ipify; pub(crate) mod ipify;
pub(crate) mod seeip;

View file

@ -1,55 +0,0 @@
use async_trait::async_trait;
use crate::ClientError;
use super::common::IPSource;
pub(crate) struct IPSourceSeeIP;
async fn get_ip(api_url: &str) -> Result<String, ClientError> {
let response = reqwest::get(api_url).await?;
let text = response.text().await?;
Ok(text)
}
#[async_trait]
impl IPSource for IPSourceSeeIP {
async fn get_ipv4(&self) -> Result<String, ClientError> {
get_ip("https://ip4.seeip.org").await
}
async fn get_ipv6(&self) -> Result<String, ClientError> {
get_ip("https://ip6.seeip.org").await
}
}
#[cfg(test)]
mod tests {
use regex::Regex;
use super::IPSource;
use super::IPSourceSeeIP;
#[tokio::test]
#[ignore]
async fn ipv4_test() {
let ipv4 = IPSourceSeeIP
.get_ipv4()
.await
.expect("Failed to get the IP address");
assert!(Regex::new(r"^\d+[.]\d+[.]\d+[.]\d+$")
.unwrap()
.is_match(ipv4.as_str()))
}
#[tokio::test]
#[ignore]
async fn ipv6_test() {
let ipv6 = IPSourceSeeIP
.get_ipv6()
.await
.expect("Failed to get the IP address");
assert!(Regex::new(r"^([0-9a-fA-F]*:){7}[0-9a-fA-F]*$")
.unwrap()
.is_match(ipv6.as_str()))
}
}

View file

@ -1,63 +1,28 @@
use crate::config::Config; use crate::config::Config;
use crate::gandi::GandiAPI; use crate::gandi::GandiAPI;
use crate::ip_source::{common::IPSource, ipify::IPSourceIpify}; use crate::ip_source::{ip_source::IPSource, ipify::IPSourceIpify};
use clap::Parser; use clap::Parser;
use config::{ConfigError, IPSourceName}; use config::IPSourceName;
use ip_source::icanhazip::IPSourceIcanhazip; use ip_source::icanhazip::IPSourceIcanhazip;
use ip_source::seeip::IPSourceSeeIP;
use opts::Opts;
use reqwest::header::InvalidHeaderValue;
use reqwest::{header, Client, ClientBuilder, StatusCode}; use reqwest::{header, Client, ClientBuilder, StatusCode};
use serde::{Deserialize, Serialize}; use serde::Serialize;
use std::{num::NonZeroU32, sync::Arc, time::Duration}; use std::{num::NonZeroU32, sync::Arc, time::Duration};
use tokio::join;
use tokio::{self, task::JoinHandle, time::sleep}; use tokio::{self, task::JoinHandle, time::sleep};
mod config; mod config;
mod gandi; mod gandi;
mod ip_source; mod ip_source;
mod opts; mod opts;
use die_exit::*; use die_exit_2::*;
use thiserror::Error;
/// 30 requests per minute, see https://api.gandi.net/docs/reference/ /// 30 requests per minute, see https://api.gandi.net/docs/reference/
const GANDI_RATE_LIMIT: u32 = 30; const GANDI_RATE_LIMIT: u32 = 30;
/// If we hit the rate limit, wait up to this many seconds before next attempt /// If we hit the rate limit, wait up to this many seconds before next attempt
const GANDI_DELAY_JITTER: u64 = 20; const GANDI_DELAY_JITTER: u64 = 20;
#[derive(Error, Debug)] fn api_client(api_key: &str) -> anyhow::Result<Client> {
pub enum ClientError {
#[error("Error occured while reading config: {0}")]
Config(#[from] ConfigError),
#[error("Error while accessing the Gandi API: {0}")]
Api(#[from] ApiError),
#[error("Error while converting the API key to a header: {0}")]
InvalidHeader(#[from] InvalidHeaderValue),
#[error("Error while sending request: {0}")]
Request(#[from] reqwest::Error),
#[error("Error while joining async tasks: {0}")]
TaskJoin(#[from] tokio::task::JoinError),
#[error("Unexpected type in config: {0}")]
BadEntry(String),
#[error("Entry '{0}' includes type A which requires an IPv4 adress but no IPv4 adress could be determined because: {1}")]
Ipv4missing(String, String),
#[error("Entry '{0}' includes type AAAA which requires an IPv6 adress but no IPv6 adress could be determined because: {1}")]
Ipv6missing(String, String),
}
#[derive(Error, Debug)]
pub enum ApiError {
#[error("API returned 403 - Forbidden. Message: {message:?}")]
Forbidden { message: String },
#[error("API returned 403 - Unauthorized. Provided API key is possibly incorrect")]
Unauthorized(),
#[error("API returned {0} - {0}")]
Unknown(StatusCode, String),
}
fn api_client(api_key: &str) -> Result<Client, ClientError> {
let client_builder = ClientBuilder::new(); let client_builder = ClientBuilder::new();
let key = format!("Apikey {api_key}"); let key = format!("Apikey {}", api_key);
let mut auth_value = header::HeaderValue::from_str(&key)?; let mut auth_value = header::HeaderValue::from_str(&key)?;
let mut headers = header::HeaderMap::new(); let mut headers = header::HeaderMap::new();
auth_value.set_sensitive(true); auth_value.set_sensitive(true);
@ -74,202 +39,77 @@ pub struct APIPayload {
pub rrset_ttl: u32, pub rrset_ttl: u32,
} }
#[derive(Debug)] async fn run<IP: IPSource>(base_url: &str, conf: &Config) -> anyhow::Result<()> {
struct ResponseFeedback { config::validate_config(conf).die_with(|error| format!("Invalid config: {}", error));
entry_name: String, println!("Finding out the IP address...");
entry_type: String, let ipv4_result = IP::get_ipv4().await;
response: Result<String, ApiError>, let ipv6_result = IP::get_ipv6().await;
} let ipv4 = ipv4_result.as_ref();
let ipv6 = ipv6_result.as_ref();
println!("Found these:");
match ipv4 {
Ok(ip) => println!("\tIPv4: {}", ip),
Err(err) => eprintln!("\tIPv4 failed: {}", err),
}
match ipv6 {
Ok(ip) => println!("\tIPv6: {}", ip),
Err(err) => eprintln!("\tIPv6 failed: {}", err),
}
#[derive(Deserialize)] let client = api_client(&conf.api_key)?;
// Allowing dead code because this is the API response we get from Gandi. let mut tasks: Vec<JoinHandle<(StatusCode, String)>> = Vec::new();
// We don't necessarily need all the fields, but we get them anyway. println!("Attempting to update DNS entries now");
#[allow(dead_code)]
struct ApiResponse {
message: String,
cause: Option<String>,
code: Option<i32>,
object: Option<String>,
}
async fn run( let governor = Arc::new(governor::RateLimiter::direct(governor::Quota::per_minute(
base_url: &str, NonZeroU32::new(GANDI_RATE_LIMIT).die("Governor rate is 0"),
ip_source: &Box<dyn IPSource>, )));
conf: &Config, let retry_jitter =
opts: &Opts, governor::Jitter::new(Duration::ZERO, Duration::from_secs(GANDI_DELAY_JITTER));
) -> Result<(), ClientError> {
let mut last_ipv4: Option<String> = None;
let mut last_ipv6: Option<String> = None;
loop { for entry in &conf.entry {
println!("Finding out the IP address..."); for entry_type in Config::types(entry) {
let (ipv4_result, ipv6_result) = join!(ip_source.get_ipv4(), ip_source.get_ipv6()); let fqdn = Config::fqdn(entry, conf).to_string();
let ipv4 = ipv4_result.as_ref(); let url = GandiAPI {
let ipv6 = ipv6_result.as_ref(); fqdn: &fqdn,
println!("Found these:"); rrset_name: &entry.name,
match ipv4 { rrset_type: entry_type,
Ok(ip) => println!("\tIPv4: {ip}"), base_url,
Err(err) => eprintln!("\tIPv4 failed: {err}"),
}
match ipv6 {
Ok(ip) => println!("\tIPv6: {ip}"),
Err(err) => eprintln!("\tIPv6 failed: {err}"),
}
let ipv4_same = last_ipv4
.as_ref()
.map(|p| ipv4.map(|q| p == q).unwrap_or(false))
.unwrap_or(false);
let ipv6_same = last_ipv6
.as_ref()
.map(|p| ipv6.map(|q| p == q).unwrap_or(false))
.unwrap_or(false);
if !ipv4_same || !ipv6_same || conf.always_update {
let client = api_client(&conf.api_key)?;
let mut tasks: Vec<JoinHandle<Result<ResponseFeedback, ClientError>>> = Vec::new();
println!("Attempting to update DNS entries now");
let governor = Arc::new(governor::RateLimiter::direct(governor::Quota::per_minute(
NonZeroU32::new(GANDI_RATE_LIMIT).die("Governor rate is 0"),
)));
let retry_jitter =
governor::Jitter::new(Duration::ZERO, Duration::from_secs(GANDI_DELAY_JITTER));
for entry in &conf.entry {
for entry_type in Config::types(entry) {
let fqdn = Config::fqdn(entry, conf).to_string();
let url = GandiAPI {
fqdn: &fqdn,
rrset_name: &entry.name,
rrset_type: entry_type,
base_url,
}
.url();
let ip = match entry_type {
"A" => match ipv4 {
Ok(ref value) => Ok(value),
Err(ref err) => Err(ClientError::Ipv4missing(
entry.name.clone(),
err.to_string(),
)),
},
"AAAA" => match ipv6 {
Ok(ref value) => Ok(value),
Err(ref err) => Err(ClientError::Ipv6missing(
entry.name.clone(),
err.to_string(),
)),
},
&_ => Err(ClientError::BadEntry(entry_type.to_string())),
}?;
let payload = APIPayload {
rrset_values: vec![ip.to_string()],
rrset_ttl: Config::ttl(entry, conf),
};
let req = client.put(url).json(&payload);
let task_governor = governor.clone();
let entry_type = entry_type.to_string();
let entry_name = entry.name.to_string();
let task: JoinHandle<Result<ResponseFeedback, ClientError>> =
tokio::task::spawn(async move {
task_governor.until_ready_with_jitter(retry_jitter).await;
println!("Updating {} record for {}", entry_type, &fqdn);
let resp = req.send().await?;
let response_feedback = match resp.status() {
StatusCode::CREATED => {
let body: ApiResponse = resp.json().await?;
ResponseFeedback {
entry_name,
entry_type,
response: Ok(body.message),
}
}
StatusCode::UNAUTHORIZED => ResponseFeedback {
entry_name,
entry_type,
response: Err(ApiError::Unauthorized()),
},
StatusCode::FORBIDDEN => {
let body: ApiResponse = resp.json().await?;
ResponseFeedback {
entry_name: entry_name.clone(),
entry_type,
response: Err(ApiError::Forbidden {
message: body.message,
}),
}
}
_ => {
let status = resp.status();
let body: ApiResponse = resp.json().await?;
ResponseFeedback {
entry_name,
entry_type,
response: Err(ApiError::Unknown(status, body.message)),
}
}
};
Ok(response_feedback)
});
tasks.push(task);
}
} }
.url();
let results = futures::future::try_join_all(tasks).await?; let ip = match entry_type {
// Only count successfull requests "A" => ipv4.die_with(|error| format!("Needed IPv4 for {fqdn}: {error}")),
println!( "AAAA" => ipv6.die_with(|error| format!("Needed IPv6 for {fqdn}: {error}")),
"Updates done for {} entries", bad_entry_type => die!("Unexpected type in config: {}", bad_entry_type),
results };
.iter() let payload = APIPayload {
.filter_map(|item| item.as_ref().ok()) rrset_values: vec![ip.to_string()],
.filter(|item| item.response.is_ok()) rrset_ttl: Config::ttl(entry, conf),
.count() };
); let req = client.put(url).json(&payload);
for item in &results { let task_governor = governor.clone();
match item { let entry_type = entry_type.to_string();
Ok(value) => println!( let task = tokio::task::spawn(async move {
"{}", task_governor.until_ready_with_jitter(retry_jitter).await;
match &value.response { println!("Updating {} record for {}", entry_type, &fqdn);
Ok(val) => format!( match req.send().await {
"Record '{}' ({}): {}", Ok(response) => (
value.entry_name, value.entry_type, val response.status(),
), response
Err(err) => format!( .text()
"Record '{}' ({}): {}", .await
value.entry_name, value.entry_type, err .unwrap_or_else(|error| error.to_string()),
),
}
), ),
Err(err) => println!("{err}"), Err(error) => (StatusCode::IM_A_TEAPOT, error.to_string()),
} }
} });
if results tasks.push(task);
.iter()
// all tasks finished OK, and all responses were OK as well
.all(|result| result.as_ref().map(|v| v.response.is_ok()).unwrap_or(false))
{
// Only then we update the last seen IP, because we want to
// retry updates in case the last update just happened to fail
last_ipv4 = ipv4.ok().map(|v| v.to_string());
last_ipv6 = ipv6.ok().map(|v| v.to_string());
} else if opts.repeat.is_some() {
println!("Some operations failed. They will be retried during the next repeat.")
}
} else {
println!("IP address has not changed since last update");
} }
}
if let Some(repeat) = opts.repeat { let results = futures::future::try_join_all(tasks).await?;
// If configured to repeat, do so println!("Updates done for {} entries", results.len());
sleep(Duration::from_secs(repeat)).await; for (status, body) in results {
continue; println!("{} - {}", status, body);
}
// Otherwise this is one-shot, we should exit now
break;
} }
Ok(()) Ok(())
@ -278,45 +118,53 @@ async fn run(
#[tokio::main(flavor = "current_thread")] #[tokio::main(flavor = "current_thread")]
async fn main() -> anyhow::Result<()> { async fn main() -> anyhow::Result<()> {
let opts = opts::Opts::parse(); let opts = opts::Opts::parse();
let conf = config::load_config(&opts)?; let conf = config::load_config(&opts)
.die_with(|error| format!("Failed to read config file: {}", error));
let ip_source: Box<dyn IPSource> = match conf.ip_source { // run indefinitely if repeat is given
IPSourceName::Ipify => Box::new(IPSourceIpify), if let Some(delay) = opts.repeat {
IPSourceName::Icanhazip => Box::new(IPSourceIcanhazip), loop {
IPSourceName::SeeIP => Box::new(IPSourceSeeIP), run_dispatch(&conf).await.ok();
}; sleep(Duration::from_secs(delay)).await
config::validate_config(&conf)?; }
run("https://api.gandi.net", &ip_source, &conf, &opts).await?; }
Ok(()) // otherwise run just once
else {
run_dispatch(&conf).await?;
Ok(())
}
}
async fn run_dispatch(conf: &Config) -> anyhow::Result<()> {
match conf.ip_source {
IPSourceName::Ipify => run::<IPSourceIpify>("https://api.gandi.net", conf).await,
IPSourceName::Icanhazip => run::<IPSourceIcanhazip>("https://api.gandi.net", conf).await,
}
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{config, ip_source::common::IPSource, opts::Opts, run, ClientError}; use std::env::temp_dir;
use crate::{config, ip_source::ip_source::IPSource, opts::Opts, run};
use async_trait::async_trait; use async_trait::async_trait;
use httpmock::MockServer; use httpmock::MockServer;
use lazy_static::lazy_static; use tokio::fs;
use std::{
env::temp_dir,
sync::atomic::{AtomicBool, Ordering::SeqCst},
time::Duration,
};
use tokio::{fs, task::LocalSet, time::sleep};
struct IPSourceMock; struct IPSourceMock {}
#[async_trait] #[async_trait]
impl IPSource for IPSourceMock { impl IPSource for IPSourceMock {
async fn get_ipv4(&self) -> Result<String, ClientError> { async fn get_ipv4() -> anyhow::Result<String> {
Ok("192.168.0.0".to_string()) Ok("192.168.0.0".to_string())
} }
async fn get_ipv6(&self) -> Result<String, ClientError> { async fn get_ipv6() -> anyhow::Result<String> {
Ok("fe80:0000:0208:74ff:feda:625c".to_string()) Ok("fe80:0000:0208:74ff:feda:625c".to_string())
} }
} }
#[tokio::test] #[tokio::test]
async fn single_shot() { async fn create_repo_success_test() {
let mut temp = temp_dir().join("gandi-live-dns-test"); let mut temp = temp_dir().join("gandi-live-dns-test");
fs::create_dir_all(&temp) fs::create_dir_all(&temp)
.await .await
@ -338,8 +186,7 @@ mod tests {
"/v5/livedns/domains/{fqdn}/records/{rname}/{rtype}" "/v5/livedns/domains/{fqdn}/records/{rname}/{rtype}"
)) ))
.body_contains("192.168.0.0"); .body_contains("192.168.0.0");
then.status(201) then.status(200);
.body("{\"cause\":\"\", \"code\":201, \"message\":\"\", \"object\":\"\"}");
}); });
let opts = Opts { let opts = Opts {
@ -347,202 +194,11 @@ mod tests {
..Opts::default() ..Opts::default()
}; };
let conf = config::load_config(&opts).expect("Failed to load config"); let conf = config::load_config(&opts).expect("Failed to load config");
let ip_source: Box<dyn IPSource> = Box::new(IPSourceMock); run::<IPSourceMock>(server.base_url().as_str(), &conf)
run(server.base_url().as_str(), &ip_source, &conf, &opts)
.await .await
.expect("Failed when running the update"); .expect("Failed when running the update");
// Assert // Assert
mock.assert(); mock.assert();
} }
#[test]
fn repeat() {
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
LocalSet::new().block_on(&runtime, async {
let mut temp = temp_dir().join("gandi-live-dns-test");
fs::create_dir_all(&temp)
.await
.expect("Failed to create test dir");
temp.push("test.toml");
fs::write(
&temp,
"fqdn = \"example.com\"\napi_key = \"xxx\"\nttl = 300\n[[entry]]\nname =\"@\"\n",
)
.await
.expect("Failed to write test config file");
let fqdn = "example.com";
let rname = "@";
let rtype = "A";
let server = MockServer::start();
let mock = server.mock(|when, then| {
when.method("PUT")
.path(format!(
"/v5/livedns/domains/{fqdn}/records/{rname}/{rtype}"
))
.body_contains("192.168.0.0");
then.status(201)
.body("{\"cause\":\"\", \"code\":201, \"message\":\"\", \"object\":\"\"}");
});
let server_url = server.base_url();
let handle = tokio::task::spawn_local(async move {
let opts = Opts {
config: Some(temp.to_string_lossy().to_string()),
repeat: Some(1),
..Opts::default()
};
let conf = config::load_config(&opts).expect("Failed to load config");
let ip_source: Box<dyn IPSource> = Box::new(IPSourceMock);
run(&server_url, &ip_source, &conf, &opts)
.await
.expect("Failed when running the update");
});
sleep(Duration::from_secs(3)).await;
handle.abort();
// Only should update once because the IP doesn't change
mock.assert();
});
}
#[test]
fn repeat_with_failure() {
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
LocalSet::new().block_on(&runtime, async {
let mut temp = temp_dir().join("gandi-live-dns-test");
fs::create_dir_all(&temp)
.await
.expect("Failed to create test dir");
temp.push("test.toml");
fs::write(
&temp,
"fqdn = \"example.com\"\napi_key = \"xxx\"\nttl = 300\n[[entry]]\nname =\"@\"\n",
)
.await
.expect("Failed to write test config file");
let fqdn = "example.com";
let rname = "@";
let rtype = "A";
let server = MockServer::start();
let mock = server.mock(|when, then| {
when.method("PUT")
.path(format!(
"/v5/livedns/domains/{fqdn}/records/{rname}/{rtype}"
))
.body_contains("192.168.0.0")
.matches(|_| {
// Don't match during the first call, but do during the second call
lazy_static! {
static ref FIRST_CALL: AtomicBool = AtomicBool::new(true);
}
if FIRST_CALL.load(SeqCst) {
FIRST_CALL.store(false, SeqCst);
return true;
}
false
});
then.status(500)
.body("{\"cause\":\"\", \"code\":500, \"message\":\"Something went wrong\", \"object\":\"\"}");
});
let mock_fail = server.mock(|when, then| {
when.method("PUT")
.path(format!(
"/v5/livedns/domains/{fqdn}/records/{rname}/{rtype}"
))
.body_contains("192.168.0.0");
then.status(201)
.body("{\"cause\":\"\", \"code\":201, \"message\":\"\", \"object\":\"\"}");
});
let server_url = server.base_url();
let handle = tokio::task::spawn_local(async move {
let opts = Opts {
config: Some(temp.to_string_lossy().to_string()),
repeat: Some(1),
..Opts::default()
};
let conf = config::load_config(&opts).expect("Failed to load config");
let ip_source: Box<dyn IPSource> = Box::new(IPSourceMock);
run(&server_url, &ip_source, &conf, &opts)
.await
.expect("Failed when running the update");
});
sleep(Duration::from_secs(4)).await;
handle.abort();
// The first call failed
mock_fail.assert();
// We then retried since the first call failed. The retry succeeds
// so we don't retry again.
mock.assert();
});
}
#[test]
fn repeat_always_update() {
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
LocalSet::new().block_on(&runtime, async {
let mut temp = temp_dir().join("gandi-live-dns-test");
fs::create_dir_all(&temp)
.await
.expect("Failed to create test dir");
temp.push("test.toml");
fs::write(
&temp,
"fqdn = \"example.com\"\nalways_update = true\napi_key = \"xxx\"\nttl = 300\n[[entry]]\nname =\"@\"\n",
)
.await
.expect("Failed to write test config file");
let fqdn = "example.com";
let rname = "@";
let rtype = "A";
let server = MockServer::start();
let mock = server.mock(|when, then| {
when.method("PUT")
.path(format!(
"/v5/livedns/domains/{fqdn}/records/{rname}/{rtype}"
))
.body_contains("192.168.0.0");
then.status(201).body("{\"cause\":\"\", \"code\":201, \"message\":\"\", \"object\":\"\"}");
});
let server_url = server.base_url();
let handle = tokio::task::spawn_local(async move {
let opts = Opts {
config: Some(temp.to_string_lossy().to_string()),
repeat: Some(1),
..Opts::default()
};
let conf = config::load_config(&opts).expect("Failed to load config");
let ip_source: Box<dyn IPSource> = Box::new(IPSourceMock);
run(&server_url, &ip_source, &conf, &opts)
.await
.expect("Failed when running the update");
});
sleep(Duration::from_secs(3)).await;
handle.abort();
// Should update multiple times since always_update
assert!(mock.hits() > 1);
});
}
} }