Skip to content

Commit

Permalink
ref(*): update to deploy/run via oci
Browse files Browse the repository at this point in the history
Signed-off-by: Vaughn Dice <vaughn.dice@fermyon.com>
  • Loading branch information
vdice committed Nov 29, 2023
1 parent 2c2be0d commit 93e7e82
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 223 deletions.
140 changes: 55 additions & 85 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,6 @@ on:
default: 'refs/heads/main'
commit:
description: 'Commit SHA to deploy from (optional)'
environment:
type: choice
description: 'Environment to deploy to (Default: canary)'
options:
- canary
- prod

# Construct a concurrency group to be shared across workflow runs.
# The default behavior ensures that only one is running at a time, with
Expand All @@ -28,9 +22,6 @@ permissions:
contents: read
id-token: write # Allow the workflow to create a JWT for AWS auth

env:
JOB: bartholomew-docs

jobs:
echo-inputs:
runs-on: ubuntu-latest
Expand All @@ -40,103 +31,82 @@ jobs:
run: |
echo ref: ${{ github.event.inputs.ref }}
echo commit: ${{ github.event.inputs.commit }}
echo environment: ${{ github.event.inputs.environment }}
deploy:
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'fermyon' }}
env:
OCI_IMAGE_NAME: bartholomew-docs
steps:
- uses: actions/checkout@v3

- name: Install Nomad
env:
NOMAD_VERSION: "1.4.3"
run: |
curl -Os https://releases.hashicorp.com/nomad/${NOMAD_VERSION}/nomad_${NOMAD_VERSION}_linux_$(dpkg --print-architecture).zip
unzip nomad_${NOMAD_VERSION}_linux_$(dpkg --print-architecture).zip -d /usr/local/bin
chmod +x /usr/local/bin/nomad
- name: Check out specific ref
if: ${{ github.event_name == 'workflow_dispatch' }} && ${{ inputs.ref != ''}}
run: git checkout ${{ inputs.ref }}

# This action currently generates a warning due to using deprecated features.
# https://github.com/aws-actions/configure-aws-credentials/issues/521 tracks the new behaviour.
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.INFRA_NAMESPACE }}-${{ secrets.AWS_REGION }}-gha-certs
role-session-name: fermyon-developer-deploy
aws-region: ${{ secrets.AWS_REGION }}
- name: Check out specific commit
if: ${{ github.event_name == 'workflow_dispatch' }} && ${{ inputs.commit != ''}}
run: git checkout ${{ inputs.commit }}

- name: Fetch Nomad Certs from S3
- name: Construct OCI image tag
shell: bash
run: |
set -euo pipefail
[[ "${{ github.event_name }}" == "push" ]] && \
echo "IMAGE_TAG=latest" >> $GITHUB_ENV || \
echo "IMAGE_TAG=canary" >> $GITHUB_ENV
for cert in infra_ca \
api_client_cert_private_key \
api_client_cert_public_key; do
- name: Setup Spin
uses: fermyon/actions/spin/setup@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
version: v2.0.1

aws s3api get-object \
--bucket "infra-certs-${{ secrets.INFRA_NAMESPACE }}-${{ secrets.AWS_REGION }}" \
--key "${cert}" \
"/tmp/${cert}"
done
- name: Configure AWS Credentials for publishing
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/${{ secrets.AWS_IAM_ROLE }}
role-session-name: ${{ env.OCI_IMAGE_NAME }}
aws-region: ${{ secrets.AWS_REGION_WEBSITES }}

- name: Configure Nomad
shell: bash
run: |
echo "NOMAD_CACERT=/tmp/infra_ca" >> $GITHUB_ENV
echo "NOMAD_CLIENT_CERT=/tmp/api_client_cert_public_key" >> $GITHUB_ENV
echo "NOMAD_CLIENT_KEY=/tmp/api_client_cert_private_key" >> $GITHUB_ENV
echo "NOMAD_ADDR=https://nomad.${{ secrets.INFRA_NAMESPACE }}.${{ secrets.AWS_REGION }}.fermyon.link:4646" >> $GITHUB_ENV
- name: Log in to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2

- name: Configure manual deploy
if: ${{ github.event_name == 'workflow_dispatch' }}
shell: bash
- name: Publish Spin app
id: push
uses: fermyon/actions/spin/push@v1
with:
registry_reference: ${{ steps.login-ecr.outputs.registry }}/${{ env.OCI_IMAGE_NAME }}:${{ env.IMAGE_TAG }}
manifest_file: docs/spin.toml

