@@ -23,6 +23,9 @@ limitations under the License.
23
23
package rbac
24
24
25
25
import (
26
+ "sort"
27
+ "strings"
28
+
26
29
rbacv1 "k8s.io/api/rbac/v1"
27
30
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28
31
@@ -44,8 +47,57 @@ type Rule struct {
44
47
URLs []string `marker:"urls,optional"`
45
48
}
46
49
50
+ // ruleKey represents the resources and non-resources a Rule applies.
51
+ type ruleKey struct {
52
+ Groups string
53
+ Resources string
54
+ URLs string
55
+ }
56
+
57
+ // key normalizes the Rule and returns a ruleKey object.
58
+ func (r * Rule ) key () ruleKey {
59
+ r .normalize ()
60
+ return ruleKey {
61
+ Groups : strings .Join (r .Groups , "&" ),
62
+ Resources : strings .Join (r .Resources , "&" ),
63
+ URLs : strings .Join (r .URLs , "&" ),
64
+ }
65
+ }
66
+
67
+ // addVerbs adds new verbs into a Rule.
68
+ // The duplicates in `r.Verbs` will be removed, and then `r.Verbs` will be sorted.
69
+ func (r * Rule ) addVerbs (verbs []string ) {
70
+ r .Verbs = removeDupAndSort (append (r .Verbs , verbs ... ))
71
+ }
72
+
73
+ // normalize removes duplicates from each field of a Rule, and sorts each field.
74
+ func (r * Rule ) normalize () {
75
+ r .Groups = removeDupAndSort (r .Groups )
76
+ r .Resources = removeDupAndSort (r .Resources )
77
+ r .Verbs = removeDupAndSort (r .Verbs )
78
+ r .URLs = removeDupAndSort (r .URLs )
79
+ }
80
+
81
+ // removeDupAndSort removes duplicates in strs, sorts the items, and returns a
82
+ // new slice of strings.
83
+ func removeDupAndSort (strs []string ) []string {
84
+ set := make (map [string ]bool )
85
+ for _ , str := range strs {
86
+ if _ , ok := set [str ]; ! ok {
87
+ set [str ] = true
88
+ }
89
+ }
90
+
91
+ var result []string
92
+ for str := range set {
93
+ result = append (result , str )
94
+ }
95
+ sort .Strings (result )
96
+ return result
97
+ }
98
+
47
99
// ToRule converts this rule to its Kubernetes API form.
48
- func (r Rule ) ToRule () rbacv1.PolicyRule {
100
+ func (r * Rule ) ToRule () rbacv1.PolicyRule {
49
101
// fix the group names first, since letting people type "core" is nice
50
102
for i , group := range r .Groups {
51
103
if group == "core" {
@@ -68,20 +120,33 @@ type Generator struct {
68
120
func (Generator ) RegisterMarkers (into * markers.Registry ) error {
69
121
return into .Register (RuleDefinition )
70
122
}
123
+
71
124
func (g Generator ) Generate (ctx * genall.GenerationContext ) error {
72
- var rules []rbacv1. PolicyRule
125
+ rules := make ( map [ ruleKey ] * Rule )
73
126
for _ , root := range ctx .Roots {
74
127
markerSet , err := markers .PackageMarkers (ctx .Collector , root )
75
128
if err != nil {
76
129
root .AddError (err )
77
130
}
78
131
79
- for _ , rule := range markerSet [RuleDefinition .Name ] {
80
- rules = append (rules , rule .(Rule ).ToRule ())
132
+ for _ , markerValue := range markerSet [RuleDefinition .Name ] {
133
+ rule := markerValue .(Rule )
134
+ key := rule .key ()
135
+ if _ , ok := rules [key ]; ! ok {
136
+ rules [key ] = & rule
137
+ continue
138
+ }
139
+ rules [key ].addVerbs (rule .Verbs )
81
140
}
82
141
}
83
142
84
- if len (rules ) == 0 {
143
+ var policyRules []rbacv1.PolicyRule
144
+ for _ , rule := range rules {
145
+ policyRules = append (policyRules , rule .ToRule ())
146
+
147
+ }
148
+
149
+ if len (policyRules ) == 0 {
85
150
return nil
86
151
}
87
152
@@ -93,7 +158,7 @@ func (g Generator) Generate(ctx *genall.GenerationContext) error {
93
158
ObjectMeta : metav1.ObjectMeta {
94
159
Name : g .RoleName ,
95
160
},
96
- Rules : rules ,
161
+ Rules : policyRules ,
97
162
}); err != nil {
98
163
return err
99
164
}
0 commit comments