diff --git a/src/provision.js b/src/provision.js index 4e9ca89..47ab306 100644 --- a/src/provision.js +++ b/src/provision.js @@ -22,7 +22,7 @@ Parameters: - prod Description: The Name Of Your Environment <% extraParameters.forEach(function(param, index) {%> - <%= param.name %> + <%= param.name %>: Type: <%= param.type %> <% }); %>Resources: <% tables.forEach(function(table, index) { %><%= table %>Table: @@ -97,9 +97,9 @@ Parameters: - "dynamodb:PutItem" - "dynamodb:Query" - "dynamodb:Scan" - - "dynamodb:UpdateItem"<% tables.forEach(function(table) { %> - Resource: !Sub arn:aws:dynamodb:$\{AWS::Region}:$\{AWS::AccountId}:table/$\{<%= table %>Table} - Resource: !Sub arn:aws:dynamodb:$\{AWS::Region}:$\{AWS::AccountId}:table/$\{<%= table %>Table}/index/*<% }); %> + - "dynamodb:UpdateItem" + Resource: <% tables.forEach(function(table) { %> + - !Sub arn:aws:dynamodb:$\{AWS::Region}:$\{AWS::AccountId}:table/$\{<%= table %>Table}*<% }); %> - PolicyName: lambda-execute PolicyDocument: Version: "2012-10-17" @@ -115,6 +115,82 @@ Parameters: - "s3:PutObject" Resource: - "arn:aws:s3:::*" + DBAutoScaleRole: + DependsOn:<% tables.forEach(function(table) { %> + - <%= table %>Table<% }); %> + Type: AWS::IAM::Role + Properties: + Path: "/" + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + Service: application-autoscaling.amazonaws.com + Action: sts:AssumeRole + Policies: + - PolicyName: DynamoAutoscale + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - dynamodb:DescribeTable + - dynamodb:UpdateTable + Resource: "*" + - Effect: Allow + Action: + - cloudwatch:PutMetricAlarm + - cloudwatch:DescribeAlarms + - cloudwatch:DeleteAlarms + Resource: "*" + <% tables.forEach(function(table, index) { %><%= table %>DatabaseReadTarget: + Type: AWS::ApplicationAutoScaling::ScalableTarget + Properties: + MaxCapacity: 1500 + MinCapacity: 5 + ResourceId: + Fn::Join: + - / + - - table + - !Ref <%= table %>Table + RoleARN: !GetAtt DBAutoScaleRole.Arn + ScalableDimension: dynamodb:table:ReadCapacityUnits + ServiceNamespace: dynamodb + <%= table %>DatabaseWriteTarget: + Type: AWS::ApplicationAutoScaling::ScalableTarget + Properties: + MaxCapacity: 1500 + MinCapacity: 5 + ResourceId: !GetAtt <%= table %>Table.Arn + RoleARN: !GetAtt DBAutoScaleRole.Arn + ScalableDimension: dynamodb:table:WriteCapacityUnits + ServiceNamespace: dynamodb + <%= table %>DatabaseAutoscaleReadPolicy: + Type: AWS::ApplicationAutoScaling::ScalingPolicy + Properties: + PolicyName: !Sub $\{Environment}-<%= project %>-<%= table %>-AutoscalingReadPolicy + PolicyType: TargetTrackingScaling + ScalingTargetId: !Ref <%= table %>DatabaseReadTarget + TargetTrackingScalingPolicyConfiguration: + PredefinedMetricSpecification: + PredefinedMetricType: DynamoDBReadCapacityUtilization + ScaleOutCooldown: 60 + ScaleInCooldown: 60 + TargetValue: 80 + <%= table %>DatabaseAutoscaleWritePolicy: + Type: AWS::ApplicationAutoScaling::ScalingPolicy + Properties: + PolicyName: !Sub $\{Environment}-<%= project %>-<%= table %>-AutoscalingWritePolicy + PolicyType: TargetTrackingScaling + ScalingTargetId: !Ref <%= table %>DatabaseWriteTarget + TargetTrackingScalingPolicyConfiguration: + PredefinedMetricSpecification: + PredefinedMetricType: DynamoDBWriteCapacityUtilization + ScaleOutCooldown: 60 + ScaleInCooldown: 60 + TargetValue: 80 + <% }); %> Outputs:<% tables.forEach(function(table) { %> <%= table %>TableName: Description: name of the <%= table %> table diff --git a/src/provision.spec.js b/src/provision.spec.js index 2aeb444..d54ec4c 100644 --- a/src/provision.spec.js +++ b/src/provision.spec.js @@ -1,6 +1,7 @@ -const provision = require('./provision') +const provision = require('./provision') const expect = require('chai').expect - +const AWS = require('aws-sdk') +const cloudformation = new AWS.CloudFormation() //-------------------------------------------------- // Tests that the basic API contract is maintained. @@ -11,7 +12,7 @@ const functions = [ functions.forEach(function(func_name) { - describe('provision module api', function() { + describe('provision module api', function() { describe('"'+func_name+'"', function() { it('should export a function', function() { expect(provision[func_name]).to.be.a('function'); @@ -24,12 +25,51 @@ functions.forEach(function(func_name) { //-------------------------------------------------- // Tests the generateCFN generates what we expect. //-------------------------------------------------- -describe('generateCFN module functionality', function() { +describe('generateCFN module functionality', function() { describe('"generateCFN"', function() { - it('return a YML formatted cloudformation template with two tables defined and one IAM role', function() { - var cfn = provision.generateCFN("unit-tests", ["one", "hat"]); + it('return a YML formatted cloudformation template with two tables defined and one IAM role', function(done) { + this.timeout(15000) + const extraParams = [ + {name: "Unit", type: "String"}, + {name: "Product", type: "String"}, + {name: "Subproduct", type: "String"}, + {name: "Version", type: "String"}, + ] + var cfn = provision.generateCFN("UnitTests", ["one", "hat"], extraParams); // console.log(cfn); + cloudformation.validateTemplate({ + TemplateBody: cfn + }, function(err, data) { + expect(err).to.be.null; + // cloudformation.createStack({ + // StackName: "int-test-dynamodb-graphql", + // OnFailure: "DELETE", + // TemplateBody: cfn, + // Parameters: [{ + // ParameterKey: 'Unit', + // ParameterValue: "test", + // UsePreviousValue: false + // },{ + // ParameterKey: 'Product', + // ParameterValue: "test", + // UsePreviousValue: false + // },{ + // ParameterKey: 'Subproduct', + // ParameterValue: "test", + // UsePreviousValue: false + // },{ + // ParameterKey: 'Version', + // ParameterValue: "test", + // UsePreviousValue: false + // }], + // Capabilities: ["CAPABILITY_NAMED_IAM"] + // }, function(err, data) { + // console.log(err, data) + // done() + // }) + + }) + }); }); }); -