github.com/operator-framework/operator-lifecycle-manager@v0.30.0/test/e2e/scoped_client_test.go (about) 1 package e2e 2 3 import ( 4 "context" 5 6 . "github.com/onsi/ginkgo/v2" 7 . "github.com/onsi/gomega" 8 "github.com/sirupsen/logrus" 9 corev1 "k8s.io/api/core/v1" 10 apierrors "k8s.io/apimachinery/pkg/api/errors" 11 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 "k8s.io/apimachinery/pkg/runtime/schema" 13 "k8s.io/client-go/rest" 14 15 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/clients" 16 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient" 17 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/scoped" 18 "github.com/operator-framework/operator-lifecycle-manager/test/e2e/ctx" 19 ) 20 21 var _ = Describe("Scoped Client bound to a service account can be used to make API calls", func() { 22 // TestScopedClient ensures that we can create a scoped client bound to a 23 // service account and then we can use the scoped client to make API calls. 24 var ( 25 config *rest.Config 26 27 kubeclient operatorclient.ClientInterface 28 29 logger *logrus.Logger 30 ) 31 32 BeforeEach(func() { 33 config = ctx.Ctx().RESTConfig() 34 35 kubeclient = ctx.Ctx().KubeClient() 36 37 logger = logrus.New() 38 logger.SetOutput(GinkgoWriter) 39 }) 40 41 type testParameter struct { 42 name string 43 grant func(namespace, name string) (cleanup cleanupFunc) 44 assertFunc func(errGot error) 45 } 46 47 tableEntries := []TableEntry{ 48 // The parent test invokes 'Get' API on non existent objects. If the 49 // scoped client has enough permission, we expect a NotFound error code. 50 // Otherwise, we expect a 'Forbidden' error code due to lack of permission. 51 52 Entry("returns error on API calls as ServiceAccount does not have any permission", testParameter{ 53 // The service account does not have any permission granted to it. 54 // We expect the get api call to return 'Forbidden' error due to 55 // lack of permission. 56 assertFunc: func(errGot error) { 57 Expect(apierrors.IsForbidden(errGot)).To(BeTrue()) 58 }, 59 }), 60 Entry("successfully allows API calls to be made when ServiceAccount has permission", testParameter{ 61 // The service account does have permission granted to it. 62 // We expect the get api call to return 'NotFound' error. 63 grant: func(namespace, name string) (cleanup cleanupFunc) { 64 cleanup = grantPermission(GinkgoT(), kubeclient, namespace, name) 65 return 66 }, 67 assertFunc: func(errGot error) { 68 Expect(apierrors.IsNotFound(errGot)).To(BeTrue()) 69 }, 70 }), 71 } 72 73 DescribeTable("API call using scoped client", func(tc testParameter) { 74 By(`Create a new namespace`) 75 namespace := genName("a") 76 _, cleanupNS := newNamespace(kubeclient, namespace) 77 defer cleanupNS() 78 79 By(`Create a service account.`) 80 saName := genName("user-defined-") 81 sa, cleanupSA := newServiceAccount(kubeclient, namespace, saName) 82 defer cleanupSA() 83 By(`Create token secret for the serviceaccount`) 84 secret, cleanupSE := newTokenSecret(kubeclient, namespace, saName) 85 defer cleanupSE() 86 87 By("Wait for token secret data to be available") 88 Eventually(func() (*corev1.Secret, error) { 89 se, err := kubeclient.KubernetesInterface().CoreV1().Secrets(secret.GetNamespace()).Get(context.TODO(), secret.GetName(), metav1.GetOptions{}) 90 return se, err 91 }).ShouldNot(WithTransform(func(v *corev1.Secret) string { 92 return string(v.Data[corev1.ServiceAccountTokenKey]) 93 }, BeEmpty())) 94 95 By(`Grant permission(s) to the service account if specified.`) 96 strategy := scoped.NewClientAttenuator(logger, config, kubeclient) 97 getter := func() (reference *corev1.ObjectReference, err error) { 98 reference = &corev1.ObjectReference{ 99 Namespace: namespace, 100 Name: saName, 101 } 102 return 103 } 104 105 if tc.grant != nil { 106 cleanupPerm := tc.grant(sa.GetNamespace(), sa.GetName()) 107 defer cleanupPerm() 108 } 109 110 By("Get scoped client instance(s)") 111 attenuate, err := strategy.AttenuateToServiceAccount(getter) 112 Expect(err).ToNot(HaveOccurred()) 113 114 factory := clients.NewFactory(config).WithConfigTransformer(attenuate) 115 kubeclientGot, err := factory.NewOperatorClient() 116 Expect(err).ToNot(HaveOccurred()) 117 Expect(kubeclientGot).ToNot(BeNil()) 118 crclientGot, err := factory.NewKubernetesClient() 119 Expect(err).ToNot(HaveOccurred()) 120 Expect(crclientGot).ToNot(BeNil()) 121 dynamicClientGot, err := factory.NewDynamicClient() 122 Expect(err).ToNot(HaveOccurred()) 123 Expect(dynamicClientGot).ToNot(BeNil()) 124 125 By(`Invoke Get API call on non existent object(s) to check if the call can be made successfully.`) 126 _, err = kubeclientGot.KubernetesInterface().CoreV1().ConfigMaps(namespace).Get(context.TODO(), genName("does-not-exist-"), metav1.GetOptions{}) 127 Expect(err).To(HaveOccurred()) 128 tc.assertFunc(err) 129 130 _, err = crclientGot.OperatorsV1alpha1().CatalogSources(namespace).Get(context.TODO(), genName("does-not-exist-"), metav1.GetOptions{}) 131 Expect(err).To(HaveOccurred()) 132 tc.assertFunc(err) 133 134 gvr := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "ConfigMap"} 135 _, err = dynamicClientGot.Resource(gvr).Namespace(namespace).Get(context.TODO(), genName("does-not-exist-"), metav1.GetOptions{}) 136 Expect(err).To(HaveOccurred()) 137 tc.assertFunc(err) 138 }, tableEntries) 139 })