diff --git a/apis/test/example/v1alpha1/foo_types.go b/apis/test/example/v1alpha1/foo_types.go index 22ac876ffd..6adc0dd19f 100644 --- a/apis/test/example/v1alpha1/foo_types.go +++ b/apis/test/example/v1alpha1/foo_types.go @@ -28,6 +28,7 @@ import ( // +genclient // +genreconciler +// +genducklogic // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // Foo is for testing. diff --git a/codegen/cmd/injection-gen/generators/packages.go b/codegen/cmd/injection-gen/generators/packages.go index ce8b6dbc68..841b57ac9e 100644 --- a/codegen/cmd/injection-gen/generators/packages.go +++ b/codegen/cmd/injection-gen/generators/packages.go @@ -206,6 +206,12 @@ func isNonNamespaced(t *types.Type) bool { return nonNamespaced } +func genDuckLogic(t *types.Type) bool { + comments := append(append([]string{}, t.SecondClosestCommentLines...), t.CommentLines...) + _, duckLogic := types.ExtractCommentTags("+", comments)["genducklogic"] + return duckLogic +} + func vendorless(p string) string { if pos := strings.LastIndex(p, "/vendor/"); pos != -1 { return p[pos+len("/vendor/"):] @@ -418,6 +424,7 @@ func reconcilerPackages(basePackage string, groupPkgName string, gv clientgentyp reconcilerClass, hasReconcilerClass := extractReconcilerClassTag(t) nonNamespaced := isNonNamespaced(t) + genDuck := genDuckLogic(t) packagePath := filepath.Join(packagePath, strings.ToLower(t.Name.Name)) @@ -506,6 +513,7 @@ func reconcilerPackages(basePackage string, groupPkgName string, gv clientgentyp reconcilerClass: reconcilerClass, hasReconcilerClass: hasReconcilerClass, nonNamespaced: nonNamespaced, + genDuckLogic: genDuck, }) return generators diff --git a/codegen/cmd/injection-gen/generators/reconciler_reconciler.go b/codegen/cmd/injection-gen/generators/reconciler_reconciler.go index e24cc321f9..eb555add3e 100644 --- a/codegen/cmd/injection-gen/generators/reconciler_reconciler.go +++ b/codegen/cmd/injection-gen/generators/reconciler_reconciler.go @@ -40,6 +40,7 @@ type reconcilerReconcilerGenerator struct { reconcilerClass string hasReconcilerClass bool nonNamespaced bool + genDuckLogic bool groupGoName string groupVersion clientgentypes.GroupVersion @@ -75,6 +76,7 @@ func (g *reconcilerReconcilerGenerator) GenerateType(c *generator.Context, t *ty "class": g.reconcilerClass, "hasClass": g.hasReconcilerClass, "nonNamespaced": g.nonNamespaced, + "genDuckLogic": g.genDuckLogic, "controllerImpl": c.Universe.Type(types.Name{ Package: "knative.dev/pkg/controller", Name: "Impl", @@ -106,6 +108,7 @@ func (g *reconcilerReconcilerGenerator) GenerateType(c *generator.Context, t *ty // Deps "clientsetInterface": c.Universe.Type(types.Name{Name: "Interface", Package: g.clientsetPkg}), "resourceLister": c.Universe.Type(types.Name{Name: g.listerName, Package: g.listerPkg}), + "conditionSet": c.Universe.Type(types.Name{Name: "ConditionSet", Package: "knative.dev/pkg/apis"}), // K8s types "recordEventRecorder": c.Universe.Type(types.Name{Name: "EventRecorder", Package: "k8s.io/client-go/tools/record"}), // methods @@ -211,6 +214,11 @@ type reconcilerImpl struct { // Check that our Reconciler implements controller.Reconciler var _ controller.Reconciler = (*reconcilerImpl)(nil) +{{if .genDuckLogic}} +// Creates the {{.conditionSet|raw}} to manipulate resource status conditions +var condSet = apis.NewLivingConditionSet() +{{end}} + ` var reconcilerNewReconciler = ` @@ -320,6 +328,24 @@ func (r *reconcilerImpl) Reconcile(ctx {{.contextContext|raw}}, key string) erro } } + {{if .genDuckLogic}} + // Bump observed generation to denote that we have processed this + // generation regardless of success or failure. + resource.Status.ObservedGeneration = resource.Generation + + if resource.Status.ObservedGeneration != original.Status.ObservedGeneration && reconcileEvent != nil { + originalRc := original.Status.GetCondition(apis.ConditionReady) + rc := resource.Status.GetCondition(apis.ConditionReady) + // if a new generation is observed and reconciliation reported an error event + // the reconciler should change the ready state. By default we will set unknown. + if equality.Semantic.DeepEqual(originalRc, rc) { + logger.Warnw("A reconconiler observed a new generation without updating the resource status") + condSet.Manage(&resource.Status).MarkUnknown( + apis.ConditionReady, "", "unsucessfully observed a new generation") + } + } + {{end}} + // Synchronize the status. if equality.Semantic.DeepEqual(original.Status, resource.Status) { // If we didn't change anything then don't call updateStatus.