~ knotless Search/
↑↓ move↵ open⇧↵ all results
0
0
main
185 lines · 6.6 KB Raw History
package childapplication

import (
	"context"
	"sort"
	"strings"

	"sigs.k8s.io/controller-runtime/pkg/client"

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

const (
	PolicyExport = "policy"

	coreUnitPrefix   = "socialweb-core-"
	coreUnitSuffix   = "-dev.service"
	coreObjectPrefix = "core-"
	coreObjectSuffix = "-dev"
)

type component struct {
	v1alpha1.ChildApplicationComponent

	failed bool
}

type componentKind struct {
	read func(ctx context.Context, l Lister, workspace string) ([]component, error)

	watch func(mapFunc func(ctx context.Context, src ctlruntime.Request) []ctlruntime.Request) ctlruntime.Option
}

func kindOf[L any, PL interface {
	*L
	client.ObjectList
}, T any, PT interface {
	*T
	ctlruntime.Object
}](export, kind string, items func(*L) []T, state func(PT) (bool, bool)) componentKind {
	return componentKind{
		read: func(ctx context.Context, l Lister, workspace string) ([]component, error) {
			var list L
			if err := l.List(ctx, workspace, PL(&list)); err != nil {
				return nil, err
			}
			src := items(&list)
			out := make([]component, 0, len(src))
			for i := range src {
				obj := PT(&src[i])
				ready, failed := state(obj)
				out = append(out, component{ChildApplicationComponent: v1alpha1.ChildApplicationComponent{Kind: kind, Name: obj.GetName(), Ready: ready}, failed: failed})
			}
			return out, nil
		},
		watch: func(mapFunc func(ctx context.Context, src ctlruntime.Request) []ctlruntime.Request) ctlruntime.Option {
			return ctlruntime.WatchesIn(export, func(ctx context.Context, src ctlruntime.Request, _ PT) []ctlruntime.Request {
				return mapFunc(ctx, src)
			})
		},
	}
}

func componentState(s v1alpha1.ComponentStatus) (bool, bool) {
	return s.Ready, s.Phase == v1alpha1.ComponentPhaseFailed
}

var componentKinds = []componentKind{
	kindOf(Export, "Bidder", func(l *v1alpha1.BidderList) []v1alpha1.Bidder { return l.Items },
		func(o *v1alpha1.Bidder) (bool, bool) { return componentState(o.Status.ComponentStatus) }),
	kindOf(Export, "GuestControlPlane", func(l *v1alpha1.GuestControlPlaneList) []v1alpha1.GuestControlPlane { return l.Items },
		func(o *v1alpha1.GuestControlPlane) (bool, bool) { return componentState(o.Status) }),
	kindOf(Export, "Pds", func(l *v1alpha1.PdsList) []v1alpha1.Pds { return l.Items },
		func(o *v1alpha1.Pds) (bool, bool) { return componentState(o.Status) }),
	kindOf(Export, "Requester", func(l *v1alpha1.RequesterList) []v1alpha1.Requester { return l.Items },
		func(o *v1alpha1.Requester) (bool, bool) { return componentState(o.Status) }),
	kindOf(Export, "GatewayAPI", func(l *v1alpha1.GatewayAPIList) []v1alpha1.GatewayAPI { return l.Items },
		func(o *v1alpha1.GatewayAPI) (bool, bool) { return componentState(o.Status) }),
	kindOf(Export, "SshServer", func(l *v1alpha1.SshServerList) []v1alpha1.SshServer { return l.Items },
		func(o *v1alpha1.SshServer) (bool, bool) { return componentState(o.Status) }),
	kindOf(Export, "Plc", func(l *v1alpha1.PlcList) []v1alpha1.Plc { return l.Items },
		func(o *v1alpha1.Plc) (bool, bool) { return componentState(o.Status) }),
	kindOf(Export, "Relay", func(l *v1alpha1.RelayList) []v1alpha1.Relay { return l.Items },
		func(o *v1alpha1.Relay) (bool, bool) { return componentState(o.Status) }),
	kindOf(Export, "IngressProxy", func(l *v1alpha1.IngressProxyList) []v1alpha1.IngressProxy { return l.Items },
		func(o *v1alpha1.IngressProxy) (bool, bool) { return componentState(o.Status) }),
	kindOf(Export, "DispatcherTLS", func(l *v1alpha1.DispatcherTLSList) []v1alpha1.DispatcherTLS { return l.Items },
		func(o *v1alpha1.DispatcherTLS) (bool, bool) { return componentState(o.Status) }),
	kindOf(PolicyExport, "PolicyEngine", func(l *policyv1alpha1.PolicyEngineList) []policyv1alpha1.PolicyEngine { return l.Items },
		func(o *policyv1alpha1.PolicyEngine) (bool, bool) {
			return o.Status.Ready, o.Status.Phase == policyv1alpha1.PolicyEngineFailed
		}),
}

func (c *Controller) observeCore(ctx context.Context, instance string) (string, bool, bool, error) {
	var list v1alpha1.HostProcessList
	if err := c.objects.List(ctx, DevWorkspacePrefix+":"+instance, &list); err != nil {
		if absent(err) {
			return "", false, false, nil
		}
		return "", false, false, err
	}
	for i := range list.Items {
		item := &list.Items[i]
		unit := item.Spec.Unit
		if !coreUnit(unit, item.Name) {
			continue
		}
		ref := unit
		if ref == "" {
			ref = item.Name
		}
		phase := item.Status.Phase
		ready := phase == v1alpha1.HostProcessPhaseRunning
		failed := phase == v1alpha1.HostProcessPhaseFailed || phase == v1alpha1.HostProcessPhaseLost
		return ref, ready, failed, nil
	}
	return "", false, false, nil
}

func coreUnit(unit, name string) bool {
	if strings.HasPrefix(unit, coreUnitPrefix) && strings.HasSuffix(unit, coreUnitSuffix) {
		return true
	}
	return strings.HasPrefix(name, coreObjectPrefix) && strings.HasSuffix(name, coreObjectSuffix)
}

func (c *Controller) observeComponents(ctx context.Context, workspace string) ([]v1alpha1.ChildApplicationComponent, bool, bool, error) {
	var out []v1alpha1.ChildApplicationComponent
	failed := false
	for _, kind := range componentKinds {
		items, err := kind.read(ctx, c.objects, workspace+":"+ComponentWorkspace)
		if absent(err) {
			continue
		}
		if err != nil {
			return nil, false, false, err
		}
		for _, item := range items {
			failed = failed || item.failed
			out = append(out, item.ChildApplicationComponent)
		}
	}
	sortComponents(out)
	return out, failed, allReady(out), nil
}

func fallbackComponents(appStatus *v1alpha1.NetworkAppStatus) ([]v1alpha1.ChildApplicationComponent, bool, bool) {
	if appStatus == nil {
		return nil, false, false
	}
	out := make([]v1alpha1.ChildApplicationComponent, 0, len(appStatus.Components))
	failed := false
	for _, component := range appStatus.Components {
		if component.Phase == string(v1alpha1.ComponentPhaseFailed) {
			failed = true
		}
		out = append(out, v1alpha1.ChildApplicationComponent{Kind: component.Kind, Name: component.Name, Ready: component.Ready})
	}
	sortComponents(out)
	return out, failed, allReady(out)
}

func sortComponents(components []v1alpha1.ChildApplicationComponent) {
	sort.Slice(components, func(i, j int) bool {
		if components[i].Kind != components[j].Kind {
			return components[i].Kind < components[j].Kind
		}
		return components[i].Name < components[j].Name
	})
}

func allReady(components []v1alpha1.ChildApplicationComponent) bool {
	if len(components) == 0 {
		return false
	}
	for _, component := range components {
		if !component.Ready {
			return false
		}
	}
	return true
}