diff --git a/Cargo.lock b/Cargo.lock index 72397935..65fb5513 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,9 +21,9 @@ dependencies = [ [[package]] name = "actix-http" -version = "3.1.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd2e9f6794b5826aff6df65e3a0d0127b271d1c03629c774238f3582e903d4e4" +checksum = "6f9ffb6db08c1c3a1f4aef540f1a63193adc73c4fbd40b75a95fc8c5258f6e51" dependencies = [ "actix-codec", "actix-rt", @@ -346,9 +346,9 @@ checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" [[package]] name = "bytes" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +checksum = "f0b3de4a0c5e67e16066a0715723abd91edc2f9001d09c46e1dca929351e130e" [[package]] name = "bytestring" @@ -361,12 +361,9 @@ dependencies = [ [[package]] name = "cast" -version = "0.2.7" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a" -dependencies = [ - "rustc_version", -] +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" @@ -390,10 +387,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "bitflags", - "textwrap", + "textwrap 0.11.0", "unicode-width", ] +[[package]] +name = "clap" +version = "3.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44bbe24bbd31a185bc2c4f7c2abe80bea13a20d57ee4e55be70ac512bdc76417" +dependencies = [ + "atty", + "bitflags", + "clap_lex", + "indexmap", + "strsim", + "termcolor", + "textwrap 0.15.0", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "combine" version = "4.6.4" @@ -461,13 +482,13 @@ dependencies = [ [[package]] name = "criterion" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10" +checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f" dependencies = [ "atty", "cast", - "clap", + "clap 2.34.0", "criterion-plot", "csv", "itertools", @@ -487,9 +508,9 @@ dependencies = [ [[package]] name = "criterion-plot" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00996de9f2f7559f7f4dc286073197f83e92256a59ed395f9aac01fe717da57" +checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" dependencies = [ "cast", "itertools", @@ -497,9 +518,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" dependencies = [ "cfg-if", "crossbeam-utils", @@ -507,9 +528,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" dependencies = [ "cfg-if", "crossbeam-epoch", @@ -518,9 +539,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d" +checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1" dependencies = [ "autocfg", "cfg-if", @@ -532,9 +553,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83" +checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" dependencies = [ "cfg-if", "once_cell", @@ -542,9 +563,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", @@ -603,9 +624,9 @@ checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" [[package]] name = "either" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" [[package]] name = "encoding_rs" @@ -631,23 +652,23 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" dependencies = [ "instant", ] [[package]] name = "filetime" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0408e2626025178a6a7f7ffc05a25bc47103229f19c113755de7bf63816290c" +checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c" dependencies = [ "cfg-if", "libc", "redox_syscall", - "winapi", + "windows-sys", ] [[package]] @@ -658,9 +679,9 @@ checksum = "2c5f6c2c942da57e2aaaa84b8a521489486f14e75e7fa91dab70aba913975f98" [[package]] name = "fixedbitset" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "279fb028e20b3c4c320317955b77c5e0c9701f05a1d309905d6fc702cdc5053e" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" @@ -849,9 +870,9 @@ checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "hashbrown" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" @@ -919,9 +940,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.19" +version = "0.14.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f" +checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" dependencies = [ "bytes", "futures-channel", @@ -1069,9 +1090,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.58" +version = "0.3.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" +checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2" dependencies = [ "wasm-bindgen", ] @@ -1132,7 +1153,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "serial_test 0.5.1", + "serial_test", "thiserror", "tokio", "ttl_cache", @@ -1144,6 +1165,7 @@ version = "0.5.1" dependencies = [ "actix-rt", "actix-web", + "clap 3.2.15", "env_logger", "limitador", "log", @@ -1153,7 +1175,6 @@ dependencies = [ "prost-types", "serde", "serde_yaml", - "serial_test 0.7.0", "thiserror", "tokio", "tonic", @@ -1325,9 +1346,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" [[package]] name = "oorandom" @@ -1337,9 +1358,9 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "openssl" -version = "0.10.40" +version = "0.10.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb81a6430ac911acb25fe5ac8f1d2af1b4ea8a4fdfda0f1ee4292af2e2d8eb0e" +checksum = "618febf65336490dfcf20b73f885f5651a0c89c64c2d4a8c3662585a70bf5bd0" dependencies = [ "bitflags", "cfg-if", @@ -1369,9 +1390,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.74" +version = "0.9.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835363342df5fba8354c5b453325b110ffd54044e588c539cf2f20a8014e4cb1" +checksum = "e5f9bd0c2710541a3cda73d6f9ac4f1b240de4ae261065d309dbe73d9dceb42f" dependencies = [ "autocfg", "cc", @@ -1380,11 +1401,17 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "os_str_bytes" +version = "6.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "648001efe5d5c0102d8cea768e348da85d90af8ba91f0bea908f157951493cd4" + [[package]] name = "paperclip" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29edecb9b5de19fcdba789406bc39144de34c100e59151095aac1b97d2b4a25e" +checksum = "f399678683ec199ddca1dd54db957dd158dedb5fc90826eb2a7e6c0800c3a868" dependencies = [ "anyhow", "itertools", @@ -1393,7 +1420,7 @@ dependencies = [ "paperclip-core", "paperclip-macros", "parking_lot 0.12.1", - "semver 0.11.0", + "semver", "serde", "serde_derive", "serde_json", @@ -1404,9 +1431,9 @@ dependencies = [ [[package]] name = "paperclip-actix" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6acb344bfe7c8be8e140ad01dc2a8bc1a1b829014a29291174be64bc45f06e" +checksum = "29880bc57ef516c272d6fdd215ecaf96375d9a5dbac5412d849b9f9afd0d7298" dependencies = [ "actix-service", "actix-web", @@ -1420,9 +1447,9 @@ dependencies = [ [[package]] name = "paperclip-core" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ba1b92909712a1186613a6ba6e1c48c59baba59672cff2b242e8e03e90101f" +checksum = "0bee516533b655ba63e41e788b49a2beb1139e1eebafb143e7cb56b8cabb5da1" dependencies = [ "actix-web", "mime", @@ -1439,9 +1466,9 @@ dependencies = [ [[package]] name = "paperclip-macros" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "992e1f19f6a449c41e166a2336c86912eedc17f5167886ef09d601607d9be1f1" +checksum = "e89990be67318e3da29c92adb3377e0251a8eee10b4f91ff349cbf2da945e9d1" dependencies = [ "heck 0.4.0", "http", @@ -1515,15 +1542,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" -[[package]] -name = "pest" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" -dependencies = [ - "ucd-trie", -] - [[package]] name = "petgraph" version = "0.6.2" @@ -1536,18 +1554,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" +checksum = "78203e83c48cffbe01e4a2d35d566ca4de445d79a85372fc64e378bfc812a260" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" +checksum = "710faf75e1b33345361201d36d04e98ac1ed8909151a017ed384700836104c74" dependencies = [ "proc-macro2", "quote", @@ -1574,9 +1592,9 @@ checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" [[package]] name = "plotters" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a" +checksum = "9428003b84df1496fb9d6eeee9c5f8145cb41ca375eb0dad204328888832811f" dependencies = [ "num-traits", "plotters-backend", @@ -1587,15 +1605,15 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.2" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c" +checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" [[package]] name = "plotters-svg" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9" +checksum = "e0918736323d1baff32ee0eade54984f6f201ad7e97d5cfb5d6ab4a358529615" dependencies = [ "plotters-backend", ] @@ -1632,9 +1650,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.40" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +checksum = "c278e965f1d8cf32d6e0e96de3d3e79712178ae67986d9cf9151f51e95aac89b" dependencies = [ "unicode-ident", ] @@ -1811,18 +1829,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.13" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.5.6" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" dependencies = [ "aho-corasick", "memchr", @@ -1837,9 +1855,9 @@ checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" [[package]] name = "regex-syntax" -version = "0.6.26" +version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "remove_dir_all" @@ -1893,14 +1911,14 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.10", + "semver", ] [[package]] name = "rustversion" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0a5f7c728f5d284929a1cccb5bc19884422bfe6ef4d6c409da2c41838983fcf" +checksum = "24c8ad4f0c00e1eb5bc7614d236a7f1300e3dbd76b68cac8e06fb00b015ad8d8" [[package]] name = "ryu" @@ -1967,33 +1985,15 @@ dependencies = [ [[package]] name = "semver" -version = "0.11.0" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41d061efea015927ac527063765e73601444cdc344ba855bc7bd44578b25e1c" - -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] +checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1" [[package]] name = "serde" -version = "1.0.137" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03" dependencies = [ "serde_derive", ] @@ -2010,9 +2010,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.137" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" +checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da" dependencies = [ "proc-macro2", "quote", @@ -2021,9 +2021,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" +checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" dependencies = [ "itoa 1.0.2", "ryu", @@ -2044,9 +2044,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.8.24" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707d15895415db6628332b737c838b88c598522e4dc70647e59b72312924aebc" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" dependencies = [ "indexmap", "ryu", @@ -2062,19 +2062,7 @@ checksum = "e0bccbcf40c8938196944a3da0e133e031a33f4d6b72db3bda3cc556e361905d" dependencies = [ "lazy_static", "parking_lot 0.11.2", - "serial_test_derive 0.5.1", -] - -[[package]] -name = "serial_test" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19dbfb999a147cedbfe82f042eb9555f5b0fa4ef95ee4570b74349103d9c9f4" -dependencies = [ - "lazy_static", - "log", - "parking_lot 0.12.1", - "serial_test_derive 0.7.0", + "serial_test_derive", ] [[package]] @@ -2088,19 +2076,6 @@ dependencies = [ "syn", ] -[[package]] -name = "serial_test_derive" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb9e2050b2be1d681f8f1c1a528bcfe4e00afa2d8995f713974f5333288659f2" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "rustversion", - "syn", -] - [[package]] name = "sha1" version = "0.6.1" @@ -2138,15 +2113,18 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] [[package]] name = "smallvec" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc88c725d61fc6c3132893370cac4a0200e3fedf5da8331c570664b1987f5ca2" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" [[package]] name = "socket2" @@ -2158,6 +2136,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "strum" version = "0.24.1" @@ -2220,6 +2204,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "textwrap" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" + [[package]] name = "thiserror" version = "1.0.31" @@ -2285,10 +2275,11 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.19.2" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" +checksum = "7a8325f63a7d4774dd041e363b2409ed1c5cbbd0f867795e661df066b2b0a581" dependencies = [ + "autocfg", "bytes", "libc", "memchr", @@ -2463,9 +2454,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c" +checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" dependencies = [ "proc-macro2", "quote", @@ -2512,12 +2503,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "ucd-trie" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" - [[package]] name = "unicode-bidi" version = "0.3.8" @@ -2526,15 +2511,15 @@ checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" [[package]] name = "unicode-ident" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" [[package]] name = "unicode-normalization" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dee68f85cab8cf68dec42158baf3a79a1cdc065a8b103025965d6ccb7f6cbd" +checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6" dependencies = [ "tinyvec", ] @@ -2610,9 +2595,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.81" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" +checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -2620,13 +2605,13 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.81" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" +checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", "quote", "syn", @@ -2635,9 +2620,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.31" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f" +checksum = "fa76fb221a1f8acddf5b54ace85912606980ad661ac7a503b4570ffd3a624dad" dependencies = [ "cfg-if", "js-sys", @@ -2647,9 +2632,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.81" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" +checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2657,9 +2642,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.81" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" +checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da" dependencies = [ "proc-macro2", "quote", @@ -2670,15 +2655,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.81" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" +checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a" [[package]] name = "web-sys" -version = "0.3.58" +version = "0.3.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90" +checksum = "ed055ab27f941423197eb86b2035720b1a3ce40504df082cac2ecc6ed73335a1" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/limitador-server/Cargo.toml b/limitador-server/Cargo.toml index 427c278f..007996f9 100644 --- a/limitador-server/Cargo.toml +++ b/limitador-server/Cargo.toml @@ -30,7 +30,7 @@ actix-rt = "2" paperclip = { version = "0.7", features = ["actix4"] } serde = { version = "1", features = ["derive"] } notify = "5.0.0-pre.15" -serial_test = "0.7.0" +clap = "3.2" [build-dependencies] tonic-build = "0.6" diff --git a/limitador-server/src/config.rs b/limitador-server/src/config.rs index 2e1c4b03..58832dba 100644 --- a/limitador-server/src/config.rs +++ b/limitador-server/src/config.rs @@ -18,31 +18,44 @@ // HTTP_API_HOST: host // just to become HTTP_API_HOST:HTTP_API_PORT as &str // HTTP_API_PORT: port -use std::env; +use log::LevelFilter; +#[derive(Debug)] pub struct Configuration { - pub limits_file: Option, + pub limits_file: String, pub storage: StorageConfiguration, rls_host: String, rls_port: u16, http_host: String, http_port: u16, pub limit_name_in_labels: bool, + pub log_level: Option, } impl Configuration { - pub fn from_env() -> Result { - let rls_port = env::var("ENVOY_RLS_PORT").unwrap_or_else(|_| "8081".to_string()); - let http_port = env::var("HTTP_API_PORT").unwrap_or_else(|_| "8080".to_string()); - Ok(Self { - limits_file: env::var("LIMITS_FILE").ok(), - storage: storage_config_from_env()?, - rls_host: env::var("ENVOY_RLS_HOST").unwrap_or_else(|_| "0.0.0.0".to_string()), - rls_port: rls_port.parse().expect("Expected a port number!"), - http_host: env::var("HTTP_API_HOST").unwrap_or_else(|_| "0.0.0.0".to_string()), - http_port: http_port.parse().expect("Expected a port number!"), - limit_name_in_labels: env_option_is_enabled("LIMIT_NAME_IN_PROMETHEUS_LABELS"), - }) + pub const DEFAULT_RLS_PORT: &'static str = "8081"; + pub const DEFAULT_HTTP_PORT: &'static str = "8080"; + pub const DEFAULT_IP_BIND: &'static str = "0.0.0.0"; + + pub fn with( + storage: StorageConfiguration, + limits_file: String, + rls_host: String, + rls_port: u16, + http_host: String, + http_port: u16, + limit_name_in_labels: bool, + ) -> Self { + Self { + limits_file, + storage, + rls_host, + rls_port, + http_host, + http_port, + limit_name_in_labels, + log_level: None, + } } pub fn rlp_address(&self) -> String { @@ -54,48 +67,19 @@ impl Configuration { } } -fn storage_config_from_env() -> Result { - let redis_url = env::var("REDIS_URL"); - let infinispan_url = env::var("INFINISPAN_URL"); - - match (redis_url, infinispan_url) { - (Ok(_), Ok(_)) => Err(()), - (Ok(url), Err(_)) => Ok(StorageConfiguration::Redis(RedisStorageConfiguration { - url, - cache: if env_option_is_enabled("REDIS_LOCAL_CACHE_ENABLED") { - Some(RedisStorageCacheConfiguration { - flushing_period: env::var("REDIS_LOCAL_CACHE_FLUSHING_PERIOD_MS") - .unwrap_or_else(|_| "1".to_string()) - .parse() - .expect("Expected an i64"), - max_ttl: env::var("REDIS_LOCAL_CACHE_MAX_TTL_CACHED_COUNTERS_MS") - .unwrap_or_else(|_| "5000".to_string()) - .parse() - .expect("Expected an u64"), - ttl_ratio: env::var("REDIS_LOCAL_CACHE_TTL_RATIO_CACHED_COUNTERS") - .unwrap_or_else(|_| "10".to_string()) - .parse() - .expect("Expected an u64"), - }) - } else { - None - }, - })), - (Err(_), Ok(url)) => Ok(StorageConfiguration::Infinispan( - InfinispanStorageConfiguration { - url, - cache: env::var("INFINISPAN_CACHE_NAME").ok(), - consistency: env::var("INFINISPAN_COUNTERS_CONSISTENCY").ok(), - }, - )), - _ => Ok(StorageConfiguration::InMemory), - } -} - -fn env_option_is_enabled(env_name: &str) -> bool { - match env::var(env_name) { - Ok(value) => value == "1", - Err(_) => false, +#[cfg(test)] +impl Default for Configuration { + fn default() -> Self { + Configuration { + limits_file: "".to_string(), + storage: StorageConfiguration::InMemory, + rls_host: "".to_string(), + rls_port: 0, + http_host: "".to_string(), + http_port: 0, + limit_name_in_labels: false, + log_level: None, + } } } @@ -117,6 +101,7 @@ pub struct RedisStorageCacheConfiguration { pub flushing_period: i64, pub max_ttl: u64, pub ttl_ratio: u64, + pub max_counters: usize, } #[derive(PartialEq, Debug)] @@ -125,85 +110,3 @@ pub struct InfinispanStorageConfiguration { pub cache: Option, pub consistency: Option, } - -#[cfg(test)] -mod tests { - use crate::config::{Configuration, StorageConfiguration}; - use serial_test::serial; - use std::env; - - struct VarEnvCleaner { - vars: Vec, - } - - impl VarEnvCleaner { - pub fn new() -> Self { - Self { vars: Vec::new() } - } - - pub fn set_var(&mut self, k: &str, v: &str) { - self.vars.insert(0, k.to_string()); - env::set_var(k, v); - } - } - - impl Drop for VarEnvCleaner { - fn drop(&mut self) { - for var in &self.vars { - env::remove_var(var); - } - } - } - - #[test] - #[serial] - fn test_config_defaults() { - let config = Configuration::from_env().unwrap(); - assert_eq!(config.limits_file, None); - assert_eq!(config.storage, StorageConfiguration::InMemory); - assert_eq!(config.http_address(), "0.0.0.0:8080".to_string()); - assert_eq!(config.rlp_address(), "0.0.0.0:8081".to_string()); - assert_eq!(config.limit_name_in_labels, false); - } - - #[test] - #[serial] - fn test_config_redis_defaults() { - let mut vars = VarEnvCleaner::new(); - let url = "redis://127.0.1.1:7654"; - vars.set_var("REDIS_URL", url); - - let config = Configuration::from_env().unwrap(); - assert_eq!(config.limits_file, None); - if let StorageConfiguration::Redis(ref redis_config) = config.storage { - assert_eq!(redis_config.url, url); - assert_eq!(redis_config.cache, None); - } else { - panic!("Should be a Redis config!"); - } - assert_eq!(config.http_address(), "0.0.0.0:8080".to_string()); - assert_eq!(config.rlp_address(), "0.0.0.0:8081".to_string()); - assert_eq!(config.limit_name_in_labels, false); - } - - #[test] - #[serial] - fn test_config_infinispan_defaults() { - let mut vars = VarEnvCleaner::new(); - - let url = "127.0.2.2:9876"; - vars.set_var("INFINISPAN_URL", url); - let config = Configuration::from_env().unwrap(); - assert_eq!(config.limits_file, None); - if let StorageConfiguration::Infinispan(ref infinispan_config) = config.storage { - assert_eq!(infinispan_config.url, url); - assert_eq!(infinispan_config.cache, None); - assert_eq!(infinispan_config.consistency, None); - } else { - panic!("Should be an Infinispan config!"); - } - assert_eq!(config.http_address(), "0.0.0.0:8080".to_string()); - assert_eq!(config.rlp_address(), "0.0.0.0:8081".to_string()); - assert_eq!(config.limit_name_in_labels, false); - } -} diff --git a/limitador-server/src/envoy_rls/server.rs b/limitador-server/src/envoy_rls/server.rs index e51352e4..8a6aa8de 100644 --- a/limitador-server/src/envoy_rls/server.rs +++ b/limitador-server/src/envoy_rls/server.rs @@ -196,9 +196,7 @@ mod tests { async fn test_returns_ok_when_no_limits_apply() { // No limits saved let rate_limiter = MyRateLimiter::new(Arc::new( - Limiter::new(Configuration::from_env().unwrap()) - .await - .unwrap(), + Limiter::new(Configuration::default()).await.unwrap(), )); let req = RateLimitRequest { @@ -228,9 +226,7 @@ mod tests { #[tokio::test] async fn test_returns_unknown_when_domain_is_empty() { let rate_limiter = MyRateLimiter::new(Arc::new( - Limiter::new(Configuration::from_env().unwrap()) - .await - .unwrap(), + Limiter::new(Configuration::default()).await.unwrap(), )); let req = RateLimitRequest { diff --git a/limitador-server/src/http_api/server.rs b/limitador-server/src/http_api/server.rs index 88345f10..848bc6ad 100644 --- a/limitador-server/src/http_api/server.rs +++ b/limitador-server/src/http_api/server.rs @@ -225,11 +225,8 @@ mod tests { #[actix_rt::test] async fn test_metrics() { - let rate_limiter: Arc = Arc::new( - Limiter::new(Configuration::from_env().unwrap()) - .await - .unwrap(), - ); + let rate_limiter: Arc = + Arc::new(Limiter::new(Configuration::default()).await.unwrap()); let data = web::Data::new(rate_limiter); let app = test::init_service( App::new() @@ -249,9 +246,7 @@ mod tests { #[actix_rt::test] async fn test_limits_read() { - let limiter = Limiter::new(Configuration::from_env().unwrap()) - .await - .unwrap(); + let limiter = Limiter::new(Configuration::default()).await.unwrap(); let namespace = "test_namespace"; let limit = create_test_limit(&limiter, namespace, 10).await; @@ -276,9 +271,7 @@ mod tests { #[actix_rt::test] async fn test_check_and_report() { - let limiter = Limiter::new(Configuration::from_env().unwrap()) - .await - .unwrap(); + let limiter = Limiter::new(Configuration::default()).await.unwrap(); // Create a limit with max == 1 let namespace = "test_namespace"; @@ -324,9 +317,7 @@ mod tests { #[actix_rt::test] async fn test_check_and_report_endpoints_separately() { let namespace = "test_namespace"; - let limiter = Limiter::new(Configuration::from_env().unwrap()) - .await - .unwrap(); + let limiter = Limiter::new(Configuration::default()).await.unwrap(); let _limit = create_test_limit(&limiter, namespace, 1).await; let rate_limiter: Arc = Arc::new(limiter); diff --git a/limitador-server/src/main.rs b/limitador-server/src/main.rs index 788ee499..0ea2dce6 100644 --- a/limitador-server/src/main.rs +++ b/limitador-server/src/main.rs @@ -2,6 +2,7 @@ #[macro_use] extern crate log; +extern crate clap; use crate::config::{ Configuration, InfinispanStorageConfiguration, RedisStorageCacheConfiguration, @@ -9,19 +10,30 @@ use crate::config::{ }; use crate::envoy_rls::server::run_envoy_rls_server; use crate::http_api::server::run_http_server; +use clap::builder::PossibleValuesParser; +use clap::{App, Arg, SubCommand}; +use env_logger::Builder; use limitador::errors::LimitadorError; use limitador::limit::Limit; use limitador::storage::infinispan::{Consistency, InfinispanStorageBuilder}; -use limitador::storage::redis::{AsyncRedisStorage, CachedRedisStorage, CachedRedisStorageBuilder}; +use limitador::storage::infinispan::{ + DEFAULT_INFINISPAN_CONSISTENCY, DEFAULT_INFINISPAN_LIMITS_CACHE_NAME, +}; +use limitador::storage::redis::{ + AsyncRedisStorage, CachedRedisStorage, CachedRedisStorageBuilder, DEFAULT_FLUSHING_PERIOD_SEC, + DEFAULT_MAX_CACHED_COUNTERS, DEFAULT_MAX_TTL_CACHED_COUNTERS_SEC, + DEFAULT_TTL_RATIO_CACHED_COUNTERS, +}; use limitador::storage::{AsyncCounterStorage, AsyncStorage}; use limitador::{AsyncRateLimiter, AsyncRateLimiterBuilder, RateLimiter, RateLimiterBuilder}; +use log::LevelFilter; use notify::event::ModifyKind; use notify::{Error, Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher}; use std::convert::TryInto; use std::path::Path; -use std::process; use std::sync::Arc; use std::time::Duration; +use std::{env, process}; use thiserror::Error; use tokio::runtime::Handle; use url::Url; @@ -31,6 +43,8 @@ mod http_api; mod config; +const LIMITADOR_VERSION: &str = env!("CARGO_PKG_VERSION"); + #[derive(Error, Debug)] pub enum LimitadorServerError { #[error("please set either the Redis or the Infinispan URL, but not both")] @@ -112,6 +126,7 @@ impl Limiter { cached_redis_storage.max_ttl_cached_counters(Duration::from_millis(cache_cfg.max_ttl)); cached_redis_storage = cached_redis_storage.ttl_ratio_cached_counters(cache_cfg.ttl_ratio); + cached_redis_storage = cached_redis_storage.max_cached_counters(cache_cfg.max_counters); cached_redis_storage.build().await } @@ -205,15 +220,17 @@ impl Limiter { #[actix_rt::main] async fn main() -> Result<(), Box> { - env_logger::init(); + let config = create_config(); - let config = match Configuration::from_env() { - Ok(config) => config, - Err(_) => { - eprintln!("Error: please set either the Redis or the Infinispan URL, but not both"); - process::exit(1) - } - }; + let mut builder = Builder::new(); + if let Some(level) = config.log_level { + builder.filter(None, level); + } else { + builder.parse_default_env(); + } + builder.init(); + + info!("Using config: {:?}", config); let limit_file = config.limits_file.clone(); let envoy_rls_address = config.rlp_address(); @@ -227,39 +244,33 @@ async fn main() -> Result<(), Box> { } }; - let _watcher = if let Some(limits_file_path) = limit_file { - if let Err(e) = rate_limiter.load_limits_from_file(&limits_file_path).await { - eprintln!("Failed to load limit file: {}", e); - process::exit(1) - } + if let Err(e) = rate_limiter.load_limits_from_file(&limit_file).await { + eprintln!("Failed to load limit file: {}", e); + process::exit(1) + } - let limiter = Arc::clone(&rate_limiter); - let handle = Handle::current(); - - let mut watcher = - RecommendedWatcher::new(move |result: Result| match result { - Ok(ref event) => { - if let EventKind::Modify(ModifyKind::Data(_)) = event.kind { - let limiter = limiter.clone(); - let location = event.paths.first().unwrap().clone(); - handle.spawn(async move { - match limiter.load_limits_from_file(&location).await { - Ok(_) => info!("Reloaded limit file"), - Err(e) => error!("Failed reloading limit file: {}", e), - } - }); + let limiter = Arc::clone(&rate_limiter); + let handle = Handle::current(); + + let mut watcher = RecommendedWatcher::new(move |result: Result| match result { + Ok(ref event) => { + if let EventKind::Modify(ModifyKind::Data(_)) = event.kind { + let limiter = limiter.clone(); + let location = event.paths.first().unwrap().clone(); + handle.spawn(async move { + match limiter.load_limits_from_file(&location).await { + Ok(_) => info!("Reloaded limit file"), + Err(e) => error!("Failed reloading limit file: {}", e), } - } - Err(ref e) => { - warn!("Something went wrong while watching limit file: {}", e); - } - })?; + }); + } + } + Err(ref e) => { + warn!("Something went wrong while watching limit file: {}", e); + } + })?; - watcher.watch(Path::new(&limits_file_path), RecursiveMode::Recursive)?; - Some(watcher) - } else { - None - }; + watcher.watch(Path::new(&limit_file), RecursiveMode::Recursive)?; info!("Envoy RLS server starting on {}", envoy_rls_address); tokio::spawn(run_envoy_rls_server( @@ -267,7 +278,303 @@ async fn main() -> Result<(), Box> { rate_limiter.clone(), )); + info!("HTTP server starting on {}", http_api_address); run_http_server(&http_api_address, rate_limiter.clone()).await?; Ok(()) } + +fn create_config() -> Configuration { + // figure defaults out + let default_limit_file = env::var("LIMITS_FILE").unwrap_or_else(|_| "".to_string()); + + let default_rls_host = + env::var("ENVOY_RLS_HOST").unwrap_or_else(|_| Configuration::DEFAULT_IP_BIND.to_string()); + let default_rls_port = + env::var("ENVOY_RLS_PORT").unwrap_or_else(|_| Configuration::DEFAULT_RLS_PORT.to_string()); + + let default_http_host = + env::var("HTTP_API_HOST").unwrap_or_else(|_| Configuration::DEFAULT_IP_BIND.to_string()); + let default_http_port = + env::var("HTTP_API_PORT").unwrap_or_else(|_| Configuration::DEFAULT_HTTP_PORT.to_string()); + + let redis_url = env::var("REDIS_URL").unwrap_or_else(|_| "".to_string()); + + let redis_cached_ttl_default = env::var("REDIS_LOCAL_CACHE_MAX_TTL_CACHED_COUNTERS_MS") + .unwrap_or_else(|_| (DEFAULT_MAX_TTL_CACHED_COUNTERS_SEC * 1000).to_string()); + let redis_flushing_period_default = env::var("REDIS_LOCAL_CACHE_FLUSHING_PERIOD_MS") + .unwrap_or_else(|_| (DEFAULT_FLUSHING_PERIOD_SEC * 1000).to_string()); + let redis_max_cached_counters_default = DEFAULT_MAX_CACHED_COUNTERS.to_string(); + let redis_ttl_ratio_default = env::var("REDIS_LOCAL_CACHE_TTL_RATIO_CACHED_COUNTERS") + .unwrap_or_else(|_| DEFAULT_TTL_RATIO_CACHED_COUNTERS.to_string()); + + let infinispan_cache_default = env::var("INFINISPAN_CACHE_NAME") + .unwrap_or_else(|_| DEFAULT_INFINISPAN_LIMITS_CACHE_NAME.to_string()); + let infinispan_consistency_default = env::var("INFINISPAN_COUNTERS_CONSISTENCY") + .unwrap_or_else(|_| DEFAULT_INFINISPAN_CONSISTENCY.to_string()); + + // wire args based of defaults + let limit_arg = Arg::with_name("LIMITS_FILE") + .help("The limit file to use") + .index(1); + let limit_arg = if default_limit_file.is_empty() { + limit_arg.required(true) + } else { + limit_arg.default_value(&default_limit_file) + }; + + let redis_url_arg = Arg::with_name("URL").help("Redis URL to use").index(1); + let redis_url_arg = if redis_url.is_empty() { + redis_url_arg.required(true) + } else { + redis_url_arg.default_value(&redis_url) + }; + + // build app + let cmdline = App::new("Limitador Server") + .version(LIMITADOR_VERSION) + .author("The Kuadrant team - github.com/Kuadrant") + .about("Rate Limiting Server") + .disable_help_subcommand(true) + .subcommand_negates_reqs(false) + .subcommand_value_name("STORAGE") + .subcommand_help_heading("STORAGES") + .subcommand_required(false) + .arg(limit_arg) + .arg( + Arg::with_name("ip") + .short('b') + .long("rls-ip") + .default_value(&default_rls_host) + .display_order(1) + .help("The IP to listen on for RLS"), + ) + .arg( + Arg::with_name("port") + .short('p') + .long("rls-port") + .default_value(&default_rls_port) + .display_order(2) + .help("The port to listen on for RLS"), + ) + .arg( + Arg::with_name("http_ip") + .short('B') + .long("http-ip") + .default_value(&default_http_host) + .display_order(3) + .help("The IP to listen on for HTTP"), + ) + .arg( + Arg::with_name("http_port") + .short('P') + .long("http-port") + .default_value(&default_http_port) + .display_order(4) + .help("The port to listen on for HTTP"), + ) + .arg( + Arg::with_name("limit_name_in_labels") + .short('l') + .long("limit-name-in-labels") + .display_order(5) + .help("Include the Limit Name in prometheus label"), + ) + .arg( + Arg::with_name("v") + .short('v') + .multiple_occurrences(true) + .max_occurrences(4) + .display_order(6) + .help("Sets the level of verbosity"), + ) + .subcommand( + SubCommand::with_name("memory") + .display_order(1) + .about("Counters are held in Limitador (ephemeral)"), + ) + .subcommand( + SubCommand::with_name("redis") + .display_order(2) + .about("Uses Redis to store counters") + .arg(redis_url_arg.clone()), + ) + .subcommand( + SubCommand::with_name("redis_cached") + .about("Uses Redis to store counters, with an in-memory cache") + .display_order(3) + .arg(redis_url_arg) + .arg( + Arg::with_name("TTL") + .long("ttl") + .takes_value(true) + .value_parser(clap::value_parser!(u64)) + .default_value(&redis_cached_ttl_default) + .display_order(2) + .help("TTL for cached counters in milliseconds"), + ) + .arg( + Arg::with_name("ratio") + .long("ratio") + .takes_value(true) + .value_parser(clap::value_parser!(u64)) + .default_value(&redis_ttl_ratio_default) + .display_order(3) + .help("Ratio to apply to the TTL from Redis on cached counters"), + ) + .arg( + Arg::with_name("flush") + .long("flush-period") + .takes_value(true) + .value_parser(clap::value_parser!(i64)) + .default_value(&redis_flushing_period_default) + .display_order(4) + .help("Flushing period for counters in milliseconds"), + ) + .arg( + Arg::with_name("max") + .long("max-cached") + .takes_value(true) + .value_parser(clap::value_parser!(usize)) + .default_value(&redis_max_cached_counters_default) + .display_order(5) + .help("Maximum amount of counters cached"), + ), + ) + .subcommand( + SubCommand::with_name("infinispan") + .about("Uses Infinispan to store counters") + .display_order(4) + .arg( + Arg::with_name("URL") + .help("Infinispan URL to use") + .display_order(1) + .required(true) + .index(1), + ) + .arg( + Arg::with_name("cache name") + .short('n') + .long("cache-name") + .takes_value(true) + .default_value(&infinispan_cache_default) + .display_order(2) + .help("Name of the cache to store counters in"), + ) + .arg( + Arg::with_name("consistency") + .short('c') + .long("consistency") + .takes_value(true) + .default_value(&infinispan_consistency_default) + .value_parser(PossibleValuesParser::new(["Strong", "Weak"])) + .display_order(3) + .help("The consistency to use to read from the cache"), + ), + ); + + let matches = cmdline.get_matches(); + + let limits_file = matches.value_of("LIMITS_FILE").unwrap(); + let storage = match matches.subcommand() { + Some(("redis", sub)) => StorageConfiguration::Redis(RedisStorageConfiguration { + url: sub.value_of("URL").unwrap().to_owned(), + cache: None, + }), + Some(("redis_cached", sub)) => StorageConfiguration::Redis(RedisStorageConfiguration { + url: sub.value_of("URL").unwrap().to_owned(), + cache: Some(RedisStorageCacheConfiguration { + flushing_period: *sub.get_one("flush").unwrap(), + max_ttl: *sub.get_one("TTL").unwrap(), + ttl_ratio: *sub.get_one("ratio").unwrap(), + max_counters: *sub.get_one("max").unwrap(), + }), + }), + Some(("infinispan", sub)) => { + StorageConfiguration::Infinispan(InfinispanStorageConfiguration { + url: sub.value_of("URL").unwrap().to_owned(), + cache: Some(sub.value_of("cache name").unwrap().to_string()), + consistency: Some(sub.value_of("consistency").unwrap().to_string()), + }) + } + Some(("memory", _sub)) => StorageConfiguration::InMemory, + None => match storage_config_from_env() { + Ok(storage_cfg) => storage_cfg, + Err(_) => { + eprintln!("Set either REDIS_URL or INFINISPAN_URL, but not both!"); + process::exit(1); + } + }, + _ => unreachable!("Some storage wasn't configured!"), + }; + + let mut config = Configuration::with( + storage, + limits_file.to_string(), + matches.value_of("ip").unwrap().into(), + matches.value_of("port").unwrap().parse().unwrap(), + matches.value_of("http_ip").unwrap().into(), + matches.value_of("http_port").unwrap().parse().unwrap(), + matches.value_of("limit_name_in_labels").is_some() + || env_option_is_enabled("LIMIT_NAME_IN_PROMETHEUS_LABELS"), + ); + + config.log_level = match matches.occurrences_of("v") { + 0 => None, + 1 => Some(LevelFilter::Warn), + 2 => Some(LevelFilter::Info), + 3 => Some(LevelFilter::Debug), + 4 => Some(LevelFilter::Trace), + _ => unreachable!("Verbosity should at most be 4!"), + }; + + config +} + +fn storage_config_from_env() -> Result { + let redis_url = env::var("REDIS_URL"); + let infinispan_url = env::var("INFINISPAN_URL"); + + match (redis_url, infinispan_url) { + (Ok(_), Ok(_)) => Err(()), + (Ok(url), Err(_)) => Ok(StorageConfiguration::Redis(RedisStorageConfiguration { + url, + cache: if env_option_is_enabled("REDIS_LOCAL_CACHE_ENABLED") { + Some(RedisStorageCacheConfiguration { + flushing_period: env::var("REDIS_LOCAL_CACHE_FLUSHING_PERIOD_MS") + .unwrap_or_else(|_| (DEFAULT_FLUSHING_PERIOD_SEC * 1000).to_string()) + .parse() + .expect("Expected an i64"), + max_ttl: env::var("REDIS_LOCAL_CACHE_MAX_TTL_CACHED_COUNTERS_MS") + .unwrap_or_else(|_| { + (DEFAULT_MAX_TTL_CACHED_COUNTERS_SEC * 1000).to_string() + }) + .parse() + .expect("Expected an u64"), + ttl_ratio: env::var("REDIS_LOCAL_CACHE_TTL_RATIO_CACHED_COUNTERS") + .unwrap_or_else(|_| DEFAULT_TTL_RATIO_CACHED_COUNTERS.to_string()) + .parse() + .expect("Expected an u64"), + max_counters: DEFAULT_MAX_CACHED_COUNTERS, + }) + } else { + None + }, + })), + (Err(_), Ok(url)) => Ok(StorageConfiguration::Infinispan( + InfinispanStorageConfiguration { + url, + cache: env::var("INFINISPAN_CACHE_NAME").ok(), + consistency: env::var("INFINISPAN_COUNTERS_CONSISTENCY").ok(), + }, + )), + _ => Ok(StorageConfiguration::InMemory), + } +} + +fn env_option_is_enabled(env_name: &str) -> bool { + match env::var(env_name) { + Ok(value) => value == "1", + Err(_) => false, + } +} diff --git a/limitador/src/storage/infinispan/counters.rs b/limitador/src/storage/infinispan/counters.rs index d4280288..60c1342b 100644 --- a/limitador/src/storage/infinispan/counters.rs +++ b/limitador/src/storage/infinispan/counters.rs @@ -18,6 +18,7 @@ use crate::storage::infinispan::response::response_to_string; use infinispan::errors::InfinispanError; use infinispan::{request, Infinispan}; use std::convert::TryFrom; +use std::fmt::{Display, Formatter}; use std::time::Duration; use thiserror::Error; @@ -27,6 +28,15 @@ pub enum Consistency { Strong, } +impl Display for Consistency { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Consistency::Weak => write!(f, "Weak"), + Consistency::Strong => write!(f, "Strong"), + } + } +} + #[derive(Error, Debug)] #[error("invalid consistency mode: {mode}")] pub struct InvalidConsistencyErr { diff --git a/limitador/src/storage/infinispan/infinispan_storage.rs b/limitador/src/storage/infinispan/infinispan_storage.rs index 7292e8a4..158ea84d 100644 --- a/limitador/src/storage/infinispan/infinispan_storage.rs +++ b/limitador/src/storage/infinispan/infinispan_storage.rs @@ -2,7 +2,9 @@ use crate::counter::Counter; use crate::limit::Limit; use crate::storage::infinispan::counters::{Consistency, CounterOpts}; use crate::storage::infinispan::response::response_to_string; -use crate::storage::infinispan::{counters, sets}; +use crate::storage::infinispan::{ + counters, sets, DEFAULT_INFINISPAN_CONSISTENCY, DEFAULT_INFINISPAN_LIMITS_CACHE_NAME, +}; use crate::storage::keys::*; use crate::storage::{AsyncCounterStorage, Authorization, StorageErr}; use async_trait::async_trait; @@ -12,8 +14,6 @@ use infinispan::Infinispan; use std::collections::HashSet; use std::time::Duration; -const DEFAULT_INFINISPAN_LIMITS_CACHE_NAME: &str = "limitador"; - pub struct InfinispanStorage { infinispan: Infinispan, cache_name: String, @@ -241,7 +241,8 @@ impl InfinispanStorageBuilder { &self.username, &self.password, self.cache_name, - self.counters_consistency.unwrap_or(Consistency::Strong), + self.counters_consistency + .unwrap_or(DEFAULT_INFINISPAN_CONSISTENCY), ) .await } diff --git a/limitador/src/storage/infinispan/mod.rs b/limitador/src/storage/infinispan/mod.rs index 3267a357..549bc559 100644 --- a/limitador/src/storage/infinispan/mod.rs +++ b/limitador/src/storage/infinispan/mod.rs @@ -21,3 +21,6 @@ impl From for StorageErr { Self { msg: e.to_string() } } } + +pub const DEFAULT_INFINISPAN_LIMITS_CACHE_NAME: &str = "limitador"; +pub const DEFAULT_INFINISPAN_CONSISTENCY: Consistency = Consistency::Strong; diff --git a/limitador/src/storage/redis/counters_cache.rs b/limitador/src/storage/redis/counters_cache.rs index 844a376f..13a6fb5d 100644 --- a/limitador/src/storage/redis/counters_cache.rs +++ b/limitador/src/storage/redis/counters_cache.rs @@ -1,11 +1,11 @@ use crate::counter::Counter; +use crate::storage::redis::{ + DEFAULT_MAX_CACHED_COUNTERS, DEFAULT_MAX_TTL_CACHED_COUNTERS_SEC, + DEFAULT_TTL_RATIO_CACHED_COUNTERS, +}; use std::time::Duration; use ttl_cache::TtlCache; -pub const DEFAULT_MAX_CACHED_COUNTERS: usize = 10000; -pub const DEFAULT_MAX_TTL_CACHED_COUNTERS: Duration = Duration::from_secs(5); -pub const DEFAULT_TTL_RATIO_CACHED_COUNTERS: u64 = 10; - pub struct CountersCache { max_ttl_cached_counters: Duration, ttl_ratio_cached_counters: u64, @@ -22,7 +22,7 @@ impl CountersCacheBuilder { pub fn new() -> Self { Self { max_cached_counters: DEFAULT_MAX_CACHED_COUNTERS, - max_ttl_cached_counters: DEFAULT_MAX_TTL_CACHED_COUNTERS, + max_ttl_cached_counters: Duration::from_secs(DEFAULT_MAX_TTL_CACHED_COUNTERS_SEC), ttl_ratio_cached_counters: DEFAULT_TTL_RATIO_CACHED_COUNTERS, } } diff --git a/limitador/src/storage/redis/mod.rs b/limitador/src/storage/redis/mod.rs index 931a0796..b9d89e69 100644 --- a/limitador/src/storage/redis/mod.rs +++ b/limitador/src/storage/redis/mod.rs @@ -7,6 +7,11 @@ mod redis_cached; mod redis_sync; mod scripts; +pub const DEFAULT_FLUSHING_PERIOD_SEC: u64 = 1; +pub const DEFAULT_MAX_CACHED_COUNTERS: usize = 10000; +pub const DEFAULT_MAX_TTL_CACHED_COUNTERS_SEC: u64 = 5; +pub const DEFAULT_TTL_RATIO_CACHED_COUNTERS: u64 = 10; + use crate::storage::StorageErr; pub use redis_async::AsyncRedisStorage; pub use redis_cached::CachedRedisStorage; diff --git a/limitador/src/storage/redis/redis_cached.rs b/limitador/src/storage/redis/redis_cached.rs index 95570cdd..98ff2ba0 100644 --- a/limitador/src/storage/redis/redis_cached.rs +++ b/limitador/src/storage/redis/redis_cached.rs @@ -2,12 +2,13 @@ use crate::counter::Counter; use crate::limit::Limit; use crate::storage::keys::*; use crate::storage::redis::batcher::Batcher; -use crate::storage::redis::counters_cache::{ - CountersCache, CountersCacheBuilder, DEFAULT_MAX_CACHED_COUNTERS, - DEFAULT_MAX_TTL_CACHED_COUNTERS, DEFAULT_TTL_RATIO_CACHED_COUNTERS, -}; +use crate::storage::redis::counters_cache::{CountersCache, CountersCacheBuilder}; use crate::storage::redis::redis_async::AsyncRedisStorage; use crate::storage::redis::scripts::VALUES_AND_TTLS; +use crate::storage::redis::{ + DEFAULT_FLUSHING_PERIOD_SEC, DEFAULT_MAX_CACHED_COUNTERS, DEFAULT_MAX_TTL_CACHED_COUNTERS_SEC, + DEFAULT_TTL_RATIO_CACHED_COUNTERS, +}; use crate::storage::{AsyncCounterStorage, Authorization, StorageErr}; use async_trait::async_trait; use redis::aio::ConnectionManager; @@ -36,8 +37,6 @@ use tokio::sync::Mutex; // - Introduce a mechanism to avoid going to Redis to fetch the same counter // multiple times when it is not cached. -const DEFAULT_FLUSHING_PERIOD: Duration = Duration::from_secs(1); - pub struct CachedRedisStorage { cached_counters: Mutex, batcher_counter_updates: Arc>, @@ -178,9 +177,9 @@ impl CachedRedisStorage { pub async fn new(redis_url: &str) -> Self { Self::new_with_options( redis_url, - Some(DEFAULT_FLUSHING_PERIOD), + Some(Duration::from_secs(DEFAULT_FLUSHING_PERIOD_SEC)), DEFAULT_MAX_CACHED_COUNTERS, - DEFAULT_MAX_TTL_CACHED_COUNTERS, + Duration::from_secs(DEFAULT_MAX_TTL_CACHED_COUNTERS_SEC), DEFAULT_TTL_RATIO_CACHED_COUNTERS, ) .await @@ -276,9 +275,9 @@ impl CachedRedisStorageBuilder { pub fn new(redis_url: &str) -> Self { Self { redis_url: redis_url.to_string(), - flushing_period: Some(DEFAULT_FLUSHING_PERIOD), + flushing_period: Some(Duration::from_secs(DEFAULT_FLUSHING_PERIOD_SEC)), max_cached_counters: DEFAULT_MAX_CACHED_COUNTERS, - max_ttl_cached_counters: DEFAULT_MAX_TTL_CACHED_COUNTERS, + max_ttl_cached_counters: Duration::from_secs(DEFAULT_MAX_TTL_CACHED_COUNTERS_SEC), ttl_ratio_cached_counters: DEFAULT_TTL_RATIO_CACHED_COUNTERS, } }