~ knotless Search/
↑↓ move↵ open⇧↵ all results
0
0
main
116 lines · 3.6 KB Raw History
package clusterkcp

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"io"

	"k8s.io/apimachinery/pkg/runtime/schema"
	utilyaml "k8s.io/apimachinery/pkg/util/yaml"

	"github.com/johnandersen777/socialweb-computer-kcp/api/kcpgvr"
	"github.com/johnandersen777/socialweb-computer-kcp/internal/clusterprovider"
	"github.com/johnandersen777/socialweb-computer-kcp/internal/controllers/runtime/kube"
	"github.com/johnandersen777/socialweb-computer-kcp/internal/eso"
	"github.com/johnandersen777/socialweb-computer-kcp/internal/kcpclient"
	"github.com/johnandersen777/socialweb-computer-kcp/internal/kcpserver"
)

const fieldManager = "socialweb-clusterprovider"

var grantResources = map[string]schema.GroupVersionResource{
	"ClusterRoleBinding":               kcpgvr.ClusterRoleBindings,
	"ValidatingAdmissionPolicy":        kcpgvr.ValidatingAdmissionPolicies,
	"ValidatingAdmissionPolicyBinding": kcpgvr.ValidatingAdmissionPolicyBindings,
}

func Grant(ctx context.Context, ref clusterprovider.Ref, cred *clusterprovider.Credential, doc []byte) (int, error) {
	grant, err := kcpclient.RenderTenantGrant(string(doc), kcpclient.GrantScope{
		OwnerDID: ref.DID,
		ESOUser:  eso.ControllerUser,
	})
	if err != nil {
		return 0, fmt.Errorf("clusterprovider: the tenant grant for %s: %w", ref.Name, err)
	}
	objs, err := GrantObjects([]byte(grant))
	if err != nil {
		return 0, err
	}
	kc, err := kube.New(kcpserver.Throughput(RestConfig(cred)))
	if err != nil {
		return 0, fmt.Errorf("clusterprovider: a dynamic client for %s: %w", ref.Name, err)
	}
	if err := applyGrant(ctx, kc, objs); err != nil {
		return 0, err
	}
	return len(objs), nil
}

func GrantObjects(doc []byte) ([]kube.Doc, error) {
	dec := utilyaml.NewYAMLOrJSONDecoder(bytes.NewReader(doc), 4096)
	var out []kube.Doc
	for {
		var raw map[string]any
		err := dec.Decode(&raw)
		if errors.Is(err, io.EOF) {
			break
		}
		if err != nil {
			return nil, fmt.Errorf("clusterprovider: the grant document: %w", err)
		}
		if len(raw) == 0 {
			continue
		}
		gvk := kindOf(raw)
		gvr, ok := grantResources[gvk.Kind]
		if !ok {
			return nil, fmt.Errorf(
				"clusterprovider: the grant document holds a %q, which is not one of the %d kinds "+
					"this bootstrap applies; a new kind is a decision (see grantResources), not an apply",
				gvk.Kind, len(grantResources))
		}
		if want := gvr.GroupVersion().String(); gvk.GroupVersion().String() != want {
			return nil, fmt.Errorf(
				"clusterprovider: the grant's %s is %s and this bootstrap applies it as %s",
				gvk.Kind, gvk.GroupVersion().String(), want)
		}
		if metadataString(raw, "name") == "" {
			return nil, fmt.Errorf("clusterprovider: the grant holds a %s with no name", gvk.Kind)
		}
		out = append(out, raw)
	}
	if len(out) == 0 {
		return nil, errors.New("clusterprovider: the grant document holds no objects")
	}
	return out, nil
}

func kindOf(d kube.Doc) schema.GroupVersionKind {
	av, _ := d["apiVersion"].(string)
	gv, err := schema.ParseGroupVersion(av)
	if err != nil {
		return schema.GroupVersionKind{}
	}
	kind, _ := d["kind"].(string)
	return gv.WithKind(kind)
}

func metadataString(d kube.Doc, field string) string {
	meta, _ := d["metadata"].(map[string]any)
	v, _ := meta[field].(string)
	return v
}

func applyGrant(ctx context.Context, kc *kube.Client, objs []kube.Doc) error {
	for _, d := range objs {
		kind, _ := d["kind"].(string)
		name := metadataString(d, "name")
		r := kc.Resource(grantResources[kind]).In(metadataString(d, "namespace"))
		if _, err := kube.Apply[kube.Doc](ctx, r, d, kube.ApplyOptions{FieldManager: fieldManager, Force: true}); err != nil {
			return fmt.Errorf("clusterprovider: applying the %s %s: %w", kind, name, err)
		}
	}
	return nil
}