1/* 2Copyright 2016 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package generators 18 19import ( 20 "fmt" 21 "io" 22 "path/filepath" 23 "strings" 24 25 "k8s.io/gengo/args" 26 "k8s.io/gengo/generator" 27 "k8s.io/gengo/namer" 28 "k8s.io/gengo/types" 29 30 "k8s.io/code-generator/cmd/client-gen/generators/util" 31 clientgentypes "k8s.io/code-generator/cmd/client-gen/types" 32 33 "k8s.io/klog" 34) 35 36// NameSystems returns the name system used by the generators in this package. 37func NameSystems() namer.NameSystems { 38 pluralExceptions := map[string]string{ 39 "Endpoints": "Endpoints", 40 } 41 return namer.NameSystems{ 42 "public": namer.NewPublicNamer(0), 43 "private": namer.NewPrivateNamer(0), 44 "raw": namer.NewRawNamer("", nil), 45 "publicPlural": namer.NewPublicPluralNamer(pluralExceptions), 46 "allLowercasePlural": namer.NewAllLowercasePluralNamer(pluralExceptions), 47 "lowercaseSingular": &lowercaseSingularNamer{}, 48 } 49} 50 51// lowercaseSingularNamer implements Namer 52type lowercaseSingularNamer struct{} 53 54// Name returns t's name in all lowercase. 55func (n *lowercaseSingularNamer) Name(t *types.Type) string { 56 return strings.ToLower(t.Name.Name) 57} 58 59// DefaultNameSystem returns the default name system for ordering the types to be 60// processed by the generators in this package. 61func DefaultNameSystem() string { 62 return "public" 63} 64 65// Packages makes the client package definition. 66func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages { 67 boilerplate, err := arguments.LoadGoBoilerplate() 68 if err != nil { 69 klog.Fatalf("Failed loading boilerplate: %v", err) 70 } 71 72 var packageList generator.Packages 73 for _, inputDir := range arguments.InputDirs { 74 p := context.Universe.Package(inputDir) 75 76 objectMeta, internal, err := objectMetaForPackage(p) 77 if err != nil { 78 klog.Fatal(err) 79 } 80 if objectMeta == nil { 81 // no types in this package had genclient 82 continue 83 } 84 85 var gv clientgentypes.GroupVersion 86 var internalGVPkg string 87 88 if internal { 89 lastSlash := strings.LastIndex(p.Path, "/") 90 if lastSlash == -1 { 91 klog.Fatalf("error constructing internal group version for package %q", p.Path) 92 } 93 gv.Group = clientgentypes.Group(p.Path[lastSlash+1:]) 94 internalGVPkg = p.Path 95 } else { 96 parts := strings.Split(p.Path, "/") 97 gv.Group = clientgentypes.Group(parts[len(parts)-2]) 98 gv.Version = clientgentypes.Version(parts[len(parts)-1]) 99 100 internalGVPkg = strings.Join(parts[0:len(parts)-1], "/") 101 } 102 groupPackageName := strings.ToLower(gv.Group.NonEmpty()) 103 104 // If there's a comment of the form "// +groupName=somegroup" or 105 // "// +groupName=somegroup.foo.bar.io", use the first field (somegroup) as the name of the 106 // group when generating. 107 if override := types.ExtractCommentTags("+", p.Comments)["groupName"]; override != nil { 108 gv.Group = clientgentypes.Group(strings.SplitN(override[0], ".", 2)[0]) 109 } 110 111 var typesToGenerate []*types.Type 112 for _, t := range p.Types { 113 tags := util.MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...)) 114 if !tags.GenerateClient || !tags.HasVerb("list") || !tags.HasVerb("get") { 115 continue 116 } 117 typesToGenerate = append(typesToGenerate, t) 118 } 119 if len(typesToGenerate) == 0 { 120 continue 121 } 122 orderer := namer.Orderer{Namer: namer.NewPrivateNamer(0)} 123 typesToGenerate = orderer.OrderTypes(typesToGenerate) 124 125 packagePath := filepath.Join(arguments.OutputPackagePath, groupPackageName, strings.ToLower(gv.Version.NonEmpty())) 126 packageList = append(packageList, &generator.DefaultPackage{ 127 PackageName: strings.ToLower(gv.Version.NonEmpty()), 128 PackagePath: packagePath, 129 HeaderText: boilerplate, 130 GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) { 131 generators = append(generators, &expansionGenerator{ 132 DefaultGen: generator.DefaultGen{ 133 OptionalName: "expansion_generated", 134 }, 135 packagePath: filepath.Join(arguments.OutputBase, packagePath), 136 types: typesToGenerate, 137 }) 138 139 for _, t := range typesToGenerate { 140 generators = append(generators, &listerGenerator{ 141 DefaultGen: generator.DefaultGen{ 142 OptionalName: strings.ToLower(t.Name.Name), 143 }, 144 outputPackage: arguments.OutputPackagePath, 145 groupVersion: gv, 146 internalGVPkg: internalGVPkg, 147 typeToGenerate: t, 148 imports: generator.NewImportTracker(), 149 objectMeta: objectMeta, 150 }) 151 } 152 return generators 153 }, 154 FilterFunc: func(c *generator.Context, t *types.Type) bool { 155 tags := util.MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...)) 156 return tags.GenerateClient && tags.HasVerb("list") && tags.HasVerb("get") 157 }, 158 }) 159 } 160 161 return packageList 162} 163 164// objectMetaForPackage returns the type of ObjectMeta used by package p. 165func objectMetaForPackage(p *types.Package) (*types.Type, bool, error) { 166 generatingForPackage := false 167 for _, t := range p.Types { 168 // filter out types which dont have genclient. 169 if !util.MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...)).GenerateClient { 170 continue 171 } 172 generatingForPackage = true 173 for _, member := range t.Members { 174 if member.Name == "ObjectMeta" { 175 return member.Type, isInternal(member), nil 176 } 177 } 178 } 179 if generatingForPackage { 180 return nil, false, fmt.Errorf("unable to find ObjectMeta for any types in package %s", p.Path) 181 } 182 return nil, false, nil 183} 184 185// isInternal returns true if the tags for a member do not contain a json tag 186func isInternal(m types.Member) bool { 187 return !strings.Contains(m.Tags, "json") 188} 189 190// listerGenerator produces a file of listers for a given GroupVersion and 191// type. 192type listerGenerator struct { 193 generator.DefaultGen 194 outputPackage string 195 groupVersion clientgentypes.GroupVersion 196 internalGVPkg string 197 typeToGenerate *types.Type 198 imports namer.ImportTracker 199 objectMeta *types.Type 200} 201 202var _ generator.Generator = &listerGenerator{} 203 204func (g *listerGenerator) Filter(c *generator.Context, t *types.Type) bool { 205 return t == g.typeToGenerate 206} 207 208func (g *listerGenerator) Namers(c *generator.Context) namer.NameSystems { 209 return namer.NameSystems{ 210 "raw": namer.NewRawNamer(g.outputPackage, g.imports), 211 } 212} 213 214func (g *listerGenerator) Imports(c *generator.Context) (imports []string) { 215 imports = append(imports, g.imports.ImportLines()...) 216 imports = append(imports, "k8s.io/apimachinery/pkg/api/errors") 217 imports = append(imports, "k8s.io/apimachinery/pkg/labels") 218 // for Indexer 219 imports = append(imports, "k8s.io/client-go/tools/cache") 220 return 221} 222 223func (g *listerGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { 224 sw := generator.NewSnippetWriter(w, c, "$", "$") 225 226 klog.V(5).Infof("processing type %v", t) 227 m := map[string]interface{}{ 228 "Resource": c.Universe.Function(types.Name{Package: t.Name.Package, Name: "Resource"}), 229 "type": t, 230 "objectMeta": g.objectMeta, 231 } 232 233 tags, err := util.ParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...)) 234 if err != nil { 235 return err 236 } 237 238 if tags.NonNamespaced { 239 sw.Do(typeListerInterface_NonNamespaced, m) 240 } else { 241 sw.Do(typeListerInterface, m) 242 } 243 244 sw.Do(typeListerStruct, m) 245 sw.Do(typeListerConstructor, m) 246 sw.Do(typeLister_List, m) 247 248 if tags.NonNamespaced { 249 sw.Do(typeLister_NonNamespacedGet, m) 250 return sw.Error() 251 } 252 253 sw.Do(typeLister_NamespaceLister, m) 254 sw.Do(namespaceListerInterface, m) 255 sw.Do(namespaceListerStruct, m) 256 sw.Do(namespaceLister_List, m) 257 sw.Do(namespaceLister_Get, m) 258 259 return sw.Error() 260} 261 262var typeListerInterface = ` 263// $.type|public$Lister helps list $.type|publicPlural$. 264type $.type|public$Lister interface { 265 // List lists all $.type|publicPlural$ in the indexer. 266 List(selector labels.Selector) (ret []*$.type|raw$, err error) 267 // $.type|publicPlural$ returns an object that can list and get $.type|publicPlural$. 268 $.type|publicPlural$(namespace string) $.type|public$NamespaceLister 269 $.type|public$ListerExpansion 270} 271` 272 273var typeListerInterface_NonNamespaced = ` 274// $.type|public$Lister helps list $.type|publicPlural$. 275type $.type|public$Lister interface { 276 // List lists all $.type|publicPlural$ in the indexer. 277 List(selector labels.Selector) (ret []*$.type|raw$, err error) 278 // Get retrieves the $.type|public$ from the index for a given name. 279 Get(name string) (*$.type|raw$, error) 280 $.type|public$ListerExpansion 281} 282` 283 284var typeListerStruct = ` 285// $.type|private$Lister implements the $.type|public$Lister interface. 286type $.type|private$Lister struct { 287 indexer cache.Indexer 288} 289` 290 291var typeListerConstructor = ` 292// New$.type|public$Lister returns a new $.type|public$Lister. 293func New$.type|public$Lister(indexer cache.Indexer) $.type|public$Lister { 294 return &$.type|private$Lister{indexer: indexer} 295} 296` 297 298var typeLister_List = ` 299// List lists all $.type|publicPlural$ in the indexer. 300func (s *$.type|private$Lister) List(selector labels.Selector) (ret []*$.type|raw$, err error) { 301 err = cache.ListAll(s.indexer, selector, func(m interface{}) { 302 ret = append(ret, m.(*$.type|raw$)) 303 }) 304 return ret, err 305} 306` 307 308var typeLister_NamespaceLister = ` 309// $.type|publicPlural$ returns an object that can list and get $.type|publicPlural$. 310func (s *$.type|private$Lister) $.type|publicPlural$(namespace string) $.type|public$NamespaceLister { 311 return $.type|private$NamespaceLister{indexer: s.indexer, namespace: namespace} 312} 313` 314 315var typeLister_NonNamespacedGet = ` 316// Get retrieves the $.type|public$ from the index for a given name. 317func (s *$.type|private$Lister) Get(name string) (*$.type|raw$, error) { 318 obj, exists, err := s.indexer.GetByKey(name) 319 if err != nil { 320 return nil, err 321 } 322 if !exists { 323 return nil, errors.NewNotFound($.Resource|raw$("$.type|lowercaseSingular$"), name) 324 } 325 return obj.(*$.type|raw$), nil 326} 327` 328 329var namespaceListerInterface = ` 330// $.type|public$NamespaceLister helps list and get $.type|publicPlural$. 331type $.type|public$NamespaceLister interface { 332 // List lists all $.type|publicPlural$ in the indexer for a given namespace. 333 List(selector labels.Selector) (ret []*$.type|raw$, err error) 334 // Get retrieves the $.type|public$ from the indexer for a given namespace and name. 335 Get(name string) (*$.type|raw$, error) 336 $.type|public$NamespaceListerExpansion 337} 338` 339 340var namespaceListerStruct = ` 341// $.type|private$NamespaceLister implements the $.type|public$NamespaceLister 342// interface. 343type $.type|private$NamespaceLister struct { 344 indexer cache.Indexer 345 namespace string 346} 347` 348 349var namespaceLister_List = ` 350// List lists all $.type|publicPlural$ in the indexer for a given namespace. 351func (s $.type|private$NamespaceLister) List(selector labels.Selector) (ret []*$.type|raw$, err error) { 352 err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { 353 ret = append(ret, m.(*$.type|raw$)) 354 }) 355 return ret, err 356} 357` 358 359var namespaceLister_Get = ` 360// Get retrieves the $.type|public$ from the indexer for a given namespace and name. 361func (s $.type|private$NamespaceLister) Get(name string) (*$.type|raw$, error) { 362 obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) 363 if err != nil { 364 return nil, err 365 } 366 if !exists { 367 return nil, errors.NewNotFound($.Resource|raw$("$.type|lowercaseSingular$"), name) 368 } 369 return obj.(*$.type|raw$), nil 370} 371` 372