@@ -34,6 +34,7 @@ import (
34
34
"sigs.k8s.io/yaml"
35
35
36
36
v2 "github.com/fluxcd/helm-controller/api/v2beta1"
37
+ sourcev1 "github.com/fluxcd/source-controller/api/v1beta2"
37
38
)
38
39
39
40
func TestHelmReleaseReconciler_composeValues (t * testing.T ) {
@@ -446,6 +447,208 @@ func TestValuesReferenceValidation(t *testing.T) {
446
447
}
447
448
}
448
449
450
+ func FuzzHelmReleaseReconciler_composeValues (f * testing.F ) {
451
+ scheme := testScheme ()
452
+
453
+ tests := []struct {
454
+ targetPath string
455
+ valuesKey string
456
+ hrValues string
457
+ createObject bool
458
+ secretData []byte
459
+ configData string
460
+ }{
461
+ {
462
+ targetPath : "flat" ,
463
+ valuesKey : "custom-values.yaml" ,
464
+ secretData : []byte (`flat:
465
+ nested: value
466
+ nested: value
467
+ ` ),
468
+ configData : `flat: value
469
+ nested:
470
+ configuration: value
471
+ ` ,
472
+ hrValues : `
473
+ other: values
474
+ ` ,
475
+ createObject : true ,
476
+ },
477
+ {
478
+ targetPath : "'flat'" ,
479
+ valuesKey : "custom-values.yaml" ,
480
+ secretData : []byte (`flat:
481
+ nested: value
482
+ nested: value
483
+ ` ),
484
+ configData : `flat: value
485
+ nested:
486
+ configuration: value
487
+ ` ,
488
+ hrValues : `
489
+ other: values
490
+ ` ,
491
+ createObject : true ,
492
+ },
493
+ {
494
+ targetPath : "flat[0]" ,
495
+ secretData : []byte (`` ),
496
+ configData : `flat: value` ,
497
+ hrValues : `
498
+ other: values
499
+ ` ,
500
+ createObject : true ,
501
+ },
502
+ {
503
+ secretData : []byte (`flat:
504
+ nested: value
505
+ nested: value
506
+ ` ),
507
+ configData : `flat: value
508
+ nested:
509
+ configuration: value
510
+ ` ,
511
+ hrValues : `
512
+ other: values
513
+ ` ,
514
+ createObject : true ,
515
+ },
516
+ {
517
+ targetPath : "some-value" ,
518
+ hrValues : `
519
+ other: values
520
+ ` ,
521
+ createObject : false ,
522
+ },
523
+ }
524
+
525
+ for _ , tt := range tests {
526
+ f .Add (tt .targetPath , tt .valuesKey , tt .hrValues , tt .createObject , tt .secretData , tt .configData )
527
+ }
528
+
529
+ f .Fuzz (func (t * testing.T ,
530
+ targetPath , valuesKey , hrValues string , createObject bool , secretData []byte , configData string ) {
531
+
532
+ // objectName represents a core Kubernetes name (Secret/ConfigMap) which is validated
533
+ // upstream, and also validated by us in the OpenAPI-based validation set in
534
+ // v2.ValuesReference. Therefore a static value here suffices, and instead we just
535
+ // play with the objects presence/absence.
536
+ objectName := "values"
537
+ resources := []runtime.Object {}
538
+
539
+ if createObject {
540
+ resources = append (resources ,
541
+ valuesConfigMap (objectName , map [string ]string {valuesKey : configData }),
542
+ valuesSecret (objectName , map [string ][]byte {valuesKey : secretData }),
543
+ )
544
+ }
545
+
546
+ references := []v2.ValuesReference {
547
+ {
548
+ Kind : "ConfigMap" ,
549
+ Name : objectName ,
550
+ ValuesKey : valuesKey ,
551
+ TargetPath : targetPath ,
552
+ },
553
+ {
554
+ Kind : "Secret" ,
555
+ Name : objectName ,
556
+ ValuesKey : valuesKey ,
557
+ TargetPath : targetPath ,
558
+ },
559
+ }
560
+
561
+ c := fake .NewFakeClientWithScheme (scheme , resources ... )
562
+ r := & HelmReleaseReconciler {Client : c }
563
+ var values * apiextensionsv1.JSON
564
+ if hrValues != "" {
565
+ v , _ := yaml .YAMLToJSON ([]byte (hrValues ))
566
+ values = & apiextensionsv1.JSON {Raw : v }
567
+ }
568
+
569
+ hr := v2.HelmRelease {
570
+ Spec : v2.HelmReleaseSpec {
571
+ ValuesFrom : references ,
572
+ Values : values ,
573
+ },
574
+ }
575
+
576
+ // OpenAPI-based validation on schema is not verified here.
577
+ // Therefore some false positives may be arise, as the apiserver
578
+ // would not allow such values to make their way into the control plane.
579
+ //
580
+ // Testenv could be used so the fuzzing covers the entire E2E.
581
+ // The downsize being the resource and time cost per test would be a lot higher.
582
+ //
583
+ // Another approach could be to add validation to reject invalid inputs before
584
+ // the r.composeValues call.
585
+ _ , _ = r .composeValues (logr .NewContext (context .TODO (), logr .Discard ()), hr )
586
+ })
587
+ }
588
+
589
+ func FuzzHelmReleaseReconciler_reconcile (f * testing.F ) {
590
+ scheme := testScheme ()
591
+ tests := []struct {
592
+ valuesKey string
593
+ hrValues string
594
+ secretData []byte
595
+ configData string
596
+ }{
597
+ {
598
+ valuesKey : "custom-values.yaml" ,
599
+ secretData : []byte (`flat:
600
+ nested: value
601
+ nested: value
602
+ ` ),
603
+ configData : `flat: value
604
+ nested:
605
+ configuration: value
606
+ ` ,
607
+ hrValues : `
608
+ other: values
609
+ ` ,
610
+ },
611
+ }
612
+
613
+ for _ , tt := range tests {
614
+ f .Add (tt .valuesKey , tt .hrValues , tt .secretData , tt .configData )
615
+ }
616
+
617
+ f .Fuzz (func (t * testing.T ,
618
+ valuesKey , hrValues string , secretData []byte , configData string ) {
619
+
620
+ var values * apiextensionsv1.JSON
621
+ if hrValues != "" {
622
+ v , _ := yaml .YAMLToJSON ([]byte (hrValues ))
623
+ values = & apiextensionsv1.JSON {Raw : v }
624
+ }
625
+
626
+ hr := v2.HelmRelease {
627
+ Spec : v2.HelmReleaseSpec {
628
+ Values : values ,
629
+ },
630
+ }
631
+
632
+ hc := sourcev1.HelmChart {}
633
+ hc .ObjectMeta .Name = hr .GetHelmChartName ()
634
+ hc .ObjectMeta .Namespace = hr .Spec .Chart .GetNamespace (hr .Namespace )
635
+
636
+ resources := []runtime.Object {
637
+ valuesConfigMap ("values" , map [string ]string {valuesKey : configData }),
638
+ valuesSecret ("values" , map [string ][]byte {valuesKey : secretData }),
639
+ & hc ,
640
+ }
641
+
642
+ c := fake .NewFakeClientWithScheme (scheme , resources ... )
643
+ r := & HelmReleaseReconciler {
644
+ Client : c ,
645
+ EventRecorder : & DummyRecorder {},
646
+ }
647
+
648
+ _ , _ , _ = r .reconcile (logr .NewContext (context .TODO (), logr .Discard ()), hr )
649
+ })
650
+ }
651
+
449
652
func valuesSecret (name string , data map [string ][]byte ) * corev1.Secret {
450
653
return & corev1.Secret {
451
654
ObjectMeta : metav1.ObjectMeta {Name : name },
@@ -459,3 +662,24 @@ func valuesConfigMap(name string, data map[string]string) *corev1.ConfigMap {
459
662
Data : data ,
460
663
}
461
664
}
665
+
666
+ func testScheme () * runtime.Scheme {
667
+ scheme := runtime .NewScheme ()
668
+ _ = corev1 .AddToScheme (scheme )
669
+ _ = v2 .AddToScheme (scheme )
670
+ _ = sourcev1 .AddToScheme (scheme )
671
+ return scheme
672
+ }
673
+
674
+ // DummyRecorder serves as a dummy for kuberecorder.EventRecorder.
675
+ type DummyRecorder struct {}
676
+
677
+ func (r * DummyRecorder ) Event (object runtime.Object , eventtype , reason , message string ) {
678
+ }
679
+
680
+ func (r * DummyRecorder ) Eventf (object runtime.Object , eventtype , reason , messageFmt string , args ... interface {}) {
681
+ }
682
+
683
+ func (r * DummyRecorder ) AnnotatedEventf (object runtime.Object , annotations map [string ]string ,
684
+ eventtype , reason string , messageFmt string , args ... interface {}) {
685
+ }
0 commit comments