From 0ffac003606a7c1f8c78f682ef68dfdf837c8c05 Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Mon, 18 May 2026 17:24:19 +0200 Subject: [PATCH 1/4] refactor: extract dereference and validate steps into controller modules Co-Authored-By: Claude Opus 4.6 --- .../src/controller/dereference.rs | 30 ++++ rust/operator-binary/src/controller/mod.rs | 2 + .../src/controller/validate.rs | 131 +++++++++++++++ rust/operator-binary/src/crd/mod.rs | 4 +- rust/operator-binary/src/hdfs_controller.rs | 155 ++++++++---------- rust/operator-binary/src/main.rs | 1 + rust/operator-binary/src/security/opa.rs | 1 + 7 files changed, 234 insertions(+), 90 deletions(-) create mode 100644 rust/operator-binary/src/controller/dereference.rs create mode 100644 rust/operator-binary/src/controller/mod.rs create mode 100644 rust/operator-binary/src/controller/validate.rs diff --git a/rust/operator-binary/src/controller/dereference.rs b/rust/operator-binary/src/controller/dereference.rs new file mode 100644 index 00000000..dc06ed18 --- /dev/null +++ b/rust/operator-binary/src/controller/dereference.rs @@ -0,0 +1,30 @@ +use snafu::{ResultExt, Snafu}; + +use crate::{crd::v1alpha1, security::opa::HdfsOpaConfig}; + +#[derive(Snafu, Debug)] +pub enum Error { + #[snafu(display("invalid OPA configuration"))] + InvalidOpaConfig { source: crate::security::opa::Error }, +} + +/// External references resolved during the dereference step. +pub struct DereferencedObjects { + pub hdfs_opa_config: Option, +} + +pub async fn dereference( + client: &stackable_operator::client::Client, + hdfs: &v1alpha1::HdfsCluster, +) -> Result { + let hdfs_opa_config = match &hdfs.spec.cluster_config.authorization { + Some(opa_config) => Some( + HdfsOpaConfig::from_opa_config(client, hdfs, opa_config) + .await + .context(InvalidOpaConfigSnafu)?, + ), + None => None, + }; + + Ok(DereferencedObjects { hdfs_opa_config }) +} diff --git a/rust/operator-binary/src/controller/mod.rs b/rust/operator-binary/src/controller/mod.rs new file mode 100644 index 00000000..1b261dfe --- /dev/null +++ b/rust/operator-binary/src/controller/mod.rs @@ -0,0 +1,2 @@ +pub mod dereference; +pub mod validate; diff --git a/rust/operator-binary/src/controller/validate.rs b/rust/operator-binary/src/controller/validate.rs new file mode 100644 index 00000000..3adb409e --- /dev/null +++ b/rust/operator-binary/src/controller/validate.rs @@ -0,0 +1,131 @@ +use std::{ + collections::{BTreeMap, HashMap}, + str::FromStr, +}; + +use product_config::{ProductConfigManager, types::PropertyNameKind}; +use snafu::{ResultExt, Snafu}; +use stackable_operator::{ + commons::product_image_selection, + product_config_utils::{transform_all_roles_to_config, validate_all_roles_and_groups_config}, + role_utils::GenericRoleConfig, +}; + +use crate::{ + crd::{AnyNodeConfig, HdfsNodeRole, v1alpha1}, + hdfs_controller::{CONTAINER_IMAGE_BASE_NAME, ValidatedCluster}, + security::opa::HdfsOpaConfig, +}; + +#[derive(Snafu, Debug)] +pub enum Error { + #[snafu(display("failed to resolve product image"))] + ResolveProductImage { + source: product_image_selection::Error, + }, + + #[snafu(display("invalid role properties"))] + RoleProperties { source: crate::crd::Error }, + + #[snafu(display("failed to generate product config"))] + GenerateProductConfig { + source: stackable_operator::product_config_utils::Error, + }, + + #[snafu(display("invalid product configuration"))] + InvalidProductConfig { + source: stackable_operator::product_config_utils::Error, + }, + + #[snafu(display("could not parse HDFS role [{role}]"))] + UnidentifiedHdfsRole { + source: strum::ParseError, + role: String, + }, + + #[snafu(display("failed to resolve and merge config for role and role group"))] + FailedToResolveConfig { source: crate::crd::Error }, +} + +/// Per-role configuration extracted during validation. +#[derive(Clone, Debug)] +pub struct ValidatedRoleConfig { + pub pdb: stackable_operator::commons::pdb::PdbConfig, +} + +/// Per-rolegroup configuration: the merged CRD config plus the product-config properties. +#[derive(Clone, Debug)] +pub struct ValidatedRoleGroupConfig { + pub merged_config: AnyNodeConfig, + pub product_config_properties: HashMap>, +} + +pub fn validate_cluster( + hdfs: &v1alpha1::HdfsCluster, + image_repository: &str, + product_config_manager: &ProductConfigManager, + hdfs_opa_config: Option, +) -> Result { + let resolved_product_image = hdfs + .spec + .image + .resolve( + CONTAINER_IMAGE_BASE_NAME, + image_repository, + crate::built_info::PKG_VERSION, + ) + .context(ResolveProductImageSnafu)?; + + let roles = hdfs + .build_role_properties() + .context(RolePropertiesSnafu)?; + + let validated_config = validate_all_roles_and_groups_config( + &resolved_product_image.product_version, + &transform_all_roles_to_config(hdfs, &roles).context(GenerateProductConfigSnafu)?, + product_config_manager, + false, + false, + ) + .context(InvalidProductConfigSnafu)?; + + let mut role_groups = BTreeMap::new(); + let mut role_configs = BTreeMap::new(); + + for (role_name, group_config) in validated_config.iter() { + let hdfs_role = HdfsNodeRole::from_str(role_name).context(UnidentifiedHdfsRoleSnafu { + role: role_name.to_string(), + })?; + + if let Some(GenericRoleConfig { + pod_disruption_budget: pdb, + }) = hdfs.role_config(&hdfs_role) + { + role_configs.insert(hdfs_role, ValidatedRoleConfig { pdb: pdb.clone() }); + } + + let mut group_configs = BTreeMap::new(); + for (rolegroup_name, rolegroup_config) in group_config.iter() { + let merged_config = hdfs_role + .merged_config(hdfs, rolegroup_name) + .context(FailedToResolveConfigSnafu)?; + + group_configs.insert( + rolegroup_name.clone(), + ValidatedRoleGroupConfig { + merged_config, + product_config_properties: rolegroup_config.clone(), + }, + ); + } + + role_groups.insert(hdfs_role, group_configs); + } + + Ok(ValidatedCluster { + image: resolved_product_image, + role_groups, + role_configs, + hdfs_opa_config, + }) +} diff --git a/rust/operator-binary/src/crd/mod.rs b/rust/operator-binary/src/crd/mod.rs index f6830464..ecd78b86 100644 --- a/rust/operator-binary/src/crd/mod.rs +++ b/rust/operator-binary/src/crd/mod.rs @@ -993,7 +993,7 @@ pub struct CommonNodeConfig { } /// Configuration for a rolegroup of an unknown type. -#[derive(Debug)] +#[derive(Clone, Debug)] pub enum AnyNodeConfig { Name(NameNodeConfig), Data(DataNodeConfig), @@ -1087,7 +1087,9 @@ impl AnyNodeConfig { Eq, Hash, JsonSchema, + Ord, PartialEq, + PartialOrd, Serialize, )] pub enum HdfsNodeRole { diff --git a/rust/operator-binary/src/hdfs_controller.rs b/rust/operator-binary/src/hdfs_controller.rs index 9e92159d..a820ea2e 100644 --- a/rust/operator-binary/src/hdfs_controller.rs +++ b/rust/operator-binary/src/hdfs_controller.rs @@ -19,10 +19,7 @@ use stackable_operator::{ cli::OperatorEnvironmentOptions, client::Client, cluster_resources::{ClusterResourceApplyStrategy, ClusterResources}, - commons::{ - product_image_selection::{self, ResolvedProductImage}, - rbac::build_rbac_resources, - }, + commons::{product_image_selection::ResolvedProductImage, rbac::build_rbac_resources}, iter::reverse_if, k8s_openapi::{ DeepMerge, @@ -40,8 +37,7 @@ use stackable_operator::{ }, kvp::{LabelError, Labels}, logging::controller::ReconcilerError, - product_config_utils::{transform_all_roles_to_config, validate_all_roles_and_groups_config}, - role_utils::{GenericRoleConfig, RoleGroupRef}, + role_utils::RoleGroupRef, shared::time::Duration, status::{ condition::{ @@ -57,6 +53,7 @@ use strum::{EnumDiscriminants, IntoEnumIterator, IntoStaticStr}; use crate::{ OPERATOR_NAME, build_recommended_labels, config::{CoreSiteConfigBuilder, HdfsSiteConfigBuilder}, + controller::validate::{ValidatedRoleConfig, ValidatedRoleGroupConfig}, container::{self, ContainerConfig, TLS_STORE_DIR, TLS_STORE_PASSWORD}, crd::{ AnyNodeConfig, HdfsClusterStatus, HdfsNodeRole, HdfsPodRef, UpgradeState, @@ -69,7 +66,7 @@ use crate::{ pdb::add_pdbs, }, product_logging::extend_role_group_config_map, - security::{self, kerberos, opa::HdfsOpaConfig}, + security::{kerberos, opa::HdfsOpaConfig}, service::{self, rolegroup_headless_service, rolegroup_metrics_service}, }; @@ -77,19 +74,31 @@ pub const RESOURCE_MANAGER_HDFS_CONTROLLER: &str = "hdfs-operator-hdfs-controlle const HDFS_CONTROLLER_NAME: &str = "hdfs-controller"; pub const HDFS_FULL_CONTROLLER_NAME: &str = concatcp!(HDFS_CONTROLLER_NAME, '.', OPERATOR_NAME); -const CONTAINER_IMAGE_BASE_NAME: &str = "hadoop"; +pub const CONTAINER_IMAGE_BASE_NAME: &str = "hadoop"; + +/// The validated cluster: proves that product-config validation and config merging +/// succeeded for every role and role group before any resources are created. +/// Placed in the controller so that subsequent steps that reference this struct +/// only depend on the controller. +#[derive(Clone, Debug)] +pub struct ValidatedCluster { + pub image: ResolvedProductImage, + pub role_groups: BTreeMap>, + pub role_configs: BTreeMap, + pub hdfs_opa_config: Option, +} #[derive(Snafu, Debug, EnumDiscriminants)] #[strum_discriminants(derive(IntoStaticStr))] pub enum Error { - #[snafu(display("invalid role configuration"))] - InvalidRoleConfig { - source: stackable_operator::product_config_utils::Error, + #[snafu(display("failed to dereference cluster resources"))] + Dereference { + source: crate::controller::dereference::Error, }, - #[snafu(display("invalid product configuration"))] - InvalidProductConfig { - source: stackable_operator::product_config_utils::Error, + #[snafu(display("failed to validate cluster configuration"))] + Validate { + source: crate::controller::validate::Error, }, #[snafu(display("invalid upgrade state"))] @@ -125,12 +134,6 @@ pub enum Error { obj_ref: ObjectRef, }, - #[snafu(display("invalid role {role:?}"))] - InvalidRole { - source: strum::ParseError, - role: String, - }, - #[snafu(display("object has no name"))] ObjectHasNoName { obj_ref: ObjectRef, @@ -172,18 +175,12 @@ pub enum Error { #[snafu(display("failed to create pod references"))] CreatePodReferences { source: crate::crd::Error }, - #[snafu(display("failed to build role properties"))] - BuildRoleProperties { source: crate::crd::Error }, - #[snafu(display("failed to add the logging configuration to the ConfigMap {cm_name:?}"))] InvalidLoggingConfig { source: crate::product_logging::Error, cm_name: String, }, - #[snafu(display("failed to merge config"))] - ConfigMerge { source: crate::crd::Error }, - #[snafu(display("failed to create cluster event"))] FailedToCreateClusterEvent { source: crate::event::Error }, @@ -234,19 +231,11 @@ pub enum Error { #[snafu(display("failed to build security config"))] BuildSecurityConfig { source: kerberos::Error }, - #[snafu(display("invalid OPA configuration"))] - InvalidOpaConfig { source: security::opa::Error }, - #[snafu(display("HdfsCluster object is invalid"))] InvalidHdfsCluster { source: error_boundary::InvalidObject, }, - #[snafu(display("failed to resolve product image"))] - ResolveProductImage { - source: product_image_selection::Error, - }, - #[snafu(display("failed to builds service"))] BuildService { source: service::Error }, } @@ -279,29 +268,19 @@ pub async fn reconcile_hdfs( .context(InvalidHdfsClusterSnafu)?; let client = &ctx.client; - let resolved_product_image = hdfs - .spec - .image - .resolve( - CONTAINER_IMAGE_BASE_NAME, - &ctx.operator_environment.image_repository, - crate::built_info::PKG_VERSION, - ) - .context(ResolveProductImageSnafu)?; - - let validated_config = { - let roles = hdfs - .build_role_properties() - .context(BuildRolePropertiesSnafu)?; - validate_all_roles_and_groups_config( - &resolved_product_image.product_version, - &transform_all_roles_to_config(hdfs, &roles).context(InvalidRoleConfigSnafu)?, - &ctx.product_config, - false, - false, - ) - .context(InvalidProductConfigSnafu)? - }; + let dereferenced = crate::controller::dereference::dereference(client, hdfs) + .await + .context(DereferenceSnafu)?; + + let validated = crate::controller::validate::validate_cluster( + hdfs, + &ctx.operator_environment.image_repository, + &ctx.product_config, + dereferenced.hdfs_opa_config, + ) + .context(ValidateSnafu)?; + + let resolved_product_image = &validated.image; let hdfs_obj_ref = hdfs.object_ref(&()); // A list of all name and journal nodes across all role groups is needed for all ConfigMaps and initialization checks. @@ -341,15 +320,6 @@ pub async fn reconcile_hdfs( .await .context(ApplyRoleBindingSnafu)?; - let hdfs_opa_config = match &hdfs.spec.cluster_config.authorization { - Some(opa_config) => Some( - HdfsOpaConfig::from_opa_config(client, hdfs, opa_config) - .await - .context(InvalidOpaConfigSnafu)?, - ), - None => None, - }; - let dfs_replication = hdfs.spec.cluster_config.dfs_replication; let mut ss_cond_builder = StatefulSetConditionBuilder::default(); @@ -371,7 +341,7 @@ pub async fn reconcile_hdfs( ); 'roles: for role in roles { let role_name: &str = role.into(); - let Some(group_config) = validated_config.get(role_name) else { + let Some(group_config) = validated.role_groups.get(&role) else { tracing::debug!(?role, "role has no configuration, skipping"); continue; }; @@ -388,20 +358,20 @@ pub async fn reconcile_hdfs( .context(FailedToCreateClusterEventSnafu)?; } - for (rolegroup_name, rolegroup_config) in group_config.iter() { - let merged_config = role - .merged_config(hdfs, rolegroup_name) - .context(ConfigMergeSnafu)?; + for (rolegroup_name, validated_rg_config) in group_config.iter() { + let merged_config = &validated_rg_config.merged_config; - let env_overrides = rolegroup_config.get(&PropertyNameKind::Env); + let env_overrides = validated_rg_config + .product_config_properties + .get(&PropertyNameKind::Env); let rolegroup_ref = hdfs.rolegroup_ref(role_name, rolegroup_name); let rg_service = - rolegroup_headless_service(hdfs, &role, &rolegroup_ref, &resolved_product_image) + rolegroup_headless_service(hdfs, &role, &rolegroup_ref, resolved_product_image) .context(BuildServiceSnafu)?; let rg_metrics_service = - rolegroup_metrics_service(hdfs, &role, &rolegroup_ref, &resolved_product_image) + rolegroup_metrics_service(hdfs, &role, &rolegroup_ref, resolved_product_image) .context(BuildServiceSnafu)?; // We need to split the creation and the usage of the "metadata" variable in two statements. @@ -428,11 +398,11 @@ pub async fn reconcile_hdfs( &client.kubernetes_cluster_info, metadata, &rolegroup_ref, - rolegroup_config, + &validated_rg_config.product_config_properties, &namenode_podrefs, &journalnode_podrefs, - &merged_config, - &hdfs_opa_config, + merged_config, + &validated.hdfs_opa_config, )?; let rg_statefulset = rolegroup_statefulset( @@ -441,9 +411,9 @@ pub async fn reconcile_hdfs( metadata, &role, &rolegroup_ref, - &resolved_product_image, + resolved_product_image, env_overrides, - &merged_config, + merged_config, &namenode_podrefs, &rbac_sa, )?; @@ -498,14 +468,16 @@ pub async fn reconcile_hdfs( } } - let role_config = hdfs.role_config(&role); - if let Some(GenericRoleConfig { - pod_disruption_budget: pdb, - }) = role_config - { - add_pdbs(pdb, hdfs, &role, client, &mut cluster_resources) - .await - .context(FailedToCreatePdbSnafu)?; + if let Some(validated_role_config) = validated.role_configs.get(&role) { + add_pdbs( + &validated_role_config.pdb, + hdfs, + &role, + client, + &mut cluster_resources, + ) + .await + .context(FailedToCreatePdbSnafu)?; } } @@ -519,7 +491,7 @@ pub async fn reconcile_hdfs( .namenode_listener_refs(client) .await .context(CollectDiscoveryConfigSnafu)?, - &resolved_product_image, + resolved_product_image, ) .context(BuildDiscoveryConfigMapSnafu)?; @@ -930,7 +902,12 @@ pub fn error_policy( #[cfg(test)] mod test { - use stackable_operator::commons::networking::DomainName; + use stackable_operator::{ + commons::networking::DomainName, + product_config_utils::{ + transform_all_roles_to_config, validate_all_roles_and_groups_config, + }, + }; use super::*; diff --git a/rust/operator-binary/src/main.rs b/rust/operator-binary/src/main.rs index 5dd87a2a..074059ee 100644 --- a/rust/operator-binary/src/main.rs +++ b/rust/operator-binary/src/main.rs @@ -43,6 +43,7 @@ use crate::{ mod config; mod container; +mod controller; mod crd; mod discovery; mod event; diff --git a/rust/operator-binary/src/security/opa.rs b/rust/operator-binary/src/security/opa.rs index 013edcdb..16a6b06f 100644 --- a/rust/operator-binary/src/security/opa.rs +++ b/rust/operator-binary/src/security/opa.rs @@ -16,6 +16,7 @@ pub enum Error { type Result = std::result::Result; +#[derive(Clone, Debug)] pub struct HdfsOpaConfig { authorization_connection_string: String, } From 3933eb8b8b2426a7870f31a684740823b237c1fe Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Mon, 18 May 2026 17:26:02 +0200 Subject: [PATCH 2/4] changelog Co-Authored-By: Claude Opus 4.6 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2cd3148..5d473cea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,10 +16,12 @@ All notable changes to this project will be documented in this file. `security.properties`). Previously, arbitrary file names were silently accepted and ignored ([#777]). - Bump `stackable-operator` to 0.111.1 ([#777], [#778]). +- Internal operator refactoring: introduce dereference() and validate() steps in the reconciler ([#783]). [#770]: https://github.com/stackabletech/hdfs-operator/pull/770 [#777]: https://github.com/stackabletech/hdfs-operator/pull/777 [#778]: https://github.com/stackabletech/hdfs-operator/pull/778 +[#783]: https://github.com/stackabletech/hdfs-operator/pull/783 ## [26.3.0] - 2026-03-16 From db7d455d7f242a17ab9e1ee5d641058a718eeaa2 Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Mon, 18 May 2026 17:47:52 +0200 Subject: [PATCH 3/4] formatting --- rust/operator-binary/src/controller/validate.rs | 4 +--- rust/operator-binary/src/hdfs_controller.rs | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/rust/operator-binary/src/controller/validate.rs b/rust/operator-binary/src/controller/validate.rs index 3adb409e..d34f6ba3 100644 --- a/rust/operator-binary/src/controller/validate.rs +++ b/rust/operator-binary/src/controller/validate.rs @@ -76,9 +76,7 @@ pub fn validate_cluster( ) .context(ResolveProductImageSnafu)?; - let roles = hdfs - .build_role_properties() - .context(RolePropertiesSnafu)?; + let roles = hdfs.build_role_properties().context(RolePropertiesSnafu)?; let validated_config = validate_all_roles_and_groups_config( &resolved_product_image.product_version, diff --git a/rust/operator-binary/src/hdfs_controller.rs b/rust/operator-binary/src/hdfs_controller.rs index a820ea2e..ce97fd5a 100644 --- a/rust/operator-binary/src/hdfs_controller.rs +++ b/rust/operator-binary/src/hdfs_controller.rs @@ -53,8 +53,8 @@ use strum::{EnumDiscriminants, IntoEnumIterator, IntoStaticStr}; use crate::{ OPERATOR_NAME, build_recommended_labels, config::{CoreSiteConfigBuilder, HdfsSiteConfigBuilder}, - controller::validate::{ValidatedRoleConfig, ValidatedRoleGroupConfig}, container::{self, ContainerConfig, TLS_STORE_DIR, TLS_STORE_PASSWORD}, + controller::validate::{ValidatedRoleConfig, ValidatedRoleGroupConfig}, crd::{ AnyNodeConfig, HdfsClusterStatus, HdfsNodeRole, HdfsPodRef, UpgradeState, UpgradeStateError, constants::*, v1alpha1, From 7eb57524b0ad2582c1cc6e731563fe6ac2cc4ac6 Mon Sep 17 00:00:00 2001 From: Andrew Kenworthy Date: Mon, 18 May 2026 22:41:46 +0200 Subject: [PATCH 4/4] extend assert step to increase test coverage by checking most generated resources Co-Authored-By: Claude Opus 4.6 --- tests/templates/kuttl/smoke/30-assert.yaml.j2 | 517 ++++++++++++++---- 1 file changed, 403 insertions(+), 114 deletions(-) diff --git a/tests/templates/kuttl/smoke/30-assert.yaml.j2 b/tests/templates/kuttl/smoke/30-assert.yaml.j2 index 2f17591a..debb4cb1 100644 --- a/tests/templates/kuttl/smoke/30-assert.yaml.j2 +++ b/tests/templates/kuttl/smoke/30-assert.yaml.j2 @@ -3,173 +3,489 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert timeout: 600 --- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: hdfs-namenode-default -spec: - template: - spec: - containers: - - name: namenode - resources: - requests: - cpu: 250m # From defaults - memory: 1Gi - limits: - cpu: "1" # From defaults - memory: 1Gi -{% if lookup('env', 'VECTOR_AGGREGATOR') %} - - name: vector -{% endif %} - - name: zkfc - terminationGracePeriodSeconds: 900 -status: - readyReplicas: 2 - replicas: 2 ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: hdfs-journalnode-default -spec: - template: - spec: - containers: - - name: journalnode - resources: - requests: - cpu: 110m # From podOverrides - memory: 512Mi - limits: - cpu: 410m # From podOverrides - memory: 512Mi -{% if lookup('env', 'VECTOR_AGGREGATOR') %} - - name: vector -{% endif %} - terminationGracePeriodSeconds: 900 -status: - readyReplicas: 1 - replicas: 1 ---- -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: hdfs-datanode-default -spec: - template: - spec: - containers: - - name: datanode - resources: - requests: - cpu: 100m # From defaults - memory: 512Mi - limits: - cpu: 400m # From defaults - memory: 512Mi -{% if lookup('env', 'VECTOR_AGGREGATOR') %} - - name: vector -{% endif %} - terminationGracePeriodSeconds: 1800 -status: - readyReplicas: {{ test_scenario['values']['number-of-datanodes'] }} - replicas: {{ test_scenario['values']['number-of-datanodes'] }} -{% if test_scenario['values']['datanode-pvcs'] == '2hdd-1ssd' %} ---- apiVersion: v1 kind: Service metadata: + labels: + app.kubernetes.io/component: namenode + app.kubernetes.io/instance: hdfs + app.kubernetes.io/managed-by: hdfs.stackable.tech_hdfs-operator-hdfs-controller + app.kubernetes.io/name: hdfs + app.kubernetes.io/role-group: default + stackable.tech/vendor: Stackable name: hdfs-namenode-default + ownerReferences: + - apiVersion: hdfs.stackable.tech/v1alpha1 + controller: true + kind: HdfsCluster + name: hdfs spec: + clusterIP: None ports: - name: rpc port: 8020 protocol: TCP - targetPort: 8020 - name: http port: 9870 protocol: TCP - targetPort: 9870 + publishNotReadyAddresses: true + selector: + app.kubernetes.io/component: namenode + app.kubernetes.io/instance: hdfs + app.kubernetes.io/name: hdfs + app.kubernetes.io/role-group: default + type: ClusterIP --- apiVersion: v1 kind: Service metadata: + annotations: + prometheus.io/path: /prom + prometheus.io/port: "9870" + prometheus.io/scheme: http + prometheus.io/scrape: "true" + labels: + app.kubernetes.io/component: namenode + app.kubernetes.io/instance: hdfs + app.kubernetes.io/managed-by: hdfs.stackable.tech_hdfs-operator-hdfs-controller + app.kubernetes.io/name: hdfs + app.kubernetes.io/role-group: default + prometheus.io/scrape: "true" + stackable.tech/vendor: Stackable name: hdfs-namenode-default-metrics + ownerReferences: + - apiVersion: hdfs.stackable.tech/v1alpha1 + controller: true + kind: HdfsCluster + name: hdfs spec: + clusterIP: None ports: - name: metrics port: 9870 protocol: TCP - targetPort: 9870 - name: jmx-metrics port: 8183 protocol: TCP - targetPort: 8183 + publishNotReadyAddresses: true + selector: + app.kubernetes.io/component: namenode + app.kubernetes.io/instance: hdfs + app.kubernetes.io/name: hdfs + app.kubernetes.io/role-group: default + type: ClusterIP --- apiVersion: v1 kind: Service metadata: + labels: + app.kubernetes.io/component: datanode + app.kubernetes.io/instance: hdfs + app.kubernetes.io/managed-by: hdfs.stackable.tech_hdfs-operator-hdfs-controller + app.kubernetes.io/name: hdfs + app.kubernetes.io/role-group: default + stackable.tech/vendor: Stackable name: hdfs-datanode-default + ownerReferences: + - apiVersion: hdfs.stackable.tech/v1alpha1 + controller: true + kind: HdfsCluster + name: hdfs spec: + clusterIP: None ports: - name: data port: 9866 protocol: TCP - targetPort: 9866 - name: ipc port: 9867 protocol: TCP - targetPort: 9867 - name: http port: 9864 protocol: TCP - targetPort: 9864 + publishNotReadyAddresses: true + selector: + app.kubernetes.io/component: datanode + app.kubernetes.io/instance: hdfs + app.kubernetes.io/name: hdfs + app.kubernetes.io/role-group: default + type: ClusterIP --- apiVersion: v1 kind: Service metadata: + annotations: + prometheus.io/path: /prom + prometheus.io/port: "9864" + prometheus.io/scheme: http + prometheus.io/scrape: "true" + labels: + app.kubernetes.io/component: datanode + app.kubernetes.io/instance: hdfs + app.kubernetes.io/managed-by: hdfs.stackable.tech_hdfs-operator-hdfs-controller + app.kubernetes.io/name: hdfs + app.kubernetes.io/role-group: default + prometheus.io/scrape: "true" + stackable.tech/vendor: Stackable name: hdfs-datanode-default-metrics + ownerReferences: + - apiVersion: hdfs.stackable.tech/v1alpha1 + controller: true + kind: HdfsCluster + name: hdfs spec: + clusterIP: None ports: - name: metrics port: 9864 protocol: TCP - targetPort: 9864 - name: jmx-metrics port: 8082 protocol: TCP - targetPort: 8082 + publishNotReadyAddresses: true + selector: + app.kubernetes.io/component: datanode + app.kubernetes.io/instance: hdfs + app.kubernetes.io/name: hdfs + app.kubernetes.io/role-group: default + type: ClusterIP --- apiVersion: v1 kind: Service metadata: + labels: + app.kubernetes.io/component: journalnode + app.kubernetes.io/instance: hdfs + app.kubernetes.io/managed-by: hdfs.stackable.tech_hdfs-operator-hdfs-controller + app.kubernetes.io/name: hdfs + app.kubernetes.io/role-group: default + stackable.tech/vendor: Stackable name: hdfs-journalnode-default + ownerReferences: + - apiVersion: hdfs.stackable.tech/v1alpha1 + controller: true + kind: HdfsCluster + name: hdfs spec: + clusterIP: None ports: - name: rpc port: 8485 protocol: TCP - targetPort: 8485 - name: http port: 8480 protocol: TCP - targetPort: 8480 + publishNotReadyAddresses: true + selector: + app.kubernetes.io/component: journalnode + app.kubernetes.io/instance: hdfs + app.kubernetes.io/name: hdfs + app.kubernetes.io/role-group: default + type: ClusterIP --- apiVersion: v1 kind: Service metadata: + annotations: + prometheus.io/path: /prom + prometheus.io/port: "8480" + prometheus.io/scheme: http + prometheus.io/scrape: "true" + labels: + app.kubernetes.io/component: journalnode + app.kubernetes.io/instance: hdfs + app.kubernetes.io/managed-by: hdfs.stackable.tech_hdfs-operator-hdfs-controller + app.kubernetes.io/name: hdfs + app.kubernetes.io/role-group: default + prometheus.io/scrape: "true" + stackable.tech/vendor: Stackable name: hdfs-journalnode-default-metrics + ownerReferences: + - apiVersion: hdfs.stackable.tech/v1alpha1 + controller: true + kind: HdfsCluster + name: hdfs spec: + clusterIP: None ports: - name: metrics port: 8480 protocol: TCP - targetPort: 8480 - name: jmx-metrics port: 8081 protocol: TCP - targetPort: 8081 + publishNotReadyAddresses: true + selector: + app.kubernetes.io/component: journalnode + app.kubernetes.io/instance: hdfs + app.kubernetes.io/name: hdfs + app.kubernetes.io/role-group: default + type: ClusterIP +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + labels: + app.kubernetes.io/component: namenode + app.kubernetes.io/instance: hdfs + app.kubernetes.io/managed-by: hdfs.stackable.tech_hdfs-operator-hdfs-controller + app.kubernetes.io/name: hdfs + app.kubernetes.io/role-group: default + stackable.tech/vendor: Stackable + name: hdfs-namenode-default + ownerReferences: + - apiVersion: hdfs.stackable.tech/v1alpha1 + controller: true + kind: HdfsCluster + name: hdfs +spec: + podManagementPolicy: OrderedReady + replicas: 2 + selector: + matchLabels: + app.kubernetes.io/component: namenode + app.kubernetes.io/instance: hdfs + app.kubernetes.io/name: hdfs + app.kubernetes.io/role-group: default + serviceName: hdfs-namenode-default + template: + spec: + containers: + - name: namenode + resources: + requests: + cpu: 250m + memory: 1Gi + limits: + cpu: "1" + memory: 1Gi +{% if lookup('env', 'VECTOR_AGGREGATOR') %} + - name: vector +{% endif %} + - name: zkfc + terminationGracePeriodSeconds: 900 +status: + readyReplicas: 2 + replicas: 2 +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + labels: + app.kubernetes.io/component: datanode + app.kubernetes.io/instance: hdfs + app.kubernetes.io/managed-by: hdfs.stackable.tech_hdfs-operator-hdfs-controller + app.kubernetes.io/name: hdfs + app.kubernetes.io/role-group: default + stackable.tech/vendor: Stackable + name: hdfs-datanode-default + ownerReferences: + - apiVersion: hdfs.stackable.tech/v1alpha1 + controller: true + kind: HdfsCluster + name: hdfs +spec: + podManagementPolicy: OrderedReady + replicas: {{ test_scenario['values']['number-of-datanodes'] }} + selector: + matchLabels: + app.kubernetes.io/component: datanode + app.kubernetes.io/instance: hdfs + app.kubernetes.io/name: hdfs + app.kubernetes.io/role-group: default + serviceName: hdfs-datanode-default + template: + spec: + containers: + - name: datanode + resources: + requests: + cpu: 100m + memory: 512Mi + limits: + cpu: 400m + memory: 512Mi +{% if lookup('env', 'VECTOR_AGGREGATOR') %} + - name: vector +{% endif %} + terminationGracePeriodSeconds: 1800 +status: + readyReplicas: {{ test_scenario['values']['number-of-datanodes'] }} + replicas: {{ test_scenario['values']['number-of-datanodes'] }} +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + labels: + app.kubernetes.io/component: journalnode + app.kubernetes.io/instance: hdfs + app.kubernetes.io/managed-by: hdfs.stackable.tech_hdfs-operator-hdfs-controller + app.kubernetes.io/name: hdfs + app.kubernetes.io/role-group: default + stackable.tech/vendor: Stackable + name: hdfs-journalnode-default + ownerReferences: + - apiVersion: hdfs.stackable.tech/v1alpha1 + controller: true + kind: HdfsCluster + name: hdfs +spec: + podManagementPolicy: OrderedReady + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/component: journalnode + app.kubernetes.io/instance: hdfs + app.kubernetes.io/name: hdfs + app.kubernetes.io/role-group: default + serviceName: hdfs-journalnode-default + template: + spec: + containers: + - name: journalnode + resources: + requests: + cpu: 110m + memory: 512Mi + limits: + cpu: 410m + memory: 512Mi +{% if lookup('env', 'VECTOR_AGGREGATOR') %} + - name: vector +{% endif %} + terminationGracePeriodSeconds: 900 +status: + readyReplicas: 1 + replicas: 1 +--- +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + app.kubernetes.io/component: namenode + app.kubernetes.io/instance: hdfs + app.kubernetes.io/managed-by: hdfs.stackable.tech_hdfs-operator-hdfs-controller + app.kubernetes.io/name: hdfs + app.kubernetes.io/role-group: default + stackable.tech/vendor: Stackable + name: hdfs-namenode-default + ownerReferences: + - apiVersion: hdfs.stackable.tech/v1alpha1 + controller: true + kind: HdfsCluster + name: hdfs +--- +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + app.kubernetes.io/component: datanode + app.kubernetes.io/instance: hdfs + app.kubernetes.io/managed-by: hdfs.stackable.tech_hdfs-operator-hdfs-controller + app.kubernetes.io/name: hdfs + app.kubernetes.io/role-group: default + stackable.tech/vendor: Stackable + name: hdfs-datanode-default + ownerReferences: + - apiVersion: hdfs.stackable.tech/v1alpha1 + controller: true + kind: HdfsCluster + name: hdfs +--- +apiVersion: v1 +kind: ConfigMap +metadata: + labels: + app.kubernetes.io/component: journalnode + app.kubernetes.io/instance: hdfs + app.kubernetes.io/managed-by: hdfs.stackable.tech_hdfs-operator-hdfs-controller + app.kubernetes.io/name: hdfs + app.kubernetes.io/role-group: default + stackable.tech/vendor: Stackable + name: hdfs-journalnode-default + ownerReferences: + - apiVersion: hdfs.stackable.tech/v1alpha1 + controller: true + kind: HdfsCluster + name: hdfs +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/instance: hdfs + app.kubernetes.io/managed-by: hdfs.stackable.tech_hdfs-operator-hdfs-controller + app.kubernetes.io/name: hdfs + name: hdfs-serviceaccount + ownerReferences: + - apiVersion: hdfs.stackable.tech/v1alpha1 + controller: true + kind: HdfsCluster + name: hdfs +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app.kubernetes.io/instance: hdfs + app.kubernetes.io/managed-by: hdfs.stackable.tech_hdfs-operator-hdfs-controller + app.kubernetes.io/name: hdfs + name: hdfs-rolebinding + ownerReferences: + - apiVersion: hdfs.stackable.tech/v1alpha1 + controller: true + kind: HdfsCluster + name: hdfs +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: hdfs-clusterrole +subjects: +- kind: ServiceAccount + name: hdfs-serviceaccount +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: hdfs-namenode + ownerReferences: + - apiVersion: hdfs.stackable.tech/v1alpha1 + controller: true + kind: HdfsCluster + name: hdfs +status: + expectedPods: 2 + currentHealthy: 2 + disruptionsAllowed: 1 +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: hdfs-datanode + ownerReferences: + - apiVersion: hdfs.stackable.tech/v1alpha1 + controller: true + kind: HdfsCluster + name: hdfs +status: + expectedPods: {{ test_scenario['values']['number-of-datanodes'] }} + currentHealthy: {{ test_scenario['values']['number-of-datanodes'] }} + disruptionsAllowed: 1 +--- +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: hdfs-journalnode + ownerReferences: + - apiVersion: hdfs.stackable.tech/v1alpha1 + controller: true + kind: HdfsCluster + name: hdfs +status: + expectedPods: 1 + currentHealthy: 1 + disruptionsAllowed: 1 +{% if test_scenario['values']['datanode-pvcs'] == '2hdd-1ssd' %} --- apiVersion: v1 kind: PersistentVolumeClaim @@ -210,30 +526,3 @@ status: - ReadWriteOnce phase: Bound {% endif %} ---- -apiVersion: policy/v1 -kind: PodDisruptionBudget -metadata: - name: hdfs-journalnode -status: - expectedPods: 1 - currentHealthy: 1 - disruptionsAllowed: 1 ---- -apiVersion: policy/v1 -kind: PodDisruptionBudget -metadata: - name: hdfs-namenode -status: - expectedPods: 2 - currentHealthy: 2 - disruptionsAllowed: 1 ---- -apiVersion: policy/v1 -kind: PodDisruptionBudget -metadata: - name: hdfs-datanode -status: - expectedPods: {{ test_scenario['values']['number-of-datanodes'] }} - currentHealthy: {{ test_scenario['values']['number-of-datanodes'] }} - disruptionsAllowed: 1