diff options
| author | Owen Jacobson <owen@grimoire.ca> | 2023-10-17 21:17:55 -0400 |
|---|---|---|
| committer | Owen Jacobson <owen@grimoire.ca> | 2023-10-17 22:32:21 -0400 |
| commit | 288205e302d9f6afa06b8602184e983d2080a5b6 (patch) | |
| tree | dfb307e8f3cb82d280e5a0392f11318194e09ef1 | |
CLI tool for updating Route53 DNS for an ASG.
| -rw-r--r-- | .envrc | 3 | ||||
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | Cargo.lock | 2060 | ||||
| -rw-r--r-- | Cargo.toml | 24 | ||||
| -rw-r--r-- | README.md | 3 | ||||
| -rw-r--r-- | src/apply.rs | 87 | ||||
| -rw-r--r-- | src/autoscaling.rs | 34 | ||||
| -rw-r--r-- | src/aws_context.rs | 48 | ||||
| -rw-r--r-- | src/bin/aws-autoscaling-dns/main.rs | 68 | ||||
| -rw-r--r-- | src/converge.rs | 85 | ||||
| -rw-r--r-- | src/dns.rs | 75 | ||||
| -rw-r--r-- | src/ec2.rs | 83 | ||||
| -rw-r--r-- | src/hashable.rs | 37 | ||||
| -rw-r--r-- | src/lib.rs | 10 | ||||
| -rw-r--r-- | src/result.rs | 4 | ||||
| -rw-r--r-- | src/route53.rs | 143 | ||||
| -rw-r--r-- | src/single.rs | 24 |
17 files changed, 2790 insertions, 0 deletions
@@ -0,0 +1,3 @@ +PATH_add target/debug + +source_env_if_exists .envrc.local diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7f1c428 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target/ +/.envrc.local diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..b789bb1 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2060 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +dependencies = [ + "anstyle", + "windows-sys 0.48.0", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "async-trait" +version = "0.1.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "aws-autoscaling-dns" +version = "0.1.0-dev" +dependencies = [ + "anyhow", + "aws-config", + "aws-sdk-autoscaling", + "aws-sdk-ec2", + "aws-sdk-route53", + "aws-types", + "clap", + "futures", + "tokio", + "tokio-stream", + "trust-dns-proto", +] + +[[package]] +name = "aws-config" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6b3804dca60326e07205179847f17a4fce45af3a1106939177ad41ac08a6de" +dependencies = [ + "aws-credential-types", + "aws-http", + "aws-sdk-sso", + "aws-sdk-sts", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-http-tower", + "aws-smithy-json", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "hex", + "http", + "hyper", + "ring", + "time", + "tokio", + "tower", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-credential-types" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70a66ac8ef5fa9cf01c2d999f39d16812e90ec1467bd382cbbb74ba23ea86201" +dependencies = [ + "aws-smithy-async", + "aws-smithy-types", + "fastrand", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-http" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e626370f9ba806ae4c439e49675fd871f5767b093075cdf4fef16cac42ba900" +dependencies = [ + "aws-credential-types", + "aws-smithy-http", + "aws-smithy-types", + "aws-types", + "bytes", + "http", + "http-body", + "lazy_static", + "percent-encoding", + "pin-project-lite", + "tracing", +] + +[[package]] +name = "aws-runtime" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07ac5cf0ff19c1bca0cea7932e11b239d1025a45696a4f44f72ea86e2b8bdd07" +dependencies = [ + "aws-credential-types", + "aws-http", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "fastrand", + "http", + "percent-encoding", + "tracing", + "uuid", +] + +[[package]] +name = "aws-sdk-autoscaling" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8efc42fe2c87e3ff9d0ae730f6bdde2f91a77d774f944b5433a70f05bfda6e1a" +dependencies = [ + "aws-credential-types", + "aws-http", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-query", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "http", + "regex", + "tokio-stream", + "tracing", +] + +[[package]] +name = "aws-sdk-ec2" +version = "0.33.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85092ae53cec4fb99eed322e2b5b5946818e23e92b36db07cf344a6e77e4b617" +dependencies = [ + "aws-credential-types", + "aws-http", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-query", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "fastrand", + "http", + "regex", + "tokio-stream", + "tracing", +] + +[[package]] +name = "aws-sdk-route53" +version = "0.33.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a3a3d8adc398258c8e80be668e082a8845ed788c47ad30d75adfda3cfe86c2" +dependencies = [ + "aws-credential-types", + "aws-http", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "http", + "regex", + "tokio-stream", + "tracing", +] + +[[package]] +name = "aws-sdk-sso" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "903f888ff190e64f6f5c83fb0f8d54f9c20481f1dc26359bb8896f5d99908949" +dependencies = [ + "aws-credential-types", + "aws-http", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "http", + "regex", + "tokio-stream", + "tracing", +] + +[[package]] +name = "aws-sdk-sts" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47ad6bf01afc00423d781d464220bf69fb6a674ad6629cbbcb06d88cdc2be82" +dependencies = [ + "aws-credential-types", + "aws-http", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-query", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "http", + "regex", + "tracing", +] + +[[package]] +name = "aws-sigv4" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7b28f4910bb956b7ab320b62e98096402354eca976c587d1eeccd523d9bac03" +dependencies = [ + "aws-smithy-http", + "form_urlencoded", + "hex", + "hmac", + "http", + "once_cell", + "percent-encoding", + "regex", + "sha2", + "time", + "tracing", +] + +[[package]] +name = "aws-smithy-async" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cdb73f85528b9d19c23a496034ac53703955a59323d581c06aa27b4e4e247af" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", + "tokio-stream", +] + +[[package]] +name = "aws-smithy-client" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c27b2756264c82f830a91cb4d2d485b2d19ad5bea476d9a966e03d27f27ba59a" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-http-tower", + "aws-smithy-types", + "bytes", + "fastrand", + "http", + "http-body", + "hyper", + "hyper-rustls", + "lazy_static", + "pin-project-lite", + "rustls", + "tokio", + "tower", + "tracing", +] + +[[package]] +name = "aws-smithy-http" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54cdcf365d8eee60686885f750a34c190e513677db58bbc466c44c588abf4199" +dependencies = [ + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "http", + "http-body", + "hyper", + "once_cell", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "aws-smithy-http-tower" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "822de399d0ce62829a69dfa8c5cd08efdbe61a7426b953e2268f8b8b52a607bd" +dependencies = [ + "aws-smithy-http", + "aws-smithy-types", + "bytes", + "http", + "http-body", + "pin-project-lite", + "tower", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb1e7ab8fa7ad10c193af7ae56d2420989e9f4758bf03601a342573333ea34f" +dependencies = [ + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-query" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28556a3902091c1f768a34f6c998028921bdab8d47d92586f363f14a4a32d047" +dependencies = [ + "aws-smithy-types", + "urlencoding", +] + +[[package]] +name = "aws-smithy-runtime" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "745e096b3553e7e0f40622aa04971ce52765af82bebdeeac53aa6fc82fe801e6" +dependencies = [ + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "fastrand", + "http", + "http-body", + "once_cell", + "pin-project-lite", + "pin-utils", + "tokio", + "tracing", +] + +[[package]] +name = "aws-smithy-runtime-api" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d0ae0c9cfd57944e9711ea610b48a963fb174a53aabacc08c5794a594b1d02" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-types", + "bytes", + "http", + "tokio", + "tracing", +] + +[[package]] +name = "aws-smithy-types" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d90dbc8da2f6be461fa3c1906b20af8f79d14968fe47f2b7d29d086f62a51728" +dependencies = [ + "base64-simd", + "itoa", + "num-integer", + "ryu", + "serde", + "time", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01d2dedcdd8023043716cfeeb3c6c59f2d447fce365d8e194838891794b23b6" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "aws-types" +version = "0.56.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85aa0451bf8af1bf22a4f028d5d28054507a14be43cb8ac0597a8471fba9edfe" +dependencies = [ + "aws-credential-types", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-types", + "http", + "rustc_version", + "tracing", +] + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" + +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "bytes-utils" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e47d3a8076e283f3acd27400535992edb3ba4b5bb72f8891ad8fbe7932a7d4b9" +dependencies = [ + "bytes", + "either", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "data-encoding" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" + +[[package]] +name = "deranged" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "enum-as-inner" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + +[[package]] +name = "h2" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.4.9", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" +dependencies = [ + "futures-util", + "http", + "hyper", + "log", + "rustls", + "rustls-native-certs", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "outref" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4030760ffd992bef45b0ae3f10ce1aba99e33464c90d14dd7c039884963ddc7a" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.45.0", +] + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d119d7c7ca818f8a53c300863d4f87566aac09943aef5b355bb83969dae75d87" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465c6fc0621e4abc4187a2bda0937bfd4f722c2730b29562e19689ea796c9a4b" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3cbb081b9784b07cceb8824c8583f86db4814d172ab043f3c23f7dc600bf83d" + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustls" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +dependencies = [ + "base64", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" + +[[package]] +name = "serde" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "socket2" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "2.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" +dependencies = [ + "deranged", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.5.4", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "trust-dns-proto" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc775440033cb114085f6f2437682b194fa7546466024b1037e82a48a052a69" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna", + "ipnet", + "once_cell", + "rand", + "smallvec", + "thiserror", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "uuid" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..2e2abd5 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "aws-autoscaling-dns" +version = "0.1.0-dev" + +authors = ["owen@grimoire.ca"] +readme = "README.md" +license = "All rights reserved" +description = "Automatically synch an AWS autoscaling group with a Route53 DNS entry" +edition = "2021" + +publish = false + +[dependencies] +anyhow = "1.0.75" +aws-config = "0.56.1" +aws-sdk-autoscaling = "0.33.0" +aws-sdk-ec2 = "0.33.1" +aws-sdk-route53 = "0.33.1" +aws-types = "0.56.1" +clap = { version = "4.4.6", features = ["derive"] } +futures = "0.3.28" +tokio = { version = "1.33.0", features = ["full"] } +tokio-stream = "0.1.14" +trust-dns-proto = "0.23.0" diff --git a/README.md b/README.md new file mode 100644 index 0000000..92c92ea --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# AWS Autoscaling DNS CLI + +See `--help` output for details. diff --git a/src/apply.rs b/src/apply.rs new file mode 100644 index 0000000..86e92b4 --- /dev/null +++ b/src/apply.rs @@ -0,0 +1,87 @@ +use aws_sdk_route53::types::{Change, ChangeAction, ChangeBatch, ResourceRecordSet}; + +use crate::result::Result; +use crate::route53::Route53; + +pub enum ApplyMode { + DryRun, + Apply, +} + +impl ApplyMode { + pub async fn apply<C, R, I>( + &self, + aws_context: &C, + zone_id: &str, + remove_records: R, + insert_records: I, + ) -> Result<()> + where + C: Route53, + R: IntoIterator<Item = ResourceRecordSet>, + I: IntoIterator<Item = ResourceRecordSet>, + { + match self { + ApplyMode::DryRun => dry_run(zone_id, remove_records, insert_records).await, + ApplyMode::Apply => apply(aws_context, zone_id, remove_records, insert_records).await, + } + } +} + +async fn dry_run<R, I>(zone_id: &str, remove_records: R, insert_records: I) -> Result<()> +where + R: IntoIterator<Item = ResourceRecordSet>, + I: IntoIterator<Item = ResourceRecordSet>, +{ + let remove_records: Vec<_> = remove_records.into_iter().collect(); + let insert_records: Vec<_> = insert_records.into_iter().collect(); + + println!("ZONE: {}", zone_id); + println!("REMOVE: {:#?}", remove_records); + println!("INSERT: {:#?}", insert_records); + + Ok(()) +} + +async fn apply<C, R, I>( + aws_context: &C, + zone_id: &str, + remove_records: R, + insert_records: I, +) -> Result<()> +where + C: Route53, + R: IntoIterator<Item = ResourceRecordSet>, + I: IntoIterator<Item = ResourceRecordSet>, +{ + let mut change_batch = ChangeBatch::builder(); + for remove_record in remove_records { + let change = Change::builder() + .action(ChangeAction::Delete) + .resource_record_set(remove_record) + .build(); + change_batch = change_batch.changes(change); + } + for insert_record in insert_records { + let change = Change::builder() + .action(ChangeAction::Create) + .resource_record_set(insert_record) + .build(); + change_batch = change_batch.changes(change); + } + let change_batch = change_batch.build(); + + if let Some(changes) = change_batch.changes() { + if !changes.is_empty() { + aws_context + .route53() + .change_resource_record_sets() + .hosted_zone_id(zone_id) + .change_batch(change_batch) + .send() + .await?; + } + } + + Ok(()) +} diff --git a/src/autoscaling.rs b/src/autoscaling.rs new file mode 100644 index 0000000..6c96279 --- /dev/null +++ b/src/autoscaling.rs @@ -0,0 +1,34 @@ +use anyhow::anyhow; +use aws_sdk_autoscaling as autoscaling; +use aws_sdk_autoscaling::types::AutoScalingGroup; + +use crate::result::Result; +use crate::single::Single; + +pub trait AutoScaling { + fn autoscaling(&self) -> &autoscaling::Client; +} + +pub async fn asg_by_name<C>(aws_context: &C, name: &str) -> Result<AutoScalingGroup> +where + C: AutoScaling, +{ + let asg_resp = aws_context + .autoscaling() + .describe_auto_scaling_groups() + .auto_scaling_group_names(name) + .send() + .await?; + + let auto_scaling_groups = asg_resp.auto_scaling_groups().unwrap_or(&[]); + let auto_scaling_group = auto_scaling_groups + .iter() + .map(ToOwned::to_owned) + .single() + .ok_or(anyhow!( + "No unique autoscaling group found with name: {}", + name + ))?; + + Ok(auto_scaling_group) +} diff --git a/src/aws_context.rs b/src/aws_context.rs new file mode 100644 index 0000000..2eff941 --- /dev/null +++ b/src/aws_context.rs @@ -0,0 +1,48 @@ +use aws_sdk_autoscaling as asg; +use aws_sdk_ec2 as ec2; +use aws_sdk_route53 as route53; +use aws_types::SdkConfig; + +use crate::autoscaling::AutoScaling; +use crate::ec2::Ec2; +use crate::route53::Route53; + +pub struct AwsContext { + asg: asg::Client, + ec2: ec2::Client, + route53: route53::Client, +} + +impl AwsContext { + pub async fn from_env() -> Self { + let config = aws_config::from_env().load().await; + + Self::new(&config) + } + + fn new(config: &SdkConfig) -> Self { + Self { + asg: asg::Client::new(config), + ec2: ec2::Client::new(config), + route53: route53::Client::new(config), + } + } +} + +impl AutoScaling for AwsContext { + fn autoscaling(&self) -> &asg::Client { + &self.asg + } +} + +impl Ec2 for AwsContext { + fn ec2(&self) -> &ec2::Client { + &self.ec2 + } +} + +impl Route53 for AwsContext { + fn route53(&self) -> &route53::Client { + &self.route53 + } +} diff --git a/src/bin/aws-autoscaling-dns/main.rs b/src/bin/aws-autoscaling-dns/main.rs new file mode 100644 index 0000000..739f0c8 --- /dev/null +++ b/src/bin/aws-autoscaling-dns/main.rs @@ -0,0 +1,68 @@ +use std::fmt::Debug; + +use clap::Parser; +use trust_dns_proto::rr::Name; + +use aws_autoscaling_dns::apply::ApplyMode; +use aws_autoscaling_dns::aws_context::AwsContext; +use aws_autoscaling_dns::converge::named_asg_changes; +use aws_autoscaling_dns::result::Result; + +/// Synchronize a DNS entry with an autoscaling group's running instances. +/// +/// The given DNS name's A and AAAA records in Route53 will be rewritten to exactly +/// match the list of pending and in-service EC2 instances in the specified +/// autoscaling group. Records of other types (including CNAMEs) will not be +/// modified, so this can be used alongside DNS ACME verification, SPF, and other +/// DNS applications. +#[derive(Parser, Debug)] +struct Args { + /// The name of the autoscaling group to synchronize. + #[arg(long)] + autoscaling_group: String, + + /// The DNS domain name to synchronize. The most specific Route53 zone that + /// contains this name will be modified. + #[arg(long)] + dns_name: Name, + + /// The TTL (in seconds) for newly-created records. + #[arg(long, default_value_t = 300)] + dns_ttl: i64, + + /// Print the affected zone ID and pending changes, without applying them (default). + #[arg(long, conflicts_with = "apply")] + dry_run: bool, + + /// Apply the changes to Route53. + #[arg(long)] + apply: bool, +} + +#[tokio::main] +async fn main() -> Result<()> { + let args = Args::parse(); + let name = args.autoscaling_group; + + let aws_context = AwsContext::from_env().await; + + let apply_mode = if args.dry_run { + ApplyMode::DryRun + } else if args.apply { + ApplyMode::Apply + } else { + ApplyMode::DryRun + }; + + let changes = named_asg_changes(&aws_context, &name, &args.dns_name, args.dns_ttl).await?; + apply_mode + .apply( + &aws_context, + &changes.zone_id, + changes.remove, + changes.insert, + ) + .await?; + + Ok(()) +} diff --git a/src/converge.rs b/src/converge.rs new file mode 100644 index 0000000..073e9e6 --- /dev/null +++ b/src/converge.rs @@ -0,0 +1,85 @@ +use std::fmt::Debug; + +use anyhow::anyhow; +use aws_sdk_autoscaling::types::AutoScalingGroup; +use aws_sdk_route53::types::ResourceRecordSet; +use futures::try_join; +use trust_dns_proto::rr::Name; + +use crate::autoscaling::{asg_by_name, AutoScaling}; +use crate::dns::AutoScalingGroupConfig; +use crate::ec2::{instance_recordsets, Ec2}; +use crate::hashable::Hashable; +use crate::result::Result; +use crate::route53::{zone_for_domain, zone_suffix_recordsets, Route53}; + +#[derive(Debug)] +pub struct Changes<T> { + pub zone_id: String, + pub remove: T, + pub insert: T, +} + +async fn changes<C>( + aws_context: &C, + auto_scaling_group: &AutoScalingGroup, + dns_name: &Name, + dns_ttl: i64, +) -> Result<Changes<impl IntoIterator<Item = ResourceRecordSet> + Debug>> +where + C: Ec2 + Route53, +{ + let AutoScalingGroupConfig { + name: asg_name, + live_instance_ids, + } = AutoScalingGroupConfig::try_from(auto_scaling_group)?; + + let zone = zone_for_domain(dns_name, aws_context).await?; + let zone_id = zone + .id() + .ok_or(anyhow!("No ID for hosted zone for name: {}", dns_name))?; + + let (intended_records, actual_records) = try_join!( + instance_recordsets( + &asg_name, + dns_name, + dns_ttl, + &live_instance_ids, + aws_context + ), + zone_suffix_recordsets(dns_name, zone_id, aws_context), + )?; + + let remove_records = actual_records.difference(&intended_records); + let insert_records = intended_records.difference(&actual_records); + + let remove_records = remove_records.map(Hashable::as_ref); + let insert_records = insert_records.map(Hashable::as_ref); + + let remove_records = remove_records.map(ToOwned::to_owned); + let insert_records = insert_records.map(ToOwned::to_owned); + + let remove_records: Vec<_> = remove_records.collect(); + let insert_records: Vec<_> = insert_records.collect(); + + Ok(Changes { + zone_id: zone_id.into(), + remove: remove_records, + insert: insert_records, + }) +} + +pub async fn named_asg_changes<C>( + aws_context: &C, + name: &str, + dns_name: &Name, + dns_ttl: i64, +) -> Result<Changes<impl IntoIterator<Item = ResourceRecordSet> + Debug>> +where + C: AutoScaling + Ec2 + Route53, +{ + let auto_scaling_group = asg_by_name(aws_context, name).await?; + + let changes = changes(aws_context, &auto_scaling_group, dns_name, dns_ttl).await?; + Ok(changes) +} diff --git a/src/dns.rs b/src/dns.rs new file mode 100644 index 0000000..13e3650 --- /dev/null +++ b/src/dns.rs @@ -0,0 +1,75 @@ +use std::convert::TryFrom; +use std::fmt::Debug; + +use anyhow::anyhow; +use aws_sdk_autoscaling::types::{AutoScalingGroup, Instance, LifecycleState}; +use trust_dns_proto::rr::Name; + +use crate::result::{Error, Result}; + +#[derive(Debug)] +pub struct AutoScalingGroupConfig { + pub name: String, + pub live_instance_ids: Vec<String>, +} + +impl AutoScalingGroupConfig { + fn live_instance_ids<'a>(instances: impl IntoIterator<Item = &'a Instance>) -> Vec<String> { + instances + .into_iter() + .filter(|instance| match instance.lifecycle_state() { + // See <https://docs.aws.amazon.com/autoscaling/ec2/userguide/AutoScalingGroupLifecycle.html> + // + // Include pending instances so that they can obtain certs, etc. + Some(LifecycleState::Pending) => true, + Some(LifecycleState::PendingWait) => true, + Some(LifecycleState::PendingProceed) => true, + Some(LifecycleState::InService) => true, + _ => false, + }) + .flat_map(|instance| instance.instance_id()) + .map(ToOwned::to_owned) + .collect() + } +} + +impl TryFrom<&AutoScalingGroup> for AutoScalingGroupConfig { + type Error = Error; + + fn try_from(autoscaling_group: &AutoScalingGroup) -> Result<Self> { + let name = autoscaling_group + .auto_scaling_group_name() + .ok_or(anyhow!("Autoscaling group returned from AWS with no name"))? + .to_owned(); + + let instances = autoscaling_group.instances().unwrap_or(&[]); + + let live_instance_ids = Self::live_instance_ids(instances); + + Ok(Self { + name, + live_instance_ids, + }) + } +} + +pub fn suffixes(mut name: Name) -> Vec<Name> { + let mut names = Vec::new(); + + loop { + names.push(name.clone()); + if name.is_root() { + break; + } else { + name = name.base_name(); + } + } + + names +} + +pub fn absolute(name: Name) -> Result<Name> { + let root = &Name::root(); + let absolute = name.append_name(root)?; + Ok(absolute) +} diff --git a/src/ec2.rs b/src/ec2.rs new file mode 100644 index 0000000..28169e7 --- /dev/null +++ b/src/ec2.rs @@ -0,0 +1,83 @@ +use std::collections::HashSet; + +use aws_sdk_ec2 as ec2; +use aws_sdk_ec2::types::Filter; +use aws_sdk_route53::types::{ResourceRecordSet, RrType}; +use tokio_stream::StreamExt; +use trust_dns_proto::rr::Name; + +use crate::dns::absolute; +use crate::hashable::Hashable; +use crate::result::Result; +use crate::route53::recordset; + +pub trait Ec2 { + fn ec2(&self) -> &ec2::Client; +} + +pub async fn instance_recordsets<C>( + asg_name: &str, + dns_suffix: &Name, + dns_ttl: i64, + live_instance_ids: &[String], + aws_context: &C, +) -> Result<HashSet<Hashable<ResourceRecordSet>>> +where + C: Ec2, +{ + // If there's nothing running, then (a) we don't need to ask AWS about + // running instances, and (b) we can't anyways as the API call requires at + // least one instance ID. Abort here. + if live_instance_ids.is_empty() { + return Ok(HashSet::new()); + } + + let asg_filter = Filter::builder() + .name("tag:aws:autoscaling:groupName") + .values(asg_name) + .build(); + + let mut apex_ip4 = HashSet::new(); + let mut apex_ip6 = HashSet::new(); + + let mut instances_paginator = aws_context + .ec2() + .describe_instances() + .set_instance_ids(Some(live_instance_ids.to_owned())) + .filters(asg_filter) + .into_paginator() + .items() + .send(); + + while let Some(reservation) = instances_paginator.try_next().await? { + let instances = reservation.instances().unwrap_or(&[]); + for instance in instances { + // Mild abuse of the fact that optional values are also iterable + apex_ip4.extend(instance.public_ip_address().map(String::from)); + + let instance_interfaces = instance.network_interfaces().unwrap_or(&[]); + let instance_ip6: Vec<_> = instance_interfaces + .iter() + .flat_map(|interface| interface.ipv6_addresses().unwrap_or(&[])) + // Flatmap here to drop the None values, unwrap the Some values + .flat_map(|ipv6| ipv6.ipv6_address()) + .map(String::from) + .collect(); + + apex_ip6.extend(instance_ip6.iter().map(ToOwned::to_owned).map(String::from)); + } + } + + let apex_hostname = absolute(dns_suffix.clone())?; + let apex_hostname = apex_hostname.to_ascii(); + + let mut asg_recordsets = HashSet::new(); + if !apex_ip4.is_empty() { + asg_recordsets.insert(recordset(&apex_hostname, dns_ttl, RrType::A, apex_ip4).into()); + } + if !apex_ip6.is_empty() { + asg_recordsets.insert(recordset(&apex_hostname, dns_ttl, RrType::Aaaa, apex_ip6).into()); + } + + Ok(asg_recordsets) +} diff --git a/src/hashable.rs b/src/hashable.rs new file mode 100644 index 0000000..22bcdd3 --- /dev/null +++ b/src/hashable.rs @@ -0,0 +1,37 @@ +use std::fmt::Debug; +use std::hash::{Hash, Hasher}; + +use aws_sdk_route53::types::ResourceRecordSet; + +pub trait SimpleHash { + fn hash<H: Hasher>(&self, state: &mut H); +} + +#[derive(Debug, PartialEq, Clone)] +pub struct Hashable<T>(T); + +impl<T> AsRef<T> for Hashable<T> { + fn as_ref(&self) -> &T { + &self.0 + } +} + +impl<T> Eq for Hashable<T> where Hashable<T>: PartialEq {} + +impl<T: SimpleHash> Hash for Hashable<T> { + fn hash<H: Hasher>(&self, state: &mut H) { + self.0.hash(state) + } +} + +impl<T> From<T> for Hashable<T> { + fn from(value: T) -> Self { + Self(value) + } +} + +impl SimpleHash for ResourceRecordSet { + fn hash<H: Hasher>(&self, state: &mut H) { + self.name().hash(state) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..ccfa2ac --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,10 @@ +pub mod apply; +mod autoscaling; +pub mod aws_context; +pub mod converge; +mod dns; +mod ec2; +mod hashable; +pub mod result; +mod route53; +mod single; diff --git a/src/result.rs b/src/result.rs new file mode 100644 index 0000000..7c607c5 --- /dev/null +++ b/src/result.rs @@ -0,0 +1,4 @@ +use anyhow; + +pub type Error = anyhow::Error; +pub type Result<T> = std::result::Result<T, Error>; diff --git a/src/route53.rs b/src/route53.rs new file mode 100644 index 0000000..22e4126 --- /dev/null +++ b/src/route53.rs @@ -0,0 +1,143 @@ +use std::collections::HashSet; +use std::str::FromStr; + +use anyhow::anyhow; +use aws_sdk_route53 as route53; +use aws_sdk_route53::types::{HostedZone, ResourceRecord, ResourceRecordSet, RrType}; +use trust_dns_proto::rr::Name; + +use crate::dns::suffixes; +use crate::hashable::Hashable; +use crate::result::Result; + +pub trait Route53 { + fn route53(&self) -> &route53::Client; +} + +pub async fn zone_for_domain<C>(name: &Name, aws_context: &C) -> Result<HostedZone> +where + C: Route53, +{ + let names = suffixes(name.clone()); + + // Outer pagination loop needs to be pulled out to a trait - this is some hot nonsense. + let mut zone = None; + let mut depth = None; + let mut zones_marker = None; + loop { + let zones_resp = aws_context + .route53() + .list_hosted_zones() + .set_marker(zones_marker) + .send() + .await?; + + let zones = zones_resp.hosted_zones().unwrap_or(&[]); + for candidate_zone in zones.iter() { + let zone_name = match candidate_zone.name() { + None => continue, + Some(name) => name, + }; + let zone_name = Name::from_str(zone_name)?; + let match_position = names.iter().position(|name| *name == zone_name); + match (depth, match_position) { + (None, Some(matched_depth)) => { + zone = Some(candidate_zone.clone()); + depth = Some(matched_depth); + } + (Some(found_depth), Some(matched_depth)) => { + if matched_depth < found_depth { + zone = Some(candidate_zone.clone()); + depth = Some(matched_depth); + } + } + (_, _) => {} + } + } + + if zones_resp.is_truncated() { + zones_marker = zones_resp.next_marker().map(String::from); + } else { + break; + } + } + + zone.ok_or(anyhow!("No Route53 zone found for DNS suffix: {}", name)) +} + +pub async fn zone_suffix_recordsets<C>( + dns_suffix: &Name, + zone_id: &str, + aws_context: &C, +) -> Result<HashSet<Hashable<ResourceRecordSet>>> +where + C: Route53, +{ + let mut suffix_records = HashSet::new(); + + let mut next_record_name = Some(dns_suffix.to_ascii()); + let mut next_record_type = None; + let mut next_record_identifier = None; + + loop { + let records_resp = aws_context + .route53() + .list_resource_record_sets() + .hosted_zone_id(zone_id) + .set_start_record_name(next_record_name) + .set_start_record_type(next_record_type) + .set_start_record_identifier(next_record_identifier) + .send() + .await?; + + let recordsets = records_resp.resource_record_sets().unwrap_or(&[]); + for recordset in recordsets { + let recordset_name = recordset.name().ok_or(anyhow!( + "Record set with no name found in zone: {}", + zone_id + ))?; + let recordset_name = Name::from_str(recordset_name)?; + let recordset_names = suffixes(recordset_name); + + if !recordset_names.iter().any(|name| name == dns_suffix) { + break; + } + + suffix_records.insert(recordset.clone().into()); + } + + if records_resp.is_truncated() { + next_record_name = records_resp.next_record_name().map(String::from); + next_record_type = records_resp.next_record_type().map(Clone::clone); + next_record_identifier = records_resp.next_record_identifier().map(String::from); + } else { + break; + } + } + + Ok(suffix_records) +} + +pub fn recordset<I, S>( + apex_hostname: &str, + dns_ttl: i64, + rr_type: RrType, + addresses: I, +) -> ResourceRecordSet +where + I: IntoIterator<Item = S>, + S: Into<String>, +{ + let apex_ip4_records = addresses + .into_iter() + .map(|address| address.into()) + .map(|address| ResourceRecord::builder().value(address).build()) + .collect(); + + ResourceRecordSet::builder() + .name(apex_hostname) + .r#type(rr_type) + .ttl(dns_ttl) + .set_resource_records(Some(apex_ip4_records)) + .build() +} diff --git a/src/single.rs b/src/single.rs new file mode 100644 index 0000000..b3f0b18 --- /dev/null +++ b/src/single.rs @@ -0,0 +1,24 @@ +pub trait Single { + type Item; + + fn single(self) -> Option<Self::Item>; +} + +impl<T, I> Single for I +where + I: IntoIterator<Item = T>, +{ + type Item = T; + + fn single(self) -> Option<Self::Item> { + let mut iter = self.into_iter(); + + // There are three cases of interest: + // + // 1. `self` has zero items -> return None. + // 2. `self` has two or more items -> return None. + // 3. `self` has exactly one item -> return `iter.next()` unchanged, as + // it holds that item. + iter.next().filter(|_| iter.next().is_none()) + } +} |
