@@ -304,9 +304,7 @@ impl Struct {
304304 ) -> Option < TokenStream > {
305305 let kubernetes_arguments = self . common . options . kubernetes_arguments . as_ref ( ) ?;
306306
307- let variant_data_ident = & self . common . idents . kubernetes_parameter ;
308307 let struct_ident = & self . common . idents . kubernetes ;
309- let spec_ident = & self . common . idents . original ;
310308
311309 let kube_client_path = & * kubernetes_arguments. crates . kube_client ;
312310 let serde_json_path = & * kubernetes_arguments. crates . serde_json ;
@@ -316,93 +314,17 @@ impl Struct {
316314 let convert_object_error = quote ! { #versioned_path:: ConvertObjectError } ;
317315
318316 // Generate conversion paths and the match arms for these paths
319- let conversion_chain = conversion_path ( versions) ;
320- let match_arms: Vec < _ > = conversion_chain
321- . iter ( )
322- . map ( |( start, path) | {
323- let current_object_version_ident = & start. idents . variant ;
324- let current_object_version_string = & start. inner . to_string ( ) ;
325-
326- let desired_object_version = path. last ( ) . expect ( "the path always contains at least one element" ) ;
327- let desired_object_version_string = desired_object_version. inner . to_string ( ) ;
328- let desired_object_variant_ident = & desired_object_version. idents . variant ;
329- let desired_object_module_ident = & desired_object_version. idents . module ;
330-
331- let conversions = path. iter ( ) . enumerate ( ) . map ( |( i, v) | {
332- let module_ident = & v. idents . module ;
333-
334- if i == 0 {
335- quote ! {
336- let converted: #module_ident:: #spec_ident = #variant_data_ident. spec. into( ) ;
337- }
338- } else {
339- quote ! {
340- let converted: #module_ident:: #spec_ident = converted. into( ) ;
341- }
342- }
343- } ) ;
344-
345- let kind = self . common . idents . kubernetes . to_string ( ) ;
346- let steps = path. len ( ) ;
347-
348- let convert_object_trace = kubernetes_arguments. options . enable_tracing . is_present ( ) . then ( || quote ! {
349- :: tracing:: trace!(
350- k8s. crd. conversion. api_version = #current_object_version_string,
351- k8s. crd. conversion. desired_api_version = #desired_object_version_string,
352- k8s. crd. conversion. steps = #steps,
353- k8s. crd. kind = #kind,
354- "Successfully converted object"
355- ) ;
356- } ) ;
357-
358- quote ! {
359- ( Self :: #current_object_version_ident( #variant_data_ident) , #desired_object_version_string) => {
360- #( #conversions) *
361-
362- let desired_object = Self :: #desired_object_variant_ident( #desired_object_module_ident:: #struct_ident {
363- metadata: #variant_data_ident. metadata,
364- spec: converted,
365- } ) ;
366-
367- let desired_object = desired_object. into_json_value( )
368- . map_err( |source| #convert_object_error:: Serialize { source } ) ?;
369-
370- #convert_object_trace
371-
372- converted_objects. push( desired_object) ;
373- }
374- }
375- } )
376- . collect ( ) ;
377-
378- // Generate tracing attribute of tracing is enabled
379- let ( try_convert_instrumentation, convert_objects_instrumentation) = kubernetes_arguments
380- . options
381- . enable_tracing
382- . is_present ( )
383- . then ( || {
384- // TODO (@Techassi): Make tracing path configurable. Currently not possible, needs
385- // upstream change
386- let try_convert_instrumentation = quote ! {
387- #[ :: tracing:: instrument(
388- skip_all,
389- fields(
390- k8s. crd. conversion. kind = review. types. kind,
391- k8s. crd. conversion. api_version = review. types. api_version,
392- )
393- ) ]
394- } ;
395-
396- let convert_objects_instrumentation = quote ! {
397- #[ :: tracing:: instrument(
398- skip_all,
399- err
400- ) ]
401- } ;
402-
403- ( try_convert_instrumentation, convert_objects_instrumentation)
404- } )
405- . unzip ( ) ;
317+ let match_arms =
318+ self . generate_kubernetes_conversion_match_arms ( versions, kubernetes_arguments) ;
319+
320+ // TODO (@Techassi): Make this a feature, drop the option from the macro arguments
321+ // Generate tracing attributes and events if tracing is enabled
322+ let TracingTokens {
323+ successful_conversion_response_event,
324+ convert_objects_instrumentation,
325+ invalid_conversion_review_event,
326+ try_convert_instrumentation,
327+ } = self . generate_kubernetes_conversion_tracing ( kubernetes_arguments) ;
406328
407329 // Generate doc comments
408330 let conversion_review_reference =
@@ -426,15 +348,31 @@ impl Struct {
426348 -> #kube_core_path:: conversion:: ConversionReview
427349 {
428350 // First, turn the review into a conversion request
429- // TODO (@Techassi): Handle this error and return status Invalid
430- let request = #kube_core_path:: conversion:: ConversionRequest :: from_review( review) . unwrap( ) ;
351+ let request = match #kube_core_path:: conversion:: ConversionRequest :: from_review( review) {
352+ :: std:: result:: Result :: Ok ( request) => request,
353+ :: std:: result:: Result :: Err ( err) => {
354+ #invalid_conversion_review_event
355+
356+ return #kube_core_path:: conversion:: ConversionResponse :: invalid(
357+ #kube_client_path:: Status {
358+ status: Some ( #kube_core_path:: response:: StatusSummary :: Failure ) ,
359+ message: err. to_string( ) ,
360+ reason: err. to_string( ) ,
361+ details: None ,
362+ code: 400 ,
363+ }
364+ ) . into_review( )
365+ }
366+ } ;
431367
432368 // Extract the desired api version
433369 let desired_api_version = request. desired_api_version. as_str( ) ;
434370
435371 // Convert all objects into the desired version
436372 let response = match Self :: convert_objects( request. objects, desired_api_version) {
437373 :: std:: result:: Result :: Ok ( converted_objects) => {
374+ #successful_conversion_response_event
375+
438376 // We construct the response from the ground up as the helper functions
439377 // don't provide any benefit over manually doing it. Constructing a
440378 // ConversionResponse via for_request is not possible due to a partial move
@@ -449,7 +387,23 @@ impl Struct {
449387 converted_objects,
450388 }
451389 } ,
452- :: std:: result:: Result :: Err ( _) => todo!( ) ,
390+ :: std:: result:: Result :: Err ( err) => {
391+ let code = err. http_status_code( ) ;
392+ let message = err. join_errors( ) ;
393+
394+ #kube_core_path:: conversion:: ConversionResponse {
395+ result: #kube_client_path:: Status {
396+ status: Some ( #kube_core_path:: response:: StatusSummary :: Failure ) ,
397+ message: message. clone( ) ,
398+ reason: message,
399+ details: None ,
400+ code,
401+ } ,
402+ types: request. types,
403+ uid: request. uid,
404+ converted_objects: vec![ ] ,
405+ }
406+ } ,
453407 } ;
454408
455409 response. into_review( )
@@ -478,14 +432,142 @@ impl Struct {
478432 // apiserver should never send such a conversion review.
479433 _ => converted_objects. push( object) ,
480434 }
481-
482-
483435 }
484436
485437 :: std:: result:: Result :: Ok ( converted_objects)
486438 }
487439 } )
488440 }
441+
442+ fn generate_kubernetes_conversion_match_arms (
443+ & self ,
444+ versions : & [ VersionDefinition ] ,
445+ kubernetes_arguments : & KubernetesArguments ,
446+ ) -> Vec < TokenStream > {
447+ let variant_data_ident = & self . common . idents . kubernetes_parameter ;
448+ let struct_ident = & self . common . idents . kubernetes ;
449+ let spec_ident = & self . common . idents . original ;
450+
451+ let versioned_path = & * kubernetes_arguments. crates . versioned ;
452+ let convert_object_error = quote ! { #versioned_path:: ConvertObjectError } ;
453+
454+ let conversion_chain = conversion_path ( versions) ;
455+
456+ conversion_chain
457+ . iter ( )
458+ . map ( |( start, path) | {
459+ let current_object_version_ident = & start. idents . variant ;
460+ let current_object_version_string = & start. inner . to_string ( ) ;
461+
462+ let desired_object_version = path. last ( ) . expect ( "the path always contains at least one element" ) ;
463+ let desired_object_version_string = desired_object_version. inner . to_string ( ) ;
464+ let desired_object_variant_ident = & desired_object_version. idents . variant ;
465+ let desired_object_module_ident = & desired_object_version. idents . module ;
466+
467+ let conversions = path. iter ( ) . enumerate ( ) . map ( |( i, v) | {
468+ let module_ident = & v. idents . module ;
469+
470+ if i == 0 {
471+ quote ! {
472+ let converted: #module_ident:: #spec_ident = #variant_data_ident. spec. into( ) ;
473+ }
474+ } else {
475+ quote ! {
476+ let converted: #module_ident:: #spec_ident = converted. into( ) ;
477+ }
478+ }
479+ } ) ;
480+
481+ let kind = self . common . idents . kubernetes . to_string ( ) ;
482+ let steps = path. len ( ) ;
483+
484+ let convert_object_trace = kubernetes_arguments. options . enable_tracing . is_present ( ) . then ( || quote ! {
485+ :: tracing:: trace!(
486+ k8s. crd. conversion. desired_api_version = #desired_object_version_string,
487+ k8s. crd. conversion. api_version = #current_object_version_string,
488+ k8s. crd. conversion. steps = #steps,
489+ k8s. crd. kind = #kind,
490+ "Successfully converted object"
491+ ) ;
492+ } ) ;
493+
494+ quote ! {
495+ ( Self :: #current_object_version_ident( #variant_data_ident) , #desired_object_version_string) => {
496+ #( #conversions) *
497+
498+ let desired_object = Self :: #desired_object_variant_ident( #desired_object_module_ident:: #struct_ident {
499+ metadata: #variant_data_ident. metadata,
500+ spec: converted,
501+ } ) ;
502+
503+ let desired_object = desired_object. into_json_value( )
504+ . map_err( |source| #convert_object_error:: Serialize { source } ) ?;
505+
506+ #convert_object_trace
507+
508+ converted_objects. push( desired_object) ;
509+ }
510+ }
511+ } )
512+ . collect ( )
513+ }
514+
515+ fn generate_kubernetes_conversion_tracing (
516+ & self ,
517+ kubernetes_arguments : & KubernetesArguments ,
518+ ) -> TracingTokens {
519+ if kubernetes_arguments. options . enable_tracing . is_present ( ) {
520+ // TODO (@Techassi): Make tracing path configurable. Currently not possible, needs
521+ // upstream change
522+ let kind = self . common . idents . kubernetes . to_string ( ) ;
523+
524+ let successful_conversion_response_event = Some ( quote ! {
525+ :: tracing:: debug!(
526+ k8s. crd. conversion. converted_object_count = converted_objects. len( ) ,
527+ k8s. crd. kind = #kind,
528+ "Successfully converted objects"
529+ ) ;
530+ } ) ;
531+
532+ let convert_objects_instrumentation = Some ( quote ! {
533+ #[ :: tracing:: instrument(
534+ skip_all,
535+ err
536+ ) ]
537+ } ) ;
538+
539+ let invalid_conversion_review_event = Some ( quote ! {
540+ :: tracing:: warn!( ?err, "received invalid conversion review" ) ;
541+ } ) ;
542+
543+ let try_convert_instrumentation = Some ( quote ! {
544+ #[ :: tracing:: instrument(
545+ skip_all,
546+ fields(
547+ k8s. crd. conversion. api_version = review. types. api_version,
548+ k8s. crd. kind = review. types. kind,
549+ )
550+ ) ]
551+ } ) ;
552+
553+ TracingTokens {
554+ successful_conversion_response_event,
555+ convert_objects_instrumentation,
556+ invalid_conversion_review_event,
557+ try_convert_instrumentation,
558+ }
559+ } else {
560+ TracingTokens :: default ( )
561+ }
562+ }
563+ }
564+
565+ #[ derive( Debug , Default ) ]
566+ struct TracingTokens {
567+ successful_conversion_response_event : Option < TokenStream > ,
568+ convert_objects_instrumentation : Option < TokenStream > ,
569+ invalid_conversion_review_event : Option < TokenStream > ,
570+ try_convert_instrumentation : Option < TokenStream > ,
489571}
490572
491573fn conversion_path < ' a , T > ( elements : & ' a [ T ] ) -> Vec < ( & ' a T , Cow < ' a , [ T ] > ) >
0 commit comments