├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── package.json ├── src └── index.js └── test └── .gitkeep /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | npm-debug.log 4 | /node_modules 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.2.0 4 | 5 | * Breaking Change: Update to Support Serverless v0.1.x 6 | 7 | ## 0.1.2 8 | 9 | * Implements Serverless' path helpers. 10 | 11 | ## 0.1.1 12 | 13 | * Fixes an issue with the CloudFormation file path. 14 | 15 | ## 0.1.0 16 | 17 | * Initial Release 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Tom Milewski 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose 4 | with or without fee is hereby granted, provided that the above copyright notice 5 | and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 8 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 9 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 10 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS 11 | OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 12 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 13 | THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Serverless CloudFormation Template Validation Plugin 2 | 3 | A Serverless Plugin for the [Serverless Framework](http://www.serverless.com) which adds an action to validate your CloudFormation template. 4 | 5 | [![npm version](https://badge.fury.io/js/serverless-resources-validation-plugin.svg)](https://badge.fury.io/js/serverless-resources-validation-plugin) 6 | [![Dependency Status](https://david-dm.org/tmilewski/serverless-resources-validation-plugin.svg)](https://david-dm.org/tmilewski/serverless-resources-validation-plugin) 7 | [![DevDependencies Status](https://david-dm.org/joostfarla/serverless-cors-plugin/dev-status.svg)](https://david-dm.org/tmilewski/serverless-resources-validation-plugin#info=devDependencies) 8 | 9 | ## Introduction 10 | 11 | This plugins does the following: 12 | 13 | * Validates your CloudFormation template directly against Amazon's parameters. 14 | 15 | ## Installation 16 | 17 | * Go to the root of your Serverless Project 18 | * Run `npm install serverless-resources-validation-plugin --save` 19 | * In your Project's `s-project.json`, in the `plugins` property, add the npm name of your recently added plugin to the array, like this: 20 | ``` 21 | plugins: [ 22 | 'serverless-resources-validation-plugin' 23 | ] 24 | ``` 25 | 26 | ## Usage 27 | 28 | `serverless resources validate` 29 | 30 | ## CLI Options 31 | 32 | * `-s --stage` 33 | * `-r --region` 34 | 35 | ## Roadmap 36 | 37 | * Add tests 38 | 39 | ## License 40 | 41 | ISC License. See the [LICENSE](LICENSE) file. 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "serverless-resources-validation-plugin", 3 | "version": "0.3.1", 4 | "description": "Serverless Resources (CloudFormation) Validation Plugin", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git@github.com:tmilewski/serverless-resources-validation-plugin.git" 12 | }, 13 | "keywords": [ 14 | "serverless", 15 | "aws", 16 | "lambda", 17 | "cloudformation", 18 | "validation" 19 | ], 20 | "author": "Tom Milewski ", 21 | "license": "ISC", 22 | "bugs": { 23 | "url": "https://github.com/tmilewski/serverless-resources-validation-plugin/issues" 24 | }, 25 | "homepage": "https://github.com/tmilewski/serverless-resources-validation-pluginreadme", 26 | "dependencies": { 27 | "bluebird": "^3.1.1", 28 | "aws-sdk": "^2.2.30" 29 | }, 30 | "devDependencies": { 31 | "serverless": "0.4.2" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Action: ResourcesValidate 5 | * - Validates the appropriate CloudFormation template via AWS 6 | */ 7 | 8 | module.exports = function (SPlugin, serverlessPath) { 9 | const AWS = require('aws-sdk') 10 | const BbPromise = require('bluebird') 11 | const path = require('path') 12 | const SCli = require(path.join(serverlessPath, 'utils/cli')) 13 | const SError = require(path.join(serverlessPath, 'ServerlessError')) 14 | 15 | class ResourcesValidate extends SPlugin { 16 | /** 17 | * Define your plugin's name 18 | */ 19 | 20 | static getName () { 21 | return 'serverless.tmilewski.' + ResourcesValidate.name 22 | } 23 | 24 | /** 25 | * @returns {Promise} upon completion of all registrations 26 | */ 27 | 28 | registerActions () { 29 | this.S.addAction(this.resourcesValidate.bind(this), { 30 | handler: 'resourcesValidate', 31 | description: `Validate AWS CloudFormation resources. 32 | usage: serverless resources validate`, 33 | context: 'resources', 34 | contextAction: 'validate', 35 | options: [ 36 | { 37 | option: 'region', 38 | shortcut: 'r', 39 | description: 'region you want to deploy to' 40 | }, 41 | { 42 | option: 'stage', 43 | shortcut: 's', 44 | description: 'stage you want to deploy to' 45 | } 46 | ] 47 | }) 48 | 49 | return BbPromise.resolve() 50 | } 51 | 52 | /** 53 | * Action 54 | */ 55 | 56 | resourcesValidate (evt) { 57 | let _this = this 58 | _this.evt = evt 59 | 60 | return _this._prompt() 61 | .bind(_this) 62 | .then(_this._validateAndPrepare) 63 | .then(_this._validateResources) 64 | .then(function () { 65 | /** 66 | * Return EVT 67 | */ 68 | 69 | return _this.evt 70 | }) 71 | } 72 | 73 | /** 74 | * Prompt 75 | */ 76 | 77 | _prompt () { 78 | let _this = this 79 | 80 | // Skip if non-interactive or stage is provided 81 | if (!_this.S.config.interactive || (_this.evt.options.stage && _this.evt.options.region)) return BbPromise.resolve() 82 | 83 | return _this.cliPromptSelectStage('Which stage are you deploying to: ', _this.evt.options.stage, false) 84 | .then((stage) => { 85 | _this.evt.options.stage = stage 86 | BbPromise.resolve() 87 | }) 88 | .then(function () { 89 | return _this.cliPromptSelectRegion('Which region are you deploying to: ', false, true, _this.evt.options.region, _this.evt.options.stage) 90 | .then((region) => { 91 | _this.evt.options.region = region 92 | BbPromise.resolve() 93 | }) 94 | }) 95 | } 96 | 97 | /** 98 | * Validate & Prepare 99 | */ 100 | 101 | _validateAndPrepare () { 102 | let _this = this 103 | 104 | // Non interactive validation 105 | if (!_this.S.config.interactive) { 106 | // Check API Keys 107 | if (!_this.S._awsProfile) { 108 | if (!_this.S.config.awsAdminKeyId || !_this.S.config.awsAdminSecretKey) { 109 | return BbPromise.reject(new SError('Missing AWS Profile and/or API Key and/or AWS Secret Key')) 110 | } 111 | } 112 | 113 | // Check Params 114 | if (!_this.evt.options.stage || !_this.evt.options.region) { 115 | return BbPromise.reject(new SError('Missing stage and/or region and/or key')) 116 | } 117 | } 118 | 119 | // Validate stage: make sure stage exists 120 | if (!_this.S.state.validateStageExists(_this.evt.options.stage) && _this.evt.options.stage !== 'local') { 121 | return BbPromise.reject(new SError('Stage ' + _this.evt.options.stage + ' does not exist in your project')) 122 | } 123 | 124 | // Validate region: make sure region exists in stage 125 | if (!_this.S.state.validateRegionExists(_this.evt.options.stage, _this.evt.options.region)) { 126 | return BbPromise.reject(new SError('Region "' + _this.evt.options.region + '" does not exist in stage "' + _this.evt.options.stage + '"')) 127 | } 128 | } 129 | 130 | /** 131 | * Validate CloudFormation Resources 132 | */ 133 | 134 | _validateResources () { 135 | let _this = this 136 | 137 | return _this.S.state.getResources({ 138 | populate: true, 139 | stage: _this.evt.options.stage, 140 | region: _this.evt.options.region 141 | }) 142 | .then(function (resources) { 143 | return BbPromise.try(function () { 144 | SCli.log('Validating resources to stage "' + 145 | _this.evt.options.stage + 146 | '" and region "' + 147 | _this.evt.options.region + 148 | '" via Cloudformation.') 149 | 150 | // Start spinner 151 | _this._spinner = SCli.spinner() 152 | _this._spinner.start() 153 | 154 | return new BbPromise(function (resolve, reject) { 155 | let cloudformation = new AWS.CloudFormation({ region: _this.evt.options.region }) 156 | let params = { 157 | TemplateBody: JSON.stringify(resources) 158 | } 159 | 160 | cloudformation.validateTemplate(params, function (err, data) { 161 | _this._spinner.stop(true) 162 | 163 | if (err) { 164 | throw new SError(err, SError.errorCodes.INVALID_PROJECT_SERVERLESS) 165 | } 166 | 167 | SCli.log('Resource Validator: Successful on "' + _this.evt.options.stage + '" in "' + _this.evt.options.region + '"') 168 | return resolve() 169 | }) 170 | }) 171 | }) 172 | }) 173 | 174 | return resolve() 175 | } 176 | } 177 | 178 | return(ResourcesValidate) 179 | } 180 | -------------------------------------------------------------------------------- /test/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmilewski/serverless-resources-validation-plugin/617f55a4dfbc53ca9fe53c22d68c573c051b09fe/test/.gitkeep --------------------------------------------------------------------------------