Skip to content

Commit

Permalink
updates by prettier
Browse files Browse the repository at this point in the history
  • Loading branch information
Chris Park committed Apr 19, 2024
1 parent 6c75d09 commit 13c3a52
Show file tree
Hide file tree
Showing 13 changed files with 1,452 additions and 1,271 deletions.
61 changes: 61 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# These are some examples of commonly ignored file patterns.
# You should customize this list as applicable to your project.
# Learn more about .gitignore:
# https://www.atlassian.com/git/tutorials/saving-changes/gitignore

# Node artifact files
node_modules/
dist/

# Compiled Java class files
*.class

# Compiled Python bytecode
*.py[cod]

# Log files
*.log

# Package files
*.jar

# Maven
target/
dist/

# JetBrains IDE
.idea/

# Unit test reports
TEST*.xml

# Generated by MacOS
.DS_Store

# Generated by Windows
Thumbs.db

# Applications
*.app
*.exe
*.war

# Large media files
*.mp4
*.tiff
*.avi
*.flv
*.mov
*.wmv

!jest.config.js

# CDK asset staging directory
.cdk.staging
cdk.out

*.d.ts
*.js

# Lock files
package-lock.json
5 changes: 5 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"semi": true,
"singleQuote": false,
"arrowParens": "avoid"
}
129 changes: 83 additions & 46 deletions packages/cloudwatch-rds-alert/app/bin/app.ts
Original file line number Diff line number Diff line change
@@ -1,77 +1,114 @@
#!/usr/bin/env node
import 'source-map-support/register';
import { Stack, StackProps, App, Duration, aws_cloudwatch, aws_cloudwatch_actions,
aws_rds, aws_ec2, aws_sns, aws_sns_subscriptions, aws_lambda, aws_lambda_nodejs, aws_ssm } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as path from 'path';
import "source-map-support/register";
import {
Stack,
StackProps,
App,
Duration,
aws_cloudwatch,
aws_cloudwatch_actions,
aws_rds,
aws_ec2,
aws_sns,
aws_sns_subscriptions,
aws_lambda,
aws_lambda_nodejs,
aws_ssm,
} from "aws-cdk-lib";
import { Construct } from "constructs";
import * as path from "path";
// import { Subscription } from 'aws-cdk-lib/aws-sns';

const RDSINSTANCES = process.env.RDSINSTANCES as string;
const SECURITYGROUP = process.env.SECURITYGROUP as string;
const CDK_DEFAULT_ACCOUNT = process.env.CDK_DEFAULT_ACCOUNT
const CDK_DEFAULT_REGION = process.env.CDK_DEFAULT_REGION
const CDK_DEFAULT_ACCOUNT = process.env.CDK_DEFAULT_ACCOUNT;
const CDK_DEFAULT_REGION = process.env.CDK_DEFAULT_REGION;
const WEBHOOK_URL_PARAMETER = process.env.WEBHOOK_URL_PARAMETER as string;
const ALERT_USERNAME = process.env.ALERT_USERNAME as string;
const ALERT_CHANNEL = process.env.ALERT_CHANNEL as string;

if (typeof RDSINSTANCES === 'string'){
var instances = RDSINSTANCES.split(',');
if (typeof RDSINSTANCES === "string") {
var instances = RDSINSTANCES.split(",");
}

export class CloudwatchRDSAlertStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);

const topic = new aws_sns.Topic(this, 'SNS');
const topic = new aws_sns.Topic(this, "SNS");

