mirror of
https://codeberg.org/spire/dispenser.git
synced 2026-06-03 10:04:07 +02:00
scheduling
This commit is contained in:
parent
f3b9d14678
commit
da0f12fb91
8 changed files with 456 additions and 39 deletions
213
Cargo.lock
generated
213
Cargo.lock
generated
|
|
@ -6,6 +6,15 @@ version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "0.7.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayref"
|
name = "arrayref"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
|
|
@ -29,6 +38,17 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atty"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
|
@ -140,6 +160,16 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cron"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "628a3464535cee4e75af89e8c293bab926deaddfa166553b75029066c846be3f"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"nom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.3"
|
version = "0.8.3"
|
||||||
|
|
@ -196,12 +226,14 @@ dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"petname",
|
"petname",
|
||||||
|
"pretty_env_logger",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"thrussh",
|
"thrussh",
|
||||||
"thrussh-keys",
|
"thrussh-keys",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-cron-scheduler",
|
||||||
"toml",
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -220,6 +252,19 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_logger"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
|
||||||
|
dependencies = [
|
||||||
|
"atty",
|
||||||
|
"humantime",
|
||||||
|
"log",
|
||||||
|
"regex",
|
||||||
|
"termcolor",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.0.20"
|
version = "1.0.20"
|
||||||
|
|
@ -374,7 +419,7 @@ checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi 0.10.0+wasi-snapshot-preview1",
|
"wasi 0.10.2+wasi-snapshot-preview1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -445,6 +490,15 @@ version = "0.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47"
|
checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "humantime"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
|
||||||
|
dependencies = [
|
||||||
|
"quick-error",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "0.14.5"
|
version = "0.14.5"
|
||||||
|
|
@ -505,6 +559,15 @@ dependencies = [
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "instant"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ipnet"
|
name = "ipnet"
|
||||||
version = "2.3.0"
|
version = "2.3.0"
|
||||||
|
|
@ -558,6 +621,15 @@ dependencies = [
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312"
|
||||||
|
dependencies = [
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "log"
|
name = "log"
|
||||||
version = "0.4.14"
|
version = "0.4.14"
|
||||||
|
|
@ -617,6 +689,15 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nom"
|
||||||
|
version = "4.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c349f68f25f596b9f44cf0e7c69752a5c633b0550c3ff849518bfba0233774a"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ntapi"
|
name = "ntapi"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
|
|
@ -699,6 +780,31 @@ dependencies = [
|
||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
|
||||||
|
dependencies = [
|
||||||
|
"instant",
|
||||||
|
"lock_api",
|
||||||
|
"parking_lot_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot_core"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"instant",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall 0.2.5",
|
||||||
|
"smallvec",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
|
|
@ -760,6 +866,16 @@ version = "0.2.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pretty_env_logger"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
|
||||||
|
dependencies = [
|
||||||
|
"env_logger",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-hack"
|
name = "proc-macro-hack"
|
||||||
version = "0.5.19"
|
version = "0.5.19"
|
||||||
|
|
@ -781,6 +897,12 @@ dependencies = [
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quick-error"
|
||||||
|
version = "1.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.9"
|
version = "1.0.9"
|
||||||
|
|
@ -836,6 +958,15 @@ version = "0.1.57"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
|
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_users"
|
name = "redox_users"
|
||||||
version = "0.3.5"
|
version = "0.3.5"
|
||||||
|
|
@ -843,10 +974,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
|
checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom 0.1.16",
|
"getrandom 0.1.16",
|
||||||
"redox_syscall",
|
"redox_syscall 0.1.57",
|
||||||
"rust-argon2",
|
"rust-argon2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.4.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.6.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
|
|
@ -929,6 +1077,12 @@ version = "1.0.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sct"
|
name = "sct"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
|
|
@ -997,6 +1151,12 @@ version = "0.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
|
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
|
@ -1024,6 +1184,15 @@ dependencies = [
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termcolor"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "textwrap"
|
name = "textwrap"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
|
|
@ -1111,12 +1280,11 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.1.44"
|
version = "0.1.43"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"wasi 0.10.0+wasi-snapshot-preview1",
|
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -1148,12 +1316,25 @@ dependencies = [
|
||||||
"mio",
|
"mio",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"parking_lot",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"signal-hook-registry",
|
"signal-hook-registry",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-cron-scheduler"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6af42ec81010dbf80a8762206e4cf5273ef291b91f203b48e250130ca4289392"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"cron",
|
||||||
|
"tokio",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-macros"
|
name = "tokio-macros"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
|
@ -1279,6 +1460,15 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.2.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vcpkg"
|
name = "vcpkg"
|
||||||
version = "0.2.11"
|
version = "0.2.11"
|
||||||
|
|
@ -1303,9 +1493,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.10.0+wasi-snapshot-preview1"
|
version = "0.10.2+wasi-snapshot-preview1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
|
|
@ -1420,6 +1610,15 @@ version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-util"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-x86_64-pc-windows-gnu"
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
|
|
||||||
|
|
@ -17,3 +17,5 @@ petname = "1"
|
||||||
thrussh = "0.32"
|
thrussh = "0.32"
|
||||||
thrussh-keys = "0.20"
|
thrussh-keys = "0.20"
|
||||||
futures-util = "0.3"
|
futures-util = "0.3"
|
||||||
|
pretty_env_logger = "0.4"
|
||||||
|
tokio-cron-scheduler = "0.2"
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
[server]
|
|
||||||
rcon = "xxxx"
|
|
||||||
password = "xxxx"
|
|
||||||
demostf_key = "xxxx" # optional
|
|
||||||
logstf_key = "xxxx" # optional
|
|
||||||
config_league = "etf2l" # optional, defaults to etf2l
|
|
||||||
config_mode = "6v6" # optional, defaults to 6v6
|
|
||||||
name = "MyCoolServer" # optional, defaults to Spire
|
|
||||||
tv_name = "MyCoolSTV" # optional, defaults to SpireTV
|
|
||||||
|
|
||||||
[vultr]
|
|
||||||
api_key = "xxx"
|
|
||||||
region = "ams" # see https://api.vultr.com/v2/regions for a list of regions
|
|
||||||
plan = "vc2-1c-2gb" # optional, defaults to vc2-1c-2gb (2GB, $10/month) see https://api.vultr.com/v2/plans for a lis of plan
|
|
||||||
28
config.sample.toml
Normal file
28
config.sample.toml
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
[server] # configuration details for the tf2 server
|
||||||
|
rcon = "xxx" # rcon password
|
||||||
|
password = "xxx" # server password
|
||||||
|
demostf_key = "xxx" # api key for demos.tf
|
||||||
|
logstf_key = "xxx" # api key for logs.tf
|
||||||
|
config_league = "etf2l" # etf2l or #ugc. optional, defaults to "etf2l"
|
||||||
|
config_mode = "6v6" # 6v6 or 9v9, defaults to "6v6"
|
||||||
|
name = "Spire" # server name. optional, defaults to "Spire"
|
||||||
|
tv_name = "SpireTV" # stv name. optional, defaults to "SpireTV"
|
||||||
|
image = "spiretf/docker-spire-server" # docker image for the tf2 server. optional, defaults to "spiretf/docker-spire-server"
|
||||||
|
|
||||||
|
[vultr]
|
||||||
|
api_key = "xxx"
|
||||||
|
region = "ams" # see https://api.vultr.com/v2/regions for a list of regions
|
||||||
|
plan = "vc2-1c-2gb" # optional, defaults to vc2-1c-2gb (2GB, $10/month) see https://api.vultr.com/v2/plans for a lis of plan
|
||||||
|
|
||||||
|
[dyndns] # optional dyndns2 details
|
||||||
|
update_url = "https://update.eurodyndns.org/update/" # Update url for dyndns2
|
||||||
|
hostname = "nipple.tf"
|
||||||
|
username = "xxx"
|
||||||
|
password = "xxx"
|
||||||
|
|
||||||
|
[schedule]
|
||||||
|
|
||||||
|
# sec min hour day-of-month month day-of-week
|
||||||
|
start = "0 0 17 * * Sun" # cron string to start the server on
|
||||||
|
stop = "0 0 23 * * Sun" # cron string to stop the server on
|
||||||
|
# note that the above is in UTC
|
||||||
|
|
@ -59,7 +59,7 @@ impl From<reqwest::Error> for ResponseError {
|
||||||
pub type Result<T, E = CloudError> = std::result::Result<T, E>;
|
pub type Result<T, E = CloudError> = std::result::Result<T, E>;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait Cloud {
|
pub trait Cloud: Send + Sync + 'static {
|
||||||
/// List all running servers on this cloud
|
/// List all running servers on this cloud
|
||||||
async fn list(&self) -> Result<Vec<Server>>;
|
async fn list(&self) -> Result<Vec<Server>>;
|
||||||
/// Create a new server with the given parameter
|
/// Create a new server with the given parameter
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use camino::Utf8PathBuf;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::fs::read_to_string;
|
use std::fs::read_to_string;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::sync::Arc;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
|
@ -31,6 +32,8 @@ impl From<toml::de::Error> for TomlError {
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub vultr: Option<VultrConfig>,
|
pub vultr: Option<VultrConfig>,
|
||||||
pub server: ServerConfig,
|
pub server: ServerConfig,
|
||||||
|
pub dyndns: Option<DynDnsConfig>,
|
||||||
|
pub schedule: ScheduleConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
|
|
@ -39,9 +42,9 @@ impl Config {
|
||||||
Ok(toml::from_str(&content).map_err(TomlError::from)?)
|
Ok(toml::from_str(&content).map_err(TomlError::from)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cloud(&self) -> Result<Box<dyn Cloud>, ConfigError> {
|
pub fn cloud(&self) -> Result<Arc<dyn Cloud>, ConfigError> {
|
||||||
if let Some(vultr) = &self.vultr {
|
if let Some(vultr) = &self.vultr {
|
||||||
Ok(Box::new(Vultr::new(
|
Ok(Arc::new(Vultr::new(
|
||||||
vultr.api_key.clone(),
|
vultr.api_key.clone(),
|
||||||
vultr.region.clone(),
|
vultr.region.clone(),
|
||||||
vultr.plan.clone(),
|
vultr.plan.clone(),
|
||||||
|
|
@ -103,3 +106,17 @@ pub struct VultrConfig {
|
||||||
fn vultr_default_plan() -> String {
|
fn vultr_default_plan() -> String {
|
||||||
String::from("vc2-1c-2gb")
|
String::from("vc2-1c-2gb")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct DynDnsConfig {
|
||||||
|
pub update_url: String,
|
||||||
|
pub hostname: String,
|
||||||
|
pub username: String,
|
||||||
|
pub password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct ScheduleConfig {
|
||||||
|
pub start: String,
|
||||||
|
pub stop: String,
|
||||||
|
}
|
||||||
|
|
|
||||||
85
src/dns.rs
Normal file
85
src/dns.rs
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
use reqwest::{Client, StatusCode};
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::net::IpAddr;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
pub type Result<T, E = DynDnsError> = std::result::Result<T, E>;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum DynDnsError {
|
||||||
|
#[error("Invalid credentials")]
|
||||||
|
Unauthorized,
|
||||||
|
#[error("Network error: {0}")]
|
||||||
|
Network(#[from] NetworkError),
|
||||||
|
#[error("Network response from server: {0}")]
|
||||||
|
InvalidResponse(String),
|
||||||
|
#[error("Domain belongs to another user")]
|
||||||
|
NotYourDomain,
|
||||||
|
#[error("Invalid hostname")]
|
||||||
|
InvalidHostname,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DynDnsError {
|
||||||
|
fn from_status_code(status: StatusCode) -> Result<()> {
|
||||||
|
if status == StatusCode::UNAUTHORIZED || status == StatusCode::FORBIDDEN {
|
||||||
|
return Err(DynDnsError::Unauthorized);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Intentionally opaque error
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
#[error("{0}")]
|
||||||
|
pub struct NetworkError(reqwest::Error);
|
||||||
|
|
||||||
|
pub struct DynDnsClient {
|
||||||
|
client: Client,
|
||||||
|
update_url: String,
|
||||||
|
username: String,
|
||||||
|
password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DynDnsClient {
|
||||||
|
pub fn new(update_url: String, username: String, password: String) -> Self {
|
||||||
|
DynDnsClient {
|
||||||
|
client: Client::new(),
|
||||||
|
update_url,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update(&self, hostname: &str, ip: IpAddr) -> Result<()> {
|
||||||
|
let response = self
|
||||||
|
.client
|
||||||
|
.get(&self.update_url)
|
||||||
|
.basic_auth(&self.username, Some(&self.password))
|
||||||
|
.query(&DynDnsParams { hostname, ip })
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.map_err(NetworkError)?;
|
||||||
|
|
||||||
|
let status = response.status();
|
||||||
|
DynDnsError::from_status_code(status)?;
|
||||||
|
|
||||||
|
let text = response.text().await.map_err(NetworkError)?;
|
||||||
|
match text.as_str() {
|
||||||
|
"badauth" => Err(DynDnsError::Unauthorized),
|
||||||
|
"!yours" => Err(DynDnsError::NotYourDomain),
|
||||||
|
"nochg" => Ok(()),
|
||||||
|
"good" => Ok(()),
|
||||||
|
"notfqdn" => Err(DynDnsError::InvalidHostname),
|
||||||
|
"nohost" => Err(DynDnsError::InvalidHostname),
|
||||||
|
"numhost" => Err(DynDnsError::InvalidHostname),
|
||||||
|
_ => Err(DynDnsError::InvalidResponse(text)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct DynDnsParams<'a> {
|
||||||
|
hostname: &'a str,
|
||||||
|
#[serde(rename = "myip")]
|
||||||
|
ip: IpAddr,
|
||||||
|
}
|
||||||
128
src/main.rs
128
src/main.rs
|
|
@ -1,18 +1,20 @@
|
||||||
use std::env::args;
|
use crate::cloud::{Cloud, CloudError};
|
||||||
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
use ssh::SshSession;
|
|
||||||
|
|
||||||
use crate::cloud::CloudError;
|
|
||||||
use crate::config::{Config, ConfigError, ServerConfig};
|
use crate::config::{Config, ConfigError, ServerConfig};
|
||||||
|
use crate::dns::{DynDnsClient, DynDnsError};
|
||||||
use crate::ssh::SshError;
|
use crate::ssh::SshError;
|
||||||
|
use ssh::SshSession;
|
||||||
|
use std::env::args;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use thiserror::Error;
|
||||||
|
use tokio::task::{spawn, JoinError};
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
|
use tokio_cron_scheduler::{Job, JobScheduler};
|
||||||
|
|
||||||
pub mod cloud;
|
mod cloud;
|
||||||
pub mod config;
|
mod config;
|
||||||
pub mod ssh;
|
mod dns;
|
||||||
|
mod ssh;
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
|
@ -24,6 +26,30 @@ pub enum Error {
|
||||||
Ssh(#[from] SshError),
|
Ssh(#[from] SshError),
|
||||||
#[error("Setup command returned an error: {0}")]
|
#[error("Setup command returned an error: {0}")]
|
||||||
SetupError(String),
|
SetupError(String),
|
||||||
|
#[error("Error while updating dyndns: {0}")]
|
||||||
|
DynDns(#[from] DynDnsError),
|
||||||
|
#[error("Already running")]
|
||||||
|
AlreadyRunning,
|
||||||
|
#[error("{0}")]
|
||||||
|
Schedule(ScheduleError),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
#[error("{0}")]
|
||||||
|
pub struct ScheduleError(ScheduleErrorImpl);
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
enum ScheduleErrorImpl {
|
||||||
|
#[error("Error setting up schedule")]
|
||||||
|
Schedule(String),
|
||||||
|
#[error("Error running schedule")]
|
||||||
|
Join(JoinError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ScheduleErrorImpl> for Error {
|
||||||
|
fn from(e: ScheduleErrorImpl) -> Self {
|
||||||
|
Error::Schedule(ScheduleError(e))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn setup(ssh: &mut SshSession, config: &ServerConfig) -> Result<(), Error> {
|
async fn setup(ssh: &mut SshSession, config: &ServerConfig) -> Result<(), Error> {
|
||||||
|
|
@ -79,6 +105,8 @@ async fn setup(ssh: &mut SshSession, config: &ServerConfig) -> Result<(), Error>
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), Error> {
|
async fn main() -> Result<(), Error> {
|
||||||
|
pretty_env_logger::init();
|
||||||
|
|
||||||
let mut args = args();
|
let mut args = args();
|
||||||
let bin = args.next().unwrap();
|
let bin = args.next().unwrap();
|
||||||
|
|
||||||
|
|
@ -91,12 +119,85 @@ async fn main() -> Result<(), Error> {
|
||||||
};
|
};
|
||||||
let cloud = config.cloud()?;
|
let cloud = config.cloud()?;
|
||||||
|
|
||||||
|
let mut sched = JobScheduler::new();
|
||||||
|
|
||||||
|
let server_id: Arc<Mutex<Option<String>>> = Arc::default();
|
||||||
|
|
||||||
|
sched
|
||||||
|
.add(stop_job(cloud.clone(), &config, server_id.clone()))
|
||||||
|
.map_err(|e| ScheduleErrorImpl::Schedule(format!("{:#}", e)))?;
|
||||||
|
sched
|
||||||
|
.add(start_job(cloud, config, server_id))
|
||||||
|
.map_err(|e| ScheduleErrorImpl::Schedule(format!("{:#}", e)))?;
|
||||||
|
|
||||||
|
sched.start().await.map_err(ScheduleErrorImpl::Join)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stop_job(_cloud: Arc<dyn Cloud>, config: &Config, server_id: Arc<Mutex<Option<String>>>) -> Job {
|
||||||
|
Job::new(&config.schedule.stop, move |_uuid, _l| {
|
||||||
|
let server_id = server_id.clone();
|
||||||
|
spawn(async move {
|
||||||
|
println!("Stopping server");
|
||||||
|
if let Some(id) = server_id.lock().unwrap().take() {
|
||||||
|
println!("Would have killed {}", id);
|
||||||
|
// match cloud.kill(&id).await {
|
||||||
|
// Ok(_) => {}
|
||||||
|
// Err(e) => eprintln!("{:#}", e),
|
||||||
|
// };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_job(cloud: Arc<dyn Cloud>, config: Config, server_id: Arc<Mutex<Option<String>>>) -> Job {
|
||||||
|
let schedule = config.schedule.start.clone();
|
||||||
|
let config = Arc::new(config);
|
||||||
|
Job::new(&schedule, move |_uuid, _l| {
|
||||||
|
let cloud = cloud.clone();
|
||||||
|
let config = config.clone();
|
||||||
|
let server_id = server_id.clone();
|
||||||
|
spawn(async move {
|
||||||
|
let cloud = cloud.as_ref();
|
||||||
|
println!("Starting server");
|
||||||
|
match start(cloud, &config).await {
|
||||||
|
Ok(id) => *server_id.lock().unwrap() = Some(id),
|
||||||
|
Err(e) => eprintln!("{:#}", e),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn start(cloud: &dyn Cloud, config: &Config) -> Result<String, Error> {
|
||||||
|
let list = cloud.list().await?;
|
||||||
|
if !list.is_empty() {
|
||||||
|
return Err(Error::AlreadyRunning);
|
||||||
|
}
|
||||||
let created = cloud.spawn().await?;
|
let created = cloud.spawn().await?;
|
||||||
let server = cloud.wait_for_ip(&created.id).await?;
|
let server = cloud.wait_for_ip(&created.id).await?;
|
||||||
|
|
||||||
println!("Server is booting");
|
println!("Server is booting");
|
||||||
println!(" IP: {}", server.ip);
|
println!(" IP: {}", server.ip);
|
||||||
println!(" Password: {}", created.password);
|
println!(" Root Password: {}", created.password);
|
||||||
|
|
||||||
|
let connect_host = if let Some(dns_config) = config.dyndns.as_ref() {
|
||||||
|
let dns = DynDnsClient::new(
|
||||||
|
dns_config.update_url.to_string(),
|
||||||
|
dns_config.username.to_string(),
|
||||||
|
dns_config.password.to_string(),
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"Updating DynDNS entry for {} to {}",
|
||||||
|
dns_config.hostname, server.ip
|
||||||
|
);
|
||||||
|
dns.update(&dns_config.hostname, server.ip).await?;
|
||||||
|
dns_config.hostname.to_string()
|
||||||
|
} else {
|
||||||
|
format!("{}", server.ip)
|
||||||
|
};
|
||||||
|
|
||||||
let mut ssh = SshSession::open(server.ip, &created.password).await?;
|
let mut ssh = SshSession::open(server.ip, &created.password).await?;
|
||||||
setup(&mut ssh, &config.server).await?;
|
setup(&mut ssh, &config.server).await?;
|
||||||
|
|
@ -106,8 +207,7 @@ async fn main() -> Result<(), Error> {
|
||||||
println!("Connect using");
|
println!("Connect using");
|
||||||
println!(
|
println!(
|
||||||
" connect {}; password {}",
|
" connect {}; password {}",
|
||||||
server.ip, config.server.password
|
connect_host, config.server.password
|
||||||
);
|
);
|
||||||
|
Ok(server.id)
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue