Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CI/CD workflows for terraform automation #183

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/workflows/devnet_main_deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Main CD for Devnet Deployment

on:
workflow_dispatch:
pull_request:
branches:
- main
push:
branches:
- "!main"
paths:
- "aws/devnet/**"

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false

jobs:
deploy:
uses: ./.github/workflows/terraform_template_deploy.yml
nazar-pc marked this conversation as resolved.
Show resolved Hide resolved
with:
project: aws
resource: devnet
tf_workspace_name: devnet-aws
tf_version: 1.5.7
tf_organization: subspace
secrets:
TRANSCRYPT: ${{ secrets.TRANSCRYPT }}
TF_API_TOKEN: ${{ secrets.TF_API_TOKEN }}
30 changes: 30 additions & 0 deletions .github/workflows/ephemeral_devnet_aws_deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Main CD for Ephememeral Devnet Deployment

on:
workflow_dispatch:
pull_request:
branches:
- main
push:
branches:
- "!main"
paths:
- "testing-framework/ec2/network/**"

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false

jobs:
deploy:
uses: ./.github/workflows/terraform_template_ephemeral_deploy.yml
with:
project: testing-framework
instance: ec2
resource: network
tf_workspace_name: ephemeral-devnet
tf_version: 1.5.7
tf_organization: subspace
secrets:
TRANSCRYPT: ${{ secrets.TRANSCRYPT }}
TF_API_TOKEN: ${{ secrets.TF_API_TOKEN }}
30 changes: 30 additions & 0 deletions .github/workflows/ephemeral_devnet_hetzner_deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Main CD for Ephememeral Devnet Deployment

on:
workflow_dispatch:
pull_request:
branches:
- main
push:
branches:
- "!main"
paths:
- "testing-framework/hetzner/network/**"

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false

jobs:
deploy:
uses: ./.github/workflows/terraform_template_ephemeral_deploy.yml
with:
project: testing-framework
instance: hetzner
resource: network
tf_workspace_name: ephemeral-devnet-hetzner
tf_version: 1.5.7
tf_organization: subspace
secrets:
TRANSCRYPT: ${{ secrets.TRANSCRYPT }}
TF_API_TOKEN: ${{ secrets.TF_API_TOKEN }}
29 changes: 29 additions & 0 deletions .github/workflows/gemini_3f_main_deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Main CD for Gemini Deployment

on:
workflow_dispatch:
pull_request:
branches:
- main
push:
branches:
- "!main"
paths:
- "aws/gemini-3f/**"

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false

jobs:
deploy:
uses: ./.github/workflows/terraform_template_deploy.yml
with:
project: aws
resource: gemini-3f
tf_workspace_name: gemini-3f
tf_version: 1.5.7
tf_organization: subspace
secrets:
TRANSCRYPT: ${{ secrets.TRANSCRYPT }}
TF_API_TOKEN: ${{ secrets.TF_API_TOKEN }}
53 changes: 29 additions & 24 deletions .github/workflows/terraform_gh_runner.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
name: Terraform Workflow
name: Terraform GH Runner Deployment

on:
push:
workflow_dispatch:
pull_request:
branches:
- main
paths:
- './github-runners/terraform/base/**'
workflow_dispatch:
- "github-runners/terraform/base/**"

jobs:
terraform_gh_runner:
runs-on: ubuntu-latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Comment on lines +14 to +15
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't seem to be used, why setting?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The env variable needs to be set when calling github api, otherwise an error is thrown.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What and where calls those APIs? I don't see any usage of it.

Copy link
Contributor Author

@DaMandal0rian DaMandal0rian Oct 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GITHUB_TOKEN is needed in CI or bash cli for API calls. The API is called with curl via https://github.com/subspace/infra/pull/183/files/99a5ac25712fdda531fd88026a12f96aafeacb83#diff-b46b68b6df852ad5f8fc96162f55c2fe198f6d53eb2fe7d7ce12fcd4b2650ba6R39-R43

