Merge branch 'master' into add-alexa-event-source

This commit is contained in:
Nik Graf 2016-12-14 09:27:17 +01:00
commit 010da2cb59
16 changed files with 502 additions and 226 deletions

View File

@ -48,7 +48,7 @@ The Serverless Framework allows you to deploy auto-scaling, pay-per-execution, e
</div>
<div>
<ul>
<li><a href="./providers/aws/cli-reference/config.md">Config</a></li>
<li><a href="./providers/aws/cli-reference/config-credentials.md">Config Credentials</a></li>
<li><a href="./providers/aws/cli-reference/create.md">Create</a></li>
<li><a href="./providers/aws/cli-reference/install.md">Install</a></li>
<li><a href="./providers/aws/cli-reference/deploy.md">Deploy</a></li>

View File

@ -1,39 +1,35 @@
<!--
title: Serverless Framework Commands - AWS Lambda - Config
menuText: Config
title: Serverless Framework Commands - AWS Lambda - Config Credentials
menuText: Config Credentials
menuOrder: 1
description: Configure Serverless.
description: Configure Serverless credentials
layout: Doc
-->
<!-- DOCS-SITE-LINK:START automatically generated -->
### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/aws/cli-reference/config)
### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/aws/cli-reference/config-credentials)
<!-- DOCS-SITE-LINK:END -->
# Config
This plugin helps you to configure Serverless.
## Configure credentials
# Config Credentials
```bash
serverless config credentials --provider provider --key key --secret secret
```
### Options
## Options
- `--provider` or `-p` The provider (in this case `aws`). **Required**.
- `--key` or `-k` The `aws_access_key_id`. **Required**.
- `--secret` or `-s` The `aws_secret_access_key`. **Required**.
- `--profile` or `-n` The name of the profile which should be created.
### Provided lifecycle events
## Provided lifecycle events
- `config:credentials:config`
### Examples
## Examples
#### Configure the `default` profile
### Configure the `default` profile
```bash
serverless config credentials --provider aws --key 1234 --secret 5678
@ -41,7 +37,7 @@ serverless config credentials --provider aws --key 1234 --secret 5678
This example will configure the `default` profile with the `aws_access_key_id` of `1234` and the `aws_secret_access_key` of `5678`.
#### Configure a custom profile
### Configure a custom profile
```bash
serverless config credentials --provider aws --key 1234 --secret 5678 --profile custom-profile

View File

@ -47,7 +47,7 @@ serverless invoke --function functionName --data '{"a":"bar"}'
### Local function invocation with data from standard input
```bash
dataGenerator.js | serverless invoke local --function functionName
node dataGenerator.js | serverless invoke local --function functionName
```
### Local function invocation with data passing

View File

@ -65,7 +65,7 @@ serverless invoke --function functionName --stage dev --region us-east-1 --data
#### Function invocation with data from standard input
```bash
dataGenerator.js | serverless invoke --function functionName --stage dev --region us-east-1
node dataGenerator.js | serverless invoke --function functionName --stage dev --region us-east-1
```
#### Function invocation with logging

View File

@ -15,12 +15,12 @@ layout: Doc
Lets you watch the metrics of a specific function.
```bash
serverless metrics --function hello
serverless metrics
```
## Options
- `--function` or `-f` The function you want to fetch the metrics for. **Required**
- `--function` or `-f` The function you want to fetch the metrics for.
- `--stage` or `-s` The stage you want to view the function metrics for. If not provided, the plugin will use the default stage listed in `serverless.yml`. If that doesn't exist either it'll just fetch the metrics from the `dev` stage.
- `--region` or `-r` The region you want to view the function metrics for. If not provided, the plugin will use the default region listed in `serverless.yml`. If that doesn't exist either it'll just fetch the metrics from the `us-east-1` region.
- `--startTime` A specific unit in time to start fetching metrics from (ie: `2010-10-20`, `1469705761`, `30m` (30 minutes ago), `2h` (2 days ago) or `3d` (3 days ago)). Date formats should be written in ISO 8601. Defaults to 24h ago.
@ -30,18 +30,34 @@ serverless metrics --function hello
**Note:** There's a small lag between invoking the function and actually having access to the metrics. It takes a few seconds for the metrics to show up right after invoking the function.
### See all metrics of the last 24h
### See service wide metrics for the last 24h
```bash
serverless metrics
```
Displays service wide metrics for the last 24h.
### See service wide metrics for a specific timespan
```bash
serverless metrics --startTime 2016-01-01 --endTime 2016-01-02
```
Displays service wide metrics for the time between January 1, 2016 and January 2, 2016.
### See all metrics for the function `hello` of the last 24h
```bash
serverless metrics --function hello
```
Displays all metrics for the last 24h.
Displays all `hello` function metrics for the last 24h.
### See metrics for a specific timespan
### See metrics for the function `hello` of a specific timespan
```bash
serverless metrics --function hello --startTime 1970-01-01 --endTime 1970-01-02
serverless metrics --function hello --startTime 2016-01-01 --endTime 2016-01-02
```
Displays all metrics for the time between January 1, 1970 and January 2, 1970.
Displays all `hello` function metrics for the time between January 1, 2016 and January 2, 2016.

