Skip to content

Commit

Permalink
feat: add suffixTemplate property to add suffix to alarm name (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
LironEr authored Dec 2, 2023
1 parent 5c83520 commit 7554663
Show file tree
Hide file tree
Showing 13 changed files with 97 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Release
name: release-please

on:
push:
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ custom:
critical: # optional - create more actions
ok: !Ref MyTeamCriticalAlertsTopic # you can also use !Ref and other CloudFormation functions
alarm: !ImportValue 'my-team-alerts'

defaults:
nameTemplate: $[functionName]-$[metricName]-alarm # Optional - naming template for alarms, can be overwritten in definitions
prefixTemplate: $[stackName] # Optional - override the alarm name prefix
suffixTemplate: alarm # Optional - override the alarm name suffix

definitions: # these defaults are merged with your definitions
lambdaErrors:
Expand Down
9 changes: 6 additions & 3 deletions src/__tests__/ServerlessAwsAlarms.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { MockLogging, MockServerless } from '@tests/utils/MockServerless';
import { PluginConfig } from '../types';
import { DEFAULT_NAME_TEMPLATE, DEFAULT_PREFIX_TEMPLATE } from '../consts/naming';
import { DEFAULT_NAME_TEMPLATE, DEFAULT_PREFIX_TEMPLATE, DEFAULT_SUFFIX_TEMPLATE } from '../consts/naming';
import { DEFAULT_ALARM_DEFINITIONS } from '../consts/definitions';
import { ServerlessAwsAlarms } from '../ServerlessAwsAlarms';
import { TEST_ACCOUNT_NUM } from '@tests/consts';
Expand Down Expand Up @@ -38,6 +38,7 @@ describe('ServerlessAwsAlarms', () => {
enabled: true,
prefixTemplate: DEFAULT_PREFIX_TEMPLATE,
nameTemplate: DEFAULT_NAME_TEMPLATE,
suffixTemplate: DEFAULT_SUFFIX_TEMPLATE,
},
definitions: DEFAULT_ALARM_DEFINITIONS,
});
Expand All @@ -56,6 +57,7 @@ describe('ServerlessAwsAlarms', () => {
enabled: true,
prefixTemplate: DEFAULT_PREFIX_TEMPLATE,
nameTemplate: DEFAULT_NAME_TEMPLATE,
suffixTemplate: DEFAULT_SUFFIX_TEMPLATE,
},
definitions: DEFAULT_ALARM_DEFINITIONS,
});
Expand All @@ -65,7 +67,7 @@ describe('ServerlessAwsAlarms', () => {
const serverless = new MockServerless();
serverless.service.custom.awsAlarms = {
defaults: {
prefixTemplate: '',
suffixTemplate: 'warning',
},
} as PluginConfig;

Expand All @@ -76,8 +78,9 @@ describe('ServerlessAwsAlarms', () => {
expect(plugin.config).toEqual({
defaults: {
enabled: true,
prefixTemplate: '',
prefixTemplate: DEFAULT_PREFIX_TEMPLATE,
nameTemplate: DEFAULT_NAME_TEMPLATE,
suffixTemplate: 'warning',
},
definitions: DEFAULT_ALARM_DEFINITIONS,
});
Expand Down
1 change: 1 addition & 0 deletions src/consts/naming.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export const DEFAULT_NAME_TEMPLATE = '$[lambdaName]-$[definitionName]';
export const DEFAULT_PREFIX_TEMPLATE = '';
export const DEFAULT_SUFFIX_TEMPLATE = '';
export const DEFAULT_METRIC_FILTER_NAME_TEMPLATE = '$[lambdaName]$[definitionName]AlarmFilter';
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface AlarmDefinition {
enabled?: boolean;
prefixTemplate?: string;
nameTemplate?: string;
suffixTemplate?: string;
description?: string;
namespace?: string;
metric?: string;
Expand Down
11 changes: 8 additions & 3 deletions src/utils/__tests__/config.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ZodError } from 'zod';
import { validateConfig } from '../config';
import { PluginConfig } from '../../types';
import { DEFAULT_NAME_TEMPLATE, DEFAULT_PREFIX_TEMPLATE } from '../../consts/naming';
import { DEFAULT_NAME_TEMPLATE, DEFAULT_PREFIX_TEMPLATE, DEFAULT_SUFFIX_TEMPLATE } from '../../consts/naming';

describe('config utils', () => {
describe('validateConfig', () => {
Expand All @@ -15,6 +15,7 @@ describe('config utils', () => {
enabled: true,
prefixTemplate: DEFAULT_PREFIX_TEMPLATE,
nameTemplate: DEFAULT_NAME_TEMPLATE,
suffixTemplate: DEFAULT_SUFFIX_TEMPLATE,
},
});
});
Expand All @@ -25,21 +26,25 @@ describe('config utils', () => {
enabled: true,
prefixTemplate: DEFAULT_PREFIX_TEMPLATE,
nameTemplate: DEFAULT_NAME_TEMPLATE,
suffixTemplate: DEFAULT_SUFFIX_TEMPLATE,
},
});
});

test('override some defaults', () => {
const input: PluginConfig = {
defaults: {
prefixTemplate: '',
suffixTemplate: 'critical',
treatMissingData: 'notBreaching',
},
};
expect(validateConfig(input)).toEqual({
defaults: {
enabled: true,
prefixTemplate: '',
prefixTemplate: DEFAULT_PREFIX_TEMPLATE,
nameTemplate: DEFAULT_NAME_TEMPLATE,
suffixTemplate: 'critical',
treatMissingData: 'notBreaching',
},
});
});
Expand Down
57 changes: 56 additions & 1 deletion src/utils/__tests__/naming.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { replacePlaceholders, getNamespaceForMetricFilter, getMetricNameForMetricFilter } from '../naming';
import {
replacePlaceholders,
getAlarmName,
getNamespaceForMetricFilter,
getMetricNameForMetricFilter,
GetAlarmNameOptions,
} from '../naming';

describe('naming utils', () => {
describe('replacePlaceholders', () => {
Expand Down Expand Up @@ -29,6 +35,55 @@ describe('naming utils', () => {
});
});

describe('getAlarmName', () => {
const placeholders: GetAlarmNameOptions['placeholders'] = {
stackName: 'stackName',
lambdaName: 'lambdaName',
lambdaId: 'lambdaId',
lambdaLogicalId: 'lambdaLogicalId',
metricName: 'metricName',
definitionName: 'definitionName',
};

test('only with nameTemplate', () => {
expect(getAlarmName({ nameTemplate: '$[lambdaName]-$[definitionName]-alarm', placeholders })).toEqual(
'lambdaName-definitionName-alarm',
);
});

test('nameTemplate with prefix', () => {
expect(
getAlarmName({
nameTemplate: '$[definitionName]-alarm',
prefixTemplate: '$[lambdaName]',
suffixTemplate: '',
placeholders,
}),
).toEqual('lambdaName-definitionName-alarm');
});

test('nameTemplate with suffix', () => {
expect(
getAlarmName({
nameTemplate: '$[lambdaName]-$[definitionName]',
suffixTemplate: '$[stackName]-warning',
placeholders,
}),
).toEqual('lambdaName-definitionName-stackName-warning');
});

test('nameTemplate with prefix and suffix', () => {
expect(
getAlarmName({
nameTemplate: '$[lambdaName]-$[definitionName]',
prefixTemplate: '$[stackName]',
suffixTemplate: 'warning',
placeholders,
}),
).toEqual('stackName-lambdaName-definitionName-warning');
});
});

describe('getNamespaceForMetricFilter', () => {
test('with namespace', () => {
expect(getNamespaceForMetricFilter('stackName', 'namespace')).toEqual('namespace');
Expand Down
1 change: 1 addition & 0 deletions src/utils/alarms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export function generateCloudFormationResourcesForDefinition(
AlarmName: getAlarmName({
nameTemplate: alarm.nameTemplate,
prefixTemplate: alarm.prefixTemplate,
suffixTemplate: alarm.suffixTemplate,
placeholders: {
definitionName: alarmDefinitionName,
lambdaId,
Expand Down
9 changes: 8 additions & 1 deletion src/utils/config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { z } from 'zod';
import { AlarmMetricFilterDefinition, AlarmActions, AlarmDefinition, PluginConfig } from '../types';
import { DEFAULT_NAME_TEMPLATE, DEFAULT_PREFIX_TEMPLATE, DEFAULT_METRIC_FILTER_NAME_TEMPLATE } from '../consts/naming';
import {
DEFAULT_NAME_TEMPLATE,
DEFAULT_PREFIX_TEMPLATE,
DEFAULT_SUFFIX_TEMPLATE,
DEFAULT_METRIC_FILTER_NAME_TEMPLATE,
} from '../consts/naming';

const alarmMetricFilterDefSchema = z.object({
pattern: z.string(),
Expand All @@ -13,6 +18,7 @@ const alarmDefSchema = z.object({
enabled: z.boolean().default(true),
prefixTemplate: z.optional(z.string()),
nameTemplate: z.optional(z.string()),
suffixTemplate: z.optional(z.string()),
description: z.optional(z.string().max(1024)),
namespace: z.optional(z.string()),
metric: z.optional(z.string()),
Expand Down Expand Up @@ -55,6 +61,7 @@ const alarmActionsSchema = z.object({
const defaultsDefsSchema = alarmDefSchema.extend({
prefixTemplate: z.optional(z.string()).default(DEFAULT_PREFIX_TEMPLATE),
nameTemplate: z.optional(z.string()).default(DEFAULT_NAME_TEMPLATE),
suffixTemplate: z.optional(z.string()).default(DEFAULT_SUFFIX_TEMPLATE),
}) satisfies z.ZodType<PluginConfig['defaults']>;

const configSchema = z.object({
Expand Down
8 changes: 5 additions & 3 deletions src/utils/naming.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,20 @@ import { DEFAULT_NAME_TEMPLATE, DEFAULT_METRIC_FILTER_NAME_TEMPLATE } from '../c

type BasePlaceholders = 'stackName' | 'lambdaName' | 'lambdaId' | 'lambdaLogicalId' | 'metricName' | 'definitionName';

interface GetAlarmNameOptions {
export interface GetAlarmNameOptions {
nameTemplate?: string;
prefixTemplate?: string;
suffixTemplate?: string;
placeholders: Record<BasePlaceholders, string>;
}

export function getAlarmName({ nameTemplate, prefixTemplate, placeholders }: GetAlarmNameOptions) {
export function getAlarmName({ nameTemplate, prefixTemplate, suffixTemplate, placeholders }: GetAlarmNameOptions) {
// nameTemplate shouldn't be undefined here, but anyway we have a default value just in case
const alarmName = replacePlaceholders(nameTemplate || DEFAULT_NAME_TEMPLATE, placeholders);
const prefix = prefixTemplate ? replacePlaceholders(prefixTemplate, placeholders) : undefined;
const suffix = suffixTemplate ? replacePlaceholders(suffixTemplate, placeholders) : undefined;

return prefix ? `${prefix}-${alarmName}` : alarmName;
return [prefix, alarmName, suffix].filter((x) => !!x).join('-');
}

interface GetMetricFilterNameOptions {
Expand Down
4 changes: 2 additions & 2 deletions tests/integration/integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ describe('integration tests', () => {

// verify alarm props

assertAlarm(alarms, `${serviceName}-some-func-lambdaErrors`, {
assertAlarm(alarms, `${serviceName}-some-func-lambdaErrors-warning`, {
Namespace: 'AWS/Lambda',
MetricName: 'Errors',
ComparisonOperator: 'GreaterThanOrEqualToThreshold',
Expand All @@ -72,7 +72,7 @@ describe('integration tests', () => {
],
});

assertAlarm(alarms, `${serviceName}-some-func2-lambdaErrors`, {
assertAlarm(alarms, `${serviceName}-some-func2-lambdaErrors-warning`, {
Namespace: 'AWS/Lambda',
MetricName: 'Errors',
ComparisonOperator: 'GreaterThanOrEqualToThreshold',
Expand Down
5 changes: 5 additions & 0 deletions tests/integration/serverless/basic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ custom:
ok: 'arn:aws:sns:${self:provider.region}:${aws:accountId}:my-team-alerts-ok'
alarm: 'arn:aws:sns:${self:provider.region}:${aws:accountId}:my-team-alerts-alarm'
insufficientData: 'arn:aws:sns:${self:provider.region}:${aws:accountId}:my-team-alerts-insufficientData'

defaults:
suffixTemplate: 'warning'

definitions:
lambdaErrors:
enabled: true
Expand Down Expand Up @@ -55,6 +59,7 @@ functions:
handler: handlers.someFunc
alarms:
criticalLambdaErrors:
suffixTemplate: ''
namespace: 'AWS/Lambda'
metric: 'Errors'
comparisonOperator: 'GreaterThanOrEqualToThreshold'
Expand Down
2 changes: 1 addition & 1 deletion tests/utils/alarms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export function assertAlarm(alarms: MetricAlarm[], alarmName: string, expected:
const alarm = alarms.find((a) => a.AlarmName === alarmName);

if (!alarm) {
throw new Error(`Alarm ${alarmName} not found`);
throw new Error(`Alarm ${alarmName} not found in ${JSON.stringify(alarms.map((a) => a.AlarmName))}`);
}

expect(alarm).toMatchObject(expected);
Expand Down

0 comments on commit 7554663

Please sign in to comment.