From ca02ef752ac658f5a315b619e56ec2f87fafdeff Mon Sep 17 00:00:00 2001 From: Francesco Guardiani Date: Sat, 30 Jan 2021 01:18:31 +0100 Subject: [PATCH] Genreconciler properly generates reconciler for Resources with Status (#2004) * Fix #2003 Signed-off-by: Francesco Guardiani * Removed useless newline Signed-off-by: Francesco Guardiani * Removed useless check Signed-off-by: Francesco Guardiani * Now we don't have any newlines anymore Signed-off-by: Francesco Guardiani --- .../reconciler/core/v1/secret/controller.go | 145 +++++++ .../reconciler/core/v1/secret/reconciler.go | 372 ++++++++++++++++++ .../kube/reconciler/core/v1/secret/state.go | 106 +++++ .../cmd/injection-gen/generators/packages.go | 11 + .../generators/reconciler_controller.go | 12 +- .../generators/reconciler_reconciler.go | 12 +- hack/update-codegen.sh | 2 +- 7 files changed, 654 insertions(+), 6 deletions(-) create mode 100644 client/injection/kube/reconciler/core/v1/secret/controller.go create mode 100644 client/injection/kube/reconciler/core/v1/secret/reconciler.go create mode 100644 client/injection/kube/reconciler/core/v1/secret/state.go diff --git a/client/injection/kube/reconciler/core/v1/secret/controller.go b/client/injection/kube/reconciler/core/v1/secret/controller.go new file mode 100644 index 000000000..50b912089 --- /dev/null +++ b/client/injection/kube/reconciler/core/v1/secret/controller.go @@ -0,0 +1,145 @@ +/* +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. +*/ + +// Code generated by injection-gen. DO NOT EDIT. + +package secret + +import ( + context "context" + fmt "fmt" + reflect "reflect" + strings "strings" + + zap "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + scheme "k8s.io/client-go/kubernetes/scheme" + v1 "k8s.io/client-go/kubernetes/typed/core/v1" + record "k8s.io/client-go/tools/record" + client "knative.dev/pkg/client/injection/kube/client" + secret "knative.dev/pkg/client/injection/kube/informers/core/v1/secret" + controller "knative.dev/pkg/controller" + logging "knative.dev/pkg/logging" + logkey "knative.dev/pkg/logging/logkey" + reconciler "knative.dev/pkg/reconciler" +) + +const ( + defaultControllerAgentName = "secret-controller" + defaultFinalizerName = "secrets.core" +) + +// NewImpl returns a controller.Impl that handles queuing and feeding work from +// the queue through an implementation of controller.Reconciler, delegating to +// the provided Interface and optional Finalizer methods. OptionsFn is used to return +// controller.Options to be used but the internal reconciler. +func NewImpl(ctx context.Context, r Interface, optionsFns ...controller.OptionsFn) *controller.Impl { + logger := logging.FromContext(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)) + } + + secretInformer := secret.Get(ctx) + + lister := secretInformer.Lister() + + rec := &reconcilerImpl{ + LeaderAwareFuncs: reconciler.LeaderAwareFuncs{ + PromoteFunc: func(bkt reconciler.Bucket, enq func(reconciler.Bucket, types.NamespacedName)) error { + all, err := lister.List(labels.Everything()) + if err != nil { + return err + } + for _, elt := range all { + // TODO: Consider letting users specify a filter in options. + enq(bkt, types.NamespacedName{ + Namespace: elt.GetNamespace(), + Name: elt.GetName(), + }) + } + return nil + }, + }, + Client: client.Get(ctx), + Lister: lister, + reconciler: r, + finalizerName: defaultFinalizerName, + } + + ctrType := reflect.TypeOf(r).Elem() + ctrTypeName := fmt.Sprintf("%s.%s", ctrType.PkgPath(), ctrType.Name()) + ctrTypeName = strings.ReplaceAll(ctrTypeName, "/", ".") + + logger = logger.With( + zap.String(logkey.ControllerType, ctrTypeName), + zap.String(logkey.Kind, "core.Secret"), + ) + + impl := controller.NewImpl(rec, logger, ctrTypeName) + 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 + } + } + + rec.Recorder = createRecorder(ctx, agentName) + + return impl +} + +func createRecorder(ctx context.Context, agentName string) record.EventRecorder { + logger := logging.FromContext(ctx) + + recorder := controller.GetEventRecorder(ctx) + if recorder == nil { + // Create event broadcaster + logger.Debug("Creating event broadcaster") + eventBroadcaster := record.NewBroadcaster() + watches := []watch.Interface{ + eventBroadcaster.StartLogging(logger.Named("event-broadcaster").Infof), + eventBroadcaster.StartRecordingToSink( + &v1.EventSinkImpl{Interface: client.Get(ctx).CoreV1().Events("")}), + } + recorder = eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: agentName}) + go func() { + <-ctx.Done() + for _, w := range watches { + w.Stop() + } + }() + } + + return recorder +} + +func init() { + scheme.AddToScheme(scheme.Scheme) +} diff --git a/client/injection/kube/reconciler/core/v1/secret/reconciler.go b/client/injection/kube/reconciler/core/v1/secret/reconciler.go new file mode 100644 index 000000000..bd545659f --- /dev/null +++ b/client/injection/kube/reconciler/core/v1/secret/reconciler.go @@ -0,0 +1,372 @@ +/* +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. +*/ + +// Code generated by injection-gen. DO NOT EDIT. + +package secret + +import ( + context "context" + json "encoding/json" + fmt "fmt" + + zap "go.uber.org/zap" + v1 "k8s.io/api/core/v1" + errors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + types "k8s.io/apimachinery/pkg/types" + sets "k8s.io/apimachinery/pkg/util/sets" + kubernetes "k8s.io/client-go/kubernetes" + corev1 "k8s.io/client-go/listers/core/v1" + record "k8s.io/client-go/tools/record" + controller "knative.dev/pkg/controller" + logging "knative.dev/pkg/logging" + reconciler "knative.dev/pkg/reconciler" +) + +// Interface defines the strongly typed interfaces to be implemented by a +// controller reconciling v1.Secret. +type Interface interface { + // ReconcileKind implements custom logic to reconcile v1.Secret. 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 context.Context, o *v1.Secret) reconciler.Event +} + +// Finalizer defines the strongly typed interfaces to be implemented by a +// controller finalizing v1.Secret. +type Finalizer interface { + // FinalizeKind implements custom logic to finalize v1.Secret. Any changes + // to the objects .Status or .Finalizers will be ignored. Returning a nil or + // Normal type reconciler.Event will allow the finalizer to be deleted on + // the resource. The resource passed to FinalizeKind will always have a set + // deletion timestamp. + FinalizeKind(ctx context.Context, o *v1.Secret) reconciler.Event +} + +// ReadOnlyInterface defines the strongly typed interfaces to be implemented by a +// controller reconciling v1.Secret if they want to process resources for which +// they are not the leader. +type ReadOnlyInterface interface { + // ObserveKind implements logic to observe v1.Secret. + // This method should not write to the API. + ObserveKind(ctx context.Context, o *v1.Secret) reconciler.Event +} + +// ReadOnlyFinalizer defines the strongly typed interfaces to be implemented by a +// controller finalizing v1.Secret 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 v1.Secret. + // This method should not write to the API. + ObserveFinalizeKind(ctx context.Context, o *v1.Secret) reconciler.Event +} + +type doReconcile func(ctx context.Context, o *v1.Secret) reconciler.Event + +// reconcilerImpl implements controller.Reconciler for v1.Secret resources. +type reconcilerImpl struct { + // LeaderAwareFuncs is inlined to help us implement reconciler.LeaderAware + reconciler.LeaderAwareFuncs + + // Client is used to write back status updates. + Client kubernetes.Interface + + // Listers index properties about resources + Lister corev1.SecretLister + + // Recorder is an event recorder for recording Event resources to the + // Kubernetes API. + Recorder record.EventRecorder + + // configStore allows for decorating a context with config maps. + // +optional + configStore reconciler.ConfigStore + + // reconciler is the implementation of the business logic of the resource. + reconciler Interface + + // finalizerName is the name of the finalizer to reconcile. + finalizerName string +} + +// Check that our Reconciler implements controller.Reconciler +var _ controller.Reconciler = (*reconcilerImpl)(nil) + +// Check that our generated Reconciler is always LeaderAware. +var _ reconciler.LeaderAware = (*reconcilerImpl)(nil) + +func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client kubernetes.Interface, lister corev1.SecretLister, recorder record.EventRecorder, r Interface, options ...controller.Options) controller.Reconciler { + // 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.(reconciler.LeaderAware); 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: reconciler.LeaderAwareFuncs{ + PromoteFunc: func(bkt reconciler.Bucket, enq func(reconciler.Bucket, types.NamespacedName)) error { + all, err := lister.List(labels.Everything()) + if err != nil { + return err + } + for _, elt := range all { + // TODO: Consider letting users specify a filter in options. + enq(bkt, types.NamespacedName{ + Namespace: elt.GetNamespace(), + Name: elt.GetName(), + }) + } + return nil + }, + }, + Client: client, + Lister: lister, + Recorder: recorder, + reconciler: r, + finalizerName: defaultFinalizerName, + } + + for _, opts := range options { + if opts.ConfigStore != nil { + rec.configStore = opts.ConfigStore + } + if opts.FinalizerName != "" { + rec.finalizerName = opts.FinalizerName + } + } + + return rec +} + +// Reconcile implements controller.Reconciler +func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error { + logger := logging.FromContext(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 controller.NewSkipKey(key) + } + + // 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 = controller.WithEventRecorder(ctx, r.Recorder) + + // Get the resource with this namespace/name. + + getter := r.Lister.Secrets(s.namespace) + + original, err := getter.Get(s.name) + + if errors.IsNotFound(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 + } + + // Don't modify the informers copy. + resource := original.DeepCopy() + + var reconcileEvent reconciler.Event + + name, do := s.reconcileMethodFor(resource) + // Append the target method to the logger. + logger = logger.With(zap.String("targetMethod", name)) + switch name { + case reconciler.DoReconcileKind: + // Set and update the finalizer on resource if r.reconciler + // implements Finalizer. + if resource, err = r.setFinalizerIfFinalizer(ctx, resource); err != nil { + return fmt.Errorf("failed to set finalizers: %w", err) + } + + // Reconcile this copy of the resource and then write back any status + // updates regardless of whether the reconciliation errored out. + reconcileEvent = do(ctx, resource) + + case reconciler.DoFinalizeKind: + // 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 fmt.Errorf("failed to clear finalizers: %w", err) + } + + case reconciler.DoObserveKind, reconciler.DoObserveFinalizeKind: + // Observe any changes to this resource, since we are not the leader. + reconcileEvent = do(ctx, resource) + + } + + // Report the reconciler event, if any. + if reconcileEvent != nil { + var event *reconciler.ReconcilerEvent + 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, v1.EventTypeWarning, "InternalError", reconcileEvent.Error()) + return reconcileEvent + } + + return nil +} + +// 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 context.Context, resource *v1.Secret) (*v1.Secret, error) { + + getter := r.Lister.Secrets(resource.Namespace) + + 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 := sets.NewString(existing.Finalizers...) + desiredFinalizers := sets.NewString(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 := json.Marshal(mergePatch) + if err != nil { + return resource, err + } + + patcher := r.Client.CoreV1().Secrets(resource.Namespace) + + resourceName := resource.Name + updated, err := patcher.Patch(ctx, resourceName, types.MergePatchType, patch, metav1.PatchOptions{}) + if err != nil { + r.Recorder.Eventf(existing, v1.EventTypeWarning, "FinalizerUpdateFailed", + "Failed to update finalizers for %q: %v", resourceName, err) + } else { + r.Recorder.Eventf(updated, v1.EventTypeNormal, "FinalizerUpdate", + "Updated %q finalizers", resource.GetName()) + } + return updated, err +} + +func (r *reconcilerImpl) setFinalizerIfFinalizer(ctx context.Context, resource *v1.Secret) (*v1.Secret, error) { + if _, ok := r.reconciler.(Finalizer); !ok { + return resource, nil + } + + finalizers := sets.NewString(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 context.Context, resource *v1.Secret, reconcileEvent reconciler.Event) (*v1.Secret, error) { + if _, ok := r.reconciler.(Finalizer); !ok { + return resource, nil + } + if resource.GetDeletionTimestamp().IsZero() { + return resource, nil + } + + finalizers := sets.NewString(resource.Finalizers...) + + if reconcileEvent != nil { + var event *reconciler.ReconcilerEvent + if reconciler.EventAs(reconcileEvent, &event) { + if event.EventType == v1.EventTypeNormal { + 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) +} diff --git a/client/injection/kube/reconciler/core/v1/secret/state.go b/client/injection/kube/reconciler/core/v1/secret/state.go new file mode 100644 index 000000000..23b0d48fd --- /dev/null +++ b/client/injection/kube/reconciler/core/v1/secret/state.go @@ -0,0 +1,106 @@ +/* +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. +*/ + +// Code generated by injection-gen. DO NOT EDIT. + +package secret + +import ( + fmt "fmt" + + v1 "k8s.io/api/core/v1" + types "k8s.io/apimachinery/pkg/types" + cache "k8s.io/client-go/tools/cache" + reconciler "knative.dev/pkg/reconciler" +) + +// 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 := cache.SplitMetaNamespaceKey(key) + if err != nil { + return nil, fmt.Errorf("invalid resource key: %s", key) + } + + roi, isROI := r.reconciler.(ReadOnlyInterface) + rof, isROF := r.reconciler.(ReadOnlyFinalizer) + + isLeader := r.IsLeaderFor(types.NamespacedName{ + 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 +} + +// 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 *v1.Secret) (string, doReconcile) { + if o.GetDeletionTimestamp().IsZero() { + if s.isLeader { + return reconciler.DoReconcileKind, s.reconciler.ReconcileKind + } else if s.isROI { + return reconciler.DoObserveKind, s.roi.ObserveKind + } + } else if fin, ok := s.reconciler.(Finalizer); s.isLeader && ok { + return reconciler.DoFinalizeKind, fin.FinalizeKind + } else if !s.isLeader && s.isROF { + return reconciler.DoObserveFinalizeKind, s.rof.ObserveFinalizeKind + } + return "unknown", nil +} diff --git a/codegen/cmd/injection-gen/generators/packages.go b/codegen/cmd/injection-gen/generators/packages.go index 75dafbf1b..e71e92dc2 100644 --- a/codegen/cmd/injection-gen/generators/packages.go +++ b/codegen/cmd/injection-gen/generators/packages.go @@ -552,6 +552,7 @@ func reconcilerPackages(basePackage string, groupPkgName string, gv clientgentyp schemePkg: filepath.Join(customArgs.VersionedClientSetPackage, "scheme"), reconcilerClass: reconcilerClass, hasReconcilerClass: hasReconcilerClass, + hasStatus: hasStatus(t), }) return generators }, @@ -613,6 +614,7 @@ func reconcilerPackages(basePackage string, groupPkgName string, gv clientgentyp hasReconcilerClass: hasReconcilerClass, nonNamespaced: nonNamespaced, isKRShaped: isKRShaped, + hasStatus: hasStatus(t), }) return generators }, @@ -737,3 +739,12 @@ func versionDuckPackages(basePackage string, groupPkgName string, gv clientgenty } return vers } + +func hasStatus(t *types.Type) bool { + for _, member := range t.Members { + if member.Name == "Status" { + return true + } + } + return false +} diff --git a/codegen/cmd/injection-gen/generators/reconciler_controller.go b/codegen/cmd/injection-gen/generators/reconciler_controller.go index 0f0f417ec..06d436648 100644 --- a/codegen/cmd/injection-gen/generators/reconciler_controller.go +++ b/codegen/cmd/injection-gen/generators/reconciler_controller.go @@ -40,6 +40,7 @@ type reconcilerControllerGenerator struct { reconcilerClass string hasReconcilerClass bool + hasStatus bool } var _ generator.Generator = (*reconcilerControllerGenerator)(nil) @@ -66,10 +67,11 @@ func (g *reconcilerControllerGenerator) GenerateType(c *generator.Context, t *ty klog.V(5).Info("processing type ", t) m := map[string]interface{}{ - "type": t, - "group": g.groupName, - "class": g.reconcilerClass, - "hasClass": g.hasReconcilerClass, + "type": t, + "group": g.groupName, + "class": g.reconcilerClass, + "hasClass": g.hasReconcilerClass, + "hasStatus": g.hasStatus, "controllerImpl": c.Universe.Type(types.Name{ Package: "knative.dev/pkg/controller", Name: "Impl", @@ -264,9 +266,11 @@ func NewImpl(ctx {{.contextContext|raw}}, r Interface{{if .hasClass}}, classValu if opts.AgentName != "" { agentName = opts.AgentName } + {{- if .hasStatus}} if opts.SkipStatusUpdates { rec.skipStatusUpdates = true } + {{- end}} } rec.Recorder = createRecorder(ctx, agentName) diff --git a/codegen/cmd/injection-gen/generators/reconciler_reconciler.go b/codegen/cmd/injection-gen/generators/reconciler_reconciler.go index 0c6e3e04e..10149fadd 100644 --- a/codegen/cmd/injection-gen/generators/reconciler_reconciler.go +++ b/codegen/cmd/injection-gen/generators/reconciler_reconciler.go @@ -41,6 +41,7 @@ type reconcilerReconcilerGenerator struct { hasReconcilerClass bool nonNamespaced bool isKRShaped bool + hasStatus bool groupGoName string groupVersion clientgentypes.GroupVersion @@ -76,6 +77,7 @@ func (g *reconcilerReconcilerGenerator) GenerateType(c *generator.Context, t *ty "class": g.reconcilerClass, "hasClass": g.hasReconcilerClass, "isKRShaped": g.isKRShaped, + "hasStatus": g.hasStatus, "nonNamespaced": g.nonNamespaced, "controllerImpl": c.Universe.Type(types.Name{ Package: "knative.dev/pkg/controller", @@ -213,7 +215,9 @@ func (g *reconcilerReconcilerGenerator) GenerateType(c *generator.Context, t *ty sw.Do(reconcilerInterfaceFactory, m) sw.Do(reconcilerNewReconciler, m) sw.Do(reconcilerImplFactory, m) - sw.Do(reconcilerStatusFactory, m) + if g.hasStatus { + sw.Do(reconcilerStatusFactory, m) + } sw.Do(reconcilerFinalizerFactory, m) return sw.Error() @@ -289,9 +293,11 @@ type reconcilerImpl struct { // finalizerName is the name of the finalizer to reconcile. finalizerName string + {{if .hasStatus}} // skipStatusUpdates configures whether or not this reconciler automatically updates // the status of the reconciled resource. skipStatusUpdates bool + {{end}} {{if .hasClass}} // classValue is the resource annotation[{{ .class }}] instance value this reconciler instance filters on. @@ -352,9 +358,11 @@ func NewReconciler(ctx {{.contextContext|raw}}, logger *{{.zapSugaredLogger|raw} if opts.FinalizerName != "" { rec.finalizerName = opts.FinalizerName } + {{- if .hasStatus}} if opts.SkipStatusUpdates { rec.skipStatusUpdates = true } + {{- end}} } return rec @@ -460,6 +468,7 @@ func (r *reconcilerImpl) Reconcile(ctx {{.contextContext|raw}}, key string) erro } + {{if .hasStatus}} // Synchronize the status. switch { case r.skipStatusUpdates: @@ -482,6 +491,7 @@ func (r *reconcilerImpl) Reconcile(ctx {{.contextContext|raw}}, key string) erro return err } } + {{end}} // Report the reconciler event, if any. if reconcileEvent != nil { diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh index 8ba165d1e..5f6df7e28 100755 --- a/hack/update-codegen.sh +++ b/hack/update-codegen.sh @@ -49,7 +49,7 @@ EXTERNAL_INFORMER_PKG="k8s.io/client-go/informers" \ 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" + --force-genreconciler-kinds "Namespace,Deployment,Secret" OUTPUT_PKG="knative.dev/pkg/client/injection/apiextensions" \ VERSIONED_CLIENTSET_PKG="k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" \