diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index e8cae5ff..dc6e68d7 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -92,6 +92,17 @@ rules: - patch - update - watch +- apiGroups: + - keda.sh + resources: + - scaledobjects + - triggerauthentications + verbs: + - create + - delete + - get + - list + - update - apiGroups: - objectbucket.io resources: diff --git a/internal/controller/function_controller.go b/internal/controller/function_controller.go index ea00b79e..5de0b1bf 100644 --- a/internal/controller/function_controller.go +++ b/internal/controller/function_controller.go @@ -78,6 +78,7 @@ type FunctionReconciler struct { // +kubebuilder:rbac:groups=tekton.dev,resources=taskruns,verbs=get;list;watch // +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=rolebindings;roles,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=http.keda.sh,resources=httpscaledobjects,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=keda.sh,resources=scaledobjects;triggerauthentications,verbs=get;list;create;update;delete // Reconcile a Function with status update func (r *FunctionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { diff --git a/internal/controller/function_rbac.go b/internal/controller/function_rbac.go index 75abc3cd..856f4214 100644 --- a/internal/controller/function_rbac.go +++ b/internal/controller/function_rbac.go @@ -78,6 +78,13 @@ func (r *FunctionReconciler) ensureDeployFunctionRole(ctx context.Context, names APIGroups: []string{"http.keda.sh"}, Resources: []string{"httpscaledobjects"}, Verbs: []string{"create", "delete", "get", "list", "patch", "update", "watch"}, + }, { + // func's keda deployer reads ScaledObjects on every deploy and creates + // ScaledObjects and TriggerAuthentications for kafka triggers + // (knative/func#4069, granted by func itself in knative/func#4077) + APIGroups: []string{"keda.sh"}, + Resources: []string{"scaledobjects", "triggerauthentications"}, + Verbs: []string{"create", "delete", "get", "list", "update"}, }, }, } diff --git a/internal/controller/function_rbac_test.go b/internal/controller/function_rbac_test.go index d482dc29..e83b699b 100644 --- a/internal/controller/function_rbac_test.go +++ b/internal/controller/function_rbac_test.go @@ -129,4 +129,63 @@ var _ = Describe("Function RBAC", func() { Expect(rb.RoleRef.Name).To(Equal(deployFunctionRoleName)) }) }) + + Context("ensureDeployFunctionRole", func() { + var reconciler *FunctionReconciler + var testNamespace string + + // func's keda deployer reads ScaledObjects on every keda deploy + kedaRule := rbacv1.PolicyRule{ + APIGroups: []string{"keda.sh"}, + Resources: []string{"scaledobjects", "triggerauthentications"}, + Verbs: []string{"create", "delete", "get", "list", "update"}, + } + + BeforeEach(func() { + testNamespace = "rbac-role-test-" + rand.String(6) + ns := &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: testNamespace}} + Expect(k8sClient.Create(ctx, ns)).To(Succeed()) + + reconciler = &FunctionReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + Recorder: &events.FakeRecorder{}, + } + }) + + It("should grant the keda deployer access to ScaledObjects and TriggerAuthentications", func() { + Expect(reconciler.ensureDeployFunctionRole(ctx, testNamespace)).To(Succeed()) + + role := &rbacv1.Role{} + Expect(k8sClient.Get(ctx, types.NamespacedName{ + Name: deployFunctionRoleName, + Namespace: testNamespace, + }, role)).To(Succeed()) + Expect(role.Rules).To(ContainElement(kedaRule)) + }) + + It("should add the keda.sh rule to an existing Role without it", func() { + existing := &rbacv1.Role{ + ObjectMeta: metav1.ObjectMeta{ + Name: deployFunctionRoleName, + Namespace: testNamespace, + }, + Rules: []rbacv1.PolicyRule{{ + APIGroups: []string{"http.keda.sh"}, + Resources: []string{"httpscaledobjects"}, + Verbs: []string{"create", "delete", "get", "list", "patch", "update", "watch"}, + }}, + } + Expect(k8sClient.Create(ctx, existing)).To(Succeed()) + + Expect(reconciler.ensureDeployFunctionRole(ctx, testNamespace)).To(Succeed()) + + role := &rbacv1.Role{} + Expect(k8sClient.Get(ctx, types.NamespacedName{ + Name: deployFunctionRoleName, + Namespace: testNamespace, + }, role)).To(Succeed()) + Expect(role.Rules).To(ContainElement(kedaRule)) + }) + }) })