@@ -50,6 +50,33 @@ pub struct CustomResourceDefinitionMaintainer {
50
50
impl CustomResourceDefinitionMaintainer {
51
51
/// Creates and returns a new [`CustomResourceDefinitionMaintainer`] which manages one or more
52
52
/// 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.
53
80
pub fn new (
54
81
client : Client ,
55
82
certificate_rx : mpsc:: Receiver < Certificate > ,
@@ -70,26 +97,36 @@ impl CustomResourceDefinitionMaintainer {
70
97
( maintainer, initial_reconcile_rx)
71
98
}
72
99
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.
73
105
pub async fn run ( mut self ) -> Result < ( ) , Error > {
74
106
let CustomResourceDefinitionMaintainerOptions {
75
107
operator_service_name,
76
108
operator_namespace,
77
109
field_manager,
78
- https_port,
110
+ webhook_https_port : https_port,
79
111
disabled,
80
112
} = self . options ;
81
113
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 ( ) {
84
117
return Ok ( ( ) ) ;
85
118
}
86
119
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.
87
123
while let Some ( certificate) = self . certificate_rx . recv ( ) . await {
88
124
tracing:: info!(
89
125
k8s. crd. names = ?self . definitions. iter( ) . map( CustomResourceDefinition :: name_any) . collect:: <Vec <_>>( ) ,
90
126
"reconciling custom resource definitions"
91
127
) ;
92
128
129
+ // The caBundle needs to be provided as a base64-encoded PEM envelope.
93
130
let ca_bundle = certificate
94
131
. to_pem ( LineEnding :: LF )
95
132
. context ( EncodeCertificateAuthorityAsPemSnafu ) ?;
@@ -109,9 +146,12 @@ impl CustomResourceDefinitionMaintainer {
109
146
crd. spec . conversion = Some ( CustomResourceConversion {
110
147
strategy : "Webhook" . to_owned ( ) ,
111
148
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.
115
155
conversion_review_versions : vec ! [ "v1" . to_owned( ) ] ,
116
156
client_config : Some ( WebhookClientConfig {
117
157
service : Some ( ServiceReference {
@@ -120,12 +160,15 @@ impl CustomResourceDefinitionMaintainer {
120
160
path : Some ( format ! ( "/convert/{crd_name}" ) ) ,
121
161
port : Some ( https_port. into ( ) ) ,
122
162
} ) ,
163
+ // Here, ByteString takes care of encoding the provided content as
164
+ // base64.
123
165
ca_bundle : Some ( ByteString ( ca_bundle. as_bytes ( ) . to_vec ( ) ) ) ,
124
166
url : None ,
125
167
} ) ,
126
168
} ) ,
127
169
} ) ;
128
170
171
+ // Deploy the updated CRDs using a server-side apply.
129
172
let patch = Patch :: Apply ( & crd) ;
130
173
let patch_params = PatchParams :: apply ( & field_manager) ;
131
174
crd_api
@@ -134,12 +177,15 @@ impl CustomResourceDefinitionMaintainer {
134
177
. with_context ( |_| PatchCrdSnafu { crd_name } ) ?;
135
178
}
136
179
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.
139
184
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
+ }
143
189
}
144
190
}
145
191
@@ -148,30 +194,20 @@ impl CustomResourceDefinitionMaintainer {
148
194
}
149
195
150
196
// TODO (@Techassi): Make this a builder instead
197
+ /// This contains required options to customize a [`CustomResourceDefinitionMaintainer`].
151
198
pub struct CustomResourceDefinitionMaintainerOptions {
199
+ /// The service name used by the operator/conversion webhook.
152
200
operator_service_name : String ,
201
+
202
+ /// The namespace the operator/conversion webhook runs in.
153
203
operator_namespace : String ,
204
+
205
+ /// The name of the field manager used for the server-side apply.
154
206
field_manager : String ,
155
- https_port : u16 ,
156
- disabled : bool ,
157
- }
158
207
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 ,
165
210
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 ,
177
213
}
0 commit comments