From 9b4f5e8c756a8db1d7b0191f9cfeed6aabd0c897 Mon Sep 17 00:00:00 2001 From: tamwuff Date: Sun, 10 Nov 2024 02:45:50 -0500 Subject: [PATCH] Support Azure blob storage in a similar fashion to S3. (#907) --- Cargo.lock | 502 ++++++++++++++++-- crates/main/Cargo.toml | 3 +- crates/store/Cargo.toml | 5 + crates/store/src/backend/azure/mod.rs | 204 +++++++ .../src/backend/composite/distributed_blob.rs | 6 + crates/store/src/backend/mod.rs | 2 + crates/store/src/config.rs | 10 + crates/store/src/dispatch/blob.rs | 6 + crates/store/src/lib.rs | 15 + crates/trc/src/event/description.rs | 2 + crates/trc/src/event/level.rs | 1 + crates/trc/src/event/mod.rs | 1 + crates/trc/src/ipc/metrics.rs | 1 + crates/trc/src/lib.rs | 1 + crates/trc/src/serializers/binary.rs | 2 + tests/Cargo.toml | 3 +- 16 files changed, 707 insertions(+), 57 deletions(-) create mode 100644 crates/store/src/backend/azure/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 34ed3f57..b0e3eee1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "RustyXML" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b5ace29ee3216de37c0546865ad08edef58b0f9e76838ed8959a84a990e58c5" + [[package]] name = "addr2line" version = "0.24.2" @@ -74,7 +80,7 @@ version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ - "getrandom", + "getrandom 0.2.15", "once_cell", "version_check", ] @@ -86,7 +92,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom", + "getrandom 0.2.15", "once_cell", "serde", "version_check", @@ -304,6 +310,17 @@ dependencies = [ "syn 2.0.79", ] +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + [[package]] name = "async-compression" version = "0.4.15" @@ -317,6 +334,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener 5.3.1", + "event-listener-strategy", + "pin-project-lite", +] + [[package]] name = "async-recursion" version = "1.1.1" @@ -461,6 +489,92 @@ dependencies = [ "tower-service", ] +[[package]] +name = "azure_core" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b552ad43a45a746461ec3d3a51dfb6466b4759209414b439c165eb6a6b7729e" +dependencies = [ + "async-trait", + "base64 0.22.1", + "bytes", + "dyn-clone", + "futures", + "getrandom 0.2.15", + "hmac 0.12.1", + "http-types", + "once_cell", + "paste", + "pin-project", + "quick-xml 0.31.0", + "rand 0.8.5", + "reqwest 0.12.8", + "rustc_version 0.4.1", + "serde", + "serde_json", + "sha2 0.10.8", + "time", + "tracing", + "url", + "uuid", +] + +[[package]] +name = "azure_storage" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59f838159f4d29cb400a14d9d757578ba495ae64feb07a7516bf9e4415127126" +dependencies = [ + "RustyXML", + "async-lock", + "async-trait", + "azure_core", + "bytes", + "serde", + "serde_derive", + "time", + "tracing", + "url", + "uuid", +] + +[[package]] +name = "azure_storage_blobs" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97e83c3636ae86d9a6a7962b2112e3b19eb3903915c50ce06ff54ff0a2e6a7e4" +dependencies = [ + "RustyXML", + "azure_core", + "azure_storage", + "azure_svc_blobstorage", + "bytes", + "futures", + "serde", + "serde_derive", + "serde_json", + "time", + "tracing", + "url", + "uuid", +] + +[[package]] +name = "azure_svc_blobstorage" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e6c6f20c5611b885ba94c7bae5e02849a267381aecb8aee577e8c35ff4064c6" +dependencies = [ + "azure_core", + "bytes", + "futures", + "log", + "once_cell", + "serde", + "serde_json", + "time", +] + [[package]] name = "backtrace" version = "0.3.74" @@ -1070,7 +1184,7 @@ dependencies = [ "idna 1.0.2", "imagesize", "imap_proto", - "infer", + "infer 0.16.0", "jmap_proto", "libc", "mail-auth", @@ -1118,6 +1232,15 @@ dependencies = [ "zip", ] +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "console" version = "0.15.8" @@ -1152,7 +1275,7 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" dependencies = [ - "getrandom", + "getrandom 0.2.15", "once_cell", "tiny-keccak", ] @@ -1299,7 +1422,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array 0.14.7", - "rand_core", + "rand_core 0.6.4", "subtle", "zeroize", ] @@ -1311,7 +1434,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array 0.14.7", - "rand_core", + "rand_core 0.6.4", "typenum", ] @@ -1512,7 +1635,7 @@ checksum = "1ab8a4ea925ce79678034870834602a2980f4b88c09e97feb266496dbb4493d2" dependencies = [ "async-trait", "deadpool 0.12.1", - "getrandom", + "getrandom 0.2.15", "tokio", "tokio-postgres", "tracing", @@ -1860,7 +1983,7 @@ checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ "curve25519-dalek", "ed25519", - "rand_core", + "rand_core 0.6.4", "serde", "sha2 0.10.8", "subtle", @@ -1908,7 +2031,7 @@ dependencies = [ "hkdf", "pem-rfc7468", "pkcs8", - "rand_core", + "rand_core 0.6.4", "sec1", "subtle", "zeroize", @@ -1968,6 +2091,43 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener 5.3.1", + "pin-project-lite", +] + [[package]] name = "event_macro" version = "0.1.0" @@ -2018,13 +2178,28 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95765f67b4b18863968b4a1bd5bb576f732b29a4a28c7cd84c09fa3e2875f33c" +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fastrand" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" + [[package]] name = "ff" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" dependencies = [ - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -2116,7 +2291,7 @@ dependencies = [ "foundationdb-sys", "futures", "memchr", - "rand", + "rand 0.8.5", "serde", "serde_bytes", "serde_json", @@ -2265,6 +2440,21 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + [[package]] name = "futures-macro" version = "0.3.31" @@ -2345,6 +2535,17 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -2354,7 +2555,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -2387,7 +2588,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -2511,7 +2712,7 @@ dependencies = [ "hickory-proto", "once_cell", "radix_trie", - "rand", + "rand 0.8.5", "rustls 0.21.12", "thiserror", "tokio", @@ -2537,7 +2738,7 @@ dependencies = [ "idna 0.4.0", "ipnet", "once_cell", - "rand", + "rand 0.8.5", "ring 0.16.20", "rustls 0.21.12", "rustls-pemfile 1.0.4", @@ -2562,7 +2763,7 @@ dependencies = [ "lru-cache", "once_cell", "parking_lot", - "rand", + "rand 0.8.5", "resolv-conf", "rustls 0.21.12", "smallvec", @@ -2687,6 +2888,26 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-types" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9b187a72d63adbfba487f48095306ac823049cb504ee195541e91c7775f5ad" +dependencies = [ + "anyhow", + "async-channel", + "base64 0.13.1", + "futures-lite", + "infer 0.2.3", + "pin-project-lite", + "rand 0.7.3", + "serde", + "serde_json", + "serde_qs", + "serde_urlencoded", + "url", +] + [[package]] name = "httparse" version = "1.9.5" @@ -2795,6 +3016,22 @@ dependencies = [ "tower-service", ] +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper 1.5.0", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.9" @@ -3024,7 +3261,7 @@ dependencies = [ "md5", "nlp", "parking_lot", - "rand", + "rand 0.8.5", "rustls 0.23.14", "rustls-pemfile 2.2.0", "store", @@ -3080,6 +3317,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "infer" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e9829a50b42bb782c1df523f78d332fe371b10c661e78b7a3c34b0198e9fac" + [[package]] name = "infer" version = "0.16.0" @@ -3255,7 +3498,7 @@ dependencies = [ "p256", "pkcs8", "quick-xml 0.36.2", - "rand", + "rand 0.8.5", "rasn", "rasn-cms", "rasn-pkix", @@ -3521,6 +3764,12 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "litemap" version = "0.7.3" @@ -3607,7 +3856,7 @@ dependencies = [ "mail-parser", "parking_lot", "quick-xml 0.36.2", - "rand", + "rand 0.8.5", "ring 0.17.8", "rsa", "rustls-pemfile 2.2.0", @@ -3823,7 +4072,7 @@ checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.48.0", ] @@ -3866,7 +4115,7 @@ dependencies = [ "pem", "percent-encoding", "pin-project", - "rand", + "rand 0.8.5", "rustls 0.22.4", "rustls-pemfile 2.2.0", "serde", @@ -3905,7 +4154,7 @@ dependencies = [ "mysql-common-derive", "num-bigint", "num-traits", - "rand", + "rand 0.8.5", "regex", "rust_decimal", "saturating", @@ -3921,6 +4170,23 @@ dependencies = [ "zstd", ] +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "new_debug_unreachable" version = "1.0.6" @@ -4009,7 +4275,7 @@ dependencies = [ "num-integer", "num-iter", "num-traits", - "rand", + "rand 0.8.5", "smallvec", "zeroize", ] @@ -4228,7 +4494,7 @@ dependencies = [ "once_cell", "opentelemetry", "percent-encoding", - "rand", + "rand 0.8.5", "serde_json", "thiserror", ] @@ -4277,10 +4543,16 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "rand_core", + "rand_core 0.6.4", "sha2 0.10.8", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.3" @@ -4311,7 +4583,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" dependencies = [ "base64ct", - "rand_core", + "rand_core 0.6.4", "subtle", ] @@ -4395,7 +4667,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" dependencies = [ "phf_shared 0.11.2", - "rand", + "rand 0.8.5", ] [[package]] @@ -4538,7 +4810,7 @@ dependencies = [ "hmac 0.12.1", "md-5 0.10.6", "memchr", - "rand", + "rand 0.8.5", "sha2 0.10.8", "stringprep", ] @@ -4761,7 +5033,7 @@ dependencies = [ "byteorder", "hmac 0.10.1", "md-5 0.9.1", - "rand", + "rand 0.8.5", "sha-1", "sha2 0.9.9", ] @@ -4772,6 +5044,16 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +[[package]] +name = "quick-xml" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "quick-xml" version = "0.32.0" @@ -4816,7 +5098,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" dependencies = [ "bytes", - "rand", + "rand 0.8.5", "ring 0.17.8", "rustc-hash 2.0.0", "rustls 0.23.14", @@ -4875,6 +5157,19 @@ dependencies = [ "nibble_vec", ] +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + [[package]] name = "rand" version = "0.8.5" @@ -4882,8 +5177,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", ] [[package]] @@ -4893,7 +5198,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", ] [[package]] @@ -4902,7 +5216,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", ] [[package]] @@ -5024,7 +5347,7 @@ dependencies = [ "num-bigint", "percent-encoding", "pin-project-lite", - "rand", + "rand 0.8.5", "rustls 0.23.14", "rustls-native-certs 0.7.3", "rustls-pemfile 2.2.0", @@ -5054,7 +5377,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom", + "getrandom 0.2.15", "libredox", "thiserror", ] @@ -5158,12 +5481,14 @@ dependencies = [ "http-body-util", "hyper 1.5.0", "hyper-rustls 0.27.3", + "hyper-tls", "hyper-util", "ipnet", "js-sys", "log", "mime", "mime_guess", + "native-tls", "once_cell", "percent-encoding", "pin-project-lite", @@ -5176,6 +5501,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper 1.0.1", "tokio", + "tokio-native-tls", "tokio-rustls 0.26.0", "tokio-util", "tower-service", @@ -5240,7 +5566,7 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.15", "libc", "spin 0.9.8", "untrusted 0.9.0", @@ -5329,7 +5655,7 @@ dependencies = [ "num-traits", "pkcs1", "pkcs8", - "rand_core", + "rand_core 0.6.4", "signature", "spki", "subtle", @@ -5434,7 +5760,7 @@ dependencies = [ "borsh", "bytes", "num-traits", - "rand", + "rand 0.8.5", "rkyv", "serde", "serde_json", @@ -5485,6 +5811,19 @@ dependencies = [ "nom", ] +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "rustls" version = "0.21.12" @@ -5783,7 +6122,7 @@ dependencies = [ "ecdsa", "ed25519", "ed25519-dalek", - "getrandom", + "getrandom 0.2.15", "idea", "idna 1.0.2", "lalrpop", @@ -5797,8 +6136,8 @@ dependencies = [ "p256", "p384", "p521", - "rand", - "rand_core", + "rand 0.8.5", + "rand_core 0.6.4", "regex", "regex-syntax", "ripemd", @@ -5854,6 +6193,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_qs" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6" +dependencies = [ + "percent-encoding", + "serde", + "thiserror", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -6015,7 +6365,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ "digest 0.10.7", - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -6081,7 +6431,7 @@ dependencies = [ "nlp", "num_cpus", "parking_lot", - "rand", + "rand 0.8.5", "rayon", "regex", "reqwest 0.12.8", @@ -6190,7 +6540,7 @@ dependencies = [ "num_cpus", "prettytable-rs", "pwhash", - "rand", + "rand 0.8.5", "reqwest 0.12.8", "rpassword", "serde", @@ -6211,6 +6561,9 @@ dependencies = [ "ahash 0.8.11", "arc-swap", "async-trait", + "azure_core", + "azure_storage", + "azure_storage_blobs", "bincode", "bitpacking", "blake3", @@ -6229,10 +6582,11 @@ dependencies = [ "num_cpus", "parking_lot", "r2d2", - "rand", + "rand 0.8.5", "rayon", "redis", "regex", + "reqwest 0.12.8", "ring 0.17.8", "roaring", "rocksdb", @@ -6401,6 +6755,19 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tempfile" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +dependencies = [ + "cfg-if", + "fastrand 2.2.0", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "term" version = "0.7.0" @@ -6502,6 +6869,7 @@ checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", + "js-sys", "num-conv", "powerfmt", "serde", @@ -6599,6 +6967,16 @@ dependencies = [ "syn 2.0.79", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-postgres" version = "0.7.12" @@ -6618,7 +6996,7 @@ dependencies = [ "pin-project-lite", "postgres-protocol", "postgres-types", - "rand", + "rand 0.8.5", "socket2", "tokio", "tokio-util", @@ -6782,7 +7160,7 @@ dependencies = [ "indexmap 1.9.3", "pin-project", "pin-project-lite", - "rand", + "rand 0.8.5", "slab", "tokio", "tokio-util", @@ -6896,7 +7274,7 @@ dependencies = [ "http 1.1.0", "httparse", "log", - "rand", + "rand 0.8.5", "rustls 0.22.4", "rustls-pki-types", "sha1", @@ -6917,7 +7295,7 @@ dependencies = [ "http 1.1.0", "httparse", "log", - "rand", + "rand 0.8.5", "sha1", "thiserror", "utf-8", @@ -6939,7 +7317,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", - "rand", + "rand 0.8.5", "static_assertions", ] @@ -7059,6 +7437,7 @@ dependencies = [ "form_urlencoded", "idna 0.5.0", "percent-encoding", + "serde", ] [[package]] @@ -7109,7 +7488,7 @@ dependencies = [ "parking_lot", "pem", "privdrop", - "rand", + "rand 0.8.5", "rcgen 0.13.1", "regex", "reqwest 0.12.8", @@ -7133,7 +7512,8 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" dependencies = [ - "getrandom", + "getrandom 0.2.15", + "serde", ] [[package]] @@ -7154,6 +7534,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +[[package]] +name = "waker-fn" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" + [[package]] name = "walkdir" version = "2.5.0" @@ -7173,6 +7559,12 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -7602,7 +7994,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ "curve25519-dalek", - "rand_core", + "rand_core 0.6.4", "zeroize", ] @@ -7789,7 +8181,7 @@ dependencies = [ "lzma-rs", "memchr", "pbkdf2", - "rand", + "rand 0.8.5", "sha1", "thiserror", "time", diff --git a/crates/main/Cargo.toml b/crates/main/Cargo.toml index 3056a5b3..c66ba4f6 100644 --- a/crates/main/Cargo.toml +++ b/crates/main/Cargo.toml @@ -34,7 +34,7 @@ tokio = { version = "1.23", features = ["full"] } jemallocator = "0.5.0" [features] -default = ["sqlite", "postgres", "mysql", "rocks", "elastic", "s3", "redis", "enterprise"] +default = ["sqlite", "postgres", "mysql", "rocks", "elastic", "s3", "redis", "azure", "enterprise"] #default = ["sqlite", "postgres", "mysql", "rocks", "elastic", "s3", "redis", "foundationdb", "enterprise"] sqlite = ["store/sqlite"] foundationdb = ["store/foundation", "common/foundation"] @@ -44,4 +44,5 @@ rocks = ["store/rocks"] elastic = ["store/elastic"] s3 = ["store/s3"] redis = ["store/redis"] +azure = ["store/azure"] enterprise = ["jmap/enterprise", "common/enterprise", "store/enterprise", "managesieve/enterprise", "directory/enterprise"] diff --git a/crates/store/Cargo.toml b/crates/store/Cargo.toml index 5fb55576..287e9ce2 100644 --- a/crates/store/Cargo.toml +++ b/crates/store/Cargo.toml @@ -12,6 +12,10 @@ rocksdb = { version = "0.22", optional = true, features = ["multi-threaded-cf"] foundationdb = { version = "0.9.0", features = ["embedded-fdb-include", "fdb-7_1"], optional = true } rusqlite = { version = "0.32", features = ["bundled"], optional = true } rust-s3 = { version = "=0.35.0-alpha.2", default-features = false, features = ["tokio-rustls-tls", "no-verify-ssl"], optional = true } +azure_core = { version = "0.21.0", optional = true } +azure_storage = { version = "0.21.0", optional = true } +azure_storage_blobs = { version = "0.21.0", optional = true } +reqwest = { version = "0.12.0", default-features = false, optional = true } tokio = { version = "1.23", features = ["sync", "fs", "io-util"] } r2d2 = { version = "0.8.10", optional = true } futures = { version = "0.3", optional = true } @@ -56,6 +60,7 @@ postgres = ["tokio-postgres", "deadpool-postgres", "tokio-rustls", "rustls", "ri elastic = ["elasticsearch", "serde_json"] mysql = ["mysql_async", "futures"] s3 = ["rust-s3"] +azure = ["azure_core", "azure_storage", "azure_storage_blobs", "reqwest"] foundation = ["foundationdb", "futures"] fdb-chunked-bm = [] redis = ["dep:redis", "deadpool"] diff --git a/crates/store/src/backend/azure/mod.rs b/crates/store/src/backend/azure/mod.rs new file mode 100644 index 00000000..b18a49f5 --- /dev/null +++ b/crates/store/src/backend/azure/mod.rs @@ -0,0 +1,204 @@ +/* + * SPDX-FileCopyrightText: 2020 Stalwart Labs Ltd + * + * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL + */ + +use std::{fmt::Display, io::Write, ops::Range, time::Duration}; + +use azure_core::error::ErrorKind; +use azure_core::{ExponentialRetryOptions, RetryOptions, StatusCode, TransportOptions}; +use azure_storage::StorageCredentials; +use azure_storage_blobs::prelude::{ClientBuilder, ContainerClient}; +use futures::stream::StreamExt; +use std::sync::Arc; +use utils::{ + codec::base32_custom::Base32Writer, + config::{utils::AsKey, Config}, +}; + +pub struct AzureStore { + client: ContainerClient, + prefix: Option, +} + +impl AzureStore { + pub async fn open(config: &mut Config, prefix: impl AsKey) -> Option { + let prefix = prefix.as_key(); + + let storage_account = config + .value_require((&prefix, "storage-account"))? + .to_string(); + let container = config.value_require((&prefix, "container"))?.to_string(); + + let credentials = match ( + config.value((&prefix, "access-key")), + config.value((&prefix, "sas-token")), + ) { + (Some(access_key), None) => { + StorageCredentials::access_key(storage_account.clone(), access_key.to_string()) + } + (None, Some(sas_token)) => match StorageCredentials::sas_token(sas_token) { + Ok(cred) => cred, + Err(err) => { + config.new_build_error( + prefix.as_str(), + format!("Failed to create credentials: {err:?}"), + ); + return None; + } + }, + _ => { + config.new_build_error( + prefix.as_str(), + format!("Failed to create credentials: exactly one of 'access-key' and 'sas-token' must be specified")); + return None; + } + }; + + let timeout = config + .property_or_default::((&prefix, "timeout"), "30s") + .unwrap_or_else(|| Duration::from_secs(30)); + let transport = match reqwest::Client::builder().timeout(timeout).build() { + Ok(client) => Arc::new(client), + Err(err) => { + config.new_build_error( + prefix.as_str(), + format!("Failed to create HTTP client: {err:?}"), + ); + return None; + } + }; + + // Take the configured number of retries and multiply by 2. This is intended to match the + // precedent set by the S3 back end, where we do the indicated number of retries, + // ourselves, but internally the rust-s3 crate is also retrying each of our requests up to + // one additional time, itself. So our retries, and the S3 backend's retries, are + // comparable to each other. + let max_retries: u32 = config + .property_or_default((&prefix, "max-retries"), "3") + .unwrap_or(3) + * 2; + + Some(AzureStore { + client: ClientBuilder::new(storage_account, credentials) + .transport(TransportOptions::new(transport)) + .retry(RetryOptions::exponential( + ExponentialRetryOptions::default().max_retries(max_retries), + )) + .container_client(container), + prefix: config.value((&prefix, "key-prefix")).map(|s| s.to_string()), + }) + } + + pub(crate) async fn get_blob( + &self, + key: &[u8], + range: Range, + ) -> trc::Result>> { + let blob_client = self.client.blob_client(self.build_key(key)); + + let mut stream = blob_client.get(); + let mut buf = if range.end == usize::MAX { + // Let's turn this into a proper RangeFrom. + stream = stream.range(range.start..); + // We don't know how big to expect the result to be. + Vec::new() + } else { + stream = stream.range(range.clone()); + Vec::with_capacity(range.end - range.start) + }; + let mut stream = stream.into_stream(); + + while let Some(response) = stream.next().await { + let err = match response { + Ok(chunks) => { + let mut chunks = chunks.data; + let mut err = None; + while let Some(chunk) = chunks.next().await { + match chunk { + Ok(ref data) => { + buf.extend(data); + } + Err(e) => { + err = Some(e); + } + } + } + err + } + Err(e) => Some(e), + }; + + if let Some(e) = err { + return if matches!( + e.kind(), + ErrorKind::HttpResponse { + status: StatusCode::NotFound, + .. + } + ) { + Ok(None) + } else { + Err(trc::StoreEvent::S3Error.reason(e)) + }; + } + } + + Ok(Some(buf)) + } + + pub(crate) async fn put_blob(&self, key: &[u8], data: &[u8]) -> trc::Result<()> { + let blob_client = self.client.blob_client(self.build_key(key)); + + // We unfortunately have to make a copy of `data`. This is because the Azure SDK wants to + // coerce the body into a value of type azure_core::Body, which doesn't have a lifetime + // parameter and so cannot hold any non-static references (directly or indirectly). + let data = data.to_vec(); + + blob_client + .put_block_blob(data) + .into_future() + .await + .map_err(into_error)?; + + Ok(()) + } + + pub(crate) async fn delete_blob(&self, key: &[u8]) -> trc::Result { + let blob_client = self.client.blob_client(self.build_key(key)); + + if let Err(e) = blob_client.delete().into_future().await { + if matches!( + e.kind(), + ErrorKind::HttpResponse { + status: StatusCode::NotFound, + .. + } + ) { + Ok(false) + } else { + Err(trc::StoreEvent::S3Error.reason(e)) + } + } else { + Ok(true) + } + } + + fn build_key(&self, key: &[u8]) -> String { + if let Some(prefix) = &self.prefix { + let mut writer = + Base32Writer::with_raw_capacity(prefix.len() + ((key.len() + 3) / 4 * 5)); + writer.push_string(prefix); + writer.write_all(key).unwrap(); + writer.finalize() + } else { + Base32Writer::from_bytes(key).finalize() + } + } +} + +#[inline(always)] +fn into_error(err: impl Display) -> trc::Error { + trc::StoreEvent::S3Error.reason(err) +} diff --git a/crates/store/src/backend/composite/distributed_blob.rs b/crates/store/src/backend/composite/distributed_blob.rs index a51c93ef..4001a832 100644 --- a/crates/store/src/backend/composite/distributed_blob.rs +++ b/crates/store/src/backend/composite/distributed_blob.rs @@ -81,6 +81,8 @@ impl DistributedBlob { BlobBackend::Fs(store) => store.get_blob(key, read_range).await, #[cfg(feature = "s3")] BlobBackend::S3(store) => store.get_blob(key, read_range).await, + #[cfg(feature = "azure")] + BlobBackend::Azure(store) => store.get_blob(key, read_range).await, BlobBackend::Composite(_) => unimplemented!(), } }) @@ -111,6 +113,8 @@ impl DistributedBlob { BlobBackend::Fs(store) => store.put_blob(key, data).await, #[cfg(feature = "s3")] BlobBackend::S3(store) => store.put_blob(key, data).await, + #[cfg(feature = "azure")] + BlobBackend::Azure(store) => store.put_blob(key, data).await, BlobBackend::Composite(_) => unimplemented!(), } }) @@ -141,6 +145,8 @@ impl DistributedBlob { BlobBackend::Fs(store) => store.delete_blob(key).await, #[cfg(feature = "s3")] BlobBackend::S3(store) => store.delete_blob(key).await, + #[cfg(feature = "azure")] + BlobBackend::Azure(store) => store.delete_blob(key).await, BlobBackend::Composite(_) => unimplemented!(), } }) diff --git a/crates/store/src/backend/mod.rs b/crates/store/src/backend/mod.rs index a4f780a5..843b1807 100644 --- a/crates/store/src/backend/mod.rs +++ b/crates/store/src/backend/mod.rs @@ -24,6 +24,8 @@ pub mod rocksdb; pub mod s3; #[cfg(feature = "sqlite")] pub mod sqlite; +#[cfg(feature = "azure")] +pub mod azure; pub const MAX_TOKEN_LENGTH: usize = (u8::MAX >> 1) as usize; pub const MAX_TOKEN_MASK: usize = MAX_TOKEN_LENGTH - 1; diff --git a/crates/store/src/config.rs b/crates/store/src/config.rs index 5160bb59..c32ce314 100644 --- a/crates/store/src/config.rs +++ b/crates/store/src/config.rs @@ -38,6 +38,9 @@ use crate::backend::elastic::ElasticSearchStore; #[cfg(feature = "redis")] use crate::backend::redis::RedisStore; +#[cfg(feature = "azure")] +use crate::backend::azure::AzureStore; + impl Stores { pub async fn parse_all(config: &mut Config) -> Self { let mut stores = Self::parse(config).await; @@ -216,6 +219,13 @@ impl Stores { "sql-read-replica" | "distributed-blob" => { composite_stores.push((store_id, protocol)); } + #[cfg(feature = "azure")] + "azure" => { + if let Some(db) = AzureStore::open(config, prefix).await.map(BlobStore::from) { + self.blob_stores + .insert(store_id, db.with_compression(compression_algo)); + } + } unknown => { config.new_parse_warning( ("store", id, "type"), diff --git a/crates/store/src/dispatch/blob.rs b/crates/store/src/dispatch/blob.rs index 0299738d..965f8b1c 100644 --- a/crates/store/src/dispatch/blob.rs +++ b/crates/store/src/dispatch/blob.rs @@ -37,6 +37,8 @@ impl BlobStore { BlobBackend::Fs(store) => store.get_blob(key, read_range).await, #[cfg(feature = "s3")] BlobBackend::S3(store) => store.get_blob(key, read_range).await, + #[cfg(feature = "azure")] + BlobBackend::Azure(store) => store.get_blob(key, read_range).await, #[cfg(feature = "enterprise")] BlobBackend::Composite(store) => store.get_blob(key, read_range).await, }; @@ -117,6 +119,8 @@ impl BlobStore { BlobBackend::Fs(store) => store.put_blob(key, data.as_ref()).await, #[cfg(feature = "s3")] BlobBackend::S3(store) => store.put_blob(key, data.as_ref()).await, + #[cfg(feature = "azure")] + BlobBackend::Azure(store) => store.put_blob(key, data.as_ref()).await, #[cfg(feature = "enterprise")] BlobBackend::Composite(store) => store.put_blob(key, data.as_ref()).await, } @@ -153,6 +157,8 @@ impl BlobStore { BlobBackend::Fs(store) => store.delete_blob(key).await, #[cfg(feature = "s3")] BlobBackend::S3(store) => store.delete_blob(key).await, + #[cfg(feature = "azure")] + BlobBackend::Azure(store) => store.delete_blob(key).await, #[cfg(feature = "enterprise")] BlobBackend::Composite(store) => store.delete_blob(key).await, } diff --git a/crates/store/src/lib.rs b/crates/store/src/lib.rs index 21a03583..08585f17 100644 --- a/crates/store/src/lib.rs +++ b/crates/store/src/lib.rs @@ -46,6 +46,9 @@ use backend::elastic::ElasticSearchStore; #[cfg(feature = "redis")] use backend::redis::RedisStore; +#[cfg(feature = "azure")] +use backend::azure::AzureStore; + pub trait Deserialize: Sized + Sync + Send { fn deserialize(bytes: &[u8]) -> trc::Result; } @@ -208,6 +211,8 @@ pub enum BlobBackend { Fs(Arc), #[cfg(feature = "s3")] S3(Arc), + #[cfg(feature = "azure")] + Azure(Arc), #[cfg(feature = "enterprise")] Composite(Arc), } @@ -288,6 +293,16 @@ impl From for BlobStore { } } +#[cfg(feature = "azure")] +impl From for BlobStore { + fn from(store: AzureStore) -> Self { + BlobStore { + backend: BlobBackend::Azure(Arc::new(store)), + compression: CompressionAlgo::None, + } + } +} + #[cfg(feature = "elastic")] impl From for FtsStore { fn from(store: ElasticSearchStore) -> Self { diff --git a/crates/trc/src/event/description.rs b/crates/trc/src/event/description.rs index a74e194e..91bd4f0a 100644 --- a/crates/trc/src/event/description.rs +++ b/crates/trc/src/event/description.rs @@ -1540,6 +1540,7 @@ impl StoreEvent { StoreEvent::ElasticsearchError => "ElasticSearch error", StoreEvent::RedisError => "Redis error", StoreEvent::S3Error => "S3 error", + StoreEvent::AzureError => "Azure error", StoreEvent::FilesystemError => "Filesystem error", StoreEvent::PoolError => "Connection pool error", StoreEvent::DataCorruption => "Data corruption detected", @@ -1574,6 +1575,7 @@ impl StoreEvent { StoreEvent::ElasticsearchError => "An ElasticSearch error occurred", StoreEvent::RedisError => "A Redis error occurred", StoreEvent::S3Error => "An S3 error occurred", + StoreEvent::AzureError => "An Azure error occurred", StoreEvent::FilesystemError => "A filesystem error occurred", StoreEvent::PoolError => "A connection pool error occurred", StoreEvent::DataCorruption => "Data corruption was detected", diff --git a/crates/trc/src/event/level.rs b/crates/trc/src/event/level.rs index 23dbef61..d40b5649 100644 --- a/crates/trc/src/event/level.rs +++ b/crates/trc/src/event/level.rs @@ -31,6 +31,7 @@ impl EventType { | StoreEvent::ElasticsearchError | StoreEvent::RedisError | StoreEvent::S3Error + | StoreEvent::AzureError | StoreEvent::FilesystemError | StoreEvent::PoolError | StoreEvent::DataCorruption diff --git a/crates/trc/src/event/mod.rs b/crates/trc/src/event/mod.rs index d614e274..61a1a0d4 100644 --- a/crates/trc/src/event/mod.rs +++ b/crates/trc/src/event/mod.rs @@ -307,6 +307,7 @@ impl StoreEvent { Self::ElasticsearchError => "ElasticSearch error", Self::RedisError => "Redis error", Self::S3Error => "S3 error", + Self::AzureError => "Azure error", Self::FilesystemError => "Filesystem error", Self::PoolError => "Connection pool error", Self::DataCorruption => "Data corruption", diff --git a/crates/trc/src/ipc/metrics.rs b/crates/trc/src/ipc/metrics.rs index d081e54e..d8e92b19 100644 --- a/crates/trc/src/ipc/metrics.rs +++ b/crates/trc/src/ipc/metrics.rs @@ -440,6 +440,7 @@ impl EventType { | StoreEvent::ElasticsearchError | StoreEvent::RedisError | StoreEvent::S3Error + | StoreEvent::AzureError | StoreEvent::FilesystemError | StoreEvent::PoolError | StoreEvent::DataCorruption diff --git a/crates/trc/src/lib.rs b/crates/trc/src/lib.rs index 39f35fd4..62c809b8 100644 --- a/crates/trc/src/lib.rs +++ b/crates/trc/src/lib.rs @@ -828,6 +828,7 @@ pub enum StoreEvent { ElasticsearchError, RedisError, S3Error, + AzureError, FilesystemError, PoolError, DataCorruption, diff --git a/crates/trc/src/serializers/binary.rs b/crates/trc/src/serializers/binary.rs index f3776810..7c635648 100644 --- a/crates/trc/src/serializers/binary.rs +++ b/crates/trc/src/serializers/binary.rs @@ -864,6 +864,7 @@ impl EventType { EventType::Ai(AiEvent::LlmResponse) => 556, EventType::Ai(AiEvent::ApiError) => 557, EventType::Security(SecurityEvent::ScanBan) => 558, + EventType::Store(StoreEvent::AzureError) => 559, } } @@ -1468,6 +1469,7 @@ impl EventType { 556 => Some(EventType::Ai(AiEvent::LlmResponse)), 557 => Some(EventType::Ai(AiEvent::ApiError)), 558 => Some(EventType::Security(SecurityEvent::ScanBan)), + 559 => Some(EventType::Store(StoreEvent::AzureError)), _ => None, } } diff --git a/tests/Cargo.toml b/tests/Cargo.toml index a66d9ced..8f5137ec 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" resolver = "2" [features] -default = ["sqlite", "postgres", "mysql", "rocks", "elastic", "s3", "redis", "foundationdb"] +default = ["sqlite", "postgres", "mysql", "rocks", "elastic", "s3", "redis", "azure", "foundationdb"] #default = ["sqlite", "postgres", "mysql", "rocks", "elastic", "s3", "redis", "foundationdb"] sqlite = ["store/sqlite"] foundationdb = ["store/foundation", "common/foundation"] @@ -15,6 +15,7 @@ rocks = ["store/rocks"] elastic = ["store/elastic"] s3 = ["store/s3"] redis = ["store/redis"] +azure = ["store/azure"] [dev-dependencies] store = { path = "../crates/store", features = ["test_mode", "enterprise"] }