Skip to content

Commit 86fc1a9

Browse files
committed
Add Lambda function for email notifications
1 parent d5600bb commit 86fc1a9

File tree

2 files changed

+139
-0
lines changed

2 files changed

+139
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ The buildspecs folder contains the following buildspec files for use with AWS Co
2121
The ci_tools folder contains the following tools for use with AWS Lambda and Amazon CloudWatch Events to hook together the end-to-end CI process:
2222
* trigger_codebuild.js: Lambda function to start a CodeBuild build.
2323
* slack_notifications.js: Lambda function to post CodeBuild build notifications into a Slack channel.
24+
* email_notifications.js: Lambda function to send CodeBuild build notification emails via Amazon SES.
2425
* cwe-rule-configuration/branch_ci.json: CloudWatch Events rule pattern to start a CodeBuild build for every push to the master branch of a CodeCommit repository.
2526
* cwe-rule-configuration/slack_event_pattern.json: CloudWatch Events rule pattern to notify Slack for failed CodeBuild builds.
2627
* cwe-rule-configuration/nightly_build_input.json: CloudWatch Events target input to start a CodeBuild build with a specific buildspec override and project name.

ci_tools/email_notifications.js

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
'use strict';
2+
3+
const AWS = require('aws-sdk');
4+
const codebuild = new AWS.CodeBuild();
5+
const cloudwatchlogs = new AWS.CloudWatchLogs();
6+
const ses = new AWS.SES();
7+
const sourceEmailAddress = process.env.sourceEmailAddress;
8+
const destinationEmailAddress = process.env.destinationEmailAddress;
9+
10+
function sendBuildEmail(subject, htmlBody, textBody, callback) {
11+
console.log(subject);
12+
console.log(htmlBody);
13+
console.log(textBody);
14+
15+
var emailParams = {
16+
Source: sourceEmailAddress,
17+
Destination: {
18+
ToAddresses: [destinationEmailAddress]
19+
},
20+
Message: {
21+
Body: {
22+
Text: {
23+
Data: textBody
24+
},
25+
Html: {
26+
Data: htmlBody
27+
}
28+
},
29+
Subject: {
30+
Data: subject
31+
}
32+
}
33+
};
34+
35+
ses.sendEmail(emailParams, function(err, data){
36+
if(err) {
37+
return callback(err);
38+
} else {
39+
return callback(null, "Email sent");
40+
}
41+
});
42+
}
43+
44+
function handleBuildEvent(event, callback) {
45+
const buildStatus = event.detail['build-status'];
46+
47+
if (buildStatus != 'IN_PROGRESS') {
48+
const buildArn = event.detail['build-id'];
49+
const buildId = buildArn.split('/').pop();
50+
const buildUuid = buildId.split(':').pop();
51+
const sourceUrl = event.detail['additional-information'].source.location;
52+
const repoName = sourceUrl.split('/').pop();
53+
const projectName = event.detail['project-name'];
54+
const region = event.region;
55+
56+
// Construct email content based on build status
57+
var subject = `Nightly ${projectName} build `;
58+
var htmlBody = `Build ${buildUuid} for project ${projectName} `;
59+
var textBody = htmlBody;
60+
61+
switch (buildStatus) {
62+
case 'SUCCEEDED':
63+
subject += "succeeded";
64+
textBody += "succeeded";
65+
htmlBody += "<b>succeeded!</b>";
66+
break;
67+
case 'STOPPED':
68+
subject += "was canceled";
69+
textBody += "was canceled";
70+
htmlBody += "was <b>canceled</b>.";
71+
break;
72+
case 'TIMED_OUT':
73+
subject += "timed out";
74+
textBody += "timed out";
75+
htmlBody += "<b>timed out</b>.";
76+
break;
77+
default:
78+
subject += "failed";
79+
textBody += "failed";
80+
htmlBody += "<b>failed</b>.";
81+
}
82+
83+
htmlBody += ` Visit the <a href=\"https:\/\/${region}.console.aws.amazon.com\/codebuild\/home?` +
84+
`region=${region}#\/builds\/${encodeURI(buildId)}\/view\/new\">AWS CodeBuild console</a> to view the build details.`;
85+
86+
textBody += ` Visit the AWS CodeBuild console to view the build details: https:\/\/${region}.console.aws.amazon.com\/codebuild\/home?` +
87+
`region=${region}#\/builds\/${encodeURI(buildId)}\/view\/new`;
88+
89+
// Add build logs snippet to the body
90+
if (buildStatus != 'SUCCEEDED' &&
91+
buildStatus != 'STOPPED' &&
92+
event.detail['additional-information'].logs &&
93+
event.detail['additional-information'].logs['stream-name'] &&
94+
event.detail['additional-information'].logs['group-name']) {
95+
96+
var logParams = {
97+
logGroupName: event.detail['additional-information'].logs['group-name'],
98+
logStreamName: event.detail['additional-information'].logs['stream-name'],
99+
limit: 30,
100+
startFromHead: false
101+
};
102+
cloudwatchlogs.getLogEvents(logParams, function(err, data) {
103+
if (err) {
104+
// an error occurred, ignore and send the email without the logs
105+
console.log(err, err.stack);
106+
} else {
107+
var logLines = data.events.map(function(event) {
108+
return event.message;
109+
}).join("");
110+
htmlBody += "<br/><br/>Logs:<br/><br/><pre>" + logLines + "</pre><br/>";
111+
textBody += "\n\nLogs:\n\n" + logLines + "\n\n";
112+
}
113+
114+
sendBuildEmail(subject, htmlBody, textBody, callback);
115+
});
116+
} else {
117+
sendBuildEmail(subject, htmlBody, textBody, callback);
118+
}
119+
} else {
120+
callback("Not a completed build");
121+
}
122+
}
123+
124+
exports.handler = (event, context, callback) => {
125+
try {
126+
console.log(event);
127+
128+
if (event.source && event.source == "aws.codebuild" &&
129+
event['detail-type'] == "CodeBuild Build State Change") {
130+
handleBuildEvent(event, callback);
131+
} else {
132+
callback("Not a CodeBuild event");
133+
}
134+
} catch (error) {
135+
console.log('Caught Error: ', error);
136+
callback(error);
137+
}
138+
};

0 commit comments

Comments
 (0)