terraform: align infrastructure module attributes (#2703)

* all vars have snail_case

* make iam schema consistent

* infrastructure schema

* terraform: update AWS infrastructure module

* fix ci

* terraform: update AWS infrastructure module

* terraform: update AWS IAM module

* terraform: update Azure Infrastructure module inputs

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* terraform: update Azure IAM module

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* terraform: update GCP infrastructure module

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* terraform: update GCP IAM module

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* terraform: update OpenStack Infrastructure module

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* terraform: update QEMU Infrastructure module

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* terraform-module: fix input name

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* terraform: tidy

* cli: ignore whitespace in Terraform variable tests

* terraform-module: fix AWS output names

* terraform-module: fix output references

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>

* terraform: rename `api_server_cert_sans`

* Update terraform/infrastructure/aws/modules/public_private_subnet/variables.tf

Co-authored-by: Daniel Weiße <66256922+daniel-weisse@users.noreply.github.com>

* fix self-managed

* terraform: revert AWS modules output file renaming

* terraform: remove duplicate varable declaration

* terraform: rename Azure location field

* ci: adjust output name in self-managed e2e test

* e2e: continuously print output in upgrade test

* e2e: write to output variables

* cli: migrate IAM variable names

* cli: make `location` field optional

---------

Signed-off-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>
Co-authored-by: Moritz Sanft <58110325+msanft@users.noreply.github.com>
Co-authored-by: Daniel Weiße <66256922+daniel-weisse@users.noreply.github.com>
This commit is contained in:
Adrian Stobbe 2023-12-15 10:36:58 +01:00 committed by GitHub
parent 6f6f28b8cc
commit 9667dfff58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
76 changed files with 745 additions and 767 deletions

View File

@ -50,11 +50,10 @@ runs:
}
}" >> terraform.tfvars
if [[ "${{ inputs.cloudProvider }}" == 'aws' ]]; then
echo "iam_instance_profile_control_plane = \"$(yq '.provider.aws.iamProfileControlPlane' constellation-conf.yaml)\"" >> terraform.tfvars
echo "iam_instance_profile_worker_nodes = \"$(yq '.provider.aws.iamProfileWorkerNodes' constellation-conf.yaml)\"" >> terraform.tfvars
echo "iam_instance_profile_name_control_plane = \"$(yq '.provider.aws.iamProfileControlPlane' constellation-conf.yaml)\"" >> terraform.tfvars
echo "iam_instance_profile_name_worker_nodes = \"$(yq '.provider.aws.iamProfileWorkerNodes' constellation-conf.yaml)\"" >> terraform.tfvars
echo "region = \"$(yq '.provider.aws.region' constellation-conf.yaml)\"" >> terraform.tfvars
echo "zone = \"$(yq '.provider.aws.zone' constellation-conf.yaml)\"" >> terraform.tfvars
echo "ami = \"${{ steps.get_image.outputs.image_ref }}\"" >> terraform.tfvars
echo "enable_snp = $(yq '.attestation | has("awsSEVSNP")' constellation-conf.yaml)" >> terraform.tfvars
elif [[ "${{ inputs.cloudProvider }}" == 'azure' ]]; then
echo "location = \"$(yq '.provider.azure.location' constellation-conf.yaml)\"" >> terraform.tfvars
@ -84,17 +83,17 @@ runs:
working-directory: ${{ github.workspace }}/e2e-infra
if: inputs.cloudProvider == 'azure'
run: |
constellation maa-patch $(terraform output attestationURL | jq -r)
constellation maa-patch $(terraform output attestation_url | jq -r)
- name: Write outputs to state file
shell: bash
working-directory: ${{ github.workspace }}/e2e-infra
run: |
yq eval '.version ="v1"' --inplace ${{ github.workspace }}/constellation-state.yaml
yq eval ".infrastructure.initSecret =\"$(terraform output initSecret | jq -r | tr -d '\n' | hexdump -ve '/1 "%02x"' && echo '')\"" --inplace ${{ github.workspace }}/constellation-state.yaml
yq eval ".infrastructure.initSecret =\"$(terraform output init_secret | jq -r | tr -d '\n' | hexdump -ve '/1 "%02x"' && echo '')\"" --inplace ${{ github.workspace }}/constellation-state.yaml
yq eval ".infrastructure.clusterEndpoint =\"$(terraform output out_of_cluster_endpoint | jq -r)\"" --inplace ${{ github.workspace }}/constellation-state.yaml
yq eval ".infrastructure.inClusterEndpoint =\"$(terraform output in_cluster_endpoint | jq -r)\"" --inplace ${{ github.workspace }}/constellation-state.yaml
yq eval ".infrastructure.ipCidrNode =\"$(terraform output ip_cidr_nodes | jq -r)\"" --inplace ${{ github.workspace }}/constellation-state.yaml
yq eval ".infrastructure.ipCidrNode =\"$(terraform output ip_cidr_node | jq -r)\"" --inplace ${{ github.workspace }}/constellation-state.yaml
yq eval ".infrastructure.uid =\"$(terraform output uid | jq -r)\"" --inplace ${{ github.workspace }}/constellation-state.yaml
yq eval ".infrastructure.name =\"$(terraform output name | jq -r)\"" --inplace ${{ github.workspace }}/constellation-state.yaml
yq eval ".infrastructure.apiServerCertSANs =$(terraform output -json api_server_cert_sans)" --inplace ${{ github.workspace }}/constellation-state.yaml
@ -104,8 +103,8 @@ runs:
yq eval ".infrastructure.azure.networkSecurityGroupName =\"$(terraform output network_security_group_name | jq -r)\"" --inplace ${{ github.workspace }}/constellation-state.yaml
yq eval ".infrastructure.azure.loadBalancerName =\"$(terraform output loadbalancer_name | jq -r)\"" --inplace ${{ github.workspace }}/constellation-state.yaml
yq eval ".infrastructure.azure.userAssignedIdentity =\"$(terraform output user_assigned_identity_client_id | jq -r)\"" --inplace ${{ github.workspace }}/constellation-state.yaml
yq eval ".infrastructure.azure.attestationURL =\"$(terraform output attestationURL | jq -r)\"" --inplace ${{ github.workspace }}/constellation-state.yaml
yq eval ".infrastructure.azure.attestationURL =\"$(terraform output attestation_url | jq -r)\"" --inplace ${{ github.workspace }}/constellation-state.yaml
elif [[ "${{ inputs.cloudProvider }}" == 'gcp' ]]; then
yq eval ".infrastructure.gcp.projectID =\"$(terraform output project | jq -r)\"" --inplace ${{ github.workspace }}/constellation-state.yaml
yq eval ".infrastructure.gcp.ipCidrPod =\"$(terraform output ip_cidr_pods | jq -r)\"" --inplace ${{ github.workspace }}/constellation-state.yaml
yq eval ".infrastructure.gcp.ipCidrPod =\"$(terraform output ip_cidr_pod | jq -r)\"" --inplace ${{ github.workspace }}/constellation-state.yaml
fi

View File

