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:
Malte Poll 2023-07-21 16:43:51 +02:00 committed by GitHub
parent 3324a4eba2
commit 8da6a23aa5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
64 changed files with 724 additions and 301 deletions

View file

@ -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