serverless-step-functions
Advanced tools
Comparing version 0.1.2 to 0.2.0
393
index.js
'use strict'; | ||
const BbPromise = require('bluebird'); | ||
const deploy = require('./lib/deploy'); | ||
const remove = require('./lib/remove'); | ||
const invoke = require('./lib/invoke'); | ||
const path = require('path'); | ||
const _ = require('lodash'); | ||
const chalk = require('chalk'); | ||
@@ -10,11 +10,36 @@ class ServerlessStepFunctions { | ||
this.serverless = serverless; | ||
this.options = options; | ||
this.options = options || {}; | ||
this.provider = this.serverless.getProvider('aws'); | ||
this.service = this.serverless.service.service; | ||
this.region = this.provider.getRegion(); | ||
this.stage = this.provider.getStage(); | ||
this.awsStateLanguage = {}; | ||
this.functionArns = {}; | ||
this.iamPolicyStatement = `{ | ||
"Version": "2012-10-17", | ||
"Statement": [ | ||
{ | ||
"Effect": "Allow", | ||
"Action": [ | ||
"lambda:InvokeFunction" | ||
], | ||
"Resource": "*" | ||
} | ||
] | ||
} | ||
`; | ||
Object.assign( | ||
this, | ||
deploy, | ||
remove, | ||
invoke | ||
); | ||
this.assumeRolePolicyDocument = `{ | ||
"Version": "2012-10-17", | ||
"Statement": [ | ||
{ | ||
"Effect": "Allow", | ||
"Principal": { | ||
"Service": "states.${this.region}.amazonaws.com" | ||
}, | ||
"Action": "sts:AssumeRole" | ||
} | ||
] | ||
} | ||
`; | ||
@@ -35,2 +60,10 @@ this.commands = { | ||
}, | ||
stage: { | ||
usage: 'Stage of the service', | ||
shortcut: 's', | ||
}, | ||
region: { | ||
usage: 'Region of the service', | ||
shortcut: 'r', | ||
}, | ||
}, | ||
@@ -53,2 +86,10 @@ }, | ||
}, | ||
stage: { | ||
usage: 'Stage of the service', | ||
shortcut: 's', | ||
}, | ||
region: { | ||
usage: 'Region of the service', | ||
shortcut: 'r', | ||
}, | ||
}, | ||
@@ -75,2 +116,15 @@ }, | ||
}, | ||
path: { | ||
usage: | ||
'The path to a json file with input data to be passed to the invoked step function', | ||
shortcut: 'p', | ||
}, | ||
stage: { | ||
usage: 'Stage of the service', | ||
shortcut: 's', | ||
}, | ||
region: { | ||
usage: 'Region of the service', | ||
shortcut: 'r', | ||
}, | ||
}, | ||
@@ -91,3 +145,322 @@ }, | ||
} | ||
deploy() { | ||
this.serverless.cli.log(`Start to deploy ${this.options.state} step function...`); | ||
return BbPromise.bind(this) | ||
.then(this.yamlParse) | ||
.then(this.getStateMachineArn) | ||
.then(this.getFunctionArns) | ||
.then(this.compile) | ||
.then(this.getIamRole) | ||
.then(this.deleteStateMachine) | ||
.then(this.createStateMachine); | ||
} | ||
remove() { | ||
return BbPromise.bind(this) | ||
.then(this.deleteIamRole) | ||
.then(this.getStateMachineArn) | ||
.then(this.deleteStateMachine) | ||
.then(() => { | ||
this.serverless.cli.log(`Remove ${this.options.state}`); | ||
return BbPromise.resolve(); | ||
}); | ||
} | ||
invoke() { | ||
return BbPromise.bind(this) | ||
.then(this.parseInputdate) | ||
.then(this.getStateMachineArn) | ||
.then(this.startExecution) | ||
.then(this.describeExecution); | ||
} | ||
getIamRoleName() { | ||
let name = `${this.service}-${this.region}-${this.stage}-${this.options.state}-`; | ||
name += 'ssf-exerole'; | ||
return name.substr(0, 64); | ||
} | ||
getIamPolicyName() { | ||
let name = `${this.service}-${this.region}-${this.stage}-${this.options.state}-`; | ||
name += 'ssf-exepolicy'; | ||
return name.substr(0, 64); | ||
} | ||
getStateMachineName() { | ||
return `${this.service}-${this.stage}-${this.options.state}`; | ||
} | ||
getIamRole() { | ||
return this.provider.request('IAM', | ||
'getRole', | ||
{ | ||
RoleName: this.getIamRoleName(), | ||
}, | ||
this.options.stage, | ||
this.options.region) | ||
.then((result) => { | ||
this.iamRoleArn = result.Role.Arn; | ||
return BbPromise.resolve(); | ||
}).catch((error) => { | ||
if (error.statusCode === 404) { | ||
return this.createIamRole(); | ||
} | ||
throw new this.serverless.classes.Error(error.message); | ||
}); | ||
} | ||
getFunctionArns() { | ||
return this.provider.request('STS', | ||
'getCallerIdentity', | ||
{}, | ||
this.options.stage, | ||
this.options.region) | ||
.then((result) => { | ||
_.forEach(this.serverless.service.functions, (value, key) => { | ||
this.functionArns[key] | ||
= `arn:aws:lambda:${this.region}:${result.Account}:function:${value.name}`; | ||
}); | ||
return BbPromise.resolve(); | ||
}); | ||
} | ||
createIamRole() { | ||
return this.provider.request('IAM', | ||
'createRole', | ||
{ | ||
AssumeRolePolicyDocument: this.assumeRolePolicyDocument, | ||
RoleName: this.getIamRoleName(), | ||
}, | ||
this.options.stage, | ||
this.options.region) | ||
.then((result) => { | ||
this.iamRoleArn = result.Role.Arn; | ||
return this.provider.request('IAM', | ||
'createPolicy', | ||
{ | ||
PolicyDocument: this.iamPolicyStatement, | ||
PolicyName: this.getIamPolicyName(), | ||
}, | ||
this.options.stage, | ||
this.options.region); | ||
}) | ||
.then((result) => this.provider.request('IAM', | ||
'attachRolePolicy', | ||
{ | ||
PolicyArn: result.Policy.Arn, | ||
RoleName: this.getIamRoleName(), | ||
}, | ||
this.options.stage, | ||
this.options.region) | ||
) | ||
.then(() => BbPromise.resolve()); | ||
} | ||
deleteIamRole() { | ||
let policyArn; | ||
return this.provider.request('STS', | ||
'getCallerIdentity', | ||
{}, | ||
this.options.stage, | ||
this.options.region) | ||
.then((result) => { | ||
policyArn = `arn:aws:iam::${result.Account}:policy/${this.getIamPolicyName()}`; | ||
return this.provider.request('IAM', | ||
'detachRolePolicy', | ||
{ | ||
PolicyArn: policyArn, | ||
RoleName: this.getIamRoleName(), | ||
}, | ||
this.options.stage, | ||
this.options.region); | ||
}) | ||
.then(() => this.provider.request('IAM', | ||
'deletePolicy', | ||
{ | ||
PolicyArn: policyArn, | ||
}, | ||
this.options.stage, | ||
this.options.region) | ||
) | ||
.then(() => this.provider.request('IAM', | ||
'deleteRole', | ||
{ | ||
RoleName: this.getIamRoleName(), | ||
}, | ||
this.options.stage, | ||
this.options.region) | ||
) | ||
.then(() => BbPromise.resolve()); | ||
} | ||
getStateMachineArn() { | ||
return this.provider.request('STS', | ||
'getCallerIdentity', | ||
{}, | ||
this.options.stage, | ||
this.options.region) | ||
.then((result) => { | ||
this.stateMachineArn = | ||
`arn:aws:states:${this.region}:${result.Account}:stateMachine:${this.getStateMachineName()}`; | ||
return BbPromise.resolve(); | ||
}); | ||
} | ||
deleteStateMachine() { | ||
return this.provider.request('StepFunctions', | ||
'deleteStateMachine', | ||
{ | ||
stateMachineArn: this.stateMachineArn, | ||
}, | ||
this.options.stage, | ||
this.options.region) | ||
.then(() => BbPromise.resolve()); | ||
} | ||
createStateMachine() { | ||
return this.provider.request('StepFunctions', | ||
'createStateMachine', | ||
{ | ||
definition: this.awsStateLanguage[this.options.state], | ||
name: this.getStateMachineName(), | ||
roleArn: this.iamRoleArn, | ||
}, | ||
this.options.stage, | ||
this.options.region) | ||
.then(() => { | ||
this.serverless.cli.consoleLog(''); | ||
this.serverless.cli.log(`Finish to deploy ${this.getStateMachineName()} step function`); | ||
return BbPromise.resolve(); | ||
}).catch((error) => { | ||
if (error.message.match(/State Machine is being deleted/)) { | ||
this.serverless.cli.printDot(); | ||
setTimeout(this.createStateMachine.bind(this), 5000); | ||
} else { | ||
throw new this.serverless.classes.Error(error.message); | ||
} | ||
}); | ||
} | ||
parseInputdate() { | ||
if (!this.options.data && this.options.path) { | ||
const absolutePath = path.isAbsolute(this.options.path) ? | ||
this.options.path : | ||
path.join(this.serverless.config.servicePath, this.options.path); | ||
if (!this.serverless.utils.fileExistsSync(absolutePath)) { | ||
throw new this.serverless.classes.Error('The file you provided does not exist.'); | ||
} | ||
this.options.data = JSON.stringify(this.serverless.utils.readFileSync(absolutePath)); | ||
} | ||
return BbPromise.resolve(); | ||
} | ||
startExecution() { | ||
this.serverless.cli.log(`Start function ${this.options.state}...`); | ||
return this.provider.request('StepFunctions', | ||
'startExecution', | ||
{ | ||
stateMachineArn: this.stateMachineArn, | ||
input: this.options.data, | ||
}, | ||
this.options.stage, | ||
this.options.region) | ||
.then((result) => { | ||
this.executionArn = result.executionArn; | ||
return BbPromise.resolve(); | ||
}).catch((error) => { | ||
throw new this.serverless.classes.Error(error.message); | ||
}); | ||
} | ||
describeExecution() { | ||
return this.provider.request('StepFunctions', | ||
'describeExecution', | ||
{ | ||
executionArn: this.executionArn, | ||
}, | ||
this.options.stage, | ||
this.options.region) | ||
.then((result) => { | ||
if (result.status === 'RUNNING') { | ||
this.serverless.cli.printDot(); | ||
setTimeout(this.describeExecution.bind(this), 5000); | ||
} else { | ||
this.serverless.cli.consoleLog(''); | ||
this.serverless.cli.consoleLog(''); | ||
const msg = 'Execution Result -----------------------------------------'; | ||
this.serverless.cli.consoleLog(chalk.yellow(msg)); | ||
this.serverless.cli.consoleLog(''); | ||
this.serverless.cli.consoleLog(result); | ||
if (result.status === 'FAILED') { | ||
return this.getExecutionHistory(); | ||
} | ||
} | ||
return BbPromise.resolve(); | ||
}); | ||
} | ||
getExecutionHistory() { | ||
return this.provider.request('StepFunctions', | ||
'getExecutionHistory', | ||
{ | ||
executionArn: this.executionArn, | ||
}, | ||
this.options.stage, | ||
this.options.region) | ||
.then((result) => { | ||
this.serverless.cli.consoleLog(''); | ||
const msg = 'Error Log ------------------------------------------------'; | ||
this.serverless.cli.consoleLog(chalk.yellow(msg)); | ||
this.serverless.cli.consoleLog(''); | ||
this.serverless.cli.consoleLog(result.events[result.events.length - 1] | ||
.executionFailedEventDetails); | ||
return BbPromise.resolve(); | ||
}); | ||
} | ||
yamlParse() { | ||
const servicePath = this.serverless.config.servicePath; | ||
if (!servicePath) { | ||
return BbPromise.resolve(); | ||
} | ||
const serverlessYmlPath = path.join(servicePath, 'serverless.yml'); | ||
return this.serverless.yamlParser | ||
.parse(serverlessYmlPath) | ||
.then((serverlessFileParam) => { | ||
this.stepFunctions = serverlessFileParam.stepFunctions; | ||
return BbPromise.resolve(); | ||
}); | ||
} | ||
compile() { | ||
if (!this.stepFunctions) { | ||
const errorMessage = [ | ||
'stepFunctions statement does not exists in serverless.yml', | ||
].join(''); | ||
throw new this.serverless.classes.Error(errorMessage); | ||
} | ||
if (typeof this.stepFunctions[this.options.state] === 'undefined') { | ||
const errorMessage = [ | ||
`Step function "${this.options.state}" is not exists`, | ||
].join(''); | ||
throw new this.serverless.classes.Error(errorMessage); | ||
} | ||
this.awsStateLanguage[this.options.state] = | ||
JSON.stringify(this.stepFunctions[this.options.state]); | ||
_.forEach(this.functionArns, (value, key) => { | ||
const regExp = new RegExp(`"Resource":"${key}"`, 'g'); | ||
this.awsStateLanguage[this.options.state] = | ||
this.awsStateLanguage[this.options.state].replace(regExp, `"Resource":"${value}"`); | ||
}); | ||
return BbPromise.resolve(); | ||
} | ||
} | ||
module.exports = ServerlessStepFunctions; |
{ | ||
"name": "serverless-step-functions", | ||
"version": "0.1.2", | ||
"version": "0.2.0", | ||
"description": "The module is AWS Step Functions plugin for Serverless Framework", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "npm run test", | ||
"test": "istanbul cover -x '*.test.js' node_modules/mocha/bin/_mocha '*.test.js' -- -R spec --recursive", | ||
"lint": "eslint ." | ||
@@ -29,3 +29,10 @@ }, | ||
"eslint-plugin-jsx-a11y": "^2.1.0", | ||
"eslint-plugin-react": "^6.1.1" | ||
"eslint-plugin-react": "^6.1.1", | ||
"serverless": "^1.4.0", | ||
"istanbul": "^0.4.4", | ||
"mocha": "^3.0.2", | ||
"mocha-lcov-reporter": "^1.2.0", | ||
"chai": "^3.5.0", | ||
"coveralls": "^2.11.12", | ||
"sinon": "^1.17.5" | ||
}, | ||
@@ -35,4 +42,5 @@ "dependencies": { | ||
"aws-sdk": "^2.7.19", | ||
"bluebird": "^3.4.0" | ||
"bluebird": "^3.4.0", | ||
"chalk": "^1.1.1" | ||
} | ||
} |
@@ -1,2 +0,2 @@ | ||
[![serverless](http://public.serverless.com/badges/v3.svg)](http://www.serverless.com) [![Build Status](https://travis-ci.org/horike37/serverless-step-functions.svg?branch=master)](https://travis-ci.org/horike37/serverless-step-functions) | ||
[![serverless](http://public.serverless.com/badges/v3.svg)](http://www.serverless.com) [![Build Status](https://travis-ci.org/horike37/serverless-step-functions.svg?branch=master)](https://travis-ci.org/horike37/serverless-step-functions) [![npm version](https://badge.fury.io/js/serverless-step-functions.svg)](https://badge.fury.io/js/serverless-step-functions) [![Coverage Status](https://coveralls.io/repos/github/horike37/serverless-step-functions/badge.svg?branch=master)](https://coveralls.io/github/horike37/serverless-step-functions?branch=master) [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE) | ||
# Serverless Step Functions | ||
@@ -51,2 +51,3 @@ Serverless plugin for AWS Step Functions. | ||
- --data or -d String data to be passed as an event to your step function. | ||
- --path or -p The path to a json file with input data to be passed to the invoked step function. | ||
``` | ||
@@ -53,0 +54,0 @@ $ sls invoke stepf --state <stepfunctionname> --data '{"foo":"bar"}' |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
44615
1003
63
4
13
1
+ Addedchalk@^1.1.1
+ Addedansi-regex@2.1.1(transitive)
+ Addedansi-styles@2.2.1(transitive)
+ Addedchalk@1.1.3(transitive)
+ Addedescape-string-regexp@1.0.5(transitive)
+ Addedhas-ansi@2.0.0(transitive)
+ Addedstrip-ansi@3.0.1(transitive)
+ Addedsupports-color@2.0.0(transitive)