Create kubernetes join token on demand

Signed-off-by: Malte Poll <mp@edgeless.systems>
This commit is contained in:
Malte Poll 2022-05-04 14:32:34 +02:00 committed by Malte Poll
parent ddcb4dc95f
commit c9226de9ab
7 changed files with 85 additions and 114 deletions

View File

@ -3,17 +3,19 @@ package core
import ( import (
"context" "context"
"strings" "strings"
"time"
"github.com/edgelesssys/constellation/coordinator/kubernetes" "github.com/edgelesssys/constellation/coordinator/kubernetes"
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources" "github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
"github.com/edgelesssys/constellation/coordinator/role" "github.com/edgelesssys/constellation/coordinator/role"
"github.com/edgelesssys/constellation/internal/constants"
"go.uber.org/zap" "go.uber.org/zap"
kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3" kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
) )
// GetK8sJoinArgs returns the args needed by a Node to join the cluster. // GetK8sJoinArgs returns the args needed by a Node to join the cluster.
func (c *Core) GetK8sJoinArgs() (*kubeadm.BootstrapTokenDiscovery, error) { func (c *Core) GetK8sJoinArgs() (*kubeadm.BootstrapTokenDiscovery, error) {
return c.data().GetKubernetesJoinArgs() return c.kube.GetJoinToken(constants.KubernetesJoinTokenTTL)
} }
// GetK8SCertificateKey returns the key needed by a Coordinator to join the cluster. // GetK8SCertificateKey returns the key needed by a Coordinator to join the cluster.
@ -71,7 +73,7 @@ func (c *Core) InitCluster(autoscalingNodeGroups []string, cloudServiceAccountUR
} }
c.zaplogger.Info("Initializing cluster") c.zaplogger.Info("Initializing cluster")
joinCommand, err := c.kube.InitCluster(kubernetes.InitClusterInput{ if err := c.kube.InitCluster(kubernetes.InitClusterInput{
APIServerAdvertiseIP: coordinatorVPNIP.String(), APIServerAdvertiseIP: coordinatorVPNIP.String(),
NodeIP: nodeIP, NodeIP: nodeIP,
NodeName: k8sCompliantHostname(nodeName), NodeName: k8sCompliantHostname(nodeName),
@ -97,17 +99,11 @@ func (c *Core) InitCluster(autoscalingNodeGroups []string, cloudServiceAccountUR
CloudNodeManagerImage: c.cloudNodeManager.Image(), CloudNodeManagerImage: c.cloudNodeManager.Image(),
CloudNodeManagerPath: c.cloudNodeManager.Path(), CloudNodeManagerPath: c.cloudNodeManager.Path(),
CloudNodeManagerExtraArgs: c.cloudNodeManager.ExtraArgs(), CloudNodeManagerExtraArgs: c.cloudNodeManager.ExtraArgs(),
}) }); err != nil {
if err != nil {
c.zaplogger.Error("Initializing cluster failed", zap.Error(err)) c.zaplogger.Error("Initializing cluster failed", zap.Error(err))
return nil, err return nil, err
} }
if err := c.data().PutKubernetesJoinArgs(joinCommand); err != nil {
c.zaplogger.Error("Storing Kubernetes join command failed", zap.Error(err))
return nil, err
}
kubeconfig, err := c.kube.GetKubeconfig() kubeconfig, err := c.kube.GetKubeconfig()
if err != nil { if err != nil {
return nil, err return nil, err
@ -180,25 +176,23 @@ func (c *Core) JoinCluster(args *kubeadm.BootstrapTokenDiscovery, certKey string
// Cluster manages the overall cluster lifecycle (init, join). // Cluster manages the overall cluster lifecycle (init, join).
type Cluster interface { type Cluster interface {
// InitCluster bootstraps a new cluster with the current node being the master, returning the arguments required to join the cluster. // InitCluster bootstraps a new cluster with the current node being the master, returning the arguments required to join the cluster.
InitCluster(kubernetes.InitClusterInput) (*kubeadm.BootstrapTokenDiscovery, error) InitCluster(kubernetes.InitClusterInput) error
// JoinCluster will join the current node to an existing cluster. // JoinCluster will join the current node to an existing cluster.
JoinCluster(args *kubeadm.BootstrapTokenDiscovery, nodeName, nodeIP, nodeVPNIP, providerID, certKey string, ccmSupported bool, peerRole role.Role) error JoinCluster(args *kubeadm.BootstrapTokenDiscovery, nodeName, nodeIP, nodeVPNIP, providerID, certKey string, ccmSupported bool, peerRole role.Role) error
// GetKubeconfig reads the kubeconfig from the filesystem. Only succeeds after cluster is initialized. // GetKubeconfig reads the kubeconfig from the filesystem. Only succeeds after cluster is initialized.
GetKubeconfig() ([]byte, error) GetKubeconfig() ([]byte, error)
// GetKubeadmCertificateKey returns the 64-byte hex string key needed to join the cluster as control-plane. This function must be executed on a control-plane. // GetKubeadmCertificateKey returns the 64-byte hex string key needed to join the cluster as control-plane. This function must be executed on a control-plane.
GetKubeadmCertificateKey() (string, error) GetKubeadmCertificateKey() (string, error)
// GetJoinToken returns a bootstrap (join) token.
GetJoinToken(ttl time.Duration) (*kubeadm.BootstrapTokenDiscovery, error)
} }
// ClusterFake behaves like a real cluster, but does not actually initialize or join Kubernetes. // ClusterFake behaves like a real cluster, but does not actually initialize or join Kubernetes.
type ClusterFake struct{} type ClusterFake struct{}
// InitCluster fakes bootstrapping a new cluster with the current node being the master, returning the arguments required to join the cluster. // InitCluster fakes bootstrapping a new cluster with the current node being the master, returning the arguments required to join the cluster.
func (c *ClusterFake) InitCluster(kubernetes.InitClusterInput) (*kubeadm.BootstrapTokenDiscovery, error) { func (c *ClusterFake) InitCluster(kubernetes.InitClusterInput) error {
return &kubeadm.BootstrapTokenDiscovery{ return nil
APIServerEndpoint: "0.0.0.0",
Token: "kube-fake-token",
CACertHashes: []string{"sha256:a60ebe9b0879090edd83b40a4df4bebb20506bac1e51d518ff8f4505a721930f"},
}, nil
} }
// JoinCluster will fake joining the current node to an existing cluster. // JoinCluster will fake joining the current node to an existing cluster.
@ -216,6 +210,15 @@ func (c *ClusterFake) GetKubeadmCertificateKey() (string, error) {
return "controlPlaneCertficateKey", nil return "controlPlaneCertficateKey", nil
} }
// GetJoinToken returns a bootstrap (join) token.
func (c *ClusterFake) GetJoinToken(_ time.Duration) (*kubeadm.BootstrapTokenDiscovery, error) {
return &kubeadm.BootstrapTokenDiscovery{
APIServerEndpoint: "0.0.0.0",
Token: "kube-fake-token",
CACertHashes: []string{"sha256:a60ebe9b0879090edd83b40a4df4bebb20506bac1e51d518ff8f4505a721930f"},
}, nil
}
// k8sCompliantHostname transforms a hostname to an RFC 1123 compliant, lowercase subdomain as required by Kubernetes node names. // k8sCompliantHostname transforms a hostname to an RFC 1123 compliant, lowercase subdomain as required by Kubernetes node names.
// The following regex is used by k8s for validation: /^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$/ . // The following regex is used by k8s for validation: /^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$/ .
// Only a simple heuristic is used for now (to lowercase, replace underscores). // Only a simple heuristic is used for now (to lowercase, replace underscores).

View File

@ -4,6 +4,7 @@ import (
"errors" "errors"
"regexp" "regexp"
"testing" "testing"
"time"
"github.com/edgelesssys/constellation/cli/file" "github.com/edgelesssys/constellation/cli/file"
"github.com/edgelesssys/constellation/coordinator/attestation/simulator" "github.com/edgelesssys/constellation/coordinator/attestation/simulator"
@ -339,20 +340,21 @@ func TestK8sCompliantHostname(t *testing.T) {
} }
type clusterStub struct { type clusterStub struct {
initJoinArgs kubeadm.BootstrapTokenDiscovery
initErr error initErr error
joinErr error joinErr error
kubeconfig []byte kubeconfig []byte
getKubeconfigErr error getKubeconfigErr error
getJoinTokenResponse *kubeadm.BootstrapTokenDiscovery
getJoinTokenErr error
initInputs []kubernetes.InitClusterInput initInputs []kubernetes.InitClusterInput
joinClusterArgs []joinClusterArgs joinClusterArgs []joinClusterArgs
} }
func (c *clusterStub) InitCluster(in kubernetes.InitClusterInput) (*kubeadm.BootstrapTokenDiscovery, error) { func (c *clusterStub) InitCluster(in kubernetes.InitClusterInput) error {
c.initInputs = append(c.initInputs, in) c.initInputs = append(c.initInputs, in)
return &c.initJoinArgs, c.initErr return c.initErr
} }
func (c *clusterStub) JoinCluster(args *kubeadm.BootstrapTokenDiscovery, nodeName, nodeIP, nodeVPNIP, providerID, certKey string, _ bool, _ role.Role) error { func (c *clusterStub) JoinCluster(args *kubeadm.BootstrapTokenDiscovery, nodeName, nodeIP, nodeVPNIP, providerID, certKey string, _ bool, _ role.Role) error {
@ -374,6 +376,10 @@ func (c *clusterStub) GetKubeadmCertificateKey() (string, error) {
return "dummy", nil return "dummy", nil
} }
func (c *clusterStub) GetJoinToken(ttl time.Duration) (*kubeadm.BootstrapTokenDiscovery, error) {
return c.getJoinTokenResponse, c.getJoinTokenErr
}
type prepareInstanceRequest struct { type prepareInstanceRequest struct {
instance Instance instance Instance
vpnIP string vpnIP string

View File

@ -6,7 +6,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"regexp" "regexp"
"strings" "time"
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources" "github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3" kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
@ -23,7 +23,7 @@ type Client interface {
} }
type ClusterUtil interface { type ClusterUtil interface {
InitCluster(initConfig []byte) (*kubeadm.BootstrapTokenDiscovery, error) InitCluster(initConfig []byte) error
JoinCluster(joinConfig []byte) error JoinCluster(joinConfig []byte) error
SetupPodNetwork(kubectl Client, podNetworkConfiguration resources.Marshaler) error SetupPodNetwork(kubectl Client, podNetworkConfiguration resources.Marshaler) error
SetupAutoscaling(kubectl Client, clusterAutoscalerConfiguration resources.Marshaler, secrets resources.Marshaler) error SetupAutoscaling(kubectl Client, clusterAutoscalerConfiguration resources.Marshaler, secrets resources.Marshaler) error
@ -31,73 +31,32 @@ type ClusterUtil interface {
SetupCloudNodeManager(kubectl Client, cloudNodeManagerConfiguration resources.Marshaler) error SetupCloudNodeManager(kubectl Client, cloudNodeManagerConfiguration resources.Marshaler) error
RestartKubelet() error RestartKubelet() error
GetControlPlaneJoinCertificateKey() (string, error) GetControlPlaneJoinCertificateKey() (string, error)
CreateJoinToken(ttl time.Duration) (*kubeadm.BootstrapTokenDiscovery, error)
} }
type KubernetesUtil struct{} type KubernetesUtil struct{}
func (k *KubernetesUtil) InitCluster(initConfig []byte) (*kubeadm.BootstrapTokenDiscovery, error) { func (k *KubernetesUtil) InitCluster(initConfig []byte) error {
initConfigFile, err := os.CreateTemp("", "kubeadm-init.*.yaml") initConfigFile, err := os.CreateTemp("", "kubeadm-init.*.yaml")
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create init config file %v: %w", initConfigFile.Name(), err) return fmt.Errorf("failed to create init config file %v: %w", initConfigFile.Name(), err)
} }
defer os.Remove(initConfigFile.Name()) defer os.Remove(initConfigFile.Name())
if _, err := initConfigFile.Write(initConfig); err != nil { if _, err := initConfigFile.Write(initConfig); err != nil {
return nil, fmt.Errorf("writing kubeadm init yaml config %v failed: %w", initConfigFile.Name(), err) return fmt.Errorf("writing kubeadm init yaml config %v failed: %w", initConfigFile.Name(), err)
} }
cmd := exec.Command("kubeadm", "init", "--config", initConfigFile.Name()) cmd := exec.Command("kubeadm", "init", "--config", initConfigFile.Name())
stdout, err := cmd.Output() _, err = cmd.Output()
if err != nil { if err != nil {
var exitErr *exec.ExitError var exitErr *exec.ExitError
if errors.As(err, &exitErr) { if errors.As(err, &exitErr) {
return nil, fmt.Errorf("kubeadm init failed (code %v) with: %s", exitErr.ExitCode(), exitErr.Stderr) return fmt.Errorf("kubeadm init failed (code %v) with: %s", exitErr.ExitCode(), exitErr.Stderr)
} }
return nil, fmt.Errorf("kubeadm init failed: %w", err) return fmt.Errorf("kubeadm init failed: %w", err)
} }
return nil
stdoutStr := string(stdout)
indexKubeadmJoin := strings.Index(stdoutStr, "kubeadm join")
if indexKubeadmJoin < 0 {
return nil, errors.New("kubeadm init did not return join command")
}
joinCommand := strings.ReplaceAll(stdoutStr[indexKubeadmJoin:], "\\\n", " ")
// `kubeadm init` returns the two join commands, each broken up into two lines with backslash + newline in between.
// The following functions assume that stdoutStr[indexKubeadmJoin:] look like the following string.
// -----------------------------------------------------------------------------------------------
// --- When modifying the kubeadm.InitConfiguration make sure that this assumption still holds ---
// -----------------------------------------------------------------------------------------------
// "kubeadm join 127.0.0.1:16443 --token vlhjr4.9l6lhek0b9v65m67 \
// --discovery-token-ca-cert-hash sha256:2b5343a162e31b70602e3cab3d87189dc10431e869633c4db63c3bfcd038dee6 \
// --control-plane
//
// Then you can join any number of worker nodes by running the following on each as root:
//
// kubeadm join 127.0.0.1:16443 --token vlhjr4.9l6lhek0b9v65m67 \
// --discovery-token-ca-cert-hash sha256:2b5343a162e31b70602e3cab3d87189dc10431e869633c4db63c3bfcd038dee6"
// Splits the string into a slice, where earch slice-element contains one line from the previous string
splittedJoinCommand := strings.SplitN(joinCommand, "\n", 2)
joinConfig, err := ParseJoinCommand(splittedJoinCommand[0])
if err != nil {
return nil, err
}
// create extra join token without expiration
cmd = exec.Command("kubeadm", "token", "create", "--ttl", "0")
joinToken, err := cmd.Output()
if err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
return nil, fmt.Errorf("kubeadm token create failed (code %v) with: %s", exitErr.ExitCode(), exitErr.Stderr)
}
return nil, fmt.Errorf("kubeadm token create failed: %w", err)
}
joinConfig.Token = strings.TrimSpace(string(joinToken))
return joinConfig, nil
} }
// SetupPodNetwork sets up the flannel pod network. // SetupPodNetwork sets up the flannel pod network.
@ -195,3 +154,14 @@ func (k *KubernetesUtil) GetControlPlaneJoinCertificateKey() (string, error) {
} }
return key, nil return key, nil
} }
// CreateJoinToken creates a new bootstrap (join) token.
func (k *KubernetesUtil) CreateJoinToken(ttl time.Duration) (*kubeadm.BootstrapTokenDiscovery, error) {
output, err := exec.Command("kubeadm", "token", "create", "--ttl", ttl.String(), "--print-join-command").Output()
if err != nil {
return nil, fmt.Errorf("kubeadm token create failed: %w", err)
}
// `kubeadm token create [...] --print-join-command` outputs the following format:
// kubeadm join [API_SERVER_ENDPOINT] --token [TOKEN] --discovery-token-ca-cert-hash [DISCOVERY_TOKEN_CA_CERT_HASH]
return ParseJoinCommand(string(output))
}

