diff --git a/Cargo.lock b/Cargo.lock index da9b1aba3..2fbc935f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -103,7 +103,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.5", + "getrandom 0.2.6", "once_cell", "version_check", ] @@ -226,6 +226,7 @@ dependencies = [ "futures-lite", "num_cpus", "once_cell", + "tokio", ] [[package]] @@ -355,7 +356,7 @@ dependencies = [ "memchr", "num_cpus", "once_cell", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "pin-utils", "slab", "wasm-bindgen-futures", @@ -440,6 +441,287 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "aws-config" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b58e10d03d2180f9fce2fe72ffa007ccf44b4714b127d8429e78c3e55fe345c0" +dependencies = [ + "aws-http", + "aws-sdk-sso", + "aws-sdk-sts", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-http-tower", + "aws-smithy-json", + "aws-smithy-types", + "aws-types", + "bytes", + "hex 0.4.3", + "http", + "hyper", + "ring", + "tokio", + "tower", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-endpoint" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eadd9ea45f65689fbd088b22a2aaed363582fab5dcd335a1a7317640436952f" +dependencies = [ + "aws-smithy-http", + "aws-types", + "http", + "regex", + "tracing", +] + +[[package]] +name = "aws-http" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80fd2c81e2782d1f57a532543524c59b9c32dcc9132a7a2d308864c05b645e98" +dependencies = [ + "aws-smithy-http", + "aws-smithy-types", + "aws-types", + "http", + "lazy_static", + "percent-encoding", + "tracing", +] + +[[package]] +name = "aws-sdk-rds" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3aec9e3ca4a565ea1098399cca17754d3dcd94ad19deb9b3acbdb39df78aae2" +dependencies = [ + "aws-endpoint", + "aws-http", + "aws-sig-auth", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-http-tower", + "aws-smithy-query", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "bytes", + "http", + "tokio-stream", + "tower", +] + +[[package]] +name = "aws-sdk-sso" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c856987e02db8c5d81cb97e2a24609b05a6289db3d861c5b7d73081d6ecc70d" +dependencies = [ + "aws-endpoint", + "aws-http", + "aws-sig-auth", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-http-tower", + "aws-smithy-json", + "aws-smithy-types", + "aws-types", + "bytes", + "http", + "tokio-stream", + "tower", +] + +[[package]] +name = "aws-sdk-sts" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0a83f4382e47778399d0e0ae38761eee2fbad1d3bdde21358bd00cea46f36" +dependencies = [ + "aws-endpoint", + "aws-http", + "aws-sig-auth", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-http-tower", + "aws-smithy-query", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "bytes", + "http", + "tower", +] + +[[package]] +name = "aws-sig-auth" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27079eb4062be105e44ab4638ade63cf6448c2df5cb093d885faab8082fd5c9d" +dependencies = [ + "aws-sigv4", + "aws-smithy-http", + "aws-types", + "http", + "tracing", +] + +[[package]] +name = "aws-sigv4" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0be4a7223c244f3c57426087204cf66e04d1e73adb62571090613889cf6ee57" +dependencies = [ + "aws-smithy-http", + "form_urlencoded", + "hex 0.4.3", + "http", + "once_cell", + "percent-encoding", + "regex", + "ring", + "time 0.3.9", + "tracing", +] + +[[package]] +name = "aws-smithy-async" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cafe7e459caf2e2f834a77062f2c47f7df1956cf3be3060b23bf0721f17e1a4" +dependencies = [ + "futures-util", + "pin-project-lite 0.2.9", + "tokio", + "tokio-stream", +] + +[[package]] +name = "aws-smithy-client" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de133c5629abab72cad7c4eeab522cb9dfa11ab7ace07d051ef7c29bb54fd370" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-http-tower", + "aws-smithy-types", + "bytes", + "fastrand", + "http", + "http-body", + "hyper", + "hyper-rustls", + "lazy_static", + "pin-project", + "pin-project-lite 0.2.9", + "tokio", + "tower", + "tracing", +] + +[[package]] +name = "aws-smithy-http" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "119f6f903f5c27308c732f6c7e460aee91e083b3229db2b21fa40436fc60eda7" +dependencies = [ + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "http", + "http-body", + "hyper", + "once_cell", + "percent-encoding", + "pin-project", + "tokio", + "tokio-util 0.7.2", + "tracing", +] + +[[package]] +name = "aws-smithy-http-tower" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "991855d24079be0b7485a5753f987f060a153757c7f21729d705f2834366c096" +dependencies = [ + "aws-smithy-http", + "bytes", + "http", + "http-body", + "pin-project", + "tower", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81c96aa965ba386c7206c0913c6cbdfd27cf6b276e8834a7408b67f04994e647" +dependencies = [ + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-query" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14ec1e497d5e75d42787c9440bbbed5fcdb02b0bdc41fd8b7fe270449e66deff" +dependencies = [ + "aws-smithy-types", + "urlencoding", +] + +[[package]] +name = "aws-smithy-types" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b470753cf7e2ce6b7c55f66cb1d17576654878cd41d72bd863a38336569543f4" +dependencies = [ + "itoa", + "num-integer", + "ryu", + "time 0.3.9", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d795da895bc0d8a2ba44550ee5ec928af06db8ecdfd0e42b88d046e97e73dd94" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "aws-types" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5641c99c740574f7a7e70c03773f5ead9f9daf2d95ce6c790ddb4747e000bb0" +dependencies = [ + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-types", + "http", + "rustc_version 0.4.0", + "tracing", + "zeroize", +] + [[package]] name = "axum" version = "0.5.7" @@ -459,7 +741,7 @@ dependencies = [ "memchr", "mime", "percent-encoding", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "serde", "serde_json", "serde_urlencoded", @@ -487,9 +769,9 @@ dependencies = [ [[package]] name = "base-x" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" +checksum = "dc19a4937b4fbd3fe3379793130e42060d10627a360f2127802b10b87e7baf74" [[package]] name = "base64" @@ -503,6 +785,12 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "base64ct" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea908e7347a8c64e378c17e30ef880ad73e3b4498346b055c2c00ea342f3179" + [[package]] name = "binascii" version = "0.1.4" @@ -597,14 +885,14 @@ dependencies = [ "hyper", "hyperlocal", "log", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "serde", "serde_derive", "serde_json", "serde_urlencoded", "thiserror", "tokio", - "tokio-util 0.7.1", + "tokio-util 0.7.2", "url", "winapi", ] @@ -649,6 +937,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +[[package]] +name = "bytes-utils" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1934a3ef9cac8efde4966a92781e77713e1ba329f1d42e446c7d7eba340d8ef1" +dependencies = [ + "bytes", + "either", +] + [[package]] name = "bytesize" version = "1.1.0" @@ -663,9 +961,9 @@ checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" [[package]] name = "camino" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f3132262930b0522068049f5870a856ab8affc80c70d08b6ecb785771a6fc23" +checksum = "07fd178c5af4d59e83498ef15cf3f154e1a6f9d091270cb86283c65ef44e9ef0" dependencies = [ "serde", ] @@ -876,7 +1174,7 @@ dependencies = [ "num-integer", "num-traits", "serde", - "time 0.1.43", + "time 0.1.44", "winapi", ] @@ -981,9 +1279,9 @@ dependencies = [ [[package]] name = "combine" -version = "4.6.3" +version = "4.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b727aacc797f9fc28e355d21f34709ac4fc9adecfe470ad07b8f4464f53062" +checksum = "2a604e93b79d1808327a6fca85a6f2d69de66461e7620f5a4cbf5fb4d1d7c948" dependencies = [ "bytes", "memchr", @@ -1033,6 +1331,12 @@ dependencies = [ "cache-padded", ] +[[package]] +name = "const-oid" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" + [[package]] name = "const_fn" version = "0.4.9" @@ -1249,6 +1553,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "crypto-bigint" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" +dependencies = [ + "generic-array", + "subtle", +] + [[package]] name = "crypto-common" version = "0.1.3" @@ -1291,6 +1605,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "ct-logs" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1a816186fa68d9e426e3cb4ae4dff1fcd8e4a2c34b781bf7a822574a0d0aac8" +dependencies = [ + "sct 0.6.1", +] + [[package]] name = "ctor" version = "0.1.22" @@ -1336,9 +1659,9 @@ dependencies = [ [[package]] name = "curl-sys" -version = "0.4.53+curl-7.82.0" +version = "0.4.55+curl-7.83.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8092905a5a9502c312f223b2775f57ec5c5b715f9a15ee9d2a8591d1364a0352" +checksum = "23734ec77368ec583c2e61dd3f0b0e5c98b93abe6d2a004ca06b91dd7e3e2762" dependencies = [ "cc", "libc", @@ -1352,9 +1675,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.13.1" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0d720b8683f8dd83c65155f0530560cba68cd2bf395f6513a483caee57ff7f4" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" dependencies = [ "darling_core", "darling_macro", @@ -1362,9 +1685,9 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.13.1" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a340f241d2ceed1deb47ae36c4144b2707ec7dd0b649f894cb39bb595986324" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" dependencies = [ "fnv", "ident_case", @@ -1376,15 +1699,26 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.13.1" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72c41b3b7352feb3211a0d743dc5700a4e3b60f51bd2b368892d1e0f9a95f44b" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ "darling_core", "quote", "syn", ] +[[package]] +name = "der" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6919815d73839e7ad218de758883aae3a257ba6759ce7a9992501efbb53d705c" +dependencies = [ + "const-oid", + "crypto-bigint", + "pem-rfc7468", +] + [[package]] name = "devise" version = "0.3.1" @@ -1521,9 +1855,9 @@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "encoding_rs" -version = "0.8.30" +version = "0.8.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" dependencies = [ "cfg-if 1.0.0", ] @@ -1607,9 +1941,9 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98" +checksum = "c0408e2626025178a6a7f7ffc05a25bc47103229f19c113755de7bf63816290c" dependencies = [ "cfg-if 1.0.0", "libc", @@ -1625,9 +1959,9 @@ checksum = "279fb028e20b3c4c320317955b77c5e0c9701f05a1d309905d6fc702cdc5053e" [[package]] name = "flate2" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +checksum = "b39522e96686d38f4bc984b9198e3a0613264abaebaff2c5c918bfa6b6da09af" dependencies = [ "cfg-if 1.0.0", "crc32fast", @@ -1743,7 +2077,7 @@ dependencies = [ "futures-io", "memchr", "parking", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "waker-fn", ] @@ -1783,7 +2117,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "pin-utils", "slab", ] @@ -1834,13 +2168,13 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" +checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" dependencies = [ "cfg-if 1.0.0", "libc", - "wasi 0.10.2+wasi-snapshot-preview1", + "wasi 0.10.0+wasi-snapshot-preview1", ] [[package]] @@ -1923,9 +2257,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62eeb471aa3e3c9197aa4bfeabfe02982f6dc96f750486c0bb0009ac58b26d2b" +checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" dependencies = [ "bytes", "fnv", @@ -1936,7 +2270,7 @@ dependencies = [ "indexmap", "slab", "tokio", - "tokio-util 0.6.9", + "tokio-util 0.7.2", "tracing", ] @@ -2076,7 +2410,7 @@ checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", "http", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", ] [[package]] @@ -2110,7 +2444,7 @@ dependencies = [ "cookie 0.14.4", "futures-lite", "infer", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "rand 0.7.3", "serde", "serde_json", @@ -2121,9 +2455,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9100414882e15fb7feccb4897e5f0ff0ff1ca7d1a86a23208ada4d7a18e6c6c4" +checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" [[package]] name = "httpdate" @@ -2153,7 +2487,7 @@ dependencies = [ "httparse", "httpdate", "itoa", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "socket2", "tokio", "tower-service", @@ -2172,6 +2506,23 @@ dependencies = [ "tracing", ] +[[package]] +name = "hyper-rustls" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64" +dependencies = [ + "ct-logs", + "futures-util", + "hyper", + "log", + "rustls 0.19.1", + "rustls-native-certs", + "tokio", + "tokio-rustls", + "webpki 0.21.4", +] + [[package]] name = "hyper-timeout" version = "0.4.1" @@ -2179,7 +2530,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" dependencies = [ "hyper", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "tokio", "tokio-io-timeout", ] @@ -2247,12 +2598,12 @@ dependencies = [ [[package]] name = "im-rc" -version = "15.0.0" +version = "15.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ca8957e71f04a205cb162508f9326aea04676c8dfd0711220190d6b83664f3f" +checksum = "af1955a75fa080c677d3972822ec4bad316169ab1cfc6c257a942c2265dbe5fe" dependencies = [ "bitmaps", - "rand_core 0.5.1", + "rand_core 0.6.3", "rand_xoshiro", "sized-chunks", "typenum", @@ -2261,9 +2612,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" dependencies = [ "autocfg", "hashbrown", @@ -2293,9 +2644,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e70ee094dc02fd9c13fdad4940090f22dbd6ac7c9e7094a46cf0232a50bc7c" +checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" [[package]] name = "itertools" @@ -2308,9 +2659,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" [[package]] name = "jni" @@ -2343,9 +2694,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.56" +version = "0.3.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" +checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397" dependencies = [ "wasm-bindgen", ] @@ -2373,6 +2724,9 @@ name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin 0.5.2", +] [[package]] name = "lazycell" @@ -2382,9 +2736,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.121" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" [[package]] name = "libgit2-sys" @@ -2410,6 +2764,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "libm" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db" + [[package]] name = "libnghttp2-sys" version = "0.1.7+1.45.0" @@ -2436,9 +2796,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.5" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f35facd4a5673cb5a48822be2be1d4236c1c99cb4113cab7061ac720d5bf859" +checksum = "92e7e15d7610cce1d9752e137625f14e61a28cd45929b6e12e47b50fe154ee2e" dependencies = [ "cc", "libc", @@ -2448,10 +2808,11 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" dependencies = [ + "autocfg", "scopeguard", ] @@ -2468,9 +2829,9 @@ dependencies = [ [[package]] name = "loom" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc5c7d328e32cc4954e8e01193d7f0ef5ab257b5090b70a964e099a36034309" +checksum = "85eb735cf3c8ebac6cc3655c5da2f4a088b6a19133aa482471a21ba0eb5d83ab" dependencies = [ "cfg-if 1.0.0", "generator", @@ -2513,9 +2874,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memoffset" @@ -2550,26 +2911,23 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.4.4" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" dependencies = [ "adler", - "autocfg", ] [[package]] name = "mio" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9" +checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799" dependencies = [ "libc", "log", - "miow", - "ntapi", "wasi 0.11.0+wasi-snapshot-preview1", - "winapi", + "windows-sys", ] [[package]] @@ -2595,9 +2953,9 @@ dependencies = [ "log", "memchr", "mime", - "spin 0.9.2", + "spin 0.9.3", "tokio", - "tokio-util 0.6.9", + "tokio-util 0.6.10", "version_check", ] @@ -2609,9 +2967,9 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "native-tls" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" +checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" dependencies = [ "lazy_static", "libc", @@ -2640,15 +2998,15 @@ dependencies = [ [[package]] name = "ndk-context" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e3c5cc68637e21fe8f077f6a1c9e0b9ca495bb74895226b476310f613325884" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" [[package]] name = "ndk-glue" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9ffb7443daba48349d545028777ca98853b018b4c16624aa01223bc29e078da" +checksum = "0d0c4a7b83860226e6b4183edac21851f05d5a51756e97a1144b7f5a6b63e65f" dependencies = [ "lazy_static", "libc", @@ -2692,31 +3050,62 @@ dependencies = [ ] [[package]] -name = "ntapi" -version = "0.3.7" +name = "num-bigint" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" +checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" dependencies = [ - "winapi", + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "566d173b2f9406afbc5510a90925d5a2cd80cae4605631f1212303df265de011" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.5", + "smallvec", + "zeroize", ] [[package]] name = "num-integer" -version = "0.1.44" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" dependencies = [ "autocfg", + "num-integer", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -2783,18 +3172,30 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.38" +version = "0.10.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95" +checksum = "fb81a6430ac911acb25fe5ac8f1d2af1b4ea8a4fdfda0f1ee4292af2e2d8eb0e" dependencies = [ "bitflags", "cfg-if 1.0.0", "foreign-types", "libc", "once_cell", + "openssl-macros", "openssl-sys", ] +[[package]] +name = "openssl-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "openssl-probe" version = "0.1.5" @@ -2803,9 +3204,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.72" +version = "0.9.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb" +checksum = "9d5fd19fb3e0a8191c1e34935718976a3e70c112ab9a24af6d7cadccd9d90bc0" dependencies = [ "autocfg", "cc", @@ -2816,9 +3217,9 @@ dependencies = [ [[package]] name = "os_info" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "023df84d545ef479cf67fd2f4459a613585c9db4852c2fad12ab70587859d340" +checksum = "04304d855bb5385d4b595edf0147b8e281871766b75dd4c87b2bdf3c9e5c2d19" dependencies = [ "log", "serde", @@ -2864,7 +3265,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" dependencies = [ "lock_api", - "parking_lot_core 0.9.1", + "parking_lot_core 0.9.3", ] [[package]] @@ -2883,9 +3284,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.1" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28141e0cc4143da2443301914478dc976a61ffdb3f043058310c70df2fed8954" +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" dependencies = [ "cfg-if 1.0.0", "libc", @@ -2896,9 +3297,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5" +checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" [[package]] name = "pathdiff" @@ -2929,6 +3330,15 @@ dependencies = [ "syn", ] +[[package]] +name = "pem-rfc7468" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01de5d978f34aa4b2296576379fcc416034702fd94117c56ffd8a1a767cefb30" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.1.0" @@ -2973,9 +3383,9 @@ checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" [[package]] name = "pin-project-lite" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[package]] name = "pin-utils" @@ -2983,11 +3393,33 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs1" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a78f66c04ccc83dd4486fd46c33896f4e17b24a7a3a6400dedc48ed0ddd72320" +dependencies = [ + "der", + "pkcs8", + "zeroize", +] + +[[package]] +name = "pkcs8" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cabda3fb821068a9a4fab19a683eac3af12edf0f34b94a8be53c4972b8149d0" +dependencies = [ + "der", + "spki", + "zeroize", +] + [[package]] name = "pkg-config" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" +checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" [[package]] name = "polling" @@ -3193,8 +3625,12 @@ dependencies = [ name = "provisioner" version = "0.1.0" dependencies = [ + "aws-config", + "aws-sdk-rds", + "aws-smithy-types", "clap 3.1.18", "ctor", + "fqdn", "lazy_static", "portpicker", "prost", @@ -3283,7 +3719,7 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" dependencies = [ - "getrandom 0.2.5", + "getrandom 0.2.6", ] [[package]] @@ -3297,11 +3733,11 @@ dependencies = [ [[package]] name = "rand_xoshiro" -version = "0.4.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9fcdd2e881d02f1d9390ae47ad8e5696a9e4be7b547a1da2afbc61973217004" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" dependencies = [ - "rand_core 0.5.1", + "rand_core 0.6.3", ] [[package]] @@ -3339,29 +3775,29 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7776223e2696f1aa4c6b0170e83212f47296a00424305117d013dfe86fb0fe55" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.5", + "getrandom 0.2.6", "redox_syscall", "thiserror", ] [[package]] name = "ref-cast" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300f2a835d808734ee295d45007adacb9ebb29dd3ae2424acfa17930cae541da" +checksum = "685d58625b6c2b83e4cc88a27c4bf65adb7b6b16dbdc413e515c9405b47432ab" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c38e3aecd2b21cb3959637b883bb3714bc7e43f0268b9a29d3743ee3e55cdd2" +checksum = "a043824e29c94169374ac5183ac0ed43f5724dc4556b19568007486bd840fa1f" dependencies = [ "proc-macro2", "quote", @@ -3427,7 +3863,7 @@ dependencies = [ "mime_guess", "native-tls", "percent-encoding", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "serde", "serde_json", "serde_urlencoded", @@ -3523,7 +3959,7 @@ dependencies = [ "multer", "num_cpus", "parking_lot 0.12.0", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "rand 0.8.5", "ref-cast", "rocket_codegen", @@ -3535,7 +3971,7 @@ dependencies = [ "time 0.3.9", "tokio", "tokio-stream", - "tokio-util 0.7.1", + "tokio-util 0.7.2", "ubyte", "uuid", "version_check", @@ -3574,7 +4010,7 @@ dependencies = [ "memchr", "pear", "percent-encoding", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "ref-cast", "serde", "smallvec", @@ -3592,6 +4028,26 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56770675ebc04927ded3e60633437841581c285dc6236109ea25fbf3beb7b59e" +[[package]] +name = "rsa" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cf22754c49613d2b3b119f0e5d46e34a2c628a937e3024b8762de4e7d8c710b" +dependencies = [ + "byteorder", + "digest 0.10.3", + "num-bigint-dig", + "num-integer", + "num-iter", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.3", + "smallvec", + "subtle", + "zeroize", +] + [[package]] name = "rustc-hash" version = "1.1.0" @@ -3613,6 +4069,15 @@ dependencies = [ "semver 0.9.0", ] +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.9", +] + [[package]] name = "rustfix" version = "0.6.0" @@ -3625,6 +4090,19 @@ dependencies = [ "serde_json", ] +[[package]] +name = "rustls" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7" +dependencies = [ + "base64 0.13.0", + "log", + "ring", + "sct 0.6.1", + "webpki 0.21.4", +] + [[package]] name = "rustls" version = "0.20.6" @@ -3633,8 +4111,20 @@ checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" dependencies = [ "log", "ring", - "sct", - "webpki", + "sct 0.7.0", + "webpki 0.22.0", +] + +[[package]] +name = "rustls-native-certs" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a07b7c1885bd8ed3831c289b7870b13ef46fe0e856d288c30d9cc17d75a2092" +dependencies = [ + "openssl-probe", + "rustls 0.19.1", + "schannel", + "security-framework", ] [[package]] @@ -3645,9 +4135,9 @@ checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" [[package]] name = "ryu" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" [[package]] name = "same-file" @@ -3660,12 +4150,12 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" dependencies = [ "lazy_static", - "winapi", + "windows-sys", ] [[package]] @@ -3680,6 +4170,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "sct" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "sct" version = "0.7.0" @@ -3768,9 +4268,9 @@ dependencies = [ [[package]] name = "serde_ignored" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c2c7d39d14f2f2ea82239de71594782f186fd03501ac81f0ce08e674819ff2f" +checksum = "1940036ca2411651a40012009d062087dfe62817b2191a03750fb569e11fa633" dependencies = [ "serde", ] @@ -3902,9 +4402,11 @@ name = "shuttle-codegen" version = "0.3.1" dependencies = [ "pretty_assertions", + "proc-macro-error", "proc-macro2", "quote", "syn", + "trybuild", ] [[package]] @@ -3926,6 +4428,7 @@ name = "shuttle-service" version = "0.3.3" dependencies = [ "anyhow", + "async-std", "async-trait", "axum", "cargo", @@ -3935,6 +4438,7 @@ dependencies = [ "lazy_static", "libloading", "log", + "paste", "portpicker", "regex", "rocket", @@ -4000,9 +4504,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" +checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" [[package]] name = "smallvec" @@ -4051,9 +4555,19 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "spin" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5" +checksum = "c530c2b0d0bf8b69304b39fe2001993e267461948b890cd037d8ad4293fa1a0d" + +[[package]] +name = "spki" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d01ac02a6ccf3e07db148d2be087da624fea0221a16152ed01f0496a6b0a27" +dependencies = [ + "base64ct", + "der", +] [[package]] name = "sqlformat" @@ -4090,6 +4604,7 @@ dependencies = [ "bytes", "crc", "crossbeam-queue", + "digest 0.10.3", "dirs", "either", "event-listener", @@ -4097,6 +4612,7 @@ dependencies = [ "futures-core", "futures-intrusive", "futures-util", + "generic-array", "hashlink", "hex 0.4.3", "hkdf 0.12.3", @@ -4107,10 +4623,12 @@ dependencies = [ "log", "md-5", "memchr", + "num-bigint", "once_cell", "paste", "percent-encoding", "rand 0.8.5", + "rsa", "serde", "serde_json", "sha-1", @@ -4196,7 +4714,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" dependencies = [ "discard", - "rustc_version", + "rustc_version 0.2.3", "stdweb-derive", "stdweb-internal-macros", "stdweb-internal-runtime", @@ -4472,7 +4990,7 @@ dependencies = [ "http-types", "kv-log-macro", "log", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "route-recognizer", "serde", "serde_json", @@ -4480,11 +4998,12 @@ dependencies = [ [[package]] name = "time" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" dependencies = [ "libc", + "wasi 0.10.0+wasi-snapshot-preview1", "winapi", ] @@ -4546,9 +5065,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] @@ -4572,7 +5091,7 @@ dependencies = [ "num_cpus", "once_cell", "parking_lot 0.12.0", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "signal-hook-registry", "socket2", "tokio-macros", @@ -4585,7 +5104,7 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" dependencies = [ - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "tokio", ] @@ -4610,6 +5129,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6844de72e57df1980054b38be3a9f4702aba4858be64dd700181a8a6d0e1b6" +dependencies = [ + "rustls 0.19.1", + "tokio", + "webpki 0.21.4", +] + [[package]] name = "tokio-stream" version = "0.1.8" @@ -4617,7 +5147,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" dependencies = [ "futures-core", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "tokio", ] @@ -4636,28 +5166,28 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.6.9" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" dependencies = [ "bytes", "futures-core", "futures-sink", "log", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "tokio", ] [[package]] name = "tokio-util" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0edfdeb067411dba2044da6d1cb2df793dd35add7888d73c16e3381ded401764" +checksum = "f988a1a1adc2fb21f9c12aa96441da33a1728193ae0b95d2be22dbd17fcb4e5c" dependencies = [ "bytes", "futures-core", "futures-sink", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "tokio", "tracing", ] @@ -4736,7 +5266,7 @@ dependencies = [ "prost-derive", "tokio", "tokio-stream", - "tokio-util 0.7.1", + "tokio-util 0.7.2", "tower", "tower-layer", "tower-service", @@ -4767,11 +5297,11 @@ dependencies = [ "futures-util", "indexmap", "pin-project", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "rand 0.8.5", "slab", "tokio", - "tokio-util 0.7.1", + "tokio-util 0.7.2", "tower-layer", "tower-service", "tracing", @@ -4790,7 +5320,7 @@ dependencies = [ "http", "http-body", "http-range-header", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "tower-layer", "tower-service", ] @@ -4808,7 +5338,7 @@ dependencies = [ "http", "http-body", "http-range-header", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "tower", "tower-layer", "tower-service", @@ -4834,16 +5364,16 @@ checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" dependencies = [ "cfg-if 1.0.0", "log", - "pin-project-lite 0.2.8", + "pin-project-lite 0.2.9", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e65ce065b4b5c53e73bb28912318cb8c9e9ad3921f1d669eb0e68b4c8143a2b" +checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c" dependencies = [ "proc-macro2", "quote", @@ -4872,9 +5402,9 @@ dependencies = [ [[package]] name = "tracing-log" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3" +checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922" dependencies = [ "lazy_static", "log", @@ -4905,6 +5435,21 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +[[package]] +name = "trybuild" +version = "1.0.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc92f558afb6d1d7c6f175eb8d615b8ef49c227543e68e19c123d4ee43d8a7d" +dependencies = [ + "glob", + "once_cell", + "serde", + "serde_derive", + "serde_json", + "termcolor", + "toml", +] + [[package]] name = "typenum" version = "1.15.0" @@ -4913,18 +5458,18 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "ubyte" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42756bb9e708855de2f8a98195643dff31a97f0485d90d8467b39dc24be9e8fe" +checksum = "a58e29f263341a29bb79e14ad7fda5f63b1c7e48929bad4c685d7876b1d04e94" dependencies = [ "serde", ] [[package]] name = "uncased" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baeed7327e25054889b9bd4f975f32e5f4c5d434042d59ab6cd4142c0a76ed0" +checksum = "09b01702b0fd0b3fadcf98e098780badda8742d4f4a7676615cad90e8ac73622" dependencies = [ "serde", "version_check", @@ -4941,9 +5486,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" [[package]] name = "unicode-ident" @@ -4974,9 +5519,9 @@ checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" [[package]] name = "unicode-xid" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" [[package]] name = "unicode_categories" @@ -5011,12 +5556,12 @@ dependencies = [ "log", "native-tls", "once_cell", - "rustls", + "rustls 0.20.6", "serde", "serde_json", "socks", "url", - "webpki", + "webpki 0.22.0", "webpki-roots", ] @@ -5033,6 +5578,12 @@ dependencies = [ "serde", ] +[[package]] +name = "urlencoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b90931029ab9b034b300b797048cf23723400aa757e8a2bfb9d748102f9821" + [[package]] name = "utf8parse" version = "0.2.0" @@ -5045,7 +5596,7 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6d5d669b51467dcf7b2f1a796ce0f955f05f01cafda6c19d6e95f730df29238" dependencies = [ - "getrandom 0.2.5", + "getrandom 0.2.6", "serde", ] @@ -5143,9 +5694,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" +version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" [[package]] name = "wasi" @@ -5155,9 +5706,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.79" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" +checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad" dependencies = [ "cfg-if 1.0.0", "serde", @@ -5167,9 +5718,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.79" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" +checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4" dependencies = [ "bumpalo", "lazy_static", @@ -5182,9 +5733,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.29" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb6ec270a31b1d3c7e266b999739109abce8b6c87e4b31fcfcd788b65267395" +checksum = "6f741de44b75e14c35df886aff5f1eb73aa114fa5d4d00dcd37b5e01259bf3b2" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -5194,9 +5745,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.79" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" +checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5204,9 +5755,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.79" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" +checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b" dependencies = [ "proc-macro2", "quote", @@ -5217,15 +5768,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.79" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" +checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" [[package]] name = "web-sys" -version = "0.3.56" +version = "0.3.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" +checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283" dependencies = [ "js-sys", "wasm-bindgen", @@ -5245,6 +5796,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "webpki" version = "0.22.0" @@ -5261,7 +5822,7 @@ version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d8de8415c823c8abd270ad483c6feeac771fad964890779f9a8cb24fbbc1bf" dependencies = [ - "webpki", + "webpki 0.22.0", ] [[package]] @@ -5333,9 +5894,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.32.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3df6e476185f92a12c072be4a189a0210dcdcf512a1891d6dff9edb874deadc6" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ "windows_aarch64_msvc", "windows_i686_gnu", @@ -5346,33 +5907,33 @@ dependencies = [ [[package]] name = "windows_aarch64_msvc" -version = "0.32.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" [[package]] name = "windows_i686_gnu" -version = "0.32.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" [[package]] name = "windows_i686_msvc" -version = "0.32.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" [[package]] name = "windows_x86_64_gnu" -version = "0.32.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" [[package]] name = "windows_x86_64_msvc" -version = "0.32.0" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" [[package]] name = "winreg" @@ -5383,8 +5944,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "xmlparser" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "114ba2b24d2167ef6d67d7d04c8cc86522b87f490025f39f0303b7db5bf5e3d8" + [[package]] name = "yansi" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" + +[[package]] +name = "zeroize" +version = "1.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94693807d016b2f2d2e14420eb3bfcca689311ff775dcf113d74ea624b7cdf07" diff --git a/api/src/deployment.rs b/api/src/deployment.rs index 28977ab14..7854ae39a 100644 --- a/api/src/deployment.rs +++ b/api/src/deployment.rs @@ -160,7 +160,6 @@ impl Deployment { let mut factory = ShuttleFactory::new( context.provisioner_client.clone(), - context.provisioner_address.clone(), meta.project.clone(), ); let addr = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), port); @@ -289,7 +288,6 @@ pub(crate) struct DeploymentSystem { job_queue: JobQueue, router: Arc, fqdn: String, - pub(crate) provisioner_address: String, } const JOB_QUEUE_SIZE: usize = 200; @@ -340,7 +338,6 @@ pub(crate) struct Context { build_system: Box, deployments: Arc>, provisioner_client: ProvisionerClient, - provisioner_address: String, } impl DeploymentSystem { @@ -384,7 +381,6 @@ impl DeploymentSystem { build_system, deployments: deployments.clone(), provisioner_client, - provisioner_address: provisioner_address.clone(), }; let job_queue = JobQueue::new(context, tx).await; @@ -400,7 +396,6 @@ impl DeploymentSystem { job_queue, router, fqdn, - provisioner_address, } } diff --git a/api/src/factory.rs b/api/src/factory.rs index f55dbf250..3a84683c0 100644 --- a/api/src/factory.rs +++ b/api/src/factory.rs @@ -1,25 +1,24 @@ use async_trait::async_trait; -use proto::provisioner::{provisioner_client::ProvisionerClient, DatabaseRequest}; +use proto::provisioner::{ + database_request::DbType, provisioner_client::ProvisionerClient, DatabaseRequest, +}; use shuttle_common::{project::ProjectName, DatabaseReadyInfo}; -use shuttle_service::Factory; +use shuttle_service::{database::Type, Factory}; use tonic::{transport::Channel, Request}; pub(crate) struct ShuttleFactory { project_name: ProjectName, provisioner_client: ProvisionerClient, - provisioner_address: String, info: Option, } impl ShuttleFactory { pub(crate) fn new( provisioner_client: ProvisionerClient, - provisioner_address: String, project_name: ProjectName, ) -> Self { Self { provisioner_client, - provisioner_address, project_name, info: None, } @@ -32,13 +31,19 @@ impl ShuttleFactory { #[async_trait] impl Factory for ShuttleFactory { - async fn get_sql_connection_string(&mut self) -> Result { + async fn get_sql_connection_string( + &mut self, + db_type: Type, + ) -> Result { if let Some(ref info) = self.info { - return Ok(info.connection_string(&self.provisioner_address)); + return Ok(info.connection_string_private()); } + let db_type: DbType = db_type.into(); + let request = Request::new(DatabaseRequest { project_name: self.project_name.to_string(), + db_type: Some(db_type), }); let response = self @@ -49,7 +54,7 @@ impl Factory for ShuttleFactory { .into_inner(); let info: DatabaseReadyInfo = response.into(); - let conn_str = info.connection_string(&self.provisioner_address); + let conn_str = info.connection_string_private(); self.info = Some(info); debug!("giving a sql connection string: {}", conn_str); diff --git a/api/src/main.rs b/api/src/main.rs index f14170793..a93c99342 100644 --- a/api/src/main.rs +++ b/api/src/main.rs @@ -148,8 +148,7 @@ async fn project_secrets( .await?; if let Some(database_deployment) = &deployment.database_deployment { - let conn_str = - database_deployment.connection_string(&state.deployment_manager.provisioner_address); + let conn_str = database_deployment.connection_string_private(); let conn = sqlx::PgPool::connect(&conn_str) .await .map_err(|e| DeploymentApiError::Internal(e.to_string()))?; diff --git a/api/users.toml b/api/users.toml index 9646d9e58..453ceb8cc 100644 --- a/api/users.toml +++ b/api/users.toml @@ -8,4 +8,5 @@ projects = [ 'authentication-rocket-app', 'hello-world-tide-app', 'hello-world-tower-app', + 'postgres-tide-app', ] diff --git a/cargo-shuttle/src/factory.rs b/cargo-shuttle/src/factory.rs index bfa3fb043..12c1432fe 100644 --- a/cargo-shuttle/src/factory.rs +++ b/cargo-shuttle/src/factory.rs @@ -9,14 +9,14 @@ use bollard::{ }; use colored::Colorize; use crossterm::{ - cursor::MoveUp, + cursor::{MoveDown, MoveUp}, terminal::{Clear, ClearType}, QueueableCommand, }; use futures::StreamExt; use portpicker::pick_unused_port; -use shuttle_common::{project::ProjectName, DatabaseReadyInfo}; -use shuttle_service::{error::CustomError, Factory}; +use shuttle_common::{database::AwsRdsEngine, project::ProjectName, DatabaseReadyInfo}; +use shuttle_service::{database::Type, error::CustomError, Factory}; use std::{collections::HashMap, io::stdout, time::Duration}; use tokio::time::sleep; @@ -34,14 +34,26 @@ impl LocalFactory { } } -const PG_PASSWORD: &str = "password"; -const PG_IMAGE: &str = "postgres:11"; - #[async_trait] impl Factory for LocalFactory { - async fn get_sql_connection_string(&mut self) -> Result { + async fn get_sql_connection_string( + &mut self, + db_type: Type, + ) -> Result { trace!("getting sql string for project '{}'", self.project); - let container_name = format!("shuttle_{}_postgres", self.project); + + let EngineConfig { + r#type, + image, + engine, + username, + password, + database_name, + port, + env, + is_ready_cmd, + } = db_type_to_config(db_type); + let container_name = format!("shuttle_{}_{}", self.project, r#type); let container = match self.docker.inspect_container(&container_name, None).await { Ok(container) => { @@ -51,9 +63,7 @@ impl Factory for LocalFactory { Err(bollard::errors::Error::DockerResponseServerError { status_code, .. }) if status_code == 404 => { - self.pull_image(PG_IMAGE) - .await - .expect("failed to pull image"); + self.pull_image(&image).await.expect("failed to pull image"); trace!("will create DB container {container_name}"); let options = Some(CreateContainerOptions { name: container_name.clone(), @@ -61,7 +71,7 @@ impl Factory for LocalFactory { let mut port_bindings = HashMap::new(); let host_port = pick_unused_port().expect("system to have a free port"); port_bindings.insert( - "5432/tcp".to_string(), + port.clone(), Some(vec![PortBinding { host_port: Some(host_port.to_string()), ..Default::default() @@ -72,10 +82,9 @@ impl Factory for LocalFactory { ..Default::default() }; - let password_env = format!("POSTGRES_PASSWORD={PG_PASSWORD}"); let config = Config { - image: Some(PG_IMAGE), - env: Some(vec![&password_env]), + image: Some(image), + env, host_config: Some(host_config), ..Default::default() }; @@ -101,10 +110,10 @@ impl Factory for LocalFactory { .expect("container to have host config") .port_bindings .expect("port bindings on container") - .get("5432/tcp") - .expect("a '5432/tcp' port bindings entry") + .get(&port) + .expect("a port bindings entry") .as_ref() - .expect("a '5432/tcp' port bindings") + .expect("a port bindings") .first() .expect("at least one port binding") .host_port @@ -125,16 +134,19 @@ impl Factory for LocalFactory { .expect("failed to start none running container"); } - self.wait_for_ready(&container_name).await?; + self.wait_for_ready(&container_name, is_ready_cmd).await?; let db_info = DatabaseReadyInfo::new( - "postgres".to_string(), - PG_PASSWORD.to_string(), - "postgres".to_string(), + engine, + username, + password, + database_name, port, + "localhost".to_string(), + "localhost".to_string(), ); - let conn_str = db_info.connection_string("localhost"); + let conn_str = db_info.connection_string_private(); println!( "{:>12} can be reached at {}\n", @@ -147,13 +159,18 @@ impl Factory for LocalFactory { } impl LocalFactory { - async fn wait_for_ready(&self, container_name: &str) -> Result<(), shuttle_service::Error> { + async fn wait_for_ready( + &self, + container_name: &str, + is_ready_cmd: Vec, + ) -> Result<(), shuttle_service::Error> { loop { trace!("waiting for '{container_name}' to be ready for connections"); let config = CreateExecOptions { - cmd: Some(vec!["pg_isready"]), + cmd: Some(is_ready_cmd.clone()), attach_stdout: Some(true), + attach_stderr: Some(true), ..Default::default() }; @@ -171,12 +188,12 @@ impl LocalFactory { if let bollard::exec::StartExecResults::Attached { mut output, .. } = ready_result { while let Some(line) = output.next().await { - if let bollard::container::LogOutput::StdOut { message } = + trace!("line: {:?}", line); + + if let bollard::container::LogOutput::StdOut { .. } = line.expect("output to have a log line") { - if message.ends_with(b"accepting connections\n") { - return Ok(()); - } + return Ok(()); } } } @@ -213,6 +230,13 @@ impl LocalFactory { print_layers(&layers); } + // Undo last MoveUps + stdout() + .queue(MoveDown( + layers.len().try_into().expect("to convert usize to u16"), + )) + .expect("to reset cursor position"); + Ok(()) } } @@ -254,3 +278,84 @@ fn print_layers(layers: &Vec) { )) .expect("to reset cursor position"); } + +struct EngineConfig { + r#type: String, + image: String, + engine: String, + username: String, + password: String, + database_name: String, + port: String, + env: Option>, + is_ready_cmd: Vec, +} + +fn db_type_to_config(db_type: Type) -> EngineConfig { + match db_type { + Type::Shared => EngineConfig { + r#type: "shared_postgres".to_string(), + image: "postgres:11".to_string(), + engine: "postgres".to_string(), + username: "postgres".to_string(), + password: "postgres".to_string(), + database_name: "postgres".to_string(), + port: "5432/tcp".to_string(), + env: Some(vec!["POSTGRES_PASSWORD=postgres".to_string()]), + is_ready_cmd: vec![ + "/bin/sh".to_string(), + "-c".to_string(), + "pg_isready | grep 'accepting connections'".to_string(), + ], + }, + Type::AwsRds(AwsRdsEngine::Postgres) => EngineConfig { + r#type: "aws_rds_postgres".to_string(), + image: "postgres:13.4".to_string(), + engine: "postgres".to_string(), + username: "postgres".to_string(), + password: "postgres".to_string(), + database_name: "postgres".to_string(), + port: "5432/tcp".to_string(), + env: Some(vec!["POSTGRES_PASSWORD=postgres".to_string()]), + is_ready_cmd: vec![ + "/bin/sh".to_string(), + "-c".to_string(), + "pg_isready | grep 'accepting connections'".to_string(), + ], + }, + Type::AwsRds(AwsRdsEngine::MariaDB) => EngineConfig { + r#type: "aws_rds_mariadb".to_string(), + image: "mariadb:10.6.7".to_string(), + engine: "mariadb".to_string(), + username: "root".to_string(), + password: "mariadb".to_string(), + database_name: "mysql".to_string(), + port: "3306/tcp".to_string(), + env: Some(vec!["MARIADB_ROOT_PASSWORD=mariadb".to_string()]), + is_ready_cmd: vec![ + "mysql".to_string(), + "-pmariadb".to_string(), + "--silent".to_string(), + "-e".to_string(), + "show databases;".to_string(), + ], + }, + Type::AwsRds(AwsRdsEngine::MySql) => EngineConfig { + r#type: "aws_rds_mysql".to_string(), + image: "mysql:8.0.28".to_string(), + engine: "mysql".to_string(), + username: "root".to_string(), + password: "mysql".to_string(), + database_name: "mysql".to_string(), + port: "3306/tcp".to_string(), + env: Some(vec!["MYSQL_ROOT_PASSWORD=mysql".to_string()]), + is_ready_cmd: vec![ + "mysql".to_string(), + "-pmysql".to_string(), + "--silent".to_string(), + "-e".to_string(), + "show databases;".to_string(), + ], + }, + } +} diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index f4643ab72..68f475f06 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -10,9 +10,11 @@ description = "Proc-macro code generator for the shuttle.rs service" proc-macro = true [dependencies] +proc-macro-error = "1.0" proc-macro2 = "1.0.39" quote = "1.0.18" -syn = { version = "1.0.96", features = ["full"] } +syn = { version = "1.0.96", features = ["full", "extra-traits"] } [dev-dependencies] pretty_assertions = "1.2.1" +trybuild = "1.0" diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index b8036dc49..a18dde819 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -1,12 +1,18 @@ use proc_macro::TokenStream; +use proc_macro_error::{emit_error, proc_macro_error}; use quote::{quote, ToTokens}; -use syn::{parse_macro_input, parse_quote, FnArg, Ident, ItemFn, Pat, ReturnType, Stmt}; +use syn::{ + parse_macro_input, parse_quote, spanned::Spanned, Attribute, FnArg, Ident, ItemFn, Pat, Path, + ReturnType, Stmt, +}; +#[proc_macro_error] #[proc_macro_attribute] pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream { - let fn_decl = parse_macro_input!(item as ItemFn); + let mut fn_decl = parse_macro_input!(item as ItemFn); + + let wrapper = Wrapper::from_item_fn(&mut fn_decl); - let wrapper = Wrapper::from_item_fn(&fn_decl); let expanded = quote! { #wrapper @@ -35,24 +41,44 @@ pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream { struct Wrapper { fn_ident: Ident, fn_output: ReturnType, - fn_inputs: Vec, + fn_inputs: Vec, +} + +#[derive(Debug, PartialEq)] +struct Input { + /// The identifier for a resource input + ident: Ident, + + /// The shuttle_service path to the builder for this resource + builder: Path, } impl Wrapper { - fn from_item_fn(item_fn: &ItemFn) -> Self { + fn from_item_fn(item_fn: &mut ItemFn) -> Self { let inputs: Vec<_> = item_fn .sig .inputs - .iter() + .iter_mut() .filter_map(|input| match input { FnArg::Receiver(_) => None, FnArg::Typed(typed) => Some(typed), }) .filter_map(|typed| match typed.pat.as_ref() { - Pat::Ident(ident) => Some(ident), + Pat::Ident(ident) => Some((ident, typed.attrs.drain(..).collect())), _ => None, }) - .map(|pat_ident| pat_ident.ident.clone()) + .filter_map(|(pat_ident, attrs)| { + match attribute_to_path(attrs) { + Ok(builder) => Some(Input { + ident: pat_ident.ident.clone(), + builder, + }), + Err(err) => { + emit_error!(pat_ident, err; hint = pat_ident.span() => "Try adding a config like `#[shared::Postgres]`"); + None + } + } + }) .collect(); Self { @@ -63,11 +89,22 @@ impl Wrapper { } } +fn attribute_to_path(attrs: Vec) -> Result { + if attrs.is_empty() { + return Err("resource needs an attribute configuration".to_string()); + } + + let builder = attrs[0].path.clone(); + + Ok(builder) +} + impl ToTokens for Wrapper { fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { let fn_output = &self.fn_output; let fn_ident = &self.fn_ident; - let fn_inputs = &self.fn_inputs; + let fn_inputs: Vec<_> = self.fn_inputs.iter().map(|i| i.ident.clone()).collect(); + let fn_inputs_builder: Vec<_> = self.fn_inputs.iter().map(|i| i.builder.clone()).collect(); let factory_ident: Ident = if self.fn_inputs.is_empty() { parse_quote!(_factory) @@ -79,7 +116,7 @@ impl ToTokens for Wrapper { None } else { Some(parse_quote!( - use shuttle_service::GetResource; + use shuttle_service::ResourceBuilder; )) }; @@ -98,7 +135,7 @@ impl ToTokens for Wrapper { }).await.unwrap(); - #(let #fn_inputs = #factory_ident.get_resource(runtime).await?;)* + #(let #fn_inputs = shuttle_service::#fn_inputs_builder::new().build(#factory_ident, runtime).await?;)* runtime.spawn(#fn_ident(#(#fn_inputs),*)).await.unwrap() } @@ -114,20 +151,20 @@ mod tests { use quote::quote; use syn::{parse_quote, Ident, ReturnType}; - use crate::Wrapper; + use crate::{Input, Wrapper}; #[test] fn from_missing_return() { - let input = parse_quote!( + let mut input = parse_quote!( async fn simple() {} ); - let actual = Wrapper::from_item_fn(&input); + let actual = Wrapper::from_item_fn(&mut input); let expected_ident: Ident = parse_quote!(simple); assert_eq!(actual.fn_ident, expected_ident); assert_eq!(actual.fn_output, ReturnType::Default); - assert_eq!(actual.fn_inputs, Vec::::new()); + assert_eq!(actual.fn_inputs, Vec::::new()); } #[test] @@ -160,17 +197,17 @@ mod tests { #[test] fn from_with_return() { - let input = parse_quote!( + let mut input = parse_quote!( async fn complex() -> Result<(), Box> {} ); - let actual = Wrapper::from_item_fn(&input); + let actual = Wrapper::from_item_fn(&mut input); let expected_ident: Ident = parse_quote!(complex); let expected_output: ReturnType = parse_quote!(-> Result<(), Box>); assert_eq!(actual.fn_ident, expected_ident); assert_eq!(actual.fn_output, expected_output); - assert_eq!(actual.fn_inputs, Vec::::new()); + assert_eq!(actual.fn_inputs, Vec::::new()); } #[test] @@ -203,18 +240,35 @@ mod tests { #[test] fn from_with_inputs() { - let input = parse_quote!( - async fn complex(pool: PgPool) -> Result<(), Box> {} + let mut input = parse_quote!( + async fn complex( + #[shared::Postgres] pool: PgPool, + ) -> Result<(), Box> { + } ); - let actual = Wrapper::from_item_fn(&input); + let actual = Wrapper::from_item_fn(&mut input); let expected_ident: Ident = parse_quote!(complex); let expected_output: ReturnType = parse_quote!(-> Result<(), Box>); - let expected_inputs: Vec = vec![parse_quote!(pool)]; + let expected_inputs: Vec = vec![Input { + ident: parse_quote!(pool), + builder: parse_quote!(shared::Postgres), + }]; assert_eq!(actual.fn_ident, expected_ident); assert_eq!(actual.fn_output, expected_output); assert_eq!(actual.fn_inputs, expected_inputs); + + // Make sure attributes was removed from input + if let syn::FnArg::Typed(param) = input.sig.inputs.first().unwrap() { + assert!( + param.attrs.is_empty(), + "some attributes were not removed: {:?}", + param.attrs + ); + } else { + panic!("expected first input to be typed") + } } #[test] @@ -222,7 +276,16 @@ mod tests { let input = Wrapper { fn_ident: parse_quote!(complex), fn_output: parse_quote!(-> Result<(), Box>), - fn_inputs: vec![parse_quote!(pool), parse_quote!(redis)], + fn_inputs: vec![ + Input { + ident: parse_quote!(pool), + builder: parse_quote!(shared::Postgres), + }, + Input { + ident: parse_quote!(redis), + builder: parse_quote!(shared::Redis), + }, + ], }; let actual = quote!(#input); @@ -232,7 +295,7 @@ mod tests { runtime: &shuttle_service::Runtime, logger: Box, ) -> Result<(), Box > { - use shuttle_service::GetResource; + use shuttle_service::ResourceBuilder; runtime.spawn_blocking(move || { shuttle_service::log::set_boxed_logger(logger) @@ -240,8 +303,8 @@ mod tests { .expect("logger set should succeed"); }).await.unwrap(); - let pool = factory.get_resource(runtime).await?; - let redis = factory.get_resource(runtime).await?; + let pool = shuttle_service::shared::Postgres::new().build(factory, runtime).await?; + let redis = shuttle_service::shared::Redis::new().build(factory, runtime).await?; runtime.spawn(complex(pool, redis)).await.unwrap() } @@ -249,4 +312,10 @@ mod tests { assert_eq!(actual.to_string(), expected.to_string()); } + + #[test] + fn ui() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/ui/*.rs"); + } } diff --git a/codegen/tests/ui/missing-attribute.rs b/codegen/tests/ui/missing-attribute.rs new file mode 100644 index 000000000..54ce54049 --- /dev/null +++ b/codegen/tests/ui/missing-attribute.rs @@ -0,0 +1,2 @@ +#[shuttle_codegen::main] +async fn missing_attriute(pool: PgPool, cache: Redis) {} diff --git a/codegen/tests/ui/missing-attribute.stderr b/codegen/tests/ui/missing-attribute.stderr new file mode 100644 index 000000000..fafa2434b --- /dev/null +++ b/codegen/tests/ui/missing-attribute.stderr @@ -0,0 +1,23 @@ +error: resource needs an attribute configuration + + = help: Try adding a config like `#[shared::Postgres]` + + --> tests/ui/missing-attribute.rs:2:27 + | +2 | async fn missing_attriute(pool: PgPool, cache: Redis) {} + | ^^^^ + +error: resource needs an attribute configuration + + = help: Try adding a config like `#[shared::Postgres]` + + --> tests/ui/missing-attribute.rs:2:41 + | +2 | async fn missing_attriute(pool: PgPool, cache: Redis) {} + | ^^^^^ + +error[E0601]: `main` function not found in crate `$CRATE` + --> tests/ui/missing-attribute.rs:2:57 + | +2 | async fn missing_attriute(pool: PgPool, cache: Redis) {} + | ^ consider adding a `main` function to `$DIR/tests/ui/missing-attribute.rs` diff --git a/common/src/database.rs b/common/src/database.rs new file mode 100644 index 000000000..6618b8ae8 --- /dev/null +++ b/common/src/database.rs @@ -0,0 +1,10 @@ +pub enum Type { + AwsRds(AwsRdsEngine), + Shared, +} + +pub enum AwsRdsEngine { + Postgres, + MySql, + MariaDB, +} diff --git a/common/src/lib.rs b/common/src/lib.rs index 091ed3688..ebbdeb50e 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -1,3 +1,4 @@ +pub mod database; pub mod project; use std::{ @@ -70,19 +71,13 @@ impl DeploymentMeta { } } -#[cfg(debug_assertions)] -const PUBLIC_IP: &str = "localhost"; - -#[cfg(not(debug_assertions))] -const PUBLIC_IP: &'static str = "pg.shuttle.rs"; - impl Display for DeploymentMeta { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let db = { if let Some(info) = &self.database_deployment { format!( "\n Database URI: {}", - info.connection_string(PUBLIC_IP) + info.connection_string_public() ) } else { "".to_string() @@ -104,30 +99,55 @@ impl Display for DeploymentMeta { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DatabaseReadyInfo { - pub role_name: String, - pub role_password: String, - pub database_name: String, - pub port: String, + engine: String, + role_name: String, + role_password: String, + database_name: String, + port: String, + address_private: String, + address_public: String, } impl DatabaseReadyInfo { pub fn new( + engine: String, role_name: String, role_password: String, database_name: String, port: String, + address_private: String, + address_public: String, ) -> Self { Self { + engine, role_name, role_password, database_name, port, + address_private, + address_public, } } - pub fn connection_string(&self, ip: &str) -> String { + pub fn connection_string_private(&self) -> String { + format!( + "{}://{}:{}@{}:{}/{}", + self.engine, + self.role_name, + self.role_password, + self.address_private, + self.port, + self.database_name + ) + } + pub fn connection_string_public(&self) -> String { format!( - "postgres://{}:{}@{}:{}/{}", - self.role_name, self.role_password, ip, self.port, self.database_name + "{}://{}:{}@{}:{}/{}", + self.engine, + self.role_name, + self.role_password, + self.address_public, + self.port, + self.database_name ) } } diff --git a/docker-compose.yml b/docker-compose.yml index d77f6fa6b..d7728b3c4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,4 +20,5 @@ services: context: . dockerfile: provisioner/Containerfile.dev environment: - - PORT=5001 \ No newline at end of file + - FQDN=unreacable + - PORT=5001 diff --git a/e2e/tests/helpers/mod.rs b/e2e/tests/helpers/mod.rs index 2ed41c8a4..6c84b4c6a 100644 --- a/e2e/tests/helpers/mod.rs +++ b/e2e/tests/helpers/mod.rs @@ -153,6 +153,10 @@ impl Services { "--network", "shuttle-net", "-e", + "FQDN=unreacable", + "-e", + &format!("PROVISIONER_ADDRESS={provisioner_container}"), + "-e", "PORT=5001", &provisioner_image, ]); diff --git a/examples/rocket/postgres/src/lib.rs b/examples/rocket/postgres/src/lib.rs index faede01bf..2e61dc89a 100644 --- a/examples/rocket/postgres/src/lib.rs +++ b/examples/rocket/postgres/src/lib.rs @@ -1,12 +1,12 @@ #[macro_use] extern crate rocket; -use shuttle_service::SecretStore; -use shuttle_service::error::CustomError; use rocket::response::status::BadRequest; use rocket::serde::json::Json; use rocket::State; use serde::{Deserialize, Serialize}; +use shuttle_service::error::CustomError; +use shuttle_service::SecretStore; use sqlx::{Executor, FromRow, PgPool}; #[get("/")] @@ -37,7 +37,11 @@ async fn add( #[get("/secret")] async fn secret(state: &State) -> Result> { // get secret defined in `Secrets.toml` file. - state.pool.get_secret("MY_API_KEY").await.map_err(|e| BadRequest(Some(e.to_string()))) + state + .pool + .get_secret("MY_API_KEY") + .await + .map_err(|e| BadRequest(Some(e.to_string()))) } struct MyState { @@ -45,7 +49,7 @@ struct MyState { } #[shuttle_service::main] -async fn rocket(pool: PgPool) -> shuttle_service::ShuttleRocket { +async fn rocket(#[shared::Postgres] pool: PgPool) -> shuttle_service::ShuttleRocket { pool.execute(include_str!("../schema.sql")) .await .map_err(CustomError::new)?; diff --git a/examples/tide/postgres/Cargo.toml b/examples/tide/postgres/Cargo.toml new file mode 100644 index 000000000..95c0274c6 --- /dev/null +++ b/examples/tide/postgres/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "postgres" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde = { version = "1.0", features = ["derive"] } +shuttle-service = { version = "0.3.3", features = ["sqlx-aws-postgres", "web-tide"] } +sqlx = { version = "0.5", features = ["runtime-tokio-native-tls", "postgres"] } +tide = "0.16.0" diff --git a/examples/tide/postgres/Shuttle.toml b/examples/tide/postgres/Shuttle.toml new file mode 100644 index 000000000..4ffbeb3e5 --- /dev/null +++ b/examples/tide/postgres/Shuttle.toml @@ -0,0 +1 @@ +name = "postgres-tide-app" diff --git a/examples/tide/postgres/schema.sql b/examples/tide/postgres/schema.sql new file mode 100644 index 000000000..460e7c23d --- /dev/null +++ b/examples/tide/postgres/schema.sql @@ -0,0 +1,6 @@ +DROP TABLE IF EXISTS todos; + +CREATE TABLE todos ( + id serial PRIMARY KEY, + note TEXT NOT NULL +); diff --git a/examples/tide/postgres/src/lib.rs b/examples/tide/postgres/src/lib.rs new file mode 100644 index 000000000..bf6a481ff --- /dev/null +++ b/examples/tide/postgres/src/lib.rs @@ -0,0 +1,56 @@ +use serde::{Deserialize, Serialize}; +use shuttle_service::{error::CustomError, ShuttleTide}; +use sqlx::{Executor, FromRow, PgPool}; +use tide::{Body, Request}; + +async fn retrieve(req: Request) -> tide::Result { + let id: i32 = req.param("id")?.parse()?; + let todo: Todo = sqlx::query_as("SELECT * FROM todos WHERE id = $1") + .bind(id) + .fetch_one(&req.state().pool) + .await?; + + Body::from_json(&todo).map(Into::into) +} + +async fn add(mut req: Request) -> tide::Result { + let data: TodoNew = req.body_json().await?; + let todo: Todo = sqlx::query_as("INSERT INTO todos(note) VALUES ($1) RETURNING id, note") + .bind(&data.note) + .fetch_one(&req.state().pool) + .await?; + + Body::from_json(&todo).map(Into::into) +} + +#[derive(Clone)] +struct MyState { + pool: PgPool, +} + +#[shuttle_service::main] +async fn tide(#[aws::rds::Postgres] pool: PgPool) -> ShuttleTide { + pool.execute(include_str!("../schema.sql")) + .await + .map_err(CustomError::new)?; + + let state = MyState { pool }; + let mut app = tide::with_state(state); + + app.with(tide::log::LogMiddleware::new()); + app.at("/todo").post(add); + app.at("/todo/:id").get(retrieve); + + Ok(app) +} + +#[derive(Deserialize)] +struct TodoNew { + pub note: String, +} + +#[derive(Serialize, FromRow)] +struct Todo { + pub id: i32, + pub note: String, +} diff --git a/examples/url-shortener/src/lib.rs b/examples/url-shortener/src/lib.rs index 84475cbfc..c38d3f93a 100644 --- a/examples/url-shortener/src/lib.rs +++ b/examples/url-shortener/src/lib.rs @@ -4,10 +4,10 @@ extern crate rocket; use rocket::{ http::Status, response::{status, Redirect}, - routes, Build, Rocket, State, + routes, State, }; -use serde::{Serialize}; -use shuttle_service::error::CustomError; +use serde::Serialize; +use shuttle_service::{error::CustomError, ShuttleRocket}; use sqlx::migrate::Migrator; use sqlx::{FromRow, PgPool}; use url::Url; @@ -71,7 +71,7 @@ async fn shorten(url: String, state: &State) -> Result Result, shuttle_service::Error> { +async fn rocket(#[shared::Postgres] pool: PgPool) -> ShuttleRocket { MIGRATOR.run(&pool).await.map_err(CustomError::new)?; let state = AppState { pool }; diff --git a/proto/provisioner.proto b/proto/provisioner.proto index f93705325..fe0dd704b 100644 --- a/proto/provisioner.proto +++ b/proto/provisioner.proto @@ -7,10 +7,30 @@ service Provisioner { message DatabaseRequest { string project_name = 1; + oneof db_type { + string shared = 10; + AwsRds AwsRds = 11; + }; +} + +message AwsRds { + oneof engine { + RdsConfig postgres = 1; + RdsConfig mysql = 2; + RdsConfig mariadb = 3; + } +} + +message RdsConfig { + } message DatabaseResponse { string username = 1; string password = 2; string database_name = 3; + string engine = 4; + string address_private = 5; + string address_public = 6; + string port = 7; } diff --git a/proto/src/lib.rs b/proto/src/lib.rs index cb2bac417..cc1d3919e 100644 --- a/proto/src/lib.rs +++ b/proto/src/lib.rs @@ -1,16 +1,53 @@ pub mod provisioner { - use shuttle_common::DatabaseReadyInfo; + use std::fmt::Display; + + use shuttle_common::{ + database::{self, AwsRdsEngine}, + DatabaseReadyInfo, + }; tonic::include_proto!("provisioner"); impl From for DatabaseReadyInfo { fn from(response: DatabaseResponse) -> Self { DatabaseReadyInfo::new( + response.engine, response.username, response.password, response.database_name, - "5432".to_string(), + response.port, + response.address_private, + response.address_public, ) } } + + impl From for database_request::DbType { + fn from(db_type: database::Type) -> Self { + match db_type { + database::Type::Shared => database_request::DbType::Shared(String::new()), + database::Type::AwsRds(engine) => { + let config = RdsConfig {}; + let engine = match engine { + AwsRdsEngine::Postgres => aws_rds::Engine::Postgres(config), + AwsRdsEngine::MariaDB => aws_rds::Engine::Mariadb(config), + AwsRdsEngine::MySql => aws_rds::Engine::Mysql(config), + }; + database_request::DbType::AwsRds(AwsRds { + engine: Some(engine), + }) + } + } + } + } + + impl Display for aws_rds::Engine { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Mariadb(_) => write!(f, "mariadb"), + Self::Mysql(_) => write!(f, "mysql"), + Self::Postgres(_) => write!(f, "postgres"), + } + } + } } diff --git a/provisioner/Cargo.toml b/provisioner/Cargo.toml index fe89731d3..252cebd95 100644 --- a/provisioner/Cargo.toml +++ b/provisioner/Cargo.toml @@ -6,7 +6,11 @@ description = "Service responsible for provisioning and managing resources for s # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +aws-config = "0.12" +aws-sdk-rds = "0.12" +aws-smithy-types = "0.42" clap = { version = "3.1.18", features = ["derive", "env"] } +fqdn = "0.1.9" prost = "0.10.4" rand = "0.8.5" sqlx = { version = "0.5.13", features = ["postgres", "runtime-tokio-native-tls"] } diff --git a/provisioner/docker/entrypoint.sh b/provisioner/docker/entrypoint.sh index af1fec058..ba81a63e5 100755 --- a/provisioner/docker/entrypoint.sh +++ b/provisioner/docker/entrypoint.sh @@ -36,4 +36,6 @@ export PG_HOST=localhost export PG_URI=postgres://postgres:${PG_PASSWORD}@localhost:${PG_PORT}/postgres +export PROVISIONER_ADDRESS=${PROVISIONER_ADDRESS:-provisioner} + exec supervisord -n -c /usr/share/supervisord/supervisord.conf diff --git a/provisioner/docker/supervisord.conf b/provisioner/docker/supervisord.conf index d085dcf30..0a40c0028 100644 --- a/provisioner/docker/supervisord.conf +++ b/provisioner/docker/supervisord.conf @@ -8,7 +8,7 @@ startsecs=20 autorestart=true [program:shuttle-provisioner] -command=/usr/bin/wait-for-pg-then /usr/local/bin/shuttle-provisioner --ip 0.0.0.0 --port %(ENV_PORT)s --shared-pg-uri %(ENV_PG_URI)s +command=/usr/bin/wait-for-pg-then /usr/local/bin/shuttle-provisioner --ip 0.0.0.0 --port %(ENV_PORT)s --shared-pg-uri %(ENV_PG_URI)s --fqdn %(ENV_FQDN)s redirect_stderr=true environment=RUST_BACKTRACE="1",RUST_LOG="debug" startretries=3 diff --git a/provisioner/src/args.rs b/provisioner/src/args.rs index 3bf09c09e..567cbc38d 100644 --- a/provisioner/src/args.rs +++ b/provisioner/src/args.rs @@ -1,19 +1,40 @@ -use std::net::{IpAddr, Ipv4Addr}; +use std::{ + net::{IpAddr, Ipv4Addr}, + str::FromStr, +}; use clap::Parser; +use fqdn::FQDN; #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] pub struct Args { /// Address to bind provisioner on - #[clap(short, long, env = "PROVISIONER_IP", default_value_t = Ipv4Addr::LOCALHOST.into())] + #[clap(long, env = "PROVISIONER_IP", default_value_t = Ipv4Addr::LOCALHOST.into())] pub ip: IpAddr, /// Port to start provisioner on - #[clap(short, long, env = "PROVISIONER_PORT", default_value_t = 5001)] + #[clap(long, env = "PROVISIONER_PORT", default_value_t = 5001)] pub port: u16, /// URI to connect to Postgres for managing shared DB resources #[clap(short, long, env = "PROVISIONER_PG_URI", hide_env_values = true)] pub shared_pg_uri: String, + + /// Fully qualified domain name this provisioner instance is reachable at + #[clap(long, env = "PROVISIONER_FQDN", parse(try_from_str = parse_fqdn))] + pub fqdn: FQDN, + + /// Address this provisioner can be reached at on the internal network + #[clap( + short, + long, + env = "PROVISIONER_ADDRESS", + default_value = "provisioner" + )] + pub internal_address: String, +} + +fn parse_fqdn(src: &str) -> Result { + FQDN::from_str(src).map_err(|e| format!("{e:?}")) } diff --git a/provisioner/src/error.rs b/provisioner/src/error.rs index 6e91436d7..7ebfb34a9 100644 --- a/provisioner/src/error.rs +++ b/provisioner/src/error.rs @@ -1,3 +1,7 @@ +use aws_sdk_rds::{ + error::{CreateDBInstanceError, DescribeDBInstancesError}, + types::SdkError, +}; use thiserror::Error; use tonic::Status; use tracing::error; @@ -15,6 +19,15 @@ pub enum Error { #[error("unexpected error")] Unexpected(#[from] sqlx::Error), + + #[error("failed to create RDS instance")] + CreateRDSInstance(#[from] SdkError), + + #[error("failed to get description of RDS instance")] + DescribeRDSInstance(#[from] SdkError), + + #[error["plain error"]] + Plain(String), } unsafe impl Send for Error {} diff --git a/provisioner/src/lib.rs b/provisioner/src/lib.rs index 47ca163c5..0a3e882b6 100644 --- a/provisioner/src/lib.rs +++ b/provisioner/src/lib.rs @@ -1,30 +1,61 @@ use std::time::Duration; pub use args::Args; +use aws_config::timeout; +use aws_sdk_rds::{error::ModifyDBInstanceErrorKind, model::DbInstance, types::SdkError, Client}; +use aws_smithy_types::tristate::TriState; pub use error::Error; use proto::provisioner::provisioner_server::Provisioner; pub use proto::provisioner::provisioner_server::ProvisionerServer; -use proto::provisioner::{DatabaseRequest, DatabaseResponse}; +use proto::provisioner::{ + aws_rds, database_request::DbType, AwsRds, DatabaseRequest, DatabaseResponse, +}; use rand::Rng; use sqlx::{postgres::PgPoolOptions, PgPool}; +use tokio::time::sleep; use tonic::{Request, Response, Status}; -use tracing::info; +use tracing::{debug, info}; mod args; mod error; +const AWS_RDS_CLASS: &str = "db.t4g.micro"; +const MASTER_USERNAME: &str = "master"; +const RDS_SUBNET_GROUP: &str = "shuttle_rds"; + pub struct MyProvisioner { pool: PgPool, + rds_client: aws_sdk_rds::Client, + fqdn: String, + internal_address: String, } impl MyProvisioner { - pub fn new(uri: &str) -> sqlx::Result { + pub async fn new(db_uri: &str, fqdn: String, internal_address: String) -> sqlx::Result { + let pool = PgPoolOptions::new() + .min_connections(4) + .max_connections(12) + .connect_timeout(Duration::from_secs(60)) + .connect_lazy(db_uri)?; + + // Default timeout is too long so lowering it + let api_timeout_config = timeout::Api::new() + .with_call_timeout(TriState::Set(Duration::from_secs(120))) + .with_call_attempt_timeout(TriState::Set(Duration::from_secs(120))); + let timeout_config = timeout::Config::new().with_api_timeouts(api_timeout_config); + + let aws_config = aws_config::from_env() + .timeout_config(timeout_config) + .load() + .await; + + let rds_client = aws_sdk_rds::Client::new(&aws_config); + Ok(Self { - pool: PgPoolOptions::new() - .min_connections(4) - .max_connections(12) - .connect_timeout(Duration::from_secs(60)) - .connect_lazy(uri)?, + pool, + rds_client, + fqdn, + internal_address, }) } @@ -33,9 +64,13 @@ impl MyProvisioner { let database_name = self.shared_db(project_name, &username).await?; Ok(DatabaseResponse { + engine: "postgres".to_string(), username, password, database_name, + address_private: self.internal_address.clone(), + address_public: self.fqdn.clone(), + port: "5432".to_string(), }) } @@ -97,6 +132,90 @@ impl MyProvisioner { Ok(database_name) } + + async fn request_aws_rds( + &self, + project_name: &str, + engine: aws_rds::Engine, + ) -> Result { + let client = &self.rds_client; + + let password = generate_password(); + let instance_name = format!("{}-{}", project_name, engine); + + debug!("trying to get AWS RDS instance: {instance_name}"); + let instance = client + .modify_db_instance() + .db_instance_identifier(&instance_name) + .master_user_password(&password) + .send() + .await; + + match instance { + Ok(_) => { + wait_for_instance(client, &instance_name, "resetting-master-credentials").await?; + } + Err(SdkError::ServiceError { err, .. }) => { + if let ModifyDBInstanceErrorKind::DbInstanceNotFoundFault(_) = err.kind { + debug!("creating new AWS RDS {instance_name}"); + + client + .create_db_instance() + .db_instance_identifier(&instance_name) + .master_username(MASTER_USERNAME) + .master_user_password(&password) + .engine(engine.to_string()) + .db_instance_class(AWS_RDS_CLASS) + .allocated_storage(20) + .backup_retention_period(0) // Disable backups + .publicly_accessible(true) + .db_name(engine.to_string()) + .set_db_subnet_group_name(Some(RDS_SUBNET_GROUP.to_string())) + .send() + .await? + .db_instance + .expect("to be able to create instance"); + + wait_for_instance(client, &instance_name, "creating").await?; + } else { + return Err(Error::Plain(format!( + "got unexpected error from AWS RDS service: {}", + err + ))); + } + } + Err(unexpected) => { + return Err(Error::Plain(format!( + "got unexpected error from AWS during API call: {}", + unexpected + ))) + } + }; + + // Wait for up + let instance = wait_for_instance(client, &instance_name, "available").await?; + + // TODO: find private IP somehow + let address = instance + .endpoint + .expect("instance to have an endpoint") + .address + .expect("endpoint to have an address"); + + Ok(DatabaseResponse { + engine: engine.to_string(), + username: instance + .master_username + .expect("instance to have a username"), + password, + database_name: instance + .db_name + .expect("instance to have a default database"), + address_private: address.clone(), + address_public: address, + port: engine_to_port(engine), + }) + } } #[tonic::async_trait] @@ -106,9 +225,16 @@ impl Provisioner for MyProvisioner { &self, request: Request, ) -> Result, Status> { - let reply = self - .request_shared_db(&request.into_inner().project_name) - .await?; + let request = request.into_inner(); + let db_type = request.db_type.unwrap(); + + let reply = match db_type { + DbType::Shared(_) => self.request_shared_db(&request.project_name).await?, + DbType::AwsRds(AwsRds { engine }) => { + self.request_aws_rds(&request.project_name, engine.expect("oneof to be set")) + .await? + } + }; Ok(Response::new(reply)) } @@ -121,3 +247,43 @@ fn generate_password() -> String { .map(char::from) .collect() } + +async fn wait_for_instance( + client: &Client, + name: &str, + wait_for: &str, +) -> Result { + debug!("waiting for {name} to enter {wait_for} state"); + loop { + let instance = client + .describe_db_instances() + .db_instance_identifier(name) + .send() + .await? + .db_instances + .expect("aws to return instances") + .get(0) + .expect("to find the instance just created or modified") + .clone(); + + let status = instance + .db_instance_status + .as_ref() + .expect("instance to have a status") + .clone(); + + if status == wait_for { + return Ok(instance); + } + + sleep(Duration::from_secs(1)).await; + } +} + +fn engine_to_port(engine: aws_rds::Engine) -> String { + match engine { + aws_rds::Engine::Postgres(_) => "5432".to_string(), + aws_rds::Engine::Mariadb(_) => "3306".to_string(), + aws_rds::Engine::Mysql(_) => "3306".to_string(), + } +} diff --git a/provisioner/src/main.rs b/provisioner/src/main.rs index 37a196adb..5575a3b80 100644 --- a/provisioner/src/main.rs +++ b/provisioner/src/main.rs @@ -12,10 +12,14 @@ async fn main() -> Result<(), Box> { ip, port, shared_pg_uri, + fqdn, + internal_address, } = Args::parse(); let addr = SocketAddr::new(ip, port); - let provisioner = MyProvisioner::new(&shared_pg_uri).unwrap(); + let provisioner = MyProvisioner::new(&shared_pg_uri, fqdn.to_string(), internal_address) + .await + .unwrap(); println!("starting provisioner on {}", addr); Server::builder() diff --git a/provisioner/tests/provisioner.rs b/provisioner/tests/provisioner.rs index cb04fe8da..7276672f2 100644 --- a/provisioner/tests/provisioner.rs +++ b/provisioner/tests/provisioner.rs @@ -110,7 +110,9 @@ fn exec(query: &str) -> String { #[tokio::test] async fn shared_db_role_does_not_exist() { - let provisioner = MyProvisioner::new(&PG.uri).unwrap(); + let provisioner = MyProvisioner::new(&PG.uri, "fqdn".to_string(), "internal".to_string()) + .await + .unwrap(); assert_eq!( exec("SELECT rolname FROM pg_roles WHERE rolname = 'user-not_exist'"), @@ -127,7 +129,9 @@ async fn shared_db_role_does_not_exist() { #[tokio::test] async fn shared_db_role_does_exist() { - let provisioner = MyProvisioner::new(&PG.uri).unwrap(); + let provisioner = MyProvisioner::new(&PG.uri, "fqdn".to_string(), "internal".to_string()) + .await + .unwrap(); exec("CREATE ROLE \"user-exist\" WITH LOGIN PASSWORD 'temp'"); assert_eq!( @@ -149,7 +153,9 @@ async fn shared_db_role_does_exist() { expected = "CreateRole(\"error returned from database: cannot insert multiple commands into a prepared statement\"" )] async fn injection_safe() { - let provisioner = MyProvisioner::new(&PG.uri).unwrap(); + let provisioner = MyProvisioner::new(&PG.uri, "fqdn".to_string(), "internal".to_string()) + .await + .unwrap(); provisioner .request_shared_db("new\"; CREATE ROLE \"injected") @@ -159,7 +165,9 @@ async fn injection_safe() { #[tokio::test] async fn shared_db_missing() { - let provisioner = MyProvisioner::new(&PG.uri).unwrap(); + let provisioner = MyProvisioner::new(&PG.uri, "fqdn".to_string(), "internal".to_string()) + .await + .unwrap(); assert_eq!( exec("SELECT datname FROM pg_database WHERE datname = 'db-missing'"), @@ -176,7 +184,9 @@ async fn shared_db_missing() { #[tokio::test] async fn shared_db_filled() { - let provisioner = MyProvisioner::new(&PG.uri).unwrap(); + let provisioner = MyProvisioner::new(&PG.uri, "fqdn".to_string(), "internal".to_string()) + .await + .unwrap(); exec("CREATE ROLE \"user-filled\" WITH LOGIN PASSWORD 'temp'"); exec("CREATE DATABASE \"db-filled\" OWNER 'user-filled'"); diff --git a/service/Cargo.toml b/service/Cargo.toml index 3b5373882..4873055f7 100644 --- a/service/Cargo.toml +++ b/service/Cargo.toml @@ -19,6 +19,7 @@ hyper = { version = "0.14.19", features = ["server", "tcp", "http1"], optional = lazy_static = "1.4.0" libloading = { version = "0.7.3", optional = true } log = "0.4.17" +paste = "1.0.7" regex = "1.5.6" rocket = { version = "0.5.0-rc.2", optional = true } sqlx = { version = "0.5.13", optional = true } @@ -28,19 +29,25 @@ tide = { version = "0.16.0", optional = true } tokio = { version = "1.19.2", features = ["rt", "rt-multi-thread"] } tower = { version = "0.4.12", features = ["make"], optional = true } +# Tide does not have tokio support. So make sure async-std is compatible with tokio +# https://github.com/http-rs/tide/issues/791 +[dependencies.async-std] +version = "1" +features = ["tokio1"] + [dependencies.shuttle-codegen] version = "0.3.1" path = "../codegen" optional = true +[dev-dependencies] +portpicker = "0.1.1" +uuid = { version = "1.1.1", features = ["v4"] } + [dependencies.shuttle-common] version = "0.3.1" path = "../common" -[dev-dependencies] -portpicker = "0.1.1" -uuid = "1.1.1" - [features] default = ["codegen"] codegen = ["shuttle-codegen"] @@ -48,6 +55,9 @@ loader = ["cargo", "libloading"] sqlx-integration = ["sqlx/runtime-tokio-native-tls"] sqlx-postgres = ["sqlx-integration", "sqlx/postgres"] +sqlx-aws-postgres = ["sqlx-integration", "sqlx/postgres"] +sqlx-aws-mysql = ["sqlx-integration", "sqlx/mysql"] +sqlx-aws-mariadb = ["sqlx-integration", "sqlx/mysql"] secrets = ["sqlx-postgres"] diff --git a/service/src/aws/mod.rs b/service/src/aws/mod.rs new file mode 100644 index 000000000..83d80c0c8 --- /dev/null +++ b/service/src/aws/mod.rs @@ -0,0 +1 @@ +pub mod rds; diff --git a/service/src/aws/rds.rs b/service/src/aws/rds.rs new file mode 100644 index 000000000..dc3d2fbf4 --- /dev/null +++ b/service/src/aws/rds.rs @@ -0,0 +1,69 @@ +use crate::{ + database::{AwsRdsEngine, Type}, + error::CustomError, + Factory, ResourceBuilder, +}; +use async_trait::async_trait; +use paste::paste; +use tokio::runtime::Runtime; + +macro_rules! aws_engine { + ($feature:expr, $pool_path:path, $options_path:path, $struct_ident:ident) => { + paste! { + #[cfg(feature = $feature)] + #[doc = "A resource connected to an AWS RDS " $struct_ident " instance"] + pub struct $struct_ident; + + #[cfg(feature = $feature)] + #[doc = "Gets a `sqlx::Pool` connected to an AWS RDS " $struct_ident " instance"] + #[async_trait] + impl ResourceBuilder<$pool_path> for $struct_ident { + fn new() -> Self { + Self {} + } + + async fn build(self, factory: &mut dyn Factory, runtime: &Runtime) -> Result<$pool_path, crate::Error> { + let connection_string = factory + .get_sql_connection_string(Type::AwsRds(AwsRdsEngine::$struct_ident)) + .await?; + + // A sqlx Pool cannot cross runtime boundaries, so make sure to create the Pool on the service end + let pool = runtime + .spawn(async move { + $options_path::new() + .min_connections(1) + .max_connections(5) + .connect(&connection_string) + .await + }) + .await + .map_err(CustomError::new)? + .map_err(CustomError::new)?; + + Ok(pool) + } + } + } + }; +} + +aws_engine!( + "sqlx-aws-postgres", + sqlx::PgPool, + sqlx::postgres::PgPoolOptions, + Postgres +); + +aws_engine!( + "sqlx-aws-mysql", + sqlx::MySqlPool, + sqlx::mysql::MySqlPoolOptions, + MySql +); + +aws_engine!( + "sqlx-aws-mariadb", + sqlx::MySqlPool, + sqlx::mysql::MySqlPoolOptions, + MariaDB +); diff --git a/service/src/lib.rs b/service/src/lib.rs index c1df2944c..681791b00 100644 --- a/service/src/lib.rs +++ b/service/src/lib.rs @@ -133,7 +133,7 @@ //! } //! //! #[shuttle_service::main] -//! async fn rocket(pool: PgPool) -> ShuttleRocket { +//! async fn rocket(#[shared::Postgres] pool: PgPool) -> ShuttleRocket { //! let state = MyState(pool); //! let rocket = rocket::build().manage(state).mount("/", routes![hello]); //! @@ -227,11 +227,23 @@ pub use error::Error; pub mod logger; +pub use shuttle_common::database; + +#[cfg(feature = "sqlx-postgres")] +pub mod shared; + #[cfg(feature = "secrets")] pub mod secrets; #[cfg(feature = "secrets")] pub use secrets::SecretStore; +#[cfg(any( + feature = "sqlx-aws-mariadb", + feature = "sqlx-aws-mysql", + feature = "sqlx-aws-postgres" +))] +pub mod aws; + #[cfg(feature = "codegen")] extern crate shuttle_codegen; #[cfg(feature = "codegen")] @@ -263,7 +275,7 @@ extern crate shuttle_codegen; /// | `Result` | web-tower | [tower](https://docs.rs/tower/0.4.12) | 0.14.12 | [GitHub](https://github.com/getsynth/shuttle/tree/main/examples/tower/hello-world) | /// /// # Getting shuttle managed services -/// Shuttle is able to manage service dependencies for you. These are passed in as inputs to your `#[shuttle_service::main]` function: +/// Shuttle is able to manage service dependencies for you. These services are passed in as inputs to your `#[shuttle_service::main]` function and are configured using attributes: /// ```rust,no_run /// use sqlx::PgPool; /// use shuttle_service::ShuttleRocket; @@ -271,7 +283,7 @@ extern crate shuttle_codegen; /// struct MyState(PgPool); /// /// #[shuttle_service::main] -/// async fn rocket(pool: PgPool) -> ShuttleRocket { +/// async fn rocket(#[shared::Postgres] pool: PgPool) -> ShuttleRocket { /// let state = MyState(pool); /// let rocket = rocket::build().manage(state); /// @@ -280,11 +292,14 @@ extern crate shuttle_codegen; /// ``` /// /// ## shuttle managed dependencies -/// The following dependencies can be managed by shuttle - remember to enable their feature flags for the `shuttle-service` dependency in `Cargo.toml`: -/// -/// | Argument type | Feature flag | Dependency | Example | -/// | ------------------------------------------------------------- | ------------- | ------------------------------------------------------------------ | -------------------------------------------------------------------------------- | -/// | [`PgPool`](https://docs.rs/sqlx/latest/sqlx/type.PgPool.html) | sqlx-postgres | A PostgresSql instance accessed using [sqlx](https://docs.rs/sqlx) | [GitHub](https://github.com/getsynth/shuttle/tree/main/examples/rocket/postgres) | +/// The following dependencies can be managed by shuttle - remember to enable their feature flags for the `shuttle-service` dependency in `Cargo.toml` and configure them using an attribute annotation: +/// +/// | Argument type | Feature flag | Attribute | Dependency | Example | +/// | ------------------------------------------------------------------- | ----------------- | -------------------- | -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | +/// | [`PgPool`](https://docs.rs/sqlx/latest/sqlx/type.PgPool.html) | sqlx-postgres | `shared::Postgres` | A shared PostgresSql instance accessed using [sqlx](https://docs.rs/sqlx) | [GitHub](https://github.com/getsynth/shuttle/tree/main/examples/rocket/postgres) | +/// | [`MySqlPool`](https://docs.rs/sqlx/latest/sqlx/type.MySqlPool.html) | sqlx-aws-mariadb | `aws::rds::MariaDB` | An AWS RDS MariaDB instance tied to your instance and accessed using [sqlx](https://docs.rs/sqlx) | | +/// | [`MySqlPool`](https://docs.rs/sqlx/latest/sqlx/type.MySqlPool.html) | sqlx-aws-mysql | `aws::rds::MySql` | An AWS RDS MySql instance tied to your instance and accessed using [sqlx](https://docs.rs/sqlx) | | +/// | [`PgPool`](https://docs.rs/sqlx/latest/sqlx/type.PgPool.html) | sqlx-aws-postgres | `aws::rds::Postgres` | An AWS RDS Postgres instance tied to your instance and accessed using [sqlx](https://docs.rs/sqlx) | [GitHub](https://github.com/getsynth/shuttle/tree/main/examples/tide/postgres) | pub use shuttle_codegen::main; use tokio::task::JoinHandle; @@ -301,7 +316,10 @@ pub trait Factory: Send + Sync { /// Declare that the [Service][Service] requires a Postgres database. /// /// Returns the connection string to the provisioned database. - async fn get_sql_connection_string(&mut self) -> Result; + async fn get_sql_connection_string( + &mut self, + db_type: database::Type, + ) -> Result; } /// Used to get resources of type `T` from factories. @@ -310,34 +328,9 @@ pub trait Factory: Send + Sync { /// Some resources cannot cross the boundary between the api runtime and the runtime of services. These resources /// should be created on the passed in runtime. #[async_trait] -pub trait GetResource { - async fn get_resource(self, runtime: &Runtime) -> Result; -} - -/// Get an `sqlx::PgPool` from any factory -#[cfg(feature = "sqlx-postgres")] -#[async_trait] -impl GetResource for &mut dyn Factory { - async fn get_resource(self, runtime: &Runtime) -> Result { - use error::CustomError; - - let connection_string = self.get_sql_connection_string().await?; - - // A sqlx Pool cannot cross runtime boundaries, so make sure to create the Pool on the service end - let pool = runtime - .spawn(async move { - sqlx::postgres::PgPoolOptions::new() - .min_connections(1) - .max_connections(5) - .connect(&connection_string) - .await - }) - .await - .map_err(CustomError::new)? - .map_err(CustomError::new)?; - - Ok(pool) - } +pub trait ResourceBuilder { + fn new() -> Self; + async fn build(self, factory: &mut dyn Factory, runtime: &Runtime) -> Result; } /// A tokio handle the service was started on diff --git a/service/src/shared.rs b/service/src/shared.rs new file mode 100644 index 000000000..a1708fcf9 --- /dev/null +++ b/service/src/shared.rs @@ -0,0 +1,39 @@ +use tokio::runtime::Runtime; + +use crate::{database, error::CustomError, Factory, ResourceBuilder}; +use async_trait::async_trait; + +pub struct Postgres; + +/// Get an `sqlx::PgPool` from any factory +#[async_trait] +impl ResourceBuilder for Postgres { + fn new() -> Self { + Self {} + } + + async fn build( + self, + factory: &mut dyn Factory, + runtime: &Runtime, + ) -> Result { + let connection_string = factory + .get_sql_connection_string(database::Type::Shared) + .await?; + + // A sqlx Pool cannot cross runtime boundaries, so make sure to create the Pool on the service end + let pool = runtime + .spawn(async move { + sqlx::postgres::PgPoolOptions::new() + .min_connections(1) + .max_connections(5) + .connect(&connection_string) + .await + }) + .await + .map_err(CustomError::new)? + .map_err(CustomError::new)?; + + Ok(pool) + } +} diff --git a/service/tests/loader.rs b/service/tests/loader.rs index f92716451..065ce4c44 100644 --- a/service/tests/loader.rs +++ b/service/tests/loader.rs @@ -3,7 +3,7 @@ mod helpers; use helpers::{build_so_create_loader, PostgresInstance}; use shuttle_service::loader::LoaderError; -use shuttle_service::{Error, Factory}; +use shuttle_service::{database, Error, Factory}; use std::net::{Ipv4Addr, SocketAddr}; use std::process::exit; @@ -29,7 +29,7 @@ impl DummyFactory { #[async_trait] impl Factory for DummyFactory { - async fn get_sql_connection_string(&mut self) -> Result { + async fn get_sql_connection_string(&mut self, _: database::Type) -> Result { let uri = if let Some(postgres_instance) = &self.postgres_instance { postgres_instance.get_uri() } else { diff --git a/service/tests/resources/sqlx-pool/src/lib.rs b/service/tests/resources/sqlx-pool/src/lib.rs index 18adbf674..8fda07723 100644 --- a/service/tests/resources/sqlx-pool/src/lib.rs +++ b/service/tests/resources/sqlx-pool/src/lib.rs @@ -1,5 +1,5 @@ use shuttle_service::error::CustomError; -use shuttle_service::{log, GetResource, IntoService, Runtime, ServeHandle, Service}; +use shuttle_service::{log, IntoService, ResourceBuilder, Runtime, ServeHandle, Service}; use sqlx::PgPool; #[macro_use] @@ -66,7 +66,9 @@ impl Service for PoolService { .await .unwrap(); - let pool = factory.get_resource(&self.runtime).await?; + let pool = shuttle_service::shared::Postgres::new() + .build(factory, &self.runtime) + .await?; self.pool = Some(pool); diff --git a/terraform/modules/shuttle/api.tf b/terraform/modules/shuttle/api.tf index c5041c6e9..17713a4fe 100644 --- a/terraform/modules/shuttle/api.tf +++ b/terraform/modules/shuttle/api.tf @@ -23,7 +23,7 @@ resource "aws_apigatewayv2_api_mapping" "backend" { resource "aws_apigatewayv2_vpc_link" "private" { name = "shuttle-api-gateway-vpc-link" - security_group_ids = [aws_security_group.unreasonable.id] + security_group_ids = [aws_default_security_group.default.id] subnet_ids = [aws_subnet.backend_a.id, aws_subnet.backend_b.id] } diff --git a/terraform/modules/shuttle/load-balancing.tf b/terraform/modules/shuttle/load-balancing.tf index 0aa981062..25ac9b29f 100644 --- a/terraform/modules/shuttle/load-balancing.tf +++ b/terraform/modules/shuttle/load-balancing.tf @@ -5,7 +5,7 @@ resource "aws_lb" "api" { load_balancer_type = "application" - security_groups = [aws_security_group.unreasonable.id] + security_groups = [aws_default_security_group.default.id] subnets = [aws_subnet.backend_a.id, aws_subnet.backend_b.id] access_logs { @@ -22,7 +22,7 @@ resource "aws_lb" "db" { load_balancer_type = "network" - //security_groups = [aws_security_group.unreasonable.id] + //security_groups = [aws_default_security_group.default.id] subnets = [aws_subnet.backend_a.id, aws_subnet.backend_b.id] access_logs { @@ -84,7 +84,7 @@ resource "aws_lb" "user" { load_balancer_type = "application" - security_groups = [aws_security_group.unreasonable.id] + security_groups = [aws_default_security_group.default.id] subnets = [aws_subnet.backend_a.id, aws_subnet.backend_b.id] access_logs { diff --git a/terraform/modules/shuttle/locals.tf b/terraform/modules/shuttle/locals.tf index e026037b9..3614bceb8 100644 --- a/terraform/modules/shuttle/locals.tf +++ b/terraform/modules/shuttle/locals.tf @@ -1,4 +1,7 @@ +data "aws_caller_identity" "current" {} + locals { + account_id = data.aws_caller_identity.current.account_id data_dir = "/opt/shuttle" docker_backend_image = "public.ecr.aws/shuttle/backend" docker_provisioner_image = "public.ecr.aws/shuttle/provisioner" diff --git a/terraform/modules/shuttle/networking.tf b/terraform/modules/shuttle/networking.tf index 275451b9b..b03b1bc4a 100644 --- a/terraform/modules/shuttle/networking.tf +++ b/terraform/modules/shuttle/networking.tf @@ -8,29 +8,29 @@ resource "aws_internet_gateway" "public" { vpc_id = aws_vpc.backend.id } -resource "aws_network_acl" "unreasonable" { - vpc_id = aws_vpc.backend.id - - egress { - protocol = "tcp" - rule_no = 100 - action = "allow" - cidr_block = "10.0.0.0/16" - from_port = 0 - to_port = 65535 - } +resource "aws_network_acl_rule" "postgres" { + network_acl_id = aws_vpc.backend.default_network_acl_id + rule_number = 10 + egress = false + protocol = "tcp" + rule_action = "allow" + cidr_block = "0.0.0.0/0" + from_port = 5432 + to_port = 5432 +} - ingress { - protocol = "tcp" - rule_no = 100 - action = "allow" - cidr_block = "10.0.0.0/16" - from_port = 0 - to_port = 65535 - } +resource "aws_network_acl_rule" "mysql" { + network_acl_id = aws_vpc.backend.default_network_acl_id + rule_number = 11 + egress = false + protocol = "tcp" + rule_action = "allow" + cidr_block = "0.0.0.0/0" + from_port = 3306 + to_port = 3306 } -resource "aws_security_group" "unreasonable" { +resource "aws_default_security_group" "default" { vpc_id = aws_vpc.backend.id ingress { diff --git a/terraform/modules/shuttle/rds.tf b/terraform/modules/shuttle/rds.tf new file mode 100644 index 000000000..7c5ffd1e3 --- /dev/null +++ b/terraform/modules/shuttle/rds.tf @@ -0,0 +1,5 @@ +resource "aws_db_subnet_group" "managed" { + name = "shuttle_rds" + description = "Subnet for RDS instances managed by shuttle" + subnet_ids = [aws_subnet.backend_a.id, aws_subnet.backend_b.id] +} diff --git a/terraform/modules/shuttle/service.tf b/terraform/modules/shuttle/service.tf index 6921f5154..087b5c3e0 100644 --- a/terraform/modules/shuttle/service.tf +++ b/terraform/modules/shuttle/service.tf @@ -7,11 +7,6 @@ resource "aws_eip" "backend" { network_interface = aws_network_interface.backend.id } -resource "aws_network_interface_sg_attachment" "backend" { - security_group_id = aws_security_group.unreasonable.id - network_interface_id = aws_network_interface.backend.id -} - resource "aws_iam_instance_profile" "backend" { name = "backend-profile" role = aws_iam_role.backend.name @@ -37,6 +32,29 @@ resource "aws_iam_role" "backend" { ] } EOF + + inline_policy { + name = "Handle_RDS" + policy = jsonencode( + { + Statement = [ + { + Action = [ + "rds:CreateDBInstance", + "rds:DescribeDBInstances", + "rds:ModifyDBInstance", + ] + Effect = "Allow" + Resource = [ + "arn:aws:rds:*:${local.account_id}:db:*", + "arn:aws:rds:*:${local.account_id}:subgrp:shuttle_rds", + ] + }, + ] + Version = "2012-10-17" + } + ) + } } resource "aws_lb_target_group_attachment" "api" { @@ -83,6 +101,14 @@ resource "aws_instance" "backend" { iam_instance_profile = aws_iam_instance_profile.backend.id + metadata_options { + http_endpoint = "enabled" + # Our api runs in a container and therefore has an extra hop limit + # https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html#imds-considerations + http_put_response_hop_limit = 2 + http_tokens = "required" + } + network_interface { network_interface_id = aws_network_interface.backend.id device_index = 0 @@ -123,6 +149,7 @@ locals { data_dir = local.data_dir, docker_image = local.docker_provisioner_image, pg_password = var.postgres_password, + fqdn = var.pg_fqdn } ) } diff --git a/terraform/modules/shuttle/storage.tf b/terraform/modules/shuttle/storage.tf index bb47a75f6..352b94d06 100644 --- a/terraform/modules/shuttle/storage.tf +++ b/terraform/modules/shuttle/storage.tf @@ -49,11 +49,11 @@ resource "aws_efs_file_system" "user_data" { resource "aws_efs_mount_target" "user_data_a" { file_system_id = aws_efs_file_system.user_data.id subnet_id = aws_subnet.backend_a.id - security_groups = [aws_security_group.unreasonable.id] + security_groups = [aws_default_security_group.default.id] } resource "aws_efs_mount_target" "user_data_b" { file_system_id = aws_efs_file_system.user_data.id subnet_id = aws_subnet.backend_b.id - security_groups = [aws_security_group.unreasonable.id] + security_groups = [aws_default_security_group.default.id] } diff --git a/terraform/modules/shuttle/systemd/system/shuttle-provisioner.service.tftpl b/terraform/modules/shuttle/systemd/system/shuttle-provisioner.service.tftpl index bd212375d..1ba546fde 100644 --- a/terraform/modules/shuttle/systemd/system/shuttle-provisioner.service.tftpl +++ b/terraform/modules/shuttle/systemd/system/shuttle-provisioner.service.tftpl @@ -15,6 +15,7 @@ ExecStart=/usr/bin/docker run --rm \ -e PG_PORT=5432 \ -p 5432:5432 \ -e PORT=5001 \ + -e FQDN=${fqdn} \ -e PG_PASSWORD=${pg_password} \ -e PG_DATA=/opt/shuttle/postgres \ -v ${data_dir}/conf/postgres:/etc/postgresql/11/shuttle:rw \