diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 580836933c5..083728b0012 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -25,6 +25,9 @@ # --- CORE --- /core @osrd-project/core-maintainers +# --- CORE CONTROLLER --- +/core_controller @osrd-project/devops + # --- CORE PHYSICS --- /core/*Physics*.java @osrd-project/physics /core/*Integration*.java @osrd-project/physics diff --git a/.github/scripts/bake-metadata.py b/.github/scripts/bake-metadata.py index 482eb978a96..1ba5186c738 100755 --- a/.github/scripts/bake-metadata.py +++ b/.github/scripts/bake-metadata.py @@ -37,15 +37,21 @@ def suffix(self): TARGETS = [ Target(name="core", image="core", release=True), Target(name="core-build", image="core", variant="build"), + Target(name="editoast", image="editoast", release=True), Target(name="editoast-test", image="editoast", variant="test"), + Target(name="front-devel", image="front", variant="devel"), Target(name="front-nginx", image="front", variant="nginx"), Target(name="front-build", image="front", variant="build"), Target(name="front-tests", image="front", variant="tests"), + Target(name="gateway-standalone", image="gateway", variant="standalone"), Target(name="gateway-test", image="gateway", variant="test"), Target(name="gateway-front", image="gateway", variant="front", release=True), + + Target(name="core_controller", image="core_controller", release=True), + Target(name="core_controller-test", image="core_controller", variant="test"), ] diff --git a/core_controller/.gitignore b/core_controller/.gitignore new file mode 100644 index 00000000000..3df278eceea --- /dev/null +++ b/core_controller/.gitignore @@ -0,0 +1,2 @@ +target +.vscode diff --git a/core_controller/Cargo.lock b/core_controller/Cargo.lock new file mode 100644 index 00000000000..5e05c689758 --- /dev/null +++ b/core_controller/Cargo.lock @@ -0,0 +1,2537 @@ +# 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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "async-trait" +version = "0.1.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "autocfg" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + +[[package]] +name = "axum" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" +dependencies = [ + "async-trait", + "axum-core", + "axum-macros", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper 1.0.0", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-extra" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0be6ea09c9b96cb5076af0de2e383bd2bc0c18f827cf1967bdd353e0b910d733" +dependencies = [ + "axum", + "axum-core", + "bytes", + "futures-util", + "headers", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "serde", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "backtrace" +version = "0.3.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[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 = "bollard" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aed08d3adb6ebe0eff737115056652670ae290f177759aac19c30456135f94c" +dependencies = [ + "base64 0.22.0", + "bollard-stubs", + "bytes", + "futures-core", + "futures-util", + "hex", + "http", + "http-body-util", + "hyper", + "hyper-named-pipe", + "hyper-util", + "hyperlocal-next", + "log", + "pin-project-lite", + "serde", + "serde_derive", + "serde_json", + "serde_repr", + "serde_urlencoded", + "thiserror", + "tokio", + "tokio-util", + "tower-service", + "url", + "winapi", +] + +[[package]] +name = "bollard-stubs" +version = "1.44.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709d9aa1c37abb89d40f19f5d0ad6f0d88cb1581264e571c9350fc5bb89cf1c5" +dependencies = [ + "serde", + "serde_repr", + "serde_with", +] + +[[package]] +name = "bumpalo" +version = "3.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" + +[[package]] +name = "bytemuck" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "cc" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.52.4", +] + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +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 = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "either" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" + +[[package]] +name = "figment" +version = "0.10.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7270677e7067213e04f323b55084586195f18308cd7546cfac9f873344ccceb6" +dependencies = [ + "atomic", + "pear", + "serde", + "serde_yaml", + "uncased", + "version_check", +] + +[[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.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +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.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "h2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 2.2.6", + "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 = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "headers" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" +dependencies = [ + "base64 0.21.7", + "bytes", + "headers-core", + "http", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" +dependencies = [ + "http", +] + +[[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.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "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 = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186548d73ac615b32a73aafe38fb4f56c0d340e110e5a200bcadbaf2e199263a" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-named-pipe" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b7d8abf35697b81a825e386fc151e0d503e8cb5fcb93cc8669c376dfd6f278" +dependencies = [ + "hex", + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", + "winapi", +] + +[[package]] +name = "hyper-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "log", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-timeout" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", +] + +[[package]] +name = "hyperlocal-next" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acf569d43fa9848e510358c07b80f4adf34084ddc28c6a4a651ee8474c070dcc" +dependencies = [ + "hex", + "http-body-util", + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +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 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", + "serde", +] + +[[package]] +name = "inlinable_string" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonpath-rust" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0268078319393f8430e850ee9d4706aeced256d34cf104d216bb496777137162" +dependencies = [ + "lazy_static", + "once_cell", + "pest", + "pest_derive", + "regex", + "serde_json", + "thiserror", +] + +[[package]] +name = "k8s-openapi" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "550f99d93aa4c2b25de527bce492d772caf5e21d7ac9bd4b508ba781c8d91e30" +dependencies = [ + "base64 0.21.7", + "chrono", + "serde", + "serde-value", + "serde_json", +] + +[[package]] +name = "kube" +version = "0.89.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92cd10d00ad38b2f72a5223cd8f2827968466a5d32ae89672d2b0df06488c499" +dependencies = [ + "k8s-openapi", + "kube-client", + "kube-core", +] + +[[package]] +name = "kube-client" +version = "0.89.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b4ee4e409c9afb4e38a30802875acb108902387a41346bbc2fd8610df5f729" +dependencies = [ + "base64 0.22.0", + "bytes", + "chrono", + "either", + "futures", + "home", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-timeout", + "hyper-util", + "jsonpath-rust", + "k8s-openapi", + "kube-core", + "pem", + "pin-project", + "rustls", + "rustls-pemfile 2.1.1", + "secrecy", + "serde", + "serde_json", + "serde_yaml", + "thiserror", + "tokio", + "tokio-util", + "tower", + "tower-http", + "tracing", +] + +[[package]] +name = "kube-core" +version = "0.89.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beab9186726ed0c2420ff8a37b02f26dc62b3c33330ac60d0cc7605e1a6f6678" +dependencies = [ + "chrono", + "form_urlencoded", + "http", + "k8s-openapi", + "once_cell", + "serde", + "serde_json", + "thiserror", +] + +[[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.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.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 = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +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.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "osrd_core_controller" +version = "0.1.0" +dependencies = [ + "axum", + "axum-extra", + "bollard", + "chrono", + "figment", + "k8s-openapi", + "kube", + "redis", + "reqwest", + "serde", + "serde_json", + "tokio", + "tracing", + "tracing-subscriber", + "uuid", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[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.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "pear" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdeeaa00ce488657faba8ebf44ab9361f9365a97bd39ffb8a60663f57ff4b467" +dependencies = [ + "inlinable_string", + "pear_codegen", + "yansi", +] + +[[package]] +name = "pear_codegen" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bab5b985dc082b345f812b7df84e1bef27e7207b39e448439ba8bd69c93f147" +dependencies = [ + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn", +] + +[[package]] +name = "pem" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8fcc794035347fb64beda2d3b462595dd2753e3f268d89c5aae77e8cf2c310" +dependencies = [ + "base64 0.21.7", + "serde", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "311fb059dee1a7b802f036316d790138c613a4e8b180c822e3925a662e9f0c95" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f73541b156d32197eecda1a4014d7f868fd2bcb3c550d5386087cfba442bf69c" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c35eeed0a3fab112f75165fdc026b3913f4183133f19b49be773ac9ea966e8bd" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2adbf29bb9776f28caece835398781ab24435585fe0d4dc1374a61db5accedca" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[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.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "version_check", + "yansi", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redis" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6472825949c09872e8f2c50bde59fcefc17748b6be5c90fd67cd8b4daca73bfd" +dependencies = [ + "async-trait", + "bytes", + "combine", + "futures-util", + "itoa", + "percent-encoding", + "pin-project-lite", + "ryu", + "sha1_smol", + "socket2", + "tokio", + "tokio-util", + "url", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.3", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "reqwest" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d66674f2b6fb864665eea7a3c1ac4e3dfacd2fda83cf6f935a612e01b0e3338" +dependencies = [ + "base64 0.21.7", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile 1.0.4", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 0.1.2", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustix" +version = "0.38.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99008d7ad0bbbea527ec27bddbc0e432c5b87d8175178cee68d2eec9c4a1813c" +dependencies = [ + "log", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" +dependencies = [ + "openssl-probe", + "rustls-pemfile 2.1.1", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +dependencies = [ + "base64 0.21.7", +] + +[[package]] +name = "rustls-pemfile" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f48172685e6ff52a556baa527774f61fcaa884f59daf3375c62a3f1cd2549dab" +dependencies = [ + "base64 0.21.7", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247" + +[[package]] +name = "rustls-webpki" +version = "0.102.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "serde", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[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_with" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee80b0e361bbf88fd2f6e242ccd19cfda072cb0faa6ae694ecee08199938569a" +dependencies = [ + "base64 0.21.7", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.2.6", + "serde", + "serde_derive", + "serde_json", + "time", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap 2.2.6", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + +[[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 = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[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.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[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.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sync_wrapper" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384595c11a4e2969895cad5a8c4029115f5ab956a9e5ef4de79d11a426e5f20c" + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "thiserror" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "time" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "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.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +dependencies = [ + "num-conv", + "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.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +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", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" +dependencies = [ + "base64 0.21.7", + "bitflags 2.5.0", + "bytes", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "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.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "uncased" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[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.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "uuid" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +dependencies = [ + "getrandom", + "serde", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[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.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +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-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.4", +] + +[[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-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.4", +] + +[[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-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", +] + +[[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_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[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_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[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_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[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_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/core_controller/Cargo.toml b/core_controller/Cargo.toml new file mode 100644 index 00000000000..01bf9c3b21a --- /dev/null +++ b/core_controller/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "osrd_core_controller" +version = "0.1.0" +edition = "2021" + +[dependencies] +uuid = { version = "1.8", features = ["v4", "serde"] } +bollard = "0.16" +kube = { version = "0.89", features = ["client"] } +figment = { version = "0.10", features = ["yaml", "env"] } +tokio = { version = "1.36", features = ["full"] } +k8s-openapi = { version = "0.21", features = ["v1_29"] } +serde = { version = "1", features = ["derive"] } +reqwest = { version = "0.12", features = ["json"] } +serde_json = "1.0" +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } +redis = { version = "0.25", features = ["tokio-comp"] } +chrono = "0.4" +axum = { version = "0.7", features = ["macros"] } +axum-extra = { version = "0.9", features = ["typed-header"] } diff --git a/core_controller/Dockerfile b/core_controller/Dockerfile new file mode 100644 index 00000000000..3cfd624fcd5 --- /dev/null +++ b/core_controller/Dockerfile @@ -0,0 +1,55 @@ +# syntax=docker/dockerfile:1 + +############## +# Cargo chef # +############## +FROM lukemathwalker/cargo-chef:latest AS chef +WORKDIR /app + +####################### +# Cargo chef : Recipe # +####################### +FROM chef as planner +COPY . . +RUN cargo chef prepare --recipe-path recipe.json + +###################### +# Cargo chef : build # +###################### +FROM chef as run_builder +COPY --from=planner /app/recipe.json recipe.json +RUN cargo chef cook --release --recipe-path recipe.json +COPY . . +RUN cargo install --locked --path . + +###################### +# Testing env: build # +###################### +FROM chef AS testing_env +RUN rustup component add llvm-tools && \ + rustup component add rustfmt && \ + cargo install grcov +COPY --from=planner /app/recipe.json recipe.json + +ENV RUSTFLAGS="-Cinstrument-coverage" +ENV LLVM_PROFILE_FILE="core_controller-%p-%m.profraw" +RUN cargo chef cook --tests --recipe-path recipe.json +COPY . . + +####################### +# Running env : build # +####################### +FROM debian:bookworm-slim as running_env +RUN apt update -yqq && \ + apt install -yqq --no-install-recommends curl ca-certificates libjemalloc2 jq && \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false && \ + rm -rf /var/lib/apt/lists/* + +COPY --from=run_builder /usr/local/cargo/bin/osrd_core_controller /usr/local/bin/osrd_core_controller + +ARG OSRD_GIT_DESCRIBE +ENV OSRD_GIT_DESCRIBE=${OSRD_GIT_DESCRIBE} +# We use jemalloc to reduce allocation fragmentation +ENV LD_PRELOAD="/usr/lib/x86_64-linux-gnu/libjemalloc.so.2" + +CMD ["/usr/local/bin/osrd_core_controller"] diff --git a/core_controller/src/api.rs b/core_controller/src/api.rs new file mode 100644 index 00000000000..1cae4f9cc3b --- /dev/null +++ b/core_controller/src/api.rs @@ -0,0 +1,53 @@ +use std::sync::Arc; + +use axum::{extract::State, routing::get, Json, Router}; +use serde::Serialize; +use tokio::sync::Mutex; +use tracing::info; + +use crate::drivers::core_driver::CoreMetadata; + +#[derive(Clone)] +struct AppState { + known_cores: Arc>>, +} + +pub async fn create_server(addr: String, known_cores: Arc>>) { + let app_state = AppState { known_cores }; + + let app = Router::new() + .route("/health", get(health_check)) + .route("/status", get(list_cores)) + .with_state(app_state); + + let listener = tokio::net::TcpListener::bind(addr.clone()) + .await + .expect("Failed to bind to address"); + + info!("Starting API server on {}", addr); + + axum::serve(listener, app) + .await + .expect("Failed to start server"); +} + +#[derive(Serialize)] +struct HealthCheckResponse { + status: &'static str, +} + +async fn health_check() -> Json { + Json(HealthCheckResponse { status: "ok" }) +} + +#[derive(Serialize)] +struct ListCoresResponse { + cores: Vec, +} + +async fn list_cores(State(state): State) -> Json { + let latest_known_cores = state.known_cores.lock().await; + Json(ListCoresResponse { + cores: latest_known_cores.clone(), + }) +} diff --git a/core_controller/src/config.rs b/core_controller/src/config.rs new file mode 100644 index 00000000000..934908e6214 --- /dev/null +++ b/core_controller/src/config.rs @@ -0,0 +1,76 @@ +use crate::drivers::{ + docker::DockerDriverOptions, kubernetes::KubernetesDriverOptions, mq::RabbitMQDriverOptions, +}; +use figment::{ + providers::{Env, Format, Serialized, Yaml}, + Figment, +}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(tag = "type")] +pub enum Provider { + Docker(DockerDriverOptions), + Kubernetes(KubernetesDriverOptions), +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct RedisOptions { + pub url: String, + pub core_last_seen_prefix: String, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct Config { + pub loop_interval: u64, + pub core_timeout: i64, + pub api_listen_addr: String, + pub provider: Provider, + pub rabbitmq: RabbitMQDriverOptions, + pub redis: RedisOptions, +} + +impl Default for Config { + fn default() -> Config { + // if env DEFAULT_CC_RMQ_HOST is not set, use rabbitmq (container name) otherwise use the env value + // Used for local development, making easier to seperate host mode to docker network mode + let default_rmq_host = match std::env::var("DEFAULT_CC_RMQ_HOST") { + Ok(val) => val, + Err(_) => "rabbitmq".to_string(), + }; + + let default_redis_host: String = match std::env::var("DEFAULT_CC_REDIS_HOST") { + Ok(val) => val, + Err(_) => "redis".to_string(), + }; + + Config { + loop_interval: 3, + core_timeout: 900, + api_listen_addr: "0.0.0.0:6000".to_string(), + redis: RedisOptions { + url: format!("redis://{}:6379/0", default_redis_host), + core_last_seen_prefix: "core/last_seen_msg".to_string(), + }, + provider: Provider::Docker(DockerDriverOptions { + core_image_name: "ghcr.io/osrd-project/edge/osrd-core".to_string(), + core_image_tag: "dev".to_string(), + container_prefix: "dyn-osrd".to_string(), + default_env: vec![], + }), + rabbitmq: RabbitMQDriverOptions { + api_url: format!("http://osrd:password@{}:15672/api", default_rmq_host), + vhost: "%2F".to_string(), // URL-encoded "/" + exchange: "amq.direct".to_string(), + queue_prefix: "core".to_string(), + }, + } + } +} + +pub fn load() -> Result { + Figment::from(Serialized::defaults(Config::default())) + .merge(Yaml::file("core_controller.toml")) + .merge(Env::prefixed("CC_")) + .extract() +} diff --git a/core_controller/src/control_loop.rs b/core_controller/src/control_loop.rs new file mode 100644 index 00000000000..67211fc7cc4 --- /dev/null +++ b/core_controller/src/control_loop.rs @@ -0,0 +1,262 @@ +use crate::config; +use crate::drivers::{ + core_driver::{CoreDriver, CoreMetadata}, + docker::DockerDriver, + kubernetes::KubernetesDriver, + mq::{Queue, RabbitMQDriver}, +}; +use std::fmt::Debug; +use std::{ + fmt::{self, Formatter}, + process, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + time::Duration, +}; +use tokio::sync::Mutex; +use tokio::time::sleep; +use tracing::{error, info, warn}; + +pub struct ControlLoop { + pub config: config::Config, + pub should_continue: Arc, + pub known_cores: Arc>>, + + core_driver: Box, + mq_driver: RabbitMQDriver, + redis_client: redis::Client, +} + +impl Debug for ControlLoop { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!( + f, + "ControlLoop (exiting: {})", + !self.should_continue.load(Ordering::Relaxed) + ) + } +} + +impl ControlLoop { + /// Create a new control loop + pub async fn new( + config: config::Config, + known_cores: Arc>>, + ) -> ControlLoop { + ControlLoop { + config: config.clone(), + core_driver: match config.provider { + config::Provider::Docker(options) => { + info!("Using Docker driver"); + Box::new(DockerDriver::new(options)) + } + config::Provider::Kubernetes(options) => { + info!("Using Kubernetes driver"); + Box::new(KubernetesDriver::new(options).await) + } + }, + mq_driver: RabbitMQDriver::new(config.rabbitmq), + redis_client: redis::Client::open(config.redis.url) + .expect("Failed to open redis connection"), + should_continue: Arc::new(AtomicBool::new(true)), + known_cores: known_cores.clone(), + } + } + + /// Remove stale cores + #[tracing::instrument(skip_all)] + async fn remove_stale_cores( + &self, + cores: Vec, + ) -> Result, Box> { + // Connect to redis + let mut redis_conn = match self.redis_client.get_multiplexed_tokio_connection().await { + Ok(conn) => conn, + Err(e) => { + warn!("Failed to connect to redis, {}", e); + return Err("error connecting to redis".into()); + } + }; + + let mut remaining_cores: Vec = vec![]; + + // For each core, check when the last message was seen on redis and if it is older than the timeout, delete the core and its queue + for core in cores { + let last_seen: Option = redis::cmd("GET") + .arg(format!( + "{}/{}", + &self.config.redis.core_last_seen_prefix, core.infra_id + )) + .query_async(&mut redis_conn) + .await + .unwrap_or(None); + + if let Some(last_seen) = last_seen { + let now = chrono::Utc::now().timestamp(); + let diff = now - last_seen; + + if diff > self.config.core_timeout { + info!( + "Core pool for infra_id: {} has timed out, deleting it", + core.infra_id + ); + + // Delete the core pool + if let Err(e) = &self.core_driver.destroy_core_pool(core.infra_id).await { + warn!("Failed to delete core pool for infra_id, error: {}", e); + return Err("Failed to delete core pool".into()); + } else { + info!("Deleted core pool for infra_id: {}", core.infra_id); + } + + // Deleting the queue + if let Err(e) = &self.mq_driver.delete_queue(core.infra_id).await { + warn!( + "Failed to delete queue for infra_id: {}, error: {}", + core.infra_id, e + ); + return Err("Failed to delete queue".into()); + } else { + info!("Deleted queue for infra_id: {}", core.infra_id); + } + + // Remove the redis key + if let Err(e) = redis::cmd("DEL") + .arg(format!( + "{}/{}", + &self.config.redis.core_last_seen_prefix, core.infra_id + )) + .query_async::<_, i64>(&mut redis_conn) + .await + { + warn!("Failed to delete redis key, error: {}", e); + return Err("Failed to delete redis key".into()); + } else { + info!("Deleted redis key for infra_id: {}", core.infra_id); + } + } + } else { + remaining_cores.push(core); + } + } + + Ok(remaining_cores) + } + + /// For each queue, check if there is a core pool running for it, if not, start one + #[tracing::instrument(skip_all)] + async fn start_missing_cores( + &self, + remaining_cores: Vec, + queues: Vec, + ) -> Result<(), &str> { + // Check if there are any cores that are not started + for queue in queues { + let found = remaining_cores + .iter() + .any(|core| core.infra_id == queue.infra_id); + + if !found { + info!( + "No core pool not found for infra_id: {}, creating it", + queue.infra_id + ); + + if let Err(_) = &self + .core_driver + .get_or_create_core_pool(queue.infra_id) + .await + { + warn!("Failed to start core pool for infra_id: {}", queue.infra_id); + return Err("Failed to start core pool"); + } else { + info!("started core pool for infra_id: {}", queue.infra_id); + } + } + } + + Ok(()) + } + + /// Run the control loop + #[tracing::instrument(name = "control_loop", skip_all)] + pub async fn run(&self) { + const MAX_ERROR_COUNT: u8 = 3; + let mut error_counter: u8 = 0; + + info!("Starting"); + while self.should_continue.load(Ordering::Relaxed) { + // If we have too many errors, exit with an error code + if error_counter >= MAX_ERROR_COUNT { + error!("Exiting due to repeated failures"); + process::exit(1); + } + + // Get the list of cores from the core driver + let cores = match self.core_driver.list_core_pools().await { + Ok(cores) => cores, + Err(_) => { + error_counter += 1; + warn!( + "Failed to list cores, error count: {}/{}", + error_counter, MAX_ERROR_COUNT + ); + sleep(Duration::from_secs(self.config.loop_interval)).await; + continue; + } + }; + + // Remove stale cores (cores that have not received a message in a while) + let remaining_cores = match self.remove_stale_cores(cores).await { + Ok(cores) => cores, + Err(e) => { + error_counter += 1; + warn!( + "Failed to remove stale cores, error count: {}/{}, error: {}", + error_counter, MAX_ERROR_COUNT, e + ); + sleep(Duration::from_secs(self.config.loop_interval)).await; + continue; + } + }; + + // Store the remaining cores in the known_cores + let mut latest_known_cores = self.known_cores.lock().await; + *latest_known_cores = remaining_cores.clone(); + drop(latest_known_cores); + + // Get the list of queues + let queues = match self.mq_driver.list_core_queues().await { + Ok(queues) => queues, + Err(e) => { + error_counter += 1; + warn!( + "Failed to list queues, error count: {}/{}. error: {}", + error_counter, MAX_ERROR_COUNT, e + ); + sleep(Duration::from_secs(self.config.loop_interval)).await; + continue; + } + }; + + // Start missing cores + if let Err(_) = self.start_missing_cores(remaining_cores, queues).await { + error_counter += 1; + warn!( + "Failed to start missing cores, error count: {}/{}", + error_counter, MAX_ERROR_COUNT + ); + sleep(Duration::from_secs(self.config.loop_interval)).await; + continue; + } + + // Reset the error counter + error_counter = 0; + + // Sleep for a while + sleep(std::time::Duration::from_secs(self.config.loop_interval)).await; + } + } +} diff --git a/core_controller/src/drivers/core_driver.rs b/core_controller/src/drivers/core_driver.rs new file mode 100644 index 00000000000..1121c5aeadd --- /dev/null +++ b/core_controller/src/drivers/core_driver.rs @@ -0,0 +1,56 @@ +use std::{ + fmt::{self, Display, Formatter}, + future::Future, + pin::Pin, +}; + +use serde::Serialize; +use uuid::Uuid; + +#[derive(Clone, Serialize, Debug)] +pub struct CoreMetadata { + /// External identifier (container id in Docker, deployment name in Kubernetes) + pub external_id: String, + /// Internal UUID of the core. + pub core_id: Uuid, + /// Infrastructure ID for which this core provides services. + pub infra_id: usize, +} + +#[derive(Debug)] +pub enum DriverError { + /// Docker error + DockerError(bollard::errors::Error), + /// Kubernetes error + KubernetesError(kube::Error), +} + +impl Display for DriverError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + DriverError::DockerError(e) => write!(f, "Docker error: {}", e), + DriverError::KubernetesError(e) => write!(f, "Kubernetes error: {}", e), + } + } +} + +pub trait CoreDriver { + /// Schedule a core to run on a specific infrastructure. + /// If the core is already scheduled, nothing happens. + /// Returns the internal UUID of the core. + fn get_or_create_core_pool( + &self, + infra_id: usize, + ) -> Pin> + Send + '_>>; + + /// Unschedules a core from the given infrastructure. + fn destroy_core_pool( + &self, + infra_id: usize, + ) -> Pin> + Send + '_>>; + + /// Returns the status of a core. + fn list_core_pools( + &self, + ) -> Pin, DriverError>> + Send + '_>>; +} diff --git a/core_controller/src/drivers/docker.rs b/core_controller/src/drivers/docker.rs new file mode 100644 index 00000000000..79836626868 --- /dev/null +++ b/core_controller/src/drivers/docker.rs @@ -0,0 +1,166 @@ +use std::{future::Future, pin::Pin}; + +use bollard::{ + container::{Config, CreateContainerOptions, RemoveContainerOptions, StartContainerOptions}, + Docker, +}; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +use super::{ + core_driver::{CoreDriver, CoreMetadata, DriverError}, + LABEL_CORE_ID, LABEL_INFRA_ID, LABEL_MANAGED_BY, MANAGED_BY_VALUE, +}; + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct DockerDriverOptions { + /// The name of the Docker image to use for the core + pub core_image_name: String, + /// The tag of the Docker image to use for the core + pub core_image_tag: String, + /// The prefix to use for the container names + pub container_prefix: String, + /// The default environment variables to set for the core + pub default_env: Vec, +} + +pub struct DockerDriver { + client: Docker, + options: DockerDriverOptions, +} + +impl DockerDriver { + pub fn new(options: DockerDriverOptions) -> DockerDriver { + DockerDriver { + client: Docker::connect_with_socket_defaults().expect("Failed to connect to Docker"), + options, + } + } +} + +impl CoreDriver for DockerDriver { + fn get_or_create_core_pool( + &self, + infra_id: usize, + ) -> Pin> + Send + '_>> { + Box::pin(async move { + let current_cores = self.list_core_pools().await?; + for core in current_cores { + if core.infra_id == infra_id { + return Ok(core.core_id); + } + } + + let new_id = Uuid::new_v4(); + + let final_env = { + let mut env = self.options.default_env.clone(); + env.push(format!("CORE_ID={}", new_id)); + env.push(format!("INFRA_ID={}", infra_id)); + env + }; + + let labels = { + let mut labels = std::collections::HashMap::new(); + labels.insert(LABEL_MANAGED_BY.to_owned(), MANAGED_BY_VALUE.to_owned()); + labels.insert(LABEL_CORE_ID.to_owned(), new_id.to_string()); + labels.insert(LABEL_INFRA_ID.to_owned(), infra_id.to_string()); + labels + }; + + let container_name = format!("{}-core-{}", self.options.container_prefix, infra_id); + + let options = CreateContainerOptions { + name: container_name.clone(), + platform: None, + }; + let config = Config { + image: Some(format!( + "{}:{}", + self.options.core_image_name, self.options.core_image_tag + )), + env: Some(final_env), + labels: Some(labels), + ..Default::default() + }; + + self.client + .create_container(Some(options), config) + .await + .map_err(DriverError::DockerError)?; + + self.client + .start_container( + container_name.as_str(), + None::>, + ) + .await + .map_err(DriverError::DockerError)?; + + Ok(new_id) + }) + } + + fn destroy_core_pool( + &self, + infra_id: usize, + ) -> Pin> + Send + '_>> { + Box::pin(async move { + let current_cores = self.list_core_pools().await?; + for core in current_cores { + if core.infra_id == infra_id { + self.client + .remove_container( + &core.external_id, + Some(RemoveContainerOptions { + force: true, + ..Default::default() + }), + ) + .await + .map_err(DriverError::DockerError)?; + } + } + + Ok(()) + }) + } + + fn list_core_pools( + &self, + ) -> Pin, DriverError>> + Send + '_>> { + Box::pin(async move { + let containers = self + .client + .list_containers::(None) + .await + .map_err(DriverError::DockerError)?; + + let cores = containers + .iter() + .filter_map(|container| { + container.labels.as_ref().and_then(|labels| { + if labels.get(LABEL_MANAGED_BY) == Some(&MANAGED_BY_VALUE.to_string()) { + Some(CoreMetadata { + external_id: container.id.clone().expect("container id missing"), + core_id: Uuid::parse_str( + labels.get(LABEL_CORE_ID).expect("core_id label missing"), + ) + .expect("core_id label is not a valid UUID"), + infra_id: labels + .get(LABEL_INFRA_ID) + .expect("infra_id label missing") + .parse() + .expect("infra_id label is not a valid number"), + }) + } else { + None + } + }) + }) + .collect(); + + Ok(cores) + }) + } +} diff --git a/core_controller/src/drivers/kubernetes.rs b/core_controller/src/drivers/kubernetes.rs new file mode 100644 index 00000000000..07ebb03fd30 --- /dev/null +++ b/core_controller/src/drivers/kubernetes.rs @@ -0,0 +1,383 @@ +use super::{ + core_driver::{CoreDriver, CoreMetadata, DriverError}, + LABEL_CORE_ID, LABEL_INFRA_ID, LABEL_MANAGED_BY, MANAGED_BY_VALUE, +}; +use k8s_openapi::{ + api::{ + apps::v1::{Deployment, DeploymentSpec}, + autoscaling::v1::HorizontalPodAutoscaler, + core::v1::{ + Affinity, Container, EnvVar, HTTPGetAction, PodSpec, PodTemplateSpec, Probe, + ResourceRequirements, Service, Toleration, + }, + }, + apimachinery::pkg::util::intstr::IntOrString, +}; +use kube::{api::ObjectMeta, Client}; +use serde::{Deserialize, Serialize}; +use std::{collections::BTreeMap, future::Future, pin::Pin}; +use uuid::Uuid; + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct AutoscalingOptions { + /// The minimum number of replicas + pub min_replicas: i32, + /// The maximum number of replicas + pub max_replicas: i32, + /// The target CPU utilization percentage + pub target_cpu_utilization_percentage: i32, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct KubernetesDeploymentOptions { + /// The default environment variables to set for the core (passthrough to kubernetes deployment) + pub default_env: Option>, + + /// The resources to allocate to the core (passthrough to kubernetes deployment) + pub resources: Option, + + /// The node selector to use for the core (passthrough to kubernetes deployment) + pub node_selector: Option>, + + /// The affinity to use for the core (passthrough to kubernetes deployment) + pub affinity: Option, + + /// The tolerations to use for the core (passthrough to kubernetes deployment) + pub tolerations: Option>, +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct KubernetesDriverOptions { + /// The name of the Docker image to use for the core + pub core_image_name: String, + + /// The tag of the Docker image to use for the core + pub core_image_tag: String, + + /// The prefix to use for the deployment names + pub deployment_prefix: String, + + /// The namespace to use for the deployments + pub namespace: String, + + /// The URL of the Editoast instance + pub editoast_url: String, + + /// The port to use for the core + pub core_port: u16, + + /// Whether to deploy a service for the core (for http request) + pub deploy_service: bool, + + /// The autoscaling options to use for the core + pub autoscaling: Option, + + /// The options to use for the kubernetes deployment + pub kube_deployment_options: KubernetesDeploymentOptions, +} + +pub struct KubernetesDriver { + client: Client, + options: KubernetesDriverOptions, +} + +impl KubernetesDriver { + pub async fn new(options: KubernetesDriverOptions) -> KubernetesDriver { + KubernetesDriver { + client: Client::try_default() + .await + .expect("Failed to connect to Kubernetes"), + options, + } + } +} + +impl CoreDriver for KubernetesDriver { + fn get_or_create_core_pool( + &self, + infra_id: usize, + ) -> Pin> + Send + '_>> { + Box::pin(async move { + let current_cores = self.list_core_pools().await?; + for core in current_cores { + if core.infra_id == infra_id { + return Ok(core.core_id); + } + } + + let new_id = Uuid::new_v4(); + let core_deployment_name = + format!("{}-core-{}", self.options.deployment_prefix, infra_id); + let final_env = { + let mut env = self + .options + .kube_deployment_options + .default_env + .clone() + .unwrap_or_default(); + env.push(EnvVar { + name: "CORE_ID".to_string(), + value: Some(new_id.to_string()), + ..Default::default() + }); + env.push(EnvVar { + name: "INFRA_ID".to_string(), + value: Some(infra_id.to_string()), + ..Default::default() + }); + env.push(EnvVar { + name: "CORE_PORT".to_string(), + value: Some(self.options.core_port.to_string()), + ..Default::default() + }); + env.push(EnvVar { + name: "CORE_EDITOAST_URL".to_string(), + value: Some(self.options.editoast_url.clone()), + ..Default::default() + }); + env + }; + + let health_probe = Some(HTTPGetAction { + path: Some("/ready".to_string()), + port: IntOrString::Int(8080), + ..Default::default() + }); + + // Create a new deployment + let deployment = Deployment { + metadata: ObjectMeta { + name: Some(core_deployment_name.clone()), + namespace: Some(self.options.namespace.clone()), + labels: Some({ + let mut labels = BTreeMap::new(); + labels.insert(LABEL_MANAGED_BY.to_owned(), MANAGED_BY_VALUE.to_owned()); + labels.insert(LABEL_CORE_ID.to_owned(), new_id.to_string()); + labels.insert(LABEL_INFRA_ID.to_owned(), infra_id.to_string()); + labels + }), + ..Default::default() + }, + spec: Some(DeploymentSpec { + replicas: Some(1), + template: PodTemplateSpec { + spec: Some(PodSpec { + containers: vec![Container { + name: core_deployment_name.clone(), + image: Some(format!( + "{}:{}", + self.options.core_image_name, self.options.core_image_tag + )), + liveness_probe: Some(Probe { + http_get: health_probe.clone(), + initial_delay_seconds: Some(30), + period_seconds: Some(10), + timeout_seconds: Some(10), + ..Default::default() + }), + readiness_probe: Some(Probe { + http_get: health_probe, + ..Default::default() + }), + env: Some(final_env), + ..Default::default() + }], + node_selector: self + .options + .kube_deployment_options + .node_selector + .clone(), + affinity: self.options.kube_deployment_options.affinity.clone(), + tolerations: self.options.kube_deployment_options.tolerations.clone(), + ..Default::default() + }), + ..Default::default() + }, + ..Default::default() + }), + ..Default::default() + }; + + // Create the service if needed + if self.options.deploy_service { + let service = Service { + metadata: ObjectMeta { + name: Some(core_deployment_name.clone()), + namespace: Some(self.options.namespace.clone()), + labels: Some({ + let mut labels = BTreeMap::new(); + labels.insert(LABEL_MANAGED_BY.to_owned(), MANAGED_BY_VALUE.to_owned()); + labels.insert(LABEL_CORE_ID.to_owned(), new_id.to_string()); + labels.insert(LABEL_INFRA_ID.to_owned(), infra_id.to_string()); + labels + }), + ..Default::default() + }, + spec: Some({ + let mut ports = Vec::new(); + ports.push(k8s_openapi::api::core::v1::ServicePort { + port: 80, + target_port: Some(IntOrString::Int(self.options.core_port as i32)), + ..Default::default() + }); + k8s_openapi::api::core::v1::ServiceSpec { + selector: Some({ + let mut selector = BTreeMap::new(); + selector.insert(LABEL_CORE_ID.to_owned(), new_id.to_string()); + selector.insert(LABEL_INFRA_ID.to_owned(), infra_id.to_string()); + selector + }), + ports: Some(ports), + ..Default::default() + } + }), + ..Default::default() + }; + + kube::api::Api::::namespaced(self.client.clone(), &self.options.namespace) + .create(&kube::api::PostParams::default(), &service) + .await + .map_err(super::core_driver::DriverError::KubernetesError)?; + } + + // Create the autoscaler if needed + if let Some(autoscaling) = &self.options.autoscaling { + let hpa = HorizontalPodAutoscaler { + metadata: ObjectMeta { + name: Some(core_deployment_name.clone()), + namespace: Some(self.options.namespace.clone()), + labels: Some({ + let mut labels = BTreeMap::new(); + labels.insert(LABEL_MANAGED_BY.to_owned(), MANAGED_BY_VALUE.to_owned()); + labels.insert(LABEL_CORE_ID.to_owned(), new_id.to_string()); + labels.insert(LABEL_INFRA_ID.to_owned(), infra_id.to_string()); + labels + }), + ..Default::default() + }, + spec: Some({ + k8s_openapi::api::autoscaling::v1::HorizontalPodAutoscalerSpec { + scale_target_ref: + k8s_openapi::api::autoscaling::v1::CrossVersionObjectReference { + api_version: Some("apps/v1".to_string()), + kind: "Deployment".to_string(), + name: core_deployment_name.clone(), + }, + min_replicas: Some(autoscaling.min_replicas), + max_replicas: autoscaling.max_replicas, + target_cpu_utilization_percentage: Some( + autoscaling.target_cpu_utilization_percentage, + ), + ..Default::default() + } + }), + ..Default::default() + }; + + kube::api::Api::::namespaced( + self.client.clone(), + &self.options.namespace, + ) + .create(&kube::api::PostParams::default(), &hpa) + .await + .map_err(super::core_driver::DriverError::KubernetesError)?; + } + + // Create the deployment + kube::api::Api::::namespaced(self.client.clone(), &self.options.namespace) + .create(&kube::api::PostParams::default(), &deployment) + .await + .map_err(super::core_driver::DriverError::KubernetesError)?; + + Ok(new_id) + }) + } + + fn destroy_core_pool( + &self, + infra_id: usize, + ) -> Pin> + Send + '_>> { + Box::pin(async move { + let current_cores = self.list_core_pools().await?; + + for core in current_cores { + if core.infra_id == infra_id { + let core_deployment_name = + format!("{}-core-{}", self.options.deployment_prefix, core.infra_id); + + // Delete the deployment + kube::api::Api::::namespaced( + self.client.clone(), + &self.options.namespace, + ) + .delete(&core_deployment_name, &kube::api::DeleteParams::default()) + .await + .map_err(super::core_driver::DriverError::KubernetesError)?; + + // Delete the service + if self.options.deploy_service { + kube::api::Api::::namespaced( + self.client.clone(), + &self.options.namespace, + ) + .delete(&core_deployment_name, &kube::api::DeleteParams::default()) + .await + .map_err(super::core_driver::DriverError::KubernetesError)?; + } + + // Delete the autoscaler + if let Some(_) = &self.options.autoscaling { + kube::api::Api::::namespaced( + self.client.clone(), + &self.options.namespace, + ) + .delete(&core_deployment_name, &kube::api::DeleteParams::default()) + .await + .map_err(super::core_driver::DriverError::KubernetesError)?; + } + + return Ok(()); + } + } + + Ok(()) + }) + } + + fn list_core_pools( + &self, + ) -> Pin, DriverError>> + Send + '_>> { + Box::pin(async move { + let deployments = kube::api::Api::::namespaced( + self.client.clone(), + &self.options.namespace, + ) + .list(&kube::api::ListParams::default()) + .await + .map_err(super::core_driver::DriverError::KubernetesError)?; + + let cores = deployments + .iter() + .filter_map(|deployment| { + deployment.metadata.labels.as_ref().and_then(|labels| { + if labels.get(LABEL_MANAGED_BY) == Some(&MANAGED_BY_VALUE.to_owned()) { + let core_id = labels.get(LABEL_CORE_ID).expect("core_id not found"); + let infra_id = labels.get(LABEL_INFRA_ID).expect("infra_id not found"); + + Some(super::core_driver::CoreMetadata { + external_id: deployment.metadata.name.clone()?, + core_id: Uuid::parse_str(core_id) + .expect("core_id not a valid UUID"), + infra_id: infra_id.parse().expect("infra_id not a valid number"), + }) + } else { + None + } + }) + }) + .collect(); + + Ok(cores) + }) + } +} diff --git a/core_controller/src/drivers/mod.rs b/core_controller/src/drivers/mod.rs new file mode 100644 index 00000000000..0d98956a2da --- /dev/null +++ b/core_controller/src/drivers/mod.rs @@ -0,0 +1,9 @@ +pub mod core_driver; +pub mod docker; +pub mod kubernetes; +pub mod mq; + +const LABEL_MANAGED_BY: &str = "osrd/managed_by"; +const LABEL_CORE_ID: &str = "osrd/core_id"; +const LABEL_INFRA_ID: &str = "osrd/infra_id"; +const MANAGED_BY_VALUE: &str = "core-controller"; diff --git a/core_controller/src/drivers/mq.rs b/core_controller/src/drivers/mq.rs new file mode 100644 index 00000000000..92fb8ca5705 --- /dev/null +++ b/core_controller/src/drivers/mq.rs @@ -0,0 +1,135 @@ +use reqwest::Client; +use reqwest::Method; +use serde::{de, Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct RabbitMQDriverOptions { + pub api_url: String, + pub vhost: String, + pub exchange: String, + pub queue_prefix: String, +} + +pub struct RabbitMQDriver { + options: RabbitMQDriverOptions, +} + +#[derive(Debug, Serialize, Deserialize)] +struct RabbitMQResponseListQueues { + pub name: String, + pub messages: Option, // This field is optional because it is not present in the response when the queue is creating +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Queue { + pub queue_name: String, + pub infra_id: usize, + pub pending_messages: u64, +} + +impl RabbitMQDriver { + pub fn new(options: RabbitMQDriverOptions) -> Self { + RabbitMQDriver { options } + } + + pub async fn request_api( + &self, + verb: String, + path: String, + body: Option, + ) -> Result<(), reqwest::Error> + where + TB: Serialize, + { + let client = Client::new(); + let url = format!("{}/{}", self.options.api_url, path); + + let method = Method::from_bytes(verb.as_bytes()).unwrap(); + + let mut request = client.request(method, url); + if let Some(body) = body { + request = request.json(&body); + } + + request.send().await?; + Ok(()) + } + + pub async fn query_api( + &self, + verb: String, + path: String, + body: Option, + ) -> Result + where + TB: Serialize, + for<'de> TR: de::Deserialize<'de>, + { + let client = Client::new(); + let url = format!("{}/{}", self.options.api_url, path); + + let method = Method::from_bytes(verb.as_bytes()).unwrap(); + + let mut request = client.request(method, url); + if let Some(body) = body { + request = request.json(&body); + } + + let response = request.send().await?; + let response_body = response.json().await?; + + Ok(response_body) + } + + /// List all queues in the RabbitMQ server that have the prefix `options.queue_prefix` + pub async fn list_core_queues(&self) -> Result, reqwest::Error> { + let response: Vec = self + .query_api( + "GET".to_string(), + format!("queues/{}", self.options.vhost), + None::<()>, + ) + .await?; + + let queues: Vec = response + .into_iter() + .filter_map(|queue| { + if let Some(queue_messages) = queue.messages { + if queue.name.starts_with(&self.options.queue_prefix) { + Some(Queue { + queue_name: queue.name.clone(), + infra_id: queue + .name + .replace(&self.options.queue_prefix, "") + .replace("-", "") + .parse() + .expect("Failed to parse infra_id"), + pending_messages: queue_messages, + }) + } else { + None + } + } else { + None + } + }) + .collect(); + + Ok(queues) + } + + /// Delete a queue from the RabbitMQ server + pub async fn delete_queue(&self, infra_id: usize) -> Result<(), reqwest::Error> { + self.request_api( + "DELETE".to_string(), + format!( + "queues/{}/{}-{}", + self.options.vhost, self.options.queue_prefix, infra_id + ), + None::<()>, + ) + .await?; + + Ok(()) + } +} diff --git a/core_controller/src/main.rs b/core_controller/src/main.rs new file mode 100644 index 00000000000..37e04643f19 --- /dev/null +++ b/core_controller/src/main.rs @@ -0,0 +1,74 @@ +use std::sync::{atomic::Ordering, Arc}; +use tokio::sync::Mutex; +use tracing::warn; +use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; + +use crate::drivers::core_driver::CoreMetadata; + +mod api; +mod config; +mod control_loop; +mod drivers; + +#[tokio::main] +async fn main() { + // Initialize tracing (logging in our case, can be extended to report to a service later) + tracing_subscriber::registry() + .with( + tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| { + if cfg!(debug_assertions) { + // Development build + "osrd_core_controller=trace,tower_http=debug,axum::rejection=trace" + } else { + // Default on release build + "osrd_core_controller=warn,tower_http=info,axum::rejection=warn" + } + .into() + }), + ) + .with(tracing_subscriber::fmt::layer()) + .init(); + + // Load configuration + let config = config::load().expect("Cannot load configuration"); + + // Create the control loop, will load configuration + let known_cores: Arc>> = Arc::new(Mutex::new(vec![])); + let control_loop = control_loop::ControlLoop::new(config.clone(), known_cores.clone()).await; + + // Create the api server + let api_server = tokio::spawn(api::create_server( + config.api_listen_addr, + known_cores.clone(), + )); + + // Catching SIGINT (Ctrl+C) + let should_continue_clone = control_loop.should_continue.clone(); + let sig_int_handler = async move { + tokio::signal::unix::signal(tokio::signal::unix::SignalKind::interrupt()) + .expect("Failed to install SIGINT handler") + .recv() + .await; + warn!("Received SIGINT, shutting down..."); + should_continue_clone.store(false, Ordering::SeqCst); + }; + + // Catching SIGTERM + let should_continue_clone = control_loop.should_continue.clone(); + let sig_term_handler = async move { + tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) + .expect("Failed to install SIGTERM handler") + .recv() + .await; + warn!("Received SIGTERM, shutting down..."); + should_continue_clone.store(false, Ordering::SeqCst); + }; + + // Run the control loop, the api server, and wait for a signal + tokio::select! { + _ = control_loop.run() => {}, + _ = api_server => {}, + _ = sig_int_handler => {}, + _ = sig_term_handler => {}, + } +} diff --git a/docker-compose.yml b/docker-compose.yml index 3cf0361cb44..270191fa81a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,6 +3,7 @@ version: '3' volumes: psql_data: redis_data: + rabbitmq_data: services: postgres: @@ -34,23 +35,39 @@ services: start_period: 4s interval: 5s + rabbitmq: + image: rabbitmq:3-management + ports: + - "5672:5672" + - "15672:15672" + environment: + RABBITMQ_DEFAULT_USER: osrd + RABBITMQ_DEFAULT_PASS: password + volumes: + - rabbitmq_data:/var/lib/rabbitmq + healthcheck: + test: ["CMD", "rabbitmqctl", "status"] + interval: 30s + timeout: 30s + retries: 3 + core: image: ghcr.io/osrd-project/edge/osrd-core:${TAG-dev} - container_name: osrd-core build: context: core dockerfile: Dockerfile additional_contexts: test_data: tests/data + profiles: + - build + + core_controller: + image: ghcr.io/osrd-project/edge/osrd-core_controller:${TAG-dev} + container_name: osrd-core_controller + build: + context: core_controller + dockerfile: Dockerfile restart: unless-stopped - ports: ["8080:8080"] - command: "java -ea -jar /app/osrd_core.jar api -p 8080" - environment: - CORE_EDITOAST_URL: "http://osrd-editoast" - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8080/health"] - start_period: 4s - interval: 5s front: image: ghcr.io/osrd-project/edge/osrd-front:${TAG-dev}-${OSRD_FRONT_MODE-devel} @@ -92,7 +109,6 @@ services: EDITOAST_PORT: 80 PSQL_HOST: "postgres" REDIS_URL: "redis://redis" - OSRD_BACKEND_URL: "http://osrd-core:8080" DATABASE_URL: "postgres://osrd:password@postgres/osrd" TELEMETRY_KIND: "opentelemetry" command: diff --git a/docker/docker-bake-simple.hcl b/docker/docker-bake-simple.hcl index e40d552d1bc..7a98e5c7675 100644 --- a/docker/docker-bake-simple.hcl +++ b/docker/docker-bake-simple.hcl @@ -47,3 +47,11 @@ target "base-gateway-test" { target "base-gateway-front" { tags = tags("gateway-front") } + +target "base-core_controller" { + tags = tags("core_controller") +} + +target "base-core_controller-test" { + tags = tags("core_controller") +} diff --git a/docker/docker-bake.hcl b/docker/docker-bake.hcl index 8e478ee56e3..6109494912f 100644 --- a/docker/docker-bake.hcl +++ b/docker/docker-bake.hcl @@ -13,6 +13,8 @@ group "default" { "gateway-standalone", "gateway-test", "gateway-front", + "core_controller", + "core_controller-test" ] } @@ -152,3 +154,23 @@ target "gateway-front" { front_build = "target:front-build" } } + +################### +# Core Controller # +################### + +target "base-core_controller" {} +target "core_controller" { + inherits = ["base", "base-core_controller"] + context = "core_controller" + dockerfile = "Dockerfile" + target = "running_env" +} + +target "base-core_controller-test" {} +target "core_controller-test" { + inherits = ["base", "base-core_controller-test"] + context = "core_controller" + dockerfile = "Dockerfile" + target = "testing_env" +} diff --git a/docker/docker-compose.host.yml b/docker/docker-compose.host.yml index 092ee6c73da..516a7373b89 100644 --- a/docker/docker-compose.host.yml +++ b/docker/docker-compose.host.yml @@ -11,11 +11,16 @@ services: ports: [] network_mode: host - core: + rabbitmq: + ports: [] + network_mode: host + + core_controller: ports: [] network_mode: host environment: - CORE_EDITOAST_URL: "http://localhost:8090" + - DEFAULT_CC_RMQ_HOST=localhost + - DEFAULT_CC_REDIS_HOST=localhost editoast: ports: [] diff --git a/scripts/build-core.sh b/scripts/build-core.sh new file mode 100755 index 00000000000..55c943fdd0b --- /dev/null +++ b/scripts/build-core.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +docker compose \ + -p "osrd" \ + -f "docker-compose.yml" \ + build core diff --git a/scripts/drop-core.sh b/scripts/drop-core.sh new file mode 100755 index 00000000000..0db5f5b2835 --- /dev/null +++ b/scripts/drop-core.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +docker rm -f dyn-osrd-core-$1 + +docker compose \ + -p "osrd" \ + -f "docker-compose.yml" \ + exec rabbitmq rabbitmqctl delete_queue core-$1