Skip to content

Commit c8e23b8

Browse files
committed
Deploy default and support custom affinities (#337)
# Description *Please add a description here. This will become the commit message of the merge request later.*
1 parent b1b4652 commit c8e23b8

10 files changed

Lines changed: 1199 additions & 15 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### Added
66

77
- Log aggregation added ([#326]).
8+
- Deploy default and support custom affinities ([#337]).
89

910
### Changed
1011

@@ -15,6 +16,7 @@
1516
[#322]: https://github.com/stackabletech/superset-operator/pull/322
1617
[#323]: https://github.com/stackabletech/superset-operator/pull/323
1718
[#326]: https://github.com/stackabletech/superset-operator/pull/326
19+
[#337]: https://github.com/stackabletech/superset-operator/pull/337
1820

1921
## [23.1.0] - 2023-01-23
2022

Cargo.lock

Lines changed: 5 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
11
[workspace]
22
members = ["rust/crd", "rust/operator-binary"]
3+
4+
# [patch."https://github.com/stackabletech/operator-rs.git"]
5+
# stackable-operator = { path = "/home/sbernauer/stackabletech/operator-rs" }
6+
# stackable-operator = { git = "https://github.com/stackabletech//operator-rs.git", branch = "feat/affinities" }

deploy/helm/superset-operator/crds/crds.yaml

Lines changed: 958 additions & 0 deletions
Large diffs are not rendered by default.

docs/modules/superset/pages/usage.adoc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,3 +239,11 @@ nodes:
239239
----
240240

241241
WARNING: The default values are _most likely_ not sufficient to run a proper cluster in production. Please adapt according to your requirements.
242+
243+
=== Pod Placement
244+
245+
You can configure the Pod placement of the Superset pods as described in xref:concepts:pod_placement.adoc[].
246+
247+
The default affinities created by the operator are:
248+
249+
1. Distribute all the Superset Pods (weight 70)

rust/crd/Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ publish = false
1111
[dependencies]
1212
serde = "1.0"
1313
serde_json = "1.0"
14-
stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.35.0" }
14+
stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.36.0" }
1515
strum = { version = "0.24", features = ["derive"] }
1616
snafu = "0.7"
1717
tracing = "0.1"
18+
19+
[dev-dependencies]
20+
serde_yaml = "0.9"

rust/crd/src/affinity.rs

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
use stackable_operator::{
2+
commons::affinity::{affinity_between_role_pods, StackableAffinityFragment},
3+
k8s_openapi::api::core::v1::PodAntiAffinity,
4+
};
5+
6+
use crate::{SupersetRole, APP_NAME};
7+
8+
pub fn get_affinity(cluster_name: &str, role: &SupersetRole) -> StackableAffinityFragment {
9+
StackableAffinityFragment {
10+
pod_affinity: None,
11+
pod_anti_affinity: Some(PodAntiAffinity {
12+
preferred_during_scheduling_ignored_during_execution: Some(vec![
13+
affinity_between_role_pods(APP_NAME, cluster_name, &role.to_string(), 70),
14+
]),
15+
required_during_scheduling_ignored_during_execution: None,
16+
}),
17+
node_affinity: None,
18+
node_selector: None,
19+
}
20+
}
21+
22+
#[cfg(test)]
23+
mod tests {
24+
use super::*;
25+
26+
use std::collections::BTreeMap;
27+
28+
use crate::SupersetCluster;
29+
use stackable_operator::{
30+
commons::affinity::{StackableAffinity, StackableNodeSelector},
31+
k8s_openapi::{
32+
api::core::v1::{
33+
NodeAffinity, NodeSelector, NodeSelectorRequirement, NodeSelectorTerm,
34+
PodAffinityTerm, PodAntiAffinity, WeightedPodAffinityTerm,
35+
},
36+
apimachinery::pkg::apis::meta::v1::LabelSelector,
37+
},
38+
};
39+
40+
#[test]
41+
fn test_affinity_defaults() {
42+
let input = r#"
43+
apiVersion: superset.stackable.tech/v1alpha1
44+
kind: SupersetCluster
45+
metadata:
46+
name: simple-superset
47+
spec:
48+
image:
49+
productVersion: 1.5.1
50+
stackableVersion: "23.1"
51+
credentialsSecret: simple-superset-credentials
52+
nodes:
53+
roleGroups:
54+
default:
55+
replicas: 1
56+
"#;
57+
let superset: SupersetCluster = serde_yaml::from_str(input).expect("illegal test input");
58+
let merged_config = superset
59+
.merged_config(&SupersetRole::Node, &superset.node_rolegroup_ref("default"))
60+
.unwrap();
61+
62+
assert_eq!(
63+
merged_config.affinity,
64+
StackableAffinity {
65+
pod_affinity: None,
66+
pod_anti_affinity: Some(PodAntiAffinity {
67+
preferred_during_scheduling_ignored_during_execution: Some(vec![
68+
WeightedPodAffinityTerm {
69+
pod_affinity_term: PodAffinityTerm {
70+
label_selector: Some(LabelSelector {
71+
match_expressions: None,
72+
match_labels: Some(BTreeMap::from([
73+
(
74+
"app.kubernetes.io/name".to_string(),
75+
"superset".to_string(),
76+
),
77+
(
78+
"app.kubernetes.io/instance".to_string(),
79+
"simple-superset".to_string(),
80+
),
81+
(
82+
"app.kubernetes.io/component".to_string(),
83+
"node".to_string(),
84+
)
85+
]))
86+
}),
87+
namespace_selector: None,
88+
namespaces: None,
89+
topology_key: "kubernetes.io/hostname".to_string(),
90+
},
91+
weight: 70
92+
}
93+
]),
94+
required_during_scheduling_ignored_during_execution: None,
95+
}),
96+
node_affinity: None,
97+
node_selector: None,
98+
}
99+
);
100+
}
101+
102+
#[test]
103+
fn test_affinity_legacy_node_selector() {
104+
let input = r#"
105+
apiVersion: superset.stackable.tech/v1alpha1
106+
kind: SupersetCluster
107+
metadata:
108+
name: simple-superset
109+
spec:
110+
image:
111+
productVersion: 1.5.1
112+
stackableVersion: "23.1"
113+
credentialsSecret: simple-superset-credentials
114+
nodes:
115+
roleGroups:
116+
default:
117+
replicas: 1
118+
selector:
119+
matchLabels:
120+
disktype: ssd
121+
matchExpressions:
122+
- key: topology.kubernetes.io/zone
123+
operator: In
124+
values:
125+
- antarctica-east1
126+
- antarctica-west1
127+
"#;
128+
let superset: SupersetCluster = serde_yaml::from_str(input).expect("illegal test input");
129+
let merged_config = superset
130+
.merged_config(&SupersetRole::Node, &superset.node_rolegroup_ref("default"))
131+
.unwrap();
132+
133+
assert_eq!(
134+
merged_config.affinity,
135+
StackableAffinity {
136+
pod_affinity: None,
137+
pod_anti_affinity: Some(PodAntiAffinity {
138+
preferred_during_scheduling_ignored_during_execution: Some(vec![
139+
WeightedPodAffinityTerm {
140+
pod_affinity_term: PodAffinityTerm {
141+
label_selector: Some(LabelSelector {
142+
match_expressions: None,
143+
match_labels: Some(BTreeMap::from([
144+
(
145+
"app.kubernetes.io/name".to_string(),
146+
"superset".to_string(),
147+
),
148+
(
149+
"app.kubernetes.io/instance".to_string(),
150+
"simple-superset".to_string(),
151+
),
152+
(
153+
"app.kubernetes.io/component".to_string(),
154+
"node".to_string(),
155+
)
156+
]))
157+
}),
158+
namespace_selector: None,
159+
namespaces: None,
160+
topology_key: "kubernetes.io/hostname".to_string(),
161+
},
162+
weight: 70
163+
}
164+
]),
165+
required_during_scheduling_ignored_during_execution: None,
166+
}),
167+
node_affinity: Some(NodeAffinity {
168+
preferred_during_scheduling_ignored_during_execution: None,
169+
required_during_scheduling_ignored_during_execution: Some(NodeSelector {
170+
node_selector_terms: vec![NodeSelectorTerm {
171+
match_expressions: Some(vec![NodeSelectorRequirement {
172+
key: "topology.kubernetes.io/zone".to_string(),
173+
operator: "In".to_string(),
174+
values: Some(vec![
175+
"antarctica-east1".to_string(),
176+
"antarctica-west1".to_string()
177+
]),
178+
}]),
179+
match_fields: None,
180+
}]
181+
}),
182+
}),
183+
node_selector: Some(StackableNodeSelector {
184+
node_selector: BTreeMap::from([("disktype".to_string(), "ssd".to_string())])
185+
}),
186+
}
187+
);
188+
}
189+
}

rust/crd/src/lib.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1+
pub mod affinity;
12
pub mod druidconnection;
23
pub mod supersetdb;
34

5+
use affinity::get_affinity;
46
use serde::{Deserialize, Serialize};
57
use snafu::{OptionExt, ResultExt, Snafu};
8+
use stackable_operator::commons::affinity::StackableAffinity;
69
use stackable_operator::commons::product_image_selection::ProductImage;
10+
use stackable_operator::kube::ResourceExt;
11+
use stackable_operator::role_utils::RoleGroup;
712
use stackable_operator::{
813
commons::resources::{
914
CpuLimitsFragment, MemoryLimitsFragment, NoRuntimeLimits, NoRuntimeLimitsFragment,
@@ -316,13 +321,15 @@ pub struct SupersetConfig {
316321
pub resources: Resources<SupersetStorageConfig, NoRuntimeLimits>,
317322
#[fragment_attrs(serde(default))]
318323
pub logging: Logging<Container>,
324+
#[fragment_attrs(serde(default))]
325+
pub affinity: StackableAffinity,
319326
}
320327

321328
impl SupersetConfig {
322329
pub const CREDENTIALS_SECRET_PROPERTY: &'static str = "credentialsSecret";
323330
pub const MAPBOX_SECRET_PROPERTY: &'static str = "mapboxSecret";
324331

325-
fn default_config() -> SupersetConfigFragment {
332+
fn default_config(cluster_name: &str, role: &SupersetRole) -> SupersetConfigFragment {
326333
SupersetConfigFragment {
327334
resources: ResourcesFragment {
328335
cpu: CpuLimitsFragment {
@@ -336,6 +343,7 @@ impl SupersetConfig {
336343
storage: SupersetStorageConfigFragment {},
337344
},
338345
logging: product_logging::spec::default_logging(),
346+
affinity: get_affinity(cluster_name, role),
339347
..Default::default()
340348
}
341349
}
@@ -421,7 +429,7 @@ impl SupersetCluster {
421429
rolegroup_ref: &RoleGroupRef<SupersetCluster>,
422430
) -> Result<SupersetConfig, Error> {
423431
// Initialize the result with all default values as baseline
424-
let conf_defaults = SupersetConfig::default_config();
432+
let conf_defaults = SupersetConfig::default_config(&self.name_any(), role);
425433

426434
let role = match role {
427435
SupersetRole::Node => self.spec.nodes.as_ref().context(UnknownSupersetRoleSnafu {
@@ -440,6 +448,17 @@ impl SupersetCluster {
440448
.map(|rg| rg.config.config.clone())
441449
.unwrap_or_default();
442450

451+
if let Some(RoleGroup {
452+
selector: Some(selector),
453+
..
454+
}) = role.role_groups.get(&rolegroup_ref.role_group)
455+
{
456+
// Migrate old `selector` attribute, see ADR 26 affinities.
457+
// TODO Can be removed after support for the old `selector` field is dropped.
458+
#[allow(deprecated)]
459+
conf_rolegroup.affinity.add_legacy_selector(selector);
460+
}
461+
443462
// Merge more specific configs into default config
444463
// Hierarchy is:
445464
// 1. RoleGroup

rust/operator-binary/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ futures = { version = "0.3", features = ["compat"] }
1616
indoc = "1.0"
1717
serde = "1.0"
1818
snafu = "0.7"
19-
stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.35.0" }
19+
stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.36.0" }
2020
stackable-superset-crd = { path = "../crd" }
2121
strum = { version = "0.24", features = ["derive"] }
2222
tokio = { version = "1.25", features = ["macros", "rt-multi-thread"] }
2323
tracing = "0.1"
2424

2525
[build-dependencies]
2626
built = { version = "0.5", features = ["chrono", "git2"] }
27-
stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.35.0" }
27+
stackable-operator = { git = "https://github.com/stackabletech/operator-rs.git", tag = "0.36.0" }
2828
stackable-superset-crd = { path = "../crd" }

0 commit comments

Comments
 (0)