Skip to content

Commit 7a0def1

Browse files
committed
refactor: remove calculated inputs from Validated* structs
1 parent 70f26ae commit 7a0def1

6 files changed

Lines changed: 105 additions & 96 deletions

File tree

rust/operator-binary/src/extensions.rs renamed to rust/operator-binary/src/controller/build/properties/extensions.rs

Lines changed: 65 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1+
//! Builder for the `druid.extensions.loadList` runtime.properties entry.
2+
//!
3+
//! The set of Druid extensions to load is derived from the metadata database, TLS, S3 and
4+
//! authentication settings carried on the validated cluster. This is a build-step concern: it
5+
//! computes a config output, so it lives here rather than in the validate step.
6+
17
use std::collections::HashSet;
28

39
use tracing::debug;
410

5-
use crate::{
6-
authentication::DruidAuthenticationConfig,
7-
crd::{database::MetadataDatabaseConnection, security::DruidTlsSecurity, v1alpha1},
8-
};
11+
use crate::{authentication::DruidAuthenticationConfig, crd::database::MetadataDatabaseConnection};
912

1013
const EXT_S3: &str = "druid-s3-extensions";
1114
const EXT_KAFKA_INDEXING: &str = "druid-kafka-indexing-service";
@@ -19,9 +22,16 @@ const EXT_HDFS: &str = "druid-hdfs-storage";
1922
const EXT_SIMPLE_CLIENT_SSL_CONTEXT: &str = "simple-client-sslcontext";
2023
const ENV_PAC4J: &str = "druid-pac4j";
2124

