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

import (
	"context"
	"errors"
	"fmt"

	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/rest"

	"github.com/johnandersen777/socialweb-computer-kcp/api/v1alpha1"
)

const clusterLinks = "clusterlinks"

func (c *Client) WriteClusterLink(ctx context.Context, logicalCluster string, link v1alpha1.ClusterLink) error {
	r, err := c.resource(logicalCluster, socialwebResource)
	if err != nil {
		return err
	}
	return WriteClusterLinkTo(ctx, r, link)
}

func WriteClusterLinkTo(ctx context.Context, r rest.Interface, link v1alpha1.ClusterLink) error {
	switch {
	case link.Name == "":
		return errors.New("kcpclient: WriteClusterLink needs a link name")
	case link.Spec.ClusterRef.Name == "":
		return errors.New("kcpclient: WriteClusterLink needs the cluster the link fronts")
	case link.Spec.Service == "":
		return errors.New("kcpclient: WriteClusterLink needs the service the link forwards to")
	case link.Spec.TargetPort <= 0:
		return fmt.Errorf("kcpclient: WriteClusterLink %s needs the loopback port it forwards to", link.Name)
	case link.Status.Phase == "":
		return fmt.Errorf("kcpclient: WriteClusterLink %s needs a phase; the schema's phase is an enum with no empty member", link.Name)
	}

	var existing v1alpha1.ClusterLink
	err := r.Get().Resource(clusterLinks).Name(link.Name).Do(ctx).Into(&existing)
	found := err == nil
	if err != nil && !apierrors.IsNotFound(err) {
		return fmt.Errorf("kcpclient: read clusterlink %s: %w", link.Name, err)
	}
	if !found || existing.Spec != link.Spec {
		obj := v1alpha1.ClusterLink{TypeMeta: clusterLinkType, ObjectMeta: metav1.ObjectMeta{Name: link.Name}, Spec: link.Spec}
		if err := applyDoc(ctx, r.Patch(types.ApplyPatchType).Resource(clusterLinks).Name(link.Name), fieldManagerClusterProvider, true, &obj, false); err != nil {
			return fmt.Errorf("kcpclient: apply clusterlink %s spec: %w", link.Name, err)
		}
	}
	if found && existing.Status == link.Status {
		return nil
	}
	return applyClusterLinkStatus(ctx, r, link.Name, link.Status)
}

var clusterLinkType = metav1.TypeMeta{APIVersion: v1alpha1.GroupVersion.String(), Kind: "ClusterLink"}

func (c *Client) WriteClusterLinkStatus(ctx context.Context, logicalCluster, name string, status v1alpha1.ClusterLinkStatus) (bool, error) {
	r, err := c.resource(logicalCluster, socialwebResource)
	if err != nil {
		return false, err
	}
	err = applyClusterLinkStatus(ctx, r, name, status)
	if apierrors.IsNotFound(err) {
		return false, nil
	}
	if err != nil {
		return false, err
	}
	return true, nil
}

func applyClusterLinkStatus(ctx context.Context, r rest.Interface, name string, status v1alpha1.ClusterLinkStatus) error {
	if name == "" {
		return errors.New("kcpclient: a clusterlink status write needs a link name")
	}
	obj := v1alpha1.ClusterLink{TypeMeta: clusterLinkType, ObjectMeta: metav1.ObjectMeta{Name: name}, Status: status}
	return applyDoc(ctx, r.Patch(types.ApplyPatchType).SubResource("status").Resource(clusterLinks).Name(name),
		fieldManagerClusterProvider, true, &obj, true)
}

func (c *Client) ReadClusterLink(ctx context.Context, logicalCluster, name string) (*v1alpha1.ClusterLink, error) {
	r, err := c.resource(logicalCluster, socialwebResource)
	if err != nil {
		return nil, err
	}
	if name == "" {
		return nil, errors.New("kcpclient: ReadClusterLink needs a link name")
	}
	var link v1alpha1.ClusterLink
	err = r.Get().Resource(clusterLinks).Name(name).Do(ctx).Into(&link)
	if apierrors.IsNotFound(err) {
		return nil, nil
	}
	if err != nil {
		return nil, fmt.Errorf("kcpclient: read clusterlink %s: %w", name, err)
	}
	return &link, nil
}

func (c *Client) DeleteClusterLink(ctx context.Context, logicalCluster, name string) error {
	r, err := c.resource(logicalCluster, socialwebResource)
	if err != nil {
		return err
	}
	if name == "" {
		return errors.New("kcpclient: DeleteClusterLink needs a link name")
	}
	err = r.Delete().Resource(clusterLinks).Name(name).Do(ctx).Error()
	if apierrors.IsNotFound(err) {
		return nil
	}
	if err != nil {
		return fmt.Errorf("kcpclient: delete clusterlink %s: %w", name, err)
	}
	return nil
}