Repository: kubernetes/kubernetes
File: pkg/apis/rbac/v1/helpers.go (Go)

Code before:
import (
	"fmt"
	"slices"

	rbacv1 "k8s.io/api/rbac/v1"

	"sort"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// +k8s:deepcopy-gen=false

// PolicyRuleBuilder let's us attach methods.  A no-no for API types.
// We use it to construct rules in code.  It's more compact than trying to write them
// out in a literal and allows us to perform some basic checking during construction
type PolicyRuleBuilder struct {
	PolicyRule rbacv1.PolicyRule `protobuf:"bytes,1,opt,name=policyRule"`
}

func NewRule(verbs ...string) *PolicyRuleBuilder {
	return &PolicyRuleBuilder{
		// The verbs parameter slice might be shared between different
		// rule instances and gets sorted (= written) below, so we have
		// to clone it to avoid a data race.
		PolicyRule: rbacv1.PolicyRule{Verbs: slices.Clone(verbs)},
	}
}

func (r *PolicyRuleBuilder) Groups(groups ...string) *PolicyRuleBuilder {
	r.PolicyRule.APIGroups = append(r.PolicyRule.APIGroups, groups...)
	return r
}

func (r *PolicyRuleBuilder) Resources(resources ...string) *PolicyRuleBuilder {
	r.PolicyRule.Resources = append(r.PolicyRule.Resources, resources...)
	return r
}

func (r *PolicyRuleBuilder) Names(names ...string) *PolicyRuleBuilder {
	r.PolicyRule.ResourceNames = append(r.PolicyRule.ResourceNames, names...)
	return r
}

func (r *PolicyRuleBuilder) URLs(urls ...string) *PolicyRuleBuilder {
	r.PolicyRule.NonResourceURLs = append(r.PolicyRule.NonResourceURLs, urls...)
	return r
}

func (r *PolicyRuleBuilder) RuleOrDie() rbacv1.PolicyRule {
	ret, err := r.Rule()

Review comment (refactor):
huh... I did not know the vararg slice would pass through the original slice in this case

slight preference for following the sort/dedupe-on-input approach in pkg/apis/rbac/helpers.go

```diff
diff --git a/pkg/apis/rbac/v1/helpers.go b/pkg/apis/rbac/v1/helpers.go
index 669e48c8a6f..d30e33a65a5 100644
--- a/pkg/apis/rbac/v1/helpers.go
+++ b/pkg/apis/rbac/v1/helpers.go
@@ -24,6 +24,7 @@ import (
 	"sort"
 
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/util/sets"
 )
 
 // +k8s:deepcopy-gen=false
@@ -37,30 +38,36 @@ type PolicyRuleBuilder struct {
 
 func NewRule(verbs ...string) *PolicyRuleBuilder {
 	return &PolicyRuleBuilder{
-		PolicyRule: rbacv1.PolicyRule{Verbs: verbs},
+		PolicyRule: rbacv1.PolicyRule{Verbs: sets.NewString(verbs...).List()},
 	}
 }
 
 func (r *PolicyRuleBuilder) Groups(groups ...string) *PolicyRuleBuilder {
-	r.PolicyRule.APIGroups = append(r.PolicyRule.APIGroups, groups...)
+	r.PolicyRule.APIGroups = combine(r.PolicyRule.APIGroups, groups)
 	return r
 }
 
 func (r *PolicyRuleBuilder) Resources(resources ...string) *PolicyRuleBuilder {
-	r.PolicyRule.Resources = append(r.PolicyRule.Resources, resources...)
+	r.PolicyRule.Resources = combine(r.PolicyRule.Resources, resources)
 	return r
 }
 
 func (r *PolicyRuleBuilder) Names(names ...string) *PolicyRuleBuilder {
-	r.PolicyRule.ResourceNames = append(r.PolicyRule.ResourceNames, names...)
+	r.PolicyRule.ResourceNames = combine(r.PolicyRule.ResourceNames, names)
 	return r
 }
 
 func (r *PolicyRuleBuilder) URLs(urls ...string) *PolicyRuleBuilder {
-	r.PolicyRule.NonResourceURLs = append(r.PolicyRule.NonResourceURLs, urls...)
+	r.PolicyRule.NonResourceURLs = combine(r.PolicyRule.NonResourceURLs, urls)
 	return r
 }
 
+func combine(s1, s2 []string) []string {
+	s := sets.NewString(s1...)
+	s.Insert(s2...)
+	return s.List()
+}
+
 func (r *PolicyRuleBuilder) RuleOrDie() rbacv1.PolicyRule {
 	ret, err := r.Rule()
 	if err != nil {
```

Code after:
import (
	"fmt"

	rbacv1 "k8s.io/api/rbac/v1"

	"sort"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/sets"
)

// +k8s:deepcopy-gen=false

// PolicyRuleBuilder let's us attach methods.  A no-no for API types.
// We use it to construct rules in code.  It's more compact than trying to write them
// out in a literal and allows us to perform some basic checking during construction
type PolicyRuleBuilder struct {
	PolicyRule rbacv1.PolicyRule `protobuf:"bytes,1,opt,name=policyRule"`
}

func NewRule(verbs ...string) *PolicyRuleBuilder {
	return &PolicyRuleBuilder{
		PolicyRule: rbacv1.PolicyRule{Verbs: sets.NewString(verbs...).List()},
	}
}

func (r *PolicyRuleBuilder) Groups(groups ...string) *PolicyRuleBuilder {
	r.PolicyRule.APIGroups = combine(r.PolicyRule.APIGroups, groups)
	return r
}

func (r *PolicyRuleBuilder) Resources(resources ...string) *PolicyRuleBuilder {
	r.PolicyRule.Resources = combine(r.PolicyRule.Resources, resources)
	return r
}

func (r *PolicyRuleBuilder) Names(names ...string) *PolicyRuleBuilder {
	r.PolicyRule.ResourceNames = combine(r.PolicyRule.ResourceNames, names)
	return r
}

func (r *PolicyRuleBuilder) URLs(urls ...string) *PolicyRuleBuilder {
	r.PolicyRule.NonResourceURLs = combine(r.PolicyRule.NonResourceURLs, urls)
	return r
}

func combine(s1, s2 []string) []string {
	s := sets.NewString(s1...)
	s.Insert(s2...)
	return s.List()
}