Compare commits
No commits in common. "e4df2e507551ac13d5146ae2961ba73d889ba2dd" and "6da77159b1599ef8b3ed00c0563ef346e241ba39" have entirely different histories.
e4df2e5075
...
6da77159b1
24 changed files with 302 additions and 818 deletions
347
Cargo.lock
generated
347
Cargo.lock
generated
|
@ -108,50 +108,6 @@ version = "1.0.86"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "askama"
|
|
||||||
version = "0.12.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b79091df18a97caea757e28cd2d5fda49c6cd4bd01ddffd7ff01ace0c0ad2c28"
|
|
||||||
dependencies = [
|
|
||||||
"askama_derive",
|
|
||||||
"askama_escape",
|
|
||||||
"humansize",
|
|
||||||
"num-traits",
|
|
||||||
"percent-encoding",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "askama_derive"
|
|
||||||
version = "0.12.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "19fe8d6cb13c4714962c072ea496f3392015f0989b1a2847bb4b2d9effd71d83"
|
|
||||||
dependencies = [
|
|
||||||
"askama_parser",
|
|
||||||
"basic-toml",
|
|
||||||
"mime",
|
|
||||||
"mime_guess",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"serde",
|
|
||||||
"syn 2.0.72",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "askama_escape"
|
|
||||||
version = "0.10.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "askama_parser"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "acb1161c6b64d1c3d83108213c2a2533a342ac225aabd0bda218278c2ddb00c0"
|
|
||||||
dependencies = [
|
|
||||||
"nom",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-lock"
|
name = "async-lock"
|
||||||
version = "3.4.0"
|
version = "3.4.0"
|
||||||
|
@ -165,13 +121,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.81"
|
version = "0.1.60"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
|
checksum = "677d1d8ab452a3936018a687b20e6f7cf5363d713b732b8884001317b0e48aa3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.72",
|
"syn 1.0.106",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -198,61 +154,6 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "axum"
|
|
||||||
version = "0.7.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf"
|
|
||||||
dependencies = [
|
|
||||||
"async-trait",
|
|
||||||
"axum-core",
|
|
||||||
"bytes",
|
|
||||||
"futures-util",
|
|
||||||
"http",
|
|
||||||
"http-body",
|
|
||||||
"http-body-util",
|
|
||||||
"hyper",
|
|
||||||
"hyper-util",
|
|
||||||
"itoa",
|
|
||||||
"matchit",
|
|
||||||
"memchr",
|
|
||||||
"mime",
|
|
||||||
"percent-encoding",
|
|
||||||
"pin-project-lite",
|
|
||||||
"rustversion",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"serde_path_to_error",
|
|
||||||
"serde_urlencoded",
|
|
||||||
"sync_wrapper 1.0.1",
|
|
||||||
"tokio",
|
|
||||||
"tower",
|
|
||||||
"tower-layer",
|
|
||||||
"tower-service",
|
|
||||||
"tracing",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "axum-core"
|
|
||||||
version = "0.4.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3"
|
|
||||||
dependencies = [
|
|
||||||
"async-trait",
|
|
||||||
"bytes",
|
|
||||||
"futures-util",
|
|
||||||
"http",
|
|
||||||
"http-body",
|
|
||||||
"http-body-util",
|
|
||||||
"mime",
|
|
||||||
"pin-project-lite",
|
|
||||||
"rustversion",
|
|
||||||
"sync_wrapper 0.1.2",
|
|
||||||
"tower-layer",
|
|
||||||
"tower-service",
|
|
||||||
"tracing",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.73"
|
version = "0.3.73"
|
||||||
|
@ -286,15 +187,6 @@ version = "1.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "basic-toml"
|
|
||||||
version = "0.1.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "823388e228f614e9558c6804262db37960ec8821856535f5c3f59913140558f8"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bincode"
|
name = "bincode"
|
||||||
version = "1.3.3"
|
version = "1.3.3"
|
||||||
|
@ -656,16 +548,6 @@ dependencies = [
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "deranged"
|
|
||||||
version = "0.3.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
|
|
||||||
dependencies = [
|
|
||||||
"powerfmt",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dhcproto"
|
name = "dhcproto"
|
||||||
version = "0.12.0"
|
version = "0.12.0"
|
||||||
|
@ -1263,101 +1145,12 @@ dependencies = [
|
||||||
"windows",
|
"windows",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "http"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
|
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"fnv",
|
|
||||||
"itoa",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "http-body"
|
|
||||||
version = "1.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
|
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"http",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "http-body-util"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
|
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"futures-util",
|
|
||||||
"http",
|
|
||||||
"http-body",
|
|
||||||
"pin-project-lite",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "httparse"
|
|
||||||
version = "1.9.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "httpdate"
|
|
||||||
version = "1.0.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "humansize"
|
|
||||||
version = "2.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7"
|
|
||||||
dependencies = [
|
|
||||||
"libm",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "humantime"
|
name = "humantime"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hyper"
|
|
||||||
version = "1.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
|
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"futures-channel",
|
|
||||||
"futures-util",
|
|
||||||
"http",
|
|
||||||
"http-body",
|
|
||||||
"httparse",
|
|
||||||
"httpdate",
|
|
||||||
"itoa",
|
|
||||||
"pin-project-lite",
|
|
||||||
"smallvec",
|
|
||||||
"tokio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hyper-util"
|
|
||||||
version = "0.1.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9"
|
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"futures-util",
|
|
||||||
"http",
|
|
||||||
"http-body",
|
|
||||||
"hyper",
|
|
||||||
"pin-project-lite",
|
|
||||||
"tokio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iana-time-zone"
|
name = "iana-time-zone"
|
||||||
version = "0.1.53"
|
version = "0.1.53"
|
||||||
|
@ -1573,12 +1366,6 @@ version = "0.1.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
|
checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "matchit"
|
|
||||||
version = "0.7.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "md-5"
|
name = "md-5"
|
||||||
version = "0.10.6"
|
version = "0.10.6"
|
||||||
|
@ -1616,22 +1403,6 @@ dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "mime"
|
|
||||||
version = "0.3.17"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "mime_guess"
|
|
||||||
version = "2.0.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e"
|
|
||||||
dependencies = [
|
|
||||||
"mime",
|
|
||||||
"unicase",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "minimal-lexical"
|
name = "minimal-lexical"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
@ -1743,12 +1514,6 @@ dependencies = [
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-conv"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-integer"
|
name = "num-integer"
|
||||||
version = "0.1.45"
|
version = "0.1.45"
|
||||||
|
@ -1881,6 +1646,7 @@ dependencies = [
|
||||||
"moka",
|
"moka",
|
||||||
"nzr-api",
|
"nzr-api",
|
||||||
"serde",
|
"serde",
|
||||||
|
"tarpc",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
@ -1895,20 +1661,6 @@ dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "omyacid"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"askama",
|
|
||||||
"axum",
|
|
||||||
"moka",
|
|
||||||
"nzr-api",
|
|
||||||
"tokio",
|
|
||||||
"tracing",
|
|
||||||
"tracing-subscriber",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.19.0"
|
version = "1.19.0"
|
||||||
|
@ -2113,12 +1865,6 @@ version = "0.3.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
|
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "powerfmt"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
|
@ -2352,12 +2098,6 @@ dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustversion"
|
|
||||||
version = "1.0.17"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.12"
|
version = "1.0.12"
|
||||||
|
@ -2422,16 +2162,6 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_path_to_error"
|
|
||||||
version = "0.1.16"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6"
|
|
||||||
dependencies = [
|
|
||||||
"itoa",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_spanned"
|
name = "serde_spanned"
|
||||||
version = "0.6.7"
|
version = "0.6.7"
|
||||||
|
@ -2555,9 +2285,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.13.2"
|
version = "1.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
@ -2861,18 +2591,6 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sync_wrapper"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sync_wrapper"
|
|
||||||
version = "1.0.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syslog"
|
name = "syslog"
|
||||||
version = "7.0.0"
|
version = "7.0.0"
|
||||||
|
@ -3002,16 +2720,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.36"
|
version = "0.3.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
|
checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"deranged",
|
|
||||||
"itoa",
|
"itoa",
|
||||||
"libc",
|
"libc",
|
||||||
"num-conv",
|
|
||||||
"num_threads",
|
"num_threads",
|
||||||
"powerfmt",
|
|
||||||
"serde",
|
"serde",
|
||||||
"time-core",
|
"time-core",
|
||||||
"time-macros",
|
"time-macros",
|
||||||
|
@ -3019,17 +2734,16 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time-core"
|
name = "time-core"
|
||||||
version = "0.1.2"
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time-macros"
|
name = "time-macros"
|
||||||
version = "0.2.18"
|
version = "0.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
|
checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-conv",
|
|
||||||
"time-core",
|
"time-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -3163,34 +2877,6 @@ dependencies = [
|
||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tower"
|
|
||||||
version = "0.4.13"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
|
|
||||||
dependencies = [
|
|
||||||
"futures-core",
|
|
||||||
"futures-util",
|
|
||||||
"pin-project",
|
|
||||||
"pin-project-lite",
|
|
||||||
"tokio",
|
|
||||||
"tower-layer",
|
|
||||||
"tower-service",
|
|
||||||
"tracing",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tower-layer"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tower-service"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing"
|
name = "tracing"
|
||||||
version = "0.1.40"
|
version = "0.1.40"
|
||||||
|
@ -3319,15 +3005,6 @@ dependencies = [
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicase"
|
|
||||||
version = "2.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
|
|
||||||
dependencies = [
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-bidi"
|
name = "unicode-bidi"
|
||||||
version = "0.3.15"
|
version = "0.3.15"
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["nzrd", "nzr-api", "client", "nzrdhcp", "nzr-virt", "omyacid"]
|
members = ["nzrd", "nzr-api", "client", "nzrdhcp", "nzr-virt"]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
|
@ -32,7 +32,6 @@ pub struct SOAConfig {
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct DNSConfig {
|
pub struct DNSConfig {
|
||||||
pub listen_addr: String,
|
pub listen_addr: String,
|
||||||
pub port: u16,
|
|
||||||
pub default_zone: Name,
|
pub default_zone: Name,
|
||||||
pub soa: SOAConfig,
|
pub soa: SOAConfig,
|
||||||
}
|
}
|
||||||
|
@ -41,15 +40,6 @@ pub struct DNSConfig {
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct DHCPConfig {
|
pub struct DHCPConfig {
|
||||||
pub listen_addr: String,
|
pub listen_addr: String,
|
||||||
pub port: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Cloud-init configuration, used by omyacid.
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct CloudConfig {
|
|
||||||
pub listen_addr: String,
|
|
||||||
pub port: u16,
|
|
||||||
pub admin_user: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Server<->Client RPC configuration.
|
/// Server<->Client RPC configuration.
|
||||||
|
@ -72,7 +62,6 @@ pub struct Config {
|
||||||
pub storage: StorageConfig,
|
pub storage: StorageConfig,
|
||||||
pub dns: DNSConfig,
|
pub dns: DNSConfig,
|
||||||
pub dhcp: DHCPConfig,
|
pub dhcp: DHCPConfig,
|
||||||
pub cloud: CloudConfig,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
|
@ -95,9 +84,8 @@ impl Default for Config {
|
||||||
base_image_pool: "images".to_owned(),
|
base_image_pool: "images".to_owned(),
|
||||||
},
|
},
|
||||||
dns: DNSConfig {
|
dns: DNSConfig {
|
||||||
listen_addr: "127.0.0.1".to_owned(),
|
listen_addr: "127.0.0.1:5353".to_owned(),
|
||||||
port: 5353,
|
default_zone: Name::from_utf8("servers.locaddral").unwrap(),
|
||||||
default_zone: Name::from_utf8("servers.local").unwrap(),
|
|
||||||
soa: SOAConfig {
|
soa: SOAConfig {
|
||||||
nzr_domain: Name::from_utf8("nzr.local").unwrap(),
|
nzr_domain: Name::from_utf8("nzr.local").unwrap(),
|
||||||
contact: Name::from_utf8("admin.nzr.local").unwrap(),
|
contact: Name::from_utf8("admin.nzr.local").unwrap(),
|
||||||
|
@ -108,12 +96,6 @@ impl Default for Config {
|
||||||
},
|
},
|
||||||
dhcp: DHCPConfig {
|
dhcp: DHCPConfig {
|
||||||
listen_addr: "127.0.0.1".to_owned(),
|
listen_addr: "127.0.0.1".to_owned(),
|
||||||
port: 67,
|
|
||||||
},
|
|
||||||
cloud: CloudConfig {
|
|
||||||
listen_addr: "0.0.0.0".to_owned(),
|
|
||||||
port: 80,
|
|
||||||
admin_user: "admin".to_owned(),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use std::net::Ipv4Addr;
|
|
||||||
|
|
||||||
use model::{CreateStatus, Instance, Subnet};
|
use model::{CreateStatus, Instance, Subnet};
|
||||||
|
|
||||||
pub mod args;
|
pub mod args;
|
||||||
|
@ -8,15 +6,6 @@ pub mod model;
|
||||||
pub mod net;
|
pub mod net;
|
||||||
|
|
||||||
pub use hickory_proto;
|
pub use hickory_proto;
|
||||||
use net::mac::MacAddr;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub enum InstanceQuery {
|
|
||||||
Name(String),
|
|
||||||
MacAddr(MacAddr),
|
|
||||||
Ipv4Addr(Ipv4Addr),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tarpc::service]
|
#[tarpc::service]
|
||||||
pub trait Nazrin {
|
pub trait Nazrin {
|
||||||
|
@ -29,8 +18,6 @@ pub trait Nazrin {
|
||||||
/// This should involve deleting all related disks and clearing
|
/// This should involve deleting all related disks and clearing
|
||||||
/// the lease information from the subnet data, if any.
|
/// the lease information from the subnet data, if any.
|
||||||
async fn delete_instance(name: String) -> Result<(), String>;
|
async fn delete_instance(name: String) -> Result<(), String>;
|
||||||
/// Gets a single instance by the given InstanceQuery.
|
|
||||||
async fn find_instance(query: InstanceQuery) -> Result<Option<Instance>, String>;
|
|
||||||
/// Gets a list of existing instances.
|
/// Gets a list of existing instances.
|
||||||
async fn get_instances(with_status: bool) -> Result<Vec<Instance>, String>;
|
async fn get_instances(with_status: bool) -> Result<Vec<Instance>, String>;
|
||||||
/// Cleans up unusable entries in the database.
|
/// Cleans up unusable entries in the database.
|
||||||
|
@ -47,20 +34,4 @@ pub trait Nazrin {
|
||||||
async fn get_subnets() -> Result<Vec<Subnet>, String>;
|
async fn get_subnets() -> Result<Vec<Subnet>, String>;
|
||||||
/// Deletes an existing subnet.
|
/// Deletes an existing subnet.
|
||||||
async fn delete_subnet(interface: String) -> Result<(), String>;
|
async fn delete_subnet(interface: String) -> Result<(), String>;
|
||||||
// Gets the cloud-init user-data for the given instance.
|
|
||||||
async fn get_instance_userdata(id: i32) -> Result<Vec<u8>, String>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new NazrinClient.
|
|
||||||
pub fn new_client(sock: tokio::net::UnixStream) -> NazrinClient {
|
|
||||||
use tarpc::tokio_serde::formats::Bincode;
|
|
||||||
use tarpc::tokio_util::codec::LengthDelimitedCodec;
|
|
||||||
|
|
||||||
let framed_io = LengthDelimitedCodec::builder()
|
|
||||||
.length_field_type::<u32>()
|
|
||||||
.new_framed(sock);
|
|
||||||
let transport = tarpc::serde_transport::new(framed_io, Bincode::default());
|
|
||||||
NazrinClient::new(Default::default(), transport).spawn()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub use tarpc::context::current as default_ctx;
|
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
DROP TABLE instances;
|
|
||||||
DROP TABLE subnets;
|
|
|
@ -1 +0,0 @@
|
||||||
ALTER TABLE instances ADD COLUMN ci_metadata TEXT NOT NULL;
|
|
|
@ -1 +0,0 @@
|
||||||
ALTER TABLE instances DROP COLUMN ci_metadata;
|
|
149
nzrd/src/cloud.rs
Normal file
149
nzrd/src/cloud.rs
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
use std::net::Ipv4Addr;
|
||||||
|
|
||||||
|
use hickory_server::proto::rr::Name;
|
||||||
|
use serde::Serialize;
|
||||||
|
use serde_with::skip_serializing_none;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use nzr_api::net::{cidr::CidrV4, mac::MacAddr};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
|
pub struct Metadata<'a> {
|
||||||
|
instance_id: &'a str,
|
||||||
|
local_hostname: &'a str,
|
||||||
|
public_keys: Option<Vec<&'a String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Metadata<'a> {
|
||||||
|
pub fn new(instance_id: &'a str) -> Self {
|
||||||
|
Self {
|
||||||
|
instance_id,
|
||||||
|
local_hostname: instance_id,
|
||||||
|
public_keys: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ssh_pubkeys(mut self, pubkeys: &'a [String]) -> Self {
|
||||||
|
self.public_keys = Some(pubkeys.iter().filter(|i| !i.is_empty()).collect());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct NetworkMeta<'a> {
|
||||||
|
version: u32,
|
||||||
|
ethernets: HashMap<String, EtherNic<'a>>,
|
||||||
|
#[serde(skip)]
|
||||||
|
ethnum: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> NetworkMeta<'a> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
version: 2,
|
||||||
|
ethernets: HashMap::new(),
|
||||||
|
ethnum: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Define a NIC with a static address.
|
||||||
|
pub fn static_nic(
|
||||||
|
mut self,
|
||||||
|
match_data: EtherMatch<'a>,
|
||||||
|
cidr: &'a CidrV4,
|
||||||
|
gateway: &'a Ipv4Addr,
|
||||||
|
dns: DNSMeta<'a>,
|
||||||
|
) -> Self {
|
||||||
|
self.ethernets.insert(
|
||||||
|
format!("eth{}", self.ethnum),
|
||||||
|
EtherNic {
|
||||||
|
r#match: match_data,
|
||||||
|
addresses: Some(vec![cidr]),
|
||||||
|
gateway4: Some(gateway),
|
||||||
|
dhcp4: false,
|
||||||
|
nameservers: Some(dns),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.ethnum += 1;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn dhcp_nic(mut self, match_data: EtherMatch<'a>) -> Self {
|
||||||
|
self.ethernets.insert(
|
||||||
|
format!("eth{}", self.ethnum),
|
||||||
|
EtherNic {
|
||||||
|
r#match: match_data,
|
||||||
|
addresses: None,
|
||||||
|
gateway4: None,
|
||||||
|
dhcp4: true,
|
||||||
|
nameservers: None,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.ethnum += 1;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct Ethernets<'a> {
|
||||||
|
nics: Vec<EtherNic<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct EtherNic<'a> {
|
||||||
|
r#match: EtherMatch<'a>,
|
||||||
|
addresses: Option<Vec<&'a CidrV4>>,
|
||||||
|
gateway4: Option<&'a Ipv4Addr>,
|
||||||
|
dhcp4: bool,
|
||||||
|
nameservers: Option<DNSMeta<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[skip_serializing_none]
|
||||||
|
#[derive(Default, Debug, Serialize)]
|
||||||
|
pub struct EtherMatch<'a> {
|
||||||
|
name: Option<&'a str>,
|
||||||
|
macaddress: Option<&'a MacAddr>,
|
||||||
|
driver: Option<&'a str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> EtherMatch<'a> {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn name(name: &'a str) -> Self {
|
||||||
|
Self {
|
||||||
|
name: Some(name),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mac_addr(addr: &'a MacAddr) -> Self {
|
||||||
|
Self {
|
||||||
|
macaddress: Some(addr),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn driver(driver: &'a str) -> Self {
|
||||||
|
Self {
|
||||||
|
driver: Some(driver),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct DNSMeta<'a> {
|
||||||
|
search: Vec<Name>,
|
||||||
|
addresses: &'a Vec<Ipv4Addr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DNSMeta<'a> {
|
||||||
|
pub fn with_addrs(search: Option<Vec<Name>>, addrs: &'a Vec<Ipv4Addr>) -> Self {
|
||||||
|
Self {
|
||||||
|
addresses: addrs,
|
||||||
|
search: search.unwrap_or_default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ use nzr_virt::{datasize, dom, vol};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::cloud::Metadata;
|
||||||
use crate::ctrl::vm::Progress;
|
use crate::ctrl::vm::Progress;
|
||||||
use crate::ctx::Context;
|
use crate::ctx::Context;
|
||||||
use crate::model::{Instance, Subnet};
|
use crate::model::{Instance, Subnet};
|
||||||
|
@ -85,7 +86,14 @@ pub async fn new_instance(
|
||||||
};
|
};
|
||||||
|
|
||||||
// generate cloud-init data
|
// generate cloud-init data
|
||||||
let db_inst = Instance::insert(&ctx, &args.name, &subnet, lease.clone(), None).await?;
|
let ci_meta = {
|
||||||
|
let m = Metadata::new(&args.name).ssh_pubkeys(&args.ssh_keys);
|
||||||
|
serde_yaml::to_string(&m)
|
||||||
|
.map_err(|err| cmd_error!("Couldn't generate cloud-init metadata: {err}"))
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let db_inst =
|
||||||
|
Instance::insert(&ctx, &args.name, &subnet, lease.clone(), ci_meta, None).await?;
|
||||||
|
|
||||||
progress!(prog_task, 30.0, "Creating instance images...");
|
progress!(prog_task, 30.0, "Creating instance images...");
|
||||||
// create primary volume from base image
|
// create primary volume from base image
|
||||||
|
|
|
@ -3,7 +3,6 @@ use diesel::{
|
||||||
SqliteConnection,
|
SqliteConnection,
|
||||||
};
|
};
|
||||||
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
|
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
|
||||||
use log::trace;
|
|
||||||
use nzr_virt::{vol, Connection};
|
use nzr_virt::{vol, Connection};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
@ -12,10 +11,6 @@ use crate::dns::ZoneData;
|
||||||
use nzr_api::config::Config;
|
use nzr_api::config::Config;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub(crate) const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
|
|
||||||
|
|
||||||
#[cfg(not(test))]
|
|
||||||
const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
|
const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
|
||||||
|
|
||||||
pub struct PoolRefs {
|
pub struct PoolRefs {
|
||||||
|
@ -69,7 +64,6 @@ impl InnerCtx {
|
||||||
baseimg: conn.get_pool(&config.storage.base_image_pool).await?,
|
baseimg: conn.get_pool(&config.storage.base_image_pool).await?,
|
||||||
};
|
};
|
||||||
|
|
||||||
trace!("Connecting to database");
|
|
||||||
let db_uri = config.db_uri.clone();
|
let db_uri = config.db_uri.clone();
|
||||||
let sqldb = tokio::task::spawn_blocking(|| {
|
let sqldb = tokio::task::spawn_blocking(|| {
|
||||||
let manager = ConnectionManager::<SqliteConnection>::new(db_uri);
|
let manager = ConnectionManager::<SqliteConnection>::new(db_uri);
|
||||||
|
@ -80,7 +74,6 @@ impl InnerCtx {
|
||||||
.unwrap()?;
|
.unwrap()?;
|
||||||
|
|
||||||
{
|
{
|
||||||
trace!("Running pending migrations");
|
|
||||||
let mut conn = sqldb.get()?;
|
let mut conn = sqldb.get()?;
|
||||||
tokio::task::spawn_blocking(move || {
|
tokio::task::spawn_blocking(move || {
|
||||||
conn.run_pending_migrations(MIGRATIONS)
|
conn.run_pending_migrations(MIGRATIONS)
|
||||||
|
|
|
@ -1,16 +1,19 @@
|
||||||
|
mod cloud;
|
||||||
mod cmd;
|
mod cmd;
|
||||||
mod ctrl;
|
mod ctrl;
|
||||||
mod ctx;
|
mod ctx;
|
||||||
mod dns;
|
mod dns;
|
||||||
mod model;
|
mod model;
|
||||||
mod rpc;
|
mod rpc;
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test;
|
||||||
|
|
||||||
use hickory_server::ServerFuture;
|
use hickory_server::ServerFuture;
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
use log::*;
|
use log::*;
|
||||||
use model::{Instance, Subnet};
|
use model::{Instance, Subnet};
|
||||||
use nzr_api::config;
|
use nzr_api::config;
|
||||||
use std::{net::IpAddr, str::FromStr};
|
use std::str::FromStr;
|
||||||
use tokio::net::UdpSocket;
|
use tokio::net::UdpSocket;
|
||||||
|
|
||||||
#[tokio::main(flavor = "multi_thread")]
|
#[tokio::main(flavor = "multi_thread")]
|
||||||
|
@ -59,10 +62,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
||||||
// DNS init
|
// DNS init
|
||||||
let mut dns_listener = ServerFuture::new(ctx.zones.catalog());
|
let mut dns_listener = ServerFuture::new(ctx.zones.catalog());
|
||||||
let dns_socket = {
|
let dns_socket = UdpSocket::bind(ctx.config.dns.listen_addr.as_str()).await?;
|
||||||
let dns_ip: IpAddr = ctx.config.dns.listen_addr.parse()?;
|
|
||||||
UdpSocket::bind((dns_ip, ctx.config.dns.port)).await?
|
|
||||||
};
|
|
||||||
dns_listener.register_socket(dns_socket);
|
dns_listener.register_socket(dns_socket);
|
||||||
|
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
use std::{net::Ipv4Addr, str::FromStr};
|
use std::{net::Ipv4Addr, str::FromStr};
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test;
|
|
||||||
pub mod tx;
|
pub mod tx;
|
||||||
|
|
||||||
use diesel::{associations::HasTable, prelude::*};
|
use diesel::{associations::HasTable, prelude::*};
|
||||||
|
@ -35,6 +33,7 @@ diesel::table! {
|
||||||
mac_addr -> Text,
|
mac_addr -> Text,
|
||||||
subnet_id -> Integer,
|
subnet_id -> Integer,
|
||||||
host_num -> Integer,
|
host_num -> Integer,
|
||||||
|
ci_metadata -> Text,
|
||||||
ci_userdata -> Nullable<Binary>,
|
ci_userdata -> Nullable<Binary>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,6 +71,7 @@ pub struct Instance {
|
||||||
pub mac_addr: MacAddr,
|
pub mac_addr: MacAddr,
|
||||||
pub subnet_id: i32,
|
pub subnet_id: i32,
|
||||||
pub host_num: i32,
|
pub host_num: i32,
|
||||||
|
pub ci_metadata: String,
|
||||||
pub ci_userdata: Option<Vec<u8>>,
|
pub ci_userdata: Option<Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,17 +91,6 @@ impl Instance {
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get(ctx: &Context, id: i32) -> Result<Option<Self>, ModelError> {
|
|
||||||
ctx.spawn_db(move |mut db| {
|
|
||||||
self::instances::table
|
|
||||||
.find(id)
|
|
||||||
.load::<Instance>(&mut db)
|
|
||||||
.map(|m| m.into_iter().next())
|
|
||||||
})
|
|
||||||
.await?
|
|
||||||
.map_err(ModelError::Db)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn all_in_subnet(ctx: &Context, net: &Subnet) -> Result<Vec<Self>, ModelError> {
|
pub async fn all_in_subnet(ctx: &Context, net: &Subnet) -> Result<Vec<Self>, ModelError> {
|
||||||
let subnet = net.clone();
|
let subnet = net.clone();
|
||||||
|
|
||||||
|
@ -133,19 +122,6 @@ impl Instance {
|
||||||
Ok(res.into_iter().next())
|
Ok(res.into_iter().next())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets an Instance model with the given MAC address.
|
|
||||||
pub async fn get_by_mac(ctx: &Context, addr: MacAddr) -> Result<Option<Self>, ModelError> {
|
|
||||||
ctx.spawn_db(move |mut db| {
|
|
||||||
use self::instances::dsl::{instances, mac_addr};
|
|
||||||
instances
|
|
||||||
.filter(mac_addr.eq(addr))
|
|
||||||
.select(Instance::as_select())
|
|
||||||
.load::<Instance>(&mut db)
|
|
||||||
})
|
|
||||||
.await?
|
|
||||||
.map_or_else(|e| Err(ModelError::Db(e)), |m| Ok(m.into_iter().next()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets an Instance model by the IPv4 address that has been assigned to it.
|
/// Gets an Instance model by the IPv4 address that has been assigned to it.
|
||||||
pub async fn get_by_ip4(ctx: &Context, ip_addr: Ipv4Addr) -> Result<Option<Self>, ModelError> {
|
pub async fn get_by_ip4(ctx: &Context, ip_addr: Ipv4Addr) -> Result<Option<Self>, ModelError> {
|
||||||
use self::instances::dsl::host_num;
|
use self::instances::dsl::host_num;
|
||||||
|
@ -181,6 +157,7 @@ impl Instance {
|
||||||
name: impl AsRef<str>,
|
name: impl AsRef<str>,
|
||||||
subnet: &Subnet,
|
subnet: &Subnet,
|
||||||
lease: nzr_api::model::Lease,
|
lease: nzr_api::model::Lease,
|
||||||
|
ci_meta: impl Into<String>,
|
||||||
ci_user: Option<Vec<u8>>,
|
ci_user: Option<Vec<u8>>,
|
||||||
) -> Result<Self, ModelError> {
|
) -> Result<Self, ModelError> {
|
||||||
// Get highest host addr + 1 for our addr
|
// Get highest host addr + 1 for our addr
|
||||||
|
@ -192,6 +169,7 @@ impl Instance {
|
||||||
|
|
||||||
let wanted_name = name.as_ref().to_owned();
|
let wanted_name = name.as_ref().to_owned();
|
||||||
let netid = subnet.id;
|
let netid = subnet.id;
|
||||||
|
let ci_meta = ci_meta.into();
|
||||||
|
|
||||||
if addr_num > subnet.end_host {
|
if addr_num > subnet.end_host {
|
||||||
Err(cidr::Error::HostBitsTooLarge)?;
|
Err(cidr::Error::HostBitsTooLarge)?;
|
||||||
|
@ -206,6 +184,7 @@ impl Instance {
|
||||||
mac_addr.eq(lease.mac_addr),
|
mac_addr.eq(lease.mac_addr),
|
||||||
subnet_id.eq(netid),
|
subnet_id.eq(netid),
|
||||||
host_num.eq(addr_num),
|
host_num.eq(addr_num),
|
||||||
|
ci_metadata.eq(ci_meta),
|
||||||
ci_userdata.eq(ci_user),
|
ci_userdata.eq(ci_user),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
use diesel::Connection;
|
|
||||||
use diesel_migrations::MigrationHarness;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn migrations() {
|
|
||||||
let mut sql = diesel::SqliteConnection::establish(":memory:").unwrap();
|
|
||||||
let pending = sql.pending_migrations(crate::ctx::MIGRATIONS).unwrap();
|
|
||||||
assert!(!pending.is_empty(), "No migrations found");
|
|
||||||
for migration in pending {
|
|
||||||
sql.run_migration(&migration).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
sql.revert_all_migrations(crate::ctx::MIGRATIONS).unwrap();
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
use futures::{future, StreamExt};
|
use futures::{future, StreamExt};
|
||||||
use nzr_api::{args, model, InstanceQuery, Nazrin};
|
use nzr_api::{args, model, Nazrin};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tarpc::server::{BaseChannel, Channel};
|
use tarpc::server::{BaseChannel, Channel};
|
||||||
use tarpc::tokio_serde::formats::Bincode;
|
use tarpc::tokio_serde::formats::Bincode;
|
||||||
|
@ -114,27 +114,6 @@ impl Nazrin for NzrServer {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn find_instance(
|
|
||||||
self,
|
|
||||||
_: tarpc::context::Context,
|
|
||||||
query: nzr_api::InstanceQuery,
|
|
||||||
) -> Result<Option<model::Instance>, String> {
|
|
||||||
let res = match query {
|
|
||||||
InstanceQuery::Name(name) => Instance::get_by_name(&self.ctx, name).await,
|
|
||||||
InstanceQuery::MacAddr(addr) => Instance::get_by_mac(&self.ctx, addr).await,
|
|
||||||
InstanceQuery::Ipv4Addr(addr) => Instance::get_by_ip4(&self.ctx, addr).await,
|
|
||||||
}
|
|
||||||
.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
if let Some(inst) = res {
|
|
||||||
inst.api_model(&self.ctx)
|
|
||||||
.await
|
|
||||||
.map_or_else(|e| Err(e.to_string()), |m| Ok(Some(m)))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_instances(
|
async fn get_instances(
|
||||||
self,
|
self,
|
||||||
_: tarpc::context::Context,
|
_: tarpc::context::Context,
|
||||||
|
@ -237,21 +216,6 @@ impl Nazrin for NzrServer {
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_instance_userdata(
|
|
||||||
self,
|
|
||||||
_: tarpc::context::Context,
|
|
||||||
id: i32,
|
|
||||||
) -> Result<Vec<u8>, String> {
|
|
||||||
let Some(db_model) = Instance::get(&self.ctx, id)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?
|
|
||||||
else {
|
|
||||||
return Err("Instance doesn't exist".to_owned());
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(db_model.ci_userdata.unwrap_or_default())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
54
nzrd/src/test.rs
Normal file
54
nzrd/src/test.rs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
use std::{net::Ipv4Addr, str::FromStr};
|
||||||
|
|
||||||
|
use crate::cloud::*;
|
||||||
|
use nzr_api::net::{cidr::CidrV4, mac::MacAddr};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cloud_metadata() {
|
||||||
|
let expected = r#"
|
||||||
|
instance-id: my-instance
|
||||||
|
local-hostname: my-instance
|
||||||
|
public-keys:
|
||||||
|
- ssh-key 123456 admin@laptop
|
||||||
|
"#
|
||||||
|
.trim_start();
|
||||||
|
let pubkeys = vec!["ssh-key 123456 admin@laptop".to_owned(), "".to_owned()];
|
||||||
|
let meta = Metadata::new("my-instance").ssh_pubkeys(&pubkeys);
|
||||||
|
|
||||||
|
let meta_xml = serde_yaml::to_string(&meta).unwrap();
|
||||||
|
assert_eq!(meta_xml, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cloud_netdata() {
|
||||||
|
let expected = r#"
|
||||||
|
version: 2
|
||||||
|
ethernets:
|
||||||
|
eth0:
|
||||||
|
match:
|
||||||
|
macaddress: 02:15:42:0b:ee:01
|
||||||
|
addresses:
|
||||||
|
- 192.0.2.69/24
|
||||||
|
gateway4: 192.0.2.1
|
||||||
|
dhcp4: false
|
||||||
|
nameservers:
|
||||||
|
search: []
|
||||||
|
addresses:
|
||||||
|
- 192.0.2.1
|
||||||
|
"#
|
||||||
|
.trim_start();
|
||||||
|
let mac_addr = MacAddr::new(0x02, 0x15, 0x42, 0x0b, 0xee, 0x01);
|
||||||
|
let cidr = CidrV4::from_str("192.0.2.69/24").unwrap();
|
||||||
|
let gateway = Ipv4Addr::from_str("192.0.2.1").unwrap();
|
||||||
|
|
||||||
|
let dns = vec![gateway];
|
||||||
|
let netconfig = NetworkMeta::new().static_nic(
|
||||||
|
EtherMatch::mac_addr(&mac_addr),
|
||||||
|
&cidr,
|
||||||
|
&gateway,
|
||||||
|
DNSMeta::with_addrs(None, &dns),
|
||||||
|
);
|
||||||
|
|
||||||
|
let net_xml = serde_yaml::to_string(&netconfig).unwrap();
|
||||||
|
assert_eq!(net_xml, expected);
|
||||||
|
}
|
|
@ -11,5 +11,11 @@ tokio = { version = "1.39.2", features = ["rt-multi-thread", "net", "macros"] }
|
||||||
nzr-api = { path = "../nzr-api" }
|
nzr-api = { path = "../nzr-api" }
|
||||||
tracing = { version = "0.1.40", features = ["log"] }
|
tracing = { version = "0.1.40", features = ["log"] }
|
||||||
tracing-subscriber = "0.3.18"
|
tracing-subscriber = "0.3.18"
|
||||||
|
tarpc = { version = "0.34", features = [
|
||||||
|
"tokio1",
|
||||||
|
"unix",
|
||||||
|
"serde-transport",
|
||||||
|
"serde-transport-bincode",
|
||||||
|
] }
|
||||||
moka = { version = "0.12.8", features = ["future"] }
|
moka = { version = "0.12.8", features = ["future"] }
|
||||||
anyhow = "1.0.86"
|
anyhow = "1.0.86"
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use std::hash::RandomState;
|
use std::hash::RandomState;
|
||||||
use std::net::IpAddr;
|
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
use anyhow::Context as _;
|
use anyhow::Context as _;
|
||||||
|
@ -11,6 +10,7 @@ use nzr_api::{
|
||||||
net::mac::MacAddr,
|
net::mac::MacAddr,
|
||||||
NazrinClient,
|
NazrinClient,
|
||||||
};
|
};
|
||||||
|
use tarpc::{tokio_serde::formats::Bincode, tokio_util::codec::LengthDelimitedCodec};
|
||||||
use tokio::net::UdpSocket;
|
use tokio::net::UdpSocket;
|
||||||
use tokio::net::UnixStream;
|
use tokio::net::UnixStream;
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ impl Context {
|
||||||
async fn hydrate_hosts(&self) -> Result<()> {
|
async fn hydrate_hosts(&self) -> Result<()> {
|
||||||
let instances = self
|
let instances = self
|
||||||
.api_client
|
.api_client
|
||||||
.get_instances(nzr_api::default_ctx(), false)
|
.get_instances(tarpc::context::current(), false)
|
||||||
.await?
|
.await?
|
||||||
.map_err(|e| anyhow::anyhow!("nzrd error: {e}"))?;
|
.map_err(|e| anyhow::anyhow!("nzrd error: {e}"))?;
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ impl Context {
|
||||||
async fn hydrate_nets(&self) -> Result<()> {
|
async fn hydrate_nets(&self) -> Result<()> {
|
||||||
let subnets = self
|
let subnets = self
|
||||||
.api_client
|
.api_client
|
||||||
.get_subnets(nzr_api::default_ctx())
|
.get_subnets(tarpc::context::current())
|
||||||
.await?
|
.await?
|
||||||
.map_err(|e| anyhow::anyhow!("nzrd error: {e}"))?;
|
.map_err(|e| anyhow::anyhow!("nzrd error: {e}"))?;
|
||||||
|
|
||||||
|
@ -68,19 +68,21 @@ impl Context {
|
||||||
let sock = UnixStream::connect(&cfg.rpc.socket_path)
|
let sock = UnixStream::connect(&cfg.rpc.socket_path)
|
||||||
.await
|
.await
|
||||||
.context("Connection to nzrd failed")?;
|
.context("Connection to nzrd failed")?;
|
||||||
nzr_api::new_client(sock)
|
let framed_io = LengthDelimitedCodec::builder()
|
||||||
};
|
.length_field_type::<u32>()
|
||||||
|
.new_framed(sock);
|
||||||
|
let transport = tarpc::serde_transport::new(framed_io, Bincode::default());
|
||||||
|
NazrinClient::new(Default::default(), transport)
|
||||||
|
}
|
||||||
|
.spawn();
|
||||||
|
|
||||||
let listen_addr: SocketAddr = {
|
let listen_addr: SocketAddr = cfg
|
||||||
let ip: IpAddr = cfg
|
|
||||||
.dhcp
|
.dhcp
|
||||||
.listen_addr
|
.listen_addr
|
||||||
.parse()
|
.parse()
|
||||||
.context("Malformed listen address")?;
|
.context("Malformed listen address")?;
|
||||||
(ip, cfg.dhcp.port).into()
|
|
||||||
};
|
|
||||||
|
|
||||||
let server_sock = UdpSocket::bind(listen_addr)
|
let server_sock = UdpSocket::bind(&listen_addr)
|
||||||
.await
|
.await
|
||||||
.context("Unable to listen")?;
|
.context("Unable to listen")?;
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::{net::Ipv4Addr, process::ExitCode};
|
||||||
use ctx::Context;
|
use ctx::Context;
|
||||||
use dhcproto::{
|
use dhcproto::{
|
||||||
v4::{DhcpOption, Message, MessageType, Opcode, OptionCode},
|
v4::{DhcpOption, Message, MessageType, Opcode, OptionCode},
|
||||||
Decodable, Decoder, Encodable, Encoder,
|
Decodable, Decoder,
|
||||||
};
|
};
|
||||||
use nzr_api::{config::Config, net::mac::MacAddr};
|
use nzr_api::{config::Config, net::mac::MacAddr};
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
@ -17,8 +17,8 @@ const DEFAULT_LEASE: u32 = 86400;
|
||||||
fn make_reply(msg: &Message, msg_type: MessageType, lease_addr: Option<Ipv4Addr>) -> Message {
|
fn make_reply(msg: &Message, msg_type: MessageType, lease_addr: Option<Ipv4Addr>) -> Message {
|
||||||
let mut resp = Message::new(
|
let mut resp = Message::new(
|
||||||
EMPTY_V4,
|
EMPTY_V4,
|
||||||
lease_addr.unwrap_or(EMPTY_V4),
|
|
||||||
EMPTY_V4,
|
EMPTY_V4,
|
||||||
|
lease_addr.unwrap_or(EMPTY_V4),
|
||||||
msg.giaddr(),
|
msg.giaddr(),
|
||||||
msg.chaddr(),
|
msg.chaddr(),
|
||||||
);
|
);
|
||||||
|
@ -46,7 +46,6 @@ async fn handle_message(ctx: &Context, from: SocketAddr, msg: &Message) {
|
||||||
tracing::info!("Received DHCP payload with invalid addr (different media type?)");
|
tracing::info!("Received DHCP payload with invalid addr (different media type?)");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
tracing::debug!("Client MAC is {client_mac}!!!");
|
|
||||||
|
|
||||||
let instance = match ctx.instance_by_mac(client_mac).await {
|
let instance = match ctx.instance_by_mac(client_mac).await {
|
||||||
Ok(Some(i)) => i,
|
Ok(Some(i)) => i,
|
||||||
|
@ -60,11 +59,6 @@ async fn handle_message(ctx: &Context, from: SocketAddr, msg: &Message) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
tracing::info!(
|
|
||||||
"Recieved {msg_type:?} from {client_mac} (assuming {})",
|
|
||||||
&instance.name
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut lease_time = None;
|
let mut lease_time = None;
|
||||||
let mut nak = false;
|
let mut nak = false;
|
||||||
|
|
||||||
|
@ -74,18 +68,12 @@ async fn handle_message(ctx: &Context, from: SocketAddr, msg: &Message) {
|
||||||
make_reply(msg, MessageType::Offer, Some(instance.lease.addr.addr))
|
make_reply(msg, MessageType::Offer, Some(instance.lease.addr.addr))
|
||||||
}
|
}
|
||||||
MessageType::Request => {
|
MessageType::Request => {
|
||||||
if let Some(DhcpOption::RequestedIpAddress(addr)) =
|
if msg.ciaddr() != instance.lease.addr.addr {
|
||||||
msg.opts().get(OptionCode::RequestedIpAddress)
|
nak = true;
|
||||||
{
|
make_reply(msg, MessageType::Nak, None)
|
||||||
if *addr == instance.lease.addr.addr {
|
} else {
|
||||||
|
lease_time = Some(DEFAULT_LEASE);
|
||||||
make_reply(msg, MessageType::Ack, Some(instance.lease.addr.addr))
|
make_reply(msg, MessageType::Ack, Some(instance.lease.addr.addr))
|
||||||
} else {
|
|
||||||
nak = true;
|
|
||||||
make_reply(msg, MessageType::Nak, None)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
nak = true;
|
|
||||||
make_reply(msg, MessageType::Nak, None)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MessageType::Decline => {
|
MessageType::Decline => {
|
||||||
|
@ -98,17 +86,16 @@ async fn handle_message(ctx: &Context, from: SocketAddr, msg: &Message) {
|
||||||
}
|
}
|
||||||
MessageType::Release => {
|
MessageType::Release => {
|
||||||
// We only provide static leases
|
// We only provide static leases
|
||||||
tracing::debug!("Ignoring DHCPRELEASE");
|
tracing::trace!("Ignoring DHCPRELEASE");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MessageType::Inform => make_reply(msg, MessageType::Ack, None),
|
MessageType::Inform => make_reply(msg, MessageType::Ack, None),
|
||||||
other => {
|
other => {
|
||||||
tracing::info!("Received unhandled message {other:?}");
|
tracing::trace!("Received unhandled message {other:?}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
|
||||||
let opts = response.opts_mut();
|
let opts = response.opts_mut();
|
||||||
let giaddr = if msg.giaddr().is_unspecified() {
|
let giaddr = if msg.giaddr().is_unspecified() {
|
||||||
todo!("no relay??")
|
todo!("no relay??")
|
||||||
|
@ -151,28 +138,6 @@ async fn handle_message(ctx: &Context, from: SocketAddr, msg: &Message) {
|
||||||
|
|
||||||
opts.insert(DhcpOption::SubnetMask(instance.lease.addr.netmask()));
|
opts.insert(DhcpOption::SubnetMask(instance.lease.addr.netmask()));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
tracing::info!(
|
|
||||||
"Sending message {:?} with yiaddr {}",
|
|
||||||
response
|
|
||||||
.opts()
|
|
||||||
.get(OptionCode::MessageType)
|
|
||||||
.unwrap_or(&DhcpOption::End),
|
|
||||||
response.yiaddr()
|
|
||||||
);
|
|
||||||
|
|
||||||
// unicast it back
|
|
||||||
let mut resp_buf = Vec::new();
|
|
||||||
let mut enc = Encoder::new(&mut resp_buf);
|
|
||||||
if let Err(err) = response.encode(&mut enc) {
|
|
||||||
tracing::error!("Couldn't encode response: {err}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(err) = ctx.sock().send_to(&resp_buf, from).await {
|
|
||||||
tracing::error!("Couldn't send response: {err}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "omyacid"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
nzr-api = { path = "../nzr-api" }
|
|
||||||
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
|
|
||||||
axum = "0.7"
|
|
||||||
tracing = "0.1"
|
|
||||||
tracing-subscriber = "0.3"
|
|
||||||
anyhow = "1"
|
|
||||||
askama = "0.12"
|
|
||||||
moka = { version = "0.12.8", features = ["future"] }
|
|
|
@ -1,95 +0,0 @@
|
||||||
use std::hash::RandomState;
|
|
||||||
use std::net::Ipv4Addr;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use anyhow::Context as _;
|
|
||||||
use anyhow::Result;
|
|
||||||
use moka::future::Cache;
|
|
||||||
use nzr_api::config::Config;
|
|
||||||
use nzr_api::model::Instance;
|
|
||||||
use nzr_api::InstanceQuery;
|
|
||||||
use nzr_api::NazrinClient;
|
|
||||||
use tokio::net::UnixStream;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct InstanceMeta {
|
|
||||||
pub inst: Instance,
|
|
||||||
pub userdata: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Context {
|
|
||||||
api_client: NazrinClient,
|
|
||||||
config: Arc<Config>,
|
|
||||||
host_cache: Cache<Ipv4Addr, InstanceMeta, RandomState>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Context {
|
|
||||||
pub async fn new(cfg: Config) -> Result<Self> {
|
|
||||||
let api_client = {
|
|
||||||
let sock = UnixStream::connect(&cfg.rpc.socket_path)
|
|
||||||
.await
|
|
||||||
.context("Connection to nzrd failed")?;
|
|
||||||
nzr_api::new_client(sock)
|
|
||||||
};
|
|
||||||
|
|
||||||
let host_cache = Cache::builder()
|
|
||||||
.time_to_live(Duration::from_secs(15))
|
|
||||||
.max_capacity(5)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
api_client,
|
|
||||||
host_cache,
|
|
||||||
config: Arc::new(cfg),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal function to hydrate the instance metadata, if needed
|
|
||||||
async fn get_instmeta(&self, addr: Ipv4Addr) -> Result<Option<InstanceMeta>> {
|
|
||||||
if let Some(meta) = self.host_cache.get(&addr).await {
|
|
||||||
tracing::debug!("Cache hit!");
|
|
||||||
Ok(Some(meta))
|
|
||||||
} else {
|
|
||||||
let inst = self
|
|
||||||
.api_client
|
|
||||||
.find_instance(nzr_api::default_ctx(), InstanceQuery::Ipv4Addr(addr))
|
|
||||||
.await
|
|
||||||
.context("RPC error")?
|
|
||||||
.map_err(|e| anyhow::anyhow!("nzrd error: {e}"))?;
|
|
||||||
if let Some(inst) = inst {
|
|
||||||
let userdata = self
|
|
||||||
.api_client
|
|
||||||
.get_instance_userdata(nzr_api::default_ctx(), inst.id)
|
|
||||||
.await
|
|
||||||
.context("RPC error")?
|
|
||||||
.map_err(|e| anyhow::anyhow!("nzrd error: {e}"))?;
|
|
||||||
|
|
||||||
let meta = InstanceMeta { inst, userdata };
|
|
||||||
|
|
||||||
self.host_cache.insert(addr, meta.clone()).await;
|
|
||||||
|
|
||||||
Ok(Some(meta))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_instance(&self, addr: Ipv4Addr) -> Result<Option<Instance>> {
|
|
||||||
self.get_instmeta(addr)
|
|
||||||
.await
|
|
||||||
.map(|opt| opt.map(|im| im.inst))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_inst_userdata(&self, addr: Ipv4Addr) -> Result<Option<Vec<u8>>> {
|
|
||||||
self.get_instmeta(addr)
|
|
||||||
.await
|
|
||||||
.map(|opt| opt.map(|im| im.userdata))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cfg(&self) -> &Config {
|
|
||||||
&self.config
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,120 +0,0 @@
|
||||||
mod ctx;
|
|
||||||
mod model;
|
|
||||||
|
|
||||||
use std::{
|
|
||||||
net::{IpAddr, SocketAddr},
|
|
||||||
process::ExitCode,
|
|
||||||
str::FromStr,
|
|
||||||
};
|
|
||||||
|
|
||||||
use askama::Template;
|
|
||||||
use axum::{
|
|
||||||
extract::{ConnectInfo, State},
|
|
||||||
http::StatusCode,
|
|
||||||
routing::get,
|
|
||||||
Router,
|
|
||||||
};
|
|
||||||
use model::Metadata;
|
|
||||||
use nzr_api::config::Config;
|
|
||||||
|
|
||||||
async fn get_meta_data(
|
|
||||||
State(ctx): State<ctx::Context>,
|
|
||||||
ConnectInfo(addr): ConnectInfo<SocketAddr>,
|
|
||||||
) -> Result<String, StatusCode> {
|
|
||||||
if let IpAddr::V4(ip) = addr.ip() {
|
|
||||||
match ctx.get_instance(ip).await {
|
|
||||||
Ok(Some(inst)) => {
|
|
||||||
let meta = Metadata {
|
|
||||||
inst_name: &inst.name,
|
|
||||||
ssh_pubkeys: Vec::new(), // TODO
|
|
||||||
username: Some(ctx.cfg().cloud.admin_user.as_ref()),
|
|
||||||
};
|
|
||||||
|
|
||||||
meta.render().map_err(|e| {
|
|
||||||
tracing::error!("Renderer error: {e}");
|
|
||||||
StatusCode::INTERNAL_SERVER_ERROR
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Ok(None) => {
|
|
||||||
tracing::warn!("Request from unregistered server {ip}");
|
|
||||||
Err(StatusCode::FORBIDDEN)
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
tracing::warn!("{err}");
|
|
||||||
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(StatusCode::BAD_REQUEST)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_user_data(
|
|
||||||
State(ctx): State<ctx::Context>,
|
|
||||||
ConnectInfo(addr): ConnectInfo<SocketAddr>,
|
|
||||||
) -> Result<Vec<u8>, StatusCode> {
|
|
||||||
if let IpAddr::V4(ip) = addr.ip() {
|
|
||||||
match ctx.get_inst_userdata(ip).await {
|
|
||||||
Ok(Some(data)) => Ok(data),
|
|
||||||
Ok(None) => {
|
|
||||||
tracing::warn!("Request from unregistered server {ip}");
|
|
||||||
Err(StatusCode::FORBIDDEN)
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
tracing::warn!("{err}");
|
|
||||||
Err(StatusCode::INTERNAL_SERVER_ERROR)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(StatusCode::BAD_REQUEST)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
async fn main() -> ExitCode {
|
|
||||||
tracing_subscriber::fmt().init();
|
|
||||||
let cfg: Config = match Config::figment().extract() {
|
|
||||||
Ok(cfg) => cfg,
|
|
||||||
Err(err) => {
|
|
||||||
tracing::error!("Unable to get configuration: {err}");
|
|
||||||
return ExitCode::FAILURE;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let http_sock = {
|
|
||||||
let addr = match IpAddr::from_str(&cfg.cloud.listen_addr) {
|
|
||||||
Ok(addr) => addr,
|
|
||||||
Err(err) => {
|
|
||||||
tracing::error!("Invalid listen IP address ({err})");
|
|
||||||
return ExitCode::FAILURE;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
match tokio::net::TcpListener::bind((addr, cfg.cloud.port)).await {
|
|
||||||
Ok(sock) => sock,
|
|
||||||
Err(err) => {
|
|
||||||
tracing::error!("Failed to bind to {addr}:{}: {err}", cfg.cloud.port);
|
|
||||||
return ExitCode::FAILURE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let ctx = match ctx::Context::new(cfg).await {
|
|
||||||
Ok(ctx) => ctx,
|
|
||||||
Err(err) => {
|
|
||||||
tracing::error!("{err}");
|
|
||||||
return ExitCode::FAILURE;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let app = Router::new()
|
|
||||||
.route("/meta-data", get(get_meta_data))
|
|
||||||
.route("/user-data", get(get_user_data))
|
|
||||||
.with_state(ctx);
|
|
||||||
|
|
||||||
if let Err(err) = axum::serve(http_sock, app).await {
|
|
||||||
tracing::error!("axum error: {err}");
|
|
||||||
return ExitCode::FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExitCode::SUCCESS
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
use askama::Template;
|
|
||||||
#[derive(Template)]
|
|
||||||
#[template(path = "meta-data.yml")]
|
|
||||||
pub struct Metadata<'a> {
|
|
||||||
pub inst_name: &'a str,
|
|
||||||
pub ssh_pubkeys: Vec<&'a String>,
|
|
||||||
pub username: Option<&'a str>,
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
instance_id: "iid-{{ inst_name }}"
|
|
||||||
local_hostname: "{{ inst_name }}"
|
|
||||||
{% if !ssh_pubkeys.is_empty() -%}
|
|
||||||
public_keys:
|
|
||||||
{% for key in ssh_pubkeys -%}
|
|
||||||
- "{{ key }}"
|
|
||||||
{% endfor %}
|
|
||||||
{% endif -%}
|
|
||||||
{% if let Some(user) = username -%}
|
|
||||||
default_username: "{{ user }}"
|
|
||||||
{%- endif %}
|
|
Loading…
Reference in a new issue