Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for ALB alarms and dashboard #97

Merged
merged 25 commits into from
Oct 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
c73da80
Add support for ALB alarms and dashboard
direnakkoc Aug 26, 2022
91ffd61
Fix syntax issue in dashboard.js
direnakkoc Aug 26, 2022
258491f
Fix syntax issue in dashboard.js
direnakkoc Aug 26, 2022
9d24e07
LoadBalancerFullName and TargetGroupFullName update
direnakkoc Sep 22, 2022
1cdf936
Update LoadBalancerFullName and TargetGroupFullName
direnakkoc Sep 23, 2022
826cda6
Separate ALB alarms module and ALB Target Group alarms module
direnakkoc Sep 26, 2022
31edcc6
Create unit-tests for ALB dashboard and ALB Target Group dashboard
direnakkoc Sep 26, 2022
83522e3
Complete unit-tests
direnakkoc Sep 26, 2022
78265f4
Trigger Lambda when there is error
direnakkoc Sep 27, 2022
4818bed
Tidy up the branch
direnakkoc Sep 27, 2022
9244a9a
Use correct CloudFormationTemplate for load balancer widgets
eoinsha Sep 30, 2022
76b06ad
Use correct CloudFormationTemplate for load balancer target group alarms
direnakkoc Oct 3, 2022
f22d07a
Correct load balancer logicalID name for target group alarms
direnakkoc Oct 4, 2022
af0a7f0
Make sure Dashboards are named globaly unique
direnakkoc Oct 6, 2022
d4cfcde
Move ALB modules into core package
eoinsha Oct 10, 2022
b056971
Update ALB tests to use function configs by logical ID
eoinsha Oct 10, 2022
6e25753
Use JS instead of YAML for default-config
eoinsha Oct 10, 2022
ce316e7
Use esbuild to bundle SLIC Watch macro
eoinsha Oct 10, 2022
fcf738f
Add support for ALB DefaultAction targets
eoinsha Oct 10, 2022
5a432b1
Add a cf-macro test case based on a synthesized CDK template
eoinsha Oct 10, 2022
27e6156
fix: don't create ALB TG Lambda alarms if Lambda is not the target
eoinsha Oct 11, 2022
8b82fd0
chore: add more cf-macro unit test coverage
eoinsha Oct 11, 2022
6a20708
chore: cover all known permutations of ALB template in tests
eoinsha Oct 12, 2022
82273e9
chore: format README intro
eoinsha Oct 12, 2022
c4c407d
chore: tidy up ALB widget screenshots
eoinsha Oct 12, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ jobs:
github-token: ${{ secrets.github_token }}
flag-name: run-node-${{ matrix.node-version }}
parallel: true
path-to-lcov: serverless-plugin/coverage/lcov.info
base-path: serverless-plugin
path-to-lcov: coverage/lcov.info
- name: Package test project with Serverless v2
working-directory: ./serverless-test-project
run: |
Expand All @@ -49,5 +48,4 @@ jobs:
with:
github-token: ${{ secrets.github_token }}
parallel-finished: true
path-to-lcov: serverless-plugin/coverage/lcov.info
base-path: serverless-plugin
path-to-lcov: coverage/lcov.info
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ The following is a checklist of steps that must be followed to add support for a
a. Add support for the new dashboard widgets in [serverless-plugin/dashboard.js](./serverless-plugin/dashboard.js)
b. Update [dashboard.test.js](./serverless-plugin/tests/dashboard.test.js) to add coverage for the new resource
6. Update SLIC Watch configuration support:
a. Add default values for your resource's alarm and dashboard configuration in [default-config.yml](./serverless-plugin/default-config.yaml).
a. Add default values for your resource's alarm and dashboard configuration in [default-config.js](./serverless-plugin/default-config.js).
b. Update the JSON schema for SLIC Watch configuration ([config-schema.js](./serverless-plugin/config-schema.js)). Here, you can define the supported alarm metrics and widgets.
7. Manually (or using automation) integration test the new feature
a. The method will vary based on the resource type, but the idea is to trigger alarms and generate metrics to validate that both alarms and dashboards are working as expected. You can use the resource you added to `serverless-test-project` for this.
Expand Down
28 changes: 23 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@
[![Coverage Status](https://coveralls.io/repos/github/fourTheorem/slic-watch/badge.svg)](https://coveralls.io/github/fourTheorem/slic-watch)
[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)


Automatic, best-practice CloudWatch **Dashboards** and **Alarms** for your SAM, CloudFormation, CDK and Serverless Framework applications.

SLIC Watch supports: _AWS Lambda, API Gateway, DynamoDB, Kinesis Data Streams, SQS Queues, Step Functions, ECS (Fargate or EC2), SNS and EventBridge._ ⚡️ **Serverless Framework**, 🐿 **AWS SAM**, **AWS CDK** and ☁️ **CloudFormation**.
SLIC Watch supports: _AWS Lambda, API Gateway, DynamoDB, Kinesis Data Streams, SQS Queues, Step Functions, ECS (Fargate or EC2), SNS, EventBridge and Application Load Balancer._

* Serverless Framework v2 and v3 are supported in the _SLIC Watch Serverless Plugin_.
* SLIC Watch is available as a _CloudFormation Macro_ published in the Serverless Application Repository (SAR). This allows you to add SLIC Watch to SAM, CDK or CloudFormation templates by simply adding a `Transform` to your template.
Supported tools include:
* ⚡️ **Serverless Framework** v2 and v3 via the [_SLIC Watch Serverless Plugin_](#getting-started-with-serverless-framework)
* 🐿 **AWS SAM**, 📦 **AWS CDK** and ☁️ **CloudFormation** using the [_CloudFormation Macro_](#getting-started-with-aws-sam-cdk-or-cloudformation), published in the Serverless Application Repository (SAR).

## Contents
- [slic-watch](#slic-watch)
Expand All @@ -32,6 +32,7 @@ SLIC Watch supports: _AWS Lambda, API Gateway, DynamoDB, Kinesis Data Streams, S
- [ECS / Fargate](#ecs--fargate)
- [SNS](#sns)
- [EventBridge](#eventbridge)
- [Application Load Balancer](#application-load-balancer)
- [Configuration](#configuration)
- [Top-level configuration](#top-level-configuration)
- [Function-level configuration](#function-level-configuration)
Expand Down Expand Up @@ -285,6 +286,23 @@ EventBridge Rule dashboard widgets show:
|--|--|
|![FailedInvocations](https://raw.githubusercontent.com/fourtheorem/slic-watch/main/docs/eventBridgeFailedInvocations.png)|![Invocations](https://raw.githubusercontent.com/fourtheorem/slic-watch/main/docs/eventBridgeInvocations.png)|

### Application Load Balancer
Application Load Balancer alarms are created for:
1. HTTP Code ELB 5XX Count
2. Rejected Connection Count
3. HTTP Code Target 5XX Count
4. UnHealthy Host Count
5. Lambda Internal Error
6. Lambda User Error

Application Load Balancer dashboard widgets show:

|HTTP Code ELB 5XX Count|HTTP Code Target 5XX Count|Rejected Connection Count|
|--|--|--|
|![HTTPCode_ELB_5XX_Count](https://raw.githubusercontent.com/fourtheorem/slic-watch/main/docs/httpCodeElb5XXCount.png) |![HTTPCode_Target_5XX_Count](https://raw.githubusercontent.com/fourtheorem/slic-watch/main/docs/httpCodeTarget5XXCount.png)| |
|**UnHealthy Host Count**|**Lambda User Error**|**Lambda Internal Error**|
|![UnHealthyHostCount](https://raw.githubusercontent.com/fourtheorem/slic-watch/main/docs/unHealthyHostCount.png) |![LambdaUserError](https://raw.githubusercontent.com/fourtheorem/slic-watch/main/docs/lambdaUserError.png)| |

## Configuration

Configuration is entirely optional - SLIC Watch provides defaults that work out of the box.
Expand Down Expand Up @@ -325,7 +343,7 @@ this.templateOptions.metadata = {
- The `topicArn` may be optionally provided as an SNS Topic destination for all alarms. If you omit the topic, alarms are still created but are not sent to any destination.
- Alarms or dashboards can be disabled at any level in the configuration by adding `enabled: false`. You can even disable all plugin functionality by specifying `enabled: false` at the top-level plugin configuration.

A complete set of supported options along with their defaults are shown in [default-config.yaml](./core/default-config.yaml)
A complete set of supported options along with their defaults are shown in [default-config.js](./core/default-config.js)

Example projects are also provided for reference:
- [serverless-test-project](./serverless-test-project)
Expand Down
8 changes: 0 additions & 8 deletions cdk-test-project/jest.config.js

This file was deleted.

4 changes: 2 additions & 2 deletions cf-macro/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ exports.handler = async function (event) {

if (slicWatchConfig.topicArn) {
alarmActions.push(slicWatchConfig.topicArn)
} else if (process.env.SNSTopic) {
alarmActions.push(process.env.SNSTopic)
} else if (process.env.ALARM_SNS_TOPIC) {
alarmActions.push(process.env.ALARM_SNS_TOPIC)
}

const context = {
Expand Down
8 changes: 8 additions & 0 deletions cf-macro/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,11 @@ Resources:
- x86_64
Policies:
- AWSLambdaBasicExecutionRole
Metadata:
BuildMethod: esbuild
BuildProperties:
Minify: false
Target: es2020
Sourcemap: true
EntryPoints:
- cf-macro/index.js
37 changes: 37 additions & 0 deletions cf-macro/tests/cdk-cf.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/* eslint-disable no-template-curly-in-string */
'use strict'

const { test } = require('tap')
const CloudFormationTemplate = require('../../core/cf-template')

const cdkStack = require('./resources/cdk-ecs-cf.json')
const cfMacroHandler = require('../index')

/**
* Test the synthesized output from the CDK ECS Stack in `cdk-test-project`
*/
test('ECS CDK stack', async (t) => {
const event = {
fragment: cdkStack
}
const handlerResponse = await cfMacroHandler.handler(event)
t.equal(handlerResponse.status, 'success')
const transformedTemplate = CloudFormationTemplate(handlerResponse.fragment)

test('alarms are generated', (t) => {
const alarms = transformedTemplate.getResourcesByType('AWS::CloudWatch::Alarm')
t.equal(Object.keys(alarms).length, 6)
const alarmNames = Object.values(alarms).map(alarm => alarm.Properties.AlarmName).sort()
t.same(alarmNames, [
'LoadBalancerHTTPCodeELB5XXCountAlarm_MyWebServerLB3B5FD3AB',
'LoadBalancerHTTPCodeTarget5XXCountAlarm_MyWebServerLBPublicListenerECSGroup5AB9F1C3',
'LoadBalancerRejectedConnectionCountAlarm_MyWebServerLB3B5FD3AB',
'LoadBalancerUnHealthyHostCountAlarm_MyWebServerLBPublicListenerECSGroup5AB9F1C3',
{ 'Fn::Sub': 'ECS_MemoryAlarm_${MyWebServerService2FE7341D.Name}' },
{ 'Fn::Sub': 'ECS_CPUAlarm_${MyWebServerService2FE7341D.Name}' }
])
t.end()
})

t.end()
})
47 changes: 47 additions & 0 deletions cf-macro/tests/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,34 @@ test('index', t => {
t.equal(result.status, 'success')
})

t.test('macro uses SNS Topic environment variable if specified', async t => {
process.env.ALARM_SNS_TOPIC = 'arn:aws:sns:eu-west-1:123456789123:TestTopic'
try {
const result = await lambda.handler(event, null)
t.equal(result.status, 'success')
} finally {
delete process.env.ALARM_SNS_TOPIC
}
})

t.test('macro uses topicArn if specified', async t => {
const eventWithTopic = {
...event,
fragment: {
...event.fragment,
Metadata: {
...event.fragment.Metadata,
slicWatch: {
...event.fragment.Metadata.slicWatch,
topicArn: 'arn:aws:sns:eu-west-1:123456789123:TestTopic'
}
}
}
}
const result = await lambda.handler(eventWithTopic, null)
t.equal(result.status, 'success')
})

t.test('Macro skips SLIC Watch if top-level enabled==false', async t => {
const testevent = _.cloneDeep(event)
testevent.fragment.Metadata.slicWatch.enabled = false
Expand All @@ -50,6 +78,25 @@ test('index', t => {
t.equal(testState.addAlarmsCfTemplate.getSourceObject(), template)
})

t.test('Macro adds dashboard and alarms if no function configuration is provided', async t => {
const testEvent = {
...event,
fragment: {
...event.fragment,
Resources: {
...event.fragment.Resources,
HelloLambdaFunction: {
...event.fragment.Resources.HelloLambdaFunction,
Metadata: {}
}
}
}
}
await lambda.handler(testEvent, null)
t.equal(testState.addDashboardCfTemplate.getSourceObject().Resources.Properties, template.Resources.Properties)
t.equal(testState.addAlarmsCfTemplate.getSourceObject().Resources.Properties, template.Resources.Properties)
})

t.test('Macro execution fails if an invalid SLIC Watch config is provided', async t => {
const testevent = _.cloneDeep(event)
testevent.fragment.Metadata.slicWatch.topicArrrrn = 'pirateTopic'
Expand Down
Loading