-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathindex.js
More file actions
executable file
·122 lines (106 loc) · 4.9 KB
/
index.js
File metadata and controls
executable file
·122 lines (106 loc) · 4.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
'use strict';
const util = require('node:util');
const exec = util.promisify(require('node:child_process').exec);
const AWS = require('aws-sdk');
const AdmZip = require('adm-zip');
const dayjs = require('dayjs');
const axios = require('axios');
// ENVIRONMENT VARIABLES
const dumpOptions = process.env.MONGODUMP_OPTIONS;
const bucketName = process.env.S3_BUCKET;
const s3bucket = new AWS.S3({ params: { Bucket: bucketName } });
const s3StorageClass = process.env.S3_STORAGE_CLASS || 'STANDARD';
const zipFilename = process.env.ZIP_FILENAME || 'mongodb_backup';
const folderPrefix = process.env.FOLDER_PREFIX || 'mongodb_backups';
const dateFormat = process.env.DATE_FORMAT || 'YYYYMMDD_HHmmss';
const slackWebhookUrl = process.env.SLACK_WEBHOOK_URL;
const environment = process.env.ENVIRONMENT || 'unknown';
const backupsToRetain = parseInt(process.env.BACKUPS_TO_RETAIN) || 10;
// Function to send notifications to Slack
const notifySlack = async (message) => {
if (!slackWebhookUrl) {
console.warn('Slack webhook URL is not set');
return;
}
try {
await axios.post(slackWebhookUrl, { text: `[${environment.toUpperCase()}] ${message}` });
} catch (err) {
console.error('Failed to send Slack notification:', err);
}
};
// Backup process
exports.handler = async function (_event, _context) {
console.info(`[${environment.toUpperCase()}] MongoDB backup to S3 bucket '${bucketName}' is starting`);
process.env['PATH'] = process.env['PATH'] + ':' + process.env['LAMBDA_TASK_ROOT'];
const fileName = zipFilename + '_' + dayjs().format(dateFormat);
const folderName = `/tmp/${fileName}/`;
let zipBuffer = null;
try {
console.info(`[${environment.toUpperCase()}] Creating directory: ${folderName}`);
await exec(`mkdir -p ${folderName}`);
} catch (err) {
console.error(`[${environment.toUpperCase()}] Failed to create directory ${folderName}`, err);
await notifySlack(`Failed to create directory ${folderName}: ${err.message}`);
throw new Error(`Failed to create directory ${folderName}: ${err.message}`);
}
try {
console.info(`[${environment.toUpperCase()}] Executing mongodump with options: ${dumpOptions}`);
const { stdout, stderr } = await exec(`mongodump ${dumpOptions} --out ${folderName}`);
console.info(`[${environment.toUpperCase()}] mongodump stdout:`, stdout);
console.error(`[${environment.toUpperCase()}] mongodump stderr:`, stderr);
} catch (err) {
console.error(`[${environment.toUpperCase()}] mongodump command failed:`, err);
await notifySlack(`mongodump command failed: ${err.message}`);
throw new Error(`mongodump command failed: ${err.message}`);
}
try {
console.info(`[${environment.toUpperCase()}] Creating ZIP archive from folder: ${folderName}`);
const zip = new AdmZip();
zip.addLocalFolder(folderName);
zipBuffer = zip.toBuffer();
} catch (err) {
console.error(`[${environment.toUpperCase()}] Archive creation failed:`, err);
await notifySlack(`Archive creation failed: ${err.message}`);
throw new Error(`Archive creation failed: ${err.message}`);
}
try {
console.info(`[${environment.toUpperCase()}] Uploading ZIP archive to S3 bucket: ${bucketName}, Key: ${folderPrefix}/${fileName}.zip`);
await s3bucket.upload({
Key: `${folderPrefix}/${fileName}.zip`,
Body: zipBuffer,
ContentType: 'application/zip',
ServerSideEncryption: 'AES256',
StorageClass: s3StorageClass
}).promise();
} catch (err) {
console.error(`[${environment.toUpperCase()}] Upload to S3 failed:`, err);
await notifySlack(`Upload to S3 failed: ${err.message}`);
throw new Error(`Upload to S3 failed: ${err.message}`);
}
try {
console.info(`[${environment.toUpperCase()}] Listing objects in S3 bucket: ${bucketName}, Prefix: ${folderPrefix}/`);
const listedObjects = await s3bucket.listObjectsV2({
Bucket: bucketName,
Prefix: folderPrefix + '/'
}).promise();
const sortedObjects = listedObjects.Contents.sort((a, b) => new Date(b.LastModified) - new Date(a.LastModified));
const objectsToDelete = sortedObjects.slice(backupsToRetain);
if (objectsToDelete.length > 0) {
console.info(`[${environment.toUpperCase()}] Deleting ${objectsToDelete.length} old backups from S3 bucket: ${bucketName}`);
await s3bucket.deleteObjects({
Bucket: bucketName,
Delete: {
Objects: objectsToDelete.map(obj => ({ Key: obj.Key }))
}
}).promise();
} else {
console.info(`[${environment.toUpperCase()}] No old backups to delete.`);
}
} catch (err) {
console.error(`[${environment.toUpperCase()}] Failed to list or delete old backups:`, err);
await notifySlack(`Failed to list or delete old backups: ${err.message}`);
throw new Error(`Failed to list or delete old backups: ${err.message}`);
}
console.info(`[${environment.toUpperCase()}] Backup completed successfully`);
await notifySlack('Backup completed successfully');
};