Skip to content

Commit 548b300

Browse files
committed
Parse desired api version early
1 parent 073c481 commit 548b300

8 files changed

+360
-209
lines changed

crates/stackable-versioned-macros/src/codegen/container/struct/k8s.rs

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,8 @@ impl Struct {
153153
// Generate additional Kubernetes code, this is split out to reduce the complexity in this
154154
// function.
155155
let status_struct = self.generate_kubernetes_status_struct(kubernetes_arguments, is_nested);
156-
let version_enum = self.generate_kubernetes_version_enum(tokens, vis, is_nested);
156+
let version_enum =
157+
self.generate_kubernetes_version_enum(kubernetes_arguments, tokens, vis, is_nested);
157158
let convert_method = self.generate_kubernetes_conversion(versions);
158159

159160
let parse_object_error = quote! { #versioned_path::ParseObjectError };
@@ -173,7 +174,7 @@ impl Struct {
173174
#k8s_openapi_path::apiextensions_apiserver::pkg::apis::apiextensions::v1::CustomResourceDefinition,
174175
#kube_core_path::crd::MergeError>
175176
{
176-
#kube_core_path::crd::merge_crds(vec![#(#crd_fns),*], stored_apiversion.as_str())
177+
#kube_core_path::crd::merge_crds(vec![#(#crd_fns),*], stored_apiversion.as_version_str())
177178
}
178179

179180
#convert_method
@@ -234,6 +235,7 @@ impl Struct {
234235

235236
fn generate_kubernetes_version_enum(
236237
&self,
238+
kubernetes_arguments: &KubernetesArguments,
237239
tokens: &KubernetesTokens,
238240
vis: &Visibility,
239241
is_nested: bool,
@@ -244,9 +246,16 @@ impl Struct {
244246
// module (in standalone mode).
245247
let automatically_derived = is_nested.not().then(|| quote! {#[automatically_derived]});
246248

249+
let versioned_path = &*kubernetes_arguments.crates.versioned;
250+
let convert_object_error = quote! { #versioned_path::ConvertObjectError };
251+
247252
// Get the per-version items to be able to iterate over them via quote
248253
let variant_strings = &tokens.variant_strings;
249254
let variant_idents = &tokens.variant_idents;
255+
let api_versions = variant_strings
256+
.iter()
257+
.map(|version| format!("{group}/{version}", group = &kubernetes_arguments.group))
258+
.collect::<Vec<_>>();
250259

251260
quote! {
252261
#automatically_derived
@@ -257,17 +266,33 @@ impl Struct {
257266
#automatically_derived
258267
impl ::std::fmt::Display for #enum_ident {
259268
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::result::Result<(), ::std::fmt::Error> {
260-
f.write_str(self.as_str())
269+
// The version (without the Kubernetes group) is probably more human-readable
270+
f.write_str(self.as_version_str())
261271
}
262272
}
263273

264274
#automatically_derived
265275
impl #enum_ident {
266-
pub fn as_str(&self) -> &str {
276+
pub fn as_version_str(&self) -> &str {
267277
match self {
268278
#(#variant_idents => #variant_strings),*
269279
}
270280
}
281+
282+
pub fn as_api_version_str(&self) -> &str {
283+
match self {
284+
#(#variant_idents => #api_versions),*
285+
}
286+
}
287+
288+
pub fn try_from_api_version(api_version: &str) -> Result<Self, #convert_object_error> {
289+
match api_version {
290+
#(#api_versions => Ok(Self::#variant_idents)),*,
291+
_ => Err(#convert_object_error::DesiredApiVersionUnknown {
292+
unknown_desired_api_version: api_version.to_string(),
293+
}),
294+
}
295+
}
271296
}
272297
}
273298
}
@@ -331,6 +356,7 @@ impl Struct {
331356
let kubernetes_arguments = self.common.options.kubernetes_arguments.as_ref()?;
332357

333358
let struct_ident = &self.common.idents.kubernetes;
359+
let version_enum_ident = &self.common.idents.kubernetes_version;
334360

335361
let kube_client_path = &*kubernetes_arguments.crates.kube_client;
336362
let serde_json_path = &*kubernetes_arguments.crates.serde_json;
@@ -342,8 +368,7 @@ impl Struct {
342368
// Generate conversion paths and the match arms for these paths
343369
let conversion_match_arms =
344370
self.generate_kubernetes_conversion_match_arms(versions, kubernetes_arguments);
345-
let noop_match_arms =
346-
self.generate_kubernetes_noop_match_arms(versions, kubernetes_arguments);
371+
let noop_match_arms = self.generate_kubernetes_noop_match_arms(versions);
347372

348373
// TODO (@Techassi): Make this a feature, drop the option from the macro arguments
349374
// Generate tracing attributes and events if tracing is enabled
@@ -393,11 +418,8 @@ impl Struct {
393418
}
394419
};
395420

396-
// Extract the desired api version
397-
let desired_api_version = request.desired_api_version.as_str();
398-
399421
// Convert all objects into the desired version
400-
let response = match Self::convert_objects(request.objects, desired_api_version) {
422+
let response = match Self::convert_objects(request.objects, &request.desired_api_version) {
401423
::std::result::Result::Ok(converted_objects) => {
402424
#successful_conversion_response_event
403425

@@ -444,6 +466,9 @@ impl Struct {
444466
)
445467
-> ::std::result::Result<::std::vec::Vec<#serde_json_path::Value>, #convert_object_error>
446468
{
469+
let desired_api_version = #version_enum_ident::try_from_api_version(desired_api_version)?;
470+
// let desired_api_version: #version_enum_ident = desired_api_version.try_into()?;
471+
447472
let mut converted_objects = ::std::vec::Vec::with_capacity(objects.len());
448473

449474
for object in objects {
@@ -452,14 +477,11 @@ impl Struct {
452477
let current_object = Self::from_json_value(object.clone())
453478
.map_err(|source| #convert_object_error::Parse { source })?;
454479

455-
match (current_object, desired_api_version) {
480+
match (current_object, &desired_api_version) {
456481
#(#conversion_match_arms,)*
482+
// We explicitly list the remaining no-op cases, so the compiler ensures we
483+
// did not miss a conversion.
457484
#(#noop_match_arms,)*
458-
(_, unknown_desired_api_version) => return ::std::result::Result::Err(
459-
#convert_object_error::DesiredApiVersionUnknown{
460-
unknown_desired_api_version: unknown_desired_api_version.to_string()
461-
}
462-
)
463485
}
464486
}
465487

@@ -476,6 +498,7 @@ impl Struct {
476498
let group = &kubernetes_arguments.group;
477499
let variant_data_ident = &self.common.idents.kubernetes_parameter;
478500
let struct_ident = &self.common.idents.kubernetes;
501+
let version_enum_ident = &self.common.idents.kubernetes_version;
479502
let spec_ident = &self.common.idents.original;
480503

481504
let versioned_path = &*kubernetes_arguments.crates.versioned;
@@ -530,7 +553,7 @@ impl Struct {
530553
.then(|| quote! { status: #variant_data_ident.status, });
531554

532555
quote! {
533-
(Self::#current_object_version_ident(#variant_data_ident), #desired_object_api_version_string) => {
556+
(Self::#current_object_version_ident(#variant_data_ident), #version_enum_ident::#desired_object_variant_ident) => {
534557
#(#conversions)*
535558

536559
let desired_object = Self::#desired_object_variant_ident(#desired_object_module_ident::#struct_ident {
@@ -554,22 +577,19 @@ impl Struct {
554577
fn generate_kubernetes_noop_match_arms(
555578
&self,
556579
versions: &[VersionDefinition],
557-
kubernetes_arguments: &KubernetesArguments,
558580
) -> Vec<TokenStream> {
559-
let group = &kubernetes_arguments.group;
581+
let version_enum_ident = &self.common.idents.kubernetes_version;
560582

561583
versions
562584
.iter()
563585
.map(|version| {
564586
let version_ident = &version.idents.variant;
565-
let version_string = version.inner.to_string();
566-
let api_version_string = format!("{group}/{version_string}");
567587

568588
quote! {
569589
// This is the case if the desired version matches the current object api version.
570590
// NOTE (@Techassi): I'm curious if this will ever happen? In theory the K8s
571591
// apiserver should never send such a conversion review.
572-
(Self::#version_ident(_), #api_version_string) => converted_objects.push(object)
592+
(Self::#version_ident(_), #version_enum_ident::#version_ident) => converted_objects.push(object)
573593
}
574594
})
575595
.collect()

crates/stackable-versioned-macros/tests/snapshots/[email protected]

Lines changed: 36 additions & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)