Last active
March 25, 2019 21:23
-
-
Save grantr/d149209f65def521e8e95b9d301f591e to your computer and use it in GitHub Desktop.
A controller interface that can live peacefully in knative/pkg
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ObjectReconciler is what the user implements. This is the generic version. | |
// The controller MUST be aware of whether it's running with CR or CG. | |
// With this interface, at least the code will be structured similarly | |
// and the client interaction can converge over time (or not, as desired) | |
// This is called ObjectReconciler to avoid confusion with existing Reconciler | |
// interfaces. | |
type ObjectReconciler interface { | |
// This could alternately be called ReconcileObject. | |
Reconcile(context.Context, runtime.Object) error | |
} | |
// ObjectFinalizer is optionally implemented by the user when a finalizer is | |
// required. | |
type ObjectFinalizer interface { | |
// AddFinalizer is called when the object has no finalizer and has not | |
// been deleted. The function can return true or false to signal that | |
// a finalizer should be added or not. Most of the time it will just | |
// return true. | |
AddFinalizer(context.Context, runtime.Object) bool | |
// FinalizeObject is called when the object has a finalizer and has been deleted. | |
Finalize(context.Context, runtime.Object) error | |
} | |
// Code generation could possibly be used to generate typed versions of these | |
// interfaces, e.g. BrokerReconciler, BrokerFinalizer that take | |
// *eventingv1alpha1.Broker instead of runtime.Object. | |
// Each Reconciler struct embeds an "implementation" struct. This allows multiple | |
// versions of controller boilerplates to be used concurrently, with as many | |
// interfaces as possible remaining the same. Ideally we would eventually converge | |
// on a single implementation, but that's not guaranteed - they may have different | |
// performance or flexibility tradeoffs. | |
// BrokerReconciler is a Reconciler that embeds the Controller Runtime reconciler | |
// struct to get its behavior. | |
type BrokerReconciler struct { | |
*ControllerRuntimeReconciler | |
} | |
// ControllerRuntimeReconciler is an implementation of all the boilerplate necessary | |
// to write and run a Reconciler that uses Controller Runtime underneath. | |
type ControllerRuntimeReconciler struct { | |
crr controllerRuntimeReconcileImpl | |
} | |
// Start implements the Controller Runtime ProvideController boilerplate. | |
// It adds the reconciler to a package-level Manager which is now an | |
// implementation detail of this package. | |
func (crr *ControllerRuntimeReconciler) Start(ctx context.Context) error { | |
// Manager setup is done by a package-level function containing | |
// a sync.Once. | |
mgr, err := GetManager() | |
// The Reconciler object given to controller.Controller is the internal | |
// controllerRuntimeReconcileImpl object rather than this outer one. | |
} | |
// This is an internal type that implements the | |
// Reconcile(reconcile.Request) (reconcile.Result, error) interface that | |
// Controller Runtime expects. Keeping this internal avoids method signature | |
// conflicts and user visibility of the Controller Runtime interface. | |
type controllerRuntimeReconcileImpl struct { | |
} | |
func (crri *controllerRuntimeReconcileImpl) Reconcile(request reconcile.Request) (reconcile.Result, error) { | |
} | |
// RevisionReconciler embeds the client-go Reconciler implementation. | |
type RevisionReconciler struct { | |
*ClientGoReconciler | |
} | |
// ClientGoReconciler is an alternative to ControllerRuntimeReconciler. | |
type ClientGoReconciler struct { | |
} | |
// Start implements the client-go boilerplate, e.g. starting informers. | |
func (cgr *ClientGoReconciler) Start(ctx context.Context) { | |
} | |
// Starter is used by the main function to start each reconciler, and blocks | |
// until the controller is stopped. This is implemented by an embedded struct. | |
type Starter interface { | |
Start(context.Context) error | |
} | |
// StarterList collects starters and starts them with an errgroup. | |
type StarterList struct{ | |
starters []Starter | |
} | |
func (sl *StarterList) Add(s Starter) { | |
sl.starters = append(sl.starters, s) | |
} | |
func (sl *StarterList) Items() []Starter { | |
return sl.starters | |
} | |
func (sl *StarterList) Start(ctx context.Context) error { | |
g, gCtx := errgroup.WithContext(ctx) | |
for _, s := range sl.Starters.Items() { | |
g.Go(func() error { | |
return s.Start(gCtx) | |
}) | |
} | |
g.Wait() | |
} | |
// There's a default package-level StartersList in the enclosing package, | |
// similar to scheme.Scheme. | |
var ( | |
Starters StarterList | |
) | |
// Reconciler packages define init() methods that set up reconcilers as | |
// package-internal vars, then add those vars to the default starters list. | |
func init() { | |
// The content of this function will be specific to the Reconciler behavior | |
// and the implementation being used. | |
br := &BrokerReconciler{ | |
// The content of this | |
} | |
Starters.Add(br) | |
} | |
// The controller manager main function looks like this. | |
func main() { | |
// Do logging, config set up tasks as needed | |
signaledCtx, cancel := SignaledContext(configuredCtx) | |
log.Fatalf(Starters.Start(signaledCtx)) | |
} | |
// SignaledContext is a slightly reworking of the existing signals code to use | |
// context cancellation instead of stop channels. | |
func SignaledContext(ctx context.Context) (context.Context, context.CancelFunc) { | |
signaledCtx, cancel := context.WithCancel(ctx) | |
go func() { | |
<-globalSignalsCh | |
cancel() | |
} | |
return signaledCtx, cancel | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment