Skip to content

Commit

Permalink
Merge pull request #56 from developmentseed/nested_templates
Browse files Browse the repository at this point in the history
add support for nested CF templates
  • Loading branch information
Alireza authored Feb 5, 2018
2 parents e9af42b + 741c227 commit 022e27a
Show file tree
Hide file tree
Showing 19 changed files with 916 additions and 123 deletions.
25 changes: 14 additions & 11 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,29 @@ jobs:
name: Install
command: |
npm install
sudo npm install -g .
# save cahce
- save_cache:
key: deploy-{{ .Branch }}-{{ checksum "package.json" }}
paths:
- ~/kes/node_modules

- run:
name: Test Examples
command: |
mkdir ~/example1
cp -r examples/lambdas ~/example1/.kes
cd ~/example1/
kes cf compile
kes cf validate
~/kes/bin/cli.js cf compile
~/kes/bin/cli.js cf validate
cd ~/kes
kes cf compile --kes-folder examples/full
kes cf validate --kes-folder examples/full
./bin/cli.js cf compile --kes-folder examples/full
./bin/cli.js cf validate --kes-folder examples/full
./bin/cli.js cf validate --kes-folder examples/nested
./bin/cli.js cf validate --kes-folder examples/app_using_template --template examples/template
kes cf validate --kes-folder examples/app_using_template --template examples/template
npm run test
Expand Down Expand Up @@ -65,8 +72,4 @@ jobs:
npm publish || echo 'npm publishing failed'
fi
# save cahce
- save_cache:
key: deploy-{{ .Branch }}-{{ checksum "package.json" }}
paths:
- ~/kes/node_modules
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ _docs
build
dist
cloudformation.yml
myNestedTemplate.yml

4 changes: 4 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## v2.1.0

- add support for nested cloudformation templates

## v2.0.3

- support symlink for lambda zipping
Expand Down
105 changes: 13 additions & 92 deletions bin/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,75 +3,30 @@
'use strict';

require('./readme');
const get = require('lodash.get');
const fs = require('fs');
const path = require('path');
const colors = require('colors/safe');
const yaml = require('js-yaml');
const prompt = require('prompt');
const program = require('commander');
const deprecate = require('deprecate');
const kes = require('../index');
const pckg = require('../package.json');
const Config = require('../src/config');

const baseDir = process.cwd();
const kesFolder = path.join(baseDir, '.kes');
const distFolder = path.join(baseDir, 'dist');

const success = (r) => process.exit(0);

program.version(pckg.version);

/**
* @name failure
* @private
*/
const failure = (e) => {
if (e.message) {
console.log(e.message);
}
else {
console.log(e);
}
process.exit(1);
};

