Skip to content

Instantly share code, notes, and snippets.

@rarous
Last active August 21, 2019 04:38
Show Gist options
  • Save rarous/50bd9f5eb11321070eadc3e6d12dc187 to your computer and use it in GitHub Desktop.
Save rarous/50bd9f5eb11321070eadc3e6d12dc187 to your computer and use it in GitHub Desktop.
Pulumi definition of Datomic Ions Resources and API Gateway integration
import * as ions from "./ions";
const cloud = new ions.DatomicCloud("test-ions", {
enableBastion: true,
applicationName: "test",
systemName: "datomic-test",
environmentMap: "{:env :test}",
keyName: "test-key"
});
// here you have to create Datomic Ions Lambda function `:test`, push & deploy it.
// after that you can add incrementally:
const api = new ions.Api("test-ions", {
stageName: "v1",
datomicCompute: cloud.compute,
routes: [
{ httpMethod: "GET", path: "/", lambdaName: "test" }
]
});
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as awsx from "@pulumi/awsx";
export class DatomicCloud extends pulumi.ComponentResource {
readonly storage: aws.cloudformation.Stack;
readonly compute: aws.cloudformation.Stack;
constructor(
name: string,
args: DatomicCloudArgs,
opts?: pulumi.ResourceOptions
) {
super("topmonks:ions:DatomicCloud", name, {}, opts);
this.storage = new aws.cloudformation.Stack(`${name}-storage`, {
name: args.systemName,
capabilities: ["CAPABILITY_NAMED_IAM"],
templateUrl:
"https://s3.amazonaws.com/datomic-cloud-1/cft/480-8772/datomic-storage-480-8772.json",
parameters: {
Restart: "false",
Subnet0CidrBlock: "10.213.0.0/20",
Subnet1CidrBlock: "10.213.16.0/20",
Subnet2CidrBlock: "10.213.32.0/20",
VpcCidrBlock: "10.213.0.0/16"
}
});
this.compute = new aws.cloudformation.Stack(
`${name}-compute`,
{
name: `${args.systemName}-compute`,
capabilities: ["CAPABILITY_NAMED_IAM"],
templateUrl:
"https://s3.amazonaws.com/datomic-cloud-1/cft/480-8772/datomic-solo-compute-480-8772.json",
parameters: {
EnableBastion: args.enableBastion ? "Yes" : "No",
KeyName: args.keyName,
SystemName: args.systemName,
ApplicationName: args.applicationName,
EnvironmentMap: args.environmentMap,
PreloadDb: ""
}
},
{ dependsOn: [this.storage] }
);
if (args.enableBastion) {
new aws.ec2.SecurityGroupRule(`${name}-bastion-ssh-access`, {
description: "SSH access to bastion",
securityGroupId: this.compute.outputs.apply(o => o.BastionSecurityGroup),
protocol: "TCP",
cidrBlocks: ["0.0.0.0/0"],
ipv6CidrBlocks: ["::/0"],
fromPort: 22,
toPort: 22,
type: "ingress"
});
}
}
}
export class Api extends pulumi.ComponentResource {
readonly gateway: awsx.apigateway.API;
constructor(name: string, args: ApiArgs, opts?: pulumi.ResourceOptions) {
super("topmonks:ions:Api", name, {}, opts);
this.gateway = new awsx.apigateway.API(name, {
stageName: "v1",
routes: createRoutes(name, args.datomicCompute, args.routes)
});
const anySchemaModel = new aws.apigateway.Model(name, {
name: "AnySchema",
contentType: "application/json",
restApi: this.gateway.restAPI,
schema: JSON.stringify({
type: "object",
title: "Any Schema"
})
});
createLambdaMethodExecutions(
name,
this.gateway,
anySchemaModel,
args.routes
);
}
}
export class LambdaMethodExecution extends pulumi.ComponentResource {
constructor(
name: string,
args: LambdaMethodExecutionArgs,
opts?: pulumi.ResourceOptions
) {
super("topmonks:ions:LambdaMethodExecution", name, {}, opts);
const methodResource = getMethodResource(args.gateway, args.path);
const methodResponse = defineMethodResponse(
this,
name,
args.gateway,
args.httpMethod,
args.responseModel,
methodResource
);
defineIntegrationResponse(
this,
name,
args.gateway,
methodResource,
methodResponse
);
}
}
function createLambdaMethodExecutions(
name: string,
gateway: awsx.apigateway.API,
anySchemaModel: aws.apigateway.Model,
routes: ApiRoute[]
) {
return routes.map(
({ httpMethod, path, responseModel }) =>
new LambdaMethodExecution(`${name}/${httpMethod}/${path}`, {
gateway,
path,
httpMethod,
responseModel: responseModel || anySchemaModel
})
);
}
function createRoutes(
name: string,
datomicCompute: aws.cloudformation.Stack,
routes: ApiRoute[]
): awsx.apigateway.Route[] {
return routes.map(({ httpMethod, path, lambdaName }) => ({
method: httpMethod,
path,
eventHandler: getLambda(
`${name}/${httpMethod}/${path}`,
datomicCompute,
lambdaName
)
}));
}
function getLambda(
name: string,
ionsComputeStack: aws.cloudformation.Stack,
ionsLambdaName: string
): aws.lambda.Function {
return aws.lambda.Function.get(
name,
ionsComputeStack.outputs.apply(
x => `${x.CodeDeployDeploymentGroup}-${ionsLambdaName}`
)
);
}
function getMethodResource(
gateway: awsx.apigateway.API,
path: string
): pulumi.Output<aws.apigateway.GetResourceResult> {
return gateway.restAPI.executionArn.apply(x =>
gateway.deployment.executionArn.apply(_ =>
aws.apigateway.getResource({
path,
restApiId: <string>x.split(":").pop()
})
)
);
}
function defineMethodResponse(
parent: LambdaMethodExecution,
name: string,
gateway: awsx.apigateway.API,
httpMethod: string,
responseModel: aws.apigateway.Model,
methodResource: pulumi.Output<aws.apigateway.GetResourceResult>
): aws.apigateway.MethodResponse {
return new aws.apigateway.MethodResponse(
name,
{
restApi: gateway.restAPI,
resourceId: methodResource.id,
httpMethod: httpMethod,
statusCode: "200",
responseParameters: {
"method.response.header.Access-Control-Allow-Headers": true,
"method.response.header.Access-Control-Allow-Methods": true,
"method.response.header.Access-Control-Allow-Origin": true
},
responseModels: { "application/json": responseModel.name }
},
{ parent }
);
}
function defineIntegrationResponse(
parent: LambdaMethodExecution,
name: string,
gateway: awsx.apigateway.API,
methodResource: pulumi.Output<aws.apigateway.GetResourceResult>,
methodResponse: aws.apigateway.MethodResponse
): aws.apigateway.IntegrationResponse {
return new aws.apigateway.IntegrationResponse(
name,
{
restApi: gateway.restAPI,
resourceId: methodResource.apply(x => x.id),
httpMethod: methodResponse.httpMethod,
statusCode: methodResponse.statusCode,
responseParameters: {
"method.response.header.Access-Control-Allow-Headers":
"'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'",
"method.response.header.Access-Control-Allow-Methods":
"'GET,OPTIONS,POST,PUT'",
"method.response.header.Access-Control-Allow-Origin": "'*'"
}
},
{ parent }
);
}
export interface LambdaMethodExecutionArgs {
gateway: awsx.apigateway.API;
httpMethod: awsx.apigateway.Method;
path: string;
responseModel: aws.apigateway.Model;
}
export interface ApiRoute {
httpMethod: awsx.apigateway.Method;
path: string;
lambdaName: string;
responseModel?: aws.apigateway.Model;
}
export interface ApiArgs {
stageName: string;
datomicCompute: aws.cloudformation.Stack;
routes: ApiRoute[];
}
export interface DatomicCloudArgs {
systemName: string;
applicationName: string;
environmentMap: string;
enableBastion: boolean;
keyName: string;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment