diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index e9693c9d5..3dfa2d2bc 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -6,7 +6,7 @@ ## What did you implement: -Closes #12345 +Closes #XXXXX - ## Todos: - [ ] Write tests - [ ] Write documentation - [ ] Fix linting errors - [ ] Make sure code coverage hasn't dropped -- [ ] Provide verification config/commands/resources -- [ ] Enable ["Allow edits from maintainers"](https://help.github.com/articles/allowing-changes-to-a-pull-request-branch-created-from-a-fork/) for this PR -- [ ] Change ready for review message below - +- [ ] Provide verification config / commands / resources +- [ ] Enable "Allow edits from maintainers" for this PR +- [ ] Update the messages below ***Is this ready for review?:*** NO ***Is it a breaking change?:*** NO diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d8ff1aa7..380629fa0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,42 @@ +# 1.6.1 (31.01.2017) +A minimal patch release that fixes an issue with rendering README.md on npm registry. + +# 1.6.0 (30.01.2017) + +**Important Note:** This release includes breaking changes. If your services stopped working after upgrading to v1.6.0, please read the following section. + +## Breaking Changes + +### CloudWatch logs are created explicitly +Up until this release, CloudWatch log groups were created implicitly by AWS/Lambda by default and were not included in your service stack. However, some users were able to easily reach the CloudWatch log group limits (currently at 500 log groups), and it wasn't an easy task to clear them all. Because of that we decided to explicitly create the log groups using CloudFormation so that you can easily remove them with `sls remove`. This was also optionally possible with the `cfLogs: true` config option. + +If your service doesn't have the `cfLogs: true` set, and one of the function has been invoked at least once (hence the log groups were created implicitly by AWS), then it's very likely that you'll receive a "log group already exists" error after upgrading to v1.6.0. That's because CF is now trying to create the already created log groups from scratch to include it in the stack resources. **To fix this breaking change,** simply delete the old log group, or rename your service if you **must** keep the old logs. + +### Removed function Arns from CloudFormation outputs +Up until this release, the output section of the generated CloudFormation template included an output resource for each function Arn. This caused deploying big services to fail because users were hitting the 60 outputs per stack limit. This effectively means that you can't have a service that has more than 60 functions. To avoid this AWS limit, we decided to remove those function output resources completely, to keep the stack clean. This also means removing the function Arns from the `sls info` command, and at the end of the deployment command. + +This is a breaking change for your project if you're depending on those function output resources in anyway, or if you're depending on function arn outputs from the deploy or info commands. Otherwise, your project shouldn't be affected by this change. Fixing this issue depends on your needs, but just remember that you can always create your own CF outputs in `serverless.yml`. + +### Moved `getStackName()` method +This is a breaking change for plugin authors only. If your plugin used the `provider.getStackName()` method, it has been moved to `naming.js`, and should be referenced with `provider.naming.getStackName()` instead. + +### Removed the `defaults` property from `serverless.yml` +We've finally dropped support for the `defaults` property which we introduced in v1. All child properties should now be moved to the `provider` object instead. + +## Non-breaking changes +- Reduce memory consumption on deploy by at least 50% (#3145) +- Added openwhisk template to `sls create` command (#3122) +- Allow Role 'Fn::GetAtt' for Lambda `role` (#3083) +- Added Access-Control-Allow-Credentials for CORS settings (#2736) +- add Support for SNS Subscription to existing topics (#2796) +- Function version resources are now optional. (#3042) +- Invoke local now supports python runtime. (#2937) +- Fixed "deployment bucket doesn't exist" error (#3107) +- Allowed function events value to be variables (#2434) + +## Meta +- [Comparison since last release](https://github.com/serverless/serverless/compare/v1.5.1...v1.6.0) + # 1.5.1 (19.01.2017) ## Bug Fixes diff --git a/README.md b/README.md index defd67243..ed2c00a92 100755 --- a/README.md +++ b/README.md @@ -106,6 +106,7 @@ The following are services you can instantly install and use by running `serverl * [serverless-examples](https://github.com/serverless/examples) * [CRUD](https://github.com/pmuens/serverless-crud) - CRUD service, [Scala Port](https://github.com/jahangirmohammed/serverless-crud-scala) +* [CRUD with FaunaDB](https://github.com/faunadb/serverless-crud) - CRUD service using FaunaDB * [CRUD with S3](https://github.com/tscanlin/serverless-s3-crud) - CRUD service using S3 * [GraphQL Boilerplate](https://github.com/serverless/serverless-graphql) - GraphQL application Boilerplate service * [Authentication](https://github.com/laardee/serverless-authentication-boilerplate) - Authentication boilerplate service @@ -158,7 +159,7 @@ This table is generated from https://github.com/serverless/plugins/blob/master/p | **[Serverless Crypt](https://github.com/marcy-terui/serverless-crypt)**
Securing the secrets on Serverless Framework by AWS KMS encryption. | [marcy-terui](http://github.com/marcy-terui) | | **[Serverless Dotnet](https://github.com/fruffin/serverless-dotnet)**
A serverless plugin to run 'dotnet' commands as part of the deploy process | [fruffin](http://github.com/fruffin) | | **[Serverless Dynamodb Local](https://github.com/99xt/serverless-dynamodb-local)**
Serverless Dynamodb Local Plugin - Allows to run dynamodb locally for serverless | [99xt](http://github.com/99xt) | -| **[Serverless Enable Api Logs](https://github.com/paulSambolin/serverless-enable-api-logs)**
Enables Coudwatch logging for API Gateway events | [paulSambolin](http://github.com/paulSambolin) | +| **[Serverless Enable Api Logs](https://github.com/paulSambolin/serverless-enable-api-logs)**
Enables Cloudwatch logging for API Gateway events | [paulSambolin](http://github.com/paulSambolin) | | **[Serverless Event Constant Inputs](https://github.com/dittto/serverless-event-constant-inputs)**
Allows you to add constant inputs to events in Serverless 1.0. For more info see [constant values in Cloudwatch](https://aws.amazon.com/blogs/compute/simply-serverless-use-constant-values-in-cloudwatch-event-triggered-lambda-functions/) | [dittto](http://github.com/dittto) | | **[Serverless Jest Plugin](https://github.com/SC5/serverless-jest-plugin)**
A Serverless Plugin for the Serverless Framework which adds support for test-driven development using Jest | [SC5](http://github.com/SC5) | | **[Serverless Mocha Plugin](https://github.com/SC5/serverless-mocha-plugin)**
A Serverless Plugin for the Serverless Framework which adds support for test-driven development using Mocha | [SC5](http://github.com/SC5) | diff --git a/docs/README.md b/docs/README.md index 57784b2ab..920b9c1a2 100644 --- a/docs/README.md +++ b/docs/README.md @@ -10,13 +10,13 @@ layout: Doc # Documentation -The Serverless Framework allows you to deploy auto-scaling, pay-per-execution, event-driven functions to any cloud. We currently support Amazon Web Service's Lambda, and are expanding to support other cloud providers. +The Serverless Framework allows you to deploy auto-scaling, pay-per-execution, event-driven functions to any cloud. We currently support AWS Lambda, IBM OpenWhisk, and are expanding to support other cloud providers.
@@ -43,7 +43,7 @@ The Serverless Framework allows you to deploy auto-scaling, pay-per-execution, e
@@ -69,7 +69,7 @@ The Serverless Framework allows you to deploy auto-scaling, pay-per-execution, e
@@ -88,7 +88,7 @@ The Serverless Framework allows you to deploy auto-scaling, pay-per-execution, e
@@ -100,3 +100,83 @@ The Serverless Framework allows you to deploy auto-scaling, pay-per-execution, e
+ + diff --git a/docs/providers/aws/cli-reference/invoke-local.md b/docs/providers/aws/cli-reference/invoke-local.md index e0f2efb95..1549bc848 100644 --- a/docs/providers/aws/cli-reference/invoke-local.md +++ b/docs/providers/aws/cli-reference/invoke-local.md @@ -61,3 +61,18 @@ This example will pass the json data in the `lib/data.json` file (relative to th ### Limitations Currently, `invoke local` only supports the NodeJs and Python runtimes. + +## Resource permissions + +Lambda functions assume an *IAM role* during execution: the framework creates this role, and set all the permission provided in the `iamRoleStatements` section of `serverless.yml`. + +Unless you explicitly state otherwise, every call to the AWS SDK inside the lambda function is made using this role (a temporary pair of key / secret is generated and set by AWS as environment variables, `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`). + +When you use `serverless invoke local`, the situation is quite different: the role isn't available (the function is executed on your local machine), so unless you set a different user directly in the code (or via a key pair of environment variables), the AWS SDK will use the default profile specified inside you AWS credential configuration file. + +Take a look to the official AWS documentation (in this particular instance, for the javascript SDK, but should be similar for all SDKs): + +- [http://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/loading-node-credentials-shared.html](http://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/loading-node-credentials-shared.html) +- [http://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/loading-node-credentials-lambda.html](http://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/loading-node-credentials-lambda.html) + +Whatever approach you decide to implement, **be aware**: the set of permissions might be (and probably is) different, so you won't have an exact simulation of the *real* IAM policy in place. diff --git a/docs/providers/aws/events/apigateway.md b/docs/providers/aws/events/apigateway.md index 12651e12e..cfeed2f1c 100644 --- a/docs/providers/aws/events/apigateway.md +++ b/docs/providers/aws/events/apigateway.md @@ -277,11 +277,11 @@ provider: - ${env:MY_API_KEY} # you can hide it in a serverless variable functions: hello: - events: - - http: - path: user/create - method: get - private: true + events: + - http: + path: user/create + method: get + private: true ``` Please note that those are the API keys names, not the actual values. Once you deploy your service, the value of those API keys will be auto generated by AWS and printed on the screen for you to use. diff --git a/docs/providers/aws/guide/credentials.md b/docs/providers/aws/guide/credentials.md index 13056cb66..abcd45ce1 100644 --- a/docs/providers/aws/guide/credentials.md +++ b/docs/providers/aws/guide/credentials.md @@ -104,6 +104,28 @@ provider: profile: devProfile ``` +##### Use an existing AWS Profile + +To easily switch between projects without the need to do `aws configure` every time you can use environment variables. +For example you define different profiles in `~/.aws/credentials` + +```ini +[profileName1] +aws_access_key_id=*************** +aws_secret_access_key=*************** + +[profileName2] +aws_access_key_id=*************** +aws_secret_access_key=*************** +``` + +Now you can switch per project (/ API) by executing once when you start your project: + +`export AWS_PROFILE="profileName2" && export AWS_REGION=eu-west-1`. + +in the Terminal. Now everything is set to execute all the `serverless` CLI options like `sls deploy`. +The AWS region setting is to prevent issues with specific services, so adapt if you need another default region. + #### Per Stage Profiles As an advanced use-case, you can deploy different stages to different accounts by using different profiles per stage. In order to use different profiles per stage, you must leverage [variables](https://serverless.com/framework/docs/providers/aws/guide/variables) and the provider profile setting. diff --git a/docs/providers/aws/guide/intro.md b/docs/providers/aws/guide/intro.md index 7c4a52984..a749ef3e0 100644 --- a/docs/providers/aws/guide/intro.md +++ b/docs/providers/aws/guide/intro.md @@ -36,7 +36,7 @@ You can perform multiple jobs in your code, but we don't recommend doing that wi Anything that triggers an AWS Lambda Function to execute is regarded by the Framework as an **Event**. Events are infrastructure events on AWS such as: -* *An AWS API Gateway HTTP endpoint (e.g., for a REST API)* +* *An AWS API Gateway HTTP endpoint request (e.g., for a REST API)* * *An AWS S3 bucket upload (e.g., for an image)* * *A CloudWatch timer (e.g., run every 5 minutes)* * *An AWS SNS topic (e.g., a message)* diff --git a/docs/providers/openwhisk/README.md b/docs/providers/openwhisk/README.md new file mode 100644 index 000000000..daf7afc24 --- /dev/null +++ b/docs/providers/openwhisk/README.md @@ -0,0 +1,19 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/) + + +# Serverless Apache OpenWhisk Provider Documentation + +Welcome to the Serverless Apache OpenWhisk documentation! + +Please select a section on the left to get started. + +If you have questions, join the [chat in gitter](https://gitter.im/serverless/serverless) or [post over on the forums](https://gitter.im/serverless/serverless) + +**Note:** Before continuing [Apache OpenWhisk system credentials](./guide/credentials.md) are required for using the CLI. diff --git a/docs/providers/openwhisk/cli-reference/README.md b/docs/providers/openwhisk/cli-reference/README.md new file mode 100644 index 000000000..9ed4202e6 --- /dev/null +++ b/docs/providers/openwhisk/cli-reference/README.md @@ -0,0 +1,17 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/cli-reference/) + + +# Serverless Apache OpenWhisk CLI Reference + +Welcome to the Serverless Apache OpenWhisk CLI Reference! Please select a section on the left to get started. + +**Note:** Before continuing [Apache OpenWhisk system credentials](../guide/credentials.md) are required for using the CLI. + +If you have questions, join the [chat in gitter](https://gitter.im/serverless/serverless) or [post over on the forums](http://forum.serverless.com/). diff --git a/docs/providers/openwhisk/cli-reference/config-credentials.md b/docs/providers/openwhisk/cli-reference/config-credentials.md new file mode 100644 index 000000000..401a6b14f --- /dev/null +++ b/docs/providers/openwhisk/cli-reference/config-credentials.md @@ -0,0 +1,37 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/cli-reference/config-credentials) + + +# Config Credentials + +```bash +serverless config credentials --provider provider --apihost apihost --auth auth +``` + +## Options + +- `--provider` or `-p` The provider (in this case `openwhisk`). **Required**. +- `--apihost` or `-h` The `openwhisk_apihost`. **Required**. +- `--auth` or `-a` The `openwhisk_auth`. **Required**. + +## Provided lifecycle events + +- `config:credentials:config` + +## Examples + +### Configure the default profile + +```bash +serverless config credentials --provider openwhisk --apihost openwhisk.ng.bluemix.net --auth username:password +``` + +Credentials are stored in `~/.wskprops`, which you can edit directly if needed. diff --git a/docs/providers/openwhisk/cli-reference/create.md b/docs/providers/openwhisk/cli-reference/create.md new file mode 100644 index 000000000..7803c0bad --- /dev/null +++ b/docs/providers/openwhisk/cli-reference/create.md @@ -0,0 +1,78 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/cli-reference/create) + + +# Create + +Creates a new service in the current working directory based on the provided template. + +**Create service in current working directory:** + +```bash +serverless create --template openwhisk-nodejs +``` + +**Create service in new folder:** + +```bash +serverless create --template openwhisk-nodejs --path myService +``` + +## Options +- `--template` or `-t` The name of one of the available templates. **Required**. +- `--path` or `-p` The path where the service should be created. +- `--name` or `-n` the name of the service in `serverless.yml`. + +## Provided lifecycle events +- `create:create` + +## Available Templates + +To see a list of available templates run `serverless create --help` + +Most commonly used templates: + +- openwhisk-nodejs +- plugin + +## Examples + +### Creating a new service + +```bash +serverless create --template openwhisk-nodejs --name my-special-service +``` + +This example will generate scaffolding for a service with `openwhisk` as a provider and `nodejs:6` as runtime. The scaffolding +will be generated in the current working directory. + +The provider which is used for deployment later on is Apache OpenWhisk. + +### Creating a named service in a (new) directory + +```bash +serverless create --template openwhisk-nodejs --path my-new-service +``` + +This example will generate scaffolding for a service with `openwhisk` as a provider and `nodejs` as runtime. The scaffolding +will be generated in the `my-new-service` directory. This directory will be created if not present. Otherwise Serverless +will use the already present directory. + +Additionally Serverless will rename the service according to the path you provide. In this example the service will be +renamed to `my-new-service`. + +### Creating a new plugin + +``` +serverless create --template plugin +``` + +This example will generate scaffolding for a hello world plugin that demonstrates how to create a new command and how to listen to the various events available. diff --git a/docs/providers/openwhisk/cli-reference/deploy-function.md b/docs/providers/openwhisk/cli-reference/deploy-function.md new file mode 100644 index 000000000..ae4c616ac --- /dev/null +++ b/docs/providers/openwhisk/cli-reference/deploy-function.md @@ -0,0 +1,22 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/cli-reference/deploy-function) + + +# Deploy Function + +The `sls deploy function` command deploys an individual function. This command simply compiles a deployment package with a single function handler. This is a much faster way of deploying changes in code. + +```bash +serverless deploy function -f functionName +``` + +## Options +- `--function` or `-f` The name of the function which should be deployed diff --git a/docs/providers/openwhisk/cli-reference/deploy.md b/docs/providers/openwhisk/cli-reference/deploy.md new file mode 100644 index 000000000..c1d63d5ae --- /dev/null +++ b/docs/providers/openwhisk/cli-reference/deploy.md @@ -0,0 +1,48 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/cli-reference/deploy) + + +# Deploy + +The `sls deploy` command deploys your entire service via the Apache OpenWhisk platform API. Run this command when you have made service changes (i.e., you edited `serverless.yml`). Use `serverless deploy function -f myFunction` when you have made code changes and you want to quickly upload your updated code to Apache OpenWhisk. + +```bash +serverless deploy +``` + +## Options +- `--noDeploy` or `-n` Skips the deployment steps and leaves artifacts in the `.serverless` directory +- `--verbose` or `-v` Shows all stack events during deployment, and display any Stack Output. + +## Artifacts + +After the `serverless deploy` command runs all created deployment artifacts are placed in the `.serverless` folder of the service. + +## Examples + +### Deployment without stage and region options + +```bash +serverless deploy +``` + +This is the simplest deployment usage possible. With this command Serverless will deploy your service to the defined +OpenWhisk platform endpoints. + +## Provided lifecycle events +- `deploy:cleanup` +- `deploy:initialize` +- `deploy:setupProviderConfiguration` +- `deploy:createDeploymentArtifacts` +- `deploy:compileFunctions` +- `deploy:compileEvents` +- `deploy:deploy` +- `deploy:function:deploy` diff --git a/docs/providers/openwhisk/cli-reference/info.md b/docs/providers/openwhisk/cli-reference/info.md new file mode 100644 index 000000000..0fb8e17f0 --- /dev/null +++ b/docs/providers/openwhisk/cli-reference/info.md @@ -0,0 +1,56 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/cli-reference/info) + + +# Info + +Displays information about the deployed service. + +```bash +serverless info +``` + +## Options +- `--verbose` or `-v` Shows displays any Stack Output. + +## Provided lifecycle events +- `info:info` + +## Examples + +### Apache OpenWhisk + +On Apache OpenWhisk the info plugin uses platform API to gather the necessary +information about deployed functions, events, routes and more. See the example +below for an example output. + +**Example:** + +```bash +$ serverless info +Service Information +platform: openwhisk.ng.bluemix.net +namespace: _ +service: hello-world + +actions: +hello-world-dev-helloWorld + +triggers: +my-hello-world-event + +rules: +my-hello-world-event-rule + +endpoints: +https://xyz1234-gws.api-gw.mybluemix.net +/hello-world GET -> hello-world +``` diff --git a/docs/providers/openwhisk/cli-reference/install.md b/docs/providers/openwhisk/cli-reference/install.md new file mode 100644 index 000000000..2cd8b8a80 --- /dev/null +++ b/docs/providers/openwhisk/cli-reference/install.md @@ -0,0 +1,53 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/cli-reference/install) + + +# Install + +Installs a service from a GitHub URL in the current working directory. + +```bash +serverless install --url https://github.com/some/service +``` + +## Options +- `--url` or `-u` The services GitHub URL. **Required**. +- `--name` or `-n` Name for the service. + +## Provided lifecycle events +- `install:install` + +## Examples + +### Installing a service from a GitHub URL + +```bash +serverless install --url https://github.com/jthomas/serverless-openwhisk-boilerplate +``` + +This example will download the .zip file of the `serverless-openwhisk-boilerplate` service from GitHub, create a new directory with the name `serverless-openwhisk-boilerplate` in the current working directory and unzips the files in this directory. + +### Installing a service from a GitHub URL with a new service name + +```bash +serverless install --url https://github.com/jthomas/serverless-openwhisk-boilerplate --name my-app +``` + +This example will download the .zip file of the `serverless-openwhisk-boilerplate` service from GitHub, create a new directory with the name `my-app` in the current working directory and unzips the files in this directory and renames the service to `my-app` if `serverless.yml` exists in the service root. + +### Installing a service from a directory in a GitHub URL + +```bash +serverless install --url +https://github.com/serverless/examples/tree/master/openwhisk-node-simple +``` + +This example will download the `openwhisk-node-simple` service from GitHub. diff --git a/docs/providers/openwhisk/cli-reference/invoke-local.md b/docs/providers/openwhisk/cli-reference/invoke-local.md new file mode 100644 index 000000000..c6f078f06 --- /dev/null +++ b/docs/providers/openwhisk/cli-reference/invoke-local.md @@ -0,0 +1,63 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/cli-reference/invoke-local) + + +# Invoke Local + +This runs your code locally by emulating the Apache OpenWhisk environment. Please keep in mind, it's not a 100% perfect emulation, there may be some differences, but it works for the vast majority of users. + +```bash +serverless invoke local --function functionName +``` + +## Options + +- `--function` or `-f` The name of the function in your service that you want to invoke locally. **Required**. +- `--path` or `-p` The path to a json file holding input data to be passed to the invoked function. This path is relative to the root directory of the service. The json file should have event and context properties to hold your mocked event and context data. +- `--data` or `-d` String data to be passed as an event to your function. 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 --function functionName +``` + +This example will locally invoke your function. + +### Local function invocation with data + +```bash +serverless invoke --function functionName --data "hello world" +``` + +```bash +serverless invoke --function functionName --data '{"a":"bar"}' +``` + +### Local function invocation with data from standard input + +```bash +node dataGenerator.js | serverless invoke local --function functionName +``` + +### Local function invocation with data passing + +```bash +serverless invoke local --function functionName --path lib/data.json +``` + +This example will pass the json data in the `lib/data.json` file (relative to the root of the service) while invoking the specified/deployed function. + +### Limitations + +Currently, `invoke local` only supports the NodeJs diff --git a/docs/providers/openwhisk/cli-reference/invoke.md b/docs/providers/openwhisk/cli-reference/invoke.md new file mode 100644 index 000000000..5bdebe8a4 --- /dev/null +++ b/docs/providers/openwhisk/cli-reference/invoke.md @@ -0,0 +1,83 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/cli-reference/invoke) + + +# Invoke + +Invokes deployed function. It allows to send event data to the function, read logs and display other important information of the function invocation. + +```bash +serverless invoke [local] --function functionName +``` + +## Options +- `--function` or `-f` The name of the function in your service that you want to invoke. **Required**. +- `--data` or `-d` String data to be passed as an event to your function. By default data is read from standard input. +- `--path` or `-p` The path to a json file with input data to be passed to the invoked function. This path is relative to the root directory of the service. +- `--type` or `-t` The type of invocation. Either `RequestResponse`, `Event` or `DryRun`. Default is `RequestResponse`. +- `--log` or `-l` If set to `true` and invocation type is `RequestResponse`, it will output logging data of the invocation. Default is `false`. + +## Provided lifecycle events +- `invoke:invoke` + +# Invoke Local + +Invokes a function locally for testing and logs the output. You can only invoke Node.js runtime locally at the moment. Keep in mind that we mock the `context` with simple mock data. + +```bash +serverless invoke local --function functionName +``` + +## Options +- `--function` or `-f` The name of the function in your service that you want to invoke locally. **Required**. +- `--path` or `-p` The path to a json file holding input data to be passed to the invoked function. This path is relative to the +root directory of the service. The json file should have event and context properties to hold your mocked event and context data. +- `--data` or `-d` String data to be passed as an event to your function. 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 + +### Apache OpenWhisk + +```bash +serverless invoke --function functionName +``` + +This example will invoke your deployed function on the configured platform +endpoint. This will output the result of the invocation in your terminal. + +#### Function invocation with data + +```bash +serverless invoke --function functionName --data '{"name": "Bernie"}' +``` + +#### Function invocation with data from standard input + +```bash +node dataGenerator.js | serverless invoke --function functionName +``` + +#### Function invocation with logging + +```bash +serverless invoke --function functionName --log +``` + +Just like the first example, but will also outputs logging information about your invocation. + +#### Function invocation with data passing + +```bash +serverless invoke --function functionName --path lib/data.json +``` + +This example will pass the json data in the `lib/data.json` file (relative to the root of the service) while invoking +the specified/deployed function. diff --git a/docs/providers/openwhisk/cli-reference/logs.md b/docs/providers/openwhisk/cli-reference/logs.md new file mode 100644 index 000000000..73ef2dba6 --- /dev/null +++ b/docs/providers/openwhisk/cli-reference/logs.md @@ -0,0 +1,75 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/cli-reference/logs) + + +# Logs + +Lets you watch the logs of a specific function. + +```bash +serverless logs -f hello +``` + +## Options + +- `--function` or `-f` The function you want to fetch the logs for. **Required** +- `--startTime` A specific unit in time to start fetching logs from (ie: `2010-10-20` or `1469705761`). Here's a list of the supported string formats: + +```bash +30m # since 30 minutes ago +2h # since 2 hours ago +3d # since 3 days ago + +2013-02-08 # A calendar date part +2013-W06-5 # A week date part +2013-039 # An ordinal date part + +20130208 # Basic (short) full date +2013W065 # Basic (short) week, weekday +2013W06 # Basic (short) week only +2013050 # Basic (short) ordinal date + +2013-02-08T09 # An hour time part separated by a T +20130208T080910,123 # Short date and time up to ms, separated by comma +20130208T080910.123 # Short date and time up to ms +20130208T080910 # Short date and time up to seconds +20130208T0809 # Short date and time up to minutes +20130208T08 # Short date and time, hours only +``` + +- `--filter` You can specify a filter string to filter the log output. This is useful if you want to to get the `error` logs for example. +- `--tail` or `-t` You can optionally tail the logs and keep listening for new logs in your terminal session by passing this option. +- `--interval` or `-i` If you choose to tail the output, you can control the interval at which the framework polls the logs with this option. The default is `1000`ms. + +## Examples + +### Apache OpenWhisk + +```bash +serverless logs -f hello --startTime 5h +``` +This will fetch the logs that happened in the past 5 hours. + +```bash +serverless logs -f hello --startTime 1469694264 +``` +This will fetch the logs that happened starting at epoch `1469694264`. + +```bash +serverless logs -f hello -t +``` + +Serverless will tail the platform log output and print new log messages coming in. + +```bash +serverless logs -f hello --filter serverless +``` +This will fetch only the logs that contain the string `serverless` diff --git a/docs/providers/openwhisk/cli-reference/remove.md b/docs/providers/openwhisk/cli-reference/remove.md new file mode 100644 index 000000000..c6894fc3d --- /dev/null +++ b/docs/providers/openwhisk/cli-reference/remove.md @@ -0,0 +1,32 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/cli-reference/remove) + + +# Remove + +The `sls remove` command will remove the deployed service, defined in your current working directory, from the provider. + +```bash +serverless remove +``` + +## Provided lifecycle events +- `remove:remove` + +## Examples + +### Removal of service in specific stage and region + +```bash +serverless remove +``` + +This example will remove the deployed service of your current working directory from the current platform endpoint. diff --git a/docs/providers/openwhisk/cli-reference/slstats.md b/docs/providers/openwhisk/cli-reference/slstats.md new file mode 100644 index 000000000..adc6bb7ce --- /dev/null +++ b/docs/providers/openwhisk/cli-reference/slstats.md @@ -0,0 +1,36 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/cli-reference/slstats) + + +# Serverless Statistics + +This plugin implements a way to toggle framework statistics. + +```bash +serverless slstats --enable +``` + +## Options +- `--enable` or `-e`. +- `--disable` or `-d` + +## Provided lifecycle events +- `slstats:slstats` + +## Examples + +### Disabling it + +```bash +serverless slstats --disable +``` + +This example will disable framework statistics. diff --git a/docs/providers/openwhisk/events/README.md b/docs/providers/openwhisk/events/README.md new file mode 100644 index 000000000..f84c2db1d --- /dev/null +++ b/docs/providers/openwhisk/events/README.md @@ -0,0 +1,19 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/events/) + + +# Serverless Apache OpenWhisk Events + +Welcome to the Serverless Apache OpenWhisk Events Glossary! + +Please select a section on the left to get started. + +If you have questions, join the [chat in gitter](https://gitter.im/serverless/serverless) or [post over on the forums](http://forum.serverless.com/) + +**Note:** Before continuing [Apache OpenWhisk system credentials](../guide/credentials.md) are required for using the CLI. diff --git a/docs/providers/openwhisk/events/apigateway.md b/docs/providers/openwhisk/events/apigateway.md new file mode 100644 index 000000000..536d57b03 --- /dev/null +++ b/docs/providers/openwhisk/events/apigateway.md @@ -0,0 +1,119 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/events/apigateway) + + +# API Gateway + +Apache OpenWhisk has an [API gateway](https://github.com/openwhisk/openwhisk/blob/master/docs/apigateway.md) included within the platform. This service +allows you to define public HTTP endpoints for your serverless functions. + +To create HTTP endpoints as Event sources for your Apache OpenWhisk Functions, use the Serverless Framework's easy API Gateway Events syntax. + +## API Gateway Events + +### Simple HTTP Endpoint + +This setup specifies that the `hello` function should be run when someone accesses the API gateway at `example/hello` via +a `GET` request. + +Here's an example: + +```yml +# serverless.yml + +functions: + example: + handler: handler.hello + events: + - http: GET hello +``` + +URL paths for the serverless functions are prefixed with the function name, e.g. +`/function_name/some/path`. + +```javascript +// handler.js + +'use strict'; + +module.exports.hello = function(params) { + // Your function handler + return { payload: 'Hello world!' }; +}; +``` + +When this service is deployed, the base API Gateway url will be +printed to the console. Combine this with your custom HTTP path to create +the full HTTP endpoint exposing your serverless function. + +``` +$ serverless deploy +... +Serverless: Configured API endpoint: https://xxx-yyy-gws.api-gw.mybluemix.net/example + +$ http get https://xxx-yyy-gws.api-gw.mybluemix.net/example/hello +{ + "payload": "Hello, World!" +} +``` + +### HTTP Endpoint with Parameters + +Here we've defined an POST endpoint for the path `posts/create`. + +```yml +# serverless.yml + +functions: + greeting: + handler: greeting.handler + events: + - http: POST greeting/generate +``` + +```javascript +// posts.js + +'use strict'; + +module.exports.handler = function(params) { + const name = params.name || 'stranger'; + // Your function handler + return { payload: `Hello ${name}!` }; +}; +``` + +The body of the incoming request is parsed as JSON and passed as the +`params` argument to the function handler. + +The returned JavaScript object will be serialised as JSON and returned in the +HTTP response body. + +### HTTP Endpoint with Extended Options + +Here we've defined an POST endpoint for the path `posts/create`. + +```yml +# serverless.yml + +functions: + create: + handler: posts.create + events: + - http: + path: posts/create + method: post +``` + +### CORS Support + +**Note:** All HTTP endpoints defined in this manner have cross-site requests +enabled for all source domains. diff --git a/docs/providers/openwhisk/events/schedule.md b/docs/providers/openwhisk/events/schedule.md new file mode 100644 index 000000000..733bfe888 --- /dev/null +++ b/docs/providers/openwhisk/events/schedule.md @@ -0,0 +1,57 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/events/schedule) + + +# Schedule + +This event allows you to set up scheduled invocations of your function. + +The plugin automatically configures a trigger and rule to connect your function +to the trigger feed from the [alarm package](https://github.com/openwhisk/openwhisk/blob/master/docs/catalog.md#using-the-alarm-package). + +## Configuration + +The `schedule` event configuration is controlled by a string, based on the UNIX +crontab syntax, in the format `cron(X X X X X)`. This can either be passed in +as a native string or through the `rate` parameter. + +### Simple + +The following config will attach a schedule event and causes the function `crawl` to be called every minute. + +```yaml +functions: + crawl: + handler: crawl + events: + - schedule: cron(* * * * * *) // run every minute +``` + +This automatically generates a new trigger (``${service}_crawl_schedule_trigger`) +and rule (`${service}_crawl_schedule_rule`) during deployment. + +### Customise Parameters + +Other schedule event parameters can be manually configured, e.g trigger or rule names. + +```yaml +functions: + aggregate: + handler: statistics.handler + events: + - schedule: + rate: cron(0 * * * *) // call once an hour + trigger: triggerName + rule: ruleName + max: 10000 // max invocations, default: 1000, max: 10000 + params: // event params for invocation + hello: world +``` diff --git a/docs/providers/openwhisk/events/triggers.md b/docs/providers/openwhisk/events/triggers.md new file mode 100644 index 000000000..e894218b9 --- /dev/null +++ b/docs/providers/openwhisk/events/triggers.md @@ -0,0 +1,82 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/events/streams) + + +# Triggers + +Functions are connected to event sources in OpenWhisk [using triggers and rules](https://github.com/openwhisk/openwhisk/blob/master/docs/triggers_rules.md). +Triggers create a named event stream within the system. Triggers can be fired +manually or connected to external data sources, like databases or message +queues. + +Rules set up a binding between triggers and serverless functions. With an active +rule, each time a trigger is fired, the function will be executed with the +trigger payload. + +Event binding using triggers and rules for functions can be configured through the `serverless.yaml` file. + +```yaml +functions: + my_function: + handler: index.main + events: + - trigger: my_trigger +``` +This configuration will create a trigger called `servicename-my_trigger` with an active rule binding `my_function` to this event stream. + +## Customising Rules + +Rule names default to the following format `servicename-trigger-to-action`. These names be explicitly set through configuration. + +```yaml +functions: + my_function: + handler: index.main + events: + - trigger: + name: "my_trigger" + rule: "rule_name" +``` + +## Customing Triggers + +Triggers can be defined as separate resources in the `serverless.yaml` file. This allows you to set up trigger properties like default parameters. + +```yaml +functions: + my_function: + handler: index.main + events: + - trigger: my_trigger + +resources: + triggers: + my_trigger: + parameters: + hello: world +``` + +## Trigger Feeds + +Triggers can be bound to external event sources using the `feed` property. OpenWhisk [provides a catalogue](https://github.com/openwhisk/openwhisk/blob/master/docs/catalog.md) of third-party event sources bundled as [packages](https://github.com/openwhisk/openwhisk/blob/master/docs/packages.md#creating-and-using-trigger-feeds). + +This example demonstrates setting up a trigger which uses the `/whisk.system/alarms/alarm` feed. The `alarm` feed will fire a trigger according to a user-supplied cron schedule. + +```yaml +resources: + triggers: + alarm_trigger: + parameters: + hello: world + feed: /whisk.system/alarms/alarm + feed_parameters: + cron: '*/8 * * * * *' +``` diff --git a/docs/providers/openwhisk/examples/README.md b/docs/providers/openwhisk/examples/README.md new file mode 100644 index 000000000..9878100d6 --- /dev/null +++ b/docs/providers/openwhisk/examples/README.md @@ -0,0 +1,19 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/examples/) + + +# Serverless Apache OpenWhisk Examples + +Have an example? Submit a PR or [open an issue](https://github.com/serverless/examples/issues). ⚡️ + +| Example | Runtime | +|:--------------------------- |:-----| +| [OpenWhisk Node Simple](https://github.com/serverless/examples/tree/master/openwhisk-node-simple)
Boilerplate project repository for OpenWhisk provider with Serverless Framework. | nodeJS | + +If you have questions, join the [chat in gitter](https://gitter.im/serverless/serverless) or [post over on the forums](http://forum.serverless.com/) diff --git a/docs/providers/openwhisk/examples/hello-world/README.md b/docs/providers/openwhisk/examples/hello-world/README.md new file mode 100644 index 000000000..8a92035e5 --- /dev/null +++ b/docs/providers/openwhisk/examples/hello-world/README.md @@ -0,0 +1,20 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/examples/hello-world/) + + +# Hello World Serverless Example 🌍 + +Welcome to the Hello World example. + +Pick your language of choice: + +* [JavaScript](./node) + +[View all examples](https://www.serverless.com/framework/docs/providers/openwhisk/examples/) diff --git a/docs/providers/openwhisk/examples/hello-world/node/README.md b/docs/providers/openwhisk/examples/hello-world/node/README.md new file mode 100644 index 000000000..f9a6d3999 --- /dev/null +++ b/docs/providers/openwhisk/examples/hello-world/node/README.md @@ -0,0 +1,38 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/examples/hello-world/node/) + + +# Hello World Node.js Example + +Make sure `serverless` is installed. [See installation guide](../../../guide/installation.md). + +## 1. Create a service +`serverless create --template openwhisk-nodejs --path myService` or `sls create --template openwhisk-nodejs --path myService`, where 'myService' is a new folder to be created with template service files. Change directories into this new folder. + +## 2. Install Provider Plugin +`npm install -g serverless-openwhisk` followed by `npm install` in the service directory. + +## 3. Deploy +`serverless deploy` or `sls deploy`. `sls` is shorthand for the Serverless CLI command + +## 4. Invoke deployed function +`serverless invoke --function helloWorld` or `serverless invoke -f helloWorld` + +`-f` is shorthand for `--function` + +In your terminal window you should see the response from Apache OpenWhisk + +```bash +{ + "payload": "Hello, World!" +} +``` + +Congrats you have just deployed and run your Hello World function! diff --git a/docs/providers/openwhisk/examples/hello-world/node/handler.js b/docs/providers/openwhisk/examples/hello-world/node/handler.js new file mode 100644 index 000000000..98edcb3ea --- /dev/null +++ b/docs/providers/openwhisk/examples/hello-world/node/handler.js @@ -0,0 +1,7 @@ +'use strict'; + +// Your function handler +module.exports.helloWorldHandler = function (params) { + const name = params.name || 'World'; + return { payload: `Hello, ${name}!` }; +}; diff --git a/docs/providers/openwhisk/examples/hello-world/node/package.json b/docs/providers/openwhisk/examples/hello-world/node/package.json new file mode 100644 index 000000000..d41fc10e8 --- /dev/null +++ b/docs/providers/openwhisk/examples/hello-world/node/package.json @@ -0,0 +1,9 @@ +{ + "name": "serverless-openwhisk-hello-world", + "version": "0.1.0", + "description": "Hello World example for OpenWhisk provider with Serverless Framework.", + "scripts": { + "postinstall": "npm link serverless-openwhisk", + "test": "echo \"Error: no test specified\" && exit 1" + } +} diff --git a/docs/providers/openwhisk/examples/hello-world/node/serverless.yml b/docs/providers/openwhisk/examples/hello-world/node/serverless.yml new file mode 100644 index 000000000..3b24c663b --- /dev/null +++ b/docs/providers/openwhisk/examples/hello-world/node/serverless.yml @@ -0,0 +1,15 @@ +# Hello World for Apache OpenWhisk +service: hello-world # Service Name + +provider: + name: openwhisk + +functions: + helloWorld: + handler: handler.helloWorldHandler + events: + - http: GET hello + +# remember to run npm install to download the provider plugin. +plugins: + - serverless-openwhisk diff --git a/docs/providers/openwhisk/guide/README.md b/docs/providers/openwhisk/guide/README.md new file mode 100644 index 000000000..2a567541a --- /dev/null +++ b/docs/providers/openwhisk/guide/README.md @@ -0,0 +1,19 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/guide/) + + +# Serverless Apache OpenWhisk Guide + +Welcome to the Serverless Apache OpenWhisk Guide! + +Get started with the **[Introduction to the framework](./intro.md)** + +If you have questions, join the [chat in gitter](https://gitter.im/serverless/serverless) or [post over on the forums](http://forum.serverless.com/) + +**Note:** Before continuing [Apache OpenWhisk user credentials](./credentials.md) are required for using the CLI. diff --git a/docs/providers/openwhisk/guide/credentials.md b/docs/providers/openwhisk/guide/credentials.md new file mode 100644 index 000000000..61b704bcd --- /dev/null +++ b/docs/providers/openwhisk/guide/credentials.md @@ -0,0 +1,125 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/guide/credentials) + + +# Credentials + +The Serverless Framework needs access to account credentials for your OpenWhisk provider so that it can create and manage resources on your behalf. + +OpenWhisk is an open-source serverless platform. This means you can either choose to run the platform yourself or choose to use a hosted provider's instance. + +Here we'll provide setup instructions for both options, just pick the one that you're using. + +## Register with OpenWhisk platform (IBM Bluemix) + +IBM's Bluemix cloud platform provides a hosted serverless solution based upon Apache OpenWhisk. + +Here's how to get started… + +- Sign up for a free account @ [https://bluemix.net](https://console.ng.bluemix.net/registration/) + +IBM Bluemix comes with a [free trial](https://www.ibm.com/cloud-computing/bluemix/pricing?cm_mc_uid=22424350960514851832143&cm_mc_sid_50200000=1485183214) that doesn't need credit card details for the first 30 days. Following the trial, developers have to enrol using a credit card but get a free tier for the platform and services. + +**All IBM Bluemix users get access to the [Free Tier for OpenWhisk](https://console.ng.bluemix.net/openwhisk/learn/pricing). This includes 400,000 GB-seconds of serverless function compute time per month.** + +Additional execution time is charged at $0.000017 per GB-second of execution, rounded to the nearest 100ms. + +### Access Account Credentials + +Once you have signed up for IBM Bluemix, we need to retrieve your account credentials. These are available on [the page](https://console.ng.bluemix.net/openwhisk/learn/cli) about installing the command-line tool from the [service homepage](https://console.ng.bluemix.net/openwhisk/). + +The second point in the instructions contains a command-line which includes the platform endpoint and authentication keys. + +``` +wsk property set --apihost openwhisk.ng.bluemix.net --auth XXX:YYY +``` + +**Make a note of the `apihost` and `auth` command flag values.** + +### (optional) Install command-line utility + +The command-line utility is linked from [the previous page](https://console.ng.bluemix.net/openwhisk/learn/cli). Download and install the binary into a location in your [shell path](http://unix.stackexchange.com/questions/26047/how-to-correctly-add-a-path-to-path). + + + +## Register with OpenWhisk platform (Self-Hosted) + +Following the [Quick Start guide](https://github.com/openwhisk/openwhisk#quick-start) will let you run the platform locally using a Virtual Machine. + +- Download and install [Vagrant](https://www.vagrantup.com/) for your platform. +- Run the following commands to retrieve, build and start an instance of the platform. + +``` +# Clone openwhisk +git clone --depth=1 https://github.com/openwhisk/openwhisk.git + +# Change directory to tools/vagrant +cd openwhisk/tools/vagrant + +# Run script to create vm and run hello action +./hello +``` + +This platform will now be running inside a virtual machine at the following IP address: `192.168.33.13` + +### Access Account Credentials + +The default environment has a guest account configured with the authentication key available here: https://github.com/openwhisk/openwhisk/blob/master/ansible/files/auth.guest + +Use the `192.168.33.13` address as the `apihost` value needed below. + +### (optional) Install command-line utility + +Building OpenWhisk from a cloned repository will result in the generation of the command line interface in `openwhisk/bin/go-cli/`. The default executable in this location will run on the operating system and CPU architecture on which it was built. + +Executables for other operating system, and CPU architectures are located in the following directories: `openwhisk/bin/go-cli/mac`, `openwhisk/bin/go-cli/linux`, `openwhisk/bin/go-cli/windows`. + +Download and install the correct binary into a location in your [shell path](http://unix.stackexchange.com/questions/26047/how-to-correctly-add-a-path-to-path). + + + +## Using Account Credentials + +You can configure the Serverless Framework to use your OpenWhisk credentials in two ways: + +#### Quick Setup + +As a quick setup to get started you can export them as environment variables so they would be accessible to Serverless Framework: + +```bash +export OW_AUTH= +export OW_APIHOST= +# OW_AUTH and OW_APIHOST are now available for serverless to use +serverless deploy +``` + +#### Using Configuration File + +For a more permanent solution you can also set up credentials through a configuration file. Here are different methods you can use to do so. + +##### Setup with the `wsk` cli + +If you have followed the instructions above to install the `wsk` command-line utility, run the following command to create the configuration file. + +```bash +$ wsk property set --apihost PLATFORM_API_HOST --auth USER_AUTH_KEY +``` + +Credentials are stored in `~/.wskprops`, which you can edit directly if needed. + +##### Edit file manually + +The following configuration values should be stored in a new file (`.wskprops`) in your home directory. Replace the `PLATFORM_API_HOST` and `USER_AUTH_KEY` values will the credentials from above. + +``` +APIHOST=PLATFORM_API_HOST +AUTH=USER_AUTH_KEY +``` diff --git a/docs/providers/openwhisk/guide/deploying.md b/docs/providers/openwhisk/guide/deploying.md new file mode 100644 index 000000000..63a2d1c9a --- /dev/null +++ b/docs/providers/openwhisk/guide/deploying.md @@ -0,0 +1,63 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/guide/deploying) + + +# Deploying + +The Serverless Framework was designed to provision your Apache OpenWhisk Functions, Triggers and Rules safely and quickly. It does this via a couple of methods designed for different types of deployments. + +## Deploy All + +This is the main method for doing deployments with the Serverless Framework: + +```bash +serverless deploy +``` + +Use this method when you have updated your Function, Event or Resource configuration in `serverless.yml` and you want to deploy that change (or multiple changes at the same time) to Apache OpenWhisk. + +### How It Works + +The Serverless Framework translates all syntax in `serverless.yml` to [platform API](http://petstore.swagger.io/?url=https://raw.githubusercontent.com/openwhisk/openwhisk/master/core/controller/src/main/resources/whiskswagger.json) calls to provision your Actions, Triggers, Rules and APIs. + +* Provider plugin parses `serverless.yml` configuration and translates to OpenWhisk resources. +* The code of your Functions is then packaged into zip files. +* Resources are deployed in the following order: *Functions, Function Sequences, API Routes, Triggers, Feeds, Rules.* +* Resources stages are deployed sequentially due to potential dependencies between the stages. +* Resources within a stage are deployed in parallel. +* Stages without any resources defined will be skipped. + +### Tips + +* Use this in your CI/CD systems, as it is the safest method of deployment. +* Apache OpenWhisk has a [maximum action artefact](https://github.com/openwhisk/openwhisk/blob/master/docs/reference.md#per-action-artifact-mb-fixed-48mb) size of 48MB. This might be an issue if you are using lots of NPM packages. JavaScript build tools like webpack can help to minify your code and save space. + +Check out the [deploy command docs](../cli-reference/deploy.md) for all details and options. + +## Deploy Function + +This deployment method updates a single function. It performs the platform API call to deploy your package without the other resources. It is much faster than redeploying your whole service each time. + +```bash +serverless deploy function --function myFunction +``` + +### How It Works + +* The Framework packages up the targeted Apache OpenWhisk Action into a zip file. +* That zip file is deployed to Apache OpenWhisk using the platform API. + +### Tips + +* Use this when you are developing and want to test on Apache OpenWhisk because it's much faster. +* During development, people will often run this command several times, as opposed to `serverless deploy` which is only run when larger infrastructure provisioning is required. + +Check out the [deploy command docs](../cli-reference/deploy.md) for all details and options. diff --git a/docs/providers/openwhisk/guide/events.md b/docs/providers/openwhisk/guide/events.md new file mode 100644 index 000000000..3727d6760 --- /dev/null +++ b/docs/providers/openwhisk/guide/events.md @@ -0,0 +1,57 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/guide/events) + + +# Events + +Simply put, events are the things that trigger your functions to run. + +If you are using Apache OpenWhisk as your provider, all `events` in the service are anything in Apache OpenWhisk that can trigger your Actions, like HTTP endpoints, message queues, database updates and cron-scheduled events. + +[View the Apache OpenWhisk events section for a list of supported events](../events) + +Upon deployment, the framework will set up the Triggers and Rules that correspond to that event and configure your `function` to listen to it. + +## Configuration + +Events belong to each Function and can be found in the `events` property in `serverless.yml`. + +```yml +# 'functions' in serverless.yml +functions: + createUser: # Function name + handler: handler.createUser # Reference to file handler.js & exported function 'createUser' + events: # All events associated with this function + - http: GET /users/create +``` + +The `events` property is an array, because it is possible for functions to be triggered by multiple events, as shown + +You can set multiple Events per Function, as long as that is supported by Apache OpenWhisk. + +```yml +# 'functions' in serverless.yml +functions: + createUser: # Function name + handler: handler.users # Reference to file handler.js & exported function 'users' + events: # All events associated with this function + - http: GET /users/create + - http: POST /users/update + - trigger: "custom trigger" +``` + +## Types + +The Serverless Framework supports all of the Apache OpenWhisk events and more. Instead of listing them here, we've put them in a separate section, since they have a lot of configurations and functionality. [Check out the events section for more information.](../events) + +## Deploying + +To deploy or update your Functions, Events and Routes, run `serverless deploy`. diff --git a/docs/providers/openwhisk/guide/functions.md b/docs/providers/openwhisk/guide/functions.md new file mode 100644 index 000000000..38f5fb23b --- /dev/null +++ b/docs/providers/openwhisk/guide/functions.md @@ -0,0 +1,97 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/guide/functions) + + +# Functions + +If you are using OpenWhisk as a provider, all *functions* inside the service are OpenWhisk Actions. + +## Configuration + +All of the OpenWhisk Actions in your serverless service can be found in `serverless.yml` under the `functions` property. + +```yml +# serverless.yml +service: myService + +provider: + name: openwhisk + runtime: nodejs:6 # optional, default is nodejs:default + memorySize: 512 # optional, default is 256 + timeout: 30 # optional, default is 60 + +functions: + hello: + handler: handler.hello # required, handler set in Apache OpenWhisk + name: some_custom_name # optional, default is ${service}_${function} + runtime: nodejs # optional overwrite, default is provider runtime + memory: 512 # optional overwrite, default is 256 + timeout: 10 # optional overwrite, default is 60 +``` + +The `handler` property points to the file and module containing the code you want to run in your function. + +```javascript +// handler.js +exports.handler = function(params) {} +``` + +You can add as many functions as you want within this property. + +```yml +# serverless.yml + +service: myService + +provider: + name: openwhisk + +functions: + functionOne: + handler: handler.functionOne + description: optional description for your Action + functionTwo: + handler: handler.functionTwo + functionThree: + handler: handler.functionThree +``` + +Your functions can either inherit their settings from the `provider` property. + +```yml +# serverless.yml +service: myService + +provider: + name: openwhisk + runtime: nodejs:6 + memory: 512 # will be inherited by all functions + +functions: + functionOne: + handler: handler.functionOne +``` + +Or you can specify properties at the function level. + +```yml +# serverless.yml +service: myService + +provider: + name: openwhisk + runtime: nodejs:6 + +functions: + functionOne: + handler: handler.functionOne + memory: 512 # function specific +``` diff --git a/docs/providers/openwhisk/guide/installation.md b/docs/providers/openwhisk/guide/installation.md new file mode 100644 index 000000000..07d152dc3 --- /dev/null +++ b/docs/providers/openwhisk/guide/installation.md @@ -0,0 +1,65 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/guide/installation) + + +# Installation + +### Installing Node.js + +Serverless is a [Node.js](https://nodejs.org) CLI tool so the first thing you need to do is to install Node.js on your machine. + +Go to the official [Node.js website](https://nodejs.org), download and follow the [installation instructions](https://nodejs.org/en/download/) to install Node.js on your local machine. + +**Note:** Serverless runs on Node v4 or higher. + +You can verify that Node.js is installed successfully by runnning `node --version` in your terminal. You should see the corresponding Node version number printed out. + +### Installing the Serverless Framework + +Next, install the Serverless Framework via [npm](https://npmjs.org) which was already installed when you installed Node.js. + +Open up a terminal and type `npm install -g serverless` to install Serverless. + +```bash +npm install -g serverless +``` + +Once the installation process is done you can verify that Serverless is installed successfully by running the following command in your terminal: + +```bash +serverless +``` + +To see which version of serverless you have installed run: + +```bash +serverless --version +``` + + + +### Installing OpenWhisk Provider Plugin + +Now we need to install the provider plugin to allow the framework to deploy services to the platform. This plugin is also [published](http://npmjs.com/package/serverless-openwhisk) on [npm](https://npmjs.org) and can installed using the same `npm install` command. + +``` +npm install -g serverless-openwhisk +``` + +*Due to an [outstanding issue](https://github.com/serverless/serverless/issues/2895) with provider plugins, the [OpenWhisk provider](https://github.com/serverless/serverless-openwhisk) must be installed as a global module.* + + + +### Setting up OpenWhisk + +To run serverless commands that interface with the OpenWhisk platform, you will need to setup your OpenWhisk account credentials on your machine. + +[Follow these instructions on setting up OpenWhisk credentials](./credentials.md) diff --git a/docs/providers/openwhisk/guide/intro.md b/docs/providers/openwhisk/guide/intro.md new file mode 100644 index 000000000..469b2962c --- /dev/null +++ b/docs/providers/openwhisk/guide/intro.md @@ -0,0 +1,77 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/guide/intro) + + +# Introduction + +The Serverless Framework helps you develop and deploy serverless applications using Apache OpenWhisk. It's a CLI that offers structure, automation and best practices out-of-the-box, allowing you to focus on building sophisticated, event-driven, serverless architectures, comprised of [Functions](#functions) and [Events](#events). + +The Serverless Framework is different than other application frameworks because: +* It manages your code as well as your infrastructure +* It supports multiple languages (Node.js, Python, Java, and more) + +## Core Concepts + +Here are the Framework's main concepts and how they pertain to Apache OpenWhisk… + +### Functions + +A Function is an [Apache OpenWhisk Action](https://github.com/openwhisk/openwhisk/blob/master/docs/actions.md). It's an independent unit of deployment, like a microservice. It's merely code, deployed in the cloud, that is most often written to perform a single job such as: + +* *Saving a user to the database* +* *Processing a file in a database* +* *Performing a scheduled task* + +You can perform multiple jobs in your code, but we don't recommend doing that without good reason. Separation of concerns is best and the Framework is designed to help you easily develop and deploy Functions, as well as manage lots of them. + +### Events + +Anything that triggers an Apache OpenWhisk Action to execute is regarded by the Framework as an **Event**. Events are platform events on Apache OpenWhisk such as: + +* *An API Gateway HTTP endpoint (e.g., for a REST API)* +* *A NoSQL database update (e.g., for a user profile)* +* *A scheduled timer (e.g., run every 5 minutes)* +* *A Kafka queue message (e.g., a message)* +* *A Webhook fires (e.g., Github project update)* +* *And more...* + +When you define an event for your Apache OpenWhisk Action in the Serverless Framework, the Framework will automatically translate this into [Triggers and Rules](https://github.com/openwhisk/openwhisk/blob/master/docs/triggers_rules.md) needed for that event and configure your functions to listen to it. + +### Services + +A **Service** is the Framework's unit of organization. You can think of it as a project file, though you can have multiple services for a single application. It's where you define your Functions, the Events that trigger them, and the Resources your Functions use, all in one file entitled `serverless.yml`. It looks like this: + +```yml +# serverless.yml + +service: users + +functions: # Your "Functions" + usersCreate: + events: # The "Events" that trigger this function + - http: post /users/create + usersDelete: + events: + - http: delete /users/delete +``` +When you deploy with the Framework by running `serverless deploy`, everything in `serverless.yml` is deployed at once. + +### Plugins + +You can overwrite or extend the functionality of the Framework using **Plugins**. Every `serverless.yml` can contain a `plugins:` property, which features multiple plugins. + +```yml +# serverless.yml + +plugins: + - serverless-plugin-identifier + - serverless-another-plugin +``` diff --git a/docs/providers/openwhisk/guide/packaging.md b/docs/providers/openwhisk/guide/packaging.md new file mode 100644 index 000000000..9af0acd58 --- /dev/null +++ b/docs/providers/openwhisk/guide/packaging.md @@ -0,0 +1,101 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/guide/packaging) + + +# Packaging + +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: + exclude: + - tmp/** + - .git/** + include: + - some-file + 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 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 +``` diff --git a/docs/providers/openwhisk/guide/plugins.md b/docs/providers/openwhisk/guide/plugins.md new file mode 100644 index 000000000..04ea5ee48 --- /dev/null +++ b/docs/providers/openwhisk/guide/plugins.md @@ -0,0 +1,318 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/guide/plugins) + + +# Plugins + +A Plugin is custom Javascript code that creates new or extends existing commands within the Serverless Framework. The Serverless Framework is merely a group of Plugins that are provided in the core. If you or your organization have a specific workflow, install a pre-written Plugin or write a plugin to customize the Framework to your needs. External Plugins are written exactly the same way as the core Plugins. + +## Installing Plugins + +External Plugins are added on a per service basis and are not applied globally. Make sure you are in your Service's root directory, then install the corresponding Plugin with the help of NPM: + +``` +npm install --save custom-serverless-plugin +``` + +We need to tell Serverless that we want to use the plugin inside our service. We do this by adding the name of the Plugin to the `plugins` section in the `serverless.yml` file. + +```yml +# serverless.yml file + +plugins: + - custom-serverless-plugin +``` + +Plugins might want to add extra information which should be accessible to Serverless. The `custom` section in the `serverless.yml` file is the place where you can add necessary +configurations for your plugins (the plugins author / documentation will tell you if you need to add anything there): + +```yml +plugins: + - custom-serverless-plugin + +custom: + customkey: customvalue +``` + +## Service local plugin + +If you are working on a plugin or have a plugin that is just designed for one project you can add them to the `.serverless_plugins` directory at the root of your service, and in the `plugins` array in `serverless.yml`. + +The plugin will be loaded based on being named `custom-serverless-plugin.js` or `custom-serverless-plugin\index.js` in the root of `.serverless_plugins` folder. + +### Load Order + +Keep in mind that the order you define your plugins matters. When Serverless loads all the core plugins and then the custom plugins in the order you've defined them. + +```yml +# serverless.yml + +plugins: + - plugin1 + - plugin2 +``` + +In this case `plugin1` is loaded before `plugin2`. + +## Writing Plugins + +### Concepts + +#### Plugin + +Code which defines *Commands*, any *Events* within a *Command*, and any *Hooks* assigned to an *Lifecycle Event*. + +* Command // CLI configuration, commands, subcommands, options + * LifecycleEvent(s) // Events that happen sequentially when the command is run + * Hook(s) // Code that runs when a Lifecycle Event happens during a Command + +#### Command + +A CLI *Command* that can be called by a user, e.g. `serverless deploy`. A Command has no logic, but simply defines the CLI configuration (e.g. command, subcommands, parameters) and the *Lifecycle Events* for the command. Every command defines its own lifecycle events. + +```javascript +'use strict'; + +class MyPlugin { + constructor() { + this.commands = { + deploy: { + lifecycleEvents: [ + 'resources', + 'functions' + ] + }, + }; + } +} + +module.exports = MyPlugin; +``` + +#### Lifecycle Events + +Events that fire sequentially during a Command. The above example list two Events. However, for each Event, and additional `before` and `after` event is created. Therefore, six Events exist in the above example: + +- `before:deploy:resources` +- `deploy:resources` +- `after:deploy:resources` +- `before:deploy:functions` +- `deploy:functions` +- `after:deploy:functions` + +The name of the command in front of lifecycle events when they are used for Hooks. + +#### Hooks + +A Hook binds code to any lifecycle event from any command. + +```javascript +'use strict'; + +class Deploy { + constructor() { + this.commands = { + deploy: { + lifecycleEvents: [ + 'resources', + 'functions' + ] + }, + }; + + this.hooks = { + 'before:deploy:resources': this.beforeDeployResources, + 'deploy:resources': this.deployResources, + 'after:deploy:functions': this.afterDeployFunctions + }; + } + + beforeDeployResources() { + console.log('Before Deploy Resources'); + } + + deployResources() { + console.log('Deploy Resources'); + } + + afterDeployFunctions() { + console.log('After Deploy Functions'); + } +} + +module.exports = Deploy; +``` + +### Nesting Commands + +You can also nest commands, e.g. if you want to provide a command `serverless deploy single`. Those nested commands have their own lifecycle events and do not inherit them from their parents. + +```javascript +'use strict'; + +class MyPlugin { + constructor() { + this.commands = { + deploy: { + lifecycleEvents: [ + 'resources', + 'functions' + ], + commands: { + function: { + lifecycleEvents: [ + 'package', + 'deploy' + ], + }, + }, + }, + } + } +} + +module.exports = MyPlugin; +``` + +### Defining Options + +Each (sub)command can have multiple Options. + +Options are passed in with a double dash (`--`) like this: `serverless function deploy --function functionName`. + +Option Shortcuts are passed in with a single dash (`-`) like this: `serverless function deploy -f functionName`. + +The `options` object will be passed in as the second parameter to the constructor of your plugin. + +In it, you can optionally add a `shortcut` property, as well as a `required` property. The Framework will return an error if a `required` Option is not included. + +**Note:** At this time, the Serverless Framework does not use parameters. + +```javascript +'use strict'; + +class Deploy { + constructor(serverless, options) { + this.serverless = serverless; + this.options = options; + + this.commands = { + deploy: { + lifecycleEvents: [ + 'functions' + ], + options: { + function: { + usage: 'Specify the function you want to deploy (e.g. "--function myFunction")', + shortcut: 'f', + required: true + } + } + }, + }; + + this.hooks = { + 'deploy:functions': this.deployFunction.bind(this) + } + } + + deployFunction() { + console.log('Deploying function: ', this.options.function); + } +} + +module.exports = Deploy; +``` + +### Provider Specific Plugins + +Plugins can be provider specific which means that they are bound to a provider. + +**Note:** Binding a plugin to a provider is optional. Serverless will always consider your plugin if you don't specify a `provider`. + +The provider definition should be added inside the plugins constructor: + +```javascript +'use strict'; + +class ProviderDeploy { + constructor(serverless, options) { + this.serverless = serverless; + this.options = options; + + // set the providers name here + this.provider = this.serverless.getProvider('providerName'); + + this.commands = { + deploy: { + lifecycleEvents: [ + 'functions' + ], + options: { + function: { + usage: 'Specify the function you want to deploy (e.g. "--function myFunction")', + required: true + } + } + }, + }; + + this.hooks = { + 'deploy:functions': this.deployFunction.bind(this) + } + } + + deployFunction() { + console.log('Deploying function: ', this.options.function); + } +} + +module.exports = ProviderDeploy; +``` + +The Plugin's functionality will now only be executed when the Serverless Service's provider matches the provider name which is defined inside the plugins constructor. + +### Serverless Instance + +The `serverless` instance which enables access to global service config during runtime is passed in as the first parameter to the plugin constructor. + +```javascript +'use strict'; + +class MyPlugin { + constructor(serverless, options) { + this.serverless = serverless; + this.options = options; + + this.commands = { + log: { + lifecycleEvents: [ + 'serverless' + ], + }, + }; + + this.hooks = { + 'log:serverless': this.logServerless.bind(this) + } + } + + logServerless() { + console.log('Serverless instance: ', this.serverless); + } +} + +module.exports = MyPlugin; +``` + +### Command Naming + +Command names need to be unique. If we load two commands and both want to specify the same command (e.g. we have an integrated command `deploy` and an external command also wants to use `deploy`) the Serverless CLI will print an error and exit. If you want to have your own `deploy` command you need to name it something different like `myCompanyDeploy` so they don't clash with existing plugins. diff --git a/docs/providers/openwhisk/guide/serverless.yml.md b/docs/providers/openwhisk/guide/serverless.yml.md new file mode 100644 index 000000000..97f27511f --- /dev/null +++ b/docs/providers/openwhisk/guide/serverless.yml.md @@ -0,0 +1,65 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/guide/serverless.yml) + + +# Serverless.yml Reference + +Here is a list of all available properties in `serverless.yml` when the provider is set to `openwhisk`. + +```yml +# serverless.yml + +service: myService + +frameworkVersion: ">=1.0.0 <2.0.0" + +provider: + name: openwhisk + runtime: nodejs:default + memory: 256 # Overwrite default memory size. Default is 512. + timeout: 10 # The default is 60 + overwrite: true # Can we overwrite deployed functions? default is true + namespace: 'custom' # use custom namespace, defaults to '_' + +functions: + usersCreate: # A Function + handler: users.create # The file and module for this specific function. + sequence: # Use sequences rather than handler to handle events. handler and sequence properties are mutually exclusive. + - function_a + - function_b + - function_c + memory: 256 # memorySize for this specific function. + timeout: 10 # Timeout for this specific function. Overrides the default set above. + runtime: nodejs:6 + overwrite: false # Can we overwrite deployed function? + namespace: 'custom' # use custom namespace, defaults to '_' + events: # The Events that trigger this Function + # This creates an API Gateway HTTP endpoint which can be used to trigger this function. Learn more in "events/apigateway" + - http: METHOD /path/to/url + - trigger: my_trigger # bind function to trigger event + - trigger: + name: my_trigger + rule: rule_name + +# The "Resources" your "Functions" use. This can be used to define custom Triggers and Rules which are bound to your Actions. +resources: + triggers: + my_trigger: # trigger with default parameter bound. + parameters: + hello: world + alarm_trigger: # trigger connected to event feed + parameters: + hello: world + feed: /whisk.system/alarms/alarm + feed_parameters: + cron: '*/8 * * * * *' + +``` diff --git a/docs/providers/openwhisk/guide/services.md b/docs/providers/openwhisk/guide/services.md new file mode 100644 index 000000000..a8f27791c --- /dev/null +++ b/docs/providers/openwhisk/guide/services.md @@ -0,0 +1,172 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/guide/services) + + +# Services + +A `service` is like a project. It's where you define your Apache OpenWhisk Functions, the `events` that trigger them and any `resources` they require, all in a file called `serverless.yml`. + +To get started building your first Serverless Framework project, create a `service`. + +## Organization + +In the beginning of an application, many people use a single Service to define all of the Functions, Events and Resources for that project. This is what we recommend in the beginning. + +```bash +myService/ + serverless.yml # Contains all functions and infrastructure resources +``` + +However, as your application grows, you can break it out into multiple services. A lot of people organize their services by workflows or data models, and group the functions related to those workflows and data models together in the service. + +```bash +users/ + serverless.yml # Contains 4 functions that do Users CRUD operations and the Users database +posts/ + serverless.yml # Contains 4 functions that do Posts CRUD operations and the Posts database +comments/ + serverless.yml # Contains 4 functions that do Comments CRUD operations and the Comments database +``` +This makes sense since related functions usually use common infrastructure resources, and you want to keep those functions and resources together as a single unit of deployment, for better organization and separation of concerns. + +## Creation + +To create a service, use the `create` command. You must also pass in a runtime (e.g., node.js, python etc.) you would like to write the service in. You can also pass in a path to create a directory and auto-name your service: + +```bash +# Create service with nodeJS template in the folder ./myService +serverless create --template openwhisk-nodejs --path myService +``` + +Here are the available runtimes for Apache OpenWhisk: + +* openwhisk-nodejs + +Check out the [create command docs](../cli-reference/create) for all the details and options. + +## Contents + +You'll see the following files in your working directory: +- `serverless.yml` +- `handler.js` + +### serverless.yml + +Each `service` configuration is managed in the `serverless.yml` file. The main responsibilities of this file are: + +- Declare a Serverless service +- Define one or more functions in the service + - Define the provider the service will be deployed to (and the runtime if provided) + - Define any custom plugins to be used + - Define events that trigger each function to execute (e.g. HTTP requests) + - Allow events listed in the `events` section to automatically create the resources required for the event upon deployment + - Allow flexible configuration using Serverless Variables + +You can see the name of the service, the provider configuration and the first function inside the `functions` definition which points to the `handler.js` file. Any further service configuration will be done in this file. + +```yml +# serverless.yml + +service: users + +provider: + name: openwhisk + runtime: nodejs:6 + memory: 512 # Overwrite the default memory size. Default is 256 + +functions: + usersCreate: # A Function + handler: users.create + events: # The Events that trigger this Function + - http: post /users/create + usersDelete: # A Function + handler: users.delete + events: # The Events that trigger this Function + - http: delete /users/delete +``` + +### handler.js + +The `handler.js` file contains your function code. The function definition in `serverless.yml` will point to this `handler.js` file and the function exported here. + +### event.json + +**Note:** This file is not created by default + +Create this file and add event data so you can invoke your function with the data via `serverless invoke -p event.json` + +## Deployment + +When you deploy a Service, all of the Functions, Events and Resources in your `serverless.yml` are translated into calls to the platform API to dynamically define those resources. + +To deploy a service, use the `deploy` command: + +```bash +serverless deploy +``` + +Check out the [deployment guide](https://serverless.com/framework/docs/providers/openwhisk/guide/deploying/) to learn more about deployments and how they work. Or, check out the [deploy command docs](../cli-reference/deploy) for all the details and options. + +## Removal + +To easily remove your Service from your Apache OpenWhisk account, you can use the `remove` command. + +Run `serverless remove -v` to trigger the removal process. As in the deploy step we're also running in the `verbose` mode so you can see all details of the remove process. + +Serverless will start the removal and informs you about it's process on the console. A success message is printed once the whole service is removed. + +The removal process will only remove the service on your provider's infrastructure. The service directory will still remain on your local machine so you can still modify and (re)deploy it to another stage, region or provider later on. + +## Version Pinning + +The Serverless framework is usually installed globally via `npm install -g serverless`. This way you have the Serverless CLI available for all your services. + +Installing tools globally has the downside that the version can't be pinned inside package.json. This can lead to issues if you upgrade Serverless, but your colleagues or CI system don't. You can now use a new feature in your serverless.yml which is available only in the latest version without worrying that your CI system will deploy with an old version of Serverless. + +### Pinning a Version + +To configure version pinning define a `frameworkVersion` property in your serverless.yaml. Whenever you run a Serverless command from the CLI it checks if your current Serverless version is matching the `frameworkVersion` range. The CLI uses [Semantic Versioning](http://semver.org/) so you can pin it to an exact version or provide a range. In general we recommend to pin to an exact version to ensure everybody in your team has the exact same setup and no unexpected problems happen. + +### Examples + +#### Exact Version + +```yml +# serverless.yml + +frameworkVersion: "=1.0.3" + +service: users + +provider: + name: openwhisk + runtime: nodejs + memorySize: 512 + +… +``` + +#### Version Range + +```yml +# serverless.yml + +frameworkVersion: ">=1.0.0 <2.0.0" + +service: users + +provider: + name: openwhisk + runtime: nodejs + memorySize: 512 + +… +``` diff --git a/docs/providers/openwhisk/guide/testing.md b/docs/providers/openwhisk/guide/testing.md new file mode 100644 index 000000000..a2d996817 --- /dev/null +++ b/docs/providers/openwhisk/guide/testing.md @@ -0,0 +1,108 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/guide/testing) + + +# Testing + +While the Serverless Architecture introduces a lot of simplicity when it comes to serving business logic, some of its characteristics present challenges for testing. They are: + +* The Serverless Architecture is an integration of separate, distributed services, which must be tested both independently, and together. +* The Serverless Architecture is dependent on internet/cloud services, which are hard to emulate locally. +* The Serverless Architecture can feature event-driven, asynchronous workflows, which are hard to emulate entirely. + +To get through these challenges, and to keep the [test pyramid](http://martinfowler.com/bliki/TestPyramid.html) in mind, keep the following principles in mind: + +* Write your business logic so that it is separate from your FaaS provider (e.g., Apache OpenWhisk), to keep it provider-independent, reusable and more easily testable. +* When your business logic is written separately from the FaaS provider, you can write traditional Unit Tests to ensure it is working properly. +* Write Integration Tests to verify integrations with other services are working correctly. + +## A Poor Example + +Here is an example in Node.js of how to follow the practices above. The job this Function should perform is to save a user in a database and then send a welcome email: + +```javascript +const db = require('db').connect(); +const mailer = require('mailer'); + +module.exports.saveUser = (params) => { + return Promise((resolve, reject) => { + const user = { + email: params.email, + created_at: Date.now() + } + + db.saveUser(user, function (err) { + if (err) { + reject(err); + } else { + mailer.sendWelcomeEmail(event.email); + resolve(); + } + }); + }) +}; +``` + +There are two main problems with this function: + +* The business logic is not separate from the FaaS Provider. It's bounded to how Apache OpenWhisk passes incoming data (`params` object). +* Testing this function will rely on separate services. Specifically, running a database instance and a mail server. + +## Writing Testable Apache OpenWhisk Functions + +Let's refactor the above example to separate the business logic from the FaaS Provider. + +```javascript +class Users { + constructor(db, mailer) { + this.db = db; + this.mailer = mailer; + } + + save(email, callback) { + return new Promise((resolve, reject) => { + const user = { + email: email, + created_at: Date.now() + } + + this.db.saveUser(user, function (err) { + if (err) { + reject(err); + } else { + this.mailer.sendWelcomeEmail(email); + resolve(); + } + }); + }) + } +} + +module.exports = Users; +``` + +```javascript +const db = require('db').connect(); +const mailer = require('mailer'); +const Users = require('users'); + +let users = new Users(db, mailer); + +module.exports.saveUser = (params) => { + return users.save(params.email); +}; +``` + +Now, the above class keeps business logic separate. Further, the code responsible for setting up dependencies, injecting them, calling business logic functions and interacting with Apache OpenWhisk is in its own file, which will be changed less often. This way, the business logic is not provider dependent, easier to re-use, and easier to test. + +Further, this code doesn't require running any external services. Instead of a real `db` and `mailer` services, we can pass mocks and assert if `saveUser` and `sendWelcomeEmail` has been called with proper arguments. + +Unit Tests can easily be written to cover the above class. An integration test can be added by invoking the function (`serverless invoke`) with fixture email address, check if user is actually saved to DB and check if email was received to see if everything is working together. diff --git a/docs/providers/openwhisk/guide/variables.md b/docs/providers/openwhisk/guide/variables.md new file mode 100644 index 000000000..f86dc6008 --- /dev/null +++ b/docs/providers/openwhisk/guide/variables.md @@ -0,0 +1,211 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/guide/variables) + + +# Variables + +The Serverless framework provides a powerful variable system which allows you to add dynamic data into your `serverless.yml`. With Serverless Variables, you'll be able to do the following: + +- Reference & load variables from environment variables +- Reference & load variables from CLI options +- Recursively reference properties of any type from the same `serverless.yml` file +- Recursively reference properties of any type from other YAML/JSON files +- Recursively nest variable references within each other for ultimate flexibility +- Combine multiple variable references to overwrite each other + +**Note:** You can only use variables in `serverless.yml` property **values**, not property keys. So you can't use variables to generate dynamic logical IDs in the custom resources section for example. + +## Reference Properties In serverless.yml +To self-reference properties in `serverless.yml`, use the `${self:someProperty}` syntax in your `serverless.yml`. This functionality is recursive, so you can go as deep in the object tree as you want. + +```yml +service: new-service +provider: openwhisk +custom: + globalSchedule: cron(0 * * * *) + +functions: + hello: + handler: handler.hello + events: + - schedule: ${self:custom.globalSchedule} + world: + handler: handler.world + events: + - schedule: ${self:custom.globalSchedule} +``` + +In the above example you're setting a global schedule for all functions by referencing the `globalSchedule` property in the same `serverless.yml` file. This way, you can easily change the schedule for all functions whenever you like. + +## Referencing Environment Variables +To reference environment variables, use the `${env:SOME_VAR}` syntax in your `serverless.yml` configuration file. + +```yml +service: new-service +provider: openwhisk +functions: + hello: + name: ${env:FUNC_PREFIX}-hello + handler: handler.hello + world: + name: ${env:FUNC_PREFIX}-world + handler: handler.world +``` + +In the above example you're dynamically adding a prefix to the function names by referencing the `FUNC_PREFIX` env var. So you can easily change that prefix for all functions by changing the `FUNC_PREFIX` env var. + +## Referencing CLI Options +To reference CLI options that you passed, use the `${opt:some_option}` syntax in your `serverless.yml` configuration file. + +```yml +service: new-service +provider: openwhisk +functions: + hello: + name: ${opt:stage}-hello + handler: handler.hello + world: + name: ${opt:stage}-world + handler: handler.world +``` + +In the above example, you're dynamically adding a prefix to the function names by referencing the `stage` option that you pass in the CLI when you run `serverless deploy --stage dev`. So when you deploy, the function name will always include the stage you're deploying to. + +## Reference Variables in Other Files +To reference variables in other YAML or JSON files, use the `${file(./myFile.yml):someProperty}` syntax in your `serverless.yml` configuration file. This functionality is recursive, so you can go as deep in that file as you want. Here's an example: + +```yml +# myCustomFile.yml +globalSchedule: cron(0 * * * *) +``` + +```yml +# serverless.yml +service: new-service +provider: openwhisk +custom: ${file(./myCustomFile.yml)} # You can reference the entire file +functions: + hello: + handler: handler.hello + events: + - schedule: ${file(./myCustomFile.yml):globalSchedule} # Or you can reference a specific property + world: + handler: handler.world + events: + - schedule: ${self:custom.globalSchedule} # This would also work in this case +``` + +In the above example, you're referencing the entire `myCustomFile.yml` file in the `custom` property. You need to pass the path relative to your service directory. You can also request specific properties in that file as shown in the `schedule` property. It's completely recursive and you can go as deep as you want. + +## Reference Variables in Javascript Files +To add dynamic data into your variables, reference javascript files by putting `${file(./myFile.js):someModule}` syntax in your `serverless.yml`. Here's an example: + +```js +// myCustomFile.js +module.exports.hello = () => { + // Code that generates dynamic data + return 'cron(0 * * * *)'; +} +``` + +```yml +# serverless.yml +service: new-service +provider: openwhisk +functions: + hello: + handler: handler.hello + events: + - schedule: ${file(./myCustomFile.js):hello} # Reference a specific module +``` + +You can also return an object and reference a specific property. Just make sure you are returning a valid object and referencing a valid property: + +```yml +# serverless.yml +service: new-service +provider: openwhisk +functions: + scheduledFunction: + handler: handler.scheduledFunction + events: + - schedule: ${file(./myCustomFile.js):schedule.hour} +``` + +```js +// myCustomFile.js +module.exports.schedule = () => { + // Code that generates dynamic data + return { + hour: 'cron(0 * * * *)' + }; +} +``` + +## Multiple Configuration Files + +Adding many custom resources to your `serverless.yml` file could bloat the whole file, so you can use the Serverless Variable syntax to split this up. + +```yml +resources: + Resources: ${file(openwhisk-resources.json)} +``` + +The corresponding resources which are defined inside the `openwhisk-resources.json` file will be resolved and loaded into the `Resources` section. + +## Nesting Variable References +The Serverless variable system allows you to nest variable references within each other for ultimate flexibility. So you can reference certain variables based on other variables. Here's an example: + +```yml +service: new-service +provider: openwhisk +custom: + myFlexibleArn: ${env:${opt:stage}_arn} + +functions: + hello: + handler: handler.hello +``` + +In the above example, if you pass `dev` as a stage option, the framework will look for the `dev_arn` environment variable. If you pass `production`, the framework will look for `production_arn`, and so on. This allows you to creatively use multiple variables by using a certain naming pattern without having to update the values of these variables constantly. You can go as deep as you want in your nesting, and can reference variables at any level of nesting from any source (env, opt, self or file). + +## Overwriting Variables +The Serverless framework gives you an intuitive way to reference multiple variables as a fallback strategy in case one of the variables is missing. This way you'll be able to use a default value from a certain source, if the variable from another source is missing. + +For example, if you want to reference the stage you're deploying to, but you don't want to keep on providing the `stage` option in the CLI. What you can do in `serverless.yml` is: + +```yml +service: new-service +provider: + name: openwhisk + stage: dev +custom: + myStage: ${opt:stage, self:provider.stage} + +functions: + hello: + handler: handler.hello +``` + +What this says is to use the `stage` CLI option if it exists, if not, use the default stage (which lives in `provider.stage`). So during development you can safely deploy with `serverless deploy`, but during production you can do `serverless deploy --stage production` and the stage will be picked up for you without having to make any changes to `serverless.yml`. + +You can have as many variable references as you want, from any source you want, and each of them can be of different type and different name. + +## Migrating serverless.env.yml +Previously we used the `serverless.env.yml` file to track Serverless Variables. It was a completely different system with different concepts. To migrate your variables from `serverless.env.yml`, you'll need to decide where you want to store your variables. + +**Using a config file:** You can still use `serverless.env.yml`, but the difference now is that you can structure the file however you want, and you'll need to reference each variable/property correctly in `serverless.yml`. For more info, you can check the file reference section above. + +**Using the same `serverless.yml` file:** You can store your variables in `serverless.yml` if they don't contain sensitive data, and then reference them elsewhere in the file using `self:someProperty`. For more info, you can check the self reference section above. + +**Using environment variables:** You can instead store your variables in environment variables and reference them with `env.someEnvVar`. For more info, you can check the environment variable reference section above. + +Now you don't need `serverless.env.yml` at all, but you can still use it if you want. It's just not required anymore. Migrating to the new variable system is easy and you just need to know how the new system works and make small adjustments to how you store & reference your variables. diff --git a/docs/providers/openwhisk/guide/workflow.md b/docs/providers/openwhisk/guide/workflow.md new file mode 100644 index 000000000..ff4a5750f --- /dev/null +++ b/docs/providers/openwhisk/guide/workflow.md @@ -0,0 +1,68 @@ + + + +### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/openwhisk/guide/workflow) + + +# Workflow + +Intro. Quick recommendations and tips for various processes. + +### Development Workflow + +1. Write your functions +2. Use `serverless deploy` only when you've made changes to `serverless.yml` and in CI/CD systems. +3. Use `serverless deploy function -f myFunction` to rapidly deploy changes when you are working on a specific Apache OpenWhisk Function. +4. Use `serverless invoke -f myFunction -l` to test your Apache OpenWhisk Functions. +5. Open up a separate tab in your console and stream logs in there via `serverless logs -f myFunction -t`. +6. Write tests to run locally. + +### Larger Projects +* Break your application/project into multiple Serverless Services. +* Model your Serverless Services around Data Models or Workflows. +* Keep the Functions and Resources in your Serverless Services to a minimum. + +## Cheat Sheet +A handy list of commands to use when developing with the Serverless Framework. + +##### Create A Service: +Creates a new Service +``` +serverless create -p [SERVICE NAME] -t openwhisk-nodejs +``` + +##### Install A Service +This is a convenience method to install a pre-made Serverless Service locally by downloading the Github repo and unzipping it. +``` +serverless install -u [GITHUB URL OF SERVICE] +``` + +##### Deploy All +Use this when you have made changes to your Functions, Events or Resources in `serverless.yml` or you simply want to deploy all changes within your Service at the same time. +``` +serverless deploy +``` + +##### Deploy Function +Use this to quickly overwrite your OpenWhisk Actions, allowing you to develop faster. +``` +serverless deploy function -f [FUNCTION NAME] +``` + +##### Invoke Function +Invokes an OpenWhisk Action and returns logs. +``` +serverless invoke function -f [FUNCTION NAME] -l +``` + +##### Streaming Logs +Open up a separate tab in your console and stream all logs for a specific Function using this command. +``` +serverless logs -f [FUNCTION NAME] +``` diff --git a/lib/Serverless.js b/lib/Serverless.js index 376cd8719..fd25eab20 100644 --- a/lib/Serverless.js +++ b/lib/Serverless.js @@ -12,7 +12,7 @@ const PluginManager = require('./classes/PluginManager'); const Utils = require('./classes/Utils'); const Service = require('./classes/Service'); const Variables = require('./classes/Variables'); -const SError = require('./classes/Error').SError; +const ServerlessError = require('./classes/Error').ServerlessError; const Version = require('./../package.json').version; class Serverless { @@ -42,7 +42,7 @@ class Serverless { this.classes.Utils = Utils; this.classes.Service = Service; this.classes.Variables = Variables; - this.classes.Error = SError; + this.classes.Error = ServerlessError; this.serverlessDirPath = path.join(os.homedir(), '.serverless'); } diff --git a/lib/Serverless.test.js b/lib/Serverless.test.js index ad0e1d940..aed7cfd8c 100644 --- a/lib/Serverless.test.js +++ b/lib/Serverless.test.js @@ -13,7 +13,7 @@ const PluginManager = require('../lib/classes/PluginManager'); const Utils = require('../lib/classes/Utils'); const Service = require('../lib/classes/Service'); const CLI = require('../lib/classes/CLI'); -const Error = require('../lib/classes/Error').SError; +const ServerlessError = require('../lib/classes/Error').ServerlessError; const testUtils = require('../tests/utils'); describe('Serverless', () => { @@ -105,7 +105,7 @@ describe('Serverless', () => { }); it('should store the Error class inside the classes object', () => { - expect(serverless.classes.Error).to.deep.equal(Error); + expect(serverless.classes.Error).to.deep.equal(ServerlessError); }); }); diff --git a/lib/classes/CLI.test.js b/lib/classes/CLI.test.js index 03a9f3f68..3a1d6b326 100644 --- a/lib/classes/CLI.test.js +++ b/lib/classes/CLI.test.js @@ -21,7 +21,7 @@ describe('CLI', () => { serverless = new Serverless({}); }); - describe('#construtor()', () => { + describe('#constructor()', () => { it('should set the serverless instance', () => { cli = new CLI(serverless); expect(cli.serverless).to.deep.equal(serverless); diff --git a/lib/classes/Error.js b/lib/classes/Error.js index 9f4607af6..f0c62b838 100644 --- a/lib/classes/Error.js +++ b/lib/classes/Error.js @@ -36,7 +36,7 @@ const writeMessage = (messageType, message) => { consoleLog(' '); }; -module.exports.SError = class ServerlessError extends Error { +module.exports.ServerlessError = class ServerlessError extends Error { constructor(message, statusCode) { super(message); this.name = this.constructor.name; @@ -46,6 +46,9 @@ module.exports.SError = class ServerlessError extends Error { } }; +// Deprecated - use ServerlessError instead +module.exports.SError = module.exports.ServerlessError; + module.exports.logError = (e) => { try { const errorType = e.name.replace(/([A-Z])/g, ' $1'); diff --git a/lib/classes/Error.test.js b/lib/classes/Error.test.js new file mode 100644 index 000000000..cff701fbd --- /dev/null +++ b/lib/classes/Error.test.js @@ -0,0 +1,149 @@ +'use strict'; +/* eslint-disable no-console */ + +const expect = require('chai').expect; +const ServerlessError = require('../../lib/classes/Error').ServerlessError; +const logError = require('../../lib/classes/Error').logError; + +describe('ServerlessError', () => { + describe('#constructor()', () => { + it('should store message', () => { + const error = new ServerlessError('a message', 'a status code'); + expect(error.message).to.be.equal('a message'); + }); + + it('should store name', () => { + const error = new ServerlessError('a message', 'a status code'); + expect(error.name).to.be.equal('ServerlessError'); + }); + + it('should store status code', () => { + const error = new ServerlessError('a message', 'a status code'); + expect(error.statusCode).to.be.equal('a status code'); + }); + + it('should have stack trace', () => { + let expectedError; + function testStackFrame() { + expectedError = new ServerlessError('a message', 'a status code'); + throw expectedError; + } + + let thrownError; + try { + testStackFrame(); + } catch (e) { + thrownError = e; + } + + expect(thrownError).to.exist; // eslint-disable-line no-unused-expressions + expect(thrownError).to.deep.equal(expectedError); + expect(thrownError.stack).to.exist; // eslint-disable-line no-unused-expressions + expect(thrownError.stack).to.have.string('testStackFrame'); + expect(thrownError.stack).to.not.have.string('new ServerlessError'); + expect(thrownError.stack).to.not.have.string('Error.js'); + }); + }); +}); + +describe('Error', () => { + describe('#logError()', () => { + beforeEach(() => { + this.cbProcessExit = null; + this.processExitCodes = []; + this.processExit = process.exit; + process.exit = (code) => { + this.processExitCodes.push(code); + if (this.cbProcessExit) { + this.cbProcessExit(); + } + }; + + this.consoleLogMessages = []; + this.consoleLog = console.log; + console.log = (msg) => { + this.consoleLogMessages.push(msg); + }; + }); + + afterEach(() => { + process.exit = this.processExit; + console.log = this.consoleLog; + + delete process.env.SLS_DEBUG; + }); + + it('should log error and exit', () => { + const error = new ServerlessError('a message', 'a status code'); + logError(error); + console.log = this.consoleLog; // For mocha to echo status of the test + + expect(this.processExitCodes.length).to.be.equal(1); + expect(this.processExitCodes).gt(0); + + const message = this.consoleLogMessages.join('\n'); + expect(message).to.have.string('Serverless Error'); + expect(message).to.have.string('a message'); + }); + + it('should shorten long messages', () => { + const error = new ServerlessError( + 'a message which is way to long so it gets shortened automatically to fit'); + logError(error); + console.log = this.consoleLog; + + const message = this.consoleLogMessages.join('\n'); + expect(message).to.have.string('Serverless Error'); + expect(message).to.have.string('a message which is way to long so it gets shortened'); + expect(message).to.not.have.string( + 'a message which is way to long so it gets shortened automatically to fit'); + }); + + it('should notify about SLS_DEBUG and ask report for unexpected errors', () => { + const error = new Error('an unexpected error'); + logError(error); + console.log = this.consoleLog; + + const message = this.consoleLogMessages.join('\n'); + expect(message).to.have.string('SLS_DEBUG=*'); + expect(message).to.have.string('Please report this error'); + }); + + it('should print stack trace with SLS_DEBUG', () => { + process.env.SLS_DEBUG = 1; + const error = new ServerlessError('a message'); + logError(error); + console.log = this.consoleLog; + + const message = this.consoleLogMessages.join('\n'); + expect(message).to.have.string('Stack Trace'); + expect(message).to.have.string(error.stack); + }); + + it('should not print stack trace without SLS_DEBUG', () => { + const error = new ServerlessError('a message'); + logError(error); + console.log = this.consoleLog; + + const message = this.consoleLogMessages.join('\n'); + expect(message).to.not.have.string('Stack Trace'); + expect(message).to.not.have.string(error.stack); + }); + + it('should re-throw error when handling raises an exception itself', () => { + const error = new ServerlessError('a message'); + const handlingError = new Error('an unexpected error'); + this.cbProcessExit = () => { throw handlingError; }; + + let thrownError = null; + try { + logError(error); + } catch (e) { + thrownError = e; + } + console.log = this.consoleLog; + + expect(thrownError).to.deep.equal(handlingError); + }); + }); +}); diff --git a/lib/classes/Service.js b/lib/classes/Service.js index 9d721cde7..fe5550d1e 100644 --- a/lib/classes/Service.js +++ b/lib/classes/Service.js @@ -1,6 +1,6 @@ 'use strict'; -const SError = require('./Error').SError; +const ServerlessError = require('./Error').ServerlessError; const path = require('path'); const _ = require('lodash'); const BbPromise = require('bluebird'); @@ -13,8 +13,7 @@ class Service { // Default properties this.service = null; - this.provider = {}; - this.defaults = { + this.provider = { stage: 'dev', region: 'us-east-1', variableSyntax: '\\${([ :a-zA-Z0-9._,\\-\\/\\(\\)]+?)}', @@ -60,13 +59,13 @@ class Service { `The Serverless version (${version}) does not satisfy the`, ` "frameworkVersion" (${ymlVersion}) in serverless.yml`, ].join(''); - throw new SError(errorMessage); + throw new ServerlessError(errorMessage); } if (!serverlessFile.service) { - throw new SError('"service" property is missing in serverless.yml'); + throw new ServerlessError('"service" property is missing in serverless.yml'); } if (!serverlessFile.provider) { - throw new SError('"provider" property is missing in serverless.yml'); + throw new ServerlessError('"provider" property is missing in serverless.yml'); } if (typeof serverlessFile.provider !== 'object') { @@ -83,7 +82,7 @@ class Service { ` Valid values for provider are: ${providers.join(', ')}.`, ' Please provide one of those values to the "provider" property in serverless.yml.', ].join(''); - throw new SError(errorMessage); + throw new ServerlessError(errorMessage); } if (Array.isArray(serverlessFile.resources)) { @@ -93,12 +92,15 @@ class Service { } that.service = serverlessFile.service; - that.provider = serverlessFile.provider; that.custom = serverlessFile.custom; that.plugins = serverlessFile.plugins; that.resources = serverlessFile.resources; that.functions = serverlessFile.functions || {}; + // merge so that the default settings are still in place and + // won't be overwritten + that.provider = _.merge(that.provider, serverlessFile.provider); + if (serverlessFile.package) { that.package.individually = serverlessFile.package.individually; that.package.artifact = serverlessFile.package.artifact; @@ -106,53 +108,6 @@ class Service { that.package.include = serverlessFile.package.include; } - if (serverlessFile.defaults && serverlessFile.defaults.stage) { - this.defaults.stage = serverlessFile.defaults.stage; - } - if (serverlessFile.defaults && serverlessFile.defaults.region) { - this.defaults.region = serverlessFile.defaults.region; - } - if (serverlessFile.defaults && serverlessFile.defaults.variableSyntax) { - this.defaults.variableSyntax = serverlessFile.defaults.variableSyntax; - } - - // load defaults property for backward compatibility - if (serverlessFile.defaults) { - const warningMessage = [ - 'Deprecation Notice: the "defaults" property in serverless.yml', - ' is deprecated. The "stage", "region" & "variableSyntax" properties', - ' has been moved to the "provider" property instead. Please update', - ' your serverless.yml file asap. For more info, you can check our docs.', - ].join(''); - this.serverless.cli.log(warningMessage); - - if (serverlessFile.defaults.stage) { - this.defaults.stage = serverlessFile.defaults.stage; - } - if (serverlessFile.defaults.region) { - this.defaults.region = serverlessFile.defaults.region; - } - if (serverlessFile.defaults.variableSyntax) { - this.defaults.variableSyntax = serverlessFile.defaults.variableSyntax; - } - } - - // if exists, move provider to defaults for backward compatibility - if (serverlessFile.provider.stage) { - this.defaults.stage = serverlessFile.provider.stage; - } - if (serverlessFile.provider.region) { - this.defaults.region = serverlessFile.provider.region; - } - if (serverlessFile.provider.variableSyntax) { - this.defaults.variableSyntax = serverlessFile.provider.variableSyntax; - } - - // make sure provider obj is in sync with default for backward compatibility - this.provider.stage = this.defaults.stage; - this.provider.region = this.defaults.region; - this.provider.variableSyntax = this.defaults.variableSyntax; - // setup function.name property const stageNameForFunction = options.stage || this.provider.stage; _.forEach(that.functions, (functionObj, functionName) => { @@ -173,7 +128,7 @@ class Service { validate() { _.forEach(this.functions, (functionObj, functionName) => { if (!_.isArray(functionObj.events)) { - throw new SError(`Events for "${functionName}" must be an array,` + + throw new ServerlessError(`Events for "${functionName}" must be an array,` + ` not an ${typeof functionObj.events}`); } }); @@ -193,14 +148,14 @@ class Service { if (functionName in this.functions) { return this.functions[functionName]; } - throw new SError(`Function "${functionName}" doesn't exist in this Service`); + throw new ServerlessError(`Function "${functionName}" doesn't exist in this Service`); } getEventInFunction(eventName, functionName) { if (eventName in this.getFunction(functionName).events) { return this.getFunction(functionName).events[eventName]; } - throw new SError(`Event "${eventName}" doesn't exist in function "${functionName}"`); + throw new ServerlessError(`Event "${eventName}" doesn't exist in function "${functionName}"`); } getAllEventsInFunction(functionName) { diff --git a/lib/classes/Service.test.js b/lib/classes/Service.test.js index 3f1676559..67d044e7e 100644 --- a/lib/classes/Service.test.js +++ b/lib/classes/Service.test.js @@ -22,8 +22,7 @@ describe('Service', () => { const serviceInstance = new Service(serverless); expect(serviceInstance.service).to.be.equal(null); - expect(serviceInstance.provider).to.deep.equal({}); - expect(serviceInstance.defaults).to.deep.equal({ + expect(serviceInstance.provider).to.deep.equal({ stage: 'dev', region: 'us-east-1', variableSyntax: '\\${([ :a-zA-Z0-9._,\\-\\/\\(\\)]+?)}', @@ -121,8 +120,8 @@ describe('Service', () => { const SUtils = new Utils(); const serverlessYml = { service: 'new-service', - provider: 'aws', - defaults: { + provider: { + name: 'aws', stage: 'dev', region: 'us-east-1', variableSyntax: '\\${{([\\s\\S]+?)}}', @@ -156,7 +155,7 @@ describe('Service', () => { return serviceInstance.load().then(() => { expect(serviceInstance.service).to.be.equal('new-service'); expect(serviceInstance.provider.name).to.deep.equal('aws'); - expect(serviceInstance.defaults.variableSyntax).to.equal('\\${{([\\s\\S]+?)}}'); + expect(serviceInstance.provider.variableSyntax).to.equal('\\${{([\\s\\S]+?)}}'); expect(serviceInstance.plugins).to.deep.equal(['testPlugin']); expect(serviceInstance.resources.aws).to.deep.equal({ resourcesProp: 'value' }); expect(serviceInstance.resources.azure).to.deep.equal({}); @@ -204,8 +203,8 @@ describe('Service', () => { const SUtils = new Utils(); const serverlessYml = { service: 'new-service', - provider: 'aws', - defaults: { + provider: { + name: 'aws', stage: 'dev', region: 'us-east-1', variableSyntax: '\\${{([\\s\\S]+?)}}', diff --git a/lib/classes/Utils.js b/lib/classes/Utils.js index ec57f161c..0dead5014 100644 --- a/lib/classes/Utils.js +++ b/lib/classes/Utils.js @@ -282,12 +282,6 @@ class Utils { let hasCustomVariableSyntaxDefined = false; const defaultVariableSyntax = '\\${([ :a-zA-Z0-9._,\\-\\/\\(\\)]+?)}'; - // check if the variableSyntax in the defaults section is defined - if (serverless.service.defaults && - serverless.service.defaults.variableSyntax && - serverless.service.defaults.variableSyntax !== defaultVariableSyntax) { - hasCustomVariableSyntaxDefined = true; - } // check if the variableSyntax in the provider section is defined if (serverless.service.provider && diff --git a/lib/classes/Utils.test.js b/lib/classes/Utils.test.js index 03f438d1b..3c338b488 100644 --- a/lib/classes/Utils.test.js +++ b/lib/classes/Utils.test.js @@ -396,8 +396,6 @@ describe('Utils', () => { provider: { name: 'aws', runtime: 'nodejs4.3', - }, - defaults: { stage: 'dev', region: 'us-east-1', variableSyntax: '\\${foo}', diff --git a/lib/classes/Variables.js b/lib/classes/Variables.js index faf3ac83d..5d4db09f3 100644 --- a/lib/classes/Variables.js +++ b/lib/classes/Variables.js @@ -20,7 +20,7 @@ class Variables { } loadVariableSyntax() { - this.variableSyntax = RegExp(this.service.defaults.variableSyntax, 'g'); + this.variableSyntax = RegExp(this.service.provider.variableSyntax, 'g'); } populateService(processedOptions) { @@ -29,10 +29,9 @@ class Variables { this.loadVariableSyntax(); - const variableSyntaxProperty = this.service.defaults.variableSyntax; + const variableSyntaxProperty = this.service.provider.variableSyntax; // temporally remove variable syntax from service otherwise it'll match - this.service.defaults.variableSyntax = true; this.service.provider.variableSyntax = true; /* @@ -49,7 +48,6 @@ class Variables { } }); - this.service.defaults.variableSyntax = variableSyntaxProperty; this.service.provider.variableSyntax = variableSyntaxProperty; return this.service; } diff --git a/lib/classes/Variables.test.js b/lib/classes/Variables.test.js index b01a265e1..913f40f4e 100644 --- a/lib/classes/Variables.test.js +++ b/lib/classes/Variables.test.js @@ -28,7 +28,7 @@ describe('Variables', () => { it('should set variableSyntax', () => { const serverless = new Serverless(); - serverless.service.defaults.variableSyntax = '\\${{([\\s\\S]+?)}}'; + serverless.service.provider.variableSyntax = '\\${{([\\s\\S]+?)}}'; serverless.variables.loadVariableSyntax(); expect(serverless.variables.variableSyntax).to.be.a('RegExp'); @@ -55,7 +55,6 @@ describe('Variables', () => { const fooValue = '${clientId()}'; const barValue = 'test'; - serverless.service.defaults.variableSyntax = variableSyntax; serverless.service.provider.variableSyntax = variableSyntax; serverless.service.custom = { @@ -68,7 +67,7 @@ describe('Variables', () => { }; serverless.variables.populateService(); - expect(serverless.service.defaults.variableSyntax).to.equal(variableSyntax); + expect(serverless.service.provider.variableSyntax).to.equal(variableSyntax); expect(serverless.service.resources.foo).to.equal(fooValue); expect(serverless.service.resources.bar).to.equal(barValue); }); @@ -367,14 +366,13 @@ describe('Variables', () => { const serverless = new Serverless(); serverless.variables.service = { service: 'testService', - provider: 'testProvider', - defaults: serverless.service.defaults, + provider: serverless.service.provider, }; serverless.variables.loadVariableSyntax(); - const valueToPopulate = serverless.variables.getValueFromSelf('self:provider'); - expect(valueToPopulate).to.be.equal('testProvider'); + const valueToPopulate = serverless.variables.getValueFromSelf('self:service'); + expect(valueToPopulate).to.be.equal('testService'); }); it('should handle self-references to the root of the serverless.yml file', () => { const serverless = new Serverless(); @@ -582,7 +580,7 @@ describe('Variables', () => { }, anotherVar: '${self:custom.var}', }, - defaults: serverless.service.defaults, + provider: serverless.service.provider, }; serverless.variables.loadVariableSyntax(); diff --git a/lib/plugins/aws/deploy/lib/uploadArtifacts.js b/lib/plugins/aws/deploy/lib/uploadArtifacts.js index 0c833d57f..0d70cda86 100644 --- a/lib/plugins/aws/deploy/lib/uploadArtifacts.js +++ b/lib/plugins/aws/deploy/lib/uploadArtifacts.js @@ -32,14 +32,13 @@ module.exports = { throw new this.serverless.classes.Error('artifactFilePath was not supplied'); } - const body = fs.readFileSync(artifactFilePath); const fileName = artifactFilePath.split(path.sep).pop(); const params = { Bucket: this.bucketName, Key: `${this.serverless.service.package.artifactDirectoryName}/${fileName}`, - Body: body, + Body: fs.createReadStream(artifactFilePath), ContentType: 'application/zip', }; diff --git a/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js b/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js index 33fa54de7..c21988996 100644 --- a/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js +++ b/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js @@ -71,10 +71,6 @@ describe('uploadArtifacts', () => { const artifactFilePath = path.join(tmpDirPath, 'artifact.zip'); serverless.utils.writeFileSync(artifactFilePath, 'artifact.zip file content'); - const artifactFileBuffer = new Buffer( - serverless.utils.readFileSync(artifactFilePath), 'binary' - ); - const putObjectStub = sinon .stub(awsDeploy.provider, 'request').returns(BbPromise.resolve()); @@ -86,7 +82,7 @@ describe('uploadArtifacts', () => { { Bucket: awsDeploy.bucketName, Key: `${awsDeploy.serverless.service.package.artifactDirectoryName}/artifact.zip`, - Body: artifactFileBuffer, + Body: sinon.match.object.and(sinon.match.has('path', artifactFilePath)), ContentType: 'application/zip', }, awsDeploy.options.stage, diff --git a/lib/plugins/aws/lib/validate.js b/lib/plugins/aws/lib/validate.js index 038754327..b0333ff95 100644 --- a/lib/plugins/aws/lib/validate.js +++ b/lib/plugins/aws/lib/validate.js @@ -10,10 +10,10 @@ module.exports = { } this.options.stage = this.options.stage - || (this.serverless.service.defaults && this.serverless.service.defaults.stage) + || (this.serverless.service.provider && this.serverless.service.provider.stage) || 'dev'; this.options.region = this.options.region - || (this.serverless.service.defaults && this.serverless.service.defaults.region) + || (this.serverless.service.provider && this.serverless.service.provider.region) || 'us-east-1'; return BbPromise.resolve(); diff --git a/lib/plugins/aws/lib/validate.test.js b/lib/plugins/aws/lib/validate.test.js index 1489cbdc0..d0c6a8dbf 100644 --- a/lib/plugins/aws/lib/validate.test.js +++ b/lib/plugins/aws/lib/validate.test.js @@ -39,9 +39,9 @@ describe('#validate', () => { }); }); - it('should use the service.defaults stage if present', () => { + it('should use the service.provider stage if present', () => { awsPlugin.options.stage = false; - awsPlugin.serverless.service.defaults = { + awsPlugin.serverless.service.provider = { stage: 'some-stage', }; @@ -57,9 +57,9 @@ describe('#validate', () => { }); }); - it('should use the service.defaults region if present', () => { + it('should use the service.provider region if present', () => { awsPlugin.options.region = false; - awsPlugin.serverless.service.defaults = { + awsPlugin.serverless.service.provider = { region: 'some-region', }; diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index f07034a84..8860e4d0d 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,6 +1,6 @@ { "name": "serverless", - "version": "1.5.1", + "version": "1.6.1", "dependencies": { "agent-base": { "version": "2.0.1", diff --git a/package.json b/package.json index 35fb340cc..681f95440 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,11 @@ { "name": "serverless", - "version": "1.5.1", + "version": "1.6.1", "engines": { "node": ">=4.0" }, "preferGlobal": true, + "homepage": "https://github.com/serverless/serverless#readme", "description": "Serverless Framework - Build web, mobile and IoT applications with serverless architectures using AWS Lambda, Azure Functions, Google CloudFunctions & more", "author": "serverless.com", "license": "MIT",