Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apis/test/pub/v1alpha1/bar_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (
)

// +genclient
// +genreconciler
// +genreconciler:class=example.com/filter.class
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// Bar is for testing.
Expand Down
46 changes: 36 additions & 10 deletions codegen/cmd/injection-gen/generators/packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,31 @@ func MustParseClientGenTags(lines []string) Tags {
}

values := types.ExtractCommentTags("+", lines)
// log.Printf("GOT values %v", values)

_, ret.GenerateDuck = values["genduck"]
_, ret.GenerateReconciler = values["genreconciler"]

_, genRec := values["genreconciler"]
_, genRecClass := values["genreconciler:class"]
// Generate Reconciler code if genreconciler OR genreconciler:class exist.
if genRec || genRecClass {
ret.GenerateReconciler = true
}

return ret
}

func extractReconcilerClassTag(t *types.Type) (string, bool) {
comments := append(append([]string{}, t.SecondClosestCommentLines...), t.CommentLines...)
values := types.ExtractCommentTags("+", comments)["genreconciler:class"]
for _, v := range values {
if len(v) == 0 {
continue
}
return v, true
}
return "", false
}

// isInternal returns true if the tags for a member do not contain a json tag
func isInternal(m types.Member) bool {
return !strings.Contains(m.Tags, "json")
Expand Down Expand Up @@ -387,6 +405,8 @@ func reconcilerPackages(basePackage string, groupPkgName string, gv clientgentyp
// Fix for golang iterator bug.
t := t

reconcilerClass, hasReconcilerClass := extractReconcilerClassTag(t)

packagePath := filepath.Join(packagePath, strings.ToLower(t.Name.Name))

clientPackagePath := filepath.Join(basePackage, "client")
Expand All @@ -412,6 +432,8 @@ func reconcilerPackages(basePackage string, groupPkgName string, gv clientgentyp
clientPkg: clientPackagePath,
informerPackagePath: informerPackagePath,
schemePkg: filepath.Join(customArgs.VersionedClientSetPackage, "scheme"),
reconcilerClass: reconcilerClass,
hasReconcilerClass: hasReconcilerClass,
})

return generators
Expand All @@ -438,6 +460,8 @@ func reconcilerPackages(basePackage string, groupPkgName string, gv clientgentyp
outputPackage: filepath.Join(packagePath, "stub"),
imports: generator.NewImportTracker(),
informerPackagePath: informerPackagePath,
reconcilerClass: reconcilerClass,
hasReconcilerClass: hasReconcilerClass,
})

return generators
Expand All @@ -459,14 +483,16 @@ func reconcilerPackages(basePackage string, groupPkgName string, gv clientgentyp
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,
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,
})

return generators
Expand Down
13 changes: 10 additions & 3 deletions codegen/cmd/injection-gen/generators/reconciler_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ type reconcilerControllerGenerator struct {
clientPkg string
schemePkg string
informerPackagePath string

reconcilerClass string
hasReconcilerClass bool
}

