Skip to content

Commit e4e2a3b

Browse files
committed
Merge branch 'main' into feat/openlineage-from-op-rs
2 parents 8452830 + cc0b62d commit e4e2a3b

7 files changed

Lines changed: 339 additions & 215 deletions

File tree

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.
44

55
## [Unreleased]
66

7+
### Changed
8+
9+
- Internal operator refactoring: introduce a build() step in the reconciler that
10+
assembles all relevant Kubernetes resources before anything is applied ([#909]).
11+
12+
[#909]: https://github.com/stackabletech/trino-operator/pull/909
13+
14+
## [26.7.0] - 2026-07-21
15+
16+
## [26.7.0-rc1] - 2026-07-16
17+
718
### Added
819

920
- BREAKING: Add required CLI argument and env var to set the image repository used to construct final product image names: `IMAGE_REPOSITORY` (`--image-repository`), eg. `oci.example.org/my/namespace` ([#884]).

rust/operator-binary/src/authentication/password/file.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use stackable_operator::{
2020

2121
use crate::{
2222
authentication::password::PASSWORD_AUTHENTICATOR_NAME,
23-
controller::build::resource::statefulset::LOG_VOLUME_NAME, trino_controller::STACKABLE_LOG_DIR,
23+
controller::{STACKABLE_LOG_DIR, build::resource::statefulset::LOG_VOLUME_NAME},
2424
};
2525

2626
// mounts

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

Lines changed: 208 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,24 @@
22
33
use std::str::FromStr;
44

5-
use stackable_operator::v2::types::operator::RoleGroupName;
5+
use snafu::{ResultExt, Snafu};
6+
use stackable_operator::{
7+
utils::cluster_info::KubernetesClusterInfo, v2::types::operator::RoleGroupName,
8+
};
9+
10+
use crate::controller::{
11+
KubernetesResources, ValidatedCluster,
12+
build::resource::{
13+
config_map,
14+
listener::{build_group_listener, group_listener_name},
15+
pdb::build_pdb,
16+
service::{
17+
build_rolegroup_headless_service, build_rolegroup_metrics_service,
18+
headless_service_ports,
19+
},
20+
statefulset,
21+
},
22+
};
623

724
pub mod command;
825
pub mod graceful_shutdown;
@@ -13,4 +30,193 @@ pub mod resource;
1330
// Placeholder role-group name used for the recommended labels of a role's group listener.
1431
// The group listener is owned by the role (not a single role-group), so there is no real
1532
// role-group to attribute it to.
16-
stackable_operator::constant!(pub(crate) PLACEHOLDER_LISTENER_ROLE_GROUP: RoleGroupName = "none");
33+
stackable_operator::constant!(PLACEHOLDER_LISTENER_ROLE_GROUP: RoleGroupName = "none");
34+
35+
#[derive(Snafu, Debug)]
36+
pub enum Error {
37+
#[snafu(display("failed to build ConfigMap for role group {role_group}"))]
38+
ConfigMap {
39+
source: config_map::Error,
40+
role_group: RoleGroupName,
41+
},
42+
43+
#[snafu(display("failed to build StatefulSet for role group {role_group}"))]
44+
StatefulSet {
45+
source: statefulset::Error,
46+
role_group: RoleGroupName,
47+
},
48+
}
49+
50+
/// Builds every Kubernetes resource for the given validated cluster.
51+
///
52+
/// Does not need a Kubernetes client: every reference to another Kubernetes resource is already
53+
/// dereferenced and validated by this point, so the errors returned here are resource-assembly
54+
/// failures only.
55+
///
56+
/// `service_account_name` is the name of the RBAC `ServiceAccount` the role-group Pods run under
57+
/// (RBAC resources are built and applied separately, in the reconcile step).
58+
pub fn build(
59+
cluster: &ValidatedCluster,
60+
cluster_info: &KubernetesClusterInfo,
61+
service_account_name: &str,
62+
) -> Result<KubernetesResources, Error> {
63+
let mut stateful_sets = vec![];
64+
let mut services = vec![];
65+
let mut listeners = vec![];
66+
let mut config_maps = vec![];
67+
let mut pod_disruption_budgets = vec![];
68+
69+
for (role, role_group_configs) in &cluster.role_group_configs {
70+
for (role_group_name, role_group_config) in role_group_configs {
71+
let recommended_labels = cluster.recommended_labels(role, role_group_name);
72+
let selector = cluster.role_group_selector(role, role_group_name);
73+
74+
services.push(build_rolegroup_headless_service(
75+
cluster,
76+
role,
77+
role_group_name,
78+
&recommended_labels,
79+
selector.clone().into(),
80+
headless_service_ports(cluster),
81+
));
82+
services.push(build_rolegroup_metrics_service(
83+
cluster,
84+
role,
85+
role_group_name,
86+
&recommended_labels,
87+
selector.into(),
88+
));
89+
config_maps.push(
90+
config_map::build_rolegroup_config_map(
91+
cluster,
92+
role,
93+
role_group_name,
94+
cluster_info,
95+
&recommended_labels,
96+
)
97+
.context(ConfigMapSnafu {
98+
role_group: role_group_name.clone(),
99+
})?,
100+
);
101+
config_maps.push(
102+
config_map::build_rolegroup_catalog_config_map(
103+
cluster,
104+
role,
105+
role_group_name,
106+
&recommended_labels,
107+
)
108+
.context(ConfigMapSnafu {
109+
role_group: role_group_name.clone(),
110+
})?,
111+
);
112+
stateful_sets.push(
113+
statefulset::build_rolegroup_statefulset(
114+
cluster,
115+
role,
116+
role_group_name,
117+
role_group_config,
118+
service_account_name,
119+
)
120+
.context(StatefulSetSnafu {
121+
role_group: role_group_name.clone(),
122+
})?,
123+
);
124+
}
125+
126+
let Some(role_config) = cluster.role_config(role) else {
127+
continue;
128+
};
129+
130+
if let Some(listener_class) = &role_config.listener_class
131+
&& let Some(listener_group_name) = group_listener_name(cluster, role)
132+
{
133+
listeners.push(build_group_listener(
134+
cluster,
135+
cluster.recommended_labels(role, &PLACEHOLDER_LISTENER_ROLE_GROUP),
136+
listener_class,
137+
listener_group_name,
138+
));
139+
}
140+
141+
pod_disruption_budgets.extend(build_pdb(&role_config.pdb, cluster, role));
142+
}
143+
144+
Ok(KubernetesResources {
145+
stateful_sets,
146+
services,
147+
listeners,
148+
config_maps,
149+
pod_disruption_budgets,
150+
})
151+
}
152+
153+
#[cfg(test)]
154+
mod tests {
155+
use stackable_operator::{
156+
commons::networking::DomainName, kube::Resource, utils::cluster_info::KubernetesClusterInfo,
157+
};
158+
159+
use super::build;
160+
use crate::controller::validated_cluster;
161+
162+
/// Collects the `.metadata.name`s of the given resources, sorted for stable comparison.
163+
fn sorted_names(resources: &[impl Resource]) -> Vec<&str> {
164+
let mut names: Vec<&str> = resources
165+
.iter()
166+
.filter_map(|resource| resource.meta().name.as_deref())
167+
.collect();
168+
names.sort();
169+
names
170+
}
171+
172+
#[test]
173+
fn build_produces_expected_resource_names() {
174+
let cluster = validated_cluster();
175+
let cluster_info = KubernetesClusterInfo {
176+
cluster_domain: DomainName::try_from("cluster.local")
177+
.expect("cluster.local is a valid domain name"),
178+
};
179+
180+
let resources =
181+
build(&cluster, &cluster_info, "simple-trino-serviceaccount").expect("build succeeds");
182+
183+
// One StatefulSet per role group.
184+
assert_eq!(
185+
sorted_names(&resources.stateful_sets),
186+
[
187+
"simple-trino-coordinator-default",
188+
"simple-trino-worker-default",
189+
]
190+
);
191+
// One headless and one metrics Service per role group.
192+
assert_eq!(
193+
sorted_names(&resources.services),
194+
[
195+
"simple-trino-coordinator-default-headless",
196+
"simple-trino-coordinator-default-metrics",
197+
"simple-trino-worker-default-headless",
198+
"simple-trino-worker-default-metrics",
199+
]
200+
);
201+
// A config ConfigMap and a catalog ConfigMap per role group.
202+
assert_eq!(
203+
sorted_names(&resources.config_maps),
204+
[
205+
"simple-trino-coordinator-default",
206+
"simple-trino-coordinator-default-catalog",
207+
"simple-trino-worker-default",
208+
"simple-trino-worker-default-catalog",
209+
]
210+
);
211+
// The coordinator is the only role with a group Listener.
212+
assert_eq!(
213+
sorted_names(&resources.listeners),
214+
["simple-trino-coordinator"]
215+
);
216+
// A default PodDisruptionBudget per role.
217+
assert_eq!(
218+
sorted_names(&resources.pod_disruption_budgets),
219+
["simple-trino-coordinator", "simple-trino-worker"]
220+
);
221+
}
222+
}

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,16 @@ use crate::{
1212
authentication::TrinoAuthenticationConfig,
1313
catalog::config::CatalogConfig,
1414
config::{client_protocol, fault_tolerant_execution},
15-
controller::{ValidatedCluster, ValidatedTrinoConfig, build::properties::ConfigFileName},
15+
controller::{
16+
STACKABLE_LOG_CONFIG_DIR, STACKABLE_LOG_DIR, ValidatedCluster, ValidatedTrinoConfig,
17+
build::properties::ConfigFileName,
18+
},
1619
crd::{
1720
CONFIG_DIR_NAME, Container, RW_CONFIG_DIR_NAME, STACKABLE_CLIENT_TLS_DIR,
1821
STACKABLE_INTERNAL_TLS_DIR, STACKABLE_MOUNT_INTERNAL_TLS_DIR,
1922
STACKABLE_MOUNT_SERVER_TLS_DIR, STACKABLE_SERVER_TLS_DIR, STACKABLE_TLS_STORE_PASSWORD,
2023
TrinoRole, catalog::TrinoCatalogName,
2124
},
22-
trino_controller::{STACKABLE_LOG_CONFIG_DIR, STACKABLE_LOG_DIR},
2325
};
2426

2527
pub fn container_prepare_args(

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,16 @@ use stackable_operator::{
4040
use crate::{
4141
authorization::opa::OPA_TLS_VOLUME_NAME,
4242
controller::{
43-
RoleGroupName, TrinoRoleGroupConfig, ValidatedCluster, build,
43+
MAX_PREPARE_LOG_FILE_SIZE, RoleGroupName, STACKABLE_LOG_CONFIG_DIR, STACKABLE_LOG_DIR,
44+
TrinoRoleGroupConfig, ValidatedCluster, build,
4445
build::{
4546
command,
4647
resource::listener::{
4748
LISTENER_VOLUME_DIR, LISTENER_VOLUME_NAME, build_group_listener_pvc,
4849
group_listener_name, secret_volume_listener_scope,
4950
},
5051
},
52+
shared_internal_secret_name, shared_spooling_secret_name,
5153
},
5254
crd::{
5355
CONFIG_DIR_NAME, Container, ENV_INTERNAL_SECRET, ENV_SPOOLING_SECRET, HTTP_PORT,
@@ -57,10 +59,6 @@ use crate::{
5759
STACKABLE_MOUNT_SERVER_TLS_DIR, STACKABLE_SERVER_TLS_DIR, STACKABLE_TLS_STORE_PASSWORD,
5860
TrinoRole,
5961
},
60-
trino_controller::{
61-
MAX_PREPARE_LOG_FILE_SIZE, STACKABLE_LOG_CONFIG_DIR, STACKABLE_LOG_DIR,
62-
shared_internal_secret_name, shared_spooling_secret_name,
63-
},
6462
};
6563

6664
stackable_operator::constant!(VECTOR_CONTAINER_NAME: ContainerName = "vector");

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,15 @@ use stackable_operator::{
77
product_image_selection::ResolvedProductImage,
88
resources::{NoRuntimeLimits, Resources},
99
},
10+
crd::listener::v1alpha1::Listener,
11+
k8s_openapi::api::{
12+
apps::v1::StatefulSet,
13+
core::v1::{ConfigMap, Service},
14+
policy::v1::PodDisruptionBudget,
15+
},
1016
kube::{Resource, api::ObjectMeta},
1117
kvp::Labels,
18+
memory::{BinaryMultiple, MemoryQuantity},
1219
shared::time::Duration,
1320
v2::{
1421
HasName, HasUid, NameIsValidLabelValue,
@@ -41,7 +48,31 @@ pub(crate) mod build;
4148
pub(crate) mod dereference;
4249
pub(crate) mod validate;
4350

51+
pub use stackable_operator::v2::product_logging::framework::STACKABLE_LOG_DIR;
4452
pub use validate::{RoleGroupName, TrinoRoleGroupConfig};
53+
pub const STACKABLE_LOG_CONFIG_DIR: &str = "/stackable/log_config";
54+
55+
pub const MAX_PREPARE_LOG_FILE_SIZE: MemoryQuantity = MemoryQuantity {
56+
value: 1.0,
57+
unit: BinaryMultiple::Mebi,
58+
};
59+
60+
pub(crate) fn shared_internal_secret_name(cluster_name: &ClusterName) -> String {
61+
format!("{cluster_name}-internal-secret")
62+
}
63+
64+
pub(crate) fn shared_spooling_secret_name(cluster_name: &ClusterName) -> String {
65+
format!("{cluster_name}-spooling-secret")
66+
}
67+
68+
/// Every Kubernetes resource produced by the client-free [`build()`](build::build) step.
69+
pub struct KubernetesResources {
70+
pub stateful_sets: Vec<StatefulSet>,
71+
pub services: Vec<Service>,
72+
pub listeners: Vec<Listener>,
73+
pub config_maps: Vec<ConfigMap>,
74+
pub pod_disruption_budgets: Vec<PodDisruptionBudget>,
75+
}
4576

4677
#[derive(Clone, Debug)]
4778
pub struct ValidatedTls {

0 commit comments

Comments
 (0)