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

import (
	"context"
	"errors"
	"fmt"

	"github.com/johnandersen777/socialweb-computer-kcp/internal/clusterstack"
	"github.com/johnandersen777/socialweb-computer-kcp/internal/procgroup"
)

const CloudControllerManagerName = "system:socialweb:cloud-controller-manager"

const CloudControllerManagerGroup = "system:socialweb:cloud-controller-manager"

const CloudControllerManagerClusterRole = "socialweb-cloud-controller-manager"

const CloudControllerManagerClusterRoleBinding = "socialweb-cloud-controller-manager"

const cloudControllerManagerKubeconfigContext = "socialweb-cloud-controller-manager"

var ErrNoCloudControllerManagerSeam = errors.New(
	"clusterprovider: this Stack puts no k3s step in the group, so the cloud controller manager's " +
		"minted credential has no moment after the cluster's CAs exist to be written in, and the " +
		"controller would start against a kubeconfig that does not exist -- which the process would " +
		"report as a missing file, not as a missing seam")

var ErrNoCloudControllerManagerCredential = errors.New(
	"clusterprovider: the cloud controller manager needs a credential minted for it and no admin " +
		"client was supplied to grant that identity its RBAC. Invariant 20 forbids the controller " +
		"the k3s admin kubeconfig, and a scoped identity with no binding is authenticated and denied, " +
		"so the controller would come up and never sync its informer")

var ErrCloudControllerManagerKubeconfigUnwritable = errors.New(
	"clusterprovider: the cloud controller manager's minted tenant kubeconfig could not be put on " +
		"disk before its step ran. The controller takes that file as its only credential and refuses " +
		"to start without it, so a write that failed quietly would leave the component failing at " +
		"start for a reason that is not in the controller's own log")

func prepareCloudControllerManager(steps []procgroup.Step, cfg StackConfig, cluster, apiserver string) ([]procgroup.Step, error) {
	idx := -1
	for i, s := range steps {
		if s.Name == procgroup.ComponentK3s {
			idx = i
			break
		}
	}
	if idx < 0 {
		return nil, ErrNoCloudControllerManagerSeam
	}
	if cfg.Admin == nil {
		return nil, ErrNoCloudControllerManagerCredential
	}
	dataDir, err := ClusterDataDir(cfg.StateDir, cluster)
	if err != nil {
		return nil, err
	}
	kubeconfig := clusterstack.CloudControllerManagerKubeconfig(dataDir)
	inner := steps[idx].Ready
	steps[idx].Ready = func(ctx context.Context, p procgroup.Member) error {
		if inner != nil {
			if err := inner(ctx, p); err != nil {
				return err
			}
		}
		minted, err := MintTenantCredentialForClusterAs(ctx, cfg.StateDir, cluster, apiserver,
			CloudControllerManagerName, []string{CloudControllerManagerGroup}, DefaultCredentialValidity)
		if err != nil {
			return fmt.Errorf("clusterprovider: minting the scoped credential the cloud controller "+
				"manager of %s authenticates with: %w", cluster, err)
		}
		if err := cfg.writeKubeconfig(kubeconfig, minted, cloudControllerManagerKubeconfigContext,
			ErrCloudControllerManagerKubeconfigUnwritable); err != nil {
			return err
		}
		core, err := cfg.Admin(ctx, cluster, apiserver)
		if err != nil {
			return err
		}
		if core == nil {
			return fmt.Errorf("%w (cluster %s)", ErrNoCloudControllerManagerCredential, cluster)
		}
		return core.GrantCloudControllerManager(ctx, cluster)
	}
	return steps, nil
}

func (cfg StackConfig) writeKubeconfig(path string, cred *Credential, contextName string, unwritable error) error {
	if cfg.WriteKubeconfig == nil {
		return fmt.Errorf("%w: no kubeconfig writer was supplied for %s", unwritable, path)
	}
	return cfg.WriteKubeconfig(path, cred, contextName, unwritable)
}