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

Deploy setup #9

Merged
merged 10 commits into from
Jan 8, 2018
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
20 changes: 14 additions & 6 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ version: 2
jobs:
build:
docker:
# specify the version you desire here
- image: circleci/node:7.10

working_directory: ~/api-services
Expand All @@ -17,17 +16,26 @@ jobs:
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "package-lock.json" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
- v1-dependencies-y{{ checksum "package-lock.json" }}

- run: npm install

- save_cache:
paths:
- node_modules
key: v1-dependencies-{{ checksum "package-lock.json" }}
key: v1-dependencies-y{{ checksum "package-lock.json" }}

- run: npm run ci:test
- run: source .env.test && npm run ci:test
- run: npm run ci:coveralls
- run: npm run flow check

- deploy:
command: |
if [ "${CIRCLE_BRANCH}" == "${STAGING_DEPLOY_BRANCH}" ]; then
echo "Deploying to STAGING"
./deploy/deploy.sh staging
elif [ "${CIRCLE_BRANCH}" == "${PRODUCTION_DEPLOY_BRANCH}" ]; then
echo "Deploying to PRODUCTION"
./deploy/deploy.sh production
fi

9 changes: 9 additions & 0 deletions .env.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export AK_API_URL="https://act.sumofus.org/rest/v1"
export MEMBER_SERVICES_SECRET='member-services-secret'
export BRAINTREE_MERCHANT_ID="6ngbwhytbhrz9z2r"
export BRAINTREE_PUBLIC_KEY='public-key'
export BRAINTREE_PRIVATE_KEY='private-key'
export BRAINTREE_ENV=Sandbox
export VCR_MODE=playback
export CHAMPAIGN_URL='https://action-staging.sumofus.org'
export UNSUBSCRIBE_PAGE_NAME=unsubscribe
1 change: 1 addition & 0 deletions .flowconfig
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[ignore]
<PROJECT_ROOT>/node_modules/.*config-chain/test/broken.json

[include]

Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ coverage

# dotenv environment variables file
.env
secrets.sh

