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 "io" 21 "sort" 22 "strings" 23 24 clientgentypes "k8s.io/code-generator/cmd/client-gen/types" 25 codegennamer "k8s.io/code-generator/pkg/namer" 26 "k8s.io/gengo/generator" 27 "k8s.io/gengo/namer" 28 "k8s.io/gengo/types" 29) 30 31// genericGenerator generates the generic informer. 32type genericGenerator struct { 33 generator.DefaultGen 34 outputPackage string 35 imports namer.ImportTracker 36 groupVersions map[string]clientgentypes.GroupVersions 37 groupGoNames map[string]string 38 pluralExceptions map[string]string 39 typesForGroupVersion map[clientgentypes.GroupVersion][]*types.Type 40 filtered bool 41} 42 43var _ generator.Generator = &genericGenerator{} 44 45func (g *genericGenerator) Filter(c *generator.Context, t *types.Type) bool { 46 if !g.filtered { 47 g.filtered = true 48 return true 49 } 50 return false 51} 52 53func (g *genericGenerator) Namers(c *generator.Context) namer.NameSystems { 54 return namer.NameSystems{ 55 "raw": namer.NewRawNamer(g.outputPackage, g.imports), 56 "allLowercasePlural": namer.NewAllLowercasePluralNamer(g.pluralExceptions), 57 "publicPlural": namer.NewPublicPluralNamer(g.pluralExceptions), 58 "resource": codegennamer.NewTagOverrideNamer("resourceName", namer.NewAllLowercasePluralNamer(g.pluralExceptions)), 59 } 60} 61 62func (g *genericGenerator) Imports(c *generator.Context) (imports []string) { 63 imports = append(imports, g.imports.ImportLines()...) 64 imports = append(imports, "fmt") 65 return 66} 67 68type group struct { 69 GroupGoName string 70 Name string 71 Versions []*version 72} 73 74type groupSort []group 75 76func (g groupSort) Len() int { return len(g) } 77func (g groupSort) Less(i, j int) bool { 78 return strings.ToLower(g[i].Name) < strings.ToLower(g[j].Name) 79} 80func (g groupSort) Swap(i, j int) { g[i], g[j] = g[j], g[i] } 81 82type version struct { 83 Name string 84 GoName string 85 Resources []*types.Type 86} 87 88type versionSort []*version 89 90func (v versionSort) Len() int { return len(v) } 91func (v versionSort) Less(i, j int) bool { 92 return strings.ToLower(v[i].Name) < strings.ToLower(v[j].Name) 93} 94func (v versionSort) Swap(i, j int) { v[i], v[j] = v[j], v[i] } 95 96func (g *genericGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { 97 sw := generator.NewSnippetWriter(w, c, "{{", "}}") 98 99 groups := []group{} 100 schemeGVs := make(map[*version]*types.Type) 101 102 orderer := namer.Orderer{Namer: namer.NewPrivateNamer(0)} 103 for groupPackageName, groupVersions := range g.groupVersions { 104 group := group{ 105 GroupGoName: g.groupGoNames[groupPackageName], 106 Name: groupVersions.Group.NonEmpty(), 107 Versions: []*version{}, 108 } 109 for _, v := range groupVersions.Versions { 110 gv := clientgentypes.GroupVersion{Group: groupVersions.Group, Version: v.Version} 111 version := &version{ 112 Name: v.Version.NonEmpty(), 113 GoName: namer.IC(v.Version.NonEmpty()), 114 Resources: orderer.OrderTypes(g.typesForGroupVersion[gv]), 115 } 116 func() { 117 schemeGVs[version] = c.Universe.Variable(types.Name{Package: g.typesForGroupVersion[gv][0].Name.Package, Name: "SchemeGroupVersion"}) 118 }() 119 group.Versions = append(group.Versions, version) 120 } 121 sort.Sort(versionSort(group.Versions)) 122 groups = append(groups, group) 123 } 124 sort.Sort(groupSort(groups)) 125 126 m := map[string]interface{}{ 127 "cacheGenericLister": c.Universe.Type(cacheGenericLister), 128 "cacheNewGenericLister": c.Universe.Function(cacheNewGenericLister), 129 "cacheSharedIndexInformer": c.Universe.Type(cacheSharedIndexInformer), 130 "groups": groups, 131 "schemeGVs": schemeGVs, 132 "schemaGroupResource": c.Universe.Type(schemaGroupResource), 133 "schemaGroupVersionResource": c.Universe.Type(schemaGroupVersionResource), 134 } 135 136 sw.Do(genericInformer, m) 137 sw.Do(forResource, m) 138 139 return sw.Error() 140} 141 142var genericInformer = ` 143// GenericInformer is type of SharedIndexInformer which will locate and delegate to other 144// sharedInformers based on type 145type GenericInformer interface { 146 Informer() {{.cacheSharedIndexInformer|raw}} 147 Lister() {{.cacheGenericLister|raw}} 148} 149 150type genericInformer struct { 151 informer {{.cacheSharedIndexInformer|raw}} 152 resource {{.schemaGroupResource|raw}} 153} 154 155// Informer returns the SharedIndexInformer. 156func (f *genericInformer) Informer() {{.cacheSharedIndexInformer|raw}} { 157 return f.informer 158} 159 160// Lister returns the GenericLister. 161func (f *genericInformer) Lister() {{.cacheGenericLister|raw}} { 162 return {{.cacheNewGenericLister|raw}}(f.Informer().GetIndexer(), f.resource) 163} 164` 165 166var forResource = ` 167// ForResource gives generic access to a shared informer of the matching type 168// TODO extend this to unknown resources with a client pool 169func (f *sharedInformerFactory) ForResource(resource {{.schemaGroupVersionResource|raw}}) (GenericInformer, error) { 170 switch resource { 171 {{range $group := .groups -}}{{$GroupGoName := .GroupGoName -}} 172 {{range $version := .Versions -}} 173 // Group={{$group.Name}}, Version={{.Name}} 174 {{range .Resources -}} 175 case {{index $.schemeGVs $version|raw}}.WithResource("{{.|resource}}"): 176 return &genericInformer{resource: resource.GroupResource(), informer: f.{{$GroupGoName}}().{{$version.GoName}}().{{.|publicPlural}}().Informer()}, nil 177 {{end}} 178 {{end}} 179 {{end -}} 180 } 181 182 return nil, fmt.Errorf("no informer found for %v", resource) 183} 184` 185