If you want to use the API in a GitHub Actions workflow, GitHub recommends that you authenticate with the built-in GITHUB_TOKEN

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What you linked doesn't use GITHUB_TOKEN environment variable, it uses $token that is obtained in a different way.

Copy link
Contributor Author

@DaMandal0rian DaMandal0rian Oct 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

@nazar-pc nazar-pc Oct 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, so it is not for curl, it was for gh CLI command, now it makes sense.

With that knowledge we understand why it is necessary. I think what we should do now in a secure way without using heavy tools like setting repo (I'm fairly certain simply setting environment variable wasn't) without using heavy tools like setting repo's secrets from workflow is to use outputs and mask them such that they are not visible in logs: https://github.com/orgs/community/discussions/25225#discussioncomment-3246942

That is the goal here: to pass the token from one step into another. What we had in earlier versions of this PR are various suboptimal/incorrect ways of achieving that ultimate goal.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

correct, github cli, I should have been more clear about that "The GITHUB_TOKEN is needed in CI or bash cli for API calls" but couldn't remember exactly since it was a while ago. That being said the solution of masking the secret and just passing it is nice if using the ephemeral runners maybe but this solution and workflow is for the dedicated runners, where i need to retain the secret so I can unregister and delete runner if need be and remove it from github. See https://docs.github.com/en/free-pro-team@latest/rest/actions/self-hosted-runners?apiVersion=2022-11-28#delete-a-self-hosted-runner-from-an-organization

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But you mentioned that tokens only live for 1 hour, so you'll have to retrieve fresh token anyway. Why retaining it then at all?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The token is not needed to deregister, a force delete can be done. So i've used the masking technique.

steps:
- name: Checkout repository
Expand All @@ -19,40 +21,43 @@ jobs:
- name: Set up Terraform
uses: hashicorp/setup-terraform@v1
with:
terraform_version: "1.4.2"
terraform_version: "1.5.7"
cli_config_credentials_token: ${{ secrets.TF_CLOUD_TOKEN }}

- name: Install dependencies
run: |
# Install any dependencies required by your Terraform code

- name: Run Bash Script
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# GitHub repository and access token
repo="subspace/infra"
token=${{ secrets.PAT_TOKEN }}
# GitHub repository and access token for github api calls, since GITHUB_TOKEN can't
# be used for this purpose.
repo="subspace/infra"
token=${{ secrets.PAT_TOKEN }}

nazar-pc marked this conversation as resolved.
Show resolved Hide resolved
# API endpoint
url="https://api.github.com/repos/$repo/actions/runners/registration-token"
# API endpoint
url="https://api.github.com/repos/$repo/actions/runners/registration-token"

# Send POST request to get the registration token
response=$(curl -X POST -H "Authorization: token $token" -s "$url")
# Send POST request to get the registration token
response=$(curl -X POST -H "Authorization: token $token" -s "$url")

# Extract the token value from the response
runner_token=$(echo "$response" | jq -r '.token')
# Extract the token value from the response
runner_token=$(echo "$response" | jq -r '.token')

# Export the token as an environment variable
echo "export RUNNER_TOKEN=$runner_token" >> $GITHUB_ENV
# Store the token as a secret in GitHub Actions for use
# in subsequent steps with terraform for runner registration
gh secret set RUNNER_TOKEN -r "$repo" -b "$runner_token"

# Set the runner token as an environment variable
export RUNNER_TOKEN="$runner_token"

# Store the token as a secret in GitHub Actions
gh secret set RUNNER_TOKEN -r "$repo" -b "$runner_token"
- name: Fetch and write terraform.tfvars
run: |
echo ${{ secrets.TF_VARS_FILE }} > /tmp/terraform.tfvars
chmod 600 /tmp/terraform.tfvars

- name: Run Terraform
working-directory: ./github-runners/terraform/base
run: |
terraform init-backend-config="organization=${{ secrets.ORGANIZATION_NAME }}" -backend-config="workspaces=${{ secrets.WORKSPACE_NAME }}"
terraform plan -var-file=${{ secrets.VAR_FILE }}
terraform apply -auto-approve -var "gh_token=${{ secrets.RUNNER_TOKEN }}"
terraform init-backend-config="organization=subspace" -backend-config="workspaces=${{ secrets.WORKSPACE_NAME }}"
terraform plan -var-file=/tmp/terraform.tfvars
terraform apply -auto-approve -var "gh_token=${{ secrets.RUNNER_TOKEN }}" -var-file=/tmp/terraform.tfvars
105 changes: 105 additions & 0 deletions .github/workflows/terraform_template_deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
name: Template Deploy

on:
workflow_call:
inputs:
project:
required: true
type: string
resource:
required: true
type: string
tf_workspace_name:
description: "Name of the workspace in terraform cloud"
required: false
type: string
tf_version:
description: "Version of the terraform"
required: true
type: string
tf_organization:
description: "Name of the TF organization"
default: "subspace"
type: string
required: true
run_apply:
description: "The code needs to be deployed or not"
type: string
default: "no"
run_destroy:
description: "The resources need to be destroyed or not"
type: string
default: "no"
secrets:
TRANSCRYPT:
required: true
TF_API_TOKEN:
required: false
env:
TF_CLOUD_ORGANIZATION: "${{ inputs.tf_organization }}"
TF_API_TOKEN: "${{ secrets.TF_API_TOKEN }}"
TF_VERSION: "${{ inputs.tf_version }}"

jobs:
template-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout the repo
uses: actions/checkout@v3

- name: Decrypt the secrets
run: |
bash scripts/transcrypt -c aes-256-cbc -p ${{ secrets.TRANSCRYPT }} -y

- uses: hashicorp/setup-terraform@v2
with:
terraform_version: ${{ env.TF_VERSION }}
terraform_wrapper: false
cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}

- name: Setup Remote Config Backend
run: |
cat > /tmp/config.remote.tfbackend <<EOF
workspaces { name = "${{ inputs.tf_workspace_name }}"}
hostname = "app.terraform.io"
organization = "${{ inputs.tf_organization }}"
EOF

- name: Terraform fmt
working-directory: ${{ inputs.project }}/${{ inputs.resource }}
run: terraform fmt -check
continue-on-error: true

- name: Terraform Init for ${{ inputs.project }}/${{ inputs.resource }}
working-directory: ${{ inputs.project }}/${{ inputs.resource }}
run: |
cat /tmp/config.remote.tfbackend
terraform init -backend-config=/tmp/config.remote.tfbackend

- name: Terraform Validate
working-directory: ${{ inputs.project }}/${{ inputs.resource }}
run: terraform validate

- name: Fetch and write terraform.tfvars
run: |
echo ${{ secrets.TF_VARS_FILE }} > /tmp/terraform.tfvars
chmod 600 /tmp/terraform.tfvars

- name: Terraform Plan for ${{ inputs.project }}/${{ inputs.resource }}
if: ${{ (inputs.run_destroy == 'no') }}
working-directory: ${{ inputs.project }}/${{ inputs.resource }}
run: |
terraform plan -var-file=/tmp/terraform.tfvars

- name: Terraform Apply for ${{ inputs.project }}/${{ inputs.resource }}
if: ${{ (inputs.run_apply == 'yes') && (inputs.run_destroy == 'no') }}
working-directory: ${{ inputs.project }}/${{ inputs.resource }}
run: |
terraform apply -auto-approve -var-file=/tmp/terraform.tfvars

- name: Terraform Destroy for ${{ inputs.project }}/${{ inputs.resource }}
if: ${{ (inputs.run_destroy == 'yes') }}
working-directory: ${{ inputs.project }}/${{ inputs.resource }}
run: |
terraform plan -destroy -var-file=/tmp/terraform.tfvars
terraform destroy -auto-approve -var-file=/tmp/terraform.tfvars
Loading
Loading