25+
/// Computes the `druid.extensions.loadList` entries from the validated cluster inputs.
26+
///
27+
/// Takes the resolved inputs (metadata database, whether TLS/S3 are in use, the user-supplied
28+
/// additional extensions and the resolved authentication config) rather than the raw
29+
/// `DruidCluster`, so it has no dependency on the validate step.
2230
pub fn get_extension_list(
23-
druid: &v1alpha1::DruidCluster,
24-
druid_tls_security: &DruidTlsSecurity,
31+
metadata_database: &MetadataDatabaseConnection,
32+
tls_enabled: bool,
33+
uses_s3: bool,
34+
additional_extensions: &HashSet<String>,
2535
druid_auth_settings: &Option<DruidAuthenticationConfig>,
2636
) -> Vec<String> {
2737
let mut extensions = HashSet::from([
@@ -33,7 +43,7 @@ pub fn get_extension_list(
3343
EXT_HDFS.to_string(),
3444
]);
3545

36-
match druid.spec.cluster_config.metadata_database {
46+
match metadata_database {
3747
MetadataDatabaseConnection::Derby(..) => {} // no additional extensions required
3848
MetadataDatabaseConnection::Postgresql(..) => {
3949
extensions.insert(EXT_PSQL_MD_ST.to_string());
@@ -43,27 +53,26 @@ pub fn get_extension_list(
4353
}
4454
};
4555

46-
if druid_tls_security.tls_enabled() {
56+
if tls_enabled {
4757
extensions.insert(EXT_SIMPLE_CLIENT_SSL_CONTEXT.to_string());
4858
}
4959

50-
if druid.uses_s3() {
60+
if uses_s3 {
5161
extensions.insert(EXT_S3.to_string());
5262
}
5363

5464
if let Some(DruidAuthenticationConfig::Oidc { .. }) = druid_auth_settings {
5565
extensions.insert(ENV_PAC4J.to_string());
5666
}
5767

58-
let additional_extensions = druid.spec.cluster_config.additional_extensions.clone();
5968
if !additional_extensions.is_empty() {
6069
debug!(
6170
enabled_extensions = ?extensions,
6271
?additional_extensions,
6372
"Adding user specified additional extensions to list of enabled extensions"
6473
);
6574
}
66-
extensions.extend(additional_extensions);
75+
extensions.extend(additional_extensions.iter().cloned());
6776

6877
let mut extensions = Vec::from_iter(extensions);
6978
extensions.sort();
@@ -77,7 +86,11 @@ mod tests {
7786
};
7887

7988
use super::*;
80-
use crate::crd::authentication::{AuthenticationClassResolved, AuthenticationClassesResolved};
89+
use crate::crd::{
90+
authentication::{AuthenticationClassResolved, AuthenticationClassesResolved},
91+
security::DruidTlsSecurity,
92+
v1alpha1,
93+
};
8194

8295
#[test]
8396
fn test_additional_extensions() {
@@ -94,42 +107,48 @@ mod tests {
94107
])
95108
);
96109

110+
let druid_tls_security = DruidTlsSecurity::new_from_druid_cluster(
111+
&cluster,
112+
&AuthenticationClassesResolved {
113+
auth_classes: vec![],
114+
},
115+
);
116+
117+
let druid_auth_config = Some(
118+
DruidAuthenticationConfig::try_from(AuthenticationClassesResolved {
119+
auth_classes: vec![AuthenticationClassResolved::Oidc {
120+
auth_class_name: "oidc".to_string(),
121+
provider: oidc::v1alpha1::AuthenticationProvider::new(
122+
"my-oidc-provider".to_string().try_into().unwrap(),
123+
None,
124+
"".to_string(),
125+
TlsClientDetails { tls: None },
126+
"".to_string(),
127+
vec![],
128+
None,
129+
),
130+
oidc: crate::authentication::oidc::DruidClientAuthenticationOptions {
131+
client_credentials_secret_ref: "".to_string(),
132+
extra_scopes: vec![],
133+
product_specific_fields: oidc::v1alpha1::ClientAuthenticationMethodOption {
134+
client_authentication_method:
135+
oidc::v1alpha1::ClientAuthenticationMethod::default(),
136+
},
137+
},
138+
}],
139+
})
140+
.unwrap()
141+
.unwrap(),
142+
);
143+
97144
assert_eq!(
98145
get_extension_list(
99-
&cluster,
100-
&DruidTlsSecurity::new_from_druid_cluster(
101-
&cluster,
102-
&AuthenticationClassesResolved {
103-
auth_classes: vec![]
104-
}
105-
),
106-
&Some(
107-
DruidAuthenticationConfig::try_from(AuthenticationClassesResolved {
108-
auth_classes: vec![AuthenticationClassResolved::Oidc {
109-
auth_class_name: "oidc".to_string(),
110-
provider: oidc::v1alpha1::AuthenticationProvider::new(
111-
"my-oidc-provider".to_string().try_into().unwrap(),
112-
None,
113-
"".to_string(),
114-
TlsClientDetails { tls: None },
115-
"".to_string(),
116-
vec![],
117-
None
118-
),
119-
oidc: crate::authentication::oidc::DruidClientAuthenticationOptions {
120-
client_credentials_secret_ref: "".to_string(),
121-
extra_scopes: vec![],
122-
product_specific_fields:
123-
oidc::v1alpha1::ClientAuthenticationMethodOption {
124-
client_authentication_method:
125-
oidc::v1alpha1::ClientAuthenticationMethod::default(),
126-
},
127-
}
128-
}]
129-
})
130-
.unwrap()
131-
.unwrap()
132-
)
146+
&cluster.spec.cluster_config.metadata_database,
147+
druid_tls_security.tls_enabled(),
148+
// simple.yaml uses HDFS deep storage and no S3 ingestion.
149+
false,
150+
&cluster.spec.cluster_config.additional_extensions,
151+
&druid_auth_config,
133152
),
134153
[
135154
"druid-avro-extensions".to_owned(),

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Per-file builders for Druid `.properties` files.
22
3+
pub mod extensions;
34
pub mod product_logging;
45
pub mod runtime_properties;
56
pub mod security_properties;

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use crate::{
2828
jvm::construct_jvm_args,
2929
properties::{
3030
ConfigFileName,
31+
extensions::get_extension_list,
3132
product_logging::{build_log4j2, vector_config_file_content},
3233
runtime_properties, security_properties,
3334
},
@@ -163,9 +164,16 @@ pub fn build_rolegroup_config_map(
163164
zk_connstr.to_string(),
164165
);
165166

167+
let extensions = get_extension_list(
168+
&cluster_config.metadata_database,
169+
druid_tls_security.tls_enabled(),
170+
cluster_config.uses_s3(),
171+
&cluster_config.additional_extensions,
172+
druid_auth_config,
173+
);
166174
conf.insert(
167175
EXTENSIONS_LOADLIST.to_string(),
168-
build_string_list(&cluster_config.extensions),
176+
build_string_list(&extensions),
169177
);
170178

171179
if let Some(opa_str) = opa_connstr {
@@ -174,7 +182,10 @@ pub fn build_rolegroup_config_map(
174182

175183
conf.insert(
176184
crate::crd::database::METADATA_STORAGE_TYPE.to_string(),
177-
cluster_config.metadata_storage_type.clone(),
185+
cluster_config
186+
.metadata_database
187+
.as_metadata_storage_type()
188+
.to_string(),
178189
);
179190

180191
conf.insert(

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

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33
//! Synchronously validates inputs that don't require a Kubernetes client. Produces
44
//! [`ValidatedCluster`], consumed by the rest of `reconcile_druid`.
55
6-
use std::{borrow::Cow, collections::BTreeMap, str::FromStr};
6+
use std::{
7+
borrow::Cow,
8+
collections::{BTreeMap, HashSet},
9+
str::FromStr,
10+
};
711

812
use snafu::{ResultExt, Snafu};
913
use stackable_operator::{
@@ -35,8 +39,10 @@ use strum::IntoEnumIterator;
3539
use crate::{
3640
authentication::DruidAuthenticationConfig,
3741
controller::{controller_name, dereference::DereferencedObjects, operator_name, product_name},
38-
crd::{DeepStorageSpec, DruidRole, security::DruidTlsSecurity, v1alpha1},
39-
extensions::get_extension_list,
42+
crd::{
43+
DeepStorageSpec, DruidRole, database::MetadataDatabaseConnection,
44+
security::DruidTlsSecurity, v1alpha1,
45+
},
4046
};
4147

4248
#[derive(Snafu, Debug)]
@@ -88,11 +94,12 @@ pub struct ValidatedClusterConfig {
8894
pub deep_storage_bucket_name: Option<String>,
8995
pub druid_tls_security: DruidTlsSecurity,
9096
pub druid_auth_config: Option<DruidAuthenticationConfig>,
91-
/// The `druid.extensions.loadList` entries, resolved from the metadata database, TLS, S3 and
92-
/// authentication settings during validation.
93-
pub extensions: Vec<String>,
94-
/// The `druid.metadata.storage.type` value derived from the configured metadata database.
95-
pub metadata_storage_type: String,
97+
/// The configured metadata database, carried so the build step can derive the
98+
/// `druid.metadata.storage.type` value and the metadata-storage extension.
99+
pub metadata_database: MetadataDatabaseConnection,
100+
/// User-supplied additional `druid.extensions.loadList` entries, carried so the build step can
101+
/// assemble the full extension list.
102+
pub additional_extensions: HashSet<String>,
96103
/// The JDBC connection details (URL plus credential env vars) for the metadata database.
97104
pub metadata_db_connection: JdbcDatabaseConnectionDetails,
98105
/// The deep-storage spec, carried so the build step can derive the cluster-level
@@ -104,6 +111,13 @@ pub struct ValidatedClusterConfig {
104111
pub extra_volumes: Vec<Volume>,
105112
}
106113

114+
impl ValidatedClusterConfig {
115+
/// Whether the cluster uses S3, for deep storage or ingestion.
116+
pub fn uses_s3(&self) -> bool {
117+
self.s3_connection.is_some()
118+
}
119+
}
120+
107121
/// Per-role configuration extracted during validation, so the reconcile/build steps consume this
108122
/// instead of re-reading the raw [`v1alpha1::DruidCluster`].
109123
#[derive(Clone, Debug)]
@@ -351,13 +365,6 @@ pub fn validate(
351365
let namespace = get_namespace(druid).context(ClusterIdentitySnafu)?;
352366
let uid = get_uid(druid).context(ClusterIdentitySnafu)?;
353367

354-
let extensions = get_extension_list(druid, &druid_tls_security, &druid_auth_config);
355-
let metadata_storage_type = druid
356-
.spec
357-
.cluster_config
358-
.metadata_database
359-
.as_metadata_storage_type()
360-
.to_string();
361368
let metadata_db_connection = druid
362369
.spec
363370
.cluster_config
@@ -377,8 +384,8 @@ pub fn validate(
377384
deep_storage_bucket_name: dereferenced_objects.deep_storage_bucket_name.clone(),
378385
druid_tls_security,
379386
druid_auth_config,
380-
extensions,
381-
metadata_storage_type,
387+
metadata_database: druid.spec.cluster_config.metadata_database.clone(),
388+
additional_extensions: druid.spec.cluster_config.additional_extensions.clone(),
382389
metadata_db_connection,
383390
deep_storage: druid.spec.cluster_config.deep_storage.clone(),
384391
extra_volumes: druid.spec.cluster_config.extra_volumes.clone(),
@@ -409,7 +416,6 @@ pub(crate) mod test_support {
409416
DruidRole, authentication::AuthenticationClassesResolved, security::DruidTlsSecurity,
410417
v1alpha1,
411418
},
412-
extensions::get_extension_list,
413419
};
414420

415421
/// A minimal but fully-valid `DruidCluster` with one role group per role.
@@ -504,13 +510,6 @@ spec:
504510
);
505511
}
506512

507-
let extensions = get_extension_list(druid, &druid_tls_security, &None);
508-
let metadata_storage_type = druid
509-
.spec
510-
.cluster_config
511-
.metadata_database
512-
.as_metadata_storage_type()
513-
.to_string();
514513
let metadata_db_connection = druid
515514
.spec
516515
.cluster_config
@@ -530,8 +529,8 @@ spec:
530529
deep_storage_bucket_name: None,
531530
druid_tls_security,
532531
druid_auth_config: None,
533-
extensions,
534-
metadata_storage_type,
532+
metadata_database: druid.spec.cluster_config.metadata_database.clone(),
533+
additional_extensions: druid.spec.cluster_config.additional_extensions.clone(),
535534
metadata_db_connection,
536535
deep_storage: druid.spec.cluster_config.deep_storage.clone(),
537536
extra_volumes: druid.spec.cluster_config.extra_volumes.clone(),

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

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -430,20 +430,6 @@ impl v1alpha1::DruidCluster {
430430
}
431431
}
432432

433-
/// Returns true if the cluster uses an s3 connection.
434-
/// This is a quicker convenience function over the [v1alpha1::DruidCluster::get_s3_connection] function.
435-
pub fn uses_s3(&self) -> bool {
436-
let s3_ingestion = self
437-
.spec
438-
.cluster_config
439-
.ingestion
440-
.as_ref()
441-
.and_then(|spec| spec.s3connection.as_ref())
442-
.is_some();
443-
let s3_storage = self.spec.cluster_config.deep_storage.is_s3();
444-
s3_ingestion || s3_storage
445-
}
446-
447433
/// Merges and validates all role groups of the given role. Invoked from the validate step
448434
/// (`controller::validate`).
449435
///
@@ -873,12 +859,6 @@ pub enum DeepStorageSpec {
873859
S3(S3DeepStorageSpec),
874860
}
875861

876-
impl DeepStorageSpec {
877-
fn is_s3(&self) -> bool {
878-
matches!(self, DeepStorageSpec::S3(_))
879-
}
880-
}
881-
882862
#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
883863
#[serde(rename_all = "camelCase")]
884864
pub struct HdfsDeepStorageSpec {

rust/operator-binary/src/main.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ use crate::{
4040
mod authentication;
4141
mod controller;
4242
mod crd;
43-
mod extensions;
4443
mod internal_secret;
4544
mod webhooks;
4645

0 commit comments

Comments
 (0)