From 074ce6a51403cc7def28c760d6f6d0f3521dd623 Mon Sep 17 00:00:00 2001 From: Maxi Wittich Date: Tue, 6 Oct 2026 10:56:27 +0200 Subject: [PATCH 1/2] Add default affinites to opa pods --- CHANGELOG.md | 1 + .../usage-guide/operations/pod-placement.adoc | 19 ++++++ .../src/controller/validate.rs | 3 +- rust/operator-binary/src/crd/affinity.rs | 64 +++++++++++++++++-- rust/operator-binary/src/crd/mod.rs | 8 ++- 5 files changed, 86 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6f12d17..0e2a1a9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file. - Add `/ready` endpoint to the operator Deployment, which reports the CRD installation status ([#770]). - Document that the Stackable Hive images restore the `get_table` and `get_table_objects_by_name` Thrift methods removed by HIVE-26537, and assert it in the smoke test ([#766]). - Add support for Hive `4.2.1` ([#766]). +- The metastore now has a default affinity to the OPA Pods when OPA authorization is configured ([#XXX]). ### Removed diff --git a/docs/modules/hive/pages/usage-guide/operations/pod-placement.adoc b/docs/modules/hive/pages/usage-guide/operations/pod-placement.adoc index 06e5a6e1..b72e4c28 100644 --- a/docs/modules/hive/pages/usage-guide/operations/pod-placement.adoc +++ b/docs/modules/hive/pages/usage-guide/operations/pod-placement.adoc @@ -20,3 +20,22 @@ affinity: ---- In the example above `cluster-name` is the name of the HiveCluster custom resource that owns this Pod. + +If OPA authorization is configured, the metastore Pods additionally prefer to run on the same Kubernetes nodes as the OPA Pods (weight 50): + +[source,yaml] +---- +affinity: + podAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: + app.kubernetes.io/name: opa + app.kubernetes.io/instance: opa-cluster-name + app.kubernetes.io/component: server + topologyKey: kubernetes.io/hostname + weight: 50 +---- + +`opa-cluster-name` is the `configMapName` from `spec.clusterConfig.authorization.opa`, which by convention is the name of the OpaCluster. diff --git a/rust/operator-binary/src/controller/validate.rs b/rust/operator-binary/src/controller/validate.rs index d72b0c0e..3ac69722 100644 --- a/rust/operator-binary/src/controller/validate.rs +++ b/rust/operator-binary/src/controller/validate.rs @@ -155,7 +155,8 @@ pub fn validate_cluster( listener_class: listener_class.clone(), }; - let default_config = MetaStoreConfig::default_config(name.as_ref(), &hive_role); + let default_config = + MetaStoreConfig::default_config(name.as_ref(), &hive_role, hive.get_opa_config()); // The Vector aggregator discovery ConfigMap name. It is only required when the Vector agent is // enabled for a role group; validity is already enforced by the `ConfigMapName` type on the CRD. diff --git a/rust/operator-binary/src/crd/affinity.rs b/rust/operator-binary/src/crd/affinity.rs index e7d32de2..5ce569cb 100644 --- a/rust/operator-binary/src/crd/affinity.rs +++ b/rust/operator-binary/src/crd/affinity.rs @@ -1,13 +1,34 @@ use stackable_operator::{ - commons::affinity::{StackableAffinityFragment, affinity_between_role_pods}, - k8s_openapi::api::core::v1::PodAntiAffinity, + commons::{ + affinity::{StackableAffinityFragment, affinity_between_role_pods}, + opa::OpaConfig, + }, + k8s_openapi::api::core::v1::{PodAffinity, PodAntiAffinity}, }; use crate::crd::{APP_NAME, HiveRole}; -pub fn get_affinity(cluster_name: &str, role: &HiveRole) -> StackableAffinityFragment { +pub fn get_affinity( + cluster_name: &str, + role: &HiveRole, + opa_config: Option<&OpaConfig>, +) -> StackableAffinityFragment { + // With OPA authorization configured, the metastore sends its authorization requests to OPA, so + // prefer to place it next to the OPA Pods. + let pod_affinity = opa_config.map(|opa_config| PodAffinity { + preferred_during_scheduling_ignored_during_execution: Some(vec![ + affinity_between_role_pods( + "opa", + &opa_config.config_map_name, // The discovery cm has the same name as the OpaCluster itself + "server", + 50, + ), + ]), + required_during_scheduling_ignored_during_execution: None, + }); + StackableAffinityFragment { - pod_affinity: None, + pod_affinity, pod_anti_affinity: Some(PodAntiAffinity { preferred_during_scheduling_ignored_during_execution: Some(vec![ affinity_between_role_pods(APP_NAME, cluster_name, &role.to_string(), 70), @@ -27,7 +48,9 @@ mod tests { use stackable_operator::{ commons::affinity::StackableAffinity, k8s_openapi::{ - api::core::v1::{PodAffinityTerm, PodAntiAffinity, WeightedPodAffinityTerm}, + api::core::v1::{ + PodAffinity, PodAffinityTerm, PodAntiAffinity, WeightedPodAffinityTerm, + }, apimachinery::pkg::apis::meta::v1::LabelSelector, }, }; @@ -50,6 +73,10 @@ mod tests { clusterConfig: metadataDatabase: derby: {} + authorization: + opa: + configMapName: simple-opa + package: hive metastore: roleGroups: default: @@ -68,7 +95,32 @@ mod tests { assert_eq!( merged_config.affinity, StackableAffinity { - pod_affinity: None, + pod_affinity: Some(PodAffinity { + preferred_during_scheduling_ignored_during_execution: Some(vec![ + WeightedPodAffinityTerm { + pod_affinity_term: PodAffinityTerm { + label_selector: Some(LabelSelector { + match_labels: Some(BTreeMap::from([ + ("app.kubernetes.io/name".to_string(), "opa".to_string()), + ( + "app.kubernetes.io/instance".to_string(), + "simple-opa".to_string(), + ), + ( + "app.kubernetes.io/component".to_string(), + "server".to_string(), + ), + ])), + ..LabelSelector::default() + }), + topology_key: "kubernetes.io/hostname".to_string(), + ..PodAffinityTerm::default() + }, + weight: 50, + } + ]), + required_during_scheduling_ignored_during_execution: None, + }), pod_anti_affinity: Some(PodAntiAffinity { preferred_during_scheduling_ignored_during_execution: Some(vec![ WeightedPodAffinityTerm { diff --git a/rust/operator-binary/src/crd/mod.rs b/rust/operator-binary/src/crd/mod.rs index 19ba251a..04cf2411 100644 --- a/rust/operator-binary/src/crd/mod.rs +++ b/rust/operator-binary/src/crd/mod.rs @@ -363,7 +363,11 @@ pub struct MetaStoreConfig { } impl MetaStoreConfig { - pub(crate) fn default_config(cluster_name: &str, role: &HiveRole) -> MetaStoreConfigFragment { + pub(crate) fn default_config( + cluster_name: &str, + role: &HiveRole, + opa_config: Option<&OpaConfig>, + ) -> MetaStoreConfigFragment { MetaStoreConfigFragment { warehouse_dir: None, resources: ResourcesFragment { @@ -384,7 +388,7 @@ impl MetaStoreConfig { }, }, logging: product_logging::spec::default_logging(), - affinity: get_affinity(cluster_name, role), + affinity: get_affinity(cluster_name, role, opa_config), graceful_shutdown_timeout: Some(DEFAULT_METASTORE_GRACEFUL_SHUTDOWN_TIMEOUT), } } From 3bf64b189d586884d4c73df0e59617d7b7fc0884 Mon Sep 17 00:00:00 2001 From: Maxi Wittich Date: Tue, 6 Oct 2026 10:57:45 +0200 Subject: [PATCH 2/2] Updating Changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e2a1a9d..e5d7b5a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ All notable changes to this project will be documented in this file. - Add `/ready` endpoint to the operator Deployment, which reports the CRD installation status ([#770]). - Document that the Stackable Hive images restore the `get_table` and `get_table_objects_by_name` Thrift methods removed by HIVE-26537, and assert it in the smoke test ([#766]). - Add support for Hive `4.2.1` ([#766]). -- The metastore now has a default affinity to the OPA Pods when OPA authorization is configured ([#XXX]). +- The metastore now has a default affinity to the OPA Pods when OPA authorization is configured ([#772]). ### Removed @@ -67,6 +67,7 @@ All notable changes to this project will be documented in this file. [#766]: https://github.com/stackabletech/hive-operator/pull/766 [#767]: https://github.com/stackabletech/hive-operator/pull/767 [#770]: https://github.com/stackabletech/hive-operator/pull/770 +[#772]: https://github.com/stackabletech/hive-operator/pull/772 ## [26.7.0] - 2026-07-21