From dc4253f1ce307840b5b42d01dc8a71a3b0b224da Mon Sep 17 00:00:00 2001 From: Alex Kalenyuk Date: Thu, 15 Jan 2026 12:38:44 +0200 Subject: [PATCH] Avoid hotloop over empty clusterrole rules For an empty list, we get in a hotloop over it being persisted with nil ``` 2026-01-14T04:29:20Z INFO DIFF {"controller": "migcontroller-controller", "object": {"name":"migcontroller-kubevirt-hyperconverged","namespace":"openshift-cnv"}, "namespace": "openshift-cnv", "name": "migcontroller-kubevirt-hyperconverged", "reconcileID": "d5cabc29-c944-4271-8b88-b5284c55c266", "obj": {"apiVersion": "rbac.authorization.k8s.io/v1", "kind": "ClusterRole", "name": "migrations.kubevirt.io:admin"}, "patch": "[{\"op\":\"add\",\"path\":\"/rules\",\"value\":[]}]"} ``` Signed-off-by: Alex Kalenyuk --- pkg/sdk/resources/rbac.go | 16 +++++++++--- pkg/sdk/resources/rbac_test.go | 30 +++++++++++++++++++++++ pkg/sdk/resources/resources_suite_test.go | 13 ++++++++++ 3 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 pkg/sdk/resources/rbac_test.go create mode 100644 pkg/sdk/resources/resources_suite_test.go diff --git a/pkg/sdk/resources/rbac.go b/pkg/sdk/resources/rbac.go index c3699ce7..2cb9428e 100644 --- a/pkg/sdk/resources/rbac.go +++ b/pkg/sdk/resources/rbac.go @@ -52,7 +52,7 @@ func (b *ResourceBuilder) CreateRoleBinding(name, roleRef, serviceAccount, servi // CreateRole creates role func (b *ResourceBuilder) CreateRole(name string, rules []rbacv1.PolicyRule) *rbacv1.Role { - return &rbacv1.Role{ + role := &rbacv1.Role{ TypeMeta: metav1.TypeMeta{ APIVersion: "rbac.authorization.k8s.io/v1", Kind: "Role", @@ -61,8 +61,12 @@ func (b *ResourceBuilder) CreateRole(name string, rules []rbacv1.PolicyRule) *rb Name: name, Labels: b.WithCommonLabels(nil), }, - Rules: rules, } + if len(rules) > 0 { + // avoid hotloop over empty rules/nil + role.Rules = rules + } + return role } // CreateClusterRoleBinding creates cluster role binding @@ -132,7 +136,7 @@ func CreateClusterRoleBinding(name, roleRef, serviceAccount, serviceAccountNames // CreateClusterRole creates a cluster role func CreateClusterRole(name string, rules []rbacv1.PolicyRule, labels map[string]string) *rbacv1.ClusterRole { - return &rbacv1.ClusterRole{ + clusterRole := &rbacv1.ClusterRole{ TypeMeta: metav1.TypeMeta{ APIVersion: "rbac.authorization.k8s.io/v1", Kind: "ClusterRole", @@ -141,6 +145,10 @@ func CreateClusterRole(name string, rules []rbacv1.PolicyRule, labels map[string Name: name, Labels: labels, }, - Rules: rules, } + if len(rules) > 0 { + // avoid hotloop over empty rules/nil + clusterRole.Rules = rules + } + return clusterRole } diff --git a/pkg/sdk/resources/rbac_test.go b/pkg/sdk/resources/rbac_test.go new file mode 100644 index 00000000..567a4252 --- /dev/null +++ b/pkg/sdk/resources/rbac_test.go @@ -0,0 +1,30 @@ +package resources + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + rbacv1 "k8s.io/api/rbac/v1" +) + +var _ = Describe("RBAC Resources", func() { + var builder ResourceBuilder + + BeforeEach(func() { + builder = NewResourceBuilder( + map[string]string{"common": "label"}, + map[string]string{"operator": "label"}, + ) + }) + + It("should treat empty rules as nil for Role", func() { + role := builder.CreateRole("test-role", []rbacv1.PolicyRule{}) + Expect(role.Rules).To(BeNil()) + }) + + It("should treat empty rules as nil for ClusterRole", func() { + labels := map[string]string{"test": "label"} + clusterRole := CreateClusterRole("test-clusterrole", []rbacv1.PolicyRule{}, labels) + Expect(clusterRole.Rules).To(BeNil()) + }) +}) diff --git a/pkg/sdk/resources/resources_suite_test.go b/pkg/sdk/resources/resources_suite_test.go new file mode 100644 index 00000000..b38445ad --- /dev/null +++ b/pkg/sdk/resources/resources_suite_test.go @@ -0,0 +1,13 @@ +package resources + +import ( + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +func TestResources(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Resources Suite") +}