mirror of
https://github.com/serverless/serverless.git
synced 2026-01-25 15:07:39 +00:00
Merge branch 'master' into add-alexa-event-source
This commit is contained in:
commit
010da2cb59
@ -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>
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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';
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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')}`;
|
||||
}
|
||||
|
||||
@ -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';
|
||||
|
||||
@ -14,7 +14,6 @@ class Metrics {
|
||||
options: {
|
||||
function: {
|
||||
usage: 'The function name',
|
||||
required: true,
|
||||
shortcut: 'f',
|
||||
},
|
||||
stage: {
|
||||
|
||||
@ -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;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user