Skip to content

Commit ddefc0e

Browse files
Merge pull request #794 from ecordell/starting-csv-multiple-ips
fix(olm): generate aggregated clusterroles for ownnamespace operatorgroups correctly
2 parents aeb24ae + 155e124 commit ddefc0e

File tree

6 files changed

+200
-83
lines changed

6 files changed

+200
-83
lines changed

pkg/controller/operators/olm/operator.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -621,11 +621,6 @@ func (a *Operator) syncCopyCSV(obj interface{}) (syncError error) {
621621
syncError = err
622622
}
623623

624-
// Ensure cluster roles exist for using provided apis
625-
if err := a.ensureClusterRolesForCSV(clusterServiceVersion, operatorGroup); err != nil {
626-
logger.WithError(err).Info("couldn't ensure clusterroles for provided api types")
627-
syncError = err
628-
}
629624
return
630625
}
631626

@@ -988,6 +983,13 @@ func (a *Operator) transitionCSVState(in v1alpha1.ClusterServiceVersion) (out *v
988983
return
989984
}
990985

986+
// Ensure cluster roles exist for using provided apis
987+
if err := a.ensureClusterRolesForCSV(out, operatorGroup); err != nil {
988+
logger.WithError(err).Info("couldn't ensure clusterroles for provided api types")
989+
syncError = err
990+
return
991+
}
992+
991993
case v1alpha1.CSVPhaseFailed:
992994
installer, strategy, _ := a.parseStrategiesAndUpdateStatus(out)
993995
if strategy == nil {

pkg/controller/operators/olm/operatorgroup.go

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -742,14 +742,10 @@ func (a *Operator) ensureOpGroupClusterRole(op *v1.OperatorGroup, suffix string)
742742
}
743743

744744
func (a *Operator) ensureOpGroupClusterRoles(op *v1.OperatorGroup) error {
745-
if err := a.ensureOpGroupClusterRole(op, AdminSuffix); err != nil {
746-
return err
747-
}
748-
if err := a.ensureOpGroupClusterRole(op, EditSuffix); err != nil {
749-
return err
750-
}
751-
if err := a.ensureOpGroupClusterRole(op, ViewSuffix); err != nil {
752-
return err
745+
for _, suffix := range Suffices {
746+
if err := a.ensureOpGroupClusterRole(op, suffix); err != nil {
747+
return err
748+
}
753749
}
754750
return nil
755751
}

scripts/build_local.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55

66
set -e
77

8-
if [ -z "$NO_MINIKUBE" ] || [ -x "$(command -v minikube)" ]; then
9-
ps x | grep -q [m]inikube || minikube start --kubernetes-version="v1.12.0" --extra-config=apiserver.v=4 || { echo 'Cannot start minikube.'; exit 1; }
8+
if [ -z "$NO_MINIKUBE" ]; then
9+
ps x | grep -q [m]inikube || minikube start --kubernetes-version="v1.13.0" --extra-config=apiserver.v=4 || { echo 'Cannot start minikube.'; exit 1; }
1010
eval $(minikube docker-env) || { echo 'Cannot switch to minikube docker'; exit 1; }
1111
kubectl config use-context minikube
1212
fi

test/e2e/catalog_e2e_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,6 @@ func TestConfigMapUpdateTriggersRegistryPodRollout(t *testing.T) {
213213
}
214214
}
215215
require.NoError(t, err)
216-
require.Equal(t, 1, ipCount)
217216
}
218217

219218
func TestConfigMapReplaceTriggersRegistryPodRollout(t *testing.T) {

test/e2e/operator_groups_e2e_test.go

Lines changed: 162 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,12 @@ package e2e
22

33
import (
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-
8631
func 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+
450511
func 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

Comments
 (0)