Remove BPA from Makefile
[icn.git] / cmd / bpa-operator / vendor / sigs.k8s.io / controller-tools / pkg / internal / codegen / parse / apis.go
1 /*
2 Copyright 2018 The Kubernetes Authors.
3
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
7
8     http://www.apache.org/licenses/LICENSE-2.0
9
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.
15 */
16
17 package parse
18
19 import (
20         "fmt"
21         "path"
22         "path/filepath"
23         "strings"
24
25         "k8s.io/apimachinery/pkg/util/sets"
26         "k8s.io/gengo/types"
27         "sigs.k8s.io/controller-tools/pkg/internal/codegen"
28 )
29
30 type genUnversionedType struct {
31         Type     *types.Type
32         Resource *codegen.APIResource
33 }
34
35 func (b *APIs) parseAPIs() {
36         apis := &codegen.APIs{
37                 Domain:    b.Domain,
38                 Package:   b.APIsPkg,
39                 Groups:    map[string]*codegen.APIGroup{},
40                 Rules:     b.Rules,
41                 Informers: b.Informers,
42         }
43
44         for group, versionMap := range b.ByGroupVersionKind {
45                 apiGroup := &codegen.APIGroup{
46                         Group:                group,
47                         GroupTitle:           strings.Title(group),
48                         Domain:               b.Domain,
49                         Versions:             map[string]*codegen.APIVersion{},
50                         UnversionedResources: map[string]*codegen.APIResource{},
51                 }
52
53                 for version, kindMap := range versionMap {
54                         apiVersion := &codegen.APIVersion{
55                                 Domain:    b.Domain,
56                                 Group:     group,
57                                 Version:   version,
58                                 Resources: map[string]*codegen.APIResource{},
59                         }
60                         for kind, resource := range kindMap {
61                                 apiResource := &codegen.APIResource{
62                                         Domain:         resource.Domain,
63                                         Version:        resource.Version,
64                                         Group:          resource.Group,
65                                         Resource:       resource.Resource,
66                                         Type:           resource.Type,
67                                         REST:           resource.REST,
68                                         Kind:           resource.Kind,
69                                         Subresources:   resource.Subresources,
70                                         StatusStrategy: resource.StatusStrategy,
71                                         Strategy:       resource.Strategy,
72                                         NonNamespaced:  resource.NonNamespaced,
73                                         ShortName:      resource.ShortName,
74                                 }
75                                 parseDoc(resource, apiResource)
76                                 apiVersion.Resources[kind] = apiResource
77                                 // Set the package for the api version
78                                 apiVersion.Pkg = b.context.Universe[resource.Type.Name.Package]
79                                 // Set the package for the api group
80                                 apiGroup.Pkg = b.context.Universe[filepath.Dir(resource.Type.Name.Package)]
81                                 if apiGroup.Pkg != nil {
82                                         apiGroup.PkgPath = apiGroup.Pkg.Path
83                                 }
84
85                                 apiGroup.UnversionedResources[kind] = apiResource
86                         }
87
88                         apiGroup.Versions[version] = apiVersion
89                 }
90                 b.parseStructs(apiGroup)
91                 apis.Groups[group] = apiGroup
92         }
93         apis.Pkg = b.context.Universe[b.APIsPkg]
94         b.APIs = apis
95 }
96
97 func (b *APIs) parseStructs(apigroup *codegen.APIGroup) {
98         remaining := []genUnversionedType{}
99         for _, version := range apigroup.Versions {
100                 for _, resource := range version.Resources {
101                         remaining = append(remaining, genUnversionedType{resource.Type, resource})
102                 }
103         }
104         for _, version := range b.SubByGroupVersionKind[apigroup.Group] {
105                 for _, kind := range version {
106                         remaining = append(remaining, genUnversionedType{kind, nil})
107                 }
108         }
109
110         done := sets.String{}
111         for len(remaining) > 0 {
112                 // Pop the next element from the list
113                 next := remaining[0]
114                 remaining[0] = remaining[len(remaining)-1]
115                 remaining = remaining[:len(remaining)-1]
116
117                 // Already processed this type.  Skip it
118                 if done.Has(next.Type.Name.Name) {
119                         continue
120                 }
121                 done.Insert(next.Type.Name.Name)
122
123                 // Generate the struct and append to the list
124                 result, additionalTypes := parseType(next.Type)
125
126                 // This is a resource, so generate the client
127                 if b.genClient(next.Type) {
128                         result.GenClient = true
129                         result.GenDeepCopy = true
130                 }
131
132                 if next.Resource != nil {
133                         result.NonNamespaced = IsNonNamespaced(next.Type)
134                 }
135
136                 if b.genDeepCopy(next.Type) {
137                         result.GenDeepCopy = true
138                 }
139                 apigroup.Structs = append(apigroup.Structs, result)
140
141                 // Add the newly discovered subtypes
142                 for _, at := range additionalTypes {
143                         remaining = append(remaining, genUnversionedType{at, nil})
144                 }
145         }
146 }
147
148 // parseType parses the type into a Struct, and returns a list of types that
149 // need to be parsed
150 func parseType(t *types.Type) (*codegen.Struct, []*types.Type) {
151         remaining := []*types.Type{}
152
153         s := &codegen.Struct{
154                 Name:           t.Name.Name,
155                 GenClient:      false,
156                 GenUnversioned: true, // Generate unversioned structs by default
157         }
158
159         for _, c := range t.CommentLines {
160                 if strings.Contains(c, "+genregister:unversioned=false") {
161                         // Don't generate the unversioned struct
162                         s.GenUnversioned = false
163                 }
164         }
165
166         for _, member := range t.Members {
167                 uType := member.Type.Name.Name
168                 memberName := member.Name
169                 uImport := ""
170
171                 // Use the element type for Pointers, Maps and Slices
172                 mSubType := member.Type
173                 hasElem := false
174                 for mSubType.Elem != nil {
175                         mSubType = mSubType.Elem
176                         hasElem = true
177                 }
178                 if hasElem {
179                         // Strip the package from the field type
180                         uType = strings.Replace(member.Type.String(), mSubType.Name.Package+".", "", 1)
181                 }
182
183                 base := filepath.Base(member.Type.String())
184                 samepkg := t.Name.Package == mSubType.Name.Package
185
186                 // If not in the same package, calculate the import pkg
187                 if !samepkg {
188                         parts := strings.Split(base, ".")
189                         if len(parts) > 1 {
190                                 // Don't generate unversioned types for core types, just use the versioned types
191                                 if strings.HasPrefix(mSubType.Name.Package, "k8s.io/api/") {
192                                         // Import the package under an alias so it doesn't conflict with other groups
193                                         // having the same version
194                                         importAlias := path.Base(path.Dir(mSubType.Name.Package)) + path.Base(mSubType.Name.Package)
195                                         uImport = fmt.Sprintf("%s \"%s\"", importAlias, mSubType.Name.Package)
196                                         if hasElem {
197                                                 // Replace the full package with the alias when referring to the type
198                                                 uType = strings.Replace(member.Type.String(), mSubType.Name.Package, importAlias, 1)
199                                         } else {
200                                                 // Replace the full package with the alias when referring to the type
201                                                 uType = fmt.Sprintf("%s.%s", importAlias, parts[1])
202                                         }
203                                 } else {
204                                         switch member.Type.Name.Package {
205                                         case "k8s.io/apimachinery/pkg/apis/meta/v1":
206                                                 // Use versioned types for meta/v1
207                                                 uImport = fmt.Sprintf("%s \"%s\"", "metav1", "k8s.io/apimachinery/pkg/apis/meta/v1")
208                                                 uType = "metav1." + parts[1]
209                                         default:
210                                                 // Use unversioned types for everything else
211                                                 t := member.Type
212
213                                                 if t.Elem != nil {
214                                                         // handle Pointers, Maps, Slices
215
216                                                         // We need to parse the package from the Type String
217                                                         t = t.Elem
218                                                         str := member.Type.String()
219                                                         startPkg := strings.LastIndexAny(str, "*]")
220                                                         endPkg := strings.LastIndexAny(str, ".")
221                                                         pkg := str[startPkg+1 : endPkg]
222                                                         name := str[endPkg+1:]
223                                                         prefix := str[:startPkg+1]
224
225                                                         uImportBase := path.Base(pkg)
226                                                         uImportName := path.Base(path.Dir(pkg)) + uImportBase
227                                                         uImport = fmt.Sprintf("%s \"%s\"", uImportName, pkg)
228
229                                                         uType = prefix + uImportName + "." + name
230                                                 } else {
231                                                         // handle non- Pointer, Maps, Slices
232                                                         pkg := t.Name.Package
233                                                         name := t.Name.Name
234
235                                                         // Come up with the alias the package is imported under
236                                                         // Concatenate with directory package to reduce naming collisions
237                                                         uImportBase := path.Base(pkg)
238                                                         uImportName := path.Base(path.Dir(pkg)) + uImportBase
239
240                                                         // Create the import statement
241                                                         uImport = fmt.Sprintf("%s \"%s\"", uImportName, pkg)
242
243                                                         // Create the field type name - should be <pkgalias>.<TypeName>
244                                                         uType = uImportName + "." + name
245                                                 }
246                                         }
247                                 }
248                         }
249                 }
250
251                 if member.Embedded {
252                         memberName = ""
253                 }
254
255                 s.Fields = append(s.Fields, &codegen.Field{
256                         Name:              memberName,
257                         VersionedPackage:  member.Type.Name.Package,
258                         UnversionedImport: uImport,
259                         UnversionedType:   uType,
260                 })
261
262                 // Add this member Type for processing if it isn't a primitive and
263                 // is part of the same API group
264                 if !mSubType.IsPrimitive() && GetGroup(mSubType) == GetGroup(t) {
265                         remaining = append(remaining, mSubType)
266                 }
267         }
268         return s, remaining
269 }
270
271 func (b *APIs) genClient(c *types.Type) bool {
272         comments := Comments(c.CommentLines)
273         resource := comments.getTag("resource", ":") + comments.getTag("kubebuilder:resource", ":")
274         return len(resource) > 0
275 }
276
277 func (b *APIs) genDeepCopy(c *types.Type) bool {
278         comments := Comments(c.CommentLines)
279         return comments.hasTag("subresource-request")
280 }
281
282 func parseDoc(resource, apiResource *codegen.APIResource) {
283         if HasDocAnnotation(resource.Type) {
284                 resource.DocAnnotation = getDocAnnotation(resource.Type, "warning", "note")
285                 apiResource.DocAnnotation = resource.DocAnnotation
286         }
287 }