for ( var i in instances) {
const db = aws_rds.DatabaseInstance.fromDatabaseInstanceAttributes(this, instances[i], {
instanceEndpointAddress: 'garbage value', // Can be an arbitrary value
instanceIdentifier: instances[i], // CloudWatch looks out for this value
port: 3306, // Can be an arbitrary value
securityGroups: [aws_ec2.SecurityGroup.fromLookupById(this, instances[i]+'-sg', `${SECURITYGROUP}`)] // SG ID has to be valid
});
for (var i in instances) {
const db = aws_rds.DatabaseInstance.fromDatabaseInstanceAttributes(
this,
instances[i],
{
instanceEndpointAddress: "garbage value", // Can be an arbitrary value
instanceIdentifier: instances[i], // CloudWatch looks out for this value
port: 3306, // Can be an arbitrary value
securityGroups: [
aws_ec2.SecurityGroup.fromLookupById(
this,
instances[i] + "-sg",
`${SECURITYGROUP}`
),
], // SG ID has to be valid
}
);
// Only "Average over 5 minutes" is available, hence one evaluation only before firing the alarm off:
// https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_rds.DatabaseInstance.html#metricwbrcpuutilizationprops
const alarm_cpu = new aws_cloudwatch.Alarm(this, instances[i]+'-alarm', {
alarmName: instances[i]+' database CPU usage alert', // Notification title if sent to Slack
evaluationPeriods: 1, // The number of periods over which data is compared to the specified threshold. ( 5 minute )
datapointsToAlarm: 1, // The number of datapoints that must be breaching to trigger the alarm.
threshold: 30, // over x %
metric: db.metricCPUUtilization(),
treatMissingData: aws_cloudwatch.TreatMissingData.BREACHING
});
const alarm_cpu = new aws_cloudwatch.Alarm(
this,
instances[i] + "-alarm",
{
alarmName: instances[i] + " database CPU usage alert", // Notification title if sent to Slack
evaluationPeriods: 1, // The number of periods over which data is compared to the specified threshold. ( 5 minute )
datapointsToAlarm: 1, // The number of datapoints that must be breaching to trigger the alarm.
threshold: 30, // over x %
metric: db.metricCPUUtilization(),
treatMissingData: aws_cloudwatch.TreatMissingData.BREACHING,
}
);
alarm_cpu.addAlarmAction(new aws_cloudwatch_actions.SnsAction(topic));
alarm_cpu.addOkAction(new aws_cloudwatch_actions.SnsAction(topic));
};
}

const notifySlack = new aws_lambda_nodejs.NodejsFunction(this, 'notifySlack', {
memorySize: 1024,
timeout: Duration.seconds(5),
runtime: aws_lambda.Runtime.NODEJS_14_X,
handler: 'handler',
entry: path.join(__dirname, `/../handlers/notifySlack.ts`),
environment: {
WEBHOOK_URL_PARAMETER: WEBHOOK_URL_PARAMETER,
ALERT_USERNAME: ALERT_USERNAME,
ALERT_CHANNEL: ALERT_CHANNEL
const notifySlack = new aws_lambda_nodejs.NodejsFunction(
this,
"notifySlack",
{
memorySize: 1024,
timeout: Duration.seconds(5),
runtime: aws_lambda.Runtime.NODEJS_14_X,
handler: "handler",
entry: path.join(__dirname, `/../handlers/notifySlack.ts`),
environment: {
WEBHOOK_URL_PARAMETER: WEBHOOK_URL_PARAMETER,
ALERT_USERNAME: ALERT_USERNAME,
ALERT_CHANNEL: ALERT_CHANNEL,
},
}
});
);

const param = aws_ssm.StringParameter.fromSecureStringParameterAttributes(this, 'webhookurl', {
parameterName: WEBHOOK_URL_PARAMETER,
version: 1
});
const param = aws_ssm.StringParameter.fromSecureStringParameterAttributes(
this,
"webhookurl",
{
parameterName: WEBHOOK_URL_PARAMETER,
version: 1,
}
);
param.grantRead(notifySlack);

topic.addSubscription(new aws_sns_subscriptions.LambdaSubscription(notifySlack));
topic.addSubscription(
new aws_sns_subscriptions.LambdaSubscription(notifySlack)
);
}
}

