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

import (
	"errors"
	"fmt"
	"io/fs"
	"os"
	"path/filepath"
	"strconv"
	"strings"

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

const (
	envTenantKubeconfig = "SOCIALWEB_CLOUDCONTROLLERMANAGER_TENANT_KUBECONFIG"

	envCluster = "SOCIALWEB_CLOUDCONTROLLERMANAGER_CLUSTER"

	envInstancesFile = "SOCIALWEB_CLOUDCONTROLLERMANAGER_INSTANCES_FILE"

	envMaxNodes = "SOCIALWEB_CLOUDCONTROLLERMANAGER_MAX_NODES"

	envPodsPerNode = "SOCIALWEB_CLOUDCONTROLLERMANAGER_PODS_PER_NODE"
)

const (
	DefaultMaxNodes = 4

	DefaultPodsPerNode = 8
)

type Config struct {
	TenantKubeconfig string

	Cluster string

	InstancesFile string

	MaxNodes int

	PodsPerNode int
}

func load(get func(string) string) (Config, error) {
	cfg := Config{
		TenantKubeconfig: strings.TrimSpace(get(envTenantKubeconfig)),
		Cluster:          strings.TrimSpace(get(envCluster)),
		InstancesFile:    strings.TrimSpace(get(envInstancesFile)),
		MaxNodes:         DefaultMaxNodes,
		PodsPerNode:      DefaultPodsPerNode,
	}

	for _, req := range []struct{ name, value string }{
		{envTenantKubeconfig, cfg.TenantKubeconfig},
		{envCluster, cfg.Cluster},
		{envInstancesFile, cfg.InstancesFile},
	} {
		if req.value == "" {
			return Config{}, fmt.Errorf(
				"%s is not set. The cloud controller manager is a peer of this cluster's control "+
					"plane: it holds a tenant credential for it, it owns a cluster name, and it "+
					"reads the instances that back the Nodes in its own state directory", req.name)
		}
	}

	if err := checkTenantKubeconfig(cfg.TenantKubeconfig); err != nil {
		return Config{}, err
	}

	if raw := strings.TrimSpace(get(envMaxNodes)); raw != "" {
		n, err := strconv.Atoi(raw)
		if err != nil {
			return Config{}, fmt.Errorf("%s is %q, which is not a whole number: %w", envMaxNodes, raw, err)
		}
		if n < 1 {
			return Config{}, fmt.Errorf(
				"%s is %q. A ceiling below one node cannot hold any work, so a cluster with any "+
					"intent could never be satisfied", envMaxNodes, raw)
		}
		cfg.MaxNodes = n
	}

	if raw := strings.TrimSpace(get(envPodsPerNode)); raw != "" {
		n, err := strconv.Atoi(raw)
		if err != nil {
			return Config{}, fmt.Errorf("%s is %q, which is not a whole number: %w", envPodsPerNode, raw, err)
		}
		if n < 1 {
			return Config{}, fmt.Errorf(
				"%s is %q. The autoscaling signal divides work by this number, so a value below "+
					"one is not a smaller node, it is no ratio at all", envPodsPerNode, raw)
		}
		cfg.PodsPerNode = n
	}

	return cfg, nil
}

func checkTenantKubeconfig(path string) error {
	if filepath.Base(path) == clusterstack.AdminKubeconfigName {
		return fmt.Errorf(
			"%s is %q, which is k3s's OWN admin kubeconfig, and it is refused. Invariant 20: the "+
				"provider's tenant credential is MINTED, never copied -- a client certificate "+
				"signed by this cluster's own ClientCA and valid only against this cluster. The "+
				"k3s-written admin kubeconfig is shared state on a multi-cluster host and its "+
				"server name is unverified against the address a node dials, so the cloud "+
				"controller manager must not be given it. Point %s at the minted tenant kubeconfig "+
				"produced by internal/clusterprovider/tenantcredential.go",
			envTenantKubeconfig, path, envTenantKubeconfig)
	}

	raw, err := os.ReadFile(path)
	if err != nil {
		if errors.Is(err, fs.ErrNotExist) {
			return fmt.Errorf(
				"%s is %q, and no such file exists. A controller with no tenant credential cannot "+
					"read the Node objects it reconciles", envTenantKubeconfig, path)
		}
		return fmt.Errorf("%s is %q, which cannot be read: %w", envTenantKubeconfig, path, err)
	}

	lower := strings.ToLower(string(raw))
	for _, forbidden := range forbiddenInTenantKubeconfig {
		if strings.Contains(lower, strings.ToLower(forbidden)) {
			return fmt.Errorf(
				"%s is %q and its document contains %q. The tenant credential is a minted client "+
					"certificate, never a static bearer token and never k3s's admin kubeconfig; a "+
					"document carrying one of those is refused rather than used",
				envTenantKubeconfig, path, forbidden)
		}
	}
	return nil
}

var forbiddenInTenantKubeconfig = []string{"bearer_token", "token:", "admin.kubeconfig"}