diff --git a/Cargo.lock b/Cargo.lock index 930461762..e52d06a6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -142,7 +142,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "258b52a1aa741b9f09783b2d86cf0aeeb617bbf847f6933340a39644227acbdb" dependencies = [ - "event-listener 5.3.0", + "event-listener 5.3.1", "event-listener-strategy 0.5.2", "futures-core", "pin-project-lite", @@ -173,9 +173,9 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b10202063978b3351199d68f8b22c4e47e4b1b822f8d43fd862d5ea8c006b29a" +checksum = "c8828ec6e544c02b0d6691d21ed9f9218d0384a82542855073c2a3f58304aaf0" dependencies = [ "async-task", "concurrent-queue 2.5.0", @@ -283,7 +283,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -351,9 +351,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" dependencies = [ "addr2line", "cc", @@ -414,12 +414,11 @@ dependencies = [ [[package]] name = "blocking" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "495f7104e962b7356f0aeb34247aca1fe7d2e783b346582db7f2904cb5717e88" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" dependencies = [ "async-channel 2.3.1", - "async-lock 3.3.0", "async-task", "futures-io", "futures-lite 2.3.0", @@ -512,9 +511,9 @@ checksum = "981520c98f422fcc584dc1a95c334e6953900b9106bc47a9839b81790009eb21" [[package]] name = "cc" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" +checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" [[package]] name = "cfg-if" @@ -528,6 +527,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.38" @@ -572,7 +577,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -651,9 +656,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] @@ -673,9 +678,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" dependencies = [ "crossbeam-utils", ] @@ -710,9 +715,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crypto-common" @@ -734,6 +739,41 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "darling" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.66", +] + +[[package]] +name = "darling_macro" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.66", +] + [[package]] name = "deranged" version = "0.3.11" @@ -752,7 +792,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -783,9 +823,9 @@ dependencies = [ [[package]] name = "diesel" -version = "2.1.6" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff236accb9a5069572099f0b350a92e9560e8e63a9b8d546162f4a5e03026bb2" +checksum = "35b696af9ff4c0d2a507db2c5faafa8aa0205e297e5f11e203a24226d5355e7a" dependencies = [ "bitflags 2.5.0", "byteorder", @@ -800,21 +840,22 @@ dependencies = [ [[package]] name = "diesel_derives" -version = "2.1.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14701062d6bed917b5c7103bdffaee1e4609279e240488ad24e7bd979ca6866c" +checksum = "0d6fdd83d5947068817016e939596d246e5367279453f2a3433287894f2f2996" dependencies = [ "diesel_table_macro_syntax", + "dsl_auto_type", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] name = "diesel_migrations" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6036b3f0120c5961381b570ee20a02432d7e2d27ea60de9578799cf9156914ac" +checksum = "8a73ce704bad4231f001bff3314d91dce4aba0770cee8b233991859abc15c1f6" dependencies = [ "diesel", "migrations_internals", @@ -823,11 +864,11 @@ dependencies = [ [[package]] name = "diesel_table_macro_syntax" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" +checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25" dependencies = [ - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -848,7 +889,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -863,6 +904,20 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +[[package]] +name = "dsl_auto_type" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab32c18ea6760d951659768a3e35ea72fc1ba0916d665a88dfe048b2a41e543f" +dependencies = [ + "darling", + "either", + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "dyn-clone" version = "1.0.17" @@ -877,9 +932,9 @@ checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "enclose" -version = "1.1.8" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1056f553da426e9c025a662efa48b52e62e0a3a7648aa2d15aeaaf7f0d329357" +checksum = "bef4f6f904480430009ad8f22edc9573e26e4f137365f014d7ea998d5341639a" [[package]] name = "encode_unicode" @@ -965,9 +1020,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.3.0" +version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" dependencies = [ "concurrent-queue 2.5.0", "parking", @@ -990,7 +1045,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" dependencies = [ - "event-listener 5.3.0", + "event-listener 5.3.1", "pin-project-lite", ] @@ -1188,7 +1243,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -1238,11 +1293,12 @@ dependencies = [ [[package]] name = "generator" -version = "0.7.5" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" +checksum = "186014d53bc231d0090ef8d6f03e0920c54d85a5ed22f4f2f74315ec56cf83fb" dependencies = [ "cc", + "cfg-if", "libc", "log", "rustversion", @@ -1274,9 +1330,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] name = "glommio" @@ -1466,9 +1522,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56" dependencies = [ "bytes", "futures-channel", @@ -1510,7 +1566,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.52.0", ] [[package]] @@ -1522,6 +1578,12 @@ dependencies = [ "cc", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.5.0" @@ -1791,7 +1853,7 @@ checksum = "fc2fb41a9bb4257a3803154bdf7e2df7d45197d1941c9b1a90ad815231630721" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -1839,9 +1901,9 @@ dependencies = [ [[package]] name = "loom" -version = "0.5.6" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" dependencies = [ "cfg-if", "generator", @@ -1895,9 +1957,9 @@ dependencies = [ [[package]] name = "metrsd_client" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49d6d743b359862d87ec9ad43ba1dca54991114a996afd327ed170852de2fa54" +checksum = "dc056be4a50fa2d97090b7ec6a9926769a068c4a209709a8b8618f189d4f0c71" dependencies = [ "futures", "metrs_stubs", @@ -1909,19 +1971,19 @@ dependencies = [ [[package]] name = "migrations_internals" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f23f71580015254b020e856feac3df5878c2c7a8812297edd6c0a485ac9dada" +checksum = "fd01039851e82f8799046eabbb354056283fb265c8ec0996af940f4e85a380ff" dependencies = [ "serde", - "toml 0.7.8", + "toml", ] [[package]] name = "migrations_macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cce3325ac70e67bbab5bd837a31cae01f1a6db64e0e744a33cb03a543469ef08" +checksum = "ffb161cc72176cb37aa47f1fc520d3ef02263d67d661f44f05d05a079e1237fd" dependencies = [ "migrations_internals", "proc-macro2", @@ -1989,7 +2051,7 @@ dependencies = [ "nanocl_error", "nanocl_utils", "nanocld_client", - "nix 0.28.0", + "nix 0.29.0", "ntex", "openssl", "regex", @@ -2001,7 +2063,7 @@ dependencies = [ "termios", "tokio", "tokio-util", - "toml 0.8.13", + "toml", "url", ] @@ -2191,7 +2253,19 @@ checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ "bitflags 2.5.0", "cfg-if", - "cfg_aliases", + "cfg_aliases 0.1.1", + "libc", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "cfg_aliases 0.2.1", "libc", ] @@ -2226,9 +2300,9 @@ dependencies = [ [[package]] name = "ntex" -version = "1.2.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60807bd24e60f2a9cb17a5bcd49244a1ecfca86302145943e140d958e75ee4c9" +checksum = "7d088df90e0dd67ffd12e267281edfddb9277f6d91a861541f1b4d826c2fea32" dependencies = [ "base64", "bitflags 2.5.0", @@ -2264,9 +2338,9 @@ dependencies = [ [[package]] name = "ntex-async-std" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0796d14f7e9a207364cc1fa0a99c45b6151bfe2c60ad9c90f1f625746ec6612" +checksum = "0755241ebac01bd919bf071cbfb4d2e6c3c74613f284cc9fcc6604b6fd949b60" dependencies = [ "async-std", "log", @@ -2274,7 +2348,6 @@ dependencies = [ "ntex-io", "ntex-util", "oneshot", - "pin-project-lite", ] [[package]] @@ -2300,9 +2373,9 @@ dependencies = [ [[package]] name = "ntex-cors" -version = "1.0.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33923472f6c4966b86fe07ab82b3ebb5d0b35102c10346f337ddd5d753a5f156" +checksum = "5240350681b4dab9bcbdbafdb7b25c526c069a9e298f06de00331cd3442311bf" dependencies = [ "derive_more", "futures", @@ -2311,11 +2384,11 @@ dependencies = [ [[package]] name = "ntex-glommio" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1289e0535066445936a9c1b7470c65dd01881776c6bb810638afc505458a9422" +checksum = "af97a09ca3840c7b16ae537fea7f24d9e8117425dadd1b84c1141c22362828e7" dependencies = [ - "futures-lite 1.13.0", + "futures-lite 2.3.0", "glommio", "log", "ntex-bytes", @@ -2326,9 +2399,9 @@ dependencies = [ [[package]] name = "ntex-h2" -version = "0.5.5" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d02baa9fb658c1d2212a4e342007b7b5c05af5c926314ad1ac96ee474ce50879" +checksum = "f2f0b6bd1bdce912d5ccb4fb9e442f89c129dc07bdb5cad321606e976e989dc0" dependencies = [ "bitflags 2.5.0", "fxhash", @@ -2362,9 +2435,9 @@ dependencies = [ [[package]] name = "ntex-io" -version = "1.2.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c3b2c13e80ebac91b90854a4b9f94f00f8e367e669aace5a069e0213dc25f4d" +checksum = "72c60a60c517453a2ea7fd40483b87531bd8d6ce0e545f4643dabd1f646620a9" dependencies = [ "bitflags 2.5.0", "log", @@ -2388,9 +2461,9 @@ dependencies = [ [[package]] name = "ntex-net" -version = "1.0.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b3d25d8f34ef7129f51a0230cddd8fa4dad9d4afc69e845828437156bbb3cd" +checksum = "44110aa49eddc9fdee2e673698fb6cc61bea3c1df1c8239bab86d465cafd2097" dependencies = [ "log", "ntex-async-std", @@ -2436,9 +2509,9 @@ dependencies = [ [[package]] name = "ntex-server" -version = "1.0.5" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29861bbf4d3fdd3803a1eee4c294f6d06ef402c3b57b1385eaca2cf8b5d428b1" +checksum = "817bfb341cb76871ae84521465f353e5ed8db7d72b01cf4e3c1fbe2c01a4a63a" dependencies = [ "async-broadcast", "async-channel 2.3.1", @@ -2457,18 +2530,18 @@ dependencies = [ [[package]] name = "ntex-service" -version = "2.0.1" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b4c979fdb3ce8ff5e7538fbdb60edfd960c6a9152456b9b6a22fcf4e830b254" +checksum = "bcebe6c7fa4d346646ef94c29957aab40078e91cd4c73ef3a1394f2a49de50d4" dependencies = [ "slab", ] [[package]] name = "ntex-tls" -version = "1.1.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "056847ea86f0a2a490fe557442a149f466e61a296f2f57a9845d29de43f3f875" +checksum = "7d996277357fbbf5bd73e1ec9e93a97e3192ee748f496f8dde88a578146647d4" dependencies = [ "log", "ntex-bytes", @@ -2481,23 +2554,22 @@ dependencies = [ [[package]] name = "ntex-tokio" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd222124711e39aac5902abf45edbb385c2152d47230da41f5e5aed448795ed4" +checksum = "5715ef31e5a599ab467846acd948ea597193fc08343338fdecbd1e5fe181af3d" dependencies = [ "log", "ntex-bytes", "ntex-io", "ntex-util", - "pin-project-lite", "tokio", ] [[package]] name = "ntex-util" -version = "1.0.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caad794a6eb4d73fb4c13b7874fe3e6e9042deab50d6d325b584436efd0db433" +checksum = "6411c290c1b2a96ebea62cf568f271e7fdeb1a5defa6f8411f13d1211a6e8b4f" dependencies = [ "bitflags 2.5.0", "futures-core", @@ -2616,6 +2688,27 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "number_prefix" version = "0.4.0" @@ -2624,9 +2717,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "object" -version = "0.32.2" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" dependencies = [ "memchr", ] @@ -2639,9 +2732,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oneshot" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f6640c6bda7731b1fdbab747981a0f896dd1fedaf9f4a53fa237a04a84431f4" +checksum = "071d1cf3298ad8e543dca18217d198cb6a3884443d204757b9624b935ef09fa0" dependencies = [ "loom", ] @@ -2669,14 +2762,14 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] name = "openssl-src" -version = "300.2.3+3.2.1" +version = "300.3.0+3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cff92b6f71555b61bb9315f7c64da3ca43d87531622120fea0195fc761b4843" +checksum = "eba8804a1c5765b18c4b3f907e6897ebabeedebc9830e1a0046c4a4cf44663e1" dependencies = [ "cc", ] @@ -2725,9 +2818,9 @@ checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -2783,7 +2876,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -2814,7 +2907,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -2897,13 +2990,22 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "pq-sys" -version = "0.4.8" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c0052426df997c0cbd30789eb44ca097e3541717a7b8fa36b1c464ee7edebd" +checksum = "5576e3fa8738e1a71285f7211ff83458514aa4864aa34c2bdb422445448d4c4b" dependencies = [ "vcpkg", ] +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit 0.21.1", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -2930,9 +3032,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" dependencies = [ "unicode-ident", ] @@ -3141,7 +3243,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.64", + "syn 2.0.66", "walkdir", ] @@ -3270,9 +3372,9 @@ dependencies = [ [[package]] name = "schemars" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0218ceea14babe24a4a5836f86ade86c1effbc198164e619194cb5069187e29" +checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" dependencies = [ "chrono", "dyn-clone", @@ -3284,14 +3386,14 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed5a1ccce8ff962e31a165d41f6e2a2dd1245099dc4d594f5574a86cd90f4d3" +checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -3314,22 +3416,22 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.202" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.202" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -3340,7 +3442,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -3362,7 +3464,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -3553,9 +3655,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.64" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ad3dee41f36859875573074334c200d1add8e4a87bb37113ebd31d926b7b11f" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", @@ -3630,7 +3732,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -3691,9 +3793,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.37.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "bytes", @@ -3730,18 +3832,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.19.15", -] - [[package]] name = "toml" version = "0.8.13" @@ -3765,13 +3855,11 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.19.15" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ "indexmap 2.2.6", - "serde", - "serde_spanned", "toml_datetime", "winnow 0.5.40", ] @@ -3786,7 +3874,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.8", + "winnow 0.6.9", ] [[package]] @@ -3802,7 +3890,6 @@ dependencies = [ "tokio", "tower-layer", "tower-service", - "tracing", ] [[package]] @@ -3823,7 +3910,6 @@ 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", @@ -3837,7 +3923,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] @@ -3991,15 +4077,15 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", "uuid", ] [[package]] name = "utoipa-swagger-ui" -version = "7.0.1" +version = "7.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49396bb19184d3ac1220bb56b3ec91e3d6275baa6cbd5a0f36f110a88d6afb" +checksum = "943e0ff606c6d57d410fd5663a4d7c074ab2c5f14ab903b9514565e59fa1189e" dependencies = [ "mime_guess", "regex", @@ -4007,6 +4093,7 @@ dependencies = [ "rust-embed", "serde", "serde_json", + "url", "utoipa", "zip", ] @@ -4047,9 +4134,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vpnkitrc" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7a90a7840beab82deb8f7b903681ee443e4cb9200f13562c6d20f3960db4b9a" +checksum = "cb13eb8fca6597c54abd81f00a71c38dd4ffeb6952910a50a35a8235cca8fb8b" dependencies = [ "ntex", "serde", @@ -4108,7 +4195,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", "wasm-bindgen-shared", ] @@ -4142,7 +4229,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4205,11 +4292,12 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.48.0" +version = "0.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" dependencies = [ - "windows-targets 0.48.5", + "windows-core 0.54.0", + "windows-targets 0.52.5", ] [[package]] @@ -4221,6 +4309,25 @@ dependencies = [ "windows-targets 0.52.5", ] +[[package]] +name = "windows-core" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" +dependencies = [ + "windows-result", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-result" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b" +dependencies = [ + "windows-targets 0.52.5", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -4371,9 +4478,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +checksum = "86c949fede1d13936a99f14fafd3e76fd642b556dd2ce96287fbe2e0151bfac6" dependencies = [ "memchr", ] @@ -4405,20 +4512,20 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.64", + "syn 2.0.66", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" [[package]] name = "zip" -version = "1.3.0" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f4a27345eb6f7aa7bd015ba7eb4175fa4e1b462a29874b779e0bbcf96c6ac7" +checksum = "9cc23c04387f4da0374be4533ad1208cbb091d5c11d070dfef13676ad6497164" dependencies = [ "arbitrary", "crc32fast", @@ -4426,5 +4533,6 @@ dependencies = [ "displaydoc", "flate2", "indexmap 2.2.6", + "num_enum", "thiserror", ] diff --git a/README.md b/README.md index a3ae623ee..20f544e48 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@
- +

Nanocl

@@ -35,7 +35,7 @@ `Nanocl` is all about easing your container and VM management with Rust-powered platform. With `Nanocl`, say goodbye to complex setups and hello to easy, efficient deployments. We stand for robust performance and efficiency with simplicity, trimming the bloat to keep your systems lean. -**_Join us and help shape the future of cloud computing - it's about time things got a bit more rusty_**. +**_Join our [discord][discord] and help shape the future of cloud computing - it's about time things got a bit more rusty_**. ## 📙 Table of Contents diff --git a/bin/nanocl/Cargo.toml b/bin/nanocl/Cargo.toml index 2f5365310..fed59574d 100644 --- a/bin/nanocl/Cargo.toml +++ b/bin/nanocl/Cargo.toml @@ -44,7 +44,7 @@ serde_yaml = "0.9" tabled = "0.15" indicatif = "0.17" serde_json = "1.0" -ntex = { version = "1.2", features = ["tokio", "openssl"] } +ntex = { version = "2", features = ["tokio", "openssl"] } serde = { version = "1.0", features = ["derive"] } clap = { version = "4.5", features = ["derive", "cargo"] } tokio = { version = "1.36", features = ["fs"] } @@ -70,7 +70,7 @@ nanocl_utils = { version = "0.6", features = ["unix"] } ctrlc = "3.4" toml = "0.8" ring = "0.17" -nix = { version = "0.28", features = ["user"] } +nix = { version = "0.29", features = ["user"] } dotenvy = "0.15" openssl = "0.10" async-recursion = "1.1" diff --git a/bin/nanocl/src/commands/state.rs b/bin/nanocl/src/commands/state.rs index 20f63fe8a..6d4678a66 100644 --- a/bin/nanocl/src/commands/state.rs +++ b/bin/nanocl/src/commands/state.rs @@ -244,7 +244,7 @@ fn gen_client( url, version: Some(version.into()), ..Default::default() - }) + })? } api_version if state_ref.data.api_version.starts_with('v') => { NanocldClient::connect_to(&ConnectOpts { @@ -257,7 +257,7 @@ fn gen_client( .ssl .clone(), version: Some(api_version.clone()), - }) + })? } _ => { let mut paths = state_ref @@ -278,7 +278,7 @@ fn gen_client( url, version: Some(version.into()), ..Default::default() - }) + })? } }; Ok(client) diff --git a/bin/nanocl/src/main.rs b/bin/nanocl/src/main.rs index 18d94cbbc..078b5ec71 100644 --- a/bin/nanocl/src/main.rs +++ b/bin/nanocl/src/main.rs @@ -1,7 +1,7 @@ use clap::Parser; use dotenvy::dotenv; -use nanocld_client::{ConnectOpts, NanocldClient}; +use nanocld_client::{stubs::system::SslConfig, ConnectOpts, NanocldClient}; use nanocl_error::io::{IoError, IoResult}; mod utils; @@ -29,7 +29,6 @@ fn create_cli_config(cli_args: &Cli) -> IoResult { } } let endpoint = context.endpoints.get("Nanocl").unwrap(); - #[allow(unused)] let mut host = cli_args.host.clone().unwrap_or(endpoint.host.clone()); #[cfg(any(feature = "dev", feature = "test"))] { @@ -40,11 +39,35 @@ fn create_cli_config(cli_args: &Cli) -> IoResult { .unwrap_or("http://nanocl.internal:8585".into()); } } + let mut ssl = match &endpoint.ssl { + Some(ssl) => { + let cert = std::fs::read_to_string(ssl.cert.clone().unwrap())?; + let cert_key = std::fs::read_to_string(ssl.cert_key.clone().unwrap())?; + Some(SslConfig { + cert: Some(cert), + cert_key: Some(cert_key), + ..Default::default() + }) + } + None => None, + }; + if let Ok(c) = std::env::var("CERT") { + if let Ok(ck) = std::env::var("CERT_KEY") { + ssl = Some(SslConfig { + cert: Some(c), + cert_key: Some(ck), + ..Default::default() + }); + } + } + if let Ok(h) = std::env::var("HOST") { + host = h; + } let client = NanocldClient::connect_to(&ConnectOpts { url: host.clone(), - ssl: endpoint.ssl.clone(), + ssl, ..Default::default() - }); + })?; Ok(CliConfig { host, client, @@ -160,7 +183,7 @@ mod tests { // Try to stop a cargo assert_cli_ok!("cargo", "stop", CARGO_NAME); // Try to remove cargo - assert_cli_ok!("cargo", "rm", "-yf", CARGO_NAME); + assert_cli_ok!("cargo", "rm", "-y", CARGO_NAME); } /// Test state file when then include other state files diff --git a/bin/nanocl/src/utils/mod.rs b/bin/nanocl/src/utils/mod.rs index 17f731458..db8269f4c 100644 --- a/bin/nanocl/src/utils/mod.rs +++ b/bin/nanocl/src/utils/mod.rs @@ -19,6 +19,7 @@ pub mod tests { url: "http://nanocl.internal:8585".into(), ..Default::default() }) + .expect("Failed to create a nanocl client") } #[macro_export] diff --git a/bin/nanocld/Cargo.toml b/bin/nanocld/Cargo.toml index 208793fc4..eb4122d7b 100644 --- a/bin/nanocld/Cargo.toml +++ b/bin/nanocld/Cargo.toml @@ -59,13 +59,14 @@ clap = { version = "4.5", features = ["derive"] } url = { version = "2", features = ["serde"] } serde = { version = "1.0", features = ["derive"] } uuid = { version = "1.8", features = ["serde", "v4"] } -ntex = { version = "1.2", features = ["tokio", "openssl"] } +ntex = { version = "2", features = ["tokio", "openssl"] } diesel = { version = "2.1", features = [ "postgres", "r2d2", "chrono", "uuid", "serde_json", + "i-implement-a-third-party-backend-and-opt-into-breaking-changes", ] } tokio = { version = "1.36", features = ["fs", "process", "io-std"] } tokio-util = "0.7" @@ -83,6 +84,6 @@ nanocl_stubs = { version = "0.15", features = ["serde", "clap"] } nanocl_utils = { version = "0.6", features = ["unix", "ntex", "logger"] } utoipa = { version = "4.2", features = ["yaml"], optional = true } notify = "6.1" -ntex-cors = "1.0" +ntex-cors = "2" rand = "0.8" openssl = { version = "0.10" } diff --git a/bin/nanocld/specs/swagger.yaml b/bin/nanocld/specs/swagger.yaml index fea3ba8f8..ee3f5a9fa 100644 --- a/bin/nanocld/specs/swagger.yaml +++ b/bin/nanocld/specs/swagger.yaml @@ -3643,10 +3643,8 @@ components: description: Generic filter for list operation properties: where: - type: object - description: Where clause - additionalProperties: - $ref: '#/components/schemas/GenericClause' + allOf: + - $ref: '#/components/schemas/GenericWhere' nullable: true limit: type: integer @@ -3658,6 +3656,12 @@ components: description: Offset to navigate through items nullable: true minimum: 0 + order_by: + type: array + items: + type: string + description: Order by + nullable: true GenericResources: type: object properties: @@ -3688,6 +3692,18 @@ components: Value: type: string nullable: true + GenericWhere: + type: object + properties: + or: + type: array + items: + type: object + additionalProperties: + $ref: '#/components/schemas/GenericClause' + nullable: true + additionalProperties: + $ref: '#/components/schemas/GenericClause' GraphDriverData: type: object description: Information about the storage driver used to store the container's and image's filesystem. diff --git a/bin/nanocld/src/models/mod.rs b/bin/nanocld/src/models/mod.rs index 6346d6183..6be7f4f58 100644 --- a/bin/nanocld/src/models/mod.rs +++ b/bin/nanocld/src/models/mod.rs @@ -1,5 +1,3 @@ -use std::sync::Arc; - use diesel::PgConnection; use diesel::r2d2::{Pool as R2D2Pool, PooledConnection, ConnectionManager}; @@ -57,12 +55,91 @@ pub use task_manager::*; mod object_process_status; pub use object_process_status::*; -pub type Pool = Arc>>; +pub type Pool = R2D2Pool>; pub type DBConn = PooledConnection>; +pub enum ColumnType { + Text, + Json, + Uuid, + Timestamptz, +} + +/// Generate a where clause for a json column +#[macro_export] +macro_rules! gen_sql_and4json { + ($query: expr, $column: expr, $value: expr) => { + match $value { + nanocl_stubs::generic::GenericClause::IsNull => { + Box::new($query.and($column.is_null())) + } + nanocl_stubs::generic::GenericClause::IsNotNull => { + Box::new($query.and($column.is_not_null())) + } + nanocl_stubs::generic::GenericClause::Contains(val) => { + Box::new($query.and($column.contains(val.clone()))) + } + nanocl_stubs::generic::GenericClause::HasKey(val) => { + Box::new($query.and($column.has_key(val.clone()))) + } + _ => { + panic!("Unsupported clause"); + } + } + }; +} + +// /// Generate clause for a string column +#[macro_export] +macro_rules! gen_sql_and4string { + ($query: expr, $column: expr, $value: expr) => { + match $value { + nanocl_stubs::generic::GenericClause::Eq(val) => { + Box::new($query.and($column.eq(val.clone()))) + } + nanocl_stubs::generic::GenericClause::Ne(val) => { + Box::new($query.and($column.ne(val.clone()))) + } + nanocl_stubs::generic::GenericClause::Gt(val) => { + Box::new($query.and($column.gt(val.clone()))) + } + nanocl_stubs::generic::GenericClause::Lt(val) => { + Box::new($query.and($column.lt(val.clone()))) + } + nanocl_stubs::generic::GenericClause::Ge(val) => { + Box::new($query.and($column.ge(val.clone()))) + } + nanocl_stubs::generic::GenericClause::Le(val) => { + Box::new($query.and($column.le(val.clone()))) + } + nanocl_stubs::generic::GenericClause::Like(val) => { + Box::new($query.and($column.like(val.clone()))) + } + nanocl_stubs::generic::GenericClause::NotLike(val) => { + Box::new($query.and($column.not_like(val.clone()))) + } + nanocl_stubs::generic::GenericClause::In(items) => { + Box::new($query.and($column.eq_any(items.clone()))) + } + nanocl_stubs::generic::GenericClause::NotIn(items) => { + Box::new($query.and($column.ne_all(items.clone()))) + } + nanocl_stubs::generic::GenericClause::IsNull => { + Box::new($query.and($column.is_null())) + } + nanocl_stubs::generic::GenericClause::IsNotNull => { + Box::new($query.and($column.is_not_null())) + } + _ => { + panic!("Unsupported clause"); + } + } + }; +} + /// Generate a where clause for a string column #[macro_export] -macro_rules! gen_where4string { +macro_rules! gen_sql_where4string { ($query: expr, $column: expr, $value: expr) => { match $value { nanocl_stubs::generic::GenericClause::Eq(val) => { @@ -110,7 +187,7 @@ macro_rules! gen_where4string { /// Generate a where clause for a json column #[macro_export] -macro_rules! gen_where4json { +macro_rules! gen_sql_where4json { ($query: expr, $column: expr, $value: expr) => { match $value { nanocl_stubs::generic::GenericClause::IsNull => { @@ -133,7 +210,28 @@ macro_rules! gen_where4json { } #[macro_export] -macro_rules! gen_where4uuid { +macro_rules! gen_sql_and4uuid { + ($query: expr, $column: expr, $value: expr) => { + match $value { + nanocl_stubs::generic::GenericClause::IsNull => { + Box::new($query.and($column.is_null())) + } + nanocl_stubs::generic::GenericClause::IsNotNull => { + Box::new($query.and($column.is_not_null())) + } + nanocl_stubs::generic::GenericClause::Eq(val) => { + let uuid = uuid::Uuid::parse_str(&val).unwrap_or_default(); + Box::new($query.and($column.eq(uuid))) + } + _ => { + panic!("Unsupported clause"); + } + } + }; +} + +#[macro_export] +macro_rules! gen_sql_where4uuid { ($query: expr, $column: expr, $value: expr) => { match $value { nanocl_stubs::generic::GenericClause::IsNull => { @@ -154,13 +252,141 @@ macro_rules! gen_where4uuid { } #[macro_export] -macro_rules! gen_multiple { - ($query: expr, $column: expr, $filter: expr) => { - $query = $query.order($column.desc()); +macro_rules! gen_sql_multiple { + ($query: expr, $filter: expr) => { let limit = $filter.limit.unwrap_or(100); - $query = $query.limit(limit as i64); - if let Some(offset) = $filter.offset { - $query = $query.offset(offset as i64); - } + let offset = $filter.offset.unwrap_or(0); + $query = $query.limit(limit as i64).offset(offset as i64); }; } + +#[macro_export] +macro_rules! gen_sql_query { + ($query:expr, $filter:expr, $columns:expr) => {{ + let r#where = $filter.r#where.to_owned().unwrap_or_default(); + let conditions = r#where.conditions; + for (key, value) in conditions { + if let Some(s_column) = $columns.get(key.as_str()) { + match s_column.0 { + ColumnType::Uuid => { + let column = + diesel::dsl::sql::(s_column.1); + $crate::gen_sql_where4uuid!($query, column, value); + } + ColumnType::Json => { + let column = + diesel::dsl::sql::(s_column.1); + $crate::gen_sql_where4json!($query, column, value); + } + ColumnType::Text => { + let column = + diesel::dsl::sql::(s_column.1); + $crate::gen_sql_where4string!($query, column, value); + } + _ => {} + } + } + } + let or = r#where.or.unwrap_or_default(); + for or in or { + // dummy condition to start with and then add the generated conditions + // It's kinda hacky but i didn't find a better way to do it + let mut or_condition: Box< + dyn BoxableExpression<_, _, SqlType = diesel::sql_types::Bool>, + > = Box::new(diesel::dsl::sql::("1=1")); + for (key, value) in or { + if let Some(s_column) = $columns.get(key.as_str()) { + match s_column.0 { + ColumnType::Uuid => { + let column = + diesel::dsl::sql::(s_column.1); + or_condition = + $crate::gen_sql_and4uuid!(or_condition, column, value); + } + ColumnType::Text => { + let column = + diesel::dsl::sql::(s_column.1); + or_condition = + $crate::gen_sql_and4string!(or_condition, column, value); + } + ColumnType::Json => { + let column = + diesel::dsl::sql::(s_column.1); + or_condition = + $crate::gen_sql_and4json!(or_condition, column, value); + } + _ => {} + } + } + } + $query = $query.or_filter(or_condition); + } + $query + }}; +} + +#[macro_export] +macro_rules! gen_sql_order_by { + ($query:expr, $orders:expr, $columns:expr) => {{ + for order in $orders { + let words: Vec<_> = order.split_whitespace().collect(); + let column = words.first().unwrap_or(&""); + let order = words.get(1).unwrap_or(&""); + use std::str::FromStr; + let order = nanocl_stubs::generic::GenericOrder::from_str(order).unwrap(); + if let Some(s_column) = $columns.get(column) { + match s_column.0 { + ColumnType::Uuid => { + let column = + diesel::dsl::sql::(s_column.1); + match order { + nanocl_stubs::generic::GenericOrder::Asc => { + $query = $query.order(column.asc()); + } + nanocl_stubs::generic::GenericOrder::Desc => { + $query = $query.order(column.desc()); + } + } + } + ColumnType::Json => { + let column = + diesel::dsl::sql::(s_column.1); + match order { + nanocl_stubs::generic::GenericOrder::Asc => { + $query = $query.order(column.asc()); + } + nanocl_stubs::generic::GenericOrder::Desc => { + $query = $query.order(column.desc()); + } + } + } + ColumnType::Text => { + let column = + diesel::dsl::sql::(s_column.1); + match order { + nanocl_stubs::generic::GenericOrder::Asc => { + $query = $query.order(column.asc()); + } + nanocl_stubs::generic::GenericOrder::Desc => { + $query = $query.order(column.desc()); + } + } + } + ColumnType::Timestamptz => { + let column = + diesel::dsl::sql::(s_column.1); + match order { + nanocl_stubs::generic::GenericOrder::Asc => { + $query = $query.order(column.asc()); + } + nanocl_stubs::generic::GenericOrder::Desc => { + $query = $query.order(column.desc()); + } + } + } + } + } + } + $query + }}; +} diff --git a/bin/nanocld/src/objects/cargo.rs b/bin/nanocld/src/objects/cargo.rs index 0cc718061..d1a8ba011 100644 --- a/bin/nanocld/src/objects/cargo.rs +++ b/bin/nanocld/src/objects/cargo.rs @@ -30,9 +30,16 @@ impl ObjCreate for CargoDb { obj: &Self::ObjCreateIn, state: &SystemState, ) -> HttpResult { - // test if the name of the cargo include a . in the name and throw error if true - if obj.spec.name.contains('.') { - return Err(HttpError::bad_request("Cargo name cannot contain '.'")); + // validate the name of a cargo to include on a-z, A-Z, 0-9, and -_ + if !obj + .spec + .name + .chars() + .all(|c| c.is_alphanumeric() || c == '_' || c == '-') + { + return Err(HttpError::bad_request( + "Cargo name can only contain a-z, A-Z, 0-9, and -_", + )); } let key = utils::key::gen_key(&obj.namespace, &obj.spec.name); let new_spec = diff --git a/bin/nanocld/src/repositories/cargo.rs b/bin/nanocld/src/repositories/cargo.rs index 6e3f23c4d..9a1167a93 100644 --- a/bin/nanocld/src/repositories/cargo.rs +++ b/bin/nanocld/src/repositories/cargo.rs @@ -1,34 +1,58 @@ -use std::sync::Arc; +use std::collections::HashMap; use diesel::prelude::*; - -use futures_util::{stream::FuturesUnordered, StreamExt}; +use futures_util::{StreamExt, stream::FuturesUnordered}; use nanocl_error::{ http::HttpResult, io::{IoError, IoResult}, }; use nanocl_stubs::{ - generic::{GenericClause, GenericFilter, GenericFilterNsp}, cargo::{Cargo, CargoDeleteQuery, CargoSummary}, cargo_spec::{CargoSpec, CargoSpecPartial}, + generic::{GenericClause, GenericFilter, GenericFilterNsp}, system::ObjPsStatus, }; use crate::{ - gen_multiple, gen_where4json, gen_where4string, + gen_sql_order_by, gen_sql_multiple, gen_sql_query, utils, + schema::cargoes, + objects::generic::*, models::{ - CargoDb, CargoUpdateDb, NamespaceDb, ObjPsStatusDb, Pool, ProcessDb, - SpecDb, SystemState, + CargoDb, CargoUpdateDb, ColumnType, NamespaceDb, ObjPsStatusDb, Pool, + ProcessDb, SpecDb, SystemState, }, - objects::generic::*, - schema::cargoes, - utils, }; use super::generic::*; -impl RepositoryBase for CargoDb {} +impl RepositoryBase for CargoDb { + fn get_columns<'a>() -> HashMap<&'a str, (ColumnType, &'a str)> { + HashMap::from([ + ("key", (ColumnType::Text, "cargoes.key")), + ("name", (ColumnType::Text, "cargoes.name")), + ( + "namespace_name", + (ColumnType::Text, "cargoes.namespace_name"), + ), + ( + "created_at", + (ColumnType::Timestamptz, "cargoes.created_at"), + ), + ("updated_at", (ColumnType::Timestamptz, "specs.created_at")), + ("data", (ColumnType::Json, "specs.data")), + ("metadata", (ColumnType::Json, "specs.metadata")), + ( + "status.wanted", + (ColumnType::Text, "object_process_statuses.wanted"), + ), + ( + "status.actual", + (ColumnType::Text, "object_process_statuses.actual"), + ), + ]) + } +} impl RepositoryCreate for CargoDb {} @@ -56,42 +80,19 @@ impl RepositoryReadBy for CargoDb { where Self::Output: Sized, { - let r#where = filter.r#where.to_owned().unwrap_or_default(); let mut query = cargoes::table .inner_join(crate::schema::specs::table) .inner_join(crate::schema::object_process_statuses::table) .into_boxed(); - if let Some(value) = r#where.get("key") { - gen_where4string!(query, cargoes::key, value); - } - if let Some(value) = r#where.get("name") { - gen_where4string!(query, cargoes::name, value); - } - if let Some(value) = r#where.get("namespace_name") { - gen_where4string!(query, cargoes::namespace_name, value); - } - if let Some(value) = r#where.get("data") { - gen_where4json!(query, crate::schema::specs::data, value); - } - if let Some(value) = r#where.get("metadata") { - gen_where4json!(query, crate::schema::specs::metadata, value); - } - if let Some(value) = r#where.get("status.wanted") { - gen_where4string!( - query, - crate::schema::object_process_statuses::wanted, - value - ); - } - if let Some(value) = r#where.get("status.actual") { - gen_where4string!( - query, - crate::schema::object_process_statuses::actual, - value - ); + let columns = Self::get_columns(); + query = gen_sql_query!(query, filter, columns); + if let Some(orders) = &filter.order_by { + query = gen_sql_order_by!(query, orders, columns); + } else { + query = query.order(cargoes::created_at.desc()); } if is_multiple { - gen_multiple!(query, cargoes::created_at, filter); + gen_sql_multiple!(query, filter); } query } @@ -102,41 +103,12 @@ impl RepositoryCountBy for CargoDb { filter: &GenericFilter, ) -> impl diesel::query_dsl::methods::LoadQuery<'static, diesel::PgConnection, i64> { - let r#where = filter.r#where.to_owned().unwrap_or_default(); let mut query = cargoes::table .inner_join(crate::schema::specs::table) .inner_join(crate::schema::object_process_statuses::table) .into_boxed(); - if let Some(value) = r#where.get("key") { - gen_where4string!(query, cargoes::key, value); - } - if let Some(value) = r#where.get("name") { - gen_where4string!(query, cargoes::name, value); - } - if let Some(value) = r#where.get("namespace_name") { - gen_where4string!(query, cargoes::namespace_name, value); - } - if let Some(value) = r#where.get("data") { - gen_where4json!(query, crate::schema::specs::data, value); - } - if let Some(value) = r#where.get("metadata") { - gen_where4json!(query, crate::schema::specs::metadata, value); - } - if let Some(value) = r#where.get("status.wanted") { - gen_where4string!( - query, - crate::schema::object_process_statuses::wanted, - value - ); - } - if let Some(value) = r#where.get("status.actual") { - gen_where4string!( - query, - crate::schema::object_process_statuses::actual, - value - ); - } - query.count() + let columns = Self::get_columns(); + gen_sql_query!(query, filter, columns).count() } } @@ -204,7 +176,7 @@ impl CargoDb { /// Count cargoes by namespace. pub async fn count_by_namespace(nsp: &str, pool: &Pool) -> IoResult { let nsp = nsp.to_owned(); - let pool = Arc::clone(pool); + let pool = pool.clone(); let count = ntex::web::block(move || { let mut conn = utils::store::get_pool_conn(&pool)?; let count = cargoes::table diff --git a/bin/nanocld/src/repositories/event.rs b/bin/nanocld/src/repositories/event.rs index 95856061a..0dbad1eab 100644 --- a/bin/nanocld/src/repositories/event.rs +++ b/bin/nanocld/src/repositories/event.rs @@ -1,15 +1,41 @@ -use crate::{ - gen_multiple, gen_where4uuid, gen_where4string, models::EventDb, - schema::events, -}; +use std::collections::HashMap; use diesel::prelude::*; + use nanocl_error::io::IoResult; use nanocl_stubs::system::Event; +use crate::{ + gen_sql_order_by, gen_sql_multiple, gen_sql_query, + schema::events, + models::{ColumnType, EventDb}, +}; + use super::generic::*; -impl RepositoryBase for EventDb {} +impl RepositoryBase for EventDb { + fn get_columns<'a>() -> HashMap<&'a str, (ColumnType, &'a str)> { + HashMap::from([ + ("key", (ColumnType::Uuid, "events.key")), + ( + "reporting_node", + (ColumnType::Text, "events.reporting_node"), + ), + ( + "reporting_controller", + (ColumnType::Text, "events.reporting_controller"), + ), + ("kind", (ColumnType::Text, "events.kind")), + ("note", (ColumnType::Text, "events.note")), + ("action", (ColumnType::Text, "events.action")), + ("reason", (ColumnType::Text, "events.reason")), + ("actor", (ColumnType::Json, "events.actor")), + ("related", (ColumnType::Json, "events.related")), + ("metadata", (ColumnType::Json, "events.metadata")), + ("created_at", (ColumnType::Timestamptz, "events.created_at")), + ]) + } +} impl RepositoryCreate for EventDb {} @@ -31,25 +57,16 @@ impl RepositoryReadBy for EventDb { where Self::Output: Sized, { - let r#where = filter.r#where.to_owned().unwrap_or_default(); let mut query = events::table.into_boxed(); - if let Some(value) = r#where.get("key") { - gen_where4uuid!(query, events::key, value); - } - if let Some(value) = r#where.get("reporting_node") { - gen_where4string!(query, events::reporting_node, value); - } - if let Some(value) = r#where.get("kind") { - gen_where4string!(query, events::kind, value); - } - if let Some(value) = r#where.get("action") { - gen_where4string!(query, events::kind, value); - } - if let Some(value) = r#where.get("reason") { - gen_where4string!(query, events::kind, value); + let columns = Self::get_columns(); + query = gen_sql_query!(query, filter, columns); + if let Some(orders) = &filter.order_by { + query = gen_sql_order_by!(query, orders, columns); + } else { + query = query.order(events::created_at.desc()); } if is_multiple { - gen_multiple!(query, events::created_at, filter); + gen_sql_multiple!(query, filter); } query } @@ -60,24 +77,9 @@ impl RepositoryCountBy for EventDb { filter: &nanocl_stubs::generic::GenericFilter, ) -> impl diesel::query_dsl::methods::LoadQuery<'static, diesel::PgConnection, i64> { - let r#where = filter.r#where.to_owned().unwrap_or_default(); let mut query = events::table.into_boxed(); - if let Some(value) = r#where.get("key") { - gen_where4uuid!(query, events::key, value); - } - if let Some(value) = r#where.get("reporting_node") { - gen_where4string!(query, events::reporting_node, value); - } - if let Some(value) = r#where.get("kind") { - gen_where4string!(query, events::kind, value); - } - if let Some(value) = r#where.get("action") { - gen_where4string!(query, events::kind, value); - } - if let Some(value) = r#where.get("reason") { - gen_where4string!(query, events::kind, value); - } - query.count() + let columns = Self::get_columns(); + gen_sql_query!(query, filter, columns).count() } } diff --git a/bin/nanocld/src/repositories/generic/create.rs b/bin/nanocld/src/repositories/generic/create.rs index f65eb24c6..bbcef4077 100644 --- a/bin/nanocld/src/repositories/generic/create.rs +++ b/bin/nanocld/src/repositories/generic/create.rs @@ -1,5 +1,3 @@ -use std::sync::Arc; - use diesel::{prelude::*, associations::HasTable}; use nanocl_error::io::{IoError, IoResult}; @@ -21,7 +19,7 @@ pub trait RepositoryCreate: super::RepositoryBase { >::Values, >: diesel::query_dsl::LoadQuery<'static, diesel::pg::PgConnection, Self>, { - let pool = Arc::clone(pool); + let pool = pool.clone(); let item = Self::from(item); ntex::rt::spawn_blocking(move || { let mut conn = utils::store::get_pool_conn(&pool)?; diff --git a/bin/nanocld/src/repositories/generic/delete.rs b/bin/nanocld/src/repositories/generic/delete.rs index ef0d32adc..f5f78fe69 100644 --- a/bin/nanocld/src/repositories/generic/delete.rs +++ b/bin/nanocld/src/repositories/generic/delete.rs @@ -1,5 +1,3 @@ -use std::sync::Arc; - use diesel::{prelude::*, associations::HasTable, query_dsl, query_builder}; use nanocl_error::io::IoResult; @@ -25,7 +23,7 @@ pub trait RepositoryDelByPk: super::RepositoryBase { >: query_builder::QueryFragment + query_builder::QueryId, { log::trace!("{}::delete_by_pk: {pk}", Self::get_name()); - let pool = Arc::clone(pool); + let pool = pool.clone(); let pk = pk.to_owned(); ntex::rt::spawn_blocking(move || { let mut conn = utils::store::get_pool_conn(&pool)?; @@ -59,7 +57,7 @@ pub trait RepositoryDelBy: super::RepositoryBase { <::Table as diesel::QuerySource>::FromClause: diesel::query_builder::QueryFragment, { log::trace!("{}::delete_by: {filter:?}", Self::get_name()); - let pool = Arc::clone(pool); + let pool = pool.clone(); let filter = filter.clone(); ntex::rt::spawn_blocking(move || { let query = Self::gen_del_query(&filter); diff --git a/bin/nanocld/src/repositories/generic/mod.rs b/bin/nanocld/src/repositories/generic/mod.rs index 73617511f..0f2a3f120 100644 --- a/bin/nanocld/src/repositories/generic/mod.rs +++ b/bin/nanocld/src/repositories/generic/mod.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use nanocl_error::io::{IoError, FromIo}; mod read; @@ -12,13 +14,13 @@ pub use update::*; pub use delete::*; pub use with_spec::*; +use crate::models::ColumnType; + pub trait RepositoryBase { - // fn map_err /// Get the name of the current type fn get_name() -> &'static str { let name = std::any::type_name::(); - let short = name.split("::").last().unwrap_or(name); - short + name.split("::").last().unwrap_or(name) } /// Map an error with the context of the current type name @@ -28,4 +30,10 @@ pub trait RepositoryBase { { err.map_err_context(Self::get_name) } + + /// Get the columns of the current type as a hashmap + /// This help to generate the sql queries based on the generic filter + fn get_columns<'a>() -> HashMap<&'a str, (ColumnType, &'a str)> { + HashMap::new() + } } diff --git a/bin/nanocld/src/repositories/generic/read.rs b/bin/nanocld/src/repositories/generic/read.rs index 24bb137d1..c682d3e73 100644 --- a/bin/nanocld/src/repositories/generic/read.rs +++ b/bin/nanocld/src/repositories/generic/read.rs @@ -1,5 +1,3 @@ -use std::sync::Arc; - use diesel::{prelude::*, query_dsl::methods::LoadQuery}; use nanocl_error::io::IoResult; @@ -29,7 +27,7 @@ pub trait RepositoryReadBy: super::RepositoryBase { where Self::Output: Sized + Send + 'static, { - let pool = Arc::clone(pool); + let pool = pool.clone(); let filter = filter.clone(); log::trace!("{}::read_one_by {filter:#?}", Self::get_name()); ntex::rt::spawn_blocking(move || { @@ -62,7 +60,7 @@ pub trait RepositoryReadBy: super::RepositoryBase { where Self::Output: Sized + Send + 'static, { - let pool = Arc::clone(pool); + let pool = pool.clone(); let filter = filter.clone(); log::trace!("{}::read_by {filter:#?}", Self::get_name()); ntex::rt::spawn_blocking(move || { @@ -131,7 +129,7 @@ pub trait RepositoryCountBy: RepositoryBase { ) -> impl LoadQuery<'static, diesel::PgConnection, i64>; async fn count_by(filter: &GenericFilter, pool: &Pool) -> IoResult { - let pool = Arc::clone(pool); + let pool = pool.clone(); let filter = filter.clone(); log::trace!("{}::count_by {filter:#?}", Self::get_name()); ntex::rt::spawn_blocking(move || { diff --git a/bin/nanocld/src/repositories/generic/update.rs b/bin/nanocld/src/repositories/generic/update.rs index f87ee17fb..b0b989173 100644 --- a/bin/nanocld/src/repositories/generic/update.rs +++ b/bin/nanocld/src/repositories/generic/update.rs @@ -1,5 +1,3 @@ -use std::sync::Arc; - use diesel::{prelude::*, associations}; use nanocl_error::io::IoResult; @@ -33,7 +31,7 @@ pub trait RepositoryUpdate: super::RepositoryBase { diesel::query_builder::AsQuery + diesel::query_dsl::LoadQuery<'static, diesel::pg::PgConnection, Self>, { log::trace!("{}::update_by_pk: {pk}", Self::get_name()); - let pool = Arc::clone(pool); + let pool = pool.clone(); let pk = pk.to_owned(); let values = values.into(); ntex::rt::spawn_blocking(move || { diff --git a/bin/nanocld/src/repositories/job.rs b/bin/nanocld/src/repositories/job.rs index 00ef0c335..daf011cd7 100644 --- a/bin/nanocld/src/repositories/job.rs +++ b/bin/nanocld/src/repositories/job.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use diesel::prelude::*; use futures_util::StreamExt; @@ -13,15 +15,35 @@ use nanocl_stubs::{ }; use crate::{ - utils, + gen_sql_multiple, gen_sql_order_by, gen_sql_query, utils, schema::jobs, - gen_multiple, gen_where4json, gen_where4string, - models::{JobDb, JobUpdateDb, ObjPsStatusDb, Pool, ProcessDb, SystemState}, + models::{ + ColumnType, JobDb, JobUpdateDb, ObjPsStatusDb, Pool, ProcessDb, SystemState, + }, }; use super::generic::*; -impl RepositoryBase for JobDb {} +impl RepositoryBase for JobDb { + fn get_columns<'a>( + ) -> std::collections::HashMap<&'a str, (ColumnType, &'a str)> { + HashMap::from([ + ("key", (ColumnType::Text, "jobs.key")), + ("data", (ColumnType::Json, "jobs.data")), + ("metadata", (ColumnType::Json, "jobs.metadata")), + ("created_at", (ColumnType::Timestamptz, "jobs.created_at")), + ("updated_at", (ColumnType::Timestamptz, "jobs.updated_at")), + ( + "status.wanted", + (ColumnType::Text, "object_process_statuses.wanted"), + ), + ( + "status.actual", + (ColumnType::Text, "object_process_statuses.actual"), + ), + ]) + } +} impl RepositoryCreate for JobDb {} @@ -46,35 +68,18 @@ impl RepositoryReadBy for JobDb { diesel::PgConnection, Self::Output, > { - let r#where = filter.r#where.clone().unwrap_or_default(); let mut query = jobs::table .inner_join(crate::schema::object_process_statuses::table) .into_boxed(); - if let Some(key) = r#where.get("key") { - gen_where4string!(query, jobs::key, key); - } - if let Some(data) = r#where.get("data") { - gen_where4json!(query, jobs::data, data); - } - if let Some(metadata) = r#where.get("metadata") { - gen_where4json!(query, jobs::metadata, metadata); - } - if let Some(value) = r#where.get("status.wanted") { - gen_where4string!( - query, - crate::schema::object_process_statuses::wanted, - value - ); - } - if let Some(value) = r#where.get("status.actual") { - gen_where4string!( - query, - crate::schema::object_process_statuses::actual, - value - ); + let columns = Self::get_columns(); + query = gen_sql_query!(query, filter, columns); + if let Some(orders) = &filter.order_by { + query = gen_sql_order_by!(query, orders, columns); + } else { + query = query.order(jobs::created_at.desc()); } if is_multiple { - gen_multiple!(query, jobs::created_at, filter); + gen_sql_multiple!(query, filter); } query } @@ -85,34 +90,11 @@ impl RepositoryCountBy for JobDb { filter: &GenericFilter, ) -> impl diesel::query_dsl::methods::LoadQuery<'static, diesel::PgConnection, i64> { - let r#where = filter.r#where.clone().unwrap_or_default(); let mut query = jobs::table .inner_join(crate::schema::object_process_statuses::table) .into_boxed(); - if let Some(key) = r#where.get("key") { - gen_where4string!(query, jobs::key, key); - } - if let Some(data) = r#where.get("data") { - gen_where4json!(query, jobs::data, data); - } - if let Some(metadata) = r#where.get("metadata") { - gen_where4json!(query, jobs::metadata, metadata); - } - if let Some(value) = r#where.get("status.wanted") { - gen_where4string!( - query, - crate::schema::object_process_statuses::wanted, - value - ); - } - if let Some(value) = r#where.get("status.actual") { - gen_where4string!( - query, - crate::schema::object_process_statuses::actual, - value - ); - } - query.count() + let columns = Self::get_columns(); + gen_sql_query!(query, filter, columns).count() } } diff --git a/bin/nanocld/src/repositories/metric.rs b/bin/nanocld/src/repositories/metric.rs index 9f140752e..b9838e9b6 100644 --- a/bin/nanocld/src/repositories/metric.rs +++ b/bin/nanocld/src/repositories/metric.rs @@ -2,13 +2,28 @@ use diesel::prelude::*; use nanocl_stubs::generic::GenericFilter; use crate::{ - gen_multiple, gen_where4json, gen_where4uuid, gen_where4string, - models::MetricDb, schema::metrics, + gen_sql_multiple, gen_sql_order_by, gen_sql_query, + models::{ColumnType, MetricDb}, + schema::metrics, }; use super::generic::*; -impl RepositoryBase for MetricDb {} +impl RepositoryBase for MetricDb { + fn get_columns<'a>( + ) -> std::collections::HashMap<&'a str, (ColumnType, &'a str)> { + std::collections::HashMap::from([ + ("key", (ColumnType::Uuid, "metrics.key")), + ("node_name", (ColumnType::Text, "metrics.node_name")), + ("kind", (ColumnType::Text, "metrics.kind")), + ("data", (ColumnType::Json, "metrics.data")), + ( + "created_at", + (ColumnType::Timestamptz, "metrics.created_at"), + ), + ]) + } +} impl RepositoryCreate for MetricDb {} @@ -27,22 +42,16 @@ impl RepositoryReadBy for MetricDb { diesel::pg::PgConnection, Self::Output, > { - let r#where = filter.r#where.clone().unwrap_or_default(); let mut query = metrics::table.into_boxed(); - if let Some(key) = r#where.get("key") { - gen_where4uuid!(query, metrics::key, key); - } - if let Some(node_name) = r#where.get("node_name") { - gen_where4string!(query, metrics::node_name, node_name); - } - if let Some(kind) = r#where.get("kind") { - gen_where4string!(query, metrics::kind, kind); - } - if let Some(data) = r#where.get("data") { - gen_where4json!(query, metrics::data, data); + let columns = Self::get_columns(); + query = gen_sql_query!(query, filter, columns); + if let Some(orders) = &filter.order_by { + query = gen_sql_order_by!(query, orders, columns); + } else { + query = query.order(metrics::created_at.desc()); } if is_multiple { - gen_multiple!(query, metrics::dsl::created_at, filter); + gen_sql_multiple!(query, filter); } query } @@ -52,20 +61,8 @@ impl RepositoryCountBy for MetricDb { fn gen_count_query( filter: &GenericFilter, ) -> impl diesel::query_dsl::LoadQuery<'static, diesel::PgConnection, i64> { - let r#where = filter.r#where.clone().unwrap_or_default(); let mut query = metrics::table.into_boxed(); - if let Some(key) = r#where.get("key") { - gen_where4uuid!(query, metrics::key, key); - } - if let Some(node_name) = r#where.get("node_name") { - gen_where4string!(query, metrics::node_name, node_name); - } - if let Some(kind) = r#where.get("kind") { - gen_where4string!(query, metrics::kind, kind); - } - if let Some(data) = r#where.get("data") { - gen_where4json!(query, metrics::data, data); - } - query.count() + let columns = Self::get_columns(); + gen_sql_query!(query, filter, columns).count() } } diff --git a/bin/nanocld/src/repositories/namespace.rs b/bin/nanocld/src/repositories/namespace.rs index 3147e0ec3..b4f57fc4a 100644 --- a/bin/nanocld/src/repositories/namespace.rs +++ b/bin/nanocld/src/repositories/namespace.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use diesel::prelude::*; use bollard_next::network::InspectNetworkOptions; @@ -6,14 +8,24 @@ use nanocl_error::http::{HttpError, HttpResult}; use nanocl_stubs::{generic::GenericFilter, namespace::NamespaceSummary}; use crate::{ + gen_sql_multiple, gen_sql_order_by, gen_sql_query, schema::namespaces, - gen_multiple, gen_where4string, - models::{CargoDb, NamespaceDb, ProcessDb, SystemState}, + models::{CargoDb, ColumnType, NamespaceDb, ProcessDb, SystemState}, }; use super::generic::*; -impl RepositoryBase for NamespaceDb {} +impl RepositoryBase for NamespaceDb { + fn get_columns<'a>() -> HashMap<&'a str, (ColumnType, &'a str)> { + HashMap::from([ + ("name", (ColumnType::Text, "namespaces.name")), + ( + "created_at", + (ColumnType::Timestamptz, "namespaces.created_at"), + ), + ]) + } +} impl RepositoryCreate for NamespaceDb {} @@ -34,13 +46,16 @@ impl RepositoryReadBy for NamespaceDb { diesel::pg::PgConnection, Self::Output, > { - let r#where = filter.r#where.clone().unwrap_or_default(); let mut query = namespaces::table.into_boxed(); - if let Some(name) = r#where.get("name") { - gen_where4string!(query, namespaces::name, name); + let columns = Self::get_columns(); + query = gen_sql_query!(query, filter, columns); + if let Some(orders) = &filter.order_by { + query = gen_sql_order_by!(query, orders, columns); + } else { + query = query.order(namespaces::created_at.desc()); } if is_multiple { - gen_multiple!(query, namespaces::created_at, filter); + gen_sql_multiple!(query, filter); } query } @@ -50,12 +65,9 @@ impl RepositoryCountBy for NamespaceDb { fn gen_count_query( filter: &GenericFilter, ) -> impl diesel::query_dsl::LoadQuery<'static, diesel::PgConnection, i64> { - let r#where = filter.r#where.clone().unwrap_or_default(); let mut query = namespaces::table.into_boxed(); - if let Some(name) = r#where.get("name") { - gen_where4string!(query, namespaces::name, name); - } - query.count() + let columns = Self::get_columns(); + gen_sql_query!(query, filter, columns).count() } } diff --git a/bin/nanocld/src/repositories/node.rs b/bin/nanocld/src/repositories/node.rs index 536140581..9b1b10d3b 100644 --- a/bin/nanocld/src/repositories/node.rs +++ b/bin/nanocld/src/repositories/node.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use diesel::prelude::*; use nanocl_error::io::IoResult; @@ -5,14 +7,22 @@ use nanocl_error::io::IoResult; use nanocl_stubs::generic::GenericFilter; use crate::{ - gen_multiple, gen_where4string, - models::{NodeDb, Pool, SystemState}, + gen_sql_multiple, gen_sql_order_by, gen_sql_query, + models::{ColumnType, NodeDb, Pool, SystemState}, schema::nodes, }; use super::generic::*; -impl RepositoryBase for NodeDb {} +impl RepositoryBase for NodeDb { + fn get_columns<'a>() -> HashMap<&'a str, (ColumnType, &'a str)> { + HashMap::from([ + ("name", (ColumnType::Text, "nodes.name")), + ("ip_address", (ColumnType::Text, "nodes.ip_address")), + ("created_at", (ColumnType::Timestamptz, "nodes.created_at")), + ]) + } +} impl RepositoryCreate for NodeDb {} @@ -33,13 +43,16 @@ impl RepositoryReadBy for NodeDb { diesel::pg::PgConnection, Self::Output, > { - let r#where = filter.r#where.clone().unwrap_or_default(); let mut query = nodes::table.into_boxed(); - if let Some(name) = r#where.get("name") { - gen_where4string!(query, nodes::name, name); + let columns = Self::get_columns(); + query = gen_sql_query!(query, filter, columns); + if let Some(orders) = &filter.order_by { + query = gen_sql_order_by!(query, orders, columns); + } else { + query = query.order(nodes::created_at.desc()); } if is_multiple { - gen_multiple!(query, nodes::created_at, filter); + gen_sql_multiple!(query, filter); } query } @@ -49,12 +62,9 @@ impl RepositoryCountBy for NodeDb { fn gen_count_query( filter: &GenericFilter, ) -> impl diesel::query_dsl::LoadQuery<'static, diesel::PgConnection, i64> { - let r#where = filter.r#where.clone().unwrap_or_default(); let mut query = nodes::table.into_boxed(); - if let Some(name) = r#where.get("name") { - gen_where4string!(query, nodes::name, name); - } - query.count() + let columns = Self::get_columns(); + gen_sql_query!(query, filter, columns).count() } } diff --git a/bin/nanocld/src/repositories/object_process_status.rs b/bin/nanocld/src/repositories/object_process_status.rs index 94a223f62..ce439f964 100644 --- a/bin/nanocld/src/repositories/object_process_status.rs +++ b/bin/nanocld/src/repositories/object_process_status.rs @@ -1,17 +1,48 @@ +use std::collections::HashMap; + use diesel::prelude::*; use nanocl_error::io::IoResult; use nanocl_stubs::{generic::GenericFilter, system::ObjPsStatusKind}; use crate::{ - gen_multiple, gen_where4string, - models::{ObjPsStatusDb, ObjPsStatusUpdate, Pool}, + gen_sql_multiple, gen_sql_order_by, gen_sql_query, + models::{ColumnType, ObjPsStatusDb, ObjPsStatusUpdate, Pool}, schema::object_process_statuses, }; use super::generic::*; -impl RepositoryBase for ObjPsStatusDb {} +impl RepositoryBase for ObjPsStatusDb { + fn get_columns<'a>() -> HashMap<&'a str, (ColumnType, &'a str)> { + HashMap::from([ + ("key", (ColumnType::Text, "object_process_statuses.key")), + ( + "wanted", + (ColumnType::Text, "object_process_statuses.wanted"), + ), + ( + "prev_wanted", + (ColumnType::Text, "object_process_statuses.prev_wanted"), + ), + ( + "actual", + (ColumnType::Text, "object_process_statuses.actual"), + ), + ( + "prev_actual", + (ColumnType::Text, "object_process_statuses.prev_actual"), + ), + ( + "created_at", + ( + ColumnType::Timestamptz, + "object_process_statuses.created_at", + ), + ), + ]) + } +} impl RepositoryCreate for ObjPsStatusDb {} @@ -36,25 +67,16 @@ impl RepositoryReadBy for ObjPsStatusDb { diesel::pg::PgConnection, Self::Output, > { - let r#where = filter.r#where.clone().unwrap_or_default(); let mut query = object_process_statuses::table.into_boxed(); - if let Some(value) = r#where.get("key") { - gen_where4string!(query, object_process_statuses::key, value); - } - if let Some(value) = r#where.get("wanted") { - gen_where4string!(query, object_process_statuses::wanted, value); - } - if let Some(value) = r#where.get("prev_wanted") { - gen_where4string!(query, object_process_statuses::prev_wanted, value); - } - if let Some(value) = r#where.get("actual") { - gen_where4string!(query, object_process_statuses::actual, value); - } - if let Some(value) = r#where.get("prev_actual") { - gen_where4string!(query, object_process_statuses::prev_actual, value); + let columns = Self::get_columns(); + query = gen_sql_query!(query, filter, columns); + if let Some(orders) = &filter.order_by { + query = gen_sql_order_by!(query, orders, columns); + } else { + query = query.order(object_process_statuses::created_at.desc()); } if is_multiple { - gen_multiple!(query, object_process_statuses::created_at, filter); + gen_sql_multiple!(query, filter); } query } diff --git a/bin/nanocld/src/repositories/process.rs b/bin/nanocld/src/repositories/process.rs index c298a2c21..fb6f53b59 100644 --- a/bin/nanocld/src/repositories/process.rs +++ b/bin/nanocld/src/repositories/process.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use diesel::prelude::*; use nanocl_error::io::IoResult; @@ -8,14 +10,29 @@ use nanocl_stubs::{ }; use crate::{ - gen_multiple, gen_where4json, gen_where4string, + gen_sql_multiple, gen_sql_order_by, gen_sql_query, + models::{ColumnType, Pool, ProcessDb, ProcessUpdateDb}, schema::processes, - models::{Pool, ProcessDb, ProcessUpdateDb}, }; use super::generic::*; -impl RepositoryBase for ProcessDb {} +impl RepositoryBase for ProcessDb { + fn get_columns<'a>() -> HashMap<&'a str, (ColumnType, &'a str)> { + HashMap::from([ + ("key", (ColumnType::Text, "processes.key")), + ("name", (ColumnType::Text, "processes.name")), + ("kind", (ColumnType::Text, "processes.kind")), + ("node_key", (ColumnType::Text, "processes.node_key")), + ("kind_key", (ColumnType::Text, "processes.kind_key")), + ("data", (ColumnType::Json, "processes.data")), + ( + "created_at", + (ColumnType::Timestamptz, "processes.created_at"), + ), + ]) + } +} impl RepositoryCreate for ProcessDb {} @@ -37,27 +54,9 @@ impl RepositoryDelBy for ProcessDb { where Self: diesel::associations::HasTable, { - let r#where = filter.r#where.to_owned().unwrap_or_default(); let mut query = diesel::delete(processes::table).into_boxed(); - if let Some(value) = r#where.get("key") { - gen_where4string!(query, processes::key, value); - } - if let Some(value) = r#where.get("name") { - gen_where4string!(query, processes::name, value); - } - if let Some(value) = r#where.get("kind") { - gen_where4string!(query, processes::kind, value); - } - if let Some(value) = r#where.get("node_key") { - gen_where4string!(query, processes::node_key, value); - } - if let Some(value) = r#where.get("kind_key") { - gen_where4string!(query, processes::kind_key, value); - } - if let Some(value) = r#where.get("data") { - gen_where4json!(query, processes::data, value); - } - query + let columns = Self::get_columns(); + gen_sql_query!(query, filter, columns) } } @@ -76,28 +75,16 @@ impl RepositoryReadBy for ProcessDb { diesel::pg::PgConnection, Self::Output, > { - let r#where = filter.r#where.to_owned().unwrap_or_default(); let mut query = processes::table.into_boxed(); - if let Some(value) = r#where.get("key") { - gen_where4string!(query, processes::key, value); - } - if let Some(value) = r#where.get("name") { - gen_where4string!(query, processes::name, value); - } - if let Some(value) = r#where.get("kind") { - gen_where4string!(query, processes::kind, value); - } - if let Some(value) = r#where.get("node_key") { - gen_where4string!(query, processes::node_key, value); - } - if let Some(value) = r#where.get("kind_key") { - gen_where4string!(query, processes::kind_key, value); - } - if let Some(value) = r#where.get("data") { - gen_where4json!(query, processes::data, value); + let columns = Self::get_columns(); + query = gen_sql_query!(query, filter, columns); + if let Some(orders) = &filter.order_by { + query = gen_sql_order_by!(query, orders, columns); + } else { + query = query.order(processes::created_at.desc()); } if is_multiple { - gen_multiple!(query, processes::created_at, filter); + gen_sql_multiple!(query, filter); } query } @@ -108,27 +95,9 @@ impl RepositoryCountBy for ProcessDb { filter: &GenericFilter, ) -> impl diesel::query_dsl::methods::LoadQuery<'static, diesel::PgConnection, i64> { - let r#where = filter.r#where.to_owned().unwrap_or_default(); let mut query = processes::table.into_boxed(); - if let Some(value) = r#where.get("key") { - gen_where4string!(query, processes::key, value); - } - if let Some(value) = r#where.get("name") { - gen_where4string!(query, processes::name, value); - } - if let Some(value) = r#where.get("kind") { - gen_where4string!(query, processes::kind, value); - } - if let Some(value) = r#where.get("node_key") { - gen_where4string!(query, processes::node_key, value); - } - if let Some(value) = r#where.get("kind_key") { - gen_where4string!(query, processes::kind_key, value); - } - if let Some(value) = r#where.get("data") { - gen_where4json!(query, processes::data, value); - } - query.count() + let columns = Self::get_columns(); + gen_sql_query!(query, filter, columns).count() } } diff --git a/bin/nanocld/src/repositories/resource.rs b/bin/nanocld/src/repositories/resource.rs index 73b8cc90d..0cc8b1e40 100644 --- a/bin/nanocld/src/repositories/resource.rs +++ b/bin/nanocld/src/repositories/resource.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use diesel::prelude::*; use jsonschema::{Draft, JSONSchema}; @@ -13,15 +15,30 @@ use nanocl_stubs::{ }; use crate::{ - utils, - schema::{specs, resources}, - gen_multiple, gen_where4json, gen_where4string, - models::{Pool, ResourceDb, ResourceKindDb, ResourceUpdateDb, SpecDb}, + gen_sql_multiple, gen_sql_order_by, gen_sql_query, utils, + schema::resources, + models::{ + ColumnType, Pool, ResourceDb, ResourceKindDb, ResourceUpdateDb, SpecDb, + }, }; use super::generic::*; -impl RepositoryBase for ResourceDb {} +impl RepositoryBase for ResourceDb { + fn get_columns<'a>() -> HashMap<&'a str, (ColumnType, &'a str)> { + HashMap::from([ + ("key", (ColumnType::Text, "resources.key")), + ( + "created_at", + (ColumnType::Timestamptz, "resources.created_at"), + ), + ("kind", (ColumnType::Text, "resources.kind")), + ("spec_key", (ColumnType::Text, "resources.spec_key")), + ("data", (ColumnType::Json, "specs.data")), + ("metadata", (ColumnType::Json, "specs.metadata")), + ]) + } +} impl RepositoryCreate for ResourceDb {} @@ -46,24 +63,18 @@ impl RepositoryReadBy for ResourceDb { diesel::PgConnection, Self::Output, > { - let r#where = filter.r#where.to_owned().unwrap_or_default(); let mut query = resources::table .inner_join(crate::schema::specs::table) .into_boxed(); - if let Some(value) = r#where.get("key") { - gen_where4string!(query, resources::key, value); - } - if let Some(value) = r#where.get("kind") { - gen_where4string!(query, resources::kind, value); - } - if let Some(value) = r#where.get("data") { - gen_where4json!(query, specs::data, value); - } - if let Some(value) = r#where.get("metadata") { - gen_where4json!(query, specs::metadata, value); + let columns = Self::get_columns(); + query = gen_sql_query!(query, filter, columns); + if let Some(orders) = &filter.order_by { + query = gen_sql_order_by!(query, orders, columns); + } else { + query = query.order(resources::created_at.desc()); } if is_multiple { - gen_multiple!(query, resources::created_at, filter); + gen_sql_multiple!(query, filter); } query } @@ -74,23 +85,11 @@ impl RepositoryCountBy for ResourceDb { filter: &GenericFilter, ) -> impl diesel::query_dsl::methods::LoadQuery<'static, diesel::PgConnection, i64> { - let r#where = filter.r#where.to_owned().unwrap_or_default(); let mut query = resources::table .inner_join(crate::schema::specs::table) .into_boxed(); - if let Some(value) = r#where.get("key") { - gen_where4string!(query, resources::key, value); - } - if let Some(value) = r#where.get("kind") { - gen_where4string!(query, resources::kind, value); - } - if let Some(value) = r#where.get("data") { - gen_where4json!(query, specs::data, value); - } - if let Some(value) = r#where.get("metadata") { - gen_where4json!(query, specs::metadata, value); - } - query.count() + let columns = Self::get_columns(); + gen_sql_query!(query, filter, columns).count() } } diff --git a/bin/nanocld/src/repositories/resource_kind.rs b/bin/nanocld/src/repositories/resource_kind.rs index c22f0d33f..ac27e671b 100644 --- a/bin/nanocld/src/repositories/resource_kind.rs +++ b/bin/nanocld/src/repositories/resource_kind.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use diesel::prelude::*; use nanocl_error::{ @@ -11,14 +13,25 @@ use nanocl_stubs::{ }; use crate::{ - gen_multiple, gen_where4string, + gen_sql_multiple, gen_sql_order_by, gen_sql_query, schema::resource_kinds, - models::{Pool, ResourceKindDb, ResourceKindDbUpdate, SpecDb}, + models::{ColumnType, Pool, ResourceKindDb, ResourceKindDbUpdate, SpecDb}, }; use super::generic::*; -impl RepositoryBase for ResourceKindDb {} +impl RepositoryBase for ResourceKindDb { + fn get_columns<'a>() -> HashMap<&'a str, (ColumnType, &'a str)> { + HashMap::from([ + ("name", (ColumnType::Text, "resource_kinds.name")), + ( + "created_at", + (ColumnType::Timestamptz, "resource_kinds.created_at"), + ), + ("spec_key", (ColumnType::Text, "resource_kinds.spec_key")), + ]) + } +} impl RepositoryCreate for ResourceKindDb {} @@ -43,15 +56,18 @@ impl RepositoryReadBy for ResourceKindDb { diesel::PgConnection, Self::Output, > { - let r#where = filter.r#where.to_owned().unwrap_or_default(); let mut query = resource_kinds::table .inner_join(crate::schema::specs::table) .into_boxed(); - if let Some(value) = r#where.get("name") { - gen_where4string!(query, resource_kinds::name, value); + let columns = Self::get_columns(); + query = gen_sql_query!(query, filter, columns); + if let Some(orders) = &filter.order_by { + query = gen_sql_order_by!(query, orders, columns); + } else { + query = query.order(resource_kinds::created_at.desc()); } if is_multiple { - gen_multiple!(query, resource_kinds::created_at, filter); + gen_sql_multiple!(query, filter); } query } @@ -61,12 +77,9 @@ impl RepositoryCountBy for ResourceKindDb { fn gen_count_query( filter: &GenericFilter, ) -> impl diesel::query_dsl::LoadQuery<'static, diesel::PgConnection, i64> { - let r#where = filter.r#where.to_owned().unwrap_or_default(); let mut query = resource_kinds::table.into_boxed(); - if let Some(value) = r#where.get("name") { - gen_where4string!(query, resource_kinds::name, value); - } - query.count() + let columns = Self::get_columns(); + gen_sql_query!(query, filter, columns).count() } } @@ -84,7 +97,7 @@ impl ResourceKindDb { pool: &Pool, ) -> HttpResult { let item = ResourceKindDb::transform_read_by_pk(pk, pool).await?; - let filter = GenericFilter::new() + let filter: GenericFilter = GenericFilter::new() .r#where("kind_key", GenericClause::Eq(item.name.to_owned())); let versions = SpecDb::read_by(&filter, pool) .await? diff --git a/bin/nanocld/src/repositories/secret.rs b/bin/nanocld/src/repositories/secret.rs index 00fd63091..37b7b1e41 100644 --- a/bin/nanocld/src/repositories/secret.rs +++ b/bin/nanocld/src/repositories/secret.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use diesel::prelude::*; use nanocl_stubs::generic::GenericFilter; @@ -5,14 +7,31 @@ use nanocl_stubs::generic::GenericFilter; use nanocl_stubs::secret::Secret; use crate::{ - gen_multiple, gen_where4string, - models::{SecretDb, SecretUpdateDb}, + gen_sql_multiple, gen_sql_order_by, gen_sql_query, + models::{ColumnType, SecretDb, SecretUpdateDb}, schema::secrets, }; use super::generic::*; -impl RepositoryBase for SecretDb {} +impl RepositoryBase for SecretDb { + fn get_columns<'a>() -> HashMap<&'a str, (ColumnType, &'a str)> { + HashMap::from([ + ("key", (ColumnType::Text, "secrets.key")), + ("kind", (ColumnType::Text, "secrets.kind")), + ( + "created_at", + (ColumnType::Timestamptz, "secrets.created_at"), + ), + ( + "updated_at", + (ColumnType::Timestamptz, "secrets.updated_at"), + ), + ("data", (ColumnType::Json, "secrets.data")), + ("metadata", (ColumnType::Json, "secrets.metadata")), + ]) + } +} impl RepositoryCreate for SecretDb {} @@ -37,16 +56,16 @@ impl RepositoryReadBy for SecretDb { diesel::pg::PgConnection, Self::Output, > { - let r#where = filter.r#where.clone().unwrap_or_default(); let mut query = secrets::table.into_boxed(); - if let Some(key) = r#where.get("key") { - gen_where4string!(query, secrets::key, key); - } - if let Some(kind) = r#where.get("kind") { - gen_where4string!(query, secrets::kind, kind); + let columns = Self::get_columns(); + query = gen_sql_query!(query, filter, columns); + if let Some(orders) = &filter.order_by { + query = gen_sql_order_by!(query, orders, columns); + } else { + query = query.order(secrets::created_at.desc()); } if is_multiple { - gen_multiple!(query, secrets::created_at, filter); + gen_sql_multiple!(query, filter); } query } @@ -57,15 +76,9 @@ impl RepositoryCountBy for SecretDb { filter: &GenericFilter, ) -> impl diesel::query_dsl::methods::LoadQuery<'static, diesel::PgConnection, i64> { - let r#where = filter.r#where.clone().unwrap_or_default(); let mut query = secrets::table.into_boxed(); - if let Some(key) = r#where.get("key") { - gen_where4string!(query, secrets::key, key); - } - if let Some(kind) = r#where.get("kind") { - gen_where4string!(query, secrets::kind, kind); - } - query.count() + let columns = Self::get_columns(); + gen_sql_query!(query, filter, columns).count() } } diff --git a/bin/nanocld/src/repositories/spec.rs b/bin/nanocld/src/repositories/spec.rs index 0e2ef7637..8d7240c32 100644 --- a/bin/nanocld/src/repositories/spec.rs +++ b/bin/nanocld/src/repositories/spec.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use diesel::prelude::*; use nanocl_error::io::IoResult; @@ -9,14 +11,26 @@ use nanocl_stubs::{ }; use crate::{ - gen_multiple, gen_where4uuid, gen_where4string, - models::{Pool, SpecDb}, schema::specs, + gen_sql_multiple, gen_sql_order_by, gen_sql_query, + models::{ColumnType, Pool, SpecDb}, }; use super::generic::*; -impl RepositoryBase for SpecDb {} +impl RepositoryBase for SpecDb { + fn get_columns<'a>() -> HashMap<&'a str, (ColumnType, &'a str)> { + HashMap::from([ + ("key", (ColumnType::Uuid, "specs.key")), + ("created_at", (ColumnType::Timestamptz, "specs.created_at")), + ("kind_name", (ColumnType::Text, "specs.kind_name")), + ("kind_key", (ColumnType::Text, "specs.kind_key")), + ("version", (ColumnType::Text, "specs.version")), + ("data", (ColumnType::Json, "specs.data")), + ("metadata", (ColumnType::Json, "specs.metadata")), + ]) + } +} impl RepositoryCreate for SpecDb {} @@ -31,15 +45,9 @@ impl RepositoryDelBy for SpecDb { where Self: diesel::associations::HasTable, { - let r#where = filter.r#where.to_owned().unwrap_or_default(); let mut query = diesel::delete(specs::table).into_boxed(); - if let Some(value) = r#where.get("kind_key") { - gen_where4string!(query, specs::kind_key, value); - } - if let Some(value) = r#where.get("version") { - gen_where4string!(query, specs::version, value); - } - query + let columns = Self::get_columns(); + gen_sql_query!(query, filter, columns) } } @@ -58,19 +66,16 @@ impl RepositoryReadBy for SpecDb { diesel::pg::PgConnection, Self::Output, > { - let r#where = filter.r#where.clone().unwrap_or_default(); let mut query = specs::table.into_boxed(); - if let Some(key) = r#where.get("key") { - gen_where4uuid!(query, specs::key, key); - } - if let Some(kind_key) = r#where.get("kind_key") { - gen_where4string!(query, specs::kind_key, kind_key); - } - if let Some(version) = r#where.get("version") { - gen_where4string!(query, specs::version, version); + let columns = Self::get_columns(); + query = gen_sql_query!(query, filter, columns); + if let Some(orders) = &filter.order_by { + query = gen_sql_order_by!(query, orders, columns); + } else { + query = query.order(specs::created_at.desc()); } if is_multiple { - gen_multiple!(query, specs::created_at, filter); + gen_sql_multiple!(query, filter); } query } diff --git a/bin/nanocld/src/repositories/vm.rs b/bin/nanocld/src/repositories/vm.rs index 840957a5f..115f1173d 100644 --- a/bin/nanocld/src/repositories/vm.rs +++ b/bin/nanocld/src/repositories/vm.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use diesel::prelude::*; use nanocl_error::{io::IoResult, http::HttpResult}; @@ -10,17 +12,37 @@ use nanocl_stubs::{ }; use crate::{ - gen_multiple, gen_where4json, gen_where4string, + gen_sql_multiple, gen_sql_order_by, gen_sql_query, utils, + schema::vms, models::{ - NamespaceDb, ObjPsStatusDb, Pool, ProcessDb, SpecDb, VmDb, VmUpdateDb, + ColumnType, NamespaceDb, ObjPsStatusDb, Pool, ProcessDb, SpecDb, VmDb, + VmUpdateDb, }, - schema::vms, - utils, }; use super::generic::*; -impl RepositoryBase for VmDb {} +impl RepositoryBase for VmDb { + fn get_columns<'a>() -> HashMap<&'a str, (ColumnType, &'a str)> { + HashMap::from([ + ("key", (ColumnType::Text, "vms.key")), + ("created_at", (ColumnType::Timestamptz, "vms.created_at")), + ("name", (ColumnType::Text, "vms.name")), + ("namespace_name", (ColumnType::Text, "vms.namespace_name")), + ("spec_key", (ColumnType::Text, "vms.spec_key")), + ("data", (ColumnType::Json, "specs.data")), + ("metadata", (ColumnType::Json, "specs.metadata")), + ( + "status.wanted", + (ColumnType::Text, "object_process_statuses.wanted"), + ), + ( + "status.actual", + (ColumnType::Text, "object_process_statuses.actual"), + ), + ]) + } +} impl RepositoryCreate for VmDb {} @@ -45,42 +67,19 @@ impl RepositoryReadBy for VmDb { diesel::PgConnection, Self::Output, > { - let r#where = filter.r#where.to_owned().unwrap_or_default(); let mut query = vms::table .inner_join(crate::schema::specs::table) .inner_join(crate::schema::object_process_statuses::table) .into_boxed(); - if let Some(value) = r#where.get("key") { - gen_where4string!(query, vms::key, value); - } - if let Some(value) = r#where.get("name") { - gen_where4string!(query, vms::name, value); - } - if let Some(value) = r#where.get("namespace_name") { - gen_where4string!(query, vms::namespace_name, value); - } - if let Some(value) = r#where.get("data") { - gen_where4json!(query, crate::schema::specs::data, value); - } - if let Some(value) = r#where.get("metadata") { - gen_where4json!(query, crate::schema::specs::metadata, value); - } - if let Some(value) = r#where.get("status.wanted") { - gen_where4string!( - query, - crate::schema::object_process_statuses::wanted, - value - ); - } - if let Some(value) = r#where.get("status.actual") { - gen_where4string!( - query, - crate::schema::object_process_statuses::actual, - value - ); + let columns = Self::get_columns(); + query = gen_sql_query!(query, filter, columns); + if let Some(orders) = &filter.order_by { + query = gen_sql_order_by!(query, orders, columns); + } else { + query = query.order(vms::created_at.desc()); } if is_multiple { - gen_multiple!(query, vms::created_at, filter); + gen_sql_multiple!(query, filter); } query } @@ -91,41 +90,12 @@ impl RepositoryCountBy for VmDb { filter: &GenericFilter, ) -> impl diesel::query_dsl::methods::LoadQuery<'static, diesel::PgConnection, i64> { - let r#where = filter.r#where.to_owned().unwrap_or_default(); let mut query = vms::table .inner_join(crate::schema::specs::table) .inner_join(crate::schema::object_process_statuses::table) .into_boxed(); - if let Some(value) = r#where.get("key") { - gen_where4string!(query, vms::key, value); - } - if let Some(value) = r#where.get("name") { - gen_where4string!(query, vms::name, value); - } - if let Some(value) = r#where.get("namespace_name") { - gen_where4string!(query, vms::namespace_name, value); - } - if let Some(value) = r#where.get("data") { - gen_where4json!(query, crate::schema::specs::data, value); - } - if let Some(value) = r#where.get("metadata") { - gen_where4json!(query, crate::schema::specs::metadata, value); - } - if let Some(value) = r#where.get("status.wanted") { - gen_where4string!( - query, - crate::schema::object_process_statuses::wanted, - value - ); - } - if let Some(value) = r#where.get("status.actual") { - gen_where4string!( - query, - crate::schema::object_process_statuses::actual, - value - ); - } - query.count() + let columns = Self::get_columns(); + gen_sql_query!(query, filter, columns).count() } } diff --git a/bin/nanocld/src/repositories/vm_image.rs b/bin/nanocld/src/repositories/vm_image.rs index 892e5f411..2d167d4ad 100644 --- a/bin/nanocld/src/repositories/vm_image.rs +++ b/bin/nanocld/src/repositories/vm_image.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use diesel::prelude::*; use nanocl_error::io::IoResult; @@ -5,14 +7,28 @@ use nanocl_error::io::IoResult; use nanocl_stubs::generic::{GenericFilter, GenericClause}; use crate::{ - gen_multiple, gen_where4string, - models::{Pool, VmImageDb, VmImageUpdateDb}, + gen_sql_multiple, gen_sql_order_by, gen_sql_query, schema::vm_images, + models::{ColumnType, Pool, VmImageDb, VmImageUpdateDb}, }; use super::generic::*; -impl RepositoryBase for VmImageDb {} +impl RepositoryBase for VmImageDb { + fn get_columns<'a>() -> HashMap<&'a str, (ColumnType, &'a str)> { + HashMap::from([ + ("name", (ColumnType::Text, "vm_images.name")), + ("kind", (ColumnType::Text, "vm_images.kind")), + ("parent", (ColumnType::Text, "vm_images.parent")), + ("format", (ColumnType::Text, "vm_images.format")), + ("path", (ColumnType::Text, "vm_images.path")), + ( + "created_at", + (ColumnType::Timestamptz, "vm_images.created_at"), + ), + ]) + } +} impl RepositoryCreate for VmImageDb {} @@ -37,25 +53,16 @@ impl RepositoryReadBy for VmImageDb { diesel::pg::PgConnection, Self::Output, > { - let r#where = filter.r#where.clone().unwrap_or_default(); let mut query = vm_images::table.into_boxed(); - if let Some(value) = r#where.get("name") { - gen_where4string!(query, vm_images::name, value); - } - if let Some(value) = r#where.get("kind") { - gen_where4string!(query, vm_images::kind, value); - } - if let Some(value) = r#where.get("parent") { - gen_where4string!(query, vm_images::parent, value); - } - if let Some(value) = r#where.get("format") { - gen_where4string!(query, vm_images::format, value); - } - if let Some(value) = r#where.get("path") { - gen_where4string!(query, vm_images::path, value); + let columns = Self::get_columns(); + query = gen_sql_query!(query, filter, columns); + if let Some(orders) = &filter.order_by { + query = gen_sql_order_by!(query, orders, columns); + } else { + query = query.order(vm_images::created_at.desc()); } if is_multiple { - gen_multiple!(query, vm_images::created_at, filter); + gen_sql_multiple!(query, filter); } query } @@ -66,24 +73,9 @@ impl RepositoryCountBy for VmImageDb { filter: &GenericFilter, ) -> impl diesel::query_dsl::methods::LoadQuery<'static, diesel::PgConnection, i64> { - let r#where = filter.r#where.clone().unwrap_or_default(); let mut query = vm_images::table.into_boxed(); - if let Some(value) = r#where.get("name") { - gen_where4string!(query, vm_images::name, value); - } - if let Some(value) = r#where.get("kind") { - gen_where4string!(query, vm_images::kind, value); - } - if let Some(value) = r#where.get("parent") { - gen_where4string!(query, vm_images::parent, value); - } - if let Some(value) = r#where.get("format") { - gen_where4string!(query, vm_images::format, value); - } - if let Some(value) = r#where.get("path") { - gen_where4string!(query, vm_images::path, value); - } - query.count() + let columns = Self::get_columns(); + gen_sql_query!(query, filter, columns).count() } } diff --git a/bin/nanocld/src/services/openapi.rs b/bin/nanocld/src/services/openapi.rs index 0eccd07c0..e2818c9aa 100644 --- a/bin/nanocld/src/services/openapi.rs +++ b/bin/nanocld/src/services/openapi.rs @@ -39,7 +39,7 @@ use nanocl_stubs::process::{Process, ProcessKind, ProcessStats}; use nanocl_stubs::config::DaemonConfig; use nanocl_stubs::secret::{Secret, SecretPartial, SecretUpdate}; use nanocl_stubs::generic::{ - GenericCount, GenericClause, GenericFilter, ImagePullPolicy, + GenericCount, GenericClause, GenericFilter, GenericWhere, ImagePullPolicy, }; use nanocl_stubs::system::{ BinaryInfo, Event, EventActor, EventActorKind, EventCondition, EventKind, @@ -517,6 +517,7 @@ impl Modify for VersionModifier { BollardDate, EmptyObject, GenericClause, + GenericWhere, GenericFilter, ImagePullPolicy, // Event diff --git a/bin/nanocld/src/system/docker_event.rs b/bin/nanocld/src/system/docker_event.rs index 3fbc70de3..ac9eac3d7 100644 --- a/bin/nanocld/src/system/docker_event.rs +++ b/bin/nanocld/src/system/docker_event.rs @@ -9,8 +9,8 @@ use bollard_next::{ service::{EventMessageTypeEnum, EventMessage}, }; use nanocl_stubs::system::{ - EventActor, EventActorKind, EventKind, EventPartial, NativeEventAction, - ObjPsStatusKind, + ObjPsStatusKind, EventActor, EventActorKind, EventKind, EventPartial, + NativeEventAction, }; use crate::{ @@ -79,9 +79,40 @@ async fn exec_docker( }), }; match action { + "start" => { + let actual_status = + ObjPsStatusDb::read_by_pk(&kind_key, &state.inner.pool).await?; + match (&kind, &actual_status.actual) { + (EventActorKind::Cargo, status) + if status != &ObjPsStatusKind::Start.to_string() => + { + ObjPsStatusDb::update_actual_status( + &kind_key, + &ObjPsStatusKind::Start, + &state.inner.pool, + ) + .await?; + } + (EventActorKind::Vm, status) + if status != &ObjPsStatusKind::Start.to_string() => + { + ObjPsStatusDb::update_actual_status( + &kind_key, + &ObjPsStatusKind::Start, + &state.inner.pool, + ) + .await?; + } + _ => {} + } + } "die" => { - match &kind { - EventActorKind::Cargo => { + let actual_status = + ObjPsStatusDb::read_by_pk(&kind_key, &state.inner.pool).await?; + match (&kind, &actual_status.wanted) { + (EventActorKind::Cargo, status) + if status != &ObjPsStatusKind::Stop.to_string() => + { ObjPsStatusDb::update_actual_status( &kind_key, &ObjPsStatusKind::Fail, @@ -93,10 +124,12 @@ async fn exec_docker( state.emit_warning_native_action( &cargo, NativeEventAction::Fail, - Some(format!("Process {name} died")), + Some(format!("Process {name}")), ); } - EventActorKind::Vm => { + (EventActorKind::Vm, status) + if status != &ObjPsStatusKind::Stop.to_string() => + { ObjPsStatusDb::update_actual_status( &kind_key, &ObjPsStatusKind::Fail, @@ -108,7 +141,7 @@ async fn exec_docker( state.emit_warning_native_action( &vm, NativeEventAction::Fail, - Some(format!("Process {name} died")), + Some(format!("Process {name}")), ); } _ => {} diff --git a/bin/nanocld/src/system/system_state.rs b/bin/nanocld/src/system/system_state.rs index 543f65e6a..4998e8022 100644 --- a/bin/nanocld/src/system/system_state.rs +++ b/bin/nanocld/src/system/system_state.rs @@ -38,7 +38,7 @@ impl SystemState { let (sx, rx) = mpsc::unbounded(); let system_state = SystemState { inner: Arc::new(SystemStateInner { - pool: Arc::clone(&pool), + pool, docker_api: docker.clone(), config: conf.to_owned(), event_emitter: sx, @@ -52,18 +52,19 @@ impl SystemState { } /// Start the system event loop + /// It will handle events and execute some actions + /// It will also emit the event to the raw event emitter for the http clients fn run(self, mut rx: mpsc::UnboundedReceiver) { self.inner.arbiter.clone().exec_fn(move || { rt::spawn(async move { while let Some(e) = rx.next().await { - let e_ptr = e.clone(); + super::exec_event(&e, &self); let self_ptr = self.clone(); rt::spawn(async move { - if let Err(err) = self_ptr.inner.event_emitter_raw.emit(&e_ptr) { + if let Err(err) = self_ptr.inner.event_emitter_raw.emit(&e) { log::error!("system::run: raw emit {err}"); } }); - super::exec_event(&e, &self); } }); }); diff --git a/bin/nanocld/src/utils/store.rs b/bin/nanocld/src/utils/store.rs index da9b5407b..a8067e488 100644 --- a/bin/nanocld/src/utils/store.rs +++ b/bin/nanocld/src/utils/store.rs @@ -1,4 +1,4 @@ -use std::{sync::Arc, time::Duration, net::ToSocketAddrs}; +use std::{net::ToSocketAddrs, time::Duration}; use ntex::{rt, web, time}; use diesel::{ @@ -27,7 +27,7 @@ pub async fn create_pool(store_addr: &str) -> IoResult { .map_err(|err| { IoError::interrupted("CockroachDB", &format!("Unable to create pool {err}")) })?; - Ok(Arc::new(pool)) + Ok(pool) } /// Get connection from the connection pool for the store `cockroachdb` diff --git a/bin/nanocld/src/utils/system.rs b/bin/nanocld/src/utils/system.rs index 639dec31e..668bc01cd 100644 --- a/bin/nanocld/src/utils/system.rs +++ b/bin/nanocld/src/utils/system.rs @@ -3,24 +3,27 @@ use std::collections::HashMap; use nanocl_error::io::{FromIo, IoResult, IoError}; use bollard_next::{ + container::{InspectContainerOptions, ListContainersOptions}, + secret::ContainerStateStatusEnum, service::ContainerInspectResponse, - container::{ListContainersOptions, InspectContainerOptions}, }; use nanocl_stubs::{ + cargo::Cargo, + cargo_spec::CargoSpecPartial, generic::{GenericClause, GenericFilter}, namespace::NamespacePartial, - cargo_spec::CargoSpecPartial, process::ProcessPartial, + system::ObjPsStatusKind, }; use crate::{ vars, utils, repositories::generic::*, + objects::generic::ObjCreate, models::{ - SystemState, CargoDb, ProcessDb, NamespaceDb, VmImageDb, ProcessUpdateDb, - CargoObjCreateIn, + CargoDb, CargoObjCreateIn, NamespaceDb, ObjPsStatusDb, ObjPsStatusUpdate, + ProcessDb, ProcessUpdateDb, SystemState, VmImageDb, }, - objects::generic::ObjCreate, }; /// Will determine if the instance is registered by nanocl @@ -111,6 +114,35 @@ pub async fn register_namespace( Ok(()) } +async fn sync_cargo_status( + cargo: &Cargo, + container: &ContainerInspectResponse, + state: &SystemState, +) -> IoResult<()> { + if let Some(status) = container.state.clone().unwrap_or_default().status { + let new_status = match status { + ContainerStateStatusEnum::RUNNING => Some(ObjPsStatusKind::Start), + ContainerStateStatusEnum::RESTARTING => Some(ObjPsStatusKind::Fail), + ContainerStateStatusEnum::DEAD => Some(ObjPsStatusKind::Stop), + ContainerStateStatusEnum::EXITED => Some(ObjPsStatusKind::Stop), + _ => None, + }; + if let Some(new_status) = new_status { + ObjPsStatusDb::update_pk( + &cargo.spec.cargo_key, + ObjPsStatusUpdate { + wanted: Some(ObjPsStatusKind::Start.to_string()), + actual: Some(new_status.to_string()), + ..Default::default() + }, + &state.inner.pool, + ) + .await?; + } + } + Ok(()) +} + /// Convert existing container instances with our labels to cargo. /// We use it to be sure that all existing containers are registered as cargo. pub async fn sync_processes(state: &SystemState) -> IoResult<()> { @@ -184,10 +216,12 @@ pub async fn sync_processes(state: &SystemState) -> IoResult<()> { spec: new_cargo.clone(), version: format!("v{}", vars::VERSION), }; - CargoDb::create_obj(obj, state).await?; + let synced_cargo = CargoDb::create_obj(obj, state).await?; + sync_cargo_status(&synced_cargo, &container, state).await?; } // If the cargo is already in our store and the config is different we update it Ok(cargo) => { + sync_cargo_status(&cargo, &container, state).await?; if cargo.spec.container == config { continue; } diff --git a/bin/nanocld/src/utils/vm_image.rs b/bin/nanocld/src/utils/vm_image.rs index 4eaa2d7c8..ed285bd82 100644 --- a/bin/nanocld/src/utils/vm_image.rs +++ b/bin/nanocld/src/utils/vm_image.rs @@ -1,4 +1,4 @@ -use std::{sync::Arc, process::Stdio}; +use std::process::Stdio; use ntex::{rt, web, util::Bytes, channel::mpsc::Receiver}; use tokio::{fs, io::AsyncReadExt, process::Command}; @@ -147,7 +147,7 @@ pub async fn clone( let name = name.to_owned(); let image = image.clone(); let daemon_conf = state.inner.config.clone(); - let pool = Arc::clone(&state.inner.pool); + let pool = state.inner.pool.clone(); rt::spawn(async move { let img_path = image.path.clone(); let base_path = diff --git a/bin/ncdns/Cargo.toml b/bin/ncdns/Cargo.toml index 6478e9264..993899371 100644 --- a/bin/ncdns/Cargo.toml +++ b/bin/ncdns/Cargo.toml @@ -33,7 +33,7 @@ serde_yaml = "0.9" nanocl_error = { version = "0.4", features = ["io", "http", "http_client"] } log = "0.4" clap = { version = "4.5", features = ["derive"] } -ntex = { version = "1.2", features = ["tokio", "openssl"] } +ntex = { version = "2", features = ["tokio", "openssl"] } tokio = { version = "1.36", features = ["fs"] } futures = "0.3" serde = { version = "1.0", features = ["derive"] } diff --git a/bin/ncdns/src/main.rs b/bin/ncdns/src/main.rs index ec09c8de0..9290c43d0 100644 --- a/bin/ncdns/src/main.rs +++ b/bin/ncdns/src/main.rs @@ -29,7 +29,7 @@ async fn run(cli: &Cli) -> IoResult<()> { client = NanocldClient::connect_to(&ConnectOpts { url: "http://nanocl.internal:8585".into(), ..Default::default() - }); + })?; } event::spawn(&client); let server = server::gen(&cli.host, &dnsmasq, &client)?; diff --git a/bin/ncdns/src/server.rs b/bin/ncdns/src/server.rs index 06462bbc3..63d689b26 100644 --- a/bin/ncdns/src/server.rs +++ b/bin/ncdns/src/server.rs @@ -60,7 +60,7 @@ mod tests { let client = NanocldClient::connect_to(&ConnectOpts { url: "http://nanocl.internal:8585".into(), ..Default::default() - }); + })?; let server = gen("unix:///tmp/ncdns.sock", &dnsmasq, &client)?; server.stop(true).await; let server = gen("tcp://0.0.0.0:9987", &dnsmasq, &client)?; @@ -74,7 +74,7 @@ mod tests { let client = NanocldClient::connect_to(&ConnectOpts { url: "http://nanocl.internal:8585".into(), ..Default::default() - }); + })?; let server = gen("wrong://dsadsa", &dnsmasq, &client); assert!(server.is_err()); Ok(()) diff --git a/bin/ncdns/src/utils.rs b/bin/ncdns/src/utils.rs index 0348a02a6..cd4af8a0a 100644 --- a/bin/ncdns/src/utils.rs +++ b/bin/ncdns/src/utils.rs @@ -166,11 +166,15 @@ pub mod tests { pub fn gen_default_test_client() -> TestClient { before(); let dnsmasq = dnsmasq::Dnsmasq::new("/tmp/dnsmasq"); - dnsmasq.ensure().unwrap(); + dnsmasq + .ensure() + .expect("Expect to setup minimal dnsmasq config"); let client = NanocldClient::connect_to(&ConnectOpts { url: "http://nanocl.internal:8585".into(), ..Default::default() - }); // Create test server + }) + .expect("Failed to create a nanocl client"); + // Create test server let srv = ntex::web::test::server(move || { ntex::web::App::new() .state(dnsmasq.clone()) diff --git a/bin/ncproxy/Cargo.toml b/bin/ncproxy/Cargo.toml index eae5f9cb3..c8e4ac49c 100644 --- a/bin/ncproxy/Cargo.toml +++ b/bin/ncproxy/Cargo.toml @@ -47,7 +47,7 @@ nanocl_error = { version = "0.4", features = [ log = "0.4" liquid = "0.26" clap = { version = "4.5", features = ["derive"] } -ntex = { version = "1.2", features = ["tokio"] } +ntex = { version = "2", features = ["tokio"] } tokio = { version = "1.36", features = ["fs"] } serde = "1.0" serde_json = "1.0" diff --git a/bin/ncproxy/src/subsystem/init.rs b/bin/ncproxy/src/subsystem/init.rs index cee773aa3..c1bf94524 100644 --- a/bin/ncproxy/src/subsystem/init.rs +++ b/bin/ncproxy/src/subsystem/init.rs @@ -20,7 +20,7 @@ pub async fn init(cli: &Cli) -> IoResult { client = NanocldClient::connect_to(&ConnectOpts { url: "http://nanocl.internal:8585".into(), ..Default::default() - }); + })?; } let event_emitter = EventEmitter::new(&client); let state = Arc::new(SystemState { diff --git a/bin/ncproxy/src/utils/mod.rs b/bin/ncproxy/src/utils/mod.rs index 2ee128d87..448f73356 100644 --- a/bin/ncproxy/src/utils/mod.rs +++ b/bin/ncproxy/src/utils/mod.rs @@ -33,9 +33,9 @@ pub(crate) mod tests { const CARGO_NAME: &str = "ncproxy-test"; const CARGO_IMAGE: &str = "ghcr.io/next-hat/nanocl-get-started:latest"; let client = NanocldClient::connect_to(&ConnectOpts { - url: "http://nanocl.internal:8585".into(), + url: "http://nanocl.internal:8585".to_owned(), ..Default::default() - }); + })?; if client.inspect_cargo(CARGO_NAME, None).await.is_err() { let cargo = CargoSpecPartial { name: CARGO_NAME.to_owned(), @@ -56,7 +56,7 @@ pub(crate) mod tests { let client = NanocldClient::connect_to(&ConnectOpts { url: "http://nanocl.internal:8585".into(), ..Default::default() - }); + })?; if client.inspect_cargo(CARGO_NAME, None).await.is_err() { return Ok(()); } diff --git a/bin/ncvpnkit/Cargo.toml b/bin/ncvpnkit/Cargo.toml index 581293101..3cc27387b 100644 --- a/bin/ncvpnkit/Cargo.toml +++ b/bin/ncvpnkit/Cargo.toml @@ -21,7 +21,7 @@ nanocl_error = { version = "0.4", features = [ ] } log = "0.4" futures-util = "0.3" -ntex = { version = "1.2", features = ["tokio"] } +ntex = { version = "2", features = ["tokio"] } vpnkitrc = { version = "0.1", features = ["tokio"] } nanocld_client = { version = "0.15" } nanocl_utils = { version = "0.6", features = ["logger"] } diff --git a/crates/nanocl_error/Cargo.toml b/crates/nanocl_error/Cargo.toml index 0db378a77..7502f72d0 100644 --- a/crates/nanocl_error/Cargo.toml +++ b/crates/nanocl_error/Cargo.toml @@ -25,7 +25,7 @@ serde_urlencoded = ["dep:serde_urlencoded"] tokio = ["dep:tokio"] [dependencies] -ntex = { version = "1.2", optional = true } +ntex = { version = "2", optional = true } serde_json = { version = "1.0", optional = true } serde_urlencoded = { version = "0.7", optional = true } diesel = { version = "2.1", default-features = false, optional = true } diff --git a/crates/nanocl_error/src/http_client.rs b/crates/nanocl_error/src/http_client.rs index eeea7aa42..551b4b240 100644 --- a/crates/nanocl_error/src/http_client.rs +++ b/crates/nanocl_error/src/http_client.rs @@ -53,6 +53,12 @@ impl From> for HttpClientError { } } +impl From for HttpClientError { + fn from(f: IoError) -> Self { + Self::IoError(f) + } +} + impl From for HttpClientError { fn from(f: HttpError) -> Self { Self::HttpError(f) diff --git a/crates/nanocl_stubs/src/generic.rs b/crates/nanocl_stubs/src/generic.rs index 7b5770448..b3f8977d4 100644 --- a/crates/nanocl_stubs/src/generic.rs +++ b/crates/nanocl_stubs/src/generic.rs @@ -68,6 +68,43 @@ pub enum GenericClause { HasKey(String), } +#[derive(Default, Debug, Clone)] +#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct GenericWhere { + #[cfg_attr(feature = "serde", serde(flatten))] + pub conditions: HashMap, + pub or: Option>>, +} + +/// Generic order enum +#[derive(Debug, Clone)] +#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum GenericOrder { + /// Ascending + Asc, + /// Descending + Desc, +} + +impl std::str::FromStr for GenericOrder { + type Err = std::io::Error; + + fn from_str(s: &str) -> Result { + match s { + "asc" => Ok(Self::Asc), + "desc" => Ok(Self::Desc), + _ => Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Invalid order", + )), + } + } +} + /// Generic filter for list operation #[derive(Default, Debug, Clone)] #[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))] @@ -76,11 +113,13 @@ pub enum GenericClause { pub struct GenericFilter { /// Where clause #[cfg_attr(feature = "serde", serde(rename = "where"))] - pub r#where: Option>, + pub r#where: Option, /// Limit number of items default (100) pub limit: Option, /// Offset to navigate through items pub offset: Option, + /// Order by + pub order_by: Option>, } /// Generic query string parameters for list operations @@ -218,12 +257,13 @@ impl GenericFilter { pub fn r#where(mut self, key: &str, clause: GenericClause) -> Self { if self.r#where.is_none() { - self.r#where = Some(HashMap::new()); + self.r#where = Some(GenericWhere::default()); } self .r#where .as_mut() .unwrap() + .conditions .insert(key.to_owned(), clause); self } diff --git a/crates/nanocl_utils/Cargo.toml b/crates/nanocl_utils/Cargo.toml index 9f2cb6e5c..3c4492d82 100644 --- a/crates/nanocl_utils/Cargo.toml +++ b/crates/nanocl_utils/Cargo.toml @@ -29,7 +29,7 @@ build_tools = ["dep:clap", "dep:clap_mangen"] ntex_test_client = ["dep:ntex", "dep:serde"] [dependencies] -ntex = { version = "1.2", optional = true } +ntex = { version = "2", optional = true } log = { version = "0.4", optional = true } env_logger = { version = "0.11", optional = true } serde = { version = "1.0", features = ["derive"], optional = true } diff --git a/crates/nanocl_utils/src/ntex/middlewares/serialize_error.rs b/crates/nanocl_utils/src/ntex/middlewares/serialize_error.rs index 805961a85..1f7dc1ce0 100644 --- a/crates/nanocl_utils/src/ntex/middlewares/serialize_error.rs +++ b/crates/nanocl_utils/src/ntex/middlewares/serialize_error.rs @@ -27,8 +27,8 @@ where type Response = WebResponse; type Error = Error; - ntex::forward_poll_ready!(service); - ntex::forward_poll_shutdown!(service); + ntex::forward_ready!(service); + ntex::forward_shutdown!(service); async fn call( &self, diff --git a/crates/nanocl_utils/src/ntex/middlewares/versioning.rs b/crates/nanocl_utils/src/ntex/middlewares/versioning.rs index c38269c29..5a3c84fb0 100644 --- a/crates/nanocl_utils/src/ntex/middlewares/versioning.rs +++ b/crates/nanocl_utils/src/ntex/middlewares/versioning.rs @@ -69,7 +69,7 @@ where type Response = WebResponse; type Error = Error; - ntex::forward_poll_ready!(service); + ntex::forward_ready!(service); async fn call( &self, diff --git a/crates/nanocld_client/Cargo.toml b/crates/nanocld_client/Cargo.toml index 283460706..5fdc66376 100644 --- a/crates/nanocld_client/Cargo.toml +++ b/crates/nanocld_client/Cargo.toml @@ -27,7 +27,7 @@ tokio = { version = "1.36", features = ["fs"] } [dependencies] futures = "0.3" serde_json = "1.0" -ntex = { version = "1.2" } +ntex = { version = "2" } serde = { version = "1.0", features = ["derive"] } bollard-next = { version = "0.16.1" } nanocl_stubs = { version = "0.15", features = ["serde"] } diff --git a/crates/nanocld_client/src/cargo.rs b/crates/nanocld_client/src/cargo.rs index cb575294b..e56b90736 100644 --- a/crates/nanocld_client/src/cargo.rs +++ b/crates/nanocld_client/src/cargo.rs @@ -261,7 +261,8 @@ mod tests { let client = NanocldClient::connect_to(&ConnectOpts { url: "http://nanocl.internal:8585".into(), ..Default::default() - }); + }) + .expect("Failed to create a nanocl client"); client.list_cargo(None).await.unwrap(); let new_cargo = CargoSpecPartial { name: CARGO_NAME.into(), @@ -321,7 +322,8 @@ mod tests { let client = NanocldClient::connect_to(&ConnectOpts { url: "http://nanocl.internal:8585".into(), ..Default::default() - }); + }) + .expect("Failed to create a nanocl client"); let new_cargo = CargoSpecPartial { name: "client-test-cargodup".into(), container: bollard_next::container::Config { diff --git a/crates/nanocld_client/src/exec.rs b/crates/nanocld_client/src/exec.rs index c18b40a9d..acddd799e 100644 --- a/crates/nanocld_client/src/exec.rs +++ b/crates/nanocld_client/src/exec.rs @@ -129,7 +129,8 @@ mod tests { let client = NanocldClient::connect_to(&ConnectOpts { url: "http://nanocl.internal:8585".into(), ..Default::default() - }); + }) + .expect("Failed to create a nanocl client"); let exec = CreateExecOptions { cmd: Some(vec!["echo".into(), "hello".into()]), ..Default::default() diff --git a/crates/nanocld_client/src/http_client.rs b/crates/nanocld_client/src/http_client.rs index 2f1ed127d..83aa4951d 100644 --- a/crates/nanocld_client/src/http_client.rs +++ b/crates/nanocld_client/src/http_client.rs @@ -1,5 +1,6 @@ use std::error::Error; +use nanocl_error::io::IoResult; use ntex::{rt, http}; use nanocl_stubs::{generic::GenericListQueryNsp, system::SslConfig}; @@ -61,28 +62,28 @@ impl NanocldClient { } } - pub fn connect_to(opts: &ConnectOpts) -> Self { + pub fn connect_to(opts: &ConnectOpts) -> IoResult { let url = opts.url.clone(); let version = opts.version.clone(); match url { url if url.starts_with("http://") || url.starts_with("https://") => { - NanocldClient { + Ok(NanocldClient { url: url.to_owned(), + ssl: opts.ssl.clone(), unix_socket: None, version: version.unwrap_or(format!("v{NANOCLD_DEFAULT_VERSION}")), - ssl: opts.ssl.clone(), - } + }) } url if url.starts_with("unix://") => { let path = url.trim_start_matches("unix://"); - NanocldClient { + Ok(NanocldClient { + ssl: None, url: "http://localhost".to_owned(), unix_socket: Some(path.to_owned()), version: version.unwrap_or(format!("v{NANOCLD_DEFAULT_VERSION}")), - ssl: None, - } + }) } - _ => panic!("Invalid url: {}", url), + _ => Err(IoError::invalid_data("Invalid url", &url)), } } @@ -99,7 +100,7 @@ impl NanocldClient { } } - fn gen_client(&self) -> http::client::Client { + fn gen_client(&self) -> IoResult { let mut client = http::client::Client::build(); if let Some(unix_socket) = &self.unix_socket { let unix_socket = unix_socket.clone(); @@ -115,19 +116,38 @@ impl NanocldClient { } #[cfg(feature = "openssl")] { - use openssl::ssl::{SslMethod, SslConnector, SslVerifyMode, SslFiletype}; + use openssl::ssl::{SslMethod, SslConnector, SslVerifyMode}; if let Some(ssl) = &self.ssl { let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); - builder.set_verify(SslVerifyMode::NONE); - builder - .set_certificate_file(&ssl.cert.clone().unwrap(), SslFiletype::PEM) - .unwrap(); - builder - .set_private_key_file( - &ssl.cert_key.clone().unwrap(), - SslFiletype::PEM, + builder.set_verify(SslVerifyMode::PEER); + let cert = openssl::x509::X509::from_pem( + ssl.cert.clone().expect("Ssl.cert to be fill").as_bytes(), + ) + .map_err(|err| { + IoError::invalid_data("Invalid ssl cert", err.to_string().as_str()) + })?; + let cert_key = openssl::pkey::PKey::private_key_from_pem( + ssl + .cert_key + .clone() + .expect("Ssl.cert_key to be fill") + .as_bytes(), + ) + .map_err(|err| { + IoError::invalid_data( + "Invalid ssl cert key", + err.to_string().as_str(), + ) + })?; + builder.set_certificate(&cert).map_err(|err| { + IoError::invalid_data("Invalid ssl cert", err.to_string().as_str()) + })?; + builder.set_private_key(&cert_key).map_err(|err| { + IoError::invalid_data( + "Invalid ssl cert key", + err.to_string().as_str(), ) - .unwrap(); + })?; client = ntex::http::client::Client::build().connector( http::client::Connector::default() .openssl(builder.build()) @@ -135,7 +155,7 @@ impl NanocldClient { ) } } - client.timeout(ntex::time::Millis::from_secs(100)).finish() + Ok(client.timeout(ntex::time::Millis::from_secs(100)).finish()) } fn send_error( @@ -154,46 +174,58 @@ impl NanocldClient { format!("{}/{}{}", self.url, self.version, url) } - fn get(&self, url: &str) -> http::client::ClientRequest { - self - .gen_client() - .get(self.gen_url(url)) - .header("User-Agent", "nanocld_client") + fn get(&self, url: &str) -> IoResult { + Ok( + self + .gen_client()? + .get(self.gen_url(url)) + .header("User-Agent", "nanocld_client"), + ) } - fn delete(&self, url: &str) -> http::client::ClientRequest { - self - .gen_client() - .delete(self.gen_url(url)) - .header("User-Agent", "nanocld_client") + fn delete(&self, url: &str) -> IoResult { + Ok( + self + .gen_client()? + .delete(self.gen_url(url)) + .header("User-Agent", "nanocld_client"), + ) } - fn post(&self, url: &str) -> http::client::ClientRequest { - self - .gen_client() - .post(self.gen_url(url)) - .header("User-Agent", "nanocld_client") + fn post(&self, url: &str) -> IoResult { + Ok( + self + .gen_client()? + .post(self.gen_url(url)) + .header("User-Agent", "nanocld_client"), + ) } - fn patch(&self, url: &str) -> http::client::ClientRequest { - self - .gen_client() - .patch(self.gen_url(url)) - .header("User-Agent", "nanocld_client") + fn patch(&self, url: &str) -> IoResult { + Ok( + self + .gen_client()? + .patch(self.gen_url(url)) + .header("User-Agent", "nanocld_client"), + ) } - fn put(&self, url: &str) -> http::client::ClientRequest { - self - .gen_client() - .put(self.gen_url(url)) - .header("User-Agent", "nanocld_client") + fn put(&self, url: &str) -> IoResult { + Ok( + self + .gen_client()? + .put(self.gen_url(url)) + .header("User-Agent", "nanocld_client"), + ) } - fn head(&self, url: &str) -> http::client::ClientRequest { - self - .gen_client() - .head(self.gen_url(url)) - .header("User-Agent", "nanocld_client") + fn head(&self, url: &str) -> IoResult { + Ok( + self + .gen_client()? + .head(self.gen_url(url)) + .header("User-Agent", "nanocld_client"), + ) } pub async fn send_get( @@ -204,9 +236,7 @@ impl NanocldClient { where Q: serde::Serialize, { - let mut req = self - .get(url) - .set_connection_type(http::ConnectionType::KeepAlive); + let mut req = self.get(url)?; if let Some(query) = query { req = req .query(&query) @@ -245,7 +275,7 @@ impl NanocldClient { B: serde::Serialize, Q: serde::Serialize, { - let mut req = self.post(url); + let mut req = self.post(url)?; if let Some(query) = query { req = req .query(&query) @@ -274,7 +304,7 @@ impl NanocldClient { Q: serde::Serialize, E: Error + 'static, { - let mut req = self.post(url); + let mut req = self.post(url)?; if let Some(query) = query { req = req .query(&query) @@ -297,7 +327,7 @@ impl NanocldClient { where Q: serde::Serialize, { - let mut req = self.delete(url); + let mut req = self.delete(url)?; if let Some(query) = query { req = req .query(&query) @@ -319,7 +349,7 @@ impl NanocldClient { B: serde::Serialize, Q: serde::Serialize, { - let mut req = self.patch(url); + let mut req = self.patch(url)?; if let Some(query) = query { req = req .query(&query) @@ -345,7 +375,7 @@ impl NanocldClient { where Q: serde::Serialize, { - let mut req = self.head(url); + let mut req = self.head(url)?; if let Some(query) = query { req = req .query(&query) @@ -367,7 +397,7 @@ impl NanocldClient { B: serde::Serialize, Q: serde::Serialize, { - let mut req = self.put(url); + let mut req = self.put(url)?; if let Some(query) = query { req = req .query(&query) diff --git a/crates/nanocld_client/src/job.rs b/crates/nanocld_client/src/job.rs index 382189a74..0b98805ed 100644 --- a/crates/nanocld_client/src/job.rs +++ b/crates/nanocld_client/src/job.rs @@ -108,7 +108,8 @@ mod tests { let client = NanocldClient::connect_to(&ConnectOpts { url: "http://nanocl.internal:8585".into(), ..Default::default() - }); + }) + .expect("Failed to create a nanocl client"); client.list_job(None).await.unwrap(); } @@ -117,7 +118,8 @@ mod tests { let client = NanocldClient::connect_to(&ConnectOpts { url: "http://nanocl.internal:8585".into(), ..Default::default() - }); + }) + .expect("Failed to create a nanocl client"); let job = client .create_job(&JobPartial { name: "my_test_job".to_owned(), diff --git a/crates/nanocld_client/src/metric.rs b/crates/nanocld_client/src/metric.rs index 2a86f0238..83cc4d4df 100644 --- a/crates/nanocld_client/src/metric.rs +++ b/crates/nanocld_client/src/metric.rs @@ -89,7 +89,8 @@ mod tests { let client = NanocldClient::connect_to(&ConnectOpts { url: "http://nanocl.internal:8585".into(), ..Default::default() - }); + }) + .expect("Failed to create a nanocl client"); let metric = client .create_metric(&MetricPartial { kind: "my-source.io/type".to_owned(), diff --git a/crates/nanocld_client/src/namespace.rs b/crates/nanocld_client/src/namespace.rs index fc2978551..5f776d81a 100644 --- a/crates/nanocld_client/src/namespace.rs +++ b/crates/nanocld_client/src/namespace.rs @@ -97,7 +97,8 @@ mod tests { let client = NanocldClient::connect_to(&ConnectOpts { url: "http://nanocl.internal:8585".into(), ..Default::default() - }); + }) + .expect("Failed to create a nanocl client"); client.list_namespace(None).await.unwrap(); let namespace = client.create_namespace(NAMESPACE).await.unwrap(); assert_eq!(namespace.name, NAMESPACE); diff --git a/crates/nanocld_client/src/node.rs b/crates/nanocld_client/src/node.rs index 14e6d63af..783bdb278 100644 --- a/crates/nanocld_client/src/node.rs +++ b/crates/nanocld_client/src/node.rs @@ -36,7 +36,8 @@ mod tests { let client = NanocldClient::connect_to(&ConnectOpts { url: "http://nanocl.internal:8585".into(), ..Default::default() - }); + }) + .expect("Failed to create a nanocl client"); let node = client.list_node().await; assert!(node.is_ok()); } diff --git a/crates/nanocld_client/src/process.rs b/crates/nanocld_client/src/process.rs index ab9bd5f3a..b9da97093 100644 --- a/crates/nanocld_client/src/process.rs +++ b/crates/nanocld_client/src/process.rs @@ -219,7 +219,8 @@ mod tests { let client = NanocldClient::connect_to(&ConnectOpts { url: "http://nanocl.internal:8585".into(), ..Default::default() - }); + }) + .expect("Failed to create a nanocl client"); let mut rx = client .logs_processes( "cargo", diff --git a/crates/nanocld_client/src/resource_kind.rs b/crates/nanocld_client/src/resource_kind.rs index 913b4ed49..4a9b18e03 100644 --- a/crates/nanocld_client/src/resource_kind.rs +++ b/crates/nanocld_client/src/resource_kind.rs @@ -131,7 +131,8 @@ mod tests { let client = NanocldClient::connect_to(&ConnectOpts { url: "http://nanocl.internal:8585".into(), ..Default::default() - }); + }) + .expect("Failed to create a nanocl client"); let resource_kind = ResourceKindPartial { name: RESOURCE_KIND_NAME.to_owned(), version: RESOURCE_KIND_VERSION.to_owned(), diff --git a/crates/nanocld_client/src/secret.rs b/crates/nanocld_client/src/secret.rs index fd5b1e55c..d53c633ac 100644 --- a/crates/nanocld_client/src/secret.rs +++ b/crates/nanocld_client/src/secret.rs @@ -105,7 +105,8 @@ mod tests { let client = NanocldClient::connect_to(&ConnectOpts { url: "http://nanocl.internal:8585".into(), ..Default::default() - }); + }) + .expect("Failed to create a nanocl client"); client.list_secret(None).await.unwrap(); let secret = SecretPartial { name: SECRET_NAME.to_owned(), diff --git a/crates/nanocld_client/src/system.rs b/crates/nanocld_client/src/system.rs index a8b4c70c7..314611a0f 100644 --- a/crates/nanocld_client/src/system.rs +++ b/crates/nanocld_client/src/system.rs @@ -89,7 +89,8 @@ mod tests { let client = NanocldClient::connect_to(&ConnectOpts { url: "http://nanocl.internal:8585".into(), ..Default::default() - }); + }) + .expect("Failed to create a nanocl client"); let version = client.get_version().await; assert!(version.is_ok()); } @@ -99,7 +100,8 @@ mod tests { let client = NanocldClient::connect_to(&ConnectOpts { url: "http://nanocl.internal:8585".into(), ..Default::default() - }); + }) + .expect("Failed to create a nanocl client"); let _stream = client.watch_events(None).await.unwrap(); // Todo : find a way to test this on CI because it's limited to 2 threads // let _event = stream.next().await.unwrap(); @@ -110,7 +112,8 @@ mod tests { let client = NanocldClient::connect_to(&ConnectOpts { url: "http://nanocl.internal:8585".into(), ..Default::default() - }); + }) + .expect("Failed to create a nanocl client"); let info = client.info().await.unwrap(); assert!(info.docker.containers.unwrap() > 0); }