diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b64b53eb..98352edea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +# 1.26.0 (29.01.2018) +- [AWS Go support](https://github.com/serverless/serverless/pull/4669) +- [Support for using an existing ApiGateway and Resources](https://github.com/serverless/serverless/pull/4247) +- [Add logRetentionInDays config](https://github.com/serverless/serverless/pull/4591) +- [Add support of `serverless.js` configuration file](https://github.com/serverless/serverless/pull/4590) +- [Add "did you mean..." CLI suggestions](https://github.com/serverless/serverless/pull/4586) +- [Add `--template-path` option to `serverless create`](https://github.com/serverless/serverless/pull/4576) +- [Add support POJO input support for Java invoke local](https://github.com/serverless/serverless/pull/4596) + +## Meta +- [Comparison since last release](https://github.com/serverless/serverless/compare/v1.25.0...v1.26.0) + + # 1.25.0 (20.12.2017) - [Improve Stage and Region Usage](https://github.com/serverless/serverless/pull/4560) - [Add API Gateway endpoint configuration](https://github.com/serverless/serverless/pull/4531) diff --git a/README.md b/README.md index e6ff0f7fa..a5382ea26 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ The following are services you can instantly install and use by running `serverl ## Features -* Supports Node.js, Python, Java, Scala, C#, F#, Groovy, Kotlin, PHP & Swift. +* Supports Node.js, Python, Java, Scala, C#, F#, Go, Groovy, Kotlin, PHP & Swift. * Manages the lifecycle of your serverless architecture (build, deploy, update, delete). * Safely deploy functions, events and their required resources together via provider resource managers (e.g., AWS CloudFormation). * Functions can be grouped ("serverless services") for easy management of code, resources & processes, across large projects & teams. @@ -158,7 +158,7 @@ This table is generated from https://github.com/serverless/plugins/blob/master/p | **[Raml Serverless](https://github.com/andrewcurioso/raml-serverless)**
Serverless plugin to work with RAML API spec documents | [andrewcurioso](http://github.com/andrewcurioso) | | **[Serverless Alexa Plugin](https://github.com/rajington/serverless-alexa-plugin)**
Serverless plugin to support Alexa Lambda events | [rajington](http://github.com/rajington) | | **[Serverless Api Stage](https://github.com/leftclickben/serverless-api-stage)**
Serverless API Stage plugin, enables stage variables and logging for AWS API Gateway. | [leftclickben](http://github.com/leftclickben) | -| **[Serverless Apig S3](https://github.com/sdd/serverless-apig-s3)**
Serve static front-end content from S3 via the API Gatewy and deploy client bundle to S3. | [sdd](http://github.com/sdd) | +| **[Serverless Apig S3](https://github.com/sdd/serverless-apig-s3)**
Serve static front-end content from S3 via the API Gateway and deploy client bundle to S3. | [sdd](http://github.com/sdd) | | **[Serverless Apigateway Plugin](https://github.com/GFG/serverless-apigateway-plugin)**
Configure the AWS api gateway: Binary support, Headers and Body template mappings | [GFG](http://github.com/GFG) | | **[Serverless Apigw Binary](https://github.com/maciejtreder/serverless-apigw-binary)**
Plugin to enable binary support in AWS API Gateway. | [maciejtreder](http://github.com/maciejtreder) | | **[Serverless Apigwy Binary](https://github.com/ryanmurakami/serverless-apigwy-binary)**
Serverless plugin for configuring API Gateway to return binary responses | [ryanmurakami](http://github.com/ryanmurakami) | diff --git a/docker-compose.yml b/docker-compose.yml index f9ae71b3e..98c398c8f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -63,6 +63,16 @@ services: image: microsoft/dotnet:1.0.4-sdk volumes: - ./tmp/serverless-integration-test-aws-fsharp:/app + aws-go: + image: golang:1.9 + volumes: + - ./tmp/serverless-integration-test-aws-go:/app + - ./tmp/serverless-integration-test-aws-go:/go/src/app + aws-go-dep: + image: yunspace/golang:1.9 + volumes: + - ./tmp/serverless-integration-test-aws-go-dep:/app + - ./tmp/serverless-integration-test-aws-go-dep:/go/src/app aws-nodejs-typescript: image: node:6.10.3 volumes: diff --git a/docs/providers/aws/events/alexa-skill.md b/docs/providers/aws/events/alexa-skill.md index 915464bcf..3ade42de3 100644 --- a/docs/providers/aws/events/alexa-skill.md +++ b/docs/providers/aws/events/alexa-skill.md @@ -14,14 +14,41 @@ layout: Doc ## Event definition -This will enable your Lambda function to be called by an Alexa skill kit. +This will enable your Lambda function to be called by an Alexa Skill kit. +`amzn1.ask.skill.xx-xx-xx-xx-xx` is a skill ID for Alexa Skills kit. You receive a skill ID once you register and create a skill in [Amazon Developer Console](https://developer.amazon.com/). +After deploying, add your deployed Lambda function ARN to which this event is attached to the Service Endpoint under Configuration on Amazon Developer Console. ```yml functions: mySkill: handler: mySkill.handler events: - - alexaSkill + - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx-xx ``` You can find detailed guides on how to create an Alexa Skill with Serverless using NodeJS [here](https://github.com/serverless/examples/tree/master/aws-node-alexa-skill) as well as in combination with Python [here](https://github.com/serverless/examples/tree/master/aws-python-alexa-skill). + +## Enabling / Disabling + +**Note:** `alexaSkill` events are enabled by default. + +This will create and attach a alexaSkill event for the `mySkill` function which is disabled. If enabled it will call +the `mySkill` function by an Alexa Skill. + +```yaml +functions: + mySkill: + handler: mySkill.handler + events: + - alexaSkill: + appId: amzn1.ask.skill.xx-xx-xx-xx + enabled: false +``` + +## Backwards compatability + +Previous syntax of this event didn't require a skill ID as parameter, but according to [Amazon's documentation](https://developer.amazon.com/docs/custom-skills/host-a-custom-skill-as-an-aws-lambda-function.html#configuring-the-alexa-skills-kit-trigger) you should restrict your lambda function to be executed only by your skill. + +Omitting the skill id will make your Lambda function available for the public, allowing any other skill developer to invoke it. + +(This is important, as [opposing to custom HTTPS endpoints](https://developer.amazon.com/docs/custom-skills/handle-requests-sent-by-alexa.html#request-verify), there's no way to validate the request was sent by your skill.) \ No newline at end of file diff --git a/docs/providers/aws/events/apigateway.md b/docs/providers/aws/events/apigateway.md index 7da3daf1b..2d04400b4 100644 --- a/docs/providers/aws/events/apigateway.md +++ b/docs/providers/aws/events/apigateway.md @@ -872,7 +872,7 @@ functions: ``` -In case the application has many chilren and grandchildren paths, you also want to break them out into smaller services. +In case the application has many children and grandchildren paths, you also want to break them out into smaller services. ```yml service: service-a diff --git a/docs/providers/aws/events/cloudwatch-log.md b/docs/providers/aws/events/cloudwatch-log.md index bddf59117..2072598ab 100644 --- a/docs/providers/aws/events/cloudwatch-log.md +++ b/docs/providers/aws/events/cloudwatch-log.md @@ -14,7 +14,7 @@ layout: Doc ## Simple event definition -This will enable your Lambda function to be called by an Log Stream. +This will enable your Lambda function to be called by a Log Stream. ```yml functions: diff --git a/docs/providers/aws/examples/hello-world/README.md b/docs/providers/aws/examples/hello-world/README.md index d1f1d070a..378b05677 100644 --- a/docs/providers/aws/examples/hello-world/README.md +++ b/docs/providers/aws/examples/hello-world/README.md @@ -17,6 +17,8 @@ Pick your language of choice: * [JavaScript](./node) * [Python](./python) -* [csharp](./csharp) +* [C#](./csharp) +* [F#](./fsharp) +* [Go](./go) [View all examples](https://www.serverless.com/framework/docs/providers/aws/examples/) diff --git a/docs/providers/aws/examples/hello-world/csharp/README.md b/docs/providers/aws/examples/hello-world/csharp/README.md index 9f0d170a4..f230f7f88 100644 --- a/docs/providers/aws/examples/hello-world/csharp/README.md +++ b/docs/providers/aws/examples/hello-world/csharp/README.md @@ -13,13 +13,32 @@ layout: Doc Make sure `serverless` is installed. [See installation guide](../../../guide/installation.md). +Once installed the Serverless CLI can be called with `serverless` or the shorthand `sls` command. + +``` +$ sls + +Commands +* You can run commands with "serverless" or the shortcut "sls" +* Pass "--verbose" to this command to get in-depth plugin info +* Pass "--no-color" to disable CLI colors +* Pass "--help" after any for contextual help +``` + ## 1. Create a service -`serverless create --template aws-csharp --path myService` or `sls create --template aws-csharp --path myService`, where 'myService' is a new folder to be created with template service files. Change directories into this new folder. + +``` +sls create --template aws-csharp --path myService +``` + +Using the `create` command we can specify one of the available [templates](https://serverless.com/framework/docs/providers/aws/cli-reference/create#available-templates). For this example use aws-csharp with the `--template` or shorthand `-t` flag. + +The `--path` or shorthand `-p` is the location to be created with the template service files. Change directories into this new folder. ## 2. Build using .NET CLI tools and create zip package ``` -# Linux or OSX +# Linux or Mac OS ./build.sh ``` @@ -29,14 +48,23 @@ Make sure `serverless` is installed. [See installation guide](../../../guide/ins ``` ## 3. Deploy -`serverless deploy` or `sls deploy`. `sls` is shorthand for the Serverless CLI command + +``` +sls deploy +``` + +This will deploy your function to AWS Lambda based on the settings in `serverless.yml`. + ## 4. Invoke deployed function -`serverless invoke --function hello` or `serverless invoke -f hello` -`-f` is shorthand for `--function` +``` +sls invoke -f hello +``` -In your terminal window you should see the response from AWS Lambda +Invoke deployed function with command `invoke` and `--function` or shorthand `-f`. + +In your terminal window you should see the response from AWS Lambda. ```bash { @@ -49,4 +77,4 @@ In your terminal window you should see the response from AWS Lambda } ``` -Congrats you have just deployed and run your Hello World function! +Congrats you have deployed and ran your Hello World function! diff --git a/docs/providers/aws/examples/hello-world/fsharp/README.md b/docs/providers/aws/examples/hello-world/fsharp/README.md index 7c7de6acb..562c1e498 100644 --- a/docs/providers/aws/examples/hello-world/fsharp/README.md +++ b/docs/providers/aws/examples/hello-world/fsharp/README.md @@ -7,31 +7,69 @@ layout: Doc # Hello World F# Example -## Prerequisites +Make sure `serverless` is installed. [See installation guide](../../../guide/installation.md). -* Make sure `serverless` is installed. [See installation guide](../../../guide/installation.md). -* [.Net Core 1.0.1 SDK](https://www.microsoft.com/net/download/core) - * 1.1 isn't currently supported by AWS Lambda -* [NodeJS v4 or higher](https://nodejs.org/en/) -* An AWS Account - -## Build and Package - -From the root of this directory, run `build.cmd`, or `build.sh` if on Linux / Mac. - -This will produce your deployment package at `bin/release/netcoreapp1.0/deploy-package.zip`. - -## Deployment and Invocation - -Once packaged, you can follow [these instructions](https://github.com/serverless/serverless#quick-start) to deploy and remotely invoke the function on AWS Lambda. - -In short the commands you will need to run are (from the root of this directory): +Once installed the Serverless CLI can be called with `serverless` or the shorthand `sls` command. ``` -serverless config credentials --provider aws --key {YourAwsAccessKey} --secret {YourAwsSecret} -serverless deploy -v -serverless invoke -f hello -l -serverless remove +$ sls + +Commands +* You can run commands with "serverless" or the shortcut "sls" +* Pass "--verbose" to this command to get in-depth plugin info +* Pass "--no-color" to disable CLI colors +* Pass "--help" after any for contextual help ``` -By default this template deploys to us-east-1, you can change that in "serverless.yml" under the `region: us-east-1` key. \ No newline at end of file +## 1. Create a service + +``` +sls create --template aws-fsharp --path myService +``` + +Using the `create` command we can specify one of the available [templates](https://serverless.com/framework/docs/providers/aws/cli-reference/create#available-templates). For this example use aws-fsharp with the `--template` or shorthand `-t` flag. + +The `--path` or shorthand `-p` is the location to be created with the template service files. Change directories into this new folder. + +## 2. Build using .NET CLI tools and create zip package + +``` +# Linux or Mac OS +./build.sh +``` + +``` +# Windows PowerShell +./build.cmd +``` + +## 3. Deploy + +``` +sls deploy +``` + +This will deploy your function to AWS Lambda based on the settings in `serverless.yml`. + +## 4. Invoke deployed function + +``` +sls invoke -f hello +``` + +Invoke deployed function with command `invoke` and `--function` or shorthand `-f`. + +In your terminal window you should see the response from AWS Lambda. + +```bash +{ + "Message": "Go Serverless v1.0! Your function executed successfully!", + "Request": { + "Key1": null, + "Key2": null, + "Key3": null + } +} +``` + +Congrats you have deployed and ran your Hello World function! diff --git a/docs/providers/aws/examples/hello-world/fsharp/serverless.yml b/docs/providers/aws/examples/hello-world/fsharp/serverless.yml index c90e1089a..6ef081deb 100644 --- a/docs/providers/aws/examples/hello-world/fsharp/serverless.yml +++ b/docs/providers/aws/examples/hello-world/fsharp/serverless.yml @@ -66,7 +66,7 @@ functions: # - schedule: rate(10 minutes) # - sns: greeter-topic # - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000 -# - alexaSkill +# - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx # - iot: # sql: "SELECT * FROM 'some_topic'" diff --git a/docs/providers/aws/examples/hello-world/go/README.md b/docs/providers/aws/examples/hello-world/go/README.md new file mode 100644 index 000000000..1720c009b --- /dev/null +++ b/docs/providers/aws/examples/hello-world/go/README.md @@ -0,0 +1,116 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/aws/examples/hello-world/go/) + + +# Hello World Go Example + +Make sure `serverless` is installed. [See installation guide](../../../guide/installation.md). + +Once installed the Serverless CLI can be called with `serverless` or the shorthand `sls` command. + +``` +$ sls + +Commands +* You can run commands with "serverless" or the shortcut "sls" +* Pass "--verbose" to this command to get in-depth plugin info +* Pass "--no-color" to disable CLI colors +* Pass "--help" after any for contextual help +``` + +You should also have [go](https://golang.org/doc/install) and [make](https://www.gnu.org/software/make/) + +It is always good practice to organize your `go` projects within [GOPATH](https://golang.org/doc/code.html#GOPATH), to maximize the benefits of go tooling. + +## 1. Create a service +There are two templates for `go`: + +The Serverless Framework includes starter templates for various languages and providers. There are two templates for `go`. + +#### [aws-go](https://github.com/serverless/serverless/tree/master/lib/plugins/create/templates/aws-go) + +`aws-go` fetches dependencies using standard `go get`. + +``` +sls create --template aws-go --path myService +``` + +#### [aws-go-dep](https://github.com/serverless/serverless/tree/master/lib/plugins/create/templates/aws-go-dep) + +`aws-go-dep` uses [go dep](https://github.com/golang/dep) and requires your project to be in `$GOPATH/src` + +``` +sls create --template aws-go-dep --path myService +``` + +Using the `create` command we can specify one of the available [templates](https://serverless.com/framework/docs/providers/aws/cli-reference/create#available-templates). For this example use aws-nodejs with the `--template` or shorthand `-t` flag. + +The `--path` or shorthand `-p` is the location to be created with the template service files. + +Change directories into 'myService' folder and you can see this project has 2 handler functions: `hello` and `world` split into 2 separate go packages (folders): + +``` +. +├── hello/ +│ └── main.go +├── world/ +│ └── main.go +``` + +This because a `main()` function is required as entry point for each handler executable. + +## 2. Build using go build to create static binaries + +Run `make build` to build both functions. Successful build should generate the following binaries: + +``` +. +├── bin/ +│ |── hello +│ └── world +``` + +## 3. Deploy + +``` +sls deploy +``` + +This will deploy your function to AWS Lambda based on the settings in `serverless.yml`. + +## 4. Invoke deployed function + +``` +sls invoke -f hello +``` + +``` +sls invoke -f world +``` + +Invoke either deployed function with command `invoke` and `--function` or shorthand `-f`. + +In your terminal window you should see the response from AWS Lambda. + +```bash +serverless invoke -f hello + +{ + "message": "Go Serverless v1.0! Your function executed successfully!" +} + +serverless invoke -f world + +{ + "message": "Okay so your other function also executed successfully!" +} +``` + +Congrats you have deployed and ran your Hello World function! diff --git a/docs/providers/aws/examples/hello-world/node/README.md b/docs/providers/aws/examples/hello-world/node/README.md index 792bd4e8d..fb927951f 100644 --- a/docs/providers/aws/examples/hello-world/node/README.md +++ b/docs/providers/aws/examples/hello-world/node/README.md @@ -13,18 +13,45 @@ layout: Doc Make sure `serverless` is installed. [See installation guide](../../../guide/installation.md). +Once installed the Serverless CLI can be called with `serverless` or the shorthand `sls` command. + +``` +$ sls + +Commands +* You can run commands with "serverless" or the shortcut "sls" +* Pass "--verbose" to this command to get in-depth plugin info +* Pass "--no-color" to disable CLI colors +* Pass "--help" after any for contextual help +``` + ## 1. Create a service -`serverless create --template aws-nodejs --path myService` or `sls create --template aws-nodejs --path myService`, where 'myService' is a new folder to be created with template service files. Change directories into this new folder. + +``` +sls create --template aws-nodejs --path myService +``` + +Using the `create` command we can specify one of the available [templates](https://serverless.com/framework/docs/providers/aws/cli-reference/create#available-templates). For this example use aws-nodejs with the `--template` or shorthand `-t` flag. + +The `--path` or shorthand `-p` is the location to be created with the template service files. Change directories into this new folder. ## 2. Deploy -`serverless deploy` or `sls deploy`. `sls` is shorthand for the Serverless CLI command + +``` +sls deploy +``` + +This will deploy your function to AWS Lambda based on the settings in `serverless.yml`. ## 3. Invoke deployed function -`serverless invoke --function hello` or `serverless invoke -f hello` -`-f` is shorthand for `--function` +``` +sls invoke -f hello +``` -In your terminal window you should see the response from AWS Lambda +Invoke deployed function with command `invoke` and `--function` or shorthand `-f`. + +In your terminal window you should see the response from AWS Lambda. ```bash { @@ -33,4 +60,4 @@ In your terminal window you should see the response from AWS Lambda } ``` -Congrats you have just deployed and run your Hello World function! +Congrats you have deployed and ran your Hello World function! diff --git a/docs/providers/aws/examples/hello-world/python/README.md b/docs/providers/aws/examples/hello-world/python/README.md index d1e432b60..c6380de14 100644 --- a/docs/providers/aws/examples/hello-world/python/README.md +++ b/docs/providers/aws/examples/hello-world/python/README.md @@ -13,18 +13,45 @@ layout: Doc Make sure `serverless` is installed. [See installation guide](../../../guide/installation.md). +Once installed the Serverless CLI can be called with `serverless` or the shorthand `sls` command. + +``` +$ sls + +Commands +* You can run commands with "serverless" or the shortcut "sls" +* Pass "--verbose" to this command to get in-depth plugin info +* Pass "--no-color" to disable CLI colors +* Pass "--help" after any for contextual help +``` + ## 1. Create a service -`serverless create --template aws-python --path myService` or `sls create --template aws-python --path myService`, where 'myService' is a new folder to be created with template service files. Change directories into this new folder. + +``` +sls create --template aws-python --path myService +``` + +Using the `create` command we can specify one of the available [templates](https://serverless.com/framework/docs/providers/aws/cli-reference/create#available-templates). For this example use aws-python with the `--template` or shorthand `-t` flag. + +The `--path` or shorthand `-p` is the location to be created with the template service files. Change directories into this new folder. ## 2. Deploy -`serverless deploy` or `sls deploy`. `sls` is shorthand for the serverless CLI command + +``` +sls deploy +``` + +This will deploy your function to AWS Lambda based on the settings in `serverless.yml`. ## 3. Invoke deployed function -`serverless invoke --function hello` or `serverless invoke -f hello` -`-f` is shorthand for `--function` +``` +sls invoke -f hello +``` -In your terminal window you should see the response from AWS Lambda +Invoke deployed function with command `invoke` and `--function` or shorthand `-f`. + +In your terminal window you should see the response from AWS Lambda. ```bash { @@ -33,4 +60,4 @@ In your terminal window you should see the response from AWS Lambda } ``` -Congrats you have just deployed and ran your Hello World function! +Congrats you have deployed and ran your Hello World function! diff --git a/docs/providers/aws/guide/functions.md b/docs/providers/aws/guide/functions.md index 2d6a970ed..468701dce 100644 --- a/docs/providers/aws/guide/functions.md +++ b/docs/providers/aws/guide/functions.md @@ -37,6 +37,7 @@ functions: runtime: python2.7 # optional overwrite, default is provider runtime memorySize: 512 # optional, in MB, default is 1024 timeout: 10 # optional, in seconds, default is 6 + reservedConcurrency: 5 # optional, reserved concurrency limit for this function. By default, AWS uses account concurrency limit ``` The `handler` property points to the file and module containing the code you want to run in your function. diff --git a/docs/providers/aws/guide/serverless.yml.md b/docs/providers/aws/guide/serverless.yml.md index c9f1c35a7..16608c5b7 100644 --- a/docs/providers/aws/guide/serverless.yml.md +++ b/docs/providers/aws/guide/serverless.yml.md @@ -159,7 +159,9 @@ functions: batchSize: 100 startingPosition: LATEST enabled: false - - alexaSkill + - alexaSkill: + appId: amzn1.ask.skill.xx-xx-xx-xx + enabled: true - alexaSmartHome: appId: amzn1.ask.skill.xx-xx-xx-xx enabled: true diff --git a/docs/providers/aws/guide/services.md b/docs/providers/aws/guide/services.md index 20a27a027..24d33dab1 100644 --- a/docs/providers/aws/guide/services.md +++ b/docs/providers/aws/guide/services.md @@ -63,6 +63,7 @@ Here are the available runtimes for AWS Lambda: * aws-scala-sbt * aws-csharp * aws-fsharp +* aws-go Check out the [create command docs](../cli-reference/create) for all the details and options. diff --git a/docs/providers/azure/examples/hello-world/node/README.md b/docs/providers/azure/examples/hello-world/node/README.md index 22fc16bb8..9c18f1818 100644 --- a/docs/providers/azure/examples/hello-world/node/README.md +++ b/docs/providers/azure/examples/hello-world/node/README.md @@ -39,4 +39,4 @@ In your terminal window you should see the response from azure } ``` -Congrats you have just deployed and run your Hello World function! +Congrats you have deployed and ran your Hello World function! diff --git a/docs/providers/google/cli-reference/invoke-local.md b/docs/providers/google/cli-reference/invoke-local.md new file mode 100644 index 000000000..54ab11701 --- /dev/null +++ b/docs/providers/google/cli-reference/invoke-local.md @@ -0,0 +1,56 @@ + + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/google/cli-reference/invoke-local) + + + +# Google - Invoke Local + +Invokes deployed function locally. It allows to send event data to the function, read logs and display other important information of the function invocation. + +```bash +serverless invoke -f functionName +``` + +## Options + +* `--function` or `-f` The name of the function in your service that you want to invoke. **Required**. + \_ `--data` or `-d` Data you want to pass into the function +* `--path` or `-p` Path to JSON or YAML file holding input data. This path is relative to the root directory of the service. +* `--raw` Pass data as a raw string even if it is JSON. If not set, JSON data are parsed and passed as an object. +* `--contextPath` or `-x`, The path to a json file holding input context to be passed to the invoked function. This path is relative to the root directory of the service. +* `--context` or `-c`, String data to be passed as a context to your function. Same like with `--data`, context included in `--contextPath` will overwrite the context you passed with `--context` flag. + +> Keep in mind that if you pass both `--path` and `--data`, the data included in the `--path` file will overwrite the data you passed with the `--data` flag. + +## Examples + +### Local function invocation + +```bash +serverless invoke local -f functionName +``` + +### Local function invocation with data + +```bash +serverless invoke local -f functionName -d '{ "data": "hello world" }' +``` + +### Local function invocation with data passing + +```bash +serverless invoke local -f functionName -p path/to/file.json + +# OR + +serverless invoke local -f functionName -p path/to/file.yaml +``` diff --git a/docs/providers/google/examples/hello-world/node/README.md b/docs/providers/google/examples/hello-world/node/README.md index 4f6f3b10f..5802b9779 100644 --- a/docs/providers/google/examples/hello-world/node/README.md +++ b/docs/providers/google/examples/hello-world/node/README.md @@ -31,8 +31,8 @@ Update the `credentials` and your `project` property in the `serverless.yml` fil ## 5. Invoke deployed function -`serverless invoke --function helloWorld` +`serverless invoke --function first` In your terminal window you should see a response from the Google Cloud -Congrats you have just deployed and run your Hello World function! +Congrats you have deployed and ran your Hello World function! diff --git a/docs/providers/kubeless/README.md b/docs/providers/kubeless/README.md index b9bba665a..aaf727677 100644 --- a/docs/providers/kubeless/README.md +++ b/docs/providers/kubeless/README.md @@ -30,6 +30,7 @@ If you have questions, join the [chat in gitter](https://gitter.im/serverless/se
  • Functions
  • Events
  • Deploying
  • +
  • Packaging
  • Debugging
  • Workflow
  • diff --git a/docs/providers/kubeless/guide/functions.md b/docs/providers/kubeless/guide/functions.md index b85619a26..8881110c8 100644 --- a/docs/providers/kubeless/guide/functions.md +++ b/docs/providers/kubeless/guide/functions.md @@ -25,6 +25,9 @@ service: my-service provider: name: kubeless runtime: python2.7 + memorySize: 512M # optional, maximum memory + timeout: 10 # optional, in seconds, default is 180 + namespace: funcions # optional, deployment namespace if not specified it uses "default" plugins: - serverless-kubeless @@ -34,7 +37,12 @@ functions: # and the K8s service object to get a request to call the function hello: # The function to call as a response to the HTTP event - handler: handler.hello + handler: handler.hello # required, handler set + description: Description of what the function does # optional, to set the description as an annotation + memorySize: 512M # optional, maximum memory + timeout: 10 # optional, in seconds, default is 180 + namespace: funcions # optional, deployment namespace, if not specified "default" will be used + port: 8081 # optional, deploy http-based function with a custom port, default is 8080 ``` The `handler` property points to the file and module containing the code you want to run in your function. @@ -88,3 +96,139 @@ The Kubeless provider plugin supports the following runtimes. Please see the following repository for sample projects using those runtimes: [https://github.com/serverless/serverless-kubeless/tree/master/examples](https://github.com/serverless/serverless-kubeless/tree/master/examples) + +## Installing dependencies + +For installing dependencies the standard dependency file should be placed in the function folder: + + - For Python functions, it will use the file `requirements.txt` + - For Nodejs functions, `dependencies` in the `package.json` file will be installed + - For Ruby functions, it will use the file `Gemfile.rb` + +If one of the above files is found, the depencies will be installed using a [`Init Container`](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/). + +## 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: + +```yml +# serverless.yml +service: service-name +provider: kubeless +plugins: + - serverless-kubeless + +functions: + hello: + handler: handler.hello + environment: + TABLE_NAME: tableName +``` + +Or if you want to apply environment variable configuration to all functions in your service, you can add the configuration to the higher level `provider` object. Environment variables configured at the function level are merged with those at the provider level, so your function with specific environment variables will also have access to the environment variables defined at the provider level. If an environment variable with the same key is defined at both the function and provider levels, the function-specific value overrides the provider-level default value. For example: + +```yml +# serverless.yml +service: service-name +provider: + name: kubeless + environment: + SYSTEM_NAME: mySystem + TABLE_NAME: tableName1 + +plugins: + - serverless-kubeless + +functions: + hello: + # this function will have SYSTEM_NAME=mySystem and TABLE_NAME=tableName1 from the provider-level environment config above + handler: handler.hello + users: + # this function will have SYSTEM_NAME=mySystem from the provider-level environment config above + # but TABLE_NAME will be tableName2 because this more specific config will override the default above + handler: handler.users + environment: + TABLE_NAME: tableName2 +``` + +## Labels + +Using the `labels` configuration makes it possible to add `key` / `value` labels to your functions. + +Those labels will appear in deployments, services and pods and will make it easier to group functions by label or find functions with a common label. + +```yml +provider: + name: kubeless + +plugins: + - serverless-kubeless + +functions: + hello: + handler: handler.hello + labels: + foo: bar +``` + +## Custom hostname and path + +It is possible to define a custom hostname and path that will be used to serve a function in a specific endpoint. For doing this, it is necessary to have an [Ingress Controller](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-controllers) available in the cluster. + +```yml +provider: + name: kubeless + hostname: myhostname.io +plugins: + - serverless-kubeless + +functions: + hello: + handler: handler.hello + events: + - http: + path: /hello +``` + +In the example above, once the Ingress Rule has been processed by the Ingress controller, you can call the function using as endpoing `myhostname.io/hello`. + +If no hostname is given but a function specifies a `path`, the plugin will use the IP of the cluster followed by a DNS mapping service. By default [nip.io](http://nip.io) will be used but this can be configured with the property `defaultDNSResolution`. + +```yml +provider: + name: kubeless + defaultDNSResolution: 'xip.io' +plugins: + - serverless-kubeless + +functions: + hello: + handler: handler.hello + events: + - http: + path: /hello +``` + +The above will result in an endpoint like `1.2.3.4.xip.io/hello` where `1.2.3.4` is the IP of the cluster server. + +The final URL in which the function will be listening can be retrieved executing `serverless info`. + +## Custom images (alpha feature) + +It is possible to skip the Kubeless build system and specify a prebuilt image to run a function. This feature is useful for using Kubeless with languages that are still not supported or if the function package [is over 1MB](./packaging.md#package-maximum-size). To get more information about how to use custom images visit the [upstream documentation](https://github.com/kubeless/kubeless/blob/master/docs/runtimes.md#custom-runtime-alpha). + +```yml +service: hello + +provider: + name: kubeless + runtime: python2.7 + +plugins: + - serverless-kubeless + +functions: + hello: + handler: handler.hello + image: tuna/kubeless-python:0.0.6 +``` diff --git a/docs/providers/kubeless/guide/packaging.md b/docs/providers/kubeless/guide/packaging.md new file mode 100644 index 000000000..2b788756c --- /dev/null +++ b/docs/providers/kubeless/guide/packaging.md @@ -0,0 +1,144 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/kubeless/guide/packaging) + + +# Kubeless - Packaging + +## Package CLI Command + +Using the Serverless CLI tool, you can package your project without deploying with Kubeless. This is best used with CI / CD workflows to ensure consistent deployable artifacts. + +Running the following command will build and save all of the deployment artifacts in the service's .serverless directory: + +```bash +serverless package +``` + +However, you can also use the --package option to add a destination path and Serverless will store your deployment artifacts there (./my-artifacts in the following case): + +```bash +serverless package --package my-artifacts +``` + +## Package Maximum Size + +Kubeless uses [Kubernetes ConfigMaps](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/) to store functions configuration and code. These ConfigMaps have a limitation in size of one MB since they are stored as a single entry in a `etcd` database. Due to this limitation, the maximum possible size for a Kubeless function package is no more than one MB. Note that only code and configuration files should be included in this package, dependencies will be installed during the build process. If your function package size is over one MB please [exclude some directories](#exclude-include) or create a [custom image](./functions#custom-images-alpha-feature) with your function. By default, files under the `node_modules` folder will be excluded. + +## Package Configuration + +Sometimes you might like to have more control over your function artifacts and how they are packaged. + +You can use the `package` and `exclude` configuration for more control over the packaging process. + +### Exclude / include + +Exclude and include allows you to define globs that will be excluded / included from the resulting artifact. If you wish to +include files you can use a glob pattern prefixed with `!` such as `!re-include-me/**` in `exclude` or the dedicated `include` config. +Serverless will run the glob patterns in order. + +At first it will apply the globs defined in `exclude`. After that it'll add all the globs from `include`. This way you can always re-include +previously excluded files and directories. + +### Examples + +Exclude all node_modules but then re-include a specific modules (in this case node-fetch) using `exclude` exclusively + +``` yml +package: + exclude: + - node_modules/** + - '!node_modules/node-fetch/**' +``` + +Exclude all files but `handler.js` using `exclude` and `include` + +``` yml +package: + exclude: + - src/** + include: + - src/function/handler.js +``` + +**Note:** Don't forget to use the correct glob syntax if you want to exclude directories + +``` +exclude: + - tmp/** + - .git/** +``` + +### Artifact + +For complete control over the packaging process you can specify your own artifact zip file. +Serverless won't zip your service if this is configured and therefore `exclude` and `include` will be ignored. Either you use artifact or include / exclude. + +The artifact option is especially useful in case your development environment allows you to generate a deployable artifact like Maven does for Java. + +### Example + +```yml +service: my-service +package: + artifact: path/to/my-artifact.zip +``` + +### Packaging functions separately + +If you want even more controls over your functions for deployment you can configure them to be packaged independently. This allows you more control for optimizing your deployment. To enable individual packaging set `individually` to true in the service or function wide packaging settings. + +Then for every function you can use the same `exclude`, `include` or `artifact` config options as you can service wide. The `exclude` and `include` option will be merged with the service wide options to create one `exclude` and `include` config per function during packaging. + +```yml +service: my-service +package: + individually: true + exclude: + - excluded-by-default.json +functions: + hello: + handler: handler.hello + package: + # We're including this file so it will be in the final package of this function only + include: + - excluded-by-default.json + world: + handler: handler.hello + package: + exclude: + - some-file.js +``` + +You can also select which functions to be packaged separately, and have the rest use the service package by setting the `individually` flag at the function level: + +```yml +service: my-service +functions: + hello: + handler: handler.hello + world: + handler: handler.hello + package: + individually: true +``` + +### Development dependencies + +Serverless will auto-detect and exclude development dependencies based on the runtime your service is using. + +This ensures that only the production relevant packages and modules are included in your zip file. Doing this drastically reduces the overall size of the deployment package which will be uploaded to the cloud provider. + +You can opt-out of automatic dev dependency exclusion by setting the `excludeDevDependencies` package config to `false`: + +```yml +package: + excludeDevDependencies: false +``` diff --git a/docs/providers/openwhisk/examples/hello-world/node/README.md b/docs/providers/openwhisk/examples/hello-world/node/README.md index f9a6d3999..4eadbc600 100644 --- a/docs/providers/openwhisk/examples/hello-world/node/README.md +++ b/docs/providers/openwhisk/examples/hello-world/node/README.md @@ -35,4 +35,4 @@ In your terminal window you should see the response from Apache OpenWhisk } ``` -Congrats you have just deployed and run your Hello World function! +Congrats you have deployed and ran your Hello World function! diff --git a/docs/providers/openwhisk/examples/hello-world/php/README.md b/docs/providers/openwhisk/examples/hello-world/php/README.md index c254887bf..4ace5e28f 100644 --- a/docs/providers/openwhisk/examples/hello-world/php/README.md +++ b/docs/providers/openwhisk/examples/hello-world/php/README.md @@ -35,4 +35,4 @@ In your terminal window you should see the response from Apache OpenWhisk } ``` -Congrats you have just deployed and run your Hello World function! +Congrats you have deployed and ran your Hello World function! diff --git a/docs/providers/openwhisk/examples/hello-world/python/README.md b/docs/providers/openwhisk/examples/hello-world/python/README.md index 1056a8060..35faa3edf 100644 --- a/docs/providers/openwhisk/examples/hello-world/python/README.md +++ b/docs/providers/openwhisk/examples/hello-world/python/README.md @@ -35,4 +35,4 @@ In your terminal window you should see the response from Apache OpenWhisk } ``` -Congrats you have just deployed and run your Hello World function! +Congrats you have deployed and ran your Hello World function! diff --git a/docs/providers/openwhisk/examples/hello-world/swift/README.md b/docs/providers/openwhisk/examples/hello-world/swift/README.md index 93dec3d30..6dc26cfda 100644 --- a/docs/providers/openwhisk/examples/hello-world/swift/README.md +++ b/docs/providers/openwhisk/examples/hello-world/swift/README.md @@ -35,4 +35,4 @@ In your terminal window you should see the response from Apache OpenWhisk } ``` -Congrats you have just deployed and run your Hello World function! +Congrats you have deployed and ran your Hello World function! diff --git a/docs/providers/spotinst/examples/java8/README.md b/docs/providers/spotinst/examples/java8/README.md index 34e9f2b30..8de2ab42b 100644 --- a/docs/providers/spotinst/examples/java8/README.md +++ b/docs/providers/spotinst/examples/java8/README.md @@ -32,7 +32,7 @@ In your terminal window you should see the response {"hello":"null"} ``` -Congrats you have just deployed and ran your Hello World function! +Congrats you have deployed and ran your Hello World function! ## Short Hand Guide diff --git a/docs/providers/spotinst/examples/node/README.md b/docs/providers/spotinst/examples/node/README.md index a4b8f43d7..cd8f956d7 100644 --- a/docs/providers/spotinst/examples/node/README.md +++ b/docs/providers/spotinst/examples/node/README.md @@ -32,7 +32,7 @@ In your terminal window you should see the response {"hello":"from NodeJS8.3 function"} ``` -Congrats you have just deployed and ran your Hello World function! +Congrats you have deployed and ran your Hello World function! ## Short Hand Guide diff --git a/docs/providers/spotinst/examples/python/README.md b/docs/providers/spotinst/examples/python/README.md index c4f58a628..a6ae89236 100644 --- a/docs/providers/spotinst/examples/python/README.md +++ b/docs/providers/spotinst/examples/python/README.md @@ -34,7 +34,7 @@ In your terminal window you should see the response '{"hello":"from Python2.7 function"}' ``` -Congrats you have just deployed and ran your Hello World function! +Congrats you have deployed and ran your Hello World function! ## Short Hand Guide diff --git a/docs/providers/spotinst/examples/ruby/README.md b/docs/providers/spotinst/examples/ruby/README.md index e0d7d2e2d..a7325a394 100644 --- a/docs/providers/spotinst/examples/ruby/README.md +++ b/docs/providers/spotinst/examples/ruby/README.md @@ -11,21 +11,21 @@ layout: Doc # Hello World Ruby Example -Make sure `serverless` is installed. +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 Ruby 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. You will need to go into the serverless.yml file and add in the environment variable that you want to deploy into. ## 2. Deploy -```bash +```bash serverless deploy ``` ## 3. Invoke deployed function ```bash serverless invoke --function hello -``` +``` In your terminal window you should see the response @@ -33,14 +33,14 @@ In your terminal window you should see the response '{"hello":"from Ruby2.4.1 function"}' ``` -Congrats you have just deployed and ran your Hello World function! +Congrats you have deployed and ran your Hello World function! ## Short Hand Guide -`sls` is short hand for serverless cli commands +`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` \ No newline at end of file +`-p` is short hang for `--path` diff --git a/lib/classes/Error.js b/lib/classes/Error.js index 47e21897c..53a97e7c0 100644 --- a/lib/classes/Error.js +++ b/lib/classes/Error.js @@ -19,7 +19,7 @@ const writeMessage = (messageType, message) => { consoleLog(' '); if (message) { - consoleLog(chalk.white(` ${message}`)); + consoleLog(` ${message}`); } consoleLog(' '); @@ -61,17 +61,16 @@ module.exports.logError = (e) => { consoleLog(' '); } - const platform = chalk.white(process.platform); - const nodeVersion = chalk.white(process.version.replace(/^[v|V]/, '')); - const slsVersion = chalk.white(version); + const platform = process.platform; + const nodeVersion = process.version.replace(/^[v|V]/, ''); + const slsVersion = version; consoleLog(chalk.yellow(' Get Support --------------------------------------------')); - consoleLog(`${chalk.yellow(' Docs: ')}${chalk.white('docs.serverless.com')}`); - consoleLog(`${chalk.yellow(' Bugs: ')}${chalk - .white('github.com/serverless/serverless/issues')}`); - consoleLog(`${chalk.yellow(' Forums: ')}${chalk.white('forum.serverless.com')}`); - consoleLog(`${chalk.yellow(' Chat: ')}${chalk - .white('gitter.im/serverless/serverless')}`); + consoleLog(`${chalk.yellow(' Docs: ')}${'docs.serverless.com'}`); + consoleLog(`${chalk.yellow(' Bugs: ')}${ + 'github.com/serverless/serverless/issues'}`); + consoleLog(`${chalk.yellow(' Forums: ')}${'forum.serverless.com'}`); + consoleLog(`${chalk.yellow(' Chat: ')}${'gitter.im/serverless/serverless'}`); consoleLog(' '); consoleLog(chalk.yellow(' Your Environment Information -----------------------------')); diff --git a/lib/plugins/aws/deploy/lib/createStack.test.js b/lib/plugins/aws/deploy/lib/createStack.test.js index 28d5820e9..370422d73 100644 --- a/lib/plugins/aws/deploy/lib/createStack.test.js +++ b/lib/plugins/aws/deploy/lib/createStack.test.js @@ -6,7 +6,6 @@ const path = require('path'); const AwsProvider = require('../../provider/awsProvider'); const AwsDeploy = require('../index'); const Serverless = require('../../../../Serverless'); -const BbPromise = require('bluebird'); const testUtils = require('../../../../../tests/utils'); describe('createStack', () => { @@ -98,8 +97,7 @@ describe('createStack', () => { it('should set the createLater flag and resolve if deployment bucket is provided', () => { awsDeploy.serverless.service.provider.deploymentBucket = 'serverless'; - sandbox.stub(awsDeploy.provider, 'request') - .returns(BbPromise.reject({ message: 'does not exist' })); + sandbox.stub(awsDeploy.provider, 'request').rejects(new Error('does not exist')); return awsDeploy.createStack().then(() => { expect(awsDeploy.createLater).to.equal(true); diff --git a/lib/plugins/aws/deploy/lib/extendedValidate.test.js b/lib/plugins/aws/deploy/lib/extendedValidate.test.js index 7b1d3d9ad..7cbabdc5f 100644 --- a/lib/plugins/aws/deploy/lib/extendedValidate.test.js +++ b/lib/plugins/aws/deploy/lib/extendedValidate.test.js @@ -1,6 +1,6 @@ 'use strict'; -const expect = require('chai').expect; +const chai = require('chai'); const sinon = require('sinon'); const path = require('path'); const AwsProvider = require('../../provider/awsProvider'); @@ -8,6 +8,10 @@ const AwsDeploy = require('../index'); const Serverless = require('../../../../Serverless'); const testUtils = require('../../../../../tests/utils'); +chai.use(require('sinon-chai')); + +const expect = chai.expect; + describe('extendedValidate', () => { let awsDeploy; const tmpDirPath = testUtils.getTmpDirPath(); @@ -60,8 +64,8 @@ describe('extendedValidate', () => { }); afterEach(() => { - awsDeploy.serverless.utils.fileExistsSync.restore(); - awsDeploy.serverless.utils.readFileSync.restore(); + fileExistsSyncStub.restore(); + readFileSyncStub.restore(); }); it('should throw error if state file does not exist', () => { diff --git a/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js b/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js index df96e23ff..7c907b345 100644 --- a/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js +++ b/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js @@ -90,8 +90,8 @@ describe('uploadArtifacts', () => { }); afterEach(() => { - normalizeFiles.normalizeCloudFormationTemplate.restore(); - awsDeploy.provider.request.restore(); + normalizeCloudFormationTemplateStub.restore(); + uploadStub.restore(); }); it('should upload the CloudFormation file to the S3 bucket', () => { @@ -159,8 +159,8 @@ describe('uploadArtifacts', () => { }); afterEach(() => { - fs.readFileSync.restore(); - awsDeploy.provider.request.restore(); + readFileSyncStub.restore(); + uploadStub.restore(); }); it('should throw for null artifact paths', () => { @@ -265,7 +265,7 @@ describe('uploadArtifacts', () => { .to.be.equal(awsDeploy.serverless.service.functions.first.package.artifact); expect(uploadZipFileStub.args[1][0]) .to.be.equal(awsDeploy.serverless.service.functions.second.package.artifact); - awsDeploy.uploadZipFile.restore(); + uploadZipFileStub.restore(); }); }); @@ -286,7 +286,7 @@ describe('uploadArtifacts', () => { const uploadZipFileStub = sinon .stub(awsDeploy, 'uploadZipFile').resolves(); - sinon.stub(fs, 'statSync').returns({ size: 1024 }); + const statSyncStub = sinon.stub(fs, 'statSync').returns({ size: 1024 }); return awsDeploy.uploadFunctions().then(() => { expect(uploadZipFileStub.calledTwice).to.be.equal(true); @@ -294,8 +294,9 @@ describe('uploadArtifacts', () => { .to.be.equal(awsDeploy.serverless.service.functions.first.package.artifact); expect(uploadZipFileStub.args[1][0]) .to.be.equal(awsDeploy.serverless.service.package.artifact); - awsDeploy.uploadZipFile.restore(); - fs.statSync.restore(); + }).finally(() => { + uploadZipFileStub.restore(); + statSyncStub.restore(); }); }); @@ -303,16 +304,16 @@ describe('uploadArtifacts', () => { awsDeploy.serverless.config.servicePath = 'some/path'; awsDeploy.serverless.service.service = 'new-service'; - sinon.stub(fs, 'statSync').returns({ size: 1024 }); - sinon.stub(awsDeploy, 'uploadZipFile').resolves(); + const statSyncStub = sinon.stub(fs, 'statSync').returns({ size: 1024 }); + const uploadZipFileStub = sinon.stub(awsDeploy, 'uploadZipFile').resolves(); sinon.spy(awsDeploy.serverless.cli, 'log'); return awsDeploy.uploadFunctions().then(() => { const expected = 'Uploading service .zip file to S3 (1 KB)...'; expect(awsDeploy.serverless.cli.log.calledWithExactly(expected)).to.be.equal(true); - - fs.statSync.restore(); - awsDeploy.uploadZipFile.restore(); + }).finally(() => { + statSyncStub.restore(); + uploadZipFileStub.restore(); }); }); }); diff --git a/lib/plugins/aws/invoke/index.js b/lib/plugins/aws/invoke/index.js index 58cd837d9..2354a1d28 100644 --- a/lib/plugins/aws/invoke/index.js +++ b/lib/plugins/aws/invoke/index.js @@ -83,12 +83,12 @@ class AwsInvoke { } log(invocationReply) { - const color = !invocationReply.FunctionError ? 'white' : 'red'; + const color = !invocationReply.FunctionError ? (x => x) : chalk.red; if (invocationReply.Payload) { const response = JSON.parse(invocationReply.Payload); - this.consoleLog(chalk[color](JSON.stringify(response, null, 4))); + this.consoleLog(color(JSON.stringify(response, null, 4))); } if (invocationReply.LogResult) { diff --git a/lib/plugins/aws/invoke/index.test.js b/lib/plugins/aws/invoke/index.test.js index 8f3a7bcda..915911695 100644 --- a/lib/plugins/aws/invoke/index.test.js +++ b/lib/plugins/aws/invoke/index.test.js @@ -6,7 +6,6 @@ const path = require('path'); const AwsInvoke = require('./index'); const AwsProvider = require('../provider/awsProvider'); const Serverless = require('../../../Serverless'); -const chalk = require('chalk'); const testUtils = require('../../../../tests/utils'); describe('AwsInvoke', () => { @@ -271,7 +270,7 @@ describe('AwsInvoke', () => { }; return awsInvoke.log(invocationReplyMock).then(() => { - const expectedPayloadMessage = `${chalk.white('{\n "testProp": "testValue"\n}')}`; + const expectedPayloadMessage = '{\n "testProp": "testValue"\n}'; expect(consoleLogStub.calledWith(expectedPayloadMessage)).to.equal(true); }); diff --git a/lib/plugins/aws/lib/naming.js b/lib/plugins/aws/lib/naming.js index f2c81c776..efb408408 100644 --- a/lib/plugins/aws/lib/naming.js +++ b/lib/plugins/aws/lib/naming.js @@ -282,8 +282,9 @@ module.exports = { getLambdaApiGatewayPermissionLogicalId(functionName) { return `${this.getNormalizedFunctionName(functionName)}LambdaPermissionApiGateway`; }, - getLambdaAlexaSkillPermissionLogicalId(functionName) { - return `${this.getNormalizedFunctionName(functionName)}LambdaPermissionAlexaSkill`; + getLambdaAlexaSkillPermissionLogicalId(functionName, alexaSkillIndex) { + return `${this.getNormalizedFunctionName(functionName)}LambdaPermissionAlexaSkill${ + alexaSkillIndex || '0'}`; }, getLambdaAlexaSmartHomePermissionLogicalId(functionName, alexaSmartHomeIndex) { return `${this.getNormalizedFunctionName(functionName)}LambdaPermissionAlexaSmartHome${ diff --git a/lib/plugins/aws/lib/naming.test.js b/lib/plugins/aws/lib/naming.test.js index 7becb1438..530d0621e 100644 --- a/lib/plugins/aws/lib/naming.test.js +++ b/lib/plugins/aws/lib/naming.test.js @@ -464,9 +464,15 @@ describe('#naming()', () => { describe('#getLambdaAlexaSkillPermissionLogicalId()', () => { it('should normalize the function name and append the standard suffix', + () => { + expect(sdk.naming.getLambdaAlexaSkillPermissionLogicalId('functionName', 2)) + .to.equal('FunctionNameLambdaPermissionAlexaSkill2'); + }); + + it('should normalize the function name and append a default suffix if not defined', () => { expect(sdk.naming.getLambdaAlexaSkillPermissionLogicalId('functionName')) - .to.equal('FunctionNameLambdaPermissionAlexaSkill'); + .to.equal('FunctionNameLambdaPermissionAlexaSkill0'); }); }); diff --git a/lib/plugins/aws/package/compile/events/alexaSkill/index.js b/lib/plugins/aws/package/compile/events/alexaSkill/index.js index ecc32cb44..89c863bb4 100644 --- a/lib/plugins/aws/package/compile/events/alexaSkill/index.js +++ b/lib/plugins/aws/package/compile/events/alexaSkill/index.js @@ -15,10 +15,45 @@ class AwsCompileAlexaSkillEvents { compileAlexaSkillEvents() { this.serverless.service.getAllFunctions().forEach((functionName) => { const functionObj = this.serverless.service.getFunction(functionName); + let alexaSkillNumberInFunction = 0; if (functionObj.events) { functionObj.events.forEach(event => { - if (event === 'alexaSkill') { + if (event === 'alexaSkill' || event.alexaSkill) { + let enabled = true; + let appId; + if (event === 'alexaSkill') { + const warningMessage = [ + 'Warning! You are using an old syntax for alexaSkill which doesn\'t', + ' restrict the invocation solely to your skill.', + ' Please refer to the documentation for additional information.', + ].join(''); + this.serverless.cli.log(warningMessage); + } else if (_.isString(event.alexaSkill)) { + appId = event.alexaSkill; + } else if (_.isPlainObject(event.alexaSkill)) { + if (!_.isString(event.alexaSkill.appId)) { + const errorMessage = [ + `Missing "appId" property for alexaSkill event in function ${functionName}`, + ' The correct syntax is: appId: amzn1.ask.skill.xx-xx-xx-xx-xx', + ' OR an object with "appId" property.', + ' Please check the docs for more info.', + ].join(''); + throw new this.serverless.classes.Error(errorMessage); + } + appId = event.alexaSkill.appId; + // Parameter `enabled` is optional, hence the explicit non-equal check for false. + enabled = event.alexaSkill.enabled !== false; + } else { + const errorMessage = [ + `Alexa Skill event of function "${functionName}" is not an object or string.`, + ' The correct syntax is: alexaSkill.', + ' Please check the docs for more info.', + ].join(''); + throw new this.serverless.classes.Error(errorMessage); + } + alexaSkillNumberInFunction++; + const lambdaLogicalId = this.provider.naming .getLambdaLogicalId(functionName); @@ -31,13 +66,18 @@ class AwsCompileAlexaSkillEvents { 'Arn', ], }, - Action: 'lambda:InvokeFunction', + Action: enabled ? 'lambda:InvokeFunction' : 'lambda:DisableInvokeFunction', Principal: 'alexa-appkit.amazon.com', }, }; + if (appId) { + permissionTemplate.Properties.EventSourceToken = appId.replace(/\\n|\\r/g, ''); + } + const lambdaPermissionLogicalId = this.provider.naming - .getLambdaAlexaSkillPermissionLogicalId(functionName); + .getLambdaAlexaSkillPermissionLogicalId(functionName, + alexaSkillNumberInFunction); const permissionCloudForamtionResource = { [lambdaPermissionLogicalId]: permissionTemplate, @@ -45,13 +85,6 @@ class AwsCompileAlexaSkillEvents { _.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources, permissionCloudForamtionResource); - } else if (event.alexaSkill) { - const errorMessage = [ - `Alexa Skill event of function "${functionName}" is not an object or string.`, - ' The correct syntax is: alexaSkill.', - ' Please check the docs for more info.', - ].join(''); - throw new this.serverless.classes.Error(errorMessage); } }); } diff --git a/lib/plugins/aws/package/compile/events/alexaSkill/index.test.js b/lib/plugins/aws/package/compile/events/alexaSkill/index.test.js index 338a9fd08..cb9090ae6 100644 --- a/lib/plugins/aws/package/compile/events/alexaSkill/index.test.js +++ b/lib/plugins/aws/package/compile/events/alexaSkill/index.test.js @@ -1,5 +1,7 @@ 'use strict'; +/* eslint-disable no-unused-expressions */ + const expect = require('chai').expect; const AwsProvider = require('../../../../provider/awsProvider'); const AwsCompileAlexaSkillEvents = require('./index'); @@ -8,11 +10,19 @@ const Serverless = require('../../../../../../Serverless'); describe('AwsCompileAlexaSkillEvents', () => { let serverless; let awsCompileAlexaSkillEvents; + let consolePrinted; beforeEach(() => { serverless = new Serverless(); serverless.service.provider.compiledCloudFormationTemplate = { Resources: {} }; serverless.setProvider('aws', new AwsProvider(serverless)); + consolePrinted = ''; + serverless.cli = { + // serverless.cli isn't available in tests, so we will mimic it. + log: txt => { + consolePrinted += `${txt}\r\n`; + }, + }; awsCompileAlexaSkillEvents = new AwsCompileAlexaSkillEvents(serverless); }); @@ -25,7 +35,42 @@ describe('AwsCompileAlexaSkillEvents', () => { }); describe('#compileAlexaSkillEvents()', () => { - it('should throw an error if alexaSkill event is not an string', () => { + it('should show a warning if alexaSkill appId is not specified', () => { + awsCompileAlexaSkillEvents.serverless.service.functions = { + first: { + events: [ + 'alexaSkill', + ], + }, + }; + + awsCompileAlexaSkillEvents.compileAlexaSkillEvents(); + + expect(consolePrinted).to.contain.string('old syntax for alexaSkill'); + + expect(awsCompileAlexaSkillEvents.serverless.service + .provider.compiledCloudFormationTemplate.Resources + .FirstLambdaPermissionAlexaSkill1.Type + ).to.equal('AWS::Lambda::Permission'); + expect(awsCompileAlexaSkillEvents.serverless.service + .provider.compiledCloudFormationTemplate.Resources + .FirstLambdaPermissionAlexaSkill1.Properties.FunctionName + ).to.deep.equal({ 'Fn::GetAtt': ['FirstLambdaFunction', 'Arn'] }); + expect(awsCompileAlexaSkillEvents.serverless.service + .provider.compiledCloudFormationTemplate.Resources + .FirstLambdaPermissionAlexaSkill1.Properties.Action + ).to.equal('lambda:InvokeFunction'); + expect(awsCompileAlexaSkillEvents.serverless.service + .provider.compiledCloudFormationTemplate.Resources + .FirstLambdaPermissionAlexaSkill1.Properties.Principal + ).to.equal('alexa-appkit.amazon.com'); + expect(awsCompileAlexaSkillEvents.serverless.service + .provider.compiledCloudFormationTemplate.Resources + .FirstLambdaPermissionAlexaSkill1.Properties.EventSourceToken + ).to.be.undefined; + }); + + it('should throw an error if alexaSkill event is not a string or an object', () => { awsCompileAlexaSkillEvents.serverless.service.functions = { first: { events: [ @@ -39,11 +84,36 @@ describe('AwsCompileAlexaSkillEvents', () => { expect(() => awsCompileAlexaSkillEvents.compileAlexaSkillEvents()).to.throw(Error); }); - it('should create corresponding resources when a alexaSkill event is provided', () => { + it('should throw an error if alexaSkill event appId is not a string', () => { awsCompileAlexaSkillEvents.serverless.service.functions = { first: { events: [ - 'alexaSkill', + { + alexaSkill: { + appId: 42, + }, + }, + ], + }, + }; + + expect(() => awsCompileAlexaSkillEvents.compileAlexaSkillEvents()).to.throw(Error); + }); + + it('should create corresponding resources when multiple alexaSkill events are provided', () => { + const skillId1 = 'amzn1.ask.skill.xx-xx-xx-xx'; + const skillId2 = 'amzn1.ask.skill.yy-yy-yy-yy'; + awsCompileAlexaSkillEvents.serverless.service.functions = { + first: { + events: [ + { + alexaSkill: skillId1, + }, + { + alexaSkill: { + appId: skillId2, + }, + }, ], }, }; @@ -52,20 +122,84 @@ describe('AwsCompileAlexaSkillEvents', () => { expect(awsCompileAlexaSkillEvents.serverless.service .provider.compiledCloudFormationTemplate.Resources - .FirstLambdaPermissionAlexaSkill.Type + .FirstLambdaPermissionAlexaSkill1.Type ).to.equal('AWS::Lambda::Permission'); expect(awsCompileAlexaSkillEvents.serverless.service .provider.compiledCloudFormationTemplate.Resources - .FirstLambdaPermissionAlexaSkill.Properties.FunctionName + .FirstLambdaPermissionAlexaSkill1.Properties.FunctionName ).to.deep.equal({ 'Fn::GetAtt': ['FirstLambdaFunction', 'Arn'] }); expect(awsCompileAlexaSkillEvents.serverless.service .provider.compiledCloudFormationTemplate.Resources - .FirstLambdaPermissionAlexaSkill.Properties.Action + .FirstLambdaPermissionAlexaSkill1.Properties.Action ).to.equal('lambda:InvokeFunction'); expect(awsCompileAlexaSkillEvents.serverless.service .provider.compiledCloudFormationTemplate.Resources - .FirstLambdaPermissionAlexaSkill.Properties.Principal + .FirstLambdaPermissionAlexaSkill1.Properties.Principal ).to.equal('alexa-appkit.amazon.com'); + expect(awsCompileAlexaSkillEvents.serverless.service + .provider.compiledCloudFormationTemplate.Resources + .FirstLambdaPermissionAlexaSkill1.Properties.EventSourceToken + ).to.equal(skillId1); + + expect(awsCompileAlexaSkillEvents.serverless.service + .provider.compiledCloudFormationTemplate.Resources + .FirstLambdaPermissionAlexaSkill2.Type + ).to.equal('AWS::Lambda::Permission'); + expect(awsCompileAlexaSkillEvents.serverless.service + .provider.compiledCloudFormationTemplate.Resources + .FirstLambdaPermissionAlexaSkill2.Properties.FunctionName + ).to.deep.equal({ 'Fn::GetAtt': ['FirstLambdaFunction', 'Arn'] }); + expect(awsCompileAlexaSkillEvents.serverless.service + .provider.compiledCloudFormationTemplate.Resources + .FirstLambdaPermissionAlexaSkill2.Properties.Action + ).to.equal('lambda:InvokeFunction'); + expect(awsCompileAlexaSkillEvents.serverless.service + .provider.compiledCloudFormationTemplate.Resources + .FirstLambdaPermissionAlexaSkill2.Properties.Principal + ).to.equal('alexa-appkit.amazon.com'); + expect(awsCompileAlexaSkillEvents.serverless.service + .provider.compiledCloudFormationTemplate.Resources + .FirstLambdaPermissionAlexaSkill2.Properties.EventSourceToken + ).to.equal(skillId2); + }); + + it('should create corresponding resources when a disabled alexaSkill event is provided', () => { + const skillId1 = 'amzn1.ask.skill.xx-xx-xx-xx'; + awsCompileAlexaSkillEvents.serverless.service.functions = { + first: { + events: [ + { + alexaSkill: { + appId: skillId1, + enabled: false, + }, + }, + ], + }, + }; + + awsCompileAlexaSkillEvents.compileAlexaSkillEvents(); + + expect(awsCompileAlexaSkillEvents.serverless.service + .provider.compiledCloudFormationTemplate.Resources + .FirstLambdaPermissionAlexaSkill1.Type + ).to.equal('AWS::Lambda::Permission'); + expect(awsCompileAlexaSkillEvents.serverless.service + .provider.compiledCloudFormationTemplate.Resources + .FirstLambdaPermissionAlexaSkill1.Properties.FunctionName + ).to.deep.equal({ 'Fn::GetAtt': ['FirstLambdaFunction', 'Arn'] }); + expect(awsCompileAlexaSkillEvents.serverless.service + .provider.compiledCloudFormationTemplate.Resources + .FirstLambdaPermissionAlexaSkill1.Properties.Action + ).to.equal('lambda:DisableInvokeFunction'); + expect(awsCompileAlexaSkillEvents.serverless.service + .provider.compiledCloudFormationTemplate.Resources + .FirstLambdaPermissionAlexaSkill1.Properties.Principal + ).to.equal('alexa-appkit.amazon.com'); + expect(awsCompileAlexaSkillEvents.serverless.service + .provider.compiledCloudFormationTemplate.Resources + .FirstLambdaPermissionAlexaSkill1.Properties.EventSourceToken + ).to.equal(skillId1); }); it('should not create corresponding resources when alexaSkill event is not given', () => { @@ -82,5 +216,22 @@ describe('AwsCompileAlexaSkillEvents', () => { .compiledCloudFormationTemplate.Resources ).to.deep.equal({}); }); + + it('should not not throw error when other events are present', () => { + awsCompileAlexaSkillEvents.serverless.service.functions = { + first: { + events: [ + { + http: { + method: 'get', + path: '/', + }, + }, + ], + }, + }; + + expect(() => awsCompileAlexaSkillEvents.compileAlexaSkillEvents()).to.not.throw(); + }); }); }); diff --git a/lib/plugins/aws/package/compile/events/cognitoUserPool/index.js b/lib/plugins/aws/package/compile/events/cognitoUserPool/index.js index d94063701..76f0a4c05 100644 --- a/lib/plugins/aws/package/compile/events/cognitoUserPool/index.js +++ b/lib/plugins/aws/package/compile/events/cognitoUserPool/index.js @@ -20,19 +20,20 @@ class AwsCompileCognitoUserPoolEvents { this.hooks = { 'package:compileEvents': this.compileCognitoUserPoolEvents.bind(this), + 'after:package:finalize': this.mergeWithCustomResources.bind(this), }; } - compileCognitoUserPoolEvents() { + findUserPoolsAndFunctions() { const userPools = []; const cognitoUserPoolTriggerFunctions = []; // Iterate through all functions declared in `serverless.yml` - this.serverless.service.getAllFunctions().forEach((functionName) => { + _.forEach(this.serverless.service.getAllFunctions(), (functionName) => { const functionObj = this.serverless.service.getFunction(functionName); if (functionObj.events) { - functionObj.events.forEach(event => { + _.forEach(functionObj.events, (event) => { if (event.cognitoUserPool) { // Check event definition for `cognitoUserPool` object if (typeof event.cognitoUserPool === 'object') { @@ -80,51 +81,61 @@ class AwsCompileCognitoUserPoolEvents { } }); - // Generate CloudFormation templates for Cognito User Pool changes - _.forEach(userPools, (poolName) => { - // Create a `LambdaConfig` object for the CloudFormation template - const currentPoolTriggerFunctions = _.filter(cognitoUserPoolTriggerFunctions, { - poolName, + return { cognitoUserPoolTriggerFunctions, userPools }; + } + + generateTemplateForPool(poolName, currentPoolTriggerFunctions) { + const lambdaConfig = _.reduce(currentPoolTriggerFunctions, (result, value) => { + const lambdaLogicalId = this.provider.naming.getLambdaLogicalId(value.functionName); + + // Return a new object to avoid lint errors + return Object.assign({}, result, { + [value.triggerSource]: { + 'Fn::GetAtt': [ + lambdaLogicalId, + 'Arn', + ], + }, }); + }, {}); - const lambdaConfig = _.reduce(currentPoolTriggerFunctions, (result, value) => { - const lambdaLogicalId = this.provider.naming.getLambdaLogicalId(value.functionName); + const userPoolLogicalId = this.provider.naming.getCognitoUserPoolLogicalId(poolName); - // Return a new object to avoid lint errors - return Object.assign({}, result, { - [value.triggerSource]: { - 'Fn::GetAtt': [ - lambdaLogicalId, - 'Arn', - ], - }, - }); - }, {}); + // Attach `DependsOn` for any relevant Lambdas + const DependsOn = _.map(currentPoolTriggerFunctions, (value) => this + .provider.naming.getLambdaLogicalId(value.functionName)); - const userPoolLogicalId = this.provider.naming.getCognitoUserPoolLogicalId(poolName); - - const DependsOn = _.map(currentPoolTriggerFunctions, (value) => this - .provider.naming.getLambdaLogicalId(value.functionName)); - - const userPoolTemplate = { + return { + [userPoolLogicalId]: { Type: 'AWS::Cognito::UserPool', Properties: { UserPoolName: poolName, LambdaConfig: lambdaConfig, }, DependsOn, - }; + }, + }; + } - const userPoolCFResource = { - [userPoolLogicalId]: userPoolTemplate, - }; + compileCognitoUserPoolEvents() { + const result = this.findUserPoolsAndFunctions(); + const cognitoUserPoolTriggerFunctions = result.cognitoUserPoolTriggerFunctions; + const userPools = result.userPools; + + // Generate CloudFormation templates for Cognito User Pool changes + _.forEach(userPools, (poolName) => { + const currentPoolTriggerFunctions = _.filter(cognitoUserPoolTriggerFunctions, { poolName }); + const userPoolCFResource = this.generateTemplateForPool( + poolName, + currentPoolTriggerFunctions + ); _.merge(this.serverless.service.provider.compiledCloudFormationTemplate.Resources, userPoolCFResource); }); // Generate CloudFormation templates for IAM permissions to allow Cognito to trigger Lambda - cognitoUserPoolTriggerFunctions.forEach((cognitoUserPoolTriggerFunction) => { + _.forEach(cognitoUserPoolTriggerFunctions, (cognitoUserPoolTriggerFunction) => { const userPoolLogicalId = this.provider.naming .getCognitoUserPoolLogicalId(cognitoUserPoolTriggerFunction.poolName); const lambdaLogicalId = this.provider.naming @@ -159,6 +170,43 @@ class AwsCompileCognitoUserPoolEvents { permissionCFResource); }); } + + mergeWithCustomResources() { + const result = this.findUserPoolsAndFunctions(); + const cognitoUserPoolTriggerFunctions = result.cognitoUserPoolTriggerFunctions; + const userPools = result.userPools; + + _.forEach(userPools, (poolName) => { + const currentPoolTriggerFunctions = _.filter(cognitoUserPoolTriggerFunctions, { poolName }); + const userPoolLogicalId = this.provider.naming.getCognitoUserPoolLogicalId(poolName); + + // If overrides exist in `Resources`, merge them in + if (_.has(this.serverless.service.resources, userPoolLogicalId)) { + const customUserPool = this.serverless.service.resources[userPoolLogicalId]; + const generatedUserPool = this.generateTemplateForPool( + poolName, + currentPoolTriggerFunctions + )[userPoolLogicalId]; + + // Merge `DependsOn` clauses + const customUserPoolDependsOn = _.get(customUserPool, 'DependsOn', []); + const DependsOn = generatedUserPool.DependsOn.concat(customUserPoolDependsOn); + + // Merge default and custom resources, and `DependsOn` clause + const mergedTemplate = Object.assign( + {}, + _.merge(generatedUserPool, customUserPool), + { DependsOn } + ); + + // Merge resource back into `Resources` + _.merge( + this.serverless.service.provider.compiledCloudFormationTemplate.Resources, + { [userPoolLogicalId]: mergedTemplate } + ); + } + }); + } } module.exports = AwsCompileCognitoUserPoolEvents; diff --git a/lib/plugins/aws/package/compile/events/cognitoUserPool/index.test.js b/lib/plugins/aws/package/compile/events/cognitoUserPool/index.test.js index 6ec9a6c28..a67e80bb7 100644 --- a/lib/plugins/aws/package/compile/events/cognitoUserPool/index.test.js +++ b/lib/plugins/aws/package/compile/events/cognitoUserPool/index.test.js @@ -1,5 +1,6 @@ 'use strict'; +const _ = require('lodash'); const expect = require('chai').expect; const AwsProvider = require('../../../../provider/awsProvider'); const AwsCompileCognitoUserPoolEvents = require('./index'); @@ -115,9 +116,15 @@ describe('AwsCompileCognitoUserPoolEvents', () => { expect(awsCompileCognitoUserPoolEvents.serverless.service.provider .compiledCloudFormationTemplate.Resources.CognitoUserPoolMyUserPool1.Type ).to.equal('AWS::Cognito::UserPool'); + expect(awsCompileCognitoUserPoolEvents.serverless.service.provider + .compiledCloudFormationTemplate.Resources.CognitoUserPoolMyUserPool1.DependsOn + ).to.have.lengthOf(1); expect(awsCompileCognitoUserPoolEvents.serverless.service.provider .compiledCloudFormationTemplate.Resources.CognitoUserPoolMyUserPool2.Type ).to.equal('AWS::Cognito::UserPool'); + expect(awsCompileCognitoUserPoolEvents.serverless.service.provider + .compiledCloudFormationTemplate.Resources.CognitoUserPoolMyUserPool2.DependsOn + ).to.have.lengthOf(1); expect(awsCompileCognitoUserPoolEvents.serverless.service.provider .compiledCloudFormationTemplate.Resources .FirstLambdaPermissionCognitoUserPoolMyUserPool1TriggerSourcePreSignUp.Type @@ -153,9 +160,15 @@ describe('AwsCompileCognitoUserPoolEvents', () => { expect(awsCompileCognitoUserPoolEvents.serverless.service.provider .compiledCloudFormationTemplate.Resources.CognitoUserPoolMyUserPool1.Type ).to.equal('AWS::Cognito::UserPool'); + expect(awsCompileCognitoUserPoolEvents.serverless.service.provider + .compiledCloudFormationTemplate.Resources.CognitoUserPoolMyUserPool1.DependsOn + ).to.have.lengthOf(1); expect(awsCompileCognitoUserPoolEvents.serverless.service.provider .compiledCloudFormationTemplate.Resources.CognitoUserPoolMyUserPool2.Type ).to.equal('AWS::Cognito::UserPool'); + expect(awsCompileCognitoUserPoolEvents.serverless.service.provider + .compiledCloudFormationTemplate.Resources.CognitoUserPoolMyUserPool2.DependsOn + ).to.have.lengthOf(1); expect(awsCompileCognitoUserPoolEvents.serverless.service.provider .compiledCloudFormationTemplate.Resources .FirstLambdaPermissionCognitoUserPoolMyUserPool1TriggerSourcePreSignUp.Type @@ -195,6 +208,9 @@ describe('AwsCompileCognitoUserPoolEvents', () => { expect(awsCompileCognitoUserPoolEvents.serverless.service.provider .compiledCloudFormationTemplate.Resources.CognitoUserPoolMyUserPool1.Type ).to.equal('AWS::Cognito::UserPool'); + expect(awsCompileCognitoUserPoolEvents.serverless.service.provider + .compiledCloudFormationTemplate.Resources.CognitoUserPoolMyUserPool1.DependsOn + ).to.have.lengthOf(1); expect(awsCompileCognitoUserPoolEvents.serverless.service.provider .compiledCloudFormationTemplate.Resources.CognitoUserPoolMyUserPool1 .Properties.LambdaConfig.PreSignUp['Fn::GetAtt'][0] @@ -204,6 +220,9 @@ describe('AwsCompileCognitoUserPoolEvents', () => { expect(awsCompileCognitoUserPoolEvents.serverless.service.provider .compiledCloudFormationTemplate.Resources.CognitoUserPoolMyUserPool2.Type ).to.equal('AWS::Cognito::UserPool'); + expect(awsCompileCognitoUserPoolEvents.serverless.service.provider + .compiledCloudFormationTemplate.Resources.CognitoUserPoolMyUserPool2.DependsOn + ).to.have.lengthOf(1); expect(awsCompileCognitoUserPoolEvents.serverless.service.provider .compiledCloudFormationTemplate.Resources.CognitoUserPoolMyUserPool2 .Properties.LambdaConfig.PreSignUp['Fn::GetAtt'][0] @@ -250,10 +269,14 @@ describe('AwsCompileCognitoUserPoolEvents', () => { .compiledCloudFormationTemplate.Resources .CognitoUserPoolMyUserPool.Type ).to.equal('AWS::Cognito::UserPool'); - expect(Object.keys(awsCompileCognitoUserPoolEvents.serverless.service.provider + expect(_.keys(awsCompileCognitoUserPoolEvents.serverless.service.provider .compiledCloudFormationTemplate.Resources - .CognitoUserPoolMyUserPool.Properties.LambdaConfig).length - ).to.equal(2); + .CognitoUserPoolMyUserPool.Properties.LambdaConfig) + ).to.have.lengthOf(2); + expect(awsCompileCognitoUserPoolEvents.serverless.service.provider + .compiledCloudFormationTemplate.Resources + .CognitoUserPoolMyUserPool.DependsOn + ).to.have.lengthOf(2); expect(awsCompileCognitoUserPoolEvents.serverless.service.provider .compiledCloudFormationTemplate.Resources .FirstLambdaPermissionCognitoUserPoolMyUserPoolTriggerSourcePreSignUp.Type @@ -279,4 +302,145 @@ describe('AwsCompileCognitoUserPoolEvents', () => { ).to.deep.equal({}); }); }); + + describe('#mergeWithCustomResources()', () => { + it('does not merge if no custom resource is found in Resources', () => { + awsCompileCognitoUserPoolEvents.serverless.service.functions = { + first: { + events: [ + { + cognitoUserPool: { + pool: 'MyUserPool', + trigger: 'PreSignUp', + }, + }, + ], + }, + }; + awsCompileCognitoUserPoolEvents.serverless.service.resources = {}; + + awsCompileCognitoUserPoolEvents.compileCognitoUserPoolEvents(); + awsCompileCognitoUserPoolEvents.mergeWithCustomResources(); + + expect(awsCompileCognitoUserPoolEvents.serverless.service.provider + .compiledCloudFormationTemplate.Resources + .CognitoUserPoolMyUserPool.Type + ).to.equal('AWS::Cognito::UserPool'); + expect(_.keys(awsCompileCognitoUserPoolEvents.serverless.service.provider + .compiledCloudFormationTemplate.Resources + .CognitoUserPoolMyUserPool.Properties) + ).to.have.lengthOf(2); + expect(_.keys(awsCompileCognitoUserPoolEvents.serverless.service.provider + .compiledCloudFormationTemplate.Resources + .CognitoUserPoolMyUserPool.Properties.LambdaConfig) + ).to.have.lengthOf(1); + expect(awsCompileCognitoUserPoolEvents.serverless.service.provider + .compiledCloudFormationTemplate.Resources + .FirstLambdaPermissionCognitoUserPoolMyUserPoolTriggerSourcePreSignUp.Type + ).to.equal('AWS::Lambda::Permission'); + }); + + it('should merge custom resources found in Resources', () => { + awsCompileCognitoUserPoolEvents.serverless.service.functions = { + first: { + events: [ + { + cognitoUserPool: { + pool: 'MyUserPool', + trigger: 'PreSignUp', + }, + }, + ], + }, + }; + awsCompileCognitoUserPoolEvents.serverless.service.resources = { + CognitoUserPoolMyUserPool: { + Type: 'AWS::Cognito::UserPool', + Properties: { + UserPoolName: 'ProdMyUserPool', + MfaConfiguration: 'OFF', + EmailVerificationSubject: 'Your verification code', + EmailVerificationMessage: 'Your verification code is {####}.', + SmsVerificationMessage: 'Your verification code is {####}.', + }, + }, + }; + + awsCompileCognitoUserPoolEvents.compileCognitoUserPoolEvents(); + awsCompileCognitoUserPoolEvents.mergeWithCustomResources(); + + expect(awsCompileCognitoUserPoolEvents.serverless.service.provider + .compiledCloudFormationTemplate.Resources + .CognitoUserPoolMyUserPool.Type + ).to.equal('AWS::Cognito::UserPool'); + expect(_.keys(awsCompileCognitoUserPoolEvents.serverless.service.provider + .compiledCloudFormationTemplate.Resources + .CognitoUserPoolMyUserPool.Properties) + ).to.have.lengthOf(6); + expect(awsCompileCognitoUserPoolEvents.serverless.service.provider + .compiledCloudFormationTemplate.Resources + .CognitoUserPoolMyUserPool.DependsOn + ).to.have.lengthOf(1); + expect(_.keys(awsCompileCognitoUserPoolEvents.serverless.service.provider + .compiledCloudFormationTemplate.Resources + .CognitoUserPoolMyUserPool.Properties.LambdaConfig) + ).to.have.lengthOf(1); + expect(awsCompileCognitoUserPoolEvents.serverless.service.provider + .compiledCloudFormationTemplate.Resources + .FirstLambdaPermissionCognitoUserPoolMyUserPoolTriggerSourcePreSignUp.Type + ).to.equal('AWS::Lambda::Permission'); + }); + + it('should merge `DependsOn` clauses correctly if being overridden from Resources', () => { + awsCompileCognitoUserPoolEvents.serverless.service.functions = { + first: { + events: [ + { + cognitoUserPool: { + pool: 'MyUserPool', + trigger: 'PreSignUp', + }, + }, + ], + }, + }; + awsCompileCognitoUserPoolEvents.serverless.service.resources = { + CognitoUserPoolMyUserPool: { + DependsOn: ['Something', 'SomethingElse', ['Nothing', 'NothingAtAll']], + Type: 'AWS::Cognito::UserPool', + Properties: { + UserPoolName: 'ProdMyUserPool', + MfaConfiguration: 'OFF', + EmailVerificationSubject: 'Your verification code', + EmailVerificationMessage: 'Your verification code is {####}.', + SmsVerificationMessage: 'Your verification code is {####}.', + }, + }, + }; + + awsCompileCognitoUserPoolEvents.compileCognitoUserPoolEvents(); + awsCompileCognitoUserPoolEvents.mergeWithCustomResources(); + + expect(awsCompileCognitoUserPoolEvents.serverless.service.provider + .compiledCloudFormationTemplate.Resources + .CognitoUserPoolMyUserPool.Type + ).to.equal('AWS::Cognito::UserPool'); + expect(awsCompileCognitoUserPoolEvents.serverless.service.provider + .compiledCloudFormationTemplate.Resources + .CognitoUserPoolMyUserPool.DependsOn + ).to.have.lengthOf(4); + expect(_.keys(awsCompileCognitoUserPoolEvents.serverless.service.provider + .compiledCloudFormationTemplate.Resources + .CognitoUserPoolMyUserPool.Properties) + ).to.have.lengthOf(6); + expect(_.keys(awsCompileCognitoUserPoolEvents.serverless.service.provider + .compiledCloudFormationTemplate.Resources + .CognitoUserPoolMyUserPool.Properties.LambdaConfig) + ).to.have.lengthOf(1); + expect(awsCompileCognitoUserPoolEvents.serverless.service.provider + .compiledCloudFormationTemplate.Resources + .FirstLambdaPermissionCognitoUserPoolMyUserPoolTriggerSourcePreSignUp.Type + ).to.equal('AWS::Lambda::Permission'); + }); + }); }); diff --git a/lib/plugins/aws/package/compile/functions/index.js b/lib/plugins/aws/package/compile/functions/index.js index 971bf1e2b..d880d0f11 100644 --- a/lib/plugins/aws/package/compile/functions/index.js +++ b/lib/plugins/aws/package/compile/functions/index.js @@ -279,6 +279,19 @@ class AwsCompileFunctions { delete newFunction.Properties.VpcConfig; } + if (functionObject.reservedConcurrency) { + if (_.isInteger(functionObject.reservedConcurrency)) { + newFunction.Properties.ReservedConcurrentExecutions = functionObject.reservedConcurrency; + } else { + const errorMessage = [ + 'You should use integer as reservedConcurrency value on function: ', + `${newFunction.Properties.FunctionName}`, + ].join(''); + + return BbPromise.reject(new this.serverless.classes.Error(errorMessage)); + } + } + newFunction.DependsOn = [this.provider.naming.getLogGroupLogicalId(functionName)] .concat(newFunction.DependsOn || []); diff --git a/lib/plugins/aws/package/compile/functions/index.test.js b/lib/plugins/aws/package/compile/functions/index.test.js index b5e18c684..d170bdeb2 100644 --- a/lib/plugins/aws/package/compile/functions/index.test.js +++ b/lib/plugins/aws/package/compile/functions/index.test.js @@ -25,6 +25,7 @@ describe('AwsCompileFunctions', () => { }; serverless = new Serverless(options); serverless.setProvider('aws', new AwsProvider(serverless, options)); + serverless.cli = new serverless.classes.CLI(); awsCompileFunctions = new AwsCompileFunctions(serverless, options); awsCompileFunctions.serverless.service.provider.compiledCloudFormationTemplate = { Resources: {}, @@ -1730,6 +1731,65 @@ describe('AwsCompileFunctions', () => { ); }); }); + + it('should set function declared reserved concurrency limit', () => { + const s3Folder = awsCompileFunctions.serverless.service.package.artifactDirectoryName; + const s3FileName = awsCompileFunctions.serverless.service.package.artifact + .split(path.sep).pop(); + awsCompileFunctions.serverless.service.functions = { + func: { + handler: 'func.function.handler', + name: 'new-service-dev-func', + reservedConcurrency: 5, + }, + }; + const compiledFunction = { + Type: 'AWS::Lambda::Function', + DependsOn: [ + 'FuncLogGroup', + 'IamRoleLambdaExecution', + ], + Properties: { + Code: { + S3Key: `${s3Folder}/${s3FileName}`, + S3Bucket: { Ref: 'ServerlessDeploymentBucket' }, + }, + FunctionName: 'new-service-dev-func', + Handler: 'func.function.handler', + MemorySize: 1024, + ReservedConcurrentExecutions: 5, + Role: { 'Fn::GetAtt': ['IamRoleLambdaExecution', 'Arn'] }, + Runtime: 'nodejs4.3', + Timeout: 6, + }, + }; + + return expect(awsCompileFunctions.compileFunctions()).to.be.fulfilled + .then(() => { + expect( + awsCompileFunctions.serverless.service.provider.compiledCloudFormationTemplate + .Resources.FuncLambdaFunction + ).to.deep.equal(compiledFunction); + }); + }); + + it('should throw an informative error message if non-integer reserved concurrency limit set ' + + 'on function', () => { + awsCompileFunctions.serverless.service.functions = { + func: { + handler: 'func.function.handler', + name: 'new-service-dev-func', + reservedConcurrency: '1', + }, + }; + + const errorMessage = [ + 'You should use integer as reservedConcurrency value on function: ', + 'new-service-dev-func', + ].join(''); + + return expect(awsCompileFunctions.compileFunctions()).to.be.rejectedWith(errorMessage); + }); }); describe('#compileRole()', () => { diff --git a/lib/plugins/aws/provider/awsProvider.js b/lib/plugins/aws/provider/awsProvider.js index bcc78e583..6eae516f6 100644 --- a/lib/plugins/aws/provider/awsProvider.js +++ b/lib/plugins/aws/provider/awsProvider.js @@ -328,8 +328,6 @@ class AwsProvider { enableS3TransferAcceleration(credentials) { this.serverless.cli.log('Using S3 Transfer Acceleration Endpoint...'); credentials.useAccelerateEndpoint = true; // eslint-disable-line no-param-reassign - credentials.signatureVersion = 'v2'; // eslint-disable-line no-param-reassign - // see https://github.com/aws/aws-sdk-js/issues/281 } getRegion() { diff --git a/lib/plugins/aws/utils/formatLambdaLogEvent.js b/lib/plugins/aws/utils/formatLambdaLogEvent.js index 2c2286e9b..62efe8f64 100644 --- a/lib/plugins/aws/utils/formatLambdaLogEvent.js +++ b/lib/plugins/aws/utils/formatLambdaLogEvent.js @@ -34,7 +34,7 @@ module.exports = (msgParam) => { } else if (!isNaN((new Date(splitted[1])).getTime())) { date = splitted[1]; reqId = splitted[2]; - level = `${chalk.white(splitted[0])}\t`; + level = `${splitted[0]}\t`; } else { return msg; } diff --git a/lib/plugins/aws/utils/formatLambdaLogEvent.test.js b/lib/plugins/aws/utils/formatLambdaLogEvent.test.js index b1ae167f2..a5d4639df 100644 --- a/lib/plugins/aws/utils/formatLambdaLogEvent.test.js +++ b/lib/plugins/aws/utils/formatLambdaLogEvent.test.js @@ -37,7 +37,7 @@ describe('#formatLambdaLogEvent()', () => { const momentDate = moment('2016-01-01T12:00:00Z').format('YYYY-MM-DD HH:mm:ss.SSS (Z)'); expectedLogMessage += `${chalk.green(momentDate)}\t`; expectedLogMessage += `${chalk.yellow('99c30000-b01a-11e5-93f7-b8e85631a00e')}\t`; - expectedLogMessage += `${chalk.white('[INFO]')}\t`; + expectedLogMessage += `${'[INFO]'}\t`; expectedLogMessage += 'test'; expect(formatLambdaLogEvent(pythonLoggerLine)).to.equal(expectedLogMessage); diff --git a/lib/plugins/create/create.js b/lib/plugins/create/create.js index 8027ba9fb..84368002a 100644 --- a/lib/plugins/create/create.js +++ b/lib/plugins/create/create.js @@ -28,6 +28,8 @@ const validTemplates = [ 'aws-scala-sbt', 'aws-csharp', 'aws-fsharp', + 'aws-go', + 'aws-go-dep', 'azure-nodejs', 'google-nodejs', 'kubeless-python', diff --git a/lib/plugins/create/create.test.js b/lib/plugins/create/create.test.js index 3bba193fa..c0bf9d726 100644 --- a/lib/plugins/create/create.test.js +++ b/lib/plugins/create/create.test.js @@ -726,5 +726,39 @@ describe('Create', () => { expect((/service: my-awesome-service/).test(serverlessYmlfileContent)).to.equal(true); }); }); + + it('should generate scaffolding for "aws-go" template', () => { + process.chdir(tmpDir); + create.options.template = 'aws-go'; + + return create.create().then(() => { + const dirContent = walkDirSync(tmpDir) + .map(elem => elem.replace(path.join(tmpDir, path.sep), '')); + + expect(dirContent).to.include('serverless.yml'); + expect(dirContent).to.include(path.join('hello', 'main.go')); + expect(dirContent).to.include(path.join('world', 'main.go')); + expect(dirContent).to.include('Makefile'); + expect(dirContent).to.include('.gitignore'); + }); + }); + + it('should generate scaffolding for "aws-go-dep" template', () => { + process.chdir(tmpDir); + create.options.template = 'aws-go-dep'; + + return create.create().then(() => { + const dirContent = walkDirSync(tmpDir) + .map(elem => elem.replace(path.join(tmpDir, path.sep), '')); + + expect(dirContent).to.include('serverless.yml'); + expect(dirContent).to.include(path.join('hello', 'main.go')); + expect(dirContent).to.include(path.join('world', 'main.go')); + expect(dirContent).to.include('Gopkg.toml'); + expect(dirContent).to.include('Gopkg.lock'); + expect(dirContent).to.include('Makefile'); + expect(dirContent).to.include('.gitignore'); + }); + }); }); }); diff --git a/lib/plugins/create/templates/aws-csharp/serverless.yml b/lib/plugins/create/templates/aws-csharp/serverless.yml index fb55c7aac..12e2196fe 100644 --- a/lib/plugins/create/templates/aws-csharp/serverless.yml +++ b/lib/plugins/create/templates/aws-csharp/serverless.yml @@ -67,7 +67,7 @@ functions: # - schedule: rate(10 minutes) # - sns: greeter-topic # - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000 -# - alexaSkill +# - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx # - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx # - iot: # sql: "SELECT * FROM 'some_topic'" diff --git a/lib/plugins/create/templates/aws-fsharp/serverless.yml b/lib/plugins/create/templates/aws-fsharp/serverless.yml index 21dd15dd3..0f8ca52b4 100644 --- a/lib/plugins/create/templates/aws-fsharp/serverless.yml +++ b/lib/plugins/create/templates/aws-fsharp/serverless.yml @@ -67,7 +67,7 @@ functions: # - schedule: rate(10 minutes) # - sns: greeter-topic # - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000 -# - alexaSkill +# - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx # - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx # - iot: # sql: "SELECT * FROM 'some_topic'" diff --git a/lib/plugins/create/templates/aws-go-dep/Gopkg.lock b/lib/plugins/create/templates/aws-go-dep/Gopkg.lock new file mode 100644 index 000000000..f93855f76 --- /dev/null +++ b/lib/plugins/create/templates/aws-go-dep/Gopkg.lock @@ -0,0 +1,19 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + name = "github.com/aws/aws-lambda-go" + packages = [ + "lambda", + "lambda/messages", + "lambdacontext" + ] + revision = "6e2e37798efbb1dfd8e9c6681702e683a6046517" + version = "v1.0.1" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "85fa166cc59d0fa113a1517ffbb5dee0f1fa4a6795239936afb18c64364af759" + solver-name = "gps-cdcl" + solver-version = 1 \ No newline at end of file diff --git a/lib/plugins/create/templates/aws-go-dep/Gopkg.toml b/lib/plugins/create/templates/aws-go-dep/Gopkg.toml new file mode 100644 index 000000000..8fce8929e --- /dev/null +++ b/lib/plugins/create/templates/aws-go-dep/Gopkg.toml @@ -0,0 +1,25 @@ +# Gopkg.toml example +# +# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" + + +[[constraint]] + name = "github.com/aws/aws-lambda-go" + version = "^1.0.1" diff --git a/lib/plugins/create/templates/aws-go-dep/Makefile b/lib/plugins/create/templates/aws-go-dep/Makefile new file mode 100644 index 000000000..46a24c449 --- /dev/null +++ b/lib/plugins/create/templates/aws-go-dep/Makefile @@ -0,0 +1,4 @@ +build: + dep ensure + env GOOS=linux go build -ldflags="-s -w" -o bin/hello hello/main.go + env GOOS=linux go build -ldflags="-s -w" -o bin/world world/main.go \ No newline at end of file diff --git a/lib/plugins/create/templates/aws-go-dep/gitignore b/lib/plugins/create/templates/aws-go-dep/gitignore new file mode 100644 index 000000000..99a966a94 --- /dev/null +++ b/lib/plugins/create/templates/aws-go-dep/gitignore @@ -0,0 +1,8 @@ +# Serverless directories +.serverless + +# golang output binary directory +bin + +# golang vendor (dependencies) directory +vendor \ No newline at end of file diff --git a/lib/plugins/create/templates/aws-go-dep/hello/main.go b/lib/plugins/create/templates/aws-go-dep/hello/main.go new file mode 100644 index 000000000..e75c5af04 --- /dev/null +++ b/lib/plugins/create/templates/aws-go-dep/hello/main.go @@ -0,0 +1,19 @@ +package main + +import ( + "github.com/aws/aws-lambda-go/lambda" +) + +type Response struct { + Message string `json:"message"` +} + +func Handler() (Response, error) { + return Response{ + Message: "Go Serverless v1.0! Your function executed successfully!", + }, nil +} + +func main() { + lambda.Start(Handler) +} diff --git a/lib/plugins/create/templates/aws-go-dep/serverless.yml b/lib/plugins/create/templates/aws-go-dep/serverless.yml new file mode 100644 index 000000000..768194143 --- /dev/null +++ b/lib/plugins/create/templates/aws-go-dep/serverless.yml @@ -0,0 +1,104 @@ +# 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: aws-go-dep # NOTE: update this with your service name + +# You can pin your service to only deploy with a specific Serverless version +# Check out our docs for more details +# frameworkVersion: "=X.X.X" + +provider: + name: aws + runtime: go1.x + +# you can overwrite defaults here +# stage: dev +# region: us-east-1 + +# you can add statements to the Lambda function's IAM Role here +# iamRoleStatements: +# - Effect: "Allow" +# Action: +# - "s3:ListBucket" +# Resource: { "Fn::Join" : ["", ["arn:aws:s3:::", { "Ref" : "ServerlessDeploymentBucket" } ] ] } +# - Effect: "Allow" +# Action: +# - "s3:PutObject" +# Resource: +# Fn::Join: +# - "" +# - - "arn:aws:s3:::" +# - "Ref" : "ServerlessDeploymentBucket" +# - "/*" + +# you can define service wide environment variables here +# environment: +# variable1: value1 + +package: + exclude: + - ./** + include: + - ./bin/** + +functions: + hello: + handler: bin/hello + world: + handler: bin/world + +# The following are a few example events you can configure +# NOTE: Please make sure to change your handler code to work with those events +# Check the event documentation for details +# events: +# events: +# - http: +# path: users/create +# method: get +# - s3: ${env:BUCKET} +# - schedule: rate(10 minutes) +# - sns: greeter-topic +# - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000 +# - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx +# - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx +# - iot: +# sql: "SELECT * FROM 'some_topic'" +# - cloudwatchEvent: +# event: +# source: +# - "aws.ec2" +# detail-type: +# - "EC2 Instance State-change Notification" +# detail: +# state: +# - pending +# - cloudwatchLog: '/aws/lambda/hello' +# - cognitoUserPool: +# pool: MyUserPool +# trigger: PreSignUp + +# Define function environment variables here +# environment: +# variable2: value2 + +# you can add CloudFormation resource templates here +#resources: +# Resources: +# NewResource: +# Type: AWS::S3::Bucket +# Properties: +# BucketName: my-new-bucket +# Outputs: +# NewOutput: +# Description: "Description for the output" +# Value: "Some output value" diff --git a/lib/plugins/create/templates/aws-go-dep/world/main.go b/lib/plugins/create/templates/aws-go-dep/world/main.go new file mode 100644 index 000000000..bf46aa6fb --- /dev/null +++ b/lib/plugins/create/templates/aws-go-dep/world/main.go @@ -0,0 +1,19 @@ +package main + +import ( + "github.com/aws/aws-lambda-go/lambda" +) + +type Response struct { + Message string `json:"message"` +} + +func Handler() (Response, error) { + return Response{ + Message: "Okay so your other function also executed successfully!", + }, nil +} + +func main() { + lambda.Start(Handler) +} diff --git a/lib/plugins/create/templates/aws-go/Makefile b/lib/plugins/create/templates/aws-go/Makefile new file mode 100644 index 000000000..3b697426e --- /dev/null +++ b/lib/plugins/create/templates/aws-go/Makefile @@ -0,0 +1,4 @@ +build: + go get github.com/aws/aws-lambda-go/lambda + env GOOS=linux go build -ldflags="-s -w" -o bin/hello hello/main.go + env GOOS=linux go build -ldflags="-s -w" -o bin/world world/main.go \ No newline at end of file diff --git a/lib/plugins/create/templates/aws-go/gitignore b/lib/plugins/create/templates/aws-go/gitignore new file mode 100644 index 000000000..f5b4c36ad --- /dev/null +++ b/lib/plugins/create/templates/aws-go/gitignore @@ -0,0 +1,5 @@ +# Serverless directories +.serverless + +# golang output binary directory +bin \ No newline at end of file diff --git a/lib/plugins/create/templates/aws-go/hello/main.go b/lib/plugins/create/templates/aws-go/hello/main.go new file mode 100644 index 000000000..e75c5af04 --- /dev/null +++ b/lib/plugins/create/templates/aws-go/hello/main.go @@ -0,0 +1,19 @@ +package main + +import ( + "github.com/aws/aws-lambda-go/lambda" +) + +type Response struct { + Message string `json:"message"` +} + +func Handler() (Response, error) { + return Response{ + Message: "Go Serverless v1.0! Your function executed successfully!", + }, nil +} + +func main() { + lambda.Start(Handler) +} diff --git a/lib/plugins/create/templates/aws-go/serverless.yml b/lib/plugins/create/templates/aws-go/serverless.yml new file mode 100644 index 000000000..47fd73787 --- /dev/null +++ b/lib/plugins/create/templates/aws-go/serverless.yml @@ -0,0 +1,104 @@ +# 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: aws-go # NOTE: update this with your service name + +# You can pin your service to only deploy with a specific Serverless version +# Check out our docs for more details +# frameworkVersion: "=X.X.X" + +provider: + name: aws + runtime: go1.x + +# you can overwrite defaults here +# stage: dev +# region: us-east-1 + +# you can add statements to the Lambda function's IAM Role here +# iamRoleStatements: +# - Effect: "Allow" +# Action: +# - "s3:ListBucket" +# Resource: { "Fn::Join" : ["", ["arn:aws:s3:::", { "Ref" : "ServerlessDeploymentBucket" } ] ] } +# - Effect: "Allow" +# Action: +# - "s3:PutObject" +# Resource: +# Fn::Join: +# - "" +# - - "arn:aws:s3:::" +# - "Ref" : "ServerlessDeploymentBucket" +# - "/*" + +# you can define service wide environment variables here +# environment: +# variable1: value1 + +package: + exclude: + - ./** + include: + - ./bin/** + +functions: + hello: + handler: bin/hello + world: + handler: bin/world + +# The following are a few example events you can configure +# NOTE: Please make sure to change your handler code to work with those events +# Check the event documentation for details +# events: +# events: +# - http: +# path: users/create +# method: get +# - s3: ${env:BUCKET} +# - schedule: rate(10 minutes) +# - sns: greeter-topic +# - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000 +# - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx +# - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx +# - iot: +# sql: "SELECT * FROM 'some_topic'" +# - cloudwatchEvent: +# event: +# source: +# - "aws.ec2" +# detail-type: +# - "EC2 Instance State-change Notification" +# detail: +# state: +# - pending +# - cloudwatchLog: '/aws/lambda/hello' +# - cognitoUserPool: +# pool: MyUserPool +# trigger: PreSignUp + +# Define function environment variables here +# environment: +# variable2: value2 + +# you can add CloudFormation resource templates here +#resources: +# Resources: +# NewResource: +# Type: AWS::S3::Bucket +# Properties: +# BucketName: my-new-bucket +# Outputs: +# NewOutput: +# Description: "Description for the output" +# Value: "Some output value" diff --git a/lib/plugins/create/templates/aws-go/world/main.go b/lib/plugins/create/templates/aws-go/world/main.go new file mode 100644 index 000000000..bf46aa6fb --- /dev/null +++ b/lib/plugins/create/templates/aws-go/world/main.go @@ -0,0 +1,19 @@ +package main + +import ( + "github.com/aws/aws-lambda-go/lambda" +) + +type Response struct { + Message string `json:"message"` +} + +func Handler() (Response, error) { + return Response{ + Message: "Okay so your other function also executed successfully!", + }, nil +} + +func main() { + lambda.Start(Handler) +} diff --git a/lib/plugins/create/templates/aws-groovy-gradle/serverless.yml b/lib/plugins/create/templates/aws-groovy-gradle/serverless.yml index a1adaaf4d..e79562829 100644 --- a/lib/plugins/create/templates/aws-groovy-gradle/serverless.yml +++ b/lib/plugins/create/templates/aws-groovy-gradle/serverless.yml @@ -64,7 +64,7 @@ functions: # - schedule: rate(10 minutes) # - sns: greeter-topic # - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000 -# - alexaSkill +# - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx # - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx # - iot: # sql: "SELECT * FROM 'some_topic'" diff --git a/lib/plugins/create/templates/aws-java-gradle/serverless.yml b/lib/plugins/create/templates/aws-java-gradle/serverless.yml index e8811732e..c1079296f 100644 --- a/lib/plugins/create/templates/aws-java-gradle/serverless.yml +++ b/lib/plugins/create/templates/aws-java-gradle/serverless.yml @@ -64,7 +64,7 @@ functions: # - schedule: rate(10 minutes) # - sns: greeter-topic # - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000 -# - alexaSkill +# - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx # - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx # - iot: # sql: "SELECT * FROM 'some_topic'" diff --git a/lib/plugins/create/templates/aws-java-maven/serverless.yml b/lib/plugins/create/templates/aws-java-maven/serverless.yml index 106bab8aa..7f94d0131 100644 --- a/lib/plugins/create/templates/aws-java-maven/serverless.yml +++ b/lib/plugins/create/templates/aws-java-maven/serverless.yml @@ -64,7 +64,7 @@ functions: # - schedule: rate(10 minutes) # - sns: greeter-topic # - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000 -# - alexaSkill +# - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx # - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx # - iot: # sql: "SELECT * FROM 'some_topic'" diff --git a/lib/plugins/create/templates/aws-kotlin-jvm-gradle/serverless.yml b/lib/plugins/create/templates/aws-kotlin-jvm-gradle/serverless.yml index fff1b5510..434eb3d81 100644 --- a/lib/plugins/create/templates/aws-kotlin-jvm-gradle/serverless.yml +++ b/lib/plugins/create/templates/aws-kotlin-jvm-gradle/serverless.yml @@ -64,7 +64,7 @@ functions: # - schedule: rate(10 minutes) # - sns: greeter-topic # - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000 -# - alexaSkill +# - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx # - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx # - iot: # sql: "SELECT * FROM 'some_topic'" diff --git a/lib/plugins/create/templates/aws-kotlin-jvm-maven/serverless.yml b/lib/plugins/create/templates/aws-kotlin-jvm-maven/serverless.yml index cb892140f..1387dfc8a 100644 --- a/lib/plugins/create/templates/aws-kotlin-jvm-maven/serverless.yml +++ b/lib/plugins/create/templates/aws-kotlin-jvm-maven/serverless.yml @@ -64,7 +64,7 @@ functions: # - schedule: rate(10 minutes) # - sns: greeter-topic # - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000 -# - alexaSkill +# - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx # - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx # - iot: # sql: "SELECT * FROM 'some_topic'" diff --git a/lib/plugins/create/templates/aws-kotlin-nodejs-gradle/serverless.yml b/lib/plugins/create/templates/aws-kotlin-nodejs-gradle/serverless.yml index 5fd69aab1..bbe5cbed0 100644 --- a/lib/plugins/create/templates/aws-kotlin-nodejs-gradle/serverless.yml +++ b/lib/plugins/create/templates/aws-kotlin-nodejs-gradle/serverless.yml @@ -60,7 +60,7 @@ functions: # - schedule: rate(10 minutes) # - sns: greeter-topic # - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000 -# - alexaSkill +# - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx # - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx # - iot: # sql: "SELECT * FROM 'some_topic'" diff --git a/lib/plugins/create/templates/aws-nodejs-typescript/webpack.config.js b/lib/plugins/create/templates/aws-nodejs-typescript/webpack.config.js index b15fcd6d4..41c1876cd 100644 --- a/lib/plugins/create/templates/aws-nodejs-typescript/webpack.config.js +++ b/lib/plugins/create/templates/aws-nodejs-typescript/webpack.config.js @@ -3,6 +3,7 @@ const slsw = require('serverless-webpack'); module.exports = { entry: slsw.lib.entries, + devtool: 'source-map', resolve: { extensions: [ '.js', diff --git a/lib/plugins/create/templates/aws-nodejs/serverless.yml b/lib/plugins/create/templates/aws-nodejs/serverless.yml index 1cce386a1..471855a41 100644 --- a/lib/plugins/create/templates/aws-nodejs/serverless.yml +++ b/lib/plugins/create/templates/aws-nodejs/serverless.yml @@ -69,7 +69,7 @@ functions: # - schedule: rate(10 minutes) # - sns: greeter-topic # - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000 -# - alexaSkill +# - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx # - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx # - iot: # sql: "SELECT * FROM 'some_topic'" diff --git a/lib/plugins/create/templates/aws-python/serverless.yml b/lib/plugins/create/templates/aws-python/serverless.yml index f651608e0..2486ba648 100644 --- a/lib/plugins/create/templates/aws-python/serverless.yml +++ b/lib/plugins/create/templates/aws-python/serverless.yml @@ -69,7 +69,7 @@ functions: # - schedule: rate(10 minutes) # - sns: greeter-topic # - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000 -# - alexaSkill +# - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx # - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx # - iot: # sql: "SELECT * FROM 'some_topic'" diff --git a/lib/plugins/create/templates/aws-python3/serverless.yml b/lib/plugins/create/templates/aws-python3/serverless.yml index 1d69fcb9d..3d77f19ac 100644 --- a/lib/plugins/create/templates/aws-python3/serverless.yml +++ b/lib/plugins/create/templates/aws-python3/serverless.yml @@ -69,7 +69,7 @@ functions: # - schedule: rate(10 minutes) # - sns: greeter-topic # - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000 -# - alexaSkill +# - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx # - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx # - iot: # sql: "SELECT * FROM 'some_topic'" diff --git a/lib/plugins/create/templates/aws-scala-sbt/serverless.yml b/lib/plugins/create/templates/aws-scala-sbt/serverless.yml index 7eb89a39d..ee0e94d28 100644 --- a/lib/plugins/create/templates/aws-scala-sbt/serverless.yml +++ b/lib/plugins/create/templates/aws-scala-sbt/serverless.yml @@ -66,7 +66,7 @@ functions: # - schedule: rate(10 minutes) # - sns: greeter-topic # - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000 -# - alexaSkill +# - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx # - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx # - iot: # sql: "SELECT * FROM 'some_topic'" diff --git a/lib/plugins/create/templates/kubeless-nodejs/package.json b/lib/plugins/create/templates/kubeless-nodejs/package.json index f3231c9d5..039292997 100644 --- a/lib/plugins/create/templates/kubeless-nodejs/package.json +++ b/lib/plugins/create/templates/kubeless-nodejs/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "description": "Example function for serverless kubeless", "dependencies": { - "serverless-kubeless": "^0.2.4", + "serverless-kubeless": "^0.3.1", "lodash": "^4.1.0" }, "devDependencies": {}, diff --git a/lib/plugins/create/templates/kubeless-python/package.json b/lib/plugins/create/templates/kubeless-python/package.json index 9809da791..2eacb7672 100644 --- a/lib/plugins/create/templates/kubeless-python/package.json +++ b/lib/plugins/create/templates/kubeless-python/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "description": "Sample Kubeless Python serverless framework service.", "dependencies": { - "serverless-kubeless": "^0.2.4" + "serverless-kubeless": "^0.3.1" }, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" diff --git a/package-lock.json b/package-lock.json index 9c6add94c..a238c6311 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "serverless", - "version": "1.25.0", + "version": "1.26.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -33,9 +33,9 @@ "dev": true }, "acorn": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.2.1.tgz", - "integrity": "sha512-jG0u7c4Ly+3QkkW18V+NRDN+4bWHdln30NL1ZL2AvFZZmQe/BfopYCtghCKKVBUSetZ4QKcyA0pY6/4Gw8Pv8w==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.3.0.tgz", + "integrity": "sha512-Yej+zOJ1Dm/IMZzzj78OntP/r3zHEaKcyNoU2lAaxPtrseM6rF0xwqoz5Q5ysAiED9hTjI2hgtvLXitlCN1/Ug==", "dev": true }, "acorn-globals": { @@ -213,7 +213,7 @@ "graphql-anywhere": "3.1.0", "graphql-tag": "2.6.1", "redux": "3.7.2", - "symbol-observable": "1.1.0", + "symbol-observable": "1.2.0", "whatwg-fetch": "2.0.3" } }, @@ -367,9 +367,9 @@ "dev": true }, "assertion-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz", - "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true }, "async": { @@ -389,12 +389,11 @@ "dev": true }, "aws-sdk": { - "version": "2.172.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.172.0.tgz", - "integrity": "sha1-R9+3mQeXbrvVOFYupaJYNbWz810=", + "version": "2.188.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.188.0.tgz", + "integrity": "sha1-kGKrx9umOTRZ+i80I89dKU8ARhE=", "requires": { "buffer": "4.9.1", - "crypto-browserify": "1.0.9", "events": "1.1.1", "jmespath": "0.15.0", "querystring": "0.2.0", @@ -887,7 +886,7 @@ "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", "dev": true, "requires": { - "assertion-error": "1.0.2", + "assertion-error": "1.1.0", "deep-eql": "0.1.3", "type-detect": "1.0.0" } @@ -951,13 +950,13 @@ } }, "cli-usage": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/cli-usage/-/cli-usage-0.1.4.tgz", - "integrity": "sha1-fAHg3HBsI0s5yTODjI4gshdXduI=", + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/cli-usage/-/cli-usage-0.1.7.tgz", + "integrity": "sha512-x/Q52iLSZsRrRb2ePmTsVYXrGcrPQ8G4yRAY7QpMlumxAfPVrnDOH2X6Z5s8qsAX7AA7YuIi8AXFrvH0wWEesA==", "dev": true, "requires": { - "marked": "0.3.7", - "marked-terminal": "1.7.0" + "marked": "0.3.12", + "marked-terminal": "2.0.0" } }, "cli-width": { @@ -1202,11 +1201,6 @@ "boom": "2.10.1" } }, - "crypto-browserify": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-1.0.9.tgz", - "integrity": "sha1-zFRJaF37hesRyYKKzHy4erW7/MA=" - }, "crypto-random-string": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", @@ -1233,7 +1227,7 @@ "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", "dev": true, "requires": { - "es5-ext": "0.10.37" + "es5-ext": "0.10.38" } }, "damerau-levenshtein": { @@ -1481,9 +1475,9 @@ "dev": true }, "doctrine": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.2.tgz", - "integrity": "sha512-y0tm5Pq6ywp3qSTZ1vPgVdAnbDEoeoc5wlOHXoY1c4Wug/a7JvqHIl7BTvwodaHmejWkK/9dSb3sCYfyo/om8A==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, "requires": { "esutils": "2.0.2" @@ -1535,9 +1529,9 @@ } }, "end-of-stream": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.0.tgz", - "integrity": "sha1-epDYM+/abPpurA9JSduw+tOmMgY=", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", "requires": { "once": "1.4.0" } @@ -1585,9 +1579,9 @@ } }, "es5-ext": { - "version": "0.10.37", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.37.tgz", - "integrity": "sha1-DudB0Ui4AGm6J9AgOTdWryV978M=", + "version": "0.10.38", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.38.tgz", + "integrity": "sha512-jCMyePo7AXbUESwbl8Qi01VSH2piY9s/a3rSU/5w/MlTIx8HPL1xn2InGN8ejt/xulcJgnTO7vqNtOAxzYd2Kg==", "dev": true, "requires": { "es6-iterator": "2.0.3", @@ -1601,7 +1595,7 @@ "dev": true, "requires": { "d": "1.0.0", - "es5-ext": "0.10.37", + "es5-ext": "0.10.38", "es6-symbol": "3.1.1" } }, @@ -1612,7 +1606,7 @@ "dev": true, "requires": { "d": "1.0.0", - "es5-ext": "0.10.37", + "es5-ext": "0.10.38", "es6-iterator": "2.0.3", "es6-set": "0.1.5", "es6-symbol": "3.1.1", @@ -1632,7 +1626,7 @@ "dev": true, "requires": { "d": "1.0.0", - "es5-ext": "0.10.37", + "es5-ext": "0.10.38", "es6-iterator": "2.0.3", "es6-symbol": "3.1.1", "event-emitter": "0.3.5" @@ -1645,7 +1639,7 @@ "dev": true, "requires": { "d": "1.0.0", - "es5-ext": "0.10.37" + "es5-ext": "0.10.38" } }, "es6-weak-map": { @@ -1655,7 +1649,7 @@ "dev": true, "requires": { "d": "1.0.0", - "es5-ext": "0.10.37", + "es5-ext": "0.10.38", "es6-iterator": "2.0.3", "es6-symbol": "3.1.1" } @@ -1714,7 +1708,7 @@ "chalk": "1.1.3", "concat-stream": "1.6.0", "debug": "2.6.9", - "doctrine": "2.0.2", + "doctrine": "2.1.0", "escope": "3.6.0", "espree": "3.5.2", "esquery": "1.0.0", @@ -1727,7 +1721,7 @@ "imurmurhash": "0.1.4", "inquirer": "0.12.0", "is-my-json-valid": "2.17.1", - "is-resolvable": "1.0.1", + "is-resolvable": "1.1.0", "js-yaml": "3.10.0", "json-stable-stringify": "1.0.1", "levn": "0.3.0", @@ -1887,7 +1881,7 @@ "doctrine": "1.5.0", "has": "1.0.1", "jsx-ast-utils": "1.4.1", - "object.assign": "4.0.4" + "object.assign": "4.1.0" }, "dependencies": { "doctrine": { @@ -1908,7 +1902,7 @@ "integrity": "sha512-sadKeYwaR/aJ3stC2CdvgXu1T16TdYN+qwCpcWbMnGJ8s0zNWemzrvb2GbD4OhmJ/fwpJjudThAlLobGbWZbCQ==", "dev": true, "requires": { - "acorn": "5.2.1", + "acorn": "5.3.0", "acorn-jsx": "3.0.1" } }, @@ -1955,7 +1949,7 @@ "dev": true, "requires": { "d": "1.0.0", - "es5-ext": "0.10.37" + "es5-ext": "0.10.38" } }, "events": { @@ -2404,9 +2398,9 @@ "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=" }, "graphlib": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.1.tgz", - "integrity": "sha1-QjUsUrovTQNctWbrkfc5X3bryVE=", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.5.tgz", + "integrity": "sha512-XvtbqCcw+EM5SqQrIetIKKD+uZVNQtDPD1goIg7K73RuRZtVI5rYMdcCVSHm/AS1sCBZ7vt0p5WgXouucHQaOA==", "requires": { "lodash": "4.17.4" } @@ -2416,7 +2410,7 @@ "resolved": "https://registry.npmjs.org/graphql/-/graphql-0.10.5.tgz", "integrity": "sha512-Q7cx22DiLhwHsEfUnUip1Ww/Vfx7FS0w6+iHItNuN61+XpegHSa3k5U0+6M5BcpavQImBwFiy0z3uYwY7cXMLQ==", "requires": { - "iterall": "1.1.3" + "iterall": "1.1.4" } }, "graphql-anywhere": { @@ -2484,7 +2478,7 @@ "dev": true, "requires": { "chalk": "1.1.3", - "commander": "2.12.2", + "commander": "2.13.0", "is-my-json-valid": "2.17.1", "pinkie-promise": "2.0.1" }, @@ -2509,9 +2503,9 @@ } }, "commander": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.12.2.tgz", - "integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", + "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", "dev": true }, "supports-color": { @@ -2549,6 +2543,12 @@ "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.1.tgz", "integrity": "sha512-JkaetveU7hFbqnAC1EV1sF4rlojU2D4Usc5CmS69l6NfmPDnpnFUegzFg33eDkkpNCxZ0mQp65HwUDrNFS/8MA==" }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true + }, "has-to-string-tag-x": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", @@ -2794,9 +2794,9 @@ "dev": true }, "is-ci": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.0.10.tgz", - "integrity": "sha1-9zkzayYyNlBhqdSCcM1WrjNpMY4=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.1.0.tgz", + "integrity": "sha512-c7TnwxLePuqIlxHgr7xtxzycJPegNHFuIrBkwbf8hc58//+Op1CqFkyS+xnIMkwn9UsJIwc174BIjkyBmSpjKg==", "dev": true, "requires": { "ci-info": "1.1.2" @@ -3000,9 +3000,9 @@ } }, "is-resolvable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.1.tgz", - "integrity": "sha512-y5CXYbzvB3jTnWAZH1Nl7ykUWb6T3BcTs56HUruwBf8MhF56n1HWqhDWnVFo8GHrUPDgvUUNVhrc2U8W7iqz5g==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", "dev": true }, "is-retry-allowed": { @@ -3193,7 +3193,7 @@ "babel-types": "6.26.0", "babylon": "6.18.0", "istanbul-lib-coverage": "1.1.1", - "semver": "5.4.1" + "semver": "5.5.0" } }, "istanbul-lib-report": { @@ -3274,9 +3274,9 @@ } }, "iterall": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.1.3.tgz", - "integrity": "sha512-Cu/kb+4HiNSejAPhSaN1VukdNTTi/r4/e+yykqjlG/IW+1gZH5b4+Bq3whDX4tvbYugta3r8KTMUiqT3fIGxuQ==" + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.1.4.tgz", + "integrity": "sha512-eaDsM/PY8D/X5mYQhecVc5/9xvSHED7yPON+ffQroBeTuqUVm7dfphMkK8NksXuImqZlVRoKtrNfMIVCYIqaUQ==" }, "jest-changed-files": { "version": "17.0.2", @@ -3294,7 +3294,7 @@ "callsites": "2.0.0", "chalk": "1.1.3", "graceful-fs": "4.1.11", - "is-ci": "1.0.10", + "is-ci": "1.1.0", "istanbul-api": "1.2.1", "istanbul-lib-coverage": "1.1.1", "istanbul-lib-instrument": "1.9.1", @@ -3823,8 +3823,8 @@ "resolved": "https://registry.npmjs.org/json-refs/-/json-refs-2.1.7.tgz", "integrity": "sha1-uesB/in16j6Sh48VrqEK04taz4k=", "requires": { - "commander": "2.12.2", - "graphlib": "2.1.1", + "commander": "2.13.0", + "graphlib": "2.1.5", "js-yaml": "3.10.0", "native-promise-only": "0.8.1", "path-loader": "1.0.4", @@ -3833,9 +3833,9 @@ }, "dependencies": { "commander": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.12.2.tgz", - "integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==" + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", + "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==" } } }, @@ -4354,12 +4354,12 @@ "dev": true }, "markdown-magic": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/markdown-magic/-/markdown-magic-0.1.19.tgz", - "integrity": "sha1-IKnWWdqgx7DOt64DCVxuLK41KgM=", + "version": "0.1.20", + "resolved": "https://registry.npmjs.org/markdown-magic/-/markdown-magic-0.1.20.tgz", + "integrity": "sha1-Xw73k0L6G0O7pCr+Y9MCMobj7sM=", "dev": true, "requires": { - "commander": "2.12.2", + "commander": "2.13.0", "deepmerge": "1.5.2", "find-up": "2.1.0", "fs-extra": "1.0.0", @@ -4370,9 +4370,9 @@ }, "dependencies": { "commander": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.12.2.tgz", - "integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz", + "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==", "dev": true }, "find-up": { @@ -4435,15 +4435,15 @@ } }, "marked": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.7.tgz", - "integrity": "sha512-zBEP4qO1YQp5aXHt8S5wTiOv9i2X74V/LQL0zhUNvVaklt6Ywa6lChxIvS+ibYlCGgADwKwZFhjC3+XfpsvQvQ==", + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.12.tgz", + "integrity": "sha512-k4NaW+vS7ytQn6MgJn3fYpQt20/mOgYM5Ft9BYMfQJDz2QT6yEeS9XJ8k2Nw8JTeWK/znPPW2n3UJGzyYEiMoA==", "dev": true }, "marked-terminal": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-1.7.0.tgz", - "integrity": "sha1-yMRgiBx3LHYEtkNnAH7l938SWQQ=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-2.0.0.tgz", + "integrity": "sha1-Xq9Wi+ZvaGVBr6UqVYKAMQox3i0=", "dev": true, "requires": { "cardinal": "1.0.0", @@ -4742,11 +4742,11 @@ "integrity": "sha1-BW0UJE89zBzq3+aK+c/wxUc6M/M=", "dev": true, "requires": { - "cli-usage": "0.1.4", + "cli-usage": "0.1.7", "growly": "1.3.0", "lodash.clonedeep": "3.0.2", "minimist": "1.2.0", - "semver": "5.4.1", + "semver": "5.5.0", "shellwords": "0.1.1", "which": "1.3.0" } @@ -4768,7 +4768,7 @@ "requires": { "hosted-git-info": "2.5.0", "is-builtin-module": "1.0.0", - "semver": "5.4.1", + "semver": "5.5.0", "validate-npm-package-license": "3.0.1" } }, @@ -4848,13 +4848,14 @@ "dev": true }, "object.assign": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.0.4.tgz", - "integrity": "sha1-scnMBE7xuf5jYG/BQau7MuFHMMw=", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", "dev": true, "requires": { "define-properties": "1.1.2", "function-bind": "1.1.1", + "has-symbols": "1.0.0", "object-keys": "1.0.11" } }, @@ -4899,9 +4900,9 @@ "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=" }, "opn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-5.1.0.tgz", - "integrity": "sha512-iPNl7SyM8L30Rm1sjGdLLheyHVw5YXVfi3SKWJzBI7efxRwHojfRFjwE/OLM6qp9xJYMgab8WicTU1cPoY+Hpg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.2.0.tgz", + "integrity": "sha512-Jd/GpzPyHF4P2/aNOVmS3lfMSWV9J7cOhCG1s08XCEAsPkB7lp6ddiU0J7XzyQRDUh8BqJ7PchfINjR8jyofRQ==", "requires": { "is-wsl": "1.1.0" } @@ -4975,10 +4976,13 @@ "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" }, "p-limit": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.1.0.tgz", - "integrity": "sha1-sH/y2aXYi+yAYDWJWiurZqJ5iLw=", - "dev": true + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.2.0.tgz", + "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", + "dev": true, + "requires": { + "p-try": "1.0.0" + } }, "p-locate": { "version": "2.0.0", @@ -4986,18 +4990,24 @@ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, "requires": { - "p-limit": "1.1.0" + "p-limit": "1.2.0" } }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, "package-json": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", "requires": { "got": "6.7.1", - "registry-auth-token": "3.3.1", + "registry-auth-token": "3.3.2", "registry-url": "3.1.0", - "semver": "5.4.1" + "semver": "5.5.0" } }, "pako": { @@ -5197,9 +5207,9 @@ } }, "promise-queue": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/promise-queue/-/promise-queue-2.2.3.tgz", - "integrity": "sha1-hTTXa/RnPDuqOoK7oBvSlcww8U8=" + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/promise-queue/-/promise-queue-2.2.5.tgz", + "integrity": "sha1-L29ffA9tCBCelnZZx5uIqe1ek7Q=" }, "proto-list": { "version": "1.2.4", @@ -5322,9 +5332,9 @@ } }, "rc": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.2.tgz", - "integrity": "sha1-2M6ctX6NZNnHut2YdsfDTL48cHc=", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.4.tgz", + "integrity": "sha1-oPYGyq4qO4YrvQ74VILAElsxX6M=", "requires": { "deep-extend": "0.4.2", "ini": "1.3.5", @@ -5420,7 +5430,7 @@ "lodash": "4.17.4", "lodash-es": "4.17.4", "loose-envify": "1.3.1", - "symbol-observable": "1.1.0" + "symbol-observable": "1.2.0" } }, "regenerator-runtime": { @@ -5439,11 +5449,11 @@ } }, "registry-auth-token": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.1.tgz", - "integrity": "sha1-+w0yie4Nmtosu1KvXf5mywcNMAY=", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", + "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", "requires": { - "rc": "1.2.2", + "rc": "1.2.4", "safe-buffer": "5.1.1" } }, @@ -5452,7 +5462,7 @@ "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", "requires": { - "rc": "1.2.2" + "rc": "1.2.4" } }, "remarkable": { @@ -5533,7 +5543,7 @@ "stringstream": "0.0.5", "tough-cookie": "2.3.3", "tunnel-agent": "0.4.3", - "uuid": "3.1.0" + "uuid": "3.2.1" }, "dependencies": { "form-data": { @@ -5560,9 +5570,9 @@ "dev": true }, "uuid": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", - "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", + "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==", "dev": true } } @@ -5694,16 +5704,16 @@ } }, "semver": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", - "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", + "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" }, "semver-diff": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", "requires": { - "semver": "5.4.1" + "semver": "5.5.0" } }, "semver-regex": { @@ -6002,9 +6012,9 @@ } }, "symbol-observable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.1.0.tgz", - "integrity": "sha512-dQoid9tqQ+uotGhuTKEY11X4xhyYePVnqGSoSm3OGKh2E8LZ6RPULp1uXTctk33IeERlrRJYoVSBglsL05F5Uw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" }, "symbol-tree": { "version": "3.2.2", @@ -6118,7 +6128,7 @@ "integrity": "sha512-mQdgLPc/Vjfr3VWqWbfxW8yQNiJCbAZ+Gf6GDu1Cy0bdb33ofyiNGBtAY96jHFhDuivCwgW1H9DgTON+INiXgg==", "requires": { "bl": "1.2.1", - "end-of-stream": "1.4.0", + "end-of-stream": "1.4.1", "readable-stream": "2.3.3", "xtend": "4.0.1" } diff --git a/package.json b/package.json index 6c45371f6..ec049036b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "serverless", - "version": "1.25.0", + "version": "1.26.0", "engines": { "node": ">=4.0" }, diff --git a/tests/templates/integration-test-template b/tests/templates/integration-test-template index 468532a57..4bbd065d8 100755 --- a/tests/templates/integration-test-template +++ b/tests/templates/integration-test-template @@ -37,5 +37,10 @@ serverless deploy -v echo "Invoking Service" serverless invoke --function hello +if [ $template == "aws-go" ] || [ $template == "aws-go-dep" ] +then + serverless invoke --function world +fi + echo "Removing Service" serverless remove -v diff --git a/tests/templates/test_all_templates b/tests/templates/test_all_templates index 79bbebd55..fd88105ab 100755 --- a/tests/templates/test_all_templates +++ b/tests/templates/test_all_templates @@ -10,6 +10,8 @@ function integration-test { integration-test aws-csharp 'apt-get -qq update && apt-get -qq -y install zip && dotnet restore && dotnet lambda package --configuration release --framework netcoreapp1.0 --output-package bin/release/netcoreapp1.0/deploy-package.zip' integration-test aws-fsharp 'apt-get -qq update && apt-get -qq -y install zip && dotnet restore && dotnet lambda package --configuration release --framework netcoreapp1.0 --output-package bin/release/netcoreapp1.0/deploy-package.zip' +integration-test aws-go 'cd /go/src/app && make build' +integration-test aws-go-dep 'cd /go/src/app && make build' integration-test aws-groovy-gradle ./gradlew build integration-test aws-java-gradle ./gradlew build integration-test aws-java-maven mvn package