View File

@ -3,6 +3,7 @@ package kubernetes
import ( import (
"fmt" "fmt"
"strings" "strings"
"time"
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi" "github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi"
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources" "github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
@ -47,7 +48,7 @@ func New(clusterUtil k8sapi.ClusterUtil, configProvider configurationProvider, c
} }
// InitCluster initializes a new Kubernetes cluster and applies pod network provider. // InitCluster initializes a new Kubernetes cluster and applies pod network provider.
func (k *KubeWrapper) InitCluster(in InitClusterInput) (*kubeadm.BootstrapTokenDiscovery, error) { func (k *KubeWrapper) InitCluster(in InitClusterInput) error {
initConfig := k.configProvider.InitConfiguration(in.SupportsCloudControllerManager) initConfig := k.configProvider.InitConfiguration(in.SupportsCloudControllerManager)
initConfig.SetApiServerAdvertiseAddress(in.APIServerAdvertiseIP) initConfig.SetApiServerAdvertiseAddress(in.APIServerAdvertiseIP)
initConfig.SetNodeIP(in.NodeIP) initConfig.SetNodeIP(in.NodeIP)
@ -57,20 +58,19 @@ func (k *KubeWrapper) InitCluster(in InitClusterInput) (*kubeadm.BootstrapTokenD
initConfig.SetProviderID(in.ProviderID) initConfig.SetProviderID(in.ProviderID)
initConfigYAML, err := initConfig.Marshal() initConfigYAML, err := initConfig.Marshal()
if err != nil { if err != nil {
return nil, fmt.Errorf("encoding kubeadm init configuration as YAML failed: %w", err) return fmt.Errorf("encoding kubeadm init configuration as YAML failed: %w", err)
} }
joinK8SClusterRequest, err := k.clusterUtil.InitCluster(initConfigYAML) if err := k.clusterUtil.InitCluster(initConfigYAML); err != nil {
if err != nil { return fmt.Errorf("kubeadm init failed: %w", err)
return nil, fmt.Errorf("kubeadm init failed: %w", err)
} }
kubeConfig, err := k.GetKubeconfig() kubeConfig, err := k.GetKubeconfig()
if err != nil { if err != nil {
return nil, fmt.Errorf("reading kubeconfig after cluster initialization failed: %w", err) return fmt.Errorf("reading kubeconfig after cluster initialization failed: %w", err)
} }
k.client.SetKubeconfig(kubeConfig) k.client.SetKubeconfig(kubeConfig)
flannel := resources.NewDefaultFlannelDeployment() flannel := resources.NewDefaultFlannelDeployment()
if err = k.clusterUtil.SetupPodNetwork(k.client, flannel); err != nil { if err = k.clusterUtil.SetupPodNetwork(k.client, flannel); err != nil {
return nil, fmt.Errorf("setup of pod network failed: %w", err) return fmt.Errorf("setup of pod network failed: %w", err)
} }
if in.SupportsCloudControllerManager { if in.SupportsCloudControllerManager {
@ -79,7 +79,7 @@ func (k *KubeWrapper) InitCluster(in InitClusterInput) (*kubeadm.BootstrapTokenD
in.CloudControllerManagerVolumes, in.CloudControllerManagerVolumeMounts, in.CloudControllerManagerEnv, in.CloudControllerManagerVolumes, in.CloudControllerManagerVolumeMounts, in.CloudControllerManagerEnv,
) )
if err := k.clusterUtil.SetupCloudControllerManager(k.client, cloudControllerManagerConfiguration, in.CloudControllerManagerConfigMaps, in.CloudControllerManagerSecrets); err != nil { if err := k.clusterUtil.SetupCloudControllerManager(k.client, cloudControllerManagerConfiguration, in.CloudControllerManagerConfigMaps, in.CloudControllerManagerSecrets); err != nil {
return nil, fmt.Errorf("failed to setup cloud-controller-manager: %w", err) return fmt.Errorf("failed to setup cloud-controller-manager: %w", err)
} }
} }
@ -88,7 +88,7 @@ func (k *KubeWrapper) InitCluster(in InitClusterInput) (*kubeadm.BootstrapTokenD
in.CloudNodeManagerImage, in.CloudNodeManagerPath, in.CloudNodeManagerExtraArgs, in.CloudNodeManagerImage, in.CloudNodeManagerPath, in.CloudNodeManagerExtraArgs,
) )
if err := k.clusterUtil.SetupCloudNodeManager(k.client, cloudNodeManagerConfiguration); err != nil { if err := k.clusterUtil.SetupCloudNodeManager(k.client, cloudNodeManagerConfiguration); err != nil {
return nil, fmt.Errorf("failed to setup cloud-node-manager: %w", err) return fmt.Errorf("failed to setup cloud-node-manager: %w", err)
} }
} }
@ -96,11 +96,11 @@ func (k *KubeWrapper) InitCluster(in InitClusterInput) (*kubeadm.BootstrapTokenD
clusterAutoscalerConfiguration := resources.NewDefaultAutoscalerDeployment(in.AutoscalingVolumes, in.AutoscalingVolumeMounts, in.AutoscalingEnv) clusterAutoscalerConfiguration := resources.NewDefaultAutoscalerDeployment(in.AutoscalingVolumes, in.AutoscalingVolumeMounts, in.AutoscalingEnv)
clusterAutoscalerConfiguration.SetAutoscalerCommand(in.AutoscalingCloudprovider, in.AutoscalingNodeGroups) clusterAutoscalerConfiguration.SetAutoscalerCommand(in.AutoscalingCloudprovider, in.AutoscalingNodeGroups)
if err := k.clusterUtil.SetupAutoscaling(k.client, clusterAutoscalerConfiguration, in.AutoscalingSecrets); err != nil { if err := k.clusterUtil.SetupAutoscaling(k.client, clusterAutoscalerConfiguration, in.AutoscalingSecrets); err != nil {
return nil, fmt.Errorf("failed to setup cluster-autoscaler: %w", err) return fmt.Errorf("failed to setup cluster-autoscaler: %w", err)
} }
} }
return joinK8SClusterRequest, nil return nil
} }
// JoinCluster joins existing Kubernetes cluster. // JoinCluster joins existing Kubernetes cluster.
@ -144,6 +144,11 @@ func (k *KubeWrapper) GetKubeadmCertificateKey() (string, error) {
return k.clusterUtil.GetControlPlaneJoinCertificateKey() return k.clusterUtil.GetControlPlaneJoinCertificateKey()
} }
// GetJoinToken returns a bootstrap (join) token.
func (k *KubeWrapper) GetJoinToken(ttl time.Duration) (*kubeadm.BootstrapTokenDiscovery, error) {
return k.clusterUtil.CreateJoinToken(ttl)
}
type fakeK8SClient struct { type fakeK8SClient struct {
kubeconfig []byte kubeconfig []byte
} }

