From db06b7c7d0cba1d9ddf3df04589c84aebee53557 Mon Sep 17 00:00:00 2001 From: Fabian Stamm Date: Mon, 6 Feb 2023 14:21:05 +0100 Subject: [PATCH] Integrating consul catalog for autoconfig of listeners --- Cargo.lock | 585 +++++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 19 +- src/config.rs | 89 ++++++++ src/consul.rs | 169 +++++++++++++++ src/main.rs | 85 +++----- src/udp.rs | 2 +- 6 files changed, 880 insertions(+), 69 deletions(-) create mode 100644 src/config.rs create mode 100644 src/consul.rs diff --git a/Cargo.lock b/Cargo.lock index 8e90a2b..2e729cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,12 +11,29 @@ dependencies = [ "libc", ] +[[package]] +name = "async-trait" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" +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 = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + [[package]] name = "bitflags" version = "1.3.2" @@ -72,6 +89,16 @@ dependencies = [ "unicode-width", ] +[[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.3" @@ -122,6 +149,24 @@ dependencies = [ "syn", ] +[[package]] +name = "encoding_rs" +version = "0.8.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "fastrand" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" +dependencies = [ + "instant", +] + [[package]] name = "fern" version = "0.6.1" @@ -131,6 +176,75 @@ dependencies = [ "log", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" + +[[package]] +name = "futures-sink" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" + +[[package]] +name = "futures-task" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" + +[[package]] +name = "futures-util" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + [[package]] name = "getrandom" version = "0.2.8" @@ -142,6 +256,25 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "h2" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -157,6 +290,77 @@ dependencies = [ "libc", ] +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e011372fa0b68db8350aa7a248930ecc7839bf46d8485577d69f117a75f164c" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "iana-time-zone" version = "0.1.53" @@ -181,6 +385,16 @@ dependencies = [ "cxx-build", ] +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "indexmap" version = "1.9.2" @@ -191,6 +405,21 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "ipnet" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" + [[package]] name = "itoa" version = "1.0.5" @@ -206,6 +435,12 @@ 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.139" @@ -246,6 +481,12 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + [[package]] name = "mio" version = "0.8.5" @@ -258,6 +499,24 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -293,6 +552,51 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" +[[package]] +name = "openssl" +version = "0.10.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -316,12 +620,30 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + [[package]] name = "pin-project-lite" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -386,13 +708,61 @@ dependencies = [ ] [[package]] -name = "rustocat" -version = "0.1.0" +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" dependencies = [ + "winapi", +] + +[[package]] +name = "reqwest" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "rustocat" +version = "0.1.1" +dependencies = [ + "async-trait", "chrono", "fern", "log", "rand", + "reqwest", "serde", "serde_json", "serde_yaml", @@ -406,6 +776,15 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" +[[package]] +name = "schannel" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" +dependencies = [ + "windows-sys 0.42.0", +] + [[package]] name = "scopeguard" version = "1.1.0" @@ -418,6 +797,29 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" +[[package]] +name = "security-framework" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.152" @@ -449,6 +851,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_yaml" version = "0.9.17" @@ -477,6 +891,15 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc47a29ce97772ca5c927f75bac34866b16d64e07f330c3248e2d7226623901b" +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + [[package]] name = "smallvec" version = "1.10.0" @@ -504,6 +927,20 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tempfile" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +dependencies = [ + "cfg-if", + "fastrand", + "libc", + "redox_syscall", + "remove_dir_all", + "winapi", +] + [[package]] name = "termcolor" version = "1.2.0" @@ -524,6 +961,21 @@ dependencies = [ "winapi", ] +[[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.25.0" @@ -555,12 +1007,83 @@ dependencies = [ "syn", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[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", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + +[[package]] +name = "unicode-bidi" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" + [[package]] name = "unicode-ident" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-width" version = "0.1.10" @@ -573,6 +1096,33 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc7ed8ba44ca06be78ea1ad2c3682a43349126c8818054231ee6f4748012aed2" +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" @@ -610,6 +1160,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f219e0d211ba40266969f6dbdd90636da12f75bee4fc9d6c23d1260dadb51454" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.84" @@ -639,6 +1201,16 @@ version = "0.2.84" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" @@ -750,3 +1322,12 @@ name = "windows_x86_64_msvc" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] diff --git a/Cargo.toml b/Cargo.toml index b8b5b41..9fe900c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,22 +1,27 @@ [package] name = "rustocat" -version = "0.1.0" +version = "0.1.1" edition = "2021" description = "Socat in rust with many less features and a configuration file" license = "ISC" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = ["consul"] +consul = [] [dependencies] tokio = { version = "1", features = ["full"] } serde = { version = "1", features = ["derive"] } serde_json = "1" -serde_yaml = "0.9.13" -simple-error = "0.2.3" -rand = "0.8.5" -log = "0.4.17" -fern = "0.6.1" -chrono = "0.4.23" +serde_yaml = "0.9" +simple-error = "0.2" +rand = "0.8" +log = "0.4" +fern = "0.6" +chrono = "0.4" +async-trait = "0.1.64" +reqwest = { version = "0.11", features = ["json"] } [profile.release] opt-level = 3 # Optimize for size. diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..45f5f47 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,89 @@ +use std::{fs::File, path::Path}; + +use serde::Deserialize; +use simple_error::bail; +use tokio::signal::unix::Signal; + +use crate::Result; + +#[derive(Debug, Deserialize)] +pub struct Target { + pub udp: Option, + pub source: String, + pub targets: Vec, +} + +#[derive(Debug, Deserialize)] +pub struct Config { + pub consul: Option, + mappings: Vec, +} + +#[async_trait::async_trait] +pub trait ConfigProvider { + async fn get_targets(&self) -> Result>; + async fn wait_for_change(&mut self) -> Result<()>; +} + +pub struct FileConfigProvider { + sighup_stream: Signal, +} + +impl FileConfigProvider { + pub fn new() -> Self { + Self { + sighup_stream: tokio::signal::unix::signal(tokio::signal::unix::SignalKind::hangup()) + .expect("Failed to create sighup stream"), + } + } + + fn load_yaml(&self, path: &Path) -> Result { + let file = File::open(path)?; + let config: Config = serde_yaml::from_reader(file).expect("Failed to parse!"); //TODO: Print path + + return Ok(config); + } + + fn load_json(&self, path: &Path) -> Result { + let file = File::open(path)?; + let config: Config = serde_json::from_reader(file).expect("Failed to parse!"); //TODO: Print path + + return Ok(config); + } + + pub fn load_config(&self) -> Result { + for path in [ + "config.yaml", + "config.json", + "/etc/rustocat.yaml", + "/etc/rustocat.json", + ] + .iter() + { + // if(p) + let config = if path.ends_with(".yaml") { + self.load_yaml(Path::new(path)) + } else { + self.load_json(Path::new(path)) + }; + if config.is_ok() { + return config; + } + } + bail!("No config file found"); + } +} + +#[async_trait::async_trait] +impl ConfigProvider for FileConfigProvider { + async fn get_targets(&self) -> Result> { + let config = self.load_config()?; + return Ok(config.mappings); + } + + async fn wait_for_change(&mut self) -> Result<()> { + self.sighup_stream.recv().await; + + return Ok(()); + } +} diff --git a/src/consul.rs b/src/consul.rs new file mode 100644 index 0000000..41011eb --- /dev/null +++ b/src/consul.rs @@ -0,0 +1,169 @@ +#![allow(non_snake_case)] + +use std::collections::HashMap; + +use log::warn; +use reqwest::header::HeaderMap; +use serde::{Deserialize, Serialize}; + +use crate::config::ConfigProvider; +use crate::config::Target; + +use crate::Result; + +pub struct ConsulConfigProvider { + consul_config: ConsulConfig, + interval: tokio::time::Interval, +} + +impl ConsulConfigProvider { + pub fn new() -> Self { + Self { + consul_config: ConsulConfig::from_env(), + interval: tokio::time::interval(tokio::time::Duration::from_secs(10)), + } + } +} + +#[async_trait::async_trait] +impl ConfigProvider for ConsulConfigProvider { + async fn get_targets(&self) -> Result> { + let mut targets: Vec = Vec::new(); + + let services = consul_get_services(&self.consul_config).await?; + + // Find consul services and tags + // Format of tags: rustocat:udp:port + // rustocat:tcp:port + for (name, tags) in services { + for tag in tags { + if tag.starts_with("rustocat") { + let parts = tag.split(":").collect::>(); + if parts.len() != 3 { + warn!("Invalid tag: {} on service {}", tag, name); + continue; + } + + let port = parts[2]; + let nodes = consul_get_service_nodes(&self.consul_config, &name).await?; + + let mut t = vec![]; + for node in nodes { + t.push(format!("{}:{}", node.Service.Address, node.Service.Port)); + } + let target = Target { + udp: Some(parts[1] == "udp"), + source: format!("0.0.0.0:{}", port), + targets: t, + }; + targets.push(target); + } + } + } + + Ok(targets) + } + + async fn wait_for_change(&mut self) -> Result<()> { + self.interval.tick().await; + + Ok(()) + } +} + +async fn consul_get_services(config: &ConsulConfig) -> Result>> { + let mut headers = HeaderMap::new(); + if let Some(token) = config.token.clone() { + headers.insert("X-Consul-Token", token.parse().unwrap()); + } + + return Ok(reqwest::Client::new() + .get(format!("{}/v1/catalog/services", config.baseurl)) + .headers(headers) + .send() + .await? + .json::>>() + .await?); +} + +async fn consul_get_service_nodes( + config: &ConsulConfig, + service: &str, +) -> Result> { + let mut headers = HeaderMap::new(); + if let Some(token) = config.token.clone() { + headers.insert("X-Consul-Token", token.parse().unwrap()); + } + + return Ok(reqwest::Client::new() + .get(format!("{}/v1/catalog/services/{service}", config.baseurl)) + .headers(headers) + .send() + .await? + .json::>() + .await?); +} + +#[derive(Eq, Default, PartialEq, Serialize, Deserialize, Debug)] +#[serde(default)] +struct AgentService { + ID: String, + Service: String, + Tags: Option>, + Port: u16, + Address: String, + EnableTagOverride: bool, + CreateIndex: u64, + ModifyIndex: u64, +} + +#[derive(Eq, Default, PartialEq, Serialize, Deserialize, Debug)] +#[serde(default)] +struct HealthCheck { + Node: String, + CheckID: String, + Name: String, + Status: String, + Notes: String, + Output: String, + ServiceID: String, + ServiceName: String, + ServiceTags: Option>, +} + +#[derive(Eq, Default, PartialEq, Serialize, Deserialize, Debug)] +#[serde(default)] +struct Node { + ID: String, + Node: String, + Address: String, + Datacenter: Option, + TaggedAddresses: Option>, + Meta: Option>, + CreateIndex: u64, + ModifyIndex: u64, +} + +#[derive(Eq, Default, PartialEq, Serialize, Deserialize, Debug)] +#[serde(default)] +struct ServiceEntry { + Node: Node, + Service: AgentService, + Checks: Vec, +} + +struct ConsulConfig { + baseurl: String, + token: Option, +} + +impl ConsulConfig { + fn from_env() -> Self { + Self { + baseurl: option_env!("CONSUL_HTTP_ADDR") + .expect("CONSUL_HTTP_ADDR not set") + .to_string(), + token: option_env!("CONSUL_HTTP_TOKEN").map(|s| s.to_string()), + } + } +} diff --git a/src/main.rs b/src/main.rs index 26951dc..98db714 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,69 +1,20 @@ +mod config; mod listener; mod shutdown; mod tcp; mod udp; +#[cfg(feature = "consul")] +mod consul; + use log::{debug, error, info, warn}; -use serde::Deserialize; -use simple_error::bail; use std::collections::{HashMap, HashSet}; use std::error::Error; -use std::fs::File; -use std::path::Path; use std::sync::Arc; -use tokio::signal::unix::{signal, SignalKind}; use tokio::sync::{broadcast, RwLock}; pub type Result = std::result::Result>; -#[derive(Debug, Deserialize)] -struct Target { - udp: Option, - source: String, - targets: Vec, -} - -#[derive(Debug, Deserialize)] -struct Config { - mappings: Vec, -} - -fn load_yaml(path: &Path) -> Result { - let file = File::open(path)?; - let config: Config = serde_yaml::from_reader(file).expect("Failed to parse!"); //TODO: Print path - - return Ok(config); -} - -fn load_json(path: &Path) -> Result { - let file = File::open(path)?; - let config: Config = serde_json::from_reader(file).expect("Failed to parse!"); //TODO: Print path - - return Ok(config); -} - -fn load_config() -> Result { - for path in [ - "config.yaml", - "config.json", - "/etc/rustocat.yaml", - "/etc/rustocat.json", - ] - .iter() - { - // if(p) - let config = if path.ends_with(".yaml") { - load_yaml(Path::new(path)) - } else { - load_json(Path::new(path)) - }; - if config.is_ok() { - return config; - } - } - bail!("No config file found"); -} - // #[derive(Debug)] struct ActiveListener { notify_shutdown: broadcast::Sender<()>, @@ -84,18 +35,34 @@ async fn main() -> Result<()> { }) // Add blanket level filter - .level(log::LevelFilter::Info) - .level_for("rustocat", log::LevelFilter::Trace) + .level_for("rustocat", log::LevelFilter::Debug) .chain(std::io::stdout()) .apply()?; let mut listeners: HashMap = HashMap::new(); - let mut sighup_stream = signal(SignalKind::hangup())?; + let text_config_provider = config::FileConfigProvider::new(); + + let mut target_provider: Box = { + #[cfg(feature = "consul")] + { + let cfg = text_config_provider.load_config()?; + if cfg.consul.is_some() && cfg.consul.unwrap() { + let consul_config_provider = consul::ConsulConfigProvider::new(); + Box::new(consul_config_provider) + } else { + Box::new(text_config_provider) + } + } + + #[cfg(not(feature = "consul"))] + Box::new(text_config_provider) + }; loop { - let config = load_config().expect("config not found"); + let mappings = target_provider.get_targets().await?; let mut required_listeners: HashSet = HashSet::new(); - for target in config.mappings { + for target in mappings { let mut source_str = "".to_owned(); if target.udp == None || target.udp == Some(false) { source_str.push_str("udp:"); @@ -151,7 +118,7 @@ async fn main() -> Result<()> { } } else { if let Err(err) = udp::start_udp_listener(l).await { - error!("udp listener error: {}", err); + error!("udp listener error {}: {}", target.source, err); } } }); @@ -183,7 +150,7 @@ async fn main() -> Result<()> { } } - sighup_stream.recv().await; + target_provider.wait_for_change().await?; info!("Recevied SIGHUP, reloading config!"); } } diff --git a/src/udp.rs b/src/udp.rs index e43210b..3349cb6 100644 --- a/src/udp.rs +++ b/src/udp.rs @@ -12,7 +12,7 @@ use crate::listener::Listener; use crate::shutdown::{Shutdown, ShutdownReceiver}; use crate::Result; -const CONNECTION_TIMEOUT: i32 = 5; +const CONNECTION_TIMEOUT: i32 = 30; #[derive(Clone)] struct UDPMultiSender {