const determineKesClass = function () {
let Kes;

// if there is a kes class specified use that
const kesClass = get(program, 'kesClass');
if (kesClass) {
Kes = require(path.join(process.cwd(), kesClass));
}
else {
// check if there is kes.js in the kes-folder
try {
let kesFolder;
if (program.kesFolder) {
kesFolder = program.kesFolder;
}
else {
kesFolder = path.join(process.cwd(), '.kes');
}
Kes = require(path.join(process.cwd(), kesFolder, 'kes.js'));
function extractCommanderOptions(program) {
const options = {};
Object.keys(program).forEach(property => {
if (typeof program[property] === 'string') {
options[property] = program[property];
}
catch (e) {
// check if there is a template and the template has kes class
const template = get(program, 'template', '/path/to/nowhere');
try {
const kesPath = path.join(process.cwd(), template, 'kes.js');
fs.lstatSync(kesPath);
Kes = require(kesPath);
}
catch (e) {
Kes = require('../index').Kes;
}
}
}

return Kes;
};
});
return options;
}

const init = function () {
if (fs.existsSync(kesFolder)) {
Expand Down Expand Up @@ -161,50 +116,16 @@ program
deploy Creates the CF stack and Update if already exists
validate Validates the CF stack
compile Compiles the CF stack`)
.action((cmd) => {
const Kes = determineKesClass(program);
const config = new Config(program);
const kes = new Kes(config);
switch (cmd) {
case 'create':
deprecate('"kes cf create" command is deprecated. Use "kes cf deploy" instead');
kes.createStack().then(r => success(r)).catch(e => failure(e));
break;
case 'update':
deprecate('"kes cf update" command is deprecated. Use "kes cf deploy" instead');
kes.updateStack().then(r => success(r)).catch(e => failure(e));
break;
case 'upsert':
deprecate('"kes cf upsert" command is deprecated. Use "kes cf deploy" instead');
kes.upsertStack().then(r => success(r)).catch(e => failure(e));
break;
case 'deploy':
kes.deployStack().then(r => success(r)).catch(e => failure(e));
break;
case 'validate':
kes.validateTemplate().then(r => success(r)).catch(e => failure(e));
break;
case 'compile':
kes.compileCF().then(r => success(r)).catch(e => failure(e));
break;
default:
console.log('Wrong choice. Accepted arguments: [create|update|upsert|deploy|validate|compile]');
}
.action((cmd, o) => {
const options = extractCommanderOptions(program);
kes.buildCf(options ,cmd);
});

program
.command('lambda <lambdaName>')
.description('uploads a given lambda function to Lambda service')
.action((cmd, options) => {
if (cmd) {
const Kes = require('../index').Kes;
const config = new Config(program);
const kes = new Kes(config);
kes.updateSingleLambda(cmd).then(r => success(r)).catch(e => failure(e));
}
else {
console.log('Lambda name is missing');
}
kes.buildLambda(program, cmd);
});

program
Expand Down
46 changes: 45 additions & 1 deletion bin/readme.js
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@
*
* To create a CF stack or update and existing one run
* ```bash
* kes cf deploy
* kes cf deploy
* ```
*
* ### Differenet deployment configurations
Expand Down Expand Up @@ -300,4 +300,48 @@
*
* This setup gives users of the templates a great degree of flexibility and ownership.
*
* ## Nested Templates
* Kes supports use of [Cloudformation Nested Templates](https://aws.amazon.com/blogs/devops/use-nested-stacks-to-create-reusable-templates-and-support-role-specialization/).
* To use nested templates, create a separate `template.yml` and `config.yml` files for each nested template using the same rules explained above.
* Then include references in your main `config.yml`.
*
* All nested templates will receive the parent configuration under the `parent` key.
*
* ### Example
* ```yaml
* # config.yml
* default:
* stackName: myStack-test
* myArray:
* - DEBUG: true
* nested_templates:
* myNestedTemplate:
* cfFile: /path/to/myNestedTemplate.template.yml
* configFile: /path/to/myNestedConfig.yml
*
* staging:
* stackName: myStack-staging
* myArray:
* - DEBUG: false
* ```
*
* ```yaml
* # myNestedConfig.yml
* default:
* timeout: 300
* ```
*
* ```yaml
* # myNestedTemplate.template.yml
* Resources:
*
* {{# each parent.myArray}}
* Lambda:
* Type: SomeAWSResource
* Properties:
* Timeout: {{../timeout}}
* Environments:
* - {{@key}}: {{this}}
* {{/each}}
* ```
*/
30 changes: 30 additions & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

- [Config](#config)
- [parse](#parse)
- [flatten](#flatten)
- [Kes](#kes)
- [updateSingleLambda](#updatesinglelambda)
- [compileCF](#compilecf)
Expand Down Expand Up @@ -31,6 +32,8 @@
- [configureAws](#configureaws)
- [fileToString](#filetostring)
- [mergeYamls](#mergeyamls)
- [determineKesClass](#determinekesclass)
- [failure](#failure)

## Config

Expand Down Expand Up @@ -73,6 +76,13 @@ config = configInstance.parse();

Returns **[Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)** the configuration object

### flatten

Return a javascript object (not a class instance) of the
config class

Returns **[object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)** a javascript object version of the class

## Kes

The main Kes class. This class is used in the command module to create
Expand Down Expand Up @@ -367,3 +377,23 @@ replace values of file 1 if they have the same key.
- `file2` **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** Yaml path to file 2 or file 2 string

Returns **[String](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)** Merged Yaml file in string format

## determineKesClass

Based on the information passed from the CLI by the commander
module this function determines whether to use the default Kes class
or use the override class provided by the user

**Parameters**

- `options` **[object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)** The options passed by the commander library

Returns **Class** Kes class

## failure

In case of error logs the error and exit with error 1

**Parameters**

- `e` **[Error](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error)** error object
53 changes: 51 additions & 2 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
- [Deployment Using IAM Role](#deployment-using-iam-role)
- [Updating One Lambda Function](#updating-one-lambda-function)
- [Use Templates](#use-templates)
- [Nested Templates](#nested-templates)
- [Example](#example)

##

Expand Down Expand Up @@ -67,7 +69,7 @@ It makes it much easier to deploy lambda functions and create API gateway resour
Go to your project directory and run the following command.
```bash
$ kes init
$ npm init
```
This will create a `.kes` folder on your project folder. It will include the following files:
Expand Down Expand Up @@ -268,7 +270,7 @@ Resources:
To create a CF stack or update and existing one run
```bash
kes cf deploy
kes cf deploy
```
### Differenet deployment configurations
Expand Down Expand Up @@ -324,3 +326,50 @@ The user still has the option of creating her own `config.yml` and `cloudformati
in the template or append it if it doesn't exist.
This setup gives users of the templates a great degree of flexibility and ownership.
## Nested Templates
Kes supports use of [Cloudformation Nested Templates](https://aws.amazon.com/blogs/devops/use-nested-stacks-to-create-reusable-templates-and-support-role-specialization/).
To use nested templates, create a separate `template.yml` and `config.yml` files for each nested template using the same rules explained above.
Then include references in your main `config.yml`.
All nested templates will receive the parent configuration under the `parent` key.
### Example
```yaml
# config.yml
default:
stackName: myStack-test
myArray:
- DEBUG: true
nested_templates:
myNestedTemplate:
cfFile: /path/to/myNestedTemplate.template.yml
configFile: /path/to/myNestedConfig.yml
staging:
stackName: myStack-staging
myArray:
- DEBUG: false
```
```yaml
# myNestedConfig.yml
default:
timeout: 300
```
```yaml
# myNestedTemplate.template.yml
Resources:
{{# each parent.myArray}}
Lambda:
Type: SomeAWSResource
Properties:
Timeout: {{../timeout}}
Environments:
- {{@key}}: {{this}}
{{/each}}
```
2 changes: 1 addition & 1 deletion examples/full/kes.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

const { Kes } = require('../../index');
const Kes = require('../../index').Kes;

class BetterKes extends Kes {
opsStack(ops) {
Expand Down
Loading

0 comments on commit 022e27a

Please sign in to comment.