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