Infrastructure

Currently, our configured sites are available at:

URL Function
https://d2knj5ocbd6xdl.cloudfront.net Staging Site
https://d3ni409s7fq6ur.cloudfront.net Production Site

Our site is hosted on Amazon AWS, and served by Amazon CloudFront edge nodes. To set this up we need to:

AWS S3 Bucket Creation / Configuration

  1. Login to Amazon AWS, select your hosting region (in our case we host in eu-west-2, ie. London).
  2. Select S3 under the Services -> Storage menu and click Create Bucket.
  3. Enter a DNS compliant bucket name, relevant to the DNS you will be assigning the content. For example www.komododigital.co.uk.
  4. Ensure the region is correct, and click next.
  5. Select the options as you would like on the next page. Our standard config is entirely default (no changes are made).
  6. On the next screen, when prompted to Set permissions, leave all as default (i.e. No public access).
  7. Confirm creation of the bucket.

AWS CloudFront Distribution Creation / Configuration

  1. Login to Amazon AWS, select your hosting region (in our case we host in eu-west-2, ie. London).
  2. Select CloudFront under the Services -> Networking & Content Delivery menu and click Create Distribution.
  3. Under the Web heading, click Get Started.
  4. On the Create Distribution page, apply the following settings:
  Origin Domain Name: www.komododigital.co.uk.s3.amazonaws.com (or whatever name you gave to your S3 bucket). 
  Origin Path: <empty>
  Origin ID: <autogenerated/default>
  Restrict Bucket Access: No
  Origin Custom Headers: <autogenerated/default>
  Path Pattern: <autogenerated/default>
  Viewer Protocol Policy: <Redirect HTTP to HTTPS>
  Allowed HTTP Methods: Get, Head
  Field-level Encryption Config: <autogenerated/default>
  Cached HTTP Methods: Get, Head
  Cache Based on Selected Request Headers: <autogenerated/default>
  Object Caching: Use Origin Cache Headers
  Minimum TTL: 0
  Maximum TTL: 31536000
  Default TTL: 86400
  Forward Cookies: <autogenerated/default>
  Query String Forwarding and Caching: <autogenerated/default>
  Smooth Streaming: No
  Restrict Viewer Access: No
  Compress Objects Automatically: No
  Lambda Function Associations: <empty>
  Price Class: Use Only U.S., Canada and Europe
  AWS WAF Web ACL: None
  Alternate Domain Names: www.komododigital.co.uk (or whatever domain this will sit on)
  SSL Certificate: Custom SSL Certificate (providing SSL for the domain above)
  Supported HTTP Versions: HTTP/2 HTTP/1.1 HTTP/1.0
  Default Root Object: <empty>
  Logging: On (You need to setup and choose as you like)
  Bucket for Logs: www.komododigital.co.uk-site-logs (You need to setup and choose as you like)
  Log Prefix: <empty>
  Cookie Loggin: Off
  Enable IPV6: Yes
  Comment: <empty>
  Distribution state: Enabled.
  1. Once created, your site should now be available at the domain of choice. We need to do some extra work (see AWS Lambda below).

AWS Lambda / CloudFront Extended Configuration

