@@ -410,3 +410,142 @@ func TestClusterExtensionVersionUpdate(t *testing.T) {
410410 require .Len (ct , cerList .Items , 2 )
411411 }, pollDuration , pollInterval )
412412}
413+
414+ func TestClusterExtensionEmptyWatchNamespace (t * testing.T ) {
415+ SkipIfFeatureGateDisabled (t , soNsFlag )
416+ t .Log ("Test support for empty watchNamespace treated as AllNamespaces mode" )
417+ defer utils .CollectTestArtifacts (t , artifactName , c , cfg )
418+
419+ t .Log ("By creating install namespace and necessary rbac resources" )
420+ namespace := corev1.Namespace {
421+ ObjectMeta : metav1.ObjectMeta {
422+ Name : "empty-watchnamespace-operator" ,
423+ },
424+ }
425+ require .NoError (t , c .Create (t .Context (), & namespace ))
426+ t .Cleanup (func () {
427+ require .NoError (t , c .Delete (context .Background (), & namespace ))
428+ })
429+
430+ serviceAccount := corev1.ServiceAccount {
431+ ObjectMeta : metav1.ObjectMeta {
432+ Name : "empty-watchnamespace-operator-installer" ,
433+ Namespace : namespace .GetName (),
434+ },
435+ }
436+ require .NoError (t , c .Create (t .Context (), & serviceAccount ))
437+ t .Cleanup (func () {
438+ require .NoError (t , c .Delete (context .Background (), & serviceAccount ))
439+ })
440+
441+ clusterRoleBinding := & rbacv1.ClusterRoleBinding {
442+ ObjectMeta : metav1.ObjectMeta {
443+ Name : "empty-watchnamespace-operator-installer" ,
444+ },
445+ Subjects : []rbacv1.Subject {
446+ {
447+ Kind : "ServiceAccount" ,
448+ APIGroup : corev1 .GroupName ,
449+ Name : serviceAccount .GetName (),
450+ Namespace : serviceAccount .GetNamespace (),
451+ },
452+ },
453+ RoleRef : rbacv1.RoleRef {
454+ APIGroup : rbacv1 .GroupName ,
455+ Kind : "ClusterRole" ,
456+ Name : "cluster-admin" ,
457+ },
458+ }
459+ require .NoError (t , c .Create (t .Context (), clusterRoleBinding ))
460+ t .Cleanup (func () {
461+ require .NoError (t , c .Delete (context .Background (), clusterRoleBinding ))
462+ })
463+
464+ t .Log ("By creating the test-catalog ClusterCatalog" )
465+ extensionCatalog := & ocv1.ClusterCatalog {
466+ ObjectMeta : metav1.ObjectMeta {
467+ Name : "test-catalog" ,
468+ },
469+ Spec : ocv1.ClusterCatalogSpec {
470+ Source : ocv1.CatalogSource {
471+ Type : ocv1 .SourceTypeImage ,
472+ Image : & ocv1.ImageSource {
473+ Ref : fmt .Sprintf ("%s/e2e/test-catalog:v1" , os .Getenv ("CLUSTER_REGISTRY_HOST" )),
474+ PollIntervalMinutes : ptr .To (1 ),
475+ },
476+ },
477+ },
478+ }
479+ require .NoError (t , c .Create (t .Context (), extensionCatalog ))
480+ t .Cleanup (func () {
481+ require .NoError (t , c .Delete (context .Background (), extensionCatalog ))
482+ })
483+
484+ t .Log ("By waiting for the catalog to serve its metadata" )
485+ require .EventuallyWithT (t , func (ct * assert.CollectT ) {
486+ require .NoError (ct , c .Get (context .Background (), types.NamespacedName {Name : extensionCatalog .GetName ()}, extensionCatalog ))
487+ cond := apimeta .FindStatusCondition (extensionCatalog .Status .Conditions , ocv1 .TypeServing )
488+ require .NotNil (ct , cond )
489+ require .Equal (ct , metav1 .ConditionTrue , cond .Status )
490+ require .Equal (ct , ocv1 .ReasonAvailable , cond .Reason )
491+ }, pollDuration , pollInterval )
492+
493+ t .Log ("By installing the single-namespace-operator ClusterExtension with empty watchNamespace" )
494+ clusterExtension := & ocv1.ClusterExtension {
495+ ObjectMeta : metav1.ObjectMeta {
496+ Name : "empty-watchnamespace-operator-extension" ,
497+ },
498+ Spec : ocv1.ClusterExtensionSpec {
499+ Source : ocv1.SourceConfig {
500+ SourceType : "Catalog" ,
501+ Catalog : & ocv1.CatalogFilter {
502+ PackageName : "single-namespace-operator" ,
503+ Selector : & metav1.LabelSelector {
504+ MatchLabels : map [string ]string {"olm.operatorframework.io/metadata.name" : extensionCatalog .Name },
505+ },
506+ },
507+ },
508+ Namespace : namespace .GetName (),
509+ ServiceAccount : ocv1.ServiceAccountReference {
510+ Name : serviceAccount .GetName (),
511+ },
512+ Config : & ocv1.ClusterExtensionConfig {
513+ ConfigType : ocv1 .ClusterExtensionConfigTypeInline ,
514+ Inline : & apiextensionsv1.JSON {
515+ Raw : []byte (`{"watchNamespace": ""}` ),
516+ },
517+ },
518+ },
519+ }
520+ require .NoError (t , c .Create (t .Context (), clusterExtension ))
521+ t .Cleanup (func () {
522+ require .NoError (t , c .Delete (context .Background (), clusterExtension ))
523+ })
524+
525+ t .Log ("By waiting for the extension to be installed successfully (empty watchNamespace treated as AllNamespaces)" )
526+ require .EventuallyWithT (t , func (ct * assert.CollectT ) {
527+ require .NoError (ct , c .Get (t .Context (), types.NamespacedName {Name : clusterExtension .Name }, clusterExtension ))
528+ cond := apimeta .FindStatusCondition (clusterExtension .Status .Conditions , ocv1 .TypeInstalled )
529+ require .NotNil (ct , cond )
530+ require .Equal (ct , metav1 .ConditionTrue , cond .Status )
531+ require .Equal (ct , ocv1 .ReasonSucceeded , cond .Reason )
532+ require .Contains (ct , cond .Message , "Installed bundle" )
533+ require .NotNil (ct , clusterExtension .Status .Install )
534+ require .NotEmpty (ct , clusterExtension .Status .Install .Bundle )
535+ }, pollDuration , pollInterval )
536+
537+ t .Log ("By verifying the deployment does not have watchNamespace annotation (should watch all namespaces)" )
538+ require .EventuallyWithT (t , func (ct * assert.CollectT ) {
539+ deployment := & appsv1.Deployment {}
540+ require .NoError (ct , c .Get (t .Context (), types.NamespacedName {Namespace : namespace .GetName (), Name : "single-namespace-operator" }, deployment ))
541+ // When watchNamespace is empty/nil, the olm.targetNamespaces annotation should not be set
542+ // or should be empty, indicating the operator watches all namespaces
543+ annotations := deployment .Spec .Template .GetAnnotations ()
544+ if annotations != nil {
545+ targetNs , exists := annotations ["olm.targetNamespaces" ]
546+ if exists {
547+ require .Empty (ct , targetNs , "olm.targetNamespaces should be empty for AllNamespaces mode" )
548+ }
549+ }
550+ }, pollDuration , pollInterval )
551+ }
0 commit comments