Merge pull request #105 from GSA/studio-cloud-gov

Set up studio cloud.gov environment
This commit is contained in:
Ryan Ahearn
2022-11-02 14:52:49 -04:00
committed by GitHub
28 changed files with 324 additions and 54 deletions

67
.github/workflows/deploy-demo.yml vendored Normal file
View File

@@ -0,0 +1,67 @@
name: Deploy to demo environment
on:
push:
branches: [ production ]
permissions:
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
environment: demo
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 2
- name: Check for changes to Terraform
id: changed-terraform-files
uses: tj-actions/changed-files@v1.1.2
with:
files: terraform/demo
- name: Terraform init
if: steps.changed-terraform-files.outputs.any_changed == 'true'
working-directory: terraform/demo
env:
AWS_ACCESS_KEY_ID: ${{ secrets.TERRAFORM_STATE_ACCESS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.TERRAFORM_STATE_SECRET_ACCESS_KEY }}
run: terraform init
- name: Terraform apply
if: steps.changed-terraform-files.outputs.any_changed == 'true'
working-directory: terraform/demo
env:
AWS_ACCESS_KEY_ID: ${{ secrets.TERRAFORM_STATE_ACCESS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.TERRAFORM_STATE_SECRET_ACCESS_KEY }}
TF_VAR_cf_user: ${{ secrets.CLOUDGOV_USERNAME }}
TF_VAR_cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
run: terraform apply -auto-approve -input=false
- uses: ./.github/actions/setup-project
- name: Install application dependencies
run: make bootstrap
- name: Create requirements.txt because Cloud Foundry does a weird pipenv thing
run: pipenv requirements > requirements.txt
- name: Deploy to cloud.gov
uses: 18f/cg-deploy-action@main
env:
DANGEROUS_SALT: ${{ secrets.DANGEROUS_SALT }}
SECRET_KEY: ${{ secrets.SECRET_KEY }}
ADMIN_CLIENT_SECRET: ${{ secrets.ADMIN_CLIENT_SECRET }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
with:
cf_username: ${{ secrets.CLOUDGOV_USERNAME }}
cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
cf_org: gsa-tts-benefits-studio-prototyping
cf_space: notify-demo
push_arguments: >-
--vars-file deploy-config/demo.yml
--var DANGEROUS_SALT="$DANGEROUS_SALT"
--var SECRET_KEY="$SECRET_KEY"
--var ADMIN_CLIENT_SECRET="$ADMIN_CLIENT_SECRET"
--var AWS_ACCESS_KEY_ID="$AWS_ACCESS_KEY_ID"
--var AWS_SECRET_ACCESS_KEY="$AWS_SECRET_ACCESS_KEY"

View File

@@ -1,4 +1,4 @@
name: Deploy to prototype environment
name: Deploy to staging environment
on:
workflow_run:
@@ -15,6 +15,7 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
environment: staging
steps:
- uses: actions/checkout@v3
with:
@@ -52,16 +53,16 @@ jobs:
- name: Deploy to cloud.gov
uses: 18f/cg-deploy-action@main
env:
DANGEROUS_SALT: ${{ secrets.PROD_DANGEROUS_SALT }}
SECRET_KEY: ${{ secrets.PROD_SECRET_KEY }}
ADMIN_CLIENT_SECRET: ${{ secrets.PROD_ADMIN_CLIENT_SECRET }}
DANGEROUS_SALT: ${{ secrets.DANGEROUS_SALT }}
SECRET_KEY: ${{ secrets.SECRET_KEY }}
ADMIN_CLIENT_SECRET: ${{ secrets.ADMIN_CLIENT_SECRET }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
with:
cf_username: ${{ secrets.cloudgov_username }}
cf_password: ${{ secrets.cloudgov_password }}
cf_org: gsa-10x-prototyping
cf_space: 10x-notifications
cf_username: ${{ secrets.CLOUDGOV_USERNAME }}
cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
cf_org: gsa-tts-benefits-studio-prototyping
cf_space: notify-staging
push_arguments: >-
--vars-file deploy-config/staging.yml
--var DANGEROUS_SALT="$DANGEROUS_SALT"
@@ -74,4 +75,5 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'failure' }}
steps:
- run: echo 'Checks failed, not deploying'
- uses: actions/github-script@v6
script: core.setFailed('Checks failed, not deploying')

