@@ -2,15 +2,12 @@ package e2e
22
33import (
44 "fmt"
5- "regexp"
65 "strings"
76 "testing"
87 "time"
98
109 "github.com/coreos/go-semver/semver"
11- "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil"
1210 "github.com/stretchr/testify/require"
13- appsv1 "k8s.io/api/apps/v1"
1411 authorizationv1 "k8s.io/api/authorization/v1"
1512 corev1 "k8s.io/api/core/v1"
1613 rbacv1 "k8s.io/api/rbac/v1"
@@ -21,7 +18,8 @@ import (
2118 "k8s.io/apimachinery/pkg/util/wait"
2219 "k8s.io/client-go/informers"
2320 "k8s.io/client-go/tools/cache"
24- "k8s.io/kubernetes/pkg/apis/rbac"
21+
22+ "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil"
2523
2624 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1"
2725 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
@@ -30,59 +28,6 @@ import (
3028 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
3129)
3230
33- func DeploymentComplete (deployment * appsv1.Deployment , newStatus * appsv1.DeploymentStatus ) bool {
34- return newStatus .UpdatedReplicas == * (deployment .Spec .Replicas ) &&
35- newStatus .Replicas == * (deployment .Spec .Replicas ) &&
36- newStatus .AvailableReplicas == * (deployment .Spec .Replicas ) &&
37- newStatus .ObservedGeneration >= deployment .Generation
38- }
39-
40- // Currently this function only modifies the watchedNamespace in the container command
41- func patchOlmDeployment (t * testing.T , c operatorclient.ClientInterface , newNamespace string ) (cleanupFunc func () error ) {
42- runningDeploy , err := c .GetDeployment (operatorNamespace , "olm-operator" )
43- require .NoError (t , err )
44-
45- oldCommand := runningDeploy .Spec .Template .Spec .Containers [0 ].Command
46- re , err := regexp .Compile (`-watchedNamespaces\W(\S+)` )
47- require .NoError (t , err )
48- newCommand := re .ReplaceAllString (strings .Join (oldCommand , " " ), "$0" + "," + newNamespace )
49- t .Logf ("original=%#v newCommand=%#v" , oldCommand , newCommand )
50- finalNewCommand := strings .Split (newCommand , " " )
51- runningDeploy .Spec .Template .Spec .Containers [0 ].Command = make ([]string , len (finalNewCommand ))
52- copy (runningDeploy .Spec .Template .Spec .Containers [0 ].Command , finalNewCommand )
53-
54- olmDeployment , updated , err := c .UpdateDeployment (runningDeploy )
55- if err != nil || updated == false {
56- t .Fatalf ("Deployment update failed: (updated %v) %v\n " , updated , err )
57- }
58- require .NoError (t , err )
59-
60- err = wait .Poll (pollInterval , pollDuration , func () (bool , error ) {
61- t .Log ("Polling for OLM deployment update..." )
62- fetchedDeployment , err := c .GetDeployment (olmDeployment .Namespace , olmDeployment .Name )
63- if err != nil {
64- return false , err
65- }
66- if DeploymentComplete (olmDeployment , & fetchedDeployment .Status ) {
67- return true , nil
68- }
69- return false , nil
70- })
71- require .NoError (t , err )
72-
73- return func () error {
74- olmDeployment .Spec .Template .Spec .Containers [0 ].Command = oldCommand
75- _ , updated , err := c .UpdateDeployment (olmDeployment )
76- if err != nil || updated == false {
77- t .Fatalf ("Deployment update failed: (updated %v) %v\n " , updated , err )
78- }
79- if err != nil {
80- return err
81- }
82- return nil
83- }
84- }
85-
8631func checkOperatorGroupAnnotations (obj metav1.Object , op * v1.OperatorGroup , checkTargetNamespaces bool , targetNamespaces string ) error {
8732 if checkTargetNamespaces {
8833 if annotation , ok := obj .GetAnnotations ()[v1 .OperatorGroupTargetsAnnotationKey ]; ! ok || annotation != targetNamespaces {
@@ -221,7 +166,7 @@ func TestOperatorGroup(t *testing.T) {
221166 ServiceAccountName : serviceAccountName ,
222167 Rules : []rbacv1.PolicyRule {
223168 {
224- Verbs : []string {rbac .VerbAll },
169+ Verbs : []string {rbacv1 .VerbAll },
225170 APIGroups : []string {mainCRD .Spec .Group },
226171 Resources : []string {mainCRDPlural },
227172 },
@@ -447,14 +392,130 @@ func TestOperatorGroup(t *testing.T) {
447392 require .NoError (t , err )
448393}
449394
395+ func createProjectAdmin (t * testing.T , c operatorclient.ClientInterface , namespace string ) (string , cleanupFunc ) {
396+ sa , err := c .CreateServiceAccount (& corev1.ServiceAccount {
397+ ObjectMeta : metav1.ObjectMeta {
398+ Namespace : namespace ,
399+ Name : genName ("padmin-" ),
400+ },
401+ })
402+ require .NoError (t , err )
403+
404+ rb , err := c .CreateRoleBinding (& rbacv1.RoleBinding {
405+ ObjectMeta : metav1.ObjectMeta {
406+ Name : genName ("padmin-" ),
407+ Namespace : namespace ,
408+ },
409+ Subjects : []rbacv1.Subject {
410+ {
411+ Kind : "ServiceAccount" ,
412+ Name : sa .GetName (),
413+ Namespace : sa .GetNamespace (),
414+ },
415+ },
416+ RoleRef : rbacv1.RoleRef {
417+ APIGroup : "rbac.authorization.k8s.io" ,
418+ Kind : "ClusterRole" ,
419+ Name : "admin" ,
420+ },
421+ })
422+ require .NoError (t , err )
423+ // kubectl -n a8v4sw auth can-i create alp999.cluster.com --as system:serviceaccount:a8v4sw:padmin-xqdfz
424+ return "system:serviceaccount:" + namespace + ":" + sa .GetName (), func () {
425+ _ = c .DeleteServiceAccount (sa .GetNamespace (), sa .GetName (), metav1 .NewDeleteOptions (0 ))
426+ _ = c .DeleteRoleBinding (rb .GetNamespace (), rb .GetName (), metav1 .NewDeleteOptions (0 ))
427+ }
428+ }
429+
430+ func TestOperatorGroupRoleAggregation (t * testing.T ) {
431+ // Generate namespaceA
432+ // Generate operatorGroupA - OwnNamespace
433+ // Generate csvA in namespaceA with all installmodes supported
434+ // Create crd so csv succeeds
435+ // Ensure clusterroles created and aggregated for access provided APIs
436+
437+ defer cleaner .NotifyTestComplete (t , true )
438+
439+ // Generate namespaceA
440+ nsA := genName ("a" )
441+ c := newKubeClient (t )
442+ for _ , ns := range []string {nsA } {
443+ namespace := & corev1.Namespace {
444+ ObjectMeta : metav1.ObjectMeta {
445+ Name : ns ,
446+ },
447+ }
448+ _ , err := c .KubernetesInterface ().CoreV1 ().Namespaces ().Create (namespace )
449+ require .NoError (t , err )
450+ defer func (name string ) {
451+ require .NoError (t , c .KubernetesInterface ().CoreV1 ().Namespaces ().Delete (name , & metav1.DeleteOptions {}))
452+ }(ns )
453+ }
454+
455+ // Generate operatorGroupA - OwnNamespace
456+ crc := newCRClient (t )
457+ groupA := newOperatorGroup (nsA , genName ("a" ), nil , nil , []string {nsA }, false )
458+ _ , err := crc .OperatorsV1 ().OperatorGroups (nsA ).Create (groupA )
459+ require .NoError (t , err )
460+ defer func () {
461+ require .NoError (t , crc .OperatorsV1 ().OperatorGroups (nsA ).Delete (groupA .GetName (), & metav1.DeleteOptions {}))
462+ }()
463+
464+ // Generate csvA in namespaceA with all installmodes supported
465+ crd := newCRD (genName ("a" ))
466+ namedStrategy := newNginxInstallStrategy (genName ("dep-" ), nil , nil )
467+ csvA := newCSV ("nginx-a" , nsA , "" , * semver .New ("0.1.0" ), []apiextensions.CustomResourceDefinition {crd }, nil , namedStrategy )
468+ _ , err = crc .OperatorsV1alpha1 ().ClusterServiceVersions (nsA ).Create (& csvA )
469+ require .NoError (t , err )
470+ defer func () {
471+ require .NoError (t , crc .OperatorsV1alpha1 ().ClusterServiceVersions (nsA ).Delete (csvA .GetName (), & metav1.DeleteOptions {}))
472+ }()
473+
474+ // Create crd so csv succeeds
475+ cleanupCRD , err := createCRD (c , crd )
476+ require .NoError (t , err )
477+ defer cleanupCRD ()
478+
479+ _ , err = fetchCSV (t , crc , csvA .GetName (), nsA , csvSucceededChecker )
480+ require .NoError (t , err )
481+
482+ // Ensure clusterroles created and aggregated for access provided APIs
483+ padmin , cleanupPadmin := createProjectAdmin (t , c , nsA )
484+ defer cleanupPadmin ()
485+
486+ err = wait .Poll (pollInterval , pollDuration , func () (bool , error ) {
487+ res , err := c .KubernetesInterface ().AuthorizationV1 ().SubjectAccessReviews ().Create (& authorizationv1.SubjectAccessReview {
488+ Spec : authorizationv1.SubjectAccessReviewSpec {
489+ User : padmin ,
490+ ResourceAttributes : & authorizationv1.ResourceAttributes {
491+ Namespace : nsA ,
492+ Group : crd .Spec .Group ,
493+ Version : crd .Spec .Version ,
494+ Resource : crd .Spec .Names .Plural ,
495+ Verb : "create" ,
496+ },
497+ },
498+ })
499+ if err != nil {
500+ return false , err
501+ }
502+ if res == nil {
503+ return false , nil
504+ }
505+ t .Log ("checking padmin for permission" )
506+ return res .Status .Allowed , nil
507+ })
508+ require .NoError (t , err )
509+ }
510+
450511func TestOperatorGroupInstallModeSupport (t * testing.T ) {
451512 // Generate namespaceA
452513 // Generate namespaceB
453514 // Create operatorGroupA in namespaceA that selects namespaceA
454515 // Generate csvA with an unfulfilled required CRD and no supported InstallModes in namespaceA
455516 // Ensure csvA transitions to Failed with reason "UnsupportedOperatorGroup"
456517 // Update csvA to have OwnNamespace supported=true
457- // Ensure csvA transitions to Pending
518+ // Ensure csvA transitions to Succeeded
458519 // Update operatorGroupA's target namespaces to select namespaceB
459520 // Ensure csvA transitions to Failed with reason "UnsupportedOperatorGroup"
460521 // Update csvA to have SingleNamespace supported=true
@@ -555,8 +616,13 @@ func TestOperatorGroupInstallModeSupport(t *testing.T) {
555616 _ , err = crc .OperatorsV1alpha1 ().ClusterServiceVersions (nsA ).Update (csvA )
556617 require .NoError (t , err )
557618
558- // Ensure csvA transitions to Pending
559- csvA , err = fetchCSV (t , crc , csvA .GetName (), nsA , csvPendingChecker )
619+ // Create crd so csv succeeds
620+ cleanupCRD , err := createCRD (c , crd )
621+ require .NoError (t , err )
622+ defer cleanupCRD ()
623+
624+ // Ensure csvA transitions to Succeeded
625+ csvA , err = fetchCSV (t , crc , csvA .GetName (), nsA , csvSucceededChecker )
560626 require .NoError (t , err )
561627
562628 // Update operatorGroupA's target namespaces to select namespaceB
@@ -592,8 +658,8 @@ func TestOperatorGroupInstallModeSupport(t *testing.T) {
592658 _ , err = crc .OperatorsV1alpha1 ().ClusterServiceVersions (nsA ).Update (csvA )
593659 require .NoError (t , err )
594660
595- // Ensure csvA transitions to Pending
596- csvA , err = fetchCSV (t , crc , csvA .GetName (), nsA , csvPendingChecker )
661+ // Ensure csvA transitions to Succeeded
662+ csvA , err = fetchCSV (t , crc , csvA .GetName (), nsA , csvSucceededChecker )
597663 require .NoError (t , err )
598664
599665 // Update operatorGroupA's target namespaces to select namespaceA and namespaceB
@@ -629,8 +695,8 @@ func TestOperatorGroupInstallModeSupport(t *testing.T) {
629695 _ , err = crc .OperatorsV1alpha1 ().ClusterServiceVersions (nsA ).Update (csvA )
630696 require .NoError (t , err )
631697
632- // Ensure csvA transitions to Pending
633- csvA , err = fetchCSV (t , crc , csvA .GetName (), nsA , csvPendingChecker )
698+ // Ensure csvA transitions to Succeeded
699+ csvA , err = fetchCSV (t , crc , csvA .GetName (), nsA , csvSucceededChecker )
634700 require .NoError (t , err )
635701
636702 // Update operatorGroupA's target namespaces to select all namespaces
@@ -667,7 +733,7 @@ func TestOperatorGroupInstallModeSupport(t *testing.T) {
667733 require .NoError (t , err )
668734
669735 // Ensure csvA transitions to Pending
670- csvA , err = fetchCSV (t , crc , csvA .GetName (), nsA , csvPendingChecker )
736+ csvA , err = fetchCSV (t , crc , csvA .GetName (), nsA , csvSucceededChecker )
671737 require .NoError (t , err )
672738}
673739
@@ -689,6 +755,7 @@ func TestOperatorGroupIntersection(t *testing.T) {
689755 // Ensure csvD in namespaceD is still successful
690756 // Generate csvA in namespaceA that owns crdA
691757 // Wait for csvA to be successful
758+ // Ensure clusterroles created and aggregated for accessing provided APIs
692759 // Wait for operatorGroupA to have providedAPI annotation with crdA's Kind.version.group in its providedAPIs annotation
693760 // Wait for csvA to have a CSV with copied status in namespace C
694761 // Generate operatorGroupB in namespaceB that selects namespace C
@@ -851,6 +918,34 @@ func TestOperatorGroupIntersection(t *testing.T) {
851918 _ , err = awaitCSV (t , crc , nsA , csvA .GetName (), csvSucceededChecker )
852919 require .NoError (t , err )
853920
921+ // Ensure clusterroles created and aggregated for access provided APIs
922+ padmin , cleanupPadmin := createProjectAdmin (t , c , nsA )
923+ defer cleanupPadmin ()
924+
925+ err = wait .Poll (pollInterval , pollDuration , func () (bool , error ) {
926+ res , err := c .KubernetesInterface ().AuthorizationV1 ().SubjectAccessReviews ().Create (& authorizationv1.SubjectAccessReview {
927+ Spec : authorizationv1.SubjectAccessReviewSpec {
928+ User : padmin ,
929+ ResourceAttributes : & authorizationv1.ResourceAttributes {
930+ Namespace : nsA ,
931+ Group : crdA .Spec .Group ,
932+ Version : crdA .Spec .Version ,
933+ Resource : crdA .Spec .Names .Plural ,
934+ Verb : "create" ,
935+ },
936+ },
937+ })
938+ if err != nil {
939+ return false , err
940+ }
941+ if res == nil {
942+ return false , nil
943+ }
944+ t .Log ("checking padmin for permission" )
945+ return res .Status .Allowed , nil
946+ })
947+ require .NoError (t , err )
948+
854949 // Await annotation on groupA
855950 q = func () (metav1.ObjectMeta , error ) {
856951 g , err := crc .OperatorsV1 ().OperatorGroups (nsA ).Get (groupA .GetName (), metav1.GetOptions {})
@@ -1175,7 +1270,7 @@ func TestCSVCopyWatchingAllNamespaces(t *testing.T) {
11751270 ServiceAccountName : serviceAccountName ,
11761271 Rules : []rbacv1.PolicyRule {
11771272 {
1178- Verbs : []string {rbac .VerbAll },
1273+ Verbs : []string {rbacv1 .VerbAll },
11791274 APIGroups : []string {mainCRD .Spec .Group },
11801275 Resources : []string {mainCRDPlural },
11811276 },
0 commit comments