1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
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)
}