Skip to content

Commit 47842bb

Browse files
committed
refactor: switch to v2 types, ResourceNAmes, RoleGroupName, reduce RoleGroupRef, cleanup
1 parent 09d7646 commit 47842bb

7 files changed

Lines changed: 257 additions & 306 deletions

File tree

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

Lines changed: 25 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,59 +6,52 @@ use stackable_operator::{
66
builder::{configmap::ConfigMapBuilder, meta::ObjectMetaBuilder},
77
k8s_openapi::api::core::v1::ConfigMap,
88
product_logging::framework::VECTOR_CONFIG_FILE,
9-
role_utils::RoleGroupRef,
109
v2::builder::meta::ownerreference_from_resource,
1110
};
1211

13-
use super::properties::{ConfigFileName, config_json, logging, user_info_fetcher};
14-
use crate::{
15-
controller::{OpaRoleGroupConfig, ValidatedCluster},
16-
crd::v1alpha2,
17-
opa_controller::build_recommended_labels,
18-
};
12+
use super::properties::{ConfigFileName, config_json, user_info_fetcher};
13+
use crate::controller::{OpaRoleGroupConfig, RoleGroupName, ValidatedCluster};
1914

2015
#[derive(Snafu, Debug)]
2116
pub enum Error {
22-
#[snafu(display("failed to build object meta data"))]
23-
ObjectMeta {
24-
source: stackable_operator::builder::meta::Error,
25-
},
26-
2717
#[snafu(display("failed to build config.json"))]
2818
BuildConfigJson { source: config_json::Error },
2919

3020
#[snafu(display("failed to build user-info-fetcher.json"))]
3121
BuildUserInfoFetcher { source: user_info_fetcher::Error },
3222

33-
#[snafu(display("failed to build ConfigMap for [{rolegroup}]"))]
34-
BuildConfigMap {
23+
#[snafu(display("failed to assemble ConfigMap for role group {role_group}"))]
24+
Assemble {
3525
source: stackable_operator::builder::configmap::Error,
36-
rolegroup: RoleGroupRef<v1alpha2::OpaCluster>,
26+
role_group: RoleGroupName,
3727
},
3828
}
3929

4030
type Result<T, E = Error> = std::result::Result<T, E>;
4131

4232
/// The rolegroup [`ConfigMap`] configures the rolegroup based on the configuration given by the
4333
/// administrator.
34+
///
35+
/// `vector_config` is the Vector agent config (`vector.yaml`) built by the caller (where a
36+
/// `RoleGroupRef` is available); it is `None` when the Vector agent is disabled.
4437
pub fn build_rolegroup_config_map(
4538
cluster: &ValidatedCluster,
39+
role_group_name: &RoleGroupName,
4640
rolegroup_config: &OpaRoleGroupConfig,
47-
rolegroup_ref: &RoleGroupRef<v1alpha2::OpaCluster>,
41+
vector_config: Option<String>,
4842
) -> Result<ConfigMap> {
4943
let mut cm_builder = ConfigMapBuilder::new();
5044

5145
let metadata = ObjectMetaBuilder::new()
52-
.name(rolegroup_ref.object_name())
53-
.namespace(&cluster.namespace)
46+
.name_and_namespace(cluster)
47+
.name(
48+
cluster
49+
.resource_names(role_group_name)
50+
.role_group_config_map()
51+
.to_string(),
52+
)
5453
.ownerreference(ownerreference_from_resource(cluster, None, Some(true)))
55-
.with_recommended_labels(&build_recommended_labels(
56-
cluster,
57-
&cluster.image.app_version_label_value,
58-
&rolegroup_ref.role,
59-
&rolegroup_ref.role_group,
60-
))
61-
.context(ObjectMetaSnafu)?
54+
.with_labels(cluster.recommended_labels(role_group_name))
6255
.build();
6356

6457
cm_builder.metadata(metadata).add_data(
@@ -74,21 +67,18 @@ pub fn build_rolegroup_config_map(
7467
);
7568
}
7669

77-
if let Some(vector_config) =
78-
logging::build_vector_config(rolegroup_ref, &rolegroup_config.config.logging)
79-
{
70+
if let Some(vector_config) = vector_config {
8071
cm_builder.add_data(VECTOR_CONFIG_FILE, vector_config);
8172
}
8273

83-
cm_builder.build().with_context(|_| BuildConfigMapSnafu {
84-
rolegroup: rolegroup_ref.clone(),
74+
cm_builder.build().with_context(|_| AssembleSnafu {
75+
role_group: role_group_name.clone(),
8576
})
8677
}
8778

8879
#[cfg(test)]
8980
mod tests {
9081
use serde_json::{Value, json};
91-
use stackable_operator::kube::runtime::reflector::ObjectRef;
9282

9383
use super::*;
9484
use crate::{
@@ -97,20 +87,15 @@ mod tests {
9787

9888
/// Renders the ConfigMap of the `default` server role group of an `OpaCluster` built from `spec`.
9989
fn build_config_map(spec: Value) -> ConfigMap {
100-
let (opa, validated) = validated_cluster_from_spec(spec);
90+
let (_opa, validated) = validated_cluster_from_spec(spec);
10191

10292
let role = OpaRole::Server;
103-
let rg_config = validated.role_group_configs[&role]
104-
.values()
93+
let (role_group_name, rg_config) = validated.role_group_configs[&role]
94+
.iter()
10595
.next()
10696
.expect("the default role group should exist");
107-
let rolegroup_ref = RoleGroupRef {
108-
cluster: ObjectRef::from_obj(&opa),
109-
role: role.to_string(),
110-
role_group: "default".to_string(),
111-
};
11297

113-
build_rolegroup_config_map(&validated, rg_config, &rolegroup_ref)
98+
build_rolegroup_config_map(&validated, role_group_name, rg_config, None)
11499
.expect("the config map should build")
115100
}
116101

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

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
//! The content comes entirely from the [`ValidatedCluster`] (plus the externally-resolved role
44
//! service and `cluster_info`).
55
6+
use std::str::FromStr;
7+
68
use snafu::{ResultExt, Snafu};
79
use stackable_operator::{
810
builder::{configmap::ConfigMapBuilder, meta::ObjectMetaBuilder},
@@ -12,9 +14,7 @@ use stackable_operator::{
1214
};
1315

1416
use crate::{
15-
controller::ValidatedCluster,
16-
crd::OpaRole,
17-
opa_controller::build_recommended_labels,
17+
controller::{RoleGroupName, ValidatedCluster},
1818
service::{APP_PORT, APP_TLS_PORT},
1919
};
2020

@@ -24,11 +24,6 @@ pub enum Error {
2424
BuildConfigMap {
2525
source: stackable_operator::builder::configmap::Error,
2626
},
27-
28-
#[snafu(display("failed to build object meta data"))]
29-
ObjectMeta {
30-
source: stackable_operator::builder::meta::Error,
31-
},
3227
}
3328

3429
type Result<T, E = Error> = std::result::Result<T, E>;
@@ -52,17 +47,14 @@ pub fn build_discovery_config_map(
5247
cluster_domain = cluster_info.cluster_domain,
5348
);
5449

50+
// Discovery is a cluster-level object (named after the cluster); `discovery` is used as a
51+
// placeholder role-group name for the recommended labels.
5552
let metadata = ObjectMetaBuilder::new()
56-
.name(cluster.name.to_string())
57-
.namespace(&cluster.namespace)
53+
.name_and_namespace(cluster)
5854
.ownerreference(ownerreference_from_resource(cluster, None, Some(true)))
59-
.with_recommended_labels(&build_recommended_labels(
60-
cluster,
61-
&cluster.image.app_version_label_value,
62-
&OpaRole::Server.to_string(),
63-
"discovery",
55+
.with_labels(cluster.recommended_labels(
56+
&RoleGroupName::from_str("discovery").expect("'discovery' is a valid role group name"),
6457
))
65-
.context(ObjectMetaSnafu)?
6658
.build();
6759

6860
let mut cm_builder = ConfigMapBuilder::new();

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

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,35 @@
11
//! Controller-level vocabulary: the [`ValidatedCluster`] type and the `build` / `validate`
22
//! sub-modules.
33
4-
use std::collections::BTreeMap;
4+
use std::{collections::BTreeMap, str::FromStr};
55

6+
// Re-exported so the rest of the controller refers to `crate::controller::RoleGroupName`.
7+
pub use stackable_operator::v2::types::operator::RoleGroupName;
68
use stackable_operator::{
79
commons::product_image_selection::ResolvedProductImage,
810
kube::{Resource as KubeResource, api::ObjectMeta},
11+
kvp::Labels,
912
v2::{
1013
HasName, HasUid, NameIsValidLabelValue,
14+
kvp::label::{recommended_labels, role_group_selector, role_selector},
15+
role_group_utils::ResourceNames,
1116
role_utils::{GenericCommonConfig, RoleGroupConfig},
1217
types::{
1318
kubernetes::{NamespaceName, Uid},
14-
operator::{ClusterName, RoleGroupName},
19+
operator::{
20+
ClusterName, ControllerName, OperatorName, ProductName, ProductVersion, RoleName,
21+
},
1522
},
1623
},
1724
};
1825

19-
use crate::crd::{OpaConfig, OpaConfigOverrides, OpaRole, user_info_fetcher, v1alpha2};
26+
use crate::{
27+
crd::{
28+
APP_NAME, OPERATOR_NAME, OpaConfig, OpaConfigOverrides, OpaRole, user_info_fetcher,
29+
v1alpha2,
30+
},
31+
opa_controller::OPA_CONTROLLER_NAME,
32+
};
2033

2134
pub mod build;
2235
pub mod validate;
@@ -69,6 +82,72 @@ impl ValidatedCluster {
6982
pub fn server_role_service_name(&self) -> String {
7083
format!("{name}-{role}", name = self.name, role = OpaRole::Server)
7184
}
85+
86+
/// The single OPA role name (`server`).
87+
pub fn role_name() -> RoleName {
88+
RoleName::from_str(&OpaRole::Server.to_string())
89+
.expect("the server role name is a valid role name")
90+
}
91+
92+
/// Type-safe names for the resources of a given role group.
93+
pub(crate) fn resource_names(&self, role_group_name: &RoleGroupName) -> ResourceNames {
94+
ResourceNames {
95+
cluster_name: self.name.clone(),
96+
role_name: Self::role_name(),
97+
role_group_name: role_group_name.clone(),
98+
}
99+
}
100+
101+
/// The product version as a type-safe label value.
102+
///
103+
/// `app_version_label_value` is constructed to be a valid label value, so it is also a valid
104+
/// [`ProductVersion`].
105+
fn product_version(&self) -> ProductVersion {
106+
ProductVersion::from_str(&self.image.app_version_label_value)
107+
.expect("the app version label value is a valid product version")
108+
}
109+
110+
/// Recommended labels for a role-group resource.
111+
///
112+
/// For role-level or cluster-level resources (e.g. the role `Service` or the discovery
113+
/// `ConfigMap`) pass a placeholder role-group name such as `global` or `discovery`.
114+
pub fn recommended_labels(&self, role_group_name: &RoleGroupName) -> Labels {
115+
recommended_labels(
116+
self,
117+
&product_name(),
118+
&self.product_version(),
119+
&operator_name(),
120+
&controller_name(),
121+
&Self::role_name(),
122+
role_group_name,
123+
)
124+
}
125+
126+
/// Selector labels matching the pods of a role group.
127+
pub fn role_group_selector(&self, role_group_name: &RoleGroupName) -> Labels {
128+
role_group_selector(self, &product_name(), &Self::role_name(), role_group_name)
129+
}
130+
131+
/// Selector labels matching all pods of the (single) OPA role.
132+
pub fn role_selector(&self) -> Labels {
133+
role_selector(self, &product_name(), &Self::role_name())
134+
}
135+
}
136+
137+
/// The product name (`opa`) as a type-safe label value.
138+
fn product_name() -> ProductName {
139+
ProductName::from_str(APP_NAME).expect("'opa' is a valid product name")
140+
}
141+
142+
/// The operator name as a type-safe label value.
143+
fn operator_name() -> OperatorName {
144+
OperatorName::from_str(OPERATOR_NAME).expect("the operator name is a valid label value")
145+
}
146+
147+
/// The controller name as a type-safe label value.
148+
fn controller_name() -> ControllerName {
149+
ControllerName::from_str(OPA_CONTROLLER_NAME)
150+
.expect("the controller name is a valid label value")
72151
}
73152

74153
impl HasName for ValidatedCluster {

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

Lines changed: 14 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
use std::str::FromStr;
2-
31
use serde::{Deserialize, Serialize};
4-
use snafu::{OptionExt, ResultExt, Snafu};
52
use stackable_operator::{
63
commons::{
74
affinity::StackableAffinity,
@@ -15,9 +12,9 @@ use stackable_operator::{
1512
config::{fragment::Fragment, merge::Merge},
1613
deep_merger::ObjectOverrides,
1714
k8s_openapi::apimachinery::pkg::api::resource::Quantity,
18-
kube::{CustomResource, ResourceExt},
15+
kube::{CustomResource, runtime::reflector::ObjectRef},
1916
product_logging::{self, spec::Logging},
20-
role_utils::{EmptyRoleConfig, Role, RoleGroup, RoleGroupRef},
17+
role_utils::{EmptyRoleConfig, Role, RoleGroupRef},
2118
schemars::{self, JsonSchema},
2219
shared::time::Duration,
2320
status::condition::{ClusterCondition, HasStatusCondition},
@@ -41,20 +38,6 @@ pub const SERVER_GRACEFUL_SHUTDOWN_SAFETY_OVERHEAD: Duration = Duration::from_se
4138
pub type OpaRoleType =
4239
Role<OpaConfigFragment, OpaConfigOverrides, EmptyRoleConfig, GenericCommonConfig>;
4340

44-
pub type OpaRoleGroupType = RoleGroup<OpaConfigFragment, GenericCommonConfig, OpaConfigOverrides>;
45-
46-
#[derive(Snafu, Debug)]
47-
pub enum Error {
48-
#[snafu(display("the role group {role_group} is not defined"))]
49-
CannotRetrieveOpaRoleGroup { role_group: String },
50-
51-
#[snafu(display("unknown role {role}"))]
52-
UnknownOpaRole {
53-
source: strum::ParseError,
54-
role: String,
55-
},
56-
}
57-
5841
#[versioned(
5942
version(name = "v1alpha1"),
6043
version(name = "v1alpha2"),
@@ -323,30 +306,18 @@ impl v1alpha2::OpaCluster {
323306
}
324307
}
325308

326-
/// Returns a reference to the role group. Raises an error if the role or role group are not defined.
327-
pub fn rolegroup(
328-
&self,
329-
rolegroup_ref: &RoleGroupRef<v1alpha2::OpaCluster>,
330-
) -> Result<&OpaRoleGroupType, Error> {
331-
let role_variant =
332-
OpaRole::from_str(&rolegroup_ref.role).with_context(|_| UnknownOpaRoleSnafu {
333-
role: rolegroup_ref.role.to_owned(),
334-
})?;
335-
let role = self.role(&role_variant);
336-
role.role_groups
337-
.get(&rolegroup_ref.role_group)
338-
.with_context(|| CannotRetrieveOpaRoleGroupSnafu {
339-
role_group: rolegroup_ref.role_group.to_owned(),
340-
})
341-
}
342-
343-
/// The name of the role-level load-balanced Kubernetes `Service`
344-
pub fn server_role_service_name(&self) -> String {
345-
format!(
346-
"{cluster_name}-{role}",
347-
cluster_name = self.name_any(),
348-
role = OpaRole::Server
349-
)
309+
/// A [`RoleGroupRef`] for the given server role group.
310+
///
311+
/// This is only needed to build the Vector agent config via the upstream
312+
/// [`create_vector_config`](stackable_operator::product_logging::framework::create_vector_config),
313+
/// which still requires a `RoleGroupRef`. All other resource naming goes through
314+
/// [`ValidatedCluster`](crate::controller::ValidatedCluster) and the v2 `ResourceNames`.
315+
pub fn server_rolegroup_ref(&self, group_name: impl Into<String>) -> RoleGroupRef<Self> {
316+
RoleGroupRef {
317+
cluster: ObjectRef::from_obj(self),
318+
role: OpaRole::Server.to_string(),
319+
role_group: group_name.into(),
320+
}
350321
}
351322
}
352323

0 commit comments

Comments
 (0)