View File

@ -3,6 +3,7 @@ package kubernetes
import ( import (
"errors" "errors"
"testing" "testing"
"time"
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi" "github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi"
"github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources" "github.com/edgelesssys/constellation/coordinator/kubernetes/k8sapi/resources"
@ -19,7 +20,6 @@ func TestMain(m *testing.M) {
} }
type stubClusterUtil struct { type stubClusterUtil struct {
joinClusterRequest *kubeadm.BootstrapTokenDiscovery
initClusterErr error initClusterErr error
setupPodNetworkErr error setupPodNetworkErr error
setupAutoscalingError error setupAutoscalingError error
@ -27,14 +27,16 @@ type stubClusterUtil struct {
setupCloudNodeManagerError error setupCloudNodeManagerError error
joinClusterErr error joinClusterErr error
restartKubeletErr error restartKubeletErr error
createJoinTokenResponse *kubeadm.BootstrapTokenDiscovery
createJoinTokenErr error
initConfigs [][]byte initConfigs [][]byte
joinConfigs [][]byte joinConfigs [][]byte
} }
func (s *stubClusterUtil) InitCluster(initConfig []byte) (*kubeadm.BootstrapTokenDiscovery, error) { func (s *stubClusterUtil) InitCluster(initConfig []byte) error {
s.initConfigs = append(s.initConfigs, initConfig) s.initConfigs = append(s.initConfigs, initConfig)
return s.joinClusterRequest, s.initClusterErr return s.initClusterErr
} }
func (s *stubClusterUtil) SetupPodNetwork(kubectl k8sapi.Client, podNetworkConfiguration resources.Marshaler) error { func (s *stubClusterUtil) SetupPodNetwork(kubectl k8sapi.Client, podNetworkConfiguration resources.Marshaler) error {
@ -66,6 +68,10 @@ func (s *stubClusterUtil) GetControlPlaneJoinCertificateKey() (string, error) {
return "", nil return "", nil
} }
func (s *stubClusterUtil) CreateJoinToken(ttl time.Duration) (*kubeadm.BootstrapTokenDiscovery, error) {
return s.createJoinTokenResponse, s.createJoinTokenErr
}
type stubConfigProvider struct { type stubConfigProvider struct {
InitConfig k8sapi.KubeadmInitYAML InitConfig k8sapi.KubeadmInitYAML
JoinConfig k8sapi.KubeadmJoinYAML JoinConfig k8sapi.KubeadmJoinYAML
@ -131,33 +137,21 @@ func TestInitCluster(t *testing.T) {
wantErr bool wantErr bool
}{ }{
"kubeadm init works": { "kubeadm init works": {
clusterUtil: stubClusterUtil{ clusterUtil: stubClusterUtil{},
joinClusterRequest: &kubeadm.BootstrapTokenDiscovery{
APIServerEndpoint: "192.0.2.0",
Token: "kube-fake-token",
CACertHashes: []string{"sha256:a60ebe9b0879090edd83b40a4df4bebb20506bac1e51d518ff8f4505a721930f"},
},
},
kubeconfigReader: stubKubeconfigReader{ kubeconfigReader: stubKubeconfigReader{
Kubeconfig: []byte("someKubeconfig"), Kubeconfig: []byte("someKubeconfig"),
}, },
wantErr: false, wantErr: false,
}, },
"kubeadm init errors": { "kubeadm init errors": {
clusterUtil: stubClusterUtil{ clusterUtil: stubClusterUtil{initClusterErr: someErr},
joinClusterRequest: nil,
initClusterErr: someErr,
},
kubeconfigReader: stubKubeconfigReader{ kubeconfigReader: stubKubeconfigReader{
Kubeconfig: []byte("someKubeconfig"), Kubeconfig: []byte("someKubeconfig"),
}, },
wantErr: true, wantErr: true,
}, },
"pod network setup errors": { "pod network setup errors": {
clusterUtil: stubClusterUtil{ clusterUtil: stubClusterUtil{setupPodNetworkErr: someErr},
joinClusterRequest: nil,
setupPodNetworkErr: someErr,
},
kubeconfigReader: stubKubeconfigReader{ kubeconfigReader: stubKubeconfigReader{
Kubeconfig: []byte("someKubeconfig"), Kubeconfig: []byte("someKubeconfig"),
}, },
@ -176,7 +170,7 @@ func TestInitCluster(t *testing.T) {
client: &tc.kubeCTL, client: &tc.kubeCTL,
kubeconfigReader: &tc.kubeconfigReader, kubeconfigReader: &tc.kubeconfigReader,
} }
joinCommand, err := kube.InitCluster( err := kube.InitCluster(
InitClusterInput{ InitClusterInput{
APIServerAdvertiseIP: coordinatorVPNIP, APIServerAdvertiseIP: coordinatorVPNIP,
NodeName: instanceName, NodeName: instanceName,
@ -195,7 +189,6 @@ func TestInitCluster(t *testing.T) {
return return
} }
require.NoError(err) require.NoError(err)
assert.Equal(tc.clusterUtil.joinClusterRequest, joinCommand)
var kubeadmConfig k8sapi.KubeadmInitYAML var kubeadmConfig k8sapi.KubeadmInitYAML
require.NoError(resources.UnmarshalK8SResources(tc.clusterUtil.initConfigs[0], &kubeadmConfig)) require.NoError(resources.UnmarshalK8SResources(tc.clusterUtil.initConfigs[0], &kubeadmConfig))

View File

@ -156,15 +156,6 @@ func (s StoreWrapper) GetKubernetesJoinArgs() (*kubeadm.BootstrapTokenDiscovery,
return &joinCommand, nil return &joinCommand, nil
} }
// PutKubernetesJoinArgs saves the Kubernetes join command to store.
func (s StoreWrapper) PutKubernetesJoinArgs(args *kubeadm.BootstrapTokenDiscovery) error {
j, err := json.Marshal(args)
if err != nil {
return err
}
return s.Store.Put(keyKubernetesJoinCommand, j)
}
// GetKubernetesConfig returns the Kubernetes kubeconfig file to authenticate with the Kubernetes API. // GetKubernetesConfig returns the Kubernetes kubeconfig file to authenticate with the Kubernetes API.
func (s StoreWrapper) GetKubernetesConfig() ([]byte, error) { func (s StoreWrapper) GetKubernetesConfig() ([]byte, error) {
return s.Store.Get(keyKubeConfig) return s.Store.Get(keyKubeConfig)

View File

@ -4,6 +4,8 @@ Constants should never be overwritable by command line flags or configuration fi
*/ */
package constants package constants
import "time"
const ( const (
// //
// Ports. // Ports.
@ -46,6 +48,7 @@ const (
// KubernetesVersion installed by kubeadm. // KubernetesVersion installed by kubeadm.
KubernetesVersion = "stable-1.23" KubernetesVersion = "stable-1.23"
KubernetesJoinTokenTTL = 15 * time.Minute
) )
// CliVersion is the version of the CLI. Left as a separate variable to allow override during build. // CliVersion is the version of the CLI. Left as a separate variable to allow override during build.