2 Copyright 2018 The Kubernetes Authors.
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
24 "github.com/markbates/inflect"
26 "sigs.k8s.io/controller-tools/pkg/internal/codegen"
27 "sigs.k8s.io/controller-tools/pkg/internal/general"
30 // parseIndex indexes all types with the comment "// +resource=RESOURCE" by GroupVersionKind and
32 func (b *APIs) parseIndex() {
33 // Index resource by group, version, kind
34 b.ByGroupVersionKind = map[string]map[string]map[string]*codegen.APIResource{}
36 // Index resources by group, kind, version
37 b.ByGroupKindVersion = map[string]map[string]map[string]*codegen.APIResource{}
39 // Index subresources by group, version, kind
40 b.SubByGroupVersionKind = map[string]map[string]map[string]*types.Type{}
42 for _, c := range b.context.Order {
43 // The type is a subresource, add it to the subresource index
44 if IsAPISubresource(c) {
46 version := GetVersion(c, group)
47 kind := GetKind(c, group)
48 if _, f := b.SubByGroupVersionKind[group]; !f {
49 b.SubByGroupVersionKind[group] = map[string]map[string]*types.Type{}
51 if _, f := b.SubByGroupVersionKind[group][version]; !f {
52 b.SubByGroupVersionKind[group][version] = map[string]*types.Type{}
54 b.SubByGroupVersionKind[group][version][kind] = c
57 // If it isn't a subresource or resource, continue to the next type
58 if !IsAPIResource(c) {
62 // Parse out the resource information
63 r := &codegen.APIResource{
65 NonNamespaced: IsNonNamespaced(c),
68 r.Version = GetVersion(c, r.Group)
69 r.Kind = GetKind(c, r.Group)
72 // TODO: revisit the part...
74 rs := inflect.NewDefaultRuleset()
75 r.Resource = rs.Pluralize(strings.ToLower(r.Kind))
77 rt, err := parseResourceAnnotation(c)
79 log.Fatalf("failed to parse resource annotations, error: %v", err.Error())
81 if rt.Resource != "" {
82 r.Resource = rt.Resource
84 r.ShortName = rt.ShortName
86 // Copy the Status strategy to mirror the non-status strategy
87 r.StatusStrategy = strings.TrimSuffix(r.Strategy, "Strategy")
88 r.StatusStrategy = fmt.Sprintf("%sStatusStrategy", r.StatusStrategy)
90 // Initialize the map entries so they aren't nill
91 if _, f := b.ByGroupKindVersion[r.Group]; !f {
92 b.ByGroupKindVersion[r.Group] = map[string]map[string]*codegen.APIResource{}
94 if _, f := b.ByGroupKindVersion[r.Group][r.Kind]; !f {
95 b.ByGroupKindVersion[r.Group][r.Kind] = map[string]*codegen.APIResource{}
97 if _, f := b.ByGroupVersionKind[r.Group]; !f {
98 b.ByGroupVersionKind[r.Group] = map[string]map[string]*codegen.APIResource{}
100 if _, f := b.ByGroupVersionKind[r.Group][r.Version]; !f {
101 b.ByGroupVersionKind[r.Group][r.Version] = map[string]*codegen.APIResource{}
104 // Add the resource to the map
105 b.ByGroupKindVersion[r.Group][r.Kind][r.Version] = r
106 b.ByGroupVersionKind[r.Group][r.Version][r.Kind] = r
111 // resourceTags contains the tags present in a "+resource=" comment
112 type resourceTags struct {
119 // resourceAnnotationValue is a helper function to extract resource annotation.
120 func resourceAnnotationValue(tag string) (resourceTags, error) {
121 res := resourceTags{}
122 for _, elem := range strings.Split(tag, ",") {
123 key, value, err := general.ParseKV(elem)
125 return resourceTags{}, fmt.Errorf("// +kubebuilder:resource: tags must be key value pairs. Expected "+
126 "keys [path=<resourcepath>] "+
127 "Got string: [%s]", tag)
133 res.ShortName = value
135 return resourceTags{}, fmt.Errorf("The given input %s is invalid", value)
141 // parseResourceAnnotation parses the tags in a "+resource=" comment into a resourceTags struct.
142 func parseResourceAnnotation(t *types.Type) (resourceTags, error) {
143 finalResult := resourceTags{}
144 var resourceAnnotationFound bool
145 for _, comment := range t.CommentLines {
146 anno := general.GetAnnotation(comment, "kubebuilder:resource")
150 result, err := resourceAnnotationValue(anno)
152 return resourceTags{}, err
154 if resourceAnnotationFound {
155 return resourceTags{}, fmt.Errorf("resource annotation should only exists once per type")
157 resourceAnnotationFound = true
160 return finalResult, nil