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

ref(docs): update to deploy/run via oci #185

Merged
merged 1 commit into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
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
Loading