Skip to content

Commit 08ca514

Browse files
authored
feat: Add affinity to OPA Pods (#1002)
* feat: Add affinity to OPA Pods * Updating Changelog
1 parent 152b8e4 commit 08ca514

6 files changed

Lines changed: 83 additions & 9 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file.
1010
([#994]).
1111
- Add support for 2.12.0 ([#997]).
1212
- Add `/ready` endpoint to the operator Deployment, which reports the CRD installation status ([#999]).
13+
- Nodes now have a default affinity to the OPA Pods when OPA authorization is configured ([#1002]).
1314

1415
### Changed
1516

@@ -73,6 +74,7 @@ All notable changes to this project will be documented in this file.
7374
[#994]: https://github.com/stackabletech/nifi-operator/pull/994
7475
[#997]: https://github.com/stackabletech/nifi-operator/pull/997
7576
[#999]: https://github.com/stackabletech/nifi-operator/pull/999
77+
[#1002]: https://github.com/stackabletech/nifi-operator/pull/1002
7678

7779
## [26.7.0] - 2026-07-21
7880

‎docs/modules/nifi/pages/usage_guide/operations/pod-placement.adoc‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ You can configure the Pod placement of the NiFi pods as described in xref:concep
55
The default affinities created by the operator are:
66

77
1. Distribute all the NiFi Pods (weight 70)
8+
2. If OPA authorization is configured: co-locate the NiFi Pods with the OPA Pods (weight 50)

‎rust/operator-binary/src/controller/validate.rs‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,11 @@ pub(crate) fn build_role_group_configs(
298298
vector_aggregator_config_map_name: &Option<ConfigMapName>,
299299
) -> Result<BTreeMap<NifiRole, BTreeMap<RoleGroupName, NifiRoleGroupConfig>>> {
300300
let role = &nifi.spec.nodes;
301-
let default_config = NifiConfig::default_config(&nifi.name_any(), &NifiRole::Node);
301+
let default_config = NifiConfig::default_config(
302+
&nifi.name_any(),
303+
&NifiRole::Node,
304+
nifi.spec.cluster_config.authorization.opa_config(),
305+
);
302306

303307
let mut groups: BTreeMap<RoleGroupName, NifiRoleGroupConfig> = BTreeMap::new();
304308
for (rg_name, rg) in &role.role_groups {

‎rust/operator-binary/src/crd/affinity.rs‎

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,34 @@
11
use stackable_operator::{
2-
commons::affinity::{StackableAffinityFragment, affinity_between_role_pods},
3-
k8s_openapi::api::core::v1::PodAntiAffinity,
2+
commons::{
3+
affinity::{StackableAffinityFragment, affinity_between_role_pods},
4+
opa::OpaConfig,
5+
},
6+
k8s_openapi::api::core::v1::{PodAffinity, PodAntiAffinity},
47
};
58

69
use crate::crd::{APP_NAME, NifiRole};
710

8-
pub fn get_affinity(cluster_name: &str, role: &NifiRole) -> StackableAffinityFragment {
11+
pub fn get_affinity(
12+
cluster_name: &str,
13+
role: &NifiRole,
14+
opa_config: Option<&OpaConfig>,
15+
) -> StackableAffinityFragment {
16+
// With OPA authorization configured, NiFi sends its authorization requests to OPA, so prefer
17+
// to place it next to the OPA Pods.
18+
let pod_affinity = opa_config.map(|opa_config| PodAffinity {
19+
preferred_during_scheduling_ignored_during_execution: Some(vec![
20+
affinity_between_role_pods(
21+
"opa",
22+
&opa_config.config_map_name, // The discovery cm has the same name as the OpaCluster itself
23+
"server",
24+
50,
25+
),
26+
]),
27+
required_during_scheduling_ignored_during_execution: None,
28+
});
29+
930
StackableAffinityFragment {
10-
pod_affinity: None,
31+
pod_affinity,
1132
pod_anti_affinity: Some(PodAntiAffinity {
1233
preferred_during_scheduling_ignored_during_execution: Some(vec![
1334
affinity_between_role_pods(APP_NAME, cluster_name, &role.to_string(), 70),
@@ -26,7 +47,9 @@ mod tests {
2647
use stackable_operator::{
2748
commons::affinity::StackableAffinity,
2849
k8s_openapi::{
29-
api::core::v1::{PodAffinityTerm, PodAntiAffinity, WeightedPodAffinityTerm},
50+
api::core::v1::{
51+
PodAffinity, PodAffinityTerm, PodAntiAffinity, WeightedPodAffinityTerm,
52+
},
3053
apimachinery::pkg::apis::meta::v1::LabelSelector,
3154
},
3255
v2::types::operator::RoleGroupName,
@@ -54,6 +77,10 @@ mod tests {
5477
sensitiveProperties:
5578
keySecret: simple-nifi-sensitive-property-key
5679
autoGenerate: true
80+
authorization:
81+
opa:
82+
configMapName: simple-opa
83+
package: nifi
5784
nodes:
5885
roleGroups:
5986
default:
@@ -80,7 +107,32 @@ mod tests {
80107
assert_eq!(
81108
merged_config.affinity,
82109
StackableAffinity {
83-
pod_affinity: None,
110+
pod_affinity: Some(PodAffinity {
111+
preferred_during_scheduling_ignored_during_execution: Some(vec![
112+
WeightedPodAffinityTerm {
113+
pod_affinity_term: PodAffinityTerm {
114+
label_selector: Some(LabelSelector {
115+
match_expressions: None,
116+
match_labels: Some(BTreeMap::from([
117+
("app.kubernetes.io/name".to_string(), "opa".to_string()),
118+
(
119+
"app.kubernetes.io/instance".to_string(),
120+
"simple-opa".to_string(),
121+
),
122+
(
123+
"app.kubernetes.io/component".to_string(),
124+
"server".to_string(),
125+
),
126+
])),
127+
}),
128+
topology_key: "kubernetes.io/hostname".to_string(),
129+
..Default::default()
130+
},
131+
weight: 50,
132+
}
133+
]),
134+
required_during_scheduling_ignored_during_execution: None,
135+
}),
84136
pod_anti_affinity: Some(PodAntiAffinity {
85137
preferred_during_scheduling_ignored_during_execution: Some(vec![
86138
WeightedPodAffinityTerm {

‎rust/operator-binary/src/crd/authorization.rs‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ impl Default for NifiAuthorization {
2424
}
2525
}
2626

27+
impl NifiAuthorization {
28+
/// The OPA config, if OPA is the configured authorizer.
29+
pub fn opa_config(&self) -> Option<&OpaConfig> {
30+
match self {
31+
Self::Opa { opa } => Some(&opa.opa),
32+
Self::SingleUser {} | Self::Standard { .. } => None,
33+
}
34+
}
35+
}
36+
2737
#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
2838
#[serde(rename_all = "camelCase")]
2939
pub struct NifiOpaConfig {

‎rust/operator-binary/src/crd/mod.rs‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use stackable_operator::{
1414
commons::{
1515
affinity::StackableAffinity,
1616
cluster_operation::ClusterOperation,
17+
opa::OpaConfig,
1718
product_image_selection::ProductImage,
1819
resources::{
1920
CpuLimitsFragment, MemoryLimitsFragment, NoRuntimeLimits, NoRuntimeLimitsFragment,
@@ -311,7 +312,11 @@ impl NifiConfig {
311312
// Auto TLS certificate lifetime
312313
const DEFAULT_NODE_SECRET_LIFETIME: Duration = Duration::from_days_unchecked(1);
313314

314-
pub fn default_config(cluster_name: &str, role: &NifiRole) -> NifiConfigFragment {
315+
pub fn default_config(
316+
cluster_name: &str,
317+
role: &NifiRole,
318+
opa_config: Option<&OpaConfig>,
319+
) -> NifiConfigFragment {
315320
NifiConfigFragment {
316321
logging: product_logging::spec::default_logging(),
317322
resources: ResourcesFragment {
@@ -356,7 +361,7 @@ impl NifiConfig {
356361
},
357362
},
358363
},
359-
affinity: get_affinity(cluster_name, role),
364+
affinity: get_affinity(cluster_name, role, opa_config),
360365
graceful_shutdown_timeout: Some(DEFAULT_NODE_GRACEFUL_SHUTDOWN_TIMEOUT),
361366
requested_secret_lifetime: Some(Self::DEFAULT_NODE_SECRET_LIFETIME),
362367
}

0 commit comments

Comments
 (0)