Skip to content

Commit

Permalink
add verify prod and refactor terraform check/deploy
Browse files Browse the repository at this point in the history
  • Loading branch information
cpanato committed May 2, 2024
1 parent 343d4d4 commit 21092cf
Show file tree
Hide file tree
Showing 3 changed files with 267 additions and 46 deletions.
216 changes: 216 additions & 0 deletions .github/workflows/.terraform.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
# Copyright 2024 Chainguard, Inc.
# SPDX-License-Identifier: Apache-2.0

name: Reusable Terraform Workflow

on:
workflow_call:
inputs:
workload_identity_provider:
type: string
required: true
description: "Workload identity provider for GCP Workload Federation."
service_account:
type: string
required: true
description: "GCP Service Account to impersonate."
checkout_sha:
required: false
type: string
description: 'Commit SHA to run at.'
working_directory:
required: true
type: string
description: 'Location for the terraform.'
project_id:
required: false
type: string
description: 'GCP project ID.'
octo-sts-identity:
required: false
default: ''
type: string
description: 'The Octo STS identity name'

jobs:
terraform:
name: Terraform
runs-on: 'ubuntu-latest'
permissions:
contents: read # clone the repository contents
id-token: write # federates with GCP

env:
SHA: ${{ inputs.checkout_sha || github.sha }}
PROJECT_ID: ${{ inputs.project_id }}
TF_PLAN_BIN: 'plan.tmp'
TF_PLAN_OUT: 'plan.out'

defaults:
run:
working-directory: "${{ inputs.working_directory }}"

steps:
- uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
with:
egress-policy: audit

- name: Checkout
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
with:
ref: ${{ env.SHA }}

- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
with:
go-version-file: './go.mod'
check-latest: true

- name: Authenticate to Google Cloud
uses: 'google-github-actions/auth@55bd3a7c6e2ae7cf1877fd1ccb9d54c0503c457c' # v2.1.2
id: auth
with:
token_format: 'access_token'
project_id: '${{ inputs.project_id }}'
workload_identity_provider: '${{ inputs.workload_identity_provider }}'
service_account: '${{ inputs.service_account }}'

- name: 'Set up Cloud SDK'
uses: google-github-actions/setup-gcloud@98ddc00a17442e89a24bbf282954a3b65ce6d200 # v2.1.0

- uses: 'docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20' # v2
with:
username: 'oauth2accesstoken'
password: '${{ steps.auth.outputs.access_token }}'
registry: 'gcr.io'

- name: Setup terraform
uses: 'hashicorp/setup-terraform@a1502cd9e758c50496cc9ac5308c4843bcd56d36' # v3.0.0
with:
terraform_version: 1.6

- name: Terraform fmt
id: fmt
run: terraform fmt -check -recursive

- name: Terraform init
id: init
run: terraform init -reconfigure

- name: Terraform Validate
id: validate
run: terraform validate -no-color

- name: Terraform Plan
id: plan
continue-on-error: true
shell: bash
run: |
set +e
terraform plan -input=false -lock=false -detailed-exitcode -no-color -out=${{ env.TF_PLAN_BIN }}
exitcode=$?
case "$exitcode" in
0)
# 0 = Succeeded with empty diff (no changes)
echo "Succeeded with empty diff (no changes)"
echo "TF_PLAN_OUTCOME=same" >> "$GITHUB_OUTPUT"
;;
1)
# 1 = Error
echo "Error"
echo "TF_PLAN_OUTCOME=error" >> "$GITHUB_OUTPUT"
exit 1
;;
2)
# 2 = Succeeded with non-empty diff (changes present)
echo "Succeeded with non-empty diff (changes present)"
echo "TF_PLAN_OUTCOME=diff" >> "$GITHUB_OUTPUT"
;;
*)
echo "unknown error"
echo "TF_PLAN_OUTCOME=unknown" >> "$GITHUB_OUTPUT"
exit 1
;;
esac
terraform show -no-color ${{ env.TF_PLAN_BIN }} > "${GITHUB_WORKSPACE}"/${{ env.TF_PLAN_OUT }}
exit 0
- uses: octo-sts/action@6177b4481c00308b3839969c3eca88c96a91775f # v1.0.0
if: github.event_name == 'pull_request_target'
id: octo-sts
with:
scope: ${{ github.repository }}
identity: ${{ inputs.octo-sts-identity }}

- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
if: github.event_name == 'pull_request_target'
with:
github-token: ${{ steps.octo-sts.outputs.token }}
script: |
// 1. Retrieve existing bot comments for the PR
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
})
const botComment = comments.find(comment => {
return comment.user.type === 'Bot' && comment.body.includes('Terraform checks for "${{ inputs.working_directory }}"')
})
// adapted from: https://github.com/actions/github-script/issues/266#issuecomment-1159681385
const run_url = process.env.GITHUB_SERVER_URL + '/' + process.env.GITHUB_REPOSITORY + '/actions/runs/' + process.env.GITHUB_RUN_ID
const run_link = '<a href="' + run_url + '">Actions</a>.'
const fs = require('fs')
const plan_file = fs.readFileSync('${{ env.TF_PLAN_OUT }}', 'utf8')
const plan = plan_file.length > 64000 ? plan_file.toString().substring(0, 64000) + " ..." : plan_file
const truncated_message = plan_file.length > 64000 ? "Output is too long and was truncated. You can read full Plan in " + run_link + "<br /><br />" : ""
// 2. Prepare format of the comment
const output = `Terraform checks for "${{ inputs.working_directory }}"
#### Terraform Format and Style 🖌 \`${{ steps.fmt.outcome }}\`
#### Terraform Initialization ⚙️ \`${{ steps.init.outcome }}\`
#### Terraform Validation 🤖 \`${{ steps.validate.outcome }}\`
<details><summary>Validation Output</summary>
\`\`\`\n
${{ steps.validate.outputs.stdout }}
\`\`\`
</details>
#### Terraform Plan 📖 \`${{ steps.plan.outcome }}\`
<details><summary>Show Plan</summary>
\`\`\`\n
${plan}
\`\`\`
</details>
${truncated_message}
*Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`, Working Directory: \`${{ inputs.working_directory }}\`, Workflow: \`${{ github.workflow }}\`*`;
// 3. If we have a comment, update it, otherwise create a new one
if (botComment) {
github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: output
})
} else {
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
})
}
- name: Terraform Plan Status
if: ${{ steps.plan.outputs.TF_PLAN_OUTCOME == 'error' || steps.plan.outputs.TF_PLAN_OUTCOME == 'unknown' }}
run: exit 1

- name: Terraform Apply
if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
run: terraform apply -auto-approve -input=false "${{ env.TF_PLAN_BIN }}"
68 changes: 22 additions & 46 deletions .github/workflows/deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ on:
- "main"
workflow_dispatch:

concurrency: deploy
concurrency:
group: deploy
cancel-in-progress: false

jobs:
deploy:
Expand All @@ -20,48 +22,22 @@ jobs:
id-token: write # federates with GCP

steps:
- uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v3
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
with:
go-version-file: './go.mod'
check-latest: true

- uses: google-github-actions/auth@55bd3a7c6e2ae7cf1877fd1ccb9d54c0503c457c # v2.1.2
id: auth
with:
token_format: 'access_token'
project_id: 'octo-sts'
workload_identity_provider: 'projects/96355665038/locations/global/workloadIdentityPools/github-pool/providers/github-provider'
service_account: '[email protected]'

- uses: 'docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20' # v2
with:
username: 'oauth2accesstoken'
password: '${{ steps.auth.outputs.access_token }}'
registry: 'gcr.io'

# Attempt to deploy the terraform configuration
- uses: hashicorp/setup-terraform@a1502cd9e758c50496cc9ac5308c4843bcd56d36 # v2.0.0
with:
terraform_version: 1.6

- working-directory: ./iac
run: |
terraform init
terraform plan
terraform apply -auto-approve
- uses: rtCamp/action-slack-notify@4e5fb42d249be6a45a298f3c9543b111b02f7907 # v2.3.0
if: ${{ failure() }}
env:
SLACK_ICON: http://github.com/chainguard-dev.png?size=48
SLACK_USERNAME: guardian
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
SLACK_CHANNEL: 'octo-sts-alerts' # Use a channel
SLACK_COLOR: '#8E1600'
MSG_MINIMAL: 'true'
SLACK_TITLE: Deploying OctoSTS to Cloud Run failed
SLACK_MESSAGE: |
For detailed logs: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
- uses: ./.github/workflows/.terraform.yaml
with:
project_id: 'octo-sts'
workload_identity_provider: 'projects/96355665038/locations/global/workloadIdentityPools/github-pool/providers/github-provider'
service_account: '[email protected]'
working_directory: ./iac

- uses: rtCamp/action-slack-notify@4e5fb42d249be6a45a298f3c9543b111b02f7907 # v2.3.0
if: ${{ failure() }}
env:
SLACK_ICON: http://github.com/chainguard-dev.png?size=48
SLACK_USERNAME: guardian
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
SLACK_CHANNEL: 'octo-sts-alerts' # Use a channel
SLACK_COLOR: '#8E1600'
MSG_MINIMAL: 'true'
SLACK_TITLE: Deploying OctoSTS to Cloud Run failed
SLACK_MESSAGE: |
For detailed logs: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
29 changes: 29 additions & 0 deletions .github/workflows/verify-prod.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Copyright 2024 Chainguard, Inc.
# SPDX-License-Identifier: Apache-2.0

name: Verify prod Octo-sts

on:
pull_request_target:
branches:
- 'main'

concurrency:
group: deploy
cancel-in-progress: false

jobs:
verify-prod-octo-sts:

permissions:
contents: read # clone the repository contents
id-token: write # federates with GCP

uses: ./.github/workflows/.terraform.yaml
with:
project_id: 'octo-sts'
workload_identity_provider: 'projects/96355665038/locations/global/workloadIdentityPools/github-pool/providers/github-provider'
service_account: '[email protected]'
working_directory: ./iac
octo-sts-identity: verify-prod
checkout_sha: ${{ github.event.pull_request.head.sha }}

0 comments on commit 21092cf

Please sign in to comment.