@@ -17,15 +17,17 @@ limitations under the License.
17
17
package parse
18
18
19
19
import (
20
+ "bufio"
21
+ "fmt"
20
22
"go/build"
23
+ "log"
24
+ "os"
21
25
"path/filepath"
22
26
"strings"
23
- "os"
24
- "bufio"
25
27
26
28
"github.com/golang/glog"
27
-
28
29
"github.com/kubernetes-sigs/kubebuilder/cmd/internal/codegen"
30
+ "github.com/markbates/inflect"
29
31
"github.com/pkg/errors"
30
32
rbacv1 "k8s.io/api/rbac/v1"
31
33
"k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -68,11 +70,90 @@ func NewAPIs(context *generator.Context, arguments *args.GeneratorArgs) *APIs {
68
70
b .parseControllers ()
69
71
b .parseRBAC ()
70
72
b .parseInformers ()
73
+ b .verifyRBACAnnotations ()
71
74
b .parseAPIs ()
72
75
b .parseCRDs ()
73
76
return b
74
77
}
75
78
79
+ // verifyRBACAnnotations verifies that there are corresponding RBAC annotations for
80
+ // each informer annotation.
81
+ // e.g. if there is an // +kubebuilder:informer annotation for Pods, then there
82
+ // should also be a // +kubebuilder:rbac annotation for Pods
83
+ func (b * APIs ) verifyRBACAnnotations () {
84
+ parseOption := b .arguments .CustomArgs .(* ParseOptions )
85
+ if parseOption .SkipRBACValidation {
86
+ log .Println ("skipping RBAC validations" )
87
+ return
88
+ }
89
+ err := rbacMatchesInformers (b .Informers , b .Rules )
90
+ if err != nil {
91
+ log .Fatal (err )
92
+ }
93
+ }
94
+
95
+ func rbacMatchesInformers (informers map [v1.GroupVersionKind ]bool , rbacRules []rbacv1.PolicyRule ) error {
96
+ rs := inflect .NewDefaultRuleset ()
97
+
98
+ // For each informer, look for the RBAC annotation
99
+ for gvk := range informers {
100
+ found := false
101
+
102
+ // Search all RBAC rules for one that matches the informer group and resource
103
+ for _ , rule := range rbacRules {
104
+
105
+ // Check if the group matches the informer group
106
+ groupFound := false
107
+ for _ , g := range rule .APIGroups {
108
+ // RBAC has the full group with domain, whereas informers do not. Strip the domain
109
+ // from the group before comparing.
110
+ parts := strings .Split (g , "." )
111
+ group := parts [len (parts )- 1 ]
112
+
113
+ // If the RBAC group is wildcard or matches, it is a match
114
+ if g == "*" || group == gvk .Group {
115
+ groupFound = true
116
+ break
117
+ }
118
+ // Edge case where "core" and "" are equivalent
119
+ if (group == "core" || group == "" ) && (gvk .Group == "core" || gvk .Group == "" ) {
120
+ groupFound = true
121
+ break
122
+ }
123
+ }
124
+ if ! groupFound {
125
+ continue
126
+ }
127
+
128
+ // The resource name is the lower-plural of the Kind
129
+ resource := rs .Pluralize (strings .ToLower (gvk .Kind ))
130
+ // Check if the resource matches the informer resource
131
+ resourceFound := false
132
+ for _ , k := range rule .Resources {
133
+ // If the RBAC resource is a wildcard or matches the informer resource, it is a match
134
+ if k == "*" || k == resource {
135
+ resourceFound = true
136
+ break
137
+ }
138
+ }
139
+ if ! resourceFound {
140
+ continue
141
+ }
142
+
143
+ // Found a matching RBAC rule
144
+ found = true
145
+ break
146
+ }
147
+ if ! found {
148
+ return fmt .Errorf ("Missing rbac rule for %s.%s. Add with // +kubebuilder:rbac:groups=%s," +
149
+ "resources=%s,verbs=get;list;watch comment on controller struct " +
150
+ "or run the command with '--skip-rbac-validation' arg" , gvk .Group , gvk .Kind , gvk .Group ,
151
+ inflect .NewDefaultRuleset ().Pluralize (strings .ToLower (gvk .Kind )))
152
+ }
153
+ }
154
+ return nil
155
+ }
156
+
76
157
// parseGroupNames initializes b.GroupNames with the set of all groups
77
158
func (b * APIs ) parseGroupNames () {
78
159
b .GroupNames = sets.String {}
@@ -129,10 +210,10 @@ func (b *APIs) parseDomain() {
129
210
130
211
func parseDomainFromFiles (paths []string ) string {
131
212
var domain string
132
- for _ , path := range paths {
133
- if strings .HasSuffix (path , "pkg/apis" ) {
134
- filePath := strings .Join ([]string {build .Default .GOPATH , "src" , path , "doc.go" }, "/" )
135
- lines := []string {}
213
+ for _ , path := range paths {
214
+ if strings .HasSuffix (path , "pkg/apis" ) {
215
+ filePath := strings .Join ([]string {build .Default .GOPATH , "src" , path , "doc.go" }, "/" )
216
+ lines := []string {}
136
217
137
218
file , err := os .Open (filePath )
138
219
if err != nil {
0 commit comments