Skip to content

Commit

Permalink
Merge 5816eb8 into 4590498
Browse files Browse the repository at this point in the history
  • Loading branch information
direnakkoc committed Oct 4, 2022
2 parents 4590498 + 5816eb8 commit 1124846
Show file tree
Hide file tree
Showing 27 changed files with 24,297 additions and 18 deletions.
57 changes: 56 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ SLIC Watch provides a CloudWatch Dashboard and Alarms for:
7. ECS (Fargate or EC2)
8. SNS
9. EventBridge

10. Application Load Balancer

Currently, SLIC Watch is available as a Serverless Framework plugin. Serverless Framework v2 and v3 are supported.

## Getting Started
Expand Down Expand Up @@ -171,6 +172,24 @@ 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|Rejected Connection Count|HTTP Code Target 5XX Count|
|--|--|--|
|![HTTPCode_ELB_5XX_Count]() |![RejectedConnectionCount]() |![HTTPCode_Target_5XX_Count]()|
|**UnHealthy Host Count**|**Lambda Internal Error**|**Lambda User Error**|
|--|--|--|
|![UnHealthyHostCount]() |![LambdaInternalError]() |![LambdaUserError]()|

## Configuration

Configuration is entirely optional - SLIC Watch provides defaults that work out of the box.
Expand Down Expand Up @@ -307,6 +326,27 @@ custom:
ThrottledRules:
Statistic: Sum
Threshold: 1
ApplicationELB:
#Application Load Balancer
HTTPCode_ELB_5XX_Count:
Statistic: Sum
Threshold: 0
RejectedConnectionCount:
Statistic: Sum
Threshold: 0
ApplicationELBTarget:
HTTPCode_Target_5XX_Count:
Statistic: Sum
Threshold: 0
UnHealthyHostCount:
Statistic: Average
Threshold: 0
LambdaInternalError:
Statistic: Sum
Threshold: 0
LambdaUserError:
Statistic: Sum
Threshold: 0

