~ knotless Search/
↑↓ move↵ open⇧↵ all results
0
0
main
129 lines · 4.1 KB Raw History
package atprotoidentity

import (
	"context"
	"errors"
	"fmt"
	"strings"
	"sync"

	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"github.com/johnandersen777/socialweb-computer-kcp/api/v1alpha1"
	"github.com/johnandersen777/socialweb-computer-kcp/internal/clusterprovider"
	"github.com/johnandersen777/socialweb-computer-kcp/internal/contract/clusterhome"
	ctlruntime "github.com/johnandersen777/socialweb-computer-kcp/internal/controllers/runtime"
	"github.com/johnandersen777/socialweb-computer-kcp/internal/revocation"
)

const (
	Name = "atprotoidentity"

	Export = "atprotoidentities"

	ClusterExport = "clusters"

	ConditionServiceAccountsErased = "ServiceAccountsErased"
)

type Clusters interface {
	AccountClusters(ctx context.Context, account string) ([]v1alpha1.Cluster, error)
}

type Controller struct {
	clusters Clusters

	mu sync.Mutex

	revoked map[string]string
}

func Register(m *ctlruntime.Manager, clusters Clusters) (*Controller, error) {
	if clusters == nil {
		return nil, errors.New("atprotoidentity: the controller needs a cluster reader")
	}
	if err := m.AddToScheme(v1alpha1.AddToScheme); err != nil {
		return nil, err
	}
	c := &Controller{clusters: clusters, revoked: map[string]string{}}
	if err := ctlruntime.For[*v1alpha1.AtprotoIdentity](m, Name, c, ctlruntime.Export(Export),
		ctlruntime.InWorkspaces(accountOrUnresolved),
		ctlruntime.WatchesIn(ClusterExport, c.clusterOf)); err != nil {
		return nil, fmt.Errorf("atprotoidentity: registering the controller: %w", err)
	}
	return c, nil
}

func (c *Controller) Reconcile(ctx context.Context, req ctlruntime.Request, obj *v1alpha1.AtprotoIdentity) (ctlruntime.Result, error) {
	if obj.Spec.Scope != v1alpha1.IdentityScopeAccount || !obj.Spec.Revoked || obj.Spec.DID == "" || !isAccount(req.Workspace) {
		return ctlruntime.Result{}, nil
	}
	c.mu.Lock()
	c.revoked[req.Workspace] = req.Cluster
	c.mu.Unlock()

	clusters, err := c.clusters.AccountClusters(ctx, req.Workspace)
	if err != nil {
		return ctlruntime.Result{}, fmt.Errorf("atprotoidentity: the clusters of %s: %w", req.Workspace, err)
	}
	views := make([]revocation.SettleView, 0, len(clusters))
	for i := range clusters {
		cl := &clusters[i]
		if cl.Spec.Account != obj.Spec.DID {
			continue
		}
		views = append(views, revocation.SettleView{
			Name:    cl.Name,
			Settled: meta.IsStatusConditionTrue(cl.Status.Conditions, clusterprovider.ConditionRevocationSettled) || cl.DeletionTimestamp != nil,
			Serving: cl.Spec.Running && cl.Status.Phase == v1alpha1.ClusterReady,
		})
	}
	effects := revocation.Settle(views)
	state := revocation.AccountState(effects)
	detail := revocation.Detail(effects)
	held := meta.FindStatusCondition(obj.Status.Conditions, ConditionServiceAccountsErased)
	if obj.Status.State == state && held != nil && held.Message == detail {
		return ctlruntime.Result{}, nil
	}
	settled := metav1.ConditionFalse
	if state == v1alpha1.IdentityRevoked {
		settled = metav1.ConditionTrue
	}
	obj.Status.State = state
	meta.SetStatusCondition(&obj.Status.Conditions, metav1.Condition{
		Type:               ConditionServiceAccountsErased,
		Status:             settled,
		Reason:             string(state),
		Message:            detail,
		ObservedGeneration: obj.Generation,
	})
	if err := req.ApplyStatus(ctx, obj); err != nil {
		return ctlruntime.Result{}, err
	}
	return ctlruntime.Result{}, nil
}

func (c *Controller) clusterOf(_ context.Context, src ctlruntime.Request, cl *v1alpha1.Cluster) []ctlruntime.Request {
	parts := strings.Split(src.Workspace, ":")
	if len(parts) != 3 || cl.Spec.Account == "" {
		return nil
	}
	account := parts[0] + ":" + parts[1]
	c.mu.Lock()
	logical, revoked := c.revoked[account]
	c.mu.Unlock()
	if !revoked {
		return nil
	}
	return []ctlruntime.Request{{Cluster: logical, Name: clusterhome.AccountHash(cl.Spec.Account)}}
}

func isAccount(workspace string) bool {
	parts := strings.Split(workspace, ":")
	return len(parts) == 2 && parts[0] == clusterhome.RootWorkspace && parts[1] != ""
}

func accountOrUnresolved(workspace string) bool {
	return !strings.Contains(workspace, ":") || isAccount(workspace)
}