forked from pulumi/examples
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a serverless-raw example app (using C#)
Adds a simple serverless example using raw AWS primitives - API Gateway, Lambda, DynamoDB, etc. The app that is deployed is a simple C# app which tracks how many times a route has been accessed in Dynamo. This highlights another option for packaging and deploying code via Pulumi. Fixes pulumi#19.
- Loading branch information
Showing
10 changed files
with
336 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
.pulumi | ||
Pulumi.*.yaml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
/bin/ | ||
/node_modules/ | ||
/yarn.lock | ||
/package-lock.json | ||
|
||
/app/**/obj | ||
/app/**/bin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
name: serverless | ||
runtime: nodejs | ||
description: Basic example of a serverless AWS application. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
# serverless-raw | ||
|
||
An example using some serverless AWS resources, including: | ||
|
||
* AWS Lambda Function | ||
* AWS IAM Role | ||
* AWS DynamoDB Table | ||
* AWS APIGateway RestAPI | ||
|
||
The Lambda function is a C# application using dotnet2.0 (a similar approach works for any other language supported by | ||
AWS Lambda). To deploy the complete application: | ||
|
||
```bash | ||
# Create and configure a new stack | ||
$ pulumi stack init testing | ||
$ pulumi config set aws:region us-east-2 | ||
|
||
# Install dependencies | ||
$ yarn install | ||
|
||
# Build the C# app | ||
$ cd ./app | ||
$ dotnet publish | ||
$ cd .. | ||
|
||
# Build the Pulumi program | ||
$ npm run build | ||
|
||
# Preview the deployment | ||
$ pulumi preview | ||
Previewing changes: | ||
[snip...] | ||
info: 9 changes previewed: | ||
+ 9 resources to create | ||
|
||
# Deploy the update | ||
$ pulumi update | ||
Performing changes: | ||
[snip...] | ||
info: 9 changes performed: | ||
+ 9 resources created | ||
Update duration: 25.017340162s | ||
|
||
# Test it out | ||
$ curl $(pulumi stack output endpoint)/hello | ||
{"Path":"/hello","Count":0} | ||
|
||
# See the logs | ||
$ pulumi logs -f | ||
2018-03-21T18:24:52.670-07:00[ mylambda-d719650] START RequestId: d1e95652-2d6f-11e8-93f6-2921c8ae65e7 Version: $LATEST | ||
2018-03-21T18:24:56.171-07:00[ mylambda-d719650] Getting count for '/hello' | ||
2018-03-21T18:25:01.327-07:00[ mylambda-d719650] Got count 0 for '/hello' | ||
2018-03-21T18:25:02.267-07:00[ mylambda-d719650] END RequestId: d1e95652-2d6f-11e8-93f6-2921c8ae65e7 | ||
2018-03-21T18:25:02.267-07:00[ mylambda-d719650] REPORT RequestId: d1e95652-2d6f-11e8-93f6-2921c8ae65e7 Duration: 9540.93 ms Billed Duration: 9600 ms Memory Size: 128 MB Max Memory Used: 37 MB | ||
|
||
# Remove the app | ||
$ pulumi destroy | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Net; | ||
using System.Threading.Tasks; | ||
|
||
using Amazon.Lambda.Core; | ||
using Amazon.Lambda.APIGatewayEvents; | ||
|
||
using Amazon; | ||
using Amazon.DynamoDBv2; | ||
using Amazon.DynamoDBv2.DataModel; | ||
|
||
using Newtonsoft.Json; | ||
|
||
// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class. | ||
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))] | ||
|
||
namespace app | ||
{ | ||
public class Counter | ||
{ | ||
public string Id { get; set; } | ||
public Int32 Count { get; set; } | ||
} | ||
|
||
public class Functions | ||
{ | ||
// This const is the name of the environment variable that the serverless.template will use to set | ||
// the name of the DynamoDB table used to store blog posts. | ||
const string TABLENAME_ENVIRONMENT_VARIABLE_LOOKUP = "COUNTER_TABLE"; | ||
|
||
IDynamoDBContext DDBContext { get; set; } | ||
|
||
/// <summary> | ||
/// Default constructor that Lambda will invoke. | ||
/// </summary> | ||
public Functions() | ||
{ | ||
// Check to see if a table name was passed in through environment variables and if so | ||
// add the table mapping. | ||
var tableName = System.Environment.GetEnvironmentVariable(TABLENAME_ENVIRONMENT_VARIABLE_LOOKUP); | ||
if(!string.IsNullOrEmpty(tableName)) | ||
{ | ||
AWSConfigsDynamoDB.Context.TypeMappings[typeof(Counter)] = new Amazon.Util.TypeMapping(typeof(Counter), tableName); | ||
} | ||
|
||
var config = new DynamoDBContextConfig { Conversion = DynamoDBEntryConversion.V2 }; | ||
this.DDBContext = new DynamoDBContext(new AmazonDynamoDBClient(), config); | ||
} | ||
|
||
/// <summary> | ||
/// A Lambda function that returns a simple JSON object. | ||
/// </summary> | ||
/// <param name="request"></param> | ||
/// <returns></returns> | ||
public async Task<APIGatewayProxyResponse> GetAsync(APIGatewayProxyRequest request, ILambdaContext context) | ||
{ | ||
context.Logger.LogLine($"Getting count for '{request.Path}'"); | ||
|
||
var counter = await DDBContext.LoadAsync<Counter>(request.Path); | ||
if (counter == null) { | ||
counter = new Counter { Id = request.Path, Count = 0 }; | ||
} | ||
var count = counter.Count++; | ||
await DDBContext.SaveAsync<Counter>(counter); | ||
|
||
context.Logger.LogLine($"Got count {count} for '{request.Path}'"); | ||
|
||
var response = new APIGatewayProxyResponse | ||
{ | ||
StatusCode = (int)HttpStatusCode.OK, | ||
Body = JsonConvert.SerializeObject(new { Path = request.Path, Count = count }), | ||
Headers = new Dictionary<string, string> { { "Content-Type", "application/json" } } | ||
}; | ||
|
||
return response; | ||
} | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>netcoreapp2.0</TargetFramework> | ||
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="AWSSDK.DynamoDBv2" Version="3.3.5" /> | ||
|
||
<PackageReference Include="Amazon.Lambda.Core" Version="1.0.0" /> | ||
<PackageReference Include="Amazon.Lambda.Serialization.Json" Version="1.1.0" /> | ||
<PackageReference Include="Amazon.Lambda.APIGatewayEvents" Version="1.1.2" /> | ||
|
||
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<DotNetCliToolReference Include="Amazon.Lambda.Tools" Version="2.1.1" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
// Copyright 2016-2018, Pulumi Corporation. All rights reserved. | ||
|
||
import * as aws from "@pulumi/aws"; | ||
import * as pulumi from "@pulumi/pulumi"; | ||
|
||
// The location of the built dotnet2.0 application to deploy | ||
let dotNetApplicationPublishFolder = "./app/bin/Debug/netcoreapp2.0/publish"; | ||
let dotNetApplicationEntryPoint = "app::app.Functions::GetAsync"; | ||
// The stage name to use for the API Gateway URL | ||
let stageName = "api"; | ||
|
||
/////////////////// | ||
// DynamoDB Table | ||
/////////////////// | ||
|
||
// A DynamoDB table with a single primary key | ||
let counterTable = new aws.dynamodb.Table("counterTable", { | ||
attributes: [ | ||
{ name: "Id", type: "S" }, | ||
], | ||
hashKey: "Id", | ||
readCapacity: 1, | ||
writeCapacity: 1, | ||
}); | ||
|
||
/////////////////// | ||
// Lambda Function | ||
/////////////////// | ||
|
||
// Create a Role giving our Lambda access. | ||
let policy: aws.iam.PolicyDocument = { | ||
"Version": "2012-10-17", | ||
"Statement": [ | ||
{ | ||
"Action": "sts:AssumeRole", | ||
"Principal": { | ||
"Service": "lambda.amazonaws.com", | ||
}, | ||
"Effect": "Allow", | ||
"Sid": "", | ||
}, | ||
], | ||
}; | ||
let role = new aws.iam.Role("mylambda-role", { | ||
assumeRolePolicy: JSON.stringify(policy), | ||
}); | ||
let fullAccess = new aws.iam.RolePolicyAttachment("mylambda-access", { | ||
role: role, | ||
policyArn: aws.iam.AWSLambdaFullAccess, | ||
}); | ||
|
||
// Create a Lambda function, using code from the `./app` folder. | ||
let lambda = new aws.lambda.Function("mylambda", { | ||
runtime: aws.lambda.DotnetCore2d0Runtime, | ||
code: new pulumi.asset.AssetArchive({ | ||
".": new pulumi.asset.FileArchive(dotNetApplicationPublishFolder), | ||
}), | ||
timeout: 300, | ||
handler: dotNetApplicationEntryPoint, | ||
role: role.arn, | ||
environment: { | ||
variables: { | ||
"COUNTER_TABLE": counterTable.name | ||
} | ||
}, | ||
}, { dependsOn: [fullAccess] }); | ||
|
||
/////////////////// | ||
// APIGateway RestAPI | ||
/////////////////// | ||
|
||
// Create the Swagger spec for a proxy which forwards all HTTP requests through to the Lambda function. | ||
function swaggerSpec(lambdaArn: string): string { | ||
let swaggerSpec = { | ||
swagger: "2.0", | ||
info: { title: "api", version: "1.0" }, | ||
paths: { | ||
"/{proxy+}": swaggerRouteHandler(lambdaArn), | ||
}, | ||
}; | ||
return JSON.stringify(swaggerSpec); | ||
} | ||
|
||
// Create a single Swagger spec route handler for a Lambda function. | ||
function swaggerRouteHandler(lambdaArn: string) { | ||
let region = aws.config.requireRegion(); | ||
return { | ||
"x-amazon-apigateway-any-method": { | ||
"x-amazon-apigateway-integration": { | ||
uri: `arn:aws:apigateway:${region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations`, | ||
passthroughBehavior: "when_no_match", | ||
httpMethod: "POST", | ||
type: "aws_proxy", | ||
}, | ||
} | ||
}; | ||
} | ||
|
||
// Create the API Gateway Rest API, using a swagger spec. | ||
let restApi = new aws.apigateway.RestApi("api", { | ||
body: lambda.arn.apply(lambdaArn => swaggerSpec(lambdaArn)), | ||
}); | ||
|
||
// Create a deployment of the Rest API. | ||
let deployment = new aws.apigateway.Deployment("api-deployment", { | ||
restApi: restApi, | ||
// Note: Set to empty to avoid creating an implicit stage, we'll create it explicitly below instead. | ||
stageName: "", | ||
}); | ||
|
||
// Create a stage, which is an addressable instance of the Rest API. Set it to point at the latest deployment. | ||
let stage = new aws.apigateway.Stage("api-stage", { | ||
restApi: restApi, | ||
deployment: deployment, | ||
stageName: stageName | ||
}); | ||
|
||
// Give permissions from API Gateway to invoke the Lambda | ||
let invokePermission = new aws.lambda.Permission("api-lambda-permission", { | ||
action: "lambda:invokeFunction", | ||
function: lambda, | ||
principal: "apigateway.amazonaws.com", | ||
sourceArn: deployment.executionArn.apply(arn => arn + "*/*"), | ||
}); | ||
|
||
// Export the https endpoint of the running Rest API | ||
export let endpoint = deployment.invokeUrl.apply(url => url + stageName); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
"name": "serverless-raw", | ||
"version": "0.1.0", | ||
"main": "bin/index.js", | ||
"typings": "bin/index.d.ts", | ||
"scripts": { | ||
"build": "tsc" | ||
}, | ||
"devDependencies": { | ||
"typescript": "^2.5.3" | ||
}, | ||
"dependencies": { | ||
"@pulumi/aws": "^0.11.0", | ||
"@pulumi/pulumi": "^0.11.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"compilerOptions": { | ||
"outDir": "bin", | ||
"target": "es6", | ||
"module": "commonjs", | ||
"moduleResolution": "node", | ||
"declaration": true, | ||
"sourceMap": true, | ||
"stripInternal": true, | ||
"experimentalDecorators": true, | ||
"pretty": true, | ||
"noFallthroughCasesInSwitch": true, | ||
"noImplicitAny": true, | ||
"noImplicitReturns": true, | ||
"forceConsistentCasingInFileNames": true, | ||
"strictNullChecks": true | ||
}, | ||
"files": [ | ||
"index.ts" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,3 @@ | ||
/node_modules/ | ||
/yarn.lock | ||
/package-lock.json | ||
/Pulumi.*.yaml |