mirror of https://github.com/knative/pkg.git
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:
parent
bc4dc000cb
commit
ae9c3f7fa8
|
@ -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.
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
# The OWNERS file is used by prow to automatically merge approved PRs.
|
||||
|
||||
approvers:
|
||||
- codegen-approvers
|
||||
|
||||
reviewers:
|
||||
- codegen-reviewers
|
|
@ -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!")
|
||||
|
|
105
client/injection/apiextensions/reconciler/apiextensions/v1/customresourcedefinition/state.go
generated
Normal file
105
client/injection/apiextensions/reconciler/apiextensions/v1/customresourcedefinition/state.go
generated
Normal 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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
//}
|
|
@ -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!")
|
||||
|
|
105
client/injection/apiextensions/reconciler/apiextensions/v1beta1/customresourcedefinition/state.go
generated
Normal file
105
client/injection/apiextensions/reconciler/apiextensions/v1beta1/customresourcedefinition/state.go
generated
Normal 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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
//}
|
|
@ -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!")
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
//}
|
|
@ -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!")
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
//}
|
|
@ -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
|
||||
|
|
|
@ -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!")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
`
|
|
@ -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`
|
||||
|
||||
|
|
Loading…
Reference in New Issue