@@ -15,11 +15,13 @@ import (
1515 "helm.sh/helm/v3/pkg/chart"
1616 "helm.sh/helm/v3/pkg/release"
1717 "helm.sh/helm/v3/pkg/storage/driver"
18+ corev1 "k8s.io/api/core/v1"
1819 "k8s.io/apimachinery/pkg/api/equality"
1920 apimeta "k8s.io/apimachinery/pkg/api/meta"
2021 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2122 "k8s.io/apimachinery/pkg/types"
2223 "k8s.io/apimachinery/pkg/util/rand"
24+ "k8s.io/client-go/kubernetes/fake"
2325 ctrl "sigs.k8s.io/controller-runtime"
2426 "sigs.k8s.io/controller-runtime/pkg/client"
2527 crfinalizer "sigs.k8s.io/controller-runtime/pkg/finalizer"
@@ -29,7 +31,6 @@ import (
2931 "github.com/operator-framework/operator-registry/alpha/declcfg"
3032
3133 ocv1 "github.com/operator-framework/operator-controller/api/v1"
32- "github.com/operator-framework/operator-controller/internal/operator-controller/authentication"
3334 "github.com/operator-framework/operator-controller/internal/operator-controller/bundle"
3435 "github.com/operator-framework/operator-controller/internal/operator-controller/conditionsets"
3536 "github.com/operator-framework/operator-controller/internal/operator-controller/controllers"
@@ -767,60 +768,177 @@ func TestClusterExtensionBoxcutterApplierFailsDoesNotLeakDeprecationErrors(t *te
767768 require .NoError (t , cl .DeleteAllOf (ctx , & ocv1.ClusterExtension {}))
768769}
769770
770- func TestClusterExtensionServiceAccountNotFound (t * testing.T ) {
771- cl , reconciler := newClientAndReconciler (t , func (d * deps ) {
772- d .RevisionStatesGetter = & MockRevisionStatesGetter {
773- Err : & authentication.ServiceAccountNotFoundError {
774- ServiceAccountName : "missing-sa" ,
775- ServiceAccountNamespace : "default" ,
776- }}
777- })
778-
779- ctx := context .Background ()
780- extKey := types.NamespacedName {Name : fmt .Sprintf ("cluster-extension-test-%s" , rand .String (8 ))}
781-
782- t .Log ("Given a cluster extension with a missing service account" )
783- clusterExtension := & ocv1.ClusterExtension {
784- ObjectMeta : metav1.ObjectMeta {Name : extKey .Name },
785- Spec : ocv1.ClusterExtensionSpec {
786- Source : ocv1.SourceConfig {
787- SourceType : "Catalog" ,
788- Catalog : & ocv1.CatalogFilter {
789- PackageName : "test-package" ,
771+ func TestValidateClusterExtension (t * testing.T ) {
772+ tests := []struct {
773+ name string
774+ validators []controllers.ClusterExtensionValidator
775+ expectError bool
776+ errorMessageIncludes string
777+ }{
778+ {
779+ name : "all validators pass" ,
780+ validators : []controllers.ClusterExtensionValidator {
781+ // Validator that always passes
782+ func (_ context.Context , _ * ocv1.ClusterExtension ) error {
783+ return nil
790784 },
791785 },
792- Namespace : "default" ,
793- ServiceAccount : ocv1.ServiceAccountReference {
794- Name : "missing-sa" ,
786+ expectError : false ,
787+ },
788+ {
789+ name : "validator fails - sets Progressing condition" ,
790+ validators : []controllers.ClusterExtensionValidator {
791+ func (_ context.Context , _ * ocv1.ClusterExtension ) error {
792+ return errors .New ("generic validation error" )
793+ },
794+ },
795+ expectError : true ,
796+ errorMessageIncludes : "generic validation error" ,
797+ },
798+ {
799+ name : "multiple validators - collects all failures" ,
800+ validators : []controllers.ClusterExtensionValidator {
801+ // First validator fails
802+ func (_ context.Context , _ * ocv1.ClusterExtension ) error {
803+ return errors .New ("first validator failed" )
804+ },
805+ // Second validator also fails
806+ func (_ context.Context , _ * ocv1.ClusterExtension ) error {
807+ return errors .New ("second validator failed" )
808+ },
809+ // Third validator fails too
810+ func (_ context.Context , _ * ocv1.ClusterExtension ) error {
811+ return errors .New ("third validator failed" )
812+ },
813+ },
814+ expectError : true ,
815+ errorMessageIncludes : "first validator failed\n second validator failed\n third validator failed" ,
816+ },
817+ {
818+ name : "multiple validators - all pass" ,
819+ validators : []controllers.ClusterExtensionValidator {
820+ func (_ context.Context , _ * ocv1.ClusterExtension ) error {
821+ return nil
822+ },
823+ func (_ context.Context , _ * ocv1.ClusterExtension ) error {
824+ return nil
825+ },
826+ func (_ context.Context , _ * ocv1.ClusterExtension ) error {
827+ return nil
828+ },
829+ },
830+ expectError : false ,
831+ },
832+ {
833+ name : "multiple validators - some pass, some fail" ,
834+ validators : []controllers.ClusterExtensionValidator {
835+ // First validator passes
836+ func (_ context.Context , _ * ocv1.ClusterExtension ) error {
837+ return nil
838+ },
839+ // Second validator fails
840+ func (_ context.Context , _ * ocv1.ClusterExtension ) error {
841+ return errors .New ("validation error 1" )
842+ },
843+ // Third validator passes
844+ func (_ context.Context , _ * ocv1.ClusterExtension ) error {
845+ return nil
846+ },
847+ // Fourth validator fails
848+ func (_ context.Context , _ * ocv1.ClusterExtension ) error {
849+ return errors .New ("validation error 2" )
850+ },
851+ },
852+ expectError : true ,
853+ errorMessageIncludes : "validation error 1\n validation error 2" ,
854+ },
855+ {
856+ name : "service account not found" ,
857+ validators : []controllers.ClusterExtensionValidator {
858+ serviceAccountValidatorWithFakeClient (& corev1.ServiceAccount {
859+ ObjectMeta : metav1.ObjectMeta {
860+ Name : "missing-sa" ,
861+ Namespace : "test-ns" ,
862+ },
863+ }),
864+ },
865+ expectError : true ,
866+ errorMessageIncludes : `service account "test-sa" not found in namespace "test-ns"` ,
867+ },
868+ {
869+ name : "service account found" ,
870+ validators : []controllers.ClusterExtensionValidator {
871+ serviceAccountValidatorWithFakeClient (& corev1.ServiceAccount {
872+ ObjectMeta : metav1.ObjectMeta {
873+ Name : "test-sa" ,
874+ Namespace : "test-ns" ,
875+ },
876+ }),
795877 },
878+ expectError : false ,
796879 },
797880 }
798881
799- require .NoError (t , cl .Create (ctx , clusterExtension ))
882+ for _ , tt := range tests {
883+ t .Run (tt .name , func (t * testing.T ) {
884+ ctx := context .Background ()
800885
801- t .Log ("When reconciling the cluster extension" )
802- res , err := reconciler .Reconcile (ctx , ctrl.Request {NamespacedName : extKey })
886+ cl , reconciler := newClientAndReconciler (t , func (d * deps ) {
887+ d .RevisionStatesGetter = & MockRevisionStatesGetter {
888+ RevisionStates : & controllers.RevisionStates {},
889+ }
890+ d .Validators = tt .validators
891+ })
803892
804- require .Equal (t , ctrl.Result {}, res )
805- require .Error (t , err )
806- var saErr * authentication.ServiceAccountNotFoundError
807- require .ErrorAs (t , err , & saErr )
808- t .Log ("By fetching updated cluster extension after reconcile" )
809- require .NoError (t , cl .Get (ctx , extKey , clusterExtension ))
893+ extKey := types.NamespacedName {Name : fmt .Sprintf ("cluster-extension-test-%s" , rand .String (8 ))}
810894
811- t .Log ("By checking the status conditions" )
812- installedCond := apimeta .FindStatusCondition (clusterExtension .Status .Conditions , ocv1 .TypeInstalled )
813- require .NotNil (t , installedCond )
814- require .Equal (t , metav1 .ConditionUnknown , installedCond .Status )
815- require .Contains (t , installedCond .Message , fmt .Sprintf ("service account %q not found in namespace %q: unable to authenticate with the Kubernetes cluster." ,
816- "missing-sa" , "default" ))
895+ clusterExtension := & ocv1.ClusterExtension {
896+ ObjectMeta : metav1.ObjectMeta {Name : extKey .Name },
897+ Spec : ocv1.ClusterExtensionSpec {
898+ Source : ocv1.SourceConfig {
899+ SourceType : "Catalog" ,
900+ Catalog : & ocv1.CatalogFilter {
901+ PackageName : "test-package" ,
902+ },
903+ },
904+ Namespace : "test-ns" ,
905+ ServiceAccount : ocv1.ServiceAccountReference {
906+ Name : "test-sa" ,
907+ },
908+ },
909+ }
817910
818- progressingCond := apimeta .FindStatusCondition (clusterExtension .Status .Conditions , ocv1 .TypeProgressing )
819- require .NotNil (t , progressingCond )
820- require .Equal (t , metav1 .ConditionTrue , progressingCond .Status )
821- require .Equal (t , ocv1 .ReasonRetrying , progressingCond .Reason )
822- require .Contains (t , progressingCond .Message , "installation cannot proceed due to missing ServiceAccount" )
823- require .NoError (t , cl .DeleteAllOf (ctx , & ocv1.ClusterExtension {}))
911+ require .NoError (t , cl .Create (ctx , clusterExtension ))
912+
913+ t .Log ("When reconciling the cluster extension" )
914+ res , err := reconciler .Reconcile (ctx , ctrl.Request {NamespacedName : extKey })
915+ require .Equal (t , ctrl.Result {}, res )
916+ if tt .expectError {
917+ require .Error (t , err )
918+ t .Log ("By fetching updated cluster extension after reconcile" )
919+ require .NoError (t , cl .Get (ctx , extKey , clusterExtension ))
920+
921+ t .Log ("By checking the status conditions" )
922+ installedCond := apimeta .FindStatusCondition (clusterExtension .Status .Conditions , ocv1 .TypeInstalled )
923+ require .NotNil (t , installedCond )
924+ require .Equal (t , metav1 .ConditionUnknown , installedCond .Status )
925+ require .Contains (t , installedCond .Message , "installation cannot proceed due to the following validation error(s)" )
926+ require .Contains (t , installedCond .Message , tt .errorMessageIncludes )
927+
928+ progressingCond := apimeta .FindStatusCondition (clusterExtension .Status .Conditions , ocv1 .TypeProgressing )
929+ require .NotNil (t , progressingCond )
930+ require .Equal (t , metav1 .ConditionTrue , progressingCond .Status )
931+ require .Equal (t , ocv1 .ReasonRetrying , progressingCond .Reason )
932+ require .Contains (t , progressingCond .Message , "installation cannot proceed due to the following validation error(s)" )
933+ require .Contains (t , progressingCond .Message , tt .errorMessageIncludes )
934+ } else {
935+ require .NoError (t , err )
936+ require .NoError (t , cl .Get (ctx , extKey , clusterExtension ))
937+ require .Empty (t , clusterExtension .Status .Conditions )
938+ }
939+ require .NoError (t , cl .DeleteAllOf (ctx , & ocv1.ClusterExtension {}))
940+ })
941+ }
824942}
825943
826944func TestClusterExtensionApplierFailsWithBundleInstalled (t * testing.T ) {
@@ -2878,3 +2996,10 @@ func TestCheckCatalogsExist(t *testing.T) {
28782996 require .False (t , exists )
28792997 })
28802998}
2999+
3000+ func serviceAccountValidatorWithFakeClient (serviceAccount * corev1.ServiceAccount ) controllers.ClusterExtensionValidator {
3001+ if serviceAccount == nil {
3002+ return controllers .ServiceAccountValidator (fake .NewClientset ().CoreV1 ())
3003+ }
3004+ return controllers .ServiceAccountValidator (fake .NewClientset (serviceAccount ).CoreV1 ())
3005+ }
0 commit comments