diff --git a/Cargo.lock b/Cargo.lock index 34e48bd6..a652fbb1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -368,6 +368,15 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "convert_case" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -1195,6 +1204,15 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.14" @@ -1280,6 +1298,16 @@ dependencies = [ "serde_json", ] +[[package]] +name = "k8s-version" +version = "0.1.2" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-versioned-0.5.1#c07fd5d7237b0774b6bf4c8f3878df3b59b4ec07" +dependencies = [ + "darling", + "regex", + "snafu 0.8.5", +] + [[package]] name = "kube" version = "0.98.0" @@ -2331,16 +2359,27 @@ dependencies = [ ] [[package]] -name = "stackable-zookeeper-crd" -version = "0.0.0-dev" +name = "stackable-versioned" +version = "0.5.1" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-versioned-0.5.1#c07fd5d7237b0774b6bf4c8f3878df3b59b4ec07" dependencies = [ - "serde", - "serde_json", - "serde_yaml", - "snafu 0.8.5", - "stackable-operator", - "strum", - "tracing", + "stackable-versioned-macros", +] + +[[package]] +name = "stackable-versioned-macros" +version = "0.5.1" +source = "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-versioned-0.5.1#c07fd5d7237b0774b6bf4c8f3878df3b59b4ec07" +dependencies = [ + "convert_case", + "darling", + "itertools", + "k8s-openapi", + "k8s-version", + "kube", + "proc-macro2", + "quote", + "syn 2.0.96", ] [[package]] @@ -2358,10 +2397,11 @@ dependencies = [ "product-config", "semver", "serde", + "serde_json", "serde_yaml", "snafu 0.8.5", "stackable-operator", - "stackable-zookeeper-crd", + "stackable-versioned", "strum", "tokio", "tokio-zookeeper", @@ -2810,6 +2850,12 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "unicode-xid" version = "0.2.6" diff --git a/Cargo.nix b/Cargo.nix index 2563ae89..1807e280 100644 --- a/Cargo.nix +++ b/Cargo.nix @@ -32,21 +32,23 @@ rec { # "public" attributes that we attempt to keep stable with new versions of crate2nix. # + rootCrate = rec { + packageId = "stackable-zookeeper-operator"; + # Use this attribute to refer to the derivation building your root crate package. + # You can override the features with rootCrate.build.override { features = [ "default" "feature1" ... ]; }. + build = internal.buildRustCrateWithFeatures { + inherit packageId; + }; + + # Debug support which might change between releases. + # File a bug if you depend on any for non-debug work! + debug = internal.debugCrate { inherit packageId; }; + }; # Refer your crate build derivation by name here. # You can override the features with # workspaceMembers."${crateName}".build.override { features = [ "default" "feature1" ... ]; }. workspaceMembers = { - "stackable-zookeeper-crd" = rec { - packageId = "stackable-zookeeper-crd"; - build = internal.buildRustCrateWithFeatures { - packageId = "stackable-zookeeper-crd"; - }; - - # Debug support which might change between releases. - # File a bug if you depend on any for non-debug work! - debug = internal.debugCrate { inherit packageId; }; - }; "stackable-zookeeper-operator" = rec { packageId = "stackable-zookeeper-operator"; build = internal.buildRustCrateWithFeatures { @@ -1086,6 +1088,25 @@ rec { }; resolvedDefaultFeatures = [ "default" ]; }; + "convert_case" = rec { + crateName = "convert_case"; + version = "0.7.1"; + edition = "2021"; + sha256 = "1rzih8qbd3xh87wp76nkjvnrimn7vlzcwl2n88898ml59j6jnh5v"; + authors = [ + "rutrum " + ]; + dependencies = [ + { + name = "unicode-segmentation"; + packageId = "unicode-segmentation"; + } + ]; + features = { + "rand" = [ "dep:rand" ]; + "random" = [ "rand" ]; + }; + }; "core-foundation 0.10.0" = rec { crateName = "core-foundation"; version = "0.10.0"; @@ -3590,6 +3611,27 @@ rec { }; resolvedDefaultFeatures = [ "default" ]; }; + "itertools" = rec { + crateName = "itertools"; + version = "0.14.0"; + edition = "2018"; + sha256 = "118j6l1vs2mx65dqhwyssbrxpawa90886m3mzafdvyip41w2q69b"; + authors = [ + "bluss" + ]; + dependencies = [ + { + name = "either"; + packageId = "either"; + usesDefaultFeatures = false; + } + ]; + features = { + "default" = [ "use_std" ]; + "use_std" = [ "use_alloc" "either/use_std" ]; + }; + resolvedDefaultFeatures = [ "default" "use_alloc" "use_std" ]; + }; "itoa" = rec { crateName = "itoa"; version = "1.0.14"; @@ -3832,6 +3874,40 @@ rec { }; resolvedDefaultFeatures = [ "schemars" "v1_32" ]; }; + "k8s-version" = rec { + crateName = "k8s-version"; + version = "0.1.2"; + edition = "2021"; + workspace_member = null; + src = pkgs.fetchgit { + url = "https://github.com/stackabletech/operator-rs.git"; + rev = "c07fd5d7237b0774b6bf4c8f3878df3b59b4ec07"; + sha256 = "1l574x35z5ylidbc3qm0crp9qravhyry8i07ww3b1kpcr4w7dd53"; + }; + libName = "k8s_version"; + authors = [ + "Stackable GmbH " + ]; + dependencies = [ + { + name = "darling"; + packageId = "darling"; + optional = true; + } + { + name = "regex"; + packageId = "regex"; + } + { + name = "snafu"; + packageId = "snafu 0.8.5"; + } + ]; + features = { + "darling" = [ "dep:darling" ]; + }; + resolvedDefaultFeatures = [ "darling" ]; + }; "kube" = rec { crateName = "kube"; version = "0.98.0"; @@ -7305,50 +7381,105 @@ rec { ]; }; - "stackable-zookeeper-crd" = rec { - crateName = "stackable-zookeeper-crd"; - version = "0.0.0-dev"; + "stackable-versioned" = rec { + crateName = "stackable-versioned"; + version = "0.5.1"; edition = "2021"; - src = lib.cleanSourceWith { filter = sourceFilter; src = ./rust/crd; }; - libName = "stackable_zookeeper_crd"; + workspace_member = null; + src = pkgs.fetchgit { + url = "https://github.com/stackabletech/operator-rs.git"; + rev = "c07fd5d7237b0774b6bf4c8f3878df3b59b4ec07"; + sha256 = "1l574x35z5ylidbc3qm0crp9qravhyry8i07ww3b1kpcr4w7dd53"; + }; + libName = "stackable_versioned"; authors = [ - "Stackable GmbH " + "Stackable GmbH " ]; dependencies = [ { - name = "serde"; - packageId = "serde"; - features = [ "derive" ]; + name = "stackable-versioned-macros"; + packageId = "stackable-versioned-macros"; } + ]; + features = { + "full" = [ "k8s" ]; + "k8s" = [ "stackable-versioned-macros/k8s" ]; + }; + resolvedDefaultFeatures = [ "k8s" ]; + }; + "stackable-versioned-macros" = rec { + crateName = "stackable-versioned-macros"; + version = "0.5.1"; + edition = "2021"; + workspace_member = null; + src = pkgs.fetchgit { + url = "https://github.com/stackabletech/operator-rs.git"; + rev = "c07fd5d7237b0774b6bf4c8f3878df3b59b4ec07"; + sha256 = "1l574x35z5ylidbc3qm0crp9qravhyry8i07ww3b1kpcr4w7dd53"; + }; + procMacro = true; + libName = "stackable_versioned_macros"; + authors = [ + "Stackable GmbH " + ]; + dependencies = [ { - name = "serde_json"; - packageId = "serde_json"; + name = "convert_case"; + packageId = "convert_case"; } { - name = "snafu"; - packageId = "snafu 0.8.5"; + name = "darling"; + packageId = "darling"; } { - name = "stackable-operator"; - packageId = "stackable-operator"; + name = "itertools"; + packageId = "itertools"; } { - name = "strum"; - packageId = "strum"; - features = [ "derive" ]; + name = "k8s-openapi"; + packageId = "k8s-openapi"; + optional = true; + usesDefaultFeatures = false; + features = [ "schemars" "v1_32" ]; } { - name = "tracing"; - packageId = "tracing"; + name = "k8s-version"; + packageId = "k8s-version"; + features = [ "darling" ]; + } + { + name = "kube"; + packageId = "kube"; + optional = true; + usesDefaultFeatures = false; + features = [ "client" "jsonpatch" "runtime" "derive" "rustls-tls" ]; + } + { + name = "proc-macro2"; + packageId = "proc-macro2"; + } + { + name = "quote"; + packageId = "quote"; + } + { + name = "syn"; + packageId = "syn 2.0.96"; } ]; devDependencies = [ { - name = "serde_yaml"; - packageId = "serde_yaml"; + name = "k8s-openapi"; + packageId = "k8s-openapi"; + usesDefaultFeatures = false; + features = [ "schemars" "v1_32" ]; } ]; - + features = { + "full" = [ "k8s" ]; + "k8s" = [ "dep:kube" "dep:k8s-openapi" ]; + }; + resolvedDefaultFeatures = [ "k8s" ]; }; "stackable-zookeeper-operator" = rec { crateName = "stackable-zookeeper-operator"; @@ -7408,6 +7539,10 @@ rec { packageId = "serde"; features = [ "derive" ]; } + { + name = "serde_json"; + packageId = "serde_json"; + } { name = "snafu"; packageId = "snafu 0.8.5"; @@ -7417,8 +7552,9 @@ rec { packageId = "stackable-operator"; } { - name = "stackable-zookeeper-crd"; - packageId = "stackable-zookeeper-crd"; + name = "stackable-versioned"; + packageId = "stackable-versioned"; + features = [ "k8s" ]; } { name = "strum"; @@ -9008,6 +9144,19 @@ rec { ]; }; + "unicode-segmentation" = rec { + crateName = "unicode-segmentation"; + version = "1.12.0"; + edition = "2018"; + sha256 = "14qla2jfx74yyb9ds3d2mpwpa4l4lzb9z57c6d2ba511458z5k7n"; + libName = "unicode_segmentation"; + authors = [ + "kwantam " + "Manish Goregaokar " + ]; + features = { + }; + }; "unicode-xid" = rec { crateName = "unicode-xid"; version = "0.2.6"; diff --git a/Cargo.toml b/Cargo.toml index a26d2810..c0cb19d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["rust/crd", "rust/operator-binary"] +members = ["rust/operator-binary"] resolver = "2" [workspace.package] @@ -24,6 +24,7 @@ serde_json = "1.0" serde_yaml = "0.9" snafu = "0.8" stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "stackable-operator-0.85.0" } +stackable-versioned = { git = "https://github.com/stackabletech/operator-rs.git", features = ["k8s"], tag = "stackable-versioned-0.5.1" } product-config = { git = "https://github.com/stackabletech/product-config.git", tag = "0.7.0" } strum = { version = "0.26", features = ["derive"] } tokio = { version = "1.40", features = ["full"] } diff --git a/crate-hashes.json b/crate-hashes.json index 290d87f2..a8f33506 100644 --- a/crate-hashes.json +++ b/crate-hashes.json @@ -2,5 +2,8 @@ "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.85.0#stackable-operator-derive@0.3.1": "0rh476rmn5850yj85hq8znwmlfhd7l5bkxz0n5i9m4cddxhi2cl5", "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.85.0#stackable-operator@0.85.0": "0rh476rmn5850yj85hq8znwmlfhd7l5bkxz0n5i9m4cddxhi2cl5", "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-operator-0.85.0#stackable-shared@0.0.1": "0rh476rmn5850yj85hq8znwmlfhd7l5bkxz0n5i9m4cddxhi2cl5", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-versioned-0.5.1#k8s-version@0.1.2": "1l574x35z5ylidbc3qm0crp9qravhyry8i07ww3b1kpcr4w7dd53", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-versioned-0.5.1#stackable-versioned-macros@0.5.1": "1l574x35z5ylidbc3qm0crp9qravhyry8i07ww3b1kpcr4w7dd53", + "git+https://github.com/stackabletech/operator-rs.git?tag=stackable-versioned-0.5.1#stackable-versioned@0.5.1": "1l574x35z5ylidbc3qm0crp9qravhyry8i07ww3b1kpcr4w7dd53", "git+https://github.com/stackabletech/product-config.git?tag=0.7.0#product-config@0.7.0": "0gjsm80g6r75pm3824dcyiz4ysq1ka4c1if6k1mjm9cnd5ym0gny" } \ No newline at end of file diff --git a/rust/crd/Cargo.toml b/rust/crd/Cargo.toml deleted file mode 100644 index a39cd89e..00000000 --- a/rust/crd/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "stackable-zookeeper-crd" -description = "Contains the Apache ZooKeeper CRD structs and utilities" -version.workspace = true -authors.workspace = true -license.workspace = true -edition.workspace = true -repository.workspace = true -publish = false - -[dependencies] -serde.workspace = true -serde_json.workspace = true -snafu.workspace = true -stackable-operator.workspace = true -strum.workspace = true -tracing.workspace = true - -[dev-dependencies] -serde_yaml.workspace = true diff --git a/rust/crd/src/tls.rs b/rust/crd/src/tls.rs deleted file mode 100644 index 01d5a920..00000000 --- a/rust/crd/src/tls.rs +++ /dev/null @@ -1,48 +0,0 @@ -use serde::{Deserialize, Serialize}; -use stackable_operator::schemars::{self, JsonSchema}; - -const TLS_DEFAULT_SECRET_CLASS: &str = "tls"; - -#[derive(Clone, Deserialize, Debug, Eq, JsonSchema, PartialEq, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct ZookeeperTls { - /// The [SecretClass](DOCS_BASE_URL_PLACEHOLDER/secret-operator/secretclass) to use for - /// internal quorum communication. Use mutual verification between Zookeeper Nodes - /// (mandatory). This setting controls: - /// - Which cert the servers should use to authenticate themselves against other servers - /// - Which ca.crt to use when validating the other server - /// - /// Defaults to `tls` - #[serde(default = "quorum_tls_default")] - pub quorum_secret_class: String, - - /// The [SecretClass](DOCS_BASE_URL_PLACEHOLDER/secret-operator/secretclass) to use for - /// client connections. This setting controls: - /// - If TLS encryption is used at all - /// - Which cert the servers should use to authenticate themselves against the client - /// - /// Defaults to `tls`. - #[serde( - default = "server_tls_default", - skip_serializing_if = "Option::is_none" - )] - pub server_secret_class: Option, -} - -/// Default TLS settings. Internal and server communication default to "tls" secret class. -pub fn default_zookeeper_tls() -> Option { - Some(ZookeeperTls { - quorum_secret_class: quorum_tls_default(), - server_secret_class: server_tls_default(), - }) -} - -/// Helper methods to provide defaults in the CRDs and tests -pub fn server_tls_default() -> Option { - Some(TLS_DEFAULT_SECRET_CLASS.into()) -} - -/// Helper methods to provide defaults in the CRDs and tests -pub fn quorum_tls_default() -> String { - TLS_DEFAULT_SECRET_CLASS.into() -} diff --git a/rust/operator-binary/Cargo.toml b/rust/operator-binary/Cargo.toml index d8c85ee5..bb22d926 100644 --- a/rust/operator-binary/Cargo.toml +++ b/rust/operator-binary/Cargo.toml @@ -10,8 +10,6 @@ publish = false build = "build.rs" [dependencies] -stackable-zookeeper-crd = { path = "../crd" } - anyhow.workspace = true clap.workspace = true const_format.workspace = true @@ -22,8 +20,10 @@ pin-project.workspace = true product-config.workspace = true semver.workspace = true serde.workspace = true +serde_json.workspace = true snafu.workspace = true stackable-operator.workspace = true +stackable-versioned.workspace = true strum.workspace = true tokio-zookeeper.workspace = true tokio.workspace = true diff --git a/rust/operator-binary/src/command.rs b/rust/operator-binary/src/command.rs index 09c871f6..00110df2 100644 --- a/rust/operator-binary/src/command.rs +++ b/rust/operator-binary/src/command.rs @@ -1,4 +1,4 @@ -use stackable_zookeeper_crd::{STACKABLE_CONFIG_DIR, STACKABLE_DATA_DIR, STACKABLE_RW_CONFIG_DIR}; +use crate::crd::{STACKABLE_CONFIG_DIR, STACKABLE_DATA_DIR, STACKABLE_RW_CONFIG_DIR}; pub fn create_init_container_command_args() -> Vec { vec![ diff --git a/rust/crd/src/affinity.rs b/rust/operator-binary/src/crd/affinity.rs similarity index 95% rename from rust/crd/src/affinity.rs rename to rust/operator-binary/src/crd/affinity.rs index 1c653d71..97d354ab 100644 --- a/rust/crd/src/affinity.rs +++ b/rust/operator-binary/src/crd/affinity.rs @@ -3,7 +3,7 @@ use stackable_operator::{ k8s_openapi::api::core::v1::PodAntiAffinity, }; -use crate::{ZookeeperRole, APP_NAME}; +use crate::crd::{ZookeeperRole, APP_NAME}; pub fn get_affinity(cluster_name: &str, role: &ZookeeperRole) -> StackableAffinityFragment { let affinity_between_role_pods = @@ -37,7 +37,7 @@ mod tests { role_utils::RoleGroupRef, }; - use crate::{ZookeeperCluster, ZookeeperRole}; + use crate::crd::{affinity::ZookeeperRole, v1alpha1}; #[test] fn test_affinity_defaults() { @@ -60,7 +60,8 @@ mod tests { default: replicas: 3 "#; - let zk: ZookeeperCluster = serde_yaml::from_str(input).expect("illegal test input"); + let zk: v1alpha1::ZookeeperCluster = + serde_yaml::from_str(input).expect("illegal test input"); let rolegroup_ref = RoleGroupRef { cluster: ObjectRef::from_obj(&zk), diff --git a/rust/crd/src/authentication.rs b/rust/operator-binary/src/crd/authentication.rs similarity index 82% rename from rust/crd/src/authentication.rs rename to rust/operator-binary/src/crd/authentication.rs index e96c66b1..12a74d42 100644 --- a/rust/crd/src/authentication.rs +++ b/rust/operator-binary/src/crd/authentication.rs @@ -5,8 +5,9 @@ use stackable_operator::{ commons::authentication::{AuthenticationClass, AuthenticationClassProvider}, schemars::{self, JsonSchema}, }; +use stackable_versioned::versioned; -use crate::ObjectRef; +use crate::crd::ObjectRef; const SUPPORTED_AUTHENTICATION_CLASS: [&str; 1] = ["TLS"]; @@ -29,19 +30,22 @@ pub enum Error { }, } -#[derive(Clone, Deserialize, Debug, Eq, JsonSchema, PartialEq, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct ZookeeperAuthentication { - /// The [AuthenticationClass](https://docs.stackable.tech/home/stable/concepts/authentication) to use. - /// - /// ## mTLS - /// - /// Only affects client connections. This setting controls: - /// - If clients need to authenticate themselves against the server via TLS - /// - Which ca.crt to use when validating the provided client certs - /// - /// This will override the server TLS settings (if set) in `spec.clusterConfig.tls.serverSecretClass`. - pub authentication_class: String, +#[versioned(version(name = "v1alpha1"))] +pub mod versioned { + #[derive(Clone, Deserialize, Debug, Eq, JsonSchema, PartialEq, Serialize)] + #[serde(rename_all = "camelCase")] + pub struct ZookeeperAuthentication { + /// The [AuthenticationClass](https://docs.stackable.tech/home/stable/concepts/authentication) to use. + /// + /// ## mTLS + /// + /// Only affects client connections. This setting controls: + /// - If clients need to authenticate themselves against the server via TLS + /// - Which ca.crt to use when validating the provided client certs + /// + /// This will override the server TLS settings (if set) in `spec.clusterConfig.tls.serverSecretClass`. + pub authentication_class: String, + } } #[derive(Clone, Debug)] @@ -99,7 +103,7 @@ impl ResolvedAuthenticationClasses { /// - Validation failed pub async fn resolve_authentication_classes( client: &Client, - auth_classes: &Vec, + auth_classes: &Vec, ) -> Result { let mut resolved_authentication_classes: Vec = vec![]; diff --git a/rust/crd/src/lib.rs b/rust/operator-binary/src/crd/mod.rs similarity index 63% rename from rust/crd/src/lib.rs rename to rust/operator-binary/src/crd/mod.rs index c87c6342..10787617 100644 --- a/rust/crd/src/lib.rs +++ b/rust/operator-binary/src/crd/mod.rs @@ -33,9 +33,10 @@ use stackable_operator::{ time::Duration, utils::cluster_info::KubernetesClusterInfo, }; +use stackable_versioned::versioned; use strum::{Display, EnumIter, EnumString, IntoEnumIterator}; -use crate::{affinity::get_affinity, authentication::ZookeeperAuthentication, tls::ZookeeperTls}; +use crate::crd::affinity::get_affinity; pub mod affinity; pub mod authentication; @@ -111,161 +112,222 @@ pub enum Error { }, } -/// A ZooKeeper cluster stacklet. This resource is managed by the Stackable operator for Apache ZooKeeper. -/// Find more information on how to use it and the resources that the operator generates in the -/// [operator documentation](DOCS_BASE_URL_PLACEHOLDER/zookeeper/). -#[derive(Clone, CustomResource, Debug, Deserialize, JsonSchema, PartialEq, Serialize)] -#[kube( - group = "zookeeper.stackable.tech", - version = "v1alpha1", - kind = "ZookeeperCluster", - plural = "zookeeperclusters", - shortname = "zk", - status = "ZookeeperClusterStatus", - namespaced, - crates( - kube_core = "stackable_operator::kube::core", - k8s_openapi = "stackable_operator::k8s_openapi", - schemars = "stackable_operator::schemars" - ) -)] -#[serde(rename_all = "camelCase")] -pub struct ZookeeperClusterSpec { - /// Settings that affect all roles and role groups. - /// The settings in the `clusterConfig` are cluster wide settings that do not need to be configurable at role or role group level. - #[serde(default = "cluster_config_default")] - pub cluster_config: ZookeeperClusterConfig, - // no doc - it's in the struct. - #[serde(default)] - pub cluster_operation: ClusterOperation, - // no doc - it's in the struct. - pub image: ProductImage, - // no doc - it's in the struct. - #[serde(skip_serializing_if = "Option::is_none")] - pub servers: Option>, -} - -#[derive(Clone, Deserialize, Debug, Eq, JsonSchema, PartialEq, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct ZookeeperClusterConfig { - /// Authentication settings for ZooKeeper like mTLS authentication. - /// Read more in the [authentication usage guide](DOCS_BASE_URL_PLACEHOLDER/zookeeper/usage_guide/authentication). - #[serde(default)] - pub authentication: Vec, - - /// Name of the Vector aggregator [discovery ConfigMap](DOCS_BASE_URL_PLACEHOLDER/concepts/service_discovery). - /// It must contain the key `ADDRESS` with the address of the Vector aggregator. - /// Follow the [logging tutorial](DOCS_BASE_URL_PLACEHOLDER/tutorials/logging-vector-aggregator) - /// to learn how to configure log aggregation with Vector. - #[serde(skip_serializing_if = "Option::is_none")] - pub vector_aggregator_config_map_name: Option, - - /// TLS encryption settings for ZooKeeper (server, quorum). - /// Read more in the [encryption usage guide](DOCS_BASE_URL_PLACEHOLDER/zookeeper/usage_guide/encryption). - #[serde( - default = "tls::default_zookeeper_tls", - skip_serializing_if = "Option::is_none" - )] - pub tls: Option, - - /// This field controls which type of Service the Operator creates for this ZookeeperCluster: - /// - /// * cluster-internal: Use a ClusterIP service - /// - /// * external-unstable: Use a NodePort service - /// - /// This is a temporary solution with the goal to keep yaml manifests forward compatible. - /// In the future, this setting will control which [ListenerClass](DOCS_BASE_URL_PLACEHOLDER/listener-operator/listenerclass.html) - /// will be used to expose the service, and ListenerClass names will stay the same, allowing for a non-breaking change. - #[serde(default)] - pub listener_class: CurrentlySupportedListenerClasses, +pub enum LoggingFramework { + LOG4J, + LOGBACK, } -fn cluster_config_default() -> ZookeeperClusterConfig { - ZookeeperClusterConfig { - authentication: vec![], - vector_aggregator_config_map_name: None, - tls: tls::default_zookeeper_tls(), - listener_class: CurrentlySupportedListenerClasses::default(), +#[versioned(version(name = "v1alpha1"))] +pub mod versioned { + /// A ZooKeeper cluster stacklet. This resource is managed by the Stackable operator for Apache ZooKeeper. + /// Find more information on how to use it and the resources that the operator generates in the + /// [operator documentation](DOCS_BASE_URL_PLACEHOLDER/zookeeper/). + #[derive(Clone, CustomResource, Debug, Deserialize, JsonSchema, PartialEq, Serialize)] + #[versioned(k8s( + group = "zookeeper.stackable.tech", + kind = "ZookeeperCluster", + plural = "zookeeperclusters", + shortname = "zk", + status = "ZookeeperClusterStatus", + namespaced, + crates( + kube_core = "stackable_operator::kube::core", + k8s_openapi = "stackable_operator::k8s_openapi", + schemars = "stackable_operator::schemars" + ) + ))] + #[serde(rename_all = "camelCase")] + pub struct ZookeeperClusterSpec { + /// Settings that affect all roles and role groups. + /// The settings in the `clusterConfig` are cluster wide settings that do not need to be configurable at role or role group level. + #[serde(default = "cluster_config_default")] + pub cluster_config: ZookeeperClusterConfig, + // no doc - it's in the struct. + #[serde(default)] + pub cluster_operation: ClusterOperation, + // no doc - it's in the struct. + pub image: ProductImage, + // no doc - it's in the struct. + #[serde(skip_serializing_if = "Option::is_none")] + pub servers: Option>, } -} -// TODO: Temporary solution until listener-operator is finished -#[derive(Clone, Debug, Default, Display, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] -#[serde(rename_all = "PascalCase")] -pub enum CurrentlySupportedListenerClasses { - #[default] - #[serde(rename = "cluster-internal")] - ClusterInternal, - #[serde(rename = "external-unstable")] - ExternalUnstable, -} + #[derive(Clone, Deserialize, Debug, Eq, JsonSchema, PartialEq, Serialize)] + #[serde(rename_all = "camelCase")] + pub struct ZookeeperClusterConfig { + /// Authentication settings for ZooKeeper like mTLS authentication. + /// Read more in the [authentication usage guide](DOCS_BASE_URL_PLACEHOLDER/zookeeper/usage_guide/authentication). + #[serde(default)] + pub authentication: Vec, + + /// Name of the Vector aggregator [discovery ConfigMap](DOCS_BASE_URL_PLACEHOLDER/concepts/service_discovery). + /// It must contain the key `ADDRESS` with the address of the Vector aggregator. + /// Follow the [logging tutorial](DOCS_BASE_URL_PLACEHOLDER/tutorials/logging-vector-aggregator) + /// to learn how to configure log aggregation with Vector. + #[serde(skip_serializing_if = "Option::is_none")] + pub vector_aggregator_config_map_name: Option, + + /// TLS encryption settings for ZooKeeper (server, quorum). + /// Read more in the [encryption usage guide](DOCS_BASE_URL_PLACEHOLDER/zookeeper/usage_guide/encryption). + #[serde( + default = "tls::default_zookeeper_tls", + skip_serializing_if = "Option::is_none" + )] + pub tls: Option, + + /// This field controls which type of Service the Operator creates for this ZookeeperCluster: + /// + /// * cluster-internal: Use a ClusterIP service + /// + /// * external-unstable: Use a NodePort service + /// + /// This is a temporary solution with the goal to keep yaml manifests forward compatible. + /// In the future, this setting will control which [ListenerClass](DOCS_BASE_URL_PLACEHOLDER/listener-operator/listenerclass.html) + /// will be used to expose the service, and ListenerClass names will stay the same, allowing for a non-breaking change. + #[serde(default)] + pub listener_class: CurrentlySupportedListenerClasses, + } -impl CurrentlySupportedListenerClasses { - pub fn k8s_service_type(&self) -> String { - match self { - CurrentlySupportedListenerClasses::ClusterInternal => "ClusterIP".to_string(), - CurrentlySupportedListenerClasses::ExternalUnstable => "NodePort".to_string(), - } + // TODO: Temporary solution until listener-operator is finished + #[derive(Clone, Debug, Default, Display, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] + #[serde(rename_all = "PascalCase")] + pub enum CurrentlySupportedListenerClasses { + #[default] + #[serde(rename = "cluster-internal")] + ClusterInternal, + #[serde(rename = "external-unstable")] + ExternalUnstable, } -} -#[derive(Clone, Debug, Default, Fragment, JsonSchema, PartialEq)] -#[fragment_attrs( - derive( - Clone, - Debug, - Default, - Deserialize, - Merge, - JsonSchema, - PartialEq, - Serialize - ), - serde(rename_all = "camelCase") -)] -pub struct ZookeeperConfig { - pub init_limit: Option, - pub sync_limit: Option, - pub tick_time: Option, - pub myid_offset: u16, + #[derive(Clone, Debug, Default, Fragment, JsonSchema, PartialEq)] + #[fragment_attrs( + derive( + Clone, + Debug, + Default, + Deserialize, + Merge, + JsonSchema, + PartialEq, + Serialize + ), + serde(rename_all = "camelCase") + )] + pub struct ZookeeperConfig { + pub init_limit: Option, + pub sync_limit: Option, + pub tick_time: Option, + pub myid_offset: u16, - #[fragment_attrs(serde(default))] - pub resources: Resources, + #[fragment_attrs(serde(default))] + pub resources: Resources, - #[fragment_attrs(serde(default))] - pub logging: Logging, + #[fragment_attrs(serde(default))] + pub logging: Logging, - #[fragment_attrs(serde(default))] - pub affinity: StackableAffinity, + #[fragment_attrs(serde(default))] + pub affinity: StackableAffinity, - /// Time period Pods have to gracefully shut down, e.g. `30m`, `1h` or `2d`. Consult the operator documentation for details. - #[fragment_attrs(serde(default))] - pub graceful_shutdown_timeout: Option, + /// Time period Pods have to gracefully shut down, e.g. `30m`, `1h` or `2d`. Consult the operator documentation for details. + #[fragment_attrs(serde(default))] + pub graceful_shutdown_timeout: Option, - /// Request secret (currently only autoTls certificates) lifetime from the secret operator, e.g. `7d`, or `30d`. - /// This can be shortened by the `maxCertificateLifetime` setting on the SecretClass issuing the TLS certificate. - #[fragment_attrs(serde(default))] - pub requested_secret_lifetime: Option, -} + /// Request secret (currently only autoTls certificates) lifetime from the secret operator, e.g. `7d`, or `30d`. + /// This can be shortened by the `maxCertificateLifetime` setting on the SecretClass issuing the TLS certificate. + #[fragment_attrs(serde(default))] + pub requested_secret_lifetime: Option, + } -#[derive(Clone, Debug, Default, JsonSchema, PartialEq, Fragment)] -#[fragment_attrs( - derive( + #[derive(Clone, Debug, Default, JsonSchema, PartialEq, Fragment)] + #[fragment_attrs( + derive( + Clone, + Debug, + Default, + Deserialize, + Merge, + JsonSchema, + PartialEq, + Serialize + ), + serde(rename_all = "camelCase") + )] + pub struct ZookeeperStorageConfig { + #[fragment_attrs(serde(default))] + pub data: PvcConfig, + } + + #[derive( Clone, Debug, - Default, Deserialize, - Merge, + Display, + Eq, + EnumIter, JsonSchema, + Ord, PartialEq, - Serialize - ), - serde(rename_all = "camelCase") -)] -pub struct ZookeeperStorageConfig { - #[fragment_attrs(serde(default))] - pub data: PvcConfig, + PartialOrd, + Serialize, + )] + #[serde(rename_all = "camelCase")] + pub enum Container { + Prepare, + Vector, + Zookeeper, + } + + #[derive(Clone, Default, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] + #[serde(rename_all = "camelCase")] + pub struct ZookeeperClusterStatus { + /// An opaque value that changes every time a discovery detail does + #[serde(default, skip_serializing_if = "Option::is_none")] + pub discovery_hash: Option, + #[serde(default)] + pub conditions: Vec, + } + + /// A claim for a single ZooKeeper ZNode tree (filesystem node). + /// + /// A ConfigMap will automatically be created with the same name, containing the connection string in the field `ZOOKEEPER`. + /// Each ZookeeperZnode gets an isolated ZNode chroot, which the `ZOOKEEPER` automatically contains. + /// All data inside of this chroot will be deleted when the corresponding `ZookeeperZnode` is. + /// + /// `ZookeeperZnode` is *not* designed to manage the contents of this ZNode. Instead, it should be used to create a chroot + /// for an installation of an application to work inside. Initializing the contents is the responsibility of the application. + /// + /// You can learn more about this in the + /// [Isolating clients with ZNodes usage guide](DOCS_BASE_URL_PLACEHOLDER/zookeeper/usage_guide/isolating_clients_with_znodes). + #[derive(Clone, CustomResource, Debug, Deserialize, JsonSchema, PartialEq, Serialize)] + #[versioned(k8s( + group = "zookeeper.stackable.tech", + kind = "ZookeeperZnode", + plural = "zookeeperznodes", + shortname = "zno", + shortname = "znode", + status = "ZookeeperZnodeStatus", + namespaced, + crates( + kube_core = "stackable_operator::kube::core", + k8s_openapi = "stackable_operator::k8s_openapi", + schemars = "stackable_operator::schemars" + ) + ))] + #[serde(rename_all = "camelCase")] + pub struct ZookeeperZnodeSpec { + /// The reference to the ZookeeperCluster that this ZNode belongs to. + #[serde(default)] + pub cluster_ref: ClusterRef, + } + + #[derive(Clone, Default, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] + #[serde(rename_all = "camelCase")] + pub struct ZookeeperZnodeStatus { + /// The absolute ZNode allocated to the ZookeeperZnode. This will typically be set by the operator. + /// + /// This can be set explicitly by an administrator, such as when restoring from a backup. + pub znode_path: Option, + } } #[derive( @@ -273,22 +335,49 @@ pub struct ZookeeperStorageConfig { Debug, Deserialize, Display, - Eq, EnumIter, + Eq, + Hash, JsonSchema, - Ord, PartialEq, - PartialOrd, Serialize, + EnumString, )] -#[serde(rename_all = "camelCase")] -pub enum Container { - Prepare, - Vector, - Zookeeper, +#[strum(serialize_all = "camelCase")] +pub enum ZookeeperRole { + #[strum(serialize = "server")] + Server, } -impl ZookeeperConfig { +/// Reference to a single `Pod` that is a component of a [`v1alpha1::ZookeeperCluster`] +/// +/// Used for service discovery. +pub struct ZookeeperPodRef { + pub namespace: String, + pub role_group_service_name: String, + pub pod_name: String, + pub zookeeper_myid: u16, +} + +fn cluster_config_default() -> v1alpha1::ZookeeperClusterConfig { + v1alpha1::ZookeeperClusterConfig { + authentication: vec![], + vector_aggregator_config_map_name: None, + tls: tls::default_zookeeper_tls(), + listener_class: v1alpha1::CurrentlySupportedListenerClasses::default(), + } +} + +impl v1alpha1::CurrentlySupportedListenerClasses { + pub fn k8s_service_type(&self) -> String { + match self { + v1alpha1::CurrentlySupportedListenerClasses::ClusterInternal => "ClusterIP".to_string(), + v1alpha1::CurrentlySupportedListenerClasses::ExternalUnstable => "NodePort".to_string(), + } + } +} + +impl v1alpha1::ZookeeperConfig { pub const INIT_LIMIT: &'static str = "initLimit"; pub const SYNC_LIMIT: &'static str = "syncLimit"; pub const TICK_TIME: &'static str = "tickTime"; @@ -300,8 +389,11 @@ impl ZookeeperConfig { const DEFAULT_SECRET_LIFETIME: Duration = Duration::from_days_unchecked(1); - fn default_server_config(cluster_name: &str, role: &ZookeeperRole) -> ZookeeperConfigFragment { - ZookeeperConfigFragment { + fn default_server_config( + cluster_name: &str, + role: &ZookeeperRole, + ) -> v1alpha1::ZookeeperConfigFragment { + v1alpha1::ZookeeperConfigFragment { init_limit: None, sync_limit: None, tick_time: None, @@ -315,7 +407,7 @@ impl ZookeeperConfig { limit: Some(Quantity("512Mi".to_owned())), runtime_limits: NoRuntimeLimitsFragment {}, }, - storage: ZookeeperStorageConfigFragment { + storage: v1alpha1::ZookeeperStorageConfigFragment { data: PvcConfigFragment { capacity: Some(Quantity("1Gi".to_owned())), storage_class: None, @@ -331,8 +423,8 @@ impl ZookeeperConfig { } } -impl Configuration for ZookeeperConfigFragment { - type Configurable = ZookeeperCluster; +impl Configuration for v1alpha1::ZookeeperConfigFragment { + type Configurable = v1alpha1::ZookeeperCluster; fn compute_env( &self, @@ -352,9 +444,9 @@ impl Configuration for ZookeeperConfigFragment { Ok([ ( - ZookeeperConfig::MYID_OFFSET.to_string(), + v1alpha1::ZookeeperConfig::MYID_OFFSET.to_string(), self.myid_offset - .or(ZookeeperConfig::default_server_config( + .or(v1alpha1::ZookeeperConfig::default_server_config( &resource.name_any(), &ZookeeperRole::Server, ) @@ -362,7 +454,7 @@ impl Configuration for ZookeeperConfigFragment { .map(|myid_offset| myid_offset.to_string()), ), ( - ZookeeperConfig::SERVER_JVMFLAGS.to_string(), + v1alpha1::ZookeeperConfig::SERVER_JVMFLAGS.to_string(), Some(jvm_flags), ), ] @@ -387,24 +479,24 @@ impl Configuration for ZookeeperConfigFragment { if file == ZOOKEEPER_PROPERTIES_FILE { if let Some(init_limit) = self.init_limit { result.insert( - ZookeeperConfig::INIT_LIMIT.to_string(), + v1alpha1::ZookeeperConfig::INIT_LIMIT.to_string(), Some(init_limit.to_string()), ); } if let Some(sync_limit) = self.sync_limit { result.insert( - ZookeeperConfig::SYNC_LIMIT.to_string(), + v1alpha1::ZookeeperConfig::SYNC_LIMIT.to_string(), Some(sync_limit.to_string()), ); } if let Some(tick_time) = self.tick_time { result.insert( - ZookeeperConfig::TICK_TIME.to_string(), + v1alpha1::ZookeeperConfig::TICK_TIME.to_string(), Some(tick_time.to_string()), ); } result.insert( - ZookeeperConfig::DATA_DIR.to_string(), + v1alpha1::ZookeeperConfig::DATA_DIR.to_string(), Some(STACKABLE_DATA_DIR.to_string()), ); } @@ -413,25 +505,6 @@ impl Configuration for ZookeeperConfigFragment { } } -#[derive( - Clone, - Debug, - Deserialize, - Display, - EnumIter, - Eq, - Hash, - JsonSchema, - PartialEq, - Serialize, - EnumString, -)] -#[strum(serialize_all = "camelCase")] -pub enum ZookeeperRole { - #[strum(serialize = "server")] - Server, -} - impl ZookeeperRole { pub fn roles() -> Vec { let mut roles = vec![]; @@ -442,17 +515,7 @@ impl ZookeeperRole { } } -#[derive(Clone, Default, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct ZookeeperClusterStatus { - /// An opaque value that changes every time a discovery detail does - #[serde(default, skip_serializing_if = "Option::is_none")] - pub discovery_hash: Option, - #[serde(default)] - pub conditions: Vec, -} - -impl HasStatusCondition for ZookeeperCluster { +impl HasStatusCondition for v1alpha1::ZookeeperCluster { fn conditions(&self) -> Vec { match &self.status { Some(status) => status.conditions.clone(), @@ -461,17 +524,27 @@ impl HasStatusCondition for ZookeeperCluster { } } -pub enum LoggingFramework { - LOG4J, - LOGBACK, +impl ZookeeperPodRef { + pub fn fqdn(&self, cluster_info: &KubernetesClusterInfo) -> String { + format!( + "{pod_name}.{service_name}.{namespace}.svc.{cluster_domain}", + pod_name = self.pod_name, + service_name = self.role_group_service_name, + namespace = self.namespace, + cluster_domain = cluster_info.cluster_domain + ) + } } -impl ZookeeperCluster { +impl v1alpha1::ZookeeperCluster { pub fn logging_framework(&self) -> LoggingFramework { let version = self .spec .image - .resolve(DOCKER_IMAGE_BASE_NAME, crate::built_info::CARGO_PKG_VERSION) + .resolve( + DOCKER_IMAGE_BASE_NAME, + crate::crd::built_info::CARGO_PKG_VERSION, + ) .product_version; let zookeeper_versions_with_log4j = [ "1.", "2.", "3.0.", "3.1.", "3.2.", "3.3.", "3.4.", "3.5.", "3.6.", "3.7.", @@ -506,7 +579,7 @@ impl ZookeeperCluster { pub fn role( &self, role_variant: &ZookeeperRole, - ) -> Result<&Role, Error> { + ) -> Result<&Role, Error> { match role_variant { ZookeeperRole::Server => self.spec.servers.as_ref(), } @@ -518,8 +591,11 @@ impl ZookeeperCluster { /// Returns a reference to the role group. Raises an error if the role or role group are not defined. pub fn rolegroup( &self, - rolegroup_ref: &RoleGroupRef, - ) -> Result, Error> { + rolegroup_ref: &RoleGroupRef, + ) -> Result< + RoleGroup, + Error, + > { let role_variant = ZookeeperRole::from_str(&rolegroup_ref.role).with_context(|_| { UnknownZookeeperRoleSnafu { role: rolegroup_ref.role.to_owned(), @@ -539,7 +615,7 @@ impl ZookeeperCluster { pub fn server_rolegroup_ref( &self, group_name: impl Into, - ) -> RoleGroupRef { + ) -> RoleGroupRef { RoleGroupRef { cluster: ObjectRef::from_obj(self), role: ZookeeperRole::Server.to_string(), @@ -592,9 +668,10 @@ impl ZookeeperCluster { &self, role: &ZookeeperRole, rolegroup_ref: &RoleGroupRef, - ) -> Result { + ) -> Result { // Initialize the result with all default values as baseline - let conf_defaults = ZookeeperConfig::default_server_config(&self.name_any(), role); + let conf_defaults = + v1alpha1::ZookeeperConfig::default_server_config(&self.name_any(), role); // Retrieve role resource config let role = self.role(role)?; @@ -619,7 +696,7 @@ impl ZookeeperCluster { &self, role: &ZookeeperRole, rolegroup_ref: &RoleGroupRef, - ) -> Result, Error> { + ) -> Result, Error> { let config = self.merged_config(role, rolegroup_ref)?; Ok(config.logging) } @@ -633,10 +710,11 @@ impl ZookeeperCluster { pub fn resources( &self, role: &ZookeeperRole, - rolegroup_ref: &RoleGroupRef, + rolegroup_ref: &RoleGroupRef, ) -> Result<(Vec, ResourceRequirements), Error> { let config = self.merged_config(role, rolegroup_ref)?; - let resources: Resources = config.resources; + let resources: Resources = + config.resources; let data_pvc = resources .storage @@ -667,76 +745,11 @@ impl ZookeeperCluster { } } -/// Reference to a single `Pod` that is a component of a [`ZookeeperCluster`] -/// -/// Used for service discovery. -pub struct ZookeeperPodRef { - pub namespace: String, - pub role_group_service_name: String, - pub pod_name: String, - pub zookeeper_myid: u16, -} - -impl ZookeeperPodRef { - pub fn fqdn(&self, cluster_info: &KubernetesClusterInfo) -> String { - format!( - "{pod_name}.{service_name}.{namespace}.svc.{cluster_domain}", - pod_name = self.pod_name, - service_name = self.role_group_service_name, - namespace = self.namespace, - cluster_domain = cluster_info.cluster_domain - ) - } -} - -/// A claim for a single ZooKeeper ZNode tree (filesystem node). -/// -/// A ConfigMap will automatically be created with the same name, containing the connection string in the field `ZOOKEEPER`. -/// Each ZookeeperZnode gets an isolated ZNode chroot, which the `ZOOKEEPER` automatically contains. -/// All data inside of this chroot will be deleted when the corresponding `ZookeeperZnode` is. -/// -/// `ZookeeperZnode` is *not* designed to manage the contents of this ZNode. Instead, it should be used to create a chroot -/// for an installation of an application to work inside. Initializing the contents is the responsibility of the application. -/// -/// You can learn more about this in the -/// [Isolating clients with ZNodes usage guide](DOCS_BASE_URL_PLACEHOLDER/zookeeper/usage_guide/isolating_clients_with_znodes). -#[derive(Clone, CustomResource, Debug, Deserialize, JsonSchema, PartialEq, Serialize)] -#[kube( - group = "zookeeper.stackable.tech", - version = "v1alpha1", - kind = "ZookeeperZnode", - plural = "zookeeperznodes", - shortname = "zno", - shortname = "znode", - status = "ZookeeperZnodeStatus", - namespaced, - crates( - kube_core = "stackable_operator::kube::core", - k8s_openapi = "stackable_operator::k8s_openapi", - schemars = "stackable_operator::schemars" - ) -)] -#[serde(rename_all = "camelCase")] -pub struct ZookeeperZnodeSpec { - /// The reference to the ZookeeperCluster that this ZNode belongs to. - #[serde(default)] - pub cluster_ref: ClusterRef, -} - -#[derive(Clone, Default, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct ZookeeperZnodeStatus { - /// The absolute ZNode allocated to the ZookeeperZnode. This will typically be set by the operator. - /// - /// This can be set explicitly by an administrator, such as when restoring from a backup. - pub znode_path: Option, -} - #[cfg(test)] mod tests { use super::*; - fn get_server_secret_class(zk: &ZookeeperCluster) -> Option<&str> { + fn get_server_secret_class(zk: &v1alpha1::ZookeeperCluster) -> Option<&str> { zk.spec .cluster_config .tls @@ -744,7 +757,7 @@ mod tests { .and_then(|tls| tls.server_secret_class.as_deref()) } - fn get_quorum_secret_class(zk: &ZookeeperCluster) -> &str { + fn get_quorum_secret_class(zk: &v1alpha1::ZookeeperCluster) -> &str { zk.spec .cluster_config .tls @@ -765,7 +778,8 @@ mod tests { image: productVersion: "3.9.2" "#; - let zookeeper: ZookeeperCluster = serde_yaml::from_str(input).expect("illegal test input"); + let zookeeper: v1alpha1::ZookeeperCluster = + serde_yaml::from_str(input).expect("illegal test input"); assert_eq!( get_server_secret_class(&zookeeper), tls::server_tls_default().as_deref() @@ -787,7 +801,8 @@ mod tests { tls: serverSecretClass: simple-zookeeper-client-tls "#; - let zookeeper: ZookeeperCluster = serde_yaml::from_str(input).expect("illegal test input"); + let zookeeper: v1alpha1::ZookeeperCluster = + serde_yaml::from_str(input).expect("illegal test input"); assert_eq!( get_server_secret_class(&zookeeper), @@ -810,7 +825,8 @@ mod tests { tls: serverSecretClass: null "#; - let zookeeper: ZookeeperCluster = serde_yaml::from_str(input).expect("illegal test input"); + let zookeeper: v1alpha1::ZookeeperCluster = + serde_yaml::from_str(input).expect("illegal test input"); assert_eq!(get_server_secret_class(&zookeeper), None); assert_eq!( get_quorum_secret_class(&zookeeper), @@ -829,7 +845,8 @@ mod tests { tls: quorumSecretClass: simple-zookeeper-quorum-tls "#; - let zookeeper: ZookeeperCluster = serde_yaml::from_str(input).expect("illegal test input"); + let zookeeper: v1alpha1::ZookeeperCluster = + serde_yaml::from_str(input).expect("illegal test input"); assert_eq!( get_server_secret_class(&zookeeper), tls::server_tls_default().as_deref() @@ -851,7 +868,8 @@ mod tests { image: productVersion: "3.9.2" "#; - let zookeeper: ZookeeperCluster = serde_yaml::from_str(input).expect("illegal test input"); + let zookeeper: v1alpha1::ZookeeperCluster = + serde_yaml::from_str(input).expect("illegal test input"); assert_eq!( get_server_secret_class(&zookeeper), @@ -874,7 +892,8 @@ mod tests { tls: quorumSecretClass: simple-zookeeper-quorum-tls "#; - let zookeeper: ZookeeperCluster = serde_yaml::from_str(input).expect("illegal test input"); + let zookeeper: v1alpha1::ZookeeperCluster = + serde_yaml::from_str(input).expect("illegal test input"); assert_eq!( get_server_secret_class(&zookeeper), tls::server_tls_default().as_deref() @@ -896,7 +915,8 @@ mod tests { tls: serverSecretClass: simple-zookeeper-server-tls "#; - let zookeeper: ZookeeperCluster = serde_yaml::from_str(input).expect("illegal test input"); + let zookeeper: v1alpha1::ZookeeperCluster = + serde_yaml::from_str(input).expect("illegal test input"); assert_eq!( get_server_secret_class(&zookeeper), Some("simple-zookeeper-server-tls") diff --git a/rust/crd/src/security.rs b/rust/operator-binary/src/crd/security.rs similarity index 98% rename from rust/crd/src/security.rs rename to rust/operator-binary/src/crd/security.rs index 88752135..9524b7a9 100644 --- a/rust/crd/src/security.rs +++ b/rust/operator-binary/src/crd/security.rs @@ -25,7 +25,7 @@ use stackable_operator::{ time::Duration, }; -use crate::{authentication, authentication::ResolvedAuthenticationClasses, tls, ZookeeperCluster}; +use crate::crd::{authentication, authentication::ResolvedAuthenticationClasses, tls, v1alpha1}; type Result = std::result::Result; @@ -93,7 +93,7 @@ impl ZookeeperSecurity { /// all provided `AuthenticationClass` references. pub async fn new_from_zookeeper_cluster( client: &Client, - zk: &ZookeeperCluster, + zk: &v1alpha1::ZookeeperCluster, ) -> Result { Ok(ZookeeperSecurity { resolved_authentication_classes: authentication::resolve_authentication_classes( diff --git a/rust/operator-binary/src/crd/tls.rs b/rust/operator-binary/src/crd/tls.rs new file mode 100644 index 00000000..50e769fe --- /dev/null +++ b/rust/operator-binary/src/crd/tls.rs @@ -0,0 +1,52 @@ +use serde::{Deserialize, Serialize}; +use stackable_operator::schemars::{self, JsonSchema}; +use stackable_versioned::versioned; + +const TLS_DEFAULT_SECRET_CLASS: &str = "tls"; + +#[versioned(version(name = "v1alpha1"))] +pub mod versioned { + #[derive(Clone, Deserialize, Debug, Eq, JsonSchema, PartialEq, Serialize)] + #[serde(rename_all = "camelCase")] + pub struct ZookeeperTls { + /// The [SecretClass](DOCS_BASE_URL_PLACEHOLDER/secret-operator/secretclass) to use for + /// internal quorum communication. Use mutual verification between Zookeeper Nodes + /// (mandatory). This setting controls: + /// - Which cert the servers should use to authenticate themselves against other servers + /// - Which ca.crt to use when validating the other server + /// + /// Defaults to `tls` + #[serde(default = "quorum_tls_default")] + pub quorum_secret_class: String, + + /// The [SecretClass](DOCS_BASE_URL_PLACEHOLDER/secret-operator/secretclass) to use for + /// client connections. This setting controls: + /// - If TLS encryption is used at all + /// - Which cert the servers should use to authenticate themselves against the client + /// + /// Defaults to `tls`. + #[serde( + default = "server_tls_default", + skip_serializing_if = "Option::is_none" + )] + pub server_secret_class: Option, + } +} + +/// Default TLS settings. Internal and server communication default to "tls" secret class. +pub fn default_zookeeper_tls() -> Option { + Some(v1alpha1::ZookeeperTls { + quorum_secret_class: quorum_tls_default(), + server_secret_class: server_tls_default(), + }) +} + +/// Helper methods to provide defaults in the CRDs and tests +pub fn server_tls_default() -> Option { + Some(TLS_DEFAULT_SECRET_CLASS.into()) +} + +/// Helper methods to provide defaults in the CRDs and tests +pub fn quorum_tls_default() -> String { + TLS_DEFAULT_SECRET_CLASS.into() +} diff --git a/rust/operator-binary/src/discovery.rs b/rust/operator-binary/src/discovery.rs index 14671b66..4f2279a2 100644 --- a/rust/operator-binary/src/discovery.rs +++ b/rust/operator-binary/src/discovery.rs @@ -8,9 +8,11 @@ use stackable_operator::{ kube::{runtime::reflector::ObjectRef, Resource, ResourceExt}, utils::cluster_info::KubernetesClusterInfo, }; -use stackable_zookeeper_crd::{security::ZookeeperSecurity, ZookeeperCluster, ZookeeperRole}; -use crate::utils::build_recommended_labels; +use crate::{ + crd::{security::ZookeeperSecurity, v1alpha1, ZookeeperRole}, + utils::build_recommended_labels, +}; type Result = std::result::Result; @@ -19,7 +21,7 @@ pub enum Error { #[snafu(display("object {} is missing metadata to build owner reference", zk))] ObjectMissingMetadataForOwnerRef { source: stackable_operator::builder::meta::Error, - zk: ObjectRef, + zk: ObjectRef, }, #[snafu(display("chroot path {} was relative (must be absolute)", chroot))] @@ -32,9 +34,7 @@ pub enum Error { NoNamespace, #[snafu(display("failed to list expected pods"))] - ExpectedPods { - source: stackable_zookeeper_crd::Error, - }, + ExpectedPods { source: crate::crd::Error }, #[snafu(display("could not find service port with name {}", port_name))] NoServicePort { port_name: String }, @@ -62,10 +62,10 @@ pub enum Error { }, } -/// Builds discovery [`ConfigMap`]s for connecting to a [`ZookeeperCluster`] for all expected scenarios +/// Builds discovery [`ConfigMap`]s for connecting to a [`v1alpha1::ZookeeperCluster`] for all expected scenarios #[allow(clippy::too_many_arguments)] pub async fn build_discovery_configmaps( - zk: &ZookeeperCluster, + zk: &v1alpha1::ZookeeperCluster, owner: &impl Resource, client: &stackable_operator::client::Client, controller_name: &str, @@ -89,7 +89,7 @@ pub async fn build_discovery_configmaps( resolved_product_image, )?]; if zk.spec.cluster_config.listener_class - == stackable_zookeeper_crd::CurrentlySupportedListenerClasses::ExternalUnstable + == crate::crd::v1alpha1::CurrentlySupportedListenerClasses::ExternalUnstable { discovery_configmaps.push(build_discovery_configmap( zk, @@ -107,12 +107,12 @@ pub async fn build_discovery_configmaps( Ok(discovery_configmaps) } -/// Build a discovery [`ConfigMap`] containing information about how to connect to a certain [`ZookeeperCluster`] +/// Build a discovery [`ConfigMap`] containing information about how to connect to a certain [`v1alpha1::ZookeeperCluster`] /// /// `hosts` will usually come from either [`pod_hosts`] or [`nodeport_hosts`]. #[allow(clippy::too_many_arguments)] fn build_discovery_configmap( - zk: &ZookeeperCluster, + zk: &v1alpha1::ZookeeperCluster, owner: &impl Resource, zookeeper_security: &ZookeeperSecurity, name: &str, @@ -168,9 +168,9 @@ fn build_discovery_configmap( .context(BuildConfigMapSnafu) } -/// Lists all Pods FQDNs expected to host the [`ZookeeperCluster`] +/// Lists all Pods FQDNs expected to host the [`v1alpha1::ZookeeperCluster`] fn pod_hosts<'a>( - zk: &'a ZookeeperCluster, + zk: &'a v1alpha1::ZookeeperCluster, zookeeper_security: &'a ZookeeperSecurity, cluster_info: &'a KubernetesClusterInfo, ) -> Result + 'a> { diff --git a/rust/operator-binary/src/main.rs b/rust/operator-binary/src/main.rs index 57d59559..23f9af50 100644 --- a/rust/operator-binary/src/main.rs +++ b/rust/operator-binary/src/main.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use clap::{crate_description, crate_version, Parser}; +use crd::{v1alpha1, ZookeeperCluster, ZookeeperZnode, APP_NAME, OPERATOR_NAME}; use futures::{pin_mut, StreamExt}; use stackable_operator::{ cli::{Command, ProductOperatorRun}, @@ -18,13 +19,14 @@ use stackable_operator::{ Resource, }, logging::controller::report_controller_reconciled, - CustomResourceExt, + shared::yaml::SerializeOptions, + YamlSchema, }; -use stackable_zookeeper_crd::{ZookeeperCluster, ZookeeperZnode, APP_NAME, OPERATOR_NAME}; use crate::{zk_controller::ZK_FULL_CONTROLLER_NAME, znode_controller::ZNODE_FULL_CONTROLLER_NAME}; mod command; +pub mod crd; mod discovery; mod operations; mod product_logging; @@ -50,8 +52,10 @@ async fn main() -> anyhow::Result<()> { let opts = Opts::parse(); match opts.cmd { Command::Crd => { - ZookeeperCluster::print_yaml_schema(built_info::CARGO_PKG_VERSION)?; - ZookeeperZnode::print_yaml_schema(built_info::CARGO_PKG_VERSION)?; + ZookeeperCluster::merged_crd(ZookeeperCluster::V1Alpha1)? + .print_yaml_schema(built_info::CARGO_PKG_VERSION, SerializeOptions::default())?; + ZookeeperZnode::merged_crd(ZookeeperZnode::V1Alpha1)? + .print_yaml_schema(built_info::CARGO_PKG_VERSION, SerializeOptions::default())?; } Command::Run(ProductOperatorRun { product_config, @@ -83,7 +87,7 @@ async fn main() -> anyhow::Result<()> { .await?; let zk_controller = Controller::new( - watch_namespace.get_api::>(&client), + watch_namespace.get_api::>(&client), watcher::Config::default(), ); @@ -154,7 +158,7 @@ async fn main() -> anyhow::Result<()> { ); let znode_controller = Controller::new( - watch_namespace.get_api::>(&client), + watch_namespace.get_api::>(&client), watcher::Config::default(), ); let znode_event_recorder = Arc::new(Recorder::new( @@ -172,7 +176,8 @@ async fn main() -> anyhow::Result<()> { watcher::Config::default(), ) .watches( - watch_namespace.get_api::>(&client), + watch_namespace + .get_api::>(&client), watcher::Config::default(), move |zk| { znode_store diff --git a/rust/operator-binary/src/operations/graceful_shutdown.rs b/rust/operator-binary/src/operations/graceful_shutdown.rs index cdbac3e9..77a7702b 100644 --- a/rust/operator-binary/src/operations/graceful_shutdown.rs +++ b/rust/operator-binary/src/operations/graceful_shutdown.rs @@ -1,6 +1,7 @@ use snafu::{ResultExt, Snafu}; use stackable_operator::builder::pod::PodBuilder; -use stackable_zookeeper_crd::ZookeeperConfig; + +use crate::crd::v1alpha1; #[derive(Debug, Snafu)] pub enum Error { @@ -11,7 +12,7 @@ pub enum Error { } pub fn add_graceful_shutdown_config( - merged_config: &ZookeeperConfig, + merged_config: &v1alpha1::ZookeeperConfig, pod_builder: &mut PodBuilder, ) -> Result<(), Error> { // This must be always set by the merge mechanism, as we provide a default value, diff --git a/rust/operator-binary/src/operations/pdb.rs b/rust/operator-binary/src/operations/pdb.rs index e2ca47cb..908d80c0 100644 --- a/rust/operator-binary/src/operations/pdb.rs +++ b/rust/operator-binary/src/operations/pdb.rs @@ -3,9 +3,11 @@ use stackable_operator::{ builder::pdb::PodDisruptionBudgetBuilder, client::Client, cluster_resources::ClusterResources, commons::pdb::PdbConfig, kube::ResourceExt, }; -use stackable_zookeeper_crd::{ZookeeperCluster, ZookeeperRole, APP_NAME, OPERATOR_NAME}; -use crate::zk_controller::ZK_CONTROLLER_NAME; +use crate::{ + crd::{v1alpha1, ZookeeperRole, APP_NAME, OPERATOR_NAME}, + zk_controller::ZK_CONTROLLER_NAME, +}; #[derive(Snafu, Debug)] pub enum Error { @@ -23,7 +25,7 @@ pub enum Error { pub async fn add_pdbs( pdb: &PdbConfig, - zookeeper: &ZookeeperCluster, + zookeeper: &v1alpha1::ZookeeperCluster, role: &ZookeeperRole, client: &Client, cluster_resources: &mut ClusterResources, diff --git a/rust/operator-binary/src/product_logging.rs b/rust/operator-binary/src/product_logging.rs index 03cce7ac..65d18965 100644 --- a/rust/operator-binary/src/product_logging.rs +++ b/rust/operator-binary/src/product_logging.rs @@ -11,9 +11,10 @@ use stackable_operator::{ }, role_utils::RoleGroupRef, }; -use stackable_zookeeper_crd::{ - Container, LoggingFramework, ZookeeperCluster, ZookeeperRole, LOG4J_CONFIG_FILE, - LOGBACK_CONFIG_FILE, MAX_ZK_LOG_FILES_SIZE, STACKABLE_LOG_DIR, ZOOKEEPER_LOG_FILE, + +use crate::crd::{ + v1alpha1, LoggingFramework, ZookeeperRole, LOG4J_CONFIG_FILE, LOGBACK_CONFIG_FILE, + MAX_ZK_LOG_FILES_SIZE, STACKABLE_LOG_DIR, ZOOKEEPER_LOG_FILE, }; #[derive(Snafu, Debug)] @@ -34,9 +35,7 @@ pub enum Error { }, #[snafu(display("crd validation failure"))] - CrdValidationFailure { - source: stackable_zookeeper_crd::Error, - }, + CrdValidationFailure { source: crate::crd::Error }, #[snafu(display("vectorAggregatorConfigMapName must be set"))] MissingVectorAggregatorAddress, @@ -50,7 +49,7 @@ const CONSOLE_CONVERSION_PATTERN: &str = "%d{ISO8601} [myid:%X{myid}] - %-5p [%t /// Return the address of the Vector aggregator if the corresponding ConfigMap name is given in the /// cluster spec pub async fn resolve_vector_aggregator_address( - zk: &ZookeeperCluster, + zk: &v1alpha1::ZookeeperCluster, client: &Client, ) -> Result> { let vector_aggregator_address = if let Some(vector_aggregator_config_map_name) = &zk @@ -86,9 +85,9 @@ pub async fn resolve_vector_aggregator_address( /// Extend the role group ConfigMap with logging and Vector configurations pub fn extend_role_group_config_map( - zk: &ZookeeperCluster, + zk: &v1alpha1::ZookeeperCluster, role: ZookeeperRole, - rolegroup: &RoleGroupRef, + rolegroup: &RoleGroupRef, vector_aggregator_address: Option<&str>, cm_builder: &mut ConfigMapBuilder, ) -> Result<()> { @@ -98,7 +97,7 @@ pub fn extend_role_group_config_map( if let Some(ContainerLogConfig { choice: Some(ContainerLogConfigChoice::Automatic(log_config)), - }) = logging.containers.get(&Container::Zookeeper) + }) = logging.containers.get(&v1alpha1::Container::Zookeeper) { match zk.logging_framework() { LoggingFramework::LOG4J => { @@ -137,7 +136,7 @@ pub fn extend_role_group_config_map( let vector_log_config = if let Some(ContainerLogConfig { choice: Some(ContainerLogConfigChoice::Automatic(log_config)), - }) = logging.containers.get(&Container::Vector) + }) = logging.containers.get(&v1alpha1::Container::Vector) { Some(log_config) } else { diff --git a/rust/operator-binary/src/zk_controller.rs b/rust/operator-binary/src/zk_controller.rs index 63966cbd..cfc2e14f 100644 --- a/rust/operator-binary/src/zk_controller.rs +++ b/rust/operator-binary/src/zk_controller.rs @@ -1,4 +1,4 @@ -//! Ensures that `Pod`s are configured and running for each [`ZookeeperCluster`] +//! Ensures that `Pod`s are configured and running for each [`v1alpha1::ZookeeperCluster`] use std::{ borrow::Cow, collections::{BTreeMap, HashMap}, @@ -64,17 +64,17 @@ use stackable_operator::{ time::Duration, utils::{cluster_info::KubernetesClusterInfo, COMMON_BASH_TRAP_FUNCTIONS}, }; -use stackable_zookeeper_crd::{ - security::{self, ZookeeperSecurity}, - Container, ZookeeperCluster, ZookeeperClusterStatus, ZookeeperConfig, ZookeeperRole, - DOCKER_IMAGE_BASE_NAME, JVM_SECURITY_PROPERTIES_FILE, MAX_PREPARE_LOG_FILE_SIZE, - MAX_ZK_LOG_FILES_SIZE, STACKABLE_CONFIG_DIR, STACKABLE_DATA_DIR, STACKABLE_LOG_CONFIG_DIR, - STACKABLE_LOG_DIR, STACKABLE_RW_CONFIG_DIR, ZOOKEEPER_PROPERTIES_FILE, -}; use strum::{EnumDiscriminants, IntoStaticStr}; use crate::{ command::create_init_container_command_args, + crd::{ + security::{self, ZookeeperSecurity}, + v1alpha1, ZookeeperRole, DOCKER_IMAGE_BASE_NAME, JVM_SECURITY_PROPERTIES_FILE, + MAX_PREPARE_LOG_FILE_SIZE, MAX_ZK_LOG_FILES_SIZE, STACKABLE_CONFIG_DIR, STACKABLE_DATA_DIR, + STACKABLE_LOG_CONFIG_DIR, STACKABLE_LOG_DIR, STACKABLE_RW_CONFIG_DIR, + ZOOKEEPER_PROPERTIES_FILE, + }, discovery::{self, build_discovery_configmaps}, operations::{graceful_shutdown::add_graceful_shutdown_config, pdb::add_pdbs}, product_logging::{extend_role_group_config_map, resolve_vector_aggregator_address}, @@ -106,9 +106,7 @@ pub enum Error { }, #[snafu(display("crd validation failure"))] - CrdValidationFailure { - source: stackable_zookeeper_crd::Error, - }, + CrdValidationFailure { source: crate::crd::Error }, #[snafu(display("object defines no server role"))] NoServerRole, @@ -120,16 +118,14 @@ pub enum Error { }, #[snafu(display("internal operator failure"))] - InternalOperatorFailure { - source: stackable_zookeeper_crd::Error, - }, + InternalOperatorFailure { source: crate::crd::Error }, #[snafu(display("failed to calculate global service name"))] GlobalServiceNameNotFound, #[snafu(display("failed to calculate service name for role {}", rolegroup))] RoleGroupServiceNameNotFound { - rolegroup: RoleGroupRef, + rolegroup: RoleGroupRef, }, #[snafu(display("failed to apply global Service"))] @@ -140,25 +136,25 @@ pub enum Error { #[snafu(display("failed to apply Service for {}", rolegroup))] ApplyRoleGroupService { source: stackable_operator::cluster_resources::Error, - rolegroup: RoleGroupRef, + rolegroup: RoleGroupRef, }, #[snafu(display("failed to build ConfigMap for {}", rolegroup))] BuildRoleGroupConfig { source: stackable_operator::builder::configmap::Error, - rolegroup: RoleGroupRef, + rolegroup: RoleGroupRef, }, #[snafu(display("failed to apply ConfigMap for {}", rolegroup))] ApplyRoleGroupConfig { source: stackable_operator::cluster_resources::Error, - rolegroup: RoleGroupRef, + rolegroup: RoleGroupRef, }, #[snafu(display("failed to apply StatefulSet for {}", rolegroup))] ApplyRoleGroupStatefulSet { source: stackable_operator::cluster_resources::Error, - rolegroup: RoleGroupRef, + rolegroup: RoleGroupRef, }, #[snafu(display("failed to generate product config"))] @@ -174,7 +170,7 @@ pub enum Error { #[snafu(display("failed to serialize [{ZOOKEEPER_PROPERTIES_FILE}] for {}", rolegroup))] SerializeZooCfg { source: PropertiesWriterError, - rolegroup: RoleGroupRef, + rolegroup: RoleGroupRef, }, #[snafu(display("object is missing metadata to build owner reference"))] @@ -196,9 +192,7 @@ pub enum Error { }, #[snafu(display("invalid java heap config"))] - InvalidJavaHeapConfig { - source: stackable_zookeeper_crd::Error, - }, + InvalidJavaHeapConfig { source: crate::crd::Error }, #[snafu(display("failed to create RBAC service account"))] ApplyServiceAccount { @@ -232,14 +226,10 @@ pub enum Error { }, #[snafu(display("failed to initialize security context"))] - FailedToInitializeSecurityContext { - source: stackable_zookeeper_crd::security::Error, - }, + FailedToInitializeSecurityContext { source: crate::crd::security::Error }, #[snafu(display("failed to resolve and merge config for role and role group"))] - FailedToResolveConfig { - source: stackable_zookeeper_crd::Error, - }, + FailedToResolveConfig { source: crate::crd::Error }, #[snafu(display("failed to create PodDisruptionBudget"))] FailedToCreatePdb { @@ -328,7 +318,7 @@ impl ReconcilerError for Error { } pub async fn reconcile_zk( - zk: Arc>, + zk: Arc>, ctx: Arc, ) -> Result { tracing::info!("Starting reconcile"); @@ -511,7 +501,7 @@ pub async fn reconcile_zk( let cluster_operation_cond_builder = ClusterOperationsConditionBuilder::new(&zk.spec.cluster_operation); - let status = ZookeeperClusterStatus { + let status = v1alpha1::ZookeeperClusterStatus { // Serialize as a string to discourage users from trying to parse the value, // and to keep things flexible if we end up changing the hasher at some point. discovery_hash: Some(discovery_hash.finish().to_string()), @@ -533,10 +523,10 @@ pub async fn reconcile_zk( /// The server-role service is the primary endpoint that should be used by clients that do not perform internal load balancing, /// including targets outside of the cluster. /// -/// Note that you should generally *not* hard-code clients to use these services; instead, create a [`ZookeeperZnode`](`stackable_zookeeper_crd::ZookeeperZnode`) +/// Note that you should generally *not* hard-code clients to use these services; instead, create a [`v1alpha1::ZookeeperZnode`](`v1alpha1::ZookeeperZnode`) /// and use the connection string that it gives you. pub fn build_server_role_service( - zk: &ZookeeperCluster, + zk: &v1alpha1::ZookeeperCluster, resolved_product_image: &ResolvedProductImage, zookeeper_security: &ZookeeperSecurity, ) -> Result { @@ -584,8 +574,8 @@ pub fn build_server_role_service( /// The rolegroup [`ConfigMap`] configures the rolegroup based on the configuration given by the administrator fn build_server_rolegroup_config_map( - zk: &ZookeeperCluster, - rolegroup: &RoleGroupRef, + zk: &v1alpha1::ZookeeperCluster, + rolegroup: &RoleGroupRef, server_config: &HashMap>, resolved_product_image: &ResolvedProductImage, vector_aggregator_address: Option<&str>, @@ -693,8 +683,8 @@ fn build_server_rolegroup_config_map( /// /// This is mostly useful for internal communication between peers, or for clients that perform client-side load balancing. fn build_server_rolegroup_service( - zk: &ZookeeperCluster, - rolegroup: &RoleGroupRef, + zk: &v1alpha1::ZookeeperCluster, + rolegroup: &RoleGroupRef, resolved_product_image: &ResolvedProductImage, zookeeper_security: &ZookeeperSecurity, ) -> Result { @@ -756,13 +746,13 @@ fn build_server_rolegroup_service( /// The [`Pod`](`stackable_operator::k8s_openapi::api::core::v1::Pod`)s are accessible through the corresponding [`Service`] (from [`build_server_rolegroup_service`]). #[allow(clippy::too_many_arguments)] fn build_server_rolegroup_statefulset( - zk: &ZookeeperCluster, + zk: &v1alpha1::ZookeeperCluster, zk_role: &ZookeeperRole, - rolegroup_ref: &RoleGroupRef, + rolegroup_ref: &RoleGroupRef, server_config: &HashMap>, zookeeper_security: &ZookeeperSecurity, resolved_product_image: &ResolvedProductImage, - merged_config: &ZookeeperConfig, + merged_config: &v1alpha1::ZookeeperConfig, service_account: &ServiceAccount, ) -> Result { let role = zk.role(zk_role).context(InternalOperatorFailureSnafu)?; @@ -794,7 +784,7 @@ fn build_server_rolegroup_statefulset( .context(InvalidJavaHeapConfigSnafu)?; if let Some(heap_limits) = heap_limits { env_vars.push(EnvVar { - name: ZookeeperConfig::ZK_SERVER_HEAP.to_string(), + name: v1alpha1::ZookeeperConfig::ZK_SERVER_HEAP.to_string(), value: Some(heap_limits.to_string()), ..EnvVar::default() }); @@ -822,7 +812,7 @@ fn build_server_rolegroup_statefulset( if let Some(ContainerLogConfig { choice: Some(ContainerLogConfigChoice::Automatic(log_config)), - }) = logging.containers.get(&Container::Prepare) + }) = logging.containers.get(&v1alpha1::Container::Prepare) { args.push(product_logging::framework::capture_shell_output( STACKABLE_LOG_DIR, @@ -990,7 +980,7 @@ fn build_server_rolegroup_statefulset( Some(ContainerLogConfigChoice::Custom(CustomContainerLogConfig { custom: ConfigMapLogConfig { config_map }, })), - }) = logging.containers.get(&Container::Zookeeper) + }) = logging.containers.get(&v1alpha1::Container::Zookeeper) { pod_builder .add_volume(Volume { @@ -1021,7 +1011,7 @@ fn build_server_rolegroup_statefulset( resolved_product_image, "config", "log", - logging.containers.get(&Container::Vector), + logging.containers.get(&v1alpha1::Container::Vector), ResourceRequirementsBuilder::new() .with_cpu_request("250m") .with_cpu_limit("500m") @@ -1082,7 +1072,7 @@ fn build_server_rolegroup_statefulset( } pub fn error_policy( - _obj: Arc>, + _obj: Arc>, error: &Error, _ctx: Arc, ) -> controller::Action { @@ -1170,7 +1160,7 @@ mod tests { } fn build_config_map(zookeeper_yaml: &str) -> ConfigMap { - let mut zookeeper: ZookeeperCluster = + let mut zookeeper: v1alpha1::ZookeeperCluster = serde_yaml::from_str(zookeeper_yaml).expect("illegal test input"); zookeeper.metadata.uid = Some("42".to_owned()); let cluster_info = KubernetesClusterInfo { diff --git a/rust/operator-binary/src/znode_controller.rs b/rust/operator-binary/src/znode_controller.rs index eda1b8ea..615c030e 100644 --- a/rust/operator-binary/src/znode_controller.rs +++ b/rust/operator-binary/src/znode_controller.rs @@ -1,6 +1,6 @@ -//! Reconciles state for ZooKeeper znodes between Kubernetes [`ZookeeperZnode`] objects and the ZooKeeper cluster +//! Reconciles state for ZooKeeper znodes between Kubernetes [`v1alpha1::ZookeeperZnode`] objects and the ZooKeeper cluster //! -//! See [`ZookeeperZnode`] for more details. +//! See [`v1alpha1::ZookeeperZnode`] for more details. use std::{borrow::Cow, convert::Infallible, sync::Arc}; use const_format::concatcp; @@ -20,14 +20,11 @@ use stackable_operator::{ time::Duration, utils::cluster_info::KubernetesClusterInfo, }; -use stackable_zookeeper_crd::{ - security::ZookeeperSecurity, ZookeeperCluster, ZookeeperZnode, ZookeeperZnodeStatus, - DOCKER_IMAGE_BASE_NAME, -}; use strum::{EnumDiscriminants, IntoStaticStr}; use tracing::{debug, info}; use crate::{ + crd::{security::ZookeeperSecurity, v1alpha1, DOCKER_IMAGE_BASE_NAME}, discovery::{self, build_discovery_configmaps}, APP_NAME, OPERATOR_NAME, }; @@ -59,37 +56,41 @@ pub enum Error { #[snafu(display("could not find {}", zk))] FindZk { source: stackable_operator::client::Error, - zk: ObjectRef, + zk: ObjectRef, }, ZkDoesNotExist { source: stackable_operator::client::Error, - zk: ObjectRef, + zk: ObjectRef, }, #[snafu(display("could not find server role service name for {}", zk))] - NoZkSvcName { zk: ObjectRef }, + NoZkSvcName { + zk: ObjectRef, + }, #[snafu(display("could not find server role service for {}", zk))] FindZkSvc { source: stackable_operator::client::Error, - zk: ObjectRef, + zk: ObjectRef, }, #[snafu(display("failed to calculate FQDN for {}", zk))] - NoZkFqdn { zk: ObjectRef }, + NoZkFqdn { + zk: ObjectRef, + }, #[snafu(display("failed to ensure that ZNode {} exists in {}", znode_path, zk))] EnsureZnode { source: znode_mgmt::Error, - zk: ObjectRef, + zk: ObjectRef, znode_path: String, }, #[snafu(display("failed to ensure that ZNode {} is missing from {}", znode_path, zk))] EnsureZnodeMissing { source: znode_mgmt::Error, - zk: ObjectRef, + zk: ObjectRef, znode_path: String, }, @@ -121,9 +122,7 @@ pub enum Error { ObjectHasNoNamespace, #[snafu(display("failed to initialize security context"))] - FailedToInitializeSecurityContext { - source: stackable_zookeeper_crd::security::Error, - }, + FailedToInitializeSecurityContext { source: crate::crd::security::Error }, } type Result = std::result::Result; @@ -177,7 +176,7 @@ impl ReconcilerError for Error { } pub async fn reconcile_znode( - znode: Arc>, + znode: Arc>, ctx: Arc, ) -> Result { tracing::info!("Starting reconcile"); @@ -199,7 +198,7 @@ pub async fn reconcile_znode( let client = &ctx.client; let zk = find_zk_of_znode(client, znode).await; - let mut default_status_updates: Option = None; + let mut default_status_updates: Option = None; // Store the znode path in the status rather than the object itself, to ensure that only K8s administrators can override it let znode_path = match znode.status.as_ref().and_then(|s| s.znode_path.as_deref()) { Some(znode_path) => { @@ -230,7 +229,7 @@ pub async fn reconcile_znode( } finalizer( - &client.get_api::(&ns), + &client.get_api::(&ns), &format!("{OPERATOR_NAME}/znode"), Arc::new(znode.clone()), |ev| async { @@ -256,8 +255,8 @@ pub async fn reconcile_znode( async fn reconcile_apply( client: &stackable_operator::client::Client, - znode: &ZookeeperZnode, - zk: Result, + znode: &v1alpha1::ZookeeperZnode, + zk: Result, znode_path: &str, resolved_product_image: &ResolvedProductImage, ) -> Result { @@ -330,7 +329,7 @@ async fn reconcile_apply( async fn reconcile_cleanup( client: &stackable_operator::client::Client, - zk: Result, + zk: Result, znode_path: &str, ) -> Result { let zk = match zk { @@ -360,7 +359,7 @@ async fn reconcile_cleanup( } fn zk_mgmt_addr( - zk: &ZookeeperCluster, + zk: &v1alpha1::ZookeeperCluster, zookeeper_security: &ZookeeperSecurity, cluster_info: &KubernetesClusterInfo, ) -> Result { @@ -378,14 +377,17 @@ fn zk_mgmt_addr( async fn find_zk_of_znode( client: &stackable_operator::client::Client, - znode: &ZookeeperZnode, -) -> Result { + znode: &v1alpha1::ZookeeperZnode, +) -> Result { let zk_ref = &znode.spec.cluster_ref; if let (Some(zk_name), Some(zk_ns)) = ( zk_ref.name.as_deref(), zk_ref.namespace_relative_from(znode), ) { - match client.get::(zk_name, zk_ns).await { + match client + .get::(zk_name, zk_ns) + .await + { Ok(zk) => Ok(zk), Err(err) => match &err { stackable_operator::client::Error::GetResource { @@ -405,7 +407,7 @@ async fn find_zk_of_znode( } pub fn error_policy( - _obj: Arc>, + _obj: Arc>, _error: &Error, _ctx: Arc, ) -> controller::Action {