Integrate the gopath hacks to allow for codegen anywhere. (#1161)

This commit is contained in:
Scott Nichols 2020-12-03 02:45:08 -08:00 committed by GitHub
parent 45162fb5d8
commit 2d44ce89e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 3673 additions and 5 deletions

View File

@ -18,6 +18,7 @@ package tools
// This package imports things required by this repository, to force `go mod` to see them as dependencies // This package imports things required by this repository, to force `go mod` to see them as dependencies
import ( import (
_ "knative.dev/hack" _ "knative.dev/hack"
_ "knative.dev/pkg/hack"
_ "k8s.io/code-generator" _ "k8s.io/code-generator"
_ "k8s.io/code-generator/cmd/deepcopy-gen" _ "k8s.io/code-generator/cmd/deepcopy-gen"

View File

@ -18,18 +18,26 @@ set -o errexit
set -o nounset set -o nounset
set -o pipefail set -o pipefail
SCRIPT_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. source $(dirname $0)/../vendor/knative.dev/hack/codegen-library.sh
CODEGEN_PKG=${CODEGEN_PKG:-$(cd "${SCRIPT_ROOT}"; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo ../code-generator)}
# If we run with -mod=vendor here, then generate-groups.sh looks for vendor files in the wrong place.
export GOFLAGS=-mod=
echo "=== Update Codegen for $MODULE_NAME"
group "Kubernetes Codegen"
# generate the code with: # generate the code with:
# --output-base because this script should also be able to run inside the vendor dir of # --output-base because this script should also be able to run inside the vendor dir of
# k8s.io/kubernetes. The output-base is needed for the generators to output into the vendor dir # k8s.io/kubernetes. The output-base is needed for the generators to output into the vendor dir
# instead of the $GOPATH directly. For normal projects this can be dropped. # instead of the $GOPATH directly. For normal projects this can be dropped.
bash "${CODEGEN_PKG}"/generate-groups.sh "deepcopy" \ "${CODEGEN_PKG}"/generate-groups.sh "deepcopy" \
knative.dev/client/pkg/apis/client/v1alpha1/generated knative.dev/client/pkg/apis \ knative.dev/client/pkg/apis/client/v1alpha1/generated knative.dev/client/pkg/apis \
client:v1alpha1 \ client:v1alpha1 \
--output-base "$(dirname "${BASH_SOURCE[0]}")/../../.." \ --output-base "$(dirname "${BASH_SOURCE[0]}")/../../.." \
--go-header-file "${SCRIPT_ROOT}"/hack/boilerplate.go.txt --go-header-file "${REPO_ROOT_DIR}"/hack/boilerplate.go.txt
group "Update deps post-codegen"
# Make sure our dependencies are up-to-date # Make sure our dependencies are up-to-date
${SCRIPT_ROOT}/hack/update-deps.sh ${REPO_ROOT_DIR}/hack/update-deps.sh

View File

@ -0,0 +1,65 @@
/*
Copyright 2019 The Knative Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package args
import (
"fmt"
"github.com/spf13/pflag"
"k8s.io/gengo/args"
)
// CustomArgs is used by the gengo framework to pass args specific to this generator.
type CustomArgs struct {
VersionedClientSetPackage string
ExternalVersionsInformersPackage string
ListersPackage string
ForceKinds string
}
// NewDefaults returns default arguments for the generator.
func NewDefaults() (*args.GeneratorArgs, *CustomArgs) {
genericArgs := args.Default().WithoutDefaultFlagParsing()
customArgs := &CustomArgs{}
genericArgs.CustomArgs = customArgs
return genericArgs, customArgs
}
// AddFlags add the generator flags to the flag set.
func (ca *CustomArgs) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&ca.VersionedClientSetPackage, "versioned-clientset-package", ca.VersionedClientSetPackage, "the full package name for the versioned injection clientset to use")
fs.StringVar(&ca.ExternalVersionsInformersPackage, "external-versions-informers-package", ca.ExternalVersionsInformersPackage, "the full package name for the external versions injection informer to use")
fs.StringVar(&ca.ListersPackage, "listers-package", ca.ListersPackage, "the full package name for client listers to use")
fs.StringVar(&ca.ForceKinds, "force-genreconciler-kinds", ca.ForceKinds, `force kinds will override the genreconciler tag setting for the given set of kinds, comma separated: "Foo,Bar,Baz"`)
}
// Validate checks the given arguments.
func Validate(genericArgs *args.GeneratorArgs) error {
customArgs := genericArgs.CustomArgs.(*CustomArgs)
if len(genericArgs.OutputPackagePath) == 0 {
return fmt.Errorf("output package cannot be empty")
}
if len(customArgs.VersionedClientSetPackage) == 0 {
return fmt.Errorf("versioned clientset package cannot be empty")
}
if len(customArgs.ExternalVersionsInformersPackage) == 0 {
return fmt.Errorf("external versions informers package cannot be empty")
}
return nil
}

View File

@ -0,0 +1,111 @@
/*
Copyright 2019 The Knative Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package generators
import (
"io"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/klog"
)
// clientGenerator produces a file of listers for a given GroupVersion and
// type.
type clientGenerator struct {
generator.DefaultGen
outputPackage string
imports namer.ImportTracker
clientSetPackage string
filtered bool
}
var _ generator.Generator = (*clientGenerator)(nil)
func (g *clientGenerator) Filter(c *generator.Context, t *types.Type) bool {
// We generate a single client, so return true once.
if !g.filtered {
g.filtered = true
return true
}
return false
}
func (g *clientGenerator) Namers(c *generator.Context) namer.NameSystems {
return namer.NameSystems{
"raw": namer.NewRawNamer(g.outputPackage, g.imports),
}
}
func (g *clientGenerator) Imports(c *generator.Context) (imports []string) {
imports = append(imports, g.imports.ImportLines()...)
return
}
func (g *clientGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
sw := generator.NewSnippetWriter(w, c, "{{", "}}")
klog.V(5).Info("processing type ", t)
m := map[string]interface{}{
"clientSetNewForConfigOrDie": c.Universe.Function(types.Name{Package: g.clientSetPackage, Name: "NewForConfigOrDie"}),
"clientSetInterface": c.Universe.Type(types.Name{Package: g.clientSetPackage, Name: "Interface"}),
"injectionRegisterClient": c.Universe.Function(types.Name{Package: "knative.dev/pkg/injection", Name: "Default.RegisterClient"}),
"restConfig": c.Universe.Type(types.Name{Package: "k8s.io/client-go/rest", Name: "Config"}),
"loggingFromContext": c.Universe.Function(types.Name{
Package: "knative.dev/pkg/logging",
Name: "FromContext",
}),
"contextContext": c.Universe.Type(types.Name{
Package: "context",
Name: "Context",
}),
}
sw.Do(injectionClient, m)
return sw.Error()
}
var injectionClient = `
func init() {
{{.injectionRegisterClient|raw}}(withClient)
}
// Key is used as the key for associating information with a context.Context.
type Key struct{}
func withClient(ctx {{.contextContext|raw}}, cfg *{{.restConfig|raw}}) context.Context {
return context.WithValue(ctx, Key{}, {{.clientSetNewForConfigOrDie|raw}}(cfg))
}
// Get extracts the {{.clientSetInterface|raw}} client from the context.
func Get(ctx {{.contextContext|raw}}) {{.clientSetInterface|raw}} {
untyped := ctx.Value(Key{})
if untyped == nil {
if injection.GetConfig(ctx) == nil {
{{.loggingFromContext|raw}}(ctx).Panic(
"Unable to fetch {{.clientSetInterface}} from context. This context is not the application context (which is typically given to constructors via sharedmain).")
} else {
{{.loggingFromContext|raw}}(ctx).Panic(
"Unable to fetch {{.clientSetInterface}} from context.")
}
}
return untyped.({{.clientSetInterface|raw}})
}
`

View File

@ -0,0 +1,75 @@
/*
Copyright 2020 The Knative Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package generators
import "strings"
// Adapted from the k8s.io comment parser https://github.com/kubernetes/gengo/blob/master/types/comments.go
// ExtractCommentTags parses comments for lines of the form:
//
// 'marker' + ':' "key=value,key2=value2".
//
// Values are optional; empty map is the default. A tag can be specified more than
// one time and all values are returned. If the resulting map has an entry for
// a key, the value (a slice) is guaranteed to have at least 1 element.
//
// Example: if you pass "+" for 'marker', and the following lines are in
// the comments:
// +foo:key=value1,key2=value2
// +bar
//
// Then this function will return:
// map[string]map[string]string{"foo":{"key":value1","key2":"value2"}, "bar": nil}
//
// Users are not expected to repeat values.
func ExtractCommentTags(marker string, lines []string) map[string]map[string]string {
out := map[string]map[string]string{}
for _, line := range lines {
line = strings.TrimSpace(line)
if len(line) == 0 || !strings.HasPrefix(line, marker) {
continue
}
options := strings.SplitN(line[len(marker):], ":", 2)
if len(options) == 2 {
vals := strings.Split(options[1], ",")
opts := out[options[0]]
if opts == nil {
opts = make(map[string]string, len(vals))
}
for _, pair := range vals {
if kv := strings.SplitN(pair, "=", 2); len(kv) == 2 {
opts[kv[0]] = kv[1]
} else if kv[0] != "" {
opts[kv[0]] = ""
}
}
if len(opts) == 0 {
out[options[0]] = nil
} else {
out[options[0]] = opts
}
} else if len(options) == 1 && options[0] != "" {
if _, has := out[options[0]]; !has {
out[options[0]] = nil
}
}
}
return out
}

View File

@ -0,0 +1,134 @@
/*
Copyright 2019 The Knative Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package generators
import (
"io"
clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/klog"
)
// duckGenerator produces logic to register a duck.InformerFactory for a particular
// type onto context.
type duckGenerator struct {
generator.DefaultGen
outputPackage string
groupVersion clientgentypes.GroupVersion
groupGoName string
typeToGenerate *types.Type
imports namer.ImportTracker
}
var _ generator.Generator = (*duckGenerator)(nil)
func (g *duckGenerator) Filter(c *generator.Context, t *types.Type) bool {
// Only process the type for this informer generator.
return t == g.typeToGenerate
}
func (g *duckGenerator) Namers(c *generator.Context) namer.NameSystems {
publicPluralNamer := &ExceptionNamer{
Exceptions: map[string]string{
// these exceptions are used to deconflict the generated code
// you can put your fully qualified package like
// to generate a name that doesn't conflict with your group.
// "k8s.io/apis/events/v1beta1.Event": "EventResource"
},
KeyFunc: func(t *types.Type) string {
return t.Name.Package + "." + t.Name.Name
},
Delegate: namer.NewPublicPluralNamer(map[string]string{
"Endpoints": "Endpoints",
}),
}
return namer.NameSystems{
"raw": namer.NewRawNamer(g.outputPackage, g.imports),
"publicPlural": publicPluralNamer,
}
}
func (g *duckGenerator) Imports(c *generator.Context) (imports []string) {
imports = append(imports, g.imports.ImportLines()...)
return
}
func (g *duckGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
sw := generator.NewSnippetWriter(w, c, "{{", "}}")
klog.V(5).Info("processing type ", t)
m := map[string]interface{}{
"group": namer.IC(g.groupGoName),
"type": t,
"version": namer.IC(g.groupVersion.Version.String()),
"injectionRegisterDuck": c.Universe.Type(types.Name{Package: "knative.dev/pkg/injection", Name: "Default.RegisterDuck"}),
"getResyncPeriod": c.Universe.Type(types.Name{Package: "knative.dev/pkg/controller", Name: "GetResyncPeriod"}),
"dynamicGet": c.Universe.Type(types.Name{Package: "knative.dev/pkg/injection/clients/dynamicclient", Name: "Get"}),
"duckTypedInformerFactory": c.Universe.Type(types.Name{Package: "knative.dev/pkg/apis/duck", Name: "TypedInformerFactory"}),
"duckCachedInformerFactory": c.Universe.Type(types.Name{Package: "knative.dev/pkg/apis/duck", Name: "CachedInformerFactory"}),
"duckInformerFactory": c.Universe.Type(types.Name{Package: "knative.dev/pkg/apis/duck", Name: "InformerFactory"}),
"loggingFromContext": c.Universe.Function(types.Name{
Package: "knative.dev/pkg/logging",
Name: "FromContext",
}),
"contextContext": c.Universe.Type(types.Name{
Package: "context",
Name: "Context",
}),
}
sw.Do(duckFactory, m)
return sw.Error()
}
var duckFactory = `
func init() {
{{.injectionRegisterDuck|raw}}(WithDuck)
}
// Key is used for associating the Informer inside the context.Context.
type Key struct{}
func WithDuck(ctx {{.contextContext|raw}}) {{.contextContext|raw}} {
dc := {{.dynamicGet|raw}}(ctx)
dif := &{{.duckCachedInformerFactory|raw}}{
Delegate: &{{.duckTypedInformerFactory|raw}}{
Client: dc,
Type: (&{{.type|raw}}{}).GetFullType(),
ResyncPeriod: {{.getResyncPeriod|raw}}(ctx),
StopChannel: ctx.Done(),
},
}
return context.WithValue(ctx, Key{}, dif)
}
// Get extracts the typed informer from the context.
func Get(ctx {{.contextContext|raw}}) {{.duckInformerFactory|raw}} {
untyped := ctx.Value(Key{})
if untyped == nil {
{{.loggingFromContext|raw}}(ctx).Panic(
"Unable to fetch {{.duckInformerFactory}} from context.")
}
return untyped.({{.duckInformerFactory|raw}})
}
`

View File

@ -0,0 +1,118 @@
/*
Copyright 2019 The Knative Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package generators
import (
"io"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/klog"
)
// factoryTestGenerator produces a file of factory injection of a given type.
type factoryGenerator struct {
generator.DefaultGen
outputPackage string
imports namer.ImportTracker
cachingClientSetPackage string
sharedInformerFactoryPackage string
filtered bool
}
var _ generator.Generator = (*factoryGenerator)(nil)
func (g *factoryGenerator) Filter(c *generator.Context, t *types.Type) bool {
// We generate a single factory, so return true once.
if !g.filtered {
g.filtered = true
return true
}
return false
}
func (g *factoryGenerator) Namers(c *generator.Context) namer.NameSystems {
return namer.NameSystems{
"raw": namer.NewRawNamer(g.outputPackage, g.imports),
}
}
func (g *factoryGenerator) Imports(c *generator.Context) (imports []string) {
imports = append(imports, g.imports.ImportLines()...)
return
}
func (g *factoryGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
sw := generator.NewSnippetWriter(w, c, "{{", "}}")
klog.V(5).Info("processing type ", t)
m := map[string]interface{}{
"cachingClientGet": c.Universe.Type(types.Name{Package: g.cachingClientSetPackage, Name: "Get"}),
"informersNewSharedInformerFactoryWithOptions": c.Universe.Function(types.Name{Package: g.sharedInformerFactoryPackage, Name: "NewSharedInformerFactoryWithOptions"}),
"informersSharedInformerOption": c.Universe.Function(types.Name{Package: g.sharedInformerFactoryPackage, Name: "SharedInformerOption"}),
"informersWithNamespace": c.Universe.Function(types.Name{Package: g.sharedInformerFactoryPackage, Name: "WithNamespace"}),
"informersSharedInformerFactory": c.Universe.Function(types.Name{Package: g.sharedInformerFactoryPackage, Name: "SharedInformerFactory"}),
"injectionRegisterInformerFactory": c.Universe.Type(types.Name{Package: "knative.dev/pkg/injection", Name: "Default.RegisterInformerFactory"}),
"injectionHasNamespace": c.Universe.Type(types.Name{Package: "knative.dev/pkg/injection", Name: "HasNamespaceScope"}),
"injectionGetNamespace": c.Universe.Type(types.Name{Package: "knative.dev/pkg/injection", Name: "GetNamespaceScope"}),
"controllerGetResyncPeriod": c.Universe.Type(types.Name{Package: "knative.dev/pkg/controller", Name: "GetResyncPeriod"}),
"loggingFromContext": c.Universe.Function(types.Name{
Package: "knative.dev/pkg/logging",
Name: "FromContext",
}),
"contextContext": c.Universe.Type(types.Name{
Package: "context",
Name: "Context",
}),
}
sw.Do(injectionFactory, m)
return sw.Error()
}
var injectionFactory = `
func init() {
{{.injectionRegisterInformerFactory|raw}}(withInformerFactory)
}
// Key is used as the key for associating information with a context.Context.
type Key struct{}
func withInformerFactory(ctx {{.contextContext|raw}}) {{.contextContext|raw}} {
c := {{.cachingClientGet|raw}}(ctx)
opts := make([]{{.informersSharedInformerOption|raw}}, 0, 1)
if {{.injectionHasNamespace|raw}}(ctx) {
opts = append(opts, {{.informersWithNamespace|raw}}({{.injectionGetNamespace|raw}}(ctx)))
}
return context.WithValue(ctx, Key{},
{{.informersNewSharedInformerFactoryWithOptions|raw}}(c, {{.controllerGetResyncPeriod|raw}}(ctx), opts...))
}
// Get extracts the InformerFactory from the context.
func Get(ctx {{.contextContext|raw}}) {{.informersSharedInformerFactory|raw}} {
untyped := ctx.Value(Key{})
if untyped == nil {
{{.loggingFromContext|raw}}(ctx).Panic(
"Unable to fetch {{.informersSharedInformerFactory}} from context.")
}
return untyped.({{.informersSharedInformerFactory|raw}})
}
`

View File

@ -0,0 +1,115 @@
/*
Copyright 2019 The Knative Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package generators
import (
"io"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/klog"
)
// fakeClientGenerator produces a file of listers for a given GroupVersion and
// type.
type fakeClientGenerator struct {
generator.DefaultGen
outputPackage string
imports namer.ImportTracker
filtered bool
fakeClientPkg string
clientInjectionPkg string
}
var _ generator.Generator = (*fakeClientGenerator)(nil)
func (g *fakeClientGenerator) Filter(c *generator.Context, t *types.Type) bool {
// We generate a single client, so return true once.
if !g.filtered {
g.filtered = true
return true
}
return false
}
func (g *fakeClientGenerator) Namers(c *generator.Context) namer.NameSystems {
return namer.NameSystems{
"raw": namer.NewRawNamer(g.outputPackage, g.imports),
}
}
func (g *fakeClientGenerator) Imports(c *generator.Context) (imports []string) {
imports = append(imports, g.imports.ImportLines()...)
return
}
func (g *fakeClientGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
sw := generator.NewSnippetWriter(w, c, "{{", "}}")
klog.V(5).Info("processing type ", t)
m := map[string]interface{}{
"clientKey": c.Universe.Type(types.Name{Package: g.clientInjectionPkg, Name: "Key"}),
"fakeClient": c.Universe.Type(types.Name{Package: g.fakeClientPkg, Name: "Clientset"}),
"injectionRegisterClient": c.Universe.Function(types.Name{
Package: "knative.dev/pkg/injection",
Name: "Fake.RegisterClient",
}),
"loggingFromContext": c.Universe.Function(types.Name{
Package: "knative.dev/pkg/logging",
Name: "FromContext",
}),
"contextContext": c.Universe.Type(types.Name{
Package: "context",
Name: "Context",
}),
"restConfig": c.Universe.Type(types.Name{Package: "k8s.io/client-go/rest", Name: "Config"}),
"runtimeObject": c.Universe.Type(types.Name{Package: "k8s.io/apimachinery/pkg/runtime", Name: "Object"}),
}
sw.Do(injectionFakeClient, m)
return sw.Error()
}
var injectionFakeClient = `
func init() {
{{.injectionRegisterClient|raw}}(withClient)
}
func withClient(ctx {{.contextContext|raw}}, cfg *{{.restConfig|raw}}) {{.contextContext|raw}} {
ctx, _ = With(ctx)
return ctx
}
func With(ctx {{.contextContext|raw}}, objects ...{{.runtimeObject|raw}}) ({{.contextContext|raw}}, *{{.fakeClient|raw}}) {
cs := fake.NewSimpleClientset(objects...)
return context.WithValue(ctx, {{.clientKey|raw}}{}, cs), cs
}
// Get extracts the Kubernetes client from the context.
func Get(ctx {{.contextContext|raw}}) *{{.fakeClient|raw}} {
untyped := ctx.Value({{.clientKey|raw}}{})
if untyped == nil {
{{.loggingFromContext|raw}}(ctx).Panic(
"Unable to fetch {{.fakeClient}} from context.")
}
return untyped.(*fake.Clientset)
}
`

View File

@ -0,0 +1,104 @@
/*
Copyright 2019 The Knative Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package generators
import (
"io"
clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/klog"
)
// fakeDuckGenerator produces a file of listers for a given GroupVersion and
// type.
type fakeDuckGenerator struct {
generator.DefaultGen
outputPackage string
imports namer.ImportTracker
typeToGenerate *types.Type
groupVersion clientgentypes.GroupVersion
groupGoName string
duckInjectionPkg string
}
var _ generator.Generator = (*fakeDuckGenerator)(nil)
func (g *fakeDuckGenerator) Filter(c *generator.Context, t *types.Type) bool {
// Only process the type for this duck generator.
return t == g.typeToGenerate
}
func (g *fakeDuckGenerator) Namers(c *generator.Context) namer.NameSystems {
publicPluralNamer := &ExceptionNamer{
Exceptions: map[string]string{
// these exceptions are used to deconflict the generated code
// you can put your fully qualified package like
// to generate a name that doesn't conflict with your group.
// "k8s.io/apis/events/v1beta1.Event": "EventResource"
},
KeyFunc: func(t *types.Type) string {
return t.Name.Package + "." + t.Name.Name
},
Delegate: namer.NewPublicPluralNamer(map[string]string{
"Endpoints": "Endpoints",
}),
}
return namer.NameSystems{
"raw": namer.NewRawNamer(g.outputPackage, g.imports),
"publicPlural": publicPluralNamer,
}
}
func (g *fakeDuckGenerator) Imports(c *generator.Context) (imports []string) {
imports = append(imports, g.imports.ImportLines()...)
return
}
func (g *fakeDuckGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
sw := generator.NewSnippetWriter(w, c, "{{", "}}")
klog.V(5).Info("processing type ", t)
m := map[string]interface{}{
"withDuck": c.Universe.Type(types.Name{Package: g.duckInjectionPkg, Name: "WithDuck"}),
"duckGet": c.Universe.Function(types.Name{Package: g.duckInjectionPkg, Name: "Get"}),
"group": namer.IC(g.groupGoName),
"type": t,
"version": namer.IC(g.groupVersion.Version.String()),
"injectionRegisterDuck": c.Universe.Function(types.Name{
Package: "knative.dev/pkg/injection",
Name: "Fake.RegisterDuck",
}),
}
sw.Do(injectionFakeDuck, m)
return sw.Error()
}
var injectionFakeDuck = `
var Get = {{.duckGet|raw}}
func init() {
{{.injectionRegisterDuck|raw}}({{.withDuck|raw}})
}
`

View File

@ -0,0 +1,109 @@
/*
Copyright 2019 The Knative Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package generators
import (
"io"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/klog"
)
// fakeFactoryGenerator produces a file of listers for a given GroupVersion and
// type.
type fakeFactoryGenerator struct {
generator.DefaultGen
outputPackage string
imports namer.ImportTracker
filtered bool
factoryInjectionPkg string
fakeClientInjectionPkg string
sharedInformerFactoryPackage string
}
var _ generator.Generator = (*fakeFactoryGenerator)(nil)
func (g *fakeFactoryGenerator) Filter(c *generator.Context, t *types.Type) bool {
// We generate a single factory, so return true once.
if !g.filtered {
g.filtered = true
return true
}
return false
}
func (g *fakeFactoryGenerator) Namers(c *generator.Context) namer.NameSystems {
return namer.NameSystems{
"raw": namer.NewRawNamer(g.outputPackage, g.imports),
}
}
func (g *fakeFactoryGenerator) Imports(c *generator.Context) (imports []string) {
imports = append(imports, g.imports.ImportLines()...)
return
}
func (g *fakeFactoryGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
sw := generator.NewSnippetWriter(w, c, "{{", "}}")
klog.V(5).Info("processing type ", t)
m := map[string]interface{}{
"factoryKey": c.Universe.Type(types.Name{Package: g.factoryInjectionPkg, Name: "Key"}),
"factoryGet": c.Universe.Function(types.Name{Package: g.factoryInjectionPkg, Name: "Get"}),
"clientGet": c.Universe.Function(types.Name{Package: g.fakeClientInjectionPkg, Name: "Get"}),
"informersNewSharedInformerFactoryWithOptions": c.Universe.Function(types.Name{Package: g.sharedInformerFactoryPackage, Name: "NewSharedInformerFactoryWithOptions"}),
"informersSharedInformerOption": c.Universe.Function(types.Name{Package: g.sharedInformerFactoryPackage, Name: "SharedInformerOption"}),
"informersWithNamespace": c.Universe.Function(types.Name{Package: g.sharedInformerFactoryPackage, Name: "WithNamespace"}),
"injectionRegisterInformerFactory": c.Universe.Function(types.Name{
Package: "knative.dev/pkg/injection",
Name: "Fake.RegisterInformerFactory",
}),
"injectionHasNamespace": c.Universe.Type(types.Name{Package: "knative.dev/pkg/injection", Name: "HasNamespaceScope"}),
"injectionGetNamespace": c.Universe.Type(types.Name{Package: "knative.dev/pkg/injection", Name: "GetNamespaceScope"}),
"controllerGetResyncPeriod": c.Universe.Type(types.Name{Package: "knative.dev/pkg/controller", Name: "GetResyncPeriod"}),
"contextContext": c.Universe.Type(types.Name{
Package: "context",
Name: "Context",
}),
}
sw.Do(injectionFakeInformerFactory, m)
return sw.Error()
}
var injectionFakeInformerFactory = `
var Get = {{.factoryGet|raw}}
func init() {
{{.injectionRegisterInformerFactory|raw}}(withInformerFactory)
}
func withInformerFactory(ctx {{.contextContext|raw}}) {{.contextContext|raw}} {
c := {{.clientGet|raw}}(ctx)
opts := make([]{{.informersSharedInformerOption|raw}}, 0, 1)
if {{.injectionHasNamespace|raw}}(ctx) {
opts = append(opts, {{.informersWithNamespace|raw}}({{.injectionGetNamespace|raw}}(ctx)))
}
return context.WithValue(ctx, {{.factoryKey|raw}}{},
{{.informersNewSharedInformerFactoryWithOptions|raw}}(c, {{.controllerGetResyncPeriod|raw}}(ctx), opts...))
}
`

View File

@ -0,0 +1,117 @@
/*
Copyright 2019 The Knative Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package generators
import (
"io"
clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/klog"
)
// fakeInformerGenerator produces a file of listers for a given GroupVersion and
// type.
type fakeInformerGenerator struct {
generator.DefaultGen
outputPackage string
imports namer.ImportTracker
typeToGenerate *types.Type
groupVersion clientgentypes.GroupVersion
groupGoName string
informerInjectionPkg string
fakeFactoryInjectionPkg string
}
var _ generator.Generator = (*fakeInformerGenerator)(nil)
func (g *fakeInformerGenerator) Filter(c *generator.Context, t *types.Type) bool {
// Only process the type for this informer generator.
return t == g.typeToGenerate
}
func (g *fakeInformerGenerator) Namers(c *generator.Context) namer.NameSystems {
publicPluralNamer := &ExceptionNamer{
Exceptions: map[string]string{
// these exceptions are used to deconflict the generated code
// you can put your fully qualified package like
// to generate a name that doesn't conflict with your group.
// "k8s.io/apis/events/v1beta1.Event": "EventResource"
},
KeyFunc: func(t *types.Type) string {
return t.Name.Package + "." + t.Name.Name
},
Delegate: namer.NewPublicPluralNamer(map[string]string{
"Endpoints": "Endpoints",
}),
}
return namer.NameSystems{
"raw": namer.NewRawNamer(g.outputPackage, g.imports),
"publicPlural": publicPluralNamer,
}
}
func (g *fakeInformerGenerator) Imports(c *generator.Context) (imports []string) {
imports = append(imports, g.imports.ImportLines()...)
return
}
func (g *fakeInformerGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
sw := generator.NewSnippetWriter(w, c, "{{", "}}")
klog.V(5).Info("processing type ", t)
m := map[string]interface{}{
"informerKey": c.Universe.Type(types.Name{Package: g.informerInjectionPkg, Name: "Key"}),
"informerGet": c.Universe.Function(types.Name{Package: g.informerInjectionPkg, Name: "Get"}),
"factoryGet": c.Universe.Function(types.Name{Package: g.fakeFactoryInjectionPkg, Name: "Get"}),
"group": namer.IC(g.groupGoName),
"type": t,
"version": namer.IC(g.groupVersion.Version.String()),
"controllerInformer": c.Universe.Type(types.Name{Package: "knative.dev/pkg/controller", Name: "Informer"}),
"injectionRegisterInformer": c.Universe.Function(types.Name{
Package: "knative.dev/pkg/injection",
Name: "Fake.RegisterInformer",
}),
"contextContext": c.Universe.Type(types.Name{
Package: "context",
Name: "Context",
}),
}
sw.Do(injectionFakeInformer, m)
return sw.Error()
}
var injectionFakeInformer = `
var Get = {{.informerGet|raw}}
func init() {
{{.injectionRegisterInformer|raw}}(withInformer)
}
func withInformer(ctx {{.contextContext|raw}}) ({{.contextContext|raw}}, {{.controllerInformer|raw}}) {
f := {{.factoryGet|raw}}(ctx)
inf := f.{{.group}}().{{.version}}().{{.type|publicPlural}}()
return context.WithValue(ctx, {{.informerKey|raw}}{}, inf), inf.Informer()
}
`

View File

@ -0,0 +1,127 @@
/*
Copyright 2019 The Knative Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package generators
import (
"io"
clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/klog"
)
// injectionTestGenerator produces a file of listers for a given GroupVersion and
// type.
type injectionGenerator struct {
generator.DefaultGen
outputPackage string
groupVersion clientgentypes.GroupVersion
groupGoName string
typeToGenerate *types.Type
imports namer.ImportTracker
typedInformerPackage string
groupInformerFactoryPackage string
}
var _ generator.Generator = (*injectionGenerator)(nil)
func (g *injectionGenerator) Filter(c *generator.Context, t *types.Type) bool {
// Only process the type for this informer generator.
return t == g.typeToGenerate
}
func (g *injectionGenerator) Namers(c *generator.Context) namer.NameSystems {
publicPluralNamer := &ExceptionNamer{
Exceptions: map[string]string{
// these exceptions are used to deconflict the generated code
// you can put your fully qualified package like
// to generate a name that doesn't conflict with your group.
// "k8s.io/apis/events/v1beta1.Event": "EventResource"
},
KeyFunc: func(t *types.Type) string {
return t.Name.Package + "." + t.Name.Name
},
Delegate: namer.NewPublicPluralNamer(map[string]string{
"Endpoints": "Endpoints",
}),
}
return namer.NameSystems{
"raw": namer.NewRawNamer(g.outputPackage, g.imports),
"publicPlural": publicPluralNamer,
}
}
func (g *injectionGenerator) Imports(c *generator.Context) (imports []string) {
imports = append(imports, g.imports.ImportLines()...)
return
}
func (g *injectionGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
sw := generator.NewSnippetWriter(w, c, "{{", "}}")
klog.V(5).Info("processing type ", t)
m := map[string]interface{}{
"group": namer.IC(g.groupGoName),
"type": t,
"version": namer.IC(g.groupVersion.Version.String()),
"injectionRegisterInformer": c.Universe.Type(types.Name{Package: "knative.dev/pkg/injection", Name: "Default.RegisterInformer"}),
"controllerInformer": c.Universe.Type(types.Name{Package: "knative.dev/pkg/controller", Name: "Informer"}),
"informersTypedInformer": c.Universe.Type(types.Name{Package: g.typedInformerPackage, Name: t.Name.Name + "Informer"}),
"factoryGet": c.Universe.Type(types.Name{Package: g.groupInformerFactoryPackage, Name: "Get"}),
"loggingFromContext": c.Universe.Function(types.Name{
Package: "knative.dev/pkg/logging",
Name: "FromContext",
}),
"contextContext": c.Universe.Type(types.Name{
Package: "context",
Name: "Context",
}),
}
sw.Do(injectionInformer, m)
return sw.Error()
}
var injectionInformer = `
func init() {
{{.injectionRegisterInformer|raw}}(withInformer)
}
// Key is used for associating the Informer inside the context.Context.
type Key struct{}
func withInformer(ctx {{.contextContext|raw}}) ({{.contextContext|raw}}, {{.controllerInformer|raw}}) {
f := {{.factoryGet|raw}}(ctx)
inf := f.{{.group}}().{{.version}}().{{.type|publicPlural}}()
return context.WithValue(ctx, Key{}, inf), inf.Informer()
}
// Get extracts the typed informer from the context.
func Get(ctx {{.contextContext|raw}}) {{.informersTypedInformer|raw}} {
untyped := ctx.Value(Key{})
if untyped == nil {
{{.loggingFromContext|raw}}(ctx).Panic(
"Unable to fetch {{.informersTypedInformer}} from context.")
}
return untyped.({{.informersTypedInformer|raw}})
}
`

View File

@ -0,0 +1,101 @@
/*
Copyright 2019 The Knative Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package generators
import (
"strings"
codegennamer "k8s.io/code-generator/pkg/namer"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
)
// NameSystems returns the name system used by the generators in this package.
func NameSystems() namer.NameSystems {
pluralExceptions := map[string]string{
"Endpoints": "Endpoints",
}
publicPluralNamer := namer.NewPublicPluralNamer(pluralExceptions)
publicNamer := &ExceptionNamer{
Exceptions: map[string]string{},
KeyFunc: func(t *types.Type) string {
return t.Name.Package + "." + t.Name.Name
},
Delegate: namer.NewPublicNamer(0),
}
return namer.NameSystems{
"public": namer.NewPublicNamer(0),
"private": namer.NewPrivateNamer(0),
"raw": namer.NewRawNamer("", nil),
"publicPlural": publicPluralNamer,
"allLowercasePlural": namer.NewAllLowercasePluralNamer(pluralExceptions),
"lowercaseSingular": &lowercaseSingularNamer{},
"apiGroup": codegennamer.NewTagOverrideNamer("publicPlural", publicPluralNamer),
"versionedClientset": &versionedClientsetNamer{public: publicNamer},
}
}
// lowercaseSingularNamer implements Namer
type lowercaseSingularNamer struct{}
// Name returns t's name in all lowercase.
func (n *lowercaseSingularNamer) Name(t *types.Type) string {
return strings.ToLower(t.Name.Name)
}
type versionedClientsetNamer struct {
public *ExceptionNamer
}
func (r *versionedClientsetNamer) Name(t *types.Type) string {
// Turns type into a GroupVersion type string based on package.
parts := strings.Split(t.Name.Package, "/")
group := parts[len(parts)-2]
version := parts[len(parts)-1]
g := r.public.Name(&types.Type{Name: types.Name{Name: group, Package: t.Name.Package}})
v := r.public.Name(&types.Type{Name: types.Name{Name: version, Package: t.Name.Package}})
return g + v
}
// DefaultNameSystem returns the default name system for ordering the types to be
// processed by the generators in this package.
func DefaultNameSystem() string {
return "public"
}
// ExceptionNamer allows you specify exceptional cases with exact names. This allows you to have control
// for handling various conflicts, like group and resource names for instance.
type ExceptionNamer struct {
Exceptions map[string]string
KeyFunc func(*types.Type) string
Delegate namer.Namer
}
// Name provides the requested name for a type.
func (n *ExceptionNamer) Name(t *types.Type) string {
key := n.KeyFunc(t)
if exception, ok := n.Exceptions[key]; ok {
return exception
}
return n.Delegate.Name(t)
}

View File

@ -0,0 +1,635 @@
/*
Copyright 2019 The Knative Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package generators
import (
"path"
"path/filepath"
"strings"
"k8s.io/code-generator/cmd/client-gen/generators/util"
clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
"k8s.io/gengo/args"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/klog"
informergenargs "knative.dev/pkg/codegen/cmd/injection-gen/args"
)
// Packages makes the client package definition.
func Packages(context *generator.Context, arguments *args.GeneratorArgs) generator.Packages {
boilerplate, err := arguments.LoadGoBoilerplate()
if err != nil {
klog.Fatal("Failed loading boilerplate: ", err)
}
customArgs, ok := arguments.CustomArgs.(*informergenargs.CustomArgs)
if !ok {
klog.Fatalf("Wrong CustomArgs type: %T", arguments.CustomArgs)
}
versionPackagePath := filepath.Join(arguments.OutputPackagePath)
var packageList generator.Packages
groupVersions := make(map[string]clientgentypes.GroupVersions)
groupGoNames := make(map[string]string)
for _, inputDir := range arguments.InputDirs {
p := context.Universe.Package(vendorless(inputDir))
var gv clientgentypes.GroupVersion
var targetGroupVersions map[string]clientgentypes.GroupVersions
parts := strings.Split(p.Path, "/")
gv.Group = clientgentypes.Group(parts[len(parts)-2])
gv.Version = clientgentypes.Version(parts[len(parts)-1])
targetGroupVersions = groupVersions
groupPackageName := gv.Group.NonEmpty()
gvPackage := path.Clean(p.Path)
// If there's a comment of the form "// +groupName=somegroup" or
// "// +groupName=somegroup.foo.bar.io", use the first field (somegroup) as the name of the
// group when generating.
if override := types.ExtractCommentTags("+", p.Comments)["groupName"]; override != nil {
gv.Group = clientgentypes.Group(override[0])
}
// If there's a comment of the form "// +groupGoName=SomeUniqueShortName", use that as
// the Go group identifier in CamelCase. It defaults
groupGoNames[groupPackageName] = namer.IC(strings.SplitN(gv.Group.NonEmpty(), ".", 2)[0])
if override := types.ExtractCommentTags("+", p.Comments)["groupGoName"]; override != nil {
groupGoNames[groupPackageName] = namer.IC(override[0])
}
// Generate the client and fake.
packageList = append(packageList, versionClientsPackages(versionPackagePath, boilerplate, customArgs)...)
// Generate the informer factory and fake.
packageList = append(packageList, versionFactoryPackages(versionPackagePath, boilerplate, customArgs)...)
var typesWithInformers []*types.Type
var duckTypes []*types.Type
var reconcilerTypes []*types.Type
for _, t := range p.Types {
tags := MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
if tags.NeedsInformerInjection() {
typesWithInformers = append(typesWithInformers, t)
}
if tags.NeedsDuckInjection() {
duckTypes = append(duckTypes, t)
}
if tags.NeedsReconciler(t, customArgs) {
reconcilerTypes = append(reconcilerTypes, t)
}
}
groupVersionsEntry, ok := targetGroupVersions[groupPackageName]
if !ok {
groupVersionsEntry = clientgentypes.GroupVersions{
PackageName: groupPackageName,
Group: gv.Group,
}
}
groupVersionsEntry.Versions = append(groupVersionsEntry.Versions, clientgentypes.PackageVersion{Version: gv.Version, Package: gvPackage})
targetGroupVersions[groupPackageName] = groupVersionsEntry
if len(typesWithInformers) != 0 {
orderer := namer.Orderer{Namer: namer.NewPrivateNamer(0)}
typesWithInformers = orderer.OrderTypes(typesWithInformers)
// Generate the informer and fake, for each type.
packageList = append(packageList, versionInformerPackages(versionPackagePath, groupPackageName, gv, groupGoNames[groupPackageName], boilerplate, typesWithInformers, customArgs)...)
}
if len(duckTypes) != 0 {
orderer := namer.Orderer{Namer: namer.NewPrivateNamer(0)}
duckTypes = orderer.OrderTypes(duckTypes)
// Generate a duck-typed informer for each type.
packageList = append(packageList, versionDuckPackages(versionPackagePath, groupPackageName, gv, groupGoNames[groupPackageName], boilerplate, duckTypes, customArgs)...)
}
if len(reconcilerTypes) != 0 {
orderer := namer.Orderer{Namer: namer.NewPrivateNamer(0)}
reconcilerTypes = orderer.OrderTypes(reconcilerTypes)
// Generate a reconciler and controller for each type.
packageList = append(packageList, reconcilerPackages(versionPackagePath, groupPackageName, gv, groupGoNames[groupPackageName], boilerplate, reconcilerTypes, customArgs)...)
}
}
return packageList
}
// Tags represents a genclient configuration for a single type.
type Tags struct {
util.Tags
GenerateDuck bool
GenerateReconciler bool
}
func (t Tags) NeedsInformerInjection() bool {
return t.GenerateClient && !t.NoVerbs && t.HasVerb("list") && t.HasVerb("watch")
}
func (t Tags) NeedsDuckInjection() bool {
return t.GenerateDuck
}
func (t Tags) NeedsReconciler(kind *types.Type, args *informergenargs.CustomArgs) bool {
// Overrides
kinds := strings.Split(args.ForceKinds, ",")
for _, k := range kinds {
if kind.Name.Name == k {
klog.V(5).Infof("Kind %s was forced to generate reconciler.", k)
return true
}
}
// Normal
return t.GenerateReconciler
}
// MustParseClientGenTags calls ParseClientGenTags but instead of returning error it panics.
func MustParseClientGenTags(lines []string) Tags {
ret := Tags{
Tags: util.MustParseClientGenTags(lines),
}
values := ExtractCommentTags("+", lines)
_, ret.GenerateDuck = values["genduck"]
_, ret.GenerateReconciler = values["genreconciler"]
return ret
}
func extractCommentTags(t *types.Type) map[string]map[string]string {
comments := append(append([]string{}, t.SecondClosestCommentLines...), t.CommentLines...)
return ExtractCommentTags("+", comments)
}
func extractReconcilerClassTag(tags map[string]map[string]string) (string, bool) {
vals, ok := tags["genreconciler"]
if !ok {
return "", false
}
classname, has := vals["class"]
return classname, has
}
func isKRShaped(tags map[string]map[string]string) bool {
vals, has := tags["genreconciler"]
if !has {
return false
}
return vals["krshapedlogic"] != "false"
}
func isNonNamespaced(tags map[string]map[string]string) bool {
vals, has := tags["genclient"]
if !has {
return false
}
_, has = vals["nonNamespaced"]
return has
}
func stubs(tags map[string]map[string]string) bool {
vals, has := tags["genreconciler"]
if !has {
return false
}
_, has = vals["stubs"]
return has
}
func vendorless(p string) string {
if pos := strings.LastIndex(p, "/vendor/"); pos != -1 {
return p[pos+len("/vendor/"):]
}
return p
}
func typedInformerPackage(groupPkgName string, gv clientgentypes.GroupVersion, externalVersionsInformersPackage string) string {
return filepath.Join(externalVersionsInformersPackage, groupPkgName, gv.Version.String())
}
func versionClientsPackages(basePackage string, boilerplate []byte, customArgs *informergenargs.CustomArgs) []generator.Package {
packagePath := filepath.Join(basePackage, "client")
return []generator.Package{
// Impl
&generator.DefaultPackage{
PackageName: "client",
PackagePath: packagePath,
HeaderText: boilerplate,
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
// Impl
generators = append(generators, &clientGenerator{
DefaultGen: generator.DefaultGen{
OptionalName: "client",
},
outputPackage: packagePath,
imports: generator.NewImportTracker(),
clientSetPackage: customArgs.VersionedClientSetPackage,
})
return generators
},
FilterFunc: func(c *generator.Context, t *types.Type) bool {
tags := MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
return tags.NeedsInformerInjection()
},
},
// Fake
&generator.DefaultPackage{
PackageName: "fake",
PackagePath: filepath.Join(packagePath, "fake"),
HeaderText: boilerplate,
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
// Impl
generators = append(generators, &fakeClientGenerator{
DefaultGen: generator.DefaultGen{
OptionalName: "fake",
},
outputPackage: filepath.Join(packagePath, "fake"),
imports: generator.NewImportTracker(),
fakeClientPkg: filepath.Join(customArgs.VersionedClientSetPackage, "fake"),
clientInjectionPkg: packagePath,
})
return generators
},
FilterFunc: func(c *generator.Context, t *types.Type) bool {
tags := MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
return tags.NeedsInformerInjection()
},
},
}
}
func versionFactoryPackages(basePackage string, boilerplate []byte, customArgs *informergenargs.CustomArgs) []generator.Package {
packagePath := filepath.Join(basePackage, "informers", "factory")
return []generator.Package{
// Impl
&generator.DefaultPackage{
PackageName: "factory",
PackagePath: packagePath,
HeaderText: boilerplate,
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
// Impl
generators = append(generators, &factoryGenerator{
DefaultGen: generator.DefaultGen{
OptionalName: "factory",
},
outputPackage: packagePath,
cachingClientSetPackage: filepath.Join(basePackage, "client"),
sharedInformerFactoryPackage: customArgs.ExternalVersionsInformersPackage,
imports: generator.NewImportTracker(),
})
return generators
},
FilterFunc: func(c *generator.Context, t *types.Type) bool {
tags := MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
return tags.NeedsInformerInjection()
},
},
// Fake
&generator.DefaultPackage{
PackageName: "fake",
PackagePath: filepath.Join(packagePath, "fake"),
HeaderText: boilerplate,
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
// Impl
generators = append(generators, &fakeFactoryGenerator{
DefaultGen: generator.DefaultGen{
OptionalName: "fake",
},
outputPackage: filepath.Join(packagePath, "fake"),
factoryInjectionPkg: packagePath,
fakeClientInjectionPkg: filepath.Join(basePackage, "client", "fake"),
sharedInformerFactoryPackage: customArgs.ExternalVersionsInformersPackage,
imports: generator.NewImportTracker(),
})
return generators
},
FilterFunc: func(c *generator.Context, t *types.Type) bool {
tags := MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
return tags.NeedsInformerInjection()
},
},
}
}
func versionInformerPackages(basePackage string, groupPkgName string, gv clientgentypes.GroupVersion, groupGoName string, boilerplate []byte, typesToGenerate []*types.Type, customArgs *informergenargs.CustomArgs) []generator.Package {
factoryPackagePath := filepath.Join(basePackage, "informers", "factory")
packagePath := filepath.Join(basePackage, "informers", groupPkgName, strings.ToLower(gv.Version.NonEmpty()))
vers := make([]generator.Package, 0, 2*len(typesToGenerate))
for _, t := range typesToGenerate {
// Fix for golang iterator bug.
t := t
packagePath := packagePath + "/" + strings.ToLower(t.Name.Name)
typedInformerPackage := typedInformerPackage(groupPkgName, gv, customArgs.ExternalVersionsInformersPackage)
// Impl
vers = append(vers, &generator.DefaultPackage{
PackageName: strings.ToLower(t.Name.Name),
PackagePath: packagePath,
HeaderText: boilerplate,
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
// Impl
generators = append(generators, &injectionGenerator{
DefaultGen: generator.DefaultGen{
OptionalName: strings.ToLower(t.Name.Name),
},
outputPackage: packagePath,
groupVersion: gv,
groupGoName: groupGoName,
typeToGenerate: t,
imports: generator.NewImportTracker(),
typedInformerPackage: typedInformerPackage,
groupInformerFactoryPackage: factoryPackagePath,
})
return generators
},
FilterFunc: func(c *generator.Context, t *types.Type) bool {
tags := MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
return tags.NeedsInformerInjection()
},
})
// Fake
vers = append(vers, &generator.DefaultPackage{
PackageName: "fake",
PackagePath: filepath.Join(packagePath, "fake"),
HeaderText: boilerplate,
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
// Impl
generators = append(generators, &fakeInformerGenerator{
DefaultGen: generator.DefaultGen{
OptionalName: "fake",
},
outputPackage: filepath.Join(packagePath, "fake"),
imports: generator.NewImportTracker(),
typeToGenerate: t,
groupVersion: gv,
groupGoName: groupGoName,
informerInjectionPkg: packagePath,
fakeFactoryInjectionPkg: filepath.Join(factoryPackagePath, "fake"),
})
return generators
},
FilterFunc: func(c *generator.Context, t *types.Type) bool {
tags := MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
return tags.NeedsInformerInjection()
},
})
}
return vers
}
func reconcilerPackages(basePackage string, groupPkgName string, gv clientgentypes.GroupVersion, groupGoName string, boilerplate []byte, typesToGenerate []*types.Type, customArgs *informergenargs.CustomArgs) []generator.Package {
packagePath := filepath.Join(basePackage, "reconciler", groupPkgName, strings.ToLower(gv.Version.NonEmpty()))
clientPackagePath := filepath.Join(basePackage, "client")
vers := make([]generator.Package, 0, 4*len(typesToGenerate))
for _, t := range typesToGenerate {
// Fix for golang iterator bug.
t := t
extracted := extractCommentTags(t)
reconcilerClass, hasReconcilerClass := extractReconcilerClassTag(extracted)
nonNamespaced := isNonNamespaced(extracted)
isKRShaped := isKRShaped(extracted)
stubs := stubs(extracted)
packagePath := filepath.Join(packagePath, strings.ToLower(t.Name.Name))
informerPackagePath := filepath.Join(basePackage, "informers", groupPkgName, strings.ToLower(gv.Version.NonEmpty()), strings.ToLower(t.Name.Name))
listerPackagePath := filepath.Join(customArgs.ListersPackage, groupPkgName, strings.ToLower(gv.Version.NonEmpty()))
// Controller
vers = append(vers, &generator.DefaultPackage{
PackageName: strings.ToLower(t.Name.Name),
PackagePath: packagePath,
HeaderText: boilerplate,
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
// Impl
generators = append(generators, &reconcilerControllerGenerator{
DefaultGen: generator.DefaultGen{
OptionalName: "controller",
},
typeToGenerate: t,
outputPackage: packagePath,
imports: generator.NewImportTracker(),
groupName: gv.Group.String(),
clientPkg: clientPackagePath,
informerPackagePath: informerPackagePath,
schemePkg: filepath.Join(customArgs.VersionedClientSetPackage, "scheme"),
reconcilerClass: reconcilerClass,
hasReconcilerClass: hasReconcilerClass,
})
return generators
},
FilterFunc: func(c *generator.Context, t *types.Type) bool {
tags := MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
return tags.NeedsReconciler(t, customArgs)
},
})
if stubs {
// Controller Stub
vers = append(vers, &generator.DefaultPackage{
PackageName: strings.ToLower(t.Name.Name),
PackagePath: filepath.Join(packagePath, "stub"),
HeaderText: boilerplate,
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
// Impl
generators = append(generators, &reconcilerControllerStubGenerator{
DefaultGen: generator.DefaultGen{
OptionalName: "controller",
},
typeToGenerate: t,
reconcilerPkg: packagePath,
outputPackage: filepath.Join(packagePath, "stub"),
imports: generator.NewImportTracker(),
informerPackagePath: informerPackagePath,
reconcilerClass: reconcilerClass,
hasReconcilerClass: hasReconcilerClass,
})
return generators
},
FilterFunc: func(c *generator.Context, t *types.Type) bool {
tags := MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
return tags.NeedsReconciler(t, customArgs)
},
})
}
// Reconciler
vers = append(vers, &generator.DefaultPackage{
PackageName: strings.ToLower(t.Name.Name),
PackagePath: packagePath,
HeaderText: boilerplate,
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
// Impl
generators = append(generators, &reconcilerReconcilerGenerator{
DefaultGen: generator.DefaultGen{
OptionalName: "reconciler",
},
typeToGenerate: t,
outputPackage: packagePath,
imports: generator.NewImportTracker(),
clientsetPkg: customArgs.VersionedClientSetPackage,
listerName: t.Name.Name + "Lister",
listerPkg: listerPackagePath,
groupGoName: groupGoName,
groupVersion: gv,
reconcilerClass: reconcilerClass,
hasReconcilerClass: hasReconcilerClass,
nonNamespaced: nonNamespaced,
isKRShaped: isKRShaped,
})
return generators
},
FilterFunc: func(c *generator.Context, t *types.Type) bool {
tags := MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
return tags.NeedsReconciler(t, customArgs)
},
})
if stubs {
// Reconciler Stub
vers = append(vers, &generator.DefaultPackage{
PackageName: strings.ToLower(t.Name.Name),
PackagePath: filepath.Join(packagePath, "stub"),
HeaderText: boilerplate,
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
// Impl
generators = append(generators, &reconcilerReconcilerStubGenerator{
DefaultGen: generator.DefaultGen{
OptionalName: "reconciler",
},
typeToGenerate: t,
reconcilerPkg: packagePath,
outputPackage: filepath.Join(packagePath, "stub"),
imports: generator.NewImportTracker(),
})
return generators
},
FilterFunc: func(c *generator.Context, t *types.Type) bool {
tags := MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
return tags.NeedsReconciler(t, customArgs)
},
})
}
// Reconciler State
vers = append(vers, &generator.DefaultPackage{
PackageName: strings.ToLower(t.Name.Name),
PackagePath: packagePath,
HeaderText: boilerplate,
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
// state
generators = append(generators, &reconcilerStateGenerator{
DefaultGen: generator.DefaultGen{
OptionalName: "state",
},
typeToGenerate: t,
outputPackage: packagePath,
imports: generator.NewImportTracker(),
})
return generators
},
FilterFunc: func(c *generator.Context, t *types.Type) bool {
tags := MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
return tags.NeedsReconciler(t, customArgs)
},
})
}
return vers
}
func versionDuckPackages(basePackage string, groupPkgName string, gv clientgentypes.GroupVersion, groupGoName string, boilerplate []byte, typesToGenerate []*types.Type, _ *informergenargs.CustomArgs) []generator.Package {
packagePath := filepath.Join(basePackage, "ducks", groupPkgName, strings.ToLower(gv.Version.NonEmpty()))
vers := make([]generator.Package, 0, 2*len(typesToGenerate))
for _, t := range typesToGenerate {
// Fix for golang iterator bug.
t := t
packagePath := filepath.Join(packagePath, strings.ToLower(t.Name.Name))
// Impl
vers = append(vers, &generator.DefaultPackage{
PackageName: strings.ToLower(t.Name.Name),
PackagePath: packagePath,
HeaderText: boilerplate,
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
// Impl
generators = append(generators, &duckGenerator{
DefaultGen: generator.DefaultGen{
OptionalName: strings.ToLower(t.Name.Name),
},
outputPackage: packagePath,
groupVersion: gv,
groupGoName: groupGoName,
typeToGenerate: t,
imports: generator.NewImportTracker(),
})
return generators
},
FilterFunc: func(c *generator.Context, t *types.Type) bool {
tags := MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
return tags.NeedsDuckInjection()
},
})
// Fake
vers = append(vers, &generator.DefaultPackage{
PackageName: "fake",
PackagePath: filepath.Join(packagePath, "fake"),
HeaderText: boilerplate,
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
// Impl
generators = append(generators, &fakeDuckGenerator{
DefaultGen: generator.DefaultGen{
OptionalName: "fake",
},
outputPackage: filepath.Join(packagePath, "fake"),
imports: generator.NewImportTracker(),
typeToGenerate: t,
groupVersion: gv,
groupGoName: groupGoName,
duckInjectionPkg: packagePath,
})
return generators
},
FilterFunc: func(c *generator.Context, t *types.Type) bool {
tags := MustParseClientGenTags(append(t.SecondClosestCommentLines, t.CommentLines...))
return tags.NeedsDuckInjection()
},
})
}
return vers
}

View File

@ -0,0 +1,286 @@
/*
Copyright 2020 The Knative Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package generators
import (
"io"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/klog"
)
// reconcilerControllerGenerator produces a file for setting up the reconciler
// with injection.
type reconcilerControllerGenerator struct {
generator.DefaultGen
outputPackage string
imports namer.ImportTracker
typeToGenerate *types.Type
groupName string
clientPkg string
schemePkg string
informerPackagePath string
reconcilerClass string
hasReconcilerClass bool
}
var _ generator.Generator = (*reconcilerControllerGenerator)(nil)
func (g *reconcilerControllerGenerator) Filter(c *generator.Context, t *types.Type) bool {
// Only process the type for this generator.
return t == g.typeToGenerate
}
func (g *reconcilerControllerGenerator) Namers(c *generator.Context) namer.NameSystems {
return namer.NameSystems{
"raw": namer.NewRawNamer(g.outputPackage, g.imports),
}
}
func (g *reconcilerControllerGenerator) Imports(c *generator.Context) (imports []string) {
imports = append(imports, g.imports.ImportLines()...)
return
}
func (g *reconcilerControllerGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
sw := generator.NewSnippetWriter(w, c, "{{", "}}")
klog.V(5).Info("processing type ", t)
m := map[string]interface{}{
"type": t,
"group": g.groupName,
"class": g.reconcilerClass,
"hasClass": g.hasReconcilerClass,
"controllerImpl": c.Universe.Type(types.Name{
Package: "knative.dev/pkg/controller",
Name: "Impl",
}),
"controllerReconciler": c.Universe.Type(types.Name{
Package: "knative.dev/pkg/controller",
Name: "Reconciler",
}),
"controllerNewImpl": c.Universe.Function(types.Name{
Package: "knative.dev/pkg/controller",
Name: "NewImpl",
}),
"loggingFromContext": c.Universe.Function(types.Name{
Package: "knative.dev/pkg/logging",
Name: "FromContext",
}),
"ptrString": c.Universe.Function(types.Name{
Package: "knative.dev/pkg/ptr",
Name: "String",
}),
"corev1EventSource": c.Universe.Function(types.Name{
Package: "k8s.io/api/core/v1",
Name: "EventSource",
}),
"clientGet": c.Universe.Function(types.Name{
Package: g.clientPkg,
Name: "Get",
}),
"informerGet": c.Universe.Function(types.Name{
Package: g.informerPackagePath,
Name: "Get",
}),
"schemeScheme": c.Universe.Function(types.Name{
Package: "k8s.io/client-go/kubernetes/scheme",
Name: "Scheme",
}),
"schemeAddToScheme": c.Universe.Function(types.Name{
Package: g.schemePkg,
Name: "AddToScheme",
}),
"kubeclientGet": c.Universe.Function(types.Name{
Package: "knative.dev/pkg/client/injection/kube/client",
Name: "Get",
}),
"typedcorev1EventSinkImpl": c.Universe.Function(types.Name{
Package: "k8s.io/client-go/kubernetes/typed/core/v1",
Name: "EventSinkImpl",
}),
"recordNewBroadcaster": c.Universe.Function(types.Name{
Package: "k8s.io/client-go/tools/record",
Name: "NewBroadcaster",
}),
"watchInterface": c.Universe.Type(types.Name{
Package: "k8s.io/apimachinery/pkg/watch",
Name: "Interface",
}),
"controllerGetEventRecorder": c.Universe.Function(types.Name{
Package: "knative.dev/pkg/controller",
Name: "GetEventRecorder",
}),
"controllerOptions": c.Universe.Type(types.Name{
Package: "knative.dev/pkg/controller",
Name: "Options",
}),
"controllerOptionsFn": c.Universe.Type(types.Name{
Package: "knative.dev/pkg/controller",
Name: "OptionsFn",
}),
"contextContext": c.Universe.Type(types.Name{
Package: "context",
Name: "Context",
}),
"reconcilerLeaderAwareFuncs": c.Universe.Type(types.Name{
Package: "knative.dev/pkg/reconciler",
Name: "LeaderAwareFuncs",
}),
"reconcilerBucket": c.Universe.Type(types.Name{
Package: "knative.dev/pkg/reconciler",
Name: "Bucket",
}),
"typesNamespacedName": c.Universe.Type(types.Name{
Package: "k8s.io/apimachinery/pkg/types",
Name: "NamespacedName",
}),
"labelsEverything": c.Universe.Function(types.Name{
Package: "k8s.io/apimachinery/pkg/labels",
Name: "Everything",
}),
"stringsReplaceAll": c.Universe.Function(types.Name{
Package: "strings",
Name: "ReplaceAll",
}),
"reflectTypeOf": c.Universe.Function(types.Name{
Package: "reflect",
Name: "TypeOf",
}),
"fmtSprintf": c.Universe.Function(types.Name{
Package: "fmt",
Name: "Sprintf",
}),
}
sw.Do(reconcilerControllerNewImpl, m)
return sw.Error()
}
var reconcilerControllerNewImpl = `
const (
defaultControllerAgentName = "{{.type|lowercaseSingular}}-controller"
defaultFinalizerName = "{{.type|allLowercasePlural}}.{{.group}}"
{{if .hasClass}}
// ClassAnnotationKey points to the annotation for the class of this resource.
ClassAnnotationKey = "{{ .class }}"
{{end}}
)
// NewImpl returns a {{.controllerImpl|raw}} that handles queuing and feeding work from
// the queue through an implementation of {{.controllerReconciler|raw}}, delegating to
// the provided Interface and optional Finalizer methods. OptionsFn is used to return
// {{.controllerOptions|raw}} to be used but the internal reconciler.
func NewImpl(ctx {{.contextContext|raw}}, r Interface{{if .hasClass}}, classValue string{{end}}, optionsFns ...{{.controllerOptionsFn|raw}}) *{{.controllerImpl|raw}} {
logger := {{.loggingFromContext|raw}}(ctx)
// Check the options function input. It should be 0 or 1.
if len(optionsFns) > 1 {
logger.Fatal("Up to one options function is supported, found: ", len(optionsFns))
}
{{.type|lowercaseSingular}}Informer := {{.informerGet|raw}}(ctx)
lister := {{.type|lowercaseSingular}}Informer.Lister()
rec := &reconcilerImpl{
LeaderAwareFuncs: {{.reconcilerLeaderAwareFuncs|raw}}{
PromoteFunc: func(bkt {{.reconcilerBucket|raw}}, enq func({{.reconcilerBucket|raw}}, {{.typesNamespacedName|raw}})) error {
all, err := lister.List({{.labelsEverything|raw}}())
if err != nil {
return err
}
for _, elt := range all {
// TODO: Consider letting users specify a filter in options.
enq(bkt, {{.typesNamespacedName|raw}}{
Namespace: elt.GetNamespace(),
Name: elt.GetName(),
})
}
return nil
},
},
Client: {{.clientGet|raw}}(ctx),
Lister: lister,
reconciler: r,
finalizerName: defaultFinalizerName,
{{if .hasClass}}classValue: classValue,{{end}}
}
t := {{.reflectTypeOf|raw}}(r).Elem()
queueName := {{.fmtSprintf|raw}}("%s.%s", {{.stringsReplaceAll|raw}}(t.PkgPath(), "/", "-"), t.Name())
impl := {{.controllerNewImpl|raw}}(rec, logger, queueName)
agentName := defaultControllerAgentName
// Pass impl to the options. Save any optional results.
for _, fn := range optionsFns {
opts := fn(impl)
if opts.ConfigStore != nil {
rec.configStore = opts.ConfigStore
}
if opts.FinalizerName != "" {
rec.finalizerName = opts.FinalizerName
}
if opts.AgentName != "" {
agentName = opts.AgentName
}
if opts.SkipStatusUpdates {
rec.skipStatusUpdates = true
}
}
rec.Recorder = createRecorder(ctx, agentName)
return impl
}
func createRecorder(ctx context.Context, agentName string) record.EventRecorder {
logger := {{.loggingFromContext|raw}}(ctx)
recorder := {{.controllerGetEventRecorder|raw}}(ctx)
if recorder == nil {
// Create event broadcaster
logger.Debug("Creating event broadcaster")
eventBroadcaster := {{.recordNewBroadcaster|raw}}()
watches := []{{.watchInterface|raw}}{
eventBroadcaster.StartLogging(logger.Named("event-broadcaster").Infof),
eventBroadcaster.StartRecordingToSink(
&{{.typedcorev1EventSinkImpl|raw}}{Interface: {{.kubeclientGet|raw}}(ctx).CoreV1().Events("")}),
}
recorder = eventBroadcaster.NewRecorder({{.schemeScheme|raw}}, {{.corev1EventSource|raw}}{Component: agentName})
go func() {
<-ctx.Done()
for _, w := range watches {
w.Stop()
}
}()
}
return recorder
}
func init() {
{{.schemeAddToScheme|raw}}({{.schemeScheme|raw}})
}
`

View File

@ -0,0 +1,147 @@
/*
Copyright 2020 The Knative Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package generators
import (
"io"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/klog"
)
// reconcilerControllerStubGenerator produces a file of the stub of the
// controller for a custom impl with injection.
type reconcilerControllerStubGenerator struct {
generator.DefaultGen
outputPackage string
imports namer.ImportTracker
typeToGenerate *types.Type
reconcilerPkg string
informerPackagePath string
reconcilerClass string
hasReconcilerClass bool
}
var _ generator.Generator = (*reconcilerControllerStubGenerator)(nil)
func (g *reconcilerControllerStubGenerator) Filter(c *generator.Context, t *types.Type) bool {
// Only process the type for this generator.
return t == g.typeToGenerate
}
func (g *reconcilerControllerStubGenerator) Namers(c *generator.Context) namer.NameSystems {
return namer.NameSystems{
"raw": namer.NewRawNamer(g.outputPackage, g.imports),
}
}
func (g *reconcilerControllerStubGenerator) Imports(c *generator.Context) (imports []string) {
imports = append(imports, g.imports.ImportLines()...)
return
}
func (g *reconcilerControllerStubGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
sw := generator.NewSnippetWriter(w, c, "{{", "}}")
klog.V(5).Info("processing type ", t)
m := map[string]interface{}{
"type": t,
"class": g.reconcilerClass,
"hasClass": g.hasReconcilerClass,
"informerGet": c.Universe.Function(types.Name{
Package: g.informerPackagePath,
Name: "Get",
}),
"controllerImpl": c.Universe.Type(types.Name{Package: "knative.dev/pkg/controller", Name: "Impl"}),
"reconcilerNewImpl": c.Universe.Type(types.Name{
Package: g.reconcilerPkg,
Name: "NewImpl",
}),
"loggingFromContext": c.Universe.Function(types.Name{
Package: "knative.dev/pkg/logging",
Name: "FromContext",
}),
"contextContext": c.Universe.Type(types.Name{
Package: "context",
Name: "Context",
}),
"configmapWatcher": c.Universe.Type(types.Name{
Package: "knative.dev/pkg/configmap",
Name: "Watcher",
}),
"classAnnotationKey": c.Universe.Variable(types.Name{
Package: g.reconcilerPkg,
Name: "ClassAnnotationKey",
}),
"annotationFilterFunc": c.Universe.Function(types.Name{
Package: "knative.dev/pkg/reconciler",
Name: "AnnotationFilterFunc",
}),
"filterHandler": c.Universe.Type(types.Name{
Package: "k8s.io/client-go/tools/cache",
Name: "FilteringResourceEventHandler",
}),
}
sw.Do(reconcilerControllerStub, m)
return sw.Error()
}
var reconcilerControllerStub = `
// TODO: PLEASE COPY AND MODIFY THIS FILE AS A STARTING POINT
// NewController creates a Reconciler for {{.type|public}} and returns the result of NewImpl.
func NewController(
ctx {{.contextContext|raw}},
cmw {{.configmapWatcher|raw}},
) *{{.controllerImpl|raw}} {
logger := {{.loggingFromContext|raw}}(ctx)
{{.type|lowercaseSingular}}Informer := {{.informerGet|raw}}(ctx)
{{if .hasClass}}
classValue := "default" // TODO: update this to the appropriate value.
classFilter := {{.annotationFilterFunc|raw}}({{.classAnnotationKey|raw}}, classValue, false /*allowUnset*/)
{{end}}
// TODO: setup additional informers here.
{{if .hasClass}}// TODO: remember to use the classFilter from above to filter appropriately.{{end}}
r := &Reconciler{}
impl := {{.reconcilerNewImpl|raw}}(ctx, r{{if .hasClass}}, classValue{{end}})
logger.Info("Setting up event handlers.")
{{if .hasClass}}
{{.type|lowercaseSingular}}Informer.Informer().AddEventHandler({{.filterHandler|raw}}{
FilterFunc: classFilter,
Handler: controller.HandleAll(impl.Enqueue),
})
{{else}}
{{.type|lowercaseSingular}}Informer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue))
{{end}}
// TODO: add additional informer event handlers here.
return impl
}
`

View File

@ -0,0 +1,662 @@
/*
Copyright 2020 The Knative Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package generators
import (
"io"
clientgentypes "k8s.io/code-generator/cmd/client-gen/types"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/klog"
)
// reconcilerReconcilerGenerator produces a reconciler struct for the given type.
type reconcilerReconcilerGenerator struct {
generator.DefaultGen
outputPackage string
imports namer.ImportTracker
typeToGenerate *types.Type
clientsetPkg string
listerName string
listerPkg string
reconcilerClass string
hasReconcilerClass bool
nonNamespaced bool
isKRShaped bool
groupGoName string
groupVersion clientgentypes.GroupVersion
}
var _ generator.Generator = (*reconcilerReconcilerGenerator)(nil)
func (g *reconcilerReconcilerGenerator) Filter(c *generator.Context, t *types.Type) bool {
// Only process the type for this generator.
return t == g.typeToGenerate
}
func (g *reconcilerReconcilerGenerator) Namers(c *generator.Context) namer.NameSystems {
return namer.NameSystems{
"raw": namer.NewRawNamer(g.outputPackage, g.imports),
}
}
func (g *reconcilerReconcilerGenerator) Imports(c *generator.Context) (imports []string) {
imports = append(imports, g.imports.ImportLines()...)
return
}
func (g *reconcilerReconcilerGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
sw := generator.NewSnippetWriter(w, c, "{{", "}}")
klog.V(5).Info("processing type ", t)
m := map[string]interface{}{
"type": t,
"group": namer.IC(g.groupGoName),
"version": namer.IC(g.groupVersion.Version.String()),
"class": g.reconcilerClass,
"hasClass": g.hasReconcilerClass,
"isKRShaped": g.isKRShaped,
"nonNamespaced": g.nonNamespaced,
"controllerImpl": c.Universe.Type(types.Name{
Package: "knative.dev/pkg/controller",
Name: "Impl",
}),
"controllerReconciler": c.Universe.Type(types.Name{
Package: "knative.dev/pkg/controller",
Name: "Reconciler",
}),
"controllerWithEventRecorder": c.Universe.Type(types.Name{
Package: "knative.dev/pkg/controller",
Name: "WithEventRecorder",
}),
"corev1EventSource": c.Universe.Function(types.Name{
Package: "k8s.io/api/core/v1",
Name: "EventSource",
}),
"corev1EventTypeNormal": c.Universe.Type(types.Name{
Package: "k8s.io/api/core/v1",
Name: "EventTypeNormal",
}),
"corev1EventTypeWarning": c.Universe.Type(types.Name{
Package: "k8s.io/api/core/v1",
Name: "EventTypeWarning",
}),
"reconcilerEvent": c.Universe.Type(types.Name{Package: "knative.dev/pkg/reconciler", Name: "Event"}),
"reconcilerReconcilerEvent": c.Universe.Type(types.Name{Package: "knative.dev/pkg/reconciler", Name: "ReconcilerEvent"}),
"reconcilerRetryUpdateConflicts": c.Universe.Function(types.Name{Package: "knative.dev/pkg/reconciler", Name: "RetryUpdateConflicts"}),
"reconcilerConfigStore": c.Universe.Type(types.Name{Name: "ConfigStore", Package: "knative.dev/pkg/reconciler"}),
// Deps
"clientsetInterface": c.Universe.Type(types.Name{Name: "Interface", Package: g.clientsetPkg}),
"resourceLister": c.Universe.Type(types.Name{Name: g.listerName, Package: g.listerPkg}),
// K8s types
"recordEventRecorder": c.Universe.Type(types.Name{Name: "EventRecorder", Package: "k8s.io/client-go/tools/record"}),
// methods
"loggingFromContext": c.Universe.Function(types.Name{
Package: "knative.dev/pkg/logging",
Name: "FromContext",
}),
"cacheSplitMetaNamespaceKey": c.Universe.Function(types.Name{
Package: "k8s.io/client-go/tools/cache",
Name: "SplitMetaNamespaceKey",
}),
"retryRetryOnConflict": c.Universe.Function(types.Name{
Package: "k8s.io/client-go/util/retry",
Name: "RetryOnConflict",
}),
"apierrsIsNotFound": c.Universe.Function(types.Name{
Package: "k8s.io/apimachinery/pkg/api/errors",
Name: "IsNotFound",
}),
"metav1GetOptions": c.Universe.Function(types.Name{
Package: "k8s.io/apimachinery/pkg/apis/meta/v1",
Name: "GetOptions",
}),
"metav1PatchOptions": c.Universe.Function(types.Name{
Package: "k8s.io/apimachinery/pkg/apis/meta/v1",
Name: "PatchOptions",
}),
"metav1UpdateOptions": c.Universe.Function(types.Name{
Package: "k8s.io/apimachinery/pkg/apis/meta/v1",
Name: "UpdateOptions",
}),
"zapSugaredLogger": c.Universe.Type(types.Name{
Package: "go.uber.org/zap",
Name: "SugaredLogger",
}),
"setsNewString": c.Universe.Function(types.Name{
Package: "k8s.io/apimachinery/pkg/util/sets",
Name: "NewString",
}),
"controllerOptions": c.Universe.Type(types.Name{
Package: "knative.dev/pkg/controller",
Name: "Options",
}),
"contextContext": c.Universe.Type(types.Name{
Package: "context",
Name: "Context",
}),
"kmpSafeDiff": c.Universe.Function(types.Name{
Package: "knative.dev/pkg/kmp",
Name: "SafeDiff",
}),
"fmtErrorf": c.Universe.Package("fmt").Function("Errorf"),
"reflectDeepEqual": c.Universe.Package("reflect").Function("DeepEqual"),
"equalitySemantic": c.Universe.Package("k8s.io/apimachinery/pkg/api/equality").Variable("Semantic"),
"jsonMarshal": c.Universe.Package("encoding/json").Function("Marshal"),
"typesMergePatchType": c.Universe.Package("k8s.io/apimachinery/pkg/types").Constant("MergePatchType"),
"syncRWMutex": c.Universe.Type(types.Name{
Package: "sync",
Name: "RWMutex",
}),
"reconcilerLeaderAware": c.Universe.Type(types.Name{
Package: "knative.dev/pkg/reconciler",
Name: "LeaderAware",
}),
"reconcilerLeaderAwareFuncs": c.Universe.Type(types.Name{
Package: "knative.dev/pkg/reconciler",
Name: "LeaderAwareFuncs",
}),
"reconcilerBucket": c.Universe.Type(types.Name{
Package: "knative.dev/pkg/reconciler",
Name: "Bucket",
}),
"typesNamespacedName": c.Universe.Type(types.Name{
Package: "k8s.io/apimachinery/pkg/types",
Name: "NamespacedName",
}),
"labelsEverything": c.Universe.Function(types.Name{
Package: "k8s.io/apimachinery/pkg/labels",
Name: "Everything",
}),
"doReconcileKind": c.Universe.Type(types.Name{
Package: "knative.dev/pkg/reconciler",
Name: "DoReconcileKind",
}),
"doObserveKind": c.Universe.Type(types.Name{
Package: "knative.dev/pkg/reconciler",
Name: "DoObserveKind",
}),
"doFinalizeKind": c.Universe.Type(types.Name{
Package: "knative.dev/pkg/reconciler",
Name: "DoFinalizeKind",
}),
"doObserveFinalizeKind": c.Universe.Type(types.Name{
Package: "knative.dev/pkg/reconciler",
Name: "DoObserveFinalizeKind",
}),
}
sw.Do(reconcilerInterfaceFactory, m)
sw.Do(reconcilerNewReconciler, m)
sw.Do(reconcilerImplFactory, m)
sw.Do(reconcilerStatusFactory, m)
sw.Do(reconcilerFinalizerFactory, m)
return sw.Error()
}
var reconcilerInterfaceFactory = `
// Interface defines the strongly typed interfaces to be implemented by a
// controller reconciling {{.type|raw}}.
type Interface interface {
// ReconcileKind implements custom logic to reconcile {{.type|raw}}. Any changes
// to the objects .Status or .Finalizers will be propagated to the stored
// object. It is recommended that implementors do not call any update calls
// for the Kind inside of ReconcileKind, it is the responsibility of the calling
// controller to propagate those properties. The resource passed to ReconcileKind
// will always have an empty deletion timestamp.
ReconcileKind(ctx {{.contextContext|raw}}, o *{{.type|raw}}) {{.reconcilerEvent|raw}}
}
// Finalizer defines the strongly typed interfaces to be implemented by a
// controller finalizing {{.type|raw}}.
type Finalizer interface {
// FinalizeKind implements custom logic to finalize {{.type|raw}}. Any changes
// to the objects .Status or .Finalizers will be ignored. Returning a nil or
// Normal type {{.reconcilerEvent|raw}} will allow the finalizer to be deleted on
// the resource. The resource passed to FinalizeKind will always have a set
// deletion timestamp.
FinalizeKind(ctx {{.contextContext|raw}}, o *{{.type|raw}}) {{.reconcilerEvent|raw}}
}
// ReadOnlyInterface defines the strongly typed interfaces to be implemented by a
// controller reconciling {{.type|raw}} if they want to process resources for which
// they are not the leader.
type ReadOnlyInterface interface {
// ObserveKind implements logic to observe {{.type|raw}}.
// This method should not write to the API.
ObserveKind(ctx {{.contextContext|raw}}, o *{{.type|raw}}) {{.reconcilerEvent|raw}}
}
// ReadOnlyFinalizer defines the strongly typed interfaces to be implemented by a
// controller finalizing {{.type|raw}} if they want to process tombstoned resources
// even when they are not the leader. Due to the nature of how finalizers are handled
// there are no guarantees that this will be called.
type ReadOnlyFinalizer interface {
// ObserveFinalizeKind implements custom logic to observe the final state of {{.type|raw}}.
// This method should not write to the API.
ObserveFinalizeKind(ctx {{.contextContext|raw}}, o *{{.type|raw}}) {{.reconcilerEvent|raw}}
}
type doReconcile func(ctx {{.contextContext|raw}}, o *{{.type|raw}}) {{.reconcilerEvent|raw}}
// reconcilerImpl implements controller.Reconciler for {{.type|raw}} resources.
type reconcilerImpl struct {
// LeaderAwareFuncs is inlined to help us implement {{.reconcilerLeaderAware|raw}}
{{.reconcilerLeaderAwareFuncs|raw}}
// Client is used to write back status updates.
Client {{.clientsetInterface|raw}}
// Listers index properties about resources
Lister {{.resourceLister|raw}}
// Recorder is an event recorder for recording Event resources to the
// Kubernetes API.
Recorder {{.recordEventRecorder|raw}}
// configStore allows for decorating a context with config maps.
// +optional
configStore {{.reconcilerConfigStore|raw}}
// reconciler is the implementation of the business logic of the resource.
reconciler Interface
// finalizerName is the name of the finalizer to reconcile.
finalizerName string
// skipStatusUpdates configures whether or not this reconciler automatically updates
// the status of the reconciled resource.
skipStatusUpdates bool
{{if .hasClass}}
// classValue is the resource annotation[{{ .class }}] instance value this reconciler instance filters on.
classValue string
{{end}}
}
// Check that our Reconciler implements controller.Reconciler
var _ controller.Reconciler = (*reconcilerImpl)(nil)
// Check that our generated Reconciler is always LeaderAware.
var _ {{.reconcilerLeaderAware|raw}} = (*reconcilerImpl)(nil)
`
var reconcilerNewReconciler = `
func NewReconciler(ctx {{.contextContext|raw}}, logger *{{.zapSugaredLogger|raw}}, client {{.clientsetInterface|raw}}, lister {{.resourceLister|raw}}, recorder {{.recordEventRecorder|raw}}, r Interface{{if .hasClass}}, classValue string{{end}}, options ...{{.controllerOptions|raw}} ) {{.controllerReconciler|raw}} {
// Check the options function input. It should be 0 or 1.
if len(options) > 1 {
logger.Fatal("Up to one options struct is supported, found: ", len(options))
}
// Fail fast when users inadvertently implement the other LeaderAware interface.
// For the typed reconcilers, Promote shouldn't take any arguments.
if _, ok := r.({{.reconcilerLeaderAware|raw}}); ok {
logger.Fatalf("%T implements the incorrect LeaderAware interface. Promote() should not take an argument as genreconciler handles the enqueuing automatically.", r)
}
// TODO: Consider validating when folks implement ReadOnlyFinalizer, but not Finalizer.
rec := &reconcilerImpl{
LeaderAwareFuncs: {{.reconcilerLeaderAwareFuncs|raw}}{
PromoteFunc: func(bkt {{.reconcilerBucket|raw}}, enq func({{.reconcilerBucket|raw}}, {{.typesNamespacedName|raw}})) error {
all, err := lister.List({{.labelsEverything|raw}}())
if err != nil {
return err
}
for _, elt := range all {
// TODO: Consider letting users specify a filter in options.
enq(bkt, {{.typesNamespacedName|raw}}{
Namespace: elt.GetNamespace(),
Name: elt.GetName(),
})
}
return nil
},
},
Client: client,
Lister: lister,
Recorder: recorder,
reconciler: r,
finalizerName: defaultFinalizerName,
{{if .hasClass}}classValue: classValue,{{end}}
}
for _, opts := range options {
if opts.ConfigStore != nil {
rec.configStore = opts.ConfigStore
}
if opts.FinalizerName != "" {
rec.finalizerName = opts.FinalizerName
}
if opts.SkipStatusUpdates {
rec.skipStatusUpdates = true
}
}
return rec
}
`
var reconcilerImplFactory = `
// Reconcile implements controller.Reconciler
func (r *reconcilerImpl) Reconcile(ctx {{.contextContext|raw}}, key string) error {
logger := {{.loggingFromContext|raw}}(ctx)
// Initialize the reconciler state. This will convert the namespace/name
// string into a distinct namespace and name, determine if this instance of
// the reconciler is the leader, and any additional interfaces implemented
// by the reconciler. Returns an error is the resource key is invalid.
s, err := newState(key, r)
if err != nil {
logger.Error("Invalid resource key: ", key)
return nil
}
// If we are not the leader, and we don't implement either ReadOnly
// observer interfaces, then take a fast-path out.
if s.isNotLeaderNorObserver() {
return nil
}
// If configStore is set, attach the frozen configuration to the context.
if r.configStore != nil {
ctx = r.configStore.ToContext(ctx)
}
// Add the recorder to context.
ctx = {{.controllerWithEventRecorder|raw}}(ctx, r.Recorder)
// Get the resource with this namespace/name.
{{if .nonNamespaced}}
getter := r.Lister
{{else}}
getter := r.Lister.{{.type|apiGroup}}(s.namespace)
{{end}}
original, err := getter.Get(s.name)
if {{.apierrsIsNotFound|raw}}(err) {
// The resource may no longer exist, in which case we stop processing.
logger.Debugf("Resource %q no longer exists", key)
return nil
} else if err != nil {
return err
}
{{if .hasClass}}
if classValue, found := original.GetAnnotations()[ClassAnnotationKey]; !found || classValue != r.classValue {
logger.Debugw("Skip reconciling resource, class annotation value does not match reconciler instance value.",
zap.String("classKey", ClassAnnotationKey),
zap.String("issue", classValue+"!="+r.classValue))
return nil
}
{{end}}
// Don't modify the informers copy.
resource := original.DeepCopy()
var reconcileEvent {{.reconcilerEvent|raw}}
name, do := s.reconcileMethodFor(resource)
// Append the target method to the logger.
logger = logger.With(zap.String("targetMethod", name))
switch name {
case {{.doReconcileKind|raw}}:
// Append the target method to the logger.
logger = logger.With(zap.String("targetMethod", "ReconcileKind"))
// Set and update the finalizer on resource if r.reconciler
// implements Finalizer.
if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil {
return {{.fmtErrorf|raw}}("failed to set finalizers: %w", err)
}
{{if .isKRShaped}}
if !r.skipStatusUpdates {
reconciler.PreProcessReconcile(ctx, resource)
}
{{end}}
// Reconcile this copy of the resource and then write back any status
// updates regardless of whether the reconciliation errored out.
reconcileEvent = do(ctx, resource)
{{if .isKRShaped}}
if !r.skipStatusUpdates {
reconciler.PostProcessReconcile(ctx, resource, original)
}
{{end}}
case {{.doFinalizeKind|raw}}:
// For finalizing reconcilers, if this resource being marked for deletion
// and reconciled cleanly (nil or normal event), remove the finalizer.
reconcileEvent = do(ctx, resource)
if resource, err = r.clearFinalizer(ctx, resource, reconcileEvent); err != nil {
return {{.fmtErrorf|raw}}("failed to clear finalizers: %w", err)
}
case {{.doObserveKind|raw}}, {{.doObserveFinalizeKind|raw}}:
// Observe any changes to this resource, since we are not the leader.
reconcileEvent = do(ctx, resource)
}
// Synchronize the status.
switch {
case r.skipStatusUpdates:
// This reconciler implementation is configured to skip resource updates.
// This may mean this reconciler does not observe spec, but reconciles external changes.
case {{.equalitySemantic|raw}}.DeepEqual(original.Status, resource.Status):
// If we didn't change anything then don't call updateStatus.
// This is important because the copy we loaded from the injectionInformer's
// cache may be stale and we don't want to overwrite a prior update
// to status with this stale state.
case !s.isLeader:
// High-availability reconcilers may have many replicas watching the resource, but only
// the elected leader is expected to write modifications.
logger.Warn("Saw status changes when we aren't the leader!")
default:
if err = r.updateStatus(ctx, original, resource); err != nil {
logger.Warnw("Failed to update resource status", zap.Error(err))
r.Recorder.Eventf(resource, {{.corev1EventTypeWarning|raw}}, "UpdateFailed",
"Failed to update status for %q: %v", resource.Name, err)
return err
}
}
// Report the reconciler event, if any.
if reconcileEvent != nil {
var event *{{.reconcilerReconcilerEvent|raw}}
if reconciler.EventAs(reconcileEvent, &event) {
logger.Infow("Returned an event", zap.Any("event", reconcileEvent))
r.Recorder.Eventf(resource, event.EventType, event.Reason, event.Format, event.Args...)
// the event was wrapped inside an error, consider the reconciliation as failed
if _, isEvent := reconcileEvent.(*reconciler.ReconcilerEvent); !isEvent {
return reconcileEvent
}
return nil
}
logger.Errorw("Returned an error", zap.Error(reconcileEvent))
r.Recorder.Event(resource, {{.corev1EventTypeWarning|raw}}, "InternalError", reconcileEvent.Error())
return reconcileEvent
}
return nil
}
`
var reconcilerStatusFactory = `
func (r *reconcilerImpl) updateStatus(ctx {{.contextContext|raw}}, existing *{{.type|raw}}, desired *{{.type|raw}}) error {
existing = existing.DeepCopy()
return {{.reconcilerRetryUpdateConflicts|raw}}(func(attempts int) (err error) {
// The first iteration tries to use the injectionInformer's state, subsequent attempts fetch the latest state via API.
if attempts > 0 {
{{if .nonNamespaced}}
getter := r.Client.{{.group}}{{.version}}().{{.type|apiGroup}}()
{{else}}
getter := r.Client.{{.group}}{{.version}}().{{.type|apiGroup}}(desired.Namespace)
{{end}}
existing, err = getter.Get(ctx, desired.Name, {{.metav1GetOptions|raw}}{})
if err != nil {
return err
}
}
// If there's nothing to update, just return.
if {{.reflectDeepEqual|raw}}(existing.Status, desired.Status) {
return nil
}
if diff, err := {{.kmpSafeDiff|raw}}(existing.Status, desired.Status); err == nil && diff != "" {
{{.loggingFromContext|raw}}(ctx).Debug("Updating status with: ", diff)
}
existing.Status = desired.Status
{{if .nonNamespaced}}
updater := r.Client.{{.group}}{{.version}}().{{.type|apiGroup}}()
{{else}}
updater := r.Client.{{.group}}{{.version}}().{{.type|apiGroup}}(existing.Namespace)
{{end}}
_, err = updater.UpdateStatus(ctx, existing, {{.metav1UpdateOptions|raw}}{})
return err
})
}
`
var reconcilerFinalizerFactory = `
// updateFinalizersFiltered will update the Finalizers of the resource.
// TODO: this method could be generic and sync all finalizers. For now it only
// updates defaultFinalizerName or its override.
func (r *reconcilerImpl) updateFinalizersFiltered(ctx {{.contextContext|raw}}, resource *{{.type|raw}}) (*{{.type|raw}}, error) {
{{if .nonNamespaced}}
getter := r.Lister
{{else}}
getter := r.Lister.{{.type|apiGroup}}(resource.Namespace)
{{end}}
actual, err := getter.Get(resource.Name)
if err != nil {
return resource, err
}
// Don't modify the informers copy.
existing := actual.DeepCopy()
var finalizers []string
// If there's nothing to update, just return.
existingFinalizers := {{.setsNewString|raw}}(existing.Finalizers...)
desiredFinalizers := {{.setsNewString|raw}}(resource.Finalizers...)
if desiredFinalizers.Has(r.finalizerName) {
if existingFinalizers.Has(r.finalizerName) {
// Nothing to do.
return resource, nil
}
// Add the finalizer.
finalizers = append(existing.Finalizers, r.finalizerName)
} else {
if !existingFinalizers.Has(r.finalizerName) {
// Nothing to do.
return resource, nil
}
// Remove the finalizer.
existingFinalizers.Delete(r.finalizerName)
finalizers = existingFinalizers.List()
}
mergePatch := map[string]interface{}{
"metadata": map[string]interface{}{
"finalizers": finalizers,
"resourceVersion": existing.ResourceVersion,
},
}
patch, err := {{.jsonMarshal|raw}}(mergePatch)
if err != nil {
return resource, err
}
{{if .nonNamespaced}}
patcher := r.Client.{{.group}}{{.version}}().{{.type|apiGroup}}()
{{else}}
patcher := r.Client.{{.group}}{{.version}}().{{.type|apiGroup}}(resource.Namespace)
{{end}}
resourceName := resource.Name
updated, err := patcher.Patch(ctx, resourceName, {{.typesMergePatchType|raw}}, patch, {{.metav1PatchOptions|raw}}{})
if err != nil {
r.Recorder.Eventf(existing, {{.corev1EventTypeWarning|raw}}, "FinalizerUpdateFailed",
"Failed to update finalizers for %q: %v", resourceName, err)
} else {
r.Recorder.Eventf(updated, {{.corev1EventTypeNormal|raw}}, "FinalizerUpdate",
"Updated %q finalizers", resource.GetName())
}
return updated, err
}
func (r *reconcilerImpl) setFinalizerIfFinalizer(ctx {{.contextContext|raw}}, resource *{{.type|raw}}) (*{{.type|raw}}, error) {
if _, ok := r.reconciler.(Finalizer); !ok {
return resource, nil
}
finalizers := {{.setsNewString|raw}}(resource.Finalizers...)
// If this resource is not being deleted, mark the finalizer.
if resource.GetDeletionTimestamp().IsZero() {
finalizers.Insert(r.finalizerName)
}
resource.Finalizers = finalizers.List()
// Synchronize the finalizers filtered by r.finalizerName.
return r.updateFinalizersFiltered(ctx, resource)
}
func (r *reconcilerImpl) clearFinalizer(ctx {{.contextContext|raw}}, resource *{{.type|raw}}, reconcileEvent {{.reconcilerEvent|raw}}) (*{{.type|raw}}, error) {
if _, ok := r.reconciler.(Finalizer); !ok {
return resource, nil
}
if resource.GetDeletionTimestamp().IsZero() {
return resource, nil
}
finalizers := {{.setsNewString|raw}}(resource.Finalizers...)
if reconcileEvent != nil {
var event *{{.reconcilerReconcilerEvent|raw}}
if reconciler.EventAs(reconcileEvent, &event) {
if event.EventType == {{.corev1EventTypeNormal|raw}} {
finalizers.Delete(r.finalizerName)
}
}
} else {
finalizers.Delete(r.finalizerName)
}
resource.Finalizers = finalizers.List()
// Synchronize the finalizers filtered by r.finalizerName.
return r.updateFinalizersFiltered(ctx, resource)
}
`

View File

@ -0,0 +1,165 @@
/*
Copyright 2020 The Knative Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package generators
import (
"io"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/klog"
)
// reconcilerReconcilerStubGenerator produces a file of the stub of how to
// implement the reconciler.
type reconcilerReconcilerStubGenerator struct {
generator.DefaultGen
outputPackage string
imports namer.ImportTracker
typeToGenerate *types.Type
reconcilerPkg string
}
var _ generator.Generator = (*reconcilerReconcilerStubGenerator)(nil)
func (g *reconcilerReconcilerStubGenerator) Filter(c *generator.Context, t *types.Type) bool {
// Only process the type for this generator.
return t == g.typeToGenerate
}
func (g *reconcilerReconcilerStubGenerator) Namers(c *generator.Context) namer.NameSystems {
return namer.NameSystems{
"raw": namer.NewRawNamer(g.outputPackage, g.imports),
}
}
func (g *reconcilerReconcilerStubGenerator) Imports(c *generator.Context) (imports []string) {
imports = append(imports, g.imports.ImportLines()...)
return
}
func (g *reconcilerReconcilerStubGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
sw := generator.NewSnippetWriter(w, c, "{{", "}}")
klog.V(5).Info("processing type ", t)
m := map[string]interface{}{
"type": t,
"reconcilerEvent": c.Universe.Type(types.Name{
Package: "knative.dev/pkg/reconciler",
Name: "Event",
}),
"reconcilerNewEvent": c.Universe.Function(types.Name{
Package: "knative.dev/pkg/reconciler",
Name: "NewEvent",
}),
"reconcilerInterface": c.Universe.Type(types.Name{
Package: g.reconcilerPkg,
Name: "Interface",
}),
"reconcilerFinalizer": c.Universe.Type(types.Name{
Package: g.reconcilerPkg,
Name: "Finalizer",
}),
"reconcilerReadOnlyInterface": c.Universe.Type(types.Name{
Package: g.reconcilerPkg,
Name: "ReadOnlyInterface",
}),
"reconcilerReadOnlyFinalizer": c.Universe.Type(types.Name{
Package: g.reconcilerPkg,
Name: "ReadOnlyFinalizer",
}),
"corev1EventTypeNormal": c.Universe.Type(types.Name{
Package: "k8s.io/api/core/v1",
Name: "EventTypeNormal",
}),
"contextContext": c.Universe.Type(types.Name{
Package: "context",
Name: "Context",
}),
}
sw.Do(reconcilerReconcilerStub, m)
return sw.Error()
}
var reconcilerReconcilerStub = `
// TODO: PLEASE COPY AND MODIFY THIS FILE AS A STARTING POINT
// newReconciledNormal makes a new reconciler event with event type Normal, and
// reason {{.type|public}}Reconciled.
func newReconciledNormal(namespace, name string) reconciler.Event {
return {{.reconcilerNewEvent|raw}}({{.corev1EventTypeNormal|raw}}, "{{.type|public}}Reconciled", "{{.type|public}} reconciled: \"%s/%s\"", namespace, name)
}
// Reconciler implements controller.Reconciler for {{.type|public}} resources.
type Reconciler struct {
// TODO: add additional requirements here.
}
// Check that our Reconciler implements Interface
var _ {{.reconcilerInterface|raw}} = (*Reconciler)(nil)
// Optionally check that our Reconciler implements Finalizer
//var _ {{.reconcilerFinalizer|raw}} = (*Reconciler)(nil)
// Optionally check that our Reconciler implements ReadOnlyInterface
// Implement this to observe resources even when we are not the leader.
//var _ {{.reconcilerReadOnlyInterface|raw}} = (*Reconciler)(nil)
// Optionally check that our Reconciler implements ReadOnlyFinalizer
// Implement this to observe tombstoned resources even when we are not
// the leader (best effort).
//var _ {{.reconcilerReadOnlyFinalizer|raw}} = (*Reconciler)(nil)
// ReconcileKind implements Interface.ReconcileKind.
func (r *Reconciler) ReconcileKind(ctx {{.contextContext|raw}}, o *{{.type|raw}}) {{.reconcilerEvent|raw}} {
{{if not .isKRShaped}}// TODO: use this if the resource implements InitializeConditions.
// o.Status.InitializeConditions()
{{end}}
// TODO: add custom reconciliation logic here.
{{if not .isKRShaped}}
// TODO: use this if the object has .status.ObservedGeneration.
// o.Status.ObservedGeneration = o.Generation
{{end}}
return nil
}
// Optionally, use FinalizeKind to add finalizers. FinalizeKind will be called
// when the resource is deleted.
//func (r *Reconciler) FinalizeKind(ctx {{.contextContext|raw}}, o *{{.type|raw}}) {{.reconcilerEvent|raw}} {
// // TODO: add custom finalization logic here.
// return nil
//}
// Optionally, use ObserveKind to observe the resource when we are not the leader.
// func (r *Reconciler) ObserveKind(ctx {{.contextContext|raw}}, o *{{.type|raw}}) {{.reconcilerEvent|raw}} {
// // TODO: add custom observation logic here.
// return nil
// }
// Optionally, use ObserveFinalizeKind to observe resources being finalized when we are no the leader.
//func (r *Reconciler) ObserveFinalizeKind(ctx {{.contextContext|raw}}, o *{{.type|raw}}) {{.reconcilerEvent|raw}} {
// // TODO: add custom observation logic here.
// return nil
//}
`

View File

@ -0,0 +1,179 @@
/*
Copyright 2020 The Knative Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package generators
import (
"io"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/types"
"k8s.io/klog"
)
// reconcilerStateGenerator produces a reconciler state object to manage reconciliation runs.
type reconcilerStateGenerator struct {
generator.DefaultGen
outputPackage string
imports namer.ImportTracker
typeToGenerate *types.Type
}
var _ generator.Generator = (*reconcilerStateGenerator)(nil)
func (g *reconcilerStateGenerator) Filter(c *generator.Context, t *types.Type) bool {
// Only process the type for this generator.
return t == g.typeToGenerate
}
func (g *reconcilerStateGenerator) Namers(c *generator.Context) namer.NameSystems {
return namer.NameSystems{
"raw": namer.NewRawNamer(g.outputPackage, g.imports),
}
}
func (g *reconcilerStateGenerator) Imports(c *generator.Context) (imports []string) {
imports = append(imports, g.imports.ImportLines()...)
return
}
func (g *reconcilerStateGenerator) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
sw := generator.NewSnippetWriter(w, c, "{{", "}}")
klog.V(5).Info("processing type ", t)
m := map[string]interface{}{
"type": t,
// methods
"cacheSplitMetaNamespaceKey": c.Universe.Function(types.Name{
Package: "k8s.io/client-go/tools/cache",
Name: "SplitMetaNamespaceKey",
}),
"fmtErrorf": c.Universe.Package("fmt").Function("Errorf"),
"reconcilerLeaderAware": c.Universe.Type(types.Name{
Package: "knative.dev/pkg/reconciler",
Name: "LeaderAware",
}),
"typesNamespacedName": c.Universe.Type(types.Name{
Package: "k8s.io/apimachinery/pkg/types",
Name: "NamespacedName",
}),
"doReconcileKind": c.Universe.Type(types.Name{
Package: "knative.dev/pkg/reconciler",
Name: "DoReconcileKind",
}),
"doObserveKind": c.Universe.Type(types.Name{
Package: "knative.dev/pkg/reconciler",
Name: "DoObserveKind",
}),
"doFinalizeKind": c.Universe.Type(types.Name{
Package: "knative.dev/pkg/reconciler",
Name: "DoFinalizeKind",
}),
"doObserveFinalizeKind": c.Universe.Type(types.Name{
Package: "knative.dev/pkg/reconciler",
Name: "DoObserveFinalizeKind",
}),
}
sw.Do(reconcilerStateType, m)
sw.Do(reconcilerStateMethods, m)
return sw.Error()
}
var reconcilerStateType = `
// state is used to track the state of a reconciler in a single run.
type state struct {
// Key is the original reconciliation key from the queue.
key string
// Namespace is the namespace split from the reconciliation key.
namespace string
// Namespace is the name split from the reconciliation key.
name string
// reconciler is the reconciler.
reconciler Interface
// rof is the read only interface cast of the reconciler.
roi ReadOnlyInterface
// IsROI (Read Only Interface) the reconciler only observes reconciliation.
isROI bool
// rof is the read only finalizer cast of the reconciler.
rof ReadOnlyFinalizer
// IsROF (Read Only Finalizer) the reconciler only observes finalize.
isROF bool
// IsLeader the instance of the reconciler is the elected leader.
isLeader bool
}
func newState(key string, r *reconcilerImpl) (*state, error) {
// Convert the namespace/name string into a distinct namespace and name
namespace, name, err := {{.cacheSplitMetaNamespaceKey|raw}}(key)
if err != nil {
return nil, {{.fmtErrorf|raw}}("invalid resource key: %s", key)
}
roi, isROI := r.reconciler.(ReadOnlyInterface)
rof, isROF := r.reconciler.(ReadOnlyFinalizer)
isLeader := r.IsLeaderFor({{.typesNamespacedName|raw}}{
Namespace: namespace,
Name: name,
})
return &state{
key: key,
namespace: namespace,
name: name,
reconciler: r.reconciler,
roi: roi,
isROI: isROI,
rof: rof,
isROF: isROF,
isLeader: isLeader,
}, nil
}
`
var reconcilerStateMethods = `
// isNotLeaderNorObserver checks to see if this reconciler with the current
// state is enabled to do any work or not.
// isNotLeaderNorObserver returns true when there is no work possible for the
// reconciler.
func (s *state) isNotLeaderNorObserver() bool {
if !s.isLeader && !s.isROI && !s.isROF {
// If we are not the leader, and we don't implement either ReadOnly
// interface, then take a fast-path out.
return true
}
return false
}
func (s *state) reconcileMethodFor(o *{{.type|raw}}) (string, doReconcile) {
if o.GetDeletionTimestamp().IsZero() {
if s.isLeader {
return {{.doReconcileKind|raw}}, s.reconciler.ReconcileKind
} else if s.isROI {
return {{.doObserveKind|raw}}, s.roi.ObserveKind
}
} else if fin, ok := s.reconciler.(Finalizer); s.isLeader && ok {
return {{.doFinalizeKind|raw}}, fin.FinalizeKind
} else if !s.isLeader && s.isROF {
return {{.doObserveFinalizeKind|raw}}, s.rof.ObserveFinalizeKind
}
return "unknown", nil
}
`

View File

@ -0,0 +1,59 @@
/*
Copyright 2019 The Knative Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"flag"
"path/filepath"
"k8s.io/code-generator/pkg/util"
"k8s.io/gengo/args"
"k8s.io/klog"
"github.com/spf13/pflag"
generatorargs "knative.dev/pkg/codegen/cmd/injection-gen/args"
"knative.dev/pkg/codegen/cmd/injection-gen/generators"
)
func main() {
klog.InitFlags(nil)
genericArgs, customArgs := generatorargs.NewDefaults()
// Override defaults.
genericArgs.GoHeaderFilePath = filepath.Join(args.DefaultSourceTree(), util.BoilerplatePath())
genericArgs.OutputPackagePath = "k8s.io/kubernetes/pkg/client/injection/informers/informers_generated"
genericArgs.AddFlags(pflag.CommandLine)
customArgs.AddFlags(pflag.CommandLine)
flag.Set("logtostderr", "true")
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
pflag.Parse()
if err := generatorargs.Validate(genericArgs); err != nil {
klog.Fatal("Error: ", err)
}
// Run it.
if err := genericArgs.Execute(
generators.NameSystems(),
generators.DefaultNameSystem(),
generators.Packages,
); err != nil {
klog.Fatal("Error: ", err)
}
klog.V(2).Info("Completed successfully.")
}

View File

@ -0,0 +1,92 @@
#!/usr/bin/env bash
# Copyright 2019 The Knative Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit
set -o nounset
set -o pipefail
# generate-groups generates everything for a project with external types only, e.g. a project based
# on CustomResourceDefinitions.
if [ "$#" -lt 4 ] || [ "${1}" == "--help" ]; then
cat <<EOF
Usage: $(basename $0) <generators> <client-package> <apis-package> <groups-versions> ...
<generators> the generators comma separated to run (deepcopy,defaulter,client,lister,informer) or "all".
<client-package> the client package dir (e.g. github.com/example/project/pkg/clientset).
<apis-package> the external types dir (e.g. github.com/example/api or github.com/example/project/pkg/apis).
<groups-versions> the groups and their versions in the format "groupA:v1,v2 groupB:v1 groupC:v2", relative
to <api-package>.
... arbitrary flags passed to all generator binaries.
Examples:
$(basename $0) all github.com/example/project/pkg/client github.com/example/project/pkg/apis "foo:v1 bar:v1alpha1,v1beta1"
$(basename $0) injection,foo github.com/example/project/pkg/client github.com/example/project/pkg/apis "foo:v1 bar:v1alpha1,v1beta1"
EOF
exit 0
fi
GENS="$1"
CLIENT_PKG="$2"
APIS_PKG="$3"
GROUPS_WITH_VERSIONS="$4"
shift 4
function codegen::join() { local IFS="$1"; shift; echo "$*"; }
# enumerate group versions
FQ_APIS=() # e.g. k8s.io/api/apps/v1
for GVs in ${GROUPS_WITH_VERSIONS}; do
IFS=: read G Vs <<<"${GVs}"
# enumerate versions
for V in ${Vs//,/ }; do
FQ_APIS+=(${APIS_PKG}/${G}/${V})
done
done
if grep -qw "injection" <<<"${GENS}"; then
if [[ -z "${OUTPUT_PKG:-}" ]]; then
OUTPUT_PKG="${CLIENT_PKG}/injection"
fi
if [[ -z "${VERSIONED_CLIENTSET_PKG:-}" ]]; then
VERSIONED_CLIENTSET_PKG="${CLIENT_PKG}/clientset/versioned"
fi
if [[ -z "${EXTERNAL_INFORMER_PKG:-}" ]]; then
EXTERNAL_INFORMER_PKG="${CLIENT_PKG}/informers/externalversions"
fi
if [[ -z "${LISTERS_PKG:-}" ]]; then
LISTERS_PKG="${CLIENT_PKG}/listers"
fi
echo "Generating injection for ${GROUPS_WITH_VERSIONS} at ${OUTPUT_PKG}"
# Clear old injection
rm -rf ${OUTPUT_PKG}
go run knative.dev/pkg/codegen/cmd/injection-gen \
--input-dirs $(codegen::join , "${FQ_APIS[@]}") \
--versioned-clientset-package ${VERSIONED_CLIENTSET_PKG} \
--external-versions-informers-package ${EXTERNAL_INFORMER_PKG} \
--listers-package ${LISTERS_PKG} \
--output-package ${OUTPUT_PKG} \
"$@"
fi

32
vendor/knative.dev/pkg/hack/tools.go vendored Normal file
View File

@ -0,0 +1,32 @@
// +build tools
/*
Copyright 2020 The Knative Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package tools
// This package imports things required by this repository, to force `go mod` to see them as dependencies
import (
_ "k8s.io/code-generator"
_ "k8s.io/code-generator/cmd/client-gen"
_ "k8s.io/code-generator/cmd/deepcopy-gen"
_ "k8s.io/code-generator/cmd/defaulter-gen"
_ "k8s.io/code-generator/cmd/informer-gen"
_ "k8s.io/code-generator/cmd/lister-gen"
_ "knative.dev/hack"
_ "knative.dev/pkg/codegen/cmd/injection-gen"
)

View File

@ -0,0 +1,86 @@
#!/usr/bin/env bash
# Copyright 2018 The Knative Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit
set -o nounset
set -o pipefail
export GO111MODULE=on
# If we run with -mod=vendor here, then generate-groups.sh looks for vendor files in the wrong place.
export GOFLAGS=-mod=
if [ -z "${GOPATH:-}" ]; then
export GOPATH=$(go env GOPATH)
fi
source $(dirname $0)/../vendor/knative.dev/hack/library.sh
CODEGEN_PKG=${CODEGEN_PKG:-$(cd ${REPO_ROOT_DIR}; ls -d -1 $(dirname $0)/../vendor/k8s.io/code-generator 2>/dev/null || echo ../code-generator)}
# generate the code with:
# --output-base because this script should also be able to run inside the vendor dir of
# k8s.io/kubernetes. The output-base is needed for the generators to output into the vendor dir
# instead of the $GOPATH directly. For normal projects this can be dropped.
# Knative Injection
${REPO_ROOT_DIR}/hack/generate-knative.sh "injection" \
knative.dev/pkg/client knative.dev/pkg/apis \
"duck:v1alpha1,v1beta1,v1" \
--go-header-file ${REPO_ROOT_DIR}/hack/boilerplate/boilerplate.go.txt
OUTPUT_PKG="knative.dev/pkg/client/injection/kube" \
VERSIONED_CLIENTSET_PKG="k8s.io/client-go/kubernetes" \
EXTERNAL_INFORMER_PKG="k8s.io/client-go/informers" \
${REPO_ROOT_DIR}/hack/generate-knative.sh "injection" \
k8s.io/client-go \
k8s.io/api \
"admissionregistration:v1beta1,v1 apps:v1 autoscaling:v1,v2beta1 batch:v1,v1beta1 core:v1 rbac:v1 coordination:v1" \
--go-header-file ${REPO_ROOT_DIR}/hack/boilerplate/boilerplate.go.txt \
--force-genreconciler-kinds "Namespace,Deployment"
OUTPUT_PKG="knative.dev/pkg/client/injection/apiextensions" \
VERSIONED_CLIENTSET_PKG="k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" \
${REPO_ROOT_DIR}/hack/generate-knative.sh "injection" \
k8s.io/apiextensions-apiserver/pkg/client \
k8s.io/apiextensions-apiserver/pkg/apis \
"apiextensions:v1beta1,v1" \
--go-header-file ${REPO_ROOT_DIR}/hack/boilerplate/boilerplate.go.txt \
--force-genreconciler-kinds "CustomResourceDefinition"
# Only deepcopy the Duck types, as they are not real resources.
chmod +x ${CODEGEN_PKG}/generate-groups.sh
${CODEGEN_PKG}/generate-groups.sh "deepcopy" \
knative.dev/pkg/client knative.dev/pkg/apis \
"duck:v1alpha1,v1beta1,v1" \
--go-header-file ${REPO_ROOT_DIR}/hack/boilerplate/boilerplate.go.txt
# Depends on generate-groups.sh to install bin/deepcopy-gen
go run k8s.io/code-generator/cmd/deepcopy-gen --input-dirs \
$(echo \
knative.dev/pkg/apis \
knative.dev/pkg/tracker \
knative.dev/pkg/logging \
knative.dev/pkg/metrics \
knative.dev/pkg/testing \
knative.dev/pkg/testing/duck \
knative.dev/pkg/webhook/resourcesemantics/conversion/internal \
| sed "s/ /,/g") \
-O zz_generated.deepcopy \
--go-header-file ${REPO_ROOT_DIR}/hack/boilerplate/boilerplate.go.txt
# Make sure our dependencies are up-to-date
${REPO_ROOT_DIR}/hack/update-deps.sh

View File

@ -0,0 +1,23 @@
#!/usr/bin/env bash
# Copyright 2018 The Knative Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit
set -o nounset
set -o pipefail
source $(dirname "$0")/../vendor/knative.dev/hack/library.sh
go_update_deps "$@"

View File

@ -0,0 +1,29 @@
#!/usr/bin/env bash
# Copyright 2020 The Knative Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit
set -o nounset
set -o pipefail
source $(dirname "$0")/../vendor/knative.dev/hack/library.sh
run_go_tool knative.dev/test-infra/buoy \
buoy float ${REPO_ROOT_DIR}/go.mod \
--release "$1" --domain k8s.io --ruleset=Release \
| xargs -n1 -t go get -d
./hack/update-deps.sh

View File

@ -0,0 +1,84 @@
#!/usr/bin/env bash
# Copyright 2018 The Knative Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit
set -o nounset
set -o pipefail
source $(dirname $0)/../vendor/knative.dev/hack/library.sh
readonly TMP_DIFFROOT="$(mktemp -d ${REPO_ROOT_DIR}/tmpdiffroot.XXXXXX)"
cleanup() {
rm -rf "${TMP_DIFFROOT}"
}
trap "cleanup" EXIT SIGINT
cleanup
# Save working tree state
mkdir -p "${TMP_DIFFROOT}"
cp -aR \
"${REPO_ROOT_DIR}/go.sum" \
"${REPO_ROOT_DIR}/apis" \
"${REPO_ROOT_DIR}/logging" \
"${REPO_ROOT_DIR}/metrics" \
"${REPO_ROOT_DIR}/testing" \
"${REPO_ROOT_DIR}/vendor" \
"${TMP_DIFFROOT}"
"${REPO_ROOT_DIR}/hack/update-codegen.sh"
echo "Diffing ${REPO_ROOT_DIR} against freshly generated codegen"
ret=0
diff -Naupr --no-dereference \
"${REPO_ROOT_DIR}/go.sum" "${TMP_DIFFROOT}/go.sum" || ret=1
diff -Naupr --no-dereference \
"${REPO_ROOT_DIR}/apis" "${TMP_DIFFROOT}/apis" || ret=1
diff -Naupr --no-dereference \
"${REPO_ROOT_DIR}/logging" "${TMP_DIFFROOT}/logging" || ret=1
diff -Naupr --no-dereference \
"${REPO_ROOT_DIR}/metrics" "${TMP_DIFFROOT}/metrics" || ret=1
diff -Naupr --no-dereference \
"${REPO_ROOT_DIR}/testing" "${TMP_DIFFROOT}/testing" || ret=1
diff -Naupr --no-dereference \
"${REPO_ROOT_DIR}/vendor" "${TMP_DIFFROOT}/vendor" || ret=1
# Restore working tree state
rm -fr \
"${REPO_ROOT_DIR}/go.sum" \
"${REPO_ROOT_DIR}/apis" \
"${REPO_ROOT_DIR}/logging" \
"${REPO_ROOT_DIR}/metrics" \
"${REPO_ROOT_DIR}/testing" \
"${REPO_ROOT_DIR}/vendor"
cp -aR "${TMP_DIFFROOT}"/* "${REPO_ROOT_DIR}"
if [[ $ret -eq 0 ]]
then
echo "${REPO_ROOT_DIR} up to date."
else
echo "${REPO_ROOT_DIR} is out of date. Please run hack/update-codegen.sh"
exit 1
fi

4
vendor/modules.txt vendored
View File

@ -891,8 +891,12 @@ knative.dev/pkg/apis/duck/v1alpha1
knative.dev/pkg/apis/duck/v1beta1 knative.dev/pkg/apis/duck/v1beta1
knative.dev/pkg/changeset knative.dev/pkg/changeset
knative.dev/pkg/client/injection/ducks/duck/v1/addressable knative.dev/pkg/client/injection/ducks/duck/v1/addressable
knative.dev/pkg/codegen/cmd/injection-gen
knative.dev/pkg/codegen/cmd/injection-gen/args
knative.dev/pkg/codegen/cmd/injection-gen/generators
knative.dev/pkg/configmap knative.dev/pkg/configmap
knative.dev/pkg/controller knative.dev/pkg/controller
knative.dev/pkg/hack
knative.dev/pkg/hash knative.dev/pkg/hash
knative.dev/pkg/injection knative.dev/pkg/injection
knative.dev/pkg/injection/clients/dynamicclient knative.dev/pkg/injection/clients/dynamicclient