After configuring CloudFront as above, there are some extra steps to complete.

  1. Go to AWS Lambda.
  2. Important: Switch AWS region to US East (N. Virginia). This is required for the correct configuration options in CloudFront.
  3. Create a new function with the following settings:
  Name: CacheControl
  Runtime: NodeJs 8.10
  Role: <This will be "Create a new role from one or more templates" if you haven't done this before, OR it will be your previously created role>
  Policy Template: Basic Lambda@Edge permissions (For CloudFront trigger)
  1. Edit the function code to be the following:
  'use strict';
  exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const response = event.Records[0].cf.response;
    const headers = response.headers;

    if (request.uri.startsWith('/static/')) {
      headers['cache-control'] = [
        {
          key: 'Cache-Control',
          value: 'public, max-age=31536000, immutable'
        }
      ];
    } else {
      headers['cache-control'] = [
        {
          key: 'Cache-Control',
          value: 'public, max-age=0, must-revalidate'
        }
      ];
    }

    callback(null, response);
  };
  1. Publish a new version, and then note the ARN in the top right corner (we need that in a bit).
  2. Create a new function with the following settings:
  Name: IndexRedirect
  Runtime: NodeJs 8.10
  Role: <This will be your previously created role>
  1. Edit the function code to be the following:
  exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const uri = request.uri;

    if (uri.endsWith('/')) {
      request.uri += 'index.html';
    } else if (!uri.includes('.')) {
      request.uri += '/index.html';
    }

    callback(null, request);
  };
  1. Publish a new version, and then note the ARN in the top right corner (we need that in a bit).
  2. Go to your AWS CloudFront distribution
  3. Select the Behaviours tab.
  4. Edit the Default behaviour.
  5. Add an Origin Request under Lambda Function Associations and paste the ARN (recorded above) for your IndexRedirect function.
  6. Add an Origin Response under Lambda Function Associations and paste the ARN (recorded above) for your CacheControl function.
  7. Hit Yes, Edit. Your CloudFront distribution should now be configured correctly.

Redirects

If you need to add redirects, do the following:

  1. Go to AWS Lambda.
  2. Important: Switch AWS region to US East (N. Virginia). This is required for the correct configuration options in CloudFront.
  3. Create a new function with the following settings:
  Name: SiteRedirects
  Runtime: NodeJs 8.10
  Role: <This will be "Create a new role from one or more templates" if you haven't done this before, OR it will be your previously created role>
  Policy Template: Basic Lambda@Edge permissions (For CloudFront trigger)
  1. Edit the function code to be the following (adding your redirects in the routes object):
  'use strict';
  exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const uri = event.Records[0].cf.request.uri;
    
    const routes = {
        "/source": "/destination",
    }

    if (uri in routes) {
        const locationRedirect = routes[uri];

        const response = {
            status: '302',
            statusDescription: 'Found',
            headers: {
                location: [{
                    key: 'Location',
                    value: locationRedirect,
                }],
            },
        };
        
        callback(null, response);
    }
    else {
      callback(null, request);
    }
  };
  1. Publish a new version, and then note the ARN in the top right corner (we need that in a bit).
  2. Go to your AWS CloudFront distribution
  3. Select the Behaviours tab.
  4. Edit the Default behaviour.
  5. Add an Viewer Request under Lambda Function Associations and paste the ARN (recorded above) for your SiteRedirects function.
  6. Hit Yes, Edit. Your CloudFront distribution should now be configured correctly and start redirecting.

Codeship Deployment Creation / Configuration

Follow the usual project creation steps on Codeship, until you’re at the point of setting up the pipeline. Configure as below under Project Settings:

Test [Setup Commands]

npm install --global gatsby-cli
npm install
npm run build

Test [Pipelines]

npm test
npm run coverage

### Deploy

  1. Setup on the branch you wish to trigger the deployment - e.g. master.
  2. Select Amazon S3.
  3. Provide the following settings:
  AWS Access Key ID: <Like Im Pasting That Here. Nice Try Though>
  AWS Secret Access Key: <Like Im Pasting That Here. Nice Try Though>
  Region: eu-west-2 (or whichever S3 region you have configured above)
  Local Path: ./public
  S3 Bucket: www.komododigital.co.uk (or whatever you named your S3 bucket name)
  ACL: public-read
  1. Once the above is configured, add a second step of Custom Script.
  2. In the script block enter the following:
  npm run cloudfront-invalidate

Environment

Add the following environment variables to the project:

CODECOV_TOKEN <This is from https://codecov.io>
DISTRIBUTION_ID <This is from AWS CloudFront>
AWS_ACCESS_KEY_ID <This is from AWS IAM>
AWS_SECRET_ACCESS_KEY <This is from AWS IAM>