github.com/operator-framework/operator-lifecycle-manager@v0.30.0/test/e2e/user_defined_sa_test.go (about)

     1  package e2e
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  
     8  	"github.com/blang/semver/v4"
     9  	. "github.com/onsi/ginkgo/v2"
    10  	. "github.com/onsi/gomega"
    11  	v1 "github.com/operator-framework/api/pkg/operators/v1"
    12  	"github.com/operator-framework/api/pkg/operators/v1alpha1"
    13  	"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned"
    14  	"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry"
    15  	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/kubernetes/pkg/apis/rbac"
    16  	"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient"
    17  	"github.com/operator-framework/operator-lifecycle-manager/test/e2e/ctx"
    18  	"github.com/stretchr/testify/assert"
    19  	"github.com/stretchr/testify/require"
    20  	corev1 "k8s.io/api/core/v1"
    21  	rbacv1 "k8s.io/api/rbac/v1"
    22  	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    23  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  )
    26  
    27  var _ = Describe("User defined service account", func() {
    28  	var (
    29  		generatedNamespace corev1.Namespace
    30  		c                  operatorclient.ClientInterface
    31  		crc                versioned.Interface
    32  	)
    33  
    34  	BeforeEach(func() {
    35  		generatedNamespace = corev1.Namespace{
    36  			ObjectMeta: metav1.ObjectMeta{
    37  				Name: genName("user-defined-sa-e2e-"),
    38  			},
    39  		}
    40  		Eventually(func() error {
    41  			return ctx.Ctx().Client().Create(context.Background(), &generatedNamespace)
    42  		}).Should(Succeed())
    43  
    44  		c = ctx.Ctx().KubeClient()
    45  		crc = ctx.Ctx().OperatorClient()
    46  	})
    47  
    48  	AfterEach(func() {
    49  		TeardownNamespace(generatedNamespace.GetName())
    50  	})
    51  
    52  	It("with no permission", func() {
    53  
    54  		By("Create a service account, but add no permission to it.")
    55  		saName := genName("scoped-sa-")
    56  		_, cleanupSA := newServiceAccount(c, generatedNamespace.GetName(), saName)
    57  		defer cleanupSA()
    58  		By("Create token secret for the serviceaccount")
    59  		_, cleanupSE := newTokenSecret(c, generatedNamespace.GetName(), saName)
    60  		defer cleanupSE()
    61  
    62  		By("Add an OperatorGroup and specify the service account.")
    63  		ogName := genName("scoped-og-")
    64  		_, cleanupOG := newOperatorGroupWithServiceAccount(crc, generatedNamespace.GetName(), ogName, saName)
    65  		defer cleanupOG()
    66  
    67  		permissions := deploymentPermissions()
    68  		catsrc, subSpec, catsrcCleanup := newCatalogSource(GinkgoT(), c, crc, "scoped", generatedNamespace.GetName(), permissions)
    69  		defer catsrcCleanup()
    70  
    71  		By("Ensure that the catalog source is resolved before we create a subscription.")
    72  		_, err := fetchCatalogSourceOnStatus(crc, catsrc.GetName(), generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
    73  		require.NoError(GinkgoT(), err)
    74  
    75  		subscriptionName := genName("scoped-sub-")
    76  		cleanupSubscription := createSubscriptionForCatalog(crc, generatedNamespace.GetName(), subscriptionName, catsrc.GetName(), subSpec.Package, subSpec.Channel, subSpec.StartingCSV, subSpec.InstallPlanApproval)
    77  		defer cleanupSubscription()
    78  
    79  		By("Wait until an install plan is created.")
    80  		subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subscriptionHasInstallPlanChecker())
    81  		require.NoError(GinkgoT(), err)
    82  		require.NotNil(GinkgoT(), subscription)
    83  
    84  		By("We expect the InstallPlan to be in status: Installing.")
    85  		ipName := subscription.Status.Install.Name
    86  		ipPhaseCheckerFunc := buildInstallPlanMessageCheckFunc(`cannot create resource`)
    87  		ipGot, err := fetchInstallPlanWithNamespace(GinkgoT(), crc, ipName, generatedNamespace.GetName(), ipPhaseCheckerFunc)
    88  		require.NoError(GinkgoT(), err)
    89  
    90  		By("Verify that all step resources are in Unknown state.")
    91  		for _, step := range ipGot.Status.Plan {
    92  			assert.Equal(GinkgoT(), v1alpha1.StepStatusUnknown, step.Status)
    93  		}
    94  	})
    95  	It("with permission", func() {
    96  
    97  		By("Create the CatalogSource")
    98  		namespace := genName("scoped-ns-")
    99  		_, cleanupNS := newNamespace(c, namespace)
   100  		defer cleanupNS()
   101  
   102  		By("Create a service account, add enough permission to it so that operator install is successful.")
   103  		saName := genName("scoped-sa")
   104  		_, cleanupSA := newServiceAccount(c, generatedNamespace.GetName(), saName)
   105  		defer cleanupSA()
   106  		By("Create token secret for the serviceaccount")
   107  		_, cleanupSE := newTokenSecret(c, generatedNamespace.GetName(), saName)
   108  		defer cleanupSE()
   109  		cleanupPerm := grantPermission(GinkgoT(), c, generatedNamespace.GetName(), saName)
   110  		defer cleanupPerm()
   111  
   112  		By("Add an OperatorGroup and specify the service account.")
   113  		ogName := genName("scoped-og-")
   114  		_, cleanupOG := newOperatorGroupWithServiceAccount(crc, generatedNamespace.GetName(), ogName, saName)
   115  		defer cleanupOG()
   116  
   117  		permissions := deploymentPermissions()
   118  		catsrc, subSpec, catsrcCleanup := newCatalogSource(GinkgoT(), c, crc, "scoped", generatedNamespace.GetName(), permissions)
   119  		defer catsrcCleanup()
   120  
   121  		By("Ensure that the catalog source is resolved before we create a subscription.")
   122  		_, err := fetchCatalogSourceOnStatus(crc, catsrc.GetName(), generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
   123  		require.NoError(GinkgoT(), err)
   124  
   125  		subscriptionName := genName("scoped-sub-")
   126  		cleanupSubscription := createSubscriptionForCatalog(crc, generatedNamespace.GetName(), subscriptionName, catsrc.GetName(), subSpec.Package, subSpec.Channel, subSpec.StartingCSV, subSpec.InstallPlanApproval)
   127  		defer cleanupSubscription()
   128  
   129  		By("Wait until an install plan is created.")
   130  		subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subscriptionHasInstallPlanChecker())
   131  		require.NoError(GinkgoT(), err)
   132  		require.NotNil(GinkgoT(), subscription)
   133  
   134  		By("We expect the InstallPlan to be in status: Complete.")
   135  		ipName := subscription.Status.Install.Name
   136  		ipPhaseCheckerFunc := buildInstallPlanPhaseCheckFunc(v1alpha1.InstallPlanPhaseComplete)
   137  		ipGot, err := fetchInstallPlanWithNamespace(GinkgoT(), crc, ipName, generatedNamespace.GetName(), ipPhaseCheckerFunc)
   138  		require.NoError(GinkgoT(), err)
   139  
   140  		conditionGot := mustHaveCondition(GinkgoT(), ipGot, v1alpha1.InstallPlanInstalled)
   141  		assert.Equal(GinkgoT(), v1alpha1.InstallPlanConditionReason(""), conditionGot.Reason)
   142  		assert.Equal(GinkgoT(), corev1.ConditionTrue, conditionGot.Status)
   143  		assert.Equal(GinkgoT(), "", conditionGot.Message)
   144  
   145  		By("Verify that all step resources are in Created state.")
   146  		for _, step := range ipGot.Status.Plan {
   147  			// TODO: switch back to commented assertion once InstallPlan status is being patched instead of updated
   148  			// assert.Equal(GinkgoT(), v1alpha1.StepStatusCreated, step.Status)
   149  			Expect(step.Status).To(Or(Equal(v1alpha1.StepStatusCreated), Equal(v1alpha1.StepStatusPresent)))
   150  		}
   151  	})
   152  	It("with retry", func() {
   153  
   154  		By("Create a service account, but add no permission to it.")
   155  		saName := genName("scoped-sa-")
   156  		_, cleanupSA := newServiceAccount(c, generatedNamespace.GetName(), saName)
   157  		defer cleanupSA()
   158  		By("Create token secret for the serviceaccount")
   159  		_, cleanupSE := newTokenSecret(c, generatedNamespace.GetName(), saName)
   160  		defer cleanupSE()
   161  
   162  		By("Add an OperatorGroup and specify the service account.")
   163  		ogName := genName("scoped-og-")
   164  		_, cleanupOG := newOperatorGroupWithServiceAccount(crc, generatedNamespace.GetName(), ogName, saName)
   165  		defer cleanupOG()
   166  
   167  		permissions := deploymentPermissions()
   168  		catsrc, subSpec, catsrcCleanup := newCatalogSource(GinkgoT(), c, crc, "scoped", generatedNamespace.GetName(), permissions)
   169  		defer catsrcCleanup()
   170  
   171  		By("Ensure that the catalog source is resolved before we create a subscription.")
   172  		_, err := fetchCatalogSourceOnStatus(crc, catsrc.GetName(), generatedNamespace.GetName(), catalogSourceRegistryPodSynced())
   173  		require.NoError(GinkgoT(), err)
   174  
   175  		subscriptionName := genName("scoped-sub-")
   176  		cleanupSubscription := createSubscriptionForCatalog(crc, generatedNamespace.GetName(), subscriptionName, catsrc.GetName(), subSpec.Package, subSpec.Channel, subSpec.StartingCSV, subSpec.InstallPlanApproval)
   177  		defer cleanupSubscription()
   178  
   179  		By("Wait until an install plan is created.")
   180  		subscription, err := fetchSubscription(crc, generatedNamespace.GetName(), subscriptionName, subscriptionHasInstallPlanChecker())
   181  		require.NoError(GinkgoT(), err)
   182  		require.NotNil(GinkgoT(), subscription)
   183  
   184  		By("We expect the InstallPlan to expose the permissions error.")
   185  		ipNameOld := subscription.Status.InstallPlanRef.Name
   186  		ipPhaseCheckerFunc := buildInstallPlanMessageCheckFunc(`cannot create resource "clusterserviceversions" in API group "operators.coreos.com" in the namespace`)
   187  		_, err = fetchInstallPlanWithNamespace(GinkgoT(), crc, ipNameOld, generatedNamespace.GetName(), ipPhaseCheckerFunc)
   188  		require.NoError(GinkgoT(), err)
   189  
   190  		By("Grant permission now and this should trigger an retry of InstallPlan.")
   191  		cleanupPerm := grantPermission(GinkgoT(), c, generatedNamespace.GetName(), saName)
   192  		defer cleanupPerm()
   193  
   194  		ipPhaseCheckerFunc = buildInstallPlanPhaseCheckFunc(v1alpha1.InstallPlanPhaseComplete)
   195  		ipGotNew, err := fetchInstallPlanWithNamespace(GinkgoT(), crc, ipNameOld, generatedNamespace.GetName(), ipPhaseCheckerFunc)
   196  		require.NoError(GinkgoT(), err)
   197  		require.Equal(GinkgoT(), v1alpha1.InstallPlanPhaseComplete, ipGotNew.Status.Phase)
   198  	})
   199  })
   200  
   201  func newNamespace(client operatorclient.ClientInterface, name string) (ns *corev1.Namespace, cleanup cleanupFunc) {
   202  	request := &corev1.Namespace{
   203  		ObjectMeta: metav1.ObjectMeta{
   204  			Name: name,
   205  		},
   206  	}
   207  
   208  	Eventually(func() (err error) {
   209  		ns, err = client.KubernetesInterface().CoreV1().Namespaces().Create(context.TODO(), request, metav1.CreateOptions{})
   210  		return
   211  	}).Should(Succeed())
   212  
   213  	cleanup = func() {
   214  		Eventually(func() error {
   215  			err := client.KubernetesInterface().CoreV1().Namespaces().Delete(context.TODO(), ns.GetName(), metav1.DeleteOptions{})
   216  			if apierrors.IsNotFound(err) {
   217  				err = nil
   218  			}
   219  
   220  			return err
   221  		}).Should(Succeed())
   222  	}
   223  
   224  	return
   225  }
   226  
   227  func newServiceAccount(client operatorclient.ClientInterface, namespace, name string) (sa *corev1.ServiceAccount, cleanup cleanupFunc) {
   228  	request := &corev1.ServiceAccount{
   229  		ObjectMeta: metav1.ObjectMeta{
   230  			Namespace: namespace,
   231  			Name:      name,
   232  		},
   233  	}
   234  
   235  	sa, err := client.KubernetesInterface().CoreV1().ServiceAccounts(namespace).Create(context.TODO(), request, metav1.CreateOptions{})
   236  	Expect(err).ToNot(HaveOccurred())
   237  	Expect(sa).ToNot(BeNil())
   238  
   239  	cleanup = func() {
   240  		if env := os.Getenv("SKIP_CLEANUP"); env != "" {
   241  			fmt.Printf("Skipping cleanup of service account %s/%s...\n", sa.GetNamespace(), sa.GetName())
   242  			return
   243  		}
   244  		err := client.KubernetesInterface().CoreV1().ServiceAccounts(sa.GetNamespace()).Delete(context.TODO(), sa.GetName(), metav1.DeleteOptions{})
   245  		Expect(err).ToNot(HaveOccurred())
   246  	}
   247  
   248  	return
   249  }
   250  
   251  func newOperatorGroupWithServiceAccount(client versioned.Interface, namespace, name, serviceAccountName string) (og *v1.OperatorGroup, cleanup cleanupFunc) {
   252  	request := &v1.OperatorGroup{
   253  		ObjectMeta: metav1.ObjectMeta{
   254  			Namespace: namespace,
   255  			Name:      name,
   256  		},
   257  		Spec: v1.OperatorGroupSpec{
   258  			TargetNamespaces: []string{
   259  				namespace,
   260  			},
   261  			ServiceAccountName: serviceAccountName,
   262  		},
   263  	}
   264  
   265  	og, err := client.OperatorsV1().OperatorGroups(namespace).Create(context.TODO(), request, metav1.CreateOptions{})
   266  	Expect(err).ToNot(HaveOccurred())
   267  	Expect(og).ToNot(BeNil())
   268  
   269  	cleanup = func() {
   270  		if env := os.Getenv("SKIP_CLEANUP"); env != "" {
   271  			fmt.Printf("Skipping cleanup of operator group %s/%s...\n", og.GetNamespace(), og.GetName())
   272  			return
   273  		}
   274  		err := client.OperatorsV1().OperatorGroups(og.GetNamespace()).Delete(context.TODO(), og.GetName(), metav1.DeleteOptions{})
   275  		Expect(err).ToNot(HaveOccurred())
   276  	}
   277  
   278  	return
   279  }
   280  
   281  func newCatalogSource(t GinkgoTInterface, kubeclient operatorclient.ClientInterface, crclient versioned.Interface, prefix, namespace string, permissions []v1alpha1.StrategyDeploymentPermissions) (catsrc *v1alpha1.CatalogSource, subscriptionSpec *v1alpha1.SubscriptionSpec, cleanup cleanupFunc) {
   282  	crdPlural := genName("ins")
   283  	crdName := crdPlural + ".cluster.com"
   284  
   285  	crd := apiextensionsv1.CustomResourceDefinition{
   286  		ObjectMeta: metav1.ObjectMeta{
   287  			Name: crdName,
   288  		},
   289  		Spec: apiextensionsv1.CustomResourceDefinitionSpec{
   290  			Group: "cluster.com",
   291  			Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
   292  				{
   293  					Name:    "v1alpha1",
   294  					Served:  true,
   295  					Storage: true,
   296  					Schema: &apiextensionsv1.CustomResourceValidation{
   297  						OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
   298  							Type:        "object",
   299  							Description: "my crd schema",
   300  						},
   301  					},
   302  				},
   303  			},
   304  			Names: apiextensionsv1.CustomResourceDefinitionNames{
   305  				Plural:   crdPlural,
   306  				Singular: crdPlural,
   307  				Kind:     crdPlural,
   308  				ListKind: "list" + crdPlural,
   309  			},
   310  			Scope: apiextensionsv1.NamespaceScoped,
   311  		},
   312  	}
   313  
   314  	prefixFunc := func(s string) string {
   315  		return fmt.Sprintf("%s-%s-", prefix, s)
   316  	}
   317  
   318  	// Create CSV
   319  	packageName := genName(prefixFunc("package"))
   320  	stableChannel := "stable"
   321  
   322  	namedStrategy := newNginxInstallStrategy(genName(prefixFunc("dep")), permissions, nil)
   323  	csvA := newCSV("nginx-a", namespace, "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{crd}, nil, &namedStrategy)
   324  	csvB := newCSV("nginx-b", namespace, "nginx-a", semver.MustParse("0.2.0"), []apiextensionsv1.CustomResourceDefinition{crd}, nil, &namedStrategy)
   325  
   326  	// Create PackageManifests
   327  	manifests := []registry.PackageManifest{
   328  		{
   329  			PackageName: packageName,
   330  			Channels: []registry.PackageChannel{
   331  				{Name: stableChannel, CurrentCSVName: csvB.GetName()},
   332  			},
   333  			DefaultChannelName: stableChannel,
   334  		},
   335  	}
   336  
   337  	catalogSourceName := genName(prefixFunc("catsrc"))
   338  	catsrc, cleanup = createInternalCatalogSource(kubeclient, crclient, catalogSourceName, namespace, manifests, []apiextensionsv1.CustomResourceDefinition{crd}, []v1alpha1.ClusterServiceVersion{csvA, csvB})
   339  	require.NotNil(t, catsrc)
   340  	require.NotNil(t, cleanup)
   341  
   342  	subscriptionSpec = &v1alpha1.SubscriptionSpec{
   343  		CatalogSource:          catsrc.GetName(),
   344  		CatalogSourceNamespace: catsrc.GetNamespace(),
   345  		Package:                packageName,
   346  		Channel:                stableChannel,
   347  		StartingCSV:            csvB.GetName(),
   348  		InstallPlanApproval:    v1alpha1.ApprovalAutomatic,
   349  	}
   350  	return
   351  }
   352  
   353  func newCatalogSourceWithDependencies(t GinkgoTInterface, kubeclient operatorclient.ClientInterface, crclient versioned.Interface, prefix, namespace string, permissions []v1alpha1.StrategyDeploymentPermissions) (catsrc *v1alpha1.CatalogSource, subscriptionSpec *v1alpha1.SubscriptionSpec, cleanup cleanupFunc) {
   354  	crdPlural := genName("ins")
   355  	crdName := crdPlural + ".cluster.com"
   356  
   357  	crd := apiextensionsv1.CustomResourceDefinition{
   358  		ObjectMeta: metav1.ObjectMeta{
   359  			Name: crdName,
   360  		},
   361  		Spec: apiextensionsv1.CustomResourceDefinitionSpec{
   362  			Group: "cluster.com",
   363  			Versions: []apiextensionsv1.CustomResourceDefinitionVersion{
   364  				{
   365  					Name:    "v1alpha1",
   366  					Served:  true,
   367  					Storage: true,
   368  					Schema: &apiextensionsv1.CustomResourceValidation{
   369  						OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
   370  							Type:        "object",
   371  							Description: "my crd schema",
   372  						},
   373  					},
   374  				},
   375  			},
   376  			Names: apiextensionsv1.CustomResourceDefinitionNames{
   377  				Plural:   crdPlural,
   378  				Singular: crdPlural,
   379  				Kind:     crdPlural,
   380  				ListKind: "list" + crdPlural,
   381  			},
   382  			Scope: apiextensionsv1.NamespaceScoped,
   383  		},
   384  	}
   385  
   386  	prefixFunc := func(s string) string {
   387  		return fmt.Sprintf("%s-%s-", prefix, s)
   388  	}
   389  
   390  	// Create CSV
   391  	packageName1 := genName(prefixFunc("package"))
   392  	packageName2 := genName(prefixFunc("package"))
   393  	stableChannel := "stable"
   394  
   395  	namedStrategy := newNginxInstallStrategy(genName(prefixFunc("dep")), permissions, nil)
   396  	csvA := newCSV("nginx-req-dep", namespace, "", semver.MustParse("0.1.0"), nil, []apiextensionsv1.CustomResourceDefinition{crd}, &namedStrategy)
   397  	csvB := newCSV("nginx-dependency", namespace, "", semver.MustParse("0.1.0"), []apiextensionsv1.CustomResourceDefinition{crd}, nil, &namedStrategy)
   398  
   399  	// Create PackageManifests
   400  	manifests := []registry.PackageManifest{
   401  		{
   402  			PackageName: packageName1,
   403  			Channels: []registry.PackageChannel{
   404  				{Name: stableChannel, CurrentCSVName: csvA.GetName()},
   405  			},
   406  			DefaultChannelName: stableChannel,
   407  		},
   408  		{
   409  			PackageName: packageName2,
   410  			Channels: []registry.PackageChannel{
   411  				{Name: stableChannel, CurrentCSVName: csvB.GetName()},
   412  			},
   413  			DefaultChannelName: stableChannel,
   414  		},
   415  	}
   416  
   417  	catalogSourceName := genName(prefixFunc("catsrc"))
   418  	catsrc, cleanup = createInternalCatalogSource(kubeclient, crclient, catalogSourceName, namespace, manifests, []apiextensionsv1.CustomResourceDefinition{crd}, []v1alpha1.ClusterServiceVersion{csvA, csvB})
   419  	require.NotNil(t, catsrc)
   420  	require.NotNil(t, cleanup)
   421  
   422  	subscriptionSpec = &v1alpha1.SubscriptionSpec{
   423  		CatalogSource:          catsrc.GetName(),
   424  		CatalogSourceNamespace: catsrc.GetNamespace(),
   425  		Package:                packageName1,
   426  		Channel:                stableChannel,
   427  		StartingCSV:            csvA.GetName(),
   428  		InstallPlanApproval:    v1alpha1.ApprovalAutomatic,
   429  	}
   430  	return
   431  }
   432  
   433  func mustHaveCondition(t GinkgoTInterface, ip *v1alpha1.InstallPlan, conditionType v1alpha1.InstallPlanConditionType) (condition *v1alpha1.InstallPlanCondition) {
   434  	for i := range ip.Status.Conditions {
   435  		if ip.Status.Conditions[i].Type == conditionType {
   436  			condition = &ip.Status.Conditions[i]
   437  			break
   438  		}
   439  	}
   440  
   441  	require.NotNil(t, condition)
   442  	return
   443  }
   444  
   445  func deploymentPermissions() []v1alpha1.StrategyDeploymentPermissions {
   446  	// Generate permissions
   447  	serviceAccountName := genName("nginx-sa-")
   448  	permissions := []v1alpha1.StrategyDeploymentPermissions{
   449  		{
   450  			ServiceAccountName: serviceAccountName,
   451  			Rules: []rbacv1.PolicyRule{
   452  				{
   453  					Verbs:     []string{rbac.VerbAll},
   454  					APIGroups: []string{rbac.APIGroupAll},
   455  					Resources: []string{rbac.ResourceAll}},
   456  			},
   457  		},
   458  	}
   459  
   460  	return permissions
   461  }
   462  
   463  func grantPermission(t GinkgoTInterface, client operatorclient.ClientInterface, namespace, serviceAccountName string) (cleanup cleanupFunc) {
   464  	role := &rbacv1.Role{
   465  		ObjectMeta: metav1.ObjectMeta{
   466  			Name:      genName("scoped-role-"),
   467  			Namespace: namespace,
   468  		},
   469  		Rules: []rbacv1.PolicyRule{
   470  			{
   471  				Verbs:     []string{rbac.VerbAll},
   472  				APIGroups: []string{rbac.APIGroupAll},
   473  				Resources: []string{rbac.ResourceAll},
   474  			},
   475  		},
   476  	}
   477  
   478  	role, err := client.KubernetesInterface().RbacV1().Roles(namespace).Create(context.TODO(), role, metav1.CreateOptions{})
   479  	require.NoError(t, err)
   480  
   481  	clusterrole := &rbacv1.ClusterRole{
   482  		ObjectMeta: metav1.ObjectMeta{
   483  			Name:      genName("scoped-clusterrole-"),
   484  			Namespace: namespace},
   485  		Rules: []rbacv1.PolicyRule{
   486  			{
   487  				Verbs:     []string{rbac.VerbAll},
   488  				APIGroups: []string{rbac.APIGroupAll},
   489  				Resources: []string{rbac.ResourceAll},
   490  			},
   491  		},
   492  	}
   493  
   494  	clusterrole, err = client.KubernetesInterface().RbacV1().ClusterRoles().Create(context.TODO(), clusterrole, metav1.CreateOptions{})
   495  	require.NoError(t, err)
   496  
   497  	binding := &rbacv1.RoleBinding{
   498  		ObjectMeta: metav1.ObjectMeta{
   499  			Name:      genName("scoped-rolebinding-"),
   500  			Namespace: namespace,
   501  		},
   502  		Subjects: []rbacv1.Subject{
   503  			{
   504  				Kind:      "ServiceAccount",
   505  				APIGroup:  "",
   506  				Name:      serviceAccountName,
   507  				Namespace: namespace,
   508  			},
   509  		},
   510  		RoleRef: rbacv1.RoleRef{
   511  			APIGroup: "rbac.authorization.k8s.io",
   512  			Kind:     "Role",
   513  			Name:     role.GetName(),
   514  		},
   515  	}
   516  
   517  	clusterbinding := &rbacv1.ClusterRoleBinding{
   518  		ObjectMeta: metav1.ObjectMeta{
   519  			Name:      genName("scoped-clusterrolebinding-"),
   520  			Namespace: namespace,
   521  		},
   522  		Subjects: []rbacv1.Subject{
   523  			{
   524  				Kind:      "ServiceAccount",
   525  				APIGroup:  "",
   526  				Name:      serviceAccountName,
   527  				Namespace: namespace,
   528  			},
   529  		},
   530  		RoleRef: rbacv1.RoleRef{
   531  			APIGroup: "rbac.authorization.k8s.io",
   532  			Kind:     "ClusterRole",
   533  			Name:     clusterrole.GetName(),
   534  		},
   535  	}
   536  
   537  	binding, err = client.KubernetesInterface().RbacV1().RoleBindings(namespace).Create(context.TODO(), binding, metav1.CreateOptions{})
   538  	require.NoError(t, err)
   539  
   540  	clusterbinding, err = client.KubernetesInterface().RbacV1().ClusterRoleBindings().Create(context.TODO(), clusterbinding, metav1.CreateOptions{})
   541  	require.NoError(t, err)
   542  
   543  	cleanup = func() {
   544  		if env := os.Getenv("SKIP_CLEANUP"); env != "" {
   545  			fmt.Printf("Skipping cleanup of role %s/%s...\n", role.GetNamespace(), role.GetName())
   546  			fmt.Printf("Skipping cleanup of role binding %s/%s...\n", binding.GetNamespace(), binding.GetName())
   547  			fmt.Printf("Skipping cleanup of cluster role %s...\n", clusterrole.GetName())
   548  			fmt.Printf("Skipping cleanup of cluster role binding %s...\n", clusterbinding.GetName())
   549  			return
   550  		}
   551  
   552  		err := client.KubernetesInterface().RbacV1().Roles(role.GetNamespace()).Delete(context.TODO(), role.GetName(), metav1.DeleteOptions{})
   553  		require.NoError(t, err)
   554  
   555  		err = client.KubernetesInterface().RbacV1().RoleBindings(binding.GetNamespace()).Delete(context.TODO(), binding.GetName(), metav1.DeleteOptions{})
   556  		require.NoError(t, err)
   557  
   558  		err = client.KubernetesInterface().RbacV1().ClusterRoles().Delete(context.TODO(), clusterrole.GetName(), metav1.DeleteOptions{})
   559  		require.NoError(t, err)
   560  
   561  		err = client.KubernetesInterface().RbacV1().ClusterRoleBindings().Delete(context.TODO(), clusterbinding.GetName(), metav1.DeleteOptions{})
   562  		require.NoError(t, err)
   563  	}
   564  
   565  	return
   566  }