parser.go 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. /*
  2. Copyright 2019 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. // Package rbac contain libraries for generating RBAC manifests from RBAC
  14. // markers in Go source files.
  15. //
  16. // The markers take the form:
  17. //
  18. // +kubebuilder:rbac:groups=<groups>,resources=<resources>,resourceNames=<resource names>,verbs=<verbs>,urls=<non resource urls>
  19. package rbac
  20. import (
  21. "fmt"
  22. "sort"
  23. "strings"
  24. rbacv1 "k8s.io/api/rbac/v1"
  25. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  26. "sigs.k8s.io/controller-tools/pkg/genall"
  27. "sigs.k8s.io/controller-tools/pkg/markers"
  28. )
  29. var (
  30. // RuleDefinition is a marker for defining RBAC rules.
  31. // Call ToRule on the value to get a Kubernetes RBAC policy rule.
  32. RuleDefinition = markers.Must(markers.MakeDefinition("kubebuilder:rbac", markers.DescribesPackage, Rule{}))
  33. )
  34. // +controllertools:marker:generateHelp:category=RBAC
  35. // Rule specifies an RBAC rule to all access to some resources or non-resource URLs.
  36. type Rule struct {
  37. // Groups specifies the API groups that this rule encompasses.
  38. Groups []string `marker:",optional"`
  39. // Resources specifies the API resources that this rule encompasses.
  40. Resources []string `marker:",optional"`
  41. // ResourceNames specifies the names of the API resources that this rule encompasses.
  42. //
  43. // Create requests cannot be restricted by resourcename, as the object's name
  44. // is not known at authorization time.
  45. ResourceNames []string `marker:",optional"`
  46. // Verbs specifies the (lowercase) kubernetes API verbs that this rule encompasses.
  47. Verbs []string
  48. // URL specifies the non-resource URLs that this rule encompasses.
  49. URLs []string `marker:"urls,optional"`
  50. // Namespace specifies the scope of the Rule.
  51. // If not set, the Rule belongs to the generated ClusterRole.
  52. // If set, the Rule belongs to a Role, whose namespace is specified by this field.
  53. Namespace string `marker:",optional"`
  54. }
  55. // ruleKey represents the resources and non-resources a Rule applies.
  56. type ruleKey struct {
  57. Groups string
  58. Resources string
  59. ResourceNames string
  60. URLs string
  61. }
  62. func (key ruleKey) String() string {
  63. return fmt.Sprintf("%s + %s + %s + %s", key.Groups, key.Resources, key.ResourceNames, key.URLs)
  64. }
  65. // ruleKeys implements sort.Interface
  66. type ruleKeys []ruleKey
  67. func (keys ruleKeys) Len() int { return len(keys) }
  68. func (keys ruleKeys) Swap(i, j int) { keys[i], keys[j] = keys[j], keys[i] }
  69. func (keys ruleKeys) Less(i, j int) bool { return keys[i].String() < keys[j].String() }
  70. // key normalizes the Rule and returns a ruleKey object.
  71. func (r *Rule) key() ruleKey {
  72. r.normalize()
  73. return ruleKey{
  74. Groups: strings.Join(r.Groups, "&"),
  75. Resources: strings.Join(r.Resources, "&"),
  76. ResourceNames: strings.Join(r.ResourceNames, "&"),
  77. URLs: strings.Join(r.URLs, "&"),
  78. }
  79. }
  80. // addVerbs adds new verbs into a Rule.
  81. // The duplicates in `r.Verbs` will be removed, and then `r.Verbs` will be sorted.
  82. func (r *Rule) addVerbs(verbs []string) {
  83. r.Verbs = removeDupAndSort(append(r.Verbs, verbs...))
  84. }
  85. // normalize removes duplicates from each field of a Rule, and sorts each field.
  86. func (r *Rule) normalize() {
  87. r.Groups = removeDupAndSort(r.Groups)
  88. r.Resources = removeDupAndSort(r.Resources)
  89. r.ResourceNames = removeDupAndSort(r.ResourceNames)
  90. r.Verbs = removeDupAndSort(r.Verbs)
  91. r.URLs = removeDupAndSort(r.URLs)
  92. }
  93. // removeDupAndSort removes duplicates in strs, sorts the items, and returns a
  94. // new slice of strings.
  95. func removeDupAndSort(strs []string) []string {
  96. set := make(map[string]bool)
  97. for _, str := range strs {
  98. if _, ok := set[str]; !ok {
  99. set[str] = true
  100. }
  101. }
  102. var result []string
  103. for str := range set {
  104. result = append(result, str)
  105. }
  106. sort.Strings(result)
  107. return result
  108. }
  109. // ToRule converts this rule to its Kubernetes API form.
  110. func (r *Rule) ToRule() rbacv1.PolicyRule {
  111. // fix the group names first, since letting people type "core" is nice
  112. for i, group := range r.Groups {
  113. if group == "core" {
  114. r.Groups[i] = ""
  115. }
  116. }
  117. return rbacv1.PolicyRule{
  118. APIGroups: r.Groups,
  119. Verbs: r.Verbs,
  120. Resources: r.Resources,
  121. ResourceNames: r.ResourceNames,
  122. NonResourceURLs: r.URLs,
  123. }
  124. }
  125. // +controllertools:marker:generateHelp
  126. // Generator generates ClusterRole objects.
  127. type Generator struct {
  128. // RoleName sets the name of the generated ClusterRole.
  129. RoleName string
  130. }
  131. func (Generator) RegisterMarkers(into *markers.Registry) error {
  132. if err := into.Register(RuleDefinition); err != nil {
  133. return err
  134. }
  135. into.AddHelp(RuleDefinition, Rule{}.Help())
  136. return nil
  137. }
  138. // GenerateRoles generate a slice of objs representing either a ClusterRole or a Role object
  139. // The order of the objs in the returned slice is stable and determined by their namespaces.
  140. func GenerateRoles(ctx *genall.GenerationContext, roleName string) ([]interface{}, error) {
  141. rulesByNS := make(map[string][]*Rule)
  142. for _, root := range ctx.Roots {
  143. markerSet, err := markers.PackageMarkers(ctx.Collector, root)
  144. if err != nil {
  145. root.AddError(err)
  146. }
  147. // group RBAC markers by namespace
  148. for _, markerValue := range markerSet[RuleDefinition.Name] {
  149. rule := markerValue.(Rule)
  150. namespace := rule.Namespace
  151. if _, ok := rulesByNS[namespace]; !ok {
  152. rules := make([]*Rule, 0)
  153. rulesByNS[namespace] = rules
  154. }
  155. rulesByNS[namespace] = append(rulesByNS[namespace], &rule)
  156. }
  157. }
  158. // NormalizeRules merge Rule with the same ruleKey and sort the Rules
  159. NormalizeRules := func(rules []*Rule) []rbacv1.PolicyRule {
  160. ruleMap := make(map[ruleKey]*Rule)
  161. // all the Rules having the same ruleKey will be merged into the first Rule
  162. for _, rule := range rules {
  163. key := rule.key()
  164. if _, ok := ruleMap[key]; !ok {
  165. ruleMap[key] = rule
  166. continue
  167. }
  168. ruleMap[key].addVerbs(rule.Verbs)
  169. }
  170. // sort the Rules in rules according to their ruleKeys
  171. keys := make([]ruleKey, 0, len(ruleMap))
  172. for key := range ruleMap {
  173. keys = append(keys, key)
  174. }
  175. sort.Sort(ruleKeys(keys))
  176. var policyRules []rbacv1.PolicyRule
  177. for _, key := range keys {
  178. policyRules = append(policyRules, ruleMap[key].ToRule())
  179. }
  180. return policyRules
  181. }
  182. // collect all the namespaces and sort them
  183. var namespaces []string
  184. for ns := range rulesByNS {
  185. namespaces = append(namespaces, ns)
  186. }
  187. sort.Strings(namespaces)
  188. // process the items in rulesByNS by the order specified in `namespaces` to make sure that the Role order is stable
  189. var objs []interface{}
  190. for _, ns := range namespaces {
  191. rules := rulesByNS[ns]
  192. policyRules := NormalizeRules(rules)
  193. if len(policyRules) == 0 {
  194. continue
  195. }
  196. if ns == "" {
  197. objs = append(objs, rbacv1.ClusterRole{
  198. TypeMeta: metav1.TypeMeta{
  199. Kind: "ClusterRole",
  200. APIVersion: rbacv1.SchemeGroupVersion.String(),
  201. },
  202. ObjectMeta: metav1.ObjectMeta{
  203. Name: roleName,
  204. },
  205. Rules: policyRules,
  206. })
  207. } else {
  208. objs = append(objs, rbacv1.Role{
  209. TypeMeta: metav1.TypeMeta{
  210. Kind: "Role",
  211. APIVersion: rbacv1.SchemeGroupVersion.String(),
  212. },
  213. ObjectMeta: metav1.ObjectMeta{
  214. Name: roleName,
  215. Namespace: ns,
  216. },
  217. Rules: policyRules,
  218. })
  219. }
  220. }
  221. return objs, nil
  222. }
  223. func (g Generator) Generate(ctx *genall.GenerationContext) error {
  224. objs, err := GenerateRoles(ctx, g.RoleName)
  225. if err != nil {
  226. return err
  227. }
  228. if len(objs) == 0 {
  229. return nil
  230. }
  231. return ctx.WriteYAML("role.yaml", objs...)
  232. }