Skip to content

Commit

Permalink
Split up Server into Renderer/API for Staging (#9827)
Browse files Browse the repository at this point in the history
* API docker file stage

* add api to ecs task definition

* add api image to workflow

* add API_ONLY config changes

* add API Only infra

* add healthcheck to routes

* change route order

* fix gh action

* use a different way to check this to allow for multiple labels

* make a separate service for the api server

* fix image nnot being pushed to the right repo

* don't set_local on api requests

* fix healthcheck

* move API help to static page controller

* render errors as json for api requests

* enable back session store in api_only

* don't set API only for api server

* run rubocop

* add API deployment to other Workflows
  • Loading branch information
FinnIckler authored Sep 10, 2024
1 parent b662106 commit 19d74c5
Show file tree
Hide file tree
Showing 16 changed files with 333 additions and 19 deletions.
79 changes: 79 additions & 0 deletions .github/workflows/deploy-main-to-staging-from-tagged-prs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: Deploy Main to Staging From Tagged PRs
on:
pull_request:
branches:
- main
jobs:
build:
if: contains(github.event.pull_request.labels.*.name, 'deploy-staging')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.CI_CD_AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.CI_CD_AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_DEFAULT_REGION }}
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Get the SHA of the current branch/fork
shell: bash
run: |
echo "SHORT_SHA=$(git rev-parse --short HEAD)" >> "$GITHUB_ENV"
- name: Build and Push Sidekiq image
uses: docker/build-push-action@v6
with:
push: true
target: sidekiq
tags: |
${{ steps.login-ecr.outputs.registry }}/wca-on-rails:sidekiq-staging
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
BUILD_TAG=${{ env.SHORT_SHA }}
WCA_LIVE_SITE=false
SHAKAPACKER_ASSET_HOST=https://assets-staging.worldcubeassociation.org
- name: Build and push Staging API Image
uses: docker/build-push-action@v6
with:
push: true
target: monolith-api
tags: |
${{ steps.login-ecr.outputs.registry }}/wca-on-rails:staging-api
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
BUILD_TAG=${{ env.SHORT_SHA }}
WCA_LIVE_SITE=false
SHAKAPACKER_ASSET_HOST=https://assets-staging.worldcubeassociation.org
- name: Build and push Staging Image
id: build-staging
uses: docker/build-push-action@v6
with:
push: true
load: true
target: monolith
tags: |
${{ steps.login-ecr.outputs.registry }}/wca-on-rails:staging
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
BUILD_TAG=${{ env.SHORT_SHA }}
WCA_LIVE_SITE=false
SHAKAPACKER_ASSET_HOST=https://assets-staging.worldcubeassociation.org
# We build assets in docker and copy it out so we don't have to install all the dependencies in the GH Action
- name: Copy assets out of the container and push to S3
run: |
id=$(docker create ${{steps.build-staging.outputs.imageid }})
docker cp $id:/rails/public/ ./assets
aws s3 sync ./assets s3://assets-staging.worldcubeassociation.org/assets/${{ env.SHORT_SHA }}
# There is no pipeline for staging so we manually force to update the image
- name: Deploy staging
run: |
aws ecs update-service --cluster wca-on-rails --service wca-on-rails-staging --force-new-deployment
aws ecs update-service --cluster wca-on-rails --service wca-on-rails-staging-API --force-new-deployment
15 changes: 15 additions & 0 deletions .github/workflows/deploy-main-to-staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ jobs:
BUILD_TAG=${{ env.SHORT_SHA }}
WCA_LIVE_SITE=false
SHAKAPACKER_ASSET_HOST=https://assets-staging.worldcubeassociation.org
- name: Build and push Staging API Image
uses: docker/build-push-action@v6
with:
push: true
target: monolith-api
tags: |
${{ steps.login-ecr.outputs.registry }}/wca-on-rails:staging-api
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
BUILD_TAG=${{ env.SHORT_SHA }}
WCA_LIVE_SITE=false
SHAKAPACKER_ASSET_HOST=https://assets-staging.worldcubeassociation.org
- name: Build and push Staging Image
id: build-staging
uses: docker/build-push-action@v6
Expand All @@ -49,6 +62,7 @@ jobs:
BUILD_TAG=${{ env.SHORT_SHA }}
WCA_LIVE_SITE=false
SHAKAPACKER_ASSET_HOST=https://assets-staging.worldcubeassociation.org
# We build assets in docker and copy it out so we don't have to install all the dependencies in the GH Action
- name: Copy assets out of the container and push to S3
run: |
Expand All @@ -59,3 +73,4 @@ jobs:
- name: Deploy staging
run: |
aws ecs update-service --cluster wca-on-rails --service wca-on-rails-staging --force-new-deployment
aws ecs update-service --cluster wca-on-rails --service wca-on-rails-staging-API --force-new-deployment
15 changes: 15 additions & 0 deletions .github/workflows/staging-deployment-from-prs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,20 @@ jobs:
BUILD_TAG=${{ env.SHORT_SHA }}
WCA_LIVE_SITE=false
SHAKAPACKER_ASSET_HOST=https://assets-staging.worldcubeassociation.org
- name: Build and push Staging API Image if triggered
if: steps.trigger-deployment.outputs.triggered == 'true'
uses: docker/build-push-action@v6
with:
push: true
target: monolith-api
tags: |
${{ steps.login-ecr.outputs.registry }}/wca-on-rails:staging-api
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
BUILD_TAG=${{ env.SHORT_SHA }}
WCA_LIVE_SITE=false
SHAKAPACKER_ASSET_HOST=https://assets-staging.worldcubeassociation.org
- name: Build Rails if triggered
if: steps.trigger-deployment.outputs.triggered == 'true'
id: build-staging
Expand Down Expand Up @@ -87,3 +101,4 @@ jobs:
if: steps.trigger-deployment.outputs.triggered == 'true'
run: |
aws ecs update-service --cluster wca-on-rails --service wca-on-rails-staging --force-new-deployment
aws ecs update-service --cluster wca-on-rails --service wca-on-rails-staging-API --force-new-deployment
10 changes: 9 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,15 @@ RUN fc-cache -f -v

