@@ -304,9 +304,7 @@ impl Struct {
304
304
) -> Option < TokenStream > {
305
305
let kubernetes_arguments = self . common . options . kubernetes_arguments . as_ref ( ) ?;
306
306
307
- let variant_data_ident = & self . common . idents . kubernetes_parameter ;
308
307
let struct_ident = & self . common . idents . kubernetes ;
309
- let spec_ident = & self . common . idents . original ;
310
308
311
309
let kube_client_path = & * kubernetes_arguments. crates . kube_client ;
312
310
let serde_json_path = & * kubernetes_arguments. crates . serde_json ;
@@ -316,93 +314,17 @@ impl Struct {
316
314
let convert_object_error = quote ! { #versioned_path:: ConvertObjectError } ;
317
315
318
316
// 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) ;
406
328
407
329
// Generate doc comments
408
330
let conversion_review_reference =
@@ -426,15 +348,31 @@ impl Struct {
426
348
-> #kube_core_path:: conversion:: ConversionReview
427
349
{
428
350
// 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
+ } ;
431
367
432
368
// Extract the desired api version
433
369
let desired_api_version = request. desired_api_version. as_str( ) ;
434
370
435
371
// Convert all objects into the desired version
436
372
let response = match Self :: convert_objects( request. objects, desired_api_version) {
437
373
:: std:: result:: Result :: Ok ( converted_objects) => {
374
+ #successful_conversion_response_event
375
+
438
376
// We construct the response from the ground up as the helper functions
439
377
// don't provide any benefit over manually doing it. Constructing a
440
378
// ConversionResponse via for_request is not possible due to a partial move
@@ -449,7 +387,23 @@ impl Struct {
449
387
converted_objects,
450
388
}
451
389
} ,
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
+ } ,
453
407
} ;
454
408
455
409
response. into_review( )
@@ -478,14 +432,142 @@ impl Struct {
478
432
// apiserver should never send such a conversion review.
479
433
_ => converted_objects. push( object) ,
480
434
}
481
-
482
-
483
435
}
484
436
485
437
:: std:: result:: Result :: Ok ( converted_objects)
486
438
}
487
439
} )
488
440
}
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 > ,
489
571
}
490
572
491
573
fn conversion_path < ' a , T > ( elements : & ' a [ T ] ) -> Vec < ( & ' a T , Cow < ' a , [ T ] > ) >
0 commit comments