Skip to content

Commit b514e71

Browse files
feat: Add scope monitoring with aspects (#34)
Ref: https://github.com/cdklabs/cdk-watchful/blob/main/src/aspect.ts The concept of `MyMonitoringOptions` for every monitoring is needed because `Omit` is not supported by JSII. Please refer this issue and the comment on the linked PR for the way to handle that. aws/jsii#1707 Opened an issue to add aspects for other left-out resources: #41
1 parent ce1c50e commit b514e71

32 files changed

+13030
-211
lines changed

.vscode/settings.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"editor.codeActionsOnSave": {
3+
"source.fixAll.eslint": true
4+
},
5+
"eslint.validate": ["javascript", "typescript"]
6+
}

API.md

Lines changed: 4945 additions & 147 deletions
Large diffs are not rendered by default.

lib/facade/MonitoringAspect.ts

Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
import { IConstruct, IAspect } from "monocdk";
2+
import * as apigw from "monocdk/aws-apigateway";
3+
import * as apigwv2 from "monocdk/aws-apigatewayv2";
4+
import * as appsync from "monocdk/aws-appsync";
5+
import * as autoscaling from "monocdk/aws-autoscaling";
6+
import * as acm from "monocdk/aws-certificatemanager";
7+
import * as cloudfront from "monocdk/aws-cloudfront";
8+
import * as codebuild from "monocdk/aws-codebuild";
9+
import * as dynamodb from "monocdk/aws-dynamodb";
10+
import * as elasticsearch from "monocdk/aws-elasticsearch";
11+
import * as glue from "monocdk/aws-glue";
12+
import * as kinesis from "monocdk/aws-kinesis";
13+
import * as kinesisanalytics from "monocdk/aws-kinesisanalytics";
14+
import * as kinesisfirehose from "monocdk/aws-kinesisfirehose";
15+
import * as lambda from "monocdk/aws-lambda";
16+
import * as opensearch from "monocdk/aws-opensearchservice";
17+
import * as rds from "monocdk/aws-rds";
18+
import * as redshift from "monocdk/aws-redshift";
19+
import * as s3 from "monocdk/aws-s3";
20+
import * as secretsmanager from "monocdk/aws-secretsmanager";
21+
import * as sns from "monocdk/aws-sns";
22+
import * as sqs from "monocdk/aws-sqs";
23+
import * as stepfunctions from "monocdk/aws-stepfunctions";
24+
25+
import { ElastiCacheClusterType } from "../monitoring";
26+
import { MonitoringAspectProps, MonitoringAspectType } from "./aspect-types";
27+
import { MonitoringFacade } from "./MonitoringFacade";
28+
29+
/**
30+
* A CDK aspect that adds support for monitoring all resources within scope.
31+
*/
32+
export class MonitoringAspect implements IAspect {
33+
/**
34+
* Whether or not we've added a monitoring to the scope for node independent monitorings.
35+
*/
36+
private addedNodeIndependentMonitoringToScope = false;
37+
38+
constructor(
39+
private readonly monitoringFacade: MonitoringFacade,
40+
private readonly props: MonitoringAspectProps = {}
41+
) {}
42+
43+
public visit(node: IConstruct): void {
44+
this.monitorAcm(node);
45+
this.monitorApiGateway(node);
46+
this.monitorApiGatewayV2(node);
47+
this.monitorAppSync(node);
48+
this.monitorAutoScalingGroup(node);
49+
this.monitorCloudFront(node);
50+
this.monitorCodeBuild(node);
51+
this.monitorDynamoDb(node);
52+
this.monitorGlue(node);
53+
this.monitorKinesisAnalytics(node);
54+
this.monitorKinesisDataStream(node);
55+
this.monitorKinesisFirehose(node);
56+
this.monitorLambda(node);
57+
this.monitorOpenSearch(node);
58+
this.monitorRds(node);
59+
this.monitorRedshift(node);
60+
this.monitorS3(node);
61+
this.monitorSecretsManager(node);
62+
this.monitorSns(node);
63+
this.monitorSqs(node);
64+
this.monitorStepFuntions(node);
65+
66+
if (!this.addedNodeIndependentMonitoringToScope) {
67+
this.addedNodeIndependentMonitoringToScope = true;
68+
69+
this.monitorEc2();
70+
this.monitorBilling();
71+
this.monitorElasticCache();
72+
}
73+
}
74+
75+
private getMonitoringDetails<T>(
76+
aspectOptions?: MonitoringAspectType<T>
77+
): [boolean, T?] {
78+
const isEnabled = aspectOptions?.enabled ?? true;
79+
const props = aspectOptions?.props;
80+
return [isEnabled, props];
81+
}
82+
83+
private monitorAcm(node: IConstruct) {
84+
const [isEnabled, props] = this.getMonitoringDetails(this.props.acm);
85+
if (isEnabled && node instanceof acm.Certificate) {
86+
this.monitoringFacade.monitorCertificate({
87+
certificate: node,
88+
alarmFriendlyName: node.node.path,
89+
...props,
90+
});
91+
}
92+
}
93+
94+
private monitorApiGateway(node: IConstruct) {
95+
const [isEnabled, props] = this.getMonitoringDetails(this.props.apiGateway);
96+
if (isEnabled && node instanceof apigw.RestApi) {
97+
this.monitoringFacade.monitorApiGateway({
98+
api: node,
99+
...props,
100+
});
101+
}
102+
}
103+
104+
private monitorApiGatewayV2(node: IConstruct) {
105+
const [isEnabled, props] = this.getMonitoringDetails(
106+
this.props.apiGatewayV2
107+
);
108+
if (isEnabled && node instanceof apigwv2.HttpApi) {
109+
this.monitoringFacade.monitorApiGatewayV2HttpApi({
110+
api: node,
111+
...props,
112+
});
113+
}
114+
}
115+
116+
private monitorAppSync(node: IConstruct) {
117+
const [isEnabled, props] = this.getMonitoringDetails(this.props.appSync);
118+
if (isEnabled && node instanceof appsync.GraphqlApi) {
119+
this.monitoringFacade.monitorAppSyncApi({
120+
api: node,
121+
alarmFriendlyName: node.node.path,
122+
...props,
123+
});
124+
}
125+
}
126+
127+
private monitorAutoScalingGroup(node: IConstruct) {
128+
const [isEnabled, props] = this.getMonitoringDetails(
129+
this.props.autoScalingGroup
130+
);
131+
if (isEnabled && node instanceof autoscaling.AutoScalingGroup) {
132+
this.monitoringFacade.monitorAutoScalingGroup({
133+
autoScalingGroup: node,
134+
...props,
135+
});
136+
}
137+
}
138+
139+
private monitorBilling() {
140+
const [isEnabled, props] = this.getMonitoringDetails(this.props.billing);
141+
if (isEnabled) {
142+
this.monitoringFacade.monitorBilling({
143+
...props,
144+
alarmFriendlyName: "Billing",
145+
});
146+
}
147+
}
148+
149+
private monitorCloudFront(node: IConstruct) {
150+
const [isEnabled, props] = this.getMonitoringDetails(this.props.cloudFront);
151+
if (isEnabled && node instanceof cloudfront.Distribution) {
152+
this.monitoringFacade.monitorCloudFrontDistribution({
153+
distribution: node,
154+
...props,
155+
});
156+
}
157+
}
158+
159+
private monitorCodeBuild(node: IConstruct) {
160+
const [isEnabled, props] = this.getMonitoringDetails(this.props.codeBuild);
161+
if (isEnabled && node instanceof codebuild.Project) {
162+
this.monitoringFacade.monitorCodeBuildProject({
163+
project: node,
164+
...props,
165+
});
166+
}
167+
}
168+
169+
private monitorDynamoDb(node: IConstruct) {
170+
const [isEnabled, props] = this.getMonitoringDetails(this.props.dynamoDB);
171+
if (isEnabled && node instanceof dynamodb.Table) {
172+
this.monitoringFacade.monitorDynamoTable({
173+
table: node,
174+
...props,
175+
});
176+
}
177+
}
178+
179+
private monitorEc2() {
180+
const [isEnabled, props] = this.getMonitoringDetails(this.props.ec2);
181+
if (isEnabled) {
182+
this.monitoringFacade.monitorEC2Instances({
183+
...props,
184+
});
185+
}
186+
}
187+
188+
private monitorElasticCache() {
189+
const [isEnabled, props] = this.getMonitoringDetails(
190+
this.props.elasticCache
191+
);
192+
if (isEnabled) {
193+
this.monitoringFacade.monitorElastiCacheCluster({
194+
clusterType: ElastiCacheClusterType.MEMCACHED,
195+
...props,
196+
});
197+
this.monitoringFacade.monitorElastiCacheCluster({
198+
clusterType: ElastiCacheClusterType.REDIS,
199+
...props,
200+
});
201+
}
202+
}
203+
204+
private monitorGlue(node: IConstruct) {
205+
const [isEnabled, props] = this.getMonitoringDetails(this.props.glue);
206+
if (isEnabled && node instanceof glue.CfnJob) {
207+
this.monitoringFacade.monitorGlueJob({
208+
jobName: node.name!,
209+
...props,
210+
});
211+
}
212+
}
213+
214+
private monitorKinesisAnalytics(node: IConstruct) {
215+
const [isEnabled, props] = this.getMonitoringDetails(
216+
this.props.kinesisDataAnalytics
217+
);
218+
if (isEnabled && node instanceof kinesisanalytics.CfnApplication) {
219+
this.monitoringFacade.monitorKinesisDataAnalytics({
220+
application: node.applicationName!,
221+
alarmFriendlyName: node.node.path,
222+
...props,
223+
});
224+
}
225+
}
226+
227+
private monitorKinesisDataStream(node: IConstruct) {
228+
const [isEnabled, props] = this.getMonitoringDetails(
229+
this.props.kinesisDataStream
230+
);
231+
if (isEnabled && node instanceof kinesis.CfnStream) {
232+
this.monitoringFacade.monitorKinesisDataStream({
233+
streamName: node.name!,
234+
alarmFriendlyName: node.node.path,
235+
...props,
236+
});
237+
}
238+
}
239+
240+
private monitorKinesisFirehose(node: IConstruct) {
241+
const [isEnabled, props] = this.getMonitoringDetails(
242+
this.props.kinesisFirehose
243+
);
244+
if (isEnabled && node instanceof kinesisfirehose.CfnDeliveryStream) {
245+
this.monitoringFacade.monitorKinesisFirehose({
246+
deliveryStreamName: node.deliveryStreamName!,
247+
alarmFriendlyName: node.node.path,
248+
...props,
249+
});
250+
}
251+
}
252+
253+
private monitorLambda(node: IConstruct) {
254+
const [isEnabled, props] = this.getMonitoringDetails(this.props.lambda);
255+
if (isEnabled && node instanceof lambda.Function) {
256+
this.monitoringFacade.monitorLambdaFunction({
257+
lambdaFunction: node,
258+
...props,
259+
});
260+
}
261+
}
262+
263+
private monitorOpenSearch(node: IConstruct) {
264+
const [isEnabled, props] = this.getMonitoringDetails(this.props.openSearch);
265+
if (
266+
isEnabled &&
267+
(node instanceof elasticsearch.Domain ||
268+
node instanceof elasticsearch.CfnDomain ||
269+
node instanceof opensearch.Domain ||
270+
node instanceof opensearch.CfnDomain)
271+
) {
272+
this.monitoringFacade.monitorOpenSearchCluster({
273+
domain: node,
274+
...props,
275+
});
276+
}
277+
}
278+
279+
private monitorRds(node: IConstruct) {
280+
const [isEnabled, props] = this.getMonitoringDetails(this.props.rds);
281+
if (isEnabled && node instanceof rds.DatabaseCluster) {
282+
this.monitoringFacade.monitorRdsCluster({
283+
clusterIdentifier: node.clusterIdentifier,
284+
alarmFriendlyName: node.node.path,
285+
...props,
286+
});
287+
}
288+
}
289+
290+
private monitorRedshift(node: IConstruct) {
291+
const [isEnabled, props] = this.getMonitoringDetails(this.props.redshift);
292+
if (isEnabled && node instanceof redshift.Cluster) {
293+
this.monitoringFacade.monitorRedshiftCluster({
294+
clusterIdentifier: node.clusterName,
295+
alarmFriendlyName: node.node.path,
296+
...props,
297+
});
298+
}
299+
}
300+
301+
private monitorS3(node: IConstruct) {
302+
const [isEnabled, props] = this.getMonitoringDetails(this.props.s3);
303+
if (isEnabled && node instanceof s3.Bucket) {
304+
this.monitoringFacade.monitorS3Bucket({
305+
bucket: node,
306+
...props,
307+
});
308+
}
309+
}
310+
311+
private monitorSecretsManager(node: IConstruct) {
312+
const [isEnabled, props] = this.getMonitoringDetails(
313+
this.props.secretsManager
314+
);
315+
if (isEnabled && node instanceof secretsmanager.Secret) {
316+
this.monitoringFacade.monitorSecretsManagerSecret({
317+
secret: node,
318+
...props,
319+
});
320+
}
321+
}
322+
323+
private monitorSns(node: IConstruct) {
324+
const [isEnabled, props] = this.getMonitoringDetails(this.props.sns);
325+
if (isEnabled && node instanceof sns.Topic) {
326+
this.monitoringFacade.monitorSnsTopic({
327+
topic: node,
328+
...props,
329+
});
330+
}
331+
}
332+
333+
private monitorSqs(node: IConstruct) {
334+
const [isEnabled, props] = this.getMonitoringDetails(this.props.sqs);
335+
if (isEnabled && node instanceof sqs.Queue) {
336+
this.monitoringFacade.monitorSqsQueue({
337+
queue: node,
338+
...props,
339+
});
340+
}
341+
}
342+
343+
private monitorStepFuntions(node: IConstruct) {
344+
const [isEnabled, props] = this.getMonitoringDetails(
345+
this.props.stepFunctions
346+
);
347+
if (isEnabled && node instanceof stepfunctions.StateMachine) {
348+
this.monitoringFacade.monitorStepFunction({
349+
stateMachine: node,
350+
...props,
351+
});
352+
}
353+
}
354+
}
355+
356+
export * from "./aspect-types";

