From 4d56305321555e854716d0d2c34e097680dec265 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Bavelier?= Date: Fri, 15 May 2026 15:23:30 +0200 Subject: [PATCH 1/2] PoC constructor coverage refactor --- .../datadogagent/component/agent/rbac.go | 87 ++++++------ .../component/clusterchecksrunner/rbac.go | 60 +++++--- .../component/otelagentgateway/rbac.go | 54 ++++---- pkg/constants/utils.go | 131 +++++++++--------- pkg/constants/utils_test.go | 16 +++ pkg/kubernetes/rbac/utils.go | 32 +++++ pkg/kubernetes/rbac/utils_test.go | 38 +++++ 7 files changed, 265 insertions(+), 153 deletions(-) create mode 100644 pkg/kubernetes/rbac/utils.go create mode 100644 pkg/kubernetes/rbac/utils_test.go diff --git a/internal/controller/datadogagent/component/agent/rbac.go b/internal/controller/datadogagent/component/agent/rbac.go index ac1ce6f2ac..a907d2d627 100644 --- a/internal/controller/datadogagent/component/agent/rbac.go +++ b/internal/controller/datadogagent/component/agent/rbac.go @@ -14,6 +14,49 @@ import ( // RBAC for Agent +var ( + agentMetricsEndpointPolicyRule = rbacv1.PolicyRule{ + NonResourceURLs: []string{ + rbac.MetricsURL, + rbac.MetricsSLIsURL, + }, + Verbs: []string{rbac.GetVerb}, + } + agentFineGrainedKubeletPolicyRule = rbacv1.PolicyRule{ + APIGroups: []string{rbac.CoreAPIGroup}, + Resources: []string{ + rbac.NodeMetricsResource, + rbac.NodeSpecResource, + rbac.NodeStats, + rbac.NodePodsResource, + rbac.NodeHealthzResource, + rbac.NodeConfigzResource, + rbac.NodeLogsResource, + }, + Verbs: []string{rbac.GetVerb}, + } + agentKubeletPolicyRule = rbacv1.PolicyRule{ + APIGroups: []string{rbac.CoreAPIGroup}, + Resources: []string{ + rbac.NodeMetricsResource, + rbac.NodeSpecResource, + rbac.NodeProxyResource, + rbac.NodeStats, + }, + Verbs: []string{rbac.GetVerb}, + } + agentEndpointPolicyRule = rbacv1.PolicyRule{ + APIGroups: []string{rbac.CoreAPIGroup}, + Resources: []string{rbac.EndpointsResource}, + Verbs: []string{rbac.GetVerb}, + } + agentLeaderElectionPolicyRule = rbacv1.PolicyRule{ + APIGroups: []string{rbac.CoordinationAPIGroup}, + Resources: []string{rbac.LeasesResource}, + Verbs: []string{rbac.GetVerb}, + } +) + // GetDefaultAgentClusterRolePolicyRules returns the default policy rules for the Agent cluster role func GetDefaultAgentClusterRolePolicyRules(excludeNonResourceRules bool, useFineGrainedAuthorization bool) []rbacv1.PolicyRule { policyRule := []rbacv1.PolicyRule{ @@ -31,55 +74,21 @@ func GetDefaultAgentClusterRolePolicyRules(excludeNonResourceRules bool, useFine } func getMetricsEndpointPolicyRule() rbacv1.PolicyRule { - return rbacv1.PolicyRule{ - NonResourceURLs: []string{ - rbac.MetricsURL, - rbac.MetricsSLIsURL, - }, - Verbs: []string{rbac.GetVerb}, - } + return rbac.ClonePolicyRule(agentMetricsEndpointPolicyRule) } func getKubeletPolicyRule(useFineGrainedAuthorization bool) rbacv1.PolicyRule { - var resources []string if useFineGrainedAuthorization { - resources = []string{ - rbac.NodeMetricsResource, - rbac.NodeSpecResource, - rbac.NodeStats, - rbac.NodePodsResource, - rbac.NodeHealthzResource, - rbac.NodeConfigzResource, - rbac.NodeLogsResource, - } - } else { - resources = []string{ - rbac.NodeMetricsResource, - rbac.NodeSpecResource, - rbac.NodeProxyResource, - rbac.NodeStats, - } + return rbac.ClonePolicyRule(agentFineGrainedKubeletPolicyRule) } - return rbacv1.PolicyRule{ - APIGroups: []string{rbac.CoreAPIGroup}, - Resources: resources, - Verbs: []string{rbac.GetVerb}, - } + return rbac.ClonePolicyRule(agentKubeletPolicyRule) } func getEndpointsPolicyRule() rbacv1.PolicyRule { - return rbacv1.PolicyRule{ - APIGroups: []string{rbac.CoreAPIGroup}, - Resources: []string{rbac.EndpointsResource}, - Verbs: []string{rbac.GetVerb}, - } + return rbac.ClonePolicyRule(agentEndpointPolicyRule) } func getLeaderElectionPolicyRule() rbacv1.PolicyRule { - return rbacv1.PolicyRule{ - APIGroups: []string{rbac.CoordinationAPIGroup}, - Resources: []string{rbac.LeasesResource}, - Verbs: []string{rbac.GetVerb}, - } + return rbac.ClonePolicyRule(agentLeaderElectionPolicyRule) } diff --git a/internal/controller/datadogagent/component/clusterchecksrunner/rbac.go b/internal/controller/datadogagent/component/clusterchecksrunner/rbac.go index 9f9c5696a9..637f25f9ba 100644 --- a/internal/controller/datadogagent/component/clusterchecksrunner/rbac.go +++ b/internal/controller/datadogagent/component/clusterchecksrunner/rbac.go @@ -17,9 +17,8 @@ import ( // RBAC for Cluster Checks Runner -// GetDefaultClusterChecksRunnerClusterRolePolicyRules returns the default Cluster Role Policy Rules for the Cluster Checks Runner -func GetDefaultClusterChecksRunnerClusterRolePolicyRules(dda metav1.Object, excludeNonResourceRules bool) []rbacv1.PolicyRule { - policyRule := []rbacv1.PolicyRule{ +var ( + clusterChecksRunnerClusterRolePolicyRulesBeforeLeaderElection = []rbacv1.PolicyRule{ { APIGroups: []string{rbac.CoreAPIGroup}, Resources: []string{ @@ -47,19 +46,9 @@ func GetDefaultClusterChecksRunnerClusterRolePolicyRules(dda metav1.Object, excl rbac.CreateVerb, }, }, - { - APIGroups: []string{rbac.CoreAPIGroup}, - Resources: []string{ - rbac.ConfigMapsResource, - }, - ResourceNames: []string{ - utils.GetDatadogLeaderElectionResourceName(dda), - }, - Verbs: []string{ - rbac.GetVerb, - rbac.UpdateVerb, - }, - }, + } + + clusterChecksRunnerClusterRolePolicyRulesAfterLeaderElection = []rbacv1.PolicyRule{ { APIGroups: []string{rbac.OpenShiftQuotaAPIGroup}, Resources: []string{ @@ -118,15 +107,40 @@ func GetDefaultClusterChecksRunnerClusterRolePolicyRules(dda metav1.Object, excl component.GetEKSControlPlaneMetricsPolicyRule(), } + clusterChecksRunnerMetricsEndpointPolicyRule = rbacv1.PolicyRule{ + NonResourceURLs: []string{ + rbac.MetricsURL, + rbac.MetricsSLIsURL, + }, + Verbs: []string{rbac.GetVerb}, + } +) + +// GetDefaultClusterChecksRunnerClusterRolePolicyRules returns the default Cluster Role Policy Rules for the Cluster Checks Runner. +func GetDefaultClusterChecksRunnerClusterRolePolicyRules(dda metav1.Object, excludeNonResourceRules bool) []rbacv1.PolicyRule { + policyRule := rbac.ClonePolicyRules(clusterChecksRunnerClusterRolePolicyRulesBeforeLeaderElection) + policyRule = append(policyRule, clusterChecksRunnerLeaderElectionPolicyRule(dda)) + policyRule = append(policyRule, rbac.ClonePolicyRules(clusterChecksRunnerClusterRolePolicyRulesAfterLeaderElection)...) + if !excludeNonResourceRules { - policyRule = append(policyRule, rbacv1.PolicyRule{ - NonResourceURLs: []string{ - rbac.MetricsURL, - rbac.MetricsSLIsURL, - }, - Verbs: []string{rbac.GetVerb}, - }) + policyRule = append(policyRule, rbac.ClonePolicyRule(clusterChecksRunnerMetricsEndpointPolicyRule)) } return policyRule } + +func clusterChecksRunnerLeaderElectionPolicyRule(dda metav1.Object) rbacv1.PolicyRule { + return rbacv1.PolicyRule{ + APIGroups: []string{rbac.CoreAPIGroup}, + Resources: []string{ + rbac.ConfigMapsResource, + }, + ResourceNames: []string{ + utils.GetDatadogLeaderElectionResourceName(dda), + }, + Verbs: []string{ + rbac.GetVerb, + rbac.UpdateVerb, + }, + } +} diff --git a/internal/controller/datadogagent/component/otelagentgateway/rbac.go b/internal/controller/datadogagent/component/otelagentgateway/rbac.go index 951d1bed21..d98593fcd7 100644 --- a/internal/controller/datadogagent/component/otelagentgateway/rbac.go +++ b/internal/controller/datadogagent/component/otelagentgateway/rbac.go @@ -14,34 +14,34 @@ import ( // RBAC for OTel Agent Gateway -// GetDefaultOtelAgentGatewayClusterRolePolicyRules returns the default Cluster Role Policy Rules for the OTel Agent Gateway -// These rules support the k8sattributes processor for enriching telemetry with Kubernetes metadata -func GetDefaultOtelAgentGatewayClusterRolePolicyRules(dda metav1.Object, excludeNonResourceRules bool) []rbacv1.PolicyRule { - policyRule := []rbacv1.PolicyRule{ - { - APIGroups: []string{rbac.CoreAPIGroup}, - Resources: []string{ - rbac.PodsResource, - rbac.NamespaceResource, - }, - Verbs: []string{ - rbac.GetVerb, - rbac.WatchVerb, - rbac.ListVerb, - }, +var defaultOtelAgentGatewayClusterRolePolicyRules = []rbacv1.PolicyRule{ + { + APIGroups: []string{rbac.CoreAPIGroup}, + Resources: []string{ + rbac.PodsResource, + rbac.NamespaceResource, }, - { - APIGroups: []string{rbac.AppsAPIGroup}, - Resources: []string{ - rbac.ReplicasetsResource, - }, - Verbs: []string{ - rbac.GetVerb, - rbac.ListVerb, - rbac.WatchVerb, - }, + Verbs: []string{ + rbac.GetVerb, + rbac.WatchVerb, + rbac.ListVerb, }, - } + }, + { + APIGroups: []string{rbac.AppsAPIGroup}, + Resources: []string{ + rbac.ReplicasetsResource, + }, + Verbs: []string{ + rbac.GetVerb, + rbac.ListVerb, + rbac.WatchVerb, + }, + }, +} - return policyRule +// GetDefaultOtelAgentGatewayClusterRolePolicyRules returns the default Cluster Role Policy Rules for the OTel Agent Gateway. +// These rules support the k8sattributes processor for enriching telemetry with Kubernetes metadata. +func GetDefaultOtelAgentGatewayClusterRolePolicyRules(_ metav1.Object, _ bool) []rbacv1.PolicyRule { + return rbac.ClonePolicyRules(defaultOtelAgentGatewayClusterRolePolicyRules) } diff --git a/pkg/constants/utils.go b/pkg/constants/utils.go index 6d41e00895..39e1370f06 100644 --- a/pkg/constants/utils.go +++ b/pkg/constants/utils.go @@ -121,109 +121,112 @@ func IsNetworkPolicyEnabled(ddaSpec *v2alpha1.DatadogAgentSpec) (bool, v2alpha1. return false, "" } -// GetDefaultLivenessProbe creates a defaulted LivenessProbe -func GetDefaultLivenessProbe() *corev1.Probe { - livenessProbe := &corev1.Probe{ +var ( + defaultLivenessProbe = &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: DefaultLivenessProbeHTTPPath, + Port: intstr.IntOrString{IntVal: DefaultAgentHealthPort}, + }, + }, InitialDelaySeconds: DefaultLivenessProbeInitialDelaySeconds, PeriodSeconds: DefaultLivenessProbePeriodSeconds, TimeoutSeconds: DefaultLivenessProbeTimeoutSeconds, SuccessThreshold: DefaultLivenessProbeSuccessThreshold, FailureThreshold: DefaultLivenessProbeFailureThreshold, } - livenessProbe.HTTPGet = &corev1.HTTPGetAction{ - Path: DefaultLivenessProbeHTTPPath, - Port: intstr.IntOrString{ - IntVal: DefaultAgentHealthPort, + defaultReadinessProbe = &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: DefaultReadinessProbeHTTPPath, + Port: intstr.IntOrString{IntVal: DefaultAgentHealthPort}, + }, }, - } - return livenessProbe -} - -// GetDefaultReadinessProbe creates a defaulted ReadinessProbe -func GetDefaultReadinessProbe() *corev1.Probe { - readinessProbe := &corev1.Probe{ InitialDelaySeconds: DefaultReadinessProbeInitialDelaySeconds, PeriodSeconds: DefaultReadinessProbePeriodSeconds, TimeoutSeconds: DefaultReadinessProbeTimeoutSeconds, SuccessThreshold: DefaultReadinessProbeSuccessThreshold, FailureThreshold: DefaultReadinessProbeFailureThreshold, } - readinessProbe.HTTPGet = &corev1.HTTPGetAction{ - Path: DefaultReadinessProbeHTTPPath, - Port: intstr.IntOrString{ - IntVal: DefaultAgentHealthPort, + defaultStartupProbe = &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: DefaultStartupProbeHTTPPath, + Port: intstr.IntOrString{IntVal: DefaultAgentHealthPort}, + }, }, - } - return readinessProbe -} - -// GetDefaultStartupProbe creates a defaulted StartupProbe -func GetDefaultStartupProbe() *corev1.Probe { - startupProbe := &corev1.Probe{ InitialDelaySeconds: DefaultStartupProbeInitialDelaySeconds, PeriodSeconds: DefaultStartupProbePeriodSeconds, TimeoutSeconds: DefaultStartupProbeTimeoutSeconds, SuccessThreshold: DefaultStartupProbeSuccessThreshold, FailureThreshold: DefaultStartupProbeFailureThreshold, } - startupProbe.HTTPGet = &corev1.HTTPGetAction{ - Path: DefaultStartupProbeHTTPPath, - Port: intstr.IntOrString{ - IntVal: DefaultAgentHealthPort, + defaultTraceAgentProbe = &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{ + Port: intstr.IntOrString{IntVal: DefaultApmPort}, + }, }, - } - return startupProbe -} - -// GetDefaultTraceAgentProbe creates a defaulted liveness/readiness probe for the Trace Agent -func GetDefaultTraceAgentProbe() *corev1.Probe { - probe := &corev1.Probe{ InitialDelaySeconds: DefaultLivenessProbeInitialDelaySeconds, PeriodSeconds: DefaultLivenessProbePeriodSeconds, TimeoutSeconds: DefaultLivenessProbeTimeoutSeconds, } - probe.TCPSocket = &corev1.TCPSocketAction{ - Port: intstr.IntOrString{ - IntVal: DefaultApmPort, + defaultAgentDataPlaneLivenessProbe = &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: DefaultLivenessProbeHTTPPath, + Port: intstr.IntOrString{IntVal: DefaultADPHealthPort}, + }, }, - } - return probe -} - -// GetDefaultAgentDataPlaneLivenessProbe creates a defaulted liveness probe for Agent Data Plane -func GetDefaultAgentDataPlaneLivenessProbe() *corev1.Probe { - livenessProbe := &corev1.Probe{ InitialDelaySeconds: DefaultADPLivenessProbeInitialDelaySeconds, PeriodSeconds: DefaultADPLivenessProbePeriodSeconds, TimeoutSeconds: DefaultADPLivenessProbeTimeoutSeconds, SuccessThreshold: DefaultADPLivenessProbeSuccessThreshold, FailureThreshold: DefaultADPLivenessProbeFailureThreshold, } - livenessProbe.HTTPGet = &corev1.HTTPGetAction{ - Path: DefaultLivenessProbeHTTPPath, - Port: intstr.IntOrString{ - IntVal: DefaultADPHealthPort, + defaultAgentDataPlaneReadinessProbe = &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: DefaultReadinessProbeHTTPPath, + Port: intstr.IntOrString{IntVal: DefaultADPHealthPort}, + }, }, - } - return livenessProbe -} - -// GetDefaultAgentDataPlaneReadinessProbe creates a defaulted readiness probe for Agent Data Plane -func GetDefaultAgentDataPlaneReadinessProbe() *corev1.Probe { - readinessProbe := &corev1.Probe{ InitialDelaySeconds: DefaultADPReadinessProbeInitialDelaySeconds, PeriodSeconds: DefaultADPReadinessProbePeriodSeconds, TimeoutSeconds: DefaultADPReadinessProbeTimeoutSeconds, SuccessThreshold: DefaultADPReadinessProbeSuccessThreshold, FailureThreshold: DefaultADPReadinessProbeFailureThreshold, } - readinessProbe.HTTPGet = &corev1.HTTPGetAction{ - Path: DefaultReadinessProbeHTTPPath, - Port: intstr.IntOrString{ - IntVal: DefaultADPHealthPort, - }, - } - return readinessProbe +) + +// GetDefaultLivenessProbe creates a defaulted LivenessProbe +func GetDefaultLivenessProbe() *corev1.Probe { + return defaultLivenessProbe.DeepCopy() +} + +// GetDefaultReadinessProbe creates a defaulted ReadinessProbe +func GetDefaultReadinessProbe() *corev1.Probe { + return defaultReadinessProbe.DeepCopy() +} + +// GetDefaultStartupProbe creates a defaulted StartupProbe +func GetDefaultStartupProbe() *corev1.Probe { + return defaultStartupProbe.DeepCopy() +} + +// GetDefaultTraceAgentProbe creates a defaulted liveness/readiness probe for the Trace Agent +func GetDefaultTraceAgentProbe() *corev1.Probe { + return defaultTraceAgentProbe.DeepCopy() +} + +// GetDefaultAgentDataPlaneLivenessProbe creates a defaulted liveness probe for Agent Data Plane +func GetDefaultAgentDataPlaneLivenessProbe() *corev1.Probe { + return defaultAgentDataPlaneLivenessProbe.DeepCopy() +} + +// GetDefaultAgentDataPlaneReadinessProbe creates a defaulted readiness probe for Agent Data Plane +func GetDefaultAgentDataPlaneReadinessProbe() *corev1.Probe { + return defaultAgentDataPlaneReadinessProbe.DeepCopy() } // GetDDAName returns the name of the DDA from DDAI labels or directly from the DDA diff --git a/pkg/constants/utils_test.go b/pkg/constants/utils_test.go index cce0f44ec6..0e0229da34 100644 --- a/pkg/constants/utils_test.go +++ b/pkg/constants/utils_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/stretchr/testify/require" + "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/utils/ptr" apicommon "github.com/DataDog/datadog-operator/api/datadoghq/common" @@ -173,6 +174,21 @@ func TestIsNetworkPolicyEnabled(t *testing.T) { require.Equal(t, v2alpha1.NetworkPolicyFlavorCilium, flavor) } +func TestDefaultProbesAreIndependentCopies(t *testing.T) { + liveness := GetDefaultLivenessProbe() + liveness.HTTPGet.Path = "/changed" + liveness.HTTPGet.Port = intstr.FromInt(1234) + + freshLiveness := GetDefaultLivenessProbe() + require.Equal(t, DefaultLivenessProbeHTTPPath, freshLiveness.HTTPGet.Path) + require.Equal(t, intstr.FromInt32(DefaultAgentHealthPort), freshLiveness.HTTPGet.Port) + + trace := GetDefaultTraceAgentProbe() + trace.TCPSocket.Port = intstr.FromInt(1234) + + require.Equal(t, intstr.FromInt(DefaultApmPort), GetDefaultTraceAgentProbe().TCPSocket.Port) +} + func TestGetDDAName(t *testing.T) { require.Equal(t, "from-label", GetDDAName(&v1.ObjectMeta{ Name: "object-name", diff --git a/pkg/kubernetes/rbac/utils.go b/pkg/kubernetes/rbac/utils.go new file mode 100644 index 0000000000..49ee3ad1b8 --- /dev/null +++ b/pkg/kubernetes/rbac/utils.go @@ -0,0 +1,32 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +package rbac + +import ( + "slices" + + rbacv1 "k8s.io/api/rbac/v1" +) + +// ClonePolicyRules returns an independent copy of policy rules templates. +func ClonePolicyRules(rules []rbacv1.PolicyRule) []rbacv1.PolicyRule { + cloned := make([]rbacv1.PolicyRule, 0, len(rules)) + for _, rule := range rules { + cloned = append(cloned, ClonePolicyRule(rule)) + } + return cloned +} + +// ClonePolicyRule returns an independent copy of a policy rule template. +func ClonePolicyRule(rule rbacv1.PolicyRule) rbacv1.PolicyRule { + return rbacv1.PolicyRule{ + Verbs: slices.Clone(rule.Verbs), + APIGroups: slices.Clone(rule.APIGroups), + Resources: slices.Clone(rule.Resources), + ResourceNames: slices.Clone(rule.ResourceNames), + NonResourceURLs: slices.Clone(rule.NonResourceURLs), + } +} diff --git a/pkg/kubernetes/rbac/utils_test.go b/pkg/kubernetes/rbac/utils_test.go new file mode 100644 index 0000000000..2c39d2ee3e --- /dev/null +++ b/pkg/kubernetes/rbac/utils_test.go @@ -0,0 +1,38 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +package rbac + +import ( + "testing" + + "github.com/stretchr/testify/require" + rbacv1 "k8s.io/api/rbac/v1" +) + +func TestClonePolicyRulesReturnsIndependentRules(t *testing.T) { + templates := []rbacv1.PolicyRule{ + { + APIGroups: []string{CoreAPIGroup}, + Resources: []string{PodsResource}, + ResourceNames: []string{"datadog"}, + NonResourceURLs: []string{MetricsURL}, + Verbs: []string{GetVerb}, + }, + } + + cloned := ClonePolicyRules(templates) + cloned[0].APIGroups[0] = AppsAPIGroup + cloned[0].Resources[0] = DeploymentsResource + cloned[0].ResourceNames[0] = "other" + cloned[0].NonResourceURLs[0] = HealthzURL + cloned[0].Verbs[0] = WatchVerb + + require.Equal(t, CoreAPIGroup, templates[0].APIGroups[0]) + require.Equal(t, PodsResource, templates[0].Resources[0]) + require.Equal(t, "datadog", templates[0].ResourceNames[0]) + require.Equal(t, MetricsURL, templates[0].NonResourceURLs[0]) + require.Equal(t, GetVerb, templates[0].Verbs[0]) +} From 38af679a920133168384ed15f9e8dbde0f4972e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Bavelier?= Date: Fri, 15 May 2026 15:40:09 +0200 Subject: [PATCH 2/2] Simplify RBAC policy rule cloning --- pkg/kubernetes/rbac/utils.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/kubernetes/rbac/utils.go b/pkg/kubernetes/rbac/utils.go index 49ee3ad1b8..ff1d0b3c92 100644 --- a/pkg/kubernetes/rbac/utils.go +++ b/pkg/kubernetes/rbac/utils.go @@ -13,9 +13,9 @@ import ( // ClonePolicyRules returns an independent copy of policy rules templates. func ClonePolicyRules(rules []rbacv1.PolicyRule) []rbacv1.PolicyRule { - cloned := make([]rbacv1.PolicyRule, 0, len(rules)) - for _, rule := range rules { - cloned = append(cloned, ClonePolicyRule(rule)) + cloned := slices.Clone(rules) + for i := range cloned { + cloned[i] = ClonePolicyRule(cloned[i]) } return cloned }