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	"strings"
23
24	"k8s.io/gengo/generator"
25	"k8s.io/gengo/namer"
26	"k8s.io/gengo/types"
27
28	"k8s.io/code-generator/cmd/client-gen/generators/util"
29	clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
30
31	"k8s.io/klog"
32)
33
34// informerGenerator produces a file of listers for a given GroupVersion and
35// type.
36type informerGenerator struct {
37	generator.DefaultGen
38	outputPackage             string
39	groupPkgName              string
40	groupVersion              clientgentypes.GroupVersion
41	groupGoName               string
42	typeToGenerate            *types.Type
43	imports                   namer.ImportTracker
44	clientSetPackage          string
45	listersPackage            string
46	internalInterfacesPackage string
47}
48
49var _ generator.Generator = &informerGenerator{}
50
51func (g *informerGenerator) Filter(c *generator.Context, t *types.Type) bool {
52	return t == g.typeToGenerate
53}
54
55func (g *informerGenerator) Namers(c *generator.Context) namer.NameSystems {
56	return namer.NameSystems{
57		"raw": namer.NewRawNamer(g.outputPackage, g.imports),
58	}
59}
60
61func (g *informerGenerator) Imports(c *generator.Context) (imports []string) {
62	imports = append(imports, g.imports.ImportLines()...)
63	return
64}
65
66func (g *informerGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
67	sw := generator.NewSnippetWriter(w, c, "$", "$")
68
69	klog.V(5).Infof("processing type %v", t)
70
71	listerPackage := fmt.Sprintf("%s/%s/%s", g.listersPackage, g.groupPkgName, strings.ToLower(g.groupVersion.Version.NonEmpty()))
72	clientSetInterface := c.Universe.Type(types.Name{Package: g.clientSetPackage, Name: "Interface"})
73	informerFor := "InformerFor"
74
75	tags, err := util.ParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
76	if err != nil {
77		return err
78	}
79
80	m := map[string]interface{}{
81		"apiScheme":                       c.Universe.Type(apiScheme),
82		"cacheIndexers":                   c.Universe.Type(cacheIndexers),
83		"cacheListWatch":                  c.Universe.Type(cacheListWatch),
84		"cacheMetaNamespaceIndexFunc":     c.Universe.Function(cacheMetaNamespaceIndexFunc),
85		"cacheNamespaceIndex":             c.Universe.Variable(cacheNamespaceIndex),
86		"cacheNewSharedIndexInformer":     c.Universe.Function(cacheNewSharedIndexInformer),
87		"cacheSharedIndexInformer":        c.Universe.Type(cacheSharedIndexInformer),
88		"clientSetInterface":              clientSetInterface,
89		"group":                           namer.IC(g.groupGoName),
90		"informerFor":                     informerFor,
91		"interfacesTweakListOptionsFunc":  c.Universe.Type(types.Name{Package: g.internalInterfacesPackage, Name: "TweakListOptionsFunc"}),
92		"interfacesSharedInformerFactory": c.Universe.Type(types.Name{Package: g.internalInterfacesPackage, Name: "SharedInformerFactory"}),
93		"listOptions":                     c.Universe.Type(listOptions),
94		"lister":                          c.Universe.Type(types.Name{Package: listerPackage, Name: t.Name.Name + "Lister"}),
95		"namespaceAll":                    c.Universe.Type(metav1NamespaceAll),
96		"namespaced":                      !tags.NonNamespaced,
97		"newLister":                       c.Universe.Function(types.Name{Package: listerPackage, Name: "New" + t.Name.Name + "Lister"}),
98		"runtimeObject":                   c.Universe.Type(runtimeObject),
99		"timeDuration":                    c.Universe.Type(timeDuration),
100		"type":                            t,
101		"v1ListOptions":                   c.Universe.Type(v1ListOptions),
102		"version":                         namer.IC(g.groupVersion.Version.String()),
103		"watchInterface":                  c.Universe.Type(watchInterface),
104	}
105
106	sw.Do(typeInformerInterface, m)
107	sw.Do(typeInformerStruct, m)
108	sw.Do(typeInformerPublicConstructor, m)
109	sw.Do(typeFilteredInformerPublicConstructor, m)
110	sw.Do(typeInformerConstructor, m)
111	sw.Do(typeInformerInformer, m)
112	sw.Do(typeInformerLister, m)
113
114	return sw.Error()
115}
116
117var typeInformerInterface = `
118// $.type|public$Informer provides access to a shared informer and lister for
119// $.type|publicPlural$.
120type $.type|public$Informer interface {
121	Informer() $.cacheSharedIndexInformer|raw$
122	Lister() $.lister|raw$
123}
124`
125
126var typeInformerStruct = `
127type $.type|private$Informer struct {
128	factory $.interfacesSharedInformerFactory|raw$
129	tweakListOptions $.interfacesTweakListOptionsFunc|raw$
130	$if .namespaced$namespace string$end$
131}
132`
133
134var typeInformerPublicConstructor = `
135// New$.type|public$Informer constructs a new informer for $.type|public$ type.
136// Always prefer using an informer factory to get a shared informer instead of getting an independent
137// one. This reduces memory footprint and number of connections to the server.
138func New$.type|public$Informer(client $.clientSetInterface|raw$$if .namespaced$, namespace string$end$, resyncPeriod $.timeDuration|raw$, indexers $.cacheIndexers|raw$) $.cacheSharedIndexInformer|raw$ {
139	return NewFiltered$.type|public$Informer(client$if .namespaced$, namespace$end$, resyncPeriod, indexers, nil)
140}
141`
142
143var typeFilteredInformerPublicConstructor = `
144// NewFiltered$.type|public$Informer constructs a new informer for $.type|public$ type.
145// Always prefer using an informer factory to get a shared informer instead of getting an independent
146// one. This reduces memory footprint and number of connections to the server.
147func NewFiltered$.type|public$Informer(client $.clientSetInterface|raw$$if .namespaced$, namespace string$end$, resyncPeriod $.timeDuration|raw$, indexers $.cacheIndexers|raw$, tweakListOptions $.interfacesTweakListOptionsFunc|raw$) $.cacheSharedIndexInformer|raw$ {
148	return $.cacheNewSharedIndexInformer|raw$(
149		&$.cacheListWatch|raw${
150			ListFunc: func(options $.v1ListOptions|raw$) ($.runtimeObject|raw$, error) {
151				if tweakListOptions != nil {
152					tweakListOptions(&options)
153				}
154				return client.$.group$$.version$().$.type|publicPlural$($if .namespaced$namespace$end$).List(options)
155			},
156			WatchFunc: func(options $.v1ListOptions|raw$) ($.watchInterface|raw$, error) {
157				if tweakListOptions != nil {
158					tweakListOptions(&options)
159				}
160				return client.$.group$$.version$().$.type|publicPlural$($if .namespaced$namespace$end$).Watch(options)
161			},
162		},
163		&$.type|raw${},
164		resyncPeriod,
165		indexers,
166	)
167}
168`
169
170var typeInformerConstructor = `
171func (f *$.type|private$Informer) defaultInformer(client $.clientSetInterface|raw$, resyncPeriod $.timeDuration|raw$) $.cacheSharedIndexInformer|raw$ {
172	return NewFiltered$.type|public$Informer(client$if .namespaced$, f.namespace$end$, resyncPeriod, $.cacheIndexers|raw${$.cacheNamespaceIndex|raw$: $.cacheMetaNamespaceIndexFunc|raw$}, f.tweakListOptions)
173}
174`
175
176var typeInformerInformer = `
177func (f *$.type|private$Informer) Informer() $.cacheSharedIndexInformer|raw$ {
178	return f.factory.$.informerFor$(&$.type|raw${}, f.defaultInformer)
179}
180`
181
182var typeInformerLister = `
183func (f *$.type|private$Informer) Lister() $.lister|raw$ {
184	return $.newLister|raw$(f.Informer().GetIndexer())
185}
186`
187