- name: Cleanup login
if: ${{ always() }}
run: |
echo "GIT_REF=${{ github.event.inputs.ref }}" >> $GITHUB_ENV
echo "GIT_SHA=${{ github.event.inputs.commit }}" >> $GITHUB_ENV
if [[ "${{ github.event.inputs.environment }}" == "prod" ]]; then
echo "PRODUCTION=true" >> $GITHUB_ENV
echo "NOMAD_NAMESPACE=prod" >> $GITHUB_ENV
else
echo "PRODUCTION=false" >> $GITHUB_ENV
echo "NOMAD_NAMESPACE=staging" >> $GITHUB_ENV
fi
- name: Configure auto-deploy
if: ${{ github.event_name == 'push' }}
shell: bash
rm -rf /home/runner/fermyon
- name: Install Nomad
env:
NOMAD_VERSION: "1.4.3"
run: |
echo "GIT_REF=${{ github.ref }}" >> $GITHUB_ENV
echo "GIT_SHA=${{ github.sha }}" >> $GITHUB_ENV
curl -Os https://releases.hashicorp.com/nomad/${NOMAD_VERSION}/nomad_${NOMAD_VERSION}_linux_$(dpkg --print-architecture).zip
unzip nomad_${NOMAD_VERSION}_linux_$(dpkg --print-architecture).zip -d /usr/local/bin
chmod +x /usr/local/bin/nomad
echo "PRODUCTION=true" >> $GITHUB_ENV
echo "NOMAD_NAMESPACE=prod" >> $GITHUB_ENV
- name: Tailscale
uses: tailscale/github-action@v2
with:
oauth-client-id: ${{ secrets.TS_CI_OAUTH_CLIENT_ID }}
oauth-secret: ${{ secrets.TS_CI_OAUTH_SECRET }}
tags: tag:ci

- name: Deploy
shell: bash
run: |
set -euox pipefail
# purge any lingering/completed publish jobs
nomad job inspect publish-${{ env.JOB }} &>/dev/null && \
nomad stop -purge -yes publish-${{ env.JOB }}
# run the publish job
nomad run \
-var "region=${{ secrets.AWS_REGION }}" \
-var "git_ref=${{ env.GIT_REF }}" \
-var "commit_sha=${{ env.GIT_SHA }}" \
deploy/publish-${{ env.JOB }}.nomad
# wait for publish job to complete
timeout 300s bash -c 'until [[ "$(nomad job inspect publish-${{ env.JOB }} | jq -j '.Job.Status')" == "dead" ]]; do sleep 2; done'
readonly bindle_id="$(nomad logs -job publish-${{ env.JOB }} | sed -n 's/pushed: //p')"
export NOMAD_ADDR="${{ secrets.NOMAD_ADDR }}"
export NOMAD_NAMESPACE=websites
# run/update the website job
nomad run \
-var "region=${{ secrets.AWS_REGION }}" \
-var "production=${{ env.PRODUCTION }}" \
-var "bindle_id=${bindle_id}" \
deploy/${{ env.JOB }}.nomad
-var "region=${{ secrets.AWS_REGION_WEBSITES }}" \
-var "commit_sha=$(git rev-parse HEAD)" \
-var "ecr_ref=${{ steps.login-ecr.outputs.registry }}/${{ env.OCI_IMAGE_NAME }}@${{ steps.push.outputs.digest }}" \
deploy/bartholomew-docs.nomad
8 changes: 4 additions & 4 deletions deploy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@

