Skip to content

Commit ff9bec0

Browse files
authored
chore: update olm deployer (#344)
* remove kubernetes cluster info options * remove short cli args to avoid -d conflict * remove hard coded container names * fix clippy lint * update changelog
1 parent fce3cb1 commit ff9bec0

File tree

6 files changed

+86
-68
lines changed

6 files changed

+86
-68
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,14 @@ All notable changes to this project will be documented in this file.
2828
- Bump csi-node-driver-registrar to `v2.15.0` ([#337]).
2929
- Bump csi-provisioner to `v5.3.0` ([#338]).
3030
- We now default to the `ephemeral-nodes` helm preset. Read on the [issue](https://github.com/stackabletech/issues/issues/770) for details ([#340]).
31+
- olm-deployer: update to align with new operator configuration ([#344]).
3132

3233
[#334]: https://github.com/stackabletech/listener-operator/pull/334
3334
[#337]: https://github.com/stackabletech/listener-operator/pull/337
3435
[#338]: https://github.com/stackabletech/listener-operator/pull/338
3536
[#339]: https://github.com/stackabletech/listener-operator/pull/339
3637
[#340]: https://github.com/stackabletech/listener-operator/pull/340
38+
[#344]: https://github.com/stackabletech/listener-operator/pull/344
3739

3840
## [25.7.0] - 2025-07-23
3941

rust/olm-deployer/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Description
2+
3+
This is an deployment helper for the Operator Lifecycle Manager which is usually present on OpenShift environments.
4+
5+
It is needed to work around various OLM restrictions.
6+
7+
What it does:
8+
9+
- creates Security Context Constraints just for this operator (maybe remove in the future)
10+
- installs the Deployment and DaemonSet objects
11+
- installs the operator service
12+
- installs the CSI driver and storage classes
13+
- assigns it's own deployment as owner of all the namespaced objects to ensure proper cleanup
14+
- patches the environment of all workload containers with any custom values provided in the Subscription object
15+
- patches the resources of all workload containers with any custom values provided in the Subscription object
16+
- patches the tolerations of all workload pods with any custom values provided in the Subscription object
17+
18+
## Usage
19+
20+
Users do not need to interact with the OLM deployer directly.

rust/olm-deployer/src/data.rs

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,11 @@
11
use anyhow::{Context, anyhow};
22
use stackable_operator::kube::{ResourceExt, api::DynamicObject};
33

4-
pub fn container<'a>(
5-
target: &'a mut DynamicObject,
6-
container_name: &str,
7-
) -> anyhow::Result<&'a mut serde_json::Value> {
4+
pub fn containers(target: &mut DynamicObject) -> anyhow::Result<&mut Vec<serde_json::Value>> {
85
let tname = target.name_any();
96
let path = "template/spec/containers".split("/");
10-
match get_or_create(
11-
target
12-
.data
13-
.pointer_mut("/spec")
14-
.context(anyhow!("object [{tname}] has no .spec property"))?,
15-
path,
16-
)? {
17-
serde_json::Value::Array(containers) => {
18-
for c in containers {
19-
if c.is_object() {
20-
if let Some(serde_json::Value::String(name)) = c.get("name") {
21-
if container_name == name {
22-
return Ok(c);
23-
}
24-
}
25-
} else {
26-
anyhow::bail!("container is not a object: {:?}", c);
27-
}
28-
}
29-
anyhow::bail!("container named {container_name} not found");
30-
}
7+
match get_or_create(target.data.pointer_mut("/spec").unwrap(), path)? {
8+
serde_json::Value::Array(containers) => Ok(containers),
319
_ => anyhow::bail!("no containers found in object {tname}"),
3210
}
3311
}

rust/olm-deployer/src/env/mod.rs

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,41 +6,46 @@ use stackable_operator::{
66
},
77
};
88

9-
use crate::data::container;
9+
use crate::data::containers;
1010

1111
/// Copy the environment from the "listener-operator-deployer" container in `source`
12-
/// to the container "listener-operator" in `target`.
13-
/// The `target` must be a DaemonSet object otherwise this is a no-op.
12+
/// to *all* containers in target.
13+
/// The target must be a DaemonSet or Deployment, otherwise this function is a no-op.
14+
/// This function allows OLM Subscription objects to configure the environment
15+
/// of operator containers.
1416
pub(super) fn maybe_copy_env(
1517
source: &Deployment,
1618
target: &mut DynamicObject,
1719
target_gvk: &GroupVersionKind,
1820
) -> anyhow::Result<()> {
19-
if target_gvk.kind == "DaemonSet" {
21+
let target_kind_set = ["DaemonSet", "Deployment"];
22+
if target_kind_set.contains(&target_gvk.kind.as_str()) {
2023
if let Some(env) = deployer_env_var(source) {
21-
match container(target, "listener-operator")? {
22-
serde_json::Value::Object(c) => {
23-
let json_env = env
24-
.iter()
25-
.map(|e| serde_json::json!(e))
26-
.collect::<Vec<serde_json::Value>>();
27-
28-
match c.get_mut("env") {
29-
Some(env) => match env {
30-
v @ serde_json::Value::Null => {
31-
*v = serde_json::json!(json_env);
24+
for container in containers(target)? {
25+
match container {
26+
serde_json::Value::Object(c) => {
27+
let json_env = env
28+
.iter()
29+
.map(|e| serde_json::json!(e))
30+
.collect::<Vec<serde_json::Value>>();
31+
32+
match c.get_mut("env") {
33+
Some(env) => match env {
34+
v @ serde_json::Value::Null => {
35+
*v = serde_json::json!(json_env);
36+
}
37+
serde_json::Value::Array(container_env) => {
38+
container_env.extend_from_slice(&json_env)
39+
}
40+
_ => anyhow::bail!("env is not null or an array"),
41+
},
42+
None => {
43+
c.insert("env".to_string(), serde_json::json!(json_env));
3244
}
33-
serde_json::Value::Array(container_env) => {
34-
container_env.extend_from_slice(&json_env)
35-
}
36-
_ => anyhow::bail!("env is not null or an array"),
37-
},
38-
None => {
39-
c.insert("env".to_string(), serde_json::json!(json_env));
4045
}
4146
}
47+
_ => anyhow::bail!("no containers found in object {}", target.name_any()),
4248
}
43-
_ => anyhow::bail!("no containers found in object {}", target.name_any()),
4449
}
4550
}
4651
}
@@ -151,7 +156,9 @@ spec:
151156
},
152157
]);
153158
assert_eq!(
154-
container(&mut daemonset, "listener-operator")?
159+
containers(&mut daemonset)?
160+
.first()
161+
.expect("daemonset has no containers")
155162
.get("env")
156163
.unwrap(),
157164
&expected

rust/olm-deployer/src/main.rs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use clap::Parser;
2222
use stackable_operator::{
2323
cli::Command,
2424
client,
25+
commons::networking::DomainName,
2526
k8s_openapi::api::{apps::v1::Deployment, rbac::v1::ClusterRole},
2627
kube::{
2728
self,
@@ -50,33 +51,28 @@ struct Opts {
5051
struct OlmDeployerRun {
5152
#[arg(
5253
long,
53-
short,
5454
default_value = "false",
5555
help = "Keep running after manifests have been successfully applied."
5656
)]
5757
keep_alive: bool,
5858

5959
#[arg(
6060
long,
61-
short,
6261
help = "Name of ClusterServiceVersion object that owns this Deployment."
6362
)]
6463
csv: String,
6564

66-
#[arg(long, short, help = "Name of deployment object that owns this Pod.")]
65+
#[arg(long, help = "Name of deployment object that owns this Pod.")]
6766
deployer: String,
6867

69-
#[arg(long, short, help = "Namespace of the ClusterServiceVersion object.")]
68+
#[arg(long, help = "Namespace of the ClusterServiceVersion object.")]
7069
namespace: String,
7170

72-
#[arg(long, short, help = "Directory with manifests to patch and apply.")]
71+
#[arg(long, help = "Directory with manifests to patch and apply.")]
7372
dir: std::path::PathBuf,
7473

7574
#[command(flatten)]
7675
pub telemetry: TelemetryOptions,
77-
78-
#[command(flatten)]
79-
pub cluster_info: KubernetesClusterInfoOptions,
8076
}
8177

8278
#[tokio::main]
@@ -89,7 +85,6 @@ async fn main() -> Result<()> {
8985
namespace,
9086
dir,
9187
telemetry,
92-
cluster_info,
9388
}) = opts.cmd
9489
{
9590
// NOTE (@NickLarsenNZ): Before stackable-telemetry was used:
@@ -108,7 +103,16 @@ async fn main() -> Result<()> {
108103
description = built_info::PKG_DESCRIPTION
109104
);
110105

111-
let client = client::initialize_operator(Some(APP_NAME.to_string()), &cluster_info).await?;
106+
// Not used by the olm deployer but still want to use client::initialize_operator()
107+
// Without this dummy value, the KUBERNETES_NODE_NAME env/cli argument would be required
108+
// but not used.
109+
let dummy_cluster_info = KubernetesClusterInfoOptions {
110+
kubernetes_cluster_domain: Some(DomainName::try_from("cluster.local")?),
111+
kubernetes_node_name: "".to_string(),
112+
};
113+
114+
let client =
115+
client::initialize_operator(Some(APP_NAME.to_string()), &dummy_cluster_info).await?;
112116

113117
let deployment = get_deployment(&csv, &deployer, &namespace, &client).await?;
114118
let cluster_role = get_cluster_role(&csv, &client).await?;

rust/olm-deployer/src/resources/mod.rs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,28 @@ use stackable_operator::{
66
},
77
};
88

9-
use crate::data::container;
9+
use crate::data::containers;
1010

1111
/// Copies the resources of the container named "listener-operator-deployer" from `source`
12-
/// to the container "listener-operator" in `target`.
13-
/// Does nothing if there are no resources or if the `target` is not a DaemonSet.
12+
/// to *all* containers in `target`.
13+
/// Does nothing if there are no resources or if the `target` is not a DaemonSet or a Deployment.
14+
/// This function allows OLM Subscription objects to configure the resources
15+
/// of operator containers.
1416
pub(super) fn maybe_copy_resources(
1517
source: &Deployment,
1618
target: &mut DynamicObject,
1719
target_gvk: &GroupVersionKind,
1820
) -> anyhow::Result<()> {
19-
if target_gvk.kind == "DaemonSet" {
21+
let target_kind_set = ["DaemonSet", "Deployment"];
22+
if target_kind_set.contains(&target_gvk.kind.as_str()) {
2023
if let Some(res) = deployment_resources(source) {
21-
match container(target, "listener-operator")? {
22-
serde_json::Value::Object(c) => {
23-
c.insert("resources".to_string(), serde_json::json!(res));
24+
for container in containers(target)? {
25+
match container {
26+
serde_json::Value::Object(c) => {
27+
c.insert("resources".to_string(), serde_json::json!(res));
28+
}
29+
_ => anyhow::bail!("no containers found in object {}", target.name_any()),
2430
}
25-
_ => anyhow::bail!("no containers found in object {}", target.name_any()),
2631
}
2732
}
2833
}
@@ -152,7 +157,9 @@ spec:
152157
..ResourceRequirements::default()
153158
});
154159
assert_eq!(
155-
container(&mut daemonset, "listener-operator")?
160+
containers(&mut daemonset)?
161+
.first()
162+
.expect("daemonset has no containers")
156163
.get("resources")
157164
.unwrap(),
158165
&expected

0 commit comments

Comments
 (0)