const app = new App();
new CloudwatchRDSAlertStack(app, 'CloudwatchRDSAlertStack', {
new CloudwatchRDSAlertStack(app, "CloudwatchRDSAlertStack", {
env: {
account: `${CDK_DEFAULT_ACCOUNT}`,
region: `${CDK_DEFAULT_REGION}`
}
});
region: `${CDK_DEFAULT_REGION}`,
},
});
4 changes: 1 addition & 3 deletions packages/cloudwatch-rds-alert/app/cdk.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
{
"app": "npx ts-node --prefer-ts-exts bin/app.ts",
"watch": {
"include": [
"**"
],
"include": ["**"],
"exclude": [
"README.md",
"cdk*.json",
Expand Down
109 changes: 59 additions & 50 deletions packages/cloudwatch-rds-alert/app/handlers/notifySlack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,73 +5,82 @@ const WEBHOOK_URL_PARAMETER = process.env.WEBHOOK_URL_PARAMETER as string;
const ALERT_USERNAME = process.env.ALERT_USERNAME as string;
const ALERT_CHANNEL = process.env.ALERT_CHANNEL as string;

const AWS = require('aws-sdk');
const AWS = require("aws-sdk");
const SSM = new AWS.SSM();
// var responseFromSSM = null;
var parameter = {
"Name" : WEBHOOK_URL_PARAMETER,
"WithDecryption": true
Name: WEBHOOK_URL_PARAMETER,
WithDecryption: true,
};

let WEBHOOK_URL: string;
const init = async () => {
WEBHOOK_URL = (await SSM.getParameter(parameter).promise()).Parameter.Value;
console.log(typeof WEBHOOK_URL);
};
WEBHOOK_URL = (await SSM.getParameter(parameter).promise()).Parameter.Value;
console.log(typeof WEBHOOK_URL);
};
const initPromise = init();

export const getFormattedSlackMessage = (snsMessage: string) => {
try {
const obj = JSON.parse(snsMessage);
const { AlarmName, StateChangeTime, NewStateReason, } = obj;
return [
// CloudWatch Alarms
AlarmName && `*${AlarmName}*`,
StateChangeTime && `StateChangeTime: ${StateChangeTime}`,
NewStateReason && `NewStateReason: ${NewStateReason}`,
'----------------'
].filter(Boolean)
.join('\n')
} catch {
return snsMessage
}
try {
const obj = JSON.parse(snsMessage);
const { AlarmName, StateChangeTime, NewStateReason } = obj;
return [
// CloudWatch Alarms
AlarmName && `*${AlarmName}*`,
StateChangeTime && `StateChangeTime: ${StateChangeTime}`,
NewStateReason && `NewStateReason: ${NewStateReason}`,
"----------------",
]
.filter(Boolean)
.join("\n");
} catch {
return snsMessage;
}
};

interface NotifySlackOutput {
event: string,
ALERT_CHANNEL: string,
status_code: number
event: string;
ALERT_CHANNEL: string;
status_code: number;
}

const getFormattedMessage = (channel: string, event: SNSEvent) => {
const { MessageId, Message } = event.Records[0].Sns;
console.log(`${MessageId} - Sending alert to ${channel}: ${Message}`);
return getFormattedSlackMessage(Message);
}
const { MessageId, Message } = event.Records[0].Sns;
console.log(`${MessageId} - Sending alert to ${channel}: ${Message}`);
return getFormattedSlackMessage(Message);
};

const sendAlertToChannel = (channel: string, message: string): Promise<AxiosResponse> => {
const payload = {
channel: `#${channel}`,
username: ALERT_USERNAME,
text: message,
icon_emoji: ':rotating_light:'
};
return axios.post(WEBHOOK_URL, payload, );
const sendAlertToChannel = (
channel: string,
message: string
): Promise<AxiosResponse> => {
const payload = {
channel: `#${channel}`,
username: ALERT_USERNAME,
text: message,
icon_emoji: ":rotating_light:",
};
return axios.post(WEBHOOK_URL, payload);
};

export const handler = async (event: SNSEvent, _context: Context): Promise<NotifySlackOutput> => {
const functionConfig = await initPromise; // just wait until
const message = getFormattedMessage(ALERT_CHANNEL, event);
console.log('Message formatted from event: ', message);
if(!ALERT_CHANNEL) {
console.log('No alert channel specified - skipping...');
return null as any;
};
const response = await sendAlertToChannel(ALERT_CHANNEL, message);
console.log(`Message sent to channel: ${ALERT_CHANNEL}, status: ${response.status}`);
return {
event: event.Records[0].Sns.MessageId,
ALERT_CHANNEL,
status_code: response.status,
}
export const handler = async (
event: SNSEvent,
_context: Context
): Promise<NotifySlackOutput> => {
const functionConfig = await initPromise; // just wait until
const message = getFormattedMessage(ALERT_CHANNEL, event);
console.log("Message formatted from event: ", message);
if (!ALERT_CHANNEL) {
console.log("No alert channel specified - skipping...");
return null as any;
}
const response = await sendAlertToChannel(ALERT_CHANNEL, message);
console.log(
`Message sent to channel: ${ALERT_CHANNEL}, status: ${response.status}`
);
return {
event: event.Records[0].Sns.MessageId,
ALERT_CHANNEL,
status_code: response.status,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@

// example test. To run these tests, uncomment this file along with the
// example resource in lib/cdk-cloudwatch-rds-cpu-alert-stack.ts
test('SQS Queue Created', () => {
// const app = new cdk.App();
// // WHEN
// const stack = new CdkCloudwatchRdsCpuAlert.CdkCloudwatchRdsCpuAlertStack(app, 'MyTestStack');
// // THEN
// const template = Template.fromStack(stack);

// template.hasResourceProperties('AWS::SQS::Queue', {
// VisibilityTimeout: 300
// });
test("SQS Queue Created", () => {
// const app = new cdk.App();
// // WHEN
// const stack = new CdkCloudwatchRdsCpuAlert.CdkCloudwatchRdsCpuAlertStack(app, 'MyTestStack');
// // THEN
// const template = Template.fromStack(stack);
// template.hasResourceProperties('AWS::SQS::Queue', {
// VisibilityTimeout: 300
// });
});
Loading

0 comments on commit 13c3a52

Please sign in to comment.