Skip to content

Commit

Permalink
Refactor alarm functions
Browse files Browse the repository at this point in the history
  • Loading branch information
direnakkoc committed Mar 15, 2023
1 parent 1ac0782 commit 4f40fce
Show file tree
Hide file tree
Showing 23 changed files with 5,033 additions and 3,698 deletions.
203 changes: 51 additions & 152 deletions core/alarms/alb-target-group.ts
@@ -1,10 +1,10 @@
'use strict'

import { type ResourceType, getResourcesByType, addResource } from '../cf-template'
import { type Context, createAlarm, type ReturnAlarm, type DefaultAlarmsProperties } from './default-config-alarms'
import { type Context, createAlarm, type DefaultAlarmsProperties } from './default-config-alarms'
import { getStatisticName } from './get-statistic-name'
import { makeResourceName } from './make-name'
import { type AlarmProperties } from 'cloudform-types/types/cloudWatch/alarm'
import type { AlarmProperties } from 'cloudform-types/types/cloudWatch/alarm'
import type Resource from 'cloudform-types/types/resource'
import type Template from 'cloudform-types/types/template'

Expand All @@ -20,10 +20,25 @@ export type AlbTargetAlarm = AlarmProperties & {
TargetGroupResourceName: string
LoadBalancerLogicalId: string
}

type TargetGroupMetrics = 'HTTPCode_Target_5XX_Count' | 'UnHealthyHostCount'

type TargetGroupLambdaMetrics = 'LambdaInternalError' | 'LambdaUserError'

function getResourceByName (resourceName: string, compiledTemplate, additionalResources = {}): Resource {
return compiledTemplate.Resources[resourceName] ?? additionalResources[resourceName]
}

const executionMetrics: TargetGroupMetrics[] = [
'HTTPCode_Target_5XX_Count',
'UnHealthyHostCount'
]

const executionMetricsLambda: TargetGroupLambdaMetrics[] = [
'LambdaInternalError',
'LambdaUserError'
]

/**
* For a given target group defined by its CloudFormation resources Logical ID, find any load balancer
* that relates to the target group by finding associated ListenerRules, their Listener and each Listener's
Expand Down Expand Up @@ -76,168 +91,52 @@ export function findLoadBalancersForTargetGroup (targetGroupLogicalId: string, c
return [...allLoadBalancerLogicalIds]
}

function alarmProperty (targetGroupResourceName: string, metrics: string[], loadBalancerLogicalIds: string[], albTargetAlarmProperties: AlbTargetAlarmProperties, compiledTemplate: Template, context: Context) {
for (const metric of metrics) {
for (const loadBalancerLogicalId of loadBalancerLogicalIds) {
const config = albTargetAlarmProperties[metric]
if (config.enabled !== false) {
const threshold = config.Threshold
const albTargetAlarmProperties: AlbTargetAlarm = {
AlarmName: `LoadBalancer${metric.replaceAll('_', '')}Alarm_${targetGroupResourceName}`,
AlarmDescription: `LoadBalancer ${metric} ${getStatisticName(config)} for ${targetGroupResourceName} breaches ${threshold}`,
TargetGroupResourceName: targetGroupResourceName,
LoadBalancerLogicalId: loadBalancerLogicalId,
MetricName: metric,
Statistic: config.Statistic,
Namespace: 'AWS/ApplicationELB',
Dimensions: [
{ Name: 'TargetGroup', Value: `\${${targetGroupResourceName}.TargetGroupFullName}` },
{ Name: 'LoadBalancer', Value: `\${${loadBalancerLogicalId}.LoadBalancerFullName}` }
],
...config
}
const resourceName = makeResourceName('LoadBalancer', targetGroupResourceName, metric)
const resource = createAlarm(albTargetAlarmProperties, context)
addResource(resourceName, resource, compiledTemplate)
}
}
}
}

/**
* The fully resolved alarm configuration
*/
export default function createALBTargetAlarms (albTargetAlarmProperties: AlbTargetAlarmProperties, context: Context, compiledTemplate: Template, additionalResources: ResourceType = {}) {
export default function createALBTargetAlarms (albTargetAlarmProperties: AlbTargetAlarmProperties, context: Context, compiledTemplate: Template, additionalResources: ResourceType = {}): void {
/**
* Add all required Application Load Balancer alarms for Target Group to the provided CloudFormation template
* based on the resources found within
*
* A CloudFormation template object
*/

const targetGroupResources = getResourcesByType('AWS::ElasticLoadBalancingV2::TargetGroup', compiledTemplate, additionalResources)
for (const [targetGroupResourceName, targetGroupResource] of Object.entries(targetGroupResources)) {
for (const tgLogicalId of Object.keys(targetGroupResources)) {
const loadBalancerLogicalIds = findLoadBalancersForTargetGroup(tgLogicalId, compiledTemplate, additionalResources)
for (const loadBalancerLogicalId of loadBalancerLogicalIds) {
if (albTargetAlarmProperties.HTTPCode_Target_5XX_Count?.enabled === true) {
const httpCodeTarget5XXCount = createHTTPCodeTarget5XXCountAlarm(
targetGroupResourceName,
targetGroupResource,
loadBalancerLogicalId,
albTargetAlarmProperties.HTTPCode_Target_5XX_Count
)
addResource(httpCodeTarget5XXCount.resourceName, httpCodeTarget5XXCount.resource, compiledTemplate)
}
if (albTargetAlarmProperties.UnHealthyHostCount?.enabled === true) {
const unHealthyHostCount = createUnHealthyHostCountAlarm(
targetGroupResourceName,
targetGroupResource,
loadBalancerLogicalId,
albTargetAlarmProperties.UnHealthyHostCount
)
addResource(unHealthyHostCount.resourceName, unHealthyHostCount.resource, compiledTemplate)
}
if (targetGroupResource.Properties?.TargetType === 'lambda') {
if (albTargetAlarmProperties.LambdaInternalError?.enabled === true) {
const lambdaInternalError = createLambdaInternalErrorAlarm(
targetGroupResourceName,
targetGroupResource,
loadBalancerLogicalId,
albTargetAlarmProperties.LambdaInternalError
)
addResource(lambdaInternalError.resourceName, lambdaInternalError.resource, compiledTemplate)
}
if (albTargetAlarmProperties.LambdaUserError?.enabled === true) {
const lambdaUserError = createLambdaUserErrorAlarm(
targetGroupResourceName,
targetGroupResource,
loadBalancerLogicalId,
albTargetAlarmProperties.LambdaUserError
)
addResource(lambdaUserError.resourceName, lambdaUserError.resource, compiledTemplate)
}
}
}
}
}

function createHTTPCodeTarget5XXCountAlarm (targetGroupResourceName: string, targetGroupResource: Resource, loadBalancerLogicalId: string, config: DefaultAlarmsProperties): ReturnAlarm {
const threshold = config.Threshold
const albTargetAlarmProperties: AlbTargetAlarm = {
AlarmName: `LoadBalancerHTTPCodeTarget5XXCountAlarm_${targetGroupResourceName}`,
AlarmDescription: `LoadBalancer HTTP Code Target 5XX Count ${getStatisticName(config)} for ${targetGroupResourceName} breaches ${threshold}`,
TargetGroupResourceName: targetGroupResourceName,
LoadBalancerLogicalId: loadBalancerLogicalId,
ComparisonOperator: config.ComparisonOperator,
Threshold: config.Threshold,
MetricName: 'HTTPCode_Target_5XX_Count',
Statistic: config.Statistic,
Period: config.Period,
ExtendedStatistic: config.ExtendedStatistic,
EvaluationPeriods: config.EvaluationPeriods,
TreatMissingData: config.TreatMissingData,
Namespace: 'AWS/ApplicationELB',
Dimensions: [
{ Name: 'TargetGroup', Value: `\${${targetGroupResourceName}.TargetGroupFullName}` },
{ Name: 'LoadBalancer', Value: `\${${loadBalancerLogicalId}.LoadBalancerFullName}` }
]
}
return {
resourceName: makeResourceName('LoadBalancer', targetGroupResourceName, 'HTTPCodeTarget5XXCount'),
resource: createAlarm(albTargetAlarmProperties, context)
}
}
const loadBalancerLogicalIds = findLoadBalancersForTargetGroup(targetGroupResourceName, compiledTemplate, additionalResources)
alarmProperty(targetGroupResourceName, executionMetrics, loadBalancerLogicalIds, albTargetAlarmProperties, compiledTemplate, context)

function createUnHealthyHostCountAlarm (targetGroupResourceName: string, targetGroupResource: Resource, loadBalancerLogicalId: string, config: DefaultAlarmsProperties): ReturnAlarm {
const threshold = config.Threshold
const albTargetAlarmProperties: AlbTargetAlarm = {
AlarmName: `LoadBalancerUnHealthyHostCountAlarm_${targetGroupResourceName}`,
AlarmDescription: `LoadBalancer UnHealthy Host Count ${getStatisticName(config)} for ${targetGroupResourceName} breaches ${threshold}`,
TargetGroupResourceName: targetGroupResourceName,
LoadBalancerLogicalId: loadBalancerLogicalId,
ComparisonOperator: config.ComparisonOperator,
Threshold: config.Threshold,
MetricName: 'UnHealthyHostCount',
Statistic: config.Statistic,
Period: config.Period,
ExtendedStatistic: config.ExtendedStatistic,
EvaluationPeriods: config.EvaluationPeriods,
TreatMissingData: config.TreatMissingData,
Namespace: 'AWS/ApplicationELB',
Dimensions: [
{ Name: 'TargetGroup', Value: `\${${targetGroupResourceName}.TargetGroupFullName}` },
{ Name: 'LoadBalancer', Value: `\${${loadBalancerLogicalId}.LoadBalancerFullName}` }
]
}
return {
resourceName: makeResourceName('LoadBalancer', targetGroupResourceName, 'UnHealthyHostCount'),
resource: createAlarm(albTargetAlarmProperties, context)
}
}

function createLambdaInternalErrorAlarm (targetGroupResourceName: string, targetGroupResource: Resource, loadBalancerLogicalId: string, config: DefaultAlarmsProperties): ReturnAlarm {
const threshold = config.Threshold
const albTargetAlarmProperties: AlbTargetAlarm = {
AlarmName: `LoadBalancerLambdaInternalErrorAlarm_${targetGroupResourceName}`,
AlarmDescription: `LoadBalancer Lambda Internal Error ${getStatisticName(config)} for ${targetGroupResourceName} breaches ${threshold}`,
TargetGroupResourceName: targetGroupResourceName,
LoadBalancerLogicalId: loadBalancerLogicalId,
ComparisonOperator: config.ComparisonOperator,
Threshold: config.Threshold,
MetricName: 'LambdaInternalError',
Statistic: config.Statistic,
Period: config.Period,
ExtendedStatistic: config.ExtendedStatistic,
EvaluationPeriods: config.EvaluationPeriods,
TreatMissingData: config.TreatMissingData,
Namespace: 'AWS/ApplicationELB',
Dimensions: [
{ Name: 'TargetGroup', Value: `\${${targetGroupResourceName}.TargetGroupFullName}` },
{ Name: 'LoadBalancer', Value: `\${${loadBalancerLogicalId}.LoadBalancerFullName}` }
]
}
return {
resourceName: makeResourceName('LoadBalancer', targetGroupResourceName, 'LambdaInternalError'),
resource: createAlarm(albTargetAlarmProperties, context)
}
}

function createLambdaUserErrorAlarm (targetGroupResourceName: string, targetGroupResource: Resource, loadBalancerLogicalId: string, config: DefaultAlarmsProperties): ReturnAlarm {
const threshold = config.Threshold
const albTargetAlarmProperties: AlbTargetAlarm = {
AlarmName: `LoadBalancerLambdaUserErrorAlarm_${targetGroupResourceName}`,
AlarmDescription: `LoadBalancer Lambda User Error ${getStatisticName(config)} for ${targetGroupResourceName} breaches ${threshold}`,
TargetGroupResourceName: targetGroupResourceName,
LoadBalancerLogicalId: loadBalancerLogicalId,
ComparisonOperator: config.ComparisonOperator,
Threshold: config.Threshold,
MetricName: 'LambdaUserError',
Statistic: config.Statistic,
Period: config.Period,
ExtendedStatistic: config.ExtendedStatistic,
EvaluationPeriods: config.EvaluationPeriods,
TreatMissingData: config.TreatMissingData,
Namespace: 'AWS/ApplicationELB',
Dimensions: [
{ Name: 'TargetGroup', Value: `\${${targetGroupResourceName}.TargetGroupFullName}` },
{ Name: 'LoadBalancer', Value: `\${${loadBalancerLogicalId}.LoadBalancerFullName}` }
]
}
return {
resourceName: makeResourceName('LoadBalancer', targetGroupResourceName, 'LambdaUserError'),
resource: createAlarm(albTargetAlarmProperties, context)
if (targetGroupResource.Properties?.TargetType === 'lambda') {
alarmProperty(targetGroupResourceName, executionMetricsLambda, loadBalancerLogicalIds, albTargetAlarmProperties, compiledTemplate, context)
}
}
}
97 changes: 28 additions & 69 deletions core/alarms/alb.ts
@@ -1,8 +1,7 @@
'use strict'

import { addResource, getResourcesByType, type ResourceType } from '../cf-template'
import type Resource from 'cloudform-types/types/resource'
import { type Context, createAlarm, type ReturnAlarm, type DefaultAlarmsProperties } from './default-config-alarms'
import { type Context, createAlarm, type DefaultAlarmsProperties } from './default-config-alarms'
import { getStatisticName } from './get-statistic-name'
import { makeResourceName } from './make-name'
import { type AlarmProperties } from 'cloudform-types/types/cloudWatch/alarm'
Expand All @@ -14,10 +13,17 @@ export interface AlbAlarmProperties {
RejectedConnectionCount: DefaultAlarmsProperties
}

type AlbMetrics = 'HTTPCode_ELB_5XX_Count' | 'RejectedConnectionCount'

export type AlbAlarm = AlarmProperties & {
LoadBalancerResourceName: string
}

const executionMetrics: AlbMetrics[] = [
'HTTPCode_ELB_5XX_Count',
'RejectedConnectionCount'
]

/**
* albAlarmProperties The fully resolved alarm configuration
*/
Expand All @@ -30,73 +36,26 @@ export default function createALBAlarms (albAlarmProperties: AlbAlarmProperties,
*/
const loadBalancerResources = getResourcesByType('AWS::ElasticLoadBalancingV2::LoadBalancer', compiledTemplate, additionalResources)

for (const [loadBalancerResourceName, loadBalancerResource] of Object.entries(loadBalancerResources)) {
if (albAlarmProperties.HTTPCode_ELB_5XX_Count?.enabled === true) {
const httpCodeELB5XXCount = createHTTPCodeELB5XXCountAlarm(
loadBalancerResourceName,
loadBalancerResource,
albAlarmProperties.HTTPCode_ELB_5XX_Count
)
addResource(httpCodeELB5XXCount.resourceName, httpCodeELB5XXCount.resource, compiledTemplate)
}

if (albAlarmProperties.RejectedConnectionCount?.enabled === true) {
const rejectedConnectionCount = createRejectedConnectionCountAlarm(
loadBalancerResourceName,
loadBalancerResource,
albAlarmProperties.RejectedConnectionCount
)
addResource(rejectedConnectionCount.resourceName, rejectedConnectionCount.resource, compiledTemplate)
}
}

function createHTTPCodeELB5XXCountAlarm (loadBalancerResourceName: string, loadBalancerResource: Resource, config: DefaultAlarmsProperties): ReturnAlarm {
const threshold = config.Threshold
const albAlarmProperties: AlbAlarm = {
AlarmName: `LoadBalancerHTTPCodeELB5XXCountAlarm_${loadBalancerResourceName}`,
AlarmDescription: `LoadBalancer HTTP Code ELB 5XX Count ${getStatisticName(config)} for ${loadBalancerResourceName} breaches ${threshold}`,
LoadBalancerResourceName: loadBalancerResourceName,
ComparisonOperator: config.ComparisonOperator,
Threshold: config.Threshold,
MetricName: 'HTTPCode_ELB_5XX_Count',
Statistic: config.Statistic,
Period: config.Period,
ExtendedStatistic: config.ExtendedStatistic,
EvaluationPeriods: config.EvaluationPeriods,
TreatMissingData: config.TreatMissingData,
Namespace: 'AWS/ApplicationELB',
Dimensions: [
{ Name: 'LoadBalancer', Value: `\${${loadBalancerResourceName}.LoadBalancerFullName}` }
]
}
return {
resourceName: makeResourceName('LoadBalancer', loadBalancerResourceName, 'HTTPCodeELB5XXCount'),
resource: createAlarm(albAlarmProperties, context)
}
}

function createRejectedConnectionCountAlarm (loadBalancerResourceName: string, loadBalancerResource: Resource, config: DefaultAlarmsProperties): ReturnAlarm {
const threshold = config.Threshold
const albAlarmProperties: AlbAlarm = {
AlarmName: `LoadBalancerRejectedConnectionCountAlarm_${loadBalancerResourceName}`,
AlarmDescription: `LoadBalancer Rejected Connection Count ${getStatisticName(config)} for ${loadBalancerResourceName} breaches ${threshold}`,
LoadBalancerResourceName: loadBalancerResourceName,
ComparisonOperator: config.ComparisonOperator,
Threshold: config.Threshold,
MetricName: 'RejectedConnectionCount',
Statistic: config.Statistic,
Period: config.Period,
ExtendedStatistic: config.ExtendedStatistic,
EvaluationPeriods: config.EvaluationPeriods,
TreatMissingData: config.TreatMissingData,
Namespace: 'AWS/ApplicationELB',
Dimensions: [
{ Name: 'LoadBalancer', Value: `\${${loadBalancerResourceName}.LoadBalancerFullName}` }
]
}
return {
resourceName: makeResourceName('LoadBalancer', loadBalancerResourceName, 'RejectedConnectionCount'),
resource: createAlarm(albAlarmProperties, context)
for (const [loadBalancerResourceName] of Object.entries(loadBalancerResources)) {
for (const metric of executionMetrics) {
const config = albAlarmProperties[metric]
if (config.enabled !== false) {
const threshold = config.Threshold
const albAlarmProperties: AlbAlarm = {
AlarmName: `LoadBalancer${metric.replaceAll('_', '')}Alarm_${loadBalancerResourceName}`,
AlarmDescription: `LoadBalancer ${metric} ${getStatisticName(config)} for ${loadBalancerResourceName} breaches ${threshold}`,
LoadBalancerResourceName: loadBalancerResourceName,
MetricName: metric,
Namespace: 'AWS/ApplicationELB',
Dimensions: [
{ Name: 'LoadBalancer', Value: `\${${loadBalancerResourceName}.LoadBalancerFullName}` }
],
...config
}
const resourceName = makeResourceName('LoadBalancer', loadBalancerResourceName, metric)
const resource = createAlarm(albAlarmProperties, context)
addResource(resourceName, resource, compiledTemplate)
}
}
}
}

0 comments on commit 4f40fce

Please sign in to comment.