The [Bartholomew](https://bartholomew.fermyon.dev) website is deployed via the [deploy.yaml](../.github/workflows/deploy.yml) GitHub workflow.

This website is a simple redirect app to redirect requests from `bartholomew.fermyon.dev/*` to `developer.fermyon.com/bartholomew/*`.

## Auto Deploys

The production version of the website is deployed whenever commits are pushed to the `main` branch.

## Manual Deploys

Deployments may also be [triggered manually](https://github.com/fermyon/bartholomew/actions/workflows/deploy.yml), providing a choice of `ref`, `sha` and `environment` (eg canary or prod).
Deployments may also be [triggered manually](https://github.com/fermyon/bartholomew/actions/workflows/deploy.yml), providing a choice of `ref` and `commit`.

## Nomad jobs

We currently deploy the website via its Nomad job directly. (In the future, we envision running the website as a Fermyon Cloud app.)

The [publish-bartholomew-docs](./publish-bartholomew-docs.nomad) Nomad job checks out this repo's source code and publishes it to Bindle.

The [bartholomew-docs](./bartholomew-docs.nomad) Nomad job contains configuration for the running website, including the bindle ID to run from.
The [bartholomew-docs](./bartholomew-docs.nomad) Nomad job contains configuration for the running website, including the OCI reference to run from.
111 changes: 55 additions & 56 deletions deploy/bartholomew-docs.nomad
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,14 @@ variable "region" {
type = string
}

variable "production" {
type = bool
default = false
description = "Whether or not this job should run in production mode. Default: false."
}

variable "dns_domain" {
type = string
default = "fermyon.dev"
description = "The root DNS domain for the Bartholomew docs website, e.g. fermyon.dev, fermyon.link"
}

variable "hostname" {
variable "ecr_ref" {
type = string
default = null
description = "An alternative hostname to use (defaults are <canary>.bartholomew.<dns_domain>})"
}

variable "letsencrypt_env" {
type = string
default = "prod"
description = <<EOF
The Let's Encrypt cert resolver to use. Options are 'staging' and 'prod'. (Default: prod)
With the letsencrypt-prod cert resolver, we're limited to *5 requests per week* for a cert with matching domain and SANs.
For testing/staging, it is recommended to use letsencrypt-staging, which has vastly increased limits.
EOF

validation {
condition = var.letsencrypt_env == "staging" || var.letsencrypt_env == "prod"
error_message = "The Let's Encrypt env must be either 'staging' or 'prod'."
}
description = "The ECR reference of the Spin app for the Bartholomew Docs website"
}

variable "bindle_id" {
variable "commit_sha" {
type = string
default = "bartholomew-docs/0.1.0"
description = "A bindle id, such as foo/bar/1.2.3"
}

locals {
hostname = "${var.hostname == null ? "${var.production == true ? "bartholomew.${var.dns_domain}" : "canary.bartholomew.${var.dns_domain}"}" : var.hostname}"
description = "The git commit sha that the website is published from"
}

job "bartholomew-docs" {
Expand All @@ -57,6 +23,12 @@ job "bartholomew-docs" {
"${var.region}f"
]

# Add unique metadata to support recreating the job even if var.ecr_ref
# represents a mutable tag (eg latest).
meta {
commit_sha = var.commit_sha
}

group "bartholomew-docs" {
count = 3

Expand All @@ -80,11 +52,11 @@ job "bartholomew-docs" {

tags = [
"traefik.enable=true",
"traefik.http.routers.bartholomew-docs-${NOMAD_NAMESPACE}.rule=Host(`${local.hostname}`)",
"traefik.http.routers.bartholomew-docs-${NOMAD_NAMESPACE}.rule=Host(`bartholomew.fermyon.dev`)",
"traefik.http.routers.bartholomew-docs-${NOMAD_NAMESPACE}.entryPoints=websecure",
"traefik.http.routers.bartholomew-docs-${NOMAD_NAMESPACE}.tls=true",
"traefik.http.routers.bartholomew-docs-${NOMAD_NAMESPACE}.tls.certresolver=letsencrypt-cf-${var.letsencrypt_env}",
"traefik.http.routers.bartholomew-docs-${NOMAD_NAMESPACE}.tls.domains[0].main=${local.hostname}"
"traefik.http.routers.bartholomew-docs-${NOMAD_NAMESPACE}.tls.certresolver=letsencrypt-cf-prod",
"traefik.http.routers.bartholomew-docs-${NOMAD_NAMESPACE}.tls.domains[0].main=bartholomew.fermyon.dev"
]

check {
Expand All @@ -98,31 +70,58 @@ job "bartholomew-docs" {
task "server" {
driver = "exec"

vault {
policies = ["svc-website-runner"]
}

artifact {
source = "https://github.com/fermyon/spin/releases/download/v0.8.0/spin-v0.8.0-linux-amd64.tar.gz"
source = "https://github.com/fermyon/spin/releases/download/v2.0.1/spin-v2.0.1-linux-amd64.tar.gz"
options {
checksum = "sha256:0ef31fe6e2b4d34ddd089b01a1f88820f88c456276bfe4e1477836a6087654c1"
checksum = "sha256:686bb12b9244ed33bf54a53e62303879036632b476ad09a728172b260f26c8e7"
}
}

env {
RUST_LOG = "spin=trace"
BINDLE_URL = "http://bindle.service.consul:3030/v1"
BASE_URL = "https://${local.hostname}"
BASE_URL = "https://bartholomew.fermyon.dev"
}

config {
command = "spin"
args = [
"up",
"--listen", "${NOMAD_IP_http}:${NOMAD_PORT_http}",
"--bindle", var.bindle_id,
"--log-dir", "${NOMAD_ALLOC_DIR}/logs",
"--temp", "${NOMAD_ALLOC_DIR}/tmp",

# Set BASE_URL for Bartholomew to override default (localhost:3000)
"-e", "BASE_URL=${BASE_URL}",
]
command = "${NOMAD_TASK_DIR}/run.sh"
}

template {
destination = "${NOMAD_TASK_DIR}/run.sh"
change_mode = "restart"
data = <<-EOF
#!/bin/bash
set -euo pipefail
IFS=/ read -r registry image <<< "${var.ecr_ref}"
aws ecr get-login-password --region ${var.region} | \
${NOMAD_TASK_DIR}/spin registry login --username AWS --password-stdin $registry
${NOMAD_TASK_DIR}/spin up \
--from-registry ${var.ecr_ref} \
--listen ${NOMAD_IP_http}:${NOMAD_PORT_http} \
--log-dir ${NOMAD_ALLOC_DIR}/logs \
--temp ${NOMAD_ALLOC_DIR}/tmp \
-e BASE_URL=${BASE_URL}
EOF
}

template {
destination = "${NOMAD_SECRETS_DIR}/env.txt"
// Don't restart the task when creds expire; these are only needed on first login/pull
change_mode = "noop"
env = true
data = <<-EOF
{{ with secret "aws/creds/website-runner" "ttl=15m" }}
AWS_ACCESS_KEY_ID="{{ .Data.access_key }}"
AWS_SECRET_ACCESS_KEY="{{ .Data.secret_key }}"
AWS_SESSION_TOKEN="{{ .Data.security_token }}"
{{ end }}
EOF
}
}
}
Expand Down
Loading

0 comments on commit 93e7e82

Please sign in to comment.