var _ generator.Generator = (*reconcilerControllerGenerator)(nil)
Expand All @@ -63,8 +66,10 @@ func (g *reconcilerControllerGenerator) GenerateType(c *generator.Context, t *ty
klog.V(5).Infof("processing type %v", t)

m := map[string]interface{}{
"type": t,
"group": g.groupName,
"type": t,
"group": g.groupName,
"class": g.reconcilerClass,
"hasClass": g.hasReconcilerClass,
"controllerImpl": c.Universe.Type(types.Name{
Package: "knative.dev/pkg/controller",
Name: "Impl",
Expand Down Expand Up @@ -149,13 +154,14 @@ const (
defaultControllerAgentName = "{{.type|lowercaseSingular}}-controller"
defaultFinalizerName = "{{.type|allLowercasePlural}}.{{.group}}"
defaultQueueName = "{{.type|allLowercasePlural}}"
{{if .hasClass}}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, optionsFns ...{{.controllerOptionsFn|raw}}) *{{.controllerImpl|raw}} {
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.
Expand Down Expand Up @@ -189,6 +195,7 @@ func NewImpl(ctx {{.contextContext|raw}}, r Interface, optionsFns ...{{.controll
Lister: {{.type|lowercaseSingular}}Informer.Lister(),
Recorder: recorder,
reconciler: r,
{{if .hasClass}}classValue: classValue,{{end}}
}
impl := {{.controllerNewImpl|raw}}(rec, logger, defaultQueueName)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ type reconcilerControllerStubGenerator struct {

reconcilerPkg string
informerPackagePath string
reconcilerClass string
hasReconcilerClass bool
}

var _ generator.Generator = (*reconcilerControllerStubGenerator)(nil)
Expand All @@ -61,7 +63,9 @@ func (g *reconcilerControllerStubGenerator) GenerateType(c *generator.Context, t
klog.V(5).Infof("processing type %v", t)

m := map[string]interface{}{
"type": t,
"type": t,
"class": g.reconcilerClass,
"hasClass": g.hasReconcilerClass,
"informerGet": c.Universe.Function(types.Name{
Package: g.informerPackagePath,
Name: "Get",
Expand Down Expand Up @@ -103,9 +107,10 @@ func NewController(
{{.type|lowercaseSingular}}Informer := {{.informerGet|raw}}(ctx)

// TODO: setup additional informers here.
{{if .hasClass}}// TODO: pass in the expected value for the class annotation filter.{{end}}

r := &Reconciler{}
impl := {{.reconcilerNewImpl|raw}}(ctx, r)
impl := {{.reconcilerNewImpl|raw}}(ctx, r{{if .hasClass}}, "default"{{end}})

logger.Info("Setting up event handlers.")

Expand Down
28 changes: 24 additions & 4 deletions codegen/cmd/injection-gen/generators/reconciler_reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ type reconcilerReconcilerGenerator struct {
listerName string
listerPkg string

reconcilerClass string
hasReconcilerClass bool

groupGoName string
groupVersion clientgentypes.GroupVersion
}
Expand Down Expand Up @@ -65,9 +68,11 @@ func (g *reconcilerReconcilerGenerator) GenerateType(c *generator.Context, t *ty
klog.V(5).Infof("processing type %v", t)

m := map[string]interface{}{
"type": t,
"group": namer.IC(g.groupGoName),
"version": namer.IC(g.groupVersion.Version.String()),
"type": t,
"group": namer.IC(g.groupGoName),
"version": namer.IC(g.groupVersion.Version.String()),
"class": g.reconcilerClass,
"hasClass": g.hasReconcilerClass,
"controllerImpl": c.Universe.Type(types.Name{
Package: "knative.dev/pkg/controller",
Name: "Impl",
Expand Down Expand Up @@ -191,6 +196,11 @@ type reconcilerImpl struct {

// reconciler is the implementation of the business logic of the resource.
reconciler Interface

{{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
Expand All @@ -199,7 +209,7 @@ var _ controller.Reconciler = (*reconcilerImpl)(nil)
`

var reconcilerNewReconciler = `
func NewReconciler(ctx {{.contextContext|raw}}, logger *{{.zapSugaredLogger|raw}}, client {{.clientsetInterface|raw}}, lister {{.resourceLister|raw}}, recorder {{.recordEventRecorder|raw}}, r Interface, options ...{{.controllerOptions|raw}} ) {{.controllerReconciler|raw}} {
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.Fatalf("up to one options struct is supported, found %d", len(options))
Expand All @@ -210,6 +220,7 @@ func NewReconciler(ctx {{.contextContext|raw}}, logger *{{.zapSugaredLogger|raw}
Lister: lister,
Recorder: recorder,
reconciler: r,
{{if .hasClass}}classValue: classValue,{{end}}
}

for _, opts := range options {
Expand Down Expand Up @@ -251,6 +262,15 @@ func (r *reconcilerImpl) Reconcile(ctx {{.contextContext|raw}}, key string) erro
} 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()

Expand Down
29 changes: 29 additions & 0 deletions injection/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,35 @@ var _ addressableservicereconciler.Interface = (*Reconciler)(nil)
var _ addressableservicereconciler.Finalizer = (*Reconciler)(nil)
```

#### Annotation based class filters

Sometimes a reconciler only wants to reconcile a class of resource identified by
a special annotation on the Custom Resource.

This behavior can be enabled in the generators by adding the annotation class
key to the type struct:

```go
// +genreconciler:class=example.com/filter.class
```

The `genreconciler` generator code will now have the addition of
`classValue string` to `NewImpl` and `NewReconciler` (for tests):

```go
NewImpl(ctx context.Context, r Interface, classValue string, optionsFns ...controller.OptionsFn) *controller.Impl
```

```go
NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client versioned.Interface, lister pubv1alpha1.BarLister, recorder record.EventRecorder, r Interface, classValue string, options ...controller.Options) controller.Reconciler
```

`ReconcileKind` and `FinalizeKind` will NOT be called for resources that DO NOT
have the provided `+genreconciler:class=<key>` key annotation. Additionally the
value of the `<key>` annotation on a resource must match the value provided to
`NewImpl` (or `NewReconcile`) for `ReconcileKind` or `FinalizeKind` to be called
for that resource.

#### Stubs

To get started, or to use as reference. It is intended to be copied out of the
Expand Down