Skip to content

Commit 68a3215

Browse files
committed
refactor: split opa config into deref and build steps
1 parent 09388a5 commit 68a3215

6 files changed

Lines changed: 139 additions & 123 deletions

File tree

rust/operator-binary/src/controller.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,11 @@ use strum::EnumDiscriminants;
4949

5050
use crate::{
5151
OPERATOR_NAME,
52-
controller::build::{
53-
opa::HiveOpaConfig,
54-
resource::{
55-
discovery,
56-
listener::build_role_listener,
57-
pdb::build_pdb,
58-
service::{build_rolegroup_headless_service, build_rolegroup_metrics_service},
59-
},
52+
controller::build::resource::{
53+
discovery,
54+
listener::build_role_listener,
55+
pdb::build_pdb,
56+
service::{build_rolegroup_headless_service, build_rolegroup_metrics_service},
6057
},
6158
crd::{APP_NAME, HdfsConnection, HiveClusterStatus, HiveRole, MetaStoreConfig, v1alpha1},
6259
};
@@ -448,7 +445,7 @@ pub struct ValidatedClusterConfig {
448445
/// The HDFS connection (discovery ConfigMap reference), if an HDFS backend is configured.
449446
pub hdfs: Option<HdfsConnection>,
450447
pub s3_connection_spec: Option<s3::v1alpha1::ConnectionSpec>,
451-
pub hive_opa_config: Option<HiveOpaConfig>,
448+
pub hive_opa_config: Option<dereference::ResolvedOpaConfig>,
452449
/// The Kerberos `SecretClass` name, if Kerberos is enabled.
453450
pub kerberos_secret_class: Option<SecretClassName>,
454451
}

rust/operator-binary/src/controller/build/command.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
use stackable_operator::crd::s3;
22

33
use super::{
4-
opa::HiveOpaConfig, properties::ConfigFileName, resource::statefulset::HDFS_CONFIG_MOUNT_DIR,
4+
opa::build_opa_tls_ca_cert_mount_path, properties::ConfigFileName,
5+
resource::statefulset::HDFS_CONFIG_MOUNT_DIR,
56
};
67
use crate::{
7-
controller::ValidatedCluster,
8+
controller::{ValidatedCluster, dereference::ResolvedOpaConfig},
89
crd::{
910
STACKABLE_CONFIG_DIR, STACKABLE_CONFIG_MOUNT_DIR, STACKABLE_LOG_CONFIG_MOUNT_DIR,
1011
STACKABLE_TRUST_STORE, STACKABLE_TRUST_STORE_PASSWORD,
@@ -15,7 +16,7 @@ pub fn build_container_command_args(
1516
cluster: &ValidatedCluster,
1617
start_command: String,
1718
s3_connection_spec: Option<&s3::v1alpha1::ConnectionSpec>,
18-
hive_opa_config: Option<&HiveOpaConfig>,
19+
hive_opa_config: Option<&ResolvedOpaConfig>,
1920
) -> Vec<String> {
2021
let log4j2_properties = ConfigFileName::Log4j2;
2122
let core_site = ConfigFileName::CoreSite;
@@ -59,7 +60,7 @@ pub fn build_container_command_args(
5960
}
6061

6162
if let Some(opa) = hive_opa_config
62-
&& let Some(ca_cert_dir) = opa.tls_ca_cert_mount_path()
63+
&& let Some(ca_cert_dir) = build_opa_tls_ca_cert_mount_path(opa)
6364
{
6465
args.push(format!(
6566
"cert-tools generate-pkcs12-truststore --pkcs12 {STACKABLE_TRUST_STORE}:{STACKABLE_TRUST_STORE_PASSWORD} --pem {ca_cert_dir}/ca.crt --out {STACKABLE_TRUST_STORE} --out-password {STACKABLE_TRUST_STORE_PASSWORD}"
Lines changed: 58 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
11
use std::{collections::BTreeMap, str::FromStr};
22

3-
use stackable_operator::{
4-
client::Client,
5-
commons::opa::{OpaApiVersion, OpaConfig},
6-
k8s_openapi::api::core::v1::ConfigMap,
7-
kube::ResourceExt,
8-
v2::types::kubernetes::VolumeName,
9-
};
3+
use stackable_operator::v2::types::kubernetes::VolumeName;
104

11-
use crate::crd::v1alpha1::HiveCluster;
5+
use crate::controller::dereference::ResolvedOpaConfig;
126

137
const HIVE_METASTORE_PRE_EVENT_LISTENERS: &str = "hive.metastore.pre.event.listeners";
148
const HIVE_SECURITY_METASTORE_AUTHORIZATION_MANAGER: &str =
@@ -38,95 +32,62 @@ const OPA_AUTHORIZATION_POLICY_URL_USER: &str = "com.bosch.bdps.opa.authorizatio
3832
// value so the produced volume/mount name is unchanged.
3933
stackable_operator::constant!(pub(crate) OPA_TLS_VOLUME_NAME: VolumeName = "opa-tls");
4034

41-
pub struct HiveOpaConfig {
42-
/// Endpoint for OPA, e.g.
43-
/// `http://localhost:8081/v1/data/<package>`
44-
pub(crate) base_endpoint: String,
45-
/// Optional TLS secret class for OPA communication.
46-
/// If set, the CA certificate from this secret class will be added
47-
/// to hive's truststore to make it trust OPA's TLS certificate.
48-
pub(crate) tls_secret_class: Option<String>,
49-
}
50-
51-
impl HiveOpaConfig {
52-
pub async fn from_opa_config(
53-
client: &Client,
54-
hive: &HiveCluster,
55-
opa_config: &OpaConfig,
56-
) -> Result<Self, stackable_operator::commons::opa::Error> {
57-
// See: https://github.com/boschglobal/hive-metastore-opa-authorizer?tab=readme-ov-file#configuration
58-
let base_endpoint = opa_config
59-
.full_document_url_from_config_map(client, hive, None, &OpaApiVersion::V1)
60-
.await?;
61-
62-
let tls_secret_class = client
63-
.get::<ConfigMap>(
64-
&opa_config.config_map_name,
65-
hive.namespace().as_deref().unwrap_or("default"),
66-
)
67-
.await
68-
.ok()
69-
.and_then(|cm| cm.data)
70-
.and_then(|mut data| data.remove("OPA_SECRET_CLASS"));
35+
/// Builds the OPA-related `hive-site.xml` properties from a [`ResolvedOpaConfig`].
36+
pub fn build_opa_hive_site_config(
37+
opa: &ResolvedOpaConfig,
38+
product_version: &str,
39+
) -> BTreeMap<String, String> {
40+
let (pre_event_listener, authorization_provider) = if product_version.starts_with("3.") {
41+
(
42+
OPA_AUTHORIZATION_PRE_EVENT_LISTENER_V3,
43+
OPA_BASED_AUTHORIZATION_PROVIDER_V3,
44+
)
45+
} else {
46+
(
47+
OPA_AUTHORIZATION_PRE_EVENT_LISTENER_V4,
48+
OPA_BASED_AUTHORIZATION_PROVIDER_V4,
49+
)
50+
};
7151

72-
Ok(HiveOpaConfig {
73-
base_endpoint,
74-
tls_secret_class,
75-
})
76-
}
77-
78-
pub fn as_config(&self, product_version: &str) -> BTreeMap<String, String> {
79-
let (pre_event_listener, authorization_provider) = if product_version.starts_with("3.") {
80-
(
81-
OPA_AUTHORIZATION_PRE_EVENT_LISTENER_V3,
82-
OPA_BASED_AUTHORIZATION_PROVIDER_V3,
83-
)
84-
} else {
85-
(
86-
OPA_AUTHORIZATION_PRE_EVENT_LISTENER_V4,
87-
OPA_BASED_AUTHORIZATION_PROVIDER_V4,
88-
)
89-
};
90-
91-
BTreeMap::from([
92-
(
93-
HIVE_METASTORE_PRE_EVENT_LISTENERS.to_string(),
94-
pre_event_listener.to_string(),
95-
),
96-
(
97-
HIVE_SECURITY_METASTORE_AUTHORIZATION_MANAGER.to_string(),
98-
authorization_provider.to_string(),
99-
),
100-
(
101-
OPA_AUTHORIZATION_BASE_ENDPOINT.to_string(),
102-
self.base_endpoint.to_owned(),
103-
),
104-
(
105-
OPA_AUTHORIZATION_POLICY_URL_DATA_BASE.to_string(),
106-
"database_allow".to_string(),
107-
),
108-
(
109-
OPA_AUTHORIZATION_POLICY_URL_TABLE.to_string(),
110-
"table_allow".to_string(),
111-
),
112-
(
113-
OPA_AUTHORIZATION_POLICY_URL_COLUMN.to_string(),
114-
"column_allow".to_string(),
115-
),
116-
(
117-
OPA_AUTHORIZATION_POLICY_URL_PARTITION.to_string(),
118-
"partition_allow".to_string(),
119-
),
120-
(
121-
OPA_AUTHORIZATION_POLICY_URL_USER.to_string(),
122-
"user_allow".to_string(),
123-
),
124-
])
125-
}
52+
BTreeMap::from([
53+
(
54+
HIVE_METASTORE_PRE_EVENT_LISTENERS.to_string(),
55+
pre_event_listener.to_string(),
56+
),
57+
(
58+
HIVE_SECURITY_METASTORE_AUTHORIZATION_MANAGER.to_string(),
59+
authorization_provider.to_string(),
60+
),
61+
(
62+
OPA_AUTHORIZATION_BASE_ENDPOINT.to_string(),
63+
opa.base_endpoint.to_owned(),
64+
),
65+
(
66+
OPA_AUTHORIZATION_POLICY_URL_DATA_BASE.to_string(),
67+
"database_allow".to_string(),
68+
),
69+
(
70+
OPA_AUTHORIZATION_POLICY_URL_TABLE.to_string(),
71+
"table_allow".to_string(),
72+
),
73+
(
74+
OPA_AUTHORIZATION_POLICY_URL_COLUMN.to_string(),
75+
"column_allow".to_string(),
76+
),
77+
(
78+
OPA_AUTHORIZATION_POLICY_URL_PARTITION.to_string(),
79+
"partition_allow".to_string(),
80+
),
81+
(
82+
OPA_AUTHORIZATION_POLICY_URL_USER.to_string(),
83+
"user_allow".to_string(),
84+
),
85+
])
86+
}
12687

127-
pub fn tls_ca_cert_mount_path(&self) -> Option<String> {
128-
self.tls_secret_class
129-
.as_ref()
130-
.map(|_| format!("/stackable/secrets/{}", *OPA_TLS_VOLUME_NAME))
131-
}
88+
/// The mount path of the OPA TLS CA certificate, or `None` when OPA TLS is not configured.
89+
pub fn build_opa_tls_ca_cert_mount_path(opa: &ResolvedOpaConfig) -> Option<String> {
90+
opa.tls_secret_class
91+
.as_ref()
92+
.map(|_| format!("/stackable/secrets/{}", *OPA_TLS_VOLUME_NAME))
13293
}

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ use std::collections::BTreeMap;
44
use snafu::{ResultExt, Snafu};
55
use stackable_operator::{crd::s3, k8s_openapi::api::core::v1::EnvVar};
66

7-
use crate::controller::{ValidatedClusterConfig, ValidatedMetaStoreConfig};
7+
use crate::controller::{
8+
ValidatedClusterConfig, ValidatedMetaStoreConfig, build::opa::build_opa_hive_site_config,
9+
};
810

911
const DEFAULT_WAREHOUSE_DIR: &str = "/stackable/warehouse";
1012
const HIVE_METASTORE_PORT: &str = "hive.metastore.port";
@@ -103,7 +105,7 @@ pub fn build(
103105
data.extend(kerberos_config);
104106

105107
if let Some(opa_config) = cluster_config.hive_opa_config.as_ref() {
106-
data.extend(opa_config.as_config(product_version));
108+
data.extend(build_opa_hive_site_config(opa_config, product_version));
107109
}
108110

109111
// 3. Spec: warehouse dir from the merged CRD config (overrides the default).

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ use crate::{
5353
graceful_shutdown::add_graceful_shutdown_config,
5454
jvm::{construct_hadoop_heapsize_env, construct_non_heap_jvm_args},
5555
kerberos::{add_kerberos_pod_config, kerberos_container_start_commands},
56-
opa::OPA_TLS_VOLUME_NAME,
56+
opa::{OPA_TLS_VOLUME_NAME, build_opa_tls_ca_cert_mount_path},
5757
properties::product_logging::MAX_HIVE_LOG_FILES_SIZE,
5858
},
5959
},
@@ -205,7 +205,7 @@ pub(crate) fn build_metastore_rolegroup_statefulset(
205205
opa_config
206206
.tls_secret_class
207207
.as_ref()
208-
.zip(opa_config.tls_ca_cert_mount_path())
208+
.zip(build_opa_tls_ca_cert_mount_path(opa_config))
209209
})
210210
{
211211
container_builder
@@ -215,7 +215,7 @@ pub(crate) fn build_metastore_rolegroup_statefulset(
215215
let opa_tls_volume = VolumeBuilder::new(&*OPA_TLS_VOLUME_NAME)
216216
.ephemeral(
217217
SecretOperatorVolumeSourceBuilder::new(
218-
tls_secret_class,
218+
tls_secret_class.clone(),
219219
// We only need the public CA certificate to verify the OPA server.
220220
SecretClassVolumeProvisionParts::Public,
221221
)

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

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
1+
use std::str::FromStr;
2+
13
use snafu::{ResultExt, Snafu};
2-
use stackable_operator::{crd::s3, v2::controller_utils::get_namespace};
4+
use stackable_operator::{
5+
client::Client,
6+
commons::opa::{OpaApiVersion, OpaConfig},
7+
crd::s3,
8+
k8s_openapi::api::core::v1::ConfigMap,
9+
kube::ResourceExt,
10+
v2::{controller_utils::get_namespace, types::kubernetes::SecretClassName},
11+
};
312

4-
use crate::{controller::build::opa::HiveOpaConfig, crd::v1alpha1};
13+
use crate::crd::v1alpha1;
514

615
#[derive(Snafu, Debug)]
716
pub enum Error {
@@ -19,12 +28,60 @@ pub enum Error {
1928
InvalidOpaConfig {
2029
source: stackable_operator::commons::opa::Error,
2130
},
31+
32+
#[snafu(display("invalid OPA TLS secret class name"))]
33+
ParseOpaTlsSecretClassName {
34+
source: stackable_operator::v2::macros::attributed_string_type::Error,
35+
},
2236
}
2337

2438
/// External references resolved during the dereference step.
2539
pub struct DereferencedObjects {
2640
pub s3_connection_spec: Option<s3::v1alpha1::ConnectionSpec>,
27-
pub hive_opa_config: Option<HiveOpaConfig>,
41+
pub hive_opa_config: Option<ResolvedOpaConfig>,
42+
}
43+
44+
/// OPA settings resolved from the cluster's OPA reference during the dereference step.
45+
pub struct ResolvedOpaConfig {
46+
/// Endpoint for OPA, e.g.
47+
/// `http://localhost:8081/v1/data/<package>`
48+
pub(crate) base_endpoint: String,
49+
/// Optional TLS secret class for OPA communication.
50+
/// If set, the CA certificate from this secret class will be added
51+
/// to hive's truststore to make it trust OPA's TLS certificate.
52+
pub(crate) tls_secret_class: Option<SecretClassName>,
53+
}
54+
55+
impl ResolvedOpaConfig {
56+
pub async fn from_opa_config(
57+
client: &Client,
58+
hive: &v1alpha1::HiveCluster,
59+
opa_config: &OpaConfig,
60+
) -> Result<Self, Error> {
61+
// See: <https://github.com/boschglobal/hive-metastore-opa-authorizer?tab=readme-ov-file#configuration>
62+
let base_endpoint = opa_config
63+
.full_document_url_from_config_map(client, hive, None, &OpaApiVersion::V1)
64+
.await
65+
.context(InvalidOpaConfigSnafu)?;
66+
67+
let tls_secret_class = client
68+
.get::<ConfigMap>(
69+
&opa_config.config_map_name,
70+
hive.namespace().as_deref().unwrap_or("default"),
71+
)
72+
.await
73+
.ok()
74+
.and_then(|cm| cm.data)
75+
.and_then(|mut data| data.remove("OPA_SECRET_CLASS"))
76+
.map(|name| SecretClassName::from_str(&name))
77+
.transpose()
78+
.context(ParseOpaTlsSecretClassNameSnafu)?;
79+
80+
Ok(ResolvedOpaConfig {
81+
base_endpoint,
82+
tls_secret_class,
83+
})
84+
}
2885
}
2986

3087
pub async fn dereference(
@@ -45,11 +102,9 @@ pub async fn dereference(
45102
};
46103

47104
let hive_opa_config = match hive.get_opa_config() {
48-
Some(opa_config) => Some(
49-
HiveOpaConfig::from_opa_config(client, hive, opa_config)
50-
.await
51-
.context(InvalidOpaConfigSnafu)?,
52-
),
105+
Some(opa_config) => {
106+
Some(ResolvedOpaConfig::from_opa_config(client, hive, opa_config).await?)
107+
}
53108
None => None,
54109
};
55110

0 commit comments

Comments
 (0)