Skip to content

Commit

Permalink
Rewrite name handling (#21)
Browse files Browse the repository at this point in the history
* Rewrite name handling

* Move texts to constants

* Use createDeploymentArtifacts hook

* Add build:watch task

* Update index/table dimensions

* Update homepage in package.json

* Update serverless declarations

* Closes #20

* Use external project for serverless declarations

* Update README.md

Add information about breaking changes in CF resource names

* Update README.md

* Update README.md

* AWS resources extend Resource class

* Add readonly properties

* Add name handling to Resource

* Remove unused imports

* Add Options export to Resource

* Add Options interface to main.d.ts
  • Loading branch information
sbstjn authored Aug 10, 2017
1 parent f8b8469 commit a253f6c
Show file tree
Hide file tree
Showing 19 changed files with 446 additions and 438 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ An error occurred while provisioning your stack: XYZ - Unable to create alarms f
Rate exceeded (Service: AmazonCloudWatch; Status Code: 400; Error Code: Throttling; Request ID: XYZ).
```

### Breaking Changes

*There have been multiple breaking changes regarding CloudFormation resource names in the past. If you end up with an error, that your CloudFormation Stack cannot be updated, try to remove the `custom > capacities` configuration from your `serverless.yml` file and deploy the service without any Auto Scaling configuration. After that, just re-add your previous configuration and deploy your service again.*

## DynamoDB

The example serverless configuration above works fine for a DynamoDB table CloudFormation resource like this:
Expand Down Expand Up @@ -117,3 +121,18 @@ Feel free to use the code, it's released using the [MIT license](LICENSE.md).
You are welcome to contribute to this project! 😘

To make sure you have a pleasant experience, please read the [code of conduct](CODE_OF_CONDUCT.md). It outlines core values and beliefs and will make working together a happier experience.

### Local Development

If you plan to change the TypeScript files and link the package to another project, use the `link` and `build` yarn commands:

```
$ > yarn link
$ > yarn build:watch # or "yarn build"
```

Use your local build of the package in another project:

```
$ > yarn link serverless-dynamodb-autoscaling
```
12 changes: 8 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
{
"name": "serverless-dynamodb-autoscaling",
"description": "Serverless Plugin for Amazon DynamoDB Auto Scaling.",
"homepage": "https://sbstjn.com/serverless-dynamodb-auto-scaling-with-cloudformation.html",
"version": "0.0.0",
"main": "dist/plugin.js",
"scripts": {
"test": "jest",
"test:cover": "jest --coverage",
"test:watch": "jest --watch",
"coveralls": "cat ./coverage/lcov.info | coveralls",
"lint": "tslint {src,test}/**/*.ts",
"build": "tsc"
"build": "tsc",
"build:watch": "tsc --watch"
},
"keywords": [
"serverless",
Expand All @@ -30,17 +33,18 @@
"bugs": {
"url": "https://github.com/sbstjn/serverless-dynamodb-autoscaling/issues"
},
"homepage": "https://github.com/sbstjn/serverless-dynamodb-autoscaling",
"devDependencies": {
"@types/jest": "^20.0.5",
"@types/lodash": "^4.14.71",
"@types/md5": "^2.1.32",
"@types/node": "^8.0.19",
"coveralls": "^2.13.1",
"dot-json": "^1.0.3",
"jasmine-data-provider": "^2.2.0",
"jest": "^20.0.4",
"serverless-types": "^0.0.1",
"ts-jest": "^20.0.7",
"tslint": "^5.5.0",
"@types/md5": "^2.1.32",
"@types/lodash": "^4.14.71",
"typescript": "^2.4.2"
},
"dependencies": {
Expand Down
108 changes: 108 additions & 0 deletions src/aws/name.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import * as md5 from 'md5'
import * as util from 'util'

const TEXT = {
DIMENSION: 'dynamodb:%s:%sCapacityUnits',
METRIC: 'DynamoDB%sCapacityUtilization',
POLICYROLE: 'DynamoDBAutoscalePolicy',
POLICYSCALE: 'TableScalingPolicy-%s',
ROLE: 'DynamoDBAutoscaleRole',
TARGET: 'AutoScalingTarget-%s'
}

function clean(input: string): string {
return truncate(input.replace(/[^a-z0-9+]+/gi, ''))
}

function truncate(input: string): string {
return input.length <= 64 ? input : input.substr(0, 32) + md5(input)
}

function ucfirst(data: string): string {
return data.charAt(0).toUpperCase() + data.slice(1)
}

export default class Name {
constructor(private options: Options) { }

public metricRead(): string {
return this.metric(true)
}

public metricWrite(): string {
return this.metric(false)
}

public targetRead(): string {
return this.target(true)
}

public targetWrite(): string {
return this.target(false)
}

public policyScaleRead(): string {
return this.policyScale(true)
}

public policyScaleWrite(): string {
return this.policyScale(false)
}

public policyRole(): string {
return clean(
this.build(TEXT.POLICYROLE)
)
}

public dimension(read: boolean): string {
const type = this.options.index === '' ? 'table' : 'index'

return util.format(TEXT.DIMENSION, type, read ? 'Read' : 'Write')
}

public role(): string {
return clean(this.build(TEXT.ROLE))
}

public target(read: boolean): string {
return clean(
this.build(TEXT.TARGET, read ? 'Read' : 'Write')
)
}

public policyScale(read: boolean): string {
return clean(
this.build(TEXT.POLICYSCALE, read ? 'Read' : 'Write')
)
}

public metric(read: boolean): string {
return clean(
util.format(TEXT.METRIC, read ? 'Read' : 'Write')
)
}

private build(data: string, ...args: string[]): string {
return [
this.prefix(),
args ? util.format(data, ...args) : data,
this.suffix()
].join('')
}

private prefix(): string {
return this.options.service
}

private suffix(): string {
return [
this.options.table,
this.options.index,
this.options.stage,
this.options.region
].map(
ucfirst
).join('')
}
}
77 changes: 0 additions & 77 deletions src/aws/names.ts

This file was deleted.

42 changes: 16 additions & 26 deletions src/aws/policy.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,33 @@
import * as names from './names'
import Resource from './resource'

export default class Policy {
private dependencies: string[] = []
private type: string = 'AWS::ApplicationAutoScaling::ScalingPolicy'
export default class Policy extends Resource {
private readonly type: string = 'AWS::ApplicationAutoScaling::ScalingPolicy'

constructor (
private service: string,
private table: string,
private value: number,
options: Options,
private read: boolean,
private value: number,
private scaleIn: number,
private scaleOut: number,
private index: string,
private stage: string
) { }

public setDependencies(list: string[]): Policy {
this.dependencies = list

return this
}
private scaleOut: number
) { super(options) }

public toJSON(): any {
const nameMetric = names.metric(this.read)
const nameScalePolicy = names.policyScale(this.service, this.table, this.read, this.index, this.stage)
const nameTarget = names.target(this.service, this.table, this.read, this.index, this.stage)
const PredefinedMetricType = this.name.metric(this.read)
const PolicyName = this.name.policyScale(this.read)
const Target = this.name.target(this.read)

const dependencies = [this.table, nameTarget ].concat(this.dependencies)
const DependsOn = [ this.options.table, Target ].concat(this.dependencies)

return {
[nameScalePolicy]: {
DependsOn: dependencies,
[PolicyName]: {
DependsOn,
Properties: {
PolicyName: nameScalePolicy,
PolicyName,
PolicyType: 'TargetTrackingScaling',
ScalingTargetId: { Ref: nameTarget },
ScalingTargetId: { Ref: Target },
TargetTrackingScalingPolicyConfiguration: {
PredefinedMetricSpecification: {
PredefinedMetricType: nameMetric
PredefinedMetricType
},
ScaleInCooldown: this.scaleIn,
ScaleOutCooldown: this.scaleOut,
Expand Down
16 changes: 16 additions & 0 deletions src/aws/resource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import Name from './name'

export default class Resource {
protected dependencies: string[] = []
protected name: Name

constructor(protected options: Options) {
this.name = new Name(options)
}

public setDependencies(list: string[]): Resource {
this.dependencies = list

return this
}
}
Loading

0 comments on commit a253f6c

Please sign in to comment.