Skip to content

Commit 9d92a57

Browse files
committed
fix: introduce ValidatedAirflowConfig, fold logging
1 parent 48c23a7 commit 9d92a57

6 files changed

Lines changed: 87 additions & 79 deletions

File tree

rust/operator-binary/src/airflow_controller.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -345,20 +345,16 @@ pub async fn reconcile_airflow(
345345
}
346346
}
347347

348-
for (rolegroup_name, validated_rg) in role_group_configs {
349-
let validated_rg_config = &validated_rg.config;
350-
let logging = &validated_rg.logging;
348+
for (rolegroup_name, validated_rg_config) in role_group_configs {
349+
let logging = &validated_rg_config.config.logging;
351350

352351
let git_sync_resources = git_sync::v1alpha2::GitSyncResources::new(
353352
&validated_cluster.cluster_config.dags_git_sync,
354353
&validated_cluster.image,
355354
&Vec::<EnvVar>::from(validated_rg_config.env_overrides.clone()),
356355
&airflow.volume_mounts(),
357356
LOG_VOLUME_NAME.as_ref(),
358-
&validated_rg_config
359-
.config
360-
.logging
361-
.for_container(&Container::GitSync),
357+
&logging.git_sync_container,
362358
)
363359
.context(InvalidGitSyncSpecSnafu)?;
364360

rust/operator-binary/src/controller/build/resource/executor.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,7 @@ pub fn build_executor_template_config_map(
157157
.resource_names(&executor_role_name(), &executor_role_group_name())
158158
.role_group_config_map()
159159
.as_ref(),
160-
merged_executor_config
161-
.logging
162-
.containers
163-
.get(&Container::Airflow),
160+
&executor_logging.product_container,
164161
))
165162
.context(AddVolumeSnafu)?;
166163

rust/operator-binary/src/controller/build/resource/statefulset.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@ use stackable_operator::{
33
builder::{
44
meta::ObjectMetaBuilder,
55
pod::{
6-
PodBuilder,
7-
resources::ResourceRequirementsBuilder,
8-
security::PodSecurityContextBuilder,
9-
volume::VolumeBuilder,
6+
PodBuilder, resources::ResourceRequirementsBuilder,
7+
security::PodSecurityContextBuilder, volume::VolumeBuilder,
108
},
119
},
1210
crd::git_sync,
@@ -47,8 +45,8 @@ use crate::{
4745
},
4846
controller_commons::{self, CONFIG_VOLUME_NAME, LOG_CONFIG_VOLUME_NAME, LOG_VOLUME_NAME},
4947
crd::{
50-
AirflowExecutor, AirflowRole, CONFIG_PATH, Container, HTTP_PORT_NAME, LISTENER_VOLUME_DIR,
51-
LISTENER_PVC_NAME, LOG_CONFIG_DIR, METRICS_CONTAINER_NAME, METRICS_PORT,
48+
AirflowExecutor, AirflowRole, CONFIG_PATH, Container, HTTP_PORT_NAME, LISTENER_PVC_NAME,
49+
LISTENER_VOLUME_DIR, LOG_CONFIG_DIR, METRICS_CONTAINER_NAME, METRICS_PORT,
5250
METRICS_PORT_NAME, STACKABLE_LOG_DIR, TEMPLATE_LOCATION, TEMPLATE_VOLUME_NAME,
5351
},
5452
};
@@ -304,10 +302,7 @@ pub fn build_server_rolegroup_statefulset(
304302
.context(AddVolumeSnafu)?;
305303
pb.add_volumes(controller_commons::create_volumes(
306304
resource_names.role_group_config_map().as_ref(),
307-
merged_airflow_config
308-
.logging
309-
.containers
310-
.get(&Container::Airflow),
305+
&logging.product_container,
311306
))
312307
.context(AddVolumeSnafu)?;
313308

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

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,20 @@ use std::{collections::BTreeMap, str::FromStr};
22

33
use stackable_operator::{
44
builder::meta::ObjectMetaBuilder,
5-
commons::product_image_selection::ResolvedProductImage,
5+
commons::{
6+
affinity::StackableAffinity,
7+
product_image_selection::ResolvedProductImage,
8+
resources::{NoRuntimeLimits, Resources},
9+
},
610
crd::git_sync,
711
database_connections::drivers::{
812
celery::CeleryDatabaseConnectionDetails, sqlalchemy::SqlAlchemyDatabaseConnectionDetails,
913
},
1014
k8s_openapi::api::core::v1::{Volume, VolumeMount},
1115
kube::{Resource, ResourceExt, api::ObjectMeta},
1216
kvp::Labels,
17+
product_logging::spec::ContainerLogConfig,
18+
shared::time::Duration,
1319
v2::{
1420
HasName, HasUid, NameIsValidLabelValue,
1521
builder::meta::ownerreference_from_resource,
@@ -32,7 +38,8 @@ use crate::{
3238
airflow_controller::AIRFLOW_CONTROLLER_NAME,
3339
crd::{
3440
APP_NAME, AirflowConfig, AirflowConfigOverrides, AirflowExecutor, AirflowRole,
35-
OPERATOR_NAME, authentication::AirflowClientAuthenticationDetailsResolved,
41+
AirflowStorageConfig, OPERATOR_NAME,
42+
authentication::AirflowClientAuthenticationDetailsResolved,
3643
authorization::AirflowAuthorizationResolved, v1alpha2,
3744
},
3845
};
@@ -56,32 +63,45 @@ pub struct ValidatedRoleConfig {
5663
/// `env_overrides`/`cli_overrides`/`pod_overrides`. The config overrides are kept typed
5764
/// ([`AirflowConfigOverrides`]) and flattened into the rendered config file later, in the build step.
5865
pub type AirflowRoleGroupConfig = stackable_operator::v2::role_utils::RoleGroupConfig<
59-
AirflowConfig,
66+
ValidatedAirflowConfig,
6067
stackable_operator::v2::role_utils::GenericCommonConfig,
6168
AirflowConfigOverrides,
6269
>;
6370

64-
/// A validated role group: the merged [`AirflowRoleGroupConfig`] paired with its up-front-validated
65-
/// [`ValidatedLogging`], so the build step reads both from here rather than re-deriving from the raw
66-
/// cluster. (Superset folds logging into a single rolegroup struct; airflow keeps the generic merged
67-
/// config as-is — retaining all its fields without bespoke `#[allow(dead_code)]` — and carries
68-
/// logging alongside it.)
69-
#[derive(Clone, Debug)]
70-
pub struct AirflowRoleGroup {
71-
pub config: AirflowRoleGroupConfig,
71+
/// A validated, merged Airflow role-group config: the merged [`AirflowConfig`] with its raw
72+
/// `logging` replaced by the up-front-validated [`ValidatedLogging`] (so an invalid custom log
73+
/// ConfigMap name or a missing Vector aggregator name fails reconciliation during validation).
74+
#[derive(Clone, Debug, PartialEq)]
75+
pub struct ValidatedAirflowConfig {
76+
pub resources: Resources<AirflowStorageConfig, NoRuntimeLimits>,
7277
pub logging: ValidatedLogging,
78+
pub affinity: StackableAffinity,
79+
pub graceful_shutdown_timeout: Option<Duration>,
7380
}
7481

75-
/// Validated logging configuration for the product container and the (optional) Vector container.
76-
///
82+
impl ValidatedAirflowConfig {
83+
/// Builds the validated config from the merged [`AirflowConfig`], swapping in the
84+
/// already-validated logging.
85+
pub(crate) fn from_merged(merged: AirflowConfig, logging: ValidatedLogging) -> Self {
86+
Self {
87+
resources: merged.resources,
88+
logging,
89+
affinity: merged.affinity,
90+
graceful_shutdown_timeout: merged.graceful_shutdown_timeout,
91+
}
92+
}
93+
}
94+
95+
/// Validated logging configuration for the containers of a role-group (or Kubernetes-executor) Pod.
7796
///
7897
/// `product_container` holds the validated log-config choice of the product's main container
7998
/// (`Container::Airflow` for the role groups, `Container::Base` for the Kubernetes-executor pod
80-
/// template).
81-
#[derive(Clone, Debug, PartialEq, Eq)]
99+
/// template). `git_sync_container` holds the log config of the git-sync sidecar (DAG fetching).
100+
#[derive(Clone, Debug, PartialEq)]
82101
pub struct ValidatedLogging {
83102
pub product_container: ValidatedContainerLogConfigChoice,
84103
pub vector_container: Option<VectorContainerLogConfig>,
104+
pub git_sync_container: ContainerLogConfig,
85105
pub enable_vector_agent: bool,
86106
}
87107

@@ -132,7 +152,7 @@ pub struct ValidatedCluster {
132152
pub product_version: ProductVersion,
133153
pub image: ResolvedProductImage,
134154
pub cluster_config: ValidatedClusterConfig,
135-
pub role_groups: BTreeMap<AirflowRole, BTreeMap<RoleGroupName, AirflowRoleGroup>>,
155+
pub role_groups: BTreeMap<AirflowRole, BTreeMap<RoleGroupName, AirflowRoleGroupConfig>>,
136156
pub role_configs: BTreeMap<AirflowRole, ValidatedRoleConfig>,
137157
}
138158

@@ -143,7 +163,7 @@ impl ValidatedCluster {
143163
uid: Uid,
144164
image: ResolvedProductImage,
145165
cluster_config: ValidatedClusterConfig,
146-
role_groups: BTreeMap<AirflowRole, BTreeMap<RoleGroupName, AirflowRoleGroup>>,
166+
role_groups: BTreeMap<AirflowRole, BTreeMap<RoleGroupName, AirflowRoleGroupConfig>>,
147167
role_configs: BTreeMap<AirflowRole, ValidatedRoleConfig>,
148168
) -> Self {
149169
// `app_version_label_value` is constructed to be a valid label value, so it is also a valid

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

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use stackable_operator::{
3030
use strum::IntoEnumIterator;
3131

3232
use super::{
33-
AirflowRoleGroup, AirflowRoleGroupConfig, ValidatedCluster, ValidatedClusterConfig,
33+
AirflowRoleGroupConfig, ValidatedAirflowConfig, ValidatedCluster, ValidatedClusterConfig,
3434
ValidatedLogging, ValidatedRoleConfig, dereference::DereferencedObjects,
3535
};
3636
use crate::{
@@ -152,15 +152,15 @@ pub fn validate_cluster(
152152
role_group: rolegroup_name.clone(),
153153
}
154154
})?;
155-
let config =
156-
validate_role_group(&resolved_role, &role_group_name, rolegroup, &default_config)?;
157-
let logging = validate_logging(
158-
&config.config.logging,
159-
&Container::Airflow,
155+
let config = validate_role_group(
156+
&resolved_role,
157+
&role_group_name,
158+
rolegroup,
159+
&default_config,
160160
&vector_aggregator_config_map_name,
161161
)?;
162162

163-
group_configs.insert(role_group_name, AirflowRoleGroup { config, logging });
163+
group_configs.insert(role_group_name, config);
164164
}
165165

166166
role_groups.insert(role, group_configs);
@@ -274,6 +274,7 @@ fn validate_role_group(
274274
role_group_name: &RoleGroupName,
275275
rolegroup: &RoleGroup<AirflowConfigFragment, GenericCommonConfig, AirflowConfigOverrides>,
276276
default_config: &AirflowConfigFragment,
277+
vector_aggregator_config_map_name: &Option<ConfigMapName>,
277278
) -> Result<AirflowRoleGroupConfig, Error> {
278279
let validated = with_validated_config::<
279280
AirflowConfig,
@@ -294,9 +295,16 @@ fn validate_role_group(
294295
);
295296
}
296297

298+
let merged_config = validated.config.config;
299+
let logging = validate_logging(
300+
&merged_config.logging,
301+
&Container::Airflow,
302+
vector_aggregator_config_map_name,
303+
)?;
304+
297305
Ok(RoleGroupConfig {
298306
replicas: validated.replicas,
299-
config: validated.config.config,
307+
config: ValidatedAirflowConfig::from_merged(merged_config, logging),
300308
config_overrides: validated.config.config_overrides,
301309
env_overrides,
302310
cli_overrides: validated.config.cli_overrides,
@@ -340,6 +348,7 @@ pub(crate) fn validate_logging(
340348
Ok(ValidatedLogging {
341349
product_container,
342350
vector_container,
351+
git_sync_container: logging.for_container(&Container::GitSync).into_owned(),
343352
enable_vector_agent: logging.enable_vector_agent,
344353
})
345354
}
@@ -414,6 +423,7 @@ mod tests {
414423
&"default".parse().expect("valid role group name"),
415424
rolegroup,
416425
&default_config,
426+
&None,
417427
)
418428
.expect("validated role group");
419429
let config_overrides = validated.config_overrides;
@@ -511,6 +521,7 @@ mod tests {
511521
&"default".parse().expect("valid role group name"),
512522
rolegroup,
513523
&default_config,
524+
&None,
514525
)
515526
.expect("validated role group");
516527

@@ -584,6 +595,7 @@ mod tests {
584595
&"default".parse().expect("valid role group name"),
585596
rolegroup,
586597
&default_config,
598+
&None,
587599
)
588600
.expect("validated role group");
589601

rust/operator-binary/src/controller_commons.rs

Lines changed: 21 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,11 @@ use std::str::FromStr;
33
use stackable_operator::{
44
builder::pod::volume::VolumeBuilder,
55
k8s_openapi::api::core::v1::{ConfigMapVolumeSource, EmptyDirVolumeSource, Volume},
6-
product_logging::{
7-
self,
8-
spec::{
9-
ConfigMapLogConfig, ContainerLogConfig, ContainerLogConfigChoice,
10-
CustomContainerLogConfig,
11-
},
6+
product_logging,
7+
v2::{
8+
product_logging::framework::ValidatedContainerLogConfigChoice,
9+
types::kubernetes::VolumeName,
1210
},
13-
v2::types::kubernetes::VolumeName,
1411
};
1512

1613
use crate::crd::MAX_LOG_FILES_SIZE;
@@ -21,7 +18,7 @@ stackable_operator::constant!(pub LOG_VOLUME_NAME: VolumeName = "log");
2118

2219
pub fn create_volumes(
2320
config_map_name: &str,
24-
log_config: Option<&ContainerLogConfig>,
21+
product_log_config: &ValidatedContainerLogConfigChoice,
2522
) -> Vec<Volume> {
2623
let mut volumes = Vec::new();
2724

@@ -41,31 +38,22 @@ pub fn create_volumes(
4138
..Volume::default()
4239
});
4340

44-
if let Some(ContainerLogConfig {
45-
choice:
46-
Some(ContainerLogConfigChoice::Custom(CustomContainerLogConfig {
47-
custom: ConfigMapLogConfig { config_map },
48-
})),
49-
}) = log_config
50-
{
51-
volumes.push(Volume {
52-
name: LOG_CONFIG_VOLUME_NAME.to_string(),
53-
config_map: Some(ConfigMapVolumeSource {
54-
name: config_map.into(),
55-
..ConfigMapVolumeSource::default()
56-
}),
57-
..Volume::default()
58-
});
59-
} else {
60-
volumes.push(Volume {
61-
name: LOG_CONFIG_VOLUME_NAME.to_string(),
62-
config_map: Some(ConfigMapVolumeSource {
63-
name: config_map_name.into(),
64-
..ConfigMapVolumeSource::default()
65-
}),
66-
..Volume::default()
67-
});
68-
}
41+
// A custom log config is mounted from its own ConfigMap; an automatic one is rendered into the
42+
// rolegroup ConfigMap.
43+
let log_config_config_map = match product_log_config {
44+
ValidatedContainerLogConfigChoice::Custom(custom_config_map) => {
45+
custom_config_map.to_string()
46+
}
47+
ValidatedContainerLogConfigChoice::Automatic(_) => config_map_name.to_string(),
48+
};
49+
volumes.push(Volume {
50+
name: LOG_CONFIG_VOLUME_NAME.to_string(),
51+
config_map: Some(ConfigMapVolumeSource {
52+
name: log_config_config_map,
53+
..ConfigMapVolumeSource::default()
54+
}),
55+
..Volume::default()
56+
});
6957

7058
volumes
7159
}

0 commit comments

Comments
 (0)