@@ -50,6 +50,33 @@ pub struct CustomResourceDefinitionMaintainer {
5050impl CustomResourceDefinitionMaintainer {
5151 /// Creates and returns a new [`CustomResourceDefinitionMaintainer`] which manages one or more
5252 /// custom resource definitions.
53+ ///
54+ /// ## Parameters
55+ ///
56+ /// This function expects four parameters:
57+ ///
58+ /// - `client`: A [`Client`] to interact with the Kubernetes API server. It continuously patches
59+ /// the CRDs when the TLS certificate is rotated.
60+ /// - `certificate_rx`: A [`mpsc::Receiver`] to receive newly generated TLS certificates. The
61+ /// certificate data sent through the channel is used to set the caBundle in the conversion
62+ /// section of the CRD.
63+ /// - `definitions`: An iterator of [`CustomResourceDefinition`]s which should be maintained
64+ /// by this maintainer. If the iterator is empty, the maintainer returns early without doing
65+ /// any work. As such, a polling mechanism which waits for all futures should be used to
66+ /// prevent premature termination of the operator.
67+ /// - `options`: Provides [`CustomResourceDefinitionMaintainerOptions`] to customize various
68+ /// parts of the maintainer. In the future, this will be converted to a builder, to enable a
69+ /// cleaner API interface.
70+ ///
71+ /// ## Return Values
72+ ///
73+ /// This function returns a 2-tuple (pair) of values:
74+ ///
75+ /// - The [`CustomResourceDefinitionMaintainer`] itself. This is used to run the maintainer.
76+ /// See [`CustomResourceDefinitionMaintainer::run`] for more details.
77+ /// - The [`oneshot::Receiver`] which will be used to send out a message once the initial
78+ /// CRD reconciliation ran. This signal can be used to trigger the deployment of custom
79+ /// resources defined by the maintained CRDs.
5380 pub fn new (
5481 client : Client ,
5582 certificate_rx : mpsc:: Receiver < Certificate > ,
@@ -70,26 +97,36 @@ impl CustomResourceDefinitionMaintainer {
7097 ( maintainer, initial_reconcile_rx)
7198 }
7299
100+ /// Runs the [`CustomResourceDefinitionMaintainer`] asynchronously.
101+ ///
102+ /// This needs to be polled in parallel with other parts of an operator, like controllers or
103+ /// webhook servers. If it is disabled, the returned future immediately resolves to
104+ /// [`std::task::Poll::Ready`] and thus doesn't consume any resources.
73105 pub async fn run ( mut self ) -> Result < ( ) , Error > {
74106 let CustomResourceDefinitionMaintainerOptions {
75107 operator_service_name,
76108 operator_namespace,
77109 field_manager,
78- https_port,
110+ webhook_https_port : https_port,
79111 disabled,
80112 } = self . options ;
81113
82- // If the maintainer is disabled, immediately return without doing any work.
83- if disabled {
114+ // If the maintainer is disabled or there are no custom resource definitions, immediately
115+ // return without doing any work.
116+ if disabled || self . definitions . is_empty ( ) {
84117 return Ok ( ( ) ) ;
85118 }
86119
120+ // This get's polled by the async runtime on a regular basis (or when woken up). Once we
121+ // receive a message containing the newly generated TLS certificate for the conversion
122+ // webhook, we need to update the caBundle in the CRD.
87123 while let Some ( certificate) = self . certificate_rx . recv ( ) . await {
88124 tracing:: info!(
89125 k8s. crd. names = ?self . definitions. iter( ) . map( CustomResourceDefinition :: name_any) . collect:: <Vec <_>>( ) ,
90126 "reconciling custom resource definitions"
91127 ) ;
92128
129+ // The caBundle needs to be provided as a base64-encoded PEM envelope.
93130 let ca_bundle = certificate
94131 . to_pem ( LineEnding :: LF )
95132 . context ( EncodeCertificateAuthorityAsPemSnafu ) ?;
@@ -109,9 +146,12 @@ impl CustomResourceDefinitionMaintainer {
109146 crd. spec . conversion = Some ( CustomResourceConversion {
110147 strategy : "Webhook" . to_owned ( ) ,
111148 webhook : Some ( WebhookConversion {
112- // conversionReviewVersions indicates what ConversionReview versions are understood/preferred by the webhook.
113- // The first version in the list understood by the API server is sent to the webhook.
114- // The webhook must respond with a ConversionReview object in the same version it received.
149+ // conversionReviewVersions indicates what ConversionReview versions are
150+ // supported by the webhook. The first version in the list understood by the
151+ // API server is sent to the webhook. The webhook must respond with a
152+ // ConversionReview object in the same version it received. We only support
153+ // the stable v1 ConversionReview to keep the implementation as simple as
154+ // possible.
115155 conversion_review_versions : vec ! [ "v1" . to_owned( ) ] ,
116156 client_config : Some ( WebhookClientConfig {
117157 service : Some ( ServiceReference {
@@ -120,12 +160,15 @@ impl CustomResourceDefinitionMaintainer {
120160 path : Some ( format ! ( "/convert/{crd_name}" ) ) ,
121161 port : Some ( https_port. into ( ) ) ,
122162 } ) ,
163+ // Here, ByteString takes care of encoding the provided content as
164+ // base64.
123165 ca_bundle : Some ( ByteString ( ca_bundle. as_bytes ( ) . to_vec ( ) ) ) ,
124166 url : None ,
125167 } ) ,
126168 } ) ,
127169 } ) ;
128170
171+ // Deploy the updated CRDs using a server-side apply.
129172 let patch = Patch :: Apply ( & crd) ;
130173 let patch_params = PatchParams :: apply ( & field_manager) ;
131174 crd_api
@@ -134,12 +177,15 @@ impl CustomResourceDefinitionMaintainer {
134177 . with_context ( |_| PatchCrdSnafu { crd_name } ) ?;
135178 }
136179
137- // Once all CRDs are reconciled, send a heartbeat for consumers to be notified that
138- // custom resources of these kinds can bow be deployed.
180+ // After the reconciliation of the CRDs, the initial reconcile heartbeat is sent out
181+ // via the oneshot channel. This channel can only be used exactly once. The sender's
182+ // send method consumes self, and as such, the sender is wrapped in an Option to be
183+ // able to call take to consume the inner value.
139184 if let Some ( initial_reconcile_tx) = self . initial_reconcile_tx . take ( ) {
140- initial_reconcile_tx
141- . send ( ( ) )
142- . ignore_context ( SendInitialReconcileHeartbeatSnafu ) ?
185+ match initial_reconcile_tx. send ( ( ) ) {
186+ Ok ( _) => { }
187+ Err ( _) => return SendInitialReconcileHeartbeatSnafu . fail ( ) ,
188+ }
143189 }
144190 }
145191
@@ -148,30 +194,20 @@ impl CustomResourceDefinitionMaintainer {
148194}
149195
150196// TODO (@Techassi): Make this a builder instead
197+ /// This contains required options to customize a [`CustomResourceDefinitionMaintainer`].
151198pub struct CustomResourceDefinitionMaintainerOptions {
199+ /// The service name used by the operator/conversion webhook.
152200 operator_service_name : String ,
201+
202+ /// The namespace the operator/conversion webhook runs in.
153203 operator_namespace : String ,
204+
205+ /// The name of the field manager used for the server-side apply.
154206 field_manager : String ,
155- https_port : u16 ,
156- disabled : bool ,
157- }
158207
159- trait ResultContextExt < T > {
160- fn ignore_context < C , E2 > ( self , context : C ) -> Result < T , E2 >
161- where
162- C : snafu:: IntoError < E2 , Source = snafu:: NoneError > ,
163- E2 : std:: error:: Error + snafu:: ErrorCompat ;
164- }
208+ /// The HTTPS port the conversion webhook listens on.
209+ webhook_https_port : u16 ,
165210
166- impl < T , E > ResultContextExt < T > for Result < T , E > {
167- fn ignore_context < C , E2 > ( self , context : C ) -> Result < T , E2 >
168- where
169- C : snafu:: IntoError < E2 , Source = snafu:: NoneError > ,
170- E2 : std:: error:: Error + snafu:: ErrorCompat ,
171- {
172- match self {
173- Ok ( v) => Ok ( v) ,
174- Err ( _) => Err ( context. into_error ( snafu:: NoneError ) ) ,
175- }
176- }
211+ /// Indicates if the maintainer should be disabled.
212+ disabled : bool ,
177213}
0 commit comments