mirror of
https://github.com/serverless/serverless.git
synced 2026-01-25 15:07:39 +00:00
Merge branch 'master' of https://github.com/serverless/serverless into HEAD
This commit is contained in:
commit
b6ca75876c
3
.gitignore
vendored
3
.gitignore
vendored
@ -49,3 +49,6 @@ tracking-config.json
|
||||
|
||||
# Misc
|
||||
jest
|
||||
|
||||
# VIM
|
||||
*.swp
|
||||
|
||||
@ -23,13 +23,6 @@ matrix:
|
||||
- SLS_IGNORE_WARNING=*
|
||||
- secure: Ia2nYzOeYvTE6qOP7DBKX3BO7s/U7TXdsvB2nlc3kOPFi//IbTVD0/cLKCAE5XqTzrrliHINSVsFcJNSfjCwmDSRmgoIGrHj5CJkWpkI6FEPageo3mdqFQYEc8CZeAjsPBNaHe6Ewzg0Ev/sjTByLSJYVqokzDCF1QostSxx1Ss6SGt1zjxeP/Hp4yOJn52VAm9IHAKYn7Y62nMAFTaaTPUQHvW0mJj6m2Z8TWyPU+2Bx6mliO65gTPFGs+PdHGwHtmSF/4IcUO504x+HjDuwzW2itomLXZmIOFfGDcFYadKWzVMAfJzoRWOcVKF4jXdMoSCOviWpHGtK35E7K956MTXkroVoWCS7V0knQDovbRZj8c8td8mS4tdprUA+TzgZoHet2atWNtMuTh79rdmwoAO+IAWJegYj62Tdfy3ycESzY+KxSaV8kysG9sR3PRFoWjZerA7MhLZEzQMORXDGjJlgwLaZfYVqjlsGe5p5etFBUTd0WbFgSwOKLoA2U/fm7WzqItkjs3UWaHuvFVvwYixGxjEVmVczS6wa2cdGpHtVD9H7km4fPEzljHqQ26v0P5e8eylgqLF2IB6mL7UqGFrAtrMvAgN/M3gnq4dTs/wq1AJIOxEP7YW7kc0NAldk8vUz6t5GzCPNcuukxAku91Awnh0twxgUywatgJLZPY=
|
||||
- secure: Dgaa5XIsA5Vbw/CYQLUAuVVsDX26C8+f1XYGwsbNmFQKbKvM8iy9lGrHlfrT3jftJkJH6re8tP1RjyZjjzLe25KPk4Tps7grNteCyiIIEDsC2aHhiXHD6zNHsItpxYusaFfyQinFWnK4CAYKWb9ZNIwHIDUIB4vq807QGAhYsnoj1Lg/ajWvtEKBwYjEzDz9OjB91lw7lpCnHtmKKw5A+TNIVGpDDZ/jRBqETsPaePtiXC9UTHZQyM3gFoeVXiJw9KSU/gjIx9REihCaWWPbnuQSeIONGGlVWY9V4DTZIsJr9/uwDcbioeXDD3G1ezGtNPPRSNTtq08QlUtE4mEtKea/+ObpllKZCeZGn6AJhMn+uqMIP95FFlqBB55YzRcLZY+Igi/qm/9LJ9RinAhxRVXiwzeQ+BdVA6jshAAzr+7wklux6lZAa0xGw9pgTv7MI4RP2LJ/LMP1ppFsnv9n/qt93Ax1VEwEu3xHZe3VTYL9tbXOPTZutf6fKjUrW7wSSuy637queESjYnnPKSb1vZcPxjSFlyh+GJvxu/3PurF9aqfiBdiorIBre+pQS4lakLtoft5nsbA+4iYUwrXR58qUPVUqQ7a0A0hedOWlp6g9ixLa6nugUP5aobJzR71T8l/IjqpnY2EEd/iINEb0XfUiZtB5zHaqFWejBtmWwCI=
|
||||
- node_js: '6.2'
|
||||
env:
|
||||
- INTEGRATION_TEST=true
|
||||
- INTEGRATION_TEST_SUITE=complex
|
||||
- SLS_IGNORE_WARNING=*
|
||||
- secure: Ia2nYzOeYvTE6qOP7DBKX3BO7s/U7TXdsvB2nlc3kOPFi//IbTVD0/cLKCAE5XqTzrrliHINSVsFcJNSfjCwmDSRmgoIGrHj5CJkWpkI6FEPageo3mdqFQYEc8CZeAjsPBNaHe6Ewzg0Ev/sjTByLSJYVqokzDCF1QostSxx1Ss6SGt1zjxeP/Hp4yOJn52VAm9IHAKYn7Y62nMAFTaaTPUQHvW0mJj6m2Z8TWyPU+2Bx6mliO65gTPFGs+PdHGwHtmSF/4IcUO504x+HjDuwzW2itomLXZmIOFfGDcFYadKWzVMAfJzoRWOcVKF4jXdMoSCOviWpHGtK35E7K956MTXkroVoWCS7V0knQDovbRZj8c8td8mS4tdprUA+TzgZoHet2atWNtMuTh79rdmwoAO+IAWJegYj62Tdfy3ycESzY+KxSaV8kysG9sR3PRFoWjZerA7MhLZEzQMORXDGjJlgwLaZfYVqjlsGe5p5etFBUTd0WbFgSwOKLoA2U/fm7WzqItkjs3UWaHuvFVvwYixGxjEVmVczS6wa2cdGpHtVD9H7km4fPEzljHqQ26v0P5e8eylgqLF2IB6mL7UqGFrAtrMvAgN/M3gnq4dTs/wq1AJIOxEP7YW7kc0NAldk8vUz6t5GzCPNcuukxAku91Awnh0twxgUywatgJLZPY=
|
||||
- secure: Dgaa5XIsA5Vbw/CYQLUAuVVsDX26C8+f1XYGwsbNmFQKbKvM8iy9lGrHlfrT3jftJkJH6re8tP1RjyZjjzLe25KPk4Tps7grNteCyiIIEDsC2aHhiXHD6zNHsItpxYusaFfyQinFWnK4CAYKWb9ZNIwHIDUIB4vq807QGAhYsnoj1Lg/ajWvtEKBwYjEzDz9OjB91lw7lpCnHtmKKw5A+TNIVGpDDZ/jRBqETsPaePtiXC9UTHZQyM3gFoeVXiJw9KSU/gjIx9REihCaWWPbnuQSeIONGGlVWY9V4DTZIsJr9/uwDcbioeXDD3G1ezGtNPPRSNTtq08QlUtE4mEtKea/+ObpllKZCeZGn6AJhMn+uqMIP95FFlqBB55YzRcLZY+Igi/qm/9LJ9RinAhxRVXiwzeQ+BdVA6jshAAzr+7wklux6lZAa0xGw9pgTv7MI4RP2LJ/LMP1ppFsnv9n/qt93Ax1VEwEu3xHZe3VTYL9tbXOPTZutf6fKjUrW7wSSuy637queESjYnnPKSb1vZcPxjSFlyh+GJvxu/3PurF9aqfiBdiorIBre+pQS4lakLtoft5nsbA+4iYUwrXR58qUPVUqQ7a0A0hedOWlp6g9ixLa6nugUP5aobJzR71T8l/IjqpnY2EEd/iINEb0XfUiZtB5zHaqFWejBtmWwCI=
|
||||
- node_js: '6.2'
|
||||
env:
|
||||
- DISABLE_TESTS=true
|
||||
@ -44,7 +37,6 @@ script:
|
||||
- if [[ -z "$INTEGRATION_TEST" && -z "$DISABLE_TESTS" ]]; then npm test; fi
|
||||
- if [[ ! -z "$DISABLE_TESTS" && ! -z "$LINTING" && -z "$INTEGRATION_TEST" ]]; then npm run lint; fi
|
||||
- if [[ ! -z "$INTEGRATION_TEST" && ! -z ${AWS_ACCESS_KEY_ID+x} && "$INTEGRATION_TEST_SUITE" == "simple" ]]; then npm run simple-integration-test; fi
|
||||
- if [[ ! -z "$INTEGRATION_TEST" && ! -z ${AWS_ACCESS_KEY_ID+x} && "$INTEGRATION_TEST_SUITE" == "complex" && "$TRAVIS_BRANCH" == "master" && "$TRAVIS_PULL_REQUEST" == "false" ]]; then npm run complex-integration-test; fi
|
||||
|
||||
after_success:
|
||||
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage
|
||||
|
||||
@ -10,9 +10,12 @@ const initializeErrorReporter = require('../lib/utils/sentry').initializeErrorRe
|
||||
|
||||
Error.stackTraceLimit = Infinity;
|
||||
|
||||
BbPromise.config({
|
||||
longStackTraces: true,
|
||||
});
|
||||
if (process.env.SLS_DEBUG) {
|
||||
// For performance reasons enabled only in SLS_DEBUG mode
|
||||
BbPromise.config({
|
||||
longStackTraces: true,
|
||||
});
|
||||
}
|
||||
|
||||
process.on('unhandledRejection', (e) => {
|
||||
logError(e);
|
||||
|
||||
@ -79,6 +79,10 @@ services:
|
||||
image: python2.7
|
||||
volumes:
|
||||
- ./tmp/serverless-integration-test-spotinst-python:/app
|
||||
spotinst-ruby:
|
||||
image: ruby2.4.1
|
||||
volumes:
|
||||
- ./tmp/serverless-integration-test-spotinst-ruby:/app
|
||||
webtasks-nodejs:
|
||||
image: node:6.10.3
|
||||
volumes:
|
||||
|
||||
@ -122,6 +122,7 @@ Already using AWS or another cloud provider? Read on.
|
||||
<li><a href="./providers/spotinst/guide">Guide</a></li>
|
||||
<li><a href="./providers/spotinst/cli-reference">CLI Reference</a></li>
|
||||
<li><a href="./providers/spotinst/events">Events</a></li>
|
||||
<li><a href="./providers/spotinst/examples">Examples</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -72,6 +72,7 @@ If you have questions, join the [chat in gitter](https://gitter.im/serverless/se
|
||||
<li><a href="./cli-reference/plugin-search.md">Plugin Search</a></li>
|
||||
<li><a href="./cli-reference/plugin-install.md">Plugin Install</a></li>
|
||||
<li><a href="./cli-reference/plugin-uninstall.md">Plugin Uninstall</a></li>
|
||||
<li><a href="./cli-reference/print.md">Print</a></li>
|
||||
<li><a href="./cli-reference/slstats.md">Serverless Stats</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
@ -91,6 +92,7 @@ If you have questions, join the [chat in gitter](https://gitter.im/serverless/se
|
||||
<li><a href="./events/schedule.md">Schedule</a></li>
|
||||
<li><a href="./events/sns.md">SNS</a></li>
|
||||
<li><a href="./events/alexa-skill.md">Alexa Skill</a></li>
|
||||
<li><a href="./events/alexa-smart-home.md">Alexa Smart Home</a></li>
|
||||
<li><a href="./events/iot.md">IoT</a></li>
|
||||
<li><a href="./events/cloudwatch-event.md">CloudWatch Event</a></li>
|
||||
<li><a href="./events/cloudwatch-log.md">CloudWatch Log</a></li>
|
||||
|
||||
@ -26,8 +26,15 @@ serverless create --template aws-nodejs
|
||||
serverless create --template aws-nodejs --path myService
|
||||
```
|
||||
|
||||
**Create service in new folder using a custom template:**
|
||||
|
||||
```bash
|
||||
serverless create --template-url https://github.com/serverless/serverless/tree/master/lib/plugins/create/templates/aws-nodejs --path myService
|
||||
```
|
||||
|
||||
## Options
|
||||
- `--template` or `-t` The name of one of the available templates. **Required**.
|
||||
- `--template` or `-t` The name of one of the available templates. **Required if --template-url is not present**.
|
||||
- `--template-url` or `-u` The name of one of the available templates. **Required if --template is not present**.
|
||||
- `--path` or `-p` The path where the service should be created.
|
||||
- `--name` or `-n` the name of the service in `serverless.yml`.
|
||||
|
||||
|
||||
78
docs/providers/aws/cli-reference/print.md
Normal file
78
docs/providers/aws/cli-reference/print.md
Normal file
@ -0,0 +1,78 @@
|
||||
<!--
|
||||
title: Serverless Framework Commands - AWS Lambda - Print
|
||||
menuText: Print
|
||||
menuOrder: 21
|
||||
description: Print your config with all variables resolved for debugging
|
||||
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/print)
|
||||
<!-- DOCS-SITE-LINK:END -->
|
||||
|
||||
# Print
|
||||
|
||||
Print your `serverless.yml` config file with all variables resolved.
|
||||
|
||||
If you're using [Serverless Variables](https://serverless.com/framework/docs/providers/aws/guide/variables/)
|
||||
in your `serverless.yml`, it can be difficult to know if your syntax is correct
|
||||
or if the variables are resolving as you expect.
|
||||
|
||||
With this command, it will print the fully-resolved config to your console.
|
||||
|
||||
```bash
|
||||
serverless print
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
- None
|
||||
|
||||
## Examples:
|
||||
|
||||
Assuming you have the following config file:
|
||||
|
||||
```yml
|
||||
service: my-service
|
||||
|
||||
custom:
|
||||
bucketName: test
|
||||
|
||||
provider:
|
||||
name: aws
|
||||
runtime: nodejs6.10
|
||||
stage: ${opt:stage, "dev"}
|
||||
|
||||
functions:
|
||||
hello:
|
||||
handler: handler.hello
|
||||
|
||||
resources:
|
||||
Resources:
|
||||
MyBucket:
|
||||
Type: AWS::S3::Bucket
|
||||
Properties:
|
||||
BucketName: ${self:custom.bucketName}
|
||||
```
|
||||
|
||||
Using `sls print` will resolve the variables in `provider.stage` and `BucketName`.
|
||||
|
||||
```bash
|
||||
$ sls print
|
||||
service: my-service
|
||||
custom:
|
||||
bucketName: test
|
||||
provider:
|
||||
name: aws
|
||||
runtime: nodejs6.10
|
||||
stage: dev # <-- Resolved
|
||||
functions:
|
||||
hello:
|
||||
handler: handler.hello
|
||||
resources:
|
||||
Resources:
|
||||
MyBucket:
|
||||
Type: 'AWS::S3::Bucket'
|
||||
Properties:
|
||||
BucketName: test # <-- Resolved
|
||||
```
|
||||
@ -19,6 +19,8 @@ Rollback the Serverless service to a specific deployment.
|
||||
serverless rollback --timestamp timestamp
|
||||
```
|
||||
|
||||
If `timestamp` is not specified, Framework will show your existing deployments.
|
||||
|
||||
## Options
|
||||
- `--timestamp` or `-t` The deployment you want to rollback to.
|
||||
- `--verbose` or `-v` Shows any Stack Output.
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<!--
|
||||
title: Serverless Framework Commands - AWS Lambda - Serverless Stats
|
||||
menuText: serverless stats
|
||||
menuOrder: 21
|
||||
menuOrder: 22
|
||||
description: Enables or disables Serverless Statistic logging within the Serverless Framework.
|
||||
layout: Doc
|
||||
-->
|
||||
|
||||
46
docs/providers/aws/events/alexa-smart-home.md
Normal file
46
docs/providers/aws/events/alexa-smart-home.md
Normal file
@ -0,0 +1,46 @@
|
||||
<!--
|
||||
title: Serverless Framework - AWS Lambda Events - Alexa Smart Home
|
||||
menuText: Alexa Smart Home
|
||||
menuOrder: 11
|
||||
description: Setting up AWS Alexa Smart Home Events with AWS Lambda via the Serverless Framework
|
||||
layout: Doc
|
||||
-->
|
||||
|
||||
<!-- DOCS-SITE-LINK:START automatically generated -->
|
||||
### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/aws/events/alexa-smart-home)
|
||||
<!-- DOCS-SITE-LINK:END -->
|
||||
|
||||
# Alexa Smart Home
|
||||
|
||||
## Event definition
|
||||
|
||||
This will enable your Lambda function to be called by an Alexa Smart Home Skill.
|
||||
`amzn1.ask.skill.xx-xx-xx-xx` is an application ID for Alexa Smart Home. You need to sign up [Amazon Developer Console](https://developer.amazon.com/) and get your application ID.
|
||||
After deploying, add your deployed Lambda function ARN to which this event is attached to the Service Endpoint under Configuration on Amazon Developer Console.
|
||||
|
||||
Please see [Steps to Create a Smart Home Skill](https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/steps-to-create-a-smart-home-skill) for more info.
|
||||
|
||||
```yml
|
||||
functions:
|
||||
mySkill:
|
||||
handler: mySkill.handler
|
||||
events:
|
||||
- alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx
|
||||
```
|
||||
|
||||
## Enabling / Disabling
|
||||
|
||||
**Note:** `alexaSmartHome` events are enabled by default.
|
||||
|
||||
This will create and attach a alexaSmartHome event for the `mySkill` function which is disabled. If enabled it will call
|
||||
the `mySkill` function by an Alexa Smart Home Skill.
|
||||
|
||||
```yaml
|
||||
functions:
|
||||
mySkill:
|
||||
handler: mySkill.handler
|
||||
events:
|
||||
- alexaSmartHome:
|
||||
appId: amzn1.ask.skill.xx-xx-xx-xx
|
||||
enabled: false
|
||||
```
|
||||
@ -277,6 +277,7 @@ functions:
|
||||
resultTtlInSeconds: 0
|
||||
identitySource: method.request.header.Authorization
|
||||
identityValidationExpression: someRegex
|
||||
type: token
|
||||
authorizerFunc:
|
||||
handler: handler.authorizerFunc
|
||||
```
|
||||
@ -313,6 +314,24 @@ functions:
|
||||
identityValidationExpression: someRegex
|
||||
```
|
||||
|
||||
You can also use the Request Type Authorizer by setting the `type` property. In this case, your `identitySource` could contain multiple entries for you policy cache. The default `type` is 'token'.
|
||||
|
||||
```yml
|
||||
functions:
|
||||
create:
|
||||
handler: posts.create
|
||||
events:
|
||||
- http:
|
||||
path: posts/create
|
||||
method: post
|
||||
authorizer:
|
||||
arn: xxx:xxx:Lambda-Name
|
||||
resultTtlInSeconds: 0
|
||||
identitySource: method.request.header.Authorization, context.identity.sourceIp
|
||||
identityValidationExpression: someRegex
|
||||
type: request
|
||||
```
|
||||
|
||||
You can also configure an existing Cognito User Pool as the authorizer, as shown
|
||||
in the following example:
|
||||
|
||||
|
||||
@ -139,7 +139,7 @@ provider:
|
||||
- Effect: "Allow"
|
||||
Action:
|
||||
- "s3:ListBucket"
|
||||
# You can put CloudFormation syntax in here. No one will judge you.
|
||||
# You can put CloudFormation syntax in here. No one will judge you.
|
||||
# Remember, this all gets translated to CloudFormation.
|
||||
Resource: { "Fn::Join" : ["", ["arn:aws:s3:::", { "Ref" : "ServerlessDeploymentBucket"} ] ] }
|
||||
- Effect: "Allow"
|
||||
@ -226,6 +226,11 @@ Then, when you run `serverless deploy`, VPC configuration will be deployed along
|
||||
|
||||
The Lambda function execution role must have permissions to create, describe and delete [Elastic Network Interfaces](http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_ElasticNetworkInterfaces.html) (ENI). When VPC configuration is provided the default AWS `AWSLambdaVPCAccessExecutionRole` will be associated with your Lambda execution role. In case custom roles are provided be sure to include the proper [ManagedPolicyArns](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-iam-role.html#cfn-iam-role-managepolicyarns). For more information please check [configuring a Lambda Function for Amazon VPC Access](http://docs.aws.amazon.com/lambda/latest/dg/vpc.html)
|
||||
|
||||
**VPC Lambda Internet Access**
|
||||
|
||||
By default, when a Lambda function is executed inside a VPC, it looses internet access and some resources inside AWS may become unavailable. In order for S3 resources and DynamoDB resources to be available for your Lambda function running inside the VPC, a VPC end point needs to be created. For more information please check [VPC Endpoint for Amazon S3](https://aws.amazon.com/blogs/aws/new-vpc-endpoint-for-amazon-s3/).
|
||||
In order for other services such as Kinesis streams to be made available, a NAT Gateway needs to be configured inside the subnets that are being used to run the Lambda, for the VPC used to execute the Lambda. For more information, please check [Enable Outgoing Internet Access within VPC](https://medium.com/@philippholly/aws-lambda-enable-outgoing-internet-access-within-vpc-8dd250e11e12)
|
||||
|
||||
## Environment Variables
|
||||
|
||||
You can add environment variable configuration to a specific function in `serverless.yml` by adding an `environment` object property in the function configuration. This object should contain a key/value collection of strings:
|
||||
@ -305,7 +310,7 @@ These versions are not cleaned up by serverless, so make sure you use a plugin o
|
||||
|
||||
## Dead Letter Queue (DLQ)
|
||||
|
||||
When AWS lambda functions fail, they are [retried](http://docs.aws.amazon.com/lambda/latest/dg/retries-on-errors.html). If the retries also fail, AWS has a feature to send information about the failed request to a SNS topic or SNS queue, called the [Dead Letter Queue](http://docs.aws.amazon.com/lambda/latest/dg/dlq.html), which you can use to track and diagnose and react to lambda failures.
|
||||
When AWS lambda functions fail, they are [retried](http://docs.aws.amazon.com/lambda/latest/dg/retries-on-errors.html). If the retries also fail, AWS has a feature to send information about the failed request to a SNS topic or SQS queue, called the [Dead Letter Queue](http://docs.aws.amazon.com/lambda/latest/dg/dlq.html), which you can use to track and diagnose and react to lambda failures.
|
||||
|
||||
You can setup a dead letter queue for your serverless functions with the help of a SNS topic and the `onError` config parameter.
|
||||
|
||||
|
||||
@ -70,7 +70,7 @@ We're also using the term `normalizedName` or similar terms in this guide. This
|
||||
|Lambda::Function | {normalizedFunctionName}LambdaFunction | HelloLambdaFunction |
|
||||
|Lambda::Version | {normalizedFunctionName}LambdaVersion{sha256} | HelloLambdaVersionr3pgoTvv1xT4E4NiCL6JG02fl6vIyi7OS1aW0FwAI |
|
||||
|Logs::LogGroup | {normalizedFunctionName}LogGroup | HelloLogGroup |
|
||||
|Lambda::Permission | <ul><li>**Schedule**: {normalizedFunctionName}LambdaPermissionEventsRuleSchedule{index}</li><li>**CloudWatch Event**: {normalizedFunctionName}LambdaPermissionEventsRuleCloudWatchEvent{index}</li><li>**CloudWatch Log**: {normalizedFunctionName}LambdaPermissionLogsSubscriptionFilterCloudWatchLog{index}</li><li>**IoT**: {normalizedFunctionName}LambdaPermissionIotTopicRule{index} </li><li>**S3**: {normalizedFunctionName}LambdaPermission{normalizedBucketName}S3</li><li>**APIG**: {normalizedFunctionName}LambdaPermissionApiGateway</li><li>**SNS**: {normalizedFunctionName}LambdaPermission{normalizedTopicName}SNS</li><li>**Alexa Skill**: {normalizedFunctionName}LambdaPermissionAlexaSkill</li><li>**Cognito User Pool Trigger Source**: {normalizedFunctionName}LambdaPermissionCognitoUserPool{normalizedPoolId}TriggerSource{triggerSource}</li> </ul> | <ul><li>**Schedule**: HelloLambdaPermissionEventsRuleSchedule1</li><li>**CloudWatch Event**: HelloLambdaPermissionEventsRuleCloudWatchEvent1</li><li>**CloudWatch Log**: HelloLambdaPermissionLogsSubscriptionFilterCloudWatchLog1</li><li>**IoT**: HelloLambdaPermissionIotTopicRule1 </li><li>**S3**: HelloLambdaPermissionBucketS3</li><li>**APIG**: HelloLambdaPermissionApiGateway</li><li>**SNS**: HelloLambdaPermissionTopicSNS</li><li>**Alexa Skill**: HelloLambdaPermissionAlexaSkill</li><li>**Cognito User Pool Trigger Source**: HelloLambdaPermissionCognitoUserPoolMyPoolTriggerSourceCustomMessage</li> </ul>|
|
||||
|Lambda::Permission | <ul><li>**Schedule**: {normalizedFunctionName}LambdaPermissionEventsRuleSchedule{index}</li><li>**CloudWatch Event**: {normalizedFunctionName}LambdaPermissionEventsRuleCloudWatchEvent{index}</li><li>**CloudWatch Log**: {normalizedFunctionName}LambdaPermissionLogsSubscriptionFilterCloudWatchLog{index}</li><li>**IoT**: {normalizedFunctionName}LambdaPermissionIotTopicRule{index} </li><li>**S3**: {normalizedFunctionName}LambdaPermission{normalizedBucketName}S3</li><li>**APIG**: {normalizedFunctionName}LambdaPermissionApiGateway</li><li>**SNS**: {normalizedFunctionName}LambdaPermission{normalizedTopicName}SNS</li><li>**Alexa Skill**: {normalizedFunctionName}LambdaPermissionAlexaSkill</li><li>**Alexa Smart Home**: {normalizedFunctionName}LambdaPermissionAlexaSmartHome{index}</li><li>**Cognito User Pool Trigger Source**: {normalizedFunctionName}LambdaPermissionCognitoUserPool{normalizedPoolId}TriggerSource{triggerSource}</li> </ul> | <ul><li>**Schedule**: HelloLambdaPermissionEventsRuleSchedule1</li><li>**CloudWatch Event**: HelloLambdaPermissionEventsRuleCloudWatchEvent1</li><li>**CloudWatch Log**: HelloLambdaPermissionLogsSubscriptionFilterCloudWatchLog1</li><li>**IoT**: HelloLambdaPermissionIotTopicRule1 </li><li>**S3**: HelloLambdaPermissionBucketS3</li><li>**APIG**: HelloLambdaPermissionApiGateway</li><li>**SNS**: HelloLambdaPermissionTopicSNS</li><li>**Alexa Skill**: HelloLambdaPermissionAlexaSkill</li><li>**Alexa Smart Home**: HelloLambdaPermissionAlexaSmartHome1</li><li>**Cognito User Pool Trigger Source**: HelloLambdaPermissionCognitoUserPoolMyPoolTriggerSourceCustomMessage</li> </ul>|
|
||||
|Events::Rule | <ul><li>**Schedule**: {normalizedFuntionName}EventsRuleSchedule{SequentialID}</li><li>**CloudWatch Event**: {normalizedFuntionName}EventsRuleCloudWatchEvent{SequentialID}</li> </ul> | <ul><li>**Schedule**: HelloEventsRuleSchedule1</li><li>**CloudWatch Event**: HelloEventsRuleCloudWatchEvent1</li></ul> |
|
||||
|AWS::Logs::SubscriptionFilter | {normalizedFuntionName}LogsSubscriptionFilterCloudWatchLog{SequentialID} | HelloLogsSubscriptionFilterCloudWatchLog1 |
|
||||
|AWS::IoT::TopicRule | {normalizedFuntionName}IotTopicRule{SequentialID} | HelloIotTopicRule1 |
|
||||
@ -111,6 +111,7 @@ functions:
|
||||
resources:
|
||||
Resources:
|
||||
WriteDashPostLogGroup:
|
||||
Type: AWS::Logs::LogGroup
|
||||
Properties:
|
||||
RetentionInDays: "30"
|
||||
```
|
||||
|
||||
@ -30,7 +30,7 @@ provider:
|
||||
region: us-east-1 # Overwrite the default region used. Default is us-east-1
|
||||
profile: production # The default profile to use with this service
|
||||
memorySize: 512 # Overwrite the default memory size. Default is 1024
|
||||
timeout: 10 # The default is 6 seconds
|
||||
timeout: 10 # The default is 6 seconds. Note: API Gateway current maximum is 30 seconds
|
||||
deploymentBucket:
|
||||
name: com.serverless.${self:provider.region}.deploys # Deployment bucket name. Default is generated by the framework
|
||||
serverSideEncryption: AES256 # when using server-side encryption
|
||||
@ -55,13 +55,13 @@ provider:
|
||||
key: value
|
||||
iamRoleStatements: # IAM role statements so that services can be accessed in the AWS account
|
||||
- Effect: 'Allow'
|
||||
Action:
|
||||
- 's3:ListBucket'
|
||||
Resource:
|
||||
Fn::Join:
|
||||
- ''
|
||||
- - 'arn:aws:s3:::'
|
||||
- Ref: ServerlessDeploymentBucket
|
||||
Action:
|
||||
- 's3:ListBucket'
|
||||
Resource:
|
||||
Fn::Join:
|
||||
- ''
|
||||
- - 'arn:aws:s3:::'
|
||||
- Ref: ServerlessDeploymentBucket
|
||||
stackPolicy: # Optional CF stack policy. The example below allows updates to all resources except deleting/replacing EC2 instances (use with caution!)
|
||||
- Effect: Allow
|
||||
Principal: "*"
|
||||
@ -150,6 +150,9 @@ functions:
|
||||
startingPosition: LATEST
|
||||
enabled: false
|
||||
- alexaSkill
|
||||
- alexaSmartHome:
|
||||
appId: amzn1.ask.skill.xx-xx-xx-xx
|
||||
enabled: true
|
||||
- iot:
|
||||
name: myIoTEvent
|
||||
description: An IoT event
|
||||
|
||||
@ -12,22 +12,63 @@ layout: Doc
|
||||
|
||||
# Variables
|
||||
|
||||
The Serverless framework provides a powerful variable system which allows you to add dynamic data into your `serverless.yml`. With Serverless Variables, you'll be able to do the following:
|
||||
Variables allow users to dynamically replace config values in `serverless.yml` config.
|
||||
|
||||
- Reference & load variables from environment variables
|
||||
- Reference & load variables from CLI options
|
||||
- Reference & load variables from CloudFormation stack outputs
|
||||
- Recursively reference properties of any type from the same `serverless.yml` file
|
||||
- Recursively reference properties of any type from other YAML/JSON files
|
||||
- Recursively reference properties exported from JS files, synchronously or asynchronously
|
||||
- Recursively nest variable references within each other for ultimate flexibility
|
||||
- Combine multiple variable references to overwrite each other
|
||||
- Define your own variable syntax if it conflicts with CF syntax
|
||||
- Reference & load variables from S3
|
||||
- Reference & load variables from SSM
|
||||
They are especially useful when providing secrets for your service to use and when you are working with multiple stages.
|
||||
|
||||
## Syntax
|
||||
|
||||
To use variables, you will need to reference values enclosed in `${}` brackets.
|
||||
|
||||
```yml
|
||||
# serverless.yml file
|
||||
yamlKeyXYZ: ${variableSource} # see list of current variable sources below
|
||||
# this is an example of providing a default value as the second parameter
|
||||
otherYamlKey: ${variableSource, defaultValue}
|
||||
```
|
||||
|
||||
You can define your own variable syntax (regex) if it conflicts with CloudFormation's syntax.
|
||||
|
||||
**Note:** You can only use variables in `serverless.yml` property **values**, not property keys. So you can't use variables to generate dynamic logical IDs in the custom resources section for example.
|
||||
|
||||
## Current variable sources:
|
||||
|
||||
- [environment variables](https://serverless.com/framework/docs/providers/aws/guide/variables#referencing-environment-variables)
|
||||
- [CLI options](https://serverless.com/framework/docs/providers/aws/guide/variables#referencing-cli-options)
|
||||
- [other properties defined in `serverless.yml`](https://serverless.com/framework/docs/providers/aws/guide/variables#reference-properties-in-serverlessyml)
|
||||
- [external YAML/JSON files](https://serverless.com/framework/docs/providers/aws/guide/variables#reference-variables-in-other-files)
|
||||
- [variables from S3](https://serverless.com/framework/docs/providers/aws/guide/variables#referencing-s3-objects)
|
||||
- [variables from AWS SSM Parameter Store](https://serverless.com/framework/docs/providers/aws/guide/variables#reference-variables-using-the-ssm-parameter-store)
|
||||
- [CloudFormation stack outputs](https://serverless.com/framework/docs/providers/aws/guide/variables#reference-cloudformation-outputs)
|
||||
- [properties exported from Javascript files (sync or async)](https://serverless.com/framework/docs/providers/aws/guide/variables#reference-variables-in-javascript-files)
|
||||
|
||||
## Recursively reference properties
|
||||
|
||||
You can also **Recursively reference properties** with the variable system. This means you can combine multiple values and variable sources for a lot of flexibility.
|
||||
|
||||
For example:
|
||||
|
||||
```yml
|
||||
provider:
|
||||
name: aws
|
||||
stage: ${opt:stage, 'dev'}
|
||||
environment:
|
||||
MY_SECRET: ${file(./config.${self:provider.stage}.json):CREDS}
|
||||
```
|
||||
|
||||
If `sls deploy --stage qa` is ran, the option `stage=qa` is used inside the `${file(./config.${self:provider.stage}.json):CREDS}` variable and it will resolve the `config.qa.json` file and use the `CREDS` key defined.
|
||||
|
||||
**How that works:**
|
||||
|
||||
1. stage is set to `qa` from the option supplied to the `sls deploy --stage qa` command
|
||||
2. `${self:provider.stage}` resolves to `qa` and is used in `${file(./config.${self:provider.stage}.json):CREDS}`
|
||||
3. `${file(./config.qa.json):CREDS}` is found & the `CREDS` value is read
|
||||
4. `MY_SECRET` value is set
|
||||
|
||||
Likewise, if `sls deploy --stage prod` is ran the `config.prod.json` file would be found and used.
|
||||
|
||||
If no `--stage` flag is provided, the second parameter defined in `${opt:stage, 'dev'}` a.k.a `dev` will be used and result in `${file(./config.dev.json):CREDS}`.
|
||||
|
||||
## Reference Properties In serverless.yml
|
||||
To self-reference properties in `serverless.yml`, use the `${self:someProperty}` syntax in your `serverless.yml`. `someProperty` can contain the empty string for a top-level self-reference or a dotted attribute reference to any depth of attribute, so you can go as shallow or deep in the object tree as you want.
|
||||
|
||||
@ -205,7 +246,7 @@ In your `serverless.yml`, depending on the type of your source file, either have
|
||||
functions:
|
||||
hello:
|
||||
handler: handler.hello
|
||||
events: ${file(./myCustomFile.yml):myevents
|
||||
events: ${file(./myCustomFile.yml):myevents}
|
||||
```
|
||||
|
||||
or for a JSON reference file use this sytax:
|
||||
@ -214,7 +255,7 @@ or for a JSON reference file use this sytax:
|
||||
functions:
|
||||
hello:
|
||||
handler: handler.hello
|
||||
events: ${file(./myCustomFile.json):myevents
|
||||
events: ${file(./myCustomFile.json):myevents}
|
||||
```
|
||||
|
||||
## Reference Variables in Javascript Files
|
||||
|
||||
@ -61,6 +61,7 @@ If you have questions, join the [chat in gitter](https://gitter.im/serverless/se
|
||||
<li><a href="./cli-reference/plugin-search.md">Plugin Search</a></li>
|
||||
<li><a href="./cli-reference/plugin-install.md">Plugin Install</a></li>
|
||||
<li><a href="./cli-reference/plugin-uninstall.md">Plugin Uninstall</a></li>
|
||||
<li><a href="./cli-reference/print.md">Print</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -28,7 +28,8 @@ serverless create --template azure-nodejs --path myService
|
||||
```
|
||||
|
||||
## Options
|
||||
- `--template` or `-t` The name of one of the available templates. **Required**.
|
||||
- `--template` or `-t` The name of one of the available templates. **Required if --template-url is not present**.
|
||||
- `--template-url` or `-u` The name of one of the available templates. **Required if --template is not present**.
|
||||
- `--path` or `-p` The path where the service should be created.
|
||||
- `--name` or `-n` the name of the service in `serverless.yml`.
|
||||
|
||||
@ -68,3 +69,9 @@ Serverless will use the already present directory.
|
||||
|
||||
Additionally Serverless will rename the service according to the path you
|
||||
provide. In this example the service will be renamed to `my-new-service`.
|
||||
|
||||
### Create service in new folder using a custom template
|
||||
|
||||
```bash
|
||||
serverless create --template-url https://github.com/serverless/serverless/tree/master/lib/plugins/create/templates/azure-nodejs --path myService
|
||||
```
|
||||
|
||||
69
docs/providers/azure/cli-reference/print.md
Normal file
69
docs/providers/azure/cli-reference/print.md
Normal file
@ -0,0 +1,69 @@
|
||||
<!--
|
||||
title: Serverless Framework Commands - Azure - Print
|
||||
menuText: Print
|
||||
menuOrder: 13
|
||||
description: Print your config with all variables resolved for debugging
|
||||
layout: Doc
|
||||
-->
|
||||
|
||||
<!-- DOCS-SITE-LINK:START automatically generated -->
|
||||
### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/azure/cli-reference/print)
|
||||
<!-- DOCS-SITE-LINK:END -->
|
||||
|
||||
# Print
|
||||
|
||||
Print your `serverless.yml` config file with all variables resolved.
|
||||
|
||||
If you're using [Serverless Variables](https://serverless.com/framework/docs/providers/azure/guide/variables/)
|
||||
in your `serverless.yml`, it can be difficult to know if your syntax is correct
|
||||
or if the variables are resolving as you expect.
|
||||
|
||||
With this command, it will print the fully-resolved config to your console.
|
||||
|
||||
```bash
|
||||
serverless print
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
- None
|
||||
|
||||
## Examples:
|
||||
|
||||
Assuming you have the following config file:
|
||||
|
||||
```yml
|
||||
service: new-service
|
||||
provider: azure
|
||||
custom:
|
||||
globalSchedule: cron(0 * * * *)
|
||||
|
||||
functions:
|
||||
hello:
|
||||
handler: handler.hello
|
||||
events:
|
||||
- timer: ${self:custom.globalSchedule}
|
||||
world:
|
||||
handler: handler.world
|
||||
events:
|
||||
- timer: ${self:custom.globalSchedule}
|
||||
```
|
||||
|
||||
Using `sls print` will resolve the variables in the `timer` blocks.
|
||||
|
||||
```bash
|
||||
service: new-service
|
||||
provider: azure
|
||||
custom:
|
||||
globalSchedule: cron(0 * * * *)
|
||||
|
||||
functions:
|
||||
hello:
|
||||
handler: handler.hello
|
||||
events:
|
||||
- timer: cron(0 * * * *) # <-- Resolved
|
||||
world:
|
||||
handler: handler.world
|
||||
events:
|
||||
- timer: cron(0 * * * *) # <-- Resolved
|
||||
```
|
||||
@ -33,7 +33,7 @@ functions:
|
||||
events:
|
||||
- timer:
|
||||
x-azure-settings:
|
||||
name: item #<string>, default - "myQueueItem", specifies which name it's available on `context.bindings`
|
||||
name: timerObj #<string>, default - "myTimer", specifies which name it's available on `context.bindings`
|
||||
schedule: 0 */5 * * * * #<string>, cron expression to run on
|
||||
```
|
||||
|
||||
|
||||
@ -62,6 +62,7 @@ If you have questions, join the [chat in gitter](https://gitter.im/serverless/se
|
||||
<li><a href="./cli-reference/plugin-search.md">Plugin Search</a></li>
|
||||
<li><a href="./cli-reference/plugin-install.md">Plugin Install</a></li>
|
||||
<li><a href="./cli-reference/plugin-uninstall.md">Plugin Uninstall</a></li>
|
||||
<li><a href="./cli-reference/print.md">Print</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -28,7 +28,8 @@ serverless create --template google-nodejs --path my-service
|
||||
|
||||
## Options
|
||||
|
||||
- `--template` or `-t` The name of one of the available templates. **Required**.
|
||||
- `--template` or `-t` The name of one of the available templates. **Required if --template-url is not present**.
|
||||
- `--template-url` or `-u` The name of one of the available templates. **Required if --template is not present**.
|
||||
- `--path` or `-p` The path where the service should be created.
|
||||
- `--name` or `-n` the name of the service in `serverless.yml`.
|
||||
|
||||
@ -59,3 +60,9 @@ serverless create --template google-nodejs --path my-new-service
|
||||
This example will generate scaffolding for a service with `google` as a provider and `nodejs` as runtime. The scaffolding will be generated in the `my-new-service` directory. This directory will be created if not present. Otherwise Serverless will use the already present directory.
|
||||
|
||||
Additionally Serverless will rename the service according to the path you provide. In this example the service will be renamed to `my-new-service`.
|
||||
|
||||
### Create service in new folder using a custom template
|
||||
|
||||
```bash
|
||||
serverless create --template-url https://github.com/serverless/serverless/tree/master/lib/plugins/create/templates/google-nodejs --path myService
|
||||
```
|
||||
|
||||
80
docs/providers/google/cli-reference/print.md
Normal file
80
docs/providers/google/cli-reference/print.md
Normal file
@ -0,0 +1,80 @@
|
||||
<!--
|
||||
title: Serverless Framework Commands - Google Cloud Functions - Print
|
||||
menuText: Print
|
||||
menuOrder: 13
|
||||
description: Print your config with all variables resolved for debugging
|
||||
layout: Doc
|
||||
-->
|
||||
|
||||
<!-- DOCS-SITE-LINK:START automatically generated -->
|
||||
### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/google/cli-reference/print)
|
||||
<!-- DOCS-SITE-LINK:END -->
|
||||
|
||||
# Print
|
||||
|
||||
Print your `serverless.yml` config file with all variables resolved.
|
||||
|
||||
If you're using [Serverless Variables](https://serverless.com/framework/docs/providers/google/guide/variables/)
|
||||
in your `serverless.yml`, it can be difficult to know if your syntax is correct
|
||||
or if the variables are resolving as you expect.
|
||||
|
||||
With this command, it will print the fully-resolved config to your console.
|
||||
|
||||
```bash
|
||||
serverless print
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
- None
|
||||
|
||||
## Examples:
|
||||
|
||||
Assuming you have the following config file:
|
||||
|
||||
```yml
|
||||
service: new-service
|
||||
provider: google
|
||||
|
||||
custom:
|
||||
resource: projects/*/topics/my-topic
|
||||
|
||||
functions:
|
||||
first:
|
||||
handler: firstPubSub
|
||||
events:
|
||||
- event:
|
||||
eventType: providers/cloud.pubsub/eventTypes/topics.publish
|
||||
resource: ${self:custom.resource}
|
||||
second:
|
||||
handler: secondPubSub
|
||||
events:
|
||||
- event:
|
||||
eventType: providers/cloud.pubsub/eventTypes/topics.publish
|
||||
resource: ${self:custom.resource}
|
||||
```
|
||||
|
||||
Using `sls print` will resolve the variables in the `resource` blocks:
|
||||
|
||||
```bash
|
||||
$ sls print
|
||||
service: new-service
|
||||
provider: google
|
||||
|
||||
custom:
|
||||
resource: projects/*/topics/my-topic
|
||||
|
||||
functions:
|
||||
first:
|
||||
handler: firstPubSub
|
||||
events:
|
||||
- event:
|
||||
eventType: providers/cloud.pubsub/eventTypes/topics.publish
|
||||
resource: projects/*/topics/my-topic # <-- Resolved.
|
||||
second:
|
||||
handler: secondPubSub
|
||||
events:
|
||||
- event:
|
||||
eventType: providers/cloud.pubsub/eventTypes/topics.publish
|
||||
resource: projects/*/topics/my-topic # <-- Resolved.
|
||||
```
|
||||
@ -28,7 +28,7 @@ If necessary, a more detailed guide on creating a Billing Account can be found <
|
||||
|
||||
A Google Cloud Project is required to use Google Cloud Functions. Here's how to create one:
|
||||
|
||||
1. Go to the <a href="https://console.cloud.google.com" target="_blank">Google Cloud Console Console</a>.
|
||||
1. Go to the <a href="https://console.cloud.google.com" target="_blank">Google Cloud Console</a>.
|
||||
2. There is a dropdown near the top left of the screen (near the search bar that lists your projects). Click it and select "Create Project".
|
||||
3. Enter a Project name and select the Billing Account you created in the steps above (or any Billing Account with a valid credit card attached).
|
||||
3. Click on "Create" to start the creation process.
|
||||
|
||||
@ -68,6 +68,7 @@ If you have questions, join the [chat in gitter](https://gitter.im/serverless/se
|
||||
<ul>
|
||||
<li><a href="./events/http.md">HTTP Events</a></li>
|
||||
<li><a href="./events/pubsub.md">PubSub Events</a></li>
|
||||
<li><a href="./events/scheduler.md">Scheduled Events</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -35,7 +35,8 @@ serverless create --template kubeless-nodejs --path my-service
|
||||
```
|
||||
|
||||
## Options
|
||||
- `--template` or `-t` The name of one of the available templates. **Required**.
|
||||
- `--template` or `-t` The name of one of the available templates. **Required if --template-url is not present**.
|
||||
- `--template-url` or `-u` The name of one of the available templates. **Required if --template is not present**.
|
||||
- `--path` or `-p` The path where the service should be created.
|
||||
- `--name` or `-n` the name of the service in `serverless.yml`.
|
||||
|
||||
@ -71,4 +72,4 @@ serverless create --template kubeless-python --path my-new-service
|
||||
|
||||
This example will generate scaffolding for a service with `kubeless` as a provider and `python2.7` as runtime. The scaffolding will be generated in the `my-new-service` directory. This directory will be created if not present. Otherwise Serverless will use the already present directory.
|
||||
|
||||
Additionally Serverless will rename the service according to the path you provide. In this example the service will be renamed to `my-new-service`.
|
||||
Additionally Serverless will rename the service according to the path you provide. In this example the service will be renamed to `my-new-service`.
|
||||
|
||||
@ -20,5 +20,8 @@ serverless remove
|
||||
|
||||
It will remove the Kubeless Function objects from your Kubernetes cluster, the Kubernetes Deployments and the Kubernetes Services associated with the Serverless service.
|
||||
|
||||
## Options
|
||||
- `--verbose` or `-v` Shows additional information during the removal.
|
||||
|
||||
## Provided lifecycle events
|
||||
- `remove:remove`
|
||||
|
||||
@ -84,10 +84,6 @@ If the events HTTP definitions contain a `path` attribute, when deploying this S
|
||||
|
||||
```
|
||||
kubectl get ingress
|
||||
NAME HOSTS ADDRESS PORTS AGE
|
||||
ingress-create * 192.168.99.100 80 2m
|
||||
ingress-delete * 192.168.99.100 80 2m
|
||||
ingress-read-all * 192.168.99.100 80 2m
|
||||
ingress-read-one * 192.168.99.100 80 2m
|
||||
ingress-update * 192.168.99.100 80 2m
|
||||
NAME HOSTS ADDRESS PORTS AGE
|
||||
ingress-1506350705094 192.168.99.100.nip.io 80 28s
|
||||
```
|
||||
34
docs/providers/kubeless/events/scheduler.md
Normal file
34
docs/providers/kubeless/events/scheduler.md
Normal file
@ -0,0 +1,34 @@
|
||||
<!--
|
||||
title: Serverless Framework - Kubeless Events - Schedule
|
||||
menuText: Schedule
|
||||
menuOrder: 3
|
||||
description: Scheduled Events in Kubeless
|
||||
layout: Doc
|
||||
-->
|
||||
|
||||
<!-- DOCS-SITE-LINK:START automatically generated -->
|
||||
### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/kubeless/events/schedule)
|
||||
<!-- DOCS-SITE-LINK:END -->
|
||||
|
||||
# Kubeless Scheduled Events
|
||||
|
||||
Kubeless functions can be triggered following a certain schedule. The schedule can be specified events section of the `serverless.yml` following the Cron notation:
|
||||
|
||||
```
|
||||
service: clock
|
||||
|
||||
provider:
|
||||
name: kubeless
|
||||
runtime: nodejs6
|
||||
|
||||
plugins:
|
||||
- serverless-kubeless
|
||||
|
||||
functions:
|
||||
clock:
|
||||
handler: handler.printClock
|
||||
events:
|
||||
- schedule: "* * * * *"
|
||||
```
|
||||
|
||||
When deploying this `serverless.yml` file, Kubeless will create a Kubernetes cron job that will trigger the function `printClock` every minute.
|
||||
@ -76,7 +76,7 @@ Kubeless will create a [Kubernetes Deployment](https://kubernetes.io/docs/concep
|
||||
|
||||
## Deploy Function
|
||||
|
||||
This deployment method updates a single function. It performs the platform API call to deploy your package without the other resources. It is much faster than redeploying your whole service each time.
|
||||
This deployment method updates or deploys a single function. It performs the platform API call to deploy your package without the other resources. It is much faster than redeploying your whole service each time.
|
||||
|
||||
```bash
|
||||
serverless deploy function --function myFunction
|
||||
|
||||
@ -26,7 +26,7 @@ Go to the official [Node.js website](https://nodejs.org), download and follow th
|
||||
|
||||
**Note:** Serverless runs on Node v4 or higher.
|
||||
|
||||
You can verify that Node.js is installed successfully by runnning `node --version` in your terminal. You should see the corresponding Node version number printed out.
|
||||
You can verify that Node.js is installed successfully by running `node --version` in your terminal. You should see the corresponding Node version number printed out.
|
||||
|
||||
## Installing the Serverless Framework
|
||||
|
||||
|
||||
@ -24,7 +24,7 @@ Here are the Serverless Framework's main concepts and how they pertain to Kubele
|
||||
|
||||
### Functions
|
||||
|
||||
A Function is an [Kubeless Function](http://kubeless.io/). It's an independent unit of deployment, like a microservice. It's merely code, deployed in the cloud, that is most often written to perform a single job such as:
|
||||
A Function is a [Kubeless Function](http://kubeless.io/). It's an independent unit of deployment, like a microservice. It's merely code, deployed in the cloud, that is most often written to perform a single job such as:
|
||||
|
||||
* *Saving a user to the database*
|
||||
* *Processing a file in a database*
|
||||
|
||||
@ -67,6 +67,7 @@ If you have questions, join the [chat in gitter](https://gitter.im/serverless/se
|
||||
<li><a href="./cli-reference/plugin-search.md">Plugin Search</a></li>
|
||||
<li><a href="./cli-reference/plugin-install.md">Plugin Install</a></li>
|
||||
<li><a href="./cli-reference/plugin-uninstall.md">Plugin Uninstall</a></li>
|
||||
<li><a href="./cli-reference/print.md">Print</a></li>
|
||||
<li><a href="./cli-reference/slstats.md">Serverless Stats</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@ -27,7 +27,8 @@ serverless create --template openwhisk-nodejs --path myService
|
||||
```
|
||||
|
||||
## Options
|
||||
- `--template` or `-t` The name of one of the available templates. **Required**.
|
||||
- `--template` or `-t` The name of one of the available templates. **Required if --template-url is not present**.
|
||||
- `--template-url` or `-u` The name of one of the available templates. **Required if --template is not present**.
|
||||
- `--path` or `-p` The path where the service should be created.
|
||||
- `--name` or `-n` the name of the service in `serverless.yml`.
|
||||
|
||||
@ -68,6 +69,12 @@ This example will generate scaffolding for a service with `openwhisk` as a provi
|
||||
|
||||
Additionally Serverless will rename the service according to the path you provide. In this example the service will be renamed to `my-new-service`.
|
||||
|
||||
### Create service in new folder using a custom template
|
||||
|
||||
```bash
|
||||
serverless create --template-url https://github.com/serverless/serverless/tree/master/lib/plugins/create/templates/openwhisk-nodejs --path myService
|
||||
```
|
||||
|
||||
### Creating a new plugin
|
||||
|
||||
```
|
||||
|
||||
70
docs/providers/openwhisk/cli-reference/print.md
Normal file
70
docs/providers/openwhisk/cli-reference/print.md
Normal file
@ -0,0 +1,70 @@
|
||||
<!--
|
||||
title: Serverless Framework Commands - Apache OpenWhisk - Print
|
||||
menuText: Print
|
||||
menuOrder: 16
|
||||
description: Print your config with all variables resolved for debugging
|
||||
layout: Doc
|
||||
-->
|
||||
|
||||
<!-- DOCS-SITE-LINK:START automatically generated -->
|
||||
### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/cli-reference/print)
|
||||
<!-- DOCS-SITE-LINK:END -->
|
||||
|
||||
# Print
|
||||
|
||||
Print your `serverless.yml` config file with all variables resolved.
|
||||
|
||||
If you're using [Serverless Variables](https://serverless.com/framework/docs/providers/openwhisk/guide/variables/)
|
||||
in your `serverless.yml`, it can be difficult to know if your syntax is correct
|
||||
or if the variables are resolving as you expect.
|
||||
|
||||
With this command, it will print the fully-resolved config to your console.
|
||||
|
||||
```bash
|
||||
serverless print
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
- None
|
||||
|
||||
## Examples:
|
||||
|
||||
Assuming you have the following config file:
|
||||
|
||||
```yml
|
||||
service: new-service
|
||||
provider: openwhisk
|
||||
custom:
|
||||
globalSchedule: cron(0 * * * *)
|
||||
|
||||
functions:
|
||||
hello:
|
||||
handler: handler.hello
|
||||
events:
|
||||
- schedule: ${self:custom.globalSchedule}
|
||||
world:
|
||||
handler: handler.world
|
||||
events:
|
||||
- schedule: ${self:custom.globalSchedule}
|
||||
```
|
||||
|
||||
Using `sls print` will resolve the variables in the `schedule` blocks.
|
||||
|
||||
```bash
|
||||
$ sls print
|
||||
service: new-service
|
||||
provider: openwhisk
|
||||
custom:
|
||||
globalSchedule: cron(0 * * * *)
|
||||
|
||||
functions:
|
||||
hello:
|
||||
handler: handler.hello
|
||||
events:
|
||||
- schedule: cron(0 * * * *) # <-- Resolved
|
||||
world:
|
||||
handler: handler.world
|
||||
events:
|
||||
- schedule: cron(0 * * * *) # <-- Resolved
|
||||
```
|
||||
@ -1,7 +1,7 @@
|
||||
<!--
|
||||
title: Serverless Framework Commands - Apache OpenWhisk - Serverless Stats
|
||||
menuText: serverless stats
|
||||
menuOrder: 16
|
||||
menuOrder: 17
|
||||
description: Enables or disables Serverless Statistic logging within the Serverless Framework.
|
||||
layout: Doc
|
||||
-->
|
||||
|
||||
@ -111,8 +111,15 @@ functions:
|
||||
- http:
|
||||
path: posts/create
|
||||
method: post
|
||||
resp: json
|
||||
```
|
||||
|
||||
HTTP event configuration supports the following parameters.
|
||||
|
||||
- `method` - HTTP method (mandatory).
|
||||
- `path` - URI path for API gateway (mandatory).
|
||||
- `resp` - controls [web action content type](https://github.com/apache/incubator-openwhisk/blob/master/docs/webactions.md#additional-features), values include: `json`, `html`, `http`, `svg`or `text` (optional, defaults to `json`).
|
||||
|
||||
### CORS Support
|
||||
|
||||
**Note:** All HTTP endpoints defined in this manner have cross-site requests
|
||||
|
||||
@ -13,7 +13,7 @@ layout: Doc
|
||||
1. Node.js `v6.5.0` or later.
|
||||
2. Serverless CLI `v1.9.0` or later. You can run
|
||||
`npm install -g serverless` to install it.
|
||||
3. An IBM Bluemix account. If you don't already have one, you can sign up for an [account](https://aws.amazon.com/s/dm/optimization/server-side-test/free-tier/free_np/) and then follow the instructions for getting access to [OpenWhisk on Bluemix](https://console.ng.bluemix.net/openwhisk/).
|
||||
3. An IBM Bluemix account. If you don't already have one, you can sign up for an [account](https://console.bluemix.net/registration/) and then follow the instructions for getting access to [OpenWhisk on Bluemix](https://console.ng.bluemix.net/openwhisk/).
|
||||
4. **Set-up your [Provider Credentials](./credentials.md)**.
|
||||
5. Install Framework & Dependencies
|
||||
*Due to an [outstanding issue](https://github.com/serverless/serverless/issues/2895) with provider plugins, the [OpenWhisk provider](https://github.com/serverless/serverless-openwhisk) must be installed as a global module.*
|
||||
|
||||
@ -14,28 +14,74 @@ Welcome to the Serverless Spotinst documentation!
|
||||
|
||||
If you have questions, join the [chat in gitter](https://gitter.im/serverless/serverless) or [post over on the forums](https://forum.serverless.com/)
|
||||
|
||||
## Getting Started
|
||||
<div class="docsSections">
|
||||
<div class="docsSection">
|
||||
<div class="docsSectionHeader">
|
||||
<a href="./guide/">
|
||||
<img src="https://s3.amazonaws.com/spotinst-public/assets/serverless-docs/functions_guide.jpg" alt="Serverless Spotinst Guide" width="250" draggable="false"/>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<ul>
|
||||
<li><a href="./guide/intro.md">Intro</a></li>
|
||||
<li><a href="./guide/quick-start.md">Quick Start</a></li>
|
||||
<li><a href="./guide/create-token.md">Create Token</a></li>
|
||||
<li><a href="./guide/credentials.md">Credentials</a></li>
|
||||
<li><a href="./guide/serverless.yml.md">Serverless.yml Reference</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="./guide">Get started here by reading the guide</a>
|
||||
<div class="docsSection">
|
||||
<div class="docsSectionHeader">
|
||||
<a href="./cli-reference/">
|
||||
<img src="https://s3.amazonaws.com/spotinst-public/assets/serverless-docs/functions_cli.jpg" alt="Serverless Spotinst CLI Reference" width="250" draggable="false"/>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<ul>
|
||||
<li><a href="./cli-reference/config-credentials.md">Config Credentials</a></li>
|
||||
<li><a href="./cli-reference/create.md">Create</a></li>
|
||||
<li><a href="./cli-reference/deploy.md">Deploy</a></li>
|
||||
<li><a href="./cli-reference/deploy-function.md">Deploy Function</a></li>
|
||||
<li><a href="./cli-reference/invoke.md">Invoke</a></li>
|
||||
<li><a href="./cli-reference/logs.md">Logs</a></li>
|
||||
<li><a href="./cli-reference/info.md">Info</a></li>
|
||||
<li><a href="./cli-reference/remove.md">Remove</a></li>
|
||||
<li><a href="./cli-reference/plugin-list.md">Plugin List</a></li>
|
||||
<li><a href="./cli-reference/plugin-search.md">Plugin Search</a></li>
|
||||
<li><a href="./cli-reference/plugin-install.md">Plugin Install</a></li>
|
||||
<li><a href="./cli-reference/plugin-uninstall.md">Plugin Uninstall</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## CLI reference
|
||||
<div class="docsSection">
|
||||
<div class="docsSectionHeader">
|
||||
<a href="./events/">
|
||||
<img src="https://s3.amazonaws.com/spotinst-public/assets/serverless-docs/functions_+events.jpg" alt="Serverless Spotinst Events" width="250" draggable="false"/>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<ul>
|
||||
<li><a href="./events/http.md">HTTP</a></li>
|
||||
<li><a href="./events/schedule.md">Schedule</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul>
|
||||
<li><a href="./cli-reference/config-credentials.md">Config Credentials</a></li>
|
||||
<li><a href="./cli-reference/create.md">Create</a></li>
|
||||
<li><a href="./cli-reference/deploy.md">Deploy</a></li>
|
||||
<li><a href="./cli-reference/invoke.md">Invoke</a></li>
|
||||
<li><a href="./cli-reference/logs.md">Logs</a></li>
|
||||
<li><a href="./cli-reference/info.md">Info</a></li>
|
||||
<li><a href="./cli-reference/remove.md">Remove</a></li>
|
||||
<li><a href="./cli-reference/plugin-list.md">Plugin List</a></li>
|
||||
<li><a href="./cli-reference/plugin-search.md">Plugin Search</a></li>
|
||||
<li><a href="./cli-reference/plugin-install.md">Plugin Install</a></li>
|
||||
<li><a href="./cli-reference/plugin-uninstall.md">Plugin Uninstall</a></li>
|
||||
</ul>
|
||||
|
||||
## Supported Events
|
||||
<ul>
|
||||
<li><a href="./events/http.md">http event</a></li>
|
||||
<li><a href="./events/schedule.md">Schedule event</a></li>
|
||||
</ul>
|
||||
<div class="docsSection">
|
||||
<div class="docsSectionHeader">
|
||||
<a href="./examples/">
|
||||
<img src="https://s3.amazonaws.com/spotinst-public/assets/serverless-docs/functions_examples.jpg" alt="Serverless Spotinst Examples" width="250" draggable="false"/>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<ul>
|
||||
<li><a href="./examples/">Hello World</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -27,7 +27,8 @@ serverless create -t spotinst-nodejs -p myService
|
||||
```
|
||||
|
||||
## Options
|
||||
- `--template` or `-t` The name of one of the available templates. **Required**.
|
||||
- `--template` or `-t` The name of one of the available templates. **Required if --template-url is not present**.
|
||||
- `--template-url` or `-u` The name of one of the available templates. **Required if --template is not present**.
|
||||
- `--path` or `-p` The path where the service should be created.
|
||||
- `--name` or `-n` the name of the service in `serverless.yml`.
|
||||
|
||||
|
||||
@ -33,6 +33,10 @@ functions:
|
||||
handler: handler.main
|
||||
memory: 128
|
||||
timeout: 30
|
||||
access: private
|
||||
# cron: # Setup scheduled trigger with cron expression
|
||||
# active: true
|
||||
# value: '* * * * *'
|
||||
|
||||
# extend the framework using plugins listed here:
|
||||
# https://github.com/serverless/plugins
|
||||
|
||||
@ -12,18 +12,8 @@ layout: Doc
|
||||
|
||||
# HTTP
|
||||
|
||||
Spotinst Functions can be triggered by an HTTP endpoint. To create HTTP endpoints as event sources for your Spotinst Functions, use the `http` event syntax.
|
||||
Spotinst Functions are automatically given an HTTP endpoint when they are created. This means that you do not need to specify the event type when writing your function. After you deploy your function for the first time a unique URL is generated based on the application ID, environment where your application is launched, and the function ID. Here is a sample of how the URL is created
|
||||
|
||||
This setup specifies that the `first` function should be run when someone accesses the Functions API endpoint via a `GET` request. You can get the URL for the endpoint by running the `serverless info` command after deploying your service.
|
||||
`https://{app id}{environment id}.spotinst.io/{function id}`
|
||||
|
||||
Here's an example:
|
||||
|
||||
```yml
|
||||
# serverless.yml
|
||||
|
||||
functions:
|
||||
first:
|
||||
handler: http
|
||||
events:
|
||||
- http: path
|
||||
```
|
||||
For information on your application ID, environment ID and function ID please checkout your Spotinst Functions dashboard on the [Spotinst website](https://console.spotinst.com/#/dashboard)
|
||||
@ -12,14 +12,38 @@ layout: Doc
|
||||
|
||||
# Schedule
|
||||
|
||||
You can trigger the functions by using a scheduled event. This will execute the function according to the cron expressions you specify
|
||||
You can trigger the functions by using a scheduled event. This will execute the function according to the cron expressions you specify.
|
||||
|
||||
You can either use the `rate` or `cron` syntax.
|
||||
You can use `cron` syntax.
|
||||
|
||||
The following example is a function configuration in the serverless.yml file that are scheduled to trigger the function crawl every day at 6:30 PM.
|
||||
|
||||
```yml
|
||||
functions:
|
||||
crawl:
|
||||
handler: crawl
|
||||
events:
|
||||
- schedule: cron(0 12 * * ? *)
|
||||
handler: handler.crawl
|
||||
cron: # Setup scheduled trigger with cron expression
|
||||
active: true
|
||||
value: '30 18 * * *'
|
||||
```
|
||||
|
||||
|
||||
## Active Status
|
||||
|
||||
You also have the option to set your functions active status as either true or false
|
||||
|
||||
**Note** `schedule` events active status are set to true by default
|
||||
|
||||
This example will create and attach a schedule event for the function `crawl` which is active status is set to `false`. If the status is changed to true the `crawl` function will be called every Monday at 6:00 PM.
|
||||
|
||||
```yml
|
||||
functions:
|
||||
crawl:
|
||||
handler: handler.crawl
|
||||
cron: # Setup scheduled trigger with cron expression
|
||||
active: false
|
||||
value: '* 18 * * 1'
|
||||
|
||||
```
|
||||
|
||||
**Note** When creating a `cron` trigger the `value` is the crontab expression. For help on crontab check out the [documentation](http://www.adminschoice.com/crontab-quick-reference)
|
||||
|
||||
18
docs/providers/spotinst/examples/README.md
Normal file
18
docs/providers/spotinst/examples/README.md
Normal file
@ -0,0 +1,18 @@
|
||||
<!--
|
||||
title: Hello World Example
|
||||
menuText: Hello World Example
|
||||
description: Example of creating a Hello World function in Node.js and Python with the Serverless framework
|
||||
layout: Doc
|
||||
-->
|
||||
|
||||
<!-- DOCS-SITE-LINK:START automatically generated -->
|
||||
### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/aws/examples/hello-world/)
|
||||
<!-- DOCS-SITE-LINK:END -->
|
||||
|
||||
# Hello World Serverless Example
|
||||
|
||||
Pick your language of choice:
|
||||
|
||||
* [JavaScript](./node)
|
||||
* [Python](./python)
|
||||
* [Ruby](./ruby)
|
||||
48
docs/providers/spotinst/examples/node/README.md
Normal file
48
docs/providers/spotinst/examples/node/README.md
Normal file
@ -0,0 +1,48 @@
|
||||
<!--
|
||||
title: Hello World Javascript Example
|
||||
menuText: Hello World JavaScript Example
|
||||
description: Create a JavaScript Hello World function
|
||||
layout: Doc
|
||||
-->
|
||||
|
||||
<!-- DOCS-SITE-LINK:START automatically generated -->
|
||||
### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/spotinst/)
|
||||
<!-- DOCS-SITE-LINK:END -->
|
||||
|
||||
# Hello World JavaScript Example
|
||||
|
||||
Make sure `serverless` is installed.
|
||||
|
||||
## 1. Create a service
|
||||
`serverless create --template spotinst-nodejs --path serviceName` `serviceName` is going to be a new directory there the python template will be loaded. Once the download is complete change into that directory. Next you will need to install the Spotinst Serverless Functions plugin by running `npm install` in the root directory
|
||||
|
||||
## 2. Deploy
|
||||
`serverless deploy`
|
||||
|
||||
## 3. Invoke deployed function
|
||||
`serverless invoke --function hello`
|
||||
|
||||
In your terminal window you should see the response
|
||||
|
||||
```bash
|
||||
{
|
||||
Deploy functions:
|
||||
hello: created
|
||||
Service Information
|
||||
service: spotinst-python
|
||||
functions:
|
||||
hello
|
||||
}
|
||||
```
|
||||
|
||||
Congrats you have just deployed and ran your Hello World function!
|
||||
|
||||
## Short Hand Guide
|
||||
|
||||
`sls` is short hand for serverless cli commands
|
||||
|
||||
`-f` is short hand for `--function`
|
||||
|
||||
`-t` is short hand for `--template`
|
||||
|
||||
`-p` is short hang for `--path`
|
||||
49
docs/providers/spotinst/examples/python/README.md
Normal file
49
docs/providers/spotinst/examples/python/README.md
Normal file
@ -0,0 +1,49 @@
|
||||
<!--
|
||||
title: Hello World Python Example
|
||||
menuText: Hello World Python Example
|
||||
description: Create a Python Hello World function
|
||||
layout: Doc
|
||||
-->
|
||||
|
||||
<!-- DOCS-SITE-LINK:START automatically generated -->
|
||||
### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/spotinst/)
|
||||
<!-- DOCS-SITE-LINK:END -->
|
||||
|
||||
# Hello World Python Example
|
||||
|
||||
Make sure `serverless` is installed.
|
||||
|
||||
## 1. Create a service
|
||||
`serverless create --template spotinst-python --path serviceName` `serviceName` is going to be a new directory there the python template will be loaded. Once the download is complete change into that directory. Next you will need to install the Spotinst Serverless Functions plugin by running `npm install` in the root directory
|
||||
|
||||
|
||||
## 2. Deploy
|
||||
`serverless deploy`
|
||||
|
||||
## 3. Invoke deployed function
|
||||
`serverless invoke --function hello`
|
||||
|
||||
In your terminal window you should see the response
|
||||
|
||||
```bash
|
||||
{
|
||||
Deploy functions:
|
||||
hello: created
|
||||
Service Information
|
||||
service: spotinst-python
|
||||
functions:
|
||||
hello
|
||||
}
|
||||
```
|
||||
|
||||
Congrats you have just deployed and ran your Hello World function!
|
||||
|
||||
## Short Hand Guide
|
||||
|
||||
`sls` is short hand for serverless cli commands
|
||||
|
||||
`-f` is short hand for `--function`
|
||||
|
||||
`-t` is short hand for `--template`
|
||||
|
||||
`-p` is short hang for `--path`
|
||||
49
docs/providers/spotinst/examples/ruby/README.md
Normal file
49
docs/providers/spotinst/examples/ruby/README.md
Normal file
@ -0,0 +1,49 @@
|
||||
<!--
|
||||
title: Hello World Ruby Example
|
||||
menuText: Hello World Ruby Example
|
||||
description: Create a Ruby Hello World function
|
||||
layout: Doc
|
||||
-->
|
||||
|
||||
<!-- DOCS-SITE-LINK:START automatically generated -->
|
||||
### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/spotinst/)
|
||||
<!-- DOCS-SITE-LINK:END -->
|
||||
|
||||
# Hello World Ruby Example
|
||||
|
||||
Make sure `serverless` is installed.
|
||||
|
||||
## 1. Create a service
|
||||
`serverless create --template spotinst-ruby --path serviceName` `serviceName` is going to be a new directory there the python template will be loaded. Once the download is complete change into that directory. Next you will need to install the Spotinst Serverless Functions plugin by running `npm install` in the root directory
|
||||
|
||||
|
||||
## 2. Deploy
|
||||
`serverless deploy`
|
||||
|
||||
## 3. Invoke deployed function
|
||||
`serverless invoke --function hello`
|
||||
|
||||
In your terminal window you should see the response
|
||||
|
||||
```bash
|
||||
{
|
||||
Deploy functions:
|
||||
hello: created
|
||||
Service Information
|
||||
service: spotinst-ruby
|
||||
functions:
|
||||
hello
|
||||
}
|
||||
```
|
||||
|
||||
Congrats you have just deployed and ran your Hello World function!
|
||||
|
||||
## Short Hand Guide
|
||||
|
||||
`sls` is short hand for serverless cli commands
|
||||
|
||||
`-f` is short hand for `--function`
|
||||
|
||||
`-t` is short hand for `--template`
|
||||
|
||||
`-p` is short hang for `--path`
|
||||
72
docs/providers/spotinst/guide/create-token.md
Normal file
72
docs/providers/spotinst/guide/create-token.md
Normal file
@ -0,0 +1,72 @@
|
||||
<!--
|
||||
title: Serverless Framework - Spotinst Functions Guide - Create Token
|
||||
menuText: Create Token
|
||||
menuOrder: 3
|
||||
description: How to set up the Serverless Framework with your Spotinst Token
|
||||
layout: Doc
|
||||
-->
|
||||
|
||||
<!-- DOCS-SITE-LINK:START automatically generated -->
|
||||
### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/spotinst/guide/credentials)
|
||||
<!-- DOCS-SITE-LINK:END -->
|
||||
|
||||
# Spotinst Functions - Create Token
|
||||
|
||||
The Serverless Framework needs access to your Spotinst account so that it can create and manage resources on your behalf. To do this you will need either a permanent or tempary token that is linked to your account
|
||||
|
||||
## Create a Permanent Token
|
||||
|
||||
You can generate a Permanent Token from the [Spotinst Console](https://console.spotinst.com/#/settings/tokens/permanent).
|
||||
|
||||
> `WARNING`: Do not share your personal access token or your application secret with anyone outside your organization. Please contact our support if you’re concerned your token has been compromised.
|
||||
|
||||
## Temporary Access Token
|
||||
You can also generate a the temporary access token, which is only valid for 2 hours (7200 seconds).
|
||||
|
||||
You can generate a temporary token from the [Spotinst Console](https://console.spotinst.com/#/settings/tokens/temporary). Or, using the below command:
|
||||
|
||||
```bash
|
||||
$ curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'username=<USERNAME>&password=<PASSWORD>&grant_type=password&client_id=<CLIENT_ID>&client_secret=<CLIENT_SECRET>' https://oauth.spotinst.io/token
|
||||
```
|
||||
|
||||
Replace the following parameters, more info can be found [here](https://console.spotinst.com/#/settings/tokens/temporary)
|
||||
- `<USERNAME>`
|
||||
- `<PASSWORD>`
|
||||
- `<CLIENT_ID>`
|
||||
- `<CLIENT_SECRET>`
|
||||
|
||||
The request will return two tokens:
|
||||
```json
|
||||
{
|
||||
"request": {
|
||||
"id": "a2285a3f-4950-4874-a931-1ee1cdf33012",
|
||||
"url": "/token",
|
||||
"method": "POST",
|
||||
"timestamp": "2017-08-30T22:00:34.610Z"
|
||||
},
|
||||
"response": {
|
||||
"status": {
|
||||
"code": 200,
|
||||
"message": "OK"
|
||||
},
|
||||
"kind": "spotinst:oauth2:token",
|
||||
"items": [
|
||||
{
|
||||
"accessToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJzcG90aW5zdCIsInVpZCI6LTgsIm9pZCI6NjA2MDc5ODYxOTExLCJyb2xlIjoyLCJleHAiOjE1MDQxMzc2MzQsImlhdCI6MTUwNDEzMDQzNH0.xyax",
|
||||
"tokenType": "bearer",
|
||||
"expiresIn": 7199
|
||||
}
|
||||
],
|
||||
"count": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* *accessToken* - Use this token when making calls to Spotinst API
|
||||
* *refreshToken* - Use this token in order to refresh the temporary token. This will return a new token that is valid for additional 2 hours:
|
||||
|
||||
|
||||
```bash
|
||||
$ curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'refresh_token=<REFRESH_TOKEN>&grant_type=refresh_token&client_id=<CLIENT_ID>&client_secret=<CLIENT_SECRET>' https://api.spotinst.io/token
|
||||
```
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<!--
|
||||
title: Serverless Framework - Spotinst Functions Guide - Credentials
|
||||
menuText: Credentials
|
||||
menuOrder: 3
|
||||
menuOrder: 4
|
||||
description: How to set up the Serverless Framework with your Spotinst Functions credentials
|
||||
layout: Doc
|
||||
-->
|
||||
@ -12,60 +12,32 @@ layout: Doc
|
||||
|
||||
# Spotinst Functions - Credentials
|
||||
|
||||
The Serverless Framework needs access to your Spotinst account so that it can create and manage resources on your behalf.
|
||||
The Serverless Framework needs access to your Spotinst account so that it can create and manage resources on your behalf. Please make sure you have created and saved your Permanent or Temporary Token before continuing.
|
||||
|
||||
## Create a Permanent Token
|
||||
## Configure Credentials
|
||||
|
||||
You can generate a Permanent Token from the [Spotinst Console](https://console.spotinst.com/#/settings/tokens/permanent).
|
||||
You will need to have your account ID number and your account token ready. Your account ID can be found on the Spotinst console and your token can be generated by following the [Create Token Guide](./create-token.md).
|
||||
|
||||
> `WARNING`: Do not share your personal access token or your application secret with anyone outside your organization. Please contact our support if you’re concerned your token has been compromised.
|
||||
In order to run the config credentials command from the terminal you will need to start a new Spotinst project and install the plugin. First you will need to run the create a new project using the Spotinst template. To do this run:
|
||||
|
||||
## Temporary Access Token
|
||||
You can also generate a the temporary access token, which is only valid for 2 hours (7200 seconds).
|
||||
|
||||
You can generate a temporary token from the [Spotinst Console](https://console.spotinst.com/#/settings/tokens/temporary). Or, using the below command:
|
||||
|
||||
```bash
|
||||
$ curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'username=<USERNAME>&password=<PASSWORD>&grant_type=password&client_id=<CLIENT_ID>&client_secret=<CLIENT_SECRET>' https://oauth.spotinst.io/token
|
||||
```
|
||||
serverless create -t spotinst-nodejs -p new-function
|
||||
```
|
||||
|
||||
Replace the following parameters, more info can be found [here](https://console.spotinst.com/#/settings/tokens/temporary)
|
||||
- `<USERNAME>`
|
||||
- `<PASSWORD>`
|
||||
- `<CLIENT_ID>`
|
||||
- `<CLIENT_SECRET>`
|
||||
Then navigate to the directory that was just created `new-function` and install the Spotinst Serverless Plugin by running the `npm install` command.
|
||||
|
||||
The request will return two tokens:
|
||||
```json
|
||||
{
|
||||
"request": {
|
||||
"id": "a2285a3f-4950-4874-a931-1ee1cdf33012",
|
||||
"url": "/token",
|
||||
"method": "POST",
|
||||
"timestamp": "2017-08-30T22:00:34.610Z"
|
||||
},
|
||||
"response": {
|
||||
"status": {
|
||||
"code": 200,
|
||||
"message": "OK"
|
||||
},
|
||||
"kind": "spotinst:oauth2:token",
|
||||
"items": [
|
||||
{
|
||||
"accessToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJzcG90aW5zdCIsInVpZCI6LTgsIm9pZCI6NjA2MDc5ODYxOTExLCJyb2xlIjoyLCJleHAiOjE1MDQxMzc2MzQsImlhdCI6MTUwNDEzMDQzNH0.xyax",
|
||||
"tokenType": "bearer",
|
||||
"expiresIn": 7199
|
||||
}
|
||||
],
|
||||
"count": 1
|
||||
}
|
||||
}
|
||||
Once this has completed you will be able to configure your credentials by running
|
||||
|
||||
```
|
||||
serverless config credentials -p spotinst -k {your account number} -t {your token}
|
||||
```
|
||||
|
||||
* *accessToken* - Use this token when making calls to Spotinst API
|
||||
* *refreshToken* - Use this token in order to refresh the temporary token. This will return a new token that is valid for additional 2 hours:
|
||||
This will create a ~/.spotinst/credentials file the file should look like this when if done correctly:
|
||||
|
||||
```
|
||||
default:
|
||||
token: {your token}
|
||||
account: {your account number}
|
||||
```
|
||||
|
||||
```bash
|
||||
$ curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'refresh_token=<REFRESH_TOKEN>&grant_type=refresh_token&client_id=<CLIENT_ID>&client_secret=<CLIENT_SECRET>' https://api.spotinst.io/token
|
||||
```
|
||||
After this is set up properly you will be able to deploy functions from your computer and monitor them on the Spotinst Console.
|
||||
|
||||
@ -92,6 +92,10 @@ functions:
|
||||
handler: handler.main
|
||||
memory: 128
|
||||
timeout: 30
|
||||
# access: private
|
||||
# cron: # Setup scheduled trigger with cron expression
|
||||
# active: true
|
||||
# value: '* * * * *'
|
||||
|
||||
```
|
||||
When you deploy with the Framework by running `serverless deploy`, everything in `serverless.yml` is deployed at once.
|
||||
@ -105,4 +109,4 @@ You can overwrite or extend the functionality of the Framework using **Plugins**
|
||||
plugins:
|
||||
- serverless-spotinst-functions
|
||||
|
||||
```
|
||||
```
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<!--
|
||||
title: Serverless Framework - Spotinst Guide - Quick Start
|
||||
menuText: Quick Start
|
||||
menuOrder: 1
|
||||
menuOrder: 2
|
||||
description: Getting started with the Serverless Framework on AWS Lambda
|
||||
layout: Doc
|
||||
-->
|
||||
|
||||
51
docs/providers/spotinst/guide/serverless.yml.md
Normal file
51
docs/providers/spotinst/guide/serverless.yml.md
Normal file
@ -0,0 +1,51 @@
|
||||
<!--
|
||||
title: Serverless Framework - Spotinst Guide - Serverless.yml Reference
|
||||
menuText: Serverless.yml
|
||||
menuOrder: 5
|
||||
description: Serverless.yml reference
|
||||
layout: Doc
|
||||
-->
|
||||
# Serverless.yml Reference
|
||||
|
||||
This is an outline of a `serverless.yml` file with descriptions of the properties for reference
|
||||
|
||||
```yml
|
||||
# serverless.yml
|
||||
|
||||
# The service can be whatever you choose. You can have multiple functions
|
||||
# under one service
|
||||
|
||||
service: your-service
|
||||
|
||||
# The provider is Spotinst and the Environment ID can be found on the
|
||||
# Spotinst Console under Functions
|
||||
|
||||
provider:
|
||||
name: spotinst
|
||||
spotinst:
|
||||
environment: #{Your Environment ID}
|
||||
|
||||
# Here is where you will list your functions for this service. Each Function is
|
||||
# required to have a name, runtime, handler, memory and timeout. The runtime is
|
||||
# the language that you want to run your function with, the handler tells which
|
||||
# file and function to run, memory is the amount of memory needed to run your
|
||||
# function, timeout is the time the function will take to run, if it goes over
|
||||
# this time it will terminate itself. Access is default set to private so if you
|
||||
# want to be able to run the function by HTTPS request this needs to be set to
|
||||
# public. For information on cron functions read the post here.
|
||||
|
||||
functions:
|
||||
function-name:
|
||||
runtime: nodejs4.8
|
||||
handler: handler.main
|
||||
memory: 128
|
||||
timeout: 30
|
||||
# access: public
|
||||
# cron:
|
||||
# active: false
|
||||
# value: '*/1 * * * *'
|
||||
|
||||
|
||||
plugins:
|
||||
- serverless-spotinst-functions
|
||||
```
|
||||
@ -28,7 +28,8 @@ serverless create --template webtasks-nodejs --path my-service
|
||||
|
||||
## Options
|
||||
|
||||
- `--template` or `-t` The name of one of the available templates. **Required**.
|
||||
- `--template` or `-t` The name of one of the available templates. **Required if --template-url is not present**.
|
||||
- `--template-url` or `-u` The name of one of the available templates. **Required if --template is not present**.
|
||||
- `--path` or `-p` The path where the service should be created.
|
||||
- `--name` or `-n` the name of the service in `serverless.yml`.
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ layout: Doc
|
||||
|
||||
The Serverless Framework helps you develop and deploy serverless applications using Auth0 Webtasks. The Serverless CLI offers structure, automation and best practices out-of-the-box. And with Auth0 Webtasks it's simple and easy to deploy code in just seconds.
|
||||
|
||||
**Note:** A local profile is required to use Auth0 Webtasks with the Serverless Framework. Follow the steps in the [Quick Start](../quick-start.md) to get setup in less than a minute.
|
||||
**Note:** A local profile is required to use Auth0 Webtasks with the Serverless Framework. Follow the steps in the [Quick Start](quick-start.md) to get setup in less than a minute.
|
||||
|
||||
## Core Concepts
|
||||
|
||||
|
||||
@ -90,17 +90,23 @@ class PluginManager {
|
||||
|
||||
this.addPlugin(Plugin);
|
||||
} catch (error) {
|
||||
// Rethrow the original error in case we're in debug mode.
|
||||
if (process.env.SLS_DEBUG) {
|
||||
throw error;
|
||||
let errorMessage;
|
||||
if (error && error.code === 'MODULE_NOT_FOUND' && error.message.endsWith(`'${plugin}'`)) {
|
||||
// Plugin not installed
|
||||
errorMessage = [
|
||||
`Serverless plugin "${plugin}" not found.`,
|
||||
' Make sure it\'s installed and listed in the "plugins" section',
|
||||
' of your serverless config file.',
|
||||
].join('');
|
||||
} else {
|
||||
// Plugin initialization error
|
||||
// Rethrow the original error in case we're in debug mode.
|
||||
if (process.env.SLS_DEBUG) {
|
||||
throw error;
|
||||
}
|
||||
errorMessage =
|
||||
`Serverless plugin "${plugin}" initialization errored: ${error.message}`;
|
||||
}
|
||||
|
||||
const errorMessage = [
|
||||
`Serverless plugin "${plugin}" not found.`,
|
||||
' Make sure it\'s installed and listed in the "plugins" section',
|
||||
' of your serverless config file.',
|
||||
].join('');
|
||||
|
||||
if (!this.cliOptions.help) {
|
||||
throw new this.serverless.classes.Error(errorMessage);
|
||||
}
|
||||
|
||||
@ -8,8 +8,10 @@ const fse = BbPromise.promisifyAll(require('fs-extra'));
|
||||
const _ = require('lodash');
|
||||
const fileExistsSync = require('../utils/fs/fileExistsSync');
|
||||
const writeFileSync = require('../utils/fs/writeFileSync');
|
||||
const copyDirContentsSync = require('../utils/fs/copyDirContentsSync');
|
||||
const readFileSync = require('../utils/fs/readFileSync');
|
||||
const walkDirSync = require('../utils/fs/walkDirSync');
|
||||
const dirExistsSync = require('../utils/fs/dirExistsSync');
|
||||
const isDockerContainer = require('is-docker');
|
||||
const version = require('../../package.json').version;
|
||||
const segment = require('../utils/segment');
|
||||
@ -25,12 +27,7 @@ class Utils {
|
||||
}
|
||||
|
||||
dirExistsSync(dirPath) {
|
||||
try {
|
||||
const stats = fse.statSync(dirPath);
|
||||
return stats.isDirectory();
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return dirExistsSync(dirPath);
|
||||
}
|
||||
|
||||
fileExistsSync(filePath) {
|
||||
@ -92,12 +89,7 @@ class Utils {
|
||||
}
|
||||
|
||||
copyDirContentsSync(srcDir, destDir) {
|
||||
const fullFilesPaths = this.walkDirSync(srcDir);
|
||||
|
||||
fullFilesPaths.forEach(fullFilePath => {
|
||||
const relativeFilePath = fullFilePath.replace(srcDir, '');
|
||||
fse.copySync(fullFilePath, path.join(destDir, relativeFilePath));
|
||||
});
|
||||
return copyDirContentsSync(srcDir, destDir);
|
||||
}
|
||||
|
||||
generateShortId(length) {
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
"./login/login.js",
|
||||
"./logout/logout.js",
|
||||
"./metrics/metrics.js",
|
||||
"./print/print.js",
|
||||
"./remove/remove.js",
|
||||
"./rollback/index.js",
|
||||
"./slstats/slstats.js",
|
||||
@ -40,6 +41,7 @@
|
||||
"./aws/package/compile/events/sns/index.js",
|
||||
"./aws/package/compile/events/stream/index.js",
|
||||
"./aws/package/compile/events/alexaSkill/index.js",
|
||||
"./aws/package/compile/events/alexaSmartHome/index.js",
|
||||
"./aws/package/compile/events/iot/index.js",
|
||||
"./aws/package/compile/events/cloudWatchEvent/index.js",
|
||||
"./aws/package/compile/events/cloudWatchLog/index.js",
|
||||
|
||||
@ -11,6 +11,7 @@ const testUtils = require('../../../../../tests/utils');
|
||||
|
||||
describe('createStack', () => {
|
||||
let awsDeploy;
|
||||
let sandbox;
|
||||
const tmpDirPath = testUtils.getTmpDirPath();
|
||||
|
||||
const serverlessYmlPath = path.join(tmpDirPath, 'serverless.yml');
|
||||
@ -38,13 +39,21 @@ describe('createStack', () => {
|
||||
awsDeploy.serverless.cli = new serverless.classes.CLI();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe('#create()', () => {
|
||||
it('should include custom stack tags', () => {
|
||||
awsDeploy.serverless.service.provider.stackTags = { STAGE: 'overridden', tag1: 'value1' };
|
||||
|
||||
const createStackStub = sinon
|
||||
const createStackStub = sandbox
|
||||
.stub(awsDeploy.provider, 'request').resolves();
|
||||
sinon.stub(awsDeploy, 'monitorStack').resolves();
|
||||
sandbox.stub(awsDeploy, 'monitorStack').resolves();
|
||||
|
||||
return awsDeploy.create().then(() => {
|
||||
expect(createStackStub.args[0][2].Tags)
|
||||
@ -52,33 +61,29 @@ describe('createStack', () => {
|
||||
{ Key: 'STAGE', Value: 'overridden' },
|
||||
{ Key: 'tag1', Value: 'value1' },
|
||||
]);
|
||||
awsDeploy.provider.request.restore();
|
||||
awsDeploy.monitorStack.restore();
|
||||
});
|
||||
});
|
||||
|
||||
it('should use CloudFormation service role ARN if it is specified', () => {
|
||||
awsDeploy.serverless.service.provider.cfnRole = 'arn:aws:iam::123456789012:role/myrole';
|
||||
|
||||
const createStackStub = sinon
|
||||
const createStackStub = sandbox
|
||||
.stub(awsDeploy.provider, 'request').resolves();
|
||||
sinon.stub(awsDeploy, 'monitorStack').resolves();
|
||||
sandbox.stub(awsDeploy, 'monitorStack').resolves();
|
||||
|
||||
return awsDeploy.create().then(() => {
|
||||
expect(createStackStub.args[0][2].RoleARN)
|
||||
.to.equal('arn:aws:iam::123456789012:role/myrole');
|
||||
awsDeploy.provider.request.restore();
|
||||
awsDeploy.monitorStack.restore();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#createStack()', () => {
|
||||
it('should resolve if stack already created', () => {
|
||||
const createStub = sinon
|
||||
const createStub = sandbox
|
||||
.stub(awsDeploy, 'create').resolves();
|
||||
|
||||
sinon.stub(awsDeploy.provider, 'request').resolves();
|
||||
sandbox.stub(awsDeploy.provider, 'request').resolves();
|
||||
|
||||
return awsDeploy.createStack().then(() => {
|
||||
expect(createStub.called).to.be.equal(false);
|
||||
@ -87,7 +92,7 @@ describe('createStack', () => {
|
||||
|
||||
it('should set the createLater flag and resolve if deployment bucket is provided', () => {
|
||||
awsDeploy.serverless.service.provider.deploymentBucket = 'serverless';
|
||||
sinon.stub(awsDeploy.provider, 'request')
|
||||
sandbox.stub(awsDeploy.provider, 'request')
|
||||
.returns(BbPromise.reject({ message: 'does not exist' }));
|
||||
|
||||
return awsDeploy.createStack().then(() => {
|
||||
@ -100,7 +105,7 @@ describe('createStack', () => {
|
||||
message: 'Something went wrong.',
|
||||
};
|
||||
|
||||
sinon.stub(awsDeploy.provider, 'request').rejects(errorMock);
|
||||
sandbox.stub(awsDeploy.provider, 'request').rejects(errorMock);
|
||||
|
||||
const createStub = sinon
|
||||
.stub(awsDeploy, 'create').resolves();
|
||||
@ -117,7 +122,7 @@ describe('createStack', () => {
|
||||
message: 'does not exist',
|
||||
};
|
||||
|
||||
sinon.stub(awsDeploy.provider, 'request').rejects(errorMock);
|
||||
sandbox.stub(awsDeploy.provider, 'request').rejects(errorMock);
|
||||
|
||||
const createStub = sinon
|
||||
.stub(awsDeploy, 'create').resolves();
|
||||
|
||||
@ -29,6 +29,29 @@ module.exports = {
|
||||
this.serverless.service.package.artifact = path
|
||||
.join(this.serverless.config.servicePath, '.serverless', state.package.artifact);
|
||||
}
|
||||
|
||||
// Check function's attached to API Gateway timeout
|
||||
if (!_.isEmpty(this.serverless.service.functions)) {
|
||||
this.serverless.service.getAllFunctions().forEach(functionName => {
|
||||
const functionObject = this.serverless.service.getFunction(functionName);
|
||||
|
||||
// Check if function timeout is greater than API Gateway timeout
|
||||
if (functionObject.timeout > 30 && functionObject.events) {
|
||||
functionObject.events.forEach(event => {
|
||||
if (Object.keys(event)[0] === 'http') {
|
||||
const warnMessage = [
|
||||
`WARNING: Function ${functionName} has timeout of ${functionObject.timeout} `,
|
||||
'seconds, however, it\'s attached to API Gateway so it\'s automatically ',
|
||||
'limited to 30 seconds.',
|
||||
].join('');
|
||||
|
||||
this.serverless.cli.log(warnMessage);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!_.isEmpty(this.serverless.service.functions) &&
|
||||
this.serverless.service.package.individually) {
|
||||
// artifact file validation (multiple function artifacts)
|
||||
|
||||
@ -42,7 +42,9 @@ describe('extendedValidate', () => {
|
||||
};
|
||||
awsDeploy = new AwsDeploy(serverless, options);
|
||||
awsDeploy.serverless.service.service = `service-${(new Date()).getTime().toString()}`;
|
||||
awsDeploy.serverless.cli = new serverless.classes.CLI();
|
||||
awsDeploy.serverless.cli = {
|
||||
log: sinon.spy(),
|
||||
};
|
||||
});
|
||||
|
||||
describe('extendedValidate()', () => {
|
||||
@ -148,5 +150,30 @@ describe('extendedValidate', () => {
|
||||
delete awsDeploy.serverless.service.package.artifact;
|
||||
});
|
||||
});
|
||||
|
||||
it('should warn if function\'s timeout is greater than 30 and it\'s attached to APIGW', () => {
|
||||
stateFileMock.service.functions = {
|
||||
first: {
|
||||
timeout: 31,
|
||||
package: {
|
||||
artifact: 'artifact.zip',
|
||||
},
|
||||
events: [{
|
||||
http: {},
|
||||
}],
|
||||
},
|
||||
};
|
||||
awsDeploy.serverless.service.package.individually = true;
|
||||
fileExistsSyncStub.returns(true);
|
||||
readFileSyncStub.returns(stateFileMock);
|
||||
|
||||
return awsDeploy.extendedValidate().then(() => {
|
||||
const msg = [
|
||||
'WARNING: Function first has timeout of 31 seconds, however, it\'s ',
|
||||
'attached to API Gateway so it\'s automatically limited to 30 seconds.',
|
||||
].join('');
|
||||
expect(awsDeploy.serverless.cli.log.firstCall.calledWithExactly(msg)).to.be.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -73,6 +73,47 @@ class AwsDeployFunction {
|
||||
});
|
||||
}
|
||||
|
||||
normalizeArnRole(role) {
|
||||
if (typeof role === 'string') {
|
||||
if (role.indexOf(':') === -1) {
|
||||
const roleResource = this.serverless.service.resources.Resources[role];
|
||||
|
||||
if (roleResource.Type !== 'AWS::IAM::Role') {
|
||||
throw new Error('Provided resource is not IAM Role.');
|
||||
}
|
||||
|
||||
const roleProperties = roleResource.Properties;
|
||||
const compiledFullRoleName = `${roleProperties.Path || '/'}${roleProperties.RoleName}`;
|
||||
|
||||
return this.provider.getAccountId().then((accountId) =>
|
||||
`arn:aws:iam::${accountId}:role${compiledFullRoleName}`
|
||||
);
|
||||
}
|
||||
|
||||
return BbPromise.resolve(role);
|
||||
}
|
||||
|
||||
return this.provider.request(
|
||||
'IAM',
|
||||
'getRole',
|
||||
{
|
||||
RoleName: role['Fn::GetAtt'][0],
|
||||
},
|
||||
this.options.stage, this.options.region
|
||||
).then((data) => data.Arn);
|
||||
}
|
||||
|
||||
callUpdateFunctionConfiguration(params) {
|
||||
return this.provider.request(
|
||||
'Lambda',
|
||||
'updateFunctionConfiguration',
|
||||
params,
|
||||
this.options.stage, this.options.region
|
||||
).then(() => {
|
||||
this.serverless.cli.log(`Successfully updated function: ${this.options.function}`);
|
||||
});
|
||||
}
|
||||
|
||||
updateFunctionConfiguration() {
|
||||
const functionObj = this.options.functionObj;
|
||||
const serviceObj = this.serverless.service.serviceObject;
|
||||
@ -97,12 +138,6 @@ class AwsDeployFunction {
|
||||
params.MemorySize = providerObj.memorySize;
|
||||
}
|
||||
|
||||
if ('role' in functionObj) {
|
||||
params.Role = functionObj.role;
|
||||
} else if ('role' in providerObj) {
|
||||
params.Role = providerObj.role;
|
||||
}
|
||||
|
||||
if ('timeout' in functionObj) {
|
||||
params.Timeout = functionObj.timeout;
|
||||
} else if ('timeout' in providerObj) {
|
||||
@ -148,14 +183,21 @@ class AwsDeployFunction {
|
||||
params.VpcConfig.SubnetIds = providerObj.vpc.subnetIds;
|
||||
}
|
||||
|
||||
return this.provider.request(
|
||||
'Lambda',
|
||||
'updateFunctionConfiguration',
|
||||
params,
|
||||
this.options.stage, this.options.region
|
||||
).then(() => {
|
||||
this.serverless.cli.log(`Successfully updated function: ${this.options.function}`);
|
||||
});
|
||||
if ('role' in functionObj) {
|
||||
return this.normalizeArnRole(functionObj.role).then(roleArn => {
|
||||
params.Role = roleArn;
|
||||
|
||||
return this.callUpdateFunctionConfiguration(params);
|
||||
});
|
||||
} else if ('role' in providerObj) {
|
||||
return this.normalizeArnRole(providerObj.role).then(roleArn => {
|
||||
params.Role = roleArn;
|
||||
|
||||
return this.callUpdateFunctionConfiguration(params);
|
||||
});
|
||||
}
|
||||
|
||||
return this.callUpdateFunctionConfiguration(params);
|
||||
}
|
||||
|
||||
deployFunction() {
|
||||
|
||||
@ -116,8 +116,73 @@ describe('AwsDeployFunction', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#normalizeArnRole', () => {
|
||||
let getAccountIdStub;
|
||||
let getRoleStub;
|
||||
|
||||
beforeEach(() => {
|
||||
getAccountIdStub = sinon
|
||||
.stub(awsDeployFunction.provider, 'getAccountId')
|
||||
.resolves('123456789012');
|
||||
getRoleStub = sinon
|
||||
.stub(awsDeployFunction.provider, 'request')
|
||||
.resolves({ Arn: 'arn:aws:iam::123456789012:role/role_2' });
|
||||
|
||||
serverless.service.resources = {
|
||||
Resources: {
|
||||
MyCustomRole: {
|
||||
Type: 'AWS::IAM::Role',
|
||||
Properties: {
|
||||
RoleName: 'role_123',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
awsDeployFunction.provider.getAccountId.restore();
|
||||
awsDeployFunction.provider.request.restore();
|
||||
serverless.service.resources = undefined;
|
||||
});
|
||||
|
||||
it('should return unmodified ARN if ARN was provided', () => {
|
||||
const arn = 'arn:aws:iam::123456789012:role/role';
|
||||
|
||||
return awsDeployFunction.normalizeArnRole(arn).then((result) => {
|
||||
expect(getAccountIdStub.calledOnce).to.be.equal(false);
|
||||
expect(result).to.be.equal(arn);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return compiled ARN if role name was provided', () => {
|
||||
const roleName = 'MyCustomRole';
|
||||
|
||||
return awsDeployFunction.normalizeArnRole(roleName).then((result) => {
|
||||
expect(getAccountIdStub.calledOnce).to.be.equal(true);
|
||||
expect(result).to.be.equal('arn:aws:iam::123456789012:role/role_123');
|
||||
});
|
||||
});
|
||||
|
||||
it('should return compiled ARN if object role was provided', () => {
|
||||
const roleObj = {
|
||||
'Fn::GetAtt': [
|
||||
'role_2',
|
||||
'Arn',
|
||||
],
|
||||
};
|
||||
|
||||
return awsDeployFunction.normalizeArnRole(roleObj).then((result) => {
|
||||
expect(getRoleStub.calledOnce).to.be.equal(true);
|
||||
expect(getAccountIdStub.calledOnce).to.be.equal(false);
|
||||
expect(result).to.be.equal('arn:aws:iam::123456789012:role/role_2');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#updateFunctionConfiguration', () => {
|
||||
let updateFunctionConfigurationStub;
|
||||
let normalizeArnRoleStub;
|
||||
const options = {
|
||||
stage: 'dev',
|
||||
region: 'us-east-1',
|
||||
@ -131,10 +196,15 @@ describe('AwsDeployFunction', () => {
|
||||
updateFunctionConfigurationStub = sinon
|
||||
.stub(awsDeployFunction.provider, 'request')
|
||||
.resolves();
|
||||
|
||||
normalizeArnRoleStub = sinon
|
||||
.stub(awsDeployFunction, 'normalizeArnRole')
|
||||
.resolves('arn:aws:us-east-1:123456789012:role/role');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
awsDeployFunction.provider.request.restore();
|
||||
awsDeployFunction.normalizeArnRole.restore();
|
||||
awsDeployFunction.serverless.service.provider.timeout = undefined;
|
||||
awsDeployFunction.serverless.service.provider.memorySize = undefined;
|
||||
awsDeployFunction.serverless.service.provider.role = undefined;
|
||||
@ -162,6 +232,8 @@ describe('AwsDeployFunction', () => {
|
||||
awsDeployFunction.options = options;
|
||||
|
||||
return awsDeployFunction.updateFunctionConfiguration().then(() => {
|
||||
expect(normalizeArnRoleStub.calledOnce).to.be.equal(true);
|
||||
expect(normalizeArnRoleStub.calledWithExactly('arn:aws:iam::123456789012:role/Admin'));
|
||||
expect(updateFunctionConfigurationStub.calledOnce).to.be.equal(true);
|
||||
expect(updateFunctionConfigurationStub.calledWithExactly(
|
||||
'Lambda',
|
||||
@ -179,7 +251,7 @@ describe('AwsDeployFunction', () => {
|
||||
FunctionName: 'first',
|
||||
KMSKeyArn: 'arn:aws:kms:us-east-1:123456789012',
|
||||
MemorySize: 128,
|
||||
Role: 'arn:aws:iam::123456789012:role/Admin',
|
||||
Role: 'arn:aws:us-east-1:123456789012:role/role',
|
||||
Timeout: 3,
|
||||
VpcConfig: {
|
||||
SecurityGroupIds: ['1'],
|
||||
@ -247,6 +319,8 @@ describe('AwsDeployFunction', () => {
|
||||
awsDeployFunction.options = options;
|
||||
|
||||
return awsDeployFunction.updateFunctionConfiguration().then(() => {
|
||||
expect(normalizeArnRoleStub.calledOnce).to.be.equal(true);
|
||||
expect(normalizeArnRoleStub.calledWithExactly('role'));
|
||||
expect(updateFunctionConfigurationStub.calledOnce).to.be.equal(true);
|
||||
expect(updateFunctionConfigurationStub.calledWithExactly(
|
||||
'Lambda',
|
||||
@ -260,7 +334,7 @@ describe('AwsDeployFunction', () => {
|
||||
},
|
||||
Timeout: 12,
|
||||
MemorySize: 512,
|
||||
Role: 'role',
|
||||
Role: 'arn:aws:us-east-1:123456789012:role/role',
|
||||
},
|
||||
awsDeployFunction.options.stage,
|
||||
awsDeployFunction.options.region
|
||||
|
||||
@ -58,6 +58,8 @@ if __name__ == '__main__':
|
||||
handler = getattr(module, args.handler_name)
|
||||
|
||||
input = json.load(sys.stdin)
|
||||
if sys.platform != 'win32':
|
||||
sys.stdin = open('/dev/tty')
|
||||
context = FakeLambdaContext(**input.get('context', {}))
|
||||
result = handler(input['event'], context)
|
||||
sys.stdout.write(json.dumps(result, indent=4))
|
||||
|
||||
@ -285,6 +285,10 @@ module.exports = {
|
||||
getLambdaAlexaSkillPermissionLogicalId(functionName) {
|
||||
return `${this.getNormalizedFunctionName(functionName)}LambdaPermissionAlexaSkill`;
|
||||
},
|
||||
getLambdaAlexaSmartHomePermissionLogicalId(functionName, alexaSmartHomeIndex) {
|
||||
return `${this.getNormalizedFunctionName(functionName)}LambdaPermissionAlexaSmartHome${
|
||||
alexaSmartHomeIndex}`;
|
||||
},
|
||||
getLambdaCloudWatchLogPermissionLogicalId(functionName, logsIndex) {
|
||||
return `${this.getNormalizedFunctionName(functionName)
|
||||
}LambdaPermissionLogsSubscriptionFilterCloudWatchLog${logsIndex}`;
|
||||
|
||||
@ -469,6 +469,14 @@ describe('#naming()', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getLambdaAlexaSmartHomePermissionLogicalId()', () => {
|
||||
it('should normalize the function name and append the standard suffix',
|
||||
() => {
|
||||
expect(sdk.naming.getLambdaAlexaSmartHomePermissionLogicalId('functionName', 0))
|
||||
.to.equal('FunctionNameLambdaPermissionAlexaSmartHome0');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getLambdaSnsSubscriptionLogicalId()', () => {
|
||||
it('should normalize the function name and append the standard suffix', () => {
|
||||
expect(sdk.naming.getLambdaSnsSubscriptionLogicalId('functionName', 'topicName'))
|
||||
|
||||
@ -0,0 +1,87 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
|
||||
class AwsCompileAlexaSmartHomeEvents {
|
||||
constructor(serverless) {
|
||||
this.serverless = serverless;
|
||||
this.provider = this.serverless.getProvider('aws');
|
||||
|
||||
this.hooks = {
|
||||
'package:compileEvents': this.compileAlexaSmartHomeEvents.bind(this),
|
||||
};
|
||||
}
|
||||
|
||||
compileAlexaSmartHomeEvents() {
|
||||
this.serverless.service.getAllFunctions().forEach((functionName) => {
|
||||
const functionObj = this.serverless.service.getFunction(functionName);
|
||||
let alexaSmartHomeNumberInFunction = 0;
|
||||
|
||||
if (functionObj.events) {
|
||||
functionObj.events.forEach(event => {
|
||||
if (event.alexaSmartHome) {
|
||||
alexaSmartHomeNumberInFunction++;
|
||||
let EventSourceToken;
|
||||
let Action;
|
||||
|
||||
if (typeof event.alexaSmartHome === 'object') {
|
||||
if (!event.alexaSmartHome.appId) {
|
||||
const errorMessage = [
|
||||
`Missing "appId" property for alexaSmartHome event in function ${functionName}`,
|
||||
' The correct syntax is: appId: amzn1.ask.skill.xxxx-xxxx',
|
||||
' OR an object with "appId" property.',
|
||||
' Please check the docs for more info.',
|
||||
].join('');
|
||||
throw new this.serverless.classes
|
||||
.Error(errorMessage);
|
||||
}
|
||||
EventSourceToken = event.alexaSmartHome.appId;
|
||||
Action = event.alexaSmartHome.enabled !== false ?
|
||||
'lambda:InvokeFunction' : 'lambda:DisableInvokeFunction';
|
||||
} else if (typeof event.alexaSmartHome === 'string') {
|
||||
EventSourceToken = event.alexaSmartHome;
|
||||
Action = 'lambda:InvokeFunction';
|
||||
} else {
|
||||
const errorMessage = [
|
||||
`Alexa Smart Home event of function "${functionName}" is not an object or string.`,
|
||||
' The correct syntax is: alexaSmartHome.',
|
||||
' Please check the docs for more info.',
|
||||
].join('');
|
||||
throw new this.serverless.classes.Error(errorMessage);
|
||||
}
|
||||
const lambdaLogicalId = this.provider.naming
|
||||
.getLambdaLogicalId(functionName);
|
||||
|
||||
const permissionTemplate = {
|
||||
Type: 'AWS::Lambda::Permission',
|
||||
Properties: {
|
||||
FunctionName: {
|
||||
'Fn::GetAtt': [
|
||||
lambdaLogicalId,
|
||||
'Arn',
|
||||
],
|
||||
},
|
||||
Action: Action.replace(/\\n|\\r/g, ''),
|
||||
Principal: 'alexa-connectedhome.amazon.com',
|
||||
EventSourceToken: EventSourceToken.replace(/\\n|\\r/g, ''),
|
||||
},
|
||||
};
|
||||
|
||||
const lambdaPermissionLogicalId = this.provider.naming
|
||||
.getLambdaAlexaSmartHomePermissionLogicalId(functionName,
|
||||
alexaSmartHomeNumberInFunction);
|
||||
|
||||
const permissionCloudFormationResource = {
|
||||
[lambdaPermissionLogicalId]: permissionTemplate,
|
||||
};
|
||||
|
||||
_.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources,
|
||||
permissionCloudFormationResource);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AwsCompileAlexaSmartHomeEvents;
|
||||
@ -0,0 +1,221 @@
|
||||
'use strict';
|
||||
|
||||
const expect = require('chai').expect;
|
||||
const AwsProvider = require('../../../../provider/awsProvider');
|
||||
const AwsCompileAlexaSmartHomeEvents = require('./index');
|
||||
const Serverless = require('../../../../../../Serverless');
|
||||
|
||||
describe('AwsCompileAlexaSmartHomeEvents', () => {
|
||||
let serverless;
|
||||
let awsCompileAlexaSmartHomeEvents;
|
||||
|
||||
beforeEach(() => {
|
||||
serverless = new Serverless();
|
||||
serverless.service.provider.compiledCloudFormationTemplate = { Resources: {} };
|
||||
serverless.setProvider('aws', new AwsProvider(serverless));
|
||||
awsCompileAlexaSmartHomeEvents = new AwsCompileAlexaSmartHomeEvents(serverless);
|
||||
awsCompileAlexaSmartHomeEvents.serverless.service.service = 'new-service';
|
||||
});
|
||||
|
||||
describe('#constructor()', () => {
|
||||
it('should set the provider variable to an instance of AwsProvider', () =>
|
||||
expect(awsCompileAlexaSmartHomeEvents.provider).to.be.instanceof(AwsProvider));
|
||||
});
|
||||
|
||||
describe('#compileAlexaSmartHomeEvents()', () => {
|
||||
it('should throw an error if alexaSmartHome event type is not a string or an object', () => {
|
||||
awsCompileAlexaSmartHomeEvents.serverless.service.functions = {
|
||||
first: {
|
||||
events: [
|
||||
{
|
||||
alexaSmartHome: 42,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
expect(() => awsCompileAlexaSmartHomeEvents.compileAlexaSmartHomeEvents()).to.throw(Error);
|
||||
});
|
||||
|
||||
it('should throw an error if the "appId" property is not given', () => {
|
||||
awsCompileAlexaSmartHomeEvents.serverless.service.functions = {
|
||||
first: {
|
||||
events: [
|
||||
{
|
||||
alexaSmartHome: {
|
||||
appId: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
expect(() => awsCompileAlexaSmartHomeEvents.compileAlexaSmartHomeEvents()).to.throw(Error);
|
||||
});
|
||||
|
||||
it('should create corresponding resources when alexaSmartHome events are given', () => {
|
||||
awsCompileAlexaSmartHomeEvents.serverless.service.functions = {
|
||||
first: {
|
||||
events: [
|
||||
{
|
||||
alexaSmartHome: {
|
||||
appId: 'amzn1.ask.skill.xx-xx-xx-xx',
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
alexaSmartHome: {
|
||||
appId: 'amzn1.ask.skill.yy-yy-yy-yy',
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
alexaSmartHome: 'amzn1.ask.skill.zz-zz-zz-zz',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
awsCompileAlexaSmartHomeEvents.compileAlexaSmartHomeEvents();
|
||||
|
||||
expect(awsCompileAlexaSmartHomeEvents.serverless.service
|
||||
.provider.compiledCloudFormationTemplate.Resources
|
||||
.FirstLambdaPermissionAlexaSmartHome1.Type
|
||||
).to.equal('AWS::Lambda::Permission');
|
||||
expect(awsCompileAlexaSmartHomeEvents.serverless.service
|
||||
.provider.compiledCloudFormationTemplate.Resources
|
||||
.FirstLambdaPermissionAlexaSmartHome2.Type
|
||||
).to.equal('AWS::Lambda::Permission');
|
||||
expect(awsCompileAlexaSmartHomeEvents.serverless.service
|
||||
.provider.compiledCloudFormationTemplate.Resources
|
||||
.FirstLambdaPermissionAlexaSmartHome3.Type
|
||||
).to.equal('AWS::Lambda::Permission');
|
||||
expect(awsCompileAlexaSmartHomeEvents.serverless.service
|
||||
.provider.compiledCloudFormationTemplate.Resources
|
||||
.FirstLambdaPermissionAlexaSmartHome1.Properties.FunctionName
|
||||
).to.deep.equal({ 'Fn::GetAtt': ['FirstLambdaFunction', 'Arn'] });
|
||||
expect(awsCompileAlexaSmartHomeEvents.serverless.service
|
||||
.provider.compiledCloudFormationTemplate.Resources
|
||||
.FirstLambdaPermissionAlexaSmartHome2.Properties.FunctionName
|
||||
).to.deep.equal({ 'Fn::GetAtt': ['FirstLambdaFunction', 'Arn'] });
|
||||
expect(awsCompileAlexaSmartHomeEvents.serverless.service
|
||||
.provider.compiledCloudFormationTemplate.Resources
|
||||
.FirstLambdaPermissionAlexaSmartHome3.Properties.FunctionName
|
||||
).to.deep.equal({ 'Fn::GetAtt': ['FirstLambdaFunction', 'Arn'] });
|
||||
expect(awsCompileAlexaSmartHomeEvents.serverless.service
|
||||
.provider.compiledCloudFormationTemplate.Resources
|
||||
.FirstLambdaPermissionAlexaSmartHome1.Properties.Action
|
||||
).to.equal('lambda:DisableInvokeFunction');
|
||||
expect(awsCompileAlexaSmartHomeEvents.serverless.service
|
||||
.provider.compiledCloudFormationTemplate.Resources
|
||||
.FirstLambdaPermissionAlexaSmartHome2.Properties.Action
|
||||
).to.equal('lambda:InvokeFunction');
|
||||
expect(awsCompileAlexaSmartHomeEvents.serverless.service
|
||||
.provider.compiledCloudFormationTemplate.Resources
|
||||
.FirstLambdaPermissionAlexaSmartHome3.Properties.Action
|
||||
).to.equal('lambda:InvokeFunction');
|
||||
expect(awsCompileAlexaSmartHomeEvents.serverless.service
|
||||
.provider.compiledCloudFormationTemplate.Resources
|
||||
.FirstLambdaPermissionAlexaSmartHome1.Properties.Principal
|
||||
).to.equal('alexa-connectedhome.amazon.com');
|
||||
expect(awsCompileAlexaSmartHomeEvents.serverless.service
|
||||
.provider.compiledCloudFormationTemplate.Resources
|
||||
.FirstLambdaPermissionAlexaSmartHome2.Properties.Principal
|
||||
).to.equal('alexa-connectedhome.amazon.com');
|
||||
expect(awsCompileAlexaSmartHomeEvents.serverless.service
|
||||
.provider.compiledCloudFormationTemplate.Resources
|
||||
.FirstLambdaPermissionAlexaSmartHome3.Properties.Principal
|
||||
).to.equal('alexa-connectedhome.amazon.com');
|
||||
expect(awsCompileAlexaSmartHomeEvents.serverless.service
|
||||
.provider.compiledCloudFormationTemplate.Resources
|
||||
.FirstLambdaPermissionAlexaSmartHome1.Properties.EventSourceToken
|
||||
).to.equal('amzn1.ask.skill.xx-xx-xx-xx');
|
||||
expect(awsCompileAlexaSmartHomeEvents.serverless.service
|
||||
.provider.compiledCloudFormationTemplate.Resources
|
||||
.FirstLambdaPermissionAlexaSmartHome2.Properties.EventSourceToken
|
||||
).to.equal('amzn1.ask.skill.yy-yy-yy-yy');
|
||||
expect(awsCompileAlexaSmartHomeEvents.serverless.service
|
||||
.provider.compiledCloudFormationTemplate.Resources
|
||||
.FirstLambdaPermissionAlexaSmartHome3.Properties.EventSourceToken
|
||||
).to.equal('amzn1.ask.skill.zz-zz-zz-zz');
|
||||
});
|
||||
|
||||
it('should respect enabled variable, defaulting to true', () => {
|
||||
awsCompileAlexaSmartHomeEvents.serverless.service.functions = {
|
||||
first: {
|
||||
events: [
|
||||
{
|
||||
alexaSmartHome: {
|
||||
appId: 'amzn1.ask.skill.xx-xx-xx-xx',
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
alexaSmartHome: {
|
||||
appId: 'amzn1.ask.skill.yy-yy-yy-yy',
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
alexaSmartHome: {
|
||||
appId: 'amzn1.ask.skill.jj-jj-jj-jj',
|
||||
},
|
||||
},
|
||||
{
|
||||
alexaSmartHome: 'amzn1.ask.skill.zz-zz-zz-zz',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
awsCompileAlexaSmartHomeEvents.compileAlexaSmartHomeEvents();
|
||||
|
||||
expect(awsCompileAlexaSmartHomeEvents.serverless.service
|
||||
.provider.compiledCloudFormationTemplate.Resources
|
||||
.FirstLambdaPermissionAlexaSmartHome1.Properties.Action
|
||||
).to.equal('lambda:DisableInvokeFunction');
|
||||
expect(awsCompileAlexaSmartHomeEvents.serverless.service
|
||||
.provider.compiledCloudFormationTemplate.Resources
|
||||
.FirstLambdaPermissionAlexaSmartHome2.Properties.Action
|
||||
).to.equal('lambda:InvokeFunction');
|
||||
expect(awsCompileAlexaSmartHomeEvents.serverless.service
|
||||
.provider.compiledCloudFormationTemplate.Resources
|
||||
.FirstLambdaPermissionAlexaSmartHome3.Properties.Action
|
||||
).to.equal('lambda:InvokeFunction');
|
||||
expect(awsCompileAlexaSmartHomeEvents.serverless.service
|
||||
.provider.compiledCloudFormationTemplate.Resources
|
||||
.FirstLambdaPermissionAlexaSmartHome4.Properties.Action
|
||||
).to.equal('lambda:InvokeFunction');
|
||||
});
|
||||
|
||||
it('should not create corresponding resources when alexaSmartHome events are not given', () => {
|
||||
awsCompileAlexaSmartHomeEvents.serverless.service.functions = {
|
||||
first: {
|
||||
events: [
|
||||
'alexaSkill',
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
awsCompileAlexaSmartHomeEvents.compileAlexaSmartHomeEvents();
|
||||
|
||||
expect(
|
||||
awsCompileAlexaSmartHomeEvents.serverless.service.provider.compiledCloudFormationTemplate
|
||||
.Resources
|
||||
).to.deep.equal({});
|
||||
});
|
||||
|
||||
it('should not create corresponding resources when events are not given', () => {
|
||||
awsCompileAlexaSmartHomeEvents.serverless.service.functions = {
|
||||
first: {},
|
||||
};
|
||||
|
||||
awsCompileAlexaSmartHomeEvents.compileAlexaSmartHomeEvents();
|
||||
|
||||
expect(
|
||||
awsCompileAlexaSmartHomeEvents.serverless.service.provider.compiledCloudFormationTemplate
|
||||
.Resources
|
||||
).to.deep.equal({});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -37,7 +37,7 @@ module.exports = {
|
||||
'/invocations',
|
||||
],
|
||||
] };
|
||||
authorizerProperties.Type = 'TOKEN';
|
||||
authorizerProperties.Type = authorizer.type ? authorizer.type.toUpperCase() : 'TOKEN';
|
||||
}
|
||||
|
||||
_.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources, {
|
||||
|
||||
@ -96,6 +96,53 @@ describe('#compileAuthorizers()', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should apply optional provided type value to Authorizer Type', () => {
|
||||
awsCompileApigEvents.validated.events = [{
|
||||
http: {
|
||||
path: 'users/create',
|
||||
method: 'POST',
|
||||
authorizer: {
|
||||
name: 'authorizer',
|
||||
arn: 'foo',
|
||||
resultTtlInSeconds: 500,
|
||||
identityValidationExpression: 'regex',
|
||||
type: 'request',
|
||||
},
|
||||
},
|
||||
}];
|
||||
|
||||
return awsCompileApigEvents.compileAuthorizers().then(() => {
|
||||
const resource = awsCompileApigEvents.serverless.service.provider
|
||||
.compiledCloudFormationTemplate.Resources.AuthorizerApiGatewayAuthorizer;
|
||||
|
||||
expect(resource.Type).to.equal('AWS::ApiGateway::Authorizer');
|
||||
expect(resource.Properties.Type).to.equal('REQUEST');
|
||||
});
|
||||
});
|
||||
|
||||
it('should apply TOKEN as authorizer Type when not given a type value', () => {
|
||||
awsCompileApigEvents.validated.events = [{
|
||||
http: {
|
||||
path: 'users/create',
|
||||
method: 'POST',
|
||||
authorizer: {
|
||||
name: 'authorizer',
|
||||
arn: 'foo',
|
||||
resultTtlInSeconds: 500,
|
||||
identityValidationExpression: 'regex',
|
||||
},
|
||||
},
|
||||
}];
|
||||
|
||||
return awsCompileApigEvents.compileAuthorizers().then(() => {
|
||||
const resource = awsCompileApigEvents.serverless.service.provider
|
||||
.compiledCloudFormationTemplate.Resources.AuthorizerApiGatewayAuthorizer;
|
||||
|
||||
expect(resource.Type).to.equal('AWS::ApiGateway::Authorizer');
|
||||
expect(resource.Properties.Type).to.equal('TOKEN');
|
||||
});
|
||||
});
|
||||
|
||||
it('should create a valid cognito user pool authorizer', () => {
|
||||
awsCompileApigEvents.validated.events = [{
|
||||
http: {
|
||||
|
||||
@ -26,6 +26,8 @@ module.exports = {
|
||||
|
||||
if (event.http.private) {
|
||||
template.Properties.ApiKeyRequired = true;
|
||||
} else {
|
||||
template.Properties.ApiKeyRequired = false;
|
||||
}
|
||||
|
||||
const methodLogicalId = this.provider.naming
|
||||
|
||||
@ -538,6 +538,24 @@ describe('#compileMethods()', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should set api key as not required if private property is not specified', () => {
|
||||
awsCompileApigEvents.validated.events = [
|
||||
{
|
||||
functionName: 'First',
|
||||
http: {
|
||||
path: 'users/create',
|
||||
method: 'post',
|
||||
},
|
||||
},
|
||||
];
|
||||
return awsCompileApigEvents.compileMethods().then(() => {
|
||||
expect(
|
||||
awsCompileApigEvents.serverless.service.provider.compiledCloudFormationTemplate
|
||||
.Resources.ApiGatewayMethodUsersCreatePost.Properties.ApiKeyRequired
|
||||
).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('should set the correct lambdaUri', () => {
|
||||
awsCompileApigEvents.validated.events = [
|
||||
{
|
||||
|
||||
@ -251,6 +251,10 @@ module.exports = {
|
||||
throw new this.serverless.classes.Error('Please provide either an authorizer name or ARN');
|
||||
}
|
||||
|
||||
if (!type) {
|
||||
type = authorizer.type;
|
||||
}
|
||||
|
||||
resultTtlInSeconds = Number.parseInt(authorizer.resultTtlInSeconds, 10);
|
||||
resultTtlInSeconds = Number.isNaN(resultTtlInSeconds) ? 300 : resultTtlInSeconds;
|
||||
claims = authorizer.claims || [];
|
||||
|
||||
@ -406,6 +406,33 @@ describe('#validate()', () => {
|
||||
expect(authorizer.identityValidationExpression).to.equal('foo');
|
||||
});
|
||||
|
||||
it('should accept authorizer config with a type', () => {
|
||||
awsCompileApigEvents.serverless.service.functions = {
|
||||
foo: {},
|
||||
first: {
|
||||
events: [
|
||||
{
|
||||
http: {
|
||||
method: 'GET',
|
||||
path: 'foo/bar',
|
||||
authorizer: {
|
||||
name: 'foo',
|
||||
type: 'request',
|
||||
resultTtlInSeconds: 500,
|
||||
identitySource: 'method.request.header.Custom',
|
||||
identityValidationExpression: 'foo',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const validated = awsCompileApigEvents.validate();
|
||||
const authorizer = validated.events[0].http.authorizer;
|
||||
expect(authorizer.type).to.equal('request');
|
||||
});
|
||||
|
||||
it('should accept authorizer config when resultTtlInSeconds is 0', () => {
|
||||
awsCompileApigEvents.serverless.service.functions = {
|
||||
foo: {},
|
||||
|
||||
@ -167,11 +167,11 @@ describe('AwsProvider', () => {
|
||||
return {
|
||||
send(cb) {
|
||||
if (first) {
|
||||
first = false;
|
||||
cb(error);
|
||||
} else {
|
||||
cb(undefined, {});
|
||||
}
|
||||
first = false;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -25,10 +25,21 @@ class AwsRollback {
|
||||
this.hooks = {
|
||||
'before:rollback:initialize': () => BbPromise.bind(this)
|
||||
.then(this.validate),
|
||||
'rollback:rollback': () => BbPromise.bind(this)
|
||||
.then(this.setBucketName)
|
||||
.then(this.setStackToUpdate)
|
||||
.then(this.updateStack),
|
||||
'rollback:rollback': () => {
|
||||
if (!this.options.timestamp) {
|
||||
const command = this.serverless.pluginManager.spawn('deploy:list');
|
||||
this.serverless.cli.log([
|
||||
'Use a timestamp from the deploy list below to rollback to a specific version.',
|
||||
'Run `sls rollback -t YourTimeStampHere`',
|
||||
].join('\n'));
|
||||
return command;
|
||||
}
|
||||
|
||||
return BbPromise.bind(this)
|
||||
.then(this.setBucketName)
|
||||
.then(this.setStackToUpdate)
|
||||
.then(this.updateStack);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -10,9 +10,11 @@ const sinon = require('sinon');
|
||||
describe('AwsRollback', () => {
|
||||
let awsRollback;
|
||||
let s3Key;
|
||||
let spawnStub;
|
||||
let serverless;
|
||||
|
||||
beforeEach(() => {
|
||||
const serverless = new Serverless();
|
||||
serverless = new Serverless();
|
||||
const options = {
|
||||
stage: 'dev',
|
||||
region: 'us-east-1',
|
||||
@ -20,11 +22,16 @@ describe('AwsRollback', () => {
|
||||
};
|
||||
serverless.setProvider('aws', new AwsProvider(serverless));
|
||||
serverless.service.service = 'rollback';
|
||||
spawnStub = sinon.stub(serverless.pluginManager, 'spawn');
|
||||
awsRollback = new AwsRollback(serverless, options);
|
||||
awsRollback.serverless.cli = new serverless.classes.CLI();
|
||||
s3Key = `serverless/${serverless.service.service}/${options.stage}`;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
serverless.pluginManager.spawn.restore();
|
||||
});
|
||||
|
||||
describe('#constructor()', () => {
|
||||
it('should have hooks', () => expect(awsRollback.hooks).to.be.not.empty);
|
||||
|
||||
@ -58,6 +65,15 @@ describe('AwsRollback', () => {
|
||||
.to.be.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should run "deploy:list" if timestamp is not specified', () => {
|
||||
const spawnDeployListStub = spawnStub.withArgs('deploy:list').resolves();
|
||||
awsRollback.options.timestamp = undefined;
|
||||
|
||||
return awsRollback.hooks['rollback:rollback']().then(() => {
|
||||
expect(spawnDeployListStub.calledOnce).to.be.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#setStackToUpdate()', () => {
|
||||
|
||||
@ -4,7 +4,9 @@ const BbPromise = require('bluebird');
|
||||
const path = require('path');
|
||||
const fse = require('fs-extra');
|
||||
const _ = require('lodash');
|
||||
|
||||
const userStats = require('../../utils/userStats');
|
||||
const download = require('../../utils/downloadTemplateFromRepo');
|
||||
|
||||
// class wide constants
|
||||
const validTemplates = [
|
||||
@ -31,6 +33,7 @@ const validTemplates = [
|
||||
'openwhisk-swift',
|
||||
'spotinst-nodejs',
|
||||
'spotinst-python',
|
||||
'spotinst-ruby',
|
||||
'webtasks-nodejs',
|
||||
'plugin',
|
||||
|
||||
@ -56,9 +59,12 @@ class Create {
|
||||
options: {
|
||||
template: {
|
||||
usage: `Template for the service. Available templates: ${humanReadableTemplateList}`,
|
||||
required: true,
|
||||
shortcut: 't',
|
||||
},
|
||||
'template-url': {
|
||||
usage: 'Template URL for the service. Supports: GitHub, BitBucket',
|
||||
shortcut: 'u',
|
||||
},
|
||||
path: {
|
||||
usage: 'The path where the service should be created (e.g. --path my-service)',
|
||||
shortcut: 'p',
|
||||
@ -79,6 +85,42 @@ class Create {
|
||||
|
||||
create() {
|
||||
this.serverless.cli.log('Generating boilerplate...');
|
||||
|
||||
if ('template' in this.options) {
|
||||
this.createFromTemplate();
|
||||
} else if ('template-url' in this.options) {
|
||||
return download.downloadTemplateFromRepo(
|
||||
this.options['template-url'],
|
||||
this.options.name,
|
||||
this.options.path
|
||||
)
|
||||
.then(dirName => {
|
||||
const message = [
|
||||
`Successfully installed "${dirName}" `,
|
||||
`${this.options.name && this.options.name !== dirName ? `as "${dirName}"` : ''}`,
|
||||
].join('');
|
||||
|
||||
this.serverless.cli.log(message);
|
||||
|
||||
userStats.track('service_created', {
|
||||
template: this.options.template,
|
||||
serviceName: this.options.name,
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
throw new this.serverless.classes.Error(err);
|
||||
});
|
||||
} else {
|
||||
const errorMessage = [
|
||||
'You must either pass a template name (--template) or a ',
|
||||
'a URL (--template-url).',
|
||||
].join('');
|
||||
throw new this.serverless.classes.Error(errorMessage);
|
||||
}
|
||||
return BbPromise.resolve();
|
||||
}
|
||||
|
||||
createFromTemplate() {
|
||||
const notPlugin = this.options.template !== 'plugin';
|
||||
|
||||
if (validTemplates.indexOf(this.options.template) === -1) {
|
||||
@ -180,8 +222,6 @@ class Create {
|
||||
this.serverless.cli
|
||||
.log('NOTE: Please update the "service" property in serverless.yml with your service name');
|
||||
}
|
||||
|
||||
return BbPromise.resolve();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ const Serverless = require('../../Serverless');
|
||||
const sinon = require('sinon');
|
||||
const testUtils = require('../../../tests/utils');
|
||||
const walkDirSync = require('../../utils/fs/walkDirSync');
|
||||
const download = require('./../../utils/downloadTemplateFromRepo');
|
||||
|
||||
describe('Create', () => {
|
||||
let create;
|
||||
@ -462,6 +463,19 @@ describe('Create', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate scaffolding for "spotinst-ruby" template', () => {
|
||||
process.chdir(tmpDir);
|
||||
create.options.template = 'spotinst-ruby';
|
||||
|
||||
return create.create().then(() => {
|
||||
const dirContent = fs.readdirSync(tmpDir);
|
||||
expect(dirContent).to.include('package.json');
|
||||
expect(dirContent).to.include('serverless.yml');
|
||||
expect(dirContent).to.include('handler.rb');
|
||||
expect(dirContent).to.include('.gitignore');
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate scaffolding for "webtasks-nodejs" template', () => {
|
||||
process.chdir(tmpDir);
|
||||
create.options.template = 'webtasks-nodejs';
|
||||
@ -606,5 +620,30 @@ describe('Create', () => {
|
||||
|
||||
expect(() => create.create()).to.throw(Error);
|
||||
});
|
||||
|
||||
it('should reject if download fails', (done) => {
|
||||
sinon.stub(download, 'downloadTemplateFromRepo');
|
||||
|
||||
create.options = {};
|
||||
create.options['template-url'] = 'https://github.com/serverless/serverless';
|
||||
|
||||
download.downloadTemplateFromRepo.rejects(new Error('Wrong'));
|
||||
|
||||
create
|
||||
.create()
|
||||
.catch(() => download.downloadTemplateFromRepo.restore())
|
||||
.then(() => done());
|
||||
});
|
||||
|
||||
it('should resolve if download succeeds', () => {
|
||||
sinon.stub(download, 'downloadTemplateFromRepo');
|
||||
|
||||
create.options = {};
|
||||
create.options['template-url'] = 'https://github.com/serverless/serverless';
|
||||
|
||||
download.downloadTemplateFromRepo.resolves();
|
||||
|
||||
return create.create().catch(() => download.downloadTemplateFromRepo.restore());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -68,6 +68,7 @@ functions:
|
||||
# - sns: greeter-topic
|
||||
# - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000
|
||||
# - alexaSkill
|
||||
# - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx
|
||||
# - iot:
|
||||
# sql: "SELECT * FROM 'some_topic'"
|
||||
# - cloudwatchEvent:
|
||||
|
||||
@ -68,6 +68,7 @@ functions:
|
||||
# - sns: greeter-topic
|
||||
# - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000
|
||||
# - alexaSkill
|
||||
# - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx
|
||||
# - iot:
|
||||
# sql: "SELECT * FROM 'some_topic'"
|
||||
# - cloudwatchEvent:
|
||||
|
||||
@ -65,6 +65,7 @@ functions:
|
||||
# - sns: greeter-topic
|
||||
# - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000
|
||||
# - alexaSkill
|
||||
# - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx
|
||||
# - iot:
|
||||
# sql: "SELECT * FROM 'some_topic'"
|
||||
# - cloudwatchEvent:
|
||||
|
||||
@ -65,6 +65,7 @@ functions:
|
||||
# - sns: greeter-topic
|
||||
# - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000
|
||||
# - alexaSkill
|
||||
# - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx
|
||||
# - iot:
|
||||
# sql: "SELECT * FROM 'some_topic'"
|
||||
# - cloudwatchEvent:
|
||||
|
||||
@ -65,6 +65,7 @@ functions:
|
||||
# - sns: greeter-topic
|
||||
# - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000
|
||||
# - alexaSkill
|
||||
# - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx
|
||||
# - iot:
|
||||
# sql: "SELECT * FROM 'some_topic'"
|
||||
# - cloudwatchEvent:
|
||||
|
||||
@ -65,6 +65,7 @@ functions:
|
||||
# - sns: greeter-topic
|
||||
# - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000
|
||||
# - alexaSkill
|
||||
# - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx
|
||||
# - iot:
|
||||
# sql: "SELECT * FROM 'some_topic'"
|
||||
# - cloudwatchEvent:
|
||||
|
||||
@ -61,6 +61,7 @@ functions:
|
||||
# - sns: greeter-topic
|
||||
# - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000
|
||||
# - alexaSkill
|
||||
# - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx
|
||||
# - iot:
|
||||
# sql: "SELECT * FROM 'some_topic'"
|
||||
# - cloudwatchEvent:
|
||||
@ -91,4 +92,4 @@ functions:
|
||||
# Outputs:
|
||||
# NewOutput:
|
||||
# Description: "Description for the output"
|
||||
# Value: "Some output value"
|
||||
# Value: "Some output value"
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
"babel-plugin-transform-runtime": "^6.23.0",
|
||||
"babel-polyfill": "^6.23.0",
|
||||
"babel-preset-env": "^1.6.0",
|
||||
"serverless-webpack": "^2.2.0",
|
||||
"serverless-webpack": "^3.1.1",
|
||||
"webpack": "^3.3.0"
|
||||
},
|
||||
"author": "The serverless webpack authors (https://github.com/elastic-coders/serverless-webpack)",
|
||||
|
||||
@ -10,14 +10,8 @@ provider:
|
||||
runtime: nodejs6.10
|
||||
|
||||
functions:
|
||||
# Example without LAMBDA-PROXY integration
|
||||
# Invoking locally:
|
||||
# sls webpack invoke -f first
|
||||
first:
|
||||
handler: first.hello
|
||||
# Example with LAMBDA-PROXY integration
|
||||
# Invoking locally:
|
||||
# sls webpack invoke -f second
|
||||
second:
|
||||
handler: second.hello
|
||||
events:
|
||||
|
||||
@ -70,6 +70,7 @@ functions:
|
||||
# - sns: greeter-topic
|
||||
# - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000
|
||||
# - alexaSkill
|
||||
# - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx
|
||||
# - iot:
|
||||
# sql: "SELECT * FROM 'some_topic'"
|
||||
# - cloudwatchEvent:
|
||||
|
||||
@ -70,6 +70,7 @@ functions:
|
||||
# - sns: greeter-topic
|
||||
# - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000
|
||||
# - alexaSkill
|
||||
# - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx
|
||||
# - iot:
|
||||
# sql: "SELECT * FROM 'some_topic'"
|
||||
# - cloudwatchEvent:
|
||||
|
||||
@ -70,6 +70,7 @@ functions:
|
||||
# - sns: greeter-topic
|
||||
# - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000
|
||||
# - alexaSkill
|
||||
# - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx
|
||||
# - iot:
|
||||
# sql: "SELECT * FROM 'some_topic'"
|
||||
# - cloudwatchEvent:
|
||||
|
||||
@ -67,6 +67,7 @@ functions:
|
||||
# - sns: greeter-topic
|
||||
# - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000
|
||||
# - alexaSkill
|
||||
# - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx
|
||||
# - iot:
|
||||
# sql: "SELECT * FROM 'some_topic'"
|
||||
# - cloudwatchEvent:
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
"serverless",
|
||||
"spotinst"
|
||||
],
|
||||
"dependencies": {
|
||||
"devDependencies": {
|
||||
"serverless-spotinst-functions": "*"
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,10 +20,14 @@ provider:
|
||||
|
||||
functions:
|
||||
hello:
|
||||
runtime: nodejs4.8
|
||||
runtime: nodejs8.3
|
||||
handler: handler.main
|
||||
memory: 128
|
||||
timeout: 30
|
||||
access: private
|
||||
# cron: # Setup scheduled trigger with cron expression
|
||||
# active: true
|
||||
# value: '* * * * *'
|
||||
|
||||
# extend the framework using plugins listed here:
|
||||
# https://github.com/serverless/plugins
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
"serverless",
|
||||
"spotinst"
|
||||
],
|
||||
"dependencies": {
|
||||
"devDependencies": {
|
||||
"serverless-spotinst-functions": "*"
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,6 +24,10 @@ functions:
|
||||
handler: handler.main
|
||||
memory: 128
|
||||
timeout: 30
|
||||
access: private
|
||||
# cron: # Setup scheduled trigger with cron expression
|
||||
# active: true
|
||||
# value: '* * * * *'
|
||||
|
||||
# extend the framework using plugins listed here:
|
||||
# https://github.com/serverless/plugins
|
||||
|
||||
6
lib/plugins/create/templates/spotinst-ruby/gitignore
Normal file
6
lib/plugins/create/templates/spotinst-ruby/gitignore
Normal file
@ -0,0 +1,6 @@
|
||||
# package directories
|
||||
node_modules
|
||||
jspm_packages
|
||||
|
||||
# Serverless directories
|
||||
.serverless
|
||||
13
lib/plugins/create/templates/spotinst-ruby/handler.rb
Normal file
13
lib/plugins/create/templates/spotinst-ruby/handler.rb
Normal file
@ -0,0 +1,13 @@
|
||||
|
||||
# Implement your function here.
|
||||
# The function will get the request as parameter.
|
||||
# The function should return an Hash
|
||||
|
||||
def main(args)
|
||||
queryparams = args["query"]
|
||||
body = args["body"]
|
||||
{
|
||||
:statusCode => 200,
|
||||
:body => '{"hello":"from Ruby2.4.1 function"}'
|
||||
}
|
||||
end
|
||||
13
lib/plugins/create/templates/spotinst-ruby/package.json
Normal file
13
lib/plugins/create/templates/spotinst-ruby/package.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "spotionst-ruby",
|
||||
"version": "1.0.0",
|
||||
"description": "Spotinst Functions Ruby sample for serverless framework service.",
|
||||
"main": "handler.js",
|
||||
"keywords": [
|
||||
"serverless",
|
||||
"spotinst"
|
||||
],
|
||||
"devDependencies": {
|
||||
"serverless-spotinst-functions": "*"
|
||||
}
|
||||
}
|
||||
35
lib/plugins/create/templates/spotinst-ruby/serverless.yml
Normal file
35
lib/plugins/create/templates/spotinst-ruby/serverless.yml
Normal file
@ -0,0 +1,35 @@
|
||||
# Welcome to Serverless!
|
||||
#
|
||||
# This file is the main config file for your service.
|
||||
# It's very minimal at this point and uses default values.
|
||||
# You can always add more config options for more control.
|
||||
# We've included some commented out config examples here.
|
||||
# Just uncomment any of them to get that config option.
|
||||
#
|
||||
# For full config options, check the docs:
|
||||
# docs.serverless.com
|
||||
#
|
||||
# Happy Coding!
|
||||
|
||||
service: spotinst-ruby # NOTE: update this with your service name
|
||||
|
||||
provider:
|
||||
name: spotinst
|
||||
spotinst:
|
||||
#environment: <env-XXXX> # NOTE: Remember to add the environment ID
|
||||
|
||||
functions:
|
||||
hello:
|
||||
runtime: ruby2.4.1
|
||||
handler: handler.main
|
||||
memory: 128
|
||||
timeout: 30
|
||||
access: private
|
||||
# cron: # Setup scheduled trigger with cron expression
|
||||
# active: true
|
||||
# value: '* * * * *'
|
||||
|
||||
# extend the framework using plugins listed here:
|
||||
# https://github.com/serverless/plugins
|
||||
plugins:
|
||||
- serverless-spotinst-functions
|
||||
@ -1,12 +1,10 @@
|
||||
'use strict';
|
||||
|
||||
const BbPromise = require('bluebird');
|
||||
const path = require('path');
|
||||
const URL = require('url');
|
||||
const download = require('download');
|
||||
const fse = require('fs-extra');
|
||||
const os = require('os');
|
||||
|
||||
const userStats = require('../../utils/userStats');
|
||||
const downloadTemplateFromRepo = require('../../utils/downloadTemplateFromRepo')
|
||||
.downloadTemplateFromRepo;
|
||||
|
||||
class Install {
|
||||
constructor(serverless, options) {
|
||||
@ -37,130 +35,23 @@ class Install {
|
||||
'install:install': () => BbPromise.bind(this)
|
||||
.then(this.install),
|
||||
};
|
||||
|
||||
this.renameService = (name, servicePath) => {
|
||||
const serviceFile = path.join(servicePath, 'serverless.yml');
|
||||
const packageFile = path.join(servicePath, 'package.json');
|
||||
|
||||
if (!this.serverless.utils.fileExistsSync(serviceFile)) {
|
||||
const errorMessage = [
|
||||
'serverless.yml not found in',
|
||||
` ${servicePath}`,
|
||||
].join('');
|
||||
throw new this.serverless.classes.Error(errorMessage);
|
||||
}
|
||||
|
||||
const serverlessYml =
|
||||
fse.readFileSync(serviceFile, 'utf-8')
|
||||
.replace(/service\s*:.+/gi, (match) => {
|
||||
const fractions = match.split('#');
|
||||
fractions[0] = `service: ${name}`;
|
||||
return fractions.join(' #');
|
||||
});
|
||||
|
||||
fse.writeFileSync(serviceFile, serverlessYml);
|
||||
|
||||
if (this.serverless.utils.fileExistsSync(packageFile)) {
|
||||
const json = this.serverless.utils.readFileSync(packageFile);
|
||||
this.serverless.utils.writeFile(packageFile, Object.assign(json, { name }));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
install() {
|
||||
const url = URL.parse(this.options.url.replace(/\/$/, ''));
|
||||
return downloadTemplateFromRepo(this.options.url, this.options.name)
|
||||
.then(dirName => {
|
||||
const message = [
|
||||
`Successfully installed "${dirName}" `,
|
||||
`${this.options.name && this.options.name !== dirName ? `as "${dirName}"` : ''}`,
|
||||
].join('');
|
||||
userStats.track('service_installed', {
|
||||
data: { // will be updated with core analtyics lib
|
||||
url: this.options.url,
|
||||
},
|
||||
});
|
||||
|
||||
// check if url parameter is a valid url
|
||||
if (!url.host) {
|
||||
throw new this.serverless.classes.Error('The URL you passed is not a valid URL');
|
||||
}
|
||||
|
||||
const parts = url.pathname.split('/');
|
||||
const parsedGitHubUrl = {
|
||||
owner: parts[1],
|
||||
repo: parts[2],
|
||||
branch: parts[4] || 'master',
|
||||
};
|
||||
|
||||
// validate if given url is a valid GitHub url
|
||||
if (url.hostname !== 'github.com' || !parsedGitHubUrl.owner || !parsedGitHubUrl.repo) {
|
||||
const errorMessage = [
|
||||
'The URL must be a valid GitHub URL in the following format:',
|
||||
' https://github.com/serverless/serverless',
|
||||
].join('');
|
||||
throw new this.serverless.classes.Error(errorMessage);
|
||||
}
|
||||
|
||||
const downloadUrl = [
|
||||
'https://github.com/',
|
||||
parsedGitHubUrl.owner,
|
||||
'/',
|
||||
parsedGitHubUrl.repo,
|
||||
'/archive/',
|
||||
parsedGitHubUrl.branch,
|
||||
'.zip',
|
||||
].join('');
|
||||
|
||||
const endIndex = parts.length - 1;
|
||||
let dirName;
|
||||
let serviceName;
|
||||
let downloadServicePath;
|
||||
|
||||
// check if it's a directory or the whole repository
|
||||
if (parts.length > 4) {
|
||||
serviceName = parts[endIndex];
|
||||
dirName = this.options.name || parts[endIndex];
|
||||
// download the repo into a temporary directory
|
||||
downloadServicePath = path.join(os.tmpdir(), parsedGitHubUrl.repo);
|
||||
} else {
|
||||
serviceName = parsedGitHubUrl.repo;
|
||||
dirName = this.options.name || parsedGitHubUrl.repo;
|
||||
downloadServicePath = path.join(process.cwd(), dirName);
|
||||
}
|
||||
|
||||
const servicePath = path.join(process.cwd(), dirName);
|
||||
const renamed = dirName !== (parts.length > 4 ? parts[endIndex] : parsedGitHubUrl.repo);
|
||||
|
||||
if (this.serverless.utils.dirExistsSync(path.join(process.cwd(), dirName))) {
|
||||
const errorMessage = `A folder named "${dirName}" already exists.`;
|
||||
throw new this.serverless.classes.Error(errorMessage);
|
||||
}
|
||||
|
||||
this.serverless.cli.log(`Downloading and installing "${serviceName}"...`);
|
||||
|
||||
const that = this;
|
||||
|
||||
// download service
|
||||
return download(
|
||||
downloadUrl,
|
||||
downloadServicePath,
|
||||
{ timeout: 30000, extract: true, strip: 1, mode: '755' }
|
||||
).then(() => {
|
||||
// if it's a directory inside of git
|
||||
if (parts.length > 4) {
|
||||
let directory = downloadServicePath;
|
||||
for (let i = 5; i <= endIndex; i++) {
|
||||
directory = path.join(directory, parts[i]);
|
||||
}
|
||||
that.serverless.utils
|
||||
.copyDirContentsSync(directory, servicePath);
|
||||
fse.removeSync(downloadServicePath);
|
||||
}
|
||||
}).then(() => {
|
||||
if (!renamed) return BbPromise.resolve();
|
||||
|
||||
return this.renameService(dirName, servicePath);
|
||||
}).then(() => {
|
||||
let message = `Successfully installed "${serviceName}"`;
|
||||
userStats.track('service_installed', {
|
||||
data: { // will be updated with core analtyics lib
|
||||
url: this.options.url,
|
||||
},
|
||||
this.serverless.cli.log(message);
|
||||
});
|
||||
if (renamed) message = `${message} as "${dirName}"`;
|
||||
|
||||
that.serverless.cli.log(message);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user