terraform: Terraform module for AWS (#2503)

This commit is contained in:
Adrian Stobbe 2023-11-08 19:10:01 +01:00 committed by GitHub
parent 0bac72261d
commit cea6204b37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
94 changed files with 912 additions and 87 deletions

View File

@ -0,0 +1,24 @@
name: Upload Terraform module
description: "Upload the Terraform module as an artifact."
runs:
using: "composite"
steps:
- name: Copy Terraform module
shell: bash
run: |
cp -r terraform terraform-module
find terraform-module -name "*.go" -type f -delete
find terraform-module -name "*.bazel" -type f -delete
- name: Zip terraform dir
shell: bash
run: |
zip -r terraform-module.zip terraform-module
- name: Upload artifact
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3
with:
name: terraform-module
path: terraform-module.zip
- name: Cleanup Terraform module dir
shell: bash
run: |
rm -r terraform-module

185
.github/workflows/e2e-test-tf-module.yml vendored Normal file
View File

@ -0,0 +1,185 @@
name: e2e test Terraform module
on:
workflow_dispatch:
inputs:
ref:
type: string
description: "Git ref to checkout"
required: false
cloudProvider:
description: "Which cloud provider to use."
type: choice
options:
- "aws"
default: "aws"
required: true
regionZone:
description: "Region or zone to create the cluster in. Leave empty for default region/zone."
type: string
required: false
image:
description: "Node image version of the cluster."
type: string
required: true
cliVersion:
description: "Constellation CLI version to use. Empty value means build from source."
type: string
default: ""
required: false
workflow_call:
inputs:
ref:
type: string
description: "Git ref to checkout"
required: false
cloudProvider:
description: "Which cloud provider to use."
type: string
default: "aws"
regionZone:
description: "Which zone to use."
type: string
image:
description: "Node image reference which is compatible with the current dev release version."
type: string
required: true
cliVersion:
description: "Constellation CLI version to use. Empty value means build from source."
type: string
default: ""
required: false
jobs:
build:
runs-on: ubuntu-22.04
permissions:
id-token: write
contents: read
packages: write
steps:
- name: Checkout
id: checkout
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
with:
ref: ${{ inputs.ref || github.head_ref }}
- name: Upload module
uses: ./.github/actions/upload_terraform_module
- name: Download Terraform module
uses: actions/download-artifact@v3
with:
name: terraform-module
- name: Unzip Terraform module
run: unzip terraform-module.zip
- name: Create resource prefix
id: create-prefix
shell: bash
run: |
run_id=${{ github.run_id }}
last_three="${run_id: -3}"
echo "prefix=e2e-${last_three}-${{ github.run_attempt }}" | tee -a "$GITHUB_OUTPUT"
- name: Create Terraform variable input file
if: inputs.cloudProvider == 'aws'
working-directory: ./terraform-module/aws-constellation
run: |
cat > terraform.tfvars <<EOF
name = "${{ steps.create-prefix.outputs.prefix }}"
image = "${{ inputs.image }}"
zone = "${{ inputs.regionZone || 'us-east-2c' }}"
name_prefix = "${{ steps.create-prefix.outputs.prefix }}"
node_groups = {
control_plane_default = {
role = "control-plane"
zone = "${{ inputs.regionZone || 'us-east-2c' }}"
instance_type = "m6a.xlarge"
disk_size = 30
disk_type = "gp3"
initial_count = 2
},
worker_default = {
role = "worker"
zone = "${{ inputs.regionZone || 'us-east-2c' }}"
instance_type = "m6a.xlarge"
disk_size = 30
disk_type = "gp3"
initial_count = 2
}
}
EOF
cat terraform.tfvars
- name: Install dependencies (Terraform)
run: |
sudo apt update && sudo apt install gpg
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
gpg --no-default-keyring --keyring /usr/share/keyrings/hashicorp-archive-keyring.gpg --fingerprint
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update
sudo apt install terraform=1.4.4-*
- name: Setup bazel
uses: ./.github/actions/setup_bazel_nix
with:
useCache: "true"
buildBuddyApiKey: ${{ secrets.BUILDBUDDY_ORG_API_KEY }}
- name: Log in to the Container registry
uses: ./.github/actions/container_registry_login
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build CLI
if: inputs.cliVersion == ''
uses: ./.github/actions/build_cli
with:
outputPath: "constellation"
enterpriseCLI: true
push: true
- name: Download CLI
if: inputs.cliVersion != ''
shell: bash
run: |
curl -fsSL -o constellation https://github.com/edgelesssys/constellation/releases/download/${{ inputs.cliVersion }}/constellation-linux-amd64
chmod u+x ./constellation
./constellation version
sudo sh -c 'echo "127.0.0.1 license.confidential.cloud" >> /etc/hosts'
- name: Login to AWS (IAM + Cluster role)
if: inputs.cloudProvider == 'aws'
uses: aws-actions/configure-aws-credentials@5fd3084fc36e372ff1fff382a39b10d03659f355 # v2.2.0
with:
role-to-assume: arn:aws:iam::795746500882:role/GithubActionsE2ETerraform
aws-region: eu-central-1
# extend token expiry to 6 hours to ensure constellation can terminate
role-duration-seconds: 21600
- name: Apply Terraform Cluster
id: apply_terraform
working-directory: ./terraform-module/${{ inputs.cloudProvider }}-constellation
run: |
cp ../../constellation .
terraform init
terraform apply -var-file=terraform.tfvars -auto-approve
- name: Destroy Terraform Cluster
# outcome is part of the steps context (https://docs.github.com/en/actions/learn-github-actions/contexts#steps-context)
if: always() && steps.apply_terraform.outcome != 'skipped'
working-directory: ./terraform-module/${{ inputs.cloudProvider }}-constellation
run: |
terraform init
terraform destroy -var-file=terraform.tfvars -auto-approve
- name: Verify cleanup
working-directory: ./terraform-module/${{ inputs.cloudProvider }}-constellation
run: |
if [ -f constellation-mastersecret.json ] || [ -f constellation-conf.yaml ]; then
echo "Files constellation-mastersecret.json or constellation-conf.yaml still exist"
exit 1
fi

View File

@ -10,7 +10,7 @@ jobs:
strategy:
fail-fast: false
matrix:
refStream: ["ref/main/stream/debug/?", "ref/release/stream/stable/?"]
refStream: ["ref/main/stream/nightly/?","ref/main/stream/debug/?", "ref/release/stream/stable/?"]
name: Find latest image
runs-on: ubuntu-22.04
permissions:
@ -19,6 +19,7 @@ jobs:
outputs:
image-main-debug: ${{ steps.relabel-output.outputs.image-main-debug }}
image-release-stable: ${{ steps.relabel-output.outputs.image-release-stable }}
image-main-nightly: ${{ steps.relabel-output.outputs.image-main-nightly }}
steps:
- name: Checkout
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0
@ -367,3 +368,17 @@ jobs:
uses: ./.github/workflows/e2e-windows.yml
with:
scheduled: ${{ github.event_name == 'schedule' }}
e2e-tf-module:
name: Test Terraform Module
permissions:
id-token: write
contents: read
packages: write
needs: [find-latest-image]
secrets: inherit
uses: ./.github/workflows/e2e-test-tf-module.yml
with:
cloudProvider: "aws"
regionZone: "eu-west-1b"
image: ${{ needs.find-latest-image.outputs.image-main-nightly }}

View File

@ -9,82 +9,6 @@ go_library(
"terraform.go",
"variables.go",
],
embedsrcs = [
"terraform/aws/.terraform.lock.hcl",
"terraform/aws/main.tf",
"terraform/aws/modules/instance_group/main.tf",
"terraform/aws/modules/instance_group/variables.tf",
"terraform/aws/modules/load_balancer_target/main.tf",
"terraform/aws/modules/load_balancer_target/output.tf",
"terraform/aws/modules/load_balancer_target/variables.tf",
"terraform/aws/modules/public_private_subnet/main.tf",
"terraform/aws/modules/public_private_subnet/output.tf",
"terraform/aws/modules/public_private_subnet/variables.tf",
"terraform/aws/outputs.tf",
"terraform/aws/variables.tf",
"terraform/azure/.terraform.lock.hcl",
"terraform/azure/main.tf",
"terraform/azure/modules/load_balancer_backend/main.tf",
"terraform/azure/modules/load_balancer_backend/outputs.tf",
"terraform/azure/modules/load_balancer_backend/variables.tf",
"terraform/azure/modules/scale_set/main.tf",
"terraform/azure/modules/scale_set/variables.tf",
"terraform/azure/outputs.tf",
"terraform/azure/variables.tf",
"terraform/gcp/.terraform.lock.hcl",
"terraform/gcp/main.tf",
"terraform/gcp/modules/instance_group/main.tf",
"terraform/gcp/modules/instance_group/outputs.tf",
"terraform/gcp/modules/instance_group/variables.tf",
"terraform/gcp/modules/loadbalancer/main.tf",
"terraform/gcp/modules/loadbalancer/variables.tf",
"terraform/gcp/outputs.tf",
"terraform/gcp/variables.tf",
"terraform/iam/aws/README.md",
"terraform/iam/aws/main.tf",
"terraform/iam/aws/outputs.tf",
"terraform/iam/aws/variables.tf",
"terraform/iam/azure/README.md",
"terraform/iam/azure/main.tf",
"terraform/iam/azure/outputs.tf",
"terraform/iam/azure/variables.tf",
"terraform/iam/gcp/README.md",
"terraform/iam/gcp/main.tf",
"terraform/iam/gcp/outputs.tf",
"terraform/iam/gcp/variables.tf",
"terraform/qemu/.terraform.lock.hcl",
"terraform/qemu/main.tf",
"terraform/qemu/modules/instance_group/domain.xsl",
"terraform/qemu/modules/instance_group/main.tf",
"terraform/qemu/modules/instance_group/outputs.tf",
"terraform/qemu/modules/instance_group/variables.tf",
"terraform/qemu/outputs.tf",
"terraform/qemu/variables.tf",
"terraform/openstack/.terraform.lock.hcl",
"terraform/openstack/main.tf",
"terraform/openstack/modules/instance_group/main.tf",
"terraform/openstack/modules/instance_group/outputs.tf",
"terraform/openstack/modules/instance_group/variables.tf",
"terraform/openstack/modules/loadbalancer/main.tf",
"terraform/openstack/modules/loadbalancer/variables.tf",
"terraform/openstack/outputs.tf",
"terraform/openstack/variables.tf",
"terraform/qemu/modules/instance_group/tdx_domain.xsl",
"terraform/iam/aws/.terraform.lock.hcl",
"terraform/iam/azure/.terraform.lock.hcl",
"terraform/iam/gcp/.terraform.lock.hcl",
"terraform/gcp/modules/internal_load_balancer/main.tf",
"terraform/gcp/modules/internal_load_balancer/variables.tf",
"terraform/gcp/modules/jump_host/main.tf",
"terraform/gcp/modules/jump_host/outputs.tf",
"terraform/gcp/modules/jump_host/variables.tf",
"terraform/aws/modules/jump_host/main.tf",
"terraform/aws/modules/jump_host/output.tf",
"terraform/aws/modules/jump_host/variables.tf",
"terraform/azure/modules/jump_host/main.tf",
"terraform/azure/modules/jump_host/variables.tf",
"terraform/azure/modules/jump_host/outputs.tf",
],
importpath = "github.com/edgelesssys/constellation/v2/cli/internal/terraform",
visibility = ["//cli:__subpackages__"],
deps = [
@ -92,6 +16,7 @@ go_library(
"//internal/cloud/cloudprovider",
"//internal/constants",
"//internal/file",
"//terraform",
"@com_github_hashicorp_go_version//:go-version",
"@com_github_hashicorp_hc_install//:hc-install",
"@com_github_hashicorp_hc_install//fs",

View File

@ -7,22 +7,18 @@ SPDX-License-Identifier: AGPL-3.0-only
package terraform
import (
"embed"
"errors"
"io/fs"
slashpath "path"
"path/filepath"
"strings"
"github.com/edgelesssys/constellation/v2/terraform"
"github.com/edgelesssys/constellation/v2/internal/file"
"github.com/spf13/afero"
)
//go:embed terraform/*
//go:embed terraform/*/.terraform.lock.hcl
//go:embed terraform/iam/*/.terraform.lock.hcl
var terraformFS embed.FS
// prepareWorkspace loads the embedded Terraform files,
// and writes them into the workspace.
func prepareWorkspace(rootDir string, fileHandler file.Handler, workingDir string) error {
@ -32,7 +28,7 @@ func prepareWorkspace(rootDir string, fileHandler file.Handler, workingDir strin
// terraformCopier copies the embedded Terraform files into the workspace.
func terraformCopier(fileHandler file.Handler, rootDir, workingDir string) error {
goEmbedRootDir := filepath.ToSlash(rootDir)
return fs.WalkDir(terraformFS, goEmbedRootDir, func(path string, d fs.DirEntry, err error) error {
return fs.WalkDir(terraform.Assets, goEmbedRootDir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
@ -41,7 +37,7 @@ func terraformCopier(fileHandler file.Handler, rootDir, workingDir string) error
}
goEmbedPath := filepath.ToSlash(path)
content, err := terraformFS.ReadFile(goEmbedPath)
content, err := terraform.Assets.ReadFile(goEmbedPath)
if err != nil {
return err
}

View File

@ -30,6 +30,10 @@ Releases should be performed using [the automated release pipeline](https://gith
git push origin ${working_branch}
```
### Update CLI version for Terraform module
Update the `version` inside `terraform/constellation-cluster/install-constellation.sh` to the new release.
### Patch release
1. `cherry-pick` (only) the required commits from `main`

View File

@ -172,7 +172,7 @@ const (
// TerraformIAMUpgradeBackupDir is the directory name being used to backup the pre-upgrade state of iam in an upgrade.
TerraformIAMUpgradeBackupDir = "terraform-iam-backup"
// TerraformEmbeddedDir is the name of the base directory embedded in the CLI binary containing the Terraform files.
TerraformEmbeddedDir = "terraform"
TerraformEmbeddedDir = "infrastructure"
// UpgradeDir is the name of the directory being used for cluster upgrades.
UpgradeDir = "constellation-upgrade"
// ControlPlaneDefault is the name of the default control plane worker group.

84
terraform/BUILD.bazel Normal file
View File

@ -0,0 +1,84 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "terraform",
srcs = ["assets.go"],
embedsrcs = [
"infrastructure/aws/.terraform.lock.hcl",
"infrastructure/aws/main.tf",
"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",
"infrastructure/azure/.terraform.lock.hcl",
"infrastructure/azure/main.tf",
"infrastructure/azure/modules/jump_host/main.tf",
"infrastructure/azure/modules/jump_host/outputs.tf",
"infrastructure/azure/modules/jump_host/variables.tf",
"infrastructure/azure/modules/load_balancer_backend/main.tf",
"infrastructure/azure/modules/load_balancer_backend/outputs.tf",
"infrastructure/azure/modules/load_balancer_backend/variables.tf",
"infrastructure/azure/modules/scale_set/main.tf",
"infrastructure/azure/modules/scale_set/variables.tf",
"infrastructure/azure/outputs.tf",
"infrastructure/azure/variables.tf",
"infrastructure/gcp/.terraform.lock.hcl",
"infrastructure/gcp/main.tf",
"infrastructure/gcp/modules/instance_group/main.tf",
"infrastructure/gcp/modules/instance_group/outputs.tf",
"infrastructure/gcp/modules/instance_group/variables.tf",
"infrastructure/gcp/modules/internal_load_balancer/main.tf",
"infrastructure/gcp/modules/internal_load_balancer/variables.tf",
"infrastructure/gcp/modules/jump_host/main.tf",
"infrastructure/gcp/modules/jump_host/outputs.tf",
"infrastructure/gcp/modules/jump_host/variables.tf",
"infrastructure/gcp/modules/loadbalancer/main.tf",
"infrastructure/gcp/modules/loadbalancer/variables.tf",
"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",
"infrastructure/openstack/.terraform.lock.hcl",
"infrastructure/openstack/main.tf",
"infrastructure/openstack/modules/instance_group/main.tf",
"infrastructure/openstack/modules/instance_group/outputs.tf",
"infrastructure/openstack/modules/instance_group/variables.tf",
"infrastructure/openstack/modules/loadbalancer/main.tf",
"infrastructure/openstack/modules/loadbalancer/variables.tf",
"infrastructure/openstack/outputs.tf",
"infrastructure/openstack/variables.tf",
"infrastructure/qemu/.terraform.lock.hcl",
"infrastructure/qemu/main.tf",
"infrastructure/qemu/modules/instance_group/domain.xsl",
"infrastructure/qemu/modules/instance_group/main.tf",
"infrastructure/qemu/modules/instance_group/outputs.tf",
"infrastructure/qemu/modules/instance_group/tdx_domain.xsl",
"infrastructure/qemu/modules/instance_group/variables.tf",
"infrastructure/qemu/outputs.tf",
"infrastructure/qemu/variables.tf",
],
importpath = "github.com/edgelesssys/constellation/v2/terraform",
visibility = ["//visibility:public"],
)

16
terraform/assets.go Normal file
View File

@ -0,0 +1,16 @@
/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package terraform
import "embed"
// Assets are the exported Terraform template files.
//
//go:embed infrastructure/*
//go:embed infrastructure/*/.terraform.lock.hcl
//go:embed infrastructure/iam/*/.terraform.lock.hcl
var Assets embed.FS

View File

@ -0,0 +1,99 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/aws" {
version = "5.17.0"
constraints = "5.17.0"
hashes = [
"h1:+riTtJ8Tqjd6js1SGim+926BtDuxy8Jn4F+xV8LXvvg=",
"h1:OJMhYliR4PFDrTtOPocwq4NfuYZVGmmxwInPmcIC1x0=",
"h1:U+EDfeUqefebA1h7KyBMD1xH0h311LMi7wijPDPkC/0=",
"h1:fKgoYBRyK55vJSChUHPptDUQuXqjfDjVKVJ11+scq64=",
"h1:rplvK7UGP2FuzM44t2eRX+QYYPC0aUIoKdi5XayRI8M=",
"zh:0087b9dd2c9c638fd63e527e5b9b70988008e263d480a199f180efe5a4f070f0",
"zh:0fd532a4fd03ddef11f0502ff9fe4343443e1ae805cb088825a71d6d48906ec7",
"zh:16411e731100cd15f7e165f53c23be784b2c86c2fcfd34781e0642d17090d342",
"zh:251d520927e77f091e2ec6302e921d839a2430ac541c6a461aed7c08fb5eae12",
"zh:4919e69682dc2a8c32d44f6ebc038a52c9f40af9c61cb574b64e322800d6a794",
"zh:5334c60759d5f76bdc51355d1a3ebcc451d4d20f632f5c73b6e55c52b5dc9e52",
"zh:7341a2b7247572eba0d0486094a870b872967702ec0ac7af728c2df2c30af4e5",
"zh:81d1b1cb2cac6b3922a05adab69543b678f344a01debd54500263700dad7a288",
"zh:882bc8e15ef6d4020a07321ec4c056977c5c1d96934118032922561d29504d43",
"zh:8cd4871ef2b03fd916de1a6dc7eb8a81a354c421177d4334a2e3308e50215e41",
"zh:97e12fe6529b21298adf1046c5e20ac35d0569c836a6f385ff041e257e00cfd2",
"zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
"zh:9f5baf5d59b9f3cf5504d1fa975f10f27da3791896a9e18ece47c258bac17634",
"zh:dffafba6731ac1db1c540bdbd6a8c878486b71de9d0ca1d23c5c00a6c3c14d80",
"zh:fa7440c3c15a42fc5731444d324ced75407d417bfe3184661ae47d40a9718dce",
]
}
provider "registry.terraform.io/hashicorp/local" {
version = "2.4.0"
hashes = [
"h1:7RnIbO3CFakblTJs7o0mUiY44dc9xGYsLhSNFSNS1Ds=",
"h1:Bs7LAkV/iQTLv72j+cTMrvx2U3KyXrcVHaGbdns1NcE=",
"h1:OMqURhlP2IgLEgUCzSlaKXyb/IbnKForgDSjZF/NY4Y=",
"h1:R97FTYETo88sT2VHfMgkPU3lzCsZLunPftjSI5vfKe8=",
"h1:ZUEYUmm2t4vxwzxy1BvN1wL6SDWrDxfH7pxtzX8c6d0=",
"zh:53604cd29cb92538668fe09565c739358dc53ca56f9f11312b9d7de81e48fab9",
"zh:66a46e9c508716a1c98efbf793092f03d50049fa4a83cd6b2251e9a06aca2acf",
"zh:70a6f6a852dd83768d0778ce9817d81d4b3f073fab8fa570bff92dcb0824f732",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:82a803f2f484c8b766e2e9c32343e9c89b91997b9f8d2697f9f3837f62926b35",
"zh:9708a4e40d6cc4b8afd1352e5186e6e1502f6ae599867c120967aebe9d90ed04",
"zh:973f65ce0d67c585f4ec250c1e634c9b22d9c4288b484ee2a871d7fa1e317406",
"zh:c8fa0f98f9316e4cfef082aa9b785ba16e36ff754d6aba8b456dab9500e671c6",
"zh:cfa5342a5f5188b20db246c73ac823918c189468e1382cb3c48a9c0c08fc5bf7",
"zh:e0e2b477c7e899c63b06b38cd8684a893d834d6d0b5e9b033cedc06dd7ffe9e2",
"zh:f62d7d05ea1ee566f732505200ab38d94315a4add27947a60afa29860822d3fc",
"zh:fa7ce69dde358e172bd719014ad637634bbdabc49363104f4fca759b4b73f2ce",
]
}
provider "registry.terraform.io/hashicorp/null" {
version = "3.2.1"
hashes = [
"h1:FbGfc+muBsC17Ohy5g806iuI1hQc4SIexpYCrQHQd8w=",
"h1:tSj1mL6OQ8ILGqR2mDu7OYYYWf+hoir0pf9KAQ8IzO8=",
"h1:vUW21lLLsKlxtBf0QF7LKJreKxs0CM7YXGzqW1N/ODY=",
"h1:wqgRvlyVIbkCeCQs+5jj6zVuQL0KDxZZtNofGqqlSdI=",
"h1:ydA0/SNRVB1o95btfshvYsmxA+jZFRZcvKzZSB+4S1M=",
"zh:58ed64389620cc7b82f01332e27723856422820cfd302e304b5f6c3436fb9840",
"zh:62a5cc82c3b2ddef7ef3a6f2fedb7b9b3deff4ab7b414938b08e51d6e8be87cb",
"zh:63cff4de03af983175a7e37e52d4bd89d990be256b16b5c7f919aff5ad485aa5",
"zh:74cb22c6700e48486b7cabefa10b33b801dfcab56f1a6ac9b6624531f3d36ea3",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:79e553aff77f1cfa9012a2218b8238dd672ea5e1b2924775ac9ac24d2a75c238",
"zh:a1e06ddda0b5ac48f7e7c7d59e1ab5a4073bbcf876c73c0299e4610ed53859dc",
"zh:c37a97090f1a82222925d45d84483b2aa702ef7ab66532af6cbcfb567818b970",
"zh:e4453fbebf90c53ca3323a92e7ca0f9961427d2f0ce0d2b65523cc04d5d999c2",
"zh:e80a746921946d8b6761e77305b752ad188da60688cfd2059322875d363be5f5",
"zh:fbdb892d9822ed0e4cb60f2fedbdbb556e4da0d88d3b942ae963ed6ff091e48f",
"zh:fca01a623d90d0cad0843102f9b8b9fe0d3ff8244593bd817f126582b52dd694",
]
}
provider "registry.terraform.io/hashicorp/random" {
version = "3.5.1"
constraints = "3.5.1"
hashes = [
"h1:3hjTP5tQBspPcFAJlfafnWrNrKnr7J4Cp0qB9jbqf30=",
"h1:6FVyQ/aG6tawPam6B+oFjgdidKd83uG9n7dOSQ66HBA=",
"h1:IL9mSatmwov+e0+++YX2V6uel+dV6bn+fC/cnGDK3Ck=",
"h1:VSnd9ZIPyfKHOObuQCaKfnjIHRtR7qTw19Rz8tJxm+k=",
"h1:sZ7MTSD4FLekNN2wSNFGpM+5slfvpm5A/NLVZiB7CO0=",
"zh:04e3fbd610cb52c1017d282531364b9c53ef72b6bc533acb2a90671957324a64",
"zh:119197103301ebaf7efb91df8f0b6e0dd31e6ff943d231af35ee1831c599188d",
"zh:4d2b219d09abf3b1bb4df93d399ed156cadd61f44ad3baf5cf2954df2fba0831",
"zh:6130bdde527587bbe2dcaa7150363e96dbc5250ea20154176d82bc69df5d4ce3",
"zh:6cc326cd4000f724d3086ee05587e7710f032f94fc9af35e96a386a1c6f2214f",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:b6d88e1d28cf2dfa24e9fdcc3efc77adcdc1c3c3b5c7ce503a423efbdd6de57b",
"zh:ba74c592622ecbcef9dc2a4d81ed321c4e44cddf7da799faa324da9bf52a22b2",
"zh:c7c5cde98fe4ef1143bd1b3ec5dc04baf0d4cc3ca2c5c7d40d17c0e9b2076865",
"zh:dac4bad52c940cd0dfc27893507c1e92393846b024c5a9db159a93c534a3da03",
"zh:de8febe2a2acd9ac454b844a4106ed295ae9520ef54dc8ed2faf29f12716b602",
"zh:eab0d0495e7e711cca367f7d4df6e322e6c562fc52151ec931176115b83ed014",
]
}

View File

@ -0,0 +1,28 @@
locals {
image_ref = startswith(var.image, "v") ? "ref/-/stream/stable/${var.image}" : var.image
fetch_ami_command = <<EOT
curl -s https://cdn.confidential.cloud/constellation/v2/${local.image_ref}/image/info.json | \
./yq eval '.list[] | select(.csp == "aws" and .attestationVariant == "${var.attestation_variant}" and .region == "${var.region}") | .reference' - | tr -d '\n' > "${path.module}/ami.txt"
echo -n "AMI: "
cat "${path.module}/ami.txt"
EOT
}
resource "null_resource" "fetch_ami" {
provisioner "local-exec" {
command = local.fetch_ami_command
environment = {
attestation_variant = var.attestation_variant
}
}
triggers = {
always_run = "${timestamp()}"
}
}
data "local_file" "ami" {
filename = "${path.module}/ami.txt"
depends_on = [null_resource.fetch_ami]
}

View File

@ -0,0 +1,4 @@
output "ami" {
description = "The fetched AMI."
value = data.local_file.ami.content
}

View File

@ -0,0 +1,14 @@
variable "attestation_variant" {
description = "The attestation variant to fetch AMI data for."
type = string
}
variable "region" {
description = "The AWS region to fetch AMI data for."
type = string
}
variable "image" {
description = "The image reference or semantical release version to fetch AMI data for."
type = string
}

View File

@ -0,0 +1,43 @@
#!/usr/bin/env bash
VERSION="v4.35.2"
if [[ -f ./yq ]] && ./yq --version | grep -q "${VERSION}"; then
echo "yq is already available and up to date."
exit 0
fi
if [[ -f ./yq ]]; then
echo "yq is already available but not at the required version. Replacing with ${VERSION}."
rm -f yq
fi
echo "Fetching yq ${VERSION}"
OS=$(uname -s)
ARCH=$(uname -m)
URL=""
if [[ ${OS} == "Darwin" ]]; then
if [[ ${ARCH} == "arm64" ]]; then
URL="https://github.com/mikefarah/yq/releases/download/${VERSION}/yq_darwin_arm64"
elif [[ ${ARCH} == "x86_64" ]]; then
URL="https://github.com/mikefarah/yq/releases/download/${VERSION}/yq_darwin_amd64"
fi
elif [[ ${OS} == "Linux" ]]; then
if [[ ${ARCH} == "x86_64" ]]; then
URL="https://github.com/mikefarah/yq/releases/download/${VERSION}/yq_linux_amd64"
elif [[ ${ARCH} == "arm64" ]]; then
URL="https://github.com/mikefarah/yq/releases/download/${VERSION}/yq_linux_arm64"
fi
fi
if [[ -z ${URL} ]]; then
echo "OS \"${OS}\" and/or architecture \"${ARCH}\" is not supported."
exit 1
else
echo "Downloading yq from ${URL}"
curl -o yq -L "${URL}"
chmod +x ./yq
./yq --version
if ! ./yq --version | grep -q "${VERSION}"; then # check that yq was installed correctly
echo "Version is incorrect"
exit 1
fi
fi

View File

@ -0,0 +1,67 @@
locals {
region = substr(var.zone, 0, length(var.zone) - 1)
}
module "aws_iam" {
source = "../infrastructure/iam/aws"
name_prefix = var.name_prefix
region = local.region
}
resource "null_resource" "ensure_yq" {
provisioner "local-exec" {
command = <<EOT
${path.module}/install-yq.sh
EOT
}
triggers = {
always_run = timestamp()
}
}
module "fetch_ami" {
source = "./fetch-ami"
attestation_variant = var.enable_snp ? "aws-sev-snp" : "aws-nitro-tpm"
region = local.region
image = var.image
depends_on = [module.aws_iam, null_resource.ensure_yq]
}
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_ami.ami
region = local.region
zone = var.zone
debug = var.debug
enable_snp = var.enable_snp
custom_endpoint = var.custom_endpoint
}
module "constellation" {
source = "../constellation-cluster"
csp = "aws"
name = var.name
image = var.image
microservice_version = var.microservice_version
kubernetes_version = var.kubernetes_version
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
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
}
depends_on = [module.aws, null_resource.ensure_yq]
}

View File

@ -0,0 +1,71 @@
variable "name" {
type = string
description = "Name of the Constellation cluster."
}
variable "image" {
type = string
description = "Node image reference or semantical release version. The field is required to resolve the AMI."
}
variable "microservice_version" {
type = string
description = "Microservice version. When not set, the latest default version will be used."
default = ""
}
variable "kubernetes_version" {
type = string
description = "Kubernetes version. When not set, the latest default version will be used."
default = ""
}
variable "node_groups" {
type = map(object({
role = string
initial_count = optional(number)
instance_type = string
disk_size = number
disk_type = string
zone = string
}))
description = "A map of node group names to node group configurations."
validation {
condition = can([for group in var.node_groups : group.role == "control-plane" || group.role == "worker"])
error_message = "The role has to be 'control-plane' or 'worker'."
}
}
variable "zone" {
type = string
description = "The AWS availability zone name to create the cluster in."
}
variable "debug" {
type = bool
default = false
description = "DON'T USE IN PRODUCTION: Enable debug mode and allow the use of debug images."
}
variable "enable_snp" {
type = bool
default = true
description = "Enable AMD SEV-SNP."
}
variable "custom_endpoint" {
type = string
default = ""
description = "Custom endpoint (DNS Name) to use for the Constellation API server. If not set, the default endpoint will be used."
}
variable "internal_load_balancer" {
type = bool
default = false
description = "Use an internal load balancer."
}
variable "name_prefix" {
type = string
description = "Prefix for all resources."
}

View File

@ -0,0 +1,25 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/null" {
version = "3.2.1"
hashes = [
"h1:FbGfc+muBsC17Ohy5g806iuI1hQc4SIexpYCrQHQd8w=",
"h1:tSj1mL6OQ8ILGqR2mDu7OYYYWf+hoir0pf9KAQ8IzO8=",
"h1:vUW21lLLsKlxtBf0QF7LKJreKxs0CM7YXGzqW1N/ODY=",
"h1:wqgRvlyVIbkCeCQs+5jj6zVuQL0KDxZZtNofGqqlSdI=",
"h1:ydA0/SNRVB1o95btfshvYsmxA+jZFRZcvKzZSB+4S1M=",
"zh:58ed64389620cc7b82f01332e27723856422820cfd302e304b5f6c3436fb9840",
"zh:62a5cc82c3b2ddef7ef3a6f2fedb7b9b3deff4ab7b414938b08e51d6e8be87cb",
"zh:63cff4de03af983175a7e37e52d4bd89d990be256b16b5c7f919aff5ad485aa5",
"zh:74cb22c6700e48486b7cabefa10b33b801dfcab56f1a6ac9b6624531f3d36ea3",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:79e553aff77f1cfa9012a2218b8238dd672ea5e1b2924775ac9ac24d2a75c238",
"zh:a1e06ddda0b5ac48f7e7c7d59e1ab5a4073bbcf876c73c0299e4610ed53859dc",
"zh:c37a97090f1a82222925d45d84483b2aa702ef7ab66532af6cbcfb567818b970",
"zh:e4453fbebf90c53ca3323a92e7ca0f9961427d2f0ce0d2b65523cc04d5d999c2",
"zh:e80a746921946d8b6761e77305b752ad188da60688cfd2059322875d363be5f5",
"zh:fbdb892d9822ed0e4cb60f2fedbdbb556e4da0d88d3b942ae963ed6ff091e48f",
"zh:fca01a623d90d0cad0843102f9b8b9fe0d3ff8244593bd817f126582b52dd694",
]
}

View File

@ -0,0 +1,31 @@
#!/usr/bin/env bash
if [[ -f ./constellation ]]; then # needed to allow using devbuilds
echo "constellation CLI is already available."
exit 0
fi
version="v2.13.0"
os=$(uname -s)
arch=$(uname -m)
url=""
if [[ ${os} == "Darwin" ]]; then
if [[ ${arch} == "arm64" ]]; then
url="https://github.com/edgelesssys/constellation/releases/download/${version}/constellation-darwin-arm64"
elif [[ ${arch} == "x86_64" ]]; then
url="https://github.com/edgelesssys/constellation/releases/download/${version}/constellation-darwin-amd64"
fi
elif [[ ${os} == "Linux" ]]; then
if [[ ${arch} == "x86_64" ]]; then
url="https://github.com/edgelesssys/constellation/releases/download/${version}/constellation-linux-amd64"
elif [[ ${arch} == "arm64" ]]; then
url="https://github.com/edgelesssys/constellation/releases/download/${version}/constellation-linux-arm64"
fi
fi
echo "Fetching constellation ${version}"
if [[ -z ${url} ]]; then
echo "OS \"${os}\" and/or architecture \"${arch}\" is not supported."
exit 1
else
curl -o constellation -L "${url}"
chmod +x constellation
fi

View File

@ -0,0 +1,109 @@
locals {
yq_node_groups = join("\n", flatten([
for name, group in var.node_groups : [
"./yq eval '.nodeGroups.${name}.role = \"${group.role}\"' -i constellation-conf.yaml",
"./yq eval '.nodeGroups.${name}.zone = \"${group.zone}\"' -i constellation-conf.yaml",
"./yq eval '.nodeGroups.${name}.instanceType = \"${group.instance_type}\"' -i constellation-conf.yaml",
"./yq eval '.nodeGroups.${name}.stateDiskSizeGB = ${group.disk_size}' -i constellation-conf.yaml",
"./yq eval '.nodeGroups.${name}.stateDiskType = \"${group.disk_type}\"' -i constellation-conf.yaml",
"./yq eval '.nodeGroups.${name}.initialCount = ${group.initial_count}' -i constellation-conf.yaml"
]
]))
}
resource "null_resource" "ensure_cli" {
provisioner "local-exec" {
command = <<EOT
${path.module}/install-constellation.sh
EOT
}
triggers = {
always_run = timestamp()
}
}
// terraform_data resource so that it is run only once
resource "terraform_data" "config_generate" {
provisioner "local-exec" {
command = <<EOT
./constellation config generate ${var.csp}
EOT
}
depends_on = [
null_resource.ensure_cli
]
}
resource "null_resource" "config" {
provisioner "local-exec" {
command = <<EOT
if [ "${var.csp}" = "aws" ]; then
./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
fi
./yq eval '.name = "${var.name}"' -i constellation-conf.yaml
if [ "${var.image}" != "" ]; then
./yq eval '.image = "${var.image}"' -i constellation-conf.yaml
fi
if [ "${var.kubernetes_version}" != "" ]; then
./yq eval '.kubernetesVersion = "${var.kubernetes_version}"' -i constellation-conf.yaml
fi
if [ "${var.microservice_version}" != "" ]; then
./yq eval '.microserviceVersion = "${var.microservice_version}"' -i constellation-conf.yaml
fi
${local.yq_node_groups}
./constellation config fetch-measurements
EOT
}
depends_on = [
terraform_data.config_generate
]
triggers = {
always_run = timestamp()
}
}
resource "null_resource" "infra_state" {
provisioner "local-exec" {
command = <<EOT
./yq eval '.infrastructure.uid = "${var.uid}"' -i constellation-state.yaml
./yq eval '.infrastructure.inClusterEndpoint = "${var.inClusterEndpoint}"' -i constellation-state.yaml
./yq eval '.infrastructure.clusterEndpoint = "${var.clusterEndpoint}"' -i constellation-state.yaml
./yq eval '.infrastructure.initSecret = "'"$(echo "${var.initSecretHash}" | tr -d '\n' | hexdump -ve '/1 "%02x"')"'"' -i constellation-state.yaml
./yq eval '.infrastructure.apiServerCertSANs = ${jsonencode(var.apiServerCertSANs)}' -i constellation-state.yaml
./yq eval '.infrastructure.name = "${var.name}"' -i constellation-state.yaml
./yq eval '.infrastructure.ipCidrNode = "${var.ipCidrNode}"' -i constellation-state.yaml
EOT
}
depends_on = [
terraform_data.config_generate
]
triggers = {
always_run = timestamp()
}
}
resource "null_resource" "apply" {
provisioner "local-exec" {
command = "./constellation apply --debug --yes --skip-phases infrastructure"
}
provisioner "local-exec" {
when = destroy
command = "./constellation terminate --yes && rm constellation-conf.yaml constellation-mastersecret.json ./fetch-ami/ami.txt && rm -r constellation-upgrade"
}
depends_on = [
null_resource.infra_state, null_resource.config, null_resource.ensure_cli
]
triggers = {
always_run = timestamp()
}
}

View File

@ -0,0 +1,86 @@
variable "csp" {
type = string
description = "The CSP to create the cluster in."
validation {
condition = var.csp == "aws"
error_message = "The CSP must be 'aws'."
}
}
variable "node_groups" {
type = map(object({
role = string
initial_count = optional(number)
instance_type = string
disk_size = number
disk_type = string
zone = string
}))
description = "A map of node group names to node group configurations."
validation {
condition = can([for group in var.node_groups : group.role == "control-plane" || group.role == "worker"])
error_message = "The role has to be 'control-plane' or 'worker'."
}
}
variable "name" {
type = string
description = "Name used in the cluster's named resources / cluster name."
}
variable "uid" {
type = string
description = "The UID of the Constellation."
}
variable "clusterEndpoint" {
type = string
description = "Endpoint of the cluster."
}
variable "inClusterEndpoint" {
type = string
description = "The endpoint the cluster uses to reach itself. This might differ from the ClusterEndpoint in case e.g. an internal load balancer is used."
}
variable "initSecretHash" {
type = string
description = "Init secret hash."
}
variable "ipCidrNode" {
type = string
description = "Node IP CIDR."
}
variable "apiServerCertSANs" {
type = list(string)
description = "List of additional SANs (Subject Alternative Names) for the Kubernetes API server certificate."
}
variable "aws_config" {
type = object({
region = string
zone = string
iam_instance_profile_worker_nodes = string
iam_instance_profile_control_plane = string
})
description = "The cluster config for AWS."
default = null
}
variable "image" {
type = string
description = "The node image reference or semantical release version."
}
variable "kubernetes_version" {
type = string
description = "Kubernetes version."
}
variable "microservice_version" {
type = string
description = "Microservice version."
}

View File

@ -256,4 +256,3 @@ moved {
from = module.load_balancer_target_bootstrapper
to = module.load_balancer_targets["bootstrapper"]
}