View File

@ -26,25 +26,27 @@ By default, the Framework uses the `lambda-proxy` method (i.e., everything is pa
### Simple HTTP Endpoint
This setup specifies that the `index` function should be run when someone accesses the API gateway at `users/index` via
This setup specifies that the `hello` function should be run when someone accesses the API gateway at `hello` via
a `GET` request.
Here's an example:
```yml
# serverless.yml
functions:
index:
handler: users.index
handler: handler.hello
events:
- http: GET users/index
- http: GET hello
```
```javascript
// users.js
// handler.js
'use strict';
exports.handler = function(event, context, callback) {
module.exports.hello = function(event, context, callback) {
console.log(event); // Contains incoming request data (e.g., query params, headers and more)
@ -70,6 +72,8 @@ JSON.parse(event.body);
Here we've defined an POST endpoint for the path `posts/create`.
```yml
# serverless.yml
functions:
create:
handler: posts.create
@ -83,12 +87,14 @@ functions:
To set CORS configurations for your HTTP endpoints, simply modify your event configurations as follows:
```yml
# serverless.yml
functions:
hello:
handler: handler.hello
events:
- http:
path: user/create
path: hello
method: get
cors: true
```
@ -96,11 +102,11 @@ functions:
If you want to use CORS with the lambda-proxy integration, remember to include `Access-Control-Allow-Origin` in your returned headers object, like this:
```javascript
// users.js
// handler.js
'use strict';
exports.handler = function(event, context, callback) {
module.exports.hello = function(event, context, callback) {
const response = {
statusCode: 200,

View File

@ -27,8 +27,6 @@ zip -r ./deploy-package.zip ./*
popd
```
`./build.sh` runs the dotnet restore and package commands then creates the zip file to upload to lambda.
## 3. Deploy
`serverless deploy` or `sls deploy`. `sls` is shorthand for the Serverless CLI command

View File

@ -26,7 +26,7 @@ Here's how to set up the Serverless Framework with your Amazon Web Services acco
If you're new to Amazon Web Services, make sure you put in a credit card.
New AWS users get access to the [AWS Free Tier](https://aws.amazon.com/free/), which lets you use many AWS resources for free for 1 year, like [AWS Lambda](https://aws.amazon.com/lambda/pricing/). New AWS users won't be charged for signing up.
All AWS users get access to the Free Tier for [AWS Lambda](https://aws.amazon.com/lambda/pricing/). ASW Lambda is part of the non-expiring [AWS Free Tier](https://aws.amazon.com/free/#AWS_FREE_TIER).
If you don't have a credit card set up, you may not be able to deploy your resources and you may run into this error:

View File

@ -28,6 +28,7 @@ class AwsInvoke {
// validate function exists in service
this.options.functionObj = this.serverless.service.getFunction(this.options.function);
this.options.data = this.options.data || '';
return new BbPromise(resolve => {
if (this.options.data) {
@ -42,10 +43,15 @@ class AwsInvoke {
this.options.data = this.serverless.utils.readFileSync(absolutePath);
resolve();
} else {
stdin().then(input => {
this.options.data = input;
try {
stdin().then(input => {
this.options.data = input;
resolve();
});
} catch (exception) {
// resolve if no stdin was provided
resolve();
});
}
}
}).then(() => {
try {

View File

@ -81,6 +81,14 @@ describe('AwsInvoke', () => {
expect(() => awsInvoke.extendedValidate()).to.throw(Error);
});
it('should not throw error when there are no input data', () => {
awsInvoke.options.data = undefined;
return awsInvoke.extendedValidate().then(() => {
expect(awsInvoke.options.data).to.equal('');
});
});
it('should keep data if it is a simple string', () => {
awsInvoke.options.data = 'simple-string';

View File

@ -28,6 +28,7 @@ class AwsInvokeLocal {
// validate function exists in service
this.options.functionObj = this.serverless.service.getFunction(this.options.function);
this.options.data = this.options.data || '';
return new BbPromise(resolve => {
if (this.options.data) {
@ -42,10 +43,15 @@ class AwsInvokeLocal {
this.options.data = this.serverless.utils.readFileSync(absolutePath);
resolve();
} else {
stdin().then(input => {
this.options.data = input;
try {
stdin().then(input => {
this.options.data = input;
resolve();
});
} catch (exception) {
// resolve if no stdin was provided
resolve();
});
}
}
}).then(() => {
try {

View File

@ -78,6 +78,14 @@ describe('AwsInvokeLocal', () => {
awsInvokeLocal.options.path = false;
});
it('should not throw error when there are no input data', () => {
awsInvokeLocal.options.data = undefined;
return awsInvokeLocal.extendedValidate().then(() => {
expect(awsInvokeLocal.options.data).to.equal('');
});
});
it('it should throw error if function is not provided', () => {
serverless.service.functions = null;
expect(() => awsInvokeLocal.extendedValidate()).to.throw(Error);

View File

@ -6,6 +6,13 @@ const _ = require('lodash');
const moment = require('moment');
const validate = require('../lib/validate');
// helper functions
const getRoundedAvgDuration = (duration, functionsCount) =>
(Math.round(duration * 100) / 100) / functionsCount;
const reduceDatapoints = (datapoints, statistic) => datapoints
.reduce((previous, datapoint) => previous + datapoint[statistic], 0);
class AwsMetrics {
constructor(serverless, options) {
this.serverless = serverless;
@ -25,9 +32,6 @@ class AwsMetrics {
extendedValidate() {
this.validate();
// validate function exists in service
this.options.function = this.serverless.service.getFunction(this.options.function).name;
const today = new Date();
let yesterday = new Date();
yesterday = yesterday.setDate(yesterday.getDate() - 1);
@ -55,160 +59,178 @@ class AwsMetrics {
}
getMetrics() {
const FunctionName = this.options.function;
const StartTime = this.options.startTime;
const EndTime = this.options.endTime;
const Namespace = 'AWS/Lambda';
// get all the function names in the service
let functions = this.serverless.service.getAllFunctions()
.map((func) => this.serverless.service.getFunction(func).name);
const hoursDiff = Math.abs(EndTime - StartTime) / 36e5;
const Period = (hoursDiff > 24) ? 3600 * 24 : 3600;
if (this.options.function) {
// validate if function can be found in service
this.options.function = this.serverless.service.getFunction(this.options.function).name;
const promises = [];
// filter out the one function the user has specified through an option
functions = functions.filter((func) => func === this.options.function);
}
// get invocations
const invocationsPromise =
this.provider.request(
'CloudWatch',
'getMetricStatistics',
{
StartTime,
EndTime,
MetricName: 'Invocations',
Namespace,
Period,
Dimensions: [
{
Name: 'FunctionName',
Value: FunctionName,
},
],
Statistics: [
'Sum',
],
Unit: 'Count',
},
this.options.stage,
this.options.region
);
// get throttles
const throttlesPromise =
this.provider.request(
'CloudWatch',
'getMetricStatistics',
{
StartTime,
EndTime,
MetricName: 'Throttles',
Namespace,
Period,
Dimensions: [
{
Name: 'FunctionName',
Value: FunctionName,
},
],
Statistics: [
'Sum',
],
Unit: 'Count',
},
this.options.stage,
this.options.region
);
// get errors
const errorsPromise =
this.provider.request(
'CloudWatch',
'getMetricStatistics',
{
StartTime,
EndTime,
MetricName: 'Errors',
Namespace,
Period,
Dimensions: [
{
Name: 'FunctionName',
Value: FunctionName,
},
],
Statistics: [
'Sum',
],
Unit: 'Count',
},
this.options.stage,
this.options.region
);
// get avg. duration
const avgDurationPromise =
this.provider.request(
'CloudWatch',
'getMetricStatistics',
{
StartTime,
EndTime,
MetricName: 'Duration',
Namespace,
Period,
Dimensions: [
{
Name: 'FunctionName',
Value: FunctionName,
},
],
Statistics: [
'Average',
],
Unit: 'Milliseconds',
},
this.options.stage,
this.options.region
);
return BbPromise.map(functions, (func) => {
const FunctionName = func;
const StartTime = this.options.startTime;
const EndTime = this.options.endTime;
const Namespace = 'AWS/Lambda';
// push all promises to the array which will be used to resolve those
promises.push(invocationsPromise);
promises.push(throttlesPromise);
promises.push(errorsPromise);
promises.push(avgDurationPromise);
const hoursDiff = Math.abs(EndTime - StartTime) / 36e5;
const Period = (hoursDiff > 24) ? 3600 * 24 : 3600;
return BbPromise.all(promises).then((metrics) => metrics);
const promises = [];
// get invocations
const invocationsPromise =
this.provider.request(
'CloudWatch',
'getMetricStatistics',
{
StartTime,
EndTime,
MetricName: 'Invocations',
Namespace,
Period,
Dimensions: [
{
Name: 'FunctionName',
Value: FunctionName,
},
],
Statistics: [
'Sum',
],
Unit: 'Count',
},
this.options.stage,
this.options.region
);
// get throttles
const throttlesPromise =
this.provider.request(
'CloudWatch',
'getMetricStatistics',
{
StartTime,
EndTime,
MetricName: 'Throttles',
Namespace,
Period,
Dimensions: [
{
Name: 'FunctionName',
Value: FunctionName,
},
],
Statistics: [
'Sum',
],
Unit: 'Count',
},
this.options.stage,
this.options.region
);
// get errors
const errorsPromise =
this.provider.request(
'CloudWatch',
'getMetricStatistics',
{
StartTime,
EndTime,
MetricName: 'Errors',
Namespace,
Period,
Dimensions: [
{
Name: 'FunctionName',
Value: FunctionName,
},
],
Statistics: [
'Sum',
],
Unit: 'Count',
},
this.options.stage,
this.options.region
);
// get avg. duration
const avgDurationPromise =
this.provider.request(
'CloudWatch',
'getMetricStatistics',
{
StartTime,
EndTime,
MetricName: 'Duration',
Namespace,
Period,
Dimensions: [
{
Name: 'FunctionName',
Value: FunctionName,
},
],
Statistics: [
'Average',
],
Unit: 'Milliseconds',
},
this.options.stage,
this.options.region
);
// push all promises to the array which will be used to resolve those
promises.push(invocationsPromise);
promises.push(throttlesPromise);
promises.push(errorsPromise);
promises.push(avgDurationPromise);
return BbPromise.all(promises).then((metrics) => metrics);
});
}
showMetrics(metrics) {
let message = '';
message += `${chalk.yellow.underline(this.options.function)}\n`;
if (this.options.function) {
message += `${chalk.yellow.underline(this.options.function)}\n`;
} else {
message += `${chalk.yellow.underline('Service wide metrics')}\n`;
}
const formattedStartTime = moment(this.options.startTime).format('LLL');
const formattedEndTime = moment(this.options.endTime).format('LLL');
message += `${formattedStartTime} - ${formattedEndTime}\n\n`;
if (metrics && metrics.length > 0) {
let invocations = 0;
let throttles = 0;
let errors = 0;
let duration = 0;
_.forEach(metrics, (metric) => {
if (metric.Label === 'Invocations') {
const datapoints = metric.Datapoints;
const invocations = datapoints
.reduce((previous, datapoint) => previous + datapoint.Sum, 0);
message += `${chalk.yellow('Invocations:', invocations, '\n')}`;
} else if (metric.Label === 'Throttles') {
const datapoints = metric.Datapoints;
const throttles = datapoints
.reduce((previous, datapoint) => previous + datapoint.Sum, 0);
message += `${chalk.yellow('Throttles:', throttles, '\n')}`;
} else if (metric.Label === 'Errors') {
const datapoints = metric.Datapoints;
const errors = datapoints
.reduce((previous, datapoint) => previous + datapoint.Sum, 0);
message += `${chalk.yellow('Errors:', errors, '\n')}`;
} else {
const datapoints = metric.Datapoints;
const duration = datapoints
.reduce((previous, datapoint) => previous + datapoint.Average, 0);
const formattedRoundedAvgDuration = `${Math.round(duration * 100) / 100}ms`;
message += `${chalk.yellow('Duration (avg.):', formattedRoundedAvgDuration)}`;
}
_.forEach(metric, (funcMetric) => {
if (funcMetric.Label === 'Invocations') {
invocations += reduceDatapoints(funcMetric.Datapoints, 'Sum');
} else if (funcMetric.Label === 'Throttles') {
throttles += reduceDatapoints(funcMetric.Datapoints, 'Sum');
} else if (funcMetric.Label === 'Errors') {
errors += reduceDatapoints(funcMetric.Datapoints, 'Sum');
} else {
duration += reduceDatapoints(funcMetric.Datapoints, 'Average');
}
});
});
const formattedDuration = `${getRoundedAvgDuration(duration, metrics.length)}ms`;
// display the data
message += `${chalk.yellow('Invocations:', invocations, '\n')}`;
message += `${chalk.yellow('Throttles:', throttles, '\n')}`;
message += `${chalk.yellow('Errors:', errors, '\n')}`;
message += `${chalk.yellow('Duration (avg.):', formattedDuration)}`;
} else {
message += `${chalk.yellow('There are no metrics to show for these options')}`;
}

View File

@ -175,7 +175,14 @@ describe('AwsMetrics', () => {
let requestStub;
beforeEach(() => {
awsMetrics.options.function = 'function1';
awsMetrics.serverless.service.functions = {
function1: {
name: 'func1',
},
function2: {
name: 'func2',
},
};
awsMetrics.options.startTime = '1970-01-01';
awsMetrics.options.endTime = '1970-01-02';
requestStub = sinon.stub(awsMetrics.provider, 'request');
@ -185,11 +192,12 @@ describe('AwsMetrics', () => {
awsMetrics.provider.request.restore();
});
it('should should gather metrics for the function', () => {
it('should gather service wide function metrics if no function option is specified', () => {
// stubs for function1
// invocations
requestStub.onCall(0).returns(
BbPromise.resolve({
ResponseMetadata: { RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755' },
ResponseMetadata: { RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755-func1' },
Label: 'Invocations',
Datapoints: [],
})
@ -197,7 +205,7 @@ describe('AwsMetrics', () => {
// throttles
requestStub.onCall(1).returns(
BbPromise.resolve({
ResponseMetadata: { RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2' },
ResponseMetadata: { RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2-func1' },
Label: 'Throttles',
Datapoints: [],
})
@ -205,7 +213,7 @@ describe('AwsMetrics', () => {
// errors
requestStub.onCall(2).returns(
BbPromise.resolve({
ResponseMetadata: { RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b' },
ResponseMetadata: { RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b-func1' },
Label: 'Errors',
Datapoints: [],
})
@ -213,29 +221,146 @@ describe('AwsMetrics', () => {
// duration
requestStub.onCall(3).returns(
BbPromise.resolve({
ResponseMetadata: { RequestId: '1f63db14-b569-11e6-8501-d98a275ce164' },
ResponseMetadata: { RequestId: '1f63db14-b569-11e6-8501-d98a275ce164-func1' },
Label: 'Duration',
Datapoints: [],
})
);
// stubs for function2
// invocations
requestStub.onCall(4).returns(
BbPromise.resolve({
ResponseMetadata: { RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755-func2' },
Label: 'Invocations',
Datapoints: [],
})
);
// throttles
requestStub.onCall(5).returns(
BbPromise.resolve({
ResponseMetadata: { RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2-func2' },
Label: 'Throttles',
Datapoints: [],
})
);
// errors
requestStub.onCall(6).returns(
BbPromise.resolve({
ResponseMetadata: { RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b-func2' },
Label: 'Errors',
Datapoints: [],
})
);
// duration
requestStub.onCall(7).returns(
BbPromise.resolve({
ResponseMetadata: { RequestId: '1f63db14-b569-11e6-8501-d98a275ce164-func2' },
Label: 'Duration',
Datapoints: [],
})
);
const expectedResult = [
{ ResponseMetadata: { RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755' },
[
{ ResponseMetadata: { RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755-func1' },
Label: 'Invocations',
Datapoints: [],
},
{ ResponseMetadata: { RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2-func1' },
Label: 'Throttles',
Datapoints: [],
},
{ ResponseMetadata: { RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b-func1' },
Label: 'Errors',
Datapoints: [],
},
{ ResponseMetadata: { RequestId: '1f63db14-b569-11e6-8501-d98a275ce164-func1' },
Label: 'Duration',
Datapoints: [],
},
],
[
{ ResponseMetadata: { RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755-func2' },
Label: 'Invocations',
Datapoints: [],
},
{ ResponseMetadata: { RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2-func2' },
Label: 'Throttles',
Datapoints: [],
},
{ ResponseMetadata: { RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b-func2' },
Label: 'Errors',
Datapoints: [],
},
{ ResponseMetadata: { RequestId: '1f63db14-b569-11e6-8501-d98a275ce164-func2' },
Label: 'Duration',
Datapoints: [],
},
],
];
return awsMetrics.getMetrics().then((result) => {
expect(result).to.deep.equal(expectedResult);
});
});
it('should gather function metrics if function option is specified', () => {
// only display metrics for function1
awsMetrics.options.function = 'function1';
// stubs for function1
// invocations
requestStub.onCall(0).returns(
BbPromise.resolve({
ResponseMetadata: { RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755-func1' },
Label: 'Invocations',
Datapoints: [],
},
{ ResponseMetadata: { RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2' },
})
);
// throttles
requestStub.onCall(1).returns(
BbPromise.resolve({
ResponseMetadata: { RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2-func1' },
Label: 'Throttles',
Datapoints: [],
},
{ ResponseMetadata: { RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b' },
})
);
// errors
requestStub.onCall(2).returns(
BbPromise.resolve({
ResponseMetadata: { RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b-func1' },
Label: 'Errors',
Datapoints: [],
},
{ ResponseMetadata: { RequestId: '1f63db14-b569-11e6-8501-d98a275ce164' },
})
);
// duration
requestStub.onCall(3).returns(
BbPromise.resolve({
ResponseMetadata: { RequestId: '1f63db14-b569-11e6-8501-d98a275ce164-func1' },
Label: 'Duration',
Datapoints: [],
},
})
);
const expectedResult = [
[
{ ResponseMetadata: { RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755-func1' },
Label: 'Invocations',
Datapoints: [],
},
{ ResponseMetadata: { RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2-func1' },
Label: 'Throttles',
Datapoints: [],
},
{ ResponseMetadata: { RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b-func1' },
Label: 'Errors',
Datapoints: [],
},
{ ResponseMetadata: { RequestId: '1f63db14-b569-11e6-8501-d98a275ce164-func1' },
Label: 'Duration',
Datapoints: [],
},
],
];
return awsMetrics.getMetrics().then((result) => {
@ -248,7 +373,14 @@ describe('AwsMetrics', () => {
let consoleLogStub;
beforeEach(() => {
awsMetrics.options.function = 'function1';
awsMetrics.serverless.service.functions = {
function1: {
name: 'func1',
},
function2: {
name: 'func2',
},
};
awsMetrics.options.startTime = '1970-01-01';
awsMetrics.options.endTime = '1970-01-02';
consoleLogStub = sinon.stub(serverless.cli, 'consoleLog').returns();
@ -258,36 +390,118 @@ describe('AwsMetrics', () => {
serverless.cli.consoleLog.restore();
});
it('should display all metrics for the given function', () => {
it('should display service wide metrics if no function option is specified', () => {
const metrics = [
{
ResponseMetadata: {
RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755',
[
{
ResponseMetadata: {
RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755-func1',
},
Label: 'Invocations',
Datapoints: [{ Sum: 12 }, { Sum: 8 }],
},
Label: 'Invocations',
Datapoints: [{ Sum: 12 }, { Sum: 8 }],
},
{
ResponseMetadata: {
RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2',
{
ResponseMetadata: {
RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2-func1',
},
Label: 'Throttles',
Datapoints: [{ Sum: 15 }, { Sum: 15 }],
},
Label: 'Throttles',
Datapoints: [{ Sum: 15 }, { Sum: 15 }],
},
{
ResponseMetadata: {
RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b',
{
ResponseMetadata: {
RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b-func1',
},
Label: 'Errors',
Datapoints: [{ Sum: 0 }],
},
Label: 'Errors',
Datapoints: [{ Sum: 0 }],
},
{
ResponseMetadata: {
RequestId: '1f63db14-b569-11e6-8501-d98a275ce164',
{
ResponseMetadata: {
RequestId: '1f63db14-b569-11e6-8501-d98a275ce164-func1',
},
Label: 'Duration',
Datapoints: [{ Average: 1000 }],
},
Label: 'Duration',
Datapoints: [{ Average: 1000 }],
},
],
[
{
ResponseMetadata: {
RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755-func2',
},
Label: 'Invocations',
Datapoints: [{ Sum: 12 }, { Sum: 8 }],
},
{
ResponseMetadata: {
RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2-func2',
},
Label: 'Throttles',
Datapoints: [{ Sum: 15 }, { Sum: 15 }],
},
{
ResponseMetadata: {
RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b-func2',
},
Label: 'Errors',
Datapoints: [{ Sum: 0 }],
},
{
ResponseMetadata: {
RequestId: '1f63db14-b569-11e6-8501-d98a275ce164-func2',
},
Label: 'Duration',
Datapoints: [{ Average: 1000 }],
},
],
];
let expectedMessage = '';
expectedMessage += `${chalk.yellow.underline('Service wide metrics')}\n`;
expectedMessage += 'January 1, 1970 12:00 AM - January 2, 1970 12:00 AM\n\n';
expectedMessage += `${chalk.yellow('Invocations: 40 \n')}`;
expectedMessage += `${chalk.yellow('Throttles: 60 \n')}`;
expectedMessage += `${chalk.yellow('Errors: 0 \n')}`;
expectedMessage += `${chalk.yellow('Duration (avg.): 1000ms')}`;
return awsMetrics.showMetrics(metrics).then((message) => {
expect(consoleLogStub.calledOnce).to.equal(true);
expect(message).to.equal(expectedMessage);
});
});
it('should display function metrics if function option is specified', () => {
awsMetrics.options.function = 'function1';
const metrics = [
[
{
ResponseMetadata: {
RequestId: '1f50045b-b569-11e6-86c6-eb54d1aaa755-func1',
},
Label: 'Invocations',
Datapoints: [{ Sum: 12 }, { Sum: 8 }],
},
{
ResponseMetadata: {
RequestId: '1f59059b-b569-11e6-aa18-c7bab68810d2-func1',
},
Label: 'Throttles',
Datapoints: [{ Sum: 15 }, { Sum: 15 }],
},
{
ResponseMetadata: {
RequestId: '1f50c7b1-b569-11e6-b1b6-ab86694b617b-func1',
},
Label: 'Errors',
Datapoints: [{ Sum: 0 }],
},
{
ResponseMetadata: {
RequestId: '1f63db14-b569-11e6-8501-d98a275ce164-func1',
},
Label: 'Duration',
Datapoints: [{ Average: 1000 }],
},
],
];
let expectedMessage = '';
@ -305,6 +519,8 @@ describe('AwsMetrics', () => {
});
it('should resolve with an error message if no metrics are available', () => {
awsMetrics.options.function = 'function1';
let expectedMessage = '';
expectedMessage += `${chalk.yellow.underline(awsMetrics.options.function)}\n`;
expectedMessage += 'January 1, 1970 12:00 AM - January 2, 1970 12:00 AM\n\n';

View File

@ -14,7 +14,6 @@ class Metrics {
options: {
function: {
usage: 'The function name',
required: true,
shortcut: 'f',
},
stage: {

View File

@ -25,10 +25,5 @@ describe('Metrics', () => {
'metrics',
]);
});
it('should have a required option "function"', () => {
// eslint-disable-next-line no-unused-expressions
expect(metrics.commands.metrics.options.function.required).to.be.true;
});
});
});