79
.github/workflows/terraform-demo.yml vendored Normal file
View File

@@ -0,0 +1,79 @@
name: Run Terraform plan in demo
on:
pull_request:
branches: [ production ]
paths: [ 'terraform/**' ]
defaults:
run:
working-directory: terraform/demo
jobs:
terraform:
name: Terraform plan
runs-on: ubuntu-latest
environment: demo
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Terraform format
id: format
run: terraform fmt -check
- name: Terraform init
id: init
env:
AWS_ACCESS_KEY_ID: ${{ secrets.TERRAFORM_STATE_ACCESS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.TERRAFORM_STATE_SECRET_ACCESS_KEY }}
run: terraform init
- name: Terraform validate
id: validation
run: terraform validate -no-color
- name: Terraform plan
id: plan
env:
AWS_ACCESS_KEY_ID: ${{ secrets.TERRAFORM_STATE_ACCESS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.TERRAFORM_STATE_SECRET_ACCESS_KEY }}
TF_VAR_cf_user: ${{ secrets.CLOUDGOV_USERNAME }}
TF_VAR_cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
run: terraform plan -no-color -input=false 2>&1 | tee plan_output.txt
- name: Read Terraform plan output file
id: terraform_output
uses: juliangruber/read-file-action@v1
if: ${{ always() }}
with:
path: ./terraform/demo/plan_output.txt
# inspiration: https://learn.hashicorp.com/tutorials/terraform/github-actions#review-actions-workflow
- name: Update PR
uses: actions/github-script@v6
# we would like to update the PR even when a prior step failed
if: ${{ always() }}
with:
script: |
const output = `Terraform Format and Style: ${{ steps.format.outcome }}
Terraform Initialization: ${{ steps.init.outcome }}
Terraform Validation: ${{ steps.validation.outcome }}
Terraform Plan: ${{ steps.plan.outcome }}
<details><summary>Show Plan</summary>
\`\`\`\n
${{ steps.terraform_output.outputs.content }}
\`\`\`
</details>
*Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
})

View File

@@ -2,7 +2,7 @@ name: Run Terraform plan in production
on:
pull_request:
branches: [ production ]
branches: [ production-disabled-for-now ]
paths: [ 'terraform/**' ]
defaults:
@@ -38,8 +38,8 @@ jobs:
env:
AWS_ACCESS_KEY_ID: ${{ secrets.TERRAFORM_STATE_ACCESS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.TERRAFORM_STATE_SECRET_ACCESS_KEY }}
TF_VAR_cf_user: ${{ secrets.CF_USERNAME }}
TF_VAR_cf_password: ${{ secrets.CF_PASSWORD }}
TF_VAR_cf_user: ${{ secrets.CLOUDGOV_USERNAME }}
TF_VAR_cf_password: ${{ secrets.CLOUDGOV_PASSWORD }}
run: terraform plan -no-color -input=false 2>&1 | tee plan_output.txt
- name: Read Terraform plan output file
@@ -51,7 +51,7 @@ jobs:
# inspiration: https://learn.hashicorp.com/tutorials/terraform/github-actions#review-actions-workflow
- name: Update PR
uses: actions/github-script@v4
uses: actions/github-script@v6
# we would like to update the PR even when a prior step failed
if: ${{ always() }}
with:
@@ -71,7 +71,7 @@ jobs:
*Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`;
github.issues.createComment({
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,

View File

@@ -13,6 +13,7 @@ jobs:
terraform:
name: Terraform plan
runs-on: ubuntu-latest
environment: staging
steps:
- name: Checkout
uses: actions/checkout@v2
@@ -50,7 +51,7 @@ jobs:
# inspiration: https://learn.hashicorp.com/tutorials/terraform/github-actions#review-actions-workflow
- name: Update PR
uses: actions/github-script@v4
uses: actions/github-script@v6
# we would like to update the PR even when a prior step failed
if: ${{ always() }}
with:
@@ -70,7 +71,7 @@ jobs:
*Pusher: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`;
github.issues.createComment({
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,

View File

@@ -416,9 +416,14 @@ class Staging(Production):
pass
class Demo(Production):
pass
configs = {
'development': Development,
'test': Test,
'staging': Staging,
'demo': Demo,
'production': Production
}

7
deploy-config/demo.yml Normal file
View File

@@ -0,0 +1,7 @@
env: demo
web_instances: 1
web_memory: 1G
worker_instances: 1
worker_memory: 512M
public_api_route: notify-api-demo.app.cloud.gov
admin_base_url: https://notify-demo.app.cloud.gov

View File

@@ -3,3 +3,5 @@ web_instances: 2
web_memory: 1G
worker_instances: 1
worker_memory: 512M
public_api_route: notify-api.app.cloud.gov
admin_base_url: https://notify.app.cloud.gov

View File

@@ -3,3 +3,5 @@ web_instances: 1
web_memory: 1G
worker_instances: 1
worker_memory: 512M
public_api_route: notify-api-staging.app.cloud.gov
admin_base_url: https://notify-staging.app.cloud.gov

View File

@@ -1,18 +1,22 @@
# Deploying
We deploy automatically to cloud.gov for production and staging environments.
We deploy automatically to cloud.gov for demo and staging environments.
Deployment runs via the [deployment action](../.github/workflows/deploy.yml) on GitHub, which pulls credentials from GitHub's secrets store.
Deployment to staging runs via the [base deployment action](../.github/workflows/deploy.yml) on GitHub, which pulls credentials from GitHub's secrets store in the staging environment.
Deployment to demo runs via the [demo deployment action](../.github/workflows/deploy-demo.yml) on GitHub, which pulls credentials from GitHub's secrets store in the demo environment.
The [action that we use](https://github.com/18F/cg-deploy-action) deploys using [a rolling strategy](https://docs.cloudfoundry.org/devguide/deploy-apps/rolling-deploy.html), so all deployments should have zero downtime.
The API has 2 deployment environments:
- Production, which deploys from `main`
- Staging, which does not, in fact, exist
- Staging, which deploys from `main`
- Demo, which deploys from `production`
In the future, we will add a Production deploy environment, which will deploy in parallel to Demo.
Configurations for these are located in [the `deploy-config` folder](../deploy-config/).
In the event that a deployment includes a Terraform change, that change will run before any code is deployed to the environment. Each environment has its own Terraform GitHub Action to handle that change.
Failures in any of these GitHub workflows will be surfaced in the Pull Request related to the code change, and in the case of `checks.yml` actively prevent the PR from being merged. Failure in the Terraform workflow will not actively prevent the PR from being merged, but reviewers should not approve a PR with a failing terraform plan.
Failures in any of these GitHub workflows will be surfaced in the Pull Request related to the code change, and in the case of `checks.yml` actively prevent the PR from being merged. Failure in the Terraform workflow will not actively prevent the PR from being merged, but reviewers should not approve a PR with a failing terraform plan.

View File

@@ -1,21 +1,18 @@
---
applications:
- name: notifications-api-((env))
buildpack: https://github.com/cloudfoundry/python-buildpack.git#v1.7.58
- name: notify-api-((env))
buildpack: python_buildpack
instances: 1
memory: 1G
disk_quota: 1G
health-check-type: process
health-check-invocation-timeout: 1
routes:
- route: notifications-api.app.cloud.gov
- route: notifications-api-((env)).apps.internal
- route: ((public_api_route))
- route: notify-api-((env)).apps.internal
services:
- notifications-api-rds-((env))
- notifications-api-redis-((env))
- notifications-api-csv-upload-bucket-((env))
- notifications-api-contact-list-bucket-((env))
- notify-api-rds-((env))
- notify-api-redis-((env))
- notify-api-csv-upload-bucket-((env))
- notify-api-contact-list-bucket-((env))
processes:
- type: web
@@ -34,8 +31,8 @@ applications:
FLASK_ENV: production
NOTIFY_ENVIRONMENT: ((env))
API_HOST_NAME: https://notifications-api.app.cloud.gov
ADMIN_BASE_URL: https://notifications-admin.app.cloud.gov
API_HOST_NAME: https://((public_api_route))
ADMIN_BASE_URL: ((admin_base_url))
# Credentials variables
INTERNAL_CLIENT_API_KEYS: '{"notify-admin":["((ADMIN_CLIENT_SECRET))"]}'

View File

@@ -4,8 +4,8 @@ read -p "Are you sure you want to import terraform state (y/n)? " verify
if [[ $verify == "y" ]]; then
echo "Importing bootstrap state"
./run.sh import module.s3.cloudfoundry_service_instance.bucket 31204bcc-aae3-4cd3-8b59-5055a338d44f
./run.sh import cloudfoundry_service_key.bucket_creds 483a6ac5-4ba0-48ad-9850-ef87b51aaa08
./run.sh import module.s3.cloudfoundry_service_instance.bucket 6b759c13-6253-4a64-9bda-dd1f620185b0
./run.sh import cloudfoundry_service_key.bucket_creds a8e40295-68b7-42ba-8955-d82ba262e948
./run.sh plan
else
echo "Not importing bootstrap state"

View File

@@ -9,8 +9,8 @@ module "s3" {
cf_api_url = local.cf_api_url
cf_user = var.cf_user
cf_password = var.cf_password
cf_org_name = "gsa-10x-prototyping"
cf_space_name = "10x-notifications"
cf_org_name = "gsa-tts-benefits-studio-prototyping"
cf_space_name = "notify-management"
s3_service_name = local.s3_service_name
}

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env bash
if [[ ! -f "secrets.auto.tfvars" ]]; then
../create_service_account.sh -s 10x-notifications -u config-bootstrap-deployer > secrets.auto.tfvars
../create_service_account.sh -s notify-management -u config-bootstrap-deployer > secrets.auto.tfvars
fi
if [[ $# -gt 0 ]]; then

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env bash
../destroy_service_account.sh -s 10x-notifications -u config-bootstrap-deployer
../destroy_service_account.sh -s notify-management -u config-bootstrap-deployer
rm secrets.auto.tfvars

View File

@@ -1,2 +1,4 @@
variable "cf_password" {}
variable "cf_password" {
sensitive = true
}
variable "cf_user" {}

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env bash
org="gsa-10x-prototyping"
org="gsa-tts-benefits-studio-prototyping"
usage="
$0: Create a Service User Account for a given space

55
terraform/demo/main.tf Normal file
View File

@@ -0,0 +1,55 @@
locals {
cf_org_name = "gsa-tts-benefits-studio-prototyping"
cf_space_name = "notify-demo"
env = "demo"
app_name = "notify-api"
recursive_delete = false
}
module "database" {
source = "github.com/18f/terraform-cloudgov//database"
cf_user = var.cf_user
cf_password = var.cf_password
cf_org_name = local.cf_org_name
cf_space_name = local.cf_space_name
env = local.env
app_name = local.app_name
recursive_delete = local.recursive_delete
rds_plan_name = "micro-psql"
}
module "redis" {
source = "github.com/18f/terraform-cloudgov//redis"
cf_user = var.cf_user
cf_password = var.cf_password
cf_org_name = local.cf_org_name
cf_space_name = local.cf_space_name
env = local.env
app_name = local.app_name
recursive_delete = local.recursive_delete
redis_plan_name = "redis-dev"
}
module "csv_upload_bucket" {
source = "github.com/18f/terraform-cloudgov//s3"
cf_user = var.cf_user
cf_password = var.cf_password
cf_org_name = local.cf_org_name
cf_space_name = local.cf_space_name
recursive_delete = local.recursive_delete
s3_service_name = "${local.app_name}-csv-upload-bucket-${local.env}"
}
module "contact_list_bucket" {
source = "github.com/18f/terraform-cloudgov//s3"
cf_user = var.cf_user
cf_password = var.cf_password
cf_org_name = local.cf_org_name
cf_space_name = local.cf_space_name
recursive_delete = local.recursive_delete
s3_service_name = "${local.app_name}-contact-list-bucket-${local.env}"
}

View File

@@ -0,0 +1,17 @@
terraform {
required_version = "~> 1.0"
required_providers {
cloudfoundry = {
source = "cloudfoundry-community/cloudfoundry"
version = "0.15.5"
}
}
backend "s3" {
bucket = "cg-6b759c13-6253-4a64-9bda-dd1f620185b0"
key = "api.tfstate.demo"
encrypt = "true"
region = "us-gov-west-1"
profile = "notify-terraform-backend"
}
}

View File

@@ -0,0 +1,4 @@
variable "cf_password" {
sensitive = true
}
variable "cf_user" {}

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env bash
org="gsa-10x-prototyping"
org="gsa-tts-benefits-studio-prototyping"
usage="
$0: Destroy a Service User Account in a given space

View File

@@ -1,8 +1,8 @@
locals {
cf_org_name = "TKTK"
cf_space_name = "TKTK"
cf_org_name = "gsa-tts-benefits-studio-prototyping"
cf_space_name = "notify-prod"
env = "production"
app_name = "notifications-api"
app_name = "notify-api"
recursive_delete = false
}
@@ -32,6 +32,28 @@ module "redis" {
redis_plan_name = "TKTK-production-redis-plan"
}
module "csv_upload_bucket" {
source = "github.com/18f/terraform-cloudgov//s3"
cf_user = var.cf_user
cf_password = var.cf_password
cf_org_name = local.cf_org_name
cf_space_name = local.cf_space_name
recursive_delete = local.recursive_delete
s3_service_name = "${local.app_name}-csv-upload-bucket-${local.env}"
}
module "contact_list_bucket" {
source = "github.com/18f/terraform-cloudgov//s3"
cf_user = var.cf_user
cf_password = var.cf_password
cf_org_name = local.cf_org_name
cf_space_name = local.cf_space_name
recursive_delete = local.recursive_delete
s3_service_name = "${local.app_name}-contact-list-bucket-${local.env}"
}
###########################################################################
# The following lines need to be commented out for the initial `terraform apply`
# It can be re-enabled after:

View File

@@ -8,7 +8,7 @@ terraform {
}
backend "s3" {
bucket = "cg-31204bcc-aae3-4cd3-8b59-5055a338d44f"
bucket = "cg-6b759c13-6253-4a64-9bda-dd1f620185b0"
key = "api.tfstate.prod"
encrypt = "true"
region = "us-gov-west-1"

View File

@@ -1,2 +1,4 @@
variable "cf_password" {}
variable "cf_password" {
sensitive = true
}
variable "cf_user" {}

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env bash
org="gsa-10x-prototyping"
org="gsa-tts-benefits-studio-prototyping"
usage="
$0: Set egress rules for given space

View File

@@ -1,8 +1,8 @@
locals {
cf_org_name = "gsa-10x-prototyping"
cf_space_name = "10x-notifications"
cf_org_name = "gsa-tts-benefits-studio-prototyping"
cf_space_name = "notify-staging"
env = "staging"
app_name = "notifications-api"
app_name = "notify-api"
recursive_delete = true
}

View File

@@ -8,7 +8,7 @@ terraform {
}
backend "s3" {
bucket = "cg-31204bcc-aae3-4cd3-8b59-5055a338d44f"
bucket = "cg-6b759c13-6253-4a64-9bda-dd1f620185b0"
key = "api.tfstate.stage"
encrypt = "true"
region = "us-gov-west-1"

View File

@@ -1,2 +1,4 @@
variable "cf_password" {}
variable "cf_password" {
sensitive = true
}
variable "cf_user" {}