From ef1de9b2af97bb4ff70a58208d5a67a404b7895f Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Thu, 20 Aug 2026 11:16:02 +0200 Subject: [PATCH 1/3] use v2 labels and envvarset, constants! plus tests, deref roles --- Cargo.lock | 251 +++++++++--------- Cargo.toml | 2 +- extra/crds.yaml | 6 +- rust/operator-binary/Cargo.toml | 3 +- rust/operator-binary/src/crd/affinity.rs | 2 +- rust/operator-binary/src/crd/mod.rs | 67 ++--- rust/operator-binary/src/discovery.rs | 133 ++++++---- rust/operator-binary/src/main.rs | 4 +- rust/operator-binary/src/zk_controller.rs | 19 +- .../src/zk_controller/apply.rs | 19 +- .../src/zk_controller/build.rs | 102 +++++-- .../build/resource/config_map.rs | 17 +- .../zk_controller/build/resource/listener.rs | 7 +- .../src/zk_controller/build/resource/pdb.rs | 12 +- .../src/zk_controller/build/resource/rbac.rs | 32 +-- .../zk_controller/build/resource/service.rs | 22 +- .../build/resource/statefulset.rs | 213 +++++++++++---- .../src/zk_controller/update_status.rs | 4 +- .../src/zk_controller/validate.rs | 158 ++--------- rust/operator-binary/src/znode_controller.rs | 24 +- .../src/znode_controller/apply.rs | 4 +- .../src/znode_controller/build.rs | 12 +- 22 files changed, 620 insertions(+), 493 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 27555b3c..619c4378 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -163,9 +163,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.91" +version = "0.1.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" dependencies = [ "proc-macro2", "quote", @@ -322,9 +322,9 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" [[package]] name = "cc" -version = "1.4.0" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9" +checksum = "509591b7bcd67f4ef775afad7662703b4935daaa6ec0e5605cfb1090b32a2b6d" dependencies = [ "find-msvc-tools", "jobserver", @@ -637,7 +637,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" dependencies = [ - "thiserror 2.0.19", + "thiserror 2.0.20", ] [[package]] @@ -916,9 +916,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" [[package]] name = "flagset" @@ -965,9 +965,9 @@ checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" [[package]] name = "futures" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a88cf1f829d945f548cf8fec32c61b1f202b6d93b45848602fc02af4b12ad218" +checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" dependencies = [ "futures-channel", "futures-core", @@ -980,9 +980,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" dependencies = [ "futures-core", "futures-sink", @@ -990,15 +990,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" [[package]] name = "futures-executor" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458" +checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" dependencies = [ "futures-core", "futures-task", @@ -1007,38 +1007,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" [[package]] name = "futures-macro" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.3", ] [[package]] name = "futures-sink" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" [[package]] name = "futures-task" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" [[package]] name = "futures-util" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures 0.1.31", "futures-channel", @@ -1148,9 +1148,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.15" +version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" +checksum = "9f877e75f39e9827ec50a572dd592684ac28c029578726c85f1b2aa6ab807449" dependencies = [ "atomic-waker", "bytes", @@ -1230,9 +1230,9 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2" +checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" dependencies = [ "bytes", "futures-core", @@ -1360,9 +1360,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" dependencies = [ "displaydoc", "potential_utf", @@ -1374,9 +1374,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" dependencies = [ "displaydoc", "litemap", @@ -1387,9 +1387,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -1401,16 +1401,17 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" +checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" [[package]] name = "icu_properties" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" dependencies = [ + "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", @@ -1421,15 +1422,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" +checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" [[package]] name = "icu_provider" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +checksum = "92a7ed671a6aad807a8651a2e1782a6598fda9ce5185dd8158549e95a91c6428" dependencies = [ "displaydoc", "icu_locale_core", @@ -1598,7 +1599,7 @@ dependencies = [ "jni-sys", "log", "simd_cesu8", - "thiserror 2.0.19", + "thiserror 2.0.20", "walkdir", "windows-link", ] @@ -1647,9 +1648,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.103" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" dependencies = [ "cfg-if", "futures-util", @@ -1666,7 +1667,7 @@ dependencies = [ "schemars", "serde", "serde_json", - "thiserror 2.0.19", + "thiserror 2.0.20", ] [[package]] @@ -1679,7 +1680,7 @@ dependencies = [ "pest_derive", "regex", "serde_json", - "thiserror 2.0.19", + "thiserror 2.0.20", ] [[package]] @@ -1708,7 +1709,7 @@ dependencies = [ [[package]] name = "k8s-version" version = "0.1.3" -source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.115.0#fb2d86579f4e3df008f78f0e527a012243483a2d" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.116.0#7b9f9ac9a76fa425ab27f2821377ef86571ca121" dependencies = [ "darling 0.24.0", "regex", @@ -1752,7 +1753,7 @@ dependencies = [ "base64 0.22.1", "bytes", "either", - "futures 0.3.33", + "futures 0.3.34", "http", "http-body", "http-body-util", @@ -1771,7 +1772,7 @@ dependencies = [ "serde", "serde-saphyr", "serde_json", - "thiserror 2.0.19", + "thiserror 2.0.20", "tokio", "tokio-util", "tower", @@ -1795,7 +1796,7 @@ dependencies = [ "serde", "serde-value", "serde_json", - "thiserror 2.0.19", + "thiserror 2.0.20", ] [[package]] @@ -1823,7 +1824,7 @@ dependencies = [ "async-stream", "backon", "educe 0.6.0", - "futures 0.3.33", + "futures 0.3.34", "hashbrown 0.16.1", "hostname", "json-patch", @@ -1833,7 +1834,7 @@ dependencies = [ "pin-project", "serde", "serde_json", - "thiserror 2.0.19", + "thiserror 2.0.20", "tokio", "tokio-util", "tracing", @@ -1886,9 +1887,9 @@ dependencies = [ [[package]] name = "litemap" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" [[package]] name = "lock_api" @@ -1992,9 +1993,9 @@ checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-integer" -version = "0.1.46" +version = "0.1.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" dependencies = [ "num-traits", ] @@ -2047,7 +2048,7 @@ dependencies = [ "futures-sink", "js-sys", "pin-project-lite", - "thiserror 2.0.19", + "thiserror 2.0.20", "tracing", ] @@ -2089,7 +2090,7 @@ dependencies = [ "opentelemetry_sdk", "prost", "reqwest", - "thiserror 2.0.19", + "thiserror 2.0.20", "tokio", "tonic", "tonic-types", @@ -2127,7 +2128,7 @@ dependencies = [ "percent-encoding", "portable-atomic", "rand 0.9.5", - "thiserror 2.0.19", + "thiserror 2.0.20", "tokio", "tokio-stream", ] @@ -2209,9 +2210,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.8" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7df728be843c7070fab6ab7c328c4e9e9d78e23bf749c0669c86ee7ebfa050a2" +checksum = "5a07a60cc7a4d00c91f95c685609d1d2f79050e6804b70ebedd7650f0b839bcf" dependencies = [ "memchr", "ucd-trie", @@ -2219,9 +2220,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.8" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e2dd6fc3b26b3462ee188aac870f5a41d398f1cd5e2408d16531bd71c9591fd" +checksum = "b3a83744a5c8455b8b3e0dc5031362780a347c878bdd11584d1a8984228cc88d" dependencies = [ "pest", "pest_generator", @@ -2229,9 +2230,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.8" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a7a9205cfb6f596a9e8b689c0a15f9ceb7a1aafae7aaf788150ac65b29975b6" +checksum = "e0cd3451aa3de60d4b9a1e736885e4dea6b31617598026f12256ad566d63304a" dependencies = [ "pest", "pest_meta", @@ -2242,9 +2243,9 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.8.8" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85abd351c0de1e8384fc791a0737111a350394937e92b956b743dac12429f57c" +checksum = "e04d3a0849e241d7dfce834c83b1c5edc8622009e8dd51a12ba1927c32f05496" dependencies = [ "pest", ] @@ -2298,15 +2299,15 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" [[package]] name = "portable-atomic" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" [[package]] name = "portable-atomic-util" @@ -2319,9 +2320,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" dependencies = [ "zerovec", ] @@ -2498,18 +2499,18 @@ dependencies = [ [[package]] name = "ref-cast" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216e8f773d7923bcba9ceb86a86c93cabb3903a11872fc3f138c49630e50b96d" +checksum = "7e440fb4e4b4147295338efb76001ab9e4efc0e5839df2c47fc5ac2381d365c3" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9283685feec7d69af75fb0e858d5e7378f33fe4fc699383b2916ab9273e03c" +checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a" dependencies = [ "proc-macro2", "quote", @@ -2695,9 +2696,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.13" +version = "0.103.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +checksum = "0527518605e68109d875e248ea259b6758801cf165e4b2c2733ae3b51f12535a" dependencies = [ "ring", "rustls-pki-types", @@ -3126,7 +3127,7 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "stackable-certs" version = "0.4.1" -source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.115.0#fb2d86579f4e3df008f78f0e527a012243483a2d" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.116.0#7b9f9ac9a76fa425ab27f2821377ef86571ca121" dependencies = [ "const-oid", "ecdsa", @@ -3149,8 +3150,8 @@ dependencies = [ [[package]] name = "stackable-operator" -version = "0.115.0" -source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.115.0#fb2d86579f4e3df008f78f0e527a012243483a2d" +version = "0.116.0" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.116.0#7b9f9ac9a76fa425ab27f2821377ef86571ca121" dependencies = [ "base64 0.23.1", "clap", @@ -3159,7 +3160,7 @@ dependencies = [ "dockerfile-parser", "educe 0.7.6", "either", - "futures 0.3.33", + "futures 0.3.34", "http", "indexmap", "java-properties", @@ -3194,7 +3195,7 @@ dependencies = [ [[package]] name = "stackable-operator-derive" version = "0.3.1" -source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.115.0#fb2d86579f4e3df008f78f0e527a012243483a2d" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.116.0#7b9f9ac9a76fa425ab27f2821377ef86571ca121" dependencies = [ "darling 0.24.0", "proc-macro2", @@ -3205,7 +3206,7 @@ dependencies = [ [[package]] name = "stackable-shared" version = "0.1.2" -source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.115.0#fb2d86579f4e3df008f78f0e527a012243483a2d" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.116.0#7b9f9ac9a76fa425ab27f2821377ef86571ca121" dependencies = [ "jiff", "k8s-openapi", @@ -3222,7 +3223,7 @@ dependencies = [ [[package]] name = "stackable-telemetry" version = "0.6.5" -source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.115.0#fb2d86579f4e3df008f78f0e527a012243483a2d" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.116.0#7b9f9ac9a76fa425ab27f2821377ef86571ca121" dependencies = [ "axum", "clap", @@ -3246,7 +3247,7 @@ dependencies = [ [[package]] name = "stackable-versioned" version = "0.11.1" -source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.115.0#fb2d86579f4e3df008f78f0e527a012243483a2d" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.116.0#7b9f9ac9a76fa425ab27f2821377ef86571ca121" dependencies = [ "kube", "schemars", @@ -3260,7 +3261,7 @@ dependencies = [ [[package]] name = "stackable-versioned-macros" version = "0.11.1" -source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.115.0#fb2d86579f4e3df008f78f0e527a012243483a2d" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.116.0#7b9f9ac9a76fa425ab27f2821377ef86571ca121" dependencies = [ "convert_case", "convert_case_extras", @@ -3278,7 +3279,7 @@ dependencies = [ [[package]] name = "stackable-webhook" version = "0.9.2" -source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.115.0#fb2d86579f4e3df008f78f0e527a012243483a2d" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.116.0#7b9f9ac9a76fa425ab27f2821377ef86571ca121" dependencies = [ "arc-swap", "async-trait", @@ -3316,7 +3317,7 @@ dependencies = [ "clap", "const_format", "fnv", - "futures 0.3.33", + "futures 0.3.34", "indoc", "pin-project", "semver", @@ -3434,11 +3435,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.19" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ - "thiserror-impl 2.0.19", + "thiserror-impl 2.0.20", ] [[package]] @@ -3454,9 +3455,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.19" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", @@ -3504,9 +3505,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" dependencies = [ "displaydoc", "zerovec", @@ -3605,7 +3606,7 @@ checksum = "0a9a838bc64b4adf9736893e0687f63cc0c04297045a5fc946210963420987be" dependencies = [ "async-trait", "byteorder", - "futures 0.3.33", + "futures 0.3.34", "once_cell", "pin-project", "snafu 0.8.9", @@ -3751,7 +3752,7 @@ checksum = "050686193eb999b4bb3bc2acfa891a13da00f79734704c4b8b4ef1a10b368a3c" dependencies = [ "crossbeam-channel", "symlink", - "thiserror 2.0.19", + "thiserror 2.0.20", "time", "tracing-subscriber", ] @@ -3916,9 +3917,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.24.0" +version = "1.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" +checksum = "2cefc03fd367c0c6d4305de1b312cf00248c4114f4a0418ce6a6af769e3b0bd9" dependencies = [ "js-sys", "wasm-bindgen", @@ -3978,9 +3979,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" dependencies = [ "cfg-if", "once_cell", @@ -3991,9 +3992,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.76" +version = "0.4.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" +checksum = "6b7777d5cc23d0e91404e53ce2d5e8ec7acae3026b16233dba62cd3246457950" dependencies = [ "js-sys", "wasm-bindgen", @@ -4001,9 +4002,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4011,9 +4012,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" dependencies = [ "bumpalo", "proc-macro2", @@ -4024,18 +4025,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.103" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" +checksum = "c435338968042f4f59a557f690a253676d47ce13ceb55d70100e7facf6620a30" dependencies = [ "js-sys", "wasm-bindgen", @@ -4218,9 +4219,9 @@ checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" [[package]] name = "writeable" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" [[package]] name = "x509-cert" @@ -4328,9 +4329,9 @@ dependencies = [ [[package]] name = "zerotrie" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" dependencies = [ "displaydoc", "yoke", @@ -4339,9 +4340,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" dependencies = [ "yoke", "zerofrom", @@ -4350,13 +4351,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.3" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +checksum = "9f212a141d820099d57ffafb9569be9617a6f27d3dc881fbee8fb56642f917a9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.3", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index af837a71..d62c5b98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ edition = "2024" repository = "https://github.com/stackabletech/zookeeper-operator" [workspace.dependencies] -stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "stackable-operator-0.115.0", features = ["webhook"] } +stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "stackable-operator-0.116.0", features = ["webhook"] } anyhow = "1.0" built = { version = "0.8", features = ["chrono", "git2"] } diff --git a/extra/crds.yaml b/extra/crds.yaml index c5d46fee..65141ff5 100644 --- a/extra/crds.yaml +++ b/extra/crds.yaml @@ -676,7 +676,8 @@ spec: default: {} description: |- `envOverrides` configure environment variables to be set in the Pods. - It is a map from strings to strings - environment variables and the value to set. + It is a map from environment variable names to their values. The names are validated to be + valid environment variable names. Read the [environment variable overrides documentation](https://docs.stackable.tech/home/nightly/concepts/overrides#env-overrides) for more information and consult the operator specific usage guide to find out about @@ -1226,7 +1227,8 @@ spec: default: {} description: |- `envOverrides` configure environment variables to be set in the Pods. - It is a map from strings to strings - environment variables and the value to set. + It is a map from environment variable names to their values. The names are validated to be + valid environment variable names. Read the [environment variable overrides documentation](https://docs.stackable.tech/home/nightly/concepts/overrides#env-overrides) for more information and consult the operator specific usage guide to find out about diff --git a/rust/operator-binary/Cargo.toml b/rust/operator-binary/Cargo.toml index 0679d91b..901f3799 100644 --- a/rust/operator-binary/Cargo.toml +++ b/rust/operator-binary/Cargo.toml @@ -10,7 +10,7 @@ publish = false build = "build.rs" [dependencies] -stackable-operator = { workspace = true, features = ["test-support"] } +stackable-operator.workspace = true anyhow.workspace = true clap.workspace = true @@ -30,6 +30,7 @@ tracing.workspace = true [dev-dependencies] serde_yaml.workspace = true +stackable-operator = { workspace = true, features = ["test-support"] } [build-dependencies] built.workspace = true diff --git a/rust/operator-binary/src/crd/affinity.rs b/rust/operator-binary/src/crd/affinity.rs index f65fcb5b..8f1c18de 100644 --- a/rust/operator-binary/src/crd/affinity.rs +++ b/rust/operator-binary/src/crd/affinity.rs @@ -7,7 +7,7 @@ use crate::crd::{APP_NAME, ZookeeperRole}; pub fn get_affinity(cluster_name: &str, role: &ZookeeperRole) -> StackableAffinityFragment { let affinity_between_role_pods = - affinity_between_role_pods(APP_NAME, cluster_name, &role.to_string(), 70); + affinity_between_role_pods(APP_NAME, cluster_name, role.as_ref(), 70); StackableAffinityFragment { pod_affinity: None, diff --git a/rust/operator-binary/src/crd/mod.rs b/rust/operator-binary/src/crd/mod.rs index 811925bf..8eac203d 100644 --- a/rust/operator-binary/src/crd/mod.rs +++ b/rust/operator-binary/src/crd/mod.rs @@ -1,4 +1,4 @@ -use std::str::FromStr; +use std::{ops::Deref, str::FromStr}; use serde::{Deserialize, Serialize}; use stackable_operator::{ @@ -12,30 +12,31 @@ use stackable_operator::{ }, }, config::{fragment::Fragment, merge::Merge}, + constant, crd::ClusterRef, deep_merger::ObjectOverrides, k8s_openapi::apimachinery::pkg::api::resource::Quantity, kube::{CustomResource, ResourceExt}, product_logging::{self, spec::Logging}, - role_utils::{GenericRoleConfig, Role}, + role_utils::GenericRoleConfig, schemars::{self, JsonSchema}, shared::time::Duration, status::condition::{ClusterCondition, HasStatusCondition}, utils::cluster_info::KubernetesClusterInfo, v2::{ config_overrides::KeyValueConfigOverrides, - role_utils::JavaCommonConfig, + role_utils::{JavaCommonConfig, Role}, types::{ common::Port, kubernetes::{ ConfigMapName, ListenerClassName, ListenerName, NamespaceName, ServiceName, }, - operator::RoleName, + operator::{OperatorName, ProductName, RoleName}, }, }, versioned::versioned, }; -use strum::{Display, EnumIter, EnumString}; +use strum::{Display, EnumIter}; use crate::crd::{affinity::get_affinity, v1alpha1::ZookeeperServerRoleConfig}; @@ -50,14 +51,21 @@ pub mod tls; /// Lives in the `crd` module (rather than the controller build tree) because it is shared by both /// controllers and by [`v1alpha1::ZookeeperCluster::server_role_listener_fqdn`]. pub fn role_listener_name(cluster_name: &str, zk_role: &ZookeeperRole) -> ListenerName { - ListenerName::from_str(&format!("{cluster_name}-{zk_role}")) + ListenerName::from_str(&format!("{cluster_name}-{role}", role = zk_role.as_ref())) .expect("the role listener name should be a valid Listener name") } pub const APP_NAME: &str = "zookeeper"; -pub const OPERATOR_NAME: &str = "zookeeper.stackable.tech"; +pub const ZOOKEEPER_OPERATOR_NAME: &str = "zookeeper.stackable.tech"; pub const FIELD_MANAGER: &str = "zookeeper-operator"; +// The product and operator names as type-safe label values. Shared by both controllers, so they +// live here rather than in a controller module. +constant!(pub PRODUCT_NAME: ProductName = APP_NAME); +constant!(pub OPERATOR_NAME: OperatorName = ZOOKEEPER_OPERATOR_NAME); + +constant!(SERVER_ROLE_NAME: RoleName = "server"); + pub const ZOOKEEPER_SERVER_PORT_NAME: &str = "zk"; pub const ZOOKEEPER_LEADER_PORT_NAME: &str = "zk-leader"; pub const ZOOKEEPER_LEADER_PORT: Port = Port(2888); @@ -307,36 +315,27 @@ pub mod versioned { } } -#[derive( - Clone, - Debug, - Deserialize, - Display, - EnumIter, - Eq, - Hash, - JsonSchema, - Ord, - PartialEq, - PartialOrd, - Serialize, - EnumString, -)] -#[strum(serialize_all = "camelCase")] +#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub enum ZookeeperRole { - #[strum(serialize = "server")] Server, } -impl From for RoleName { - fn from(value: ZookeeperRole) -> Self { - RoleName::from_str(&value.to_string()).expect("a ZookeeperRole is a valid role name") +impl Deref for ZookeeperRole { + type Target = RoleName; + + fn deref(&self) -> &Self::Target { + match self { + ZookeeperRole::Server => &SERVER_ROLE_NAME, + } } } -impl From<&ZookeeperRole> for RoleName { - fn from(value: &ZookeeperRole) -> Self { - RoleName::from_str(&value.to_string()).expect("a ZookeeperRole is a valid role name") +impl ZookeeperRole { + /// The type-safe name of this role, e.g. to build [`ResourceNames`][rn]. + /// + /// [rn]: stackable_operator::v2::role_group_utils::ResourceNames + pub fn role_name(&self) -> RoleName { + RoleName::clone(self) } } @@ -471,6 +470,14 @@ mod tests { use super::*; + #[test] + fn test_constants() { + // Test that dereferencing the constants does not panic. + let _ = *PRODUCT_NAME; + let _ = *OPERATOR_NAME; + let _ = *SERVER_ROLE_NAME; + } + fn get_server_secret_class(zk: &v1alpha1::ZookeeperCluster) -> Option<&str> { zk.spec .cluster_config diff --git a/rust/operator-binary/src/discovery.rs b/rust/operator-binary/src/discovery.rs index 9b3a7d8b..29035d05 100644 --- a/rust/operator-binary/src/discovery.rs +++ b/rust/operator-binary/src/discovery.rs @@ -3,32 +3,25 @@ //! Shared by the build steps of both controllers: the ZookeeperCluster controller publishes the //! whole ensemble, the ZookeeperZnode controller publishes the same ensemble narrowed to a chroot. -use std::str::FromStr; - use snafu::{ResultExt, Snafu}; use stackable_operator::{ builder::{configmap::ConfigMapBuilder, meta::ObjectMetaBuilder}, k8s_openapi::api::core::v1::ConfigMap, kube::Resource, + kvp::{Label, Labels}, v2::{ - HasName, HasUid, NameIsValidLabelValue, - builder::meta::ownerreference_from_resource, - kvp::label::recommended_labels, - types::operator::{ControllerName, ProductVersion, RoleGroupName}, + HasName, HasUid, NameIsValidLabelValue, builder::meta::ownerreference_from_resource, + kvp::label, }, }; use crate::{ - crd::{ZookeeperRole, security::ZookeeperSecurity}, + crd::{OPERATOR_NAME, PRODUCT_NAME, ZookeeperRole, security::ZookeeperSecurity}, listener_addresses::ListenerAddresses, - zk_controller::validate::{ValidatedCluster, operator_name, product_name}, - znode_controller::validate::ValidatedZnode, + zk_controller::{build::recommended_labels_for_role_resources, validate::ValidatedCluster}, + znode_controller::{self, validate::ValidatedZnode}, }; -// Placeholder role-group name used for the recommended labels of the role-level discovery -// `ConfigMap` (which is not tied to a single role group). -stackable_operator::constant!(PLACEHOLDER_DISCOVERY_ROLE_GROUP: RoleGroupName = "discovery"); - type Result = std::result::Result; #[derive(Snafu, Debug)] @@ -58,14 +51,13 @@ pub enum Error { /// published. pub fn build_discovery_configmap( validated_cluster: &ValidatedCluster, - controller_name: &str, + zk_role: &ZookeeperRole, listener_addresses: &ListenerAddresses, ) -> Result { build_discovery_configmap_for_owner( validated_cluster, &validated_cluster.namespace, - controller_name, - &validated_cluster.product_version, + recommended_labels_for_role_resources(validated_cluster, zk_role), listener_addresses, None, &validated_cluster.cluster_config.zookeeper_security, @@ -80,21 +72,42 @@ pub fn build_discovery_configmap( /// ensemble. pub fn build_znode_discovery_configmap( validated_znode: &ValidatedZnode, - controller_name: &str, listener_addresses: &ListenerAddresses, chroot: &str, ) -> Result { build_discovery_configmap_for_owner( validated_znode, &validated_znode.namespace, - controller_name, - &validated_znode.product_version, + znode_discovery_labels(validated_znode), listener_addresses, Some(chroot), &validated_znode.zookeeper_security, ) } +/// The recommended labels for the znode's discovery [`ConfigMap`]. +/// +/// The znode controller's discovery ConfigMap cannot use the label functions from +/// [`stackable_operator::v2::kvp::label`], because its `app.kubernetes.io/instance` value is the +/// name of the owning [`ZookeeperZnode`](crate::crd::v1alpha1::ZookeeperZnode), not a +/// [`ClusterName`](stackable_operator::v2::types::operator::ClusterName). The label set matches +/// the role-level recommended labels of the cluster controller's discovery ConfigMap otherwise. +fn znode_discovery_labels(validated_znode: &ValidatedZnode) -> Labels { + Labels::from_iter([ + Label::instance(&validated_znode.to_label_value()).expect( + "the value implements NameIsValidLabelValue and is therefore a valid label value", + ), + label::label_app_kubernetes_io_name(&PRODUCT_NAME), + label::label_app_kubernetes_io_version(&validated_znode.product_version), + label::label_app_kubernetes_io_component(&ZookeeperRole::Server), + label::label_app_kubernetes_io_managed_by( + &OPERATOR_NAME, + &znode_controller::CONTROLLER_NAME, + ), + label::label_stackable_tech_vendor(), + ]) +} + /// Build a discovery [`ConfigMap`] containing ZooKeeper connection details from the /// [`ListenerAddresses`] published by the role Listener. /// @@ -104,21 +117,13 @@ pub fn build_znode_discovery_configmap( fn build_discovery_configmap_for_owner( owner: &(impl Resource + HasName + HasUid + NameIsValidLabelValue), namespace: impl Into, - controller_name: &str, - product_version: &ProductVersion, + labels: Labels, listener_addresses: &ListenerAddresses, chroot: Option<&str>, zookeeper_security: &ZookeeperSecurity, ) -> Result { let name = owner.to_name(); - // The discovery ConfigMap is a role-level resource of the `server` role, conventionally - // labelled with the `discovery` role group. The controller name differs between the cluster and - // znode controllers, so it is passed in and validated into the type-safe newtype here. - let controller_name = ControllerName::from_str(controller_name) - .expect("the controller name is a valid label value"); - let role_group_name = PLACEHOLDER_DISCOVERY_ROLE_GROUP.clone(); - // Write a connection string of the format that Java ZooKeeper client expects: // "{host1}:{port1},{host2:port2},.../{chroot}" // See https://zookeeper.apache.org/doc/current/apidocs/zookeeper-server/org/apache/zookeeper/ZooKeeper.html#ZooKeeper-java.lang.String-int-org.apache.zookeeper.Watcher- @@ -136,15 +141,7 @@ fn build_discovery_configmap_for_owner( .name(name) .namespace(namespace) .ownerreference(ownerreference_from_resource(owner, None, Some(true))) - .with_labels(recommended_labels( - owner, - &product_name(), - product_version, - &operator_name(), - &controller_name, - &ZookeeperRole::Server.into(), - &role_group_name, - )) + .with_labels(labels) .build(), ) .add_data("ZOOKEEPER", conn_str) @@ -161,6 +158,8 @@ fn build_discovery_configmap_for_owner( #[cfg(test)] mod tests { + use std::collections::BTreeMap; + use super::*; use crate::{ crd::ZOOKEEPER_SERVER_PORT_NAME, @@ -168,14 +167,10 @@ mod tests { listener_addresses, test_support::{ingress_address, role_listener}, }, - zk_controller::{ - ZK_CONTROLLER_NAME, - test_support::{minimal_zk, try_validate_with_role_listener}, - }, - znode_controller::{ - ZNODE_CONTROLLER_NAME, - test_support::{minimal_znode, validated_znode}, + zk_controller::test_support::{ + app_version_label, minimal_zk, try_validate_with_role_listener, }, + znode_controller::test_support::{minimal_znode, validated_znode}, }; const ZK_YAML: &str = r#" @@ -242,7 +237,7 @@ mod tests { let addresses = published_addresses(&[("node-0", 2282), ("node-1", 2282)]); let config_map = - build_discovery_configmap(&cluster, ZK_CONTROLLER_NAME, &addresses).expect("build"); + build_discovery_configmap(&cluster, &ZookeeperRole::Server, &addresses).expect("build"); assert_eq!( config_map.metadata.name.as_deref(), @@ -266,9 +261,12 @@ mod tests { let cluster = try_validate_with_role_listener(&minimal_zk(ZK_YAML), None) .expect("validate should succeed for the test fixture"); - let config_map = - build_discovery_configmap(&cluster, ZK_CONTROLLER_NAME, &ListenerAddresses::default()) - .expect("build"); + let config_map = build_discovery_configmap( + &cluster, + &ZookeeperRole::Server, + &ListenerAddresses::default(), + ) + .expect("build"); assert_eq!( config_map.metadata.name.as_deref(), @@ -287,13 +285,9 @@ mod tests { fn znode_discovery_config_map_narrows_the_ensemble_to_the_chroot() { let znode = validated_znode(&minimal_znode(ZNODE_YAML)); - let config_map = build_znode_discovery_configmap( - &znode, - ZNODE_CONTROLLER_NAME, - &znode.discovery_addresses, - ZNODE_PATH, - ) - .expect("build"); + let config_map = + build_znode_discovery_configmap(&znode, &znode.discovery_addresses, ZNODE_PATH) + .expect("build"); // The ConfigMap is named after the znode, not after the referenced cluster. assert_eq!(config_map.metadata.name.as_deref(), Some("simple-znode")); @@ -315,11 +309,38 @@ mod tests { assert!(matches!( build_znode_discovery_configmap( &znode, - ZNODE_CONTROLLER_NAME, &znode.discovery_addresses, "znode-without-a-leading-slash", ), Err(Error::RelativeChroot { .. }) )); } + + /// The znode discovery ConfigMap's labels are hand-composed (see [`znode_discovery_labels`]), + /// so lock the whole set: `instance` is the znode name, the rest matches the role-level + /// recommended labels of the cluster controller's discovery ConfigMap. + #[test] + fn znode_discovery_config_map_carries_the_expected_labels() { + let znode = validated_znode(&minimal_znode(ZNODE_YAML)); + + let config_map = + build_znode_discovery_configmap(&znode, &znode.discovery_addresses, ZNODE_PATH) + .expect("build"); + + let expected_labels = BTreeMap::from( + [ + ("app.kubernetes.io/component", "server".to_owned()), + ("app.kubernetes.io/instance", "simple-znode".to_owned()), + ( + "app.kubernetes.io/managed-by", + "zookeeper.stackable.tech_znode".to_owned(), + ), + ("app.kubernetes.io/name", "zookeeper".to_owned()), + ("app.kubernetes.io/version", app_version_label("3.9.5")), + ("stackable.tech/vendor", "Stackable".to_owned()), + ] + .map(|(key, value)| (key.to_owned(), value)), + ); + assert_eq!(config_map.metadata.labels, Some(expected_labels)); + } } diff --git a/rust/operator-binary/src/main.rs b/rust/operator-binary/src/main.rs index c8babfeb..c7d5d4d0 100644 --- a/rust/operator-binary/src/main.rs +++ b/rust/operator-binary/src/main.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use anyhow::anyhow; use clap::Parser; use crd::{ - APP_NAME, OPERATOR_NAME, ZookeeperCluster, ZookeeperClusterVersion, ZookeeperZnode, + APP_NAME, ZOOKEEPER_OPERATOR_NAME, ZookeeperCluster, ZookeeperClusterVersion, ZookeeperZnode, ZookeeperZnodeVersion, v1alpha1, }; use futures::{FutureExt, StreamExt, TryFutureExt}; @@ -101,7 +101,7 @@ async fn main() -> anyhow::Result<()> { .map(anyhow::Ok); let client = stackable_operator::client::initialize_operator( - Some(OPERATOR_NAME.to_string()), + Some(ZOOKEEPER_OPERATOR_NAME.to_string()), &common.cluster_info, ) .await?; diff --git a/rust/operator-binary/src/zk_controller.rs b/rust/operator-binary/src/zk_controller.rs index f246b8d5..9f80646b 100644 --- a/rust/operator-binary/src/zk_controller.rs +++ b/rust/operator-binary/src/zk_controller.rs @@ -3,13 +3,14 @@ //! This is the controller driver: it runs the //! `dereference -> validate -> build -> apply -> update_status` pipeline, with each step living //! in its own submodule. -use std::{marker::PhantomData, sync::Arc}; +use std::{marker::PhantomData, str::FromStr, sync::Arc}; use const_format::concatcp; use snafu::{ResultExt, Snafu}; use stackable_operator::{ cli::OperatorEnvironmentOptions, cluster_resources::ClusterResourceApplyStrategy, + constant, crd::listener::v1alpha1::Listener, k8s_openapi::api::{ apps::v1::StatefulSet, @@ -24,11 +25,12 @@ use stackable_operator::{ }, logging::controller::ReconcilerError, shared::time::Duration, + v2::types::operator::ControllerName, }; use strum::{EnumDiscriminants, IntoStaticStr}; use crate::{ - OPERATOR_NAME, ObjectRef, + ObjectRef, ZOOKEEPER_OPERATOR_NAME, crd::v1alpha1, zk_controller::{apply::Applier, update_status::update_status}, }; @@ -40,10 +42,13 @@ mod update_status; pub(crate) mod validate; pub const ZK_CONTROLLER_NAME: &str = "zookeepercluster"; -pub const ZK_FULL_CONTROLLER_NAME: &str = concatcp!(ZK_CONTROLLER_NAME, '.', OPERATOR_NAME); +pub const ZK_FULL_CONTROLLER_NAME: &str = + concatcp!(ZK_CONTROLLER_NAME, '.', ZOOKEEPER_OPERATOR_NAME); pub const LISTENER_VOLUME_NAME: &str = "listener"; pub const LISTENER_VOLUME_DIR: &str = "/stackable/listener"; +constant!(pub(crate) CONTROLLER_NAME: ControllerName = ZK_CONTROLLER_NAME); + pub struct Ctx { pub client: stackable_operator::client::Client, pub operator_environment: OperatorEnvironmentOptions, @@ -273,11 +278,18 @@ mod tests { use crate::{ crd::ZookeeperRole, zk_controller::{ + CONTROLLER_NAME, build::resource::config_map, test_support::{cluster_info, minimal_zk, validated_cluster}, }, }; + #[test] + fn test_constants() { + // Test that dereferencing the constants does not panic. + let _ = *CONTROLLER_NAME; + } + #[test] fn test_default_config() { let zookeeper_yaml = r#" @@ -554,6 +566,7 @@ mod tests { config_map::build_server_rolegroup_config_map( &validated_cluster, &cluster_info(), + &ZookeeperRole::Server, &role_group_name, rolegroup_config, ) diff --git a/rust/operator-binary/src/zk_controller/apply.rs b/rust/operator-binary/src/zk_controller/apply.rs index 9169e09a..07fb97bc 100644 --- a/rust/operator-binary/src/zk_controller/apply.rs +++ b/rust/operator-binary/src/zk_controller/apply.rs @@ -1,19 +1,21 @@ //! The apply step in the ZookeeperCluster controller. -use std::{marker::PhantomData, str::FromStr}; +use std::marker::PhantomData; use snafu::{ResultExt, Snafu}; use stackable_operator::{ client::Client, cluster_resources::{ClusterResource, ClusterResourceApplyStrategy, ClusterResources}, deep_merger::ObjectOverrides, - v2::{cluster_resources::cluster_resources_new, types::operator::ControllerName}, + v2::cluster_resources::cluster_resources_new, }; use strum::{EnumDiscriminants, IntoStaticStr}; -use crate::zk_controller::{ - Applied, KubernetesResources, Prepared, ZK_CONTROLLER_NAME, - validate::{ValidatedCluster, operator_name, product_name}, +use crate::{ + crd::{OPERATOR_NAME, PRODUCT_NAME}, + zk_controller::{ + Applied, CONTROLLER_NAME, KubernetesResources, Prepared, validate::ValidatedCluster, + }, }; #[derive(Snafu, Debug, EnumDiscriminants)] @@ -50,10 +52,9 @@ impl<'a> Applier<'a> { ) -> Applier<'a> { // Names are derived from compile-time constants. let cluster_resources = cluster_resources_new( - &product_name(), - &operator_name(), - &ControllerName::from_str(ZK_CONTROLLER_NAME) - .expect("ZK_CONTROLLER_NAME should be a valid controller name"), + &PRODUCT_NAME, + &OPERATOR_NAME, + &CONTROLLER_NAME, &cluster.name, &cluster.namespace, &cluster.uid, diff --git a/rust/operator-binary/src/zk_controller/build.rs b/rust/operator-binary/src/zk_controller/build.rs index b762fc4d..f3c9146c 100644 --- a/rust/operator-binary/src/zk_controller/build.rs +++ b/rust/operator-binary/src/zk_controller/build.rs @@ -9,20 +9,25 @@ //! remaining submodules ([`command`], [`graceful_shutdown`], [`jvm`], //! [`properties`]) produce fragments that those resource builders assemble. -use std::{marker::PhantomData, str::FromStr}; +use std::marker::PhantomData; use snafu::{ResultExt, Snafu}; use stackable_operator::{ builder::meta::ObjectMetaBuilder, + kvp::Labels, utils::cluster_info::KubernetesClusterInfo, - v2::{builder::meta::ownerreference_from_resource, types::operator::RoleGroupName}, + v2::{ + builder::meta::ownerreference_from_resource, + kvp::label, + types::operator::{RoleGroupName, RoleName}, + }, }; use crate::{ - crd::ZookeeperRole, + crd::{OPERATOR_NAME, PRODUCT_NAME, ZookeeperRole}, discovery, zk_controller::{ - KubernetesResources, Prepared, ZK_CONTROLLER_NAME, + CONTROLLER_NAME, KubernetesResources, Prepared, build::resource::{ config_map, listener::build_role_listener, @@ -37,10 +42,6 @@ use crate::{ }, }; -// Placeholder role-group name used for the recommended labels of the role-level `Listener` -// (which is not tied to a single role group). -stackable_operator::constant!(pub(crate) NONE_ROLE_GROUP_NAME: RoleGroupName = "none"); - pub mod command; pub mod graceful_shutdown; pub mod jvm; @@ -98,10 +99,12 @@ pub fn build( // the type-safe `RoleGroupName`. services.push(build_server_rolegroup_headless_service( cluster, + &zk_role, rolegroup_name, )); services.push(build_server_rolegroup_metrics_service( cluster, + &zk_role, rolegroup_name, rolegroup_config, )); @@ -109,6 +112,7 @@ pub fn build( config_map::build_server_rolegroup_config_map( cluster, cluster_info, + &zk_role, rolegroup_name, rolegroup_config, ) @@ -117,7 +121,7 @@ pub fn build( })?, ); stateful_sets.push( - build_server_rolegroup_statefulset(cluster, rolegroup_name, rolegroup_config) + build_server_rolegroup_statefulset(cluster, &zk_role, rolegroup_name, rolegroup_config) .with_context(|_| StatefulSetSnafu { rolegroup: rolegroup_name.clone(), })?, @@ -130,12 +134,9 @@ pub fn build( let listeners = vec![build_role_listener(cluster, &zk_role)]; - let discovery_config_map = discovery::build_discovery_configmap( - cluster, - ZK_CONTROLLER_NAME, - &cluster.discovery_addresses, - ) - .context(DiscoveryConfigMapSnafu)?; + let discovery_config_map = + discovery::build_discovery_configmap(cluster, &zk_role, &cluster.discovery_addresses) + .context(DiscoveryConfigMapSnafu)?; Ok(KubernetesResources { stateful_sets, @@ -151,24 +152,89 @@ pub fn build( } /// Returns an [`ObjectMetaBuilder`] pre-filled with the namespace, an owner reference back to -/// the cluster, and the recommended labels for a resource named `name` in `role_group_name`. +/// the cluster, the given `name`, and the given `labels` (usually one of the recommended label +/// sets built by the functions below). /// /// Consolidates the metadata chain repeated by the child-resource builders. Call sites that /// need extra labels/annotations chain them onto the returned builder. pub(crate) fn object_meta( cluster: &ValidatedCluster, name: impl Into, - role_group_name: &RoleGroupName, + labels: Labels, ) -> ObjectMetaBuilder { let mut builder = ObjectMetaBuilder::new(); builder .name_and_namespace(cluster) .name(name) .ownerreference(ownerreference_from_resource(cluster, None, Some(true))) - .with_labels(cluster.recommended_labels(role_group_name)); + .with_labels(labels); builder } +pub(crate) fn recommended_labels_for_cluster_resources(cluster: &ValidatedCluster) -> Labels { + label::recommended_labels_for_cluster_resources( + &cluster.name, + &PRODUCT_NAME, + &cluster.product_version, + &OPERATOR_NAME, + &CONTROLLER_NAME, + ) +} + +pub(crate) fn recommended_labels_for_role_resources( + cluster: &ValidatedCluster, + role_name: &RoleName, +) -> Labels { + label::recommended_labels_for_role_resources( + &cluster.name, + &PRODUCT_NAME, + &cluster.product_version, + &OPERATOR_NAME, + &CONTROLLER_NAME, + role_name, + ) +} + +pub(crate) fn recommended_labels_for_role_group_resources( + cluster: &ValidatedCluster, + role_name: &RoleName, + role_group_name: &RoleGroupName, +) -> Labels { + label::recommended_labels_for_role_group_resources( + &cluster.name, + &PRODUCT_NAME, + &cluster.product_version, + &OPERATOR_NAME, + &CONTROLLER_NAME, + role_name, + role_group_name, + ) +} + +pub(crate) fn recommended_labels_for_unversioned_role_group_resources( + cluster: &ValidatedCluster, + role_name: &RoleName, + role_group_name: &RoleGroupName, +) -> Labels { + label::recommended_labels_for_unversioned_role_group_resources( + &cluster.name, + &PRODUCT_NAME, + &OPERATOR_NAME, + &CONTROLLER_NAME, + role_name, + role_group_name, + ) +} + +/// Selector labels matching the pods of a role group. +pub(crate) fn role_group_selector( + cluster: &ValidatedCluster, + role_name: &RoleName, + role_group_name: &RoleGroupName, +) -> Labels { + label::role_group_selector(&cluster.name, &PRODUCT_NAME, role_name, role_group_name) +} + #[cfg(test)] mod tests { use stackable_operator::kube::Resource; diff --git a/rust/operator-binary/src/zk_controller/build/resource/config_map.rs b/rust/operator-binary/src/zk_controller/build/resource/config_map.rs index 508ed29c..2a6386dc 100644 --- a/rust/operator-binary/src/zk_controller/build/resource/config_map.rs +++ b/rust/operator-binary/src/zk_controller/build/resource/config_map.rs @@ -19,12 +19,16 @@ use stackable_operator::{ }, }; -use crate::zk_controller::{ - build::{ - object_meta, - properties::{ConfigFileName, product_logging, security_properties, zoo_cfg}, +use crate::{ + crd::ZookeeperRole, + zk_controller::{ + build::{ + object_meta, + properties::{ConfigFileName, product_logging, security_properties, zoo_cfg}, + recommended_labels_for_role_group_resources, + }, + validate::{ValidatedCluster, ZookeeperRoleGroupConfig}, }, - validate::{ValidatedCluster, ZookeeperRoleGroupConfig}, }; #[derive(Snafu, Debug)] @@ -55,6 +59,7 @@ type Result = std::result::Result; pub fn build_server_rolegroup_config_map( cluster: &ValidatedCluster, cluster_info: &KubernetesClusterInfo, + zk_role: &ZookeeperRole, role_group_name: &RoleGroupName, rolegroup_config: &ZookeeperRoleGroupConfig, ) -> Result { @@ -106,7 +111,7 @@ pub fn build_server_rolegroup_config_map( .role_group_resource_names(role_group_name) .role_group_config_map() .to_string(), - role_group_name, + recommended_labels_for_role_group_resources(cluster, zk_role, role_group_name), ) .build(), ) diff --git a/rust/operator-binary/src/zk_controller/build/resource/listener.rs b/rust/operator-binary/src/zk_controller/build/resource/listener.rs index 34df12d7..70b546d9 100644 --- a/rust/operator-binary/src/zk_controller/build/resource/listener.rs +++ b/rust/operator-binary/src/zk_controller/build/resource/listener.rs @@ -7,7 +7,7 @@ use crate::{ ZOOKEEPER_SERVER_PORT_NAME, ZookeeperRole, role_listener_name, security::ZookeeperSecurity, }, zk_controller::{ - build::{NONE_ROLE_GROUP_NAME, object_meta}, + build::{object_meta, recommended_labels_for_role_resources}, validate::ValidatedCluster, }, }; @@ -15,7 +15,8 @@ use crate::{ /// Builds the role-level [`Listener`](listener::v1alpha1::Listener) exposing the ZooKeeper servers. /// /// The listener is owned by, labelled and named from the [`ValidatedCluster`]; the ListenerClass -/// and security settings are taken from its validated cluster config. +/// and security settings are taken from its validated cluster config. It is a role-level (not +/// role-group-level) object, so it carries the role-level recommended labels. pub fn build_role_listener( cluster: &ValidatedCluster, zk_role: &ZookeeperRole, @@ -24,7 +25,7 @@ pub fn build_role_listener( metadata: object_meta( cluster, role_listener_name(cluster.name.as_ref(), zk_role), - &NONE_ROLE_GROUP_NAME, + recommended_labels_for_role_resources(cluster, zk_role), ) .build(), spec: listener::v1alpha1::ListenerSpec { diff --git a/rust/operator-binary/src/zk_controller/build/resource/pdb.rs b/rust/operator-binary/src/zk_controller/build/resource/pdb.rs index b32f533d..b0514fb5 100644 --- a/rust/operator-binary/src/zk_controller/build/resource/pdb.rs +++ b/rust/operator-binary/src/zk_controller/build/resource/pdb.rs @@ -4,8 +4,8 @@ use stackable_operator::{ }; use crate::{ - crd::ZookeeperRole, - zk_controller::validate::{ValidatedCluster, controller_name, operator_name, product_name}, + crd::{OPERATOR_NAME, PRODUCT_NAME, ZookeeperRole}, + zk_controller::{CONTROLLER_NAME, validate::ValidatedCluster}, }; /// Builds the [`PodDisruptionBudget`] for the given `role`, or `None` if PDBs are disabled. @@ -23,10 +23,10 @@ pub fn build_pdb( let pdb = pod_disruption_budget_builder_with_role( cluster, - &product_name(), - &role.into(), - &operator_name(), - &controller_name(), + &PRODUCT_NAME, + role, + &OPERATOR_NAME, + &CONTROLLER_NAME, ) .with_max_unavailable(max_unavailable) .build(); diff --git a/rust/operator-binary/src/zk_controller/build/resource/rbac.rs b/rust/operator-binary/src/zk_controller/build/resource/rbac.rs index a2daef7b..8bc343ac 100644 --- a/rust/operator-binary/src/zk_controller/build/resource/rbac.rs +++ b/rust/operator-binary/src/zk_controller/build/resource/rbac.rs @@ -1,27 +1,20 @@ //! Builds the RBAC resources (ServiceAccount + RoleBinding) shared by all role groups. -use std::str::FromStr; - use stackable_operator::{ k8s_openapi::api::{core::v1::ServiceAccount, rbac::v1::RoleBinding}, - kvp::Labels, - v2::{ - rbac, - types::operator::{RoleGroupName, RoleName}, - }, + v2::rbac, }; -use crate::zk_controller::validate::ValidatedCluster; - -stackable_operator::constant!(NONE_ROLE_NAME: RoleName = "none"); -stackable_operator::constant!(NONE_ROLE_GROUP_NAME: RoleGroupName = "none"); +use crate::zk_controller::{ + build::recommended_labels_for_cluster_resources, validate::ValidatedCluster, +}; /// Builds the [`ServiceAccount`] that the role-group Pods run under. pub fn build_service_account(cluster: &ValidatedCluster) -> ServiceAccount { rbac::build_service_account( cluster, &cluster.cluster_resource_names(), - rbac_labels(cluster), + recommended_labels_for_cluster_resources(cluster), ) } @@ -31,16 +24,10 @@ pub fn build_role_binding(cluster: &ValidatedCluster) -> RoleBinding { rbac::build_role_binding( cluster, &cluster.cluster_resource_names(), - rbac_labels(cluster), + recommended_labels_for_cluster_resources(cluster), ) } -/// Both resources are shared by the whole cluster rather than tied to a role or role group, so -/// the recommended labels carry `none` for both values. -fn rbac_labels(cluster: &ValidatedCluster) -> Labels { - cluster.recommended_labels_for(&NONE_ROLE_NAME, &NONE_ROLE_GROUP_NAME) -} - #[cfg(test)] mod tests { use serde_json::json; @@ -77,13 +64,12 @@ mod tests { "apiVersion": "v1", "kind": "ServiceAccount", "metadata": { - // The RBAC resources are cluster-shared, so role and role group are `none`. + // The RBAC resources are cluster-shared, so they carry no component or + // role-group label. "labels": { - "app.kubernetes.io/component": "none", "app.kubernetes.io/instance": "simple-zookeeper", "app.kubernetes.io/managed-by": "zookeeper.stackable.tech_zookeepercluster", "app.kubernetes.io/name": "zookeeper", - "app.kubernetes.io/role-group": "none", "app.kubernetes.io/version": app_version_label("3.9.5"), "stackable.tech/vendor": "Stackable" }, @@ -114,11 +100,9 @@ mod tests { "kind": "RoleBinding", "metadata": { "labels": { - "app.kubernetes.io/component": "none", "app.kubernetes.io/instance": "simple-zookeeper", "app.kubernetes.io/managed-by": "zookeeper.stackable.tech_zookeepercluster", "app.kubernetes.io/name": "zookeeper", - "app.kubernetes.io/role-group": "none", "app.kubernetes.io/version": app_version_label("3.9.5"), "stackable.tech/vendor": "Stackable" }, diff --git a/rust/operator-binary/src/zk_controller/build/resource/service.rs b/rust/operator-binary/src/zk_controller/build/resource/service.rs index 67cf1884..76678fd6 100644 --- a/rust/operator-binary/src/zk_controller/build/resource/service.rs +++ b/rust/operator-binary/src/zk_controller/build/resource/service.rs @@ -10,10 +10,10 @@ use crate::{ crd::{ JMX_METRICS_PORT, JMX_METRICS_PORT_NAME, METRICS_PROVIDER_HTTP_PORT_NAME, ZOOKEEPER_ELECTION_PORT, ZOOKEEPER_ELECTION_PORT_NAME, ZOOKEEPER_LEADER_PORT, - ZOOKEEPER_LEADER_PORT_NAME, + ZOOKEEPER_LEADER_PORT_NAME, ZookeeperRole, }, zk_controller::{ - build::object_meta, + build::{object_meta, recommended_labels_for_role_group_resources, role_group_selector}, validate::{ValidatedCluster, ZookeeperRoleGroupConfig}, }, }; @@ -23,6 +23,7 @@ use crate::{ /// This is mostly useful for internal communication between peers, or for clients that perform client-side load balancing. pub(crate) fn build_server_rolegroup_headless_service( cluster: &ValidatedCluster, + zk_role: &ZookeeperRole, role_group_name: &RoleGroupName, ) -> Service { let metadata = object_meta( @@ -31,7 +32,7 @@ pub(crate) fn build_server_rolegroup_headless_service( .role_group_resource_names(role_group_name) .headless_service_name() .to_string(), - role_group_name, + recommended_labels_for_role_group_resources(cluster, zk_role, role_group_name), ) .build(); @@ -53,7 +54,7 @@ pub(crate) fn build_server_rolegroup_headless_service( ..ServicePort::default() }, ]), - selector: Some(cluster.role_group_selector(role_group_name).into()), + selector: Some(role_group_selector(cluster, zk_role, role_group_name).into()), publish_not_ready_addresses: Some(true), ..ServiceSpec::default() }; @@ -68,6 +69,7 @@ pub(crate) fn build_server_rolegroup_headless_service( /// The rolegroup [`Service`] for exposing metrics pub(crate) fn build_server_rolegroup_metrics_service( cluster: &ValidatedCluster, + zk_role: &ZookeeperRole, role_group_name: &RoleGroupName, rolegroup_config: &ZookeeperRoleGroupConfig, ) -> Service { @@ -77,7 +79,7 @@ pub(crate) fn build_server_rolegroup_metrics_service( cluster .role_group_resource_names(role_group_name) .metrics_service_name(), - role_group_name, + recommended_labels_for_role_group_resources(cluster, zk_role, role_group_name), ) .with_labels(prometheus_labels(&Scraping::Enabled)) .with_annotations(prometheus_annotations( @@ -107,7 +109,7 @@ pub(crate) fn build_server_rolegroup_metrics_service( ..ServicePort::default() }, ]), - selector: Some(cluster.role_group_selector(role_group_name).into()), + selector: Some(role_group_selector(cluster, zk_role, role_group_name).into()), publish_not_ready_addresses: Some(true), ..ServiceSpec::default() }; @@ -155,8 +157,12 @@ mod tests { let rolegroup_config = &cluster.role_group_configs[&ZookeeperRole::Server][&role_group_name]; - let service = - build_server_rolegroup_metrics_service(&cluster, &role_group_name, rolegroup_config); + let service = build_server_rolegroup_metrics_service( + &cluster, + &ZookeeperRole::Server, + &role_group_name, + rolegroup_config, + ); assert_eq!( json!({ diff --git a/rust/operator-binary/src/zk_controller/build/resource/statefulset.rs b/rust/operator-binary/src/zk_controller/build/resource/statefulset.rs index c7afd3e3..dbad7578 100644 --- a/rust/operator-binary/src/zk_controller/build/resource/statefulset.rs +++ b/rust/operator-binary/src/zk_controller/build/resource/statefulset.rs @@ -10,20 +10,21 @@ use stackable_operator::{ meta::ObjectMetaBuilder, pod::{ PodBuilder, - container::ContainerBuilder, + container::{ContainerBuilder, FieldPathEnvVar}, resources::ResourceRequirementsBuilder, security::PodSecurityContextBuilder, volume::{ListenerOperatorVolumeSourceBuilder, ListenerReference}, }, }, + constant, constants::RESTART_CONTROLLER_ENABLED_LABEL, k8s_openapi::{ DeepMerge, api::{ apps::v1::{StatefulSet, StatefulSetSpec}, core::v1::{ - ConfigMapVolumeSource, EmptyDirVolumeSource, EnvVar, EnvVarSource, ExecAction, - ObjectFieldSelector, PersistentVolumeClaim, Probe, ResourceRequirements, Volume, + ConfigMapVolumeSource, EmptyDirVolumeSource, ExecAction, PersistentVolumeClaim, + Probe, ResourceRequirements, Volume, }, }, apimachinery::pkg::apis::meta::v1::LabelSelector, @@ -62,8 +63,10 @@ use crate::{ jvm::{construct_non_heap_jvm_args, construct_zk_server_heap_env}, object_meta, properties::{self, ConfigFileName}, + recommended_labels_for_role_group_resources, + recommended_labels_for_unversioned_role_group_resources, role_group_selector, }, - validate::{ValidatedCluster, ZookeeperRoleGroupConfig}, + validate::{ValidatedCluster, ValidatedZookeeperConfig, ZookeeperRoleGroupConfig}, }, }; @@ -77,16 +80,27 @@ const MAX_PREPARE_LOG_FILE_SIZE: MemoryQuantity = MemoryQuantity { // Volume names. Each is shared between a `Volume`/PVC definition and one or more volume mounts; the // strings must match, so they are defined once here rather than repeated at every call site. -stackable_operator::constant!(DATA_VOLUME_NAME: VolumeName = "data"); -stackable_operator::constant!(CONFIG_VOLUME_NAME: VolumeName = "config"); -stackable_operator::constant!(RW_CONFIG_VOLUME_NAME: VolumeName = "rwconfig"); -stackable_operator::constant!(LOG_VOLUME_NAME: VolumeName = "log"); -stackable_operator::constant!(LOG_CONFIG_VOLUME_NAME: VolumeName = "log-config"); +constant!(DATA_VOLUME_NAME: VolumeName = "data"); +constant!(CONFIG_VOLUME_NAME: VolumeName = "config"); +constant!(RW_CONFIG_VOLUME_NAME: VolumeName = "rwconfig"); +constant!(LOG_VOLUME_NAME: VolumeName = "log"); +constant!(LOG_CONFIG_VOLUME_NAME: VolumeName = "log-config"); /// Name of the `prepare` init container (also used as its log subdirectory). const PREPARE_CONTAINER_NAME: &str = "prepare"; -stackable_operator::constant!(VECTOR_CONTAINER_NAME: ContainerName = "vector"); +constant!(VECTOR_CONTAINER_NAME: ContainerName = "vector"); + +// Env vars the operator sets on the containers. +constant!(POD_NAME: EnvVarName = "POD_NAME"); +constant!(MYID_OFFSET: EnvVarName = v1alpha1::ZookeeperConfig::MYID_OFFSET); +// Used by zkEnv.sh and the shell scripts in bin/. If unset the scripts try to find the conf +// directory automatically and that fails. +constant!(ZOOCFGDIR: EnvVarName = "ZOOCFGDIR"); +constant!(ZK_SERVER_HEAP: EnvVarName = "ZK_SERVER_HEAP"); +constant!(SERVER_JVMFLAGS: EnvVarName = "SERVER_JVMFLAGS"); +// Needed for the `containerdebug` process to log its tracing information to. +constant!(CONTAINERDEBUG_LOG_DIRECTORY: EnvVarName = "CONTAINERDEBUG_LOG_DIRECTORY"); /// The shell invocation shared by the `prepare` init container and the main ZooKeeper container. fn container_command() -> Vec { @@ -152,6 +166,7 @@ fn build_role_listener_pvc( /// [`build_server_rolegroup_headless_service`](super::service::build_server_rolegroup_headless_service)). pub fn build_server_rolegroup_statefulset( cluster: &ValidatedCluster, + zk_role: &ZookeeperRole, role_group_name: &RoleGroupName, rolegroup_config: &ZookeeperRoleGroupConfig, ) -> Result { @@ -161,18 +176,23 @@ pub fn build_server_rolegroup_statefulset( let zookeeper_security = &cluster.cluster_config.zookeeper_security; let metrics_port = cluster.metrics_http_port(rolegroup_config); - // The operator-injected environment variables plus the user-provided `envOverrides` - // (which win on conflict). - let env_vars = EnvVarSet::new() + // Operator-set env vars first; the user's `envOverrides` are merged on top last and win. + let prepare_env_vars = common_env_vars(merged_config) + .with_field_path(&POD_NAME, &FieldPathEnvVar::Name) + .merge(rolegroup_config.env_overrides.clone()); + + let zookeeper_env_vars = common_env_vars(merged_config) .with_value( - &EnvVarName::from_str_unsafe(v1alpha1::ZookeeperConfig::MYID_OFFSET), - merged_config.myid_offset.to_string(), + &ZK_SERVER_HEAP, + construct_zk_server_heap_env(merged_config).context(ConstructJvmArgumentsSnafu)?, + ) + .with_value( + &SERVER_JVMFLAGS, + construct_non_heap_jvm_args(rolegroup_config), ) - // Used by zkEnv.sh and the shell scripts in bin/. If unset it tries to find the - // conf directory automatically and that fails. .with_value( - &EnvVarName::from_str_unsafe("ZOOCFGDIR"), - STACKABLE_RW_CONFIG_DIR, + &CONTAINERDEBUG_LOG_DIRECTORY, + format!("{STACKABLE_LOG_DIR}/containerdebug"), ) .merge(rolegroup_config.env_overrides.clone()); @@ -192,12 +212,13 @@ pub fn build_server_rolegroup_statefulset( ContainerBuilder::new(APP_NAME).expect("invalid hard-coded container name"); let mut pod_builder = PodBuilder::new(); - // Used for PVC templates that cannot be modified once they are deployed. A constant version - // keeps the labels stable across version upgrades. - let unversioned_recommended_labels = cluster.unversioned_recommended_labels(role_group_name); + // Used for PVC templates, which cannot be modified once they are deployed. The version label + // is omitted so the labels stay stable across version upgrades. + let unversioned_recommended_labels = + recommended_labels_for_unversioned_role_group_resources(cluster, zk_role, role_group_name); let listener_pvc = build_role_listener_pvc( - role_listener_name(cluster.name.as_ref(), &ZookeeperRole::Server).as_ref(), + role_listener_name(cluster.name.as_ref(), zk_role).as_ref(), &unversioned_recommended_labels, )?; @@ -237,18 +258,7 @@ pub fn build_server_rolegroup_statefulset( .image_from_product_image(resolved_product_image) .command(container_command()) .args(vec![args.join("\n")]) - .add_env_vars(env_vars.clone()) - .add_env_vars(vec![EnvVar { - name: "POD_NAME".to_string(), - value_from: Some(EnvVarSource { - field_ref: Some(ObjectFieldSelector { - api_version: Some("v1".to_string()), - field_path: "metadata.name".to_string(), - }), - ..EnvVarSource::default() - }), - ..EnvVar::default() - }]) + .add_env_vars(prepare_env_vars) .add_volume_mount(&*DATA_VOLUME_NAME, STACKABLE_DATA_DIR) .context(AddVolumeMountSnafu)? .add_volume_mount(&*CONFIG_VOLUME_NAME, STACKABLE_CONFIG_DIR) @@ -285,19 +295,7 @@ pub fn build_server_rolegroup_statefulset( create_vector_shutdown_file_command = create_vector_shutdown_file_command(STACKABLE_LOG_DIR), }]) - .add_env_vars(env_vars) - .add_env_var( - "ZK_SERVER_HEAP", - construct_zk_server_heap_env(merged_config).context(ConstructJvmArgumentsSnafu)?, - ) - .add_env_var( - "SERVER_JVMFLAGS", - construct_non_heap_jvm_args(rolegroup_config), - ) - .add_env_var( - "CONTAINERDEBUG_LOG_DIRECTORY", - format!("{STACKABLE_LOG_DIR}/containerdebug"), - ) + .add_env_vars(zookeeper_env_vars) // Only allow the global load balancing service to send traffic to pods that are members of the quorum // This also acts as a hint to the StatefulSet controller to wait for each pod to enter quorum before taking down the next .readiness_probe(Probe { @@ -341,7 +339,11 @@ pub fn build_server_rolegroup_statefulset( .build(); let pb_metadata = ObjectMetaBuilder::new() - .with_labels(cluster.recommended_labels(role_group_name)) + .with_labels(recommended_labels_for_role_group_resources( + cluster, + zk_role, + role_group_name, + )) .build(); pod_builder @@ -427,7 +429,7 @@ pub fn build_server_rolegroup_statefulset( let metadata = object_meta( cluster, resource_names.stateful_set_name().to_string(), - role_group_name, + recommended_labels_for_role_group_resources(cluster, zk_role, role_group_name), ) .with_label(RESTART_CONTROLLER_ENABLED_LABEL.to_owned()) .build(); @@ -438,7 +440,7 @@ pub fn build_server_rolegroup_statefulset( // HorizontalPodAutoscaler can manage the replica count. replicas: rolegroup_config.replicas.map(i32::from), selector: LabelSelector { - match_labels: Some(cluster.role_group_selector(role_group_name).into()), + match_labels: Some(role_group_selector(cluster, zk_role, role_group_name).into()), ..LabelSelector::default() }, service_name: Some(resource_names.headless_service_name().to_string()), @@ -454,18 +456,47 @@ pub fn build_server_rolegroup_statefulset( }) } +/// Environment variables the operator sets on both the `prepare` and the ZooKeeper container. +/// +/// Returned as an [`EnvVarSet`] so the callers can merge the user's `envOverrides` on top, +/// letting an override win on a name collision. +fn common_env_vars(merged_config: &ValidatedZookeeperConfig) -> EnvVarSet { + EnvVarSet::new() + .with_value(&MYID_OFFSET, merged_config.myid_offset.to_string()) + .with_value(&ZOOCFGDIR, STACKABLE_RW_CONFIG_DIR) +} + #[cfg(test)] mod tests { + use stackable_operator::k8s_openapi::api::core::v1::{Container, EnvVar}; + use super::*; use crate::zk_controller::test_support::{minimal_zk, validated_cluster}; + #[test] + fn test_constants() { + // Test that dereferencing the constants does not panic. + let _ = *DATA_VOLUME_NAME; + let _ = *CONFIG_VOLUME_NAME; + let _ = *RW_CONFIG_VOLUME_NAME; + let _ = *LOG_VOLUME_NAME; + let _ = *LOG_CONFIG_VOLUME_NAME; + let _ = *VECTOR_CONTAINER_NAME; + let _ = *POD_NAME; + let _ = *MYID_OFFSET; + let _ = *ZOOCFGDIR; + let _ = *ZK_SERVER_HEAP; + let _ = *SERVER_JVMFLAGS; + let _ = *CONTAINERDEBUG_LOG_DIRECTORY; + } + /// Builds the `default` server StatefulSet for `yaml` and returns the ConfigMap name mounted by /// its `log-config` volume. fn log_config_map_name(yaml: &str) -> String { let validated = validated_cluster(&minimal_zk(yaml)); let rg_name = RoleGroupName::from_str("default").expect("valid role group name"); let rg = validated.role_group_configs[&ZookeeperRole::Server][&rg_name].clone(); - build_server_rolegroup_statefulset(&validated, &rg_name, &rg) + build_server_rolegroup_statefulset(&validated, &ZookeeperRole::Server, &rg_name, &rg) .expect("statefulset builds") .spec .and_then(|spec| spec.template.spec) @@ -527,4 +558,82 @@ mod tests { assert_ne!(name, "my-log-config"); assert!(name.contains("simple-zookeeper"), "{name}"); } + + /// Builds the `default` server StatefulSet with the given env override applied and returns the + /// env vars of the container named `container_name`. + fn env_with_override(container_name: &str, name: &EnvVarName, value: &str) -> Vec { + let validated = validated_cluster(&minimal_zk( + r#" + apiVersion: zookeeper.stackable.tech/v1alpha1 + kind: ZookeeperCluster + metadata: + name: simple-zookeeper + spec: + image: + productVersion: "3.9.5" + servers: + roleGroups: + default: + replicas: 1 + "#, + )); + let rg_name = RoleGroupName::from_str("default").expect("valid role group name"); + let mut rg = validated.role_group_configs[&ZookeeperRole::Server][&rg_name].clone(); + rg.env_overrides = rg.env_overrides.with_value(name, value); + + let stateful_set = + build_server_rolegroup_statefulset(&validated, &ZookeeperRole::Server, &rg_name, &rg) + .expect("statefulset builds"); + + let pod_spec = stateful_set + .spec + .expect("the StatefulSet has a spec") + .template + .spec + .expect("the pod template has a spec"); + pod_spec + .containers + .into_iter() + .chain(pod_spec.init_containers.into_iter().flatten()) + .find(|container: &Container| container.name == container_name) + .unwrap_or_else(|| panic!("the {container_name} container exists")) + .env + .unwrap_or_else(|| panic!("the {container_name} container has env vars")) + } + + /// The user-supplied `envOverrides` must be merged in after all operator-set environment + /// variables, so that they can override any of them. `CONTAINERDEBUG_LOG_DIRECTORY` is used + /// as the example here because it is set unconditionally by the operator. + #[test] + fn env_overrides_override_operator_set_env_vars() { + let env = env_with_override(APP_NAME, &CONTAINERDEBUG_LOG_DIRECTORY, "/custom/log/dir"); + + let containerdebug: Vec<_> = env + .iter() + .filter(|env_var| env_var.name == "CONTAINERDEBUG_LOG_DIRECTORY") + .collect(); + assert_eq!( + containerdebug.len(), + 1, + "the override must replace the operator-set value, not duplicate it" + ); + assert_eq!(containerdebug[0].value.as_deref(), Some("/custom/log/dir")); + } + + /// Same guarantee for the `prepare` init container, whose env vars are assembled separately. + #[test] + fn prepare_env_overrides_override_operator_set_env_vars() { + let env = env_with_override(PREPARE_CONTAINER_NAME, &ZOOCFGDIR, "/custom/conf/dir"); + + let zoocfgdir: Vec<_> = env + .iter() + .filter(|env_var| env_var.name == "ZOOCFGDIR") + .collect(); + assert_eq!( + zoocfgdir.len(), + 1, + "the override must replace the operator-set value, not duplicate it" + ); + assert_eq!(zoocfgdir[0].value.as_deref(), Some("/custom/conf/dir")); + } } diff --git a/rust/operator-binary/src/zk_controller/update_status.rs b/rust/operator-binary/src/zk_controller/update_status.rs index 455d34a8..491a85cf 100644 --- a/rust/operator-binary/src/zk_controller/update_status.rs +++ b/rust/operator-binary/src/zk_controller/update_status.rs @@ -15,7 +15,7 @@ use stackable_operator::{ use strum::{EnumDiscriminants, IntoStaticStr}; use crate::{ - OPERATOR_NAME, + ZOOKEEPER_OPERATOR_NAME, crd::v1alpha1, zk_controller::{Applied, KubernetesResources}, }; @@ -61,7 +61,7 @@ pub async fn update_status( }; client - .apply_patch_status(OPERATOR_NAME, zk, &status) + .apply_patch_status(ZOOKEEPER_OPERATOR_NAME, zk, &status) .await .context(ApplyStatusSnafu)?; diff --git a/rust/operator-binary/src/zk_controller/validate.rs b/rust/operator-binary/src/zk_controller/validate.rs index 79e16f01..8d80cbea 100644 --- a/rust/operator-binary/src/zk_controller/validate.rs +++ b/rust/operator-binary/src/zk_controller/validate.rs @@ -23,41 +23,33 @@ use stackable_operator::{ deep_merger::ObjectOverrides, k8s_openapi::apimachinery::pkg::apis::meta::v1::ObjectMeta, kube::{Resource, ResourceExt}, - kvp::Labels, product_logging::spec::Logging, - role_utils::RoleGroup, shared::time::Duration, v2::{ HasName, HasUid, NameIsValidLabelValue, - builder::pod::container::{EnvVarName, EnvVarSet}, controller_utils::{get_cluster_name, get_namespace, get_uid}, - kvp::label::{recommended_labels, role_group_selector}, product_logging::framework::{ ValidatedContainerLogConfigChoice, VectorContainerLogConfig, validate_logging_configuration_for_container, }, role_group_utils::ResourceNames, - role_utils::{self, JavaCommonConfig, RoleGroupConfig, with_validated_config}, + role_utils::{self, JavaCommonConfig, RoleGroup, RoleGroupConfig, with_validated_config}, types::{ kubernetes::{ConfigMapName, ListenerClassName, NamespaceName, Uid}, - operator::{ - ClusterName, ControllerName, OperatorName, ProductName, ProductVersion, - RoleGroupName, RoleName, - }, + operator::{ClusterName, ProductVersion, RoleGroupName}, }, }, }; -use strum::IntoEnumIterator; use crate::{ crd::{ - APP_NAME, CONTAINER_IMAGE_BASE_NAME, OPERATOR_NAME, ZOOKEEPER_SERVER_PORT_NAME, - ZookeeperRole, ZookeeperServerRoleType, authentication, + CONTAINER_IMAGE_BASE_NAME, PRODUCT_NAME, ZOOKEEPER_SERVER_PORT_NAME, ZookeeperRole, + ZookeeperServerRoleType, authentication, security::ZookeeperSecurity, v1alpha1::{self, ZookeeperConfig, ZookeeperConfigOverrides, ZookeeperServerRoleConfig}, }, listener_addresses::{self, ListenerAddresses, listener_addresses}, - zk_controller::{ZK_CONTROLLER_NAME, dereference::DereferencedObjects}, + zk_controller::dereference::DereferencedObjects, }; #[derive(Snafu, Debug)] @@ -88,12 +80,6 @@ pub enum Error { role_group: String, }, - #[snafu(display("invalid environment variable override name in role group {role_group:?}"))] - ParseEnvVarName { - source: stackable_operator::v2::macros::attributed_string_type::Error, - role_group: String, - }, - #[snafu(display("failed to get the cluster name"))] GetClusterName { source: stackable_operator::v2::controller_utils::Error, @@ -255,10 +241,6 @@ pub struct ValidatedCluster { pub discovery_addresses: ListenerAddresses, } -// Placeholder product version used for labels on PVC templates, which cannot be modified once -// deployed. A constant value keeps the labels stable across version upgrades. -stackable_operator::constant!(UNVERSIONED_PRODUCT_VERSION: ProductVersion = "none"); - impl ValidatedCluster { #[allow(clippy::too_many_arguments)] pub fn new( @@ -305,7 +287,7 @@ impl ValidatedCluster { ) -> ResourceNames { ResourceNames { cluster_name: self.name.clone(), - role_name: ZookeeperRole::Server.into(), + role_name: ZookeeperRole::Server.role_name(), role_group_name: role_group_name.clone(), } } @@ -315,72 +297,9 @@ impl ValidatedCluster { pub fn cluster_resource_names(&self) -> role_utils::ResourceNames { role_utils::ResourceNames { cluster_name: self.name.clone(), - product_name: product_name(), + product_name: PRODUCT_NAME.clone(), } } - - pub fn recommended_labels(&self, role_group_name: &RoleGroupName) -> Labels { - self.recommended_labels_for(&ZookeeperRole::Server.into(), role_group_name) - } - - pub fn recommended_labels_for( - &self, - role_name: &RoleName, - role_group_name: &RoleGroupName, - ) -> Labels { - self.recommended_labels_with(&self.product_version, role_name, role_group_name) - } - - pub fn unversioned_recommended_labels(&self, role_group_name: &RoleGroupName) -> Labels { - self.recommended_labels_with( - &UNVERSIONED_PRODUCT_VERSION, - &ZookeeperRole::Server.into(), - role_group_name, - ) - } - - fn recommended_labels_with( - &self, - product_version: &ProductVersion, - role_name: &RoleName, - role_group_name: &RoleGroupName, - ) -> Labels { - recommended_labels( - self, - &product_name(), - product_version, - &operator_name(), - &controller_name(), - role_name, - role_group_name, - ) - } - - /// Selector labels matching the pods of a role group. - pub fn role_group_selector(&self, role_group_name: &RoleGroupName) -> Labels { - role_group_selector( - self, - &product_name(), - &ZookeeperRole::Server.into(), - role_group_name, - ) - } -} - -/// The product name (`zookeeper`) as a type-safe label value. -pub(crate) fn product_name() -> ProductName { - ProductName::from_str(APP_NAME).expect("'zookeeper' is a valid product name") -} - -/// The operator name as a type-safe label value. -pub(crate) fn operator_name() -> OperatorName { - OperatorName::from_str(OPERATOR_NAME).expect("the operator name is a valid label value") -} - -/// The controller name as a type-safe label value. -pub(crate) fn controller_name() -> ControllerName { - ControllerName::from_str(ZK_CONTROLLER_NAME) - .expect("the controller name is a valid label value") } impl HasName for ValidatedCluster { @@ -476,28 +395,26 @@ pub fn validate( .vector_aggregator_config_map_name .clone(); - let mut role_group_configs = BTreeMap::new(); - for zk_role in ZookeeperRole::iter() { - let role = zk.role(&zk_role); - let default_config = ZookeeperConfig::default_server_config(&zk.name_any(), &zk_role); - - let mut groups = BTreeMap::new(); - for (rg_name, rg) in &role.role_groups { - let role_group_name = - RoleGroupName::from_str(rg_name).with_context(|_| ParseRoleGroupNameSnafu { - role_group: rg_name.clone(), - })?; - let validated_rg = validate_role_group_config( - rg_name, - rg, - role, - &default_config, - &vector_aggregator_config_map_name, - )?; - groups.insert(role_group_name, validated_rg); - } - role_group_configs.insert(zk_role, groups); + let zk_role = ZookeeperRole::Server; + let role = zk.role(&zk_role); + let default_config = ZookeeperConfig::default_server_config(&zk.name_any(), &zk_role); + + let mut groups = BTreeMap::new(); + for (rg_name, rg) in &role.role_groups { + let role_group_name = + RoleGroupName::from_str(rg_name).with_context(|_| ParseRoleGroupNameSnafu { + role_group: rg_name.clone(), + })?; + let validated_rg = validate_role_group_config( + rg_name, + rg, + role, + &default_config, + &vector_aggregator_config_map_name, + )?; + groups.insert(role_group_name, validated_rg); } + let role_group_configs = BTreeMap::from([(zk_role, groups)]); let name = get_cluster_name(zk).context(GetClusterNameSnafu)?; let namespace = get_namespace(zk).context(GetNamespaceSnafu)?; @@ -575,16 +492,6 @@ fn validate_role_group_config( role_group: role_group_name.to_owned(), })?; - let mut env_overrides = EnvVarSet::new(); - for (env_var_name, env_var_value) in merged.config.env_overrides { - env_overrides = env_overrides.with_value( - &EnvVarName::from_str(&env_var_name).with_context(|_| ParseEnvVarNameSnafu { - role_group: role_group_name.to_owned(), - })?, - env_var_value, - ); - } - let config = merged.config.config; let logging = validate_logging(&config.logging, vector_aggregator_config_map_name)?; @@ -592,7 +499,8 @@ fn validate_role_group_config( replicas: merged.replicas, config: ValidatedZookeeperConfig::from_merged(config, logging), config_overrides: merged.config.config_overrides, - env_overrides, + // The env override names are validated on deserialization. + env_overrides: merged.config.env_overrides.into(), cli_overrides: merged.config.cli_overrides, pod_overrides: merged.config.pod_overrides, product_specific_common_config: merged.config.product_specific_common_config, @@ -834,16 +742,6 @@ mod tests { ); } - /// Locks the invariant behind the `expect` in the `From for RoleName` impls: - /// every `ZookeeperRole` variant (present and future) must serialise to a valid `RoleName`. - #[test] - fn every_zookeeper_role_serialises_to_a_valid_role_name() { - for role in ZookeeperRole::iter() { - let _: RoleName = (&role).into(); - let _: RoleName = role.into(); - } - } - /// The `zk` port is a constant, so a role Listener that publishes addresses without it is a /// fault rather than a transient state, and must fail validation. #[test] diff --git a/rust/operator-binary/src/znode_controller.rs b/rust/operator-binary/src/znode_controller.rs index f9014701..190be624 100644 --- a/rust/operator-binary/src/znode_controller.rs +++ b/rust/operator-binary/src/znode_controller.rs @@ -6,13 +6,14 @@ //! pipeline, with each step living in its own submodule. There is no update_status step, because //! the only status the ZookeeperZnode carries (the znode path) is written before the finalizer //! runs. -use std::{borrow::Cow, convert::Infallible, sync::Arc}; +use std::{borrow::Cow, convert::Infallible, str::FromStr, sync::Arc}; use const_format::concatcp; use snafu::{OptionExt, ResultExt, Snafu}; use stackable_operator::{ cli::OperatorEnvironmentOptions, cluster_resources::ClusterResourceApplyStrategy, + constant, k8s_openapi::api::core::v1::ConfigMap, kube::{ api::ObjectMeta, @@ -22,12 +23,13 @@ use stackable_operator::{ logging::controller::ReconcilerError, shared::time::Duration, utils::cluster_info::KubernetesClusterInfo, + v2::types::operator::ControllerName, }; use strum::{EnumDiscriminants, IntoStaticStr}; use tracing::{debug, info}; use crate::{ - OPERATOR_NAME, + ZOOKEEPER_OPERATOR_NAME, crd::{security::ZookeeperSecurity, v1alpha1}, znode_controller::apply::{Applier, ensure_znode_exists}, }; @@ -38,7 +40,10 @@ mod dereference; pub(crate) mod validate; pub const ZNODE_CONTROLLER_NAME: &str = "znode"; -pub const ZNODE_FULL_CONTROLLER_NAME: &str = concatcp!(ZNODE_CONTROLLER_NAME, '.', OPERATOR_NAME); +pub const ZNODE_FULL_CONTROLLER_NAME: &str = + concatcp!(ZNODE_CONTROLLER_NAME, '.', ZOOKEEPER_OPERATOR_NAME); + +constant!(pub(crate) CONTROLLER_NAME: ControllerName = ZNODE_CONTROLLER_NAME); pub struct Ctx { pub client: stackable_operator::client::Client, @@ -202,7 +207,7 @@ pub async fn reconcile_znode( finalizer( &client.get_api::(&ns), - &format!("{OPERATOR_NAME}/znode"), + &format!("{ZOOKEEPER_OPERATOR_NAME}/znode"), Arc::new(znode.clone()), |ev| async { match ev { @@ -418,6 +423,17 @@ pub(crate) mod test_support { } } +#[cfg(test)] +mod tests { + use super::CONTROLLER_NAME; + + #[test] + fn test_constants() { + // Test that dereferencing the constants does not panic. + let _ = *CONTROLLER_NAME; + } +} + mod znode_mgmt { use std::{collections::VecDeque, net::SocketAddr}; diff --git a/rust/operator-binary/src/znode_controller/apply.rs b/rust/operator-binary/src/znode_controller/apply.rs index 86c9d69d..2634b163 100644 --- a/rust/operator-binary/src/znode_controller/apply.rs +++ b/rust/operator-binary/src/znode_controller/apply.rs @@ -9,7 +9,7 @@ use stackable_operator::{ }; use crate::{ - APP_NAME, OPERATOR_NAME, + APP_NAME, ZOOKEEPER_OPERATOR_NAME, znode_controller::{ KubernetesResources, ZNODE_CONTROLLER_NAME, validate::ValidatedZnode, znode_mgmt, }, @@ -59,7 +59,7 @@ impl<'a> Applier<'a> { // requires. let cluster_resources = ClusterResources::new( APP_NAME, - OPERATOR_NAME, + ZOOKEEPER_OPERATOR_NAME, ZNODE_CONTROLLER_NAME, &znode.object_ref(&()), apply_strategy, diff --git a/rust/operator-binary/src/znode_controller/build.rs b/rust/operator-binary/src/znode_controller/build.rs index 509b4adb..b1d5f873 100644 --- a/rust/operator-binary/src/znode_controller/build.rs +++ b/rust/operator-binary/src/znode_controller/build.rs @@ -4,7 +4,7 @@ use snafu::{ResultExt, Snafu}; use crate::{ discovery::{self, build_znode_discovery_configmap}, - znode_controller::{KubernetesResources, ZNODE_CONTROLLER_NAME, validate::ValidatedZnode}, + znode_controller::{KubernetesResources, validate::ValidatedZnode}, }; #[derive(Snafu, Debug)] @@ -19,13 +19,9 @@ pub enum Error { /// role Listener are already dereferenced and validated by this point. The znode itself (a path /// inside the ZooKeeper ensemble, not a Kubernetes object) is created by the apply step. pub fn build(znode: &ValidatedZnode, znode_path: &str) -> Result { - let discovery_config_map = build_znode_discovery_configmap( - znode, - ZNODE_CONTROLLER_NAME, - &znode.discovery_addresses, - znode_path, - ) - .context(DiscoveryConfigMapSnafu)?; + let discovery_config_map = + build_znode_discovery_configmap(znode, &znode.discovery_addresses, znode_path) + .context(DiscoveryConfigMapSnafu)?; Ok(KubernetesResources { discovery_config_maps: vec![discovery_config_map], From c1b39ade133519bd176d1e5fd18bbc838c5ed3c4 Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Thu, 20 Aug 2026 11:36:35 +0200 Subject: [PATCH 2/3] converted missed constants, regenerate nix --- Cargo.nix | 323 +++++++++--------- crate-hashes.json | 18 +- rust/operator-binary/src/crd/security.rs | 52 ++- .../build/resource/statefulset.rs | 66 ++-- 4 files changed, 250 insertions(+), 209 deletions(-) diff --git a/Cargo.nix b/Cargo.nix index f7e0a1d0..6aab6b79 100644 --- a/Cargo.nix +++ b/Cargo.nix @@ -489,9 +489,9 @@ rec { }; "async-trait" = rec { crateName = "async-trait"; - version = "0.1.91"; + version = "0.1.92"; edition = "2021"; - sha256 = "1v3cm8mzg66037wm392p1vsdx0lq8bid6y2ivr7z03lpfx0xqdmf"; + sha256 = "0rqn5iga1hlv2lm8xzav1zhar46jb4dvx89i6kfv93kb53maxxl2"; procMacro = true; libName = "async_trait"; authors = [ @@ -1002,12 +1002,9 @@ rec { }; "cc" = rec { crateName = "cc"; - version = "1.4.0"; - edition = "2018"; - sha256 = "1fc26n76n7gr37m2q0xw5l8jpn4sd33hvyppmwhv6v4fcyxq3pas"; - authors = [ - "Alex Crichton " - ]; + version = "1.4.3"; + edition = "2021"; + sha256 = "0v9b5arr047vbihfbh3fmbd3aj9vf1i7dbdgfpvlwzynpjvr35ah"; dependencies = [ { name = "find-msvc-tools"; @@ -1032,7 +1029,7 @@ rec { } ]; features = { - "parallel" = [ "dep:libc" "dep:jobserver" ]; + "parallel" = [ "dep:jobserver" "dep:libc" ]; }; resolvedDefaultFeatures = [ "parallel" ]; }; @@ -1919,7 +1916,7 @@ rec { dependencies = [ { name = "thiserror"; - packageId = "thiserror 2.0.19"; + packageId = "thiserror 2.0.20"; } ]; features = { @@ -2810,9 +2807,9 @@ rec { }; "find-msvc-tools" = rec { crateName = "find-msvc-tools"; - version = "0.1.9"; - edition = "2018"; - sha256 = "10nmi0qdskq6l7zwxw5g56xny7hb624iki1c39d907qmfh3vrbjv"; + version = "0.1.11"; + edition = "2021"; + sha256 = "145qpfb9r4ml2klr8v4byvrkikp61qyiks9n69b8z0vbscbb0pfl"; libName = "find_msvc_tools"; }; @@ -2939,11 +2936,11 @@ rec { }; resolvedDefaultFeatures = [ "default" "use_std" "with-deprecated" ]; }; - "futures 0.3.33" = rec { + "futures 0.3.34" = rec { crateName = "futures"; - version = "0.3.33"; + version = "0.3.34"; edition = "2018"; - sha256 = "066j5aqz8an05xh4hn5ljdnjn80z3g335v4grx4gaifr57wg3358"; + sha256 = "18yhwmbdalhz2z9i1vm10hy2v0cfm82dkgcb6vr2msxazfix4ccs"; dependencies = [ { name = "futures-channel"; @@ -3003,9 +3000,9 @@ rec { }; "futures-channel" = rec { crateName = "futures-channel"; - version = "0.3.33"; + version = "0.3.34"; edition = "2018"; - sha256 = "1bn5hlhfkl1sgypmiachaqcgwmr6wmjal7dyhfyb1zkazvs90996"; + sha256 = "1i4kwcanpaphn1ax62ci3nx176kglxqx0gnhzqpqdr1rkpbf7ydi"; libName = "futures_channel"; dependencies = [ { @@ -3031,9 +3028,9 @@ rec { }; "futures-core" = rec { crateName = "futures-core"; - version = "0.3.33"; + version = "0.3.34"; edition = "2018"; - sha256 = "1iqdbvcdlplfr2g43h7xrfkv2sg5p1a26x8acz1xgxl07i3hrm9c"; + sha256 = "0pjgv4fx0np6hrs5sz5a2phabwv0z70yr51v03injbi44bjrkmlj"; libName = "futures_core"; features = { "default" = [ "std" ]; @@ -3044,9 +3041,9 @@ rec { }; "futures-executor" = rec { crateName = "futures-executor"; - version = "0.3.33"; + version = "0.3.34"; edition = "2018"; - sha256 = "0n3lpkmcfrsnh40i4armn040gnqbpd257hz5qs46zipjr6f8fm37"; + sha256 = "0cjl3y7jgg60wwb96ikxj23r6q91ylvx8v675yychv1w3b7lf6q3"; libName = "futures_executor"; dependencies = [ { @@ -3074,9 +3071,9 @@ rec { }; "futures-io" = rec { crateName = "futures-io"; - version = "0.3.33"; + version = "0.3.34"; edition = "2018"; - sha256 = "0yjx13qdm9b2p4w00ddw85k6yccnnmqrlrrz8yfmi5jg7jmfqxs5"; + sha256 = "1v9z6wj92ra18kpv0xig21hgpzrvcwmcr8fszyzh64yyay0zmh2k"; libName = "futures_io"; features = { "default" = [ "std" ]; @@ -3085,9 +3082,9 @@ rec { }; "futures-macro" = rec { crateName = "futures-macro"; - version = "0.3.33"; + version = "0.3.34"; edition = "2018"; - sha256 = "02xiyd5y1nk9b805aympj4wq2czgvxnhcml9w9xkc665d3g3qv9d"; + sha256 = "0i0czvcvsqq4hrccibq2f23004si5z34zjwdxfmqhlrmm15nbfcz"; procMacro = true; libName = "futures_macro"; dependencies = [ @@ -3101,7 +3098,7 @@ rec { } { name = "syn"; - packageId = "syn 2.0.119"; + packageId = "syn 3.0.3"; features = [ "full" ]; } ]; @@ -3109,9 +3106,9 @@ rec { }; "futures-sink" = rec { crateName = "futures-sink"; - version = "0.3.33"; + version = "0.3.34"; edition = "2018"; - sha256 = "01z38z344hpryw84b6r0rbwcb669d8pyvl2szg10aqwx96n1hi73"; + sha256 = "07cfvrgc3vxk6sw5g8a8dnrm1mzg6d5mwy08ywa1sgyhyxml4i0r"; libName = "futures_sink"; features = { "default" = [ "std" ]; @@ -3121,9 +3118,9 @@ rec { }; "futures-task" = rec { crateName = "futures-task"; - version = "0.3.33"; + version = "0.3.34"; edition = "2018"; - sha256 = "02f1y1yvjg1cv998zkgl1706pi9y4fyc9045l1hlmyqyhclfscdj"; + sha256 = "1zfilqs8nwlfqz4prk7ihvpp5avvzins87ibzlxzq5fhs7ipshfd"; libName = "futures_task"; features = { "default" = [ "std" ]; @@ -3133,9 +3130,9 @@ rec { }; "futures-util" = rec { crateName = "futures-util"; - version = "0.3.33"; + version = "0.3.34"; edition = "2018"; - sha256 = "1anyg40j5www5l22r2jbn1birsafz4q1w9qmcjk4vqzwasi90ym7"; + sha256 = "1g3r9ghzq7c2fh34lis43i72xavk9p84npgfwgb5vfpqcwjajl0d"; libName = "futures_util"; dependencies = [ { @@ -3612,9 +3609,9 @@ rec { }; "h2" = rec { crateName = "h2"; - version = "0.4.15"; + version = "0.4.17"; edition = "2021"; - sha256 = "0mgilh1g8gydcchqi6acs5l6j0gwg5jwpa64sj4b3ncb9v497c3c"; + sha256 = "0jblh2mscahvbz42d1sp5702ib444rcxswm5a3n2g64yydspx1wz"; authors = [ "Carl Lerche " "Sean McArthur " @@ -3852,9 +3849,9 @@ rec { }; "http-body-util" = rec { crateName = "http-body-util"; - version = "0.1.4"; + version = "0.1.5"; edition = "2018"; - sha256 = "1wizkqx9a75x8v5lm7cawpammz8sfvd7cngnkp34wkcfl3b1zx79"; + sha256 = "07773iilap808wjp6vywlq15zkgwnswqzrv270zxvg2z9biry5i3"; libName = "http_body_util"; authors = [ "Carl Lerche " @@ -4362,9 +4359,9 @@ rec { }; "icu_collections" = rec { crateName = "icu_collections"; - version = "2.2.0"; - edition = "2021"; - sha256 = "070r7xd0pynm0hnc1v2jzlbxka6wf50f81wybf9xg0y82v6x3119"; + version = "2.3.0"; + edition = "2024"; + sha256 = "04x59h6vdq0cnpippim1nr471ivlsnnn470sj1d5v864h48d4s7s"; authors = [ "The ICU4X Project Developers" ]; @@ -4412,9 +4409,9 @@ rec { }; "icu_locale_core" = rec { crateName = "icu_locale_core"; - version = "2.2.0"; - edition = "2021"; - sha256 = "0a9cmin5w1x3bg941dlmgszn33qgq428k7qiqn5did72ndi9n8cj"; + version = "2.3.0"; + edition = "2024"; + sha256 = "1sqdj16wwl7h9y6r7j394av4kpdb7zryz9h169ffwbm9imc2hvnm"; authors = [ "The ICU4X Project Developers" ]; @@ -4464,9 +4461,9 @@ rec { }; "icu_normalizer" = rec { crateName = "icu_normalizer"; - version = "2.2.0"; - edition = "2021"; - sha256 = "1d7krxr0xpc4x9635k1100a24nh0nrc59n65j6yk6gbfkplmwvn5"; + version = "2.3.0"; + edition = "2024"; + sha256 = "0vv43ixk2wmbxrx7kl33cwkhx1wdyb1q3pa18qkyshan4dgwzy8j"; authors = [ "The ICU4X Project Developers" ]; @@ -4518,9 +4515,9 @@ rec { }; "icu_normalizer_data" = rec { crateName = "icu_normalizer_data"; - version = "2.2.0"; - edition = "2021"; - sha256 = "0f5d5d5fhhr9937m2z6z38fzh6agf14z24kwlr6lyczafypf0fys"; + version = "2.3.0"; + edition = "2024"; + sha256 = "1811h0ppb7lwq1q2492p5x6lcmlwmhbmkf69fhyvzcz0scgdlqqm"; authors = [ "The ICU4X Project Developers" ]; @@ -4528,13 +4525,18 @@ rec { }; "icu_properties" = rec { crateName = "icu_properties"; - version = "2.2.0"; - edition = "2021"; - sha256 = "1pkh3s837808cbwxvfagwc28cvwrz2d9h5rl02jwrhm51ryvdqxy"; + version = "2.3.0"; + edition = "2024"; + sha256 = "0j51hi8qgf0l6a7qzvnwsc61598w2fpnsklicld6ci9immva4z3y"; authors = [ "The ICU4X Project Developers" ]; dependencies = [ + { + name = "displaydoc"; + packageId = "displaydoc"; + usesDefaultFeatures = false; + } { name = "icu_collections"; packageId = "icu_collections"; @@ -4576,6 +4578,7 @@ rec { "datagen" = [ "serde" "dep:databake" "zerovec/databake" "icu_collections/databake" "icu_locale_core/databake" "zerotrie/databake" "icu_provider/export" ]; "default" = [ "compiled_data" ]; "harfbuzz_traits" = [ "dep:harfbuzz-traits" ]; + "log" = [ "dep:log" ]; "serde" = [ "dep:serde" "icu_locale_core/serde" "zerovec/serde" "icu_collections/serde" "icu_provider/serde" "zerotrie/serde" ]; "unicode_bidi" = [ "dep:unicode-bidi" ]; }; @@ -4583,9 +4586,9 @@ rec { }; "icu_properties_data" = rec { crateName = "icu_properties_data"; - version = "2.2.0"; - edition = "2021"; - sha256 = "052awny0qwkbcbpd5jg2cd7vl5ry26pq4hz1nfsgf10c3qhbnawf"; + version = "2.3.0"; + edition = "2024"; + sha256 = "1akw1gp5rcaiz377xzsnkx8f92qax4kh3lfn9y4rcjj6q4wg1475"; authors = [ "The ICU4X Project Developers" ]; @@ -4593,9 +4596,9 @@ rec { }; "icu_provider" = rec { crateName = "icu_provider"; - version = "2.2.0"; - edition = "2021"; - sha256 = "08dl8pxbwr8zsz4c5vphqb7xw0hykkznwi4rw7bk6pwb3krlr70k"; + version = "2.3.0"; + edition = "2024"; + sha256 = "0a343jlrb7jlb20xv1airslzv63559wf38jihrx81bba39kyv9wj"; authors = [ "The ICU4X Project Developers" ]; @@ -5094,7 +5097,7 @@ rec { } { name = "thiserror"; - packageId = "thiserror 2.0.19"; + packageId = "thiserror 2.0.20"; } { name = "windows-link"; @@ -5212,9 +5215,9 @@ rec { }; "js-sys" = rec { crateName = "js-sys"; - version = "0.3.103"; + version = "0.3.104"; edition = "2021"; - sha256 = "00lib0b6hqmw56r2hjp7xrv730qacslirbkdlhvmi39zvgy4pd2k"; + sha256 = "0fjsgady7wbv7bbyy6c8qhrd93bnx11qbl83l1g7bb9a4601030f"; libName = "js_sys"; authors = [ "The wasm-bindgen Developers" @@ -5274,7 +5277,7 @@ rec { } { name = "thiserror"; - packageId = "thiserror 2.0.19"; + packageId = "thiserror 2.0.20"; } ]; devDependencies = [ @@ -5323,7 +5326,7 @@ rec { } { name = "thiserror"; - packageId = "thiserror 2.0.19"; + packageId = "thiserror 2.0.20"; } ]; @@ -5421,8 +5424,8 @@ rec { workspace_member = null; src = pkgs.fetchgit { url = "https://github.com/stackabletech/operator-rs.git"; - rev = "fb2d86579f4e3df008f78f0e527a012243483a2d"; - sha256 = "1w57n5xx0ik63r252l1v5ymm51jlsf7v4pj682b902k8vinlhyqb"; + rev = "7b9f9ac9a76fa425ab27f2821377ef86571ca121"; + sha256 = "1p3744fxgvs12sqwvi8hhainwrgvhdfwmbyqf0sp0aq3awq3q1v9"; }; libName = "k8s_version"; authors = [ @@ -5600,7 +5603,7 @@ rec { } { name = "futures"; - packageId = "futures 0.3.33"; + packageId = "futures 0.3.34"; optional = true; usesDefaultFeatures = false; features = [ "std" ]; @@ -5700,7 +5703,7 @@ rec { } { name = "thiserror"; - packageId = "thiserror 2.0.19"; + packageId = "thiserror 2.0.20"; } { name = "tokio"; @@ -5736,7 +5739,7 @@ rec { devDependencies = [ { name = "futures"; - packageId = "futures 0.3.33"; + packageId = "futures 0.3.34"; usesDefaultFeatures = false; features = [ "async-await" ]; } @@ -5866,7 +5869,7 @@ rec { } { name = "thiserror"; - packageId = "thiserror 2.0.19"; + packageId = "thiserror 2.0.20"; } ]; devDependencies = [ @@ -5973,7 +5976,7 @@ rec { } { name = "futures"; - packageId = "futures 0.3.33"; + packageId = "futures 0.3.34"; usesDefaultFeatures = false; features = [ "async-await" ]; } @@ -6018,7 +6021,7 @@ rec { } { name = "thiserror"; - packageId = "thiserror 2.0.19"; + packageId = "thiserror 2.0.20"; } { name = "tokio"; @@ -6197,9 +6200,9 @@ rec { }; "litemap" = rec { crateName = "litemap"; - version = "0.8.2"; + version = "0.8.3"; edition = "2021"; - sha256 = "1w7628bc7wwcxc4n4s5kw0610xk06710nh2hn5kwwk2wa91z9nlj"; + sha256 = "1bpgpj87560hmckh3875fbahpmfxbk4g8pzns84h3ykf3nfx3na7"; authors = [ "The ICU4X Project Developers" ]; @@ -6517,9 +6520,9 @@ rec { }; "num-integer" = rec { crateName = "num-integer"; - version = "0.1.46"; + version = "0.1.47"; edition = "2018"; - sha256 = "13w5g54a9184cqlbsq80rnxw4jj4s0d8wv75jsq5r2lms8gncsbr"; + sha256 = "02z1p3azy6p10n99skrab4a6hhfd4amf2i9gm8sxqd1p9dfxkqkw"; libName = "num_integer"; authors = [ "The Rust Project Developers" @@ -6661,7 +6664,7 @@ rec { } { name = "thiserror"; - packageId = "thiserror 2.0.19"; + packageId = "thiserror 2.0.20"; optional = true; usesDefaultFeatures = false; } @@ -6831,7 +6834,7 @@ rec { } { name = "thiserror"; - packageId = "thiserror 2.0.19"; + packageId = "thiserror 2.0.20"; usesDefaultFeatures = false; } { @@ -7038,7 +7041,7 @@ rec { } { name = "thiserror"; - packageId = "thiserror 2.0.19"; + packageId = "thiserror 2.0.20"; usesDefaultFeatures = false; } { @@ -7339,9 +7342,9 @@ rec { }; "pest" = rec { crateName = "pest"; - version = "2.8.8"; + version = "2.9.0"; edition = "2021"; - sha256 = "18jhl2zpxvl6kikc0jgp7gi7i7cy9s634z5bnvx70w1whjz2ixvx"; + sha256 = "1kwvhc5hyrfpxpmp0jw0wr891xyjs44mcs2wz68hrl54qw6ac1ss"; authors = [ "Dragoș Tiselice " ]; @@ -7368,9 +7371,9 @@ rec { }; "pest_derive" = rec { crateName = "pest_derive"; - version = "2.8.8"; + version = "2.9.0"; edition = "2021"; - sha256 = "1zcijlfdf6sk2s6l1qnm3j7kj7d4ymqcial8w4p4dcr67gydcbcy"; + sha256 = "13f8ihi8928s9mc13pcbhxy382kqc89h7i8d7s5mnif8lm23ga5k"; procMacro = true; authors = [ "Dragoș Tiselice " @@ -7396,9 +7399,9 @@ rec { }; "pest_generator" = rec { crateName = "pest_generator"; - version = "2.8.8"; + version = "2.9.0"; edition = "2021"; - sha256 = "1dkmk6r6bb2hh5wayymfmwd7mswwbyhw12dnx2lrdxdnrw2r4yka"; + sha256 = "0jihcdnmdban4bqjd02r2wbb79nywj2nhwqyk95hvrixm98k9kg0"; authors = [ "Dragoș Tiselice " ]; @@ -7434,9 +7437,9 @@ rec { }; "pest_meta" = rec { crateName = "pest_meta"; - version = "2.8.8"; + version = "2.9.0"; edition = "2021"; - sha256 = "0z7m54jc3nj3nxbbk4kyjfa06d8s24vhf6krzj2867nyq18x7aw5"; + sha256 = "15jly0r7r4m15fhm3pg814h65j7dqnqq6k43rvgxfhg29443lkg0"; authors = [ "Dragoș Tiselice " ]; @@ -7567,9 +7570,9 @@ rec { }; "pkg-config" = rec { crateName = "pkg-config"; - version = "0.3.33"; - edition = "2018"; - sha256 = "17jnqmcbxsnwhg9gjf0nh6dj5k0x3hgwi3mb9krjnmfa9v435w8r"; + version = "0.3.34"; + edition = "2021"; + sha256 = "0j05h08nzg0q8rf6lzw7nry0b7kn7x97vc9n4hwrl52fqzxn9d7n"; libName = "pkg_config"; authors = [ "Alex Crichton " @@ -7578,9 +7581,9 @@ rec { }; "portable-atomic" = rec { crateName = "portable-atomic"; - version = "1.14.0"; + version = "1.15.0"; edition = "2018"; - sha256 = "1hyfma9n2cs2ibazpfwrbv61zwg7cv86g0pr5yjkg07qgr4xa81x"; + sha256 = "11csag858ndk5w4yz17h91vy53ynh67r2903gwwdn2cnilzbdj05"; libName = "portable_atomic"; features = { "critical-section" = [ "dep:critical-section" ]; @@ -7611,9 +7614,9 @@ rec { }; "potential_utf" = rec { crateName = "potential_utf"; - version = "0.1.5"; + version = "0.1.6"; edition = "2021"; - sha256 = "0r0518fr32xbkgzqap509s3r60cr0iancsg9j1jgf37cyz7b20q1"; + sha256 = "0qbndl2fpphq7mph41m11vaixs05xrh1s451wxlgap4fdnybjgnq"; authors = [ "The ICU4X Project Developers" ]; @@ -8105,9 +8108,9 @@ rec { }; "ref-cast" = rec { crateName = "ref-cast"; - version = "1.0.26"; + version = "1.0.27"; edition = "2021"; - sha256 = "0vdra0766jcc2czzqwhql41kkfyajdnai1pbkjxbq8vr7mvqyvi1"; + sha256 = "1hv5sf0j7b65gz2g57c3wp0fzr5r3807dywf6fap455lwjs0yi3y"; libName = "ref_cast"; authors = [ "David Tolnay " @@ -8122,9 +8125,9 @@ rec { }; "ref-cast-impl" = rec { crateName = "ref-cast-impl"; - version = "1.0.26"; + version = "1.0.27"; edition = "2021"; - sha256 = "0g70ff9an5i97cw9kijgzqrqydz7smcfic2zyydddizfbxl874ic"; + sha256 = "0fnzgkvddgl9xs3884x5ypi9rd0dgc1p5vd1k4b74lw49ybdiv4j"; procMacro = true; libName = "ref_cast_impl"; authors = [ @@ -8955,9 +8958,9 @@ rec { }; "rustls-webpki" = rec { crateName = "rustls-webpki"; - version = "0.103.13"; + version = "0.103.14"; edition = "2021"; - sha256 = "0vkm7z9pnxz5qz66p2kmyy2pwx0g4jnsbqk5xzfhs4czcjl2ki31"; + sha256 = "0njk28gvbqrsfg1b5r35y4f80n37kcjylj72fpc0k0g60n3529q5"; libName = "webpki"; dependencies = [ { @@ -8981,7 +8984,7 @@ rec { "alloc" = [ "ring?/alloc" "pki-types/alloc" ]; "aws-lc-rs" = [ "dep:aws-lc-rs" "aws-lc-rs/aws-lc-sys" "aws-lc-rs/prebuilt-nasm" ]; "aws-lc-rs-fips" = [ "dep:aws-lc-rs" "aws-lc-rs/fips" ]; - "aws-lc-rs-unstable" = [ "aws-lc-rs" "aws-lc-rs/unstable" ]; + "aws-lc-rs-unstable" = [ "aws-lc-rs" ]; "default" = [ "std" ]; "ring" = [ "dep:ring" ]; "std" = [ "alloc" "pki-types/std" ]; @@ -10258,8 +10261,8 @@ rec { workspace_member = null; src = pkgs.fetchgit { url = "https://github.com/stackabletech/operator-rs.git"; - rev = "fb2d86579f4e3df008f78f0e527a012243483a2d"; - sha256 = "1w57n5xx0ik63r252l1v5ymm51jlsf7v4pj682b902k8vinlhyqb"; + rev = "7b9f9ac9a76fa425ab27f2821377ef86571ca121"; + sha256 = "1p3744fxgvs12sqwvi8hhainwrgvhdfwmbyqf0sp0aq3awq3q1v9"; }; libName = "stackable_certs"; authors = [ @@ -10356,13 +10359,13 @@ rec { }; "stackable-operator" = rec { crateName = "stackable-operator"; - version = "0.115.0"; + version = "0.116.0"; edition = "2024"; workspace_member = null; src = pkgs.fetchgit { url = "https://github.com/stackabletech/operator-rs.git"; - rev = "fb2d86579f4e3df008f78f0e527a012243483a2d"; - sha256 = "1w57n5xx0ik63r252l1v5ymm51jlsf7v4pj682b902k8vinlhyqb"; + rev = "7b9f9ac9a76fa425ab27f2821377ef86571ca121"; + sha256 = "1p3744fxgvs12sqwvi8hhainwrgvhdfwmbyqf0sp0aq3awq3q1v9"; }; libName = "stackable_operator"; authors = [ @@ -10402,7 +10405,7 @@ rec { } { name = "futures"; - packageId = "futures 0.3.33"; + packageId = "futures 0.3.34"; } { name = "http"; @@ -10542,7 +10545,8 @@ rec { "client-feature-gates" = [ "dep:winnow" ]; "crds" = [ "dep:stackable-versioned" ]; "default" = [ "crds" ]; - "full" = [ "client-feature-gates" "crds" "certs" "test-support" "time" "webhook" "kube-ws" ]; + "full" = [ "client-feature-gates" "crds" "certs" "test-support" "time" "webhook" "kube-ws" "kube-cel" ]; + "kube-cel" = [ "kube/cel" ]; "kube-ws" = [ "kube/ws" ]; "time" = [ "stackable-shared/time" ]; "webhook" = [ "dep:stackable-webhook" ]; @@ -10556,8 +10560,8 @@ rec { workspace_member = null; src = pkgs.fetchgit { url = "https://github.com/stackabletech/operator-rs.git"; - rev = "fb2d86579f4e3df008f78f0e527a012243483a2d"; - sha256 = "1w57n5xx0ik63r252l1v5ymm51jlsf7v4pj682b902k8vinlhyqb"; + rev = "7b9f9ac9a76fa425ab27f2821377ef86571ca121"; + sha256 = "1p3744fxgvs12sqwvi8hhainwrgvhdfwmbyqf0sp0aq3awq3q1v9"; }; procMacro = true; libName = "stackable_operator_derive"; @@ -10591,8 +10595,8 @@ rec { workspace_member = null; src = pkgs.fetchgit { url = "https://github.com/stackabletech/operator-rs.git"; - rev = "fb2d86579f4e3df008f78f0e527a012243483a2d"; - sha256 = "1w57n5xx0ik63r252l1v5ymm51jlsf7v4pj682b902k8vinlhyqb"; + rev = "7b9f9ac9a76fa425ab27f2821377ef86571ca121"; + sha256 = "1p3744fxgvs12sqwvi8hhainwrgvhdfwmbyqf0sp0aq3awq3q1v9"; }; libName = "stackable_shared"; authors = [ @@ -10672,8 +10676,8 @@ rec { workspace_member = null; src = pkgs.fetchgit { url = "https://github.com/stackabletech/operator-rs.git"; - rev = "fb2d86579f4e3df008f78f0e527a012243483a2d"; - sha256 = "1w57n5xx0ik63r252l1v5ymm51jlsf7v4pj682b902k8vinlhyqb"; + rev = "7b9f9ac9a76fa425ab27f2821377ef86571ca121"; + sha256 = "1p3744fxgvs12sqwvi8hhainwrgvhdfwmbyqf0sp0aq3awq3q1v9"; }; libName = "stackable_telemetry"; authors = [ @@ -10782,8 +10786,8 @@ rec { workspace_member = null; src = pkgs.fetchgit { url = "https://github.com/stackabletech/operator-rs.git"; - rev = "fb2d86579f4e3df008f78f0e527a012243483a2d"; - sha256 = "1w57n5xx0ik63r252l1v5ymm51jlsf7v4pj682b902k8vinlhyqb"; + rev = "7b9f9ac9a76fa425ab27f2821377ef86571ca121"; + sha256 = "1p3744fxgvs12sqwvi8hhainwrgvhdfwmbyqf0sp0aq3awq3q1v9"; }; libName = "stackable_versioned"; authors = [ @@ -10832,8 +10836,8 @@ rec { workspace_member = null; src = pkgs.fetchgit { url = "https://github.com/stackabletech/operator-rs.git"; - rev = "fb2d86579f4e3df008f78f0e527a012243483a2d"; - sha256 = "1w57n5xx0ik63r252l1v5ymm51jlsf7v4pj682b902k8vinlhyqb"; + rev = "7b9f9ac9a76fa425ab27f2821377ef86571ca121"; + sha256 = "1p3744fxgvs12sqwvi8hhainwrgvhdfwmbyqf0sp0aq3awq3q1v9"; }; procMacro = true; libName = "stackable_versioned_macros"; @@ -10900,8 +10904,8 @@ rec { workspace_member = null; src = pkgs.fetchgit { url = "https://github.com/stackabletech/operator-rs.git"; - rev = "fb2d86579f4e3df008f78f0e527a012243483a2d"; - sha256 = "1w57n5xx0ik63r252l1v5ymm51jlsf7v4pj682b902k8vinlhyqb"; + rev = "7b9f9ac9a76fa425ab27f2821377ef86571ca121"; + sha256 = "1p3744fxgvs12sqwvi8hhainwrgvhdfwmbyqf0sp0aq3awq3q1v9"; }; libName = "stackable_webhook"; authors = [ @@ -11059,7 +11063,7 @@ rec { } { name = "futures"; - packageId = "futures 0.3.33"; + packageId = "futures 0.3.34"; features = [ "compat" ]; } { @@ -11090,7 +11094,7 @@ rec { { name = "stackable-operator"; packageId = "stackable-operator"; - features = [ "webhook" "test-support" ]; + features = [ "webhook" ]; } { name = "strum"; @@ -11123,6 +11127,11 @@ rec { name = "serde_yaml"; packageId = "serde_yaml"; } + { + name = "stackable-operator"; + packageId = "stackable-operator"; + features = [ "webhook" "test-support" ]; + } ]; }; @@ -11384,18 +11393,18 @@ rec { ]; }; - "thiserror 2.0.19" = rec { + "thiserror 2.0.20" = rec { crateName = "thiserror"; - version = "2.0.19"; + version = "2.0.20"; edition = "2021"; - sha256 = "1ngwxsjsa64v1n7vb90h2b0i3fqk1piwaf0z6fqdacqfhjc3b909"; + sha256 = "0kxs6p295jffxhzaxpxv1dwaaf5iqlm6sx8h0djp6ancbxgj71pc"; authors = [ "David Tolnay " ]; dependencies = [ { name = "thiserror-impl"; - packageId = "thiserror-impl 2.0.19"; + packageId = "thiserror-impl 2.0.20"; } ]; features = { @@ -11429,11 +11438,11 @@ rec { ]; }; - "thiserror-impl 2.0.19" = rec { + "thiserror-impl 2.0.20" = rec { crateName = "thiserror-impl"; - version = "2.0.19"; + version = "2.0.20"; edition = "2021"; - sha256 = "1ka10pqy1g8zy5al9m8yadg30jp8hx0q80j8awmd8131yw6gxjs3"; + sha256 = "1bwjc94gi0xn5jz26h1a8bjj1wdkvvr6jifamyc4mp9n28zcs15w"; procMacro = true; libName = "thiserror_impl"; authors = [ @@ -11582,9 +11591,9 @@ rec { }; "tinystr" = rec { crateName = "tinystr"; - version = "0.8.3"; + version = "0.8.4"; edition = "2021"; - sha256 = "0vfr8x285w6zsqhna0a9jyhylwiafb2kc8pj2qaqaahw48236cn8"; + sha256 = "0hzncw8rgk4syla79qscfml46jm7ll1zdp7kdacc42cj8n8prqmi"; authors = [ "The ICU4X Project Developers" ]; @@ -11993,7 +12002,7 @@ rec { } { name = "futures"; - packageId = "futures 0.3.33"; + packageId = "futures 0.3.34"; } { name = "once_cell"; @@ -12643,7 +12652,7 @@ rec { } { name = "thiserror"; - packageId = "thiserror 2.0.19"; + packageId = "thiserror 2.0.20"; } { name = "time"; @@ -13178,9 +13187,9 @@ rec { }; "uuid" = rec { crateName = "uuid"; - version = "1.24.0"; + version = "1.24.1"; edition = "2021"; - sha256 = "0faj5x0zgri8m3i8dv9qgyhiwqwdyhbl2g351cp3iin4ynk26fdz"; + sha256 = "1n8b7fg7dbx6ws64387l2i0qq900rw9b7qax63acdh37sczw1vrc"; authors = [ "Ashley Mannix" "Dylan DPC" @@ -13344,9 +13353,9 @@ rec { }; "wasm-bindgen" = rec { crateName = "wasm-bindgen"; - version = "0.2.126"; + version = "0.2.127"; edition = "2021"; - sha256 = "197rma4qg1kb8l4bl7857pgszzval8s1w740g9myyjh92467q1jb"; + sha256 = "0w6fa1mkbb6qlkffgy4qaz0hdf496zbjkyiyvs4lvmpd8xbr6w0v"; libName = "wasm_bindgen"; authors = [ "The wasm-bindgen Developers" @@ -13395,9 +13404,9 @@ rec { }; "wasm-bindgen-futures" = rec { crateName = "wasm-bindgen-futures"; - version = "0.4.76"; + version = "0.4.77"; edition = "2021"; - sha256 = "0799v92cpaprapnmpaflc51sdnz362q2fsjdqnwiq8ij1wsg2bf6"; + sha256 = "0l3r8m335kb2p8yj65kb0biwlypcx3ay4g750hafkl13rkapfxvb"; libName = "wasm_bindgen_futures"; authors = [ "The wasm-bindgen Developers" @@ -13423,9 +13432,9 @@ rec { }; "wasm-bindgen-macro" = rec { crateName = "wasm-bindgen-macro"; - version = "0.2.126"; + version = "0.2.127"; edition = "2021"; - sha256 = "1cda6wl5zyiy7777cfgrix7fhpaqba55l5zpqj4zig7ng7jyaz0n"; + sha256 = "1hcvlb6bv771fvgifd367wd0cm4giyar8fq5i4h705vj7y7myxvp"; procMacro = true; libName = "wasm_bindgen_macro"; authors = [ @@ -13447,9 +13456,9 @@ rec { }; "wasm-bindgen-macro-support" = rec { crateName = "wasm-bindgen-macro-support"; - version = "0.2.126"; + version = "0.2.127"; edition = "2021"; - sha256 = "03iq412frl2py55skwb3ya08xha0cf6q22zr5kqlwbr675w7r6gk"; + sha256 = "112j4d7dv8y2sk9yy9czrl9fpjx9388ywnn7icdv2bywazw367g1"; libName = "wasm_bindgen_macro_support"; authors = [ "The wasm-bindgen Developers" @@ -13483,10 +13492,10 @@ rec { }; "wasm-bindgen-shared" = rec { crateName = "wasm-bindgen-shared"; - version = "0.2.126"; + version = "0.2.127"; edition = "2021"; links = "wasm_bindgen"; - sha256 = "097a3kbjls447s1lwr41l21x5crrh5vq3h6zsxccz7slrjq4q6yw"; + sha256 = "1gywp6xv8a27fvm3ga9xby93xyic3hc2s626b9z9rw2xqny4vxky"; libName = "wasm_bindgen_shared"; authors = [ "The wasm-bindgen Developers" @@ -13501,9 +13510,9 @@ rec { }; "web-sys" = rec { crateName = "web-sys"; - version = "0.3.103"; + version = "0.3.104"; edition = "2021"; - sha256 = "0hb1zdnrp99p5r5q66jagsddmwha460yv2wklvzrzk0b3jvdq8l6"; + sha256 = "0c0acbvaqzqf21q5vdff2g74fvb7afi91xjplmclybq4d24k6df4"; libName = "web_sys"; authors = [ "The wasm-bindgen Developers" @@ -13759,6 +13768,10 @@ rec { "MouseEvent" = [ "Event" "UiEvent" ]; "MouseScrollEvent" = [ "Event" "MouseEvent" "UiEvent" ]; "MutationEvent" = [ "Event" ]; + "NavigateEvent" = [ "Event" ]; + "Navigation" = [ "EventTarget" ]; + "NavigationCurrentEntryChangeEvent" = [ "Event" ]; + "NavigationHistoryEntry" = [ "EventTarget" ]; "NetworkInformation" = [ "EventTarget" ]; "Node" = [ "EventTarget" ]; "Notification" = [ "EventTarget" ]; @@ -14856,9 +14869,9 @@ rec { }; "writeable" = rec { crateName = "writeable"; - version = "0.6.3"; + version = "0.6.4"; edition = "2021"; - sha256 = "1i54d13h9bpap2hf13xcry1s4lxh7ap3923g8f3c0grd7c9fbyhz"; + sha256 = "1p3r4s4wbf3dksfpj3xyrn7id5p0f7r74mj6qx6ngjfd6cm2vn1s"; authors = [ "The ICU4X Project Developers" ]; @@ -15168,9 +15181,9 @@ rec { }; "zerotrie" = rec { crateName = "zerotrie"; - version = "0.2.4"; + version = "0.2.5"; edition = "2021"; - sha256 = "1gr0pkcn3qsr6in6iixqyp0vbzwf2j1jzyvh7yl2yydh3p9m548g"; + sha256 = "0gss16krjzk22m57dz5hkdjg99ibj6pa41qr68na7w1jpp1nk8jf"; authors = [ "The ICU4X Project Developers" ]; @@ -15208,9 +15221,9 @@ rec { }; "zerovec" = rec { crateName = "zerovec"; - version = "0.11.6"; + version = "0.11.8"; edition = "2021"; - sha256 = "0fdjsy6b31q9i0d73sl7xjd12xadbwi45lkpfgqnmasrqg5i3ych"; + sha256 = "1n3xlvyba8riys9s8awy4xp533phqycr78nbsmvdkh86g3hn815v"; authors = [ "The ICU4X Project Developers" ]; @@ -15254,9 +15267,9 @@ rec { }; "zerovec-derive" = rec { crateName = "zerovec-derive"; - version = "0.11.3"; + version = "0.11.5"; edition = "2021"; - sha256 = "0m85qj92mmfvhjra6ziqky5b1p4kcmp5069k7kfadp5hr8jw8pb2"; + sha256 = "1a8pz516ddcgxvxq3j1xgprac5wnprlrbyzsgzarj0423la2l8cz"; procMacro = true; libName = "zerovec_derive"; authors = [ @@ -15273,7 +15286,7 @@ rec { } { name = "syn"; - packageId = "syn 2.0.119"; + packageId = "syn 3.0.3"; features = [ "extra-traits" ]; } ]; diff --git a/crate-hashes.json b/crate-hashes.json index 43cd4b72..3fae0f9f 100644 --- a/crate-hashes.json +++ b/crate-hashes.json @@ -1,11 +1,11 @@ { - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.115.0#k8s-version@0.1.3": "1w57n5xx0ik63r252l1v5ymm51jlsf7v4pj682b902k8vinlhyqb", - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.115.0#stackable-certs@0.4.1": "1w57n5xx0ik63r252l1v5ymm51jlsf7v4pj682b902k8vinlhyqb", - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.115.0#stackable-operator-derive@0.3.1": "1w57n5xx0ik63r252l1v5ymm51jlsf7v4pj682b902k8vinlhyqb", - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.115.0#stackable-operator@0.115.0": "1w57n5xx0ik63r252l1v5ymm51jlsf7v4pj682b902k8vinlhyqb", - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.115.0#stackable-shared@0.1.2": "1w57n5xx0ik63r252l1v5ymm51jlsf7v4pj682b902k8vinlhyqb", - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.115.0#stackable-telemetry@0.6.5": "1w57n5xx0ik63r252l1v5ymm51jlsf7v4pj682b902k8vinlhyqb", - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.115.0#stackable-versioned-macros@0.11.1": "1w57n5xx0ik63r252l1v5ymm51jlsf7v4pj682b902k8vinlhyqb", - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.115.0#stackable-versioned@0.11.1": "1w57n5xx0ik63r252l1v5ymm51jlsf7v4pj682b902k8vinlhyqb", - "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.115.0#stackable-webhook@0.9.2": "1w57n5xx0ik63r252l1v5ymm51jlsf7v4pj682b902k8vinlhyqb" + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.116.0#k8s-version@0.1.3": "1p3744fxgvs12sqwvi8hhainwrgvhdfwmbyqf0sp0aq3awq3q1v9", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.116.0#stackable-certs@0.4.1": "1p3744fxgvs12sqwvi8hhainwrgvhdfwmbyqf0sp0aq3awq3q1v9", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.116.0#stackable-operator-derive@0.3.1": "1p3744fxgvs12sqwvi8hhainwrgvhdfwmbyqf0sp0aq3awq3q1v9", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.116.0#stackable-operator@0.116.0": "1p3744fxgvs12sqwvi8hhainwrgvhdfwmbyqf0sp0aq3awq3q1v9", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.116.0#stackable-shared@0.1.2": "1p3744fxgvs12sqwvi8hhainwrgvhdfwmbyqf0sp0aq3awq3q1v9", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.116.0#stackable-telemetry@0.6.5": "1p3744fxgvs12sqwvi8hhainwrgvhdfwmbyqf0sp0aq3awq3q1v9", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.116.0#stackable-versioned-macros@0.11.1": "1p3744fxgvs12sqwvi8hhainwrgvhdfwmbyqf0sp0aq3awq3q1v9", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.116.0#stackable-versioned@0.11.1": "1p3744fxgvs12sqwvi8hhainwrgvhdfwmbyqf0sp0aq3awq3q1v9", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.116.0#stackable-webhook@0.9.2": "1p3744fxgvs12sqwvi8hhainwrgvhdfwmbyqf0sp0aq3awq3q1v9" } \ No newline at end of file diff --git a/rust/operator-binary/src/crd/security.rs b/rust/operator-binary/src/crd/security.rs index e9543f46..5ed28ce2 100644 --- a/rust/operator-binary/src/crd/security.rs +++ b/rust/operator-binary/src/crd/security.rs @@ -20,10 +20,14 @@ use stackable_operator::{ }, }, commons::secret_class::SecretClassVolumeProvisionParts, + constant, crd::authentication::core, k8s_openapi::api::core::v1::Volume, shared::time::Duration, - v2::types::{common::Port, kubernetes::SecretClassName}, + v2::types::{ + common::Port, + kubernetes::{SecretClassName, VolumeName}, + }, }; use crate::{ @@ -31,6 +35,10 @@ use crate::{ zk_controller::LISTENER_VOLUME_NAME, }; +// TLS volume names (the mount name must match the volume name). +constant!(SERVER_TLS_VOLUME_NAME: VolumeName = "server-tls"); +constant!(QUORUM_TLS_VOLUME_NAME: VolumeName = "quorum-tls"); + type Result = std::result::Result; #[derive(Snafu, Debug)] @@ -66,14 +74,11 @@ impl ZookeeperSecurity { // directories pub const QUORUM_TLS_DIR: &'static str = "/stackable/quorum_tls"; pub const QUORUM_TLS_MOUNT_DIR: &'static str = "/stackable/quorum_tls_mount"; - pub const QUORUM_TLS_VOLUME_NAME: &'static str = "quorum-tls"; pub const SECURE_CLIENT_PORT: Port = Port(2282); pub const SECURE_CLIENT_PORT_NAME: &'static str = "secureClientPort"; pub const SERVER_CNXN_FACTORY: &'static str = "serverCnxnFactory"; pub const SERVER_TLS_DIR: &'static str = "/stackable/server_tls"; pub const SERVER_TLS_MOUNT_DIR: &'static str = "/stackable/server_tls_mount"; - // TLS volume names (the mount name must match the volume name) - pub const SERVER_TLS_VOLUME_NAME: &'static str = "server-tls"; // Common TLS pub const SSL_AUTH_PROVIDER_X509: &'static str = "authProvider.x509"; // Client TLS @@ -92,7 +97,6 @@ impl ZookeeperSecurity { pub const SSL_TRUST_STORE_LOCATION: &'static str = "ssl.trustStore.location"; pub const SSL_TRUST_STORE_PASSWORD: &'static str = "ssl.trustStore.password"; // Mis - pub const STORE_PASSWORD_ENV: &'static str = "STORE_PASSWORD"; pub const SYSTEM_TRUST_STORE_DIR: &'static str = "/etc/pki/java/cacerts"; pub const TRUSTSTORE_FILE: &'static str = "truststore.p12"; @@ -156,13 +160,12 @@ impl ZookeeperSecurity { let tls_secret_class = self.get_tls_secret_class(); if let Some(secret_class) = tls_secret_class { - let tls_volume_name = Self::SERVER_TLS_VOLUME_NAME; cb_zookeeper - .add_volume_mount(tls_volume_name, Self::SERVER_TLS_DIR) + .add_volume_mount(&*SERVER_TLS_VOLUME_NAME, Self::SERVER_TLS_DIR) .context(AddVolumeMountSnafu)?; pod_builder .add_volume(Self::create_server_tls_volume( - tls_volume_name, + &SERVER_TLS_VOLUME_NAME, secret_class, requested_secret_lifetime, )?) @@ -170,13 +173,12 @@ impl ZookeeperSecurity { } // quorum - let tls_volume_name = Self::QUORUM_TLS_VOLUME_NAME; cb_zookeeper - .add_volume_mount(tls_volume_name, Self::QUORUM_TLS_DIR) + .add_volume_mount(&*QUORUM_TLS_VOLUME_NAME, Self::QUORUM_TLS_DIR) .context(AddVolumeMountSnafu)?; pod_builder .add_volume(Self::create_quorum_tls_volume( - tls_volume_name, + &QUORUM_TLS_VOLUME_NAME, self.quorum_secret_class.as_ref(), requested_secret_lifetime, )?) @@ -323,11 +325,11 @@ impl ZookeeperSecurity { /// /// [ListenerStatus]: ::stackable_operator::crd::listener::v1alpha1::ListenerStatus fn create_server_tls_volume( - volume_name: &str, + volume_name: &VolumeName, secret_class_name: &str, requested_secret_lifetime: &Duration, ) -> Result { - let volume = VolumeBuilder::new(volume_name) + let volume = VolumeBuilder::new(volume_name.to_string()) .ephemeral( SecretOperatorVolumeSourceBuilder::new( secret_class_name, @@ -338,7 +340,9 @@ impl ZookeeperSecurity { .with_format(SecretFormat::TlsPkcs12) .with_auto_tls_cert_lifetime(*requested_secret_lifetime) .build() - .context(BuildTlsVolumeSnafu { volume_name })?, + .context(BuildTlsVolumeSnafu { + volume_name: volume_name.to_string(), + })?, ) .build(); @@ -349,11 +353,11 @@ impl ZookeeperSecurity { /// /// The resulting volume will contain TLS certificates with the FQDN of the Pod in relation to the StatefulSet's headless service. fn create_quorum_tls_volume( - volume_name: &str, + volume_name: &VolumeName, secret_class_name: &str, requested_secret_lifetime: &Duration, ) -> Result { - let volume = VolumeBuilder::new(volume_name) + let volume = VolumeBuilder::new(volume_name.to_string()) .ephemeral( SecretOperatorVolumeSourceBuilder::new( secret_class_name, @@ -364,7 +368,9 @@ impl ZookeeperSecurity { .with_format(SecretFormat::TlsPkcs12) .with_auto_tls_cert_lifetime(*requested_secret_lifetime) .build() - .context(BuildTlsVolumeSnafu { volume_name })?, + .context(BuildTlsVolumeSnafu { + volume_name: volume_name.to_string(), + })?, ) .build(); @@ -383,3 +389,15 @@ impl ZookeeperSecurity { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_constants() { + // Test that dereferencing the constants does not panic. + let _ = *SERVER_TLS_VOLUME_NAME; + let _ = *QUORUM_TLS_VOLUME_NAME; + } +} diff --git a/rust/operator-binary/src/zk_controller/build/resource/statefulset.rs b/rust/operator-binary/src/zk_controller/build/resource/statefulset.rs index dbad7578..a2f9c693 100644 --- a/rust/operator-binary/src/zk_controller/build/resource/statefulset.rs +++ b/rust/operator-binary/src/zk_controller/build/resource/statefulset.rs @@ -9,11 +9,8 @@ use stackable_operator::{ self, meta::ObjectMetaBuilder, pod::{ - PodBuilder, - container::{ContainerBuilder, FieldPathEnvVar}, - resources::ResourceRequirementsBuilder, + PodBuilder, container::FieldPathEnvVar, resources::ResourceRequirementsBuilder, security::PodSecurityContextBuilder, - volume::{ListenerOperatorVolumeSourceBuilder, ListenerReference}, }, }, constant, @@ -37,10 +34,13 @@ use stackable_operator::{ }, utils::COMMON_BASH_TRAP_FUNCTIONS, v2::{ - builder::pod::container::{EnvVarName, EnvVarSet}, + builder::pod::{ + container::{EnvVarName, EnvVarSet, new_container_builder}, + volume::{ListenerReference, listener_operator_volume_source_builder_build_pvc}, + }, product_logging::framework::{ValidatedContainerLogConfigChoice, vector_container}, types::{ - kubernetes::{ContainerName, VolumeName}, + kubernetes::{ContainerName, ListenerName, PersistentVolumeClaimName, VolumeName}, operator::RoleGroupName, }, }, @@ -86,9 +86,16 @@ constant!(RW_CONFIG_VOLUME_NAME: VolumeName = "rwconfig"); constant!(LOG_VOLUME_NAME: VolumeName = "log"); constant!(LOG_CONFIG_VOLUME_NAME: VolumeName = "log-config"); -/// Name of the `prepare` init container (also used as its log subdirectory). -const PREPARE_CONTAINER_NAME: &str = "prepare"; +// The listener volume is provisioned as a PVC by the listener-operator; this is its typed name. +// It must match `LISTENER_VOLUME_NAME`, by which the volume mount and the secret-operator volume +// scope reference it. +constant!(LISTENER_PVC_NAME: PersistentVolumeClaimName = "listener"); +// Container names. These must match the corresponding (kebab-cased) `crate::crd::Container` +// variants, which key the per-container logging config. The prepare container name is also used +// as its log subdirectory. +constant!(PREPARE_CONTAINER_NAME: ContainerName = "prepare"); +constant!(ZOOKEEPER_CONTAINER_NAME: ContainerName = APP_NAME); constant!(VECTOR_CONTAINER_NAME: ContainerName = "vector"); // Env vars the operator sets on the containers. @@ -139,23 +146,17 @@ pub enum Error { GracefulShutdown { source: crate::zk_controller::build::graceful_shutdown::Error, }, - - #[snafu(display("failed to build listener volume"))] - BuildListenerPersistentVolume { - source: stackable_operator::builder::pod::volume::ListenerOperatorVolumeSourceBuilderError, - }, } fn build_role_listener_pvc( - group_listener_name: &str, + role_listener_name: ListenerName, unversioned_recommended_labels: &Labels, -) -> Result { - ListenerOperatorVolumeSourceBuilder::new( - &ListenerReference::ListenerName(group_listener_name.to_string()), +) -> PersistentVolumeClaim { + listener_operator_volume_source_builder_build_pvc( + &ListenerReference::Listener(role_listener_name), unversioned_recommended_labels, + &LISTENER_PVC_NAME, ) - .build_pvc(LISTENER_VOLUME_NAME.to_string()) - .context(BuildListenerPersistentVolumeSnafu) } /// The rolegroup [`StatefulSet`] runs the rolegroup, as configured by the administrator. @@ -206,10 +207,8 @@ pub fn build_server_rolegroup_statefulset( let original_pvcs = vec![data_pvc]; let resources: ResourceRequirements = resources_config.into(); - let mut cb_prepare = - ContainerBuilder::new(PREPARE_CONTAINER_NAME).expect("invalid hard-coded container name"); - let mut cb_zookeeper = - ContainerBuilder::new(APP_NAME).expect("invalid hard-coded container name"); + let mut cb_prepare = new_container_builder(&PREPARE_CONTAINER_NAME); + let mut cb_zookeeper = new_container_builder(&ZOOKEEPER_CONTAINER_NAME); let mut pod_builder = PodBuilder::new(); // Used for PVC templates, which cannot be modified once they are deployed. The version label @@ -218,9 +217,9 @@ pub fn build_server_rolegroup_statefulset( recommended_labels_for_unversioned_role_group_resources(cluster, zk_role, role_group_name); let listener_pvc = build_role_listener_pvc( - role_listener_name(cluster.name.as_ref(), zk_role).as_ref(), + role_listener_name(cluster.name.as_ref(), zk_role), &unversioned_recommended_labels, - )?; + ); let mut pvcs = original_pvcs; pvcs.extend([listener_pvc]); @@ -248,7 +247,7 @@ pub fn build_server_rolegroup_statefulset( { args.push(product_logging::framework::capture_shell_output( STACKABLE_LOG_DIR, - PREPARE_CONTAINER_NAME, + PREPARE_CONTAINER_NAME.as_ref(), log_config, )); } @@ -481,6 +480,9 @@ mod tests { let _ = *RW_CONFIG_VOLUME_NAME; let _ = *LOG_VOLUME_NAME; let _ = *LOG_CONFIG_VOLUME_NAME; + let _ = *LISTENER_PVC_NAME; + let _ = *PREPARE_CONTAINER_NAME; + let _ = *ZOOKEEPER_CONTAINER_NAME; let _ = *VECTOR_CONTAINER_NAME; let _ = *POD_NAME; let _ = *MYID_OFFSET; @@ -606,7 +608,11 @@ mod tests { /// as the example here because it is set unconditionally by the operator. #[test] fn env_overrides_override_operator_set_env_vars() { - let env = env_with_override(APP_NAME, &CONTAINERDEBUG_LOG_DIRECTORY, "/custom/log/dir"); + let env = env_with_override( + ZOOKEEPER_CONTAINER_NAME.as_ref(), + &CONTAINERDEBUG_LOG_DIRECTORY, + "/custom/log/dir", + ); let containerdebug: Vec<_> = env .iter() @@ -623,7 +629,11 @@ mod tests { /// Same guarantee for the `prepare` init container, whose env vars are assembled separately. #[test] fn prepare_env_overrides_override_operator_set_env_vars() { - let env = env_with_override(PREPARE_CONTAINER_NAME, &ZOOCFGDIR, "/custom/conf/dir"); + let env = env_with_override( + PREPARE_CONTAINER_NAME.as_ref(), + &ZOOCFGDIR, + "/custom/conf/dir", + ); let zoocfgdir: Vec<_> = env .iter() From 9a37feca302e0c3d848bedc3cc4ddd8dc5d15187 Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Thu, 20 Aug 2026 11:59:44 +0200 Subject: [PATCH 3/3] changelog --- CHANGELOG.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4683c192..3f043d1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ All notable changes to this project will be documented in this file. - Internal operator refactoring: introduce a build() step in the reconciler that assembles all relevant Kubernetes resources before anything is applied ([#1053]). -- Bump `stackable-operator` to 0.114.0 ([#1063]). +- Bump `stackable-operator` to 0.116.0 ([#1063], [#1077]). - The RBAC ServiceAccount and RoleBinding are now built with the operator-rs `v2::rbac` functions and carry the full set of recommended labels ([#1060]). - BREAKING: The `servers` role is now required by the CRD. @@ -22,6 +22,15 @@ All notable changes to this project will be documented in this file. - The discovery ConfigMap is now always written, with empty `ZOOKEEPER` and `ZOOKEEPER_HOSTS` while the listener publishes no addresses ([#1069]). - All product containers now run with `securityContext.runAsNonRoot` set to `true` to improve security ([#1070]). +- BREAKING: Remove the `app.kubernetes.io/component` and `app.kubernetes.io/role-group` labels + from the resources they don't apply to (previously set to `none` or the placeholder value + `discovery`). + Server StatefulSets created by older operator versions cannot be updated in place: after the operator + upgrade, delete each server StatefulSet so that the operator immediately recreates it with the new labels ([#1077]). +- Environment variable overrides (`envOverrides`) are now merged into the operator-set + environment variables by name, so an override replaces the operator's value instead of + producing a duplicated entry whose precedence depended on Kubernetes' duplicate-name + handling ([#1077]). ### Fixed @@ -35,6 +44,7 @@ All notable changes to this project will be documented in this file. [#1068]: https://github.com/stackabletech/zookeeper-operator/pull/1068 [#1069]: https://github.com/stackabletech/zookeeper-operator/pull/1069 [#1070]: https://github.com/stackabletech/zookeeper-operator/pull/1070 +[#1077]: https://github.com/stackabletech/zookeeper-operator/pull/1077 ## [26.7.0] - 2026-07-21