# Entrypoint prepares database and starts app on 0.0.0.0:3000 by default,
# but can also take a rails command, like "console" or "runner" to start instead.
ENV PIDFILE "/rails/pids/puma.pid"
ENV PIDFILE="/rails/pids/puma.pid"

ENTRYPOINT ["/rails/bin/docker-entrypoint"]
CMD ["./bin/rails", "server"]

FROM runtime AS monolith-api

EXPOSE 3000

USER rails:rails
ENV API_ONLY="true"
CMD ["./bin/rails", "server"]
7 changes: 4 additions & 3 deletions app/controllers/api/v0/api_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ def me
render json: { me: current_api_user }, private_attributes: doorkeeper_token.scopes
end

def healthcheck
render json: { status: "ok", api_instance: EnvConfig.API_ONLY? }
end

def auth_results
if !current_user
return render status: :unauthorized, json: { error: "Please log in" }
Expand Down Expand Up @@ -132,9 +136,6 @@ def scramble_program
}
end

def help
end

def search(*models)
query = params[:q]&.slice(0...SearchResultsController::SEARCH_QUERY_LIMIT)

Expand Down
12 changes: 12 additions & 0 deletions app/controllers/api_errors_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# frozen_string_literal: true

class ApiErrorsController < ApplicationController
skip_before_action :set_locale
skip_before_action :store_user_location!
def show
exception = request.env["action_dispatch.exception"]
status_code = ActionDispatch::ExceptionWrapper.new(request.env, exception).status_code
request_id = request.env["action_dispatch.request_id"]
render json: { error_code: status_code, request_id: request_id, contact_url: Rails.application.routes.url_helpers.contact_url(contactRecipient: 'wst', requestId: request_id) }, status: status_code
end
end
2 changes: 1 addition & 1 deletion app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class ApplicationController < ActionController::Base

protect_from_forgery with: :exception

prepend_before_action :set_locale
prepend_before_action :set_locale, unless: :is_api_request?
before_action :store_user_location!, if: :storable_location?
before_action :add_new_relic_headers
protected def add_new_relic_headers
Expand Down
3 changes: 3 additions & 0 deletions app/controllers/static_pages_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ def score_tools
def logo
end

def api_help
end

def robots
respond_to :txt
end
Expand Down
File renamed without changes.
6 changes: 5 additions & 1 deletion config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,11 @@ class Application < Rails::Application
end

