Skip to content

Commit 99d221a

Browse files
authored
Merge branch 'main' into spike/generic-databases
2 parents e9e9011 + a4a204b commit 99d221a

11 files changed

Lines changed: 469 additions & 384 deletions

File tree

Cargo.lock

Lines changed: 304 additions & 377 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ product-config = { git = "https://github.com/stackabletech/product-config.git",
1414
arc-swap = "1.7.0"
1515
async-trait = "0.1.89"
1616
axum = { version = "0.8.1", features = ["http2"] }
17+
base64 = "0.22"
1718
clap = { version = "4.5.17", features = ["derive", "cargo", "env"] }
1819
const_format = "0.2.33"
1920
# Cannot be updated until x509-cert uses a newer version
@@ -68,7 +69,7 @@ serde_json = "1.0.128"
6869
serde_yaml = "0.9.34" # This is the last available version, see https://github.com/dtolnay/serde-yaml/releases/tag/0.9.34 for details
6970
sha2 = { version = "0.10.8", features = ["oid"] }
7071
signature = "2.2.0"
71-
snafu = "0.8.4"
72+
snafu = "0.9.0"
7273
stackable-operator-derive = { path = "stackable-operator-derive" }
7374
strum = { version = "0.28.0", features = ["derive"] }
7475
syn = "2.0.77"

crates/stackable-operator/CHANGELOG.md

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

55
## [Unreleased]
66

7+
- Add generic database connection mechanism ([#1163]).
8+
9+
[#1163]: https://github.com/stackabletech/operator-rs/pull/1163
10+
11+
## [0.109.0] - 2026-04-07
12+
713
### Added
814

915
- Git sync: add support for CAs ([#1154]).
1016
- Add support for specifying a `clientAuthenticationMethod` for OIDC ([#1178]).
1117
This was originally done in [#1158] and had been reverted in [#1170].
12-
- Add generic database connection mechanism ([#1163]).
1318

1419
### Changed
1520

1621
- Implement `Deref` for `kvp::Key` to be more ergonomic to use ([#1182]).
22+
- Add `create_random_secret_if_not_exists` function, which create a random Secret in case it doesn't already exist.
23+
It notably also fixes a bug we had in trino and airflow-operator, where we created immutable Secrets,
24+
which lead to problems ([#1187]).
25+
26+
### Changed
27+
28+
- Bump stackable-versioned to `0.9.0`, refer to its [changelog](../stackable-versioned/CHANGELOG.md) ([#1189]).
29+
- Bump stackable-webhook to `0.9.1`, refer to its [changelog](../stackable-webhook/CHANGELOG.md) ([#1189]).
1730
- BREAKING: Add mandatory `provision_parts` argument to `SecretOperatorVolumeSourceBuilder::new` ([#1165]).
1831
It now forces the caller to make an explicit choice if the public parts are sufficient or if private
1932
(e.g. a certificate for the Pod) parts are needed as well. This is done to avoid accidentally requesting
@@ -29,10 +42,11 @@ All notable changes to this project will be documented in this file.
2942
functions from `kvp::Key` to disallow mutable access to inner values ([#1182]).
3043

3144
[#1154]: https://github.com/stackabletech/operator-rs/pull/1154
32-
[#1163]: https://github.com/stackabletech/operator-rs/pull/1163
3345
[#1165]: https://github.com/stackabletech/operator-rs/pull/1165
3446
[#1178]: https://github.com/stackabletech/operator-rs/pull/1178
3547
[#1182]: https://github.com/stackabletech/operator-rs/pull/1182
48+
[#1187]: https://github.com/stackabletech/operator-rs/pull/1187
49+
[#1189]: https://github.com/stackabletech/operator-rs/pull/1189
3650

3751
## [0.108.0] - 2026-03-10
3852

crates/stackable-operator/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "stackable-operator"
33
description = "Stackable Operator Framework"
4-
version = "0.108.0"
4+
version = "0.109.0"
55
authors.workspace = true
66
license.workspace = true
77
edition.workspace = true
@@ -25,6 +25,7 @@ stackable-telemetry = { path = "../stackable-telemetry", features = ["clap"] }
2525
stackable-versioned = { path = "../stackable-versioned", optional = true }
2626
stackable-webhook = { path = "../stackable-webhook", optional = true }
2727

28+
base64.workspace = true
2829
clap.workspace = true
2930
const_format.workspace = true
3031
delegate.workspace = true
@@ -39,6 +40,7 @@ json-patch.workspace = true
3940
k8s-openapi.workspace = true
4041
kube.workspace = true
4142
product-config.workspace = true
43+
rand.workspace = true
4244
regex.workspace = true
4345
schemars.workspace = true
4446
semver.workspace = true

crates/stackable-operator/src/commons/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub mod networking;
77
pub mod opa;
88
pub mod pdb;
99
pub mod product_image_selection;
10+
pub mod random_secret_creation;
1011
pub mod rbac;
1112
pub mod resources;
1213
pub mod secret_class;
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
use std::collections::BTreeMap;
2+
3+
use base64::Engine;
4+
use k8s_openapi::api::core::v1::Secret;
5+
use kube::{Api, Resource, ResourceExt, api::DeleteParams};
6+
use rand::{RngCore, SeedableRng, rngs::StdRng};
7+
use snafu::{OptionExt, ResultExt, Snafu};
8+
9+
use crate::{builder::meta::ObjectMetaBuilder, client::Client};
10+
11+
#[derive(Snafu, Debug)]
12+
pub enum Error {
13+
#[snafu(display("object defines no namespace"))]
14+
ObjectHasNoNamespace,
15+
16+
#[snafu(display("failed to retrieve random secret"))]
17+
RetrieveRandomSecret { source: crate::client::Error },
18+
19+
#[snafu(display("failed to create random secret"))]
20+
CreateRandomSecret { source: crate::client::Error },
21+
22+
#[snafu(display("failed to delete random secret"))]
23+
DeleteRandomSecret { source: kube::Error },
24+
25+
#[snafu(display("object is missing metadata to build owner reference"))]
26+
ObjectMissingMetadataForOwnerRef { source: crate::builder::meta::Error },
27+
}
28+
29+
/// This function creates a random Secret if it doesn't already exist.
30+
///
31+
/// As this function generates random Secret contents, if the Secret already exists, it will *not*
32+
/// be patched, as otherwise we would generate new Secret contents on every reconcile. Which would
33+
/// in turn cause Pods restarts, which would cause reconciles ;)
34+
///
35+
/// However, there is one special handling needed:
36+
///
37+
/// We can't mark Secrets as immutable, as this caused problems, see https://github.com/stackabletech/issues/issues/843.
38+
/// As Secrets have been created as immutable up to SDP release 26.3.0, we need to delete the, to be
39+
/// able to re-create them as mutable. This function detects old (immutable) Secrets and re-creates
40+
/// them as mutable. The contents of the Secret will be kept to prevent unnecessary Secret content
41+
/// changes.
42+
//
43+
// TODO: This can be removed in a future SDP release, likely 26.11, as all Secrets have been migrated.
44+
pub async fn create_random_secret_if_not_exists<R>(
45+
secret_name: &str,
46+
secret_key: &str,
47+
secret_size_bytes: usize,
48+
stacklet: &R,
49+
client: &Client,
50+
) -> Result<(), Error>
51+
where
52+
R: Resource<DynamicType = ()>,
53+
{
54+
let secret_namespace = stacklet.namespace().context(ObjectHasNoNamespaceSnafu)?;
55+
let existing_secret = client
56+
.get_opt::<Secret>(secret_name, &secret_namespace)
57+
.await
58+
.context(RetrieveRandomSecretSnafu)?;
59+
60+
match existing_secret {
61+
Some(
62+
existing_secret @ Secret {
63+
immutable: Some(true),
64+
..
65+
},
66+
) => {
67+
tracing::info!(
68+
k8s.secret.name = secret_name,
69+
k8s.secret.namespace = secret_namespace,
70+
"Old (immutable) random Secret detected, re-creating it to be able to make it mutable. The contents will stay the same."
71+
);
72+
Api::<Secret>::namespaced(client.as_kube_client(), &secret_namespace)
73+
.delete(secret_name, &DeleteParams::default())
74+
.await
75+
.context(DeleteRandomSecretSnafu)?;
76+
77+
let mut mutable_secret = existing_secret;
78+
mutable_secret.immutable = Some(false);
79+
// Prevent "ApiError: resourceVersion should not be set on objects to be created"
80+
mutable_secret.metadata.resource_version = None;
81+
82+
client
83+
.create(&mutable_secret)
84+
.await
85+
.context(CreateRandomSecretSnafu)?;
86+
87+
// Note: restart-controller will restart all Pods mounting this Secret, as it has
88+
// changed.
89+
}
90+
Some(_) => {
91+
tracing::debug!(
92+
k8s.secret.name = secret_name,
93+
k8s.secret.namespace = secret_namespace,
94+
"Existing (mutable) random Secret detected, nothing to do"
95+
);
96+
}
97+
None => {
98+
tracing::info!(
99+
k8s.secret.name = secret_name,
100+
k8s.secret.namespace = secret_namespace,
101+
"Random Secret missing, creating it"
102+
);
103+
let secret = Secret {
104+
metadata: ObjectMetaBuilder::new()
105+
.name(secret_name)
106+
.namespace_opt(stacklet.namespace())
107+
.ownerreference_from_resource(stacklet, None, Some(true))
108+
.context(ObjectMissingMetadataForOwnerRefSnafu)?
109+
.build(),
110+
string_data: Some(BTreeMap::from([(
111+
secret_key.to_string(),
112+
get_random_base64(secret_size_bytes),
113+
)])),
114+
..Secret::default()
115+
};
116+
client
117+
.create(&secret)
118+
.await
119+
.context(CreateRandomSecretSnafu)?;
120+
}
121+
}
122+
123+
Ok(())
124+
}
125+
126+
/// Generates a cryptographically secure base64 String with the specified size in bytes.
127+
fn get_random_base64(size_bytes: usize) -> String {
128+
// As we are using the OS rng, we are using `getrandom`, which should be cryptographically
129+
// secure
130+
let mut rng = StdRng::from_os_rng();
131+
132+
let mut bytes = vec![0u8; size_bytes];
133+
rng.fill_bytes(&mut bytes);
134+
135+
base64::engine::general_purpose::STANDARD.encode(bytes)
136+
}

crates/stackable-versioned-macros/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "stackable-versioned-macros"
3-
version = "0.8.3"
3+
version = "0.9.0"
44
authors.workspace = true
55
license.workspace = true
66
edition.workspace = true

crates/stackable-versioned/CHANGELOG.md

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

55
## [Unreleased]
66

7+
## [0.9.0] - 2026-04-07
8+
79
### Added
810

911
- Add support to override kube crate ([#1173]).

crates/stackable-versioned/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "stackable-versioned"
3-
version = "0.8.3"
3+
version = "0.9.0"
44
authors.workspace = true
55
license.workspace = true
66
edition.workspace = true

crates/stackable-webhook/CHANGELOG.md

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

55
## [Unreleased]
66

7+
## [0.9.1] - 2026-04-07
8+
79
### Changed
810

911
- Check for certificate rotation every 5 minutes to prevent expiry due to monotonic and wall clock divergence ([#1175]).

0 commit comments

Comments
 (0)