From 15c948441c4e8a13c569c8d556d03a34afad211d Mon Sep 17 00:00:00 2001 From: Theo Barber-Bany Date: Thu, 17 Feb 2022 15:41:21 +0000 Subject: [PATCH] Add barebones termination controller A very bare bones termination controller to watch Consoles, determine why their pods fail and update conditions on their status. --- cmd/workloads-manager/main.go | 14 +++ .../console/termination/handler/handler.go | 29 ++++++ .../console/termination/status/status.go | 10 ++ .../console/termination/status/types.go | 13 +++ .../termination/termination_controller.go | 94 +++++++++++++++++++ 5 files changed, 160 insertions(+) create mode 100644 controllers/workloads/console/termination/handler/handler.go create mode 100644 controllers/workloads/console/termination/status/status.go create mode 100644 controllers/workloads/console/termination/status/types.go create mode 100644 controllers/workloads/console/termination/termination_controller.go diff --git a/cmd/workloads-manager/main.go b/cmd/workloads-manager/main.go index 280247f8..bd8eebcb 100644 --- a/cmd/workloads-manager/main.go +++ b/cmd/workloads-manager/main.go @@ -16,6 +16,7 @@ import ( workloadsv1alpha1 "github.com/gocardless/theatre/v3/apis/workloads/v1alpha1" "github.com/gocardless/theatre/v3/cmd" consolecontroller "github.com/gocardless/theatre/v3/controllers/workloads/console" + terminationcontroller "github.com/gocardless/theatre/v3/controllers/workloads/console/termination" "github.com/gocardless/theatre/v3/pkg/signals" "github.com/gocardless/theatre/v3/pkg/workloads/console/events" ) @@ -102,6 +103,19 @@ func main() { app.Fatalf("failed to create controller: %v", err) } + // controller to observe termination states + if err = (&terminationcontroller.TerminationReconciler{ + // Client: mgr.GetClient(), + // LifecycleRecorder: lifecycleRecorder, + // ConsoleIdBuilder: idBuilder, + // Log: ctrl.Log.WithName("controllers").WithName("termination-watcher"), + // Scheme: mgr.GetScheme(), + // SessionPubsubProjectId: *sessionPubsubProjectId, + // SessionPubsubTopicId: *sessionPubsubTopicId, + }).SetupWithManager(ctx, mgr); err != nil { + app.Fatalf("failed to create termination watching controller: %v", err) + } + // console authenticator webhook mgr.GetWebhookServer().Register("/mutate-consoles", &admission.Webhook{ Handler: workloadsv1alpha1.NewConsoleAuthenticatorWebhook( diff --git a/controllers/workloads/console/termination/handler/handler.go b/controllers/workloads/console/termination/handler/handler.go new file mode 100644 index 00000000..f53ce817 --- /dev/null +++ b/controllers/workloads/console/termination/handler/handler.go @@ -0,0 +1,29 @@ +package handler + +import ( + workloadsv1alpha1 "github.com/gocardless/theatre/v3/apis/workloads/v1alpha1" + "github.com/gocardless/theatre/v3/controllers/workloads/console/termination/status" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func main() { +} + +type TerminationHandler struct { + client client.Client +} + +type Options struct{} + +func NewTerminationHandler(c client.Client, opts *Options) *TerminationHandler { + return &TerminationHandler{ + client: c, + } +} + +func (h *TerminationHandler) Handle(instance *workloadsv1alpha1.Console) (*status.Result, error) { + switch instance.Status.Phase { + default: + return nil, nil + } +} diff --git a/controllers/workloads/console/termination/status/status.go b/controllers/workloads/console/termination/status/status.go new file mode 100644 index 00000000..f69d77c4 --- /dev/null +++ b/controllers/workloads/console/termination/status/status.go @@ -0,0 +1,10 @@ +package status + +import ( + workloadsv1alpha1 "github.com/gocardless/theatre/v3/apis/workloads/v1alpha1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func UpdateStatus(c client.Client, instance *workloadsv1alpha1.Console, result *Result) error { + return nil +} diff --git a/controllers/workloads/console/termination/status/types.go b/controllers/workloads/console/termination/status/types.go new file mode 100644 index 00000000..53c981f0 --- /dev/null +++ b/controllers/workloads/console/termination/status/types.go @@ -0,0 +1,13 @@ +package status + +import ( + workloadsv1alpha1 "github.com/gocardless/theatre/v3/apis/workloads/v1alpha1" +) + +// Result is the basis for updating the status of a Console with termination +// events +type Result struct { + Phase *workloadsv1alpha1.ConsolePhase + Condition workloadsv1alpha1.ConsoleConditionType + Reason workloadsv1alpha1.ConsoleConditionReason +} diff --git a/controllers/workloads/console/termination/termination_controller.go b/controllers/workloads/console/termination/termination_controller.go new file mode 100644 index 00000000..16e030cc --- /dev/null +++ b/controllers/workloads/console/termination/termination_controller.go @@ -0,0 +1,94 @@ +package termination + +import ( + "context" + "fmt" + + workloadsv1alpha1 "github.com/gocardless/theatre/v3/apis/workloads/v1alpha1" + + "github.com/gocardless/theatre/v3/controllers/workloads/console/termination/handler" + "github.com/gocardless/theatre/v3/controllers/workloads/console/termination/status" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + watchhandler "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" +) + +// newReconciler returns a new reconcile.Reconciler +func newReconciler(mgr manager.Manager) reconcile.Reconciler { + h := handler.NewTerminationHandler(mgr.GetClient(), &handler.Options{}) + return &TerminationReconciler{Client: mgr.GetClient(), handler: h, scheme: mgr.GetScheme()} +} + +func add(mgr manager.Manager, r reconcile.Reconciler) error { + // create the controller + c, err := controller.New("console-termination-controller", mgr, controller.Options{Reconciler: r}) + if err != nil { + return err + } + + // Watch for Console changes + err = c.Watch(&source.Kind{Type: &workloadsv1alpha1.Console{}}, &watchhandler.EnqueueRequestForOwner{ + IsController: true, + }) + if err != nil { + return nil + } + + // Watch for job changes + err = c.Watch(&source.Kind{Type: &batchv1.JobP{}}, &watchhandler.EnqueueRequestForOwner{ + OwnerType: &workloadsv1alpha1.Console{}, + IsController: true, + }) + if err != nil { + return nil + } + + // err = mgr.GetCache().IndexField(&batchv1.Job{}, "metadata.labels" ,func(obj runtime.Object) []string + + return nil +} + +var _ reconcile.Reconciler = &TerminationReconciler{} + +type TerminationReconciler struct { + client.Client + handler *handler.TerminationHandler + scheme *runtime.Scheme +} + +func (r *TerminationReconciler) SetupWithManager(ctx context.Context, mgr manager.Manager) error { + return add(mgr, newReconciler(mgr)) +} + +func (r *TerminationReconciler) Reconcile(request reconcile.Request) (reconcile.Result, error) { + instance := &workloadsv1alpha1.Console{} + err := r.Get(context.Background(), request.NamespacedName, instance) + if err != nil { + if errors.IsNotFound(err) { + return reconcile.Result{}, nil + } + return reconcile.Result{}, err + } + + result, err := r.handler.Handle(instance) + if err != nil { + statusErr := status.UpdateStatus(r.Client, instance, result) + if statusErr != nil { + // todo: logging good + panic("failed to update status") + } + + return reconcile.Result{}, fmt.Errorf("error handling console %s: %v", instance.GetName(), err) + } + + err = status.UpdateStatus(r.Client, instance, result) + if err != nil { + return reconcile.Result{}, fmt.Errorf("error updating status: %v", err) + } + return reconcile.Result{}, nil +}