# Settings files!
settings/*
!settings/prod.yml
!settings/production.yml
!settings/staging.yml
.DS_Store

Expand Down
5 changes: 5 additions & 0 deletions deploy/deploy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
ENV=$1
./deploy/downloadSecrets.js "/$ENV/api-services/" > secrets.sh
source secrets.sh
$(npm bin)/serverless deploy -s $ENV --conceal
36 changes: 36 additions & 0 deletions deploy/downloadSecrets.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env node
const getParametersByPath = require('./parametersStoreClient');
const AWS = require('aws-sdk');
const commandLineArgs = require('command-line-args');

const optionDefinitions = [
{ name: 'secrets-path', alias: 'p', type: String, defaultOption: true },
{ name: 'aws-region', alias: 'r', type: String, defaultValue: 'us-west-2' },
{ name: 'help', alias: 'h' },
];
const options = commandLineArgs(optionDefinitions);

if (options.help !== undefined || !options['secrets-path']) {
console.warn(
`usage: ./downloadSecrets.js secrets-path [Options]

Options:
-p or --secrets-path PATH
AWS Parameter Store path (Required)
-r or --aws-region REGION
AWS region used to fetch params from (default: us-west-2)
-h or --help
Help`
);
process.exit(1);
}

getParametersByPath(options['secrets-path'], options['aws-region'])
.then(function(secrets) {
secrets.forEach(function(secret) {
console.log(`export ${secret[0]}='${secret[1]}'`);
});
})
.catch(function(error) {
throw error;
});
37 changes: 37 additions & 0 deletions deploy/parametersStoreClient.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const AWS = require('aws-sdk');

const getParametersByPath = function(path, awsRegion, nextToken = null) {
const ssm = new AWS.SSM({ region: awsRegion });
let params = { Path: path };
if (nextToken !== null) params.NextToken = nextToken;

return new Promise(function(resolve, reject) {
ssm.getParametersByPath(params, function(err, data) {
if (err) reject(err);
else {
let secrets = parse(data);
if (data.NextToken) {
getParametersByPath(path, awsRegion, data.NextToken).then(function(
nextSecrets
) {
resolve(secrets.concat(nextSecrets));
});
} else {
resolve(secrets);
}
}
});
});
};

function parse(data) {
const secrets = [];
data.Parameters.forEach(function(secret) {
const name = secret.Name.replace(/^.*\//, '');
const value = secret.Value;
secrets.push([name, value]);
});
return secrets;
}

module.exports = getParametersByPath;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rodrei let me know if I'm missing something (I suspect I am), but why aren't we referencing the SSM params directly? eg DDB_TABLE: ${ssm:/path/to/dynamodbTable}? https://serverless.com/framework/docs/providers/aws/guide/variables/#reference-variables-using-the-ssm-parameter-store

1 change: 0 additions & 1 deletion lib/clients/actionkit/index.js

This file was deleted.

1 change: 1 addition & 0 deletions lib/clients/actionkit/recurringOrders.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @flow weak
import { AKClient } from './resources/client';

export function cancel(recurringId, client = AKClient) {
Expand Down
1 change: 1 addition & 0 deletions lib/clients/actionkit/resources/client.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @flow weak
import axios from 'axios';
import { basicAuthToken } from '../../../util/basicAuthToken';

Expand Down
1 change: 1 addition & 0 deletions lib/clients/actionkit/resources/orderrecurring.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @flow weak
import { IAKClient as AKClient } from './client';
import { pick as _pick, filter as _filter } from 'lodash';

Expand Down
1 change: 1 addition & 0 deletions lib/clients/actionkit/resources/orderrecurring.test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @flow weak
import { getOrderRecurrings, getGCOrderRecurrings } from './orderrecurring';

describe('getOrderRecurings', () => {
Expand Down
4 changes: 2 additions & 2 deletions lib/clients/actionkit/resources/users.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// @flow
// @flow weak
import { AKClient as client } from './client';
import { resolveProxyShape, rejectProxyShape } from '../helpers';
import type { ProxyShape } from '../helpers';
Expand All @@ -10,7 +10,7 @@ export type SearchFilters = { [key: string]: string | number | boolean };

export function search(f: ?SearchFilters = {}) {
return client
.get(`/user/`, { params: f })
.get(`/user/`, { params: f || {} })
.then(resolveProxyShape, rejectProxyShape);
}

Expand Down
6 changes: 4 additions & 2 deletions lib/clients/champaign/champaign.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
// @flow weak
import axios from 'axios';
import uuid from 'uuid/v1';
import crypto from 'crypto';

const nonce = uuid();

const signature = crypto
.createHmac('sha256', process.env.MEMBER_SERVICES_SECRET)
.createHmac('sha256', process.env.MEMBER_SERVICES_SECRET || '')
.update(nonce)
.digest('hex');

// FIXME: Use one client for all champaign files
const http = axios.create({
baseURL: `${process.env.CHAMPAIGN_URL}/api/member_services/`,
baseURL: `${process.env.CHAMPAIGN_URL || ''}/api/member_services/`,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Expand Down
1 change: 1 addition & 0 deletions lib/clients/champaign/member.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @flow weak
import axios from 'axios';
import { integrationHeaders } from './utils';

Expand Down
1 change: 1 addition & 0 deletions lib/clients/champaign/member.test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @flow weak
import { updateMember } from './member';

const { objectContaining } = expect;
Expand Down
1 change: 1 addition & 0 deletions lib/clients/champaign/recurringDonation.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @flow weak
import axios from 'axios';
import { integrationHeaders } from './utils';
import crypto from 'crypto';
Expand Down
3 changes: 2 additions & 1 deletion lib/clients/gocardless/gocardless.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @flow weak
import axios from 'axios';
import { pick as _pick } from 'lodash';

Expand All @@ -11,7 +12,7 @@ const http = axios.create({
baseURL,
headers: {
Accept: 'application/json',
Authorization: `Bearer ${process.env.GOCARDLESS_TOKEN}`,
Authorization: `Bearer ${process.env.GOCARDLESS_TOKEN || ''}`,
'GoCardless-Version': '2015-07-06',
},
});
Expand Down
1 change: 1 addition & 0 deletions lib/clients/gocardless/gocardless.test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// @flow weak
import { getSubscription } from './gocardless';

describe('getSubscription', () => {
Expand Down
6 changes: 3 additions & 3 deletions lib/lambda-utils/responses.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ export function notFound(options?: ResponseOptions = {}): ProxyResult {
// }

// 5xx Server Error responses
// export function serverError(options?: ResponseOptions = {}): ProxyResult {
// return response({ ...options, statusCode: 500 });
// }
export function serverError(options?: ResponseOptions = {}): ProxyResult {
return response({ ...options, statusCode: 500 });
}

// export function notImplemented(options?: ResponseOptions = {}): ProxyResult {
// return response({ ...options, statusCode: 501, body: '' });
Expand Down
15 changes: 14 additions & 1 deletion lib/lambda-utils/responses.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
// @flow
import { body, badRequest, headers, ok, response } from './responses';
import {
body,
badRequest,
headers,
ok,
response,
serverError,
} from './responses';

describe('badRequest(...)', () => {
test('always returns a statusCode 400', () => {
Expand Down Expand Up @@ -56,6 +63,12 @@ describe('ok(...)', () => {
});
});

describe('serverError(...)', () => {
test('statusCode defaults to 500 OK', () => {
expect(serverError().statusCode).toEqual(500);
});
});

describe('response(...)', () => {
test('statusCode defaults to 200 OK', () => {
expect(response().statusCode).toEqual(200);
Expand Down
Loading