From cc7700346e51aa80bb3844854b09804efdab2d42 Mon Sep 17 00:00:00 2001 From: garrettmills Date: Sat, 29 Mar 2025 20:28:30 -0400 Subject: [PATCH] [WIP] Start implementing init-time process to create /mnt/p5x-system-data volume --- Cargo.lock | 836 +++++++++++++++++++++++++++++++++++--- Cargo.toml | 2 + README.md | 1 + deploy/10-dynamic-kv.yaml | 10 + src/api/cluster/system.rs | 79 +++- src/api/cluster/volume.rs | 118 +++--- src/api/entity/nodes.rs | 49 ++- src/main.rs | 15 +- 8 files changed, 978 insertions(+), 132 deletions(-) create mode 100644 deploy/10-dynamic-kv.yaml diff --git a/Cargo.lock b/Cargo.lock index dbe0f60..1467b00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -35,6 +35,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", + "getrandom", "once_cell", "version_check", "zerocopy", @@ -131,6 +132,18 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + [[package]] name = "async-stream" version = "0.3.6" @@ -150,7 +163,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.100", ] [[package]] @@ -161,7 +174,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.100", ] [[package]] @@ -194,6 +207,17 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "backon" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "970d91570c01a8a5959b36ad7dd1c30642df24b6b3068710066f6809f7033bb7" +dependencies = [ + "fastrand", + "gloo-timers", + "tokio", +] + [[package]] name = "backtrace" version = "0.3.74" @@ -215,6 +239,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64" version = "0.22.1" @@ -303,7 +333,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.100", "syn_derive", ] @@ -428,7 +458,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.100", ] [[package]] @@ -479,6 +509,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -578,7 +618,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.100", ] [[package]] @@ -601,7 +641,8 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "syn 2.0.86", + "strsim", + "syn 2.0.100", ] [[package]] @@ -612,7 +653,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.86", + "syn 2.0.100", ] [[package]] @@ -699,7 +740,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics 0.10.1", "quote", - "syn 2.0.86", + "syn 2.0.100", ] [[package]] @@ -726,6 +767,12 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +[[package]] +name = "dyn-clone" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" + [[package]] name = "ecdsa" version = "0.16.9" @@ -761,6 +808,18 @@ dependencies = [ "subtle", ] +[[package]] +name = "educe" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "either" version = "1.13.0" @@ -798,6 +857,26 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "enum-ordinalize" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea0dcfa4e54eeb516fe454635a95753ddd39acda650ce703031c6973e315dd5" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "env_filter" version = "0.1.2" @@ -859,6 +938,16 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + [[package]] name = "fastrand" version = "2.1.1" @@ -922,6 +1011,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foreign-types" version = "0.3.2" @@ -1019,7 +1114,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.100", ] [[package]] @@ -1062,7 +1157,7 @@ dependencies = [ "libc", "log", "rustversion", - "windows", + "windows 0.48.0", ] [[package]] @@ -1099,6 +1194,18 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "group" version = "0.13.0" @@ -1153,6 +1260,11 @@ name = "hashbrown" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] [[package]] name = "hashlink" @@ -1163,6 +1275,30 @@ dependencies = [ "hashbrown 0.14.5", ] +[[package]] +name = "headers" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" +dependencies = [ + "base64 0.21.7", + "bytes", + "headers-core", + "http 1.1.0", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" +dependencies = [ + "http 1.1.0", +] + [[package]] name = "heck" version = "0.4.1" @@ -1220,6 +1356,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "hostname" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9c7c7c8ac16c798734b8a24560c1362120597c40d5e1459f09498f8f6c8f2ba" +dependencies = [ + "cfg-if", + "libc", + "windows 0.52.0", +] + [[package]] name = "http" version = "0.2.12" @@ -1253,6 +1400,29 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http 1.1.0", + "http-body 1.0.1", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.9.5" @@ -1283,7 +1453,7 @@ dependencies = [ "futures-util", "h2", "http 0.2.12", - "http-body", + "http-body 0.4.6", "httparse", "httpdate", "itoa", @@ -1295,6 +1465,96 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-http-proxy" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ad4b0a1e37510028bc4ba81d0e38d239c39671b0f0ce9e02dfa93a8133f7c08" +dependencies = [ + "bytes", + "futures-util", + "headers", + "http 1.1.0", + "hyper 1.6.0", + "hyper-rustls", + "hyper-util", + "pin-project-lite", + "rustls-native-certs 0.7.3", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.6.0", + "hyper-util", + "log", + "rustls", + "rustls-native-certs 0.8.1", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper 1.6.0", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "hyper 1.6.0", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + [[package]] name = "iana-time-zone" version = "0.1.61" @@ -1353,7 +1613,7 @@ checksum = "0122b7114117e64a63ac49f752a5ca4624d534c7b1c7de796ac196381cd2d947" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.100", ] [[package]] @@ -1421,6 +1681,164 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "json-patch" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "159294d661a039f7644cea7e4d844e6b25aaf71c1ffe9d73a96d768c24b0faf4" +dependencies = [ + "jsonptr", + "serde", + "serde_json", + "thiserror 1.0.66", +] + +[[package]] +name = "jsonpath-rust" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c00ae348f9f8fd2d09f82a98ca381c60df9e0820d8d79fce43e649b4dc3128b" +dependencies = [ + "pest", + "pest_derive", + "regex", + "serde_json", + "thiserror 2.0.12", +] + +[[package]] +name = "jsonptr" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5a3cc660ba5d72bce0b3bb295bf20847ccbb40fd423f3f05b61273672e561fe" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "k8s-openapi" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c75b990324f09bef15e791606b7b7a296d02fc88a344f6eba9390970a870ad5" +dependencies = [ + "base64 0.22.1", + "chrono", + "serde", + "serde-value", + "serde_json", +] + +[[package]] +name = "kube" +version = "0.99.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a4eb20010536b48abe97fec37d23d43069bcbe9686adcf9932202327bc5ca6e" +dependencies = [ + "k8s-openapi", + "kube-client", + "kube-core", + "kube-derive", + "kube-runtime", +] + +[[package]] +name = "kube-client" +version = "0.99.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc2ed952042df20d15ac2fe9614d0ec14b6118eab89633985d4b36e688dccf1" +dependencies = [ + "base64 0.22.1", + "bytes", + "chrono", + "either", + "futures", + "home", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.6.0", + "hyper-http-proxy", + "hyper-rustls", + "hyper-timeout", + "hyper-util", + "jsonpath-rust", + "k8s-openapi", + "kube-core", + "pem", + "rustls", + "secrecy", + "serde", + "serde_json", + "serde_yaml", + "thiserror 2.0.12", + "tokio", + "tokio-util", + "tower", + "tower-http", + "tracing", +] + +[[package]] +name = "kube-core" +version = "0.99.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff0d0793db58e70ca6d689489183816cb3aa481673e7433dc618cf7e8007c675" +dependencies = [ + "chrono", + "form_urlencoded", + "http 1.1.0", + "json-patch", + "k8s-openapi", + "schemars", + "serde", + "serde-value", + "serde_json", + "thiserror 2.0.12", +] + +[[package]] +name = "kube-derive" +version = "0.99.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c562f58dc9f7ca5feac8a6ee5850ca221edd6f04ce0dd2ee873202a88cd494c9" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 2.0.100", +] + +[[package]] +name = "kube-runtime" +version = "0.99.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88f34cfab9b4bd8633062e0e85edb81df23cb09f159f2e31c60b069ae826ffdc" +dependencies = [ + "ahash 0.8.11", + "async-broadcast", + "async-stream", + "async-trait", + "backon", + "educe", + "futures", + "hashbrown 0.15.0", + "hostname", + "json-patch", + "k8s-openapi", + "kube-client", + "parking_lot 0.12.3", + "pin-project", + "serde", + "serde_json", + "thiserror 2.0.12", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -1605,7 +2023,7 @@ dependencies = [ "openssl-probe", "openssl-sys", "schannel", - "security-framework", + "security-framework 2.11.1", "security-framework-sys", "tempfile", ] @@ -1741,7 +2159,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.100", ] [[package]] @@ -1762,6 +2180,15 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + [[package]] name = "ordered-float" version = "3.9.2" @@ -1793,7 +2220,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics 0.10.1", "quote", - "syn 2.0.86", + "syn 2.0.100", ] [[package]] @@ -1848,6 +2275,8 @@ dependencies = [ "dotenv", "env_logger", "futures", + "k8s-openapi", + "kube", "log", "proxmox-api", "rand", @@ -1944,7 +2373,17 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics 0.10.1", "quote", - "syn 2.0.86", + "syn 2.0.100", +] + +[[package]] +name = "pem" +version = "3.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" +dependencies = [ + "base64 0.22.1", + "serde", ] [[package]] @@ -1962,6 +2401,71 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pest" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" +dependencies = [ + "memchr", + "thiserror 2.0.12", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.100", +] + +[[package]] +name = "pest_meta" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "pin-project-lite" version = "0.2.15" @@ -2076,14 +2580,14 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.100", ] [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] @@ -2109,7 +2613,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.100", "version_check", "yansi 1.0.1", ] @@ -2228,7 +2732,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.100", ] [[package]] @@ -2388,7 +2892,7 @@ dependencies = [ "proc-macro2", "quote", "rocket_http", - "syn 2.0.86", + "syn 2.0.100", "unicode-xid", "version_check", ] @@ -2403,7 +2907,7 @@ dependencies = [ "either", "futures", "http 0.2.12", - "hyper", + "hyper 0.14.31", "indexmap", "log", "memchr", @@ -2487,9 +2991,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.16" +version = "0.23.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" +checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c" dependencies = [ "log", "once_cell", @@ -2501,16 +3005,50 @@ dependencies = [ ] [[package]] -name = "rustls-pki-types" -version = "1.10.0" +name = "rustls-native-certs" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "rustls-pki-types", + "schannel", + "security-framework 2.11.1", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework 3.2.0", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" [[package]] name = "rustls-webpki" -version = "0.102.8" +version = "0.103.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" dependencies = [ "ring", "rustls-pki-types", @@ -2538,6 +3076,30 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.100", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -2560,7 +3122,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.100", ] [[package]] @@ -2584,7 +3146,7 @@ dependencies = [ "serde_json", "sqlx", "strum", - "thiserror", + "thiserror 1.0.66", "time", "tracing", "url", @@ -2618,7 +3180,7 @@ dependencies = [ "proc-macro2", "quote", "sea-bae", - "syn 2.0.86", + "syn 2.0.100", "unicode-ident", ] @@ -2668,7 +3230,7 @@ dependencies = [ "bigdecimal", "chrono", "inherent", - "ordered-float", + "ordered-float 3.9.2", "rust_decimal", "sea-query-derive", "serde_json", @@ -2702,8 +3264,8 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.86", - "thiserror", + "syn 2.0.100", + "thiserror 1.0.66", ] [[package]] @@ -2726,7 +3288,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.100", ] [[package]] @@ -2749,6 +3311,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "secrecy" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" +dependencies = [ + "zeroize", +] + [[package]] name = "security-framework" version = "2.11.1" @@ -2756,7 +3327,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ "bitflags 2.6.0", - "core-foundation", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +dependencies = [ + "bitflags 2.6.0", + "core-foundation 0.10.0", "core-foundation-sys", "libc", "security-framework-sys", @@ -2764,9 +3348,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -2787,6 +3371,16 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float 2.10.1", + "serde", +] + [[package]] name = "serde_derive" version = "1.0.214" @@ -2795,7 +3389,18 @@ checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.100", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", ] [[package]] @@ -2831,6 +3436,19 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "sha1" version = "0.10.6" @@ -2999,7 +3617,7 @@ dependencies = [ "sha2", "smallvec", "sqlformat", - "thiserror", + "thiserror 1.0.66", "time", "tokio", "tokio-stream", @@ -3018,7 +3636,7 @@ dependencies = [ "quote", "sqlx-core", "sqlx-macros-core", - "syn 2.0.86", + "syn 2.0.100", ] [[package]] @@ -3041,7 +3659,7 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 2.0.86", + "syn 2.0.100", "tempfile", "tokio", "url", @@ -3054,7 +3672,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64bb4714269afa44aef2755150a0fc19d756fb580a67db8885608cf02f47d06a" dependencies = [ "atoi", - "base64", + "base64 0.22.1", "bigdecimal", "bitflags 2.6.0", "byteorder", @@ -3087,7 +3705,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror", + "thiserror 1.0.66", "time", "tracing", "uuid", @@ -3101,7 +3719,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fa91a732d854c5d7726349bb4bb879bb9478993ceb764247660aee25f67c2f8" dependencies = [ "atoi", - "base64", + "base64 0.22.1", "bigdecimal", "bitflags 2.6.0", "byteorder", @@ -3131,7 +3749,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror", + "thiserror 1.0.66", "time", "tracing", "uuid", @@ -3284,9 +3902,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.86" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -3302,9 +3920,15 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.100", ] +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + [[package]] name = "tap" version = "1.0.1" @@ -3330,7 +3954,16 @@ version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d171f59dbaa811dbbb1aee1e73db92ec2b122911a48e1390dfe327a821ddede" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.66", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", ] [[package]] @@ -3341,7 +3974,18 @@ checksum = "b08be0f17bd307950653ce45db00cd31200d82b624b36e181337d9c7d92765b5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.100", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", ] [[package]] @@ -3425,7 +4069,17 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.100", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +dependencies = [ + "rustls", + "tokio", ] [[package]] @@ -3449,6 +4103,7 @@ dependencies = [ "futures-core", "futures-sink", "pin-project-lite", + "slab", "tokio", ] @@ -3486,6 +4141,47 @@ dependencies = [ "winnow", ] +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" +dependencies = [ + "base64 0.22.1", + "bitflags 2.6.0", + "bytes", + "http 1.1.0", + "http-body 1.0.1", + "mime", + "pin-project-lite", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + [[package]] name = "tower-service" version = "0.3.3" @@ -3512,7 +4208,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.100", ] [[package]] @@ -3575,6 +4271,12 @@ dependencies = [ "serde", ] +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + [[package]] name = "uncased" version = "0.9.10" @@ -3624,6 +4326,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "untrusted" version = "0.9.0" @@ -3636,7 +4344,7 @@ version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b74fc6b57825be3373f7054754755f03ac3a8f5d70015ccad699ba2029956f4a" dependencies = [ - "base64", + "base64 0.22.1", "flate2", "log", "once_cell", @@ -3735,7 +4443,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.100", "wasm-bindgen-shared", ] @@ -3757,7 +4465,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.100", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3818,6 +4526,16 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-targets 0.52.6", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -4026,7 +4744,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.86", + "syn 2.0.100", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index a60bd46..55efe82 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,3 +22,5 @@ ureq = "2.10.1" dotenv = "0.15.0" ssh-key = { version = "0.6.7", features = ["ed25519"] } rand = "0.8.5" +kube = { version = "0.99.0", features = ["runtime", "derive"] } +k8s-openapi = { version = "0.24", features = ["v1_32"] } diff --git a/README.md b/README.md index d7d70d1..4af30df 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ In your Kubernetes cluster, in the `p5x-system` namespace, you should now see a ## License P5x: Proxmox on Kubernetes - API Server + Copyright (C) 2025 Garrett Mills This program is free software: you can redistribute it and/or modify diff --git a/deploy/10-dynamic-kv.yaml b/deploy/10-dynamic-kv.yaml new file mode 100644 index 0000000..ad57620 --- /dev/null +++ b/deploy/10-dynamic-kv.yaml @@ -0,0 +1,10 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + namespace: p5x-system + name: dynamic-kv + labels: + p5x.garrettmills.dev/managed-by: 'p5x' +data: + p5x: "true" diff --git a/src/api/cluster/system.rs b/src/api/cluster/system.rs index 0c24d3e..f1c3b88 100644 --- a/src/api/cluster/system.rs +++ b/src/api/cluster/system.rs @@ -1,11 +1,86 @@ -use std::env; +use std::{env, fs}; use std::fs::{File, Permissions}; use std::io::Write; use std::os::unix::fs::PermissionsExt; use std::path::Path; -use log::info; +use kube::{Api, Client}; +use log::{info, debug}; use rand::rngs::OsRng; use ssh_key::{PrivateKey, Algorithm, LineEnding}; +use k8s_openapi::api::core::v1::{ConfigMap, Node, Pod}; +use kube::api::{Patch, PatchParams}; +use serde_json::json; +use crate::api::cluster::volume::create_volume_unmanaged; +use crate::api::entity::nodes::P5xError; +use crate::api::services::Services; + +pub async fn ensure_system_disk(svc: &Services<'_>) -> Result<(), P5xError> { + info!(target: "p5x", "Ensuring that the P5x API system disk exists..."); + + // Load the dynamic-kv + let client = Client::try_default().await.map_err(P5xError::KubeError)?; + let namespace = fs::read_to_string("/var/run/secrets/kubernetes.io/serviceaccount/namespace") + .unwrap_or_else(|_| "p5x-system".to_string()); + + let maps: Api = Api::namespaced(client.clone(), &namespace); + let map = maps.get("dynamic-kv").await.map_err(P5xError::KubeError)?; + + // If there's already a PVE host and disk mount in the config, we're done + let mut data = map.data.unwrap_or_default(); + if let Some(host) = data.get("api-pve-host") { + if let Some(mount) = data.get("api-pve-disk") { + info!(target: "p5x", "Found existing P5x API system disk on {host}:{mount}"); + return Ok(()); + } + } + + // Otherwise, create the disk on this PVE host + info!(target: "p5x", "Provisioning new P5x API system disk (this is a one-time fixup)..."); + + // Load the labels for this pod's node + let pod_name = env::var("POD_NAME").expect("Could not determine POD_NAME from environment!"); + let pods: Api = Api::namespaced(client.clone(), &namespace); + let pod = pods.get(&pod_name).await.map_err(P5xError::KubeError)?; + + let node_name = pod.spec.and_then(|spec| spec.node_name).expect("Could not determine the Node name for pod!"); + + let nodes: Api = Api::all(client); + let node = nodes.get(&node_name).await.map_err(P5xError::KubeError)?; + let labels = node.metadata.labels.expect("Could not load labels for node"); + + let pve_host = labels.get("p5x.garrettmills.dev/pve-host") + .expect("Node is missing required label: p5x.garrettmills.dev/pve-host"); + + let pve_id: i32 = labels.get("p5x.garrettmills.dev/pve-id") + .expect("Node is missing required label: p5x.garrettmills.dev/pve-id") + .parse() + .unwrap(); + + debug!(target: "p5x", "pod {pod_name} | node {node_name} | host {pve_host} | vmid {pve_id}"); + + // Create a new disk and mount it to the K8s node (LXC container) + let (disk_name, _) = create_volume_unmanaged( + svc, + 5 * 1024 * 1024 * 1024, + pve_host, + pve_id, + "p5x-api-system-disk" + ).await?; + + // Add it to the dynamic-kv config and save + data.insert("api-pve-host".to_string(), pve_host.to_string()); + data.insert("api-pve-disk".to_string(), disk_name); + let patch = json!({ + "data": data + }); + + let params = PatchParams::apply("p5x-api"); + maps.patch("dynamic-kv", ¶ms, &Patch::Merge(&patch)) + .await.map_err(P5xError::KubeError)?; + + info!(target: "p5x", "Successfully provisioned P5x API system disk."); + Ok(()) +} /** Check if the SSH pubkey/privkey exist at the configured paths. If not, generate them. */ diff --git a/src/api/cluster/volume.rs b/src/api/cluster/volume.rs index b543f43..e283b9e 100644 --- a/src/api/cluster/volume.rs +++ b/src/api/cluster/volume.rs @@ -13,7 +13,7 @@ use tokio::time::sleep; use ureq::Error::Status; use crate::api::cluster::carrier::{provision_carrier, terminate_carrier}; use crate::api::cluster::node::migrate_node; -use crate::api::entity::nodes::{lock_first_available, P5xError}; +use crate::api::entity::nodes::{lock_first_available, P5xError, PveConfig}; use crate::api::entity::{nodes, volumes}; use crate::api::services::{ssh_run_trimmed, Services}; @@ -40,6 +40,69 @@ impl VolumeParams { } +/** + * At runtime, you *probably* want to use actual create(). + * This just creates/mounts the actual PVE disk, but doesn't do any of + * our locking/database work. + * Returns (Proxmox disk name, mountpoint ID) + * Example (vm-100-disk-1, 0) + */ +pub async fn create_volume_unmanaged( + svc: &Services<'_>, + size_in_bytes: u64, + pve_host: &str, + pve_id: i32, + name: &str, +) -> Result<(String, u32), P5xError> { + // Get the next available mountpoint ID + let conf = PveConfig::load(svc, pve_host, pve_id)?; + let mp_id = conf.next_nth("mp"); + info!(target: "p5x", "Volume {name} will become mp{mp_id} on PVE {}", pve_id); + + // Generate a new mountpoint entry for the node's config + let storage = &svc.config.pve_storage_pool; + let size_in_gib = max(size_in_bytes.div_ceil(1024 * 1024 * 1024) as i64, 1); + let line = format!("{storage}:{size_in_gib},mp=/mnt/p5x-{name},backup=1"); + debug!(target: "p5x", "Volume {name}: {line}"); + + let mut params = PutParams::default(); + params.mps.insert(mp_id, line); + + // Update the node config to create the volume + debug!(target: "p5x", "Patching PVE config for volume {name}"); + let vm_id = VmId::new(i64::from(pve_id)).unwrap(); + let res = svc.pve_node(pve_host) + .map_err(P5xError::ServiceError)? + .lxc() + .vmid(vm_id) + .config() + .put(params); + + // This is necessary because PUT returns {data: null} on success, + // which the UreqClient flags as an unknown error. + if let Err(UreqError::EncounteredErrors(e)) = res { + return Err(P5xError::PveError(UreqError::EncounteredErrors(e))); + } + + // Stupid hack is stupid, but we don't get back a UPID to wait on the vol to be created + info!(target: "p5x", "Successfully patched PVE config. Waiting for volume {name} to appear"); + sleep(Duration::from_secs(5)).await; + + // Load the updated config + debug!(target: "p5x", "Loading updated node config for volume {name}"); + let conf = PveConfig::load(svc, pve_host, pve_id)?; + let mount = conf.get(&format!("mp{mp_id}")) + .ok_or(P5xError::BadPrecondition("Could not find mountpoint in config after creating volume"))?; + debug!(target: "p5x", "Found mountpoint details for volume {name}: {mount}"); + + // Parse the disk name from the config + // synology-scsi-lun:vm-103-disk-1,mp=/mnt/p5x-test0,backup=1,size=1G // fixme: how does this behave for NFS? + let name_offset = storage.len() + 1; // 1 for the colon (:) + let disk_name = mount[name_offset..].split(",").next().unwrap().to_string(); + Ok((disk_name, mp_id)) +} + + /** Create a new PVE volume of the given size. */ pub async fn create( svc: &Services<'_>, @@ -64,51 +127,14 @@ pub async fn create( let (node, _lock) = lock_first_available(svc.db, Some(&reason)) .await.map_err(P5xError::LockErr)?; - // Get the next available mountpoint ID - let conf = node.config(svc)?; - let mp_id = conf.next_nth("mp"); - info!("Volume {name} will become mp{mp_id} on node {} ({})", node.hostname, node.pve_id); - - // Generate a new mountpoint entry for the node's config - let storage = &svc.config.pve_storage_pool; - let size_in_gib = max((size_in_bytes as u64).div_ceil(1024 * 1024 * 1024) as i64, 1); - let line = format!("{storage}:{size_in_gib},mp=/mnt/p5x-{name},backup=1"); - debug!("Volume {name}: {line}"); - - let mut params = PutParams::default(); - params.mps.insert(mp_id, line); - - // Update the node config to create the volume - debug!("Patching PVE config for volume {name}"); - let vm_id = VmId::new(i64::from(node.pve_id)).unwrap(); - let res = svc.pve_node(&node.pve_host) - .map_err(P5xError::ServiceError)? - .lxc() - .vmid(vm_id) - .config() - .put(params); - - // This is necessary because PUT returns {data: null} on success, - // which the UreqClient flags as an unknown error. - if let Err(UreqError::EncounteredErrors(e)) = res { - return Err(P5xError::PveError(UreqError::EncounteredErrors(e))); - } - - // Stupid hack is stupid, but we don't get back a UPID to wait on the vol to be created - info!("Successfully patched PVE config. Waiting for volume {name} to appear"); - sleep(Duration::from_secs(5)).await; - - // Load the updated config - debug!("Loading updated node config for volume {name}"); - let conf = node.config(svc)?; - let mount = conf.get(&format!("mp{mp_id}")) - .ok_or(P5xError::BadPrecondition("Could not find mountpoint in config after creating volume"))?; - debug!("Found mountpoint details for volume {name}: {mount}"); - - // Parse the disk name from the config - // synology-scsi-lun:vm-103-disk-1,mp=/mnt/p5x-test0,backup=1,size=1G // fixme: how does this behave for NFS? - let name_offset = storage.len() + 1; // 1 for the colon (:) - let disk_name = mount[name_offset..].split(",").next().unwrap(); + // Create and attach the actual PVE disk + let (disk_name, mp_id) = create_volume_unmanaged( + svc, + size_in_bytes as u64, + &node.pve_host, + node.pve_id, + name, + ).await?; // Persist the volume debug!("Inserting record into volumes table for volume {name} ({disk_name})"); diff --git a/src/api/entity/nodes.rs b/src/api/entity/nodes.rs index e695a94..66632f8 100644 --- a/src/api/entity/nodes.rs +++ b/src/api/entity/nodes.rs @@ -28,6 +28,7 @@ pub enum P5xError { ServiceError(ServiceError), PveError(proxmox_api::UreqError), SshError(SshError), + KubeError(kube::Error), InvalidNetworkInterface, BadPrecondition(&'static str), BadPostcondition(&'static str), @@ -110,6 +111,31 @@ impl PveConfig { PveConfig { lines } } + pub fn load(svc: &Services<'_>, pve_host: &str, pve_id: i32) -> Result { + let pve_ssh = svc.pve_ssh(pve_host) + .map_err(P5xError::ServiceError)?; + + let path = format!("/etc/pve/lxc/{}.conf", pve_id); + let path = Path::new(&path); + + let (mut remote_file, _) = pve_ssh.scp_recv(path) + .map_err(SshError::ClientError) + .map_err(P5xError::SshError)?; + + let mut contents = String::new(); + remote_file.read_to_string(&mut contents) + .map_err(SshError::IoError) + .map_err(P5xError::SshError)?; + + // Close the channel and wait for the whole content to be transferred + remote_file.send_eof().map_err(SshError::ClientError).map_err(P5xError::SshError)?; + remote_file.wait_eof().map_err(SshError::ClientError).map_err(P5xError::SshError)?; + remote_file.close().map_err(SshError::ClientError).map_err(P5xError::SshError)?; + remote_file.wait_close().map_err(SshError::ClientError).map_err(P5xError::SshError)?; + + Ok(PveConfig::new(&contents)) + } + /** Replace a line in the config file. */ pub fn replace( &mut self, @@ -272,28 +298,7 @@ impl Model { /** Load the LXC container config for this node. */ pub fn config(&self, svc: &Services) -> Result { - let pve_ssh = svc.pve_ssh(&self.pve_host) - .map_err(P5xError::ServiceError)?; - - let path = format!("/etc/pve/lxc/{}.conf", self.pve_id); - let path = Path::new(&path); - - let (mut remote_file, _) = pve_ssh.scp_recv(path) - .map_err(SshError::ClientError) - .map_err(P5xError::SshError)?; - - let mut contents = String::new(); - remote_file.read_to_string(&mut contents) - .map_err(SshError::IoError) - .map_err(P5xError::SshError)?; - - // Close the channel and wait for the whole content to be transferred - remote_file.send_eof().map_err(SshError::ClientError).map_err(P5xError::SshError)?; - remote_file.wait_eof().map_err(SshError::ClientError).map_err(P5xError::SshError)?; - remote_file.close().map_err(SshError::ClientError).map_err(P5xError::SshError)?; - remote_file.wait_close().map_err(SshError::ClientError).map_err(P5xError::SshError)?; - - Ok(PveConfig::new(&contents)) + PveConfig::load(svc, &self.pve_host, self.pve_id) } /** Replace the LXC container config for this node. */ diff --git a/src/main.rs b/src/main.rs index d622a4e..930fdd2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,9 @@ use dotenv::dotenv; use rocket::{Build, Rocket}; use log::{error, info}; use std::{env, process}; -use crate::api::cluster::system::ensure_ssh_keypair; +use sea_orm::Database; +use crate::api::cluster::system::{ensure_ssh_keypair, ensure_system_disk}; +use crate::api::services::Services; use crate::api::util::read_p5x_config; fn configure_rocket() -> Rocket { @@ -20,10 +22,18 @@ async fn main() { let args: Vec = env::args().collect(); if args.len() < 2 { - error!(target: "p5x", "Missing required argument. Valid modes: api-server"); + error!(target: "p5x", "Missing required argument. Valid modes: api-server,ensure-system-disk"); process::exit(1); } + let mode = &args[1]; + if mode == "ensure-system-disk" { + let anon_db = Database::connect("sqlite::memory:").await.unwrap(); + let svc = Services::build(&anon_db).await.unwrap(); // fixme: this is going to fail because of the SSH keys + ensure_system_disk(&svc).await.unwrap(); + return; + } + ensure_ssh_keypair().expect("Could not ensure SSH keypair exists."); let config = read_p5x_config(); // Do this so we early-fail if there are missing env vars @@ -31,7 +41,6 @@ async fn main() { info!(target: "p5x", "Cluster host: {} ({})", config.pve_host_name, config.pve_api_host); info!(target: "p5x", "Storage pool: {} ({})", config.pve_storage_pool, config.pve_storage_driver); - let mode = &args[1]; if mode == "api-server" { let rocket = configure_rocket(); if let Err(e) = rocket.launch().await {