@@ -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