dashboard:
enabled: true
Expand Down Expand Up @@ -400,6 +440,21 @@ custom:
Statistic: ["Sum"]
Invocations:
Statistic: ["Sum"]
ApplicationELB:
#Application Load Balancer
HTTPCode_ELB_5XX_Count:
Statistic: ["Sum"]
RejectedConnectionCount:
Statistic: ["Sum"]
ApplicationELBTarget:
HTTPCode_Target_5XX_Count:
Statistic: ["Sum"]
UnHealthyHostCount:
Statistic: ["Average"]
LambdaInternalError:
Statistic: ["Sum"]
LambdaUserError:
Statistic: ["Sum"]
```

An example project is provided for reference: [serverless-test-project](./serverless-test-project)
Expand Down
3 changes: 2 additions & 1 deletion lerna.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"packages": [
"serverless-plugin",
"serverless-test-project"
"serverless-test-project",
"serverless-test-project-alb"
],
"version": "1.3.0"
}
193 changes: 193 additions & 0 deletions serverless-plugin/alarms-alb-target-group.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
'use strict'

const { makeResourceName, getStatisticName, findLoadBalancersForTargetGroup } = require('./util')

/**
* @param {object} albTargetAlarmConfig The fully resolved alarm configuration
*/
module.exports = function ALBTargetAlarms (albTargetAlarmConfig, context) {
return {
createALBTargetAlarms
}

/**
* Add all required Application Load Balancer alarms for Target Group to the provided CloudFormation template
* based on the resources found within
*
* @param {CloudFormationTemplate} cfTemplate A CloudFormation template object
*/
function createALBTargetAlarms (cfTemplate) {
const targetGroupResources = cfTemplate.getResourcesByType(
'AWS::ElasticLoadBalancingV2::TargetGroup'
)
for (const [targetGroupResourceName, targetGroupResource] of Object.entries(targetGroupResources)) {
for (const tgLogicalId of Object.keys(targetGroupResources)) {
const loadBalancerLogicalIds = findLoadBalancersForTargetGroup(tgLogicalId, cfTemplate)
for (const loadBalancerLogicalId of loadBalancerLogicalIds) {
// const loadBalancerFullName = resolveLoadBalancerFullNameForSub(loadBalancerLogicalId)
if (albTargetAlarmConfig.HTTPCode_Target_5XX_Count && albTargetAlarmConfig.HTTPCode_Target_5XX_Count.enabled) {
const httpCodeTarget5XXCount = createHTTPCodeTarget5XXCountAlarm(
targetGroupResourceName,
targetGroupResource,
loadBalancerLogicalId,
albTargetAlarmConfig.HTTPCode_Target_5XX_Count
)
cfTemplate.addResource(httpCodeTarget5XXCount.resourceName, httpCodeTarget5XXCount.resource)
}
if (albTargetAlarmConfig.UnHealthyHostCount && albTargetAlarmConfig.UnHealthyHostCount.enabled) {
const unHealthyHostCount = createUnHealthyHostCountAlarm(
targetGroupResourceName,
targetGroupResource,
loadBalancerLogicalId,
albTargetAlarmConfig.UnHealthyHostCount
)
cfTemplate.addResource(unHealthyHostCount.resourceName, unHealthyHostCount.resource)
}
if (albTargetAlarmConfig.LambdaInternalError && albTargetAlarmConfig.LambdaInternalError.enabled) {
const lambdaInternalError = createLambdaInternalErrorAlarm(
targetGroupResourceName,
targetGroupResource,
loadBalancerLogicalId,
albTargetAlarmConfig.LambdaInternalError
)
cfTemplate.addResource(lambdaInternalError.resourceName, lambdaInternalError.resource)
}
if (albTargetAlarmConfig.LambdaUserError && albTargetAlarmConfig.LambdaUserError.enabled) {
const lambdaUserError = createLambdaUserErrorAlarm(
targetGroupResourceName,
targetGroupResource,
loadBalancerLogicalId,
albTargetAlarmConfig.LambdaUserError
)
cfTemplate.addResource(lambdaUserError.resourceName, lambdaUserError.resource)
}
}
}
}
}

function createLoadBalancerTargetAlarm (
alarmName,
alarmDescription,
targetGroupResourceName, // Logical ID of the CloudFormation Target Group Resource
loadBalancerLogicalId,
comparisonOperator,
threshold,
metricName,
statistic,
period,
extendedStatistic,
evaluationPeriods,
treatMissingData
) {
const targetGroupFullName = { 'Fn::GetAtt': [targetGroupResourceName, 'TargetGroupFullName'] }
const loadBalancerFullName = { 'Fn::GetAtt': [loadBalancerLogicalId, 'LoadBalancerFullName'] }
const metricProperties = {
Dimensions: [{ Name: 'TargetGroup', Value: targetGroupFullName }, { Name: 'LoadBalancer', Value: loadBalancerFullName }],
MetricName: metricName,
Namespace: 'AWS/ApplicationELB',
Period: period,
Statistic: statistic,
ExtendedStatistic: extendedStatistic
}

return {
Type: 'AWS::CloudWatch::Alarm',
Properties: {
ActionsEnabled: true,
AlarmActions: context.alarmActions,
AlarmName: alarmName,
AlarmDescription: alarmDescription,
EvaluationPeriods: evaluationPeriods,
ComparisonOperator: comparisonOperator,
Threshold: threshold,
TreatMissingData: treatMissingData,
...metricProperties
}
}
}

function createHTTPCodeTarget5XXCountAlarm (targetGroupResourceName, targetGroupResource, loadBalancerLogicalID, config) {
const threshold = config.Threshold
return {
resourceName: makeResourceName('LoadBalancer', targetGroupResourceName, 'HTTPCodeTarget5XXCount'),
resource: createLoadBalancerTargetAlarm(
`LoadBalancerHTTPCodeTarget5XXCountAlarm_${targetGroupResourceName}`,
`LoadBalancer HTTP Code Target 5XX Count ${getStatisticName(config)} for ${targetGroupResourceName} breaches ${threshold}`,
targetGroupResourceName,
loadBalancerLogicalID,
config.ComparisonOperator,
threshold,
'HTTPCode_Target_5XX_Count',
config.Statistic,
config.Period,
config.ExtendedStatistic,
config.EvaluationPeriods,
config.TreatMissingData
)
}
}

function createUnHealthyHostCountAlarm (targetGroupResourceName, targetGroupResource, loadBalancerLogicalID, config) {
const threshold = config.Threshold
return {
resourceName: makeResourceName('LoadBalancer', targetGroupResourceName, 'UnHealthyHostCount'),
resource: createLoadBalancerTargetAlarm(
`LoadBalancerUnHealthyHostCountAlarm_${targetGroupResourceName}`,
`LoadBalancer UnHealthy Host Count ${getStatisticName(config)} for ${targetGroupResourceName} breaches ${threshold}`,
targetGroupResourceName,
loadBalancerLogicalID,
config.ComparisonOperator,
threshold,
'UnHealthyHostCount',
config.Statistic,
config.Period,
config.ExtendedStatistic,
config.EvaluationPeriods,
config.TreatMissingData
)
}
}

function createLambdaInternalErrorAlarm (targetGroupResourceName, targetGroupResource, loadBalancerLogicalID, config) {
const threshold = config.Threshold
return {
resourceName: makeResourceName('LoadBalancer', targetGroupResourceName, 'LambdaInternalError'),
resource: createLoadBalancerTargetAlarm(
`LoadBalancerLambdaInternalErrorAlarm_${targetGroupResourceName}`,
`LoadBalancer Lambda Internal Error ${getStatisticName(config)} for ${targetGroupResourceName} breaches ${threshold}`,
targetGroupResourceName,
loadBalancerLogicalID,
config.ComparisonOperator,
threshold,
'LambdaInternalError',
config.Statistic,
config.Period,
config.ExtendedStatistic,
config.EvaluationPeriods,
config.TreatMissingData
)
}
}

function createLambdaUserErrorAlarm (targetGroupResourceName, targetGroupResource, loadBalancerLogicalID, config) {
const threshold = config.Threshold
return {
resourceName: makeResourceName('LoadBalancer', targetGroupResourceName, 'LambdaUserError'),
resource: createLoadBalancerTargetAlarm(
`LoadBalancerLambdaUserErrorAlarm_${targetGroupResourceName}`,
`LoadBalancer Lambda User Error ${getStatisticName(config)} for ${targetGroupResourceName} breaches ${threshold}`,
targetGroupResourceName,
loadBalancerLogicalID,
config.ComparisonOperator,
threshold,
'LambdaUserError',
config.Statistic,
config.Period,
config.ExtendedStatistic,
config.EvaluationPeriods,
config.TreatMissingData
)
}
}
}
Loading

0 comments on commit 1124846

Please sign in to comment.