Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
79 changes: 79 additions & 0 deletions API.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 36 additions & 0 deletions src/__tests__/monitoring.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,17 +108,24 @@ test('monitoring exposes a list of high severity alarms', () => {
const alarm2 = topic
.metricSMSMonthToDateSpentUSD()
.createAlarm(stack, 'Alarm2', { threshold: 100, evaluationPeriods: 1 });
const alarm3 = topic
.metricSMSMonthToDateSpentUSD()
.createAlarm(stack, 'Alarm3', { threshold: 500, evaluationPeriods: 1 });

// WHEN
monitoring.addHighSeverityAlarm('My Alarm', alarm1);
monitoring.addLowSeverityAlarm('My Other Alarm', alarm2);
monitoring.addMediumSeverityAlarm('My Third Alarm', alarm3);

expect(monitoring.highSeverityAlarms.map((alarm) => alarm.alarmArn)).toEqual([
alarm1.alarmArn,
]);
expect(monitoring.lowSeverityAlarms.map((alarm) => alarm.alarmArn)).toEqual([
alarm2.alarmArn,
]);
expect(
monitoring.mediumSeverityAlarms.map((alarm) => alarm.alarmArn)
).toEqual([alarm3.alarmArn]);
});

test('web canaries can ping URLs and raise high severity alarms', () => {
Expand Down Expand Up @@ -230,3 +237,32 @@ test('high-severity alarm actions are registered', () => {
AlarmActions: [highSeverity, highSeverityActionArn],
});
});

