Skip to content

Commit 1a7799c

Browse files
authored
refactor: Flatten config maps and introduce RoleBuilder (#887)
* refactor: Flatten role-group config maps and introduce RoleBuilder * changelog * reorganise imports * reorganise imports * provide more robust/readable yaml for the test * remove entries from CL that do nto belong there * move drain_pre_stop_hook to middle_manager.rs, removing graceful_shutdown.rs * build role group resources directly instead of via build() * only resolve graceful validated logging, shutdown and secret lifetime once
1 parent 05f5cb7 commit 1a7799c

27 files changed

Lines changed: 3090 additions & 1796 deletions

‎CHANGELOG.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ All notable changes to this project will be documented in this file.
3636
operator upgrade, delete each broker, coordinator and router StatefulSet so that the operator
3737
immediately recreates it with the new labels ([#865]).
3838
- Make operations infallible where dependent on static inputs ([#869], [#874]).
39+
- Internal operator refactoring: build each role group through per-role modules (brokers,
40+
coordinators, historicals, middle managers, routers) and a shared role group builder, with
41+
each role's configuration held in typed fields of the validated cluster ([#887]).
3942
- Support Vector 0.58.0 ([#885], [#889]).
4043
- Replace the unmaintained `serde_yaml` with `yaml_serde` ([#888]).
4144
- All pods now set `securityContext.seccompProfile.type` to `RuntimeDefault` ([#888]).
@@ -66,6 +69,7 @@ All notable changes to this project will be documented in this file.
6669
[#879]: https://github.com/stackabletech/druid-operator/pull/879
6770
[#880]: https://github.com/stackabletech/druid-operator/pull/880
6871
[#885]: https://github.com/stackabletech/druid-operator/pull/885
72+
[#887]: https://github.com/stackabletech/druid-operator/pull/887
6973
[#888]: https://github.com/stackabletech/druid-operator/pull/888
7074
[#889]: https://github.com/stackabletech/druid-operator/pull/889
7175
[#891]: https://github.com/stackabletech/druid-operator/pull/891

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

Lines changed: 0 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -178,22 +178,14 @@ pub fn error_policy(
178178
mod test {
179179
use std::str::FromStr;
180180

181-
use rstest::*;
182181
use stackable_operator::{
183182
client::Client,
184183
commons::networking::DomainName,
185184
kube::{Client as KubeClient, Config, runtime::controller::Action},
186185
utils::cluster_info::KubernetesClusterInfo,
187-
v2::types::operator::RoleGroupName,
188186
};
189187

190188
use super::{CONTROLLER_NAME, OPERATOR_NAME, PRODUCT_NAME, *};
191-
use crate::{
192-
controller::build::{
193-
properties::ConfigFileName, resource::config_map::build_rolegroup_config_map,
194-
},
195-
crd::{DruidRole, PROP_SEGMENT_CACHE_LOCATIONS},
196-
};
197189

198190
#[test]
199191
fn test_constants() {
@@ -203,70 +195,6 @@ mod test {
203195
let _ = *CONTROLLER_NAME;
204196
}
205197

206-
#[rstest]
207-
#[case(
208-
"segment_cache.yaml",
209-
"default",
210-
"[{\"path\":\"/stackable/var/druid/segment-cache\",\"maxSize\":\"1G\",\"freeSpacePercent\":\"5\"}]"
211-
)]
212-
#[case(
213-
"segment_cache.yaml",
214-
"secondary",
215-
"[{\"path\":\"/stackable/var/druid/segment-cache\",\"maxSize\":\"5G\",\"freeSpacePercent\":\"2\"}]"
216-
)]
217-
fn segment_cache_location_property(
218-
#[case] druid_manifest: &str,
219-
#[case] tested_rolegroup_name: &str,
220-
#[case] expected_druid_segment_cache_property: &str,
221-
) {
222-
let yaml =
223-
std::fs::read_to_string(format!("test/resources/druid_controller/{druid_manifest}"))
224-
.unwrap();
225-
let druid = crate::controller::validate::test_support::druid_from_yaml(&yaml);
226-
227-
let cluster = crate::controller::validate::test_support::validated_cluster(&druid);
228-
229-
// The segment cache property is injected dynamically by the config_map builder from the
230-
// merged resources of the validated role group config.
231-
let rg = cluster
232-
.role_group_configs
233-
.get(&DruidRole::Historical)
234-
.expect("historical role groups")
235-
.get(&RoleGroupName::from_str(tested_rolegroup_name).unwrap())
236-
.expect("tested rolegroup")
237-
.clone();
238-
239-
let rg_configmap = build_rolegroup_config_map(
240-
&cluster,
241-
&DruidRole::Historical,
242-
&RoleGroupName::from_str(tested_rolegroup_name).unwrap(),
243-
&rg,
244-
)
245-
.expect("build rolegroup config map");
246-
247-
let druid_segment_cache_property = rg_configmap
248-
.data
249-
.unwrap()
250-
.get(&ConfigFileName::RuntimeProperties.to_string())
251-
.unwrap()
252-
.to_string();
253-
254-
let escaped_segment_cache_property =
255-
stackable_operator::v2::config_file_writer::to_java_properties_string(
256-
vec![(
257-
&PROP_SEGMENT_CACHE_LOCATIONS.to_string(),
258-
&expected_druid_segment_cache_property.to_string(),
259-
)]
260-
.into_iter(),
261-
)
262-
.unwrap();
263-
264-
assert!(
265-
druid_segment_cache_property.contains(&escaped_segment_cache_property),
266-
"role group {tested_rolegroup_name}"
267-
);
268-
}
269-
270198
/// The client points at a closed port, so any API call would fail the reconciliation: an `Ok`
271199
/// proves that a cluster being deleted returns before the reconciler touches the Kubernetes
272200
/// API, and because the spec is invalid, before the [`DeserializeGuard`] is unwrapped.

‎rust/operator-binary/src/controller/build/authentication/ldap.rs‎

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ use std::collections::BTreeMap;
22

33
use snafu::ResultExt;
44
use stackable_operator::{
5-
builder::pod::{PodBuilder, container::ContainerBuilder},
65
crd::authentication::ldap,
6+
k8s_openapi::api::core::v1::{Volume, VolumeMount},
77
};
88

99
use super::{
10-
AddLdapVolumesSnafu, ConstructLdapEndpointUrlSnafu, Error, MissingLdapBindCredentialsSnafu,
10+
ConstructLdapEndpointUrlSnafu, Error, LdapVolumesSnafu, MissingLdapBindCredentialsSnafu,
1111
};
1212
use crate::crd::{
1313
STACKABLE_TRUST_STORE_PASSWORD, file_reference,
@@ -97,20 +97,17 @@ pub(super) fn prepare_container_commands(
9797
}
9898
}
9999

100-
pub(super) fn add_volumes_and_mounts(
100+
/// The volumes of the LDAP bind credentials and the provider's TLS CA certificate, and their
101+
/// mounts.
102+
pub(super) fn volumes_and_mounts(
101103
provider: &ldap::v1alpha1::AuthenticationProvider,
102-
pb: &mut PodBuilder,
103-
cb_druid: &mut ContainerBuilder,
104-
cb_prepare: &mut ContainerBuilder,
105-
) -> Result<(), Error> {
104+
) -> Result<(Vec<Volume>, Vec<VolumeMount>), Error> {
106105
// TODO: Connecting to an LDAP server without bind credentials does not seem to be configurable in Druid at the moment
107106
// see https://github.com/stackabletech/druid-operator/issues/383 for future work.
108107
// Expect bind credentials to be provided for now, and throw return a useful error if there are none.
109108
if provider.bind_credentials_mount_paths().is_none() {
110109
return MissingLdapBindCredentialsSnafu.fail();
111110
}
112111

113-
provider
114-
.add_volumes_and_mounts(pb, vec![cb_druid, cb_prepare])
115-
.context(AddLdapVolumesSnafu)
112+
provider.volumes_and_mounts().context(LdapVolumesSnafu)
116113
}

‎rust/operator-binary/src/controller/build/authentication/mod.rs‎

Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ use std::collections::BTreeMap;
99

1010
use snafu::Snafu;
1111
use stackable_operator::{
12-
builder::pod::{PodBuilder, container::ContainerBuilder},
12+
k8s_openapi::api::core::v1::{Volume, VolumeMount},
1313
v2::builder::pod::container::EnvVarSet,
1414
};
1515

1616
use crate::{
1717
authentication::DruidAuthenticationConfig,
1818
controller::validate::ValidatedCluster,
1919
crd::{
20-
DruidRole, env_var_reference,
20+
env_var_reference,
2121
security::{
2222
INTERNAL_INITIAL_CLIENT_PASSWORD_ENV, INTERNAL_INITIAL_CLIENT_PASSWORD_SECRET_KEY,
2323
},
@@ -51,13 +51,13 @@ pub enum Error {
5151
source: stackable_operator::crd::authentication::oidc::v1alpha1::Error,
5252
},
5353

54-
#[snafu(display("failed to add LDAP Volumes and VolumeMounts to the Pod and containers"))]
55-
AddLdapVolumes {
54+
#[snafu(display("failed to build the LDAP volumes and volume mounts"))]
55+
LdapVolumes {
5656
source: stackable_operator::crd::authentication::ldap::v1alpha1::Error,
5757
},
5858

59-
#[snafu(display("failed to add OIDC Volumes and VolumeMounts to the Pod and containers"))]
60-
AddOidcVolumes {
59+
#[snafu(display("failed to build the OIDC volumes and volume mounts"))]
60+
OidcVolumes {
6161
source: stackable_operator::commons::tls_verification::TlsClientDetailsError,
6262
},
6363

@@ -98,9 +98,12 @@ fn set_authenticator_chain(
9898

9999
/// Creates the authentication and authorization parts of the runtime.properties config file.
100100
/// Configuration related to TLS authentication is added in `controller::build::security`.
101+
///
102+
/// `oidc_enabled` says whether the role authenticates its users with OIDC when the cluster uses
103+
/// OIDC; a role that does not gets the system authenticator only.
101104
pub fn generate_runtime_properties_config(
102105
auth: &DruidAuthenticationConfig,
103-
role: &DruidRole,
106+
oidc_enabled: bool,
104107
) -> Result<BTreeMap<String, String>> {
105108
let mut config: BTreeMap<String, String> = BTreeMap::new();
106109

@@ -111,7 +114,7 @@ pub fn generate_runtime_properties_config(
111114
}
112115
DruidAuthenticationConfig::Oidc { provider, oidc } => {
113116
generate_common_runtime_properties_config(&mut config);
114-
oidc::generate_runtime_properties_config(provider, oidc, role, &mut config)?
117+
oidc::generate_runtime_properties_config(provider, oidc, oidc_enabled, &mut config)?
115118
}
116119
DruidAuthenticationConfig::Tls {} => (),
117120
}
@@ -145,10 +148,12 @@ pub fn prepare_container_commands(auth: &DruidAuthenticationConfig) -> Vec<Strin
145148
command
146149
}
147150

151+
/// The environment variables of the `druid` container that carry the authentication secrets.
152+
/// `oidc_enabled` is as for [`generate_runtime_properties_config`].
148153
pub fn get_env_var_mounts(
149154
auth: &DruidAuthenticationConfig,
150155
cluster: &ValidatedCluster,
151-
role: &DruidRole,
156+
oidc_enabled: bool,
152157
) -> EnvVarSet {
153158
let internal_secret_name = build_shared_internal_secret_name(&cluster.name);
154159
let mut env_vars = EnvVarSet::new().with_secret_key_ref(
@@ -158,25 +163,24 @@ pub fn get_env_var_mounts(
158163
);
159164

160165
if let DruidAuthenticationConfig::Oidc { oidc, .. } = auth {
161-
env_vars = env_vars.merge(oidc::get_env_var_mounts(role, oidc, &internal_secret_name));
166+
env_vars = env_vars.merge(oidc::get_env_var_mounts(
167+
oidc_enabled,
168+
oidc,
169+
&internal_secret_name,
170+
));
162171
}
163172
env_vars
164173
}
165174

166-
pub fn add_volumes_and_mounts(
175+
/// The pod volumes the authentication provider needs and their mounts on the `druid` and
176+
/// `prepare` containers.
177+
pub fn volumes_and_mounts(
167178
auth: &DruidAuthenticationConfig,
168-
pb: &mut PodBuilder,
169-
cb_druid: &mut ContainerBuilder,
170-
cb_prepare: &mut ContainerBuilder,
171-
) -> Result<()> {
179+
) -> Result<(Vec<Volume>, Vec<VolumeMount>)> {
172180
match auth {
173-
DruidAuthenticationConfig::Ldap { provider } => {
174-
ldap::add_volumes_and_mounts(provider, pb, cb_druid, cb_prepare)
175-
}
176-
DruidAuthenticationConfig::Oidc { provider, .. } => {
177-
oidc::add_volumes_and_mounts(provider, pb, cb_druid, cb_prepare)
178-
}
179-
DruidAuthenticationConfig::Tls {} => Ok(()),
181+
DruidAuthenticationConfig::Ldap { provider } => ldap::volumes_and_mounts(provider),
182+
DruidAuthenticationConfig::Oidc { provider, .. } => oidc::volumes_and_mounts(provider),
183+
DruidAuthenticationConfig::Tls {} => Ok((Vec::new(), Vec::new())),
180184
}
181185
}
182186

@@ -258,8 +262,7 @@ mod test {
258262

259263
#[test]
260264
fn test_ldap_config_is_added() {
261-
let got = generate_runtime_properties_config(&ldap_auth_config(), &DruidRole::Coordinator)
262-
.unwrap();
265+
let got = generate_runtime_properties_config(&ldap_auth_config(), true).unwrap();
263266

264267
assert!(got.contains_key("druid.auth.authenticator.Ldap.type"));
265268
}
@@ -268,8 +271,7 @@ mod test {
268271
/// alongside the provider-specific (LDAP/OIDC) config.
269272
#[test]
270273
fn common_system_auth_config_is_added() {
271-
let got = generate_runtime_properties_config(&ldap_auth_config(), &DruidRole::Coordinator)
272-
.unwrap();
274+
let got = generate_runtime_properties_config(&ldap_auth_config(), true).unwrap();
273275

274276
assert_eq!(
275277
got.get("druid.auth.authenticator.DruidSystemAuthenticator.type"),
@@ -290,11 +292,8 @@ mod test {
290292
/// contributes nothing to runtime.properties.
291293
#[test]
292294
fn tls_only_produces_no_runtime_properties() {
293-
let got = generate_runtime_properties_config(
294-
&DruidAuthenticationConfig::Tls {},
295-
&DruidRole::Broker,
296-
)
297-
.unwrap();
295+
let got =
296+
generate_runtime_properties_config(&DruidAuthenticationConfig::Tls {}, true).unwrap();
298297

299298
assert!(got.is_empty());
300299
}

0 commit comments

Comments
 (0)