init: create kubeconfig file with unique user/cluster name (#1133)

* Generate kubeconfig with unique name

* Move create name flag to config

* Add name validation to config

* Move name flag in e2e tests to config generation

* Remove name flag from create

* Update ascii cinema flow

---------

Signed-off-by: Daniel Weiße <dw@edgeless.systems>
This commit is contained in:
Daniel Weiße 2023-02-10 13:27:22 +01:00 committed by GitHub
parent fd860ddb91
commit c29107f5be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 359 additions and 436 deletions

View File

@ -73,6 +73,8 @@ runs:
run: |
constellation config generate ${{ inputs.cloudProvider }}
yq eval -i "(.name) = \"e2e-test\"" constellation-conf.yaml
yq eval -i \
"(.provider | select(. | has(\"azure\")).azure.subscription) = \"${{ inputs.azureSubscription }}\" |
(.provider | select(. | has(\"azure\")).azure.tenant) = \"${{ inputs.azureTenant }}\" |
@ -158,7 +160,7 @@ runs:
echo "Creating cluster using config:"
cat constellation-conf.yaml
sudo sh -c 'echo "127.0.0.1 license.confidential.cloud" >> /etc/hosts' || true
constellation create -c ${{ inputs.controlNodesCount }} -w ${{ inputs.workerNodesCount }} --name e2e-test -y --force
constellation create -c ${{ inputs.controlNodesCount }} -w ${{ inputs.workerNodesCount }} -y --force
- name: Cdbg deploy
if: inputs.isDebugImage == 'true'

View File

@ -21,7 +21,7 @@ type clusterFake struct{}
// 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(
context.Context, string, string, []byte, []uint32, bool, bool,
context.Context, string, string, string, []byte, []uint32, bool, bool,
[]byte, bool, components.Components, *logger.Logger,
) ([]byte, error) {
return []byte{}, nil

View File

@ -41,6 +41,7 @@ type InitRequest struct {
ConformanceMode bool `protobuf:"varint,14,opt,name=conformance_mode,json=conformanceMode,proto3" json:"conformance_mode,omitempty"`
KubernetesComponents []*KubernetesComponent `protobuf:"bytes,15,rep,name=kubernetes_components,json=kubernetesComponents,proto3" json:"kubernetes_components,omitempty"`
InitSecret []byte `protobuf:"bytes,16,opt,name=init_secret,json=initSecret,proto3" json:"init_secret,omitempty"`
ClusterName string `protobuf:"bytes,17,opt,name=cluster_name,json=clusterName,proto3" json:"cluster_name,omitempty"`
}
func (x *InitRequest) Reset() {
@ -173,6 +174,13 @@ func (x *InitRequest) GetInitSecret() []byte {
return nil
}
func (x *InitRequest) GetClusterName() string {
if x != nil {
return x.ClusterName
}
return ""
}
type InitResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@ -311,7 +319,7 @@ var File_init_proto protoreflect.FileDescriptor
var file_init_proto_rawDesc = []byte{
0x0a, 0x0a, 0x69, 0x6e, 0x69, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x69, 0x6e,
0x69, 0x74, 0x22, 0xe4, 0x04, 0x0a, 0x0b, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
0x69, 0x74, 0x22, 0x87, 0x05, 0x0a, 0x0b, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x65, 0x63,
0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6d, 0x61, 0x73, 0x74, 0x65,
0x72, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x6b, 0x6d, 0x73, 0x5f, 0x75,
@ -349,29 +357,31 @@ var file_init_proto_rawDesc = []byte{
0x6e, 0x74, 0x52, 0x14, 0x6b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65, 0x73, 0x43, 0x6f,
0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, 0x69, 0x74,
0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x69,
0x6e, 0x69, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x22, 0x68, 0x0a, 0x0c, 0x49, 0x6e, 0x69,
0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x6b, 0x75, 0x62,
0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x6b,
0x75, 0x62, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x77, 0x6e,
0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6f, 0x77, 0x6e,
0x65, 0x72, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x5f,
0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65,
0x72, 0x49, 0x64, 0x22, 0x78, 0x0a, 0x13, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x65,
0x73, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72,
0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04,
0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68,
0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68,
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x50,
0x61, 0x74, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x18, 0x04,
0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x32, 0x34, 0x0a,
0x03, 0x41, 0x50, 0x49, 0x12, 0x2d, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x11, 0x2e, 0x69,
0x6e, 0x69, 0x74, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x12, 0x2e, 0x69, 0x6e, 0x69, 0x74, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
0x6d, 0x2f, 0x65, 0x64, 0x67, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x73, 0x79, 0x73, 0x2f, 0x63, 0x6f,
0x6e, 0x73, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x32, 0x2f, 0x62,
0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x2f, 0x69, 0x6e, 0x69, 0x74,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x6e, 0x69, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6c, 0x75,
0x73, 0x74, 0x65, 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0b, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x68, 0x0a, 0x0c,
0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a,
0x6b, 0x75, 0x62, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
0x52, 0x0a, 0x6b, 0x75, 0x62, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x08,
0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07,
0x6f, 0x77, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, 0x75, 0x73, 0x74,
0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x63, 0x6c, 0x75,
0x73, 0x74, 0x65, 0x72, 0x49, 0x64, 0x22, 0x78, 0x0a, 0x13, 0x4b, 0x75, 0x62, 0x65, 0x72, 0x6e,
0x65, 0x74, 0x65, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a,
0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12,
0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68,
0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x5f, 0x70,
0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61,
0x6c, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63,
0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74,
0x32, 0x34, 0x0a, 0x03, 0x41, 0x50, 0x49, 0x12, 0x2d, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x12,
0x11, 0x2e, 0x69, 0x6e, 0x69, 0x74, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x12, 0x2e, 0x69, 0x6e, 0x69, 0x74, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x64, 0x67, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x73, 0x79, 0x73,
0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76,
0x32, 0x2f, 0x62, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x2f, 0x69,
0x6e, 0x69, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

View File

@ -25,6 +25,7 @@ message InitRequest {
bool conformance_mode = 14;
repeated KubernetesComponent kubernetes_components = 15;
bytes init_secret = 16;
string cluster_name = 17;
}
message InitResponse {

View File

@ -161,9 +161,15 @@ func (s *Server) Init(ctx context.Context, req *initproto.InitRequest) (*initpro
// Check if we are running on a CVM
_, isCVM := s.issuer.(*snp.Issuer)
clusterName := req.ClusterName
if clusterName == "" {
clusterName = "constellation"
}
kubeconfig, err := s.initializer.InitCluster(ctx,
req.CloudServiceAccountUri,
req.KubernetesVersion,
clusterName,
measurementSalt,
req.EnforcedPcrs,
req.EnforceIdkeydigest,
@ -237,6 +243,7 @@ type ClusterInitializer interface {
ctx context.Context,
cloudServiceAccountURI string,
k8sVersion string,
clusterName string,
measurementSalt []byte,
enforcedPcrs []uint32,
enforceIDKeyDigest bool,

View File

@ -314,7 +314,7 @@ type stubClusterInitializer struct {
}
func (i *stubClusterInitializer) InitCluster(
context.Context, string, string, []byte, []uint32, bool, bool,
context.Context, string, string, string, []byte, []uint32, bool, bool,
[]byte, bool, components.Components, *logger.Logger,
) ([]byte, error) {
return i.initClusterKubeconfig, i.initClusterErr

View File

@ -28,6 +28,7 @@ import (
"github.com/edgelesssys/constellation/v2/internal/role"
"github.com/edgelesssys/constellation/v2/internal/versions/components"
corev1 "k8s.io/api/core/v1"
"k8s.io/apiserver/pkg/authentication/user"
kubeconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"github.com/edgelesssys/constellation/v2/internal/crypto"
@ -87,25 +88,26 @@ func (k *KubernetesUtil) InstallComponents(ctx context.Context, kubernetesCompon
}
// InitCluster instruments kubeadm to initialize the K8s cluster.
// On success an admin kubeconfig file is returned.
func (k *KubernetesUtil) InitCluster(
ctx context.Context, initConfig []byte, nodeName string, ips []net.IP, controlPlaneEndpoint string, conformanceMode bool, log *logger.Logger,
) error {
ctx context.Context, initConfig []byte, nodeName, clusterName string, ips []net.IP, controlPlaneEndpoint string, conformanceMode bool, log *logger.Logger,
) ([]byte, error) {
// TODO: audit policy should be user input
auditPolicy, err := resources.NewDefaultAuditPolicy().Marshal()
if err != nil {
return fmt.Errorf("generating default audit policy: %w", err)
return nil, fmt.Errorf("generating default audit policy: %w", err)
}
if err := os.WriteFile(auditPolicyPath, auditPolicy, 0o644); err != nil {
return fmt.Errorf("writing default audit policy: %w", err)
return nil, fmt.Errorf("writing default audit policy: %w", err)
}
initConfigFile, err := os.CreateTemp("", "kubeadm-init.*.yaml")
if err != nil {
return fmt.Errorf("creating init config file %v: %w", initConfigFile.Name(), err)
return nil, fmt.Errorf("creating init config file %v: %w", initConfigFile.Name(), err)
}
if _, err := initConfigFile.Write(initConfig); err != nil {
return fmt.Errorf("writing kubeadm init yaml config %v: %w", initConfigFile.Name(), err)
return nil, fmt.Errorf("writing kubeadm init yaml config %v: %w", initConfigFile.Name(), err)
}
// preflight
@ -115,9 +117,9 @@ func (k *KubernetesUtil) InitCluster(
if err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
return fmt.Errorf("kubeadm init phase preflight failed (code %v) with: %s", exitErr.ExitCode(), out)
return nil, fmt.Errorf("kubeadm init phase preflight failed (code %v) with: %s", exitErr.ExitCode(), out)
}
return fmt.Errorf("kubeadm init: %w", err)
return nil, fmt.Errorf("kubeadm init: %w", err)
}
// create CA certs
@ -127,20 +129,20 @@ func (k *KubernetesUtil) InitCluster(
if err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
return fmt.Errorf("kubeadm init phase certs all failed (code %v) with: %s", exitErr.ExitCode(), out)
return nil, fmt.Errorf("kubeadm init phase certs all failed (code %v) with: %s", exitErr.ExitCode(), out)
}
return fmt.Errorf("kubeadm init: %w", err)
return nil, fmt.Errorf("kubeadm init: %w", err)
}
// create kubelet key and CA signed certificate for the node
log.Infof("Creating signed kubelet certificate")
if err := k.createSignedKubeletCert(nodeName, ips); err != nil {
return err
return nil, fmt.Errorf("creating signed kubelete certificate: %w", err)
}
log.Infof("Preparing node for Konnectivity")
if err := k.prepareControlPlaneForKonnectivity(ctx, controlPlaneEndpoint); err != nil {
return fmt.Errorf("setup konnectivity: %w", err)
return nil, fmt.Errorf("setup konnectivity: %w", err)
}
// initialize the cluster
@ -155,12 +157,29 @@ func (k *KubernetesUtil) InitCluster(
if err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
return fmt.Errorf("kubeadm init failed (code %v) with: %s", exitErr.ExitCode(), out)
return nil, fmt.Errorf("kubeadm init failed (code %v) with: %s", exitErr.ExitCode(), out)
}
return fmt.Errorf("kubeadm init: %w", err)
return nil, fmt.Errorf("kubeadm init: %w", err)
}
log.With(zap.String("output", string(out))).Infof("kubeadm init succeeded")
return nil
userName := clusterName + "-admin"
log.With(zap.String("userName", userName)).Infof("Creating admin kubeconfig file")
cmd = exec.CommandContext(
ctx, constants.KubeadmPath, "kubeconfig", "user",
"--client-name", userName, "--config", initConfigFile.Name(), "--org", user.SystemPrivilegedGroup,
)
out, err = cmd.Output()
if err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
return nil, fmt.Errorf("kubeadm kubeconfig user failed (code %v) with: %s", exitErr.ExitCode(), out)
}
return nil, fmt.Errorf("kubeadm kubeconfig user: %w", err)
}
log.Infof("kubeadm kubeconfig user succeeded")
return out, nil
}
func (k *KubernetesUtil) prepareControlPlaneForKonnectivity(ctx context.Context, loadBalancerEndpoint string) error {

View File

@ -271,6 +271,12 @@ func (k *KubeadmInitYAML) SetNodeName(nodeName string) {
k.InitConfiguration.NodeRegistration.Name = nodeName
}
// SetClusterName sets the name of the Kubernetes cluster.
// This name is reflected in the kubeconfig file and in the name of the default admin user.
func (k *KubeadmInitYAML) SetClusterName(clusterName string) {
k.ClusterConfiguration.ClusterName = clusterName
}
// SetCertSANs sets the SANs for the certificate.
func (k *KubeadmInitYAML) SetCertSANs(certSANs []string) {
for _, certSAN := range certSANs {

View File

@ -19,7 +19,7 @@ import (
type clusterUtil interface {
InstallComponents(ctx context.Context, kubernetesComponents components.Components) error
InitCluster(ctx context.Context, initConfig []byte, nodeName string, ips []net.IP, controlPlaneEndpoint string, conformanceMode bool, log *logger.Logger) error
InitCluster(ctx context.Context, initConfig []byte, nodeName, clusterName string, ips []net.IP, controlPlaneEndpoint string, conformanceMode bool, log *logger.Logger) ([]byte, error)
JoinCluster(ctx context.Context, joinConfig []byte, peerRole role.Role, controlPlaneEndpoint string, log *logger.Logger) error
FixCilium(log *logger.Logger)
StartKubelet() error

View File

@ -1,29 +0,0 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package kubernetes
import (
"fmt"
"github.com/spf13/afero"
)
const kubeconfigPath = "/etc/kubernetes/admin.conf"
// KubeconfigReader implements ConfigReader.
type KubeconfigReader struct {
fs afero.Afero
}
// ReadKubeconfig reads the Kubeconfig from disk.
func (r KubeconfigReader) ReadKubeconfig() ([]byte, error) {
kubeconfig, err := r.fs.ReadFile(kubeconfigPath)
if err != nil {
return nil, fmt.Errorf("reading kubernetes config: %w", err)
}
return kubeconfig, nil
}

View File

@ -1,40 +0,0 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package kubernetes
import (
"testing"
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestReadKubeconfig(t *testing.T) {
require := require.New(t)
assert := assert.New(t)
fs := afero.Afero{
Fs: afero.NewMemMapFs(),
}
require.NoError(fs.WriteFile(kubeconfigPath, []byte("someConfig"), 0o644))
reader := KubeconfigReader{fs}
config, err := reader.ReadKubeconfig()
require.NoError(err)
assert.Equal([]byte("someConfig"), config)
}
func TestReadKubeconfigFails(t *testing.T) {
assert := assert.New(t)
fs := afero.Afero{
Fs: afero.NewMemMapFs(),
}
reader := KubeconfigReader{fs}
_, err := reader.ReadKubeconfig()
assert.Error(err)
}

View File

@ -30,7 +30,6 @@ import (
"github.com/edgelesssys/constellation/v2/internal/logger"
"github.com/edgelesssys/constellation/v2/internal/role"
"github.com/edgelesssys/constellation/v2/internal/versions/components"
"github.com/spf13/afero"
"go.uber.org/zap"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -39,11 +38,6 @@ import (
var validHostnameRegex = regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`)
// configReader provides kubeconfig as []byte.
type configReader interface {
ReadKubeconfig() ([]byte, error)
}
// configurationProvider provides kubeadm init and join configuration.
type configurationProvider interface {
InitConfiguration(externalCloudProvider bool, k8sVersion string) k8sapi.KubeadmInitYAML
@ -62,7 +56,6 @@ type KubeWrapper struct {
kubeAPIWaiter kubeAPIWaiter
configProvider configurationProvider
client k8sapi.Client
kubeconfigReader configReader
providerMetadata ProviderMetadata
initialMeasurements measurements.M
getIPAddr func() (string, error)
@ -79,7 +72,6 @@ func New(cloudProvider string, clusterUtil clusterUtil, configProvider configura
kubeAPIWaiter: kubeAPIWaiter,
configProvider: configProvider,
client: client,
kubeconfigReader: &KubeconfigReader{fs: afero.Afero{Fs: afero.NewOsFs()}},
providerMetadata: providerMetadata,
initialMeasurements: measurements,
getIPAddr: getIPAddr,
@ -88,8 +80,8 @@ func New(cloudProvider string, clusterUtil clusterUtil, configProvider configura
// InitCluster initializes a new Kubernetes cluster and applies pod network provider.
func (k *KubeWrapper) InitCluster(
ctx context.Context, cloudServiceAccountURI, versionString string, measurementSalt []byte, enforcedPCRs []uint32,
enforceIDKeyDigest bool, azureCVM bool,
ctx context.Context, cloudServiceAccountURI, versionString, clusterName string,
measurementSalt []byte, enforcedPCRs []uint32, enforceIDKeyDigest bool, azureCVM bool,
helmReleasesRaw []byte, conformanceMode bool, kubernetesComponents components.Components, log *logger.Logger,
) ([]byte, error) {
log.With(zap.String("version", versionString)).Infof("Installing Kubernetes components")
@ -139,6 +131,7 @@ func (k *KubeWrapper) InitCluster(
cloudprovider.FromString(k.cloudProvider) == cloudprovider.GCP
initConfig := k.configProvider.InitConfiguration(ccmSupported, versionString)
initConfig.SetNodeIP(nodeIP)
initConfig.SetClusterName(clusterName)
initConfig.SetCertSANs([]string{nodeIP})
initConfig.SetNodeName(nodeName)
initConfig.SetProviderID(instance.ProviderID)
@ -148,13 +141,11 @@ func (k *KubeWrapper) InitCluster(
return nil, fmt.Errorf("encoding kubeadm init configuration as YAML: %w", err)
}
log.Infof("Initializing Kubernetes cluster")
if err := k.clusterUtil.InitCluster(ctx, initConfigYAML, nodeName, validIPs, controlPlaneEndpoint, conformanceMode, log); err != nil {
kubeConfig, err := k.clusterUtil.InitCluster(ctx, initConfigYAML, nodeName, clusterName, validIPs, controlPlaneEndpoint, conformanceMode, log)
if err != nil {
return nil, fmt.Errorf("kubeadm init: %w", err)
}
kubeConfig, err := k.GetKubeconfig()
if err != nil {
return nil, fmt.Errorf("reading kubeconfig after cluster initialization: %w", err)
}
err = k.client.Initialize(kubeConfig)
if err != nil {
return nil, fmt.Errorf("initializing kubectl client: %w", err)
@ -250,7 +241,7 @@ func (k *KubeWrapper) InitCluster(
k.clusterUtil.FixCilium(log)
return k.GetKubeconfig()
return kubeConfig, nil
}
// JoinCluster joins existing Kubernetes cluster.
@ -311,11 +302,6 @@ func (k *KubeWrapper) JoinCluster(ctx context.Context, args *kubeadm.BootstrapTo
return nil
}
// GetKubeconfig returns the current nodes kubeconfig of stored on disk.
func (k *KubeWrapper) GetKubeconfig() ([]byte, error) {
return k.kubeconfigReader.ReadKubeconfig()
}
// setupK8sComponentsConfigMap applies a ConfigMap (cf. server-side apply) to store the installed k8s components.
// It returns the name of the ConfigMap.
func (k *KubeWrapper) setupK8sComponentsConfigMap(ctx context.Context, components components.Components, clusterVersion string) (string, error) {

View File

@ -50,16 +50,12 @@ func TestInitCluster(t *testing.T) {
kubectl stubKubectl
kubeAPIWaiter stubKubeAPIWaiter
providerMetadata ProviderMetadata
kubeconfigReader configReader
wantConfig k8sapi.KubeadmInitYAML
wantErr bool
k8sVersion versions.ValidK8sVersion
}{
"kubeadm init works with metadata and loadbalancer": {
clusterUtil: stubClusterUtil{},
kubeconfigReader: &stubKubeconfigReader{
kubeconfig: []byte("someKubeconfig"),
},
clusterUtil: stubClusterUtil{kubeconfig: []byte("someKubeconfig")},
kubeAPIWaiter: stubKubeAPIWaiter{},
providerMetadata: &stubProviderMetadata{
selfResp: metadata.InstanceMetadata{
@ -81,6 +77,7 @@ func TestInitCluster(t *testing.T) {
},
},
ClusterConfiguration: kubeadm.ClusterConfiguration{
ClusterName: "kubernetes",
ControlPlaneEndpoint: loadbalancerIP,
APIServer: kubeadm.APIServer{
CertSANs: []string{privateIP},
@ -91,10 +88,7 @@ func TestInitCluster(t *testing.T) {
k8sVersion: versions.Default,
},
"kubeadm init fails when annotating itself": {
clusterUtil: stubClusterUtil{},
kubeconfigReader: &stubKubeconfigReader{
kubeconfig: []byte("someKubeconfig"),
},
clusterUtil: stubClusterUtil{kubeconfig: []byte("someKubeconfig")},
kubeAPIWaiter: stubKubeAPIWaiter{},
providerMetadata: &stubProviderMetadata{
selfResp: metadata.InstanceMetadata{
@ -110,10 +104,7 @@ func TestInitCluster(t *testing.T) {
k8sVersion: versions.Default,
},
"kubeadm init fails when retrieving metadata self": {
clusterUtil: stubClusterUtil{},
kubeconfigReader: &stubKubeconfigReader{
kubeconfig: []byte("someKubeconfig"),
},
clusterUtil: stubClusterUtil{kubeconfig: []byte("someKubeconfig")},
kubeAPIWaiter: stubKubeAPIWaiter{},
providerMetadata: &stubProviderMetadata{
selfErr: someErr,
@ -122,10 +113,7 @@ func TestInitCluster(t *testing.T) {
k8sVersion: versions.Default,
},
"kubeadm init fails when retrieving metadata loadbalancer ip": {
clusterUtil: stubClusterUtil{},
kubeconfigReader: &stubKubeconfigReader{
kubeconfig: []byte("someKubeconfig"),
},
clusterUtil: stubClusterUtil{kubeconfig: []byte("someKubeconfig")},
providerMetadata: &stubProviderMetadata{
getLoadBalancerEndpointErr: someErr,
},
@ -133,8 +121,8 @@ func TestInitCluster(t *testing.T) {
k8sVersion: versions.Default,
},
"kubeadm init fails when applying the init config": {
clusterUtil: stubClusterUtil{initClusterErr: someErr},
kubeconfigReader: &stubKubeconfigReader{
clusterUtil: stubClusterUtil{
initClusterErr: someErr,
kubeconfig: []byte("someKubeconfig"),
},
kubeAPIWaiter: stubKubeAPIWaiter{},
@ -143,95 +131,67 @@ func TestInitCluster(t *testing.T) {
k8sVersion: versions.Default,
},
"kubeadm init fails when deploying cilium": {
clusterUtil: stubClusterUtil{},
clusterUtil: stubClusterUtil{kubeconfig: []byte("someKubeconfig")},
helmClient: stubHelmClient{ciliumError: someErr},
kubeconfigReader: &stubKubeconfigReader{
kubeconfig: []byte("someKubeconfig"),
},
providerMetadata: &stubProviderMetadata{},
wantErr: true,
k8sVersion: versions.Default,
},
"kubeadm init fails when setting up constellation-services chart": {
clusterUtil: stubClusterUtil{},
clusterUtil: stubClusterUtil{kubeconfig: []byte("someKubeconfig")},
helmClient: stubHelmClient{servicesError: someErr},
kubeconfigReader: &stubKubeconfigReader{
kubeconfig: []byte("someKubeconfig"),
},
kubeAPIWaiter: stubKubeAPIWaiter{},
providerMetadata: &stubProviderMetadata{},
wantErr: true,
k8sVersion: versions.Default,
},
"kubeadm init fails when setting the cloud node manager": {
clusterUtil: stubClusterUtil{},
clusterUtil: stubClusterUtil{kubeconfig: []byte("someKubeconfig")},
helmClient: stubHelmClient{servicesError: someErr},
kubeconfigReader: &stubKubeconfigReader{
kubeconfig: []byte("someKubeconfig"),
},
kubeAPIWaiter: stubKubeAPIWaiter{},
providerMetadata: &stubProviderMetadata{},
wantErr: true,
k8sVersion: versions.Default,
},
"kubeadm init fails when setting the cluster autoscaler": {
clusterUtil: stubClusterUtil{},
clusterUtil: stubClusterUtil{kubeconfig: []byte("someKubeconfig")},
helmClient: stubHelmClient{servicesError: someErr},
kubeconfigReader: &stubKubeconfigReader{
kubeconfig: []byte("someKubeconfig"),
},
kubeAPIWaiter: stubKubeAPIWaiter{},
providerMetadata: &stubProviderMetadata{},
wantErr: true,
k8sVersion: versions.Default,
},
"kubeadm init fails when reading kubeconfig": {
clusterUtil: stubClusterUtil{},
kubeconfigReader: &stubKubeconfigReader{
readErr: someErr,
},
clusterUtil: stubClusterUtil{kubeconfig: []byte("someKubeconfig")},
kubeAPIWaiter: stubKubeAPIWaiter{},
providerMetadata: &stubProviderMetadata{},
wantErr: true,
k8sVersion: versions.Default,
},
"kubeadm init fails when setting up konnectivity": {
clusterUtil: stubClusterUtil{},
clusterUtil: stubClusterUtil{kubeconfig: []byte("someKubeconfig")},
helmClient: stubHelmClient{servicesError: someErr},
kubeconfigReader: &stubKubeconfigReader{
kubeconfig: []byte("someKubeconfig"),
},
kubeAPIWaiter: stubKubeAPIWaiter{},
providerMetadata: &stubProviderMetadata{},
wantErr: true,
k8sVersion: versions.Default,
},
"kubeadm init fails when setting up verification service": {
clusterUtil: stubClusterUtil{},
helmClient: stubHelmClient{servicesError: someErr},
kubeconfigReader: &stubKubeconfigReader{
kubeconfig: []byte("someKubeconfig"),
},
clusterUtil: stubClusterUtil{kubeconfig: []byte("someKubeconfig")},
kubeAPIWaiter: stubKubeAPIWaiter{},
providerMetadata: &stubProviderMetadata{},
wantErr: true,
k8sVersion: versions.Default,
},
"kubeadm init fails when waiting for kubeAPI server": {
clusterUtil: stubClusterUtil{},
kubeconfigReader: &stubKubeconfigReader{
kubeconfig: []byte("someKubeconfig"),
},
clusterUtil: stubClusterUtil{kubeconfig: []byte("someKubeconfig")},
kubeAPIWaiter: stubKubeAPIWaiter{waitErr: someErr},
providerMetadata: &stubProviderMetadata{},
k8sVersion: versions.Default,
wantErr: true,
},
"unsupported k8sVersion fails cluster creation": {
clusterUtil: stubClusterUtil{},
kubeconfigReader: &stubKubeconfigReader{
kubeconfig: []byte("someKubeconfig"),
},
clusterUtil: stubClusterUtil{kubeconfig: []byte("someKubeconfig")},
kubeAPIWaiter: stubKubeAPIWaiter{},
providerMetadata: &stubProviderMetadata{},
k8sVersion: "1.19",
@ -251,12 +211,11 @@ func TestInitCluster(t *testing.T) {
kubeAPIWaiter: &tc.kubeAPIWaiter,
configProvider: &stubConfigProvider{initConfig: k8sapi.KubeadmInitYAML{}},
client: &tc.kubectl,
kubeconfigReader: tc.kubeconfigReader,
getIPAddr: func() (string, error) { return privateIP, nil },
}
_, err := kube.InitCluster(
context.Background(), serviceAccountURI, string(tc.k8sVersion),
context.Background(), serviceAccountURI, string(tc.k8sVersion), "kubernetes",
nil, nil, false, true, []byte("{}"), false, nil, logger.NewTest(t),
)
@ -503,6 +462,8 @@ type stubClusterUtil struct {
joinClusterErr error
startKubeletErr error
kubeconfig []byte
initConfigs [][]byte
joinConfigs [][]byte
}
@ -515,9 +476,9 @@ func (s *stubClusterUtil) InstallComponents(ctx context.Context, kubernetesCompo
return s.installComponentsErr
}
func (s *stubClusterUtil) InitCluster(ctx context.Context, initConfig []byte, nodeName string, ips []net.IP, controlPlaneEndpoint string, conformanceMode bool, log *logger.Logger) error {
func (s *stubClusterUtil) InitCluster(ctx context.Context, initConfig []byte, nodeName, clusterName string, ips []net.IP, controlPlaneEndpoint string, conformanceMode bool, log *logger.Logger) ([]byte, error) {
s.initConfigs = append(s.initConfigs, initConfig)
return s.initClusterErr
return s.kubeconfig, s.initClusterErr
}
func (s *stubClusterUtil) SetupAutoscaling(kubectl k8sapi.Client, clusterAutoscalerConfiguration kubernetes.Marshaler, secrets kubernetes.Marshaler) error {
@ -611,15 +572,6 @@ func (s *stubKubectl) ListAllNamespaces(ctx context.Context) (*corev1.NamespaceL
return s.listAllNamespacesResp, s.listAllNamespacesErr
}
type stubKubeconfigReader struct {
kubeconfig []byte
readErr error
}
func (s *stubKubeconfigReader) ReadKubeconfig() ([]byte, error) {
return s.kubeconfig, s.readErr
}
type stubHelmClient struct {
ciliumError error
certManagerError error

View File

@ -53,7 +53,7 @@ func NewCreator(out io.Writer) *Creator {
}
// Create creates the handed amount of instances and all the needed resources.
func (c *Creator) Create(ctx context.Context, provider cloudprovider.Provider, config *config.Config, name, insType string, controlPlaneCount, workerCount int,
func (c *Creator) Create(ctx context.Context, provider cloudprovider.Provider, config *config.Config, insType string, controlPlaneCount, workerCount int,
) (clusterid.File, error) {
image, err := c.image.FetchReference(ctx, config)
if err != nil {
@ -67,21 +67,21 @@ func (c *Creator) Create(ctx context.Context, provider cloudprovider.Provider, c
return clusterid.File{}, err
}
defer cl.RemoveInstaller()
return c.createAWS(ctx, cl, config, name, insType, controlPlaneCount, workerCount, image)
return c.createAWS(ctx, cl, config, insType, controlPlaneCount, workerCount, image)
case cloudprovider.GCP:
cl, err := c.newTerraformClient(ctx)
if err != nil {
return clusterid.File{}, err
}
defer cl.RemoveInstaller()
return c.createGCP(ctx, cl, config, name, insType, controlPlaneCount, workerCount, image)
return c.createGCP(ctx, cl, config, insType, controlPlaneCount, workerCount, image)
case cloudprovider.Azure:
cl, err := c.newTerraformClient(ctx)
if err != nil {
return clusterid.File{}, err
}
defer cl.RemoveInstaller()
return c.createAzure(ctx, cl, config, name, insType, controlPlaneCount, workerCount, image)
return c.createAzure(ctx, cl, config, insType, controlPlaneCount, workerCount, image)
case cloudprovider.QEMU:
if runtime.GOARCH != "amd64" || runtime.GOOS != "linux" {
return clusterid.File{}, fmt.Errorf("creation of a QEMU based Constellation is not supported for %s/%s", runtime.GOOS, runtime.GOARCH)
@ -92,18 +92,18 @@ func (c *Creator) Create(ctx context.Context, provider cloudprovider.Provider, c
}
defer cl.RemoveInstaller()
lv := c.newLibvirtRunner()
return c.createQEMU(ctx, cl, lv, name, config, controlPlaneCount, workerCount, image)
return c.createQEMU(ctx, cl, lv, config, controlPlaneCount, workerCount, image)
default:
return clusterid.File{}, fmt.Errorf("unsupported cloud provider: %s", provider)
}
}
func (c *Creator) createAWS(ctx context.Context, cl terraformClient, config *config.Config,
name, insType string, controlPlaneCount, workerCount int, image string,
insType string, controlPlaneCount, workerCount int, image string,
) (idFile clusterid.File, retErr error) {
vars := terraform.AWSClusterVariables{
CommonVariables: terraform.CommonVariables{
Name: name,
Name: config.Name,
CountControlPlanes: controlPlaneCount,
CountWorkers: workerCount,
StateDiskSizeGB: config.StateDiskSizeGB,
@ -137,11 +137,11 @@ func (c *Creator) createAWS(ctx context.Context, cl terraformClient, config *con
}
func (c *Creator) createGCP(ctx context.Context, cl terraformClient, config *config.Config,
name, insType string, controlPlaneCount, workerCount int, image string,
insType string, controlPlaneCount, workerCount int, image string,
) (idFile clusterid.File, retErr error) {
vars := terraform.GCPClusterVariables{
CommonVariables: terraform.CommonVariables{
Name: name,
Name: config.Name,
CountControlPlanes: controlPlaneCount,
CountWorkers: workerCount,
StateDiskSizeGB: config.StateDiskSizeGB,
@ -175,11 +175,11 @@ func (c *Creator) createGCP(ctx context.Context, cl terraformClient, config *con
}
func (c *Creator) createAzure(ctx context.Context, cl terraformClient, config *config.Config,
name, insType string, controlPlaneCount, workerCount int, image string,
insType string, controlPlaneCount, workerCount int, image string,
) (idFile clusterid.File, retErr error) {
vars := terraform.AzureClusterVariables{
CommonVariables: terraform.CommonVariables{
Name: name,
Name: config.Name,
CountControlPlanes: controlPlaneCount,
CountWorkers: workerCount,
StateDiskSizeGB: config.StateDiskSizeGB,
@ -241,7 +241,7 @@ func normalizeAzureURIs(vars terraform.AzureClusterVariables) terraform.AzureClu
return vars
}
func (c *Creator) createQEMU(ctx context.Context, cl terraformClient, lv libvirtRunner, name string, config *config.Config,
func (c *Creator) createQEMU(ctx context.Context, cl terraformClient, lv libvirtRunner, config *config.Config,
controlPlaneCount, workerCount int, source string,
) (idFile clusterid.File, retErr error) {
qemuRollbacker := &rollbackerQEMU{client: cl, libvirt: lv, createdWorkspace: false}
@ -260,7 +260,7 @@ func (c *Creator) createQEMU(ctx context.Context, cl terraformClient, lv libvirt
switch {
// if no libvirt URI is specified, start a libvirt container
case libvirtURI == "":
if err := lv.Start(ctx, name, config.Provider.QEMU.LibvirtContainerImage); err != nil {
if err := lv.Start(ctx, config.Name, config.Provider.QEMU.LibvirtContainerImage); err != nil {
return clusterid.File{}, err
}
libvirtURI = libvirt.LibvirtTCPConnectURI
@ -292,7 +292,7 @@ func (c *Creator) createQEMU(ctx context.Context, cl terraformClient, lv libvirt
vars := terraform.QEMUVariables{
CommonVariables: terraform.CommonVariables{
Name: name,
Name: config.Name,
CountControlPlanes: controlPlaneCount,
CountWorkers: workerCount,
StateDiskSizeGB: config.StateDiskSizeGB,

View File

@ -114,7 +114,7 @@ func TestCreator(t *testing.T) {
},
}
idFile, err := creator.Create(context.Background(), tc.provider, tc.config, "name", "type", 2, 3)
idFile, err := creator.Create(context.Background(), tc.provider, tc.config, "type", 2, 3)
if tc.wantErr {
assert.Error(err)

View File

@ -21,7 +21,7 @@ type cloudCreator interface {
ctx context.Context,
provider cloudprovider.Provider,
config *config.Config,
name, insType string,
insType string,
coordCount, nodeCount int,
) (clusterid.File, error)
}

View File

@ -35,7 +35,7 @@ func (c *stubCloudCreator) Create(
ctx context.Context,
provider cloudprovider.Provider,
config *config.Config,
name, insType string,
insType string,
coordCount, nodeCount int,
) (clusterid.File, error) {
c.createCalled = true

View File

@ -41,12 +41,12 @@ func TestConfigGenerateDefaultGCPSpecific(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
wantConf := config.Default()
wantConf.RemoveProviderExcept(cloudprovider.GCP)
fileHandler := file.NewHandler(afero.NewMemMapFs())
cmd := newConfigGenerateCmd()
wantConf := config.Default()
wantConf.RemoveProviderExcept(cloudprovider.GCP)
cg := &configGenerateCmd{log: logger.NewTest(t)}
require.NoError(cg.configGenerate(cmd, fileHandler, cloudprovider.GCP))

View File

@ -30,7 +30,6 @@ func NewCreateCmd() *cobra.Command {
Args: cobra.ExactArgs(0),
RunE: runCreate,
}
cmd.Flags().String("name", "constell", "create the cluster with the specified name")
cmd.Flags().BoolP("yes", "y", false, "create the cluster without further confirmation")
cmd.Flags().IntP("control-plane-nodes", "c", 0, "number of control-plane nodes (required)")
must(cobra.MarkFlagRequired(cmd.Flags(), "control-plane-nodes"))
@ -110,9 +109,6 @@ func (c *createCmd) create(cmd *cobra.Command, creator cloudCreator, fileHandler
case cloudprovider.AWS:
c.log.Debugf("Configuring instance type for AWS")
instanceType = conf.Provider.AWS.InstanceType
if len(flags.name) > 10 {
return fmt.Errorf("cluster name on AWS must not be longer than 10 characters")
}
case cloudprovider.Azure:
c.log.Debugf("Configuring instance type for Azure")
instanceType = conf.Provider.Azure.InstanceType
@ -142,7 +138,7 @@ func (c *createCmd) create(cmd *cobra.Command, creator cloudCreator, fileHandler
}
spinner.Start("Creating", false)
idFile, err := creator.Create(cmd.Context(), provider, conf, flags.name, instanceType, flags.controllerCount, flags.workerCount)
idFile, err := creator.Create(cmd.Context(), provider, conf, instanceType, flags.controllerCount, flags.workerCount)
c.log.Debugf("Successfully created the cloud resources for the cluster")
spinner.Stop()
if err != nil {
@ -177,18 +173,6 @@ func (c *createCmd) parseCreateFlags(cmd *cobra.Command) (createFlags, error) {
return createFlags{}, fmt.Errorf("number of worker nodes must be at least %d", constants.MinWorkerCount)
}
name, err := cmd.Flags().GetString("name")
if err != nil {
return createFlags{}, fmt.Errorf("parsing name argument: %w", err)
}
c.log.Debugf("Name flag is %q", name)
if len(name) > constants.ConstellationNameLength {
return createFlags{}, fmt.Errorf(
"name for Constellation cluster too long, maximum length is %d, got %d: %s",
constants.ConstellationNameLength, len(name), name,
)
}
yes, err := cmd.Flags().GetBool("yes")
if err != nil {
return createFlags{}, fmt.Errorf("%w; Set '-yes' without a value to automatically confirm", err)
@ -210,7 +194,6 @@ func (c *createCmd) parseCreateFlags(cmd *cobra.Command) (createFlags, error) {
return createFlags{
controllerCount: controllerCount,
workerCount: workerCount,
name: name,
configPath: configPath,
force: force,
yes: yes,
@ -221,7 +204,6 @@ func (c *createCmd) parseCreateFlags(cmd *cobra.Command) (createFlags, error) {
type createFlags struct {
controllerCount int
workerCount int
name string
configPath string
force bool
yes bool

View File

@ -10,7 +10,6 @@ import (
"bytes"
"errors"
"strconv"
"strings"
"testing"
"github.com/edgelesssys/constellation/v2/cli/internal/clusterid"
@ -42,7 +41,6 @@ func TestCreate(t *testing.T) {
controllerCountFlag *int
workerCountFlag *int
configFlag string
nameFlag string
stdin string
wantErr bool
wantAbort bool
@ -81,15 +79,6 @@ func TestCreate(t *testing.T) {
stdin: "foo\nfoo\nfoo\n",
wantErr: true,
},
"flag name to long": {
setupFs: fsWithDefaultConfig,
creator: &stubCloudCreator{},
provider: cloudprovider.GCP,
controllerCountFlag: intPtr(1),
workerCountFlag: intPtr(1),
nameFlag: strings.Repeat("a", constants.ConstellationNameLength+1),
wantErr: true,
},
"flag control-plane-count invalid": {
setupFs: fsWithDefaultConfig,
creator: &stubCloudCreator{},
@ -200,9 +189,6 @@ func TestCreate(t *testing.T) {
if tc.yesFlag {
require.NoError(cmd.Flags().Set("yes", "true"))
}
if tc.nameFlag != "" {
require.NoError(cmd.Flags().Set("name", tc.nameFlag))
}
if tc.configFlag != "" {
require.NoError(cmd.Flags().Set("config", tc.configFlag))
}

View File

@ -226,6 +226,7 @@ func TestIAMCreateAWS(t *testing.T) {
cmd.Flags().String("config", constants.ConfigFilename, "") // register persistent flag manually
cmd.Flags().Bool("generate-config", false, "") // register persistent flag manually
cmd.Flags().Bool("yes", false, "") // register persistent flag manually
cmd.Flags().String("name", "constell", "") // register persistent flag manually
if tc.zoneFlag != "" {
require.NoError(cmd.Flags().Set("zone", tc.zoneFlag))
@ -446,7 +447,8 @@ func TestIAMCreateAzure(t *testing.T) {
cmd.Flags().String("config", constants.ConfigFilename, "") // register persistent flag manually
cmd.Flags().Bool("generate-config", false, "") // register persistent flag manually
cmd.Flags().Bool("yes", false, "") // register persistent flag
cmd.Flags().Bool("yes", false, "") // register persistent flag manually
cmd.Flags().String("name", "constell", "") // register persistent flag manually
if tc.regionFlag != "" {
require.NoError(cmd.Flags().Set("region", tc.regionFlag))
@ -694,7 +696,8 @@ func TestIAMCreateGCP(t *testing.T) {
cmd.Flags().String("config", constants.ConfigFilename, "") // register persistent flag manually
cmd.Flags().Bool("generate-config", false, "") // register persistent flag manually
cmd.Flags().Bool("yes", false, "") // register persistent flag
cmd.Flags().Bool("yes", false, "") // register persistent flag manually
cmd.Flags().String("name", "constell", "") // register persistent flag manually
if tc.zoneFlag != "" {
require.NoError(cmd.Flags().Set("zone", tc.zoneFlag))

View File

@ -137,11 +137,14 @@ func (i *initCmd) initialize(cmd *cobra.Command, newDialer func(validator *cloud
helmLoader := helm.NewLoader(provider, k8sVersion)
i.log.Debugf("Created new Helm loader")
helmDeployments, err := helmLoader.Load(conf, flags.conformance, masterSecret.Key, masterSecret.Salt)
i.log.Debugf("Loaded Helm heployments")
i.log.Debugf("Loaded Helm deployments")
if err != nil {
return fmt.Errorf("loading Helm charts: %w", err)
}
clusterName := conf.Name + "-" + idFile.UID
i.log.Debugf("Setting cluster name to %s", clusterName)
spinner.Start("Initializing cluster ", false)
req := &initproto.InitRequest{
MasterSecret: masterSecret.Key,
@ -158,6 +161,7 @@ func (i *initCmd) initialize(cmd *cobra.Command, newDialer func(validator *cloud
EnforceIdkeydigest: conf.EnforcesIDKeyDigest(),
ConformanceMode: flags.conformance,
InitSecret: idFile.InitSecret,
ClusterName: clusterName,
}
i.log.Debugf("Sending initialization request")
resp, err := i.initCall(cmd.Context(), newDialer(validator), idFile.IP, req)

View File

@ -479,6 +479,7 @@ func defaultConfigWithExpectedMeasurements(t *testing.T, conf *config.Config, cs
t.Helper()
conf.Image = constants.VersionInfo
conf.Name = "kubernetes"
switch csp {
case cloudprovider.Azure:

View File

@ -213,6 +213,7 @@ func (m *miniUpCmd) prepareConfig(cmd *cobra.Command, fileHandler file.Handler)
}
config := config.Default()
config.Name = constants.MiniConstellationUID
config.RemoveProviderExcept(cloudprovider.QEMU)
config.StateDiskSizeGB = 8
m.log.Debugf("Prepared configuration")
@ -223,7 +224,7 @@ func (m *miniUpCmd) prepareConfig(cmd *cobra.Command, fileHandler file.Handler)
// createMiniCluster creates a new cluster using the given config.
func (m *miniUpCmd) createMiniCluster(ctx context.Context, fileHandler file.Handler, creator cloudCreator, config *config.Config) error {
m.log.Debugf("Creating mini cluster")
idFile, err := creator.Create(ctx, cloudprovider.QEMU, config, "mini", "", 1, 1)
idFile, err := creator.Create(ctx, cloudprovider.QEMU, config, "", 1, 1)
if err != nil {
return err
}

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 76 KiB

View File

@ -58,9 +58,12 @@ type Config struct {
// Schema version of this configuration file.
Version string `yaml:"version" validate:"eq=v2"`
// description: |
// Machine image used to create Constellation nodes.
// Machine image version used to create Constellation nodes.
Image string `yaml:"image" validate:"required,version_compatibility"`
// description: |
// Name of the cluster.
Name string `yaml:"name" validate:"required,valid_name"`
// description: |
// Size (in GB) of a node's disk to store the non-volatile state.
StateDiskSizeGB int `yaml:"stateDiskSizeGB" validate:"min=0"`
// description: |
@ -252,6 +255,7 @@ func Default() *Config {
return &Config{
Version: Version2,
Image: defaultImage,
Name: "constell",
MicroserviceVersion: compatibility.EnsurePrefixV(constants.VersionInfo),
KubernetesVersion: string(versions.Default),
StateDiskSizeGB: 30,
@ -505,6 +509,13 @@ func (c *Config) Validate(force bool) error {
if err := validate.RegisterTranslation("version_compatibility", trans, registerVersionCompatibilityError, translateVersionCompatibilityError); err != nil {
return err
}
if err := validate.RegisterTranslation("valid_name", trans, registerValidateNameError, c.translateValidateNameError); err != nil {
return err
}
if err := validate.RegisterValidation("valid_name", c.validateName); err != nil {
return err
}
if err := validate.RegisterValidation("no_placeholders", validateNoPlaceholder); err != nil {
return err

View File

@ -24,7 +24,7 @@ func init() {
ConfigDoc.Type = "Config"
ConfigDoc.Comments[encoder.LineComment] = "Config defines configuration used by CLI."
ConfigDoc.Description = "Config defines configuration used by CLI."
ConfigDoc.Fields = make([]encoder.Doc, 8)
ConfigDoc.Fields = make([]encoder.Doc, 9)
ConfigDoc.Fields[0].Name = "version"
ConfigDoc.Fields[0].Type = "string"
ConfigDoc.Fields[0].Note = ""
@ -33,40 +33,45 @@ func init() {
ConfigDoc.Fields[1].Name = "image"
ConfigDoc.Fields[1].Type = "string"
ConfigDoc.Fields[1].Note = ""
ConfigDoc.Fields[1].Description = "Machine image used to create Constellation nodes."
ConfigDoc.Fields[1].Comments[encoder.LineComment] = "Machine image used to create Constellation nodes."
ConfigDoc.Fields[2].Name = "stateDiskSizeGB"
ConfigDoc.Fields[2].Type = "int"
ConfigDoc.Fields[1].Description = "Machine image version used to create Constellation nodes."
ConfigDoc.Fields[1].Comments[encoder.LineComment] = "Machine image version used to create Constellation nodes."
ConfigDoc.Fields[2].Name = "name"
ConfigDoc.Fields[2].Type = "string"
ConfigDoc.Fields[2].Note = ""
ConfigDoc.Fields[2].Description = "Size (in GB) of a node's disk to store the non-volatile state."
ConfigDoc.Fields[2].Comments[encoder.LineComment] = "Size (in GB) of a node's disk to store the non-volatile state."
ConfigDoc.Fields[3].Name = "kubernetesVersion"
ConfigDoc.Fields[3].Type = "string"
ConfigDoc.Fields[2].Description = "Name of the cluster."
ConfigDoc.Fields[2].Comments[encoder.LineComment] = "Name of the cluster."
ConfigDoc.Fields[3].Name = "stateDiskSizeGB"
ConfigDoc.Fields[3].Type = "int"
ConfigDoc.Fields[3].Note = ""
ConfigDoc.Fields[3].Description = "Kubernetes version to be installed into the cluster."
ConfigDoc.Fields[3].Comments[encoder.LineComment] = "Kubernetes version to be installed into the cluster."
ConfigDoc.Fields[4].Name = "microserviceVersion"
ConfigDoc.Fields[3].Description = "Size (in GB) of a node's disk to store the non-volatile state."
ConfigDoc.Fields[3].Comments[encoder.LineComment] = "Size (in GB) of a node's disk to store the non-volatile state."
ConfigDoc.Fields[4].Name = "kubernetesVersion"
ConfigDoc.Fields[4].Type = "string"
ConfigDoc.Fields[4].Note = ""
ConfigDoc.Fields[4].Description = "Microservice version to be installed into the cluster. Setting this value is optional until v2.7. Defaults to the version of the CLI."
ConfigDoc.Fields[4].Comments[encoder.LineComment] = "Microservice version to be installed into the cluster. Setting this value is optional until v2.7. Defaults to the version of the CLI."
ConfigDoc.Fields[5].Name = "debugCluster"
ConfigDoc.Fields[5].Type = "bool"
ConfigDoc.Fields[4].Description = "Kubernetes version to be installed into the cluster."
ConfigDoc.Fields[4].Comments[encoder.LineComment] = "Kubernetes version to be installed into the cluster."
ConfigDoc.Fields[5].Name = "microserviceVersion"
ConfigDoc.Fields[5].Type = "string"
ConfigDoc.Fields[5].Note = ""
ConfigDoc.Fields[5].Description = "DON'T USE IN PRODUCTION: enable debug mode and use debug images. For usage, see: https://github.com/edgelesssys/constellation/blob/main/debugd/README.md"
ConfigDoc.Fields[5].Comments[encoder.LineComment] = "DON'T USE IN PRODUCTION: enable debug mode and use debug images. For usage, see: https://github.com/edgelesssys/constellation/blob/main/debugd/README.md"
ConfigDoc.Fields[6].Name = "provider"
ConfigDoc.Fields[6].Type = "ProviderConfig"
ConfigDoc.Fields[5].Description = "Microservice version to be installed into the cluster. Setting this value is optional until v2.7. Defaults to the version of the CLI."
ConfigDoc.Fields[5].Comments[encoder.LineComment] = "Microservice version to be installed into the cluster. Setting this value is optional until v2.7. Defaults to the version of the CLI."
ConfigDoc.Fields[6].Name = "debugCluster"
ConfigDoc.Fields[6].Type = "bool"
ConfigDoc.Fields[6].Note = ""
ConfigDoc.Fields[6].Description = "Supported cloud providers and their specific configurations."
ConfigDoc.Fields[6].Comments[encoder.LineComment] = "Supported cloud providers and their specific configurations."
ConfigDoc.Fields[7].Name = "upgrade"
ConfigDoc.Fields[7].Type = "UpgradeConfig"
ConfigDoc.Fields[6].Description = "DON'T USE IN PRODUCTION: enable debug mode and use debug images. For usage, see: https://github.com/edgelesssys/constellation/blob/main/debugd/README.md"
ConfigDoc.Fields[6].Comments[encoder.LineComment] = "DON'T USE IN PRODUCTION: enable debug mode and use debug images. For usage, see: https://github.com/edgelesssys/constellation/blob/main/debugd/README.md"
ConfigDoc.Fields[7].Name = "provider"
ConfigDoc.Fields[7].Type = "ProviderConfig"
ConfigDoc.Fields[7].Note = ""
ConfigDoc.Fields[7].Description = "Configuration to apply during constellation upgrade."
ConfigDoc.Fields[7].Comments[encoder.LineComment] = "Configuration to apply during constellation upgrade."
ConfigDoc.Fields[7].Description = "Supported cloud providers and their specific configurations."
ConfigDoc.Fields[7].Comments[encoder.LineComment] = "Supported cloud providers and their specific configurations."
ConfigDoc.Fields[8].Name = "upgrade"
ConfigDoc.Fields[8].Type = "UpgradeConfig"
ConfigDoc.Fields[8].Note = ""
ConfigDoc.Fields[8].Description = "Configuration to apply during constellation upgrade."
ConfigDoc.Fields[8].Comments[encoder.LineComment] = "Configuration to apply during constellation upgrade."
ConfigDoc.Fields[7].AddExample("", UpgradeConfig{Image: "", Measurements: Measurements{}})
ConfigDoc.Fields[8].AddExample("", UpgradeConfig{Image: "", Measurements: Measurements{}})
UpgradeConfigDoc.Type = "UpgradeConfig"
UpgradeConfigDoc.Comments[encoder.LineComment] = "UpgradeConfig defines configuration used during constellation upgrade."

View File

@ -12,6 +12,7 @@ import (
"fmt"
"io"
"sort"
"strconv"
"strings"
"github.com/edgelesssys/constellation/v2/internal/attestation/measurements"
@ -366,3 +367,28 @@ func returnsTrue(fl validator.FieldLevel) bool {
func validateUpgradeConfig(sl validator.StructLevel) {
fmt.Printf("WARNING: the config key `upgrade` will be deprecated in an upcoming version. Please check the documentation for more information.\n")
}
func registerValidateNameError(ut ut.Translator) error {
return ut.Add("validate_name", "{0} must be no more than {1} characters long", true)
}
func (c *Config) translateValidateNameError(ut ut.Translator, fe validator.FieldError) string {
var t string
if c.Provider.AWS != nil {
t, _ = ut.T("validate_name", fe.Field(), strconv.Itoa(constants.AWSConstellationNameLength))
} else {
t, _ = ut.T("validate_name", fe.Field(), strconv.Itoa(constants.ConstellationNameLength))
}
return t
}
// validateName makes sure the name of the constellation is not too long.
// Since this value may differ between providers, we can't simply use built-in validation.
// This also allows us to eventually add more validation rules for constellation names if necessary.
func (c *Config) validateName(fl validator.FieldLevel) bool {
if c.Provider.AWS != nil {
return len(fl.Field().String()) <= constants.AWSConstellationNameLength
}
return len(fl.Field().String()) <= constants.ConstellationNameLength
}

View File

@ -21,6 +21,8 @@ const (
// ConstellationNameLength is the maximum length of a Constellation's name.
ConstellationNameLength = 37
// AWSConstellationNameLength is the maximum length of a Constellation's name on AWS.
AWSConstellationNameLength = 10
// ConstellationMasterSecretStoreName is the name for the Constellation secrets in Kubernetes.
ConstellationMasterSecretStoreName = "constellation-mastersecret"
// ConstellationMasterSecretKey is the name of the key for the master secret in the master secret kubernetes secret.