diff --git a/README.md b/README.md index 85c65c8..b62eb4a 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,17 @@ Add a few lines to your `package.json`. Your [account id](https://console.aws.am Environments for a shep project are defined by the aliases on the functions associated with a project. Environments are created through `shep deploy --env new_env` and managed by using the `shep config` commands. Shep takes a strong stance against having different environments for different functions within a project. If you attempt a command which requires the listing of environments and there is a mismatch detected, then shep will throw a `EnvironmentMistmach` error until you remedy the issue. Most issues can be automatically fixed by using `shep config sync`, the only issues this can't solve are conflicting environment variable values. Conflicting value issues can be solved by using `shep config set my_env CONFLICT_VARIABLE=value`. +Additionally, environment specific configuration for the lambda deployment itself can be defined in both the project's and function's `lambda.{env}.json` files. This is particularly useful in specifying which VPC a lambda function should be placed in when you have a VPC per environment setup. Additionally if you are still using the `nodejs4.3` runtime in production and are looking to upgrade to the `nodejs6.10` runtime, you can test this in only for your development stage. + +```json +{ + "VpcConfig": { + "SecurityGroupIds": [ "sg-12345678" ], + "SubnetIds": [ "subnet-12345678", "subnet-abcdef12", "subnet-abcd1234" ] + }, + "Runtime": "nodejs6.10" +} +``` ### Custom Builds Commands By default shep builds all your functions using webpack. If your project requires a different build process, then edit your `package.json`. Before running your build command, shep populates the `PATTERN` environment variable which can be accessed as `process.env.PATTERN` in your build command. Be aware that using your own build process will break pattern matching for `shep build` unless your build command respects the `PATTERN` variable. diff --git a/src/config-sync/index.js b/src/config-sync/index.js index 14d6b4b..1ed91a2 100644 --- a/src/config-sync/index.js +++ b/src/config-sync/index.js @@ -6,7 +6,7 @@ import uploadEnvironment from '../util/upload-environment' import { funcs, lambdaConfig } from '../util/load' export default async function ({ env }) { - const configs = await Promise.map(funcs(), lambdaConfig) + const configs = await Promise.map(funcs(), (func) => lambdaConfig(func, env)) const environments = await getFunctionEnvs(env, configs) const { common, differences, conflicts } = environmentCheck(environments) diff --git a/src/logs/index.js b/src/logs/index.js index a8d9dcc..3afc334 100644 --- a/src/logs/index.js +++ b/src/logs/index.js @@ -5,7 +5,7 @@ import getLogs from '../util/get-logs' import { lambdaConfig } from '../util/load' export default async function ({ name, env, time = Infinity, logger = () => {} }) { - const { FunctionName } = await lambdaConfig(name) + const { FunctionName } = await lambdaConfig(name, env) const aliasName = env const [logGroupName, functionVersion] = await Promise.all([getLogGroup({ FunctionName }), getAliasVersion({ functionName: FunctionName, aliasName })]) diff --git a/src/run/index.js b/src/run/index.js index 1beb3ce..f9644d0 100644 --- a/src/run/index.js +++ b/src/run/index.js @@ -41,7 +41,7 @@ function runFunction (opts) { return async (name) => { const env = opts.environment || 'development' const performBuild = opts.build - const lambdaConfig = await load.lambdaConfig(name) + const lambdaConfig = await load.lambdaConfig(name, env) const events = await load.events(name, opts.event) const [ fileName, handler ] = lambdaConfig.Handler.split('.') diff --git a/src/util/get-environment.js b/src/util/get-environment.js index eb545c5..418deb6 100644 --- a/src/util/get-environment.js +++ b/src/util/get-environment.js @@ -3,6 +3,6 @@ import { getEnvironment } from './aws/lambda' import { lambdaConfig, funcs } from './load' export default async function (env, name) { - const configs = await Promise.map(funcs(name), lambdaConfig) + const configs = await Promise.map(funcs(name), (func) => lambdaConfig(func, env)) return Promise.map(configs, (config) => getEnvironment(env, config)) } diff --git a/src/util/load.js b/src/util/load.js index 12a5d60..d33ba84 100644 --- a/src/util/load.js +++ b/src/util/load.js @@ -1,7 +1,7 @@ import isEqual from 'lodash.isequal' import path from 'path' import Promise from 'bluebird' -import { readdir, readJSON } from './modules/fs' +import { readdir, readJSON, exists } from './modules/fs' import minimatch from 'minimatch' import { listAliases, isFunctionDeployed } from './aws/lambda' @@ -53,11 +53,15 @@ export async function funcs (pattern = '*') { return funcs } -export async function lambdaConfig (name) { +export async function lambdaConfig (name, env = null) { const functionConfig = await readJSON(`functions/${name}/lambda.json`) + const functionEnvConfigExists = env ? await exists(`functions/${name}/lambda.${env}.json`) : false + const functionEnvConfig = functionEnvConfigExists ? await readJSON(`functions/${name}/lambda.${env}.json`) : {} const projectConfig = await readJSON(`lambda.json`) + const projectEnvConfigExists = env ? await exists(`lambda.${env}.json`) : false + const projectEnvConfig = projectEnvConfigExists ? await readJSON(`lambda.${env}.json`) : {} - return Object.assign({}, projectConfig, functionConfig) + return Object.assign({}, projectConfig, projectEnvConfig, functionConfig, functionEnvConfig) } export async function pkg () { diff --git a/src/util/remove-environment.js b/src/util/remove-environment.js index e4bdf92..365a016 100644 --- a/src/util/remove-environment.js +++ b/src/util/remove-environment.js @@ -7,7 +7,7 @@ import { AWSEnvironmentVariableNotFound } from './errors' const pattern = '*' export default async function (env, vars) { - const configs = await Promise.map(funcs(pattern), async (name) => { return { name, config: await lambdaConfig(name) } }) + const configs = await Promise.map(funcs(pattern), async (name) => { return { name, config: await lambdaConfig(name, env) } }) return Promise.map(configs, async ({ name, config }) => { const oldFunc = await getFunction({ FunctionName: config.FunctionName, Qualifier: env }) const wantedFunc = merge({}, oldFunc) diff --git a/src/util/upload-environment.js b/src/util/upload-environment.js index 91ffa7e..fc7b3a6 100644 --- a/src/util/upload-environment.js +++ b/src/util/upload-environment.js @@ -6,7 +6,7 @@ import { lambdaConfig, funcs } from './load' const pattern = '*' export default async function (env, vars) { - const configs = await Promise.map(funcs(pattern), async (name) => { return { name, config: await lambdaConfig(name) } }) + const configs = await Promise.map(funcs(pattern), async (name) => { return { name, config: await lambdaConfig(name, env) } }) return Promise.map(configs, async ({name, config}) => { const aliasExists = await doesAliasExist({ FunctionName: config.FunctionName, Alias: env }) const oldFunc = await getFunction({ FunctionName: config.FunctionName, Qualifier: (aliasExists ? env : undefined) }) diff --git a/src/util/upload-functions.js b/src/util/upload-functions.js index 8422d8c..e3210d7 100644 --- a/src/util/upload-functions.js +++ b/src/util/upload-functions.js @@ -6,7 +6,7 @@ import zipDir from './zip-dir' export default async function (fns, env) { return Promise.map(fns, async ({ name, key, bucket }) => { - const config = await lambdaConfig(name) + const config = await lambdaConfig(name, env) let wantedFunc = { Config: config, Code: {}, Identifier: {} } if (bucket && key) { diff --git a/test-ava/logs/index.js b/test-ava/logs/index.js index 163fa78..609f405 100644 --- a/test-ava/logs/index.js +++ b/test-ava/logs/index.js @@ -7,7 +7,7 @@ const pkg = { region: 'us-east-1' } } -const stage = 'development' +const env = 'development' const functionName = 'bar' const callback = td.function('callback') const getLogResponse = { @@ -17,19 +17,19 @@ const getLogResponse = { const load = td.replace('../../src/util/load') td.when(load.pkg()).thenResolve(pkg) -td.when(load.lambdaConfig(functionName)).thenResolve({ FunctionName: functionName }) +td.when(load.lambdaConfig(functionName, env)).thenResolve({ FunctionName: functionName }) const cloudwatchLogs = td.replace('../../src/util/aws/cloudwatch-logs') td.when(cloudwatchLogs.getLogGroup(td.matchers.contains({ FunctionName: functionName }))).thenResolve('/aws/log/group') const lambda = td.replace('../../src/util/aws/lambda') -td.when(lambda.getAliasVersion(td.matchers.contains({ aliasName: stage }))).thenResolve('1') +td.when(lambda.getAliasVersion(td.matchers.contains({ aliasName: env }))).thenResolve('1') const getLogs = td.replace('../../src/util/get-logs') td.when(getLogs(td.matchers.isA(Object))).thenResolve(getLogResponse) test('Continues loop', async (t) => { const shep = require('../../src/index') - await t.throws(shep.logs({ stage, name: functionName, stream: true })) + await t.throws(shep.logs({ env, name: functionName, stream: true })) td.verify(callback(td.matchers.isA(Number))) }) diff --git a/test-ava/run/index-build.js b/test-ava/run/index-build.js index af02335..6f30553 100644 --- a/test-ava/run/index-build.js +++ b/test-ava/run/index-build.js @@ -3,6 +3,7 @@ import td from '../helpers/testdouble' import path from 'path' const funcName = 'foo' +const env = 'development' const handler = 'handler' const config = { Handler: `index.${handler}` } const events = ['event'] @@ -12,7 +13,7 @@ td.when(lambdaFunc[handler](td.matchers.anything(), td.matchers.isA(Object))).th const load = td.replace('../../src/util/load') load.distPath = async (joinPath) => joinPath ? path.join('dist', joinPath) : 'dist' td.when(load.funcs(funcName)).thenResolve([funcName]) -td.when(load.lambdaConfig(funcName)).thenResolve(config) +td.when(load.lambdaConfig(funcName, env)).thenResolve(config) td.when(load.events(funcName, td.matchers.anything())).thenResolve(events) const build = td.replace('../../src/util/build-functions') diff --git a/test-ava/run/index.js b/test-ava/run/index.js index c35f20d..a161548 100644 --- a/test-ava/run/index.js +++ b/test-ava/run/index.js @@ -3,6 +3,7 @@ import test from 'ava' import td from '../helpers/testdouble' const funcName = 'foo' +const environment = 'development' const handler = 'handler' const config = { Handler: `index.${handler}` } const events = ['event'] @@ -12,7 +13,7 @@ td.when(lambdaFunc[handler](td.matchers.anything(), td.matchers.isA(Object))).th const load = td.replace('../../src/util/load') load.distPath = async (joinPath) => joinPath ? path.join('dist', joinPath) : 'dist' td.when(load.funcs(funcName)).thenResolve([funcName]) -td.when(load.lambdaConfig(funcName)).thenResolve(config) +td.when(load.lambdaConfig(funcName, environment)).thenResolve(config) td.when(load.events(funcName, td.matchers.anything())).thenResolve(events) const requireProject = td.replace('../../src/util/require-project')