Skip to content

Commit 85fdfae

Browse files
committed
add replicas and pod_overrides to ValidatedRoleGroupConfig
1 parent 6a22f47 commit 85fdfae

2 files changed

Lines changed: 87 additions & 15 deletions

File tree

rust/operator-binary/src/airflow_controller.rs

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::{
55
};
66

77
use const_format::concatcp;
8-
use snafu::{OptionExt, ResultExt, Snafu};
8+
use snafu::{ResultExt, Snafu};
99
use stackable_operator::{
1010
builder::{
1111
self,
@@ -113,9 +113,6 @@ pub struct Ctx {
113113
#[derive(Snafu, Debug, EnumDiscriminants)]
114114
#[strum_discriminants(derive(IntoStaticStr))]
115115
pub enum Error {
116-
#[snafu(display("object defines no airflow config role"))]
117-
NoAirflowRole,
118-
119116
#[snafu(display("failed to apply Service for {rolegroup}"))]
120117
ApplyRoleGroupService {
121118
source: stackable_operator::cluster_resources::Error,
@@ -740,11 +737,6 @@ fn build_server_rolegroup_statefulset(
740737
let authorization_config = &validated_cluster.authorization_config;
741738
let executor = &validated_cluster.executor;
742739

743-
let binding = airflow.get_role(airflow_role);
744-
let role = binding.as_ref().context(NoAirflowRoleSnafu)?;
745-
746-
let rolegroup = role.role_groups.get(&rolegroup_ref.role_group);
747-
748740
let mut pb = PodBuilder::new();
749741
let recommended_object_labels = build_recommended_labels(
750742
airflow,
@@ -868,7 +860,11 @@ fn build_server_rolegroup_statefulset(
868860

869861
let mut pvcs: Option<Vec<PersistentVolumeClaim>> = None;
870862

871-
if let Some(listener_group_name) = airflow.group_listener_name(airflow_role) {
863+
if let Some(listener_group_name) = validated_cluster
864+
.role_configs
865+
.get(airflow_role)
866+
.and_then(|role_config| role_config.group_listener_name.clone())
867+
{
872868
// Listener endpoints for the Webserver role will use persistent volumes
873869
// so that load balancers can hard-code the target addresses. This will
874870
// be the case even when no class is set (and the value defaults to
@@ -981,10 +977,7 @@ fn build_server_rolegroup_statefulset(
981977
}
982978
}
983979
let mut pod_template = pb.build_template();
984-
pod_template.merge_from(role.config.pod_overrides.clone());
985-
if let Some(rolegroup) = rolegroup {
986-
pod_template.merge_from(rolegroup.config.pod_overrides.clone());
987-
}
980+
pod_template.merge_from(validated_rg_config.pod_overrides.clone());
988981

989982
let restarter_label =
990983
Label::try_from(("restarter.stackable.tech/enabled", "true")).context(BuildLabelSnafu)?;
@@ -1018,7 +1011,7 @@ fn build_server_rolegroup_statefulset(
10181011
}
10191012
.to_string(),
10201013
),
1021-
replicas: rolegroup.and_then(|rg| rg.replicas).map(i32::from),
1014+
replicas: validated_rg_config.replicas.map(i32::from),
10221015
selector: LabelSelector {
10231016
match_labels: Some(statefulset_match_labels.into()),
10241017
..LabelSelector::default()

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

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use snafu::{ResultExt, Snafu};
44
use stackable_operator::{
55
commons::product_image_selection::{self, ResolvedProductImage},
66
config::fragment,
7+
k8s_openapi::api::core::v1::PodTemplateSpec,
78
kube::ResourceExt,
89
role_utils::{GenericRoleConfig, RoleGroup},
910
v2::role_utils::{GenericCommonConfig, with_validated_config},
@@ -52,6 +53,8 @@ pub struct ValidatedRoleGroupConfig {
5253
pub merged_config: AirflowConfig,
5354
pub config_overrides: AirflowConfigOverrides,
5455
pub env_overrides: HashMap<String, String>,
56+
pub replicas: Option<u16>,
57+
pub pod_overrides: PodTemplateSpec,
5558
}
5659

5760
/// The validated cluster: proves that config merging succeeded for every role and
@@ -160,6 +163,8 @@ fn validate_role_group(
160163
merged_config: validated.config.config,
161164
config_overrides: validated.config.config_overrides,
162165
env_overrides: validated.config.env_overrides,
166+
replicas: validated.replicas,
167+
pod_overrides: validated.config.pod_overrides,
163168
})
164169
}
165170

@@ -367,4 +372,78 @@ mod tests {
367372
);
368373
assert!(validated.env_overrides.is_empty());
369374
}
375+
376+
/// `replicas` and the role←role-group merged `pod_overrides` are produced by
377+
/// `with_validated_config` and must be carried on `ValidatedRoleGroupConfig`, so the build
378+
/// step reads them from here rather than re-deriving from the raw cluster.
379+
#[test]
380+
fn role_group_carries_merged_pod_overrides_and_replicas() {
381+
let cluster_yaml = r#"
382+
apiVersion: airflow.stackable.tech/v1alpha2
383+
kind: AirflowCluster
384+
metadata:
385+
name: airflow
386+
spec:
387+
image:
388+
productVersion: 3.1.6
389+
clusterConfig:
390+
loadExamples: false
391+
exposeConfig: false
392+
credentialsSecretName: airflow-admin-credentials
393+
metadataDatabase:
394+
postgresql:
395+
host: airflow-postgresql
396+
database: airflow
397+
credentialsSecretName: airflow-postgresql-credentials
398+
webservers:
399+
config: {}
400+
podOverrides:
401+
metadata:
402+
labels:
403+
role-label: role
404+
shared: role
405+
roleGroups:
406+
default:
407+
replicas: 3
408+
config: {}
409+
podOverrides:
410+
metadata:
411+
labels:
412+
rg-label: rg
413+
shared: rg
414+
schedulers:
415+
config: {}
416+
roleGroups:
417+
default:
418+
config: {}
419+
kubernetesExecutors:
420+
config: {}
421+
"#;
422+
let deserializer = serde_yaml::Deserializer::from_str(cluster_yaml);
423+
let cluster: v1alpha2::AirflowCluster =
424+
serde_yaml::with::singleton_map_recursive::deserialize(deserializer).unwrap();
425+
let role = cluster
426+
.get_role(&AirflowRole::Webserver)
427+
.expect("webserver role");
428+
let default_config = AirflowConfig::default_config("airflow", &AirflowRole::Webserver);
429+
let rolegroup = role.role_groups.get("default").expect("default role group");
430+
431+
let validated =
432+
validate_role_group(&role, rolegroup, &default_config).expect("validated role group");
433+
434+
// replicas is carried through from the role group.
435+
assert_eq!(validated.replicas, Some(3));
436+
437+
// pod_overrides is merged role←role-group (role-group wins on shared keys, both levels'
438+
// unique keys survive).
439+
let labels = validated
440+
.pod_overrides
441+
.metadata
442+
.expect("pod override metadata")
443+
.labels
444+
.expect("pod override labels");
445+
assert_eq!(labels.get("role-label"), Some(&"role".to_string()));
446+
assert_eq!(labels.get("rg-label"), Some(&"rg".to_string()));
447+
assert_eq!(labels.get("shared"), Some(&"rg".to_string()));
448+
}
370449
}

0 commit comments

Comments
 (0)