@@ -8,7 +8,7 @@ use std::{
88#[ cfg( doc) ]
99use k8s_openapi:: api:: core:: v1:: { NodeSelector , Pod } ;
1010use k8s_openapi:: {
11- NamespaceResourceScope ,
11+ DeepMerge , NamespaceResourceScope ,
1212 api:: {
1313 apps:: v1:: {
1414 DaemonSet , DaemonSetSpec , Deployment , DeploymentSpec , StatefulSet , StatefulSetSpec ,
@@ -22,9 +22,10 @@ use k8s_openapi::{
2222 } ,
2323 apimachinery:: pkg:: apis:: meta:: v1:: { LabelSelector , LabelSelectorRequirement } ,
2424} ;
25- use kube:: { Resource , ResourceExt , core:: ErrorResponse } ;
25+ use kube:: { Resource , ResourceExt , api :: DynamicObject , core:: ErrorResponse } ;
2626use serde:: { Serialize , de:: DeserializeOwned } ;
2727use snafu:: { OptionExt , ResultExt , Snafu } ;
28+ use stackable_shared:: patchinator:: { self , apply_patches, parse_patches} ;
2829use strum:: Display ;
2930use tracing:: { debug, info, warn} ;
3031
@@ -87,6 +88,12 @@ pub enum Error {
8788 #[ snafu( source( from( crate :: client:: Error , Box :: new) ) ) ]
8889 source : Box < crate :: client:: Error > ,
8990 } ,
91+
92+ #[ snafu( display( "failed to parse user-provided object overrides" ) ) ]
93+ ParseObjectOverrides { source : patchinator:: Error } ,
94+
95+ #[ snafu( display( "failed to apply user-provided object overrides" ) ) ]
96+ ApplyObjectOverrides { source : patchinator:: Error } ,
9097}
9198
9299/// A cluster resource handled by [`ClusterResources`].
@@ -97,6 +104,7 @@ pub enum Error {
97104/// it must be added to [`ClusterResources::delete_orphaned_resources`] as well.
98105pub trait ClusterResource :
99106 Clone
107+ + DeepMerge
100108 + Debug
101109 + DeserializeOwned
102110 + Resource < DynamicType = ( ) , Scope = NamespaceResourceScope >
@@ -413,7 +421,7 @@ impl ClusterResource for Deployment {
413421/// Ok(Action::await_change())
414422/// }
415423/// ```
416- #[ derive( Debug , Eq , PartialEq ) ]
424+ #[ derive( Debug ) ]
417425pub struct ClusterResources {
418426 /// The namespace of the cluster
419427 namespace : String ,
@@ -442,6 +450,9 @@ pub struct ClusterResources {
442450 /// Strategy to manage how cluster resources are applied. Resources could be patched, merged
443451 /// or not applied at all depending on the strategy.
444452 apply_strategy : ClusterResourceApplyStrategy ,
453+
454+ /// Arbitrary Kubernetes object overrides specified by the user via the CRD.
455+ object_overrides : Vec < DynamicObject > ,
445456}
446457
447458impl ClusterResources {
@@ -470,6 +481,7 @@ impl ClusterResources {
470481 controller_name : & str ,
471482 cluster : & ObjectReference ,
472483 apply_strategy : ClusterResourceApplyStrategy ,
484+ object_overrides : Option < impl AsRef < str > > ,
473485 ) -> Result < Self > {
474486 let namespace = cluster
475487 . namespace
@@ -483,6 +495,12 @@ impl ClusterResources {
483495 . uid
484496 . clone ( )
485497 . context ( MissingObjectKeySnafu { key : "uid" } ) ?;
498+ let object_overrides = match object_overrides {
499+ Some ( object_overrides) => {
500+ parse_patches ( object_overrides) . context ( ParseObjectOverridesSnafu ) ?
501+ }
502+ None => vec ! [ ] ,
503+ } ;
486504
487505 Ok ( ClusterResources {
488506 namespace,
@@ -494,6 +512,7 @@ impl ClusterResources {
494512 manager : format_full_controller_name ( operator_name, controller_name) ,
495513 resource_ids : Default :: default ( ) ,
496514 apply_strategy,
515+ object_overrides,
497516 } )
498517 }
499518
@@ -563,7 +582,11 @@ impl ClusterResources {
563582 . unwrap_or_else ( |err| warn ! ( "{}" , err) ) ;
564583 }
565584
566- let mutated = resource. maybe_mutate ( & self . apply_strategy ) ;
585+ let mut mutated = resource. maybe_mutate ( & self . apply_strategy ) ;
586+
587+ // We apply the object overrides of the user at the very last to offer maximum flexibility.
588+ apply_patches ( & mut mutated, self . object_overrides . iter ( ) )
589+ . context ( ApplyObjectOverridesSnafu ) ?;
567590
568591 let patched_resource = self
569592 . apply_strategy
0 commit comments