config.default_from_address = "[email protected]"
config.site_name = "World Cube Association"
config.site_name = if EnvConfig.API_ONLY?
"World Cube Association API"
else
"World Cube Association"
end

config.middleware.insert_before 0, Rack::Cors, debug: false, logger: (-> { Rails.logger }) do
allow do
Expand Down
7 changes: 6 additions & 1 deletion config/environments/production.rb
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,13 @@

# Error pages for production
config.exceptions_app = ->(env) {
ErrorsController.action(:show).call(env)
if EnvConfig.API_ONLY?
ApiErrorsController.action(:show).call(env)
else
ErrorsController.action(:show).call(env)
end
}

# Inserts middleware to perform automatic connection switching.
# The `database_selector` hash is used to pass options to the DatabaseSelector
# middleware. The `delay` is used to determine how long to wait after a write
Expand Down
7 changes: 5 additions & 2 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@

get 'robots' => 'static_pages#robots'

get 'help/api' => 'static_pages#api_help'

get 'server-status' => 'server_status#index'

get 'translations', to: redirect('translations/status', status: 302)
Expand Down Expand Up @@ -320,7 +322,7 @@
end

namespace :api do
get '/', to: redirect('/api/v0', status: 302)
get '/', to: redirect('/help/api', status: 302)
namespace :internal do
namespace :v1 do
get '/users/:id/permissions' => 'permissions#index'
Expand All @@ -330,8 +332,9 @@
end
end
namespace :v0 do
get '/' => 'api#help'
get '/', to: redirect('/help/api', status: 302)
get '/me' => 'api#me'
get '/healthcheck' => 'api#healthcheck'
get '/auth/results' => 'api#auth_results'
get '/export/public' => 'api#export_public'
get '/scramble-program' => 'api#scramble_program'
Expand Down
3 changes: 3 additions & 0 deletions env_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@

# For Asset Compilation
optional :ASSETS_COMPILATION, :bool, false

# For API Only Server
optional :API_ONLY, :bool, false
end

# Require Asset Specific ENV variables
Expand Down
52 changes: 51 additions & 1 deletion infra/wca_on_rails/shared/elb.tf
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,30 @@ resource "aws_lb_target_group" "rails-staging" {
}
}

resource "aws_lb_target_group" "rails-staging-api" {
name = "wca-rails-staging-api"
port = 3000
protocol = "HTTP"
vpc_id = aws_default_vpc.default.id
target_type = "ip"

deregistration_delay = 10
health_check {
interval = 5
path = "/api/v0/healthcheck"
port = "traffic-port"
protocol = "HTTP"
timeout = 2
healthy_threshold = 2
unhealthy_threshold = 5
matcher = 200
}
tags = {
Name = var.name_prefix
Env = "staging"
}
}

resource "aws_lb_target_group" "mailcatcher-staging" {
name = "wca-staging-mailcatcher"
port = 1080
Expand Down Expand Up @@ -300,7 +324,7 @@ resource "aws_lb_listener_rule" "pma_forward_prod" {

resource "aws_lb_listener_rule" "rails_forward_staging" {
listener_arn = aws_lb_listener.https.arn
priority = 2
priority = 4

action {
type = "forward"
Expand All @@ -314,6 +338,28 @@ resource "aws_lb_listener_rule" "rails_forward_staging" {
}
}

resource "aws_lb_listener_rule" "rails_forward_staging_api" {
listener_arn = aws_lb_listener.https.arn
priority = 2

action {
type = "forward"
target_group_arn = aws_lb_target_group.rails-staging-api.arn
}

condition {
host_header {
values = ["staging.worldcubeassociation.org"]
}
}

condition {
path_pattern {
values = ["/api*"]
}
}
}

resource "aws_lb_listener_rule" "pma_forward_staging" {
listener_arn = aws_lb_listener.https.arn
priority = 1
Expand Down Expand Up @@ -374,6 +420,10 @@ output "rails_staging"{
value = aws_lb_target_group.rails-staging
}

output "rails_staging-api"{
value = aws_lb_target_group.rails-staging-api
}

output "pma_production"{
value = aws_lb_target_group.auxiliary
}
Expand Down
Loading

0 comments on commit 19d74c5

Please sign in to comment.