~ knotless Search/
↑↓ move↵ open⇧↵ all results
0
0
main
97 lines · 2.7 KB Raw History
package guestidentitygrant

import (
	"context"
	"errors"
	"fmt"
	"log/slog"
	"time"

	"k8s.io/apimachinery/pkg/api/equality"

	"github.com/johnandersen777/socialweb-computer-kcp/api/v1alpha1"
	ctlruntime "github.com/johnandersen777/socialweb-computer-kcp/internal/controllers/runtime"
	"github.com/johnandersen777/socialweb-computer-kcp/internal/openbaoctl"
)

const (
	Name = "guestidentitygrant"

	Export = "computevms"
)

type Outcomes interface {
	Outcome(cluster, name string) (v1alpha1.GuestIdentityGrantStatus, bool)
	Forget(cluster, name string)
}

type Options struct {
	Outcomes Outcomes

	Events <-chan ctlruntime.Request

	Scope func(workspace string) bool

	Log *slog.Logger
}

type Controller struct {
	log *slog.Logger

	outcomes Outcomes
}

func New(opts Options) (*Controller, error) {
	if opts.Outcomes == nil {
		return nil, errors.New("guestidentitygrant: the grant outcomes of the OpenBaoServer controller are required")
	}
	log := opts.Log
	if log == nil {
		log = slog.New(slog.DiscardHandler)
	}
	return &Controller{log: log.With("component", "guestidentitygrant-controller"), outcomes: opts.Outcomes}, nil
}

func Register(m *ctlruntime.Manager, opts Options) (*Controller, error) {
	ctl, err := New(opts)
	if err != nil {
		return nil, err
	}
	options := []ctlruntime.Option{ctlruntime.Export(Export), ctlruntime.Gone(ctl.gone)}
	if opts.Events != nil {
		options = append(options, ctlruntime.Events(opts.Events))
	}
	if opts.Scope != nil {
		options = append(options, ctlruntime.InWorkspaces(opts.Scope))
	}
	if err := ctlruntime.For[*v1alpha1.GuestIdentityGrant](m, Name, ctl, options...); err != nil {
		return nil, fmt.Errorf("guestidentitygrant: registering the GuestIdentityGrant controller: %w", err)
	}
	return ctl, nil
}

func (c *Controller) Reconcile(ctx context.Context, req ctlruntime.Request, object *v1alpha1.GuestIdentityGrant) (ctlruntime.Result, error) {
	if !object.DeletionTimestamp.IsZero() {
		return ctlruntime.Result{}, nil
	}
	outcome, ok := c.outcomes.Outcome(req.Cluster, object.Name)
	if !ok || outcome.ObservedGeneration != object.Generation {
		return ctlruntime.Result{}, nil
	}
	status := openbaoctl.MergeGrantStatus(object.Status, outcome, time.Now())
	if equality.Semantic.DeepEqual(object.Status, status) {
		return ctlruntime.Result{}, nil
	}
	next := object.DeepCopy()
	next.Status = status
	if err := req.ApplyStatus(ctx, next); err != nil {
		return ctlruntime.Result{}, err
	}
	c.log.Info("the GuestIdentityGrant status follows its OpenBaoServer", "workspace", req.Workspace, "name", req.Name,
		"applied", status.Applied, "role", status.Role)
	return ctlruntime.Result{}, nil
}

func (c *Controller) gone(_ context.Context, req ctlruntime.Request) {
	c.outcomes.Forget(req.Cluster, req.Name)
}