test('medium-severity alarm actions are registered', () => {
// GIVEN
const stack = new Stack(undefined, 'TestStack');
const alarm = new Alarm(stack, 'Alarm', {
evaluationPeriods: 1,
metric: new Metric({
metricName: 'FakeMetricName',
namespace: 'FakeNamespace',
}),
threshold: 0,
});

const mediumSeverity = 'fake::arn::of::an::action';
const mediumSeverityActionArn = 'fake::arn::of::bound:alarm::action';
const mediumSeverityAction: IAlarmAction = {
bind: () => ({ alarmActionArn: mediumSeverityActionArn }),
};

// WHEN
new Monitoring(stack, 'Monitoring', {
alarmActions: { mediumSeverity, mediumSeverityAction },
}).addMediumSeverityAlarm('Alarm', alarm);

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::CloudWatch::Alarm', {
AlarmActions: [mediumSeverity, mediumSeverityActionArn],
});
});
17 changes: 17 additions & 0 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,23 @@ export interface AlarmActions {
*/
readonly highSeverityAction?: IAlarmAction;

/**
* The ARN of the CloudWatch alarm action to take for alarms of medium-severity
* alarms.
*
* This must be an ARN that can be used with CloudWatch alarms.
* @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/AlarmThatSendsEmail.html#alarms-and-actions
*/
readonly mediumSeverity?: string;

/**
* The CloudWatch alarm action to take for alarms of medium-severity alarms.
*
* This must be an ARN that can be used with CloudWatch alarms.
* @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/AlarmThatSendsEmail.html#alarms-and-actions
*/
readonly mediumSeverityAction?: IAlarmAction;

/**
* The ARN of the CloudWatch alarm action to take for alarms of normal
* severity.
Expand Down
25 changes: 18 additions & 7 deletions src/construct-hub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,13 +350,13 @@ export class ConstructHub extends Construct implements iam.IGrantable {
// Create an internal CodeArtifact repository if we run in network-controlled mode, or if a domain is provided.
const codeArtifact =
isolation === Isolation.NO_INTERNET_ACCESS ||
props.codeArtifactDomain != null
props.codeArtifactDomain != null
? new Repository(this, 'CodeArtifact', {
description: 'Proxy to npmjs.com for ConstructHub',
domainName: props.codeArtifactDomain?.name,
domainExists: props.codeArtifactDomain != null,
upstreams: props.codeArtifactDomain?.upstreams,
})
description: 'Proxy to npmjs.com for ConstructHub',
domainName: props.codeArtifactDomain?.name,
domainExists: props.codeArtifactDomain != null,
upstreams: props.codeArtifactDomain?.upstreams,
})
: undefined;
const { vpc, vpcEndpoints, vpcSubnets, vpcSecurityGroups } = this.createVpc(
isolation,
Expand Down Expand Up @@ -557,6 +557,17 @@ export class ConstructHub extends Construct implements iam.IGrantable {
return this.monitoring.highSeverityAlarms;
}

/**
* Returns a list of all low-severity alarms from this ConstructHub instance.
* These do not necessitate immediate attention, as they do not have direct
* customer-visible impact, or handling is not time-sensitive. They indicate
* that something unusual (not necessarily bad) is happening.
*/
public get mediumSeverityAlarms(): cw.IAlarm[] {
// Note: the array is already returned by-copy by Monitoring, so not copying again.
return this.monitoring.mediumSeverityAlarms;
}

/**
* Returns a list of all low-severity alarms from this ConstructHub instance.
* These do not necessitate immediate attention, as they do not have direct
Expand All @@ -572,7 +583,7 @@ export class ConstructHub extends Construct implements iam.IGrantable {
* Returns a list of all alarms configured by this ConstructHub instance.
*/
public get allAlarms(): cw.IAlarm[] {
return [...this.highSeverityAlarms, ...this.lowSeverityAlarms];
return [...this.highSeverityAlarms, ...this.lowSeverityAlarms, ...this.mediumSeverityAlarms];
}

public get grantPrincipal(): iam.IPrincipal {
Expand Down
9 changes: 9 additions & 0 deletions src/monitoring/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ export interface IMonitoring {
*/
addHighSeverityAlarm(title: string, alarm: AlarmBase): void;

/**
* Adds a medium-severity alarm. If this alarm goes off, the action specified in
* `mediumSeverityAlarmAction` is triggered.
*
* @param title a user-friendly title for the alarm (not currently used).
* @param alarm the alarm to be added.
*/
addMediumSeverityAlarm(title: string, alarm: AlarmBase): void;

/**
* Adds a low-severity alarm. If this alarm goes off, the action specified in
* `normalAlarmAction` is triggered.
Expand Down
20 changes: 20 additions & 0 deletions src/monitoring/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface MonitoringProps {
export class Monitoring extends Construct implements IMonitoring {
private alarmActions?: AlarmActions;
private _highSeverityAlarms: cw.AlarmBase[];
private _mediumSeverityAlarms: cw.AlarmBase[];
private _lowSeverityAlarms: cw.AlarmBase[];

/**
Expand Down Expand Up @@ -55,6 +56,7 @@ export class Monitoring extends Construct implements IMonitoring {

this._highSeverityAlarms = [];
this._lowSeverityAlarms = [];
this._mediumSeverityAlarms = [];

this.highSeverityDashboard = new cw.Dashboard(
this,
Expand Down Expand Up @@ -103,10 +105,28 @@ export class Monitoring extends Construct implements IMonitoring {
this._lowSeverityAlarms.push(alarm);
}

public addMediumSeverityAlarm(_title: string, alarm: cw.AlarmBase) {
const actionArn = this.alarmActions?.mediumSeverity;
if (actionArn) {
alarm.addAlarmAction({
bind: () => ({ alarmActionArn: actionArn }),
});
}
const action = this.alarmActions?.mediumSeverityAction;
if (action) {
alarm.addAlarmAction(action);
}
this._mediumSeverityAlarms.push(alarm);
}

public get highSeverityAlarms() {
return [...this._highSeverityAlarms];
}

public get mediumSeverityAlarms() {
return [...this._mediumSeverityAlarms];
}

public get lowSeverityAlarms() {
return [...this._lowSeverityAlarms];
}
Expand Down
1 change: 1 addition & 0 deletions test/integ.transliterator.ecstask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const transliterator = new Transliterator(stack, 'Transliterator', {
bucket,
monitoring: {
addHighSeverityAlarm: () => {},
addMediumSeverityAlarm: () => {},
addLowSeverityAlarm: () => {},
},
});
Expand Down