mirror of
https://github.com/SeriousBug/gandi-live-dns-rust
synced 2024-11-13 19:17:26 -06:00
Compare commits
16 commits
Author | SHA1 | Date | |
---|---|---|---|
Kaan Barmore-Genç | 847b6eb1c5 | ||
Kaan Barmore-Genç | 706251c4d8 | ||
Kaan Barmore-Genç | d4dffb19e9 | ||
Kaan Barmore-Genç | 5c6b38f7b0 | ||
8413555d2f | |||
Kaan Barmore-Genç | 5cdd7b9e83 | ||
Kaan Barmore-Genç | 1bb4c7af1c | ||
Kaan Barmore-Genç | b71a78118b | ||
Kaan Barmore-Genç | 27a60d3ac2 | ||
Kaan Barmore-Genç | 327b14a00a | ||
Kaan Barmore-Genç | f8060fad42 | ||
Kaan Barmore-Genç | 7e7a9da65e | ||
Kaan Barmore-Genç | e95cf42b69 | ||
4ada0b7fb4 | |||
d52ca4b840 | |||
Kaan Barmore-Genç | ed83f7dedc |
25
.all-contributorsrc
Normal file
25
.all-contributorsrc
Normal file
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"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"
|
||||
}
|
3
.clippy.toml
Normal file
3
.clippy.toml
Normal file
|
@ -0,0 +1,3 @@
|
|||
# assert_eq!(..., true) or false is a lot clearer when testing functions that
|
||||
# return booleans.
|
||||
bool_assert_comparison = false
|
4
.codecov.yml
Normal file
4
.codecov.yml
Normal file
|
@ -0,0 +1,4 @@
|
|||
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
443
Cargo.lock
generated
|
@ -13,9 +13,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.67"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7724808837b77f4b4de9d283820f9d98bcf496d5692934b857a2399d31ff22e6"
|
||||
checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800"
|
||||
|
||||
[[package]]
|
||||
name = "ascii-canvas"
|
||||
|
@ -168,9 +168,9 @@ checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524"
|
|||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.60"
|
||||
version = "0.1.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d1d8ab452a3936018a687b20e6f7cf5363d713b732b8884001317b0e48aa3"
|
||||
checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -179,9 +179,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "atomic-waker"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a"
|
||||
checksum = "debc29dde2e69f9e47506b525f639ed42300fc014a3e007832592448fa8e4599"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
|
@ -206,6 +206,12 @@ version = "0.13.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
|
||||
|
||||
[[package]]
|
||||
name = "basic-cookies"
|
||||
version = "0.1.4"
|
||||
|
@ -254,15 +260,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.11.1"
|
||||
version = "3.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
|
||||
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.3.0"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
|
||||
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
|
||||
|
||||
[[package]]
|
||||
name = "castaway"
|
||||
|
@ -272,9 +278,9 @@ checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6"
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.78"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
|
||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
|
@ -284,9 +290,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.0.29"
|
||||
version = "4.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d"
|
||||
checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
|
@ -302,9 +308,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.0.21"
|
||||
version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014"
|
||||
checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
|
@ -315,18 +321,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
|
||||
checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "2.0.0"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd7bef69dc86e3c610e4e7aed41035e2a7ed12e72dd7530f61327a6579a4390b"
|
||||
checksum = "c278839b831783b70278b14df4d45e1beb1aad306c07bb796637de9a0e323e8e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
@ -401,10 +407,10 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "die-exit-2"
|
||||
name = "die-exit"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e9ac3d64c794ef4965e2efc9f9eb3c815f40201a25fe47ee19c0801ec428b7c"
|
||||
checksum = "38c7de4151fc657b3ef1c90aebd13b25d089166f1f8c1d0a7950ebdfe6499f49"
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
|
@ -455,9 +461,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.0"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
||||
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||
|
||||
[[package]]
|
||||
name = "ena"
|
||||
|
@ -470,9 +476,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.31"
|
||||
version = "0.8.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b"
|
||||
checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
@ -536,9 +542,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
|
||||
checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
|
@ -551,9 +557,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
|
||||
checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
|
@ -561,15 +567,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
|
||||
checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2"
|
||||
checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
|
@ -578,9 +584,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
|
||||
checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531"
|
||||
|
||||
[[package]]
|
||||
name = "futures-lite"
|
||||
|
@ -599,9 +605,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
|
||||
checksum = "95a73af87da33b5acf53acfebdc339fe592ecf5357ac7c0a7734ab9d8c876a70"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -610,15 +616,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
|
||||
checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
|
||||
checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366"
|
||||
|
||||
[[package]]
|
||||
name = "futures-timer"
|
||||
|
@ -628,9 +634,9 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c"
|
|||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.25"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
|
||||
checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
|
@ -646,20 +652,22 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gandi-live-dns"
|
||||
version = "1.5.0"
|
||||
version = "1.8.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"clap",
|
||||
"die-exit-2",
|
||||
"die-exit",
|
||||
"directories",
|
||||
"futures",
|
||||
"governor",
|
||||
"httpmock",
|
||||
"json",
|
||||
"lazy_static",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"toml",
|
||||
]
|
||||
|
@ -677,9 +685,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gloo-timers"
|
||||
version = "0.2.5"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98c4a8d6391675c6b2ee1a6c8d06e8e2d03605c44cec1270675985a4c2a5500b"
|
||||
checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
|
@ -732,9 +740,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
|||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.0"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
|
@ -754,6 +762,12 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.8"
|
||||
|
@ -797,7 +811,7 @@ dependencies = [
|
|||
"assert-json-diff",
|
||||
"async-object-pool",
|
||||
"async-trait",
|
||||
"base64",
|
||||
"base64 0.13.1",
|
||||
"basic-cookies",
|
||||
"crossbeam-utils",
|
||||
"form_urlencoded",
|
||||
|
@ -818,9 +832,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.23"
|
||||
version = "0.14.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c"
|
||||
checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
|
@ -884,30 +898,30 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.3"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c"
|
||||
checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.42.0",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.7.0"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11b0d96e660696543b251e58030cf9787df56da39dab19ad60eae7353040917e"
|
||||
checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146"
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.1"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330"
|
||||
checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef"
|
||||
dependencies = [
|
||||
"hermit-abi 0.2.6",
|
||||
"hermit-abi 0.3.1",
|
||||
"io-lifetimes",
|
||||
"rustix",
|
||||
"windows-sys 0.42.0",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -954,9 +968,9 @@ checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
|
|||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.60"
|
||||
version = "0.3.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
|
||||
checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
@ -1022,9 +1036,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.138"
|
||||
version = "0.2.139"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
||||
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||
|
||||
[[package]]
|
||||
name = "libnghttp2-sys"
|
||||
|
@ -1119,6 +1133,15 @@ version = "0.4.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c"
|
||||
|
||||
[[package]]
|
||||
name = "nom8"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nonzero_ext"
|
||||
version = "0.3.0"
|
||||
|
@ -1127,19 +1150,19 @@ checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21"
|
|||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.14.0"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6058e64324c71e02bc2b150e4f3bc8286db6c83092132ffa3f6b1eab0f9def5"
|
||||
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
|
||||
dependencies = [
|
||||
"hermit-abi 0.1.19",
|
||||
"hermit-abi 0.2.6",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.16.0"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
|
||||
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
|
@ -1149,9 +1172,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
|||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.79"
|
||||
version = "0.9.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5454462c0eced1e97f2ec09036abc8da362e66802f66fd20f86854d9d8cbcbc4"
|
||||
checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cc",
|
||||
|
@ -1184,15 +1207,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.5"
|
||||
version = "0.9.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba"
|
||||
checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-sys 0.42.0",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1203,9 +1226,9 @@ checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e"
|
|||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.6.2"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6d5014253a1331579ce62aa67443b4a658c5e7dd03d4bc6d302b94474888143"
|
||||
checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"indexmap",
|
||||
|
@ -1316,9 +1339,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.48"
|
||||
version = "1.0.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9d89e5dba24725ae5678020bf8f1357a9aa7ff10736b551adbcd3f8d17d766f"
|
||||
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
@ -1341,9 +1364,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.22"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "556d0f47a940e895261e77dc200d5eadfc6ef644c179c6f5edfc105e3a2292c8"
|
||||
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
@ -1380,9 +1403,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "raw-cpuid"
|
||||
version = "10.6.0"
|
||||
version = "10.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6823ea29436221176fe662da99998ad3b4db2c7f31e7b6f5fe43adccd6320bb"
|
||||
checksum = "c307f7aacdbab3f0adee67d52739a1d71112cc068d6fab169ddeb18e48877fad"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
@ -1409,9 +1432,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.7.0"
|
||||
version = "1.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
|
||||
checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
@ -1426,11 +1449,11 @@ checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
|||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.13"
|
||||
version = "0.11.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c"
|
||||
checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.21.0",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
|
@ -1480,23 +1503,23 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.36.5"
|
||||
version = "0.36.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588"
|
||||
checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.42.0",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.20.7"
|
||||
version = "0.20.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c"
|
||||
checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
|
@ -1506,11 +1529,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55"
|
||||
checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.21.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1527,12 +1550,11 @@ checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
|
|||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.20"
|
||||
version = "0.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
|
||||
checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"windows-sys 0.36.1",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1553,18 +1575,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.151"
|
||||
version = "1.0.152"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0"
|
||||
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.151"
|
||||
version = "1.0.152"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8"
|
||||
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1573,9 +1595,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.90"
|
||||
version = "1.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8778cc0b528968fe72abec38b5db5a20a70d148116cd9325d2bc5f5180ca3faf"
|
||||
checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
|
@ -1592,6 +1614,15 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_urlencoded"
|
||||
version = "0.7.1"
|
||||
|
@ -1606,9 +1637,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.14"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d"
|
||||
checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
|
@ -1616,9 +1647,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.0"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
|
||||
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
@ -1698,9 +1729,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.106"
|
||||
version = "1.0.107"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ee3a69cd2c7e06684677e5629b3878b253af05e4714964204279c6bc02cf0b"
|
||||
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1720,9 +1751,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
||||
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
@ -1777,15 +1808,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.23.0"
|
||||
version = "1.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46"
|
||||
checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
|
@ -1825,9 +1856,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.4"
|
||||
version = "0.7.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740"
|
||||
checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
|
@ -1839,11 +1870,36 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.10"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f"
|
||||
checksum = "f7afcae9e3f0fe2c370fd4657108972cbb2fa9db1b9f84849cefd80741b01cb6"
|
||||
dependencies = [
|
||||
"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]]
|
||||
|
@ -1897,9 +1953,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "try-lock"
|
||||
version = "0.2.3"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
|
||||
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
|
@ -1912,9 +1968,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.8"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
|
||||
checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
|
@ -2012,9 +2068,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.83"
|
||||
version = "0.2.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
|
||||
checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
|
@ -2022,9 +2078,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.83"
|
||||
version = "0.2.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
|
||||
checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
|
@ -2037,9 +2093,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.33"
|
||||
version = "0.4.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d"
|
||||
checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
|
@ -2049,9 +2105,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.83"
|
||||
version = "0.2.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
|
||||
checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
|
@ -2059,9 +2115,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.83"
|
||||
version = "0.2.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
|
||||
checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -2072,15 +2128,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.83"
|
||||
version = "0.2.84"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
|
||||
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.60"
|
||||
version = "0.3.61"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
|
||||
checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
|
@ -2145,19 +2201,6 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "windows-sys"
|
||||
version = "0.42.0"
|
||||
|
@ -2165,85 +2208,79 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc 0.42.0",
|
||||
"windows_i686_gnu 0.42.0",
|
||||
"windows_i686_msvc 0.42.0",
|
||||
"windows_x86_64_gnu 0.42.0",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc 0.42.0",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[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]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.0"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
|
||||
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.36.1"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
|
||||
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.36.1"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
|
||||
checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.36.1"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
|
||||
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.36.1"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
|
||||
checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.0"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
|
||||
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.36.1"
|
||||
version = "0.42.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
|
||||
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
|
|
12
Cargo.toml
12
Cargo.toml
|
@ -1,11 +1,11 @@
|
|||
[package]
|
||||
name = "gandi-live-dns"
|
||||
description = "Automatically updates your IP address in Gandi's Live DNS. Makes it possible to use Gandi as a dynamic DNS system."
|
||||
version = "1.6.0"
|
||||
version = "1.8.0"
|
||||
edition = "2021"
|
||||
authors = ["Kaan Barmore-Genç <kaan@bgenc.net>"]
|
||||
license = "MIT"
|
||||
readme = "Readme.md"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/SeriousBug/gandi-live-dns-rust"
|
||||
|
||||
[profile.release]
|
||||
|
@ -17,7 +17,7 @@ reqwest = { version = "0.11", default-features = false, features = [
|
|||
"json",
|
||||
"rustls-tls",
|
||||
] }
|
||||
toml = "0.5"
|
||||
toml = "0.7"
|
||||
json = "0.12"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
directories = "4.0"
|
||||
|
@ -32,12 +32,14 @@ futures = "0.3"
|
|||
anyhow = "1.0"
|
||||
governor = "0.5"
|
||||
async-trait = "0.1"
|
||||
die-exit-2 = "0.4"
|
||||
die-exit = "0.4"
|
||||
thiserror = "1.0.38"
|
||||
|
||||
[dev-dependencies]
|
||||
httpmock = "0.6"
|
||||
regex = "1.6"
|
||||
lazy_static = "1.4.0"
|
||||
|
||||
[dev-dependencies.die-exit-2]
|
||||
[dev-dependencies.die-exit]
|
||||
version = "0.4"
|
||||
features = ["test"]
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
## gandi-live-dns-rust
|
||||
## Gandi Live Dns Rust <!-- omit in toc -->
|
||||
|
||||
[![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)
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
|
||||
[![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
|
||||
[Gandi](https://gandi.net)'s domain configuration. Thanks to Gandi's
|
||||
|
@ -13,6 +21,23 @@ 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
|
||||
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
|
||||
|
||||
The Gandi Live DNS API is rate limited at 30 requests per minute. This program
|
||||
|
@ -44,8 +69,14 @@ Download the latest version from the releases page, extract it from the archive,
|
|||
|
||||
### With docker
|
||||
|
||||
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,
|
||||
arm64, armv6, and armv7 platforms. Follow the steps below to use these images.
|
||||
Container images are available on both Github Packages and Docker Hub.
|
||||
- [ghcr.io/seriousbug/gandi-live-dns-rust](https://github.com/users/seriousbug/packages/container/package/gandi-live-dns-rust)
|
||||
- [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)
|
||||
- Follow the instructions in the example config to get your API key and put it in the config
|
||||
|
@ -84,15 +115,23 @@ 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:
|
||||
|
||||
```yml
|
||||
gandi-live-dns:
|
||||
image: seriousbug/gandi-live-dns-rust:latest
|
||||
restart: always
|
||||
volumes:
|
||||
- ./gandi.toml:/gandi.toml:ro
|
||||
# Repeat the update every day
|
||||
command: --repeat=86400
|
||||
gandi-live-dns:
|
||||
image: seriousbug/gandi-live-dns-rust:latest
|
||||
restart: always
|
||||
volumes:
|
||||
- ./gandi.toml:/gandi.toml:ro
|
||||
# Repeat the update every day
|
||||
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
|
||||
|
||||
The `Packaging` folder contains a Systemd service and timer, which you can use
|
||||
|
@ -128,7 +167,8 @@ Docker with `docker login`. Then follow these steps:
|
|||
- Create a release on Github
|
||||
- Make sure to create a tag for the release version on `master`
|
||||
- Upload the binary archives to the Github release
|
||||
- Update the AUR version manually
|
||||
- Update the AUR version
|
||||
- Run `cargo publish` to update the crates.io version
|
||||
|
||||
## Alternatives
|
||||
|
||||
|
@ -136,3 +176,28 @@ 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)
|
||||
- [Yago Riveiro's Python based updater](https://github.com/yriveiro/giu)
|
||||
- [ 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 -->
|
|
@ -19,8 +19,9 @@ ttl = 300
|
|||
# Ipify is used by default. If you want to change it, uncomment the one you want
|
||||
# to use.
|
||||
#
|
||||
# ip_source = "Ipify"
|
||||
# ip_source = "Icanhazip"
|
||||
#ip_source = "Ipify" # An open source and public service. https://github.com/rdegges/ipify-api
|
||||
#ip_source = "Icanhazip" # A free service, currently run by Cloudflare. https://major.io/2021/06/06/a-new-future-for-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.
|
||||
|
||||
|
|
|
@ -3,9 +3,7 @@
|
|||
# Make sure `cross` is installed.
|
||||
# You'll also need `sed`, a relatively recent version of `tar`, and `7z`.
|
||||
#
|
||||
# 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"
|
||||
DOCKER="docker"
|
||||
#
|
||||
shopt -s extglob
|
||||
# Trap errors and interrupts
|
||||
|
@ -49,6 +47,9 @@ VERSION=$(sed -nr 's/^version *= *"([0-9.]+)"/\1/p' Cargo.toml | head --lines=1)
|
|||
# Make the builds
|
||||
for target in "${!TARGETS[@]}"; do
|
||||
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}"
|
||||
if [[ "${target}" =~ .*"windows".* ]]; then
|
||||
zip -j "gandi-live-dns.${VERSION}.${TARGETS[${target}]}.zip" target/"${target}"/release/gandi-live-dns.exe 1>/dev/null
|
||||
|
@ -76,4 +77,6 @@ ${DOCKER} buildx build . \
|
|||
--file "Dockerfile" \
|
||||
--tag "seriousbug/gandi-live-dns-rust:latest" \
|
||||
--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
|
||||
|
|
|
@ -1,13 +1,26 @@
|
|||
use crate::opts;
|
||||
use directories::ProjectDirs;
|
||||
use serde::Deserialize;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::{fs, io};
|
||||
use thiserror::Error;
|
||||
|
||||
fn default_types() -> Vec<String> {
|
||||
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)]
|
||||
pub struct Entry {
|
||||
pub name: String,
|
||||
|
@ -25,6 +38,7 @@ fn default_ttl() -> u32 {
|
|||
pub enum IPSourceName {
|
||||
Ipify,
|
||||
Icanhazip,
|
||||
SeeIP,
|
||||
}
|
||||
|
||||
impl Default for IPSourceName {
|
||||
|
@ -44,6 +58,8 @@ pub struct Config {
|
|||
pub entry: Vec<Entry>,
|
||||
#[serde(default = "default_ttl")]
|
||||
pub ttl: u32,
|
||||
#[serde(default)]
|
||||
pub always_update: bool,
|
||||
}
|
||||
|
||||
const DEFAULT_TYPES: &[&str] = &["A"];
|
||||
|
@ -62,18 +78,20 @@ impl Config {
|
|||
}
|
||||
}
|
||||
|
||||
fn load_config_from<P: std::convert::AsRef<std::path::Path>>(path: P) -> anyhow::Result<Config> {
|
||||
fn load_config_from<P: std::convert::AsRef<std::path::Path>>(
|
||||
path: P,
|
||||
) -> Result<Config, ConfigError> {
|
||||
let contents = fs::read_to_string(path)?;
|
||||
Ok(toml::from_str(&contents)?)
|
||||
}
|
||||
|
||||
pub fn load_config(opts: &opts::Opts) -> anyhow::Result<Config> {
|
||||
pub fn load_config(opts: &opts::Opts) -> Result<Config, ConfigError> {
|
||||
let mut config = match &opts.config {
|
||||
Some(config_path) => load_config_from(config_path),
|
||||
None => {
|
||||
let confpath = ProjectDirs::from("me", "kaangenc", "gandi-dynamic-dns")
|
||||
.map(|dir| PathBuf::from(dir.config_dir()).join("config.toml"))
|
||||
.ok_or_else(|| anyhow::anyhow!("Can't find config directory"));
|
||||
.ok_or(ConfigError::ConfigNotFound());
|
||||
confpath
|
||||
.and_then(|path| {
|
||||
println!("Checking for config: {}", path.to_string_lossy());
|
||||
|
@ -102,11 +120,14 @@ pub fn load_config(opts: &opts::Opts) -> anyhow::Result<Config> {
|
|||
Ok(config)
|
||||
}
|
||||
|
||||
pub fn validate_config(config: &Config) -> anyhow::Result<()> {
|
||||
pub fn validate_config(config: &Config) -> Result<(), ConfigError> {
|
||||
for entry in &config.entry {
|
||||
for entry_type in Config::types(entry) {
|
||||
if entry_type != "A" && entry_type != "AAAA" {
|
||||
anyhow::bail!("Entry {} has invalid type {}", entry.name, entry_type);
|
||||
return Err(ConfigError::Validation(
|
||||
entry.name.clone(),
|
||||
entry_type.to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -156,6 +177,7 @@ name = "@"
|
|||
assert_eq!(conf.entry[1].types, vec!["A".to_string()]);
|
||||
// default
|
||||
assert_eq!(conf.ip_source, IPSourceName::Ipify);
|
||||
assert_eq!(conf.always_update, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -170,6 +192,7 @@ fqdn = "example.com"
|
|||
api_key = "yyy"
|
||||
ttl = 1200
|
||||
ip_source = "Icanhazip"
|
||||
always_update = true
|
||||
|
||||
[[entry]]
|
||||
name = "www"
|
||||
|
@ -193,6 +216,7 @@ name = "@"
|
|||
assert_eq!(conf.entry[0].name, "www");
|
||||
assert_eq!(conf.entry[1].name, "@");
|
||||
assert_eq!(conf.ip_source, IPSourceName::Icanhazip);
|
||||
assert_eq!(conf.always_update, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -2,5 +2,5 @@ The IP sources. These are APIs that we can query to get the IP address of the
|
|||
current service.
|
||||
|
||||
The tests under this directory are all marked to be skipped, the tests hit the
|
||||
actual APIs and can be flakey in CI. Make sure to run the tests manually if you
|
||||
actual APIs and can be flaky in CI. Make sure to run the tests manually if you
|
||||
have to modify the code.
|
||||
|
|
9
src/ip_source/common.rs
Normal file
9
src/ip_source/common.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
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>;
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use super::ip_source::IPSource;
|
||||
use crate::ClientError;
|
||||
|
||||
pub(crate) struct IPSourceIcanhazip {}
|
||||
use super::common::IPSource;
|
||||
|
||||
async fn get_ip(api_url: &str) -> anyhow::Result<String> {
|
||||
pub(crate) struct IPSourceIcanhazip;
|
||||
|
||||
async fn get_ip(api_url: &str) -> Result<String, ClientError> {
|
||||
let response = reqwest::get(api_url).await?;
|
||||
let text = response.text().await?;
|
||||
Ok(text)
|
||||
|
@ -12,14 +14,14 @@ async fn get_ip(api_url: &str) -> anyhow::Result<String> {
|
|||
|
||||
#[async_trait]
|
||||
impl IPSource for IPSourceIcanhazip {
|
||||
async fn get_ipv4() -> anyhow::Result<String> {
|
||||
async fn get_ipv4(&self) -> Result<String, ClientError> {
|
||||
Ok(get_ip("https://ipv4.icanhazip.com")
|
||||
.await?
|
||||
// icanazip puts a newline at the end
|
||||
.trim()
|
||||
.to_string())
|
||||
}
|
||||
async fn get_ipv6() -> anyhow::Result<String> {
|
||||
async fn get_ipv6(&self) -> Result<String, ClientError> {
|
||||
Ok(get_ip("https://ipv6.icanhazip.com")
|
||||
.await?
|
||||
// icanazip puts a newline at the end
|
||||
|
@ -32,13 +34,15 @@ impl IPSource for IPSourceIcanhazip {
|
|||
mod tests {
|
||||
use regex::Regex;
|
||||
|
||||
use super::IPSource;
|
||||
use crate::ip_source::common::IPSource;
|
||||
|
||||
use super::IPSourceIcanhazip;
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore]
|
||||
async fn ipv4_test() {
|
||||
let ipv4 = IPSourceIcanhazip::get_ipv4()
|
||||
let ipv4 = IPSourceIcanhazip
|
||||
.get_ipv4()
|
||||
.await
|
||||
.expect("Failed to get the IP address");
|
||||
assert!(Regex::new(r"^\d+[.]\d+[.]\d+[.]\d+$")
|
||||
|
@ -49,7 +53,8 @@ mod tests {
|
|||
#[tokio::test]
|
||||
#[ignore]
|
||||
async fn ipv6_test() {
|
||||
let ipv6 = IPSourceIcanhazip::get_ipv6()
|
||||
let ipv6 = IPSourceIcanhazip
|
||||
.get_ipv6()
|
||||
.await
|
||||
.expect("Failed to get the IP address");
|
||||
assert!(Regex::new(r"^([0-9a-fA-F]*:){7}[0-9a-fA-F]*$")
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
#[async_trait]
|
||||
pub trait IPSource {
|
||||
async fn get_ipv4() -> anyhow::Result<String>;
|
||||
async fn get_ipv6() -> anyhow::Result<String>;
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
use super::ip_source::IPSource;
|
||||
use crate::ClientError;
|
||||
|
||||
pub(crate) struct IPSourceIpify {}
|
||||
use super::common::IPSource;
|
||||
|
||||
async fn get_ip(api_url: &str) -> anyhow::Result<String> {
|
||||
pub(crate) struct IPSourceIpify;
|
||||
|
||||
async fn get_ip(api_url: &str) -> Result<String, ClientError> {
|
||||
let response = reqwest::get(api_url).await?;
|
||||
let text = response.text().await?;
|
||||
Ok(text)
|
||||
|
@ -12,10 +14,10 @@ async fn get_ip(api_url: &str) -> anyhow::Result<String> {
|
|||
|
||||
#[async_trait]
|
||||
impl IPSource for IPSourceIpify {
|
||||
async fn get_ipv4() -> anyhow::Result<String> {
|
||||
async fn get_ipv4(&self) -> Result<String, ClientError> {
|
||||
get_ip("https://api.ipify.org").await
|
||||
}
|
||||
async fn get_ipv6() -> anyhow::Result<String> {
|
||||
async fn get_ipv6(&self) -> Result<String, ClientError> {
|
||||
get_ip("https://api6.ipify.org").await
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +32,8 @@ mod tests {
|
|||
#[tokio::test]
|
||||
#[ignore]
|
||||
async fn ipv4_test() {
|
||||
let ipv4 = IPSourceIpify::get_ipv4()
|
||||
let ipv4 = IPSourceIpify
|
||||
.get_ipv4()
|
||||
.await
|
||||
.expect("Failed to get the IP address");
|
||||
assert!(Regex::new(r"^\d+[.]\d+[.]\d+[.]\d+$")
|
||||
|
@ -41,7 +44,8 @@ mod tests {
|
|||
#[tokio::test]
|
||||
#[ignore]
|
||||
async fn ipv6_test() {
|
||||
let ipv6 = IPSourceIpify::get_ipv6()
|
||||
let ipv6 = IPSourceIpify
|
||||
.get_ipv6()
|
||||
.await
|
||||
.expect("Failed to get the IP address");
|
||||
assert!(Regex::new(r"^([0-9a-fA-F]*:){7}[0-9a-fA-F]*$")
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
pub(crate) mod common;
|
||||
pub(crate) mod icanhazip;
|
||||
pub(crate) mod ip_source;
|
||||
pub(crate) mod ipify;
|
||||
pub(crate) mod seeip;
|
||||
|
|
55
src/ip_source/seeip.rs
Normal file
55
src/ip_source/seeip.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
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()))
|
||||
}
|
||||
}
|
550
src/main.rs
550
src/main.rs
|
@ -1,28 +1,63 @@
|
|||
use crate::config::Config;
|
||||
use crate::gandi::GandiAPI;
|
||||
use crate::ip_source::{ip_source::IPSource, ipify::IPSourceIpify};
|
||||
use crate::ip_source::{common::IPSource, ipify::IPSourceIpify};
|
||||
use clap::Parser;
|
||||
use config::IPSourceName;
|
||||
use config::{ConfigError, IPSourceName};
|
||||
use ip_source::icanhazip::IPSourceIcanhazip;
|
||||
use ip_source::seeip::IPSourceSeeIP;
|
||||
use opts::Opts;
|
||||
use reqwest::header::InvalidHeaderValue;
|
||||
use reqwest::{header, Client, ClientBuilder, StatusCode};
|
||||
use serde::Serialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{num::NonZeroU32, sync::Arc, time::Duration};
|
||||
use tokio::join;
|
||||
use tokio::{self, task::JoinHandle, time::sleep};
|
||||
mod config;
|
||||
mod gandi;
|
||||
mod ip_source;
|
||||
mod opts;
|
||||
use die_exit_2::*;
|
||||
use die_exit::*;
|
||||
use thiserror::Error;
|
||||
|
||||
/// 30 requests per minute, see https://api.gandi.net/docs/reference/
|
||||
const GANDI_RATE_LIMIT: u32 = 30;
|
||||
/// If we hit the rate limit, wait up to this many seconds before next attempt
|
||||
const GANDI_DELAY_JITTER: u64 = 20;
|
||||
|
||||
fn api_client(api_key: &str) -> anyhow::Result<Client> {
|
||||
#[derive(Error, Debug)]
|
||||
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 key = format!("Apikey {}", api_key);
|
||||
let key = format!("Apikey {api_key}");
|
||||
let mut auth_value = header::HeaderValue::from_str(&key)?;
|
||||
let mut headers = header::HeaderMap::new();
|
||||
auth_value.set_sensitive(true);
|
||||
|
@ -39,77 +74,202 @@ pub struct APIPayload {
|
|||
pub rrset_ttl: u32,
|
||||
}
|
||||
|
||||
async fn run<IP: IPSource>(base_url: &str, conf: &Config) -> anyhow::Result<()> {
|
||||
config::validate_config(conf).die_with(|error| format!("Invalid config: {}", error));
|
||||
println!("Finding out the IP address...");
|
||||
let ipv4_result = IP::get_ipv4().await;
|
||||
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(Debug)]
|
||||
struct ResponseFeedback {
|
||||
entry_name: String,
|
||||
entry_type: String,
|
||||
response: Result<String, ApiError>,
|
||||
}
|
||||
|
||||
let client = api_client(&conf.api_key)?;
|
||||
let mut tasks: Vec<JoinHandle<(StatusCode, String)>> = Vec::new();
|
||||
println!("Attempting to update DNS entries now");
|
||||
#[derive(Deserialize)]
|
||||
// Allowing dead code because this is the API response we get from Gandi.
|
||||
// We don't necessarily need all the fields, but we get them anyway.
|
||||
#[allow(dead_code)]
|
||||
struct ApiResponse {
|
||||
message: String,
|
||||
cause: Option<String>,
|
||||
code: Option<i32>,
|
||||
object: Option<String>,
|
||||
}
|
||||
|
||||
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));
|
||||
async fn run(
|
||||
base_url: &str,
|
||||
ip_source: &Box<dyn IPSource>,
|
||||
conf: &Config,
|
||||
opts: &Opts,
|
||||
) -> Result<(), ClientError> {
|
||||
let mut last_ipv4: Option<String> = None;
|
||||
let mut last_ipv6: Option<String> = None;
|
||||
|
||||
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" => ipv4.die_with(|error| format!("Needed IPv4 for {fqdn}: {error}")),
|
||||
"AAAA" => ipv6.die_with(|error| format!("Needed IPv6 for {fqdn}: {error}")),
|
||||
bad_entry_type => die!("Unexpected type in config: {}", bad_entry_type),
|
||||
};
|
||||
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 task = tokio::task::spawn(async move {
|
||||
task_governor.until_ready_with_jitter(retry_jitter).await;
|
||||
println!("Updating {} record for {}", entry_type, &fqdn);
|
||||
match req.send().await {
|
||||
Ok(response) => (
|
||||
response.status(),
|
||||
response
|
||||
.text()
|
||||
.await
|
||||
.unwrap_or_else(|error| error.to_string()),
|
||||
),
|
||||
Err(error) => (StatusCode::IM_A_TEAPOT, error.to_string()),
|
||||
}
|
||||
});
|
||||
tasks.push(task);
|
||||
loop {
|
||||
println!("Finding out the IP address...");
|
||||
let (ipv4_result, ipv6_result) = join!(ip_source.get_ipv4(), ip_source.get_ipv6());
|
||||
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}"),
|
||||
}
|
||||
}
|
||||
|
||||
let results = futures::future::try_join_all(tasks).await?;
|
||||
println!("Updates done for {} entries", results.len());
|
||||
for (status, body) in results {
|
||||
println!("{} - {}", status, body);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
let results = futures::future::try_join_all(tasks).await?;
|
||||
// Only count successfull requests
|
||||
println!(
|
||||
"Updates done for {} entries",
|
||||
results
|
||||
.iter()
|
||||
.filter_map(|item| item.as_ref().ok())
|
||||
.filter(|item| item.response.is_ok())
|
||||
.count()
|
||||
);
|
||||
for item in &results {
|
||||
match item {
|
||||
Ok(value) => println!(
|
||||
"{}",
|
||||
match &value.response {
|
||||
Ok(val) => format!(
|
||||
"Record '{}' ({}): {}",
|
||||
value.entry_name, value.entry_type, val
|
||||
),
|
||||
Err(err) => format!(
|
||||
"Record '{}' ({}): {}",
|
||||
value.entry_name, value.entry_type, err
|
||||
),
|
||||
}
|
||||
),
|
||||
Err(err) => println!("{err}"),
|
||||
}
|
||||
}
|
||||
if results
|
||||
.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 {
|
||||
// If configured to repeat, do so
|
||||
sleep(Duration::from_secs(repeat)).await;
|
||||
continue;
|
||||
}
|
||||
// Otherwise this is one-shot, we should exit now
|
||||
break;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -118,53 +278,45 @@ async fn run<IP: IPSource>(base_url: &str, conf: &Config) -> anyhow::Result<()>
|
|||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
let opts = opts::Opts::parse();
|
||||
let conf = config::load_config(&opts)
|
||||
.die_with(|error| format!("Failed to read config file: {}", error));
|
||||
let conf = config::load_config(&opts)?;
|
||||
|
||||
// run indefinitely if repeat is given
|
||||
if let Some(delay) = opts.repeat {
|
||||
loop {
|
||||
run_dispatch(&conf).await.ok();
|
||||
sleep(Duration::from_secs(delay)).await
|
||||
}
|
||||
}
|
||||
// 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,
|
||||
}
|
||||
let ip_source: Box<dyn IPSource> = match conf.ip_source {
|
||||
IPSourceName::Ipify => Box::new(IPSourceIpify),
|
||||
IPSourceName::Icanhazip => Box::new(IPSourceIcanhazip),
|
||||
IPSourceName::SeeIP => Box::new(IPSourceSeeIP),
|
||||
};
|
||||
config::validate_config(&conf)?;
|
||||
run("https://api.gandi.net", &ip_source, &conf, &opts).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::env::temp_dir;
|
||||
|
||||
use crate::{config, ip_source::ip_source::IPSource, opts::Opts, run};
|
||||
use crate::{config, ip_source::common::IPSource, opts::Opts, run, ClientError};
|
||||
use async_trait::async_trait;
|
||||
use httpmock::MockServer;
|
||||
use tokio::fs;
|
||||
use lazy_static::lazy_static;
|
||||
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]
|
||||
impl IPSource for IPSourceMock {
|
||||
async fn get_ipv4() -> anyhow::Result<String> {
|
||||
async fn get_ipv4(&self) -> Result<String, ClientError> {
|
||||
Ok("192.168.0.0".to_string())
|
||||
}
|
||||
async fn get_ipv6() -> anyhow::Result<String> {
|
||||
async fn get_ipv6(&self) -> Result<String, ClientError> {
|
||||
Ok("fe80:0000:0208:74ff:feda:625c".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn create_repo_success_test() {
|
||||
async fn single_shot() {
|
||||
let mut temp = temp_dir().join("gandi-live-dns-test");
|
||||
fs::create_dir_all(&temp)
|
||||
.await
|
||||
|
@ -186,7 +338,8 @@ mod tests {
|
|||
"/v5/livedns/domains/{fqdn}/records/{rname}/{rtype}"
|
||||
))
|
||||
.body_contains("192.168.0.0");
|
||||
then.status(200);
|
||||
then.status(201)
|
||||
.body("{\"cause\":\"\", \"code\":201, \"message\":\"\", \"object\":\"\"}");
|
||||
});
|
||||
|
||||
let opts = Opts {
|
||||
|
@ -194,11 +347,202 @@ mod tests {
|
|||
..Opts::default()
|
||||
};
|
||||
let conf = config::load_config(&opts).expect("Failed to load config");
|
||||
run::<IPSourceMock>(server.base_url().as_str(), &conf)
|
||||
let ip_source: Box<dyn IPSource> = Box::new(IPSourceMock);
|
||||
run(server.base_url().as_str(), &ip_source, &conf, &opts)
|
||||
.await
|
||||
.expect("Failed when running the update");
|
||||
|
||||
// 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue