Skip to content

Commit 5cf3383

Browse files
authored
Merge pull request jupyterhub#2652 from manics/aws-curvenote
EKS in AWS Curvenote account
2 parents 4214275 + c00c237 commit 5cf3383

22 files changed

+1231
-0
lines changed
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# See terraform/aws/curvenote/README.md
2+
name: Terraform aws-curvenote
3+
4+
on:
5+
push:
6+
branches:
7+
- main
8+
paths:
9+
- "terraform/aws/curvenote/**"
10+
- .github/workflows/terraform-deploy-aws-curvenote.yml
11+
workflow_dispatch:
12+
13+
# Only allow one workflow to run at a time
14+
concurrency: terraform-deploy-aws-curvenote
15+
16+
env:
17+
TFPLAN: aws-curvenote.tfplan
18+
AWS_DEPLOYMENT_ROLE: arn:aws:iam::166088433508:role/binderhub-github-oidc-mybinderorgdeploy-terraform
19+
AWS_REGION: us-east-2
20+
WORKDIR: ./terraform/aws/curvenote
21+
22+
jobs:
23+
terraform-plan:
24+
runs-on: ubuntu-22.04
25+
timeout-minutes: 10
26+
# These permissions are needed to interact with GitHub's OIDC Token endpoint.
27+
permissions:
28+
id-token: write
29+
contents: read
30+
defaults:
31+
run:
32+
working-directory: ${{ env.WORKDIR }}
33+
outputs:
34+
apply: ${{ steps.terraform-plan.outputs.apply }}
35+
36+
steps:
37+
- uses: actions/checkout@v3
38+
39+
- name: Configure AWS credentials
40+
uses: aws-actions/configure-aws-credentials@v2
41+
with:
42+
role-to-assume: ${{ env.AWS_DEPLOYMENT_ROLE }}
43+
aws-region: ${{ env.AWS_REGION }}
44+
role-session-name: terraform-plan
45+
46+
# Capture the console output of terraform plan to a file, so we can include
47+
# it as a job summary in the Actions view:
48+
# https://github.blog/2022-05-09-supercharging-github-actions-with-job-summaries/
49+
- name: Terraform plan
50+
id: terraform-plan
51+
run: |
52+
terraform init
53+
terraform plan -out="${TFPLAN}" -detailed-exitcode -no-color | tee tfplan.stdout
54+
# Get the exit code of the terraform plan command, not the tee command.
55+
EXIT_CODE="${PIPESTATUS[0]}"
56+
if [ $EXIT_CODE -eq 0 ]; then
57+
echo "No changes"
58+
echo "apply=false" >> "$GITHUB_OUTPUT"
59+
elif [ $EXIT_CODE -eq 2 ]; then
60+
echo "Changes found"
61+
echo "apply=true" >> "$GITHUB_OUTPUT"
62+
else
63+
echo "Terraform plan failed"
64+
exit $EXIT_CODE
65+
fi
66+
67+
# Skip the first bit of the terraform plan stdout
68+
# https://unix.stackexchange.com/a/205680
69+
- name: Set job summary
70+
if: steps.terraform-plan.outputs.apply == 'true'
71+
run: |
72+
echo '### Terraform Plan summary' >> $GITHUB_STEP_SUMMARY
73+
echo '```' >> $GITHUB_STEP_SUMMARY
74+
sed -n '/Terraform will perform the following/,$p' tfplan.stdout >> $GITHUB_STEP_SUMMARY
75+
echo '```' >> $GITHUB_STEP_SUMMARY
76+
77+
- name: Install age
78+
if: steps.terraform-plan.outputs.apply == 'true'
79+
run: |
80+
sudo apt-get update -y -q
81+
sudo apt-get install -y -q age
82+
83+
- name: Encrypt plan
84+
if: steps.terraform-plan.outputs.apply == 'true'
85+
run: |
86+
echo ${{ secrets.TFPLAN_ARTIFACT_SECRET_KEY }} > tfplan.key
87+
age --identity tfplan.key --encrypt --output "${TFPLAN}.enc" "${TFPLAN}"
88+
89+
- name: Upload plan
90+
if: steps.terraform-plan.outputs.apply == 'true'
91+
uses: actions/upload-artifact@v3
92+
with:
93+
name: ${{ env.TFPLAN }}
94+
path: ${{ env.WORKDIR }}/${{ env.TFPLAN }}.enc
95+
if-no-files-found: error
96+
97+
terraform-apply:
98+
needs:
99+
- terraform-plan
100+
runs-on: ubuntu-22.04
101+
timeout-minutes: 60
102+
# This environment requires approval before the deploy is run.
103+
environment: aws-curvenote
104+
# These permissions are needed to interact with GitHub's OIDC Token endpoint.
105+
permissions:
106+
id-token: write
107+
contents: read
108+
defaults:
109+
run:
110+
working-directory: ${{ env.WORKDIR }}
111+
if: needs.terraform-plan.outputs.apply == 'true'
112+
113+
steps:
114+
- uses: actions/checkout@v3
115+
116+
- name: Configure AWS credentials
117+
uses: aws-actions/configure-aws-credentials@v2
118+
with:
119+
role-to-assume: ${{ env.AWS_DEPLOYMENT_ROLE }}
120+
aws-region: ${{ env.AWS_REGION }}
121+
role-session-name: terraform-apply
122+
123+
- name: Download plan
124+
uses: actions/download-artifact@v3
125+
with:
126+
name: ${{ env.TFPLAN }}
127+
path: ${{ env.WORKDIR }}
128+
129+
- name: Install age
130+
run: |
131+
sudo apt-get update -y -q
132+
sudo apt-get install -y -q age
133+
134+
- name: Decrypt plan
135+
run: |
136+
echo ${{ secrets.TFPLAN_ARTIFACT_SECRET_KEY }} > tfplan.key
137+
age --identity tfplan.key --decrypt --output "${TFPLAN}" "${TFPLAN}.enc"
138+
139+
- name: Terraform apply
140+
run: |
141+
terraform init
142+
terraform apply "${TFPLAN}"

.github/workflows/terraform.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: Terraform static checks
2+
3+
on:
4+
pull_request:
5+
paths:
6+
- "terraform/**"
7+
push:
8+
paths:
9+
- "terraform/**"
10+
workflow_dispatch:
11+
12+
# We can't run CI tests on Terraform, so use as many static linters as possible
13+
14+
jobs:
15+
terraform-pre-commit:
16+
runs-on: ubuntu-22.04
17+
steps:
18+
- uses: actions/checkout@v3
19+
- uses: actions/setup-python@v4
20+
with:
21+
python-version-file: ".python-version"
22+
23+
- name: Install dependencies
24+
run: pip install pre-commit
25+
26+
# https://github.com/terraform-linters/setup-tflint
27+
- name: Install tflint
28+
uses: terraform-linters/[email protected]
29+
with:
30+
tflint_version: v0.47.0
31+
32+
- name: Run terraform pre-commit
33+
run: pre-commit run --all --config .pre-commit-config-terraform.yaml

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,5 @@ env
2020

2121
.terraform
2222
.terraform.lock.hcl
23+
# Keep .terraform.lock.hcl to ensure reproducible deployments
24+
!terraform/aws/curvenote/.terraform.lock.hcl

.pre-commit-config-terraform.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Config reference: https://pre-commit.com/#pre-commit-configyaml---top-level
2+
#
3+
# Common tasks
4+
#
5+
# - Run on all files: pre-commit run --all --config .pre-commit-config-terraform.yaml
6+
#
7+
# Prerequisites:
8+
# - terraform
9+
# - tflint
10+
11+
# Currently only aws/ is checked
12+
files: "^terraform/aws/"
13+
exclude: "^terraform/aws/pangeo/"
14+
15+
repos:
16+
# We can't run any CI tests on production Terraform code, so use as many static linters as possible
17+
- repo: https://github.com/antonbabenko/pre-commit-terraform
18+
rev: v1.83.0
19+
hooks:
20+
- id: terraform_fmt
21+
- id: terraform_tflint
22+
- id: terraform_validate

terraform/aws/binder-eks/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# BinderHub on AWS EKS
2+
3+
This module deploys an AWS EKS cluster with IRSA roles to support BinderHub ECR access.
4+
5+
The module has optional support for using a limited non-administrative AWS role with a permissions boundary to deploy the cluster.
6+
7+
For an example see [curvenote](../curvenote/README.md)
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# https://registry.terraform.io/modules/terraform-aws-modules/eks/aws/19.15.2
2+
# Full example:
3+
# https://github.com/terraform-aws-modules/terraform-aws-eks/blame/v19.14.0/examples/complete/main.tf
4+
# https://github.com/terraform-aws-modules/terraform-aws-eks/blob/v19.14.0/docs/compute_resources.md
5+
6+
data "aws_caller_identity" "current" {}
7+
8+
locals {
9+
permissions_boundary_arn = (
10+
var.permissions_boundary_name != null ?
11+
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:policy/${var.permissions_boundary_name}" :
12+
null
13+
)
14+
}
15+
16+
# This assumes the EKS service linked role is already created (or the current user has permissions to create it)
17+
module "eks" {
18+
source = "terraform-aws-modules/eks/aws"
19+
version = "19.15.3"
20+
cluster_name = var.cluster_name
21+
cluster_version = var.k8s_version
22+
subnet_ids = module.vpc.public_subnets
23+
24+
cluster_endpoint_private_access = true
25+
cluster_endpoint_public_access = true
26+
cluster_endpoint_public_access_cidrs = var.k8s_api_cidrs
27+
28+
vpc_id = module.vpc.vpc_id
29+
30+
# Allow all allowed roles to access the KMS key
31+
kms_key_enable_default_policy = true
32+
# This duplicates the above, but the default is the current user/role so this will avoid
33+
# a deployment change when run by different users/roles
34+
kms_key_administrators = [
35+
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:root",
36+
]
37+
38+
enable_irsa = var.enable_irsa
39+
iam_role_permissions_boundary = local.permissions_boundary_arn
40+
41+
eks_managed_node_group_defaults = {
42+
capacity_type = "SPOT"
43+
iam_role_permissions_boundary = local.permissions_boundary_arn
44+
}
45+
46+
eks_managed_node_groups = {
47+
worker_group_1 = {
48+
name = "${var.cluster_name}-wg1"
49+
instance_types = [var.instance_type_wg1]
50+
ami_type = var.use_bottlerocket ? "BOTTLEROCKET_x86_64" : "AL2_x86_64"
51+
platform = var.use_bottlerocket ? "bottlerocket" : "linux"
52+
53+
# additional_userdata = "echo foo bar"
54+
vpc_security_group_ids = [
55+
aws_security_group.all_worker_mgmt.id,
56+
aws_security_group.worker_group_all.id,
57+
]
58+
desired_size = var.wg1_size
59+
min_size = 1
60+
max_size = var.wg1_max_size
61+
62+
# Disk space can't be set with the default custom launch template
63+
# disk_size = 100
64+
block_device_mappings = [
65+
{
66+
# https://github.com/bottlerocket-os/bottlerocket/discussions/2011
67+
device_name = var.use_bottlerocket ? "/dev/xvdb" : "/dev/xvda"
68+
ebs = {
69+
# Uses default alias/aws/ebs key
70+
encrypted = true
71+
volume_size = var.root_volume_size
72+
volume_type = "gp3"
73+
}
74+
}
75+
]
76+
77+
subnet_ids = slice(module.vpc.public_subnets, 0, var.number_azs)
78+
},
79+
# Add more worker groups here
80+
}
81+
82+
manage_aws_auth_configmap = true
83+
# Anyone in the AWS account with sufficient permissions can access the cluster
84+
aws_auth_accounts = [
85+
data.aws_caller_identity.current.account_id,
86+
]
87+
aws_auth_roles = [
88+
{
89+
# GitHub OIDC role
90+
rolearn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/${var.cluster_name}-${var.github_oidc_role_suffix}"
91+
username = "binderhub-github-oidc"
92+
groups = ["system:masters"]
93+
},
94+
{
95+
# GitHub OIDC terraform role
96+
rolearn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/${var.cluster_name}-${var.github_oidc_role_suffix}-terraform"
97+
username = "binderhub-github-oidc"
98+
groups = ["system:masters"]
99+
},
100+
{
101+
# BinderHub admins role
102+
rolearn = aws_iam_role.eks_access.arn
103+
username = "binderhub-admin"
104+
groups = ["system:masters"]
105+
}
106+
]
107+
}
108+
109+
data "aws_eks_cluster_auth" "binderhub" {
110+
name = var.cluster_name
111+
}

0 commit comments

Comments
 (0)