mirror of
https://github.com/edgelesssys/constellation.git
synced 2025-12-15 16:09:39 -05:00
bootstrapper: add fallback endpoint and custom endpoint to SAN field (#2108)
terraform: collect apiserver cert SANs and support custom endpoint constants: add new constants for cluster configuration and custom endpoint cloud: support apiserver cert sans and prepare for endpoint migration on AWS config: add customEndpoint field bootstrapper: use per-CSP apiserver cert SANs cli: route customEndpoint to terraform and add migration for apiserver cert SANs bootstrapper: change interface of GetLoadBalancerEndpoint to return host and port separately
This commit is contained in:
parent
3324a4eba2
commit
8da6a23aa5
64 changed files with 724 additions and 301 deletions
|
|
@ -26,7 +26,7 @@ type imageFetcher interface {
|
|||
|
||||
type terraformClient interface {
|
||||
PrepareWorkspace(path string, input terraform.Variables) error
|
||||
CreateCluster(ctx context.Context, logLevel terraform.LogLevel) (terraform.CreateOutput, error)
|
||||
CreateCluster(ctx context.Context, logLevel terraform.LogLevel) (terraform.ApplyOutput, error)
|
||||
CreateIAMConfig(ctx context.Context, provider cloudprovider.Provider, logLevel terraform.LogLevel) (terraform.IAMOutput, error)
|
||||
Destroy(ctx context.Context, logLevel terraform.LogLevel) error
|
||||
CleanUpWorkspace() error
|
||||
|
|
|
|||
|
|
@ -45,8 +45,8 @@ type stubTerraformClient struct {
|
|||
showErr error
|
||||
}
|
||||
|
||||
func (c *stubTerraformClient) CreateCluster(_ context.Context, _ terraform.LogLevel) (terraform.CreateOutput, error) {
|
||||
return terraform.CreateOutput{
|
||||
func (c *stubTerraformClient) CreateCluster(_ context.Context, _ terraform.LogLevel) (terraform.ApplyOutput, error) {
|
||||
return terraform.ApplyOutput{
|
||||
IP: c.ip,
|
||||
Secret: c.initSecret,
|
||||
UID: c.uid,
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ func (c *Creator) Create(ctx context.Context, opts CreateOptions) (clusterid.Fil
|
|||
}
|
||||
defer cl.RemoveInstaller()
|
||||
|
||||
var tfOutput terraform.CreateOutput
|
||||
var tfOutput terraform.ApplyOutput
|
||||
switch opts.Provider {
|
||||
case cloudprovider.AWS:
|
||||
|
||||
|
|
@ -117,48 +117,49 @@ func (c *Creator) Create(ctx context.Context, opts CreateOptions) (clusterid.Fil
|
|||
}
|
||||
|
||||
return clusterid.File{
|
||||
CloudProvider: opts.Provider,
|
||||
IP: tfOutput.IP,
|
||||
InitSecret: []byte(tfOutput.Secret),
|
||||
UID: tfOutput.UID,
|
||||
AttestationURL: tfOutput.AttestationURL,
|
||||
CloudProvider: opts.Provider,
|
||||
IP: tfOutput.IP,
|
||||
APIServerCertSANs: tfOutput.APIServerCertSANs,
|
||||
InitSecret: []byte(tfOutput.Secret),
|
||||
UID: tfOutput.UID,
|
||||
AttestationURL: tfOutput.AttestationURL,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Creator) createAWS(ctx context.Context, cl terraformClient, opts CreateOptions) (tfOutput terraform.CreateOutput, retErr error) {
|
||||
func (c *Creator) createAWS(ctx context.Context, cl terraformClient, opts CreateOptions) (tfOutput terraform.ApplyOutput, retErr error) {
|
||||
vars := awsTerraformVars(opts.Config, opts.image, &opts.ControlPlaneCount, &opts.WorkerCount)
|
||||
|
||||
tfOutput, err := runTerraformCreate(ctx, cl, cloudprovider.AWS, vars, c.out, opts.TFLogLevel)
|
||||
if err != nil {
|
||||
return terraform.CreateOutput{}, err
|
||||
return terraform.ApplyOutput{}, err
|
||||
}
|
||||
|
||||
return tfOutput, nil
|
||||
}
|
||||
|
||||
func (c *Creator) createGCP(ctx context.Context, cl terraformClient, opts CreateOptions) (tfOutput terraform.CreateOutput, retErr error) {
|
||||
func (c *Creator) createGCP(ctx context.Context, cl terraformClient, opts CreateOptions) (tfOutput terraform.ApplyOutput, retErr error) {
|
||||
vars := gcpTerraformVars(opts.Config, opts.image, &opts.ControlPlaneCount, &opts.WorkerCount)
|
||||
|
||||
tfOutput, err := runTerraformCreate(ctx, cl, cloudprovider.GCP, vars, c.out, opts.TFLogLevel)
|
||||
if err != nil {
|
||||
return terraform.CreateOutput{}, err
|
||||
return terraform.ApplyOutput{}, err
|
||||
}
|
||||
|
||||
return tfOutput, nil
|
||||
}
|
||||
|
||||
func (c *Creator) createAzure(ctx context.Context, cl terraformClient, opts CreateOptions) (tfOutput terraform.CreateOutput, retErr error) {
|
||||
func (c *Creator) createAzure(ctx context.Context, cl terraformClient, opts CreateOptions) (tfOutput terraform.ApplyOutput, retErr error) {
|
||||
vars := azureTerraformVars(opts.Config, opts.image, &opts.ControlPlaneCount, &opts.WorkerCount)
|
||||
|
||||
tfOutput, err := runTerraformCreate(ctx, cl, cloudprovider.Azure, vars, c.out, opts.TFLogLevel)
|
||||
if err != nil {
|
||||
return terraform.CreateOutput{}, err
|
||||
return terraform.ApplyOutput{}, err
|
||||
}
|
||||
|
||||
if vars.GetCreateMAA() {
|
||||
// Patch the attestation policy to allow the cluster to boot while having secure boot disabled.
|
||||
if err := c.policyPatcher.Patch(ctx, tfOutput.AttestationURL); err != nil {
|
||||
return terraform.CreateOutput{}, err
|
||||
return terraform.ApplyOutput{}, err
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -196,13 +197,13 @@ func normalizeAzureURIs(vars *terraform.AzureClusterVariables) *terraform.AzureC
|
|||
return vars
|
||||
}
|
||||
|
||||
func (c *Creator) createOpenStack(ctx context.Context, cl terraformClient, opts CreateOptions) (tfOutput terraform.CreateOutput, retErr error) {
|
||||
func (c *Creator) createOpenStack(ctx context.Context, cl terraformClient, opts CreateOptions) (tfOutput terraform.ApplyOutput, retErr error) {
|
||||
// TODO(malt3): Remove this once OpenStack is supported.
|
||||
if os.Getenv("CONSTELLATION_OPENSTACK_DEV") != "1" {
|
||||
return terraform.CreateOutput{}, errors.New("OpenStack isn't supported yet")
|
||||
return terraform.ApplyOutput{}, errors.New("OpenStack isn't supported yet")
|
||||
}
|
||||
if _, hasOSAuthURL := os.LookupEnv("OS_AUTH_URL"); !hasOSAuthURL && opts.Config.Provider.OpenStack.Cloud == "" {
|
||||
return terraform.CreateOutput{}, errors.New(
|
||||
return terraform.ApplyOutput{}, errors.New(
|
||||
"neither environment variable OS_AUTH_URL nor cloud name for \"clouds.yaml\" is set. OpenStack authentication requires a set of " +
|
||||
"OS_* environment variables that are typically sourced into the current shell with an openrc file " +
|
||||
"or a cloud name for \"clouds.yaml\". " +
|
||||
|
|
@ -214,21 +215,21 @@ func (c *Creator) createOpenStack(ctx context.Context, cl terraformClient, opts
|
|||
|
||||
tfOutput, err := runTerraformCreate(ctx, cl, cloudprovider.OpenStack, vars, c.out, opts.TFLogLevel)
|
||||
if err != nil {
|
||||
return terraform.CreateOutput{}, err
|
||||
return terraform.ApplyOutput{}, err
|
||||
}
|
||||
|
||||
return tfOutput, nil
|
||||
}
|
||||
|
||||
func runTerraformCreate(ctx context.Context, cl terraformClient, provider cloudprovider.Provider, vars terraform.Variables, outWriter io.Writer, loglevel terraform.LogLevel) (output terraform.CreateOutput, retErr error) {
|
||||
func runTerraformCreate(ctx context.Context, cl terraformClient, provider cloudprovider.Provider, vars terraform.Variables, outWriter io.Writer, loglevel terraform.LogLevel) (output terraform.ApplyOutput, retErr error) {
|
||||
if err := cl.PrepareWorkspace(path.Join("terraform", strings.ToLower(provider.String())), vars); err != nil {
|
||||
return terraform.CreateOutput{}, err
|
||||
return terraform.ApplyOutput{}, err
|
||||
}
|
||||
|
||||
defer rollbackOnError(outWriter, &retErr, &rollbackerTerraform{client: cl}, loglevel)
|
||||
tfOutput, err := cl.CreateCluster(ctx, loglevel)
|
||||
if err != nil {
|
||||
return terraform.CreateOutput{}, err
|
||||
return terraform.ApplyOutput{}, err
|
||||
}
|
||||
|
||||
return tfOutput, nil
|
||||
|
|
@ -239,7 +240,7 @@ type qemuCreateOptions struct {
|
|||
CreateOptions
|
||||
}
|
||||
|
||||
func (c *Creator) createQEMU(ctx context.Context, cl terraformClient, lv libvirtRunner, opts qemuCreateOptions) (tfOutput terraform.CreateOutput, retErr error) {
|
||||
func (c *Creator) createQEMU(ctx context.Context, cl terraformClient, lv libvirtRunner, opts qemuCreateOptions) (tfOutput terraform.ApplyOutput, retErr error) {
|
||||
qemuRollbacker := &rollbackerQEMU{client: cl, libvirt: lv, createdWorkspace: false}
|
||||
defer rollbackOnError(c.out, &retErr, qemuRollbacker, opts.TFLogLevel)
|
||||
|
||||
|
|
@ -247,7 +248,7 @@ func (c *Creator) createQEMU(ctx context.Context, cl terraformClient, lv libvirt
|
|||
downloader := c.newRawDownloader()
|
||||
imagePath, err := downloader.Download(ctx, c.out, false, opts.source, opts.Config.Image)
|
||||
if err != nil {
|
||||
return terraform.CreateOutput{}, fmt.Errorf("download raw image: %w", err)
|
||||
return terraform.ApplyOutput{}, fmt.Errorf("download raw image: %w", err)
|
||||
}
|
||||
|
||||
libvirtURI := opts.Config.Provider.QEMU.LibvirtURI
|
||||
|
|
@ -257,7 +258,7 @@ func (c *Creator) createQEMU(ctx context.Context, cl terraformClient, lv libvirt
|
|||
// if no libvirt URI is specified, start a libvirt container
|
||||
case libvirtURI == "":
|
||||
if err := lv.Start(ctx, opts.Config.Name, opts.Config.Provider.QEMU.LibvirtContainerImage); err != nil {
|
||||
return terraform.CreateOutput{}, fmt.Errorf("start libvirt container: %w", err)
|
||||
return terraform.ApplyOutput{}, fmt.Errorf("start libvirt container: %w", err)
|
||||
}
|
||||
libvirtURI = libvirt.LibvirtTCPConnectURI
|
||||
|
||||
|
|
@ -273,11 +274,11 @@ func (c *Creator) createQEMU(ctx context.Context, cl terraformClient, lv libvirt
|
|||
case strings.HasPrefix(libvirtURI, "qemu+unix://"):
|
||||
unixURI, err := url.Parse(strings.TrimPrefix(libvirtURI, "qemu+unix://"))
|
||||
if err != nil {
|
||||
return terraform.CreateOutput{}, err
|
||||
return terraform.ApplyOutput{}, err
|
||||
}
|
||||
libvirtSocketPath = unixURI.Query().Get("socket")
|
||||
if libvirtSocketPath == "" {
|
||||
return terraform.CreateOutput{}, fmt.Errorf("socket path not specified in qemu+unix URI: %s", libvirtURI)
|
||||
return terraform.ApplyOutput{}, fmt.Errorf("socket path not specified in qemu+unix URI: %s", libvirtURI)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -293,7 +294,7 @@ func (c *Creator) createQEMU(ctx context.Context, cl terraformClient, lv libvirt
|
|||
}
|
||||
|
||||
if err := cl.PrepareWorkspace(path.Join("terraform", strings.ToLower(cloudprovider.QEMU.String())), vars); err != nil {
|
||||
return terraform.CreateOutput{}, fmt.Errorf("prepare workspace: %w", err)
|
||||
return terraform.ApplyOutput{}, fmt.Errorf("prepare workspace: %w", err)
|
||||
}
|
||||
|
||||
// Allow rollback of QEMU Terraform workspace from this point on
|
||||
|
|
@ -301,7 +302,7 @@ func (c *Creator) createQEMU(ctx context.Context, cl terraformClient, lv libvirt
|
|||
|
||||
tfOutput, err = cl.CreateCluster(ctx, opts.TFLogLevel)
|
||||
if err != nil {
|
||||
return terraform.CreateOutput{}, fmt.Errorf("create cluster: %w", err)
|
||||
return terraform.ApplyOutput{}, fmt.Errorf("create cluster: %w", err)
|
||||
}
|
||||
|
||||
return tfOutput, nil
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ func awsTerraformVars(conf *config.Config, imageRef string, controlPlaneCount, w
|
|||
IAMProfileWorkerNodes: conf.Provider.AWS.IAMProfileWorkerNodes,
|
||||
Debug: conf.IsDebugCluster(),
|
||||
EnableSNP: conf.GetAttestationConfig().GetVariant().Equal(variant.AWSSEVSNP{}),
|
||||
CustomEndpoint: conf.CustomEndpoint,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -98,6 +99,7 @@ func azureTerraformVars(conf *config.Config, imageRef string, controlPlaneCount,
|
|||
SecureBoot: conf.Provider.Azure.SecureBoot,
|
||||
UserAssignedIdentity: conf.Provider.Azure.UserAssignedIdentity,
|
||||
ResourceGroup: conf.Provider.Azure.ResourceGroup,
|
||||
CustomEndpoint: conf.CustomEndpoint,
|
||||
}
|
||||
|
||||
vars = normalizeAzureURIs(vars)
|
||||
|
|
@ -127,11 +129,12 @@ func gcpTerraformVars(conf *config.Config, imageRef string, controlPlaneCount, w
|
|||
DiskType: conf.Provider.GCP.StateDiskType,
|
||||
},
|
||||
},
|
||||
Project: conf.Provider.GCP.Project,
|
||||
Region: conf.Provider.GCP.Region,
|
||||
Zone: conf.Provider.GCP.Zone,
|
||||
ImageID: imageRef,
|
||||
Debug: conf.IsDebugCluster(),
|
||||
Project: conf.Provider.GCP.Project,
|
||||
Region: conf.Provider.GCP.Region,
|
||||
Zone: conf.Provider.GCP.Zone,
|
||||
ImageID: imageRef,
|
||||
Debug: conf.IsDebugCluster(),
|
||||
CustomEndpoint: conf.CustomEndpoint,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -165,6 +168,7 @@ func openStackTerraformVars(conf *config.Config, imageRef string, controlPlaneCo
|
|||
StateDiskSizeGB: conf.StateDiskSizeGB,
|
||||
},
|
||||
},
|
||||
CustomEndpoint: conf.CustomEndpoint,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@ import (
|
|||
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
|
||||
)
|
||||
|
||||
// File contains identifying information about a cluster.
|
||||
// File contains state information about a cluster.
|
||||
// This information is accessible after the creation
|
||||
// and can be used by further operations such as initialization and upgrades.
|
||||
type File struct {
|
||||
// ClusterID is the unique identifier of the cluster.
|
||||
ClusterID string `json:"clusterID,omitempty"`
|
||||
|
|
@ -22,6 +24,9 @@ type File struct {
|
|||
CloudProvider cloudprovider.Provider `json:"cloudprovider,omitempty"`
|
||||
// IP is the IP address the cluster can be reached at (often the load balancer).
|
||||
IP string `json:"ip,omitempty"`
|
||||
// APIServerCertSANs are subject alternative names (SAN) that are added to
|
||||
// the TLS certificate of each apiserver instance.
|
||||
APIServerCertSANs []string `json:"apiServerCertSANs,omitempty"`
|
||||
// InitSecret is the secret the first Bootstrapper uses to verify the user.
|
||||
InitSecret []byte `json:"initsecret,omitempty"`
|
||||
// AttestationURL is the URL of the attestation service.
|
||||
|
|
|
|||
|
|
@ -197,6 +197,7 @@ func (i *initCmd) initialize(cmd *cobra.Command, newDialer func(validator atls.V
|
|||
ConformanceMode: flags.conformance,
|
||||
InitSecret: idFile.InitSecret,
|
||||
ClusterName: clusterName,
|
||||
ApiserverCertSans: idFile.APIServerCertSANs,
|
||||
}
|
||||
i.log.Debugf("Sending initialization request")
|
||||
resp, err := i.initCall(cmd.Context(), newDialer(validator), idFile.IP, req)
|
||||
|
|
|
|||
|
|
@ -113,6 +113,20 @@ func (u *upgradeApplyCmd) upgradeApply(cmd *cobra.Command, fileHandler file.Hand
|
|||
if err := u.migrateTerraform(cmd, u.imageFetcher, conf, flags); err != nil {
|
||||
return fmt.Errorf("performing Terraform migrations: %w", err)
|
||||
}
|
||||
// reload idFile after terraform migration
|
||||
// it might have been updated by the migration
|
||||
if err := fileHandler.ReadJSON(constants.ClusterIDsFileName, &idFile); err != nil {
|
||||
return fmt.Errorf("reading updated cluster ID file: %w", err)
|
||||
}
|
||||
|
||||
// extend the clusterConfig cert SANs with any of the supported endpoints:
|
||||
// - (legacy) public IP
|
||||
// - fallback endpoint
|
||||
// - custom (user-provided) endpoint
|
||||
sans := append([]string{idFile.IP, conf.CustomEndpoint}, idFile.APIServerCertSANs...)
|
||||
if err := u.upgrader.ExtendClusterConfigCertSANs(cmd.Context(), sans); err != nil {
|
||||
return fmt.Errorf("extending cert SANs: %w", err)
|
||||
}
|
||||
|
||||
if conf.GetProvider() == cloudprovider.Azure || conf.GetProvider() == cloudprovider.GCP || conf.GetProvider() == cloudprovider.AWS {
|
||||
var upgradeErr *compatibility.InvalidUpgradeError
|
||||
|
|
@ -350,6 +364,7 @@ type cloudUpgrader interface {
|
|||
UpgradeNodeVersion(ctx context.Context, conf *config.Config, force bool) error
|
||||
UpgradeHelmServices(ctx context.Context, config *config.Config, timeout time.Duration, allowDestructive bool, force bool) error
|
||||
UpdateAttestationConfig(ctx context.Context, newConfig config.AttestationCfg) error
|
||||
ExtendClusterConfigCertSANs(ctx context.Context, alternativeNames []string) error
|
||||
GetClusterAttestationConfig(ctx context.Context, variant variant.Variant) (config.AttestationCfg, *corev1.ConfigMap, error)
|
||||
PlanTerraformMigrations(ctx context.Context, opts upgrade.TerraformUpgradeOptions) (bool, error)
|
||||
ApplyTerraformMigrations(ctx context.Context, opts upgrade.TerraformUpgradeOptions) error
|
||||
|
|
|
|||
|
|
@ -197,6 +197,10 @@ func (u stubUpgrader) ApplyTerraformMigrations(context.Context, upgrade.Terrafor
|
|||
return u.applyTerraformErr
|
||||
}
|
||||
|
||||
func (u stubUpgrader) ExtendClusterConfigCertSANs(_ context.Context, _ []string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddManualStateMigration is not used in this test.
|
||||
// TODO(AB#3248): remove this method together with the definition in the interfaces.
|
||||
func (u stubUpgrader) AddManualStateMigration(_ terraform.StateMigration) {
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ go_library(
|
|||
"@io_k8s_client_go//dynamic",
|
||||
"@io_k8s_client_go//kubernetes",
|
||||
"@io_k8s_client_go//tools/clientcmd",
|
||||
"@io_k8s_kubernetes//cmd/kubeadm/app/apis/kubeadm/v1beta3",
|
||||
"@io_k8s_sigs_yaml//:yaml",
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
|
@ -42,6 +43,8 @@ import (
|
|||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
kubeadmv1beta3 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta3"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
// UpgradeCmdKind is the kind of the upgrade command (check, apply).
|
||||
|
|
@ -343,6 +346,69 @@ func (u *Upgrader) GetClusterAttestationConfig(ctx context.Context, variant vari
|
|||
return existingAttestationConfig, existingConf, nil
|
||||
}
|
||||
|
||||
// ExtendClusterConfigCertSANs extends the ClusterConfig stored under "kube-system/kubeadm-config" with the given SANs.
|
||||
// Existing SANs are preserved.
|
||||
func (u *Upgrader) ExtendClusterConfigCertSANs(ctx context.Context, alternativeNames []string) error {
|
||||
clusterConfiguration, kubeadmConfig, err := u.GetClusterConfiguration(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("getting ClusterConfig: %w", err)
|
||||
}
|
||||
|
||||
existingSANs := make(map[string]struct{})
|
||||
for _, existingSAN := range clusterConfiguration.APIServer.CertSANs {
|
||||
existingSANs[existingSAN] = struct{}{}
|
||||
}
|
||||
|
||||
var missingSANs []string
|
||||
for _, san := range alternativeNames {
|
||||
if _, ok := existingSANs[san]; !ok {
|
||||
missingSANs = append(missingSANs, san)
|
||||
}
|
||||
}
|
||||
|
||||
if len(missingSANs) == 0 {
|
||||
return nil
|
||||
}
|
||||
u.log.Debugf("Extending the cluster's apiserver SAN field with the following SANs: %s\n", strings.Join(missingSANs, ", "))
|
||||
|
||||
clusterConfiguration.APIServer.CertSANs = append(clusterConfiguration.APIServer.CertSANs, missingSANs...)
|
||||
sort.Strings(clusterConfiguration.APIServer.CertSANs)
|
||||
|
||||
newConfigYAML, err := yaml.Marshal(clusterConfiguration)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshaling ClusterConfiguration: %w", err)
|
||||
}
|
||||
|
||||
kubeadmConfig.Data[constants.ClusterConfigurationKey] = string(newConfigYAML)
|
||||
u.log.Debugf("Triggering kubeadm config update now")
|
||||
if _, err = u.stableInterface.UpdateConfigMap(ctx, kubeadmConfig); err != nil {
|
||||
return fmt.Errorf("setting new kubeadm config: %w", err)
|
||||
}
|
||||
|
||||
fmt.Fprintln(u.outWriter, "Successfully extended the cluster's apiserver SAN field")
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetClusterConfiguration fetches the kubeadm-config configmap from the cluster, extracts the config
|
||||
// and returns both the full configmap and the ClusterConfiguration.
|
||||
func (u *Upgrader) GetClusterConfiguration(ctx context.Context) (kubeadmv1beta3.ClusterConfiguration, *corev1.ConfigMap, error) {
|
||||
existingConf, err := u.stableInterface.GetCurrentConfigMap(ctx, constants.KubeadmConfigMap)
|
||||
if err != nil {
|
||||
return kubeadmv1beta3.ClusterConfiguration{}, nil, fmt.Errorf("retrieving current kubeadm-config: %w", err)
|
||||
}
|
||||
clusterConf, ok := existingConf.Data[constants.ClusterConfigurationKey]
|
||||
if !ok {
|
||||
return kubeadmv1beta3.ClusterConfiguration{}, nil, errors.New("ClusterConfiguration missing from kubeadm-config")
|
||||
}
|
||||
|
||||
var existingClusterConfig kubeadmv1beta3.ClusterConfiguration
|
||||
if err := yaml.Unmarshal([]byte(clusterConf), &existingClusterConfig); err != nil {
|
||||
return kubeadmv1beta3.ClusterConfiguration{}, nil, fmt.Errorf("unmarshaling ClusterConfiguration: %w", err)
|
||||
}
|
||||
|
||||
return existingClusterConfig, existingConf, nil
|
||||
}
|
||||
|
||||
// applyComponentsCM applies the k8s components ConfigMap to the cluster.
|
||||
func (u *Upgrader) applyComponentsCM(ctx context.Context, components *corev1.ConfigMap) error {
|
||||
_, err := u.stableInterface.CreateConfigMap(ctx, components)
|
||||
|
|
|
|||
|
|
@ -103,53 +103,66 @@ func (c *Client) PrepareUpgradeWorkspace(path, oldWorkingDir, newWorkingDir, bac
|
|||
}
|
||||
|
||||
// CreateCluster creates a Constellation cluster using Terraform.
|
||||
func (c *Client) CreateCluster(ctx context.Context, logLevel LogLevel) (CreateOutput, error) {
|
||||
func (c *Client) CreateCluster(ctx context.Context, logLevel LogLevel) (ApplyOutput, error) {
|
||||
if err := c.setLogLevel(logLevel); err != nil {
|
||||
return CreateOutput{}, fmt.Errorf("set terraform log level %s: %w", logLevel.String(), err)
|
||||
return ApplyOutput{}, fmt.Errorf("set terraform log level %s: %w", logLevel.String(), err)
|
||||
}
|
||||
|
||||
if err := c.tf.Init(ctx); err != nil {
|
||||
return CreateOutput{}, fmt.Errorf("terraform init: %w", err)
|
||||
return ApplyOutput{}, fmt.Errorf("terraform init: %w", err)
|
||||
}
|
||||
|
||||
if err := c.applyManualStateMigrations(ctx); err != nil {
|
||||
return CreateOutput{}, fmt.Errorf("apply manual state migrations: %w", err)
|
||||
return ApplyOutput{}, fmt.Errorf("apply manual state migrations: %w", err)
|
||||
}
|
||||
|
||||
if err := c.tf.Apply(ctx); err != nil {
|
||||
return CreateOutput{}, fmt.Errorf("terraform apply: %w", err)
|
||||
return ApplyOutput{}, fmt.Errorf("terraform apply: %w", err)
|
||||
}
|
||||
|
||||
tfState, err := c.tf.Show(ctx)
|
||||
if err != nil {
|
||||
return CreateOutput{}, fmt.Errorf("terraform show: %w", err)
|
||||
return ApplyOutput{}, fmt.Errorf("terraform show: %w", err)
|
||||
}
|
||||
|
||||
ipOutput, ok := tfState.Values.Outputs["ip"]
|
||||
if !ok {
|
||||
return CreateOutput{}, errors.New("no IP output found")
|
||||
return ApplyOutput{}, errors.New("no IP output found")
|
||||
}
|
||||
ip, ok := ipOutput.Value.(string)
|
||||
if !ok {
|
||||
return CreateOutput{}, errors.New("invalid type in IP output: not a string")
|
||||
return ApplyOutput{}, errors.New("invalid type in IP output: not a string")
|
||||
}
|
||||
|
||||
apiServerCertSANsOutput, ok := tfState.Values.Outputs["api_server_cert_sans"]
|
||||
if !ok {
|
||||
return ApplyOutput{}, errors.New("no api_server_cert_sans output found")
|
||||
}
|
||||
apiServerCertSANsUntyped, ok := apiServerCertSANsOutput.Value.([]any)
|
||||
if !ok {
|
||||
return ApplyOutput{}, fmt.Errorf("invalid type in api_server_cert_sans output: %s is not a list of elements", apiServerCertSANsOutput.Type.FriendlyName())
|
||||
}
|
||||
apiServerCertSANs, err := toStringSlice(apiServerCertSANsUntyped)
|
||||
if err != nil {
|
||||
return ApplyOutput{}, fmt.Errorf("convert api_server_cert_sans output: %w", err)
|
||||
}
|
||||
|
||||
secretOutput, ok := tfState.Values.Outputs["initSecret"]
|
||||
if !ok {
|
||||
return CreateOutput{}, errors.New("no initSecret output found")
|
||||
return ApplyOutput{}, errors.New("no initSecret output found")
|
||||
}
|
||||
secret, ok := secretOutput.Value.(string)
|
||||
if !ok {
|
||||
return CreateOutput{}, errors.New("invalid type in initSecret output: not a string")
|
||||
return ApplyOutput{}, errors.New("invalid type in initSecret output: not a string")
|
||||
}
|
||||
|
||||
uidOutput, ok := tfState.Values.Outputs["uid"]
|
||||
if !ok {
|
||||
return CreateOutput{}, errors.New("no uid output found")
|
||||
return ApplyOutput{}, errors.New("no uid output found")
|
||||
}
|
||||
uid, ok := uidOutput.Value.(string)
|
||||
if !ok {
|
||||
return CreateOutput{}, errors.New("invalid type in uid output: not a string")
|
||||
return ApplyOutput{}, errors.New("invalid type in uid output: not a string")
|
||||
}
|
||||
|
||||
var attestationURL string
|
||||
|
|
@ -159,19 +172,22 @@ func (c *Client) CreateCluster(ctx context.Context, logLevel LogLevel) (CreateOu
|
|||
}
|
||||
}
|
||||
|
||||
return CreateOutput{
|
||||
IP: ip,
|
||||
Secret: secret,
|
||||
UID: uid,
|
||||
AttestationURL: attestationURL,
|
||||
return ApplyOutput{
|
||||
IP: ip,
|
||||
APIServerCertSANs: apiServerCertSANs,
|
||||
Secret: secret,
|
||||
UID: uid,
|
||||
AttestationURL: attestationURL,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateOutput contains the Terraform output values of a cluster creation.
|
||||
type CreateOutput struct {
|
||||
IP string
|
||||
Secret string
|
||||
UID string
|
||||
// ApplyOutput contains the Terraform output values of a cluster creation
|
||||
// or apply operation.
|
||||
type ApplyOutput struct {
|
||||
IP string
|
||||
APIServerCertSANs []string
|
||||
Secret string
|
||||
UID string
|
||||
// AttestationURL is the URL of the attestation provider.
|
||||
// It is only set if the cluster is created on Azure.
|
||||
AttestationURL string
|
||||
|
|
@ -447,6 +463,18 @@ type StateMigration struct {
|
|||
Hook func(ctx context.Context, tfClient TFMigrator) error
|
||||
}
|
||||
|
||||
func toStringSlice(in []any) ([]string, error) {
|
||||
out := make([]string, len(in))
|
||||
for i, v := range in {
|
||||
s, ok := v.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid type in list: item at index %v of list is not a string", i)
|
||||
}
|
||||
out[i] = s
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
type tfInterface interface {
|
||||
Apply(context.Context, ...tfexec.ApplyOption) error
|
||||
Destroy(context.Context, ...tfexec.DestroyOption) error
|
||||
|
|
|
|||
|
|
@ -46,8 +46,13 @@ locals {
|
|||
zones = distinct(sort([
|
||||
for node_group in var.node_groups : node_group.zone
|
||||
]))
|
||||
// wildcard_lb_dns_name is the DNS name of the load balancer with a wildcard for the name.
|
||||
// example: given "name-1234567890.region.elb.amazonaws.com" it will return "*.region.elb.amazonaws.com"
|
||||
wildcard_lb_dns_name = replace(aws_lb.front_end.dns_name, "/^[^.]*\\./", "*.")
|
||||
|
||||
tags = { constellation-uid = local.uid }
|
||||
tags = {
|
||||
constellation-uid = local.uid,
|
||||
}
|
||||
}
|
||||
|
||||
resource "random_id" "uid" {
|
||||
|
|
@ -83,7 +88,7 @@ resource "aws_eip" "lb" {
|
|||
# control-plane.
|
||||
for_each = toset([var.zone])
|
||||
domain = "vpc"
|
||||
tags = local.tags
|
||||
tags = merge(local.tags, { "constellation-ip-endpoint" = each.key == var.zone ? "legacy-primary-zone" : "additional-zone" })
|
||||
}
|
||||
|
||||
resource "aws_lb" "front_end" {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@ output "ip" {
|
|||
value = aws_eip.lb[var.zone].public_ip
|
||||
}
|
||||
|
||||
output "api_server_cert_sans" {
|
||||
value = sort(concat([aws_eip.lb[var.zone].public_ip, local.wildcard_lb_dns_name], var.custom_endpoint == "" ? [] : [var.custom_endpoint]))
|
||||
}
|
||||
|
||||
output "uid" {
|
||||
value = local.uid
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,3 +63,9 @@ variable "enable_snp" {
|
|||
default = true
|
||||
description = "Enable AMD SEV SNP. Setting this to true sets the cpu-option AmdSevSnp to enable."
|
||||
}
|
||||
|
||||
variable "custom_endpoint" {
|
||||
type = string
|
||||
default = ""
|
||||
description = "Custom endpoint to use for the Kubernetes apiserver. If not set, the default endpoint will be used."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,10 +20,12 @@ provider "azurerm" {
|
|||
}
|
||||
|
||||
locals {
|
||||
uid = random_id.uid.hex
|
||||
name = "${var.name}-${local.uid}"
|
||||
initSecretHash = random_password.initSecret.bcrypt_hash
|
||||
tags = { constellation-uid = local.uid }
|
||||
uid = random_id.uid.hex
|
||||
name = "${var.name}-${local.uid}"
|
||||
initSecretHash = random_password.initSecret.bcrypt_hash
|
||||
tags = {
|
||||
constellation-uid = local.uid,
|
||||
}
|
||||
ports_node_range = "30000-32767"
|
||||
ports_kubernetes = "6443"
|
||||
ports_bootstrapper = "9000"
|
||||
|
|
@ -33,6 +35,9 @@ locals {
|
|||
ports_debugd = "4000"
|
||||
cidr_vpc_subnet_nodes = "192.168.178.0/24"
|
||||
cidr_vpc_subnet_pods = "10.10.0.0/16"
|
||||
// wildcard_lb_dns_name is the DNS name of the load balancer with a wildcard for the name.
|
||||
// example: given "name-1234567890.location.cloudapp.azure.com" it will return "*.location.cloudapp.azure.com"
|
||||
wildcard_lb_dns_name = replace(azurerm_public_ip.loadbalancer_ip.fqdn, "/^[^.]*\\./", "*.")
|
||||
}
|
||||
|
||||
resource "random_id" "uid" {
|
||||
|
|
@ -72,6 +77,7 @@ resource "azurerm_application_insights" "insights" {
|
|||
|
||||
resource "azurerm_public_ip" "loadbalancer_ip" {
|
||||
name = "${local.name}-lb"
|
||||
domain_name_label = local.name
|
||||
resource_group_name = var.resource_group
|
||||
location = var.location
|
||||
allocation_method = "Static"
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@ output "ip" {
|
|||
value = azurerm_public_ip.loadbalancer_ip.ip_address
|
||||
}
|
||||
|
||||
output "api_server_cert_sans" {
|
||||
value = sort(concat([azurerm_public_ip.loadbalancer_ip.ip_address, local.wildcard_lb_dns_name], var.custom_endpoint == "" ? [] : [var.custom_endpoint]))
|
||||
}
|
||||
|
||||
output "uid" {
|
||||
value = local.uid
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,3 +61,9 @@ variable "user_assigned_identity" {
|
|||
type = string
|
||||
description = "The name of the user assigned identity to attache to the nodes of the cluster."
|
||||
}
|
||||
|
||||
variable "custom_endpoint" {
|
||||
type = string
|
||||
default = ""
|
||||
description = "Custom endpoint to use for the Kubernetes apiserver. If not set, the default endpoint will be used."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,10 +30,12 @@ provider "google-beta" {
|
|||
}
|
||||
|
||||
locals {
|
||||
uid = random_id.uid.hex
|
||||
name = "${var.name}-${local.uid}"
|
||||
initSecretHash = random_password.initSecret.bcrypt_hash
|
||||
labels = { constellation-uid = local.uid }
|
||||
uid = random_id.uid.hex
|
||||
name = "${var.name}-${local.uid}"
|
||||
initSecretHash = random_password.initSecret.bcrypt_hash
|
||||
labels = {
|
||||
constellation-uid = local.uid,
|
||||
}
|
||||
ports_node_range = "30000-32767"
|
||||
ports_kubernetes = "6443"
|
||||
ports_bootstrapper = "9000"
|
||||
|
|
@ -170,6 +172,7 @@ module "instance_group" {
|
|||
named_ports = each.value.role == "control-plane" ? local.control_plane_named_ports : []
|
||||
labels = local.labels
|
||||
init_secret_hash = local.initSecretHash
|
||||
custom_endpoint = var.custom_endpoint
|
||||
}
|
||||
|
||||
resource "google_compute_global_address" "loadbalancer_ip" {
|
||||
|
|
|
|||
|
|
@ -94,3 +94,8 @@ variable "zone" {
|
|||
type = string
|
||||
description = "Zone to deploy the instance group in."
|
||||
}
|
||||
|
||||
variable "custom_endpoint" {
|
||||
type = string
|
||||
description = "Custom endpoint to use for the Kubernetes apiserver. If not set, the default endpoint will be used."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,14 @@ output "ip" {
|
|||
value = google_compute_global_address.loadbalancer_ip.address
|
||||
}
|
||||
|
||||
output "api_server_cert_sans" {
|
||||
value = sort(concat([google_compute_global_address.loadbalancer_ip.address], var.custom_endpoint == "" ? [] : [var.custom_endpoint]))
|
||||
}
|
||||
|
||||
output "fallback_endpoint" {
|
||||
value = google_compute_global_address.loadbalancer_ip.address
|
||||
}
|
||||
|
||||
output "uid" {
|
||||
value = local.uid
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,3 +45,9 @@ variable "debug" {
|
|||
default = false
|
||||
description = "Enable debug mode. This opens up a debugd port that can be used to deploy a custom bootstrapper."
|
||||
}
|
||||
|
||||
variable "custom_endpoint" {
|
||||
type = string
|
||||
default = ""
|
||||
description = "Custom endpoint to use for the Kubernetes apiserver. If not set, the default endpoint will be used."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@ output "ip" {
|
|||
value = openstack_networking_floatingip_v2.public_ip.address
|
||||
}
|
||||
|
||||
output "api_server_cert_sans" {
|
||||
value = sort(concat([openstack_networking_floatingip_v2.public_ip.address], var.custom_endpoint == "" ? [] : [var.custom_endpoint]))
|
||||
}
|
||||
|
||||
output "uid" {
|
||||
value = local.uid
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,3 +67,9 @@ variable "debug" {
|
|||
default = false
|
||||
description = "Enable debug mode. This opens up a debugd port that can be used to deploy a custom bootstrapper."
|
||||
}
|
||||
|
||||
variable "custom_endpoint" {
|
||||
type = string
|
||||
default = ""
|
||||
description = "Custom endpoint to use for the Kubernetes apiserver. If not set, the default endpoint will be used."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@ output "ip" {
|
|||
value = module.node_group["control_plane_default"].instance_ips[0]
|
||||
}
|
||||
|
||||
output "api_server_cert_sans" {
|
||||
value = sort(concat([module.node_group["control_plane_default"].instance_ips[0]], var.custom_endpoint == "" ? [] : [var.custom_endpoint]))
|
||||
}
|
||||
|
||||
output "uid" {
|
||||
value = "qemu" // placeholder
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,3 +96,9 @@ variable "name" {
|
|||
default = "constellation"
|
||||
description = "name prefix of the cluster VMs"
|
||||
}
|
||||
|
||||
variable "custom_endpoint" {
|
||||
type = string
|
||||
default = ""
|
||||
description = "Custom endpoint to use for the Kubernetes apiserver. If not set, the default endpoint will be used."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -221,6 +221,9 @@ func TestCreateCluster(t *testing.T) {
|
|||
"uid": {
|
||||
Value: "12345abc",
|
||||
},
|
||||
"api_server_cert_sans": {
|
||||
Value: []any{"192.0.2.100"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -242,6 +245,9 @@ func TestCreateCluster(t *testing.T) {
|
|||
"attestationURL": {
|
||||
Value: "https://12345.neu.attest.azure.net",
|
||||
},
|
||||
"api_server_cert_sans": {
|
||||
Value: []any{"192.0.2.100"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,29 +30,6 @@ type ClusterVariables interface {
|
|||
GetCreateMAA() bool
|
||||
}
|
||||
|
||||
// CommonVariables is user configuration for creating a cluster with Terraform.
|
||||
type CommonVariables struct {
|
||||
// Name of the cluster.
|
||||
Name string
|
||||
// CountControlPlanes is the number of control-plane nodes to create.
|
||||
CountControlPlanes int
|
||||
// CountWorkers is the number of worker nodes to create.
|
||||
CountWorkers int
|
||||
// StateDiskSizeGB is the size of the state disk to allocate to each node, in GB.
|
||||
StateDiskSizeGB int
|
||||
}
|
||||
|
||||
// String returns a string representation of the variables, formatted as Terraform variables.
|
||||
func (v *CommonVariables) String() string {
|
||||
b := &strings.Builder{}
|
||||
writeLinef(b, "name = %q", v.Name)
|
||||
writeLinef(b, "control_plane_count = %d", v.CountControlPlanes)
|
||||
writeLinef(b, "worker_count = %d", v.CountWorkers)
|
||||
writeLinef(b, "state_disk_size = %d", v.StateDiskSizeGB)
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// AWSClusterVariables is user configuration for creating a cluster with Terraform on AWS.
|
||||
type AWSClusterVariables struct {
|
||||
// Name of the cluster.
|
||||
|
|
@ -73,6 +50,8 @@ type AWSClusterVariables struct {
|
|||
EnableSNP bool `hcl:"enable_snp" cty:"enable_snp"`
|
||||
// NodeGroups is a map of node groups to create.
|
||||
NodeGroups map[string]AWSNodeGroup `hcl:"node_groups" cty:"node_groups"`
|
||||
// CustomEndpoint is the (optional) custom dns hostname for the kubernetes api server.
|
||||
CustomEndpoint string `hcl:"custom_endpoint" cty:"custom_endpoint"`
|
||||
}
|
||||
|
||||
// GetCreateMAA gets the CreateMAA variable.
|
||||
|
|
@ -138,6 +117,8 @@ type GCPClusterVariables struct {
|
|||
Debug bool `hcl:"debug" cty:"debug"`
|
||||
// NodeGroups is a map of node groups to create.
|
||||
NodeGroups map[string]GCPNodeGroup `hcl:"node_groups" cty:"node_groups"`
|
||||
// CustomEndpoint is the (optional) custom dns hostname for the kubernetes api server.
|
||||
CustomEndpoint string `hcl:"custom_endpoint" cty:"custom_endpoint"`
|
||||
}
|
||||
|
||||
// GetCreateMAA gets the CreateMAA variable.
|
||||
|
|
@ -212,6 +193,8 @@ type AzureClusterVariables struct {
|
|||
SecureBoot *bool `hcl:"secure_boot" cty:"secure_boot"`
|
||||
// NodeGroups is a map of node groups to create.
|
||||
NodeGroups map[string]AzureNodeGroup `hcl:"node_groups" cty:"node_groups"`
|
||||
// CustomEndpoint is the (optional) custom dns hostname for the kubernetes api server.
|
||||
CustomEndpoint string `hcl:"custom_endpoint" cty:"custom_endpoint"`
|
||||
}
|
||||
|
||||
// GetCreateMAA gets the CreateMAA variable.
|
||||
|
|
@ -287,6 +270,8 @@ type OpenStackClusterVariables struct {
|
|||
OpenstackPassword string `hcl:"openstack_password" cty:"openstack_password"`
|
||||
// Debug is true if debug mode is enabled.
|
||||
Debug bool `hcl:"debug" cty:"debug"`
|
||||
// CustomEndpoint is the (optional) custom dns hostname for the kubernetes api server.
|
||||
CustomEndpoint string `hcl:"custom_endpoint" cty:"custom_endpoint"`
|
||||
}
|
||||
|
||||
// GetCreateMAA gets the CreateMAA variable.
|
||||
|
|
@ -354,6 +339,8 @@ type QEMUVariables struct {
|
|||
InitrdPath *string `hcl:"constellation_initrd" cty:"constellation_initrd"`
|
||||
// KernelCmdline is the kernel command line.
|
||||
KernelCmdline *string `hcl:"constellation_cmdline" cty:"constellation_cmdline"`
|
||||
// CustomEndpoint is the (optional) custom dns hostname for the kubernetes api server.
|
||||
CustomEndpoint string `hcl:"custom_endpoint" cty:"custom_endpoint"`
|
||||
}
|
||||
|
||||
// GetCreateMAA gets the CreateMAA variable.
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ func TestAWSClusterVariables(t *testing.T) {
|
|||
IAMProfileWorkerNodes: "arn:aws:iam::123456789012:instance-profile/cluster-name-worker",
|
||||
Debug: true,
|
||||
EnableSNP: true,
|
||||
CustomEndpoint: "example.com",
|
||||
}
|
||||
|
||||
// test that the variables are correctly rendered
|
||||
|
|
@ -72,6 +73,7 @@ node_groups = {
|
|||
zone = "eu-central-1c"
|
||||
}
|
||||
}
|
||||
custom_endpoint = "example.com"
|
||||
`
|
||||
got := vars.String()
|
||||
assert.Equal(t, want, got)
|
||||
|
|
@ -117,6 +119,7 @@ func TestGCPClusterVariables(t *testing.T) {
|
|||
DiskType: "pd-ssd",
|
||||
},
|
||||
},
|
||||
CustomEndpoint: "example.com",
|
||||
}
|
||||
|
||||
// test that the variables are correctly rendered
|
||||
|
|
@ -144,6 +147,7 @@ node_groups = {
|
|||
zone = "eu-central-1b"
|
||||
}
|
||||
}
|
||||
custom_endpoint = "example.com"
|
||||
`
|
||||
got := vars.String()
|
||||
assert.Equal(t, want, got)
|
||||
|
|
@ -186,6 +190,7 @@ func TestAzureClusterVariables(t *testing.T) {
|
|||
CreateMAA: to.Ptr(true),
|
||||
Debug: to.Ptr(true),
|
||||
Location: "eu-central-1",
|
||||
CustomEndpoint: "example.com",
|
||||
}
|
||||
|
||||
// test that the variables are correctly rendered
|
||||
|
|
@ -207,6 +212,7 @@ node_groups = {
|
|||
zones = null
|
||||
}
|
||||
}
|
||||
custom_endpoint = "example.com"
|
||||
`
|
||||
got := vars.String()
|
||||
assert.Equal(t, want, got)
|
||||
|
|
@ -249,6 +255,7 @@ func TestOpenStackClusterVariables(t *testing.T) {
|
|||
StateDiskSizeGB: 30,
|
||||
},
|
||||
},
|
||||
CustomEndpoint: "example.com",
|
||||
}
|
||||
|
||||
// test that the variables are correctly rendered
|
||||
|
|
@ -271,6 +278,7 @@ openstack_user_domain_name = "my-user-domain"
|
|||
openstack_username = "my-username"
|
||||
openstack_password = "my-password"
|
||||
debug = true
|
||||
custom_endpoint = "example.com"
|
||||
`
|
||||
got := vars.String()
|
||||
assert.Equal(t, want, got)
|
||||
|
|
@ -299,6 +307,7 @@ func TestQEMUClusterVariables(t *testing.T) {
|
|||
NVRAM: "production",
|
||||
InitrdPath: toPtr("/var/lib/libvirt/images/cluster-name-initrd"),
|
||||
KernelCmdline: toPtr("console=ttyS0,115200n8"),
|
||||
CustomEndpoint: "example.com",
|
||||
}
|
||||
|
||||
// test that the variables are correctly rendered
|
||||
|
|
@ -323,6 +332,7 @@ metadata_libvirt_uri = "qemu:///system"
|
|||
nvram = "/usr/share/OVMF/constellation_vars.production.fd"
|
||||
constellation_initrd = "/var/lib/libvirt/images/cluster-name-initrd"
|
||||
constellation_cmdline = "console=ttyS0,115200n8"
|
||||
custom_endpoint = "example.com"
|
||||
`
|
||||
got := vars.String()
|
||||
assert.Equal(t, want, got)
|
||||
|
|
|
|||
|
|
@ -156,11 +156,12 @@ func (u *TerraformUpgrader) ApplyTerraformMigrations(ctx context.Context, opts T
|
|||
}
|
||||
|
||||
outputFileContents := clusterid.File{
|
||||
CloudProvider: opts.CSP,
|
||||
InitSecret: []byte(tfOutput.Secret),
|
||||
IP: tfOutput.IP,
|
||||
UID: tfOutput.UID,
|
||||
AttestationURL: tfOutput.AttestationURL,
|
||||
CloudProvider: opts.CSP,
|
||||
InitSecret: []byte(tfOutput.Secret),
|
||||
IP: tfOutput.IP,
|
||||
APIServerCertSANs: tfOutput.APIServerCertSANs,
|
||||
UID: tfOutput.UID,
|
||||
AttestationURL: tfOutput.AttestationURL,
|
||||
}
|
||||
|
||||
if err := u.fileHandler.RemoveAll(constants.TerraformWorkingDir); err != nil {
|
||||
|
|
@ -201,7 +202,7 @@ type tfClient interface {
|
|||
PrepareUpgradeWorkspace(path, oldWorkingDir, newWorkingDir, upgradeID string, vars terraform.Variables) error
|
||||
ShowPlan(ctx context.Context, logLevel terraform.LogLevel, planFilePath string, output io.Writer) error
|
||||
Plan(ctx context.Context, logLevel terraform.LogLevel, planFile string) (bool, error)
|
||||
CreateCluster(ctx context.Context, logLevel terraform.LogLevel) (terraform.CreateOutput, error)
|
||||
CreateCluster(ctx context.Context, logLevel terraform.LogLevel) (terraform.ApplyOutput, error)
|
||||
}
|
||||
|
||||
// policyPatcher interacts with the CSP (currently only applies for Azure) to update the attestation policy.
|
||||
|
|
|
|||
|
|
@ -371,8 +371,8 @@ func (u *stubTerraformClient) Plan(context.Context, terraform.LogLevel, string)
|
|||
return u.hasDiff, u.planErr
|
||||
}
|
||||
|
||||
func (u *stubTerraformClient) CreateCluster(context.Context, terraform.LogLevel) (terraform.CreateOutput, error) {
|
||||
return terraform.CreateOutput{}, u.CreateClusterErr
|
||||
func (u *stubTerraformClient) CreateCluster(context.Context, terraform.LogLevel) (terraform.ApplyOutput, error) {
|
||||
return terraform.ApplyOutput{}, u.CreateClusterErr
|
||||
}
|
||||
|
||||
type stubPolicyPatcher struct {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue