Refactor the genreconciler code to pull state out (#1544)

* Refactor the genreconciler code to pull state out

* don't generate the stubs

* fix indent
This commit is contained in:
Scott Nichols 2020-07-22 23:02:57 -07:00 committed by GitHub
parent bc4dc000cb
commit ae9c3f7fa8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 895 additions and 835 deletions

View File

@ -27,7 +27,7 @@ import (
)
// +genclient
// +genreconciler:class=example.com/filter.class
// +genreconciler:class=example.com/filter.class,stubs
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Bar is for testing.

7
client/OWNERS generated
View File

@ -1,7 +0,0 @@
# The OWNERS file is used by prow to automatically merge approved PRs.
approvers:
- codegen-approvers
reviewers:
- codegen-reviewers

View File

@ -35,7 +35,6 @@ import (
labels "k8s.io/apimachinery/pkg/labels"
types "k8s.io/apimachinery/pkg/types"
sets "k8s.io/apimachinery/pkg/util/sets"
cache "k8s.io/client-go/tools/cache"
record "k8s.io/client-go/tools/record"
controller "knative.dev/pkg/controller"
logging "knative.dev/pkg/logging"
@ -84,6 +83,15 @@ type ReadOnlyFinalizer interface {
ObserveFinalizeKind(ctx context.Context, o *v1.CustomResourceDefinition) reconciler.Event
}
type doReconcile func(ctx context.Context, o *v1.CustomResourceDefinition) reconciler.Event
const (
doReconcileKind = "ReconcileKind"
doFinalizeKind = "FinalizeKind"
doObserveKind = "ObserveKind"
doObserveFinalizeKind = "ObserveFinalizeKind"
)
// reconcilerImpl implements controller.Reconciler for v1.CustomResourceDefinition resources.
type reconcilerImpl struct {
// LeaderAwareFuncs is inlined to help us implement reconciler.LeaderAware
@ -176,22 +184,19 @@ func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client client
func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
logger := logging.FromContext(ctx)
// Convert the namespace/name string into a distinct namespace and name
namespace, name, err := cache.SplitMetaNamespaceKey(key)
// Initialize the reconciler state. This will convert the namespace/name
// string into a distinct namespace and name, determin 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.Errorf("invalid resource key: %s", key)
return nil
}
// Establish whether we are the leader for use below.
isLeader := r.IsLeaderFor(types.NamespacedName{
Namespace: namespace,
Name: name,
})
roi, isROI := r.reconciler.(ReadOnlyInterface)
rof, isROF := r.reconciler.(ReadOnlyFinalizer)
if !isLeader && !isROI && !isROF {
// If we are not the leader, and we don't implement either ReadOnly
// interface, then take a fast-path out.
// 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
}
@ -207,7 +212,7 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
getter := r.Lister
original, err := getter.Get(name)
original, err := getter.Get(s.name)
if errors.IsNotFound(err) {
// The resource may no longer exist, in which case we stop processing.
@ -221,44 +226,38 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
resource := original.DeepCopy()
var reconcileEvent reconciler.Event
if resource.GetDeletionTimestamp().IsZero() {
if isLeader {
// 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 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 = r.reconciler.ReconcileKind(ctx, resource)
} else if isROI {
// Append the target method to the logger.
logger = logger.With(zap.String("targetMethod", "ObserveKind"))
// Observe any changes to this resource, since we are not the leader.
reconcileEvent = roi.ObserveKind(ctx, resource)
}
} else if fin, ok := r.reconciler.(Finalizer); isLeader && ok {
name, do := s.reconcileMethodFor(resource)
// Append the target method to the logger.
logger = logger.With(zap.String("targetMethod", name))
switch name {
case doReconcileKind:
// Append the target method to the logger.
logger = logger.With(zap.String("targetMethod", "FinalizeKind"))
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 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 "FinalizeKind":
// For finalizing reconcilers, if this resource being marked for deletion
// and reconciled cleanly (nil or normal event), remove the finalizer.
reconcileEvent = fin.FinalizeKind(ctx, resource)
reconcileEvent = do(ctx, resource)
if resource, err = r.clearFinalizer(ctx, resource, reconcileEvent); err != nil {
return fmt.Errorf("failed to clear finalizers: %w", err)
}
} else if !isLeader && isROF {
// Append the target method to the logger.
logger = logger.With(zap.String("targetMethod", "ObserveFinalizeKind"))
// For finalizing reconcilers, just observe when we aren't the leader.
reconcileEvent = rof.ObserveFinalizeKind(ctx, resource)
case "ObserveKind", "ObserveFinalizeKind":
// Observe any changes to this resource, since we are not the leader.
reconcileEvent = do(ctx, resource)
}
// Synchronize the status.
@ -271,7 +270,7 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
// 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 !isLeader:
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!")

View File

@ -0,0 +1,105 @@
/*
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 customresourcedefinition
import (
fmt "fmt"
v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
types "k8s.io/apimachinery/pkg/types"
cache "k8s.io/client-go/tools/cache"
)
// 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.CustomResourceDefinition) (string, doReconcile) {
if o.GetDeletionTimestamp().IsZero() {
if s.isLeader {
return doReconcileKind, s.reconciler.ReconcileKind
} else if s.isROI {
return doObserveKind, s.roi.ObserveKind
}
} else if fin, ok := s.reconciler.(Finalizer); s.isLeader && ok {
return doFinalizeKind, fin.FinalizeKind
} else if !s.isLeader && s.isROF {
return doObserveFinalizeKind, s.rof.ObserveFinalizeKind
}
return "unknown", nil
}

View File

@ -1,54 +0,0 @@
/*
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 customresourcedefinition
import (
context "context"
customresourcedefinition "knative.dev/pkg/client/injection/apiextensions/informers/apiextensions/v1/customresourcedefinition"
v1customresourcedefinition "knative.dev/pkg/client/injection/apiextensions/reconciler/apiextensions/v1/customresourcedefinition"
configmap "knative.dev/pkg/configmap"
controller "knative.dev/pkg/controller"
logging "knative.dev/pkg/logging"
)
// TODO: PLEASE COPY AND MODIFY THIS FILE AS A STARTING POINT
// NewController creates a Reconciler for CustomResourceDefinition and returns the result of NewImpl.
func NewController(
ctx context.Context,
cmw configmap.Watcher,
) *controller.Impl {
logger := logging.FromContext(ctx)
customresourcedefinitionInformer := customresourcedefinition.Get(ctx)
// TODO: setup additional informers here.
r := &Reconciler{}
impl := v1customresourcedefinition.NewImpl(ctx, r)
logger.Info("Setting up event handlers.")
customresourcedefinitionInformer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue))
// TODO: add additional informer event handlers here.
return impl
}

View File

@ -1,87 +0,0 @@
/*
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 customresourcedefinition
import (
context "context"
v1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
customresourcedefinition "knative.dev/pkg/client/injection/apiextensions/reconciler/apiextensions/v1/customresourcedefinition"
reconciler "knative.dev/pkg/reconciler"
)
// TODO: PLEASE COPY AND MODIFY THIS FILE AS A STARTING POINT
// newReconciledNormal makes a new reconciler event with event type Normal, and
// reason CustomResourceDefinitionReconciled.
func newReconciledNormal(namespace, name string) reconciler.Event {
return reconciler.NewEvent(v1.EventTypeNormal, "CustomResourceDefinitionReconciled", "CustomResourceDefinition reconciled: \"%s/%s\"", namespace, name)
}
// Reconciler implements controller.Reconciler for CustomResourceDefinition resources.
type Reconciler struct {
// TODO: add additional requirements here.
}
// Check that our Reconciler implements Interface
var _ customresourcedefinition.Interface = (*Reconciler)(nil)
// Optionally check that our Reconciler implements Finalizer
//var _ customresourcedefinition.Finalizer = (*Reconciler)(nil)
// Optionally check that our Reconciler implements ReadOnlyInterface
// Implement this to observe resources even when we are not the leader.
//var _ customresourcedefinition.ReadOnlyInterface = (*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 _ customresourcedefinition.ReadOnlyFinalizer = (*Reconciler)(nil)
// ReconcileKind implements Interface.ReconcileKind.
func (r *Reconciler) ReconcileKind(ctx context.Context, o *apiextensionsv1.CustomResourceDefinition) reconciler.Event {
// TODO: use this if the resource implements InitializeConditions.
// o.Status.InitializeConditions()
// TODO: add custom reconciliation logic here.
// TODO: use this if the object has .status.ObservedGeneration.
// o.Status.ObservedGeneration = o.Generation
return newReconciledNormal(o.Namespace, o.Name)
}
// Optionally, use FinalizeKind to add finalizers. FinalizeKind will be called
// when the resource is deleted.
//func (r *Reconciler) FinalizeKind(ctx context.Context, o *apiextensionsv1.CustomResourceDefinition) reconciler.Event {
// // 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 context.Context, o *apiextensionsv1.CustomResourceDefinition) reconciler.Event {
// // 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 context.Context, o *apiextensionsv1.CustomResourceDefinition) reconciler.Event {
// // TODO: add custom observation logic here.
// return nil
//}

View File

@ -35,7 +35,6 @@ import (
labels "k8s.io/apimachinery/pkg/labels"
types "k8s.io/apimachinery/pkg/types"
sets "k8s.io/apimachinery/pkg/util/sets"
cache "k8s.io/client-go/tools/cache"
record "k8s.io/client-go/tools/record"
controller "knative.dev/pkg/controller"
logging "knative.dev/pkg/logging"
@ -84,6 +83,15 @@ type ReadOnlyFinalizer interface {
ObserveFinalizeKind(ctx context.Context, o *v1beta1.CustomResourceDefinition) reconciler.Event
}
type doReconcile func(ctx context.Context, o *v1beta1.CustomResourceDefinition) reconciler.Event
const (
doReconcileKind = "ReconcileKind"
doFinalizeKind = "FinalizeKind"
doObserveKind = "ObserveKind"
doObserveFinalizeKind = "ObserveFinalizeKind"
)
// reconcilerImpl implements controller.Reconciler for v1beta1.CustomResourceDefinition resources.
type reconcilerImpl struct {
// LeaderAwareFuncs is inlined to help us implement reconciler.LeaderAware
@ -176,22 +184,19 @@ func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client client
func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
logger := logging.FromContext(ctx)
// Convert the namespace/name string into a distinct namespace and name
namespace, name, err := cache.SplitMetaNamespaceKey(key)
// Initialize the reconciler state. This will convert the namespace/name
// string into a distinct namespace and name, determin 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.Errorf("invalid resource key: %s", key)
return nil
}
// Establish whether we are the leader for use below.
isLeader := r.IsLeaderFor(types.NamespacedName{
Namespace: namespace,
Name: name,
})
roi, isROI := r.reconciler.(ReadOnlyInterface)
rof, isROF := r.reconciler.(ReadOnlyFinalizer)
if !isLeader && !isROI && !isROF {
// If we are not the leader, and we don't implement either ReadOnly
// interface, then take a fast-path out.
// 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
}
@ -207,7 +212,7 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
getter := r.Lister
original, err := getter.Get(name)
original, err := getter.Get(s.name)
if errors.IsNotFound(err) {
// The resource may no longer exist, in which case we stop processing.
@ -221,44 +226,38 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
resource := original.DeepCopy()
var reconcileEvent reconciler.Event
if resource.GetDeletionTimestamp().IsZero() {
if isLeader {
// 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 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 = r.reconciler.ReconcileKind(ctx, resource)
} else if isROI {
// Append the target method to the logger.
logger = logger.With(zap.String("targetMethod", "ObserveKind"))
// Observe any changes to this resource, since we are not the leader.
reconcileEvent = roi.ObserveKind(ctx, resource)
}
} else if fin, ok := r.reconciler.(Finalizer); isLeader && ok {
name, do := s.reconcileMethodFor(resource)
// Append the target method to the logger.
logger = logger.With(zap.String("targetMethod", name))
switch name {
case doReconcileKind:
// Append the target method to the logger.
logger = logger.With(zap.String("targetMethod", "FinalizeKind"))
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 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 "FinalizeKind":
// For finalizing reconcilers, if this resource being marked for deletion
// and reconciled cleanly (nil or normal event), remove the finalizer.
reconcileEvent = fin.FinalizeKind(ctx, resource)
reconcileEvent = do(ctx, resource)
if resource, err = r.clearFinalizer(ctx, resource, reconcileEvent); err != nil {
return fmt.Errorf("failed to clear finalizers: %w", err)
}
} else if !isLeader && isROF {
// Append the target method to the logger.
logger = logger.With(zap.String("targetMethod", "ObserveFinalizeKind"))
// For finalizing reconcilers, just observe when we aren't the leader.
reconcileEvent = rof.ObserveFinalizeKind(ctx, resource)
case "ObserveKind", "ObserveFinalizeKind":
// Observe any changes to this resource, since we are not the leader.
reconcileEvent = do(ctx, resource)
}
// Synchronize the status.
@ -271,7 +270,7 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
// 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 !isLeader:
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!")

View File

@ -0,0 +1,105 @@
/*
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 customresourcedefinition
import (
fmt "fmt"
v1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
types "k8s.io/apimachinery/pkg/types"
cache "k8s.io/client-go/tools/cache"
)
// 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 *v1beta1.CustomResourceDefinition) (string, doReconcile) {
if o.GetDeletionTimestamp().IsZero() {
if s.isLeader {
return doReconcileKind, s.reconciler.ReconcileKind
} else if s.isROI {
return doObserveKind, s.roi.ObserveKind
}
} else if fin, ok := s.reconciler.(Finalizer); s.isLeader && ok {
return doFinalizeKind, fin.FinalizeKind
} else if !s.isLeader && s.isROF {
return doObserveFinalizeKind, s.rof.ObserveFinalizeKind
}
return "unknown", nil
}

View File

@ -1,54 +0,0 @@
/*
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 customresourcedefinition
import (
context "context"
customresourcedefinition "knative.dev/pkg/client/injection/apiextensions/informers/apiextensions/v1beta1/customresourcedefinition"
v1beta1customresourcedefinition "knative.dev/pkg/client/injection/apiextensions/reconciler/apiextensions/v1beta1/customresourcedefinition"
configmap "knative.dev/pkg/configmap"
controller "knative.dev/pkg/controller"
logging "knative.dev/pkg/logging"
)
// TODO: PLEASE COPY AND MODIFY THIS FILE AS A STARTING POINT
// NewController creates a Reconciler for CustomResourceDefinition and returns the result of NewImpl.
func NewController(
ctx context.Context,
cmw configmap.Watcher,
) *controller.Impl {
logger := logging.FromContext(ctx)
customresourcedefinitionInformer := customresourcedefinition.Get(ctx)
// TODO: setup additional informers here.
r := &Reconciler{}
impl := v1beta1customresourcedefinition.NewImpl(ctx, r)
logger.Info("Setting up event handlers.")
customresourcedefinitionInformer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue))
// TODO: add additional informer event handlers here.
return impl
}

View File

@ -1,87 +0,0 @@
/*
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 customresourcedefinition
import (
context "context"
v1 "k8s.io/api/core/v1"
v1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
customresourcedefinition "knative.dev/pkg/client/injection/apiextensions/reconciler/apiextensions/v1beta1/customresourcedefinition"
reconciler "knative.dev/pkg/reconciler"
)
// TODO: PLEASE COPY AND MODIFY THIS FILE AS A STARTING POINT
// newReconciledNormal makes a new reconciler event with event type Normal, and
// reason CustomResourceDefinitionReconciled.
func newReconciledNormal(namespace, name string) reconciler.Event {
return reconciler.NewEvent(v1.EventTypeNormal, "CustomResourceDefinitionReconciled", "CustomResourceDefinition reconciled: \"%s/%s\"", namespace, name)
}
// Reconciler implements controller.Reconciler for CustomResourceDefinition resources.
type Reconciler struct {
// TODO: add additional requirements here.
}
// Check that our Reconciler implements Interface
var _ customresourcedefinition.Interface = (*Reconciler)(nil)
// Optionally check that our Reconciler implements Finalizer
//var _ customresourcedefinition.Finalizer = (*Reconciler)(nil)
// Optionally check that our Reconciler implements ReadOnlyInterface
// Implement this to observe resources even when we are not the leader.
//var _ customresourcedefinition.ReadOnlyInterface = (*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 _ customresourcedefinition.ReadOnlyFinalizer = (*Reconciler)(nil)
// ReconcileKind implements Interface.ReconcileKind.
func (r *Reconciler) ReconcileKind(ctx context.Context, o *v1beta1.CustomResourceDefinition) reconciler.Event {
// TODO: use this if the resource implements InitializeConditions.
// o.Status.InitializeConditions()
// TODO: add custom reconciliation logic here.
// TODO: use this if the object has .status.ObservedGeneration.
// o.Status.ObservedGeneration = o.Generation
return newReconciledNormal(o.Namespace, o.Name)
}
// Optionally, use FinalizeKind to add finalizers. FinalizeKind will be called
// when the resource is deleted.
//func (r *Reconciler) FinalizeKind(ctx context.Context, o *v1beta1.CustomResourceDefinition) reconciler.Event {
// // 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 context.Context, o *v1beta1.CustomResourceDefinition) reconciler.Event {
// // 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 context.Context, o *v1beta1.CustomResourceDefinition) reconciler.Event {
// // TODO: add custom observation logic here.
// return nil
//}

View File

@ -35,7 +35,6 @@ import (
sets "k8s.io/apimachinery/pkg/util/sets"
kubernetes "k8s.io/client-go/kubernetes"
appsv1 "k8s.io/client-go/listers/apps/v1"
cache "k8s.io/client-go/tools/cache"
record "k8s.io/client-go/tools/record"
controller "knative.dev/pkg/controller"
logging "knative.dev/pkg/logging"
@ -84,6 +83,15 @@ type ReadOnlyFinalizer interface {
ObserveFinalizeKind(ctx context.Context, o *v1.Deployment) reconciler.Event
}
type doReconcile func(ctx context.Context, o *v1.Deployment) reconciler.Event
const (
doReconcileKind = "ReconcileKind"
doFinalizeKind = "FinalizeKind"
doObserveKind = "ObserveKind"
doObserveFinalizeKind = "ObserveFinalizeKind"
)
// reconcilerImpl implements controller.Reconciler for v1.Deployment resources.
type reconcilerImpl struct {
// LeaderAwareFuncs is inlined to help us implement reconciler.LeaderAware
@ -176,22 +184,19 @@ func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client kubern
func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
logger := logging.FromContext(ctx)
// Convert the namespace/name string into a distinct namespace and name
namespace, name, err := cache.SplitMetaNamespaceKey(key)
// Initialize the reconciler state. This will convert the namespace/name
// string into a distinct namespace and name, determin 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.Errorf("invalid resource key: %s", key)
return nil
}
// Establish whether we are the leader for use below.
isLeader := r.IsLeaderFor(types.NamespacedName{
Namespace: namespace,
Name: name,
})
roi, isROI := r.reconciler.(ReadOnlyInterface)
rof, isROF := r.reconciler.(ReadOnlyFinalizer)
if !isLeader && !isROI && !isROF {
// If we are not the leader, and we don't implement either ReadOnly
// interface, then take a fast-path out.
// 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
}
@ -205,9 +210,9 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
// Get the resource with this namespace/name.
getter := r.Lister.Deployments(namespace)
getter := r.Lister.Deployments(s.namespace)
original, err := getter.Get(name)
original, err := getter.Get(s.name)
if errors.IsNotFound(err) {
// The resource may no longer exist, in which case we stop processing.
@ -221,44 +226,38 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
resource := original.DeepCopy()
var reconcileEvent reconciler.Event
if resource.GetDeletionTimestamp().IsZero() {
if isLeader {
// 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 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 = r.reconciler.ReconcileKind(ctx, resource)
} else if isROI {
// Append the target method to the logger.
logger = logger.With(zap.String("targetMethod", "ObserveKind"))
// Observe any changes to this resource, since we are not the leader.
reconcileEvent = roi.ObserveKind(ctx, resource)
}
} else if fin, ok := r.reconciler.(Finalizer); isLeader && ok {
name, do := s.reconcileMethodFor(resource)
// Append the target method to the logger.
logger = logger.With(zap.String("targetMethod", name))
switch name {
case doReconcileKind:
// Append the target method to the logger.
logger = logger.With(zap.String("targetMethod", "FinalizeKind"))
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 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 "FinalizeKind":
// For finalizing reconcilers, if this resource being marked for deletion
// and reconciled cleanly (nil or normal event), remove the finalizer.
reconcileEvent = fin.FinalizeKind(ctx, resource)
reconcileEvent = do(ctx, resource)
if resource, err = r.clearFinalizer(ctx, resource, reconcileEvent); err != nil {
return fmt.Errorf("failed to clear finalizers: %w", err)
}
} else if !isLeader && isROF {
// Append the target method to the logger.
logger = logger.With(zap.String("targetMethod", "ObserveFinalizeKind"))
// For finalizing reconcilers, just observe when we aren't the leader.
reconcileEvent = rof.ObserveFinalizeKind(ctx, resource)
case "ObserveKind", "ObserveFinalizeKind":
// Observe any changes to this resource, since we are not the leader.
reconcileEvent = do(ctx, resource)
}
// Synchronize the status.
@ -271,7 +270,7 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
// 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 !isLeader:
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!")

View File

@ -0,0 +1,105 @@
/*
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 deployment
import (
fmt "fmt"
v1 "k8s.io/api/apps/v1"
types "k8s.io/apimachinery/pkg/types"
cache "k8s.io/client-go/tools/cache"
)
// 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.Deployment) (string, doReconcile) {
if o.GetDeletionTimestamp().IsZero() {
if s.isLeader {
return doReconcileKind, s.reconciler.ReconcileKind
} else if s.isROI {
return doObserveKind, s.roi.ObserveKind
}
} else if fin, ok := s.reconciler.(Finalizer); s.isLeader && ok {
return doFinalizeKind, fin.FinalizeKind
} else if !s.isLeader && s.isROF {
return doObserveFinalizeKind, s.rof.ObserveFinalizeKind
}
return "unknown", nil
}

View File

@ -1,54 +0,0 @@
/*
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 deployment
import (
context "context"
deployment "knative.dev/pkg/client/injection/kube/informers/apps/v1/deployment"
v1deployment "knative.dev/pkg/client/injection/kube/reconciler/apps/v1/deployment"
configmap "knative.dev/pkg/configmap"
controller "knative.dev/pkg/controller"
logging "knative.dev/pkg/logging"
)
// TODO: PLEASE COPY AND MODIFY THIS FILE AS A STARTING POINT
// NewController creates a Reconciler for Deployment and returns the result of NewImpl.
func NewController(
ctx context.Context,
cmw configmap.Watcher,
) *controller.Impl {
logger := logging.FromContext(ctx)
deploymentInformer := deployment.Get(ctx)
// TODO: setup additional informers here.
r := &Reconciler{}
impl := v1deployment.NewImpl(ctx, r)
logger.Info("Setting up event handlers.")
deploymentInformer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue))
// TODO: add additional informer event handlers here.
return impl
}

View File

@ -1,87 +0,0 @@
/*
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 deployment
import (
context "context"
appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
deployment "knative.dev/pkg/client/injection/kube/reconciler/apps/v1/deployment"
reconciler "knative.dev/pkg/reconciler"
)
// TODO: PLEASE COPY AND MODIFY THIS FILE AS A STARTING POINT
// newReconciledNormal makes a new reconciler event with event type Normal, and
// reason DeploymentReconciled.
func newReconciledNormal(namespace, name string) reconciler.Event {
return reconciler.NewEvent(v1.EventTypeNormal, "DeploymentReconciled", "Deployment reconciled: \"%s/%s\"", namespace, name)
}
// Reconciler implements controller.Reconciler for Deployment resources.
type Reconciler struct {
// TODO: add additional requirements here.
}
// Check that our Reconciler implements Interface
var _ deployment.Interface = (*Reconciler)(nil)
// Optionally check that our Reconciler implements Finalizer
//var _ deployment.Finalizer = (*Reconciler)(nil)
// Optionally check that our Reconciler implements ReadOnlyInterface
// Implement this to observe resources even when we are not the leader.
//var _ deployment.ReadOnlyInterface = (*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 _ deployment.ReadOnlyFinalizer = (*Reconciler)(nil)
// ReconcileKind implements Interface.ReconcileKind.
func (r *Reconciler) ReconcileKind(ctx context.Context, o *appsv1.Deployment) reconciler.Event {
// TODO: use this if the resource implements InitializeConditions.
// o.Status.InitializeConditions()
// TODO: add custom reconciliation logic here.
// TODO: use this if the object has .status.ObservedGeneration.
// o.Status.ObservedGeneration = o.Generation
return newReconciledNormal(o.Namespace, o.Name)
}
// Optionally, use FinalizeKind to add finalizers. FinalizeKind will be called
// when the resource is deleted.
//func (r *Reconciler) FinalizeKind(ctx context.Context, o *appsv1.Deployment) reconciler.Event {
// // 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 context.Context, o *appsv1.Deployment) reconciler.Event {
// // 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 context.Context, o *appsv1.Deployment) reconciler.Event {
// // TODO: add custom observation logic here.
// return nil
//}

View File

@ -34,7 +34,6 @@ import (
sets "k8s.io/apimachinery/pkg/util/sets"
kubernetes "k8s.io/client-go/kubernetes"
corev1 "k8s.io/client-go/listers/core/v1"
cache "k8s.io/client-go/tools/cache"
record "k8s.io/client-go/tools/record"
controller "knative.dev/pkg/controller"
logging "knative.dev/pkg/logging"
@ -83,6 +82,15 @@ type ReadOnlyFinalizer interface {
ObserveFinalizeKind(ctx context.Context, o *v1.Namespace) reconciler.Event
}
type doReconcile func(ctx context.Context, o *v1.Namespace) reconciler.Event
const (
doReconcileKind = "ReconcileKind"
doFinalizeKind = "FinalizeKind"
doObserveKind = "ObserveKind"
doObserveFinalizeKind = "ObserveFinalizeKind"
)
// reconcilerImpl implements controller.Reconciler for v1.Namespace resources.
type reconcilerImpl struct {
// LeaderAwareFuncs is inlined to help us implement reconciler.LeaderAware
@ -175,22 +183,19 @@ func NewReconciler(ctx context.Context, logger *zap.SugaredLogger, client kubern
func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
logger := logging.FromContext(ctx)
// Convert the namespace/name string into a distinct namespace and name
namespace, name, err := cache.SplitMetaNamespaceKey(key)
// Initialize the reconciler state. This will convert the namespace/name
// string into a distinct namespace and name, determin 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.Errorf("invalid resource key: %s", key)
return nil
}
// Establish whether we are the leader for use below.
isLeader := r.IsLeaderFor(types.NamespacedName{
Namespace: namespace,
Name: name,
})
roi, isROI := r.reconciler.(ReadOnlyInterface)
rof, isROF := r.reconciler.(ReadOnlyFinalizer)
if !isLeader && !isROI && !isROF {
// If we are not the leader, and we don't implement either ReadOnly
// interface, then take a fast-path out.
// 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
}
@ -206,7 +211,7 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
getter := r.Lister
original, err := getter.Get(name)
original, err := getter.Get(s.name)
if errors.IsNotFound(err) {
// The resource may no longer exist, in which case we stop processing.
@ -220,44 +225,38 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
resource := original.DeepCopy()
var reconcileEvent reconciler.Event
if resource.GetDeletionTimestamp().IsZero() {
if isLeader {
// 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 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 = r.reconciler.ReconcileKind(ctx, resource)
} else if isROI {
// Append the target method to the logger.
logger = logger.With(zap.String("targetMethod", "ObserveKind"))
// Observe any changes to this resource, since we are not the leader.
reconcileEvent = roi.ObserveKind(ctx, resource)
}
} else if fin, ok := r.reconciler.(Finalizer); isLeader && ok {
name, do := s.reconcileMethodFor(resource)
// Append the target method to the logger.
logger = logger.With(zap.String("targetMethod", name))
switch name {
case doReconcileKind:
// Append the target method to the logger.
logger = logger.With(zap.String("targetMethod", "FinalizeKind"))
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 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 "FinalizeKind":
// For finalizing reconcilers, if this resource being marked for deletion
// and reconciled cleanly (nil or normal event), remove the finalizer.
reconcileEvent = fin.FinalizeKind(ctx, resource)
reconcileEvent = do(ctx, resource)
if resource, err = r.clearFinalizer(ctx, resource, reconcileEvent); err != nil {
return fmt.Errorf("failed to clear finalizers: %w", err)
}
} else if !isLeader && isROF {
// Append the target method to the logger.
logger = logger.With(zap.String("targetMethod", "ObserveFinalizeKind"))
// For finalizing reconcilers, just observe when we aren't the leader.
reconcileEvent = rof.ObserveFinalizeKind(ctx, resource)
case "ObserveKind", "ObserveFinalizeKind":
// Observe any changes to this resource, since we are not the leader.
reconcileEvent = do(ctx, resource)
}
// Synchronize the status.
@ -270,7 +269,7 @@ func (r *reconcilerImpl) Reconcile(ctx context.Context, key string) error {
// 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 !isLeader:
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!")

View File

@ -0,0 +1,105 @@
/*
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 namespace
import (
fmt "fmt"
v1 "k8s.io/api/core/v1"
types "k8s.io/apimachinery/pkg/types"
cache "k8s.io/client-go/tools/cache"
)
// 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.Namespace) (string, doReconcile) {
if o.GetDeletionTimestamp().IsZero() {
if s.isLeader {
return doReconcileKind, s.reconciler.ReconcileKind
} else if s.isROI {
return doObserveKind, s.roi.ObserveKind
}
} else if fin, ok := s.reconciler.(Finalizer); s.isLeader && ok {
return doFinalizeKind, fin.FinalizeKind
} else if !s.isLeader && s.isROF {
return doObserveFinalizeKind, s.rof.ObserveFinalizeKind
}
return "unknown", nil
}

View File

@ -1,54 +0,0 @@
/*
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 namespace
import (
context "context"
namespace "knative.dev/pkg/client/injection/kube/informers/core/v1/namespace"
v1namespace "knative.dev/pkg/client/injection/kube/reconciler/core/v1/namespace"
configmap "knative.dev/pkg/configmap"
controller "knative.dev/pkg/controller"
logging "knative.dev/pkg/logging"
)
// TODO: PLEASE COPY AND MODIFY THIS FILE AS A STARTING POINT
// NewController creates a Reconciler for Namespace and returns the result of NewImpl.
func NewController(
ctx context.Context,
cmw configmap.Watcher,
) *controller.Impl {
logger := logging.FromContext(ctx)
namespaceInformer := namespace.Get(ctx)
// TODO: setup additional informers here.
r := &Reconciler{}
impl := v1namespace.NewImpl(ctx, r)
logger.Info("Setting up event handlers.")
namespaceInformer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue))
// TODO: add additional informer event handlers here.
return impl
}

View File

@ -1,86 +0,0 @@
/*
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 namespace
import (
context "context"
v1 "k8s.io/api/core/v1"
namespace "knative.dev/pkg/client/injection/kube/reconciler/core/v1/namespace"
reconciler "knative.dev/pkg/reconciler"
)
// TODO: PLEASE COPY AND MODIFY THIS FILE AS A STARTING POINT
// newReconciledNormal makes a new reconciler event with event type Normal, and
// reason NamespaceReconciled.
func newReconciledNormal(namespace, name string) reconciler.Event {
return reconciler.NewEvent(v1.EventTypeNormal, "NamespaceReconciled", "Namespace reconciled: \"%s/%s\"", namespace, name)
}
// Reconciler implements controller.Reconciler for Namespace resources.
type Reconciler struct {
// TODO: add additional requirements here.
}
// Check that our Reconciler implements Interface
var _ namespace.Interface = (*Reconciler)(nil)
// Optionally check that our Reconciler implements Finalizer
//var _ namespace.Finalizer = (*Reconciler)(nil)
// Optionally check that our Reconciler implements ReadOnlyInterface
// Implement this to observe resources even when we are not the leader.
//var _ namespace.ReadOnlyInterface = (*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 _ namespace.ReadOnlyFinalizer = (*Reconciler)(nil)
// ReconcileKind implements Interface.ReconcileKind.
func (r *Reconciler) ReconcileKind(ctx context.Context, o *v1.Namespace) reconciler.Event {
// TODO: use this if the resource implements InitializeConditions.
// o.Status.InitializeConditions()
// TODO: add custom reconciliation logic here.
// TODO: use this if the object has .status.ObservedGeneration.
// o.Status.ObservedGeneration = o.Generation
return newReconciledNormal(o.Namespace, o.Name)
}
// Optionally, use FinalizeKind to add finalizers. FinalizeKind will be called
// when the resource is deleted.
//func (r *Reconciler) FinalizeKind(ctx context.Context, o *v1.Namespace) reconciler.Event {
// // 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 context.Context, o *v1.Namespace) reconciler.Event {
// // 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 context.Context, o *v1.Namespace) reconciler.Event {
// // TODO: add custom observation logic here.
// return nil
//}

View File

@ -213,6 +213,15 @@ func isNonNamespaced(tags map[string]map[string]string) bool {
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/"):]
@ -414,6 +423,7 @@ func reconcilerPackages(basePackage string, groupPkgName string, gv clientgentyp
reconcilerClass, hasReconcilerClass := extractReconcilerClassTag(extracted)
nonNamespaced := isNonNamespaced(extracted)
isKRShaped := isKRShaped(extracted)
stubs := stubs(extracted)
packagePath := filepath.Join(packagePath, strings.ToLower(t.Name.Name))
@ -449,32 +459,34 @@ func reconcilerPackages(basePackage string, groupPkgName string, gv clientgentyp
},
})
// 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)
},
})
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{
@ -508,20 +520,45 @@ func reconcilerPackages(basePackage string, groupPkgName string, gv clientgentyp
},
})
// Reconciler Stub
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: filepath.Join(packagePath, "stub"),
PackagePath: packagePath,
HeaderText: boilerplate,
GeneratorFunc: func(c *generator.Context) (generators []generator.Generator) {
// Impl
generators = append(generators, &reconcilerReconcilerStubGenerator{
// state
generators = append(generators, &reconcilerStateGenerator{
DefaultGen: generator.DefaultGen{
OptionalName: "reconciler",
OptionalName: "state",
},
typeToGenerate: t,
reconcilerPkg: packagePath,
outputPackage: filepath.Join(packagePath, "stub"),
outputPackage: packagePath,
imports: generator.NewImportTracker(),
})
return generators

View File

@ -230,6 +230,15 @@ type ReadOnlyFinalizer interface {
ObserveFinalizeKind(ctx {{.contextContext|raw}}, o *{{.type|raw}}) {{.reconcilerEvent|raw}}
}
type doReconcile func(ctx {{.contextContext|raw}}, o *{{.type|raw}}) {{.reconcilerEvent|raw}}
const (
doReconcileKind = "ReconcileKind"
doFinalizeKind = "FinalizeKind"
doObserveKind = "ObserveKind"
doObserveFinalizeKind = "ObserveFinalizeKind"
)
// reconcilerImpl implements controller.Reconciler for {{.type|raw}} resources.
type reconcilerImpl struct {
// LeaderAwareFuncs is inlined to help us implement {{.reconcilerLeaderAware|raw}}
@ -332,22 +341,19 @@ var reconcilerImplFactory = `
func (r *reconcilerImpl) Reconcile(ctx {{.contextContext|raw}}, key string) error {
logger := {{.loggingFromContext|raw}}(ctx)
// Convert the namespace/name string into a distinct namespace and name
namespace, name, err := {{.cacheSplitMetaNamespaceKey|raw}}(key)
// Initialize the reconciler state. This will convert the namespace/name
// string into a distinct namespace and name, determin 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.Errorf("invalid resource key: %s", key)
return nil
}
// Establish whether we are the leader for use below.
isLeader := r.IsLeaderFor({{.typesNamespacedName|raw}}{
Namespace: namespace,
Name: name,
})
roi, isROI := r.reconciler.(ReadOnlyInterface)
rof, isROF := r.reconciler.(ReadOnlyFinalizer);
if !isLeader && !isROI && !isROF {
// If we are not the leader, and we don't implement either ReadOnly
// interface, then take a fast-path out.
// 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
}
@ -363,9 +369,9 @@ func (r *reconcilerImpl) Reconcile(ctx {{.contextContext|raw}}, key string) erro
{{if .nonNamespaced}}
getter := r.Lister
{{else}}
getter := r.Lister.{{.type|apiGroup}}(namespace)
getter := r.Lister.{{.type|apiGroup}}(s.namespace)
{{end}}
original, err := getter.Get(name)
original, err := getter.Get(s.name)
if {{.apierrsIsNotFound|raw}}(err) {
// The resource may no longer exist, in which case we stop processing.
@ -387,50 +393,45 @@ func (r *reconcilerImpl) Reconcile(ctx {{.contextContext|raw}}, key string) erro
resource := original.DeepCopy()
var reconcileEvent {{.reconcilerEvent|raw}}
if resource.GetDeletionTimestamp().IsZero() {
if isLeader {
// 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}}
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 = r.reconciler.ReconcileKind(ctx, resource)
{{if .isKRShaped}}
reconciler.PostProcessReconcile(ctx, resource, original)
{{end}}
} else if isROI {
// Append the target method to the logger.
logger = logger.With(zap.String("targetMethod", "ObserveKind"))
// Observe any changes to this resource, since we are not the leader.
reconcileEvent = roi.ObserveKind(ctx, resource)
}
} else if fin, ok := r.reconciler.(Finalizer); isLeader && ok {
name, do := s.reconcileMethodFor(resource)
// Append the target method to the logger.
logger = logger.With(zap.String("targetMethod", name))
switch name {
case doReconcileKind:
// Append the target method to the logger.
logger = logger.With(zap.String("targetMethod", "FinalizeKind"))
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}}
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}}
reconciler.PostProcessReconcile(ctx, resource, original)
{{end}}
case "FinalizeKind":
// For finalizing reconcilers, if this resource being marked for deletion
// and reconciled cleanly (nil or normal event), remove the finalizer.
reconcileEvent = fin.FinalizeKind(ctx, resource)
reconcileEvent = do(ctx, resource)
if resource, err = r.clearFinalizer(ctx, resource, reconcileEvent); err != nil {
return {{.fmtErrorf|raw}}("failed to clear finalizers: %w", err)
}
} else if !isLeader && isROF {
// Append the target method to the logger.
logger = logger.With(zap.String("targetMethod", "ObserveFinalizeKind"))
// For finalizing reconcilers, just observe when we aren't the leader.
reconcileEvent = rof.ObserveFinalizeKind(ctx, resource)
case "ObserveKind", "ObserveFinalizeKind":
// Observe any changes to this resource, since we are not the leader.
reconcileEvent = do(ctx, resource)
}
// Synchronize the status.
@ -443,7 +444,7 @@ func (r *reconcilerImpl) Reconcile(ctx {{.contextContext|raw}}, key string) erro
// 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 !isLeader:
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!")

View File

@ -139,8 +139,9 @@ func (r *Reconciler) ReconcileKind(ctx {{.contextContext|raw}}, o *{{.type|raw}}
{{if not .isKRShaped}}
// TODO: use this if the object has .status.ObservedGeneration.
// o.Status.ObservedGeneration = o.Generation{{end}}
return newReconciledNormal(o.Namespace, o.Name)
// o.Status.ObservedGeneration = o.Generation
{{end}}
return nil
}
// Optionally, use FinalizeKind to add finalizers. FinalizeKind will be called

View File

@ -0,0 +1,163 @@
/*
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 reconcilation 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).Infof("processing type %v", 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",
}),
}
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, s.reconciler.ReconcileKind
} else if s.isROI {
return doObserveKind, s.roi.ObserveKind
}
} else if fin, ok := s.reconciler.(Finalizer); s.isLeader && ok {
return doFinalizeKind, fin.FinalizeKind
} else if !s.isLeader && s.isROF {
return doObserveFinalizeKind, s.rof.ObserveFinalizeKind
}
return "unknown", nil
}
`

View File

@ -476,8 +476,20 @@ reconciler.PostProcessReconcile(ctx, resource, oldResource)
#### Stubs
To get started, or to use as reference. It is intended to be copied out of the
`client` dir.
To enable stubs generation, add the stubs flag:
```go
// +genreconciler:stubs
```
Or with the class annotation:
```go
// +genreconciler:class=example.com/filter.class,stubs
```
The stubs are intended to be used to get started, or to use as reference. It is
intended to be copied out of the `client` dir.
`knative.dev/<repo>/pkg/client/injection/reconciler/<clientgroup>/<version>/<kind>/stubs/controller.go`