2022-09-05 03:06:08 -04:00
|
|
|
/*
|
|
|
|
Copyright (c) Edgeless Systems GmbH
|
|
|
|
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
*/
|
|
|
|
|
2023-01-19 09:57:50 -05:00
|
|
|
// Package kubernetes provides functionality to bootstrap a Kubernetes cluster, or join an exiting one.
|
2022-03-22 11:03:15 -04:00
|
|
|
package kubernetes
|
|
|
|
|
|
|
|
import (
|
2022-05-19 11:18:22 -04:00
|
|
|
"context"
|
2022-10-24 06:23:18 -04:00
|
|
|
"encoding/base64"
|
2022-08-12 09:59:45 -04:00
|
|
|
"encoding/json"
|
2022-10-26 04:37:10 -04:00
|
|
|
"errors"
|
2022-03-22 11:03:15 -04:00
|
|
|
"fmt"
|
2022-07-15 03:33:11 -04:00
|
|
|
"net"
|
2022-12-23 12:59:15 -05:00
|
|
|
"regexp"
|
2022-03-22 11:03:15 -04:00
|
|
|
"strings"
|
2022-11-04 07:36:26 -04:00
|
|
|
"time"
|
2022-03-22 11:03:15 -04:00
|
|
|
|
2023-05-02 03:35:52 -04:00
|
|
|
"go.uber.org/zap"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
kubeadm "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
|
|
|
|
|
2022-09-21 07:47:57 -04:00
|
|
|
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes/k8sapi"
|
2023-01-19 09:57:50 -05:00
|
|
|
"github.com/edgelesssys/constellation/v2/bootstrapper/internal/kubernetes/kubewaiter"
|
2022-10-26 04:37:10 -04:00
|
|
|
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
2022-11-09 08:43:48 -05:00
|
|
|
"github.com/edgelesssys/constellation/v2/internal/cloud/gcpshared"
|
2023-03-17 04:54:15 -04:00
|
|
|
"github.com/edgelesssys/constellation/v2/internal/cloud/openstack"
|
2022-09-21 07:47:57 -04:00
|
|
|
"github.com/edgelesssys/constellation/v2/internal/constants"
|
2022-10-18 07:15:54 -04:00
|
|
|
"github.com/edgelesssys/constellation/v2/internal/deploy/helm"
|
2023-02-01 08:18:35 -05:00
|
|
|
"github.com/edgelesssys/constellation/v2/internal/kubernetes"
|
2022-09-21 07:47:57 -04:00
|
|
|
"github.com/edgelesssys/constellation/v2/internal/logger"
|
|
|
|
"github.com/edgelesssys/constellation/v2/internal/role"
|
2023-01-06 06:04:36 -05:00
|
|
|
"github.com/edgelesssys/constellation/v2/internal/versions/components"
|
2022-03-22 11:03:15 -04:00
|
|
|
)
|
|
|
|
|
2022-12-23 12:59:15 -05:00
|
|
|
var validHostnameRegex = regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`)
|
|
|
|
|
2022-03-22 11:03:15 -04:00
|
|
|
// configurationProvider provides kubeadm init and join configuration.
|
|
|
|
type configurationProvider interface {
|
2023-01-04 11:03:40 -05:00
|
|
|
InitConfiguration(externalCloudProvider bool, k8sVersion string) k8sapi.KubeadmInitYAML
|
2022-04-27 10:37:05 -04:00
|
|
|
JoinConfiguration(externalCloudProvider bool) k8sapi.KubeadmJoinYAML
|
2022-03-22 11:03:15 -04:00
|
|
|
}
|
|
|
|
|
2022-11-04 07:36:26 -04:00
|
|
|
type kubeAPIWaiter interface {
|
|
|
|
Wait(ctx context.Context, kubernetesClient kubewaiter.KubernetesClient) error
|
|
|
|
}
|
|
|
|
|
2022-05-24 04:04:42 -04:00
|
|
|
// KubeWrapper implements Cluster interface.
|
2022-03-22 11:03:15 -04:00
|
|
|
type KubeWrapper struct {
|
2023-03-29 05:16:56 -04:00
|
|
|
cloudProvider string
|
|
|
|
clusterUtil clusterUtil
|
|
|
|
helmClient helmClient
|
|
|
|
kubeAPIWaiter kubeAPIWaiter
|
|
|
|
configProvider configurationProvider
|
|
|
|
client k8sapi.Client
|
|
|
|
providerMetadata ProviderMetadata
|
|
|
|
getIPAddr func() (string, error)
|
2022-03-22 11:03:15 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// New creates a new KubeWrapper with real values.
|
2022-11-09 08:43:48 -05:00
|
|
|
func New(cloudProvider string, clusterUtil clusterUtil, configProvider configurationProvider, client k8sapi.Client,
|
2023-03-29 05:16:56 -04:00
|
|
|
providerMetadata ProviderMetadata, helmClient helmClient, kubeAPIWaiter kubeAPIWaiter,
|
2022-05-24 04:04:42 -04:00
|
|
|
) *KubeWrapper {
|
2022-03-22 11:03:15 -04:00
|
|
|
return &KubeWrapper{
|
2023-03-29 05:16:56 -04:00
|
|
|
cloudProvider: cloudProvider,
|
|
|
|
clusterUtil: clusterUtil,
|
|
|
|
helmClient: helmClient,
|
|
|
|
kubeAPIWaiter: kubeAPIWaiter,
|
|
|
|
configProvider: configProvider,
|
|
|
|
client: client,
|
|
|
|
providerMetadata: providerMetadata,
|
|
|
|
getIPAddr: getIPAddr,
|
2022-03-22 11:03:15 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-26 05:22:21 -04:00
|
|
|
// InitCluster initializes a new Kubernetes cluster and applies pod network provider.
|
2022-06-15 10:00:48 -04:00
|
|
|
func (k *KubeWrapper) InitCluster(
|
2023-03-29 05:16:56 -04:00
|
|
|
ctx context.Context, cloudServiceAccountURI, versionString, clusterName string, measurementSalt []byte,
|
2023-03-21 07:46:49 -04:00
|
|
|
helmReleasesRaw []byte, conformanceMode bool, kubernetesComponents components.Components, log *logger.Logger,
|
2022-06-28 12:33:27 -04:00
|
|
|
) ([]byte, error) {
|
2023-01-04 11:03:40 -05:00
|
|
|
log.With(zap.String("version", versionString)).Infof("Installing Kubernetes components")
|
2022-12-29 08:51:26 -05:00
|
|
|
if err := k.clusterUtil.InstallComponents(ctx, kubernetesComponents); err != nil {
|
2022-06-28 12:33:27 -04:00
|
|
|
return nil, err
|
2022-05-19 11:18:22 -04:00
|
|
|
}
|
|
|
|
|
2022-05-24 04:04:42 -04:00
|
|
|
var nodePodCIDR string
|
2022-07-15 03:33:11 -04:00
|
|
|
var validIPs []net.IP
|
2022-05-24 04:04:42 -04:00
|
|
|
|
|
|
|
// Step 1: retrieve cloud metadata for Kubernetes configuration
|
2022-11-09 08:43:48 -05:00
|
|
|
log.Infof("Retrieving node metadata")
|
|
|
|
instance, err := k.providerMetadata.Self(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("retrieving own instance metadata: %w", err)
|
|
|
|
}
|
|
|
|
if instance.VPCIP != "" {
|
|
|
|
validIPs = append(validIPs, net.ParseIP(instance.VPCIP))
|
|
|
|
}
|
2022-12-23 12:59:15 -05:00
|
|
|
nodeName, err := k8sCompliantHostname(instance.Name)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("generating node name: %w", err)
|
|
|
|
}
|
|
|
|
|
2022-11-09 08:43:48 -05:00
|
|
|
nodeIP := instance.VPCIP
|
|
|
|
subnetworkPodCIDR := instance.SecondaryIPRange
|
|
|
|
if len(instance.AliasIPRanges) > 0 {
|
|
|
|
nodePodCIDR = instance.AliasIPRanges[0]
|
|
|
|
}
|
2022-08-04 05:08:20 -04:00
|
|
|
|
2022-11-09 08:43:48 -05:00
|
|
|
// this is the endpoint in "kubeadm init --control-plane-endpoint=<IP/DNS>:<port>"
|
|
|
|
controlPlaneEndpoint, err := k.providerMetadata.GetLoadBalancerEndpoint(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("retrieving load balancer endpoint: %w", err)
|
2022-05-24 04:04:42 -04:00
|
|
|
}
|
2022-11-09 08:43:48 -05:00
|
|
|
|
2022-07-14 07:30:44 -04:00
|
|
|
log.With(
|
|
|
|
zap.String("nodeName", nodeName),
|
2022-11-09 08:43:48 -05:00
|
|
|
zap.String("providerID", instance.ProviderID),
|
2022-07-14 07:30:44 -04:00
|
|
|
zap.String("nodeIP", nodeIP),
|
2022-10-24 10:58:21 -04:00
|
|
|
zap.String("controlPlaneEndpoint", controlPlaneEndpoint),
|
2022-07-14 07:30:44 -04:00
|
|
|
zap.String("podCIDR", subnetworkPodCIDR),
|
|
|
|
).Infof("Setting information for node")
|
2022-05-24 04:04:42 -04:00
|
|
|
|
|
|
|
// Step 2: configure kubeadm init config
|
2022-11-09 08:43:48 -05:00
|
|
|
ccmSupported := cloudprovider.FromString(k.cloudProvider) == cloudprovider.Azure ||
|
2023-06-13 03:58:39 -04:00
|
|
|
cloudprovider.FromString(k.cloudProvider) == cloudprovider.GCP ||
|
|
|
|
cloudprovider.FromString(k.cloudProvider) == cloudprovider.AWS
|
2023-01-04 11:03:40 -05:00
|
|
|
initConfig := k.configProvider.InitConfiguration(ccmSupported, versionString)
|
2022-05-24 04:04:42 -04:00
|
|
|
initConfig.SetNodeIP(nodeIP)
|
2023-02-10 07:27:22 -05:00
|
|
|
initConfig.SetClusterName(clusterName)
|
2022-11-02 07:56:16 -04:00
|
|
|
initConfig.SetCertSANs([]string{nodeIP})
|
2022-05-24 04:04:42 -04:00
|
|
|
initConfig.SetNodeName(nodeName)
|
2022-11-09 08:43:48 -05:00
|
|
|
initConfig.SetProviderID(instance.ProviderID)
|
2022-08-01 10:51:34 -04:00
|
|
|
initConfig.SetControlPlaneEndpoint(controlPlaneEndpoint)
|
2022-03-22 11:03:15 -04:00
|
|
|
initConfigYAML, err := initConfig.Marshal()
|
|
|
|
if err != nil {
|
2022-06-28 12:33:27 -04:00
|
|
|
return nil, fmt.Errorf("encoding kubeadm init configuration as YAML: %w", err)
|
2022-03-22 11:03:15 -04:00
|
|
|
}
|
2022-07-14 07:30:44 -04:00
|
|
|
log.Infof("Initializing Kubernetes cluster")
|
2023-02-10 07:27:22 -05:00
|
|
|
kubeConfig, err := k.clusterUtil.InitCluster(ctx, initConfigYAML, nodeName, clusterName, validIPs, controlPlaneEndpoint, conformanceMode, log)
|
2022-03-22 11:03:15 -04:00
|
|
|
if err != nil {
|
2023-02-10 07:27:22 -05:00
|
|
|
return nil, fmt.Errorf("kubeadm init: %w", err)
|
2022-03-22 11:03:15 -04:00
|
|
|
}
|
2023-02-10 07:27:22 -05:00
|
|
|
|
2022-11-25 05:19:22 -05:00
|
|
|
err = k.client.Initialize(kubeConfig)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("initializing kubectl client: %w", err)
|
|
|
|
}
|
2022-05-24 04:04:42 -04:00
|
|
|
|
2022-11-04 07:36:26 -04:00
|
|
|
waitCtx, cancel := context.WithTimeout(ctx, 2*time.Minute)
|
|
|
|
defer cancel()
|
|
|
|
if err := k.kubeAPIWaiter.Wait(waitCtx, k.client); err != nil {
|
|
|
|
return nil, fmt.Errorf("waiting for Kubernetes API to be available: %w", err)
|
|
|
|
}
|
|
|
|
|
2023-01-03 06:09:53 -05:00
|
|
|
// Setup the K8s components ConfigMap.
|
2023-01-04 11:03:40 -05:00
|
|
|
k8sComponentsConfigMap, err := k.setupK8sComponentsConfigMap(ctx, kubernetesComponents, versionString)
|
2023-01-03 06:09:53 -05:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to setup k8s version ConfigMap: %w", err)
|
|
|
|
}
|
|
|
|
|
2022-12-06 12:48:01 -05:00
|
|
|
// Annotate Node with the hash of the installed components
|
|
|
|
if err := k.client.AnnotateNode(ctx, nodeName,
|
2023-01-06 14:48:03 -05:00
|
|
|
constants.NodeKubernetesComponentsAnnotationKey, k8sComponentsConfigMap,
|
2022-12-06 12:48:01 -05:00
|
|
|
); err != nil {
|
|
|
|
return nil, fmt.Errorf("annotating node with Kubernetes components hash: %w", err)
|
|
|
|
}
|
|
|
|
|
2022-05-24 04:04:42 -04:00
|
|
|
// Step 3: configure & start kubernetes controllers
|
2022-07-14 07:30:44 -04:00
|
|
|
log.Infof("Starting Kubernetes controllers and deployments")
|
2022-05-24 04:04:42 -04:00
|
|
|
setupPodNetworkInput := k8sapi.SetupPodNetworkInput{
|
2022-08-31 09:37:07 -04:00
|
|
|
CloudProvider: k.cloudProvider,
|
|
|
|
NodeName: nodeName,
|
|
|
|
FirstNodePodCIDR: nodePodCIDR,
|
|
|
|
SubnetworkPodCIDR: subnetworkPodCIDR,
|
|
|
|
LoadBalancerEndpoint: controlPlaneEndpoint,
|
2022-05-24 04:04:42 -04:00
|
|
|
}
|
2022-10-18 07:15:54 -04:00
|
|
|
|
|
|
|
var helmReleases helm.Releases
|
|
|
|
if err := json.Unmarshal(helmReleasesRaw, &helmReleases); err != nil {
|
|
|
|
return nil, fmt.Errorf("unmarshalling helm releases: %w", err)
|
|
|
|
}
|
|
|
|
|
2023-03-10 12:36:16 -05:00
|
|
|
log.Infof("Installing Cilium")
|
2022-10-21 06:01:28 -04:00
|
|
|
if err = k.helmClient.InstallCilium(ctx, k.client, helmReleases.Cilium, setupPodNetworkInput); err != nil {
|
2022-10-18 07:15:54 -04:00
|
|
|
return nil, fmt.Errorf("installing pod network: %w", err)
|
2022-03-22 11:03:15 -04:00
|
|
|
}
|
|
|
|
|
2023-03-10 12:27:01 -05:00
|
|
|
log.Infof("Waiting for Cilium to become healthy")
|
|
|
|
timeToStartWaiting := time.Now()
|
2023-05-19 07:57:31 -04:00
|
|
|
// TODO(3u13r): Reduce the timeout when we switched the package repository - this is only this high because we once
|
2023-03-10 12:27:01 -05:00
|
|
|
// saw polling times of ~16 minutes when hitting a slow PoP from Fastly (GitHub's / ghcr.io CDN).
|
|
|
|
waitCtx, cancel = context.WithTimeout(ctx, 20*time.Minute)
|
|
|
|
defer cancel()
|
|
|
|
if err := k.clusterUtil.WaitForCilium(waitCtx, log); err != nil {
|
|
|
|
return nil, fmt.Errorf("waiting for Cilium to become healthy: %w", err)
|
|
|
|
}
|
|
|
|
timeUntilFinishedWaiting := time.Since(timeToStartWaiting)
|
2023-03-10 12:47:14 -05:00
|
|
|
log.With(zap.Duration("duration", timeUntilFinishedWaiting)).Infof("Cilium became healthy")
|
2023-03-10 12:27:01 -05:00
|
|
|
|
2023-03-16 07:10:24 -04:00
|
|
|
log.Infof("Restarting Cilium")
|
2023-03-10 12:27:01 -05:00
|
|
|
if err := k.clusterUtil.FixCilium(ctx); err != nil {
|
|
|
|
log.With(zap.Error(err)).Errorf("FixCilium failed")
|
|
|
|
// Continue and don't throw an error here - things might be okay.
|
|
|
|
}
|
|
|
|
|
2022-08-31 21:40:29 -04:00
|
|
|
var controlPlaneIP string
|
|
|
|
if strings.Contains(controlPlaneEndpoint, ":") {
|
|
|
|
controlPlaneIP, _, err = net.SplitHostPort(controlPlaneEndpoint)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("parsing control plane endpoint: %w", err)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
controlPlaneIP = controlPlaneEndpoint
|
|
|
|
}
|
2022-11-24 04:57:58 -05:00
|
|
|
serviceConfig := constellationServicesConfig{
|
2023-03-29 05:16:56 -04:00
|
|
|
measurementSalt: measurementSalt,
|
|
|
|
subnetworkPodCIDR: subnetworkPodCIDR,
|
|
|
|
cloudServiceAccountURI: cloudServiceAccountURI,
|
|
|
|
loadBalancerIP: controlPlaneIP,
|
2022-11-24 04:57:58 -05:00
|
|
|
}
|
2022-11-21 11:06:41 -05:00
|
|
|
extraVals, err := k.setupExtraVals(ctx, serviceConfig)
|
2022-10-26 04:37:10 -04:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("setting up extraVals: %w", err)
|
|
|
|
}
|
2022-10-24 06:23:18 -04:00
|
|
|
|
2023-03-10 12:36:16 -05:00
|
|
|
log.Infof("Setting up internal-config ConfigMap")
|
2023-03-14 06:46:27 -04:00
|
|
|
if err := k.setupInternalConfigMap(ctx); err != nil {
|
2022-08-31 14:10:49 -04:00
|
|
|
return nil, fmt.Errorf("failed to setup internal ConfigMap: %w", err)
|
|
|
|
}
|
|
|
|
|
2023-07-03 04:19:27 -04:00
|
|
|
log.Infof("Installing Constellation microservices")
|
2023-06-26 04:13:28 -04:00
|
|
|
if err = k.helmClient.InstallChart(ctx, helmReleases.ConstellationServices, extraVals); err != nil {
|
2023-07-03 04:19:27 -04:00
|
|
|
return nil, fmt.Errorf("installing constellation-services: %w", err)
|
|
|
|
}
|
|
|
|
|
2023-06-22 10:31:35 -04:00
|
|
|
// cert-manager provides CRDs used by other deployments,
|
2023-06-26 04:13:28 -04:00
|
|
|
// so it should be installed as early as possible, but after the services cert-manager depends on.
|
2023-03-10 12:36:16 -05:00
|
|
|
log.Infof("Installing cert-manager")
|
2023-06-26 04:13:28 -04:00
|
|
|
if err = k.helmClient.InstallChart(ctx, helmReleases.CertManager, nil); err != nil {
|
2022-12-09 12:30:20 -05:00
|
|
|
return nil, fmt.Errorf("installing cert-manager: %w", err)
|
2022-11-21 04:35:40 -05:00
|
|
|
}
|
2023-03-13 11:22:22 -04:00
|
|
|
|
2023-06-26 04:13:28 -04:00
|
|
|
// Install CSI drivers if enabled by the user.
|
|
|
|
if helmReleases.CSI != nil {
|
|
|
|
var csiVals map[string]any
|
|
|
|
if cloudprovider.FromString(k.cloudProvider) == cloudprovider.OpenStack {
|
|
|
|
creds, err := openstack.AccountKeyFromURI(serviceConfig.cloudServiceAccountURI)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
cinderIni := creds.CloudINI().CinderCSIConfiguration()
|
|
|
|
csiVals = map[string]any{
|
|
|
|
"cinder-config": map[string]any{
|
|
|
|
"secretData": cinderIni,
|
|
|
|
},
|
|
|
|
}
|
2023-06-22 10:31:35 -04:00
|
|
|
}
|
|
|
|
|
2023-06-26 04:13:28 -04:00
|
|
|
log.Infof("Installing CSI deployments")
|
|
|
|
if err := k.helmClient.InstallChart(ctx, *helmReleases.CSI, csiVals); err != nil {
|
|
|
|
return nil, fmt.Errorf("installing CSI snapshot CRDs: %w", err)
|
2023-06-22 10:31:35 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-21 04:35:40 -05:00
|
|
|
operatorVals, err := k.setupOperatorVals(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("setting up operator vals: %w", err)
|
|
|
|
}
|
|
|
|
|
2023-06-22 10:31:35 -04:00
|
|
|
// Constellation operators require CRDs from cert-manager.
|
|
|
|
// They must be installed after it.
|
2023-03-10 12:36:16 -05:00
|
|
|
log.Infof("Installing operators")
|
2023-06-26 04:13:28 -04:00
|
|
|
if err = k.helmClient.InstallChart(ctx, helmReleases.Operators, operatorVals); err != nil {
|
2022-11-21 04:35:40 -05:00
|
|
|
return nil, fmt.Errorf("installing operators: %w", err)
|
2022-08-04 10:15:52 -04:00
|
|
|
}
|
|
|
|
|
2023-02-10 07:27:22 -05:00
|
|
|
return kubeConfig, nil
|
2022-03-22 11:03:15 -04:00
|
|
|
}
|
|
|
|
|
2022-04-26 05:22:21 -04:00
|
|
|
// JoinCluster joins existing Kubernetes cluster.
|
2023-03-20 06:03:36 -04:00
|
|
|
func (k *KubeWrapper) JoinCluster(ctx context.Context, args *kubeadm.BootstrapTokenDiscovery, peerRole role.Role, k8sComponents components.Components, log *logger.Logger) error {
|
2022-12-29 08:51:26 -05:00
|
|
|
log.With("k8sComponents", k8sComponents).Infof("Installing provided kubernetes components")
|
|
|
|
if err := k.clusterUtil.InstallComponents(ctx, k8sComponents); err != nil {
|
|
|
|
return fmt.Errorf("installing kubernetes components: %w", err)
|
2022-05-19 11:18:22 -04:00
|
|
|
}
|
|
|
|
|
2022-05-24 04:04:42 -04:00
|
|
|
// Step 1: retrieve cloud metadata for Kubernetes configuration
|
2022-11-09 08:43:48 -05:00
|
|
|
log.Infof("Retrieving node metadata")
|
|
|
|
instance, err := k.providerMetadata.Self(ctx)
|
2022-06-28 12:23:24 -04:00
|
|
|
if err != nil {
|
2022-11-09 08:43:48 -05:00
|
|
|
return fmt.Errorf("retrieving own instance metadata: %w", err)
|
2022-06-28 12:23:24 -04:00
|
|
|
}
|
2022-11-09 08:43:48 -05:00
|
|
|
providerID := instance.ProviderID
|
|
|
|
nodeInternalIP := instance.VPCIP
|
2022-12-23 12:59:15 -05:00
|
|
|
nodeName, err := k8sCompliantHostname(instance.Name)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("generating node name: %w", err)
|
|
|
|
}
|
2022-11-09 08:43:48 -05:00
|
|
|
|
|
|
|
loadbalancerEndpoint, err := k.providerMetadata.GetLoadBalancerEndpoint(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("retrieving own instance metadata: %w", err)
|
2022-05-24 04:04:42 -04:00
|
|
|
}
|
|
|
|
|
2022-07-14 07:30:44 -04:00
|
|
|
log.With(
|
|
|
|
zap.String("nodeName", nodeName),
|
|
|
|
zap.String("providerID", providerID),
|
|
|
|
zap.String("nodeIP", nodeInternalIP),
|
|
|
|
).Infof("Setting information for node")
|
|
|
|
|
2022-08-31 21:40:29 -04:00
|
|
|
// Step 2: configure kubeadm join config
|
2022-11-09 08:43:48 -05:00
|
|
|
ccmSupported := cloudprovider.FromString(k.cloudProvider) == cloudprovider.Azure ||
|
|
|
|
cloudprovider.FromString(k.cloudProvider) == cloudprovider.GCP
|
|
|
|
joinConfig := k.configProvider.JoinConfiguration(ccmSupported)
|
2022-07-08 04:59:59 -04:00
|
|
|
joinConfig.SetAPIServerEndpoint(args.APIServerEndpoint)
|
2022-03-22 11:03:15 -04:00
|
|
|
joinConfig.SetToken(args.Token)
|
|
|
|
joinConfig.AppendDiscoveryTokenCaCertHash(args.CACertHashes[0])
|
2022-04-25 11:24:48 -04:00
|
|
|
joinConfig.SetNodeIP(nodeInternalIP)
|
2022-03-22 11:03:15 -04:00
|
|
|
joinConfig.SetNodeName(nodeName)
|
|
|
|
joinConfig.SetProviderID(providerID)
|
2022-06-29 09:26:29 -04:00
|
|
|
if peerRole == role.ControlPlane {
|
2022-07-11 07:29:22 -04:00
|
|
|
joinConfig.SetControlPlane(nodeInternalIP)
|
2022-04-25 11:24:48 -04:00
|
|
|
}
|
2022-03-22 11:03:15 -04:00
|
|
|
joinConfigYAML, err := joinConfig.Marshal()
|
|
|
|
if err != nil {
|
2022-06-09 10:04:30 -04:00
|
|
|
return fmt.Errorf("encoding kubeadm join configuration as YAML: %w", err)
|
2022-03-22 11:03:15 -04:00
|
|
|
}
|
2022-07-14 07:30:44 -04:00
|
|
|
log.With(zap.String("apiServerEndpoint", args.APIServerEndpoint)).Infof("Joining Kubernetes cluster")
|
2022-08-31 21:40:29 -04:00
|
|
|
if err := k.clusterUtil.JoinCluster(ctx, joinConfigYAML, peerRole, loadbalancerEndpoint, log); err != nil {
|
2022-06-09 10:04:30 -04:00
|
|
|
return fmt.Errorf("joining cluster: %v; %w ", string(joinConfigYAML), err)
|
2022-03-22 11:03:15 -04:00
|
|
|
}
|
|
|
|
|
2023-03-10 12:27:01 -05:00
|
|
|
log.Infof("Waiting for Cilium to become healthy")
|
|
|
|
if err := k.clusterUtil.WaitForCilium(context.Background(), log); err != nil {
|
|
|
|
return fmt.Errorf("waiting for Cilium to become healthy: %w", err)
|
|
|
|
}
|
|
|
|
|
2023-03-16 07:10:24 -04:00
|
|
|
log.Infof("Restarting Cilium")
|
2023-03-10 12:27:01 -05:00
|
|
|
if err := k.clusterUtil.FixCilium(context.Background()); err != nil {
|
|
|
|
log.With(zap.Error(err)).Errorf("FixCilium failed")
|
|
|
|
// Continue and don't throw an error here - things might be okay.
|
|
|
|
}
|
2022-06-13 10:01:21 -04:00
|
|
|
|
2022-03-22 11:03:15 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-01-03 06:09:53 -05:00
|
|
|
// setupK8sComponentsConfigMap applies a ConfigMap (cf. server-side apply) to store the installed k8s components.
|
|
|
|
// It returns the name of the ConfigMap.
|
2023-01-06 06:04:36 -05:00
|
|
|
func (k *KubeWrapper) setupK8sComponentsConfigMap(ctx context.Context, components components.Components, clusterVersion string) (string, error) {
|
2023-02-01 08:18:35 -05:00
|
|
|
componentsConfig, err := kubernetes.ConstructK8sComponentsCM(components, clusterVersion)
|
2022-11-23 04:29:36 -05:00
|
|
|
if err != nil {
|
2023-02-01 08:18:35 -05:00
|
|
|
return "", fmt.Errorf("constructing k8s-components ConfigMap: %w", err)
|
2022-11-23 04:29:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := k.client.CreateConfigMap(ctx, componentsConfig); err != nil {
|
2023-01-03 06:09:53 -05:00
|
|
|
return "", fmt.Errorf("apply in KubeWrapper.setupK8sVersionConfigMap(..) for components config map failed with: %w", err)
|
2022-11-23 04:29:36 -05:00
|
|
|
}
|
|
|
|
|
2023-02-01 08:18:35 -05:00
|
|
|
return componentsConfig.ObjectMeta.Name, nil
|
2022-08-31 14:10:49 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// setupInternalConfigMap applies a ConfigMap (cf. server-side apply) to store information that is not supposed to be user-editable.
|
2023-03-14 06:46:27 -04:00
|
|
|
func (k *KubeWrapper) setupInternalConfigMap(ctx context.Context) error {
|
2022-08-31 14:10:49 -04:00
|
|
|
config := corev1.ConfigMap{
|
|
|
|
TypeMeta: metav1.TypeMeta{
|
|
|
|
APIVersion: "v1",
|
|
|
|
Kind: "ConfigMap",
|
|
|
|
},
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: constants.InternalConfigMap,
|
|
|
|
Namespace: "kube-system",
|
|
|
|
},
|
2023-03-14 06:46:27 -04:00
|
|
|
Data: map[string]string{},
|
2022-08-31 14:10:49 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// We do not use the client's Apply method here since we are handling a kubernetes-native type.
|
|
|
|
// These types don't implement our custom Marshaler interface.
|
|
|
|
if err := k.client.CreateConfigMap(ctx, config); err != nil {
|
|
|
|
return fmt.Errorf("apply in KubeWrapper.setupInternalConfigMap failed with: %w", err)
|
2022-07-18 06:28:02 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-05-24 04:04:42 -04:00
|
|
|
// 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])?)*$/ .
|
|
|
|
// Only a simple heuristic is used for now (to lowercase, replace underscores).
|
2022-12-23 12:59:15 -05:00
|
|
|
func k8sCompliantHostname(in string) (string, error) {
|
2022-05-24 04:04:42 -04:00
|
|
|
hostname := strings.ToLower(in)
|
|
|
|
hostname = strings.ReplaceAll(hostname, "_", "-")
|
2022-12-23 12:59:15 -05:00
|
|
|
if !validHostnameRegex.MatchString(hostname) {
|
|
|
|
return "", fmt.Errorf("failed to generate a Kubernetes compliant hostname for %s", in)
|
|
|
|
}
|
|
|
|
return hostname, nil
|
2022-05-04 08:32:34 -04:00
|
|
|
}
|
|
|
|
|
2022-05-19 11:18:22 -04:00
|
|
|
// StartKubelet starts the kubelet service.
|
2022-09-08 08:45:27 -04:00
|
|
|
func (k *KubeWrapper) StartKubelet(log *logger.Logger) error {
|
|
|
|
if err := k.clusterUtil.StartKubelet(); err != nil {
|
|
|
|
return fmt.Errorf("starting kubelet: %w", err)
|
|
|
|
}
|
|
|
|
|
2023-03-10 12:27:01 -05:00
|
|
|
log.Infof("Waiting for Cilium to become healthy")
|
|
|
|
if err := k.clusterUtil.WaitForCilium(context.Background(), log); err != nil {
|
|
|
|
return fmt.Errorf("waiting for Cilium to become healthy: %w", err)
|
|
|
|
}
|
|
|
|
|
2023-03-16 07:10:24 -04:00
|
|
|
log.Infof("Restarting Cilium")
|
2023-03-10 12:27:01 -05:00
|
|
|
if err := k.clusterUtil.FixCilium(context.Background()); err != nil {
|
|
|
|
log.With(zap.Error(err)).Errorf("FixCilium failed")
|
|
|
|
// Continue and don't throw an error here - things might be okay.
|
|
|
|
}
|
|
|
|
|
2022-09-08 08:45:27 -04:00
|
|
|
return nil
|
2022-05-19 11:18:22 -04:00
|
|
|
}
|
2022-08-26 05:44:05 -04:00
|
|
|
|
|
|
|
// getIPAddr retrieves to default sender IP used for outgoing connection.
|
|
|
|
func getIPAddr() (string, error) {
|
|
|
|
conn, err := net.Dial("udp", "8.8.8.8:80")
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
defer conn.Close()
|
|
|
|
|
|
|
|
localAddr := conn.LocalAddr().(*net.UDPAddr)
|
|
|
|
|
|
|
|
return localAddr.IP.String(), nil
|
|
|
|
}
|
2022-10-24 06:23:18 -04:00
|
|
|
|
|
|
|
// setupExtraVals create a helm values map for consumption by helm-install.
|
|
|
|
// Will move to a more dedicated place once that place becomes apparent.
|
2022-11-21 11:06:41 -05:00
|
|
|
func (k *KubeWrapper) setupExtraVals(ctx context.Context, serviceConfig constellationServicesConfig) (map[string]any, error) {
|
2022-10-26 04:37:10 -04:00
|
|
|
extraVals := map[string]any{
|
2022-10-25 09:51:23 -04:00
|
|
|
"join-service": map[string]any{
|
2022-11-21 11:06:41 -05:00
|
|
|
"measurementSalt": base64.StdEncoding.EncodeToString(serviceConfig.measurementSalt),
|
2022-10-24 06:23:18 -04:00
|
|
|
},
|
2022-11-21 11:06:41 -05:00
|
|
|
"verification-service": map[string]any{
|
|
|
|
"loadBalancerIP": serviceConfig.loadBalancerIP,
|
|
|
|
},
|
2022-11-23 02:26:09 -05:00
|
|
|
"konnectivity": map[string]any{
|
|
|
|
"loadBalancerIP": serviceConfig.loadBalancerIP,
|
|
|
|
},
|
2022-10-26 04:37:10 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
instance, err := k.providerMetadata.Self(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("retrieving current instance: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
switch cloudprovider.FromString(k.cloudProvider) {
|
|
|
|
case cloudprovider.GCP:
|
2022-11-09 08:43:48 -05:00
|
|
|
uid, err := k.providerMetadata.UID(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("getting uid: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
projectID, _, _, err := gcpshared.SplitProviderID(instance.ProviderID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("splitting providerID: %w", err)
|
|
|
|
}
|
|
|
|
|
2022-11-21 11:06:41 -05:00
|
|
|
serviceAccountKey, err := gcpshared.ServiceAccountKeyFromURI(serviceConfig.cloudServiceAccountURI)
|
2022-11-09 08:43:48 -05:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("getting service account key: %w", err)
|
|
|
|
}
|
|
|
|
rawKey, err := json.Marshal(serviceAccountKey)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("marshaling service account key: %w", err)
|
|
|
|
}
|
|
|
|
|
2023-06-26 04:13:28 -04:00
|
|
|
extraVals["ccm"] = map[string]any{
|
|
|
|
"GCP": map[string]any{
|
|
|
|
"projectID": projectID,
|
|
|
|
"uid": uid,
|
|
|
|
"secretData": string(rawKey),
|
|
|
|
"subnetworkPodCIDR": serviceConfig.subnetworkPodCIDR,
|
|
|
|
},
|
2022-10-26 04:37:10 -04:00
|
|
|
}
|
2022-11-09 08:43:48 -05:00
|
|
|
|
2022-10-26 04:37:10 -04:00
|
|
|
case cloudprovider.Azure:
|
2022-11-09 08:43:48 -05:00
|
|
|
ccmAzure, ok := k.providerMetadata.(ccmConfigGetter)
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("invalid cloud provider metadata for Azure")
|
|
|
|
}
|
|
|
|
|
2022-11-21 11:06:41 -05:00
|
|
|
ccmConfig, err := ccmAzure.GetCCMConfig(ctx, instance.ProviderID, serviceConfig.cloudServiceAccountURI)
|
2022-11-09 08:43:48 -05:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("creating ccm secret: %w", err)
|
|
|
|
}
|
|
|
|
|
2023-06-26 04:13:28 -04:00
|
|
|
extraVals["ccm"] = map[string]any{
|
|
|
|
"Azure": map[string]any{
|
|
|
|
"azureConfig": string(ccmConfig),
|
|
|
|
},
|
2022-11-09 08:43:48 -05:00
|
|
|
}
|
|
|
|
|
2023-03-17 04:54:15 -04:00
|
|
|
case cloudprovider.OpenStack:
|
|
|
|
creds, err := openstack.AccountKeyFromURI(serviceConfig.cloudServiceAccountURI)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2023-05-02 03:35:52 -04:00
|
|
|
credsIni := creds.CloudINI().FullConfiguration()
|
|
|
|
networkIDsGetter, ok := k.providerMetadata.(openstackMetadata)
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("generating yawol configuration: cloud provider metadata does not implement OpenStack specific methods")
|
|
|
|
}
|
|
|
|
networkIDs, err := networkIDsGetter.GetNetworkIDs(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("getting network IDs: %w", err)
|
|
|
|
}
|
|
|
|
if len(networkIDs) == 0 {
|
|
|
|
return nil, errors.New("getting network IDs: no network IDs found")
|
|
|
|
}
|
2023-03-17 04:54:15 -04:00
|
|
|
extraVals["ccm"] = map[string]any{
|
|
|
|
"OpenStack": map[string]any{
|
|
|
|
"secretData": credsIni,
|
|
|
|
},
|
|
|
|
}
|
2023-05-02 03:35:52 -04:00
|
|
|
yawolIni := creds.CloudINI().YawolConfiguration()
|
|
|
|
extraVals["yawol-config"] = map[string]any{
|
|
|
|
"secretData": yawolIni,
|
|
|
|
}
|
|
|
|
extraVals["yawol-controller"] = map[string]any{
|
|
|
|
"yawolNetworkID": networkIDs[0],
|
|
|
|
"yawolAPIHost": fmt.Sprintf("https://%s:%d", serviceConfig.loadBalancerIP, constants.KubernetesPort),
|
|
|
|
}
|
2022-10-24 06:23:18 -04:00
|
|
|
}
|
2022-10-26 04:37:10 -04:00
|
|
|
return extraVals, nil
|
2022-10-24 06:23:18 -04:00
|
|
|
}
|
2022-11-09 08:43:48 -05:00
|
|
|
|
2022-11-21 04:35:40 -05:00
|
|
|
func (k *KubeWrapper) setupOperatorVals(ctx context.Context) (map[string]any, error) {
|
|
|
|
uid, err := k.providerMetadata.UID(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("retrieving constellation UID: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return map[string]any{
|
|
|
|
"constellation-operator": map[string]any{
|
|
|
|
"constellationUID": uid,
|
|
|
|
},
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2022-11-09 08:43:48 -05:00
|
|
|
type ccmConfigGetter interface {
|
|
|
|
GetCCMConfig(ctx context.Context, providerID, cloudServiceAccountURI string) ([]byte, error)
|
|
|
|
}
|
2022-11-21 11:06:41 -05:00
|
|
|
|
|
|
|
type constellationServicesConfig struct {
|
2023-03-29 05:16:56 -04:00
|
|
|
measurementSalt []byte
|
|
|
|
subnetworkPodCIDR string
|
|
|
|
cloudServiceAccountURI string
|
|
|
|
loadBalancerIP string
|
2022-11-21 11:06:41 -05:00
|
|
|
}
|
2023-05-02 03:35:52 -04:00
|
|
|
|
|
|
|
type openstackMetadata interface {
|
|
|
|
GetNetworkIDs(ctx context.Context) ([]string, error)
|
|
|
|
}
|