Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bare bones termination controller #260

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apis/rbac/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

54 changes: 52 additions & 2 deletions apis/workloads/v1alpha1/console_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,60 @@ type ConsoleStatus struct {
PodName string `json:"podName"`
ExpiryTime *metav1.Time `json:"expiryTime,omitempty"`
// Time at which the job completed successfully
CompletionTime *metav1.Time `json:"completionTime,omitempty"`
Phase ConsolePhase `json:"phase"`
CompletionTime *metav1.Time `json:"completionTime,omitempty"`
Phase ConsolePhase `json:"phase"`
Conditions []ConsoleCondition `json:"conditions,omitempty"`
}

// Console conditions describe a valid condition for a console
type ConsoleConditionType string

const (
// ConsoleTerminatedType describes the type of condition where the console
// is terminated
ConsoleTerminatedType ConsoleConditionType = "ConsoleTerminated"

// ConsoleFailedType describes the type of condition where the controller
// tried to start the console, but there was some error causing it to fail
ConsoleFailedType ConsoleConditionType = "ConsoleFailed"
)

// ConsoleCondition is a status condition for a console
type ConsoleCondition struct {
// Type of this condition
Type ConsoleConditionType `json:"type"`

// Status of this condition
Status corev1.ConditionStatus `json:"status"`

// LastUpdateTime of this condition
LastUpdateTime metav1.Time `json:"lastUpdateTime,omitempty"`

// LastTransitionTime of this condition
LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty"`

// Reason for the current status of this condition
Reason ConsoleConditionReason `json:"reason,omitempty"`

// Message associated with this condition
Message string `json:"message,omitempty"`
}

// ConsoleConditionReason represents a valid condition reason for a console
type ConsoleConditionReason string

const (
// ReasonTlogNotFound is a console condition for a failed console because
// tlog-rec was not found in the image
ReasonTlogNotFound ConsoleConditionReason = "tlog-rec binary not found"

ReasonCrashLoopBackoff ConsoleConditionReason = "pod for console is in CrashLoopBackoff"

ReasonImagePull ConsoleConditionReason = "specified image failed to pull"

ReasonTimedOutAuthorize ConsoleConditionReason = "specified image failed to pull"
)

// +kubebuilder:object:root=true
// +kubebuilder:storageversion

Expand Down
25 changes: 25 additions & 0 deletions apis/workloads/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions cmd/workloads-manager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -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(
Expand Down
29 changes: 29 additions & 0 deletions config/base/crds/workloads.crd.gocardless.com_consoles.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,35 @@ spec:
description: Time at which the job completed successfully
format: date-time
type: string
conditions:
items:
description: ConsoleCondition is a status condition for a console
properties:
lastTransitionTime:
description: LastTransitionTime of this condition
format: date-time
type: string
lastUpdateTime:
description: LastUpdateTime of this condition
format: date-time
type: string
message:
description: Message associated with this condition
type: string
reason:
description: Reason for the current status of this condition
type: string
status:
description: Status of this condition
type: string
type:
description: Type of this condition
type: string
required:
- status
- type
type: object
type: array
expiryTime:
format: date-time
type: string
Expand Down
29 changes: 29 additions & 0 deletions controllers/workloads/console/termination/handler/handler.go
Original file line number Diff line number Diff line change
@@ -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
}
}
10 changes: 10 additions & 0 deletions controllers/workloads/console/termination/status/status.go
Original file line number Diff line number Diff line change
@@ -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
}
13 changes: 13 additions & 0 deletions controllers/workloads/console/termination/status/types.go
Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
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"
batchv1 "k8s.io/api/batch/v1"
"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.Job{}}, &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
}