diff --git a/Cargo.lock b/Cargo.lock index 53aba67..9e5056b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,6 +26,21 @@ 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 = "anstream" version = "0.6.15" @@ -241,6 +256,58 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +[[package]] +name = "bollard" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d41711ad46fda47cd701f6908e59d1bd6b9a2b7464c0d0aeab95c6d37096ff8a" +dependencies = [ + "base64", + "bollard-stubs", + "bytes", + "chrono", + "futures-core", + "futures-util", + "hex", + "http", + "http-body-util", + "hyper", + "hyper-named-pipe", + "hyper-util", + "hyperlocal", + "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.45.0-rc.26.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7c5415e3a6bc6d3e99eff6268e488fd4ee25e7b28c10f08fa6760bd9de16e4" +dependencies = [ + "chrono", + "serde", + "serde_repr", + "serde_with", +] + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + [[package]] name = "bytecount" version = "0.6.8" @@ -274,6 +341,21 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link", +] + [[package]] name = "clang-sys" version = "1.8.1" @@ -373,6 +455,20 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "container" +version = "0.1.0" +dependencies = [ + "bollard", + "chrono", + "clap", + "enumset", + "futures-util", + "serde_json", + "swss-common", + "tokio", +] + [[package]] name = "contracts" version = "0.6.3" @@ -384,12 +480,52 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "crossbeam-utils" version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.58", +] + [[package]] name = "dashmap" version = "6.0.1" @@ -404,6 +540,16 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + [[package]] name = "derivative" version = "2.2.0" @@ -421,6 +567,17 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "either" version = "1.10.0" @@ -433,6 +590,27 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +[[package]] +name = "enumset" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a4b049558765cef5f0c1a273c3fc57084d768b44d2f98127aef4cceb17293" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59c3b24c345d8c314966bdc1832f6c2635bfcce8e7cf363bd115987bba2ee242" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -477,6 +655,15 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[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-channel" version = "0.3.30" @@ -492,6 +679,17 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[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 2.0.58", +] + [[package]] name = "futures-sink" version = "0.3.30" @@ -511,9 +709,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", + "futures-macro", "futures-task", "pin-project-lite", "pin-utils", + "slab", ] [[package]] @@ -616,6 +816,12 @@ 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 = "http" version = "1.1.0" @@ -699,6 +905,21 @@ dependencies = [ "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-timeout" version = "0.5.1" @@ -732,6 +953,189 @@ dependencies = [ "tracing", ] +[[package]] +name = "hyperlocal" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "986c5ce3b994526b3cd75578e62554abd09f0899d6206de48b3e96ab34ccc8c7" +dependencies = [ + "hex", + "http-body-util", + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +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 = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "indenter" version = "0.3.3" @@ -746,6 +1150,7 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", + "serde", ] [[package]] @@ -756,6 +1161,7 @@ checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown 0.14.3", + "serde", ] [[package]] @@ -779,6 +1185,16 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -807,6 +1223,12 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +[[package]] +name = "litemap" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" + [[package]] name = "lock_api" version = "0.4.12" @@ -903,6 +1325,21 @@ dependencies = [ "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.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "object" version = "0.32.2" @@ -1019,6 +1456,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1320,6 +1763,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "serde_serializer_quick_unsupported" version = "0.1.2" @@ -1335,6 +1789,35 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +dependencies = [ + "base64", + "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" @@ -1424,6 +1907,12 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "strsim" version = "0.11.1" @@ -1634,6 +2123,17 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] + [[package]] name = "syslog-tracing" version = "0.3.1" @@ -1720,6 +2220,47 @@ dependencies = [ "once_cell", ] +[[package]] +name = "time" +version = "0.3.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef" + +[[package]] +name = "time-macros" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tokio" version = "1.42.0" @@ -1989,6 +2530,29 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -2040,6 +2604,64 @@ dependencies = [ "wit-bindgen-rt", ] +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.58", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + [[package]] name = "winapi" version = "0.3.9" @@ -2062,6 +2684,21 @@ 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-link" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3" + [[package]] name = "windows-sys" version = "0.48.0" @@ -2203,8 +2840,87 @@ dependencies = [ "bitflags", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "yansi" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", + "synstructure", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", +] diff --git a/Cargo.toml b/Cargo.toml index d79219f..817b882 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ members = [ "crates/swbus-proto", "crates/swbus-cli", "crates/swbus-config", + "crates/container", ] exclude = [] @@ -75,6 +76,10 @@ atomic_enum = "0.3" tempfile = "3" tabled = "0.17" futures-core = "0.3" +futures-util = "0.3" +chrono = "0.4" +enumset = "1" +bollard = { version = "0.17.1", features = ["chrono"] } uuid = { version = "1.15", features = ["v4"] } # Internal dependencies sonic-common = { version = "0.1.0", path = "crates/sonic-common" } diff --git a/crates/container/Cargo.toml b/crates/container/Cargo.toml new file mode 100644 index 0000000..90d8896 --- /dev/null +++ b/crates/container/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "container" +version.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +documentation.workspace = true +keywords.workspace = true +edition.workspace = true + +[dependencies] +bollard = { workspace = true } +chrono = { workspace = true } +enumset = { workspace = true } +futures-util = { workspace = true } +clap = { workspace = true } +swss-common = { path = "../swss-common" } +tokio = { workspace = true } +serde_json = { workspace = true } + +[lints] +workspace = true diff --git a/crates/container/src/container.rs b/crates/container/src/container.rs new file mode 100644 index 0000000..ea82114 --- /dev/null +++ b/crates/container/src/container.rs @@ -0,0 +1,358 @@ +use bollard::container::*; +use bollard::Docker; +use chrono::Local; +use futures_util::stream::TryStreamExt; +use std::borrow::Cow; +use std::collections::HashMap; +use std::fs::File; +use std::io::Read; +use std::path::Path; +use std::thread::sleep; +use std::time::Duration; +use swss_common::{CxxString, DbConnector}; + +// DB field names +const DB_FEATURE_TABLE: &str = "FEATURE"; +const DB_FIELD_SET_OWNER: &str = "set_owner"; +const DB_FIELD_NO_FALLBACK: &str = "no_fallback_to_local"; + +const DB_FIELD_CURRENT_OWNER: &str = "current_owner"; +const DB_FIELD_UPD_TIMESTAMP: &str = "update_time"; +const DB_FIELD_CONTAINER_ID: &str = "container_id"; +const DB_FIELD_REMOTE_STATE: &str = "remote_state"; +const DB_FIELD_SYSTEM_STATE: &str = "system_state"; +const DB_FIELD_STATE: &str = "state"; +const DB_FIELD_ST_FEAT_CTR_STABLE_VER: &str = "container_stable_version"; + +const DB_KUBE_LABEL_TABLE: &str = "KUBE_LABELS"; +const DB_KUBE_LABEL_SET_KEY: &str = "SET"; +const DB_SERVER_TABLE: &str = "KUBERNETES_MASTER"; +const DB_SERVER_KEY: &str = "SERVER"; +const DB_FIELD_ST_SER_CONNECTED: &str = "connected"; +const DB_FIELD_ST_SER_UPDATE_TS: &str = "update_time"; + +// Get seconds to wait for remote docker to start. +// If not, revert to local +// +const SONIC_CTR_CONFIG: &str = "/etc/sonic/remote_ctr.config.json"; +const SONIC_CTR_CONFIG_PEND_SECS: &str = "revert_to_local_on_wait_seconds"; +const DEFAULT_PEND_SECS: u32 = 5 * 60; +const WAIT_POLL_SECS: u32 = 2; + +struct DbConnections { + config_db: DbConnector, + state_db: DbConnector, + remote_ctr_enabled: bool, +} + +impl DbConnections { + fn initialize_connection() -> DbConnections { + DbConnections { + config_db: DbConnector::new_tcp(4, "localhost", 6379, 0).expect("Unable to connect to Redis DB"), + state_db: DbConnector::new_tcp(6, "localhost", 6379, 0).expect("Unable to connect to Redis DB"), + remote_ctr_enabled: Path::new("/lib/systemd/system/ctrmgrd.service").exists(), + } + } +} + +pub struct Container<'a> { + feature: &'a str, + db_connections: DbConnections, +} + +#[derive(enumset::EnumSetType, Debug)] +enum StartFlags { + StartLocal, + StartKube, +} + +impl<'a> Container<'a> { + fn get_config_data(field: &str) -> Option { + let mut file = match File::open(SONIC_CTR_CONFIG) { + Ok(f) => f, + Err(_e) => return None, + }; + let mut file_contents = String::new(); + file.read_to_string(&mut file_contents).unwrap(); + let data: serde_json::Value = serde_json::from_str(&file_contents).unwrap(); + data.as_object().unwrap().get(field).cloned() + } + + fn read_data(db_connector: &DbConnector, key: &str, fields: &mut HashMap<&str, String>) { + let table_name = if key == DB_SERVER_KEY { + DB_SERVER_TABLE + } else { + DB_FEATURE_TABLE + }; + + let data = db_connector + .hgetall(&format!("{}|{}", table_name, key)) + .expect("Unable to get data"); + for (field, default) in fields.iter_mut() { + if let Some(value) = data.get(field as &str) { + *default = value.to_str().unwrap().to_string() + } + } + } + + pub fn new(feature: &str) -> Container { + Container { + feature, + db_connections: DbConnections::initialize_connection(), + } + } + + fn read_config(&self) -> HashMap<&'static str, String> { + let mut fields: HashMap<&str, String> = HashMap::from([ + (DB_FIELD_SET_OWNER, "local".to_string()), + (DB_FIELD_NO_FALLBACK, "False".to_string()), + (DB_FIELD_STATE, "disabled".to_string()), + ]); + Self::read_data(&self.db_connections.config_db, self.feature, &mut fields); + fields + } + + fn read_state(&self) -> HashMap<&'static str, String> { + let mut fields: HashMap<&str, String> = HashMap::from([ + (DB_FIELD_CURRENT_OWNER, "local".to_string()), + (DB_FIELD_REMOTE_STATE, "none".to_string()), + (DB_FIELD_CONTAINER_ID, "".to_string()), + ]); + Self::read_data(&self.db_connections.state_db, self.feature, &mut fields); + fields + } + + fn read_server_state(&self) -> HashMap<&'static str, String> { + let mut fields: HashMap<&str, String> = HashMap::from([ + (DB_FIELD_ST_SER_CONNECTED, "false".to_string()), + (DB_FIELD_ST_SER_UPDATE_TS, "".to_string()), + ]); + Self::read_data(&self.db_connections.state_db, DB_SERVER_KEY, &mut fields); + fields + } + + fn set_label(&self, feature: &str, create: bool) { + if self.db_connections.remote_ctr_enabled { + self.db_connections + .state_db + .hset( + DB_KUBE_LABEL_TABLE, + &format!("{}|{}_enabled", DB_KUBE_LABEL_SET_KEY, feature), + &CxxString::new(if create { "true" } else { "false" }), + ) + .expect("Unable to set label of container in Redis"); + } + } + + fn update_data(&self, data: &HashMap<&str, &str>) { + if self.db_connections.remote_ctr_enabled { + for (key, value) in data.iter() { + let _ = self.db_connections.state_db.hset( + DB_FEATURE_TABLE, + &format!("{}|{}", self.feature, key), + &CxxString::new(value), + ); + } + } + } + + async fn container_version(&self, docker: &Docker) -> Option { + let container_options = docker + .inspect_container(self.feature, None) + .await + .expect("Error getting the version of container"); + if let Some(config) = container_options.config { + if let Some(envs) = config.env { + for env in envs { + if env.starts_with("IMAGE_VERSION=") { + return Some(env.split('=').collect::>()[1].to_string()); + } + } + } + } + None + } + + pub fn container_id(&self) -> Cow<'a, str> { + let data = self + .db_connections + .state_db + .hgetall(&format!("FEATURE|{}", self.feature)) + .expect("Unable to get data"); + if data + .get(DB_FIELD_CURRENT_OWNER) + .map_or("", |value| value.to_str().unwrap()) + == "local" + { + Cow::Borrowed(self.feature) + } else { + data.get(DB_FIELD_CONTAINER_ID) + .map_or(Cow::Borrowed(self.feature), |value| { + Cow::Owned(value.to_str().unwrap().to_string()) + }) + } + } + + pub async fn start(&self) { + let feature_config = self.read_config(); + let feature_state = self.read_state(); + let server_state = self.read_server_state(); + + let timestamp = format!("{}", Local::now().format("%Y-%m-%d %H:%M:%S")); + let mut data: HashMap<&str, &str> = + HashMap::from([(DB_FIELD_SYSTEM_STATE, "up"), (DB_FIELD_UPD_TIMESTAMP, ×tamp)]); + + let mut start_val = enumset::EnumSet::new(); + if feature_config.get(DB_FIELD_SET_OWNER).unwrap() == "local" { + start_val |= StartFlags::StartLocal; + } else { + start_val |= StartFlags::StartKube; + if feature_config.get(DB_FIELD_NO_FALLBACK).unwrap() == "False" + && (feature_state.get(DB_FIELD_REMOTE_STATE).unwrap() == "none" + || server_state.get(DB_FIELD_ST_SER_CONNECTED).unwrap() == "false") + { + start_val |= StartFlags::StartLocal; + data.insert(DB_FIELD_REMOTE_STATE, "none"); + } + } + + if start_val & StartFlags::StartLocal != enumset::EnumSet::empty() { + data.insert(DB_FIELD_CURRENT_OWNER, "local"); + data.insert(DB_FIELD_CONTAINER_ID, self.feature); + if start_val == StartFlags::StartLocal { + self.set_label(self.feature, false); + data.insert(DB_FIELD_REMOTE_STATE, "none"); + } + } + + self.update_data(&data); + + if start_val & StartFlags::StartLocal != enumset::EnumSet::empty() { + let docker = Docker::connect_with_local_defaults().unwrap(); + docker + .start_container(self.feature, None::>) + .await + .expect("Unable to start container"); + } + + if start_val & StartFlags::StartKube != enumset::EnumSet::empty() { + self.set_label(self.feature, true); + } + } + + pub async fn stop(&self, timeout: Option) { + let feature_config = self.read_config(); + let feature_state = self.read_state(); + let docker_id = self.container_id(); + let remove_label = feature_config.get(DB_FIELD_SET_OWNER).unwrap() != "local"; + + let docker = Docker::connect_with_local_defaults().unwrap(); + + if remove_label { + self.set_label(self.feature, false); + } + + if !docker_id.is_empty() { + let stop_options = timeout.map(|timeout| StopContainerOptions { t: timeout }); + docker + .stop_container(self.feature, stop_options) + .await + .expect("Unable to stop container"); + } + + let timestamp = format!("{}", Local::now().format("%Y-%m-%d %H:%M:%S")); + let mut data: HashMap<&str, &str> = HashMap::from([ + (DB_FIELD_CURRENT_OWNER, "none"), + (DB_FIELD_SYSTEM_STATE, "down"), + (DB_FIELD_CONTAINER_ID, ""), + (DB_FIELD_UPD_TIMESTAMP, ×tamp), + ]); + if feature_state.get(DB_FIELD_REMOTE_STATE).unwrap() == "running" { + data.insert(DB_FIELD_REMOTE_STATE, "stopped"); + } + + self.update_data(&data); + } + + pub async fn kill(&self) { + let feature_config = self.read_config(); + let feature_state = self.read_state(); + let docker_id = self.container_id(); + let remove_label = (feature_config.get(DB_FIELD_SET_OWNER).unwrap() != "local") + || (feature_state.get(DB_FIELD_CURRENT_OWNER).unwrap() != "local"); + + if remove_label { + self.set_label(self.feature, false); + } + + if feature_config.get(DB_FIELD_SET_OWNER).unwrap() == "local" { + let current_state = feature_state.get(DB_FIELD_STATE).unwrap(); + if current_state != "enabled" && current_state != "always_enabled" { + println!("{} is not enabled", self.feature); + return; + } + } + + let docker = Docker::connect_with_local_defaults().unwrap(); + + if !docker_id.is_empty() { + docker + .kill_container(self.feature, Some(KillContainerOptions { signal: "SIGINT" })) + .await + .expect("Unable to kill container"); + } + } + + pub async fn wait(&self) { + let feature_config = self.read_config(); + //let mut feature_state = read_state(&db_connections, feature); + let mut feature_state; + let mut docker_id = self.container_id(); + let mut pend_wait_seconds: u32 = 0; + + let docker = Docker::connect_with_local_defaults().unwrap(); + + if *docker_id == *self.feature { + let version_option = self.container_version(&docker).await; + if let Some(version) = version_option { + self.update_data(&HashMap::from([(DB_FIELD_ST_FEAT_CTR_STABLE_VER, version.as_str())])); + } + } + + if docker_id.is_empty() && feature_config.get(DB_FIELD_NO_FALLBACK).unwrap() == "False" { + pend_wait_seconds = Self::get_config_data(SONIC_CTR_CONFIG_PEND_SECS) + .and_then(|value| value.as_u64()) + .map(|value| value as u32) + .unwrap_or(DEFAULT_PEND_SECS); + } + + while docker_id.is_empty() { + if feature_config.get(DB_FIELD_NO_FALLBACK).unwrap() == "False" { + if pend_wait_seconds < WAIT_POLL_SECS { + break; + } + pend_wait_seconds -= WAIT_POLL_SECS; + } + + sleep(Duration::from_secs(WAIT_POLL_SECS as u64)); + feature_state = self.read_state(); + + docker_id = Cow::Borrowed(feature_state.get(DB_FIELD_CONTAINER_ID).unwrap()); + + if feature_state.get(DB_FIELD_REMOTE_STATE).unwrap() == "pending" { + self.update_data(&HashMap::from([(DB_FIELD_REMOTE_STATE, "ready")])); + } + } + + docker + .wait_container( + self.feature, + Some(WaitContainerOptions { + condition: "not-running", + }), + ) + .try_collect::>() + .await + .expect("Unable to wait for container"); + } +} diff --git a/crates/container/src/main.rs b/crates/container/src/main.rs new file mode 100644 index 0000000..53accd4 --- /dev/null +++ b/crates/container/src/main.rs @@ -0,0 +1,42 @@ +use clap::{Parser, ValueEnum}; +use container::Container; +mod container; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] +enum Action { + Start, + Stop, + Kill, + Wait, + Id, +} + +#[derive(Parser)] +#[command(version, about, long_about = None)] +struct Cli { + #[arg(value_enum)] + /// The action to take for the container + action: Action, + + /// The name of the container + name: String, + + /// Timeout for the action to occur + #[arg(short, long)] + timeout: Option, +} + +#[tokio::main] +async fn main() { + let cli = Cli::parse(); + + let container = Container::new(&cli.name); + + match cli.action { + Action::Start => container.start().await, + Action::Wait => container.wait().await, + Action::Stop => container.stop(cli.timeout).await, + Action::Kill => container.kill().await, + Action::Id => println!("{}", container.container_id()), + }; +}