Skip to content

Commit ac5925c

Browse files
committed
fix: Place internal secrets in mutable Kubernetes Secrets
1 parent 6cdffb9 commit ac5925c

2 files changed

Lines changed: 97 additions & 33 deletions

File tree

CHANGELOG.md

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

55
## [Unreleased]
66

7+
### Fixed
8+
9+
- Previously, the shared secret as well as the client spooling secret have been placed in immutable Kubernetes Secrets.
10+
This caused problems, as they have been cached by Kubernetes, so re-creations of the mentioned Secrets (e.g. by deleting and re-creating the stacklet)
11+
could cause Trino Pods to have different shared secrets, causing workers failing to join the coordinator.
12+
This fix places the secrets in mutable Kubernetes Secrets going forward and migrates existing immutable Secrets to mutable by re-creating them ([#XXX]).
13+
14+
[#XXX]: https://github.com/stackabletech/trino-operator/pull/XXX
15+
716
## [26.3.0] - 2026-03-16
817

918
## [26.3.0-rc1] - 2026-03-16

rust/operator-binary/src/controller.rs

Lines changed: 88 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ use stackable_operator::{
4646
apimachinery::pkg::{apis::meta::v1::LabelSelector, util::intstr::IntOrString},
4747
},
4848
kube::{
49-
Resource, ResourceExt,
49+
Api, Resource, ResourceExt,
50+
api::DeleteParams,
5051
core::{DeserializeGuard, error_boundary},
5152
runtime::{controller::Action, reflector::ObjectRef},
5253
},
@@ -186,6 +187,11 @@ pub enum Error {
186187
source: stackable_operator::client::Error,
187188
},
188189

190+
#[snafu(display("failed to delete internal secret"))]
191+
DeleteInternalSecret {
192+
source: stackable_operator::kube::Error,
193+
},
194+
189195
#[snafu(display("invalid product config"))]
190196
InvalidProductConfig {
191197
source: stackable_operator::product_config_utils::Error,
@@ -537,7 +543,7 @@ pub async fn reconcile_trino(
537543
None => None,
538544
};
539545

540-
create_random_secret(
546+
create_random_secret_if_not_exists(
541547
&shared_internal_secret_name(trino),
542548
ENV_INTERNAL_SECRET,
543549
512,
@@ -548,7 +554,7 @@ pub async fn reconcile_trino(
548554

549555
// This secret is created even if spooling is not configured.
550556
// Trino currently requires the secret to be exactly 256 bits long.
551-
create_random_secret(
557+
create_random_secret_if_not_exists(
552558
&shared_spooling_secret_name(trino),
553559
ENV_SPOOLING_SECRET,
554560
32,
@@ -1525,44 +1531,93 @@ fn build_recommended_labels<'a>(
15251531
}
15261532
}
15271533

1528-
async fn create_random_secret(
1534+
/// This function creates a random Secret if it doesn't already exist.
1535+
///
1536+
/// As this function generates random Secret contents, if the Secret already exists, it will *not*
1537+
/// be patched, as otherwise we would generate new Secret contents on every reconcile. Which would
1538+
/// in turn cause Pods restarts, which would cause reconciles ;)
1539+
///
1540+
/// However, there is one special handling needed:
1541+
///
1542+
/// We can't mark Secrets as immutable, as this caused problems, see https://github.com/stackabletech/issues/issues/843.
1543+
/// As Secrets have been created as immutable up to SDP release 26.3.0, we need to delete the, to be
1544+
/// able to re-create them as mutable. This function detects old (immutable) Secrets and re-creates
1545+
/// them as mutable. The contents of the Secret will be kept to prevent unnecessary Secret content
1546+
/// changes.
1547+
async fn create_random_secret_if_not_exists(
15291548
secret_name: &str,
15301549
secret_key: &str,
15311550
secret_byte_size: usize,
15321551
trino: &v1alpha1::TrinoCluster,
15331552
client: &Client,
15341553
) -> Result<()> {
1535-
let mut internal_secret = BTreeMap::new();
1536-
internal_secret.insert(secret_key.to_string(), get_random_base64(secret_byte_size));
1554+
let secret_namespace = trino.namespace().context(ObjectHasNoNamespaceSnafu)?;
1555+
let existing_secret = client
1556+
.get_opt::<Secret>(secret_name, &secret_namespace)
1557+
.await
1558+
.context(FailedToRetrieveInternalSecretSnafu)?;
15371559

1538-
let secret = Secret {
1539-
immutable: Some(true),
1540-
metadata: ObjectMetaBuilder::new()
1541-
.name(secret_name)
1542-
.namespace_opt(trino.namespace())
1543-
.ownerreference_from_resource(trino, None, Some(true))
1544-
.context(ObjectMissingMetadataForOwnerRefSnafu)?
1545-
.build(),
1546-
string_data: Some(internal_secret),
1547-
..Secret::default()
1548-
};
1560+
match existing_secret {
1561+
Some(
1562+
existing_secret @ Secret {
1563+
immutable: Some(true),
1564+
..
1565+
},
1566+
) => {
1567+
tracing::info!(
1568+
k8s.secret.name = secret_name,
1569+
k8s.secret.namespace = secret_namespace,
1570+
"Old (immutable) Secret detected, re-creating it to be able to make it mutable. The contents will stay the same."
1571+
);
1572+
Api::<Secret>::namespaced(client.as_kube_client(), &secret_namespace)
1573+
.delete(secret_name, &DeleteParams::default())
1574+
.await
1575+
.context(DeleteInternalSecretSnafu)?;
15491576

1550-
if client
1551-
.get_opt::<Secret>(
1552-
&secret.name_any(),
1553-
secret
1554-
.namespace()
1555-
.as_deref()
1556-
.context(ObjectHasNoNamespaceSnafu)?,
1557-
)
1558-
.await
1559-
.context(FailedToRetrieveInternalSecretSnafu)?
1560-
.is_none()
1561-
{
1562-
client
1563-
.apply_patch(CONTROLLER_NAME, &secret, &secret)
1564-
.await
1565-
.context(ApplyInternalSecretSnafu)?;
1577+
let mut mutable_secret = existing_secret;
1578+
mutable_secret.immutable = Some(false);
1579+
// Prevent "ApiError: resourceVersion should not be set on objects to be created"
1580+
mutable_secret.metadata.resource_version = None;
1581+
1582+
client
1583+
.create(&mutable_secret)
1584+
.await
1585+
.context(ApplyInternalSecretSnafu)?;
1586+
1587+
// Note: restart-controller will restart all Trino (coordinator and worker) Pods, but
1588+
// this should be fine.
1589+
}
1590+
Some(_) => {
1591+
tracing::debug!(
1592+
k8s.secret.name = secret_name,
1593+
k8s.secret.namespace = secret_namespace,
1594+
"Existing (mutable) Secret detected, nothing to do"
1595+
);
1596+
}
1597+
None => {
1598+
tracing::info!(
1599+
k8s.secret.name = secret_name,
1600+
k8s.secret.namespace = secret_namespace,
1601+
"Random Secret missing, creating it"
1602+
);
1603+
let secret = Secret {
1604+
metadata: ObjectMetaBuilder::new()
1605+
.name(secret_name)
1606+
.namespace_opt(trino.namespace())
1607+
.ownerreference_from_resource(trino, None, Some(true))
1608+
.context(ObjectMissingMetadataForOwnerRefSnafu)?
1609+
.build(),
1610+
string_data: Some(BTreeMap::from([(
1611+
secret_key.to_string(),
1612+
get_random_base64(secret_byte_size),
1613+
)])),
1614+
..Secret::default()
1615+
};
1616+
client
1617+
.create(&secret)
1618+
.await
1619+
.context(ApplyInternalSecretSnafu)?;
1620+
}
15661621
}
15671622

15681623
Ok(())

0 commit comments

Comments
 (0)