diff --git a/.gitignore b/.gitignore
index 54f8012af..c1351c17d 100755
--- a/.gitignore
+++ b/.gitignore
@@ -55,4 +55,4 @@ jest
# DotNet
[Bb]in/
-[Oo]bj/
\ No newline at end of file
+[Oo]bj/
diff --git a/docker-compose.yml b/docker-compose.yml
index 4c721f1af..cbc760b01 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -56,7 +56,7 @@ services:
volumes:
- ./tmp/serverless-integration-test-aws-scala-sbt:/app
aws-csharp:
- image: microsoft/dotnet:1.0.4-sdk
+ image: microsoft/dotnet:2.0-sdk
volumes:
- ./tmp/serverless-integration-test-aws-csharp:/app
aws-fsharp:
@@ -99,7 +99,7 @@ services:
- ./tmp/serverless-integration-test-spotinst-ruby:/app
spotinst-java8:
image: maven:3-jdk-8
- volumes:
+ volumes:
- ./tmp/serverless-integration-test-spotinst-java8:/app
webtasks-nodejs:
image: node:6.10.3
diff --git a/docs/providers/aws/events/apigateway.md b/docs/providers/aws/events/apigateway.md
index 724cacd4a..c5a6f0dac 100644
--- a/docs/providers/aws/events/apigateway.md
+++ b/docs/providers/aws/events/apigateway.md
@@ -12,6 +12,32 @@ layout: Doc
# API Gateway
+- [Lambda Proxy Integration](#lambda-proxy-integration)
+ - [Simple HTTP Endpoint](#simple-http-endpoint)
+ - [Example "LAMBDA-PROXY" event (default)](#example-lambda-proxy-event-default)
+ - [HTTP Endpoint with Extended Options](#http-endpoint-with-extended-options)
+ - [Enabling CORS](#enabling-cors)
+ - [HTTP Endpoints with `AWS_IAM` Authorizers](#http-endpoints-with-awsiam-authorizers)
+ - [HTTP Endpoints with Custom Authorizers](#http-endpoints-with-custom-authorizers)
+ - [Catching Exceptions In Your Lambda Function](#catching-exceptions-in-your-lambda-function)
+ - [Setting API keys for your Rest API](#setting-api-keys-for-your-rest-api)
+ - [Request Parameters](#request-parameters)
+- [Lambda Integration](#lambda-integration)
+ - [Example "LAMBDA" event (before customization)](#example-lambda-event-before-customization)
+ - [Request templates](#request-templates)
+ - [Default Request Templates](#default-request-templates)
+ - [Custom Request Templates](#custom-request-templates)
+ - [Pass Through Behavior](#pass-through-behavior)
+ - [Responses](#responses)
+ - [Custom Response Headers](#custom-response-headers)
+ - [Custom Response Templates](#custom-response-templates)
+ - [Status codes](#status-codes)
+ - [Available Status Codes](#available-status-codes)
+ - [Using Status Codes](#using-status-codes)
+ - [Custom Status Codes](#custom-status-codes)
+- [Setting an HTTP Proxy on API Gateway](#setting-an-http-proxy-on-api-gateway)
+- [Share API Gateway and API Resources](#share-api-gateway-and-api-resources)
+
_Are you looking for tutorials on using API Gateway? Check out the following resources:_
> - [Add a custom domain for your API Gateway](https://serverless.com/blog/serverless-api-gateway-domain/)
@@ -634,33 +660,7 @@ See the [api gateway documentation](https://docs.aws.amazon.com/apigateway/lates
**Notes:**
- A missing/empty request Content-Type is considered to be the API Gateway default (`application/json`)
- - [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/aws/events/apigateway)
-- [API Gateway](#api-gateway)
- - [Lambda Proxy Integration](#lambda-proxy-integration)
- - [Simple HTTP Endpoint](#simple-http-endpoint)
- - [Example "LAMBDA-PROXY" event (default)](#example-lambda-proxy-event-default)
- - [HTTP Endpoint with Extended Options](#http-endpoint-with-extended-options)
- - [Enabling CORS](#enabling-cors)
- - [HTTP Endpoints with `AWS_IAM` Authorizers](#http-endpoints-with-awsiam-authorizers)
- - [HTTP Endpoints with Custom Authorizers](#http-endpoints-with-custom-authorizers)
- - [Catching Exceptions In Your Lambda Function](#catching-exceptions-in-your-lambda-function)
- - [Setting API keys for your Rest API](#setting-api-keys-for-your-rest-api)
- - [Request Parameters](#request-parameters)
- - [Lambda Integration](#lambda-integration)
- - [Example "LAMBDA" event (before customization)](#example-lambda-event-before-customization)
- - [Request templates](#request-templates)
- - [Default Request Templates](#default-request-templates)
- - [Custom Request Templates](#custom-request-templates)
- - [Pass Through Behavior](#pass-through-behavior)
- - [Responses](#responses)
- - [Custom Response Headers](#custom-response-headers)
- - [Custom Response Templates](#custom-response-templates)
- - [Status codes](#status-codes)
- - [Available Status Codes](#available-status-codes)
- - [Using Status Codes](#using-status-codes)
- - [Custom Status Codes](#custom-status-codes)
- - [Setting an HTTP Proxy on API Gateway](#setting-an-http-proxy-on-api-gateway)
- - [Share API Gateway and API Resources](#share-api-gateway-and-api-resources)
+- API Gateway docs refer to "WHEN_NO_TEMPLATE" (singular), but this will fail during creation as the actual value should be "WHEN_NO_TEMPLATES" (plural)
### Responses
@@ -862,7 +862,7 @@ As your application grows, you will likely need to break it out into multiple, s
```yml
service: service-name
-provider:
+provider:
name: aws
apiGateway:
restApiId: xxxxxxxxxx # REST API resource ID. Default is generated by the framework
@@ -873,13 +873,14 @@ functions:
```
+
If your application has many nested paths, you might also want to break them out into smaller services.
```yml
service: service-a
-provider:
+provider:
apiGateway:
- restApiId: xxxxxxxxxx
+ restApiId: xxxxxxxxxx
restApiRootResourceId: xxxxxxxxxx
functions:
@@ -893,10 +894,10 @@ functions:
```yml
service: service-b
-provider:
+provider:
apiGateway:
- restApiId: xxxxxxxxxx
- restApiRootResourceId: xxxxxxxxxx
+ restApiId: xxxxxxxxxx
+ restApiRootResourceId: xxxxxxxxxx
functions:
create:
@@ -911,13 +912,13 @@ The above example services both reference the same parent path `/posts`. However
```yml
service: service-a
-provider:
+provider:
apiGateway:
- restApiId: xxxxxxxxxx
+ restApiId: xxxxxxxxxx
restApiRootResourceId: xxxxxxxxxx
restApiResources:
/posts: xxxxxxxxxx
-
+
functions:
...
@@ -925,10 +926,10 @@ functions:
```yml
service: service-b
-provider:
+provider:
apiGateway:
- restApiId: xxxxxxxxxx
- restApiRootResourceId: xxxxxxxxxx
+ restApiId: xxxxxxxxxx
+ restApiRootResourceId: xxxxxxxxxx
restApiResources:
/posts: xxxxxxxxxx
@@ -942,14 +943,14 @@ You can define more than one path resource, but by default, Serverless will gene
```yml
service: service-a
-provider:
+provider:
apiGateway:
- restApiId: xxxxxxxxxx
+ restApiId: xxxxxxxxxx
# restApiRootResourceId: xxxxxxxxxx # Optional
restApiResources:
/posts: xxxxxxxxxx
/categories: xxxxxxxxx
-
+
functions:
listPosts:
diff --git a/docs/providers/aws/examples/hello-world/csharp/Handler.cs b/docs/providers/aws/examples/hello-world/csharp/Handler.cs
deleted file mode 100644
index cf4ff9549..000000000
--- a/docs/providers/aws/examples/hello-world/csharp/Handler.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using Amazon.Lambda.Core;
-using System;
-
-[assembly:LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))]
-
-namespace AwsDotnetCsharp
-{
- public class Handler
- {
- public Response Hello(Request request)
- {
- return new Response("Go Serverless v1.0! Your function executed successfully!", request);
- }
- }
-
- public class Response
- {
- public string Message {get; set;}
- public Request Request {get; set;}
-
- public Response(string message, Request request){
- Message = message;
- Request = request;
- }
- }
-
- public class Request
- {
- public string Key1 {get; set;}
- public string Key2 {get; set;}
- public string Key3 {get; set;}
-
- public Request(string key1, string key2, string key3){
- Key1 = key1;
- Key2 = key2;
- Key3 = key3;
- }
- }
-}
diff --git a/docs/providers/aws/examples/hello-world/csharp/README.md b/docs/providers/aws/examples/hello-world/csharp/README.md
index f230f7f88..371c7dc36 100644
--- a/docs/providers/aws/examples/hello-world/csharp/README.md
+++ b/docs/providers/aws/examples/hello-world/csharp/README.md
@@ -35,7 +35,7 @@ Using the `create` command we can specify one of the available [templates](https
The `--path` or shorthand `-p` is the location to be created with the template service files. Change directories into this new folder.
-## 2. Build using .NET CLI tools and create zip package
+## 2. Build using .NET Core 2.X CLI tools and create zip package
```
# Linux or Mac OS
diff --git a/docs/providers/aws/examples/hello-world/csharp/csharp.csproj b/docs/providers/aws/examples/hello-world/csharp/csharp.csproj
deleted file mode 100644
index c0d2e4c69..000000000
--- a/docs/providers/aws/examples/hello-world/csharp/csharp.csproj
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
- netcoreapp1.0
- CsharpHandlers
- csharp
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/docs/providers/aws/examples/hello-world/csharp/serverless.yml b/docs/providers/aws/examples/hello-world/csharp/serverless.yml
deleted file mode 100644
index 3e622e1f5..000000000
--- a/docs/providers/aws/examples/hello-world/csharp/serverless.yml
+++ /dev/null
@@ -1,85 +0,0 @@
-# Welcome to Serverless!
-#
-# This file is the main config file for your service.
-# It's very minimal at this point and uses default values.
-# You can always add more config options for more control.
-# We've included some commented out config examples here.
-# Just uncomment any of them to get that config option.
-#
-# For full config options, check the docs:
-# docs.serverless.com
-#
-# Happy Coding!
-
-service: aws-csharp # NOTE: update this with your service name
-
-# You can pin your service to only deploy with a specific Serverless version
-# Check out our docs for more details
-# frameworkVersion: "=X.X.X"
-
-provider:
- name: aws
- runtime: dotnetcore1.0
-
-# you can overwrite defaults here
-# stage: dev
-# region: us-east-1
-
-# you can add statements to the Lambda function's IAM Role here
-# iamRoleStatements:
-# - Effect: "Allow"
-# Action:
-# - "s3:ListBucket"
-# Resource: { "Fn::Join" : ["", ["arn:aws:s3:::", { "Ref" : "ServerlessDeploymentBucket" } ] ] }
-# - Effect: "Allow"
-# Action:
-# - "s3:PutObject"
-# Resource:
-# Fn::Join:
-# - ""
-# - - "arn:aws:s3:::"
-# - "Ref" : "ServerlessDeploymentBucket"
-# - "/*"
-
-# you can define service wide environment variables here
-# environment:
-# variable1: value1
-
-# you can add packaging information here
-package:
- artifact: bin/release/netcoreapp1.0/deploy-package.zip
-# exclude:
-# - exclude-me.js
-# - exclude-me-dir/**
-
-functions:
- hello:
- handler: CsharpHandlers::AwsDotnetCsharp.Handler::Hello
-
-# The following are a few example events you can configure
-# NOTE: Please make sure to change your handler code to work with those events
-# Check the event documentation for details
-# events:
-# - http:
-# path: users/create
-# method: get
-# - s3: ${env:BUCKET}
-# - schedule: rate(10 minutes)
-# - sns: greeter-topic
-# - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000
-
-# Define function environment variables here
-# environment:
-# variable2: value2
-
-# you can add CloudFormation resource templates here
-#resources:
-# Resources:
-# NewResource:
-# Type: AWS::S3::Bucket
-# Properties:
-# BucketName: my-new-bucket
-# Outputs:
-# NewOutput:
-# Description: "Description for the output"
-# Value: "Some output value"
diff --git a/docs/providers/aws/examples/hello-world/fsharp/README.md b/docs/providers/aws/examples/hello-world/fsharp/README.md
index b973a9c46..f1e14c97d 100644
--- a/docs/providers/aws/examples/hello-world/fsharp/README.md
+++ b/docs/providers/aws/examples/hello-world/fsharp/README.md
@@ -5,6 +5,10 @@ description: Create a F# Hello World Lambda function
layout: Doc
-->
+
+### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/aws/examples/hello-world/fsharp/)
+
+
# Hello World F# Example
Make sure `serverless` is installed. [See installation guide](../../../guide/installation.md).
diff --git a/docs/providers/aws/examples/hello-world/go/README.md b/docs/providers/aws/examples/hello-world/go/README.md
index 1720c009b..05a233a33 100644
--- a/docs/providers/aws/examples/hello-world/go/README.md
+++ b/docs/providers/aws/examples/hello-world/go/README.md
@@ -30,7 +30,6 @@ You should also have [go](https://golang.org/doc/install) and [make](https://www
It is always good practice to organize your `go` projects within [GOPATH](https://golang.org/doc/code.html#GOPATH), to maximize the benefits of go tooling.
## 1. Create a service
-There are two templates for `go`:
The Serverless Framework includes starter templates for various languages and providers. There are two templates for `go`.
@@ -50,7 +49,7 @@ sls create --template aws-go --path myService
sls create --template aws-go-dep --path myService
```
-Using the `create` command we can specify one of the available [templates](https://serverless.com/framework/docs/providers/aws/cli-reference/create#available-templates). For this example use aws-nodejs with the `--template` or shorthand `-t` flag.
+Using the `create` command we can specify one of the available [templates](https://serverless.com/framework/docs/providers/aws/cli-reference/create#available-templates). For this example use aws-go-dep with the `--template` or shorthand `-t` flag.
The `--path` or shorthand `-p` is the location to be created with the template service files.
diff --git a/docs/providers/aws/guide/iam.md b/docs/providers/aws/guide/iam.md
index 19aacd6f3..9a0164cce 100644
--- a/docs/providers/aws/guide/iam.md
+++ b/docs/providers/aws/guide/iam.md
@@ -45,7 +45,17 @@ provider:
- "/*"
```
+Alongside `provider.iamRoleStatements` managed policies can also be added to this service-wide Role, define managed policies in `provider.iamManagedPolicies`. These will also be merged into the generated IAM Role so you can use `Join`, `Ref` or any other CloudFormation method or feature here too.
+```yml
+service: new-service
+provider:
+ name: aws
+ iamManagedPolicies:
+ - 'some:aws:arn:xxx:*:*'
+ - 'someOther:aws:arn:xxx:*:*'
+ - { 'Fn::Join': [':', ['arn:aws:iam:', { Ref: 'AWSAccountId' }, 'some/path']] }
+```
## Custom IAM Roles
**WARNING:** You need to take care of the overall role setup as soon as you define custom roles.
diff --git a/docs/providers/aws/guide/serverless.yml.md b/docs/providers/aws/guide/serverless.yml.md
index a10c2767d..3660e307c 100644
--- a/docs/providers/aws/guide/serverless.yml.md
+++ b/docs/providers/aws/guide/serverless.yml.md
@@ -62,6 +62,8 @@ provider:
rateLimit: 100
stackTags: # Optional CF stack tags
key: value
+ iamManagedPolicies: # Optional IAM Managed Policies, which allows to include the policies into IAM Role
+ - arn:aws:iam:*****:policy/some-managed-policy
iamRoleStatements: # IAM role statements so that services can be accessed in the AWS account
- Effect: 'Allow'
Action:
@@ -103,6 +105,8 @@ package: # Optional deployment packaging configuration
- .git/**
- .travis.yml
excludeDevDependencies: false # Config if Serverless should automatically exclude dev dependencies in the deployment package. Defaults to true
+ artifact: path/to/my-artifact.zip # Own package that should be used. You must provide this file.
+ individually: true # Enables individual packaging for each function. If true you must provide package for each function. Defaults to false
functions:
@@ -127,6 +131,15 @@ functions:
subnetIds:
- subnetId1
- subnetId2
+ package:
+ include: # Specify the directories and files which should be included in the deployment package for this specific function.
+ - src/**
+ - handler.js
+ exclude: # Specify the directories and files which should be excluded in the deployment package for this specific function.
+ - .git/**
+ - .travis.yml
+ artifact: path/to/my-artifact.zip # Own package that should be use for this specific function. You must provide this file.
+ individually: true # Enables individual packaging for specific function. If true you must provide package for each function. Defaults to false
events: # The Events that trigger this Function
- http: # This creates an API Gateway HTTP endpoint which can be used to trigger this function. Learn more in "events/apigateway"
path: users/create # Path for this endpoint
diff --git a/docs/providers/aws/guide/services.md b/docs/providers/aws/guide/services.md
index 24d33dab1..57e2975e9 100644
--- a/docs/providers/aws/guide/services.md
+++ b/docs/providers/aws/guide/services.md
@@ -116,6 +116,7 @@ provider:
Action:
- Update:Replace
- Update:Delete
+ Resource: "*"
Condition:
StringEquals:
ResourceType:
diff --git a/docs/providers/azure/guide/intro.md b/docs/providers/azure/guide/intro.md
index f212424a9..f33a846c7 100644
--- a/docs/providers/azure/guide/intro.md
+++ b/docs/providers/azure/guide/intro.md
@@ -78,7 +78,7 @@ functions: # Your "Functions"
x-azure-settings:
name: req
methods:
- - POST
+ - post
route: /users/create
usersDelete:
events:
@@ -86,7 +86,7 @@ functions: # Your "Functions"
x-azure-settings:
name: req
methods:
- - DELETE
+ - delete
route: /users/delete
```
diff --git a/docs/providers/kubeless/events/pubsub.md b/docs/providers/kubeless/events/pubsub.md
index d3ef2065b..cd2f707ae 100644
--- a/docs/providers/kubeless/events/pubsub.md
+++ b/docs/providers/kubeless/events/pubsub.md
@@ -49,4 +49,4 @@ serverless logs -f hello
hello world!
```
-You can install the Kubeless CLI tool following the [../guide/installation](installation guide).
+You can install the Kubeless CLI tool following the [installation guide](../guide/installation.md).
diff --git a/lib/plugins/aws/deploy/lib/uploadArtifacts.js b/lib/plugins/aws/deploy/lib/uploadArtifacts.js
index 951d11ace..8d6c94556 100644
--- a/lib/plugins/aws/deploy/lib/uploadArtifacts.js
+++ b/lib/plugins/aws/deploy/lib/uploadArtifacts.js
@@ -2,6 +2,7 @@
/* eslint-disable no-use-before-define */
+const _ = require('lodash');
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
@@ -9,6 +10,8 @@ const BbPromise = require('bluebird');
const filesize = require('filesize');
const normalizeFiles = require('../../lib/normalizeFiles');
+const NUM_CONCURRENT_UPLOADS = 3;
+
module.exports = {
uploadArtifacts() {
return BbPromise.bind(this)
@@ -76,36 +79,36 @@ module.exports = {
},
uploadFunctions() {
- let shouldUploadService = false;
this.serverless.cli.log('Uploading artifacts...');
+
const functionNames = this.serverless.service.getAllFunctions();
- return BbPromise.map(functionNames, (name) => {
- const functionArtifactFileName = this.provider.naming.getFunctionArtifactName(name);
- const functionObject = this.serverless.service.getFunction(name);
- functionObject.package = functionObject.package || {};
- let artifactFilePath = functionObject.package.artifact ||
- this.serverless.service.package.artifact;
- if (!artifactFilePath ||
- (this.serverless.service.artifact && !functionObject.package.artifact)) {
- if (this.serverless.service.package.individually || functionObject.package.individually) {
- const artifactFileName = functionArtifactFileName;
- artifactFilePath = path.join(this.packagePath, artifactFileName);
- return this.uploadZipFile(artifactFilePath);
+ const artifactFilePaths = _.uniq(
+ _.map(functionNames, (name) => {
+ const functionArtifactFileName = this.provider.naming.getFunctionArtifactName(name);
+ const functionObject = this.serverless.service.getFunction(name);
+ functionObject.package = functionObject.package || {};
+ const artifactFilePath = functionObject.package.artifact ||
+ this.serverless.service.package.artifact;
+
+ if (!artifactFilePath ||
+ (this.serverless.service.artifact && !functionObject.package.artifact)) {
+ if (this.serverless.service.package.individually || functionObject.package.individually) {
+ const artifactFileName = functionArtifactFileName;
+ return path.join(this.packagePath, artifactFileName);
+ }
+ return this.provider.naming.getServiceArtifactName();
}
- shouldUploadService = true;
- return BbPromise.resolve();
- }
+
+ return artifactFilePath;
+ })
+ );
+
+ return BbPromise.map(artifactFilePaths, (artifactFilePath) => {
+ const stats = fs.statSync(artifactFilePath);
+ this.serverless.cli.log(`Uploading service .zip file to S3 (${filesize(stats.size)})...`);
return this.uploadZipFile(artifactFilePath);
- }, { concurrency: 3 }).then(() => {
- if (shouldUploadService) {
- const artifactFileName = this.provider.naming.getServiceArtifactName();
- const artifactFilePath = path.join(this.packagePath, artifactFileName);
- const stats = fs.statSync(artifactFilePath);
- this.serverless.cli.log(`Uploading service .zip file to S3 (${filesize(stats.size)})...`);
- return this.uploadZipFile(artifactFilePath);
- }
- return BbPromise.resolve();
- });
+ }, { concurrency: NUM_CONCURRENT_UPLOADS }
+ );
},
};
diff --git a/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js b/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js
index 7c907b345..3168988df 100644
--- a/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js
+++ b/lib/plugins/aws/deploy/lib/uploadArtifacts.test.js
@@ -225,19 +225,45 @@ describe('uploadArtifacts', () => {
});
describe('#uploadFunctions()', () => {
+ let uploadZipFileStub;
+
+ beforeEach(() => {
+ sinon.stub(fs, 'statSync').returns({ size: 1024 });
+ uploadZipFileStub = sinon.stub(awsDeploy, 'uploadZipFile').resolves();
+ });
+
+ afterEach(() => {
+ fs.statSync.restore();
+ uploadZipFileStub.restore();
+ });
+
it('should upload the service artifact file to the S3 bucket', () => {
awsDeploy.serverless.config.servicePath = 'some/path';
awsDeploy.serverless.service.service = 'new-service';
- sinon.stub(fs, 'statSync').returns({ size: 0 });
+ return awsDeploy.uploadFunctions().then(() => {
+ expect(uploadZipFileStub.calledOnce).to.be.equal(true);
+ expect(uploadZipFileStub.args[0][0]).to.be.equal('new-service.zip');
+ });
+ });
- const uploadZipFileStub = sinon
- .stub(awsDeploy, 'uploadZipFile').resolves();
+ it('should upload a single .zip file to the S3 bucket when not packaging individually', () => {
+ awsDeploy.serverless.service.functions = {
+ first: {
+ package: {
+ artifact: 'artifact.zip',
+ },
+ },
+ second: {
+ package: {
+ artifact: 'artifact.zip',
+ },
+ },
+ };
return awsDeploy.uploadFunctions().then(() => {
expect(uploadZipFileStub.calledOnce).to.be.equal(true);
- fs.statSync.restore();
- awsDeploy.uploadZipFile.restore();
+ expect(uploadZipFileStub.args[0][0]).to.be.equal('artifact.zip');
});
});
@@ -256,16 +282,12 @@ describe('uploadArtifacts', () => {
},
};
- const uploadZipFileStub = sinon
- .stub(awsDeploy, 'uploadZipFile').resolves();
-
return awsDeploy.uploadFunctions().then(() => {
expect(uploadZipFileStub.calledTwice).to.be.equal(true);
expect(uploadZipFileStub.args[0][0])
.to.be.equal(awsDeploy.serverless.service.functions.first.package.artifact);
expect(uploadZipFileStub.args[1][0])
.to.be.equal(awsDeploy.serverless.service.functions.second.package.artifact);
- uploadZipFileStub.restore();
});
});
@@ -284,19 +306,12 @@ describe('uploadArtifacts', () => {
},
};
- const uploadZipFileStub = sinon
- .stub(awsDeploy, 'uploadZipFile').resolves();
- const statSyncStub = sinon.stub(fs, 'statSync').returns({ size: 1024 });
-
return awsDeploy.uploadFunctions().then(() => {
expect(uploadZipFileStub.calledTwice).to.be.equal(true);
expect(uploadZipFileStub.args[0][0])
.to.be.equal(awsDeploy.serverless.service.functions.first.package.artifact);
expect(uploadZipFileStub.args[1][0])
.to.be.equal(awsDeploy.serverless.service.package.artifact);
- }).finally(() => {
- uploadZipFileStub.restore();
- statSyncStub.restore();
});
});
@@ -304,16 +319,11 @@ describe('uploadArtifacts', () => {
awsDeploy.serverless.config.servicePath = 'some/path';
awsDeploy.serverless.service.service = 'new-service';
- const statSyncStub = sinon.stub(fs, 'statSync').returns({ size: 1024 });
- const uploadZipFileStub = sinon.stub(awsDeploy, 'uploadZipFile').resolves();
sinon.spy(awsDeploy.serverless.cli, 'log');
return awsDeploy.uploadFunctions().then(() => {
const expected = 'Uploading service .zip file to S3 (1 KB)...';
expect(awsDeploy.serverless.cli.log.calledWithExactly(expected)).to.be.equal(true);
- }).finally(() => {
- statSyncStub.restore();
- uploadZipFileStub.restore();
});
});
});
diff --git a/lib/plugins/aws/package/lib/mergeIamTemplates.js b/lib/plugins/aws/package/lib/mergeIamTemplates.js
index a17e2dffd..12508e3d1 100644
--- a/lib/plugins/aws/package/lib/mergeIamTemplates.js
+++ b/lib/plugins/aws/package/lib/mergeIamTemplates.js
@@ -7,6 +7,7 @@ const path = require('path');
module.exports = {
mergeIamTemplates() {
this.validateStatements(this.serverless.service.provider.iamRoleStatements);
+ this.validateManagedPolicies(this.serverless.service.provider.iamManagedPolicies);
return this.merge();
},
@@ -120,6 +121,20 @@ module.exports = {
.Statement.concat(this.serverless.service.provider.iamRoleStatements);
}
+ if (this.serverless.service.provider.iamManagedPolicies) {
+ // add iam managed policies
+ const iamManagedPolicies = this.serverless.service.provider.iamManagedPolicies;
+ const resource = this.serverless.service.provider.compiledCloudFormationTemplate
+ .Resources[this.provider.naming.getRoleLogicalId()].Properties;
+ if (iamManagedPolicies.length > 0) {
+ if (!_.has(resource, 'ManagedPolicyArns') || _.isEmpty(resource.ManagedPolicyArns)) {
+ resource.ManagedPolicyArns = [];
+ }
+ resource.ManagedPolicyArns = resource.ManagedPolicyArns
+ .concat(iamManagedPolicies);
+ }
+ }
+
// check if one of the functions contains vpc configuration
const vpcConfigProvided = [];
this.serverless.service.getAllFunctions().forEach((functionName) => {
@@ -173,4 +188,14 @@ module.exports = {
throw new this.serverless.classes.Error(errorMessage);
}
},
+
+ validateManagedPolicies(iamManagedPolicies) {
+ // Verify that iamManagedPolicies (if present) is an array
+ if (!iamManagedPolicies) {
+ return;
+ }
+ if (!_.isArray(iamManagedPolicies)) {
+ throw new this.serverless.classes.Error('iamManagedPolicies should be an array of arns');
+ }
+ },
};
diff --git a/lib/plugins/aws/package/lib/mergeIamTemplates.test.js b/lib/plugins/aws/package/lib/mergeIamTemplates.test.js
index 140798925..373f0aa32 100644
--- a/lib/plugins/aws/package/lib/mergeIamTemplates.test.js
+++ b/lib/plugins/aws/package/lib/mergeIamTemplates.test.js
@@ -155,6 +155,22 @@ describe('#mergeIamTemplates()', () => {
});
});
+ it('should add managed policy arns', () => {
+ awsPackage.serverless.service.provider.iamManagedPolicies = [
+ 'some:aws:arn:xxx:*:*',
+ 'someOther:aws:arn:xxx:*:*',
+ { 'Fn::Join': [':', ['arn:aws:iam:', { Ref: 'AWSAccountId' }, 'some/path']] },
+ ];
+ return awsPackage.mergeIamTemplates()
+ .then(() => {
+ expect(awsPackage.serverless.service.provider.compiledCloudFormationTemplate
+ .Resources[awsPackage.provider.naming.getRoleLogicalId()]
+ .Properties
+ .ManagedPolicyArns
+ ).to.deep.equal(awsPackage.serverless.service.provider.iamManagedPolicies);
+ });
+ });
+
it('should throw error if custom IAM policy statements is not an array', () => {
awsPackage.serverless.service.provider.iamRoleStatements = {
policy: 'some_value',
@@ -222,6 +238,12 @@ describe('#mergeIamTemplates()', () => {
.to.throw(/statement 0 is missing.*Resource; statement 2 is missing.*Effect, Action/);
});
+ it('should throw error if managed policies is not an array', () => {
+ awsPackage.serverless.service.provider.iamManagedPolicies = 'a string';
+ expect(() => awsPackage.mergeIamTemplates())
+ .to.throw('iamManagedPolicies should be an array of arns');
+ });
+
it('should add a CloudWatch LogGroup resource', () => {
const normalizedName = awsPackage.provider.naming.getLogGroupLogicalId(functionName);
return awsPackage.mergeIamTemplates().then(() => {
diff --git a/lib/plugins/create/create.js b/lib/plugins/create/create.js
index 84368002a..a5d60a8ba 100644
--- a/lib/plugins/create/create.js
+++ b/lib/plugins/create/create.js
@@ -34,6 +34,7 @@ const validTemplates = [
'google-nodejs',
'kubeless-python',
'kubeless-nodejs',
+ 'openwhisk-java-maven',
'openwhisk-nodejs',
'openwhisk-php',
'openwhisk-python',
diff --git a/lib/plugins/create/create.test.js b/lib/plugins/create/create.test.js
index 4ea33d8b8..731b566ba 100644
--- a/lib/plugins/create/create.test.js
+++ b/lib/plugins/create/create.test.js
@@ -143,7 +143,6 @@ describe('Create', () => {
expect(dirContent).to.include('aws-csharp.csproj');
expect(dirContent).to.include('build.cmd');
expect(dirContent).to.include('build.sh');
- expect(dirContent).to.include('global.json');
});
});
@@ -264,9 +263,9 @@ describe('Create', () => {
expect(dirContent).to.include('gradlew.bat');
expect(dirContent).to.include('package.json');
expect(dirContent).to.include(path.join('gradle', 'wrapper',
- 'gradle-wrapper.jar'));
+ 'gradle-wrapper.jar'));
expect(dirContent).to.include(path.join('gradle', 'wrapper',
- 'gradle-wrapper.properties'));
+ 'gradle-wrapper.properties'));
expect(dirContent).to.include(path.join('src', 'main', 'kotlin', 'com', 'serverless',
'Handler.kt'));
expect(dirContent).to.include(path.join('src', 'main', 'kotlin', 'com', 'serverless',
@@ -291,17 +290,17 @@ describe('Create', () => {
expect(dirContent).to.include('gradlew');
expect(dirContent).to.include('gradlew.bat');
expect(dirContent).to.include(path.join('gradle', 'wrapper',
- 'gradle-wrapper.jar'));
+ 'gradle-wrapper.jar'));
expect(dirContent).to.include(path.join('gradle', 'wrapper',
- 'gradle-wrapper.properties'));
+ 'gradle-wrapper.properties'));
expect(dirContent).to.include(path.join('src', 'main', 'resources',
- 'log4j.properties'));
+ 'log4j.properties'));
expect(dirContent).to.include(path.join('src', 'main', 'java',
- 'com', 'serverless', 'Handler.java'));
+ 'com', 'serverless', 'Handler.java'));
expect(dirContent).to.include(path.join('src', 'main', 'java',
- 'com', 'serverless', 'ApiGatewayResponse.java'));
+ 'com', 'serverless', 'ApiGatewayResponse.java'));
expect(dirContent).to.include(path.join('src', 'main', 'java',
- 'com', 'serverless', 'Response.java'));
+ 'com', 'serverless', 'Response.java'));
expect(dirContent).to.include(path.join('.gitignore'));
});
});
@@ -319,17 +318,17 @@ describe('Create', () => {
expect(dirContent).to.include('gradlew');
expect(dirContent).to.include('gradlew.bat');
expect(dirContent).to.include(path.join('gradle', 'wrapper',
- 'gradle-wrapper.jar'));
+ 'gradle-wrapper.jar'));
expect(dirContent).to.include(path.join('gradle', 'wrapper',
- 'gradle-wrapper.properties'));
+ 'gradle-wrapper.properties'));
expect(dirContent).to.include(path.join('src', 'main', 'resources',
- 'log4j.properties'));
+ 'log4j.properties'));
expect(dirContent).to.include(path.join('src', 'main', 'groovy',
- 'com', 'serverless', 'Handler.groovy'));
+ 'com', 'serverless', 'Handler.groovy'));
expect(dirContent).to.include(path.join('src', 'main', 'groovy',
- 'com', 'serverless', 'ApiGatewayResponse.groovy'));
+ 'com', 'serverless', 'ApiGatewayResponse.groovy'));
expect(dirContent).to.include(path.join('src', 'main', 'groovy',
- 'com', 'serverless', 'Response.groovy'));
+ 'com', 'serverless', 'Response.groovy'));
expect(dirContent).to.include('.gitignore');
});
});
@@ -354,6 +353,23 @@ describe('Create', () => {
});
});
+ it('should generate scaffolding for "openwhisk-java-maven" template', () => {
+ process.chdir(tmpDir);
+ create.options.template = 'openwhisk-java-maven';
+
+ return create.create().then(() => {
+ const dirContent = walkDirSync(tmpDir)
+ .map(elem => elem.replace(path.join(tmpDir, path.sep), ''));
+ expect(dirContent).to.include('pom.xml');
+ expect(dirContent).to.include(path.join('src', 'main', 'java',
+ 'com', 'example', 'FunctionApp.java'));
+ expect(dirContent).to.include(path.join('src', 'test', 'java',
+ 'com', 'example', 'FunctionAppTest.java'));
+ expect(dirContent).to.include('.gitignore');
+ expect(dirContent).to.include('serverless.yml');
+ });
+ });
+
it('should generate scaffolding for "openwhisk-nodejs" template', () => {
process.chdir(tmpDir);
create.options.template = 'openwhisk-nodejs';
@@ -509,7 +525,7 @@ describe('Create', () => {
expect(dirContent).to.include('serverless.yml');
expect(dirContent).to.include('pom.xml');
expect(dirContent).to.include(path.join('src', 'main', 'java',
- 'com', 'serverless', 'Handler.java'));
+ 'com', 'serverless', 'Handler.java'));
expect(dirContent).to.include('.gitignore');
});
});
@@ -608,7 +624,7 @@ describe('Create', () => {
});
it('should create a custom renamed service in the directory if using ' +
- 'the "path" and "name" option', () => {
+ 'the "path" and "name" option', () => {
process.chdir(tmpDir);
create.options.path = 'my-new-service';
@@ -640,7 +656,7 @@ describe('Create', () => {
create.options.template = 'aws-nodejs';
create.options.path = '';
create.serverless.utils.copyDirContentsSync(path.join(create.serverless.config.serverlessPath,
- 'plugins', 'create', 'templates', create.options.template), tmpDir);
+ 'plugins', 'create', 'templates', create.options.template), tmpDir);
const dirContent = fs.readdirSync(tmpDir);
@@ -749,7 +765,7 @@ describe('Create', () => {
return create.create().then(() => {
const dirContent = walkDirSync(tmpDir)
- .map(elem => elem.replace(path.join(tmpDir, path.sep), ''));
+ .map(elem => elem.replace(path.join(tmpDir, path.sep), ''));
expect(dirContent).to.include('serverless.yml');
expect(dirContent).to.include(path.join('hello', 'main.go'));
diff --git a/lib/plugins/create/templates/aws-csharp/.vs/aws-csharp/v15/.suo b/lib/plugins/create/templates/aws-csharp/.vs/aws-csharp/v15/.suo
deleted file mode 100644
index 9c228fb45..000000000
Binary files a/lib/plugins/create/templates/aws-csharp/.vs/aws-csharp/v15/.suo and /dev/null differ
diff --git a/lib/plugins/create/templates/aws-csharp/aws-csharp.csproj b/lib/plugins/create/templates/aws-csharp/aws-csharp.csproj
index efa84bbf5..9cedd48b6 100644
--- a/lib/plugins/create/templates/aws-csharp/aws-csharp.csproj
+++ b/lib/plugins/create/templates/aws-csharp/aws-csharp.csproj
@@ -1,7 +1,8 @@
- netcoreapp1.0
+ netcoreapp2.0
+ true
CsharpHandlers
aws-csharp
@@ -12,7 +13,7 @@
-
+
diff --git a/lib/plugins/create/templates/aws-csharp/build.cmd b/lib/plugins/create/templates/aws-csharp/build.cmd
index f33ae0254..9bd8efb21 100644
--- a/lib/plugins/create/templates/aws-csharp/build.cmd
+++ b/lib/plugins/create/templates/aws-csharp/build.cmd
@@ -1,2 +1,2 @@
dotnet restore
-dotnet lambda package --configuration release --framework netcoreapp1.0 --output-package bin/release/netcoreapp1.0/deploy-package.zip
\ No newline at end of file
+dotnet lambda package --configuration release --framework netcoreapp2.0 --output-package bin/release/netcoreapp2.0/deploy-package.zip
diff --git a/lib/plugins/create/templates/aws-csharp/build.sh b/lib/plugins/create/templates/aws-csharp/build.sh
index 892b0f289..f05233ca5 100755
--- a/lib/plugins/create/templates/aws-csharp/build.sh
+++ b/lib/plugins/create/templates/aws-csharp/build.sh
@@ -1,10 +1,11 @@
#!/bin/bash
-#install zip
-apt-get -qq update
-apt-get -qq -y install zip
+#install zip on debian OS, since microsoft/dotnet container doesn't have zip by default
+if [ -f /etc/debian_version ]
+then
+ apt -qq update
+ apt -qq -y install zip
+fi
dotnet restore
-
-#create deployment package
-dotnet lambda package --configuration release --framework netcoreapp1.0 --output-package bin/release/netcoreapp1.0/deploy-package.zip
+dotnet lambda package --configuration release --framework netcoreapp2.0 --output-package bin/release/netcoreapp2.0/deploy-package.zip
diff --git a/lib/plugins/create/templates/aws-csharp/global.json b/lib/plugins/create/templates/aws-csharp/global.json
deleted file mode 100644
index 8af244a46..000000000
--- a/lib/plugins/create/templates/aws-csharp/global.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "sdk": {
- "version": "1.0.4"
- }
-}
diff --git a/lib/plugins/create/templates/aws-csharp/serverless.yml b/lib/plugins/create/templates/aws-csharp/serverless.yml
index 12e2196fe..8f2c5f370 100644
--- a/lib/plugins/create/templates/aws-csharp/serverless.yml
+++ b/lib/plugins/create/templates/aws-csharp/serverless.yml
@@ -19,7 +19,7 @@ service: aws-csharp # NOTE: update this with your service name
provider:
name: aws
- runtime: dotnetcore1.0
+ runtime: dotnetcore2.0
# you can overwrite defaults here
# stage: dev
@@ -47,7 +47,7 @@ provider:
# you can add packaging information here
package:
- artifact: bin/release/netcoreapp1.0/deploy-package.zip
+ artifact: bin/release/netcoreapp2.0/deploy-package.zip
# exclude:
# - exclude-me.js
# - exclude-me-dir/**
diff --git a/lib/plugins/create/templates/aws-fsharp/build.sh b/lib/plugins/create/templates/aws-fsharp/build.sh
index 3f92f87ce..f05233ca5 100644
--- a/lib/plugins/create/templates/aws-fsharp/build.sh
+++ b/lib/plugins/create/templates/aws-fsharp/build.sh
@@ -1,15 +1,11 @@
#!/bin/bash
-isMacOs=`uname -a | grep Darwin`
-
-#install zip
-if [ -z "$isMacOs" ]
+#install zip on debian OS, since microsoft/dotnet container doesn't have zip by default
+if [ -f /etc/debian_version ]
then
- apt-get -qq update
- apt-get -qq -y install zip
+ apt -qq update
+ apt -qq -y install zip
fi
dotnet restore
-
-#create deployment package
dotnet lambda package --configuration release --framework netcoreapp2.0 --output-package bin/release/netcoreapp2.0/deploy-package.zip
diff --git a/lib/plugins/create/templates/openwhisk-java-maven/.gitignore b/lib/plugins/create/templates/openwhisk-java-maven/.gitignore
new file mode 100644
index 000000000..186e57ceb
--- /dev/null
+++ b/lib/plugins/create/templates/openwhisk-java-maven/.gitignore
@@ -0,0 +1,8 @@
+target
+.vscode
+.sts4-cache
+.project
+.classpath
+.settings
+*.iml
+.idea
diff --git a/lib/plugins/create/templates/openwhisk-java-maven/pom.xml b/lib/plugins/create/templates/openwhisk-java-maven/pom.xml
new file mode 100644
index 000000000..95c0ac0ff
--- /dev/null
+++ b/lib/plugins/create/templates/openwhisk-java-maven/pom.xml
@@ -0,0 +1,47 @@
+
+ 4.0.0
+ com.example
+ demo-function
+ 1.0-SNAPSHOT
+ https://openwhisk.apache.org/
+
+ UTF-8
+ 1.8
+ 1.8
+ 2.8.2
+
+
+
+ com.google.code.gson
+ gson
+ ${gson.version}
+
+
+ junit
+ junit
+ 4.12
+ test
+
+
+
+ demo-function
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.1.0
+
+
+ package
+
+ shade
+
+
+
+
+
+
+
diff --git a/lib/plugins/create/templates/openwhisk-java-maven/serverless.yml b/lib/plugins/create/templates/openwhisk-java-maven/serverless.yml
new file mode 100644
index 000000000..d3541cdf8
--- /dev/null
+++ b/lib/plugins/create/templates/openwhisk-java-maven/serverless.yml
@@ -0,0 +1,49 @@
+# Welcome to Serverless!
+#
+# This file is the main config file for your service.
+# It's very minimal at this point and uses default values.
+# You can always add more config options for more control.
+# We've included some commented out config examples here.
+# Just uncomment any of them to get that config option.
+#
+# For full config options, check the docs:
+# docs.serverless.com
+#
+# Happy Coding!
+
+service: openwhisk-java-maven # NOTE: update this with your service name
+
+# Please ensure the serverless-openwhisk provider plugin is installed globally.
+# $ npm install -g serverless-openwhisk
+# ...before installing project dependencies to register this provider.
+# $ npm install
+provider:
+ name: openwhisk
+ runtime: java
+
+# you can add packaging information here
+package:
+ artifact: target/demo-function.jar
+
+functions:
+ demo:
+ handler: com.example.FunctionApp
+
+# extend the framework using plugins listed here:
+# https://github.com/serverless/plugins
+plugins:
+ - "serverless-openwhisk"
+
+# you can define custom triggers and trigger feeds using the resources section.
+#
+#resources:
+# triggers:
+# my_trigger:
+# parameters:
+# hello: world
+# alarm_trigger:
+# parameters:
+# hello: world
+# feed: /whisk.system/alarms/alarm
+# feed_parameters:
+# cron: '*/8 * * * * *'
diff --git a/lib/plugins/create/templates/openwhisk-java-maven/src/main/java/com/example/FunctionApp.java b/lib/plugins/create/templates/openwhisk-java-maven/src/main/java/com/example/FunctionApp.java
new file mode 100644
index 000000000..f216f79c9
--- /dev/null
+++ b/lib/plugins/create/templates/openwhisk-java-maven/src/main/java/com/example/FunctionApp.java
@@ -0,0 +1,31 @@
+package com.example;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import com.google.gson.JsonObject;
+
+/**
+ * Hello FunctionApp
+ */
+public class FunctionApp {
+ public static JsonObject main(JsonObject args) {
+ JsonObject response = new JsonObject();
+ response.addProperty("greetings", "Hello! Welcome to OpenWhisk");
+ return response;
+ }
+}
diff --git a/lib/plugins/create/templates/openwhisk-java-maven/src/test/java/com/example/FunctionAppTest.java b/lib/plugins/create/templates/openwhisk-java-maven/src/test/java/com/example/FunctionAppTest.java
new file mode 100644
index 000000000..20229769e
--- /dev/null
+++ b/lib/plugins/create/templates/openwhisk-java-maven/src/test/java/com/example/FunctionAppTest.java
@@ -0,0 +1,39 @@
+package com.example;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import static org.junit.Assert.*;
+
+import com.google.gson.JsonObject;
+
+import org.junit.Test;
+
+/**
+ * Unit test for simple function.
+ */
+public class FunctionAppTest {
+ @Test
+ public void testFunction() {
+ JsonObject args = new JsonObject();
+ JsonObject response = FunctionApp.main(args);
+ assertNotNull(response);
+ String greetings = response.getAsJsonPrimitive("greetings").getAsString();
+ assertNotNull(greetings);
+ assertEquals("Hello! Welcome to OpenWhisk", greetings);
+ }
+}
diff --git a/tests/templates/test_all_templates b/tests/templates/test_all_templates
index 88980ab50..d207639bc 100755
--- a/tests/templates/test_all_templates
+++ b/tests/templates/test_all_templates
@@ -8,7 +8,7 @@ function integration-test {
$DIR/integration-test-template $@
}
-integration-test aws-csharp 'apt-get -qq update && apt-get -qq -y install zip && dotnet restore && dotnet lambda package --configuration release --framework netcoreapp1.0 --output-package bin/release/netcoreapp1.0/deploy-package.zip'
+integration-test aws-csharp './build.sh'
integration-test aws-fsharp './build.sh'
integration-test aws-go 'cd /go/src/app && make build'
integration-test aws-go-dep 'cd /go/src/app && make build'