lib/facade/MonitoringFacade.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Construct, Stack } from "monocdk";
1+
import { Aspects, Construct, Stack } from "monocdk";
22
import { CompositeAlarm, Dashboard, IWidget } from "monocdk/aws-cloudwatch";
33

44
import {
@@ -102,6 +102,7 @@ import {
102102
getQueueProcessingEc2ServiceMonitoring,
103103
getQueueProcessingFargateServiceMonitoring,
104104
} from "../monitoring";
105+
import { MonitoringAspect, MonitoringAspectProps } from "./MonitoringAspect";
105106

106107
export interface MonitoringFacadeProps {
107108
readonly metricFactoryDefaults: MetricFactoryDefaults;
@@ -615,4 +616,15 @@ export class MonitoringFacade extends MonitoringScope {
615616
this.addSegment(segment, props);
616617
return this;
617618
}
619+
620+
/**
621+
* Monitor all the resources in the given scope.
622+
* @param scope
623+
* @param aspectProps
624+
* @experimental
625+
*/
626+
monitorScope(scope: Construct, aspectProps?: MonitoringAspectProps) {
627+
const aspect = new MonitoringAspect(this, aspectProps);
628+
Aspects.of(scope).add(aspect);
629+
}
618630
}

0 commit comments

Comments
 (0)