~ knotless Search/
↑↓ move↵ open⇧↵ all results
0
0
main
146 lines · 4.8 KB Raw History
package events

import (
	"context"
	"errors"
	"fmt"

	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/client/apiutil"

	eventsv1alpha1 "github.com/johnandersen777/socialweb-computer-kcp/api/events/v1alpha1"
	"github.com/johnandersen777/socialweb-computer-kcp/api/v1alpha1"
	"github.com/johnandersen777/socialweb-computer-kcp/internal/controllers/atprotorecord"
	"github.com/johnandersen777/socialweb-computer-kcp/internal/controllers/atprotorecordwatch"
	"github.com/johnandersen777/socialweb-computer-kcp/internal/controllers/runtime/kube"
	"github.com/johnandersen777/socialweb-computer-kcp/internal/eventstream"
)

type ClientFor func(workspace string) (client.Client, error)

var (
	_ atprotorecordwatch.Children = recordWatchChildren{}
	_ atprotorecordwatch.Repos    = recordWatchRepos{}
)

func RecordWatch(clientFor ClientFor) (atprotorecordwatch.Children, atprotorecordwatch.Repos, atprotorecordwatch.ValueReader) {
	return recordWatchChildren{clientFor: clientFor}, recordWatchRepos{clientFor: clientFor}, recordWatchValues(clientFor)
}

func (f ClientFor) client(workspace string) (client.Client, error) {
	if f == nil {
		return nil, errors.New("events: the record watch has no workspace clients")
	}
	return f(workspace)
}

type recordWatchChildren struct {
	clientFor ClientFor
}

func (r recordWatchChildren) ApplyEventStream(ctx context.Context, workspace string, obj *eventsv1alpha1.EventStream) error {
	return r.apply(ctx, workspace, obj)
}

func (r recordWatchChildren) ApplyEventSource(ctx context.Context, workspace string, obj *eventsv1alpha1.EventSource) error {
	return r.apply(ctx, workspace, obj)
}

func (r recordWatchChildren) ApplyPdsEventSource(ctx context.Context, workspace string, obj *eventsv1alpha1.PdsEventSource) error {
	return r.apply(ctx, workspace, obj)
}

func (r recordWatchChildren) EventStreams(ctx context.Context, workspace, watch string) ([]eventsv1alpha1.EventStream, error) {
	c, err := r.clientFor.client(workspace)
	if err != nil {
		return nil, err
	}
	var list eventsv1alpha1.EventStreamList
	if err := c.List(ctx, &list, client.MatchingLabels{eventstream.RecordWatchLabel: watch}); err != nil {
		return nil, err
	}
	return list.Items, nil
}

func (r recordWatchChildren) DeleteEventStream(ctx context.Context, workspace, name string) error {
	c, err := r.clientFor.client(workspace)
	if err != nil {
		return err
	}
	obj := &eventsv1alpha1.EventStream{ObjectMeta: metav1.ObjectMeta{Name: name}}
	if err := c.Delete(ctx, obj, client.PropagationPolicy(metav1.DeletePropagationBackground)); err != nil && !apierrors.IsNotFound(err) {
		return err
	}
	return nil
}

func (r recordWatchChildren) apply(ctx context.Context, workspace string, obj client.Object) error {
	c, err := r.clientFor.client(workspace)
	if err != nil {
		return err
	}
	gvk, err := apiutil.GVKForObject(obj, c.Scheme())
	if err != nil {
		return err
	}
	if err := kube.ApplyWith(ctx, c, obj, kube.ApplyOptions{FieldManager: atprotorecordwatch.FieldManager, Force: true}); err != nil {
		return fmt.Errorf("events: applying %s %s in %s: %w", gvk.Kind, obj.GetName(), workspace, err)
	}
	return nil
}

type recordWatchRepos struct {
	clientFor ClientFor
}

func (r recordWatchRepos) AtprotoRepo(ctx context.Context, workspace, name string) (*v1alpha1.AtprotoRepo, error) {
	return get[v1alpha1.AtprotoRepo](ctx, r.clientFor, workspace, name)
}

func (r recordWatchRepos) LocalRepo(ctx context.Context, workspace, name string) (*v1alpha1.LocalRepo, error) {
	return get[v1alpha1.LocalRepo](ctx, r.clientFor, workspace, name)
}

func (r recordWatchRepos) RemoteRepo(ctx context.Context, workspace, name string) (*v1alpha1.RemoteRepo, error) {
	return get[v1alpha1.RemoteRepo](ctx, r.clientFor, workspace, name)
}

func get[T any, P interface {
	*T
	client.Object
}](ctx context.Context, clientFor ClientFor, workspace, name string) (*T, error) {
	c, err := clientFor.client(workspace)
	if err != nil {
		return nil, err
	}
	out := P(new(T))
	if err := c.Get(ctx, client.ObjectKey{Name: name}, out); err != nil {
		return nil, err
	}
	return (*T)(out), nil
}

func recordWatchValues(clientFor ClientFor) atprotorecordwatch.ValueReader {
	return func(ctx context.Context, workspace string, from v1alpha1.AtprotoValueFrom) (map[string]any, error) {
		c, err := clientFor.client(workspace)
		if err != nil {
			return nil, err
		}
		gvk, err := c.RESTMapper().KindFor(atprotorecord.ReferenceResource(from))
		if err != nil {
			return nil, err
		}
		mapping, err := c.RESTMapper().RESTMapping(gvk.GroupKind(), gvk.Version)
		if err != nil {
			return nil, err
		}
		key := client.ObjectKey{Name: from.Name}
		if mapping.Scope.Name() == meta.RESTScopeNameNamespace {
			key.Namespace = from.Namespace
		}
		return kube.GetWith(ctx, c, gvk, key)
	}
}