@ -31,7 +31,7 @@ func NewIAMDestroyer() *IAMDestroyer {
return &IAMDestroyer{newTerraformClient: newTerraformIAMClient}
}
// GetTfStateServiceAccountKey returns the sa_key output from the terraform state.
// GetTfStateServiceAccountKey returns the service_account_key output from the terraform state.
func (d *IAMDestroyer) GetTfStateServiceAccountKey(ctx context.Context, tfWorkspace string) (gcpshared.ServiceAccountKey, error) {
client, err := d.newTerraformClient(ctx, tfWorkspace)
if err != nil {
@ -95,7 +95,7 @@ type GCPIAMConfig struct {
// AzureIAMConfig holds the necessary values for Azure IAM configuration.
type AzureIAMConfig struct {
Region string
Location string
ServicePrincipal string
ResourceGroup string
}
@ -167,7 +167,7 @@ func (c *IAMCreator) createAzure(ctx context.Context, cl tfIAMClient, opts *IAMC
defer rollbackOnError(c.out, &retErr, &rollbackerTerraform{client: cl}, opts.TFLogLevel)
vars := terraform.AzureIAMVariables{
Region: opts.Azure.Region,
Location: opts.Azure.Location,
ResourceGroup: opts.Azure.ResourceGroup,
ServicePrincipal: opts.Azure.ServicePrincipal,
}

View File

@ -40,7 +40,7 @@ func TestIAMCreator(t *testing.T) {
}
validAzureIAMConfig := AzureIAMConfig{
Region: "westus",
Location: "westus",
ServicePrincipal: "constell-test",
ResourceGroup: "constell-test",
}

View File

@ -64,6 +64,14 @@ func TerraformIAMUpgradeVars(conf *config.Config, fileHandler file.Handler) (ter
if err := terraform.VariablesFromBytes(oldVarBytes, &oldVars); err != nil {
return nil, fmt.Errorf("parsing existing IAM workspace: %w", err)
}
// Migration from the "region" to the "location" field na.
// TODO(msanft): Remove after v2.14.0 is released.
if oldVars.Region != nil && *oldVars.Region != "" && oldVars.Location == "" {
oldVars.Location = *oldVars.Region
oldVars.Region = nil
}
vars = azureTerraformIAMVars(conf, oldVars)
case cloudprovider.GCP:
var oldVars terraform.GCPIAMVariables
@ -96,7 +104,7 @@ func awsTerraformVars(conf *config.Config, imageRef string) *terraform.AWSCluste
NodeGroups: nodeGroups,
Region: conf.Provider.AWS.Region,
Zone: conf.Provider.AWS.Zone,
AMIImageID: imageRef,
ImageID: imageRef,
IAMProfileControlPlane: conf.Provider.AWS.IAMProfileControlPlane,
IAMProfileWorkerNodes: conf.Provider.AWS.IAMProfileWorkerNodes,
Debug: conf.IsDebugCluster(),
@ -188,7 +196,7 @@ func azureTerraformVars(conf *config.Config, imageRef string) (*terraform.AzureC
func azureTerraformIAMVars(conf *config.Config, oldVars terraform.AzureIAMVariables) *terraform.AzureIAMVariables {
return &terraform.AzureIAMVariables{
Region: conf.Provider.Azure.Location,
Location: conf.Provider.Azure.Location,
ServicePrincipal: oldVars.ServicePrincipal,
ResourceGroup: conf.Provider.Azure.ResourceGroup,
}

View File

@ -75,7 +75,7 @@ func TestIAMCreateAWS(t *testing.T) {
CloudProvider: cloudprovider.AWS,
AWSOutput: cloudcmd.AWSIAMOutput{
ControlPlaneInstanceProfile: "test_control_plane_instance_profile",
WorkerNodeInstanceProfile: "test_worker_nodes_instance_profile",
WorkerNodeInstanceProfile: "test_worker_nodes_instance_profile_name",
},
}

View File

@ -75,7 +75,7 @@ type azureIAMCreator struct {
func (c *azureIAMCreator) getIAMConfigOptions() *cloudcmd.IAMConfigOptions {
return &cloudcmd.IAMConfigOptions{
Azure: cloudcmd.AzureIAMConfig{
Region: c.flags.region,
Location: c.flags.region,
ResourceGroup: c.flags.resourceGroup,
ServicePrincipal: c.flags.servicePrincipal,
},

View File

@ -37,7 +37,7 @@ func TestIamUpgradeApply(t *testing.T) {
require.NoError(t, fh.Write(
filepath.Join(constants.TerraformIAMWorkingDir, "terraform.tfvars"),
[]byte(
"region = \"foo\"\n"+
"location = \"foo\"\n"+
"resource_group_name = \"bar\"\n"+
"service_principal_name = \"baz\"\n",
),

View File

@ -95,13 +95,13 @@ func (c *Client) ShowIAM(ctx context.Context, provider cloudprovider.Provider) (
switch provider {
case cloudprovider.GCP:
saKeyOutputRaw, ok := tfState.Values.Outputs["sa_key"]
saKeyOutputRaw, ok := tfState.Values.Outputs["service_account_key"]
if !ok {
return IAMOutput{}, errors.New("no service account key output found")
return IAMOutput{}, errors.New("no service_account_key output found")
}
saKeyOutput, ok := saKeyOutputRaw.Value.(string)
if !ok {
return IAMOutput{}, errors.New("invalid type in service account key output: not a string")
return IAMOutput{}, errors.New("invalid type in service_account_key output: not a string")
}
return IAMOutput{
GCP: GCPIAMOutput{
@ -111,27 +111,27 @@ func (c *Client) ShowIAM(ctx context.Context, provider cloudprovider.Provider) (
case cloudprovider.Azure:
subscriptionIDRaw, ok := tfState.Values.Outputs["subscription_id"]
if !ok {
return IAMOutput{}, errors.New("no subscription id output found")
return IAMOutput{}, errors.New("no subscription_id output found")
}
subscriptionIDOutput, ok := subscriptionIDRaw.Value.(string)
if !ok {
return IAMOutput{}, errors.New("invalid type in subscription id output: not a string")
return IAMOutput{}, errors.New("invalid type in subscription_id output: not a string")
}
tenantIDRaw, ok := tfState.Values.Outputs["tenant_id"]
if !ok {
return IAMOutput{}, errors.New("no tenant id output found")
return IAMOutput{}, errors.New("no tenant_id output found")
}
tenantIDOutput, ok := tenantIDRaw.Value.(string)
if !ok {
return IAMOutput{}, errors.New("invalid type in tenant id output: not a string")
return IAMOutput{}, errors.New("invalid type in tenant_id output: not a string")
}
uamiIDRaw, ok := tfState.Values.Outputs["uami_id"]
if !ok {
return IAMOutput{}, errors.New("no UAMI id output found")
return IAMOutput{}, errors.New("no uami_id output found")
}
uamiIDOutput, ok := uamiIDRaw.Value.(string)
if !ok {
return IAMOutput{}, errors.New("invalid type in UAMI id output: not a string")
return IAMOutput{}, errors.New("invalid type in uami_id output: not a string")
}
return IAMOutput{
Azure: AzureIAMOutput{
@ -141,21 +141,21 @@ func (c *Client) ShowIAM(ctx context.Context, provider cloudprovider.Provider) (
},
}, nil
case cloudprovider.AWS:
controlPlaneProfileRaw, ok := tfState.Values.Outputs["control_plane_instance_profile"]
controlPlaneProfileRaw, ok := tfState.Values.Outputs["iam_instance_profile_name_control_plane"]
if !ok {
return IAMOutput{}, errors.New("no control plane instance profile output found")
return IAMOutput{}, errors.New("no iam_instance_profile_name_control_plane output found")
}
controlPlaneProfileOutput, ok := controlPlaneProfileRaw.Value.(string)
if !ok {
return IAMOutput{}, errors.New("invalid type in control plane instance profile output: not a string")
return IAMOutput{}, errors.New("invalid type in iam_instance_profile_name_control_plane output: not a string")
}
workerNodeProfileRaw, ok := tfState.Values.Outputs["worker_nodes_instance_profile"]
workerNodeProfileRaw, ok := tfState.Values.Outputs["iam_instance_profile_name_worker_nodes"]
if !ok {
return IAMOutput{}, errors.New("no worker node instance profile output found")
return IAMOutput{}, errors.New("no iam_instance_profile_name_worker_nodes output found")
}
workerNodeProfileOutput, ok := workerNodeProfileRaw.Value.(string)
if !ok {
return IAMOutput{}, errors.New("invalid type in worker node instance profile output: not a string")
return IAMOutput{}, errors.New("invalid type in iam_instance_profile_name_worker_nodes output: not a string")
}
return IAMOutput{
AWS: AWSIAMOutput{
@ -209,13 +209,13 @@ func (c *Client) ShowInfrastructure(ctx context.Context, provider cloudprovider.
return state.Infrastructure{}, fmt.Errorf("convert api_server_cert_sans output: %w", err)
}
secretOutput, ok := tfState.Values.Outputs["initSecret"]
secretOutput, ok := tfState.Values.Outputs["init_secret"]
if !ok {
return state.Infrastructure{}, errors.New("no initSecret output found")
return state.Infrastructure{}, errors.New("no init_secret output found")
}
secret, ok := secretOutput.Value.(string)
if !ok {
return state.Infrastructure{}, errors.New("invalid type in initSecret output: not a string")
return state.Infrastructure{}, errors.New("invalid type in init_Secret output: not a string")
}
uidOutput, ok := tfState.Values.Outputs["uid"]
@ -236,13 +236,13 @@ func (c *Client) ShowInfrastructure(ctx context.Context, provider cloudprovider.
return state.Infrastructure{}, errors.New("invalid type in name output: not a string")
}
cidrNodesOutput, ok := tfState.Values.Outputs["ip_cidr_nodes"]
cidrNodesOutput, ok := tfState.Values.Outputs["ip_cidr_node"]
if !ok {
return state.Infrastructure{}, errors.New("no ip_cidr_nodes output found")
return state.Infrastructure{}, errors.New("no ip_cidr_node output found")
}
cidrNodes, ok := cidrNodesOutput.Value.(string)
if !ok {
return state.Infrastructure{}, errors.New("invalid type in ip_cidr_nodes output: not a string")
return state.Infrastructure{}, errors.New("invalid type in ip_cidr_node output: not a string")
}
res := state.Infrastructure{
@ -266,13 +266,13 @@ func (c *Client) ShowInfrastructure(ctx context.Context, provider cloudprovider.
return state.Infrastructure{}, errors.New("invalid type in project output: not a string")
}
cidrPodsOutput, ok := tfState.Values.Outputs["ip_cidr_pods"]
cidrPodsOutput, ok := tfState.Values.Outputs["ip_cidr_pod"]
if !ok {
return state.Infrastructure{}, errors.New("no ip_cidr_pods output found")
return state.Infrastructure{}, errors.New("no ip_cidr_pod output found")
}
cidrPods, ok := cidrPodsOutput.Value.(string)
if !ok {
return state.Infrastructure{}, errors.New("invalid type in ip_cidr_pods output: not a string")
return state.Infrastructure{}, errors.New("invalid type in ip_cidr_pod output: not a string")
}
res.GCP = &state.GCP{
@ -280,13 +280,13 @@ func (c *Client) ShowInfrastructure(ctx context.Context, provider cloudprovider.
IPCidrPod: cidrPods,
}
case cloudprovider.Azure:
attestationURLOutput, ok := tfState.Values.Outputs["attestationURL"]
attestationURLOutput, ok := tfState.Values.Outputs["attestation_url"]
if !ok {
return state.Infrastructure{}, errors.New("no attestationURL output found")
return state.Infrastructure{}, errors.New("no attestation_url output found")
}
attestationURL, ok := attestationURLOutput.Value.(string)
if !ok {
return state.Infrastructure{}, errors.New("invalid type in attestationURL output: not a string")
return state.Infrastructure{}, errors.New("invalid type in attestation_url output: not a string")
}
azureUAMIOutput, ok := tfState.Values.Outputs["user_assigned_identity_client_id"]

View File

@ -122,7 +122,7 @@ func TestPrepareIAM(t *testing.T) {
ServiceAccountID: "const-test-case",
}
azureVars := &AzureIAMVariables{
Region: "westus",
Location: "westus",
ResourceGroup: "constell-test-rg",
}
awsVars := &AWSIAMVariables{
@ -218,7 +218,7 @@ func TestCreateCluster(t *testing.T) {
"in_cluster_endpoint": {
Value: "192.0.2.101",
},
"initSecret": {
"init_secret": {
Value: "initSecret",
},
"uid": {
@ -230,7 +230,7 @@ func TestCreateCluster(t *testing.T) {
"name": {
Value: "constell-12345abc",
},
"ip_cidr_nodes": {
"ip_cidr_node": {
Value: "192.0.2.103/32",
},
},
@ -248,13 +248,13 @@ func TestCreateCluster(t *testing.T) {
"in_cluster_endpoint": {
Value: "192.0.2.101",
},
"initSecret": {
"init_secret": {
Value: "initSecret",
},
"uid": {
Value: "12345abc",
},
"attestationURL": {
"attestation_url": {
Value: "https://12345.neu.attest.azure.net",
},
"api_server_cert_sans": {
@ -278,7 +278,7 @@ func TestCreateCluster(t *testing.T) {
"name": {
Value: "constell-12345abc",
},
"ip_cidr_nodes": {
"ip_cidr_node": {
Value: "192.0.2.103/32",
},
},
@ -460,7 +460,7 @@ func TestCreateCluster(t *testing.T) {
tf: &stubTerraform{
showState: &tfjson.State{
Values: &tfjson.StateValues{
Outputs: map[string]*tfjson.StateOutput{"attestationURL": {Value: 42}},
Outputs: map[string]*tfjson.StateOutput{"attestation_url": {Value: 42}},
},
},
},
@ -506,7 +506,7 @@ func TestCreateIAM(t *testing.T) {
workingState := tfjson.State{
Values: &tfjson.StateValues{
Outputs: map[string]*tfjson.StateOutput{
"sa_key": {
"service_account_key": {
Value: "12345678_abcdefg",
},
"subscription_id": {
@ -524,11 +524,11 @@ func TestCreateIAM(t *testing.T) {
"application_client_secret_value": {
Value: "test_application_client_secret_value",
},
"control_plane_instance_profile": {
Value: "test_control_plane_instance_profile",
"iam_instance_profile_name_control_plane": {
Value: "test_iam_instance_profile_name_control_plane",
},
"worker_nodes_instance_profile": {
Value: "test_worker_nodes_instance_profile",
"iam_instance_profile_name_worker_nodes": {
Value: "test_iam_instance_profile_name_worker_nodes",
},
},
},
@ -542,7 +542,7 @@ func TestCreateIAM(t *testing.T) {
ServiceAccountID: "const-test-case",
}
azureVars := &AzureIAMVariables{
Region: "westus",
Location: "westus",
ResourceGroup: "constell-test-rg",
}
awsVars := &AWSIAMVariables{
@ -607,7 +607,7 @@ func TestCreateIAM(t *testing.T) {
fs: afero.NewMemMapFs(),
wantErr: true,
},
"gcp no sa_key": {
"gcp no service_account_key": {
pathBase: path.Join(constants.TerraformEmbeddedDir, "iam"),
provider: cloudprovider.GCP,
vars: gcpVars,
@ -621,14 +621,14 @@ func TestCreateIAM(t *testing.T) {
fs: afero.NewMemMapFs(),
wantErr: true,
},
"gcp sa_key has wrong type": {
"gcp service_account_key has wrong type": {
pathBase: path.Join(constants.TerraformEmbeddedDir, "iam"),
provider: cloudprovider.GCP,
vars: gcpVars,
tf: &stubTerraform{
showState: &tfjson.State{
Values: &tfjson.StateValues{
Outputs: map[string]*tfjson.StateOutput{"sa_key": {Value: 42}},
Outputs: map[string]*tfjson.StateOutput{"service_account_key": {Value: 42}},
},
},
},
@ -706,8 +706,8 @@ func TestCreateIAM(t *testing.T) {
tf: &stubTerraform{showState: newTestState()},
fs: afero.NewMemMapFs(),
want: IAMOutput{AWS: AWSIAMOutput{
ControlPlaneInstanceProfile: "test_control_plane_instance_profile",
WorkerNodeInstanceProfile: "test_worker_nodes_instance_profile",
ControlPlaneInstanceProfile: "test_iam_instance_profile_name_control_plane",
WorkerNodeInstanceProfile: "test_iam_instance_profile_name_worker_nodes",
}},
},
"aws init fails": {
@ -734,7 +734,7 @@ func TestCreateIAM(t *testing.T) {
fs: afero.NewMemMapFs(),
wantErr: true,
},
"aws no control_plane_instance_profile": {
"aws no iam_instance_profile_name_control_plane": {
pathBase: path.Join(constants.TerraformEmbeddedDir, "iam"),
provider: cloudprovider.AWS,
vars: awsVars,
@ -748,14 +748,14 @@ func TestCreateIAM(t *testing.T) {
fs: afero.NewMemMapFs(),
wantErr: true,
},
"azure control_plane_instance_profile has wrong type": {
"azure iam_instance_profile_name_control_plane has wrong type": {
pathBase: path.Join(constants.TerraformEmbeddedDir, "iam"),
provider: cloudprovider.AWS,
vars: awsVars,
tf: &stubTerraform{
showState: &tfjson.State{
Values: &tfjson.StateValues{
Outputs: map[string]*tfjson.StateOutput{"control_plane_instance_profile": {Value: 42}},
Outputs: map[string]*tfjson.StateOutput{"iam_instance_profile_name_control_plane": {Value: 42}},
},
},
},
@ -1129,7 +1129,7 @@ func TestShowIAM(t *testing.T) {
"GCP success": {
tf: &stubTerraform{
showState: getTfjsonState(map[string]any{
"sa_key": "key",
"service_account_key": "key",
}),
},
csp: cloudprovider.GCP,
@ -1137,7 +1137,7 @@ func TestShowIAM(t *testing.T) {
"GCP wrong data type": {
tf: &stubTerraform{
showState: getTfjsonState(map[string]any{
"sa_key": map[string]any{},
"service_account_key": map[string]any{},
}),
},
csp: cloudprovider.GCP,
@ -1226,45 +1226,45 @@ func TestShowIAM(t *testing.T) {
"AWS success": {
tf: &stubTerraform{
showState: getTfjsonState(map[string]any{
"control_plane_instance_profile": "profile",
"worker_nodes_instance_profile": "profile",
"iam_instance_profile_name_control_plane": "profile",
"iam_instance_profile_name_worker_nodes": "profile",
}),
},
csp: cloudprovider.AWS,
},
"AWS wrong data type control_plane_instance_profile": {
"AWS wrong data type iam_instance_profile_name_control_plane": {
tf: &stubTerraform{
showState: getTfjsonState(map[string]any{
"control_plane_instance_profile": map[string]any{},
"worker_nodes_instance_profile": "profile",
"iam_instance_profile_name_control_plane": map[string]any{},
"iam_instance_profile_name_worker_nodes": "profile",
}),
},
csp: cloudprovider.AWS,
wantErr: true,
},
"AWS wrong data type worker_nodes_instance_profile": {
"AWS wrong data type iam_instance_profile_name_worker_nodes": {
tf: &stubTerraform{
showState: getTfjsonState(map[string]any{
"control_plane_instance_profile": "profile",
"worker_nodes_instance_profile": map[string]any{},
"iam_instance_profile_name_control_plane": "profile",
"iam_instance_profile_name_worker_nodes": map[string]any{},
}),
},
csp: cloudprovider.AWS,
wantErr: true,
},
"AWS missing control_plane_instance_profile": {
"AWS missing iam_instance_profile_name_control_plane": {
tf: &stubTerraform{
showState: getTfjsonState(map[string]any{
"worker_nodes_instance_profile": "profile",
"iam_instance_profile_name_worker_nodes": "profile",
}),
},
csp: cloudprovider.AWS,
wantErr: true,
},
"AWS missing worker_nodes_instance_profile": {
"AWS missing iam_instance_profile_name_worker_nodes": {
tf: &stubTerraform{
showState: getTfjsonState(map[string]any{
"control_plane_instance_profile": "profile",
"iam_instance_profile_name_control_plane": "profile",
}),
},
csp: cloudprovider.AWS,

View File

@ -38,8 +38,9 @@ func VariablesFromBytes[T any](b []byte, vars *T) error {
return fmt.Errorf("parsing variables: %w", err)
}
if err := gohcl.DecodeBody(file.Body, nil, vars); err != nil {
return fmt.Errorf("decoding variables: %w", err)
diags := gohcl.DecodeBody(file.Body, nil, vars)
if diags.HasErrors() {
return fmt.Errorf("decoding variables: %w", diags)
}
return nil
}
@ -52,12 +53,12 @@ type AWSClusterVariables struct {
Region string `hcl:"region" cty:"region"`
// Zone is the AWS zone to use in the given region.
Zone string `hcl:"zone" cty:"zone"`
// AMIImageID is the ID of the AMI image to use.
AMIImageID string `hcl:"ami" cty:"ami"`
// IAMGroupControlPlane is the IAM group to use for the control-plane nodes.
IAMProfileControlPlane string `hcl:"iam_instance_profile_control_plane" cty:"iam_instance_profile_control_plane"`
// IAMGroupWorkerNodes is the IAM group to use for the worker nodes.
IAMProfileWorkerNodes string `hcl:"iam_instance_profile_worker_nodes" cty:"iam_instance_profile_worker_nodes"`
// ImageID is the ID of the AMI to use.
ImageID string `hcl:"image_id" cty:"image_id"`
// IAMProfileControlPlane is the IAM group to use for the control-plane nodes.
IAMProfileControlPlane string `hcl:"iam_instance_profile_name_control_plane" cty:"iam_instance_profile_name_control_plane"`
// IAMProfileWorkerNodes is the IAM group to use for the worker nodes.
IAMProfileWorkerNodes string `hcl:"iam_instance_profile_name_worker_nodes" cty:"iam_instance_profile_name_worker_nodes"`
// Debug is true if debug mode is enabled.
Debug bool `hcl:"debug" cty:"debug"`
// EnableSNP controls enablement of the EC2 cpu-option "AmdSevSnp".
@ -244,8 +245,11 @@ type AzureNodeGroup struct {
// AzureIAMVariables is user configuration for creating the IAM configuration with Terraform on Microsoft Azure.
type AzureIAMVariables struct {
// Region is the Azure region to use. (e.g. westus)
Region string `hcl:"region" cty:"region"`
// Region is the Azure location to use. (e.g. westus).
// THIS FIELD IS DEPRECATED AND ONLY KEPT FOR MIGRATION PURPOSES. DO NOT USE.
Region *string `hcl:"region" cty:"region"` // TODO(msanft): Remove this field once v2.14.0 is released.
// Location is the Azure location to use. (e.g. westus)
Location string `hcl:"location,optional" cty:"location"` // TODO(msanft): Make this required once v2.14.0 is released.
// ServicePrincipal is the name of the service principal to use.
ServicePrincipal string `hcl:"service_principal_name" cty:"service_principal_name"`
// ResourceGroup is the name of the resource group to use.
@ -282,7 +286,7 @@ type OpenStackClusterVariables struct {
// FloatingIPPoolID is the ID of the OpenStack floating IP pool to use for public IPs.
FloatingIPPoolID string `hcl:"floating_ip_pool_id" cty:"floating_ip_pool_id"`
// ImageURL is the URL of the OpenStack image to use.
ImageURL string `hcl:"image_url" cty:"image_url"`
ImageURL string `hcl:"image_id" cty:"image_id"`
// DirectDownload decides whether to download the image directly from the URL to OpenStack or to upload it from the local machine.
DirectDownload bool `hcl:"direct_download" cty:"direct_download"`
// OpenstackUserDomainName is the OpenStack user domain name to use.
@ -347,7 +351,7 @@ type QEMUVariables struct {
// Can be either "uefi" or "direct-linux-boot".
BootMode string `hcl:"constellation_boot_mode" cty:"constellation_boot_mode"`
// ImagePath is the path to the image to use for the nodes.
ImagePath string `hcl:"constellation_os_image" cty:"constellation_os_image"`
ImagePath string `hcl:"image_id" cty:"image_id"`
// ImageFormat is the format of the image from ImagePath.
ImageFormat string `hcl:"image_format" cty:"image_format"`
// MetadataAPIImage is the container image to use for the metadata API.

View File

@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
package terraform
import (
"strings"
"testing"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
@ -38,7 +39,7 @@ func TestAWSClusterVariables(t *testing.T) {
},
Region: "eu-central-1",
Zone: "eu-central-1a",
AMIImageID: "ami-0123456789abcdef",
ImageID: "ami-0123456789abcdef",
IAMProfileControlPlane: "arn:aws:iam::123456789012:instance-profile/cluster-name-controlplane",
IAMProfileWorkerNodes: "arn:aws:iam::123456789012:instance-profile/cluster-name-worker",
Debug: true,
@ -47,14 +48,14 @@ func TestAWSClusterVariables(t *testing.T) {
}
// test that the variables are correctly rendered
want := `name = "cluster-name"
region = "eu-central-1"
zone = "eu-central-1a"
ami = "ami-0123456789abcdef"
iam_instance_profile_control_plane = "arn:aws:iam::123456789012:instance-profile/cluster-name-controlplane"
iam_instance_profile_worker_nodes = "arn:aws:iam::123456789012:instance-profile/cluster-name-worker"
debug = true
enable_snp = true
want := `name = "cluster-name"
region = "eu-central-1"
zone = "eu-central-1a"
image_id = "ami-0123456789abcdef"
iam_instance_profile_name_control_plane = "arn:aws:iam::123456789012:instance-profile/cluster-name-controlplane"
iam_instance_profile_name_worker_nodes = "arn:aws:iam::123456789012:instance-profile/cluster-name-worker"
debug = true
enable_snp = true
node_groups = {
control_plane_default = {
disk_size = 30
@ -77,7 +78,7 @@ custom_endpoint = "example.com"
internal_load_balancer = false
`
got := vars.String()
assert.Equal(t, want, got)
assert.Equal(t, strings.Fields(want), strings.Fields(got)) // to ignore whitespace differences
}
func TestAWSIAMVariables(t *testing.T) {
@ -91,7 +92,7 @@ func TestAWSIAMVariables(t *testing.T) {
name_prefix = "my-prefix"
`
got := vars.String()
assert.Equal(t, want, got)
assert.Equal(t, strings.Fields(want), strings.Fields(got)) // to ignore whitespace differences
}
func TestGCPClusterVariables(t *testing.T) {
@ -152,7 +153,7 @@ custom_endpoint = "example.com"
internal_load_balancer = false
`
got := vars.String()
assert.Equal(t, want, got)
assert.Equal(t, strings.Fields(want), strings.Fields(got)) // to ignore whitespace differences
}
func TestGCPIAMVariables(t *testing.T) {
@ -170,7 +171,7 @@ zone = "eu-central-1a"
service_account_id = "my-service-account"
`
got := vars.String()
assert.Equal(t, want, got)
assert.Equal(t, strings.Fields(want), strings.Fields(got)) // to ignore whitespace differences
}
func TestAzureClusterVariables(t *testing.T) {
@ -230,23 +231,23 @@ marketplace_image = {
}
`
got := vars.String()
assert.Equal(t, want, got)
assert.Equal(t, strings.Fields(want), strings.Fields(got)) // to ignore whitespace differences
}
func TestAzureIAMVariables(t *testing.T) {
vars := AzureIAMVariables{
Region: "eu-central-1",
Location: "eu-central-1",
ServicePrincipal: "my-service-principal",
ResourceGroup: "my-resource-group",
}
// test that the variables are correctly rendered
want := `region = "eu-central-1"
want := `location = "eu-central-1"
service_principal_name = "my-service-principal"
resource_group_name = "my-resource-group"
`
got := vars.String()
assert.Equal(t, want, got)
assert.Equal(t, strings.Fields(want), strings.Fields(got)) // to ignore whitespace differences
}
func TestOpenStackClusterVariables(t *testing.T) {
@ -287,7 +288,7 @@ node_groups = {
}
cloud = "my-cloud"
floating_ip_pool_id = "fip-pool-0123456789abcdef"
image_url = "https://example.com/image.raw"
image_id = "https://example.com/image.raw"
direct_download = true
openstack_user_domain_name = "my-user-domain"
openstack_username = "my-username"
@ -297,7 +298,7 @@ custom_endpoint = "example.com"
internal_load_balancer = false
`
got := vars.String()
assert.Equal(t, want, got)
assert.Equal(t, strings.Fields(want), strings.Fields(got)) // to ignore whitespace differences
}
func TestQEMUClusterVariables(t *testing.T) {
@ -341,7 +342,7 @@ machine = "q35"
libvirt_uri = "qemu:///system"
libvirt_socket_path = "/var/run/libvirt/libvirt-sock"
constellation_boot_mode = "uefi"
constellation_os_image = "/var/lib/libvirt/images/cluster-name.qcow2"
image_id = "/var/lib/libvirt/images/cluster-name.qcow2"
image_format = "raw"
metadata_api_image = "example.com/metadata-api:latest"
metadata_libvirt_uri = "qemu:///system"
@ -352,7 +353,7 @@ custom_endpoint = "example.com"
internal_load_balancer = false
`
got := vars.String()
assert.Equal(t, want, got)
assert.Equal(t, strings.Fields(want), strings.Fields(got)) // to ignore whitespace differences
}
func TestVariablesFromBytes(t *testing.T) {
@ -367,7 +368,7 @@ func TestVariablesFromBytes(t *testing.T) {
assert.Equal(awsVars, loadedAWSVars)
azureVars := AzureIAMVariables{
Region: "test",
Location: "test",
}
var loadedAzureVars AzureIAMVariables
err = VariablesFromBytes([]byte(azureVars.String()), &loadedAzureVars)

View File

@ -273,13 +273,13 @@ The following describes the configuration fields and how you obtain the required
* **iamProfileControlPlane**: The name of an IAM instance profile attached to all control-plane nodes.
You can create the resource with [Terraform](https://www.terraform.io/). For that, use the [provided Terraform script](https://github.com/edgelesssys/constellation/tree/release/v2.2/hack/terraform/aws/iam) to generate the necessary profile. The profile name will be provided as Terraform output value: `control_plane_instance_profile`.
You can create the resource with [Terraform](https://www.terraform.io/). For that, use the [provided Terraform script](https://github.com/edgelesssys/constellation/tree/release/v2.2/hack/terraform/aws/iam) to generate the necessary profile. The profile name will be provided as Terraform output value: `control_plane_instance_profile_name`.
Alternatively, you can create the AWS profile with a tool of your choice. Use the JSON policy in [main.tf](https://github.com/edgelesssys/constellation/tree/release/v2.2/hack/terraform/aws/iam/main.tf) in the resource `aws_iam_policy.control_plane_policy`.
* **iamProfileWorkerNodes**: The name of an IAM instance profile attached to all worker nodes.
You can create the resource with [Terraform](https://www.terraform.io/). For that, use the [provided Terraform script](https://github.com/edgelesssys/constellation/tree/release/v2.2/hack/terraform/aws/iam) to generate the necessary profile. The profile name will be provided as Terraform output value: `worker_nodes_instance_profile`.
You can create the resource with [Terraform](https://www.terraform.io/). For that, use the [provided Terraform script](https://github.com/edgelesssys/constellation/tree/release/v2.2/hack/terraform/aws/iam) to generate the necessary profile. The profile name will be provided as Terraform output value: `worker_nodes_instance_profile_name`.
Alternatively, you can create the AWS profile with a tool of your choice. Use the JSON policy in [main.tf](https://github.com/edgelesssys/constellation/tree/release/v2.2/hack/terraform/aws/iam/main.tf) in the resource `aws_iam_policy.worker_node_policy`.

View File

@ -57,7 +57,7 @@ management tooling of your choice. You need to keep the essential functionality
:::info
On Azure, if the enforcement policy is set to `MAAFallback` in `constellation-config.yaml`, a manual update to the MAA provider's policy is necessary.
You can apply the update with the following command after creating the infrastructure, with `<URL>` being the URL of the MAA provider (i.e., `$(terraform output attestationURL | jq -r)`, when using the minimal Terraform configuration).
You can apply the update with the following command after creating the infrastructure, with `<URL>` being the URL of the MAA provider (i.e., `$(terraform output attestation_url | jq -r)`, when using the minimal Terraform configuration).
```bash
constellation maa-patch <URL>

View File

@ -9,6 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
package upgrade
import (
"bufio"
"context"
"errors"
"flag"
@ -454,6 +455,9 @@ type versionContainer struct {
// runCommandWithSeparateOutputs runs the given command while separating buffers for
// stdout and stderr.
func runCommandWithSeparateOutputs(cmd *exec.Cmd) (stdout, stderr []byte, err error) {
stdout = []byte{}
stderr = []byte{}
stdoutIn, err := cmd.StdoutPipe()
if err != nil {
err = fmt.Errorf("create stdout pipe: %w", err)
@ -471,21 +475,26 @@ func runCommandWithSeparateOutputs(cmd *exec.Cmd) (stdout, stderr []byte, err er
return
}
stdout, err = io.ReadAll(stdoutIn)
if err != nil {
err = fmt.Errorf("start command: %w", err)
return
continuouslyPrintOutput := func(r io.Reader, prefix string) {
scanner := bufio.NewScanner(r)
for scanner.Scan() {
output := scanner.Text()
fmt.Printf("%s: %s\n", prefix, output)
switch prefix {
case "stdout":
stdout = append(stdout, output...)
case "stderr":
stderr = append(stderr, output...)
}
}
}
stderr, err = io.ReadAll(stderrIn)
if err != nil {
err = fmt.Errorf("start command: %w", err)
return
}
go continuouslyPrintOutput(stdoutIn, "stdout")
go continuouslyPrintOutput(stderrIn, "stderr")
if err = cmd.Wait(); err != nil {
err = fmt.Errorf("wait for command to finish: %w", err)
}
return
return stdout, stderr, err
}

View File

@ -976,7 +976,7 @@ func TestConfigVersionCompatibility(t *testing.T) {
AWS: &AWSConfig{
Region: "us-east-2",
Zone: "us-east-2a",
IAMProfileControlPlane: "control_plane_instance_profile",
IAMProfileControlPlane: "control_plane_instance_profile_name",
IAMProfileWorkerNodes: "node_instance_profile",
},
},

View File

@ -57,9 +57,9 @@ resource "constellation_cluster" "aws_example" {
### Optional
- `api_server_cert_sans` (List of String) List of Subject Alternative Names (SANs) for the API server certificate. Usually, this will be the out-of-cluster endpoint and the in-cluster endpoint, if existing.
- `azure` (Attributes) Azure-specific configuration. (see [below for nested schema](#nestedatt--azure))
- `constellation_microservice_version` (String) The version of Constellation's microservices used within the cluster. When not set, the provider default version is used.
- `extra_api_server_cert_sans` (List of String) List of additional Subject Alternative Names (SANs) for the API server certificate.
- `extra_microservices` (Attributes) Extra microservice settings. (see [below for nested schema](#nestedatt--extra_microservices))
- `gcp` (Attributes) GCP-specific configuration. (see [below for nested schema](#nestedatt--gcp))
- `in_cluster_endpoint` (String) The endpoint of the cluster. When not set, the out-of-cluster endpoint is used.

View File

@ -60,25 +60,25 @@ type ClusterResource struct {
// ClusterResourceModel describes the resource data model.
type ClusterResourceModel struct {
Name types.String `tfsdk:"name"`
CSP types.String `tfsdk:"csp"`
UID types.String `tfsdk:"uid"`
ImageVersion types.String `tfsdk:"image_version"`
ImageReference types.String `tfsdk:"image_reference"`
KubernetesVersion types.String `tfsdk:"kubernetes_version"`
MicroserviceVersion types.String `tfsdk:"constellation_microservice_version"`
OutOfClusterEndpoint types.String `tfsdk:"out_of_cluster_endpoint"`
InClusterEndpoint types.String `tfsdk:"in_cluster_endpoint"`
ExtraMicroservices types.Object `tfsdk:"extra_microservices"`
ExtraAPIServerCertSANs types.List `tfsdk:"extra_api_server_cert_sans"`
NetworkConfig types.Object `tfsdk:"network_config"`
MasterSecret types.String `tfsdk:"master_secret"`
MasterSecretSalt types.String `tfsdk:"master_secret_salt"`
MeasurementSalt types.String `tfsdk:"measurement_salt"`
InitSecret types.String `tfsdk:"init_secret"`
Attestation types.Object `tfsdk:"attestation"`
GCP types.Object `tfsdk:"gcp"`
Azure types.Object `tfsdk:"azure"`
Name types.String `tfsdk:"name"`
CSP types.String `tfsdk:"csp"`
UID types.String `tfsdk:"uid"`
ImageVersion types.String `tfsdk:"image_version"`
ImageReference types.String `tfsdk:"image_reference"`
KubernetesVersion types.String `tfsdk:"kubernetes_version"`
MicroserviceVersion types.String `tfsdk:"constellation_microservice_version"`
OutOfClusterEndpoint types.String `tfsdk:"out_of_cluster_endpoint"`
InClusterEndpoint types.String `tfsdk:"in_cluster_endpoint"`
ExtraMicroservices types.Object `tfsdk:"extra_microservices"`
APIServerCertSANs types.List `tfsdk:"api_server_cert_sans"`
NetworkConfig types.Object `tfsdk:"network_config"`
MasterSecret types.String `tfsdk:"master_secret"`
MasterSecretSalt types.String `tfsdk:"master_secret_salt"`
MeasurementSalt types.String `tfsdk:"measurement_salt"`
InitSecret types.String `tfsdk:"init_secret"`
Attestation types.Object `tfsdk:"attestation"`
GCP types.Object `tfsdk:"gcp"`
Azure types.Object `tfsdk:"azure"`
OwnerID types.String `tfsdk:"owner_id"`
ClusterID types.String `tfsdk:"cluster_id"`
@ -178,11 +178,12 @@ func (r *ClusterResource) Schema(_ context.Context, _ resource.SchemaRequest, re
},
},
},
"extra_api_server_cert_sans": schema.ListAttribute{
MarkdownDescription: "List of additional Subject Alternative Names (SANs) for the API server certificate.",
Description: "List of additional Subject Alternative Names (SANs) for the API server certificate.",
ElementType: types.StringType,
Optional: true,
"api_server_cert_sans": schema.ListAttribute{
MarkdownDescription: "List of Subject Alternative Names (SANs) for the API server certificate. Usually, this will be" +
" the out-of-cluster endpoint and the in-cluster endpoint, if existing.",
Description: "List of Subject Alternative Names (SANs) for the API server certificate.",
ElementType: types.StringType,
Optional: true,
},
"network_config": schema.SingleNestedAttribute{
MarkdownDescription: "Configuration for the cluster's network.",
@ -503,8 +504,8 @@ func (r *ClusterResource) apply(ctx context.Context, data *ClusterResourceModel,
}
// parse API server certificate SANs
apiServerCertSANs := make([]string, 0, len(data.ExtraAPIServerCertSANs.Elements()))
for _, san := range data.ExtraAPIServerCertSANs.Elements() {
apiServerCertSANs := make([]string, 0, len(data.APIServerCertSANs.Elements()))
for _, san := range data.APIServerCertSANs.Elements() {
apiServerCertSANs = append(apiServerCertSANs, san.String())
}

View File

@ -9,13 +9,10 @@ go_library(
"infrastructure/aws/modules/instance_group/main.tf",
"infrastructure/aws/modules/instance_group/variables.tf",
"infrastructure/aws/modules/jump_host/main.tf",
"infrastructure/aws/modules/jump_host/output.tf",
"infrastructure/aws/modules/jump_host/variables.tf",
"infrastructure/aws/modules/load_balancer_target/main.tf",
"infrastructure/aws/modules/load_balancer_target/output.tf",
"infrastructure/aws/modules/load_balancer_target/variables.tf",
"infrastructure/aws/modules/public_private_subnet/main.tf",
"infrastructure/aws/modules/public_private_subnet/output.tf",
"infrastructure/aws/modules/public_private_subnet/variables.tf",
"infrastructure/aws/outputs.tf",
"infrastructure/aws/variables.tf",
@ -46,17 +43,14 @@ go_library(
"infrastructure/gcp/outputs.tf",
"infrastructure/gcp/variables.tf",
"infrastructure/iam/aws/.terraform.lock.hcl",
"infrastructure/iam/aws/README.md",
"infrastructure/iam/aws/main.tf",
"infrastructure/iam/aws/outputs.tf",
"infrastructure/iam/aws/variables.tf",
"infrastructure/iam/azure/.terraform.lock.hcl",
"infrastructure/iam/azure/README.md",
"infrastructure/iam/azure/main.tf",
"infrastructure/iam/azure/outputs.tf",
"infrastructure/iam/azure/variables.tf",
"infrastructure/iam/gcp/.terraform.lock.hcl",
"infrastructure/iam/gcp/README.md",
"infrastructure/iam/gcp/main.tf",
"infrastructure/iam/gcp/outputs.tf",
"infrastructure/iam/gcp/variables.tf",
@ -78,6 +72,9 @@ go_library(
"infrastructure/qemu/modules/instance_group/variables.tf",
"infrastructure/qemu/outputs.tf",
"infrastructure/qemu/variables.tf",
"infrastructure/aws/modules/jump_host/output.tf",
"infrastructure/aws/modules/load_balancer_target/output.tf",
"infrastructure/aws/modules/public_private_subnet/output.tf",
],
importpath = "github.com/edgelesssys/constellation/v2/terraform",
visibility = ["//visibility:public"],

View File

@ -11,7 +11,6 @@ terraform {
}
}
# Configure the AWS Provider
provider "aws" {
region = var.region
}
@ -19,7 +18,7 @@ provider "aws" {
locals {
uid = random_id.uid.hex
name = "${var.name}-${local.uid}"
initSecretHash = random_password.initSecret.bcrypt_hash
init_secret_hash = random_password.init_secret.bcrypt_hash
cidr_vpc_subnet_nodes = "192.168.176.0/20"
ports_node_range = "30000-32767"
load_balancer_ports = flatten([
@ -38,8 +37,8 @@ locals {
worker : []
}
iam_instance_profile = {
control-plane : var.iam_instance_profile_control_plane
worker : var.iam_instance_profile_worker_nodes
control-plane : var.iam_instance_profile_name_control_plane
worker : var.iam_instance_profile_name_worker_nodes
}
# zones are all availability zones that are used by the node groups
zones = distinct(sort([
@ -61,7 +60,7 @@ resource "random_id" "uid" {
byte_length = 4
}
resource "random_password" "initSecret" {
resource "random_password" "init_secret" {
length = 32
special = true
override_special = "_%@"
@ -172,7 +171,7 @@ resource "aws_cloudwatch_log_group" "log_group" {
module "load_balancer_targets" {
for_each = { for port in local.load_balancer_ports : port.name => port }
source = "./modules/load_balancer_target"
name = "${local.name}-${each.value.name}"
base_name = "${local.name}-${each.value.name}"
port = each.value.port
healthcheck_protocol = each.value.health_check
healthcheck_path = each.value.name == "kubernetes" ? "/readyz" : ""
@ -191,7 +190,7 @@ module "instance_group" {
uid = local.uid
instance_type = each.value.instance_type
initial_count = each.value.initial_count
image_id = var.ami
image_id = var.image_id
state_disk_type = each.value.disk_type
state_disk_size = each.value.disk_size
target_group_arns = local.target_group_arns[each.value.role]
@ -205,7 +204,7 @@ module "instance_group" {
{ constellation-role = each.value.role },
{ constellation-node-group = each.key },
{ constellation-uid = local.uid },
{ constellation-init-secret-hash = local.initSecretHash },
{ constellation-init-secret-hash = local.init_secret_hash },
{ "kubernetes.io/cluster/${local.name}" = "owned" }
)
}
@ -217,42 +216,6 @@ module "jump_host" {
subnet_id = module.public_private_subnet.public_subnet_id[var.zone]
lb_internal_ip = aws_lb.front_end.dns_name
ports = [for port in local.load_balancer_ports : port.port]
iam_instance_profile = var.iam_instance_profile_worker_nodes
security_groups = [aws_security_group.security_group.id]
}
# TODO(31u3r): Remove once 2.12 is released
moved {
from = module.load_balancer_target_konnectivity
to = module.load_balancer_targets["konnectivity"]
}
moved {
from = module.load_balancer_target_verify
to = module.load_balancer_targets["verify"]
}
moved {
from = module.load_balancer_target_recovery
to = module.load_balancer_targets["recovery"]
}
moved {
from = module.load_balancer_target_join
to = module.load_balancer_targets["join"]
}
moved {
from = module.load_balancer_target_debugd[0]
to = module.load_balancer_targets["debugd"]
}
moved {
from = module.load_balancer_target_kubernetes
to = module.load_balancer_targets["kubernetes"]
}
moved {
from = module.load_balancer_target_bootstrapper
to = module.load_balancer_targets["bootstrapper"]
iam_instance_profile = var.iam_instance_profile_name_worker_nodes
}

View File

@ -10,7 +10,7 @@ variable "node_group_name" {
variable "role" {
type = string
description = "The role of the instance group."
description = "Role of the instance group."
validation {
condition = contains(["control-plane", "worker"], var.role)
error_message = "The role has to be 'control-plane' or 'worker'."
@ -19,7 +19,7 @@ variable "role" {
variable "uid" {
type = string
description = "UID of the cluster. This is used for tags."
description = "Unique Identifier (UID) of the cluster."
}
variable "instance_type" {
@ -34,7 +34,7 @@ variable "initial_count" {
variable "image_id" {
type = string
description = "Image ID for the nodes."
description = "Amazon Machine Image (AMI) ID for the cluster's nodes."
}
variable "state_disk_type" {
@ -64,18 +64,18 @@ variable "iam_instance_profile" {
variable "security_groups" {
type = list(string)
description = "List of IDs of the security groups for an instance."
description = "List of security group IDs for an instance."
}
variable "tags" {
type = map(string)
description = "The tags to add to the instance group."
description = "Tags to add to the instance group."
}
variable "enable_snp" {
type = bool
default = true
description = "Enable AMD SEV SNP. Setting this to true sets the cpu-option AmdSevSnp to enable."
description = "Enable AMD SEV-SNP for the instances."
}
variable "zone" {

View File

@ -7,7 +7,6 @@ terraform {
}
}
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["099720109477"] # Canonical
@ -55,5 +54,4 @@ iptables -t nat -A PREROUTING -p tcp --dport ${port} -j DNAT --to-destination $$
iptables -t nat -A POSTROUTING -p tcp -d $${lb_ip} --dport ${port} -j SNAT --to-source $${internal_ip}
%{endfor~}
EOF
}

View File

@ -1,3 +1,4 @@
output "ip" {
value = aws_instance.jump_host.public_ip
value = aws_instance.jump_host.public_ip
description = "Public IP of the jump host."
}

View File

@ -1,28 +1,28 @@
variable "base_name" {
description = "Base name of the jump host"
type = string
}
variable "subnet_id" {
description = "Subnet ID to deploy the jump host into"
type = string
}
variable "lb_internal_ip" {
description = "Internal IP of the load balancer"
type = string
}
variable "iam_instance_profile" {
description = "IAM instance profile to attach to the jump host"
description = "Base name of the jump host."
type = string
}
variable "ports" {
description = "Ports to forward to the load balancer"
description = "Ports to forward to the load balancer."
type = list(number)
}
variable "subnet_id" {
description = "Subnet ID to deploy the jump host into."
type = string
}
variable "lb_internal_ip" {
description = "Internal IP of the load balancer."
type = string
}
variable "iam_instance_profile" {
description = "IAM instance profile to attach to the jump host."
type = string
}
variable "security_groups" {
type = list(string)
description = "List of IDs of the security groups for an instance."

View File

@ -8,7 +8,7 @@ terraform {
}
resource "aws_lb_target_group" "front_end" {
name = var.name
name = var.base_name
port = var.port
protocol = "TCP"
vpc_id = var.vpc_id

View File

@ -1,3 +1,4 @@
output "target_group_arn" {
value = aws_lb_target_group.front_end.arn
value = aws_lb_target_group.front_end.arn
description = "ARN of the load balancer target group."
}

View File

@ -1,6 +1,6 @@
variable "name" {
variable "base_name" {
type = string
description = "Name of the load balancer target."
description = "Base name of the load balancer target."
}
variable "port" {
@ -32,5 +32,5 @@ variable "healthcheck_path" {
variable "tags" {
type = map(string)
description = "The tags to add to the loadbalancer."
description = "Tags to add to the loadbalancer."
}

View File

@ -3,6 +3,7 @@ output "private_subnet_id" {
for az in data.aws_availability_zone.all :
az.name => aws_subnet.private[az.name].id
}
description = "Map of availability zones to private subnet id."
}
output "public_subnet_id" {

View File

@ -1,6 +1,6 @@
variable "name" {
type = string
description = "Name of your Constellation, which is used as a prefix for tags."
description = "Name of the Constellation cluster."
}
variable "vpc_id" {
@ -30,5 +30,5 @@ variable "cidr_vpc_subnet_internet" {
variable "tags" {
type = map(string)
description = "The tags to add to the resource."
description = "Tags to add to the resource."
}

View File

@ -1,10 +1,15 @@
# Outputs common to all CSPs
output "out_of_cluster_endpoint" {
value = local.out_of_cluster_endpoint
value = local.out_of_cluster_endpoint
description = "External endpoint for the Kubernetes API server. Only varies from the `in_cluster_endpoint` when using an internal load balancer."
}
output "in_cluster_endpoint" {
value = local.in_cluster_endpoint
value = local.in_cluster_endpoint
description = "Internal endpoint for the Kubernetes API server."
}
output "api_server_cert_sans" {
value = sort(
distinct(
@ -17,21 +22,26 @@ output "api_server_cert_sans" {
)
)
)
description = "List of additional Subject Alternative Names (SANs) for the API server certificate."
}
output "uid" {
value = local.uid
value = local.uid
description = "Unique Identifier (UID) of the cluster."
}
output "initSecret" {
value = random_password.initSecret.result
sensitive = true
output "init_secret" {
value = random_password.init_secret.result
sensitive = true
description = "Initialization secret to authenticate the bootstrapping node."
}
output "name" {
value = local.name
value = local.name
description = "Unique name of the Constellation cluster, comprised by name and UID."
}
output "ip_cidr_nodes" {
value = local.cidr_vpc_subnet_nodes
output "ip_cidr_node" {
value = local.cidr_vpc_subnet_nodes
description = "CIDR block of the node network."
}

View File

@ -1,13 +1,15 @@
# Variables common to all CSPs
variable "name" {
type = string
description = "Name of your Constellation"
description = "Name of the Constellation cluster."
validation {
condition = length(var.name) <= 10
error_message = "The length of the name of the Constellation must be <= 10 characters"
error_message = "The length of the name of the Constellation must be <= 10 characters."
}
validation {
condition = var.name == lower(var.name)
error_message = "The name of the Constellation must be in lowercase"
error_message = "The name of the Constellation must be in lowercase."
}
}
@ -27,39 +29,53 @@ variable "node_groups" {
}
}
variable "iam_instance_profile_worker_nodes" {
variable "image_id" {
type = string
description = "Name of the IAM instance profile for worker nodes"
}
variable "iam_instance_profile_control_plane" {
type = string
description = "Name of the IAM instance profile for control plane nodes"
}
variable "ami" {
type = string
description = "AMI ID"
description = "Amazon Machine Image (AMI) ID for the cluster's nodes."
validation {
condition = length(var.ami) > 4 && substr(var.ami, 0, 4) == "ami-"
error_message = "The image_id value must be a valid AMI id, starting with \"ami-\"."
condition = length(var.image_id) > 4 && substr(var.image_id, 0, 4) == "ami-"
error_message = "The \"image_id\" value must be a valid AMI ID, starting with \"ami-\"."
}
}
variable "region" {
type = string
description = "The AWS region to create the cluster in"
}
variable "zone" {
type = string
description = "The AWS availability zone name to create the cluster in"
}
variable "debug" {
type = bool
default = false
description = "Enable debug mode. This opens up a debugd port that can be used to deploy a custom bootstrapper."
description = "DO NOT USE IN PRODUCTION. 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 API server. If not set, the default endpoint will be used."
}
variable "internal_load_balancer" {
type = bool
default = false
description = "Whether to use an internal load balancer for the cluster."
}
# AWS-specific variables
variable "iam_instance_profile_name_worker_nodes" {
type = string
description = "Name of the IAM instance profile for worker nodes."
}
variable "iam_instance_profile_name_control_plane" {
type = string
description = "Name of the IAM instance profile for control plane nodes."
}
variable "region" {
type = string
description = "AWS region to create the cluster in."
}
variable "zone" {
type = string
description = "AWS availability zone name to create the cluster in."
}
variable "enable_snp" {
@ -67,15 +83,3 @@ 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."
}
variable "internal_load_balancer" {
type = bool
default = false
description = "Use an internal load balancer."
}

View File

@ -20,9 +20,9 @@ provider "azurerm" {
}
locals {
uid = random_id.uid.hex
name = "${var.name}-${local.uid}"
initSecretHash = random_password.initSecret.bcrypt_hash
uid = random_id.uid.hex
name = "${var.name}-${local.uid}"
init_secret_hash = random_password.init_secret.bcrypt_hash
tags = {
constellation-uid = local.uid,
}
@ -54,7 +54,7 @@ resource "random_id" "uid" {
byte_length = 4
}
resource "random_password" "initSecret" {
resource "random_password" "init_secret" {
length = 32
special = true
override_special = "_%@"
@ -245,7 +245,7 @@ module "scale_set_group" {
zones = each.value.zones
tags = merge(
local.tags,
{ constellation-init-secret-hash = local.initSecretHash },
{ constellation-init-secret-hash = local.init_secret_hash },
{ constellation-maa-url = var.create_maa ? azurerm_attestation_provider.attestation_provider[0].attestation_uri : "" },
)

View File

@ -1,3 +1,4 @@
output "ip" {
value = azurerm_linux_virtual_machine.jump_host.public_ip_address
value = azurerm_linux_virtual_machine.jump_host.public_ip_address
description = "Public IP of the jump host."
}

View File

@ -1,29 +1,29 @@
variable "base_name" {
description = "Base name of the jump host"
description = "Base name of the jump host."
type = string
}
variable "ports" {
description = "Ports to forward to the load balancer"
description = "Ports to forward to the load balancer."
type = list(number)
}
variable "resource_group" {
description = "Resource group name to deploy the jump host into"
type = string
}
variable "location" {
description = "Location to deploy the jump host into"
type = string
}
variable "subnet_id" {
description = "Subnet ID to deploy the jump host into"
description = "Subnet ID to deploy the jump host into."
type = string
}
variable "lb_internal_ip" {
description = "Internal IP of the load balancer"
description = "Internal IP of the load balancer."
type = string
}
variable "resource_group" {
description = "Resource group name to deploy the jump host into."
type = string
}
variable "location" {
description = "Location to deploy the jump host into."
type = string
}

View File

@ -1,4 +1,4 @@
output "backendpool_id" {
value = azurerm_lb_backend_address_pool.backend_pool.id
description = "The ID of the created backend pool."
description = "ID of the created backend address pool."
}

View File

@ -1,17 +1,17 @@
variable "name" {
type = string
default = "constell"
description = "Base name of the cluster."
description = "Name of the Constellation cluster."
}
variable "frontend_ip_configuration_name" {
type = string
description = "The name of the frontend IP configuration to use for the load balancer."
description = "Name of the frontend IP configuration to use for the load balancer."
}
variable "loadbalancer_id" {
type = string
description = "The ID of the load balancer to add the backend to."
description = "ID of the load balancer to add the backend to."
}
variable "ports" {
@ -21,5 +21,5 @@ variable "ports" {
health_check_protocol = string
path = string
}))
description = "The ports to add to the backend. Protocol can be either 'Tcp' or 'Https'. Path is only used for 'Https' protocol and can otherwise be null."
description = "Ports to add to the backend. Healtch check protocol can be either 'Tcp' or 'Https'. Path is only used for the 'Https' protocol and can otherwise be null."
}

View File

@ -24,6 +24,7 @@ locals {
resource "random_id" "uid" {
byte_length = 4
}
resource "random_password" "password" {
length = 16
min_lower = 1
@ -92,7 +93,6 @@ resource "azurerm_linux_virtual_machine_scale_set" "scale_set" {
}
}
data_disk {
storage_account_type = var.state_disk_type
disk_size_gb = var.state_disk_size

View File

@ -1,6 +1,6 @@
variable "base_name" {
type = string
description = "Base name of the instance group."
description = "Base name of the scale set."
}
variable "node_group_name" {
@ -10,7 +10,7 @@ variable "node_group_name" {
variable "role" {
type = string
description = "The role of the instance group."
description = "Role of the instance group."
validation {
condition = contains(["control-plane", "worker"], var.role)
error_message = "The role has to be 'control-plane' or 'worker'."
@ -19,7 +19,7 @@ variable "role" {
variable "tags" {
type = map(string)
description = "Tags to include in the scale_set."
description = "Tags to include in the scale set."
}
variable "zones" {
@ -30,59 +30,59 @@ variable "zones" {
variable "initial_count" {
type = number
description = "The number of instances in this scale set."
description = "Number of instances in this scale set."
}
variable "instance_type" {
type = string
description = "The Azure instance type to deploy."
description = "Azure instance type to deploy."
}
variable "state_disk_size" {
type = number
default = 30
description = "The size of the state disk in GB."
description = "Disk size for the state disk of the nodes [GB]."
}
variable "resource_group" {
type = string
description = "The name of the Azure resource group to create the Constellation cluster in."
description = "Name of the Azure resource group to create the Constellation cluster in."
}
variable "location" {
type = string
description = "The Azure location to deploy the cluster in."
description = "Azure location to deploy the cluster in."
}
variable "image_id" {
type = string
description = "The image to use for the cluster nodes."
description = "OS Image reference for the cluster's nodes."
}
variable "user_assigned_identity" {
type = string
description = "The name of the user assigned identity to attache to the nodes of the cluster."
description = "Name of the user assigned identity to attache to the nodes of the cluster."
}
variable "state_disk_type" {
type = string
default = "Premium_LRS"
description = "The type of the state disk."
description = "Type of the state disk."
}
variable "network_security_group_id" {
type = string
description = "The ID of the network security group to use for the scale set."
description = "ID of the network security group to use for the scale set."
}
variable "backend_address_pool_ids" {
type = list(string)
description = "The IDs of the backend address pools to use for the scale set."
description = "IDs of the backend address pools to use for the scale set."
}
variable "subnet_id" {
type = string
description = "The ID of the subnet to use for the scale set."
description = "ID of the subnet to use for the scale set."
}
variable "confidential_vm" {

View File

@ -1,9 +1,13 @@
# Outputs common to all CSPs
output "out_of_cluster_endpoint" {
value = local.out_of_cluster_endpoint
value = local.out_of_cluster_endpoint
description = "External endpoint for the Kubernetes API server. Only varies from the `in_cluster_endpoint` when using an internal load balancer."
}
output "in_cluster_endpoint" {
value = local.in_cluster_endpoint
value = local.in_cluster_endpoint
description = "Internal endpoint for the Kubernetes API server."
}
output "api_server_cert_sans" {
@ -19,46 +23,58 @@ output "api_server_cert_sans" {
)
)
)
description = "List of Subject Alternative Names (SANs) for the API server certificate."
}
output "uid" {
value = local.uid
value = local.uid
description = "Unique Identifier (UID) of the cluster."
}
output "initSecret" {
value = random_password.initSecret.result
sensitive = true
}
output "attestationURL" {
value = var.create_maa ? azurerm_attestation_provider.attestation_provider[0].attestation_uri : ""
}
output "network_security_group_name" {
value = azurerm_network_security_group.security_group.name
}
output "loadbalancer_name" {
value = azurerm_lb.loadbalancer.name
}
output "user_assigned_identity_client_id" {
value = data.azurerm_user_assigned_identity.uaid.client_id
}
output "resource_group" {
value = var.resource_group
}
output "subscription_id" {
value = data.azurerm_subscription.current.subscription_id
output "init_secret" {
value = random_password.init_secret.result
sensitive = true
description = "Initialization secret to authenticate the bootstrapping node."
}
output "name" {
value = local.name
value = local.name
description = "Unique name of the Constellation cluster, comprised by name and UID."
}
output "ip_cidr_nodes" {
value = local.cidr_vpc_subnet_nodes
output "ip_cidr_node" {
value = local.cidr_vpc_subnet_nodes
description = "CIDR block of the node network."
}
# Azure-specific outputs
output "attestation_url" {
value = var.create_maa ? azurerm_attestation_provider.attestation_provider[0].attestation_uri : ""
description = "URL of the cluster's Microsoft Azure Attestation (MAA) provider."
}
output "network_security_group_name" {
value = azurerm_network_security_group.security_group.name
description = "Name of the cluster's network security group."
}
output "loadbalancer_name" {
value = azurerm_lb.loadbalancer.name
description = "Name of the cluster's load balancer."
}
output "user_assigned_identity_client_id" {
value = data.azurerm_user_assigned_identity.uaid.client_id
description = "Client ID of the user assigned identity used within the cluster."
}
output "resource_group" {
value = var.resource_group
description = "Name of the resource group the cluster resides in."
}
output "subscription_id" {
value = data.azurerm_subscription.current.subscription_id
description = "ID of the Azure subscription the cluster resides in."
}

View File

@ -1,6 +1,8 @@
# Variables common to all CSPs
variable "name" {
type = string
description = "Base name of the cluster."
description = "Name of the Constellation cluster."
}
variable "node_groups" {
@ -19,26 +21,40 @@ variable "node_groups" {
}
}
variable "location" {
type = string
description = "The Azure location to deploy the cluster in."
}
variable "image_id" {
type = string
description = "The image to use for the cluster nodes."
}
variable "create_maa" {
type = bool
default = false
description = "Whether to create a Microsoft Azure attestation provider."
description = "OS image reference for the cluster's nodes."
}
variable "debug" {
type = bool
default = false
description = "Enable debug mode. This opens up a debugd port that can be used to deploy a custom bootstrapper."
description = "DO NOT USE IN PRODUCTION. 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 API server. If not set, the default endpoint will be used."
}
variable "internal_load_balancer" {
type = bool
default = false
description = "Whether to use an internal load balancer for the cluster."
}
# Azure-specific variables
variable "location" {
type = string
description = "Azure location to deploy the cluster in."
}
variable "create_maa" {
type = bool
default = false
description = "Whether to create a Microsoft Azure Attestation (MAA) provider."
}
variable "confidential_vm" {
@ -55,23 +71,12 @@ variable "secure_boot" {
variable "resource_group" {
type = string
description = "The name of the Azure resource group to create the Constellation cluster in."
description = "Name of the Azure resource group to create the cluster in."
}
variable "user_assigned_identity" {
type = string
description = "The name of the user assigned identity to attach to the nodes of the cluster. Should be of format: /subscriptions/$ID/resourceGroups/$RG/providers/Microsoft.ManagedIdentity/userAssignedIdentities/$NAME"
}
variable "custom_endpoint" {
type = string
default = ""
description = "Custom endpoint to use for the Kubernetes apiserver. If not set, the default endpoint will be used."
}
variable "internal_load_balancer" {
type = bool
default = false
description = "Whether to use an internal load balancer for the Constellation."
description = "Name of the user assigned identity to attach to the nodes of the cluster. Should be of format: /subscriptions/$ID/resourceGroups/$RG/providers/Microsoft.ManagedIdentity/userAssignedIdentities/$NAME"
}
variable "marketplace_image" {
@ -82,5 +87,5 @@ variable "marketplace_image" {
version = string
})
default = null
description = "Marketplace image to use for the cluster nodes."
description = "Marketplace image for the cluster's nodes."
}

View File

@ -30,9 +30,9 @@ provider "google-beta" {
}
locals {
uid = random_id.uid.hex
name = "${var.name}-${local.uid}"
initSecretHash = random_password.initSecret.bcrypt_hash
uid = random_id.uid.hex
name = "${var.name}-${local.uid}"
init_secret_hash = random_password.init_secret.bcrypt_hash
labels = {
constellation-uid = local.uid,
}
@ -55,7 +55,7 @@ locals {
for name, node_group in var.node_groups : node_group.role => name...
}
control_plane_instance_groups = [
for control_plane in local.node_groups_by_role["control-plane"] : module.instance_group[control_plane].instance_group
for control_plane in local.node_groups_by_role["control-plane"] : module.instance_group[control_plane].instance_group_url
]
in_cluster_endpoint = var.internal_load_balancer ? google_compute_address.loadbalancer_ip_internal[0].address : google_compute_global_address.loadbalancer_ip[0].address
out_of_cluster_endpoint = var.debug && var.internal_load_balancer ? module.jump_host[0].ip : local.in_cluster_endpoint
@ -65,7 +65,7 @@ resource "random_id" "uid" {
byte_length = 4
}
resource "random_password" "initSecret" {
resource "random_password" "init_secret" {
length = 32
special = true
override_special = "_%@"
@ -187,7 +187,7 @@ module "instance_group" {
debug = var.debug
named_ports = each.value.role == "control-plane" ? local.control_plane_named_ports : []
labels = local.labels
init_secret_hash = local.initSecretHash
init_secret_hash = local.init_secret_hash
custom_endpoint = var.custom_endpoint
}

View File

@ -1,3 +1,4 @@
output "instance_group" {
value = google_compute_instance_group_manager.instance_group_manager.instance_group
output "instance_group_url" {
value = google_compute_instance_group_manager.instance_group_manager.instance_group
description = "Full URL of the instance group."
}

View File

@ -10,7 +10,7 @@ variable "node_group_name" {
variable "role" {
type = string
description = "The role of the instance group."
description = "Role of the instance group."
validation {
condition = contains(["control-plane", "worker"], var.role)
error_message = "The role has to be 'control-plane' or 'worker'."
@ -19,7 +19,7 @@ variable "role" {
variable "uid" {
type = string
description = "UID of the cluster. This is used for tags."
description = "Unique Identifier (UID) of the cluster."
}
variable "labels" {
@ -35,22 +35,22 @@ variable "instance_type" {
variable "initial_count" {
type = number
description = "Number of instances in the instance group."
description = "Number of instances in the group."
}
variable "image_id" {
type = string
description = "Image ID for the nodes."
description = "OS Image reference for the cluster's nodes."
}
variable "disk_size" {
type = number
description = "Disk size for the nodes, in GB."
description = "Disk size for the state disk of the nodes [GB]."
}
variable "disk_type" {
type = string
description = "Disk type for the nodes. Has to be 'pd-standard' or 'pd-ssd'."
description = "Disk type for the nodes. Has to be either 'pd-standard' or 'pd-ssd'."
}
variable "network" {
@ -65,12 +65,12 @@ variable "subnetwork" {
variable "kube_env" {
type = string
description = "Kubernetes env."
description = "Value of the \"kube-env\" metadata key."
}
variable "init_secret_hash" {
type = string
description = "Hash of the init secret."
description = "BCrypt Hash of the initialization secret."
}
variable "named_ports" {
@ -82,7 +82,7 @@ variable "named_ports" {
variable "debug" {
type = bool
default = false
description = "Enable debug mode. This will enable serial port access on the instances."
description = "DO NOT USE IN PRODUCTION. Enable debug mode. This opens up a debugd port that can be used to deploy a custom bootstrapper."
}
variable "alias_ip_range_name" {
@ -97,5 +97,5 @@ variable "zone" {
variable "custom_endpoint" {
type = string
description = "Custom endpoint to use for the Kubernetes apiserver. If not set, the default endpoint will be used."
description = "Custom endpoint to use for the Kubernetes API server. If not set, the default endpoint will be used."
}

View File

@ -5,22 +5,22 @@ variable "name" {
variable "region" {
type = string
description = "The region where the load balancer will be created."
description = "Region to create the load balancer in."
}
variable "network" {
type = string
description = "The network to which all network resources will be attached."
description = "Network to which network resources will be attached."
}
variable "backend_subnet" {
type = string
description = "The subnet to which all backend network resources will be attached."
description = "Subnet to which backend network resources will be attached."
}
variable "health_check" {
type = string
description = "The type of the health check. 'HTTPS' or 'TCP'."
description = "Type of the health check. Can either be 'HTTPS' or 'TCP'."
validation {
condition = contains(["HTTPS", "TCP"], var.health_check)
error_message = "Health check must be either 'HTTPS' or 'TCP'."
@ -29,22 +29,22 @@ variable "health_check" {
variable "port" {
type = string
description = "The port on which to listen for incoming traffic."
description = "Port to listen on for incoming traffic."
}
variable "backend_port_name" {
type = string
description = "Name of backend port. The same name should appear in the instance groups referenced by this service."
description = "Name of the load balancer's backend port. The same name should appear in the instance groups referenced by this service."
}
variable "backend_instance_group" {
type = string
description = "The URL of the instance group resource from which the load balancer will direct traffic."
description = "Full URL of the instance group resource from which the load balancer will direct traffic."
}
variable "ip_address" {
type = string
description = "The IP address that this forwarding rule serves."
description = "IP address that this forwarding rule serves."
}
variable "frontend_labels" {

View File

@ -12,7 +12,6 @@ terraform {
}
}
data "google_compute_image" "image_ubuntu" {
family = "ubuntu-2204-lts"
project = "ubuntu-os-cloud"
@ -47,7 +46,7 @@ resource "google_compute_instance" "vm_instance" {
metadata_startup_script = <<EOF
#!/bin/bash
set -x
set -x
# Uncomment to create user with password
# useradd -m user
@ -69,5 +68,4 @@ iptables -t nat -A PREROUTING -p tcp --dport ${port} -j DNAT --to-destination ${
iptables -t nat -A POSTROUTING -p tcp -d ${var.lb_internal_ip} --dport ${port} -j SNAT --to-source $${internal_ip}
%{endfor~}
EOF
}

View File

@ -1,3 +1,4 @@
output "ip" {
value = google_compute_instance.vm_instance.network_interface[0].access_config[0].nat_ip
value = google_compute_instance.vm_instance.network_interface[0].access_config[0].nat_ip
description = "Public IP address of the jump host."
}

View File

@ -1,22 +1,22 @@
variable "base_name" {
type = string
description = "Base name of the instance group."
description = "Base name of the jump host."
}
variable "labels" {
type = map(string)
default = {}
description = "Labels to apply to the instance group."
description = "Labels to apply to the jump host."
}
variable "subnetwork" {
type = string
description = "Name of the subnetwork to use."
description = "Subnetwork to deplyo the jump host into."
}
variable "zone" {
type = string
description = "Zone to deploy the instance group in."
description = "Zone to deploy the jump host into."
}
variable "lb_internal_ip" {

View File

@ -1,35 +1,35 @@
variable "name" {
type = string
description = "Base name of the load balancer."
description = "Name of the Constellation cluster."
}
variable "health_check" {
type = string
description = "The type of the health check. 'HTTPS' or 'TCP'."
description = "Type of the health check. Can either be 'HTTPS' or 'TCP'."
}
variable "backend_port_name" {
type = string
description = "Name of backend port. The same name should appear in the instance groups referenced by this service."
description = "Name of the load balancer's backend port. The same name should appear in the instance groups referenced by this service."
}
variable "backend_instance_groups" {
type = list(string)
description = "The URLs of the instance group resources from which the load balancer will direct traffic."
description = "URLs of the instance group resources from which the load balancer will direct traffic."
}
variable "ip_address" {
type = string
description = "The IP address that this forwarding rule serves. An address can be specified either by a literal IP address or a reference to an existing Address resource."
description = "IP address that this forwarding rule serves. An address can be specified either by a literal IP address or a reference to an existing Address resource."
}
variable "port" {
type = number
description = "The port on which to listen for incoming traffic."
description = "Port to listen on for incoming traffic."
}
variable "frontend_labels" {
type = map(string)
default = {}
description = "Labels to apply to the forwarding rule."
description = "Labels to apply to the load balancer's forwarding rule."
}

View File

@ -1,9 +1,13 @@
# Outputs common to all CSPs
output "out_of_cluster_endpoint" {
value = local.out_of_cluster_endpoint
value = local.out_of_cluster_endpoint
description = "External endpoint for the Kubernetes API server. Only varies from the `in_cluster_endpoint` when using an internal load balancer."
}
output "in_cluster_endpoint" {
value = local.in_cluster_endpoint
value = local.in_cluster_endpoint
description = "Internal endpoint for the Kubernetes API server."
}
output "api_server_cert_sans" {
value = sort(
@ -17,29 +21,38 @@ output "api_server_cert_sans" {
)
)
)
description = "List of Subject Alternative Names (SANs) for the API server certificate."
}
output "uid" {
value = local.uid
value = local.uid
description = "Unique Identifier (UID) of the cluster."
}
output "initSecret" {
value = random_password.initSecret.result
sensitive = true
}
output "project" {
value = var.project
}
output "ip_cidr_nodes" {
value = local.cidr_vpc_subnet_nodes
}
output "ip_cidr_pods" {
value = local.cidr_vpc_subnet_pods
output "init_secret" {
value = random_password.init_secret.result
sensitive = true
description = "Initialization secret to authenticate the bootstrapping node."
}
output "name" {
value = local.name
value = local.name
description = "Unique name of the Constellation cluster, comprised by name and UID."
}
output "ip_cidr_node" {
value = local.cidr_vpc_subnet_nodes
description = "CIDR block of the node network."
}
# GCP-specific outputs
output "project" {
value = var.project
description = "The GCP project the cluster is deployed in."
}
output "ip_cidr_pod" {
value = local.cidr_vpc_subnet_pods
description = "CIDR block of the pod network."
}

View File

@ -1,7 +1,8 @@
# Variables common to all CSPs
variable "name" {
type = string
default = "constell"
description = "Base name of the cluster."
description = "Name of the Constellation cluster."
}
variable "node_groups" {
@ -20,40 +21,42 @@ variable "node_groups" {
}
}
variable "project" {
type = string
description = "The GCP project to deploy the cluster in."
}
variable "region" {
type = string
description = "The GCP region to deploy the cluster in."
}
variable "zone" {
type = string
description = "The GCP zone to deploy the cluster in."
}
variable "image_id" {
type = string
description = "The GCP image to use for the cluster nodes."
description = "OS image reference for the cluster's nodes."
}
variable "debug" {
type = bool
default = false
description = "Enable debug mode. This opens up a debugd port that can be used to deploy a custom bootstrapper."
description = "DO NOT USE IN PRODUCTION. 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."
description = "Custom endpoint to use for the Kubernetes API server. If not set, the default endpoint will be used."
}
variable "internal_load_balancer" {
type = bool
default = false
description = "Enable internal load balancer. This can only be enabled if the control-plane is deployed in one zone."
description = "Whether to use an internal load balancer for the cluster."
}
# GCP-specific variables
variable "project" {
type = string
description = "GCP project to deploy the cluster in."
}
variable "region" {
type = string
description = "GCP region to deploy the cluster in."
}
variable "zone" {
type = string
description = "GCP zone to deploy the cluster in."
}

View File

@ -1,24 +0,0 @@
# IAM instance profiles for AWS
This terraform script creates the necessary profiles that need to be attached to Constellation nodes.
You can create the profiles with the following commands:
```sh
mkdir constellation_aws_iam
cd constellation_aws_iam
curl --remote-name-all https://raw.githubusercontent.com/edgelesssys/constellation/main/hack/terraform/aws/iam/{main,output,variables}.tf
terraform init
terraform apply -auto-approve -var name_prefix=my_constellation
```
You can either get the profile names from the Terraform output values `control_plane_instance_profile` and `worker_nodes_instance_profile` and manually add them to your Constellation configuration file.
Or you can do this with a `yq` command:
```sh
yq -i "
.provider.aws.iamProfileControlPlane = $(terraform output control_plane_instance_profile) |
.provider.aws.iamProfileWorkerNodes = $(terraform output worker_nodes_instance_profile)
" path/to/constellation-conf.yaml
```

View File

@ -7,7 +7,6 @@ terraform {
}
}
# Configure the AWS Provider
provider "aws" {
region = var.region
}

View File

@ -1,7 +1,9 @@
output "control_plane_instance_profile" {
value = aws_iam_instance_profile.control_plane_instance_profile.name
output "iam_instance_profile_name_control_plane" {
value = aws_iam_instance_profile.control_plane_instance_profile.name
description = "Name of the control plane's instance profile."
}
output "worker_nodes_instance_profile" {
value = aws_iam_instance_profile.worker_node_instance_profile.name
output "iam_instance_profile_name_worker_nodes" {
value = aws_iam_instance_profile.worker_node_instance_profile.name
description = "Name of the worker nodes' instance profile"
}

View File

@ -1,10 +1,10 @@
variable "name_prefix" {
type = string
description = "Prefix for all resources"
description = "Name prefix to use on named resources."
}
variable "region" {
type = string
description = "AWS region"
description = "AWS region."
default = "us-east-2"
}

View File

@ -1,32 +0,0 @@
# Terraform Azure IAM creation
This terraform configuration creates the necessary Azure resources that need to be available to host a Constellation cluster.
You can create the resources with the following commands:
```sh
mkdir constellation_azure_iam
cd constellation_azure_iam
curl --remote-name-all https://raw.githubusercontent.com/edgelesssys/constellation/main/hack/terraform/azure/iam/{main.tf,output.tf,variables.tf,.terraform.lock.hcl}
terraform init
terraform apply
```
The following terraform output values are available (with their corresponding keys in the Constellation configuration file):
- `subscription_id` (subscription)
- `tenant_id` (tenant)
- `uami_id` (userAssignedIdentity)
You can either get the profile names from the Terraform output and manually add them to your Constellation configuration file according to our [Documentation](https://docs.edgeless.systems/constellation/getting-started/first-steps).
Or you can do this with a `yq` command:
```sh
yq -i "
.provider.azure.subscription = $(terraform output subscription_id) |
.provider.azure.tenant = $(terraform output tenant_id) |
.provider.azure.userAssignedIdentity = $(terraform output uami_id) |
" path/to/constellation-conf.yaml
```
Where `path/to/constellation-conf.yaml` is the path to your Constellation configuration file.

View File

@ -28,24 +28,24 @@ provider "azuread" {
# Access current subscription (available via Azure CLI)
data "azurerm_subscription" "current" {}
# # Access current AzureAD configuration
# Access current AzureAD configuration
data "azuread_client_config" "current" {}
# Create base resource group
resource "azurerm_resource_group" "base_resource_group" {
name = var.resource_group_name
location = var.region
location = var.location
}
# Create identity resource group
resource "azurerm_resource_group" "identity_resource_group" {
name = "${var.resource_group_name}-identity"
location = var.region
location = var.location
}
# Create managed identity
resource "azurerm_user_assigned_identity" "identity_uami" {
location = var.region
location = var.location
name = var.service_principal_name
resource_group_name = azurerm_resource_group.identity_resource_group.name
}

View File

@ -1,16 +1,19 @@
output "subscription_id" {
value = data.azurerm_subscription.current.subscription_id
value = data.azurerm_subscription.current.subscription_id
description = "ID of the Azure subscription."
}
output "tenant_id" {
value = data.azurerm_subscription.current.tenant_id
value = data.azurerm_subscription.current.tenant_id
description = "ID of the Azure tenant."
}
output "uami_id" {
description = "Outputs the id in the format: /$ID/resourceGroups/$RG/providers/Microsoft.ManagedIdentity/userAssignedIdentities/$NAME. Not to be confused with the client_id"
value = azurerm_user_assigned_identity.identity_uami.id
description = "Resource ID of the UAMI in the format: /$ID/resourceGroups/$RG/providers/Microsoft.ManagedIdentity/userAssignedIdentities/$NAME. Not to be confused with the Client ID of the UAMI."
}
output "base_resource_group" {
value = azurerm_resource_group.base_resource_group.name
value = azurerm_resource_group.base_resource_group.name
description = "Name of the resource group."
}

View File

@ -1,14 +1,14 @@
variable "resource_group_name" {
type = string
description = "Resource group name"
description = "Name for the resource group the cluster should reside in."
}
variable "service_principal_name" {
type = string
description = "Service principal name"
description = "Name for the service principal used within the cluster."
}
variable "region" {
variable "location" {
type = string
description = "Azure resource location"
description = "Azure location the cluster should reside in."
}

View File

@ -1,36 +0,0 @@
# IAM configuration for GCP
This terraform script creates the necessary GCP IAM configuration to be attached to Constellation nodes.
You can create the configuration with the following commands:
```sh
mkdir constellation_gcp_iam
cd constellation_gcp_iam
curl --remote-name-all https://raw.githubusercontent.com/edgelesssys/constellation/main/terraform/infrastructure/iam/gcp/{main.tf,outputs.tf,variables.tf,.terraform.lock.hcl}
terraform init
terraform apply
```
The following terraform output values are available (with their corresponding keys in the Constellation configuration file):
- `sa_key` - **Sensitive Value**
- `region` (region)
- `zone` (zone)
- `project_id` (project)
You can either get the values from the Terraform output and manually add them to your Constellation configuration file according to our [Documentation](https://docs.edgeless.systems/constellation/getting-started/first-steps). (If you add the values manually, you need to base64-decode the `sa_key` value and place it in a JSON file, then specify the path to this file in the Constellation configuration file for the `serviceAccountKeyPath` key.)
Or you can setup the constellation configuration file automaticcaly with the following commands:
```sh
terraform output sa_key | sed "s/\"//g" | base64 --decode | tee gcpServiceAccountKey.json
yq -i "
.provider.gcp.serviceAccountKeyPath = \"$(realpath gcpServiceAccountKey.json)\" |
.provider.gcp.project = $(terraform output project_id) |
.provider.gcp.region = $(terraform output region) |
.provider.gcp.zone = $(terraform output zone)
" path/to/constellation-conf.yaml
```
Where `path/to/constellation-conf.yaml` is the path to your Constellation configuration file.

View File

@ -1,4 +1,5 @@
output "sa_key" {
value = google_service_account_key.service_account_key.private_key
sensitive = true
output "service_account_key" {
value = google_service_account_key.service_account_key.private_key
description = "Private key of the service account."
sensitive = true
}

View File

@ -1,19 +1,19 @@
variable "project_id" {
type = string
description = "GCP Project ID"
description = "ID of the GCP project the cluster should reside in."
}
variable "service_account_id" {
type = string
description = "ID for the service account being created. Must match ^[a-z](?:[-a-z0-9]{4,28}[a-z0-9])$"
description = "ID for the service account being created. Must match ^[a-z](?:[-a-z0-9]{4,28}[a-z0-9])$."
}
variable "region" {
type = string
description = "Region used for constellation clusters. Needs to have the N2D machine type available."
description = "GCP region the cluster should reside in. Needs to have the N2D machine type available."
}
variable "zone" {
type = string
description = "Zone used for constellation clusters. Needs to be within the specified region."
description = "GCP zone the cluster should reside in. Needs to be within the specified region."
}

View File

@ -23,7 +23,7 @@ data "openstack_identity_auth_scope_v3" "scope" {
locals {
uid = random_id.uid.hex
name = "${var.name}-${local.uid}"
initSecretHash = random_password.initSecret.bcrypt_hash
init_secret_hash = random_password.init_secret.bcrypt_hash
ports_node_range_start = "30000"
ports_node_range_end = "32767"
ports_kubernetes = "6443"
@ -49,15 +49,15 @@ resource "random_id" "uid" {
byte_length = 4
}
resource "random_password" "initSecret" {
resource "random_password" "init_secret" {
length = 32
special = true
override_special = "_%@"
}
resource "openstack_images_image_v2" "constellation_os_image" {
resource "openstack_images_image_v2" "image_id" {
name = local.name
image_source_url = var.image_url
image_source_url = var.image_id
web_download = var.direct_download
container_format = "bare"
disk_format = "raw"
@ -168,13 +168,13 @@ module "instance_group" {
disk_size = each.value.state_disk_size
state_disk_type = each.value.state_disk_type
availability_zone = each.value.zone
image_id = openstack_images_image_v2.constellation_os_image.image_id
image_id = openstack_images_image_v2.image_id.image_id
flavor_id = each.value.flavor_id
security_groups = [openstack_compute_secgroup_v2.vpc_secgroup.id]
tags = local.tags
uid = local.uid
network_id = openstack_networking_network_v2.vpc_network.id
init_secret_hash = local.initSecretHash
init_secret_hash = local.init_secret_hash
identity_internal_url = local.identity_internal_url
openstack_username = var.openstack_username
openstack_password = var.openstack_password

View File

@ -1,11 +1,9 @@
output "instance_group" {
value = local.name
}
output "ips" {
value = openstack_compute_instance_v2.instance_group_member.*.access_ip_v4
value = openstack_compute_instance_v2.instance_group_member.*.access_ip_v4
description = "Public IP addresses of the instances."
}
output "instance_ids" {
value = openstack_compute_instance_v2.instance_group_member.*.id
value = openstack_compute_instance_v2.instance_group_member.*.id
description = "IDs of the instances."
}

View File

@ -1,16 +1,11 @@
variable "node_group_name" {
type = string
description = "Constellation name for the node group (used for configuration and CSP-independent naming)."
}
variable "base_name" {
type = string
description = "Base name of the instance group."
}
variable "uid" {
variable "node_group_name" {
type = string
description = "Unique ID of the Constellation."
description = "Constellation name for the instance group (used for configuration and CSP-independent naming)."
}
variable "role" {
@ -22,14 +17,24 @@ variable "role" {
}
}
variable "tags" {
type = list(string)
description = "Tags to attach to each node."
}
variable "uid" {
type = string
description = "Unique ID of the Constellation."
}
variable "initial_count" {
type = number
description = "Number of instances in the instance group."
description = "Number of instances in this instance group."
}
variable "image_id" {
type = string
description = "Image ID for the nodes."
description = "OS Image reference for the cluster's nodes."
}
variable "flavor_id" {
@ -42,24 +47,19 @@ variable "security_groups" {
description = "Security groups to place the nodes in."
}
variable "tags" {
type = list(string)
description = "Tags to attach to each node."
}
variable "disk_size" {
type = number
description = "Disk size for the nodes, in GiB."
description = "Disk size for the state disk of the nodes [GB]."
}
variable "state_disk_type" {
type = string
description = "Disk/volume type to be used."
description = "Type of the state disk."
}
variable "availability_zone" {
type = string
description = "The availability zone to deploy the nodes in."
description = "Availability zone to deploy the nodes in."
}
variable "network_id" {

View File

@ -1,25 +1,25 @@
variable "name" {
type = string
description = "Base name of the load balancer rule."
description = "Base name of the load balancer."
}
variable "member_ips" {
type = list(string)
description = "The IP addresses of the members of the load balancer pool."
description = "IP addresses of the members of the load balancer pool."
default = []
}
variable "loadbalancer_id" {
type = string
description = "The ID of the load balancer."
description = "ID of the load balancer."
}
variable "subnet_id" {
type = string
description = "The ID of the members subnet."
description = "ID of the members subnet."
}
variable "port" {
type = number
description = "The port on which to listen for incoming traffic."
description = "Port to listen on incoming traffic."
}

View File

@ -1,28 +1,37 @@
# Outputs common to all CSPs
output "out_of_cluster_endpoint" {
value = openstack_networking_floatingip_v2.public_ip.address
value = openstack_networking_floatingip_v2.public_ip.address
description = "External endpoint for the Kubernetes API server. Only varies from the `in_cluster_endpoint` when using an internal load balancer."
}
output "in_cluster_endpoint" {
value = openstack_networking_floatingip_v2.public_ip.address
value = openstack_networking_floatingip_v2.public_ip.address
description = "Internal endpoint for the Kubernetes API server."
}
output "api_server_cert_sans" {
value = sort(concat([openstack_networking_floatingip_v2.public_ip.address], var.custom_endpoint == "" ? [] : [var.custom_endpoint]))
value = sort(concat([openstack_networking_floatingip_v2.public_ip.address], var.custom_endpoint == "" ? [] : [var.custom_endpoint]))
description = "List of Subject Alternative Names (SANs) for the API server certificate."
}
output "uid" {
value = local.uid
value = local.uid
description = "Unique Identifier (UID) of the cluster."
}
output "initSecret" {
value = random_password.initSecret.result
sensitive = true
output "init_secret" {
value = random_password.init_secret.result
sensitive = true
description = "Initialization secret to authenticate the bootstrapping node."
}
output "name" {
value = local.name
value = local.name
description = "Unique name of the Constellation cluster, comprised by name and UID."
}
output "ip_cidr_nodes" {
value = local.cidr_vpc_subnet_nodes
output "ip_cidr_node" {
value = local.cidr_vpc_subnet_nodes
description = "CIDR block of the node network."
}

View File

@ -1,3 +1,10 @@
# Variables common to all CSPs
variable "name" {
type = string
default = "constell"
description = "Base name of the cluster."
}
variable "node_groups" {
type = map(object({
role = string
@ -16,31 +23,39 @@ variable "node_groups" {
description = "A map of node group names to node group configurations."
}
variable "image_id" {
type = string
description = "OS image URL for the cluster's nodes."
}
variable "debug" {
type = bool
default = false
description = "DO NOT USE IN PRODUCTION. 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 API server. If not set, the default endpoint will be used."
}
# OpenStack-specific variables
variable "cloud" {
type = string
default = null
description = "The cloud to use within the OpenStack \"clouds.yaml\" file. Optional. If not set, environment variables are used."
}
variable "name" {
type = string
default = "constell"
description = "Base name of the cluster."
}
variable "image_url" {
type = string
description = "The image to use for cluster nodes."
description = "Cloud to use within the OpenStack \"clouds.yaml\" file. Optional. If not set, environment variables are used."
}
variable "direct_download" {
type = bool
description = "If enabled, downloads OS image directly from source URL to OpenStack. Otherwise, downloads image to local machine and uploads to OpenStack."
description = "Download OS image directly from source URL to OpenStack. Otherwise, the image is downloaded to the local machine and uploads to OpenStack."
}
variable "floating_ip_pool_id" {
type = string
description = "The pool (network name) to use for floating IPs."
description = "Pool (network name) to use for floating IPs."
}
variable "openstack_user_domain_name" {
@ -57,15 +72,3 @@ variable "openstack_password" {
type = string
description = "OpenStack password."
}
variable "debug" {
type = bool
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."
}

View File

@ -25,7 +25,7 @@ locals {
cidr_vpc_subnet_worker = "10.42.2.0/24"
}
resource "random_password" "initSecret" {
resource "random_password" "init_secret" {
length = 32
special = true
override_special = "_%@"
@ -46,7 +46,7 @@ resource "docker_container" "qemu_metadata" {
"--libvirt-uri",
"${var.metadata_libvirt_uri}",
"--initsecrethash",
"${random_password.initSecret.bcrypt_hash}",
"${random_password.init_secret.bcrypt_hash}",
]
mounts {
source = abspath(var.libvirt_socket_path)
@ -55,7 +55,6 @@ resource "docker_container" "qemu_metadata" {
}
}
module "node_group" {
source = "./modules/instance_group"
base_name = var.name
@ -71,7 +70,7 @@ module "node_group" {
network_id = libvirt_network.constellation.id
pool = libvirt_pool.cluster.name
boot_mode = var.constellation_boot_mode
boot_volume_id = libvirt_volume.constellation_os_image.id
boot_volume_id = libvirt_volume.image_id.id
kernel_volume_id = local.kernel_volume_id
initrd_volume_id = local.initrd_volume_id
kernel_cmdline = each.value.role == "control-plane" ? local.kernel_cmdline : var.constellation_cmdline
@ -85,10 +84,10 @@ resource "libvirt_pool" "cluster" {
path = "/var/lib/libvirt/images"
}
resource "libvirt_volume" "constellation_os_image" {
resource "libvirt_volume" "image_id" {
name = "${var.name}-node-image"
pool = libvirt_pool.cluster.name
source = var.constellation_os_image
source = var.image_id
format = var.image_format
}

View File

@ -1,3 +1,4 @@
output "instance_ips" {
value = flatten(libvirt_domain.instance_group[*].network_interface[*].addresses[*])
value = flatten(libvirt_domain.instance_group[*].network_interface[*].addresses[*])
description = "IP addresses of the instances in the instance group."
}

View File

@ -1,41 +1,51 @@
variable "base_name" {
type = string
description = "Name prefix fort the cluster's VMs."
}
variable "node_group_name" {
type = string
description = "Constellation name for the node group (used for configuration and CSP-independent naming)."
}
variable "amount" {
type = number
description = "amount of nodes"
description = "Amount of nodes."
}
variable "vcpus" {
type = number
description = "amount of vcpus per instance"
description = "Amount of vCPUs per instance."
}
variable "memory" {
type = number
description = "amount of memory per instance (MiB)"
description = "Amount of memory per instance (MiB)."
}
variable "state_disk_size" {
type = number
description = "size of state disk (GiB)"
description = "Disk size for the state disk of the nodes [GB]."
}
variable "cidr" {
type = string
description = "subnet to use for dhcp"
description = "Subnet to use for DHCP."
}
variable "network_id" {
type = string
description = "id of the network to use"
description = "ID of the Libvirt network to use."
}
variable "pool" {
type = string
description = "name of the storage pool to use"
description = "Name of the Libvirt storage pool to use."
}
variable "boot_mode" {
type = string
description = "boot mode. Can be 'uefi' or 'direct-linux-boot'"
description = "Boot mode. Can be 'uefi' or 'direct-linux-boot'"
validation {
condition = can(regex("^(uefi|direct-linux-boot)$", var.boot_mode))
error_message = "boot_mode must be 'uefi' or 'direct-linux-boot'"
@ -44,52 +54,43 @@ variable "boot_mode" {
variable "boot_volume_id" {
type = string
description = "id of the constellation boot disk"
description = "ID of the Constellation boot disk."
}
variable "kernel_volume_id" {
type = string
description = "id of the constellation kernel volume"
description = "ID of the Constellation kernel volume."
default = ""
}
variable "initrd_volume_id" {
type = string
description = "id of the constellation initrd volume"
description = "ID of the constellation initrd volume."
default = ""
}
variable "kernel_cmdline" {
type = string
description = "kernel cmdline"
description = "Kernel cmdline."
default = ""
}
variable "role" {
type = string
description = "role of the node in the constellation. either 'control-plane' or 'worker'"
description = "Role of the node in the Constellation cluster. Can either be'control-plane' or 'worker'."
}
variable "machine" {
type = string
description = "machine type. use 'q35' for secure boot and 'pc' for non secure boot. See 'qemu-system-x86_64 -machine help'"
description = "Machine type. Use 'q35' for secure boot and 'pc' for non secure boot. See 'qemu-system-x86_64 -machine help'."
}
variable "firmware" {
type = string
description = "path to UEFI firmware file. Ignored for direct-linux-boot."
description = "Path to UEFI firmware file. Ignored for direct-linux-boot."
}
variable "nvram" {
type = string
description = "path to UEFI NVRAM template file. Used for secure boot."
}
variable "base_name" {
type = string
description = "name prefix of the cluster VMs"
}
variable "node_group_name" {
type = string
description = "name of the node group"
description = "Path to UEFI NVRAM template file. Used for secure boot."
}

View File

@ -1,30 +1,50 @@
# Outputs common to all CSPs
output "out_of_cluster_endpoint" {
value = module.node_group["control_plane_default"].instance_ips[0]
value = module.node_group["control_plane_default"].instance_ips[0]
description = "External endpoint for the Kubernetes API server. Only varies from the `in_cluster_endpoint` when using an internal load balancer."
}
output "in_cluster_endpoint" {
value = module.node_group["control_plane_default"].instance_ips[0]
value = module.node_group["control_plane_default"].instance_ips[0]
description = "Internal endpoint for the Kubernetes API server."
}
output "api_server_cert_sans" {
value = sort(concat([module.node_group["control_plane_default"].instance_ips[0]], var.custom_endpoint == "" ? [] : [var.custom_endpoint]))
value = sort(concat([module.node_group["control_plane_default"].instance_ips[0]], var.custom_endpoint == "" ? [] : [var.custom_endpoint]))
description = "List of Subject Alternative Names (SANs) for the API server certificate."
}
output "uid" {
value = "qemu" // placeholder
value = "qemu" // placeholder
description = "Unique Identifier (UID) of the cluster."
}
output "initSecret" {
value = random_password.initSecret.result
sensitive = true
output "init_secret" {
value = random_password.init_secret.result
sensitive = true
description = "Initialization secret to authenticate the bootstrapping node."
}
output "name" {
value = "${var.name}-qemu" // placeholder, as per "uid" output
description = "Unique name of the Constellation cluster, comprised by name and UID."
}
output "ip_cidr_node" {
value = local.cidr_vpc_subnet_nodes
description = "CIDR block of the node network."
}
# QEMU-specific outputs
output "validate_constellation_kernel" {
value = null
precondition {
condition = var.constellation_boot_mode != "direct-linux-boot" || length(var.constellation_kernel) > 0
error_message = "constellation_kernel must be set if constellation_boot_mode is 'direct-linux-boot'"
}
description = "Validation placeholder. Do not consume as output."
}
output "validate_constellation_initrd" {
@ -33,6 +53,7 @@ output "validate_constellation_initrd" {
condition = var.constellation_boot_mode != "direct-linux-boot" || length(var.constellation_initrd) > 0
error_message = "constellation_initrd must be set if constellation_boot_mode is 'direct-linux-boot'"
}
description = "Validation placeholder. Do not consume as output."
}
output "validate_constellation_cmdline" {
@ -41,12 +62,5 @@ output "validate_constellation_cmdline" {
condition = var.constellation_boot_mode != "direct-linux-boot" || length(var.constellation_cmdline) > 0
error_message = "constellation_cmdline must be set if constellation_boot_mode is 'direct-linux-boot'"
}
}
output "name" {
value = "${var.name}-qemu" // placeholder, as per "uid" output
}
output "ip_cidr_nodes" {
value = local.cidr_vpc_subnet_nodes
description = "Validation placeholder. Do not consume as output."
}

View File

@ -1,3 +1,11 @@
# Variables common to all CSPs
variable "name" {
type = string
default = "constellation"
description = "Name of the Constellation cluster."
}
variable "node_groups" {
type = map(object({
role = string
@ -14,91 +22,88 @@ variable "node_groups" {
description = "A map of node group names to node group configurations."
}
variable "image_id" {
type = string
description = "Path to the OS image for the cluster's nodes."
}
variable "custom_endpoint" {
type = string
default = ""
description = "Custom endpoint to use for the Kubernetes API server. If not set, the default endpoint will be used."
}
# QEMU-specific variables
variable "machine" {
type = string
default = "q35"
description = "machine type. use 'q35' for secure boot and 'pc' for non secure boot. See 'qemu-system-x86_64 -machine help'"
description = "Machine type. Use 'q35' for secure boot and 'pc' for non secure boot. See 'qemu-system-x86_64 -machine help'."
}
variable "libvirt_uri" {
type = string
description = "libvirt socket uri"
description = "URI of the Libvirt socket."
}
variable "constellation_boot_mode" {
type = string
description = "constellation boot mode. Can be 'uefi' or 'direct-linux-boot'"
description = "Constellation boot mode. Can be 'uefi' or 'direct-linux-boot'."
validation {
condition = anytrue([
var.constellation_boot_mode == "uefi",
var.constellation_boot_mode == "direct-linux-boot",
])
error_message = "constellation_boot_mode must be 'uefi' or 'direct-linux-boot'"
error_message = "constellation_boot_mode must be 'uefi' or 'direct-linux-boot'."
}
}
variable "constellation_os_image" {
type = string
description = "constellation OS file path"
}
variable "constellation_kernel" {
type = string
description = "constellation Kernel file path"
description = "Constellation Kernel file path."
default = ""
}
variable "constellation_initrd" {
type = string
description = "constellation initrd file path"
description = "Constellation initrd file path."
default = ""
}
variable "constellation_cmdline" {
type = string
description = "constellation kernel cmdline"
description = "Constellation kernel cmdline."
default = ""
}
variable "image_format" {
type = string
default = "qcow2"
description = "image format"
description = "Image format."
}
variable "firmware" {
type = string
default = "/usr/share/OVMF/OVMF_CODE.fd"
description = "path to UEFI firmware file. Use \"OVMF_CODE_4M.ms.fd\" on Ubuntu and \"OVMF_CODE.fd\" or \"OVMF_CODE.secboot.fd\" on Fedora."
description = "Path to UEFI firmware file. Use \"OVMF_CODE_4M.ms.fd\" on Ubuntu and \"OVMF_CODE.fd\" or \"OVMF_CODE.secboot.fd\" on Fedora."
}
variable "nvram" {
type = string
description = "path to UEFI NVRAM template file. Used for secure boot."
description = "Path to UEFI NVRAM template file. Used for secure boot."
}
variable "metadata_api_image" {
type = string
description = "container image of the QEMU metadata api server"
description = "Container image of the QEMU metadata API server."
}
variable "metadata_libvirt_uri" {
type = string
description = "libvirt uri for the metadata api server"
description = "Libvirt URI for the metadata API server."
}
variable "libvirt_socket_path" {
type = string
description = "path to libvirt socket in case of unix socket"
}
variable "name" {
type = string
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."
description = "Path to Libvirt socket in case of unix socket."
}

View File

@ -29,17 +29,17 @@ module "fetch_image" {
}
module "aws" {
source = "../../infrastructure/aws"
name = var.name
node_groups = var.node_groups
iam_instance_profile_worker_nodes = module.aws_iam.worker_nodes_instance_profile
iam_instance_profile_control_plane = module.aws_iam.control_plane_instance_profile
ami = module.fetch_image.image
region = local.region
zone = var.zone
debug = var.debug
enable_snp = var.enable_snp
custom_endpoint = var.custom_endpoint
source = "../../infrastructure/aws"
name = var.name
node_groups = var.node_groups
iam_instance_profile_name_worker_nodes = module.aws_iam.iam_instance_profile_name_worker_nodes
iam_instance_profile_name_control_plane = module.aws_iam.iam_instance_profile_name_control_plane
image_id = module.fetch_image.image
region = local.region
zone = var.zone
debug = var.debug
enable_snp = var.enable_snp
custom_endpoint = var.custom_endpoint
}
module "constellation" {
@ -53,15 +53,15 @@ module "constellation" {
uid = module.aws.uid
clusterEndpoint = module.aws.out_of_cluster_endpoint
inClusterEndpoint = module.aws.in_cluster_endpoint
initSecretHash = module.aws.initSecret
ipCidrNode = module.aws.ip_cidr_nodes
initSecretHash = module.aws.init_secret
ipCidrNode = module.aws.ip_cidr_node
apiServerCertSANs = module.aws.api_server_cert_sans
node_groups = var.node_groups
aws_config = {
region = local.region
zone = var.zone
iam_instance_profile_worker_nodes = module.aws_iam.worker_nodes_instance_profile
iam_instance_profile_control_plane = module.aws_iam.control_plane_instance_profile
region = local.region
zone = var.zone
iam_instance_profile_name_worker_nodes = module.aws_iam.iam_instance_profile_name_worker_nodes
iam_instance_profile_name_control_plane = module.aws_iam.iam_instance_profile_name_control_plane
}
depends_on = [module.aws, null_resource.ensure_yq]
}

View File

@ -19,7 +19,7 @@ module "fetch_image" {
module "azure_iam" {
source = "../../infrastructure/iam/azure"
region = var.location
location = var.location
service_principal_name = var.service_principal_name
resource_group_name = var.resource_group_name
}
@ -47,8 +47,8 @@ module "constellation" {
uid = module.azure.uid
clusterEndpoint = module.azure.out_of_cluster_endpoint
inClusterEndpoint = module.azure.in_cluster_endpoint
initSecretHash = module.azure.initSecret
ipCidrNode = module.azure.ip_cidr_nodes
initSecretHash = module.azure.init_secret
ipCidrNode = module.azure.ip_cidr_node
apiServerCertSANs = module.azure.api_server_cert_sans
node_groups = var.node_groups
azure_config = {
@ -59,7 +59,7 @@ module "constellation" {
userAssignedIdentity = module.azure_iam.uami_id
deployCSIDriver = var.deploy_csi_driver
secureBoot = var.secure_boot
maaURL = module.azure.attestationURL
maaURL = module.azure.attestation_url
networkSecurityGroupName = module.azure.network_security_group_name
loadBalancerName = module.azure.loadbalancer_name
}

View File

@ -41,8 +41,8 @@ resource "null_resource" "aws_config" {
command = <<EOT
./yq eval '.provider.aws.region = "${var.aws_config.region}"' -i constellation-conf.yaml
./yq eval '.provider.aws.zone = "${var.aws_config.zone}"' -i constellation-conf.yaml
./yq eval '.provider.aws.iamProfileControlPlane = "${var.aws_config.iam_instance_profile_control_plane}"' -i constellation-conf.yaml
./yq eval '.provider.aws.iamProfileWorkerNodes = "${var.aws_config.iam_instance_profile_worker_nodes}"' -i constellation-conf.yaml
./yq eval '.provider.aws.iamProfileControlPlane = "${var.aws_config.iam_instance_profile_name_control_plane}"' -i constellation-conf.yaml
./yq eval '.provider.aws.iamProfileWorkerNodes = "${var.aws_config.iam_instance_profile_name_worker_nodes}"' -i constellation-conf.yaml
EOT
}
triggers = {

View File

@ -78,10 +78,10 @@ variable "apiServerCertSANs" {
variable "aws_config" {
type = object({
region = string
zone = string
iam_instance_profile_worker_nodes = string
iam_instance_profile_control_plane = string
region = string
zone = string
iam_instance_profile_name_worker_nodes = string
iam_instance_profile_name_control_plane = string
})
description = "The cluster config for AWS."
default = null

View File

@ -54,16 +54,16 @@ module "constellation" {
uid = module.gcp.uid
clusterEndpoint = module.gcp.out_of_cluster_endpoint
inClusterEndpoint = module.gcp.in_cluster_endpoint
initSecretHash = module.gcp.initSecret
ipCidrNode = module.gcp.ip_cidr_nodes
initSecretHash = module.gcp.init_secret
ipCidrNode = module.gcp.ip_cidr_node
apiServerCertSANs = module.gcp.api_server_cert_sans
node_groups = var.node_groups
gcp_config = {
region = local.region
zone = var.zone
project = var.project
ipCidrPod = module.gcp.ip_cidr_pods
serviceAccountKey = module.gcp_iam.sa_key
ipCidrPod = module.gcp.ip_cidr_pod
serviceAccountKey = module.gcp_iam.service_account_key
}
depends_on = [module.gcp, null_resource.ensure_yq]
}