in src/consumer.js [321:446]
async function lifeCycle(snapshot_type) {
console.log(`Performing Lifecycle Management for: ${snapshot_type}`);
let rsp = {};
let snapshots = {};
let sources = {};
let identity = await sts.getCallerIdentity({}).promise();
const start = Date.now();
// Collect all current snapshots and determine their age
let params = { };
do {
switch (snapshot_type) {
case 'RDS Cluster':
params.SnapshotType = 'manual';
rsp = await rds.describeDBClusterSnapshots(params).promise();
snapshots = rsp.DBClusterSnapshots;
break;
case 'RDS':
params.SnapshotType = 'manual';
rsp = await rds.describeDBSnapshots(params).promise();
snapshots = rsp.DBSnapshots;
break;
case 'EBS': // Request all the snapshots owned by the DR account with Draco_Lifecycle tags (no pagination)
params.OwnerIds = [ identity.Account ];
params.Filters = [ { Name: 'tag-key', Values: [ 'Draco_Lifecycle' ] } ];
rsp = await ec2.describeSnapshots(params).promise();
snapshots = rsp.Snapshots;
break;
default:
throw "Invalid Snapshot Type: "+snapshot_type;
}
if ('Marker' in rsp) params.Marker = rsp.Marker;
else delete params.Marker;
for (const snapshot of snapshots) {
if (snapshot_type.startsWith('RDS') && snapshot.Status != "available") continue;
if (snapshot_type.startsWith('EBS') && snapshot.State != "completed") continue;
let source_id, snapshot_id, snapshot_arn, snapshot_date;
switch(snapshot_type) {
case 'RDS Cluster':
source_id = snapshot.DBClusterIdentifier;
snapshot_id = snapshot.DBClusterSnapshotIdentifier;
snapshot_arn = snapshot.DBClusterSnapshotArn;
snapshot_date = new Date(snapshot.SnapshotCreateTime);
break;
case 'RDS':
source_id = snapshot.DBInstanceIdentifier;
snapshot_id = snapshot.DBSnapshotIdentifier;
snapshot_arn = snapshot.DBSnapshotArn;
snapshot_date = new Date(snapshot.SnapshotCreateTime);
break;
case 'EBS':
source_id = snapshot.Description.match(/\bvol-[0-9a-f]+\b/i)[0];
snapshot_id = snapshot.SnapshotId;
snapshot_date = new Date(snapshot.StartTime);
break;
}
if (!(source_id in sources)) {
sources[source_id] = new Array();
}
sources[source_id].push ({
age: (start - snapshot_date.valueOf()) / (24 * 3600 * 1000.0),
id: snapshot_id,
arn: snapshot_arn,
date: snapshot.SnapshotCreateTime,
});
}
}
while ('Marker' in params);
for (const source in sources) {
const snapshots = sources[source].sort((a,b) => a.age - b.age); // youngest first
// Get the Tags on the most recent Snapshot
let youngest = snapshots[0];
let taglist;
switch (snapshot_type) {
case 'RDS Cluster':
case 'RDS':
rsp = await rds.listTagsForResource({"ResourceName": youngest.arn}).promise();
taglist = rsp.TagList;
break;
case 'EBS': {
let p = {
Filters: [ { Name: "resource-id", Values: [ youngest.id ] } ],
MaxResults: 500
}
let rsp = await ec2.describeTags(p).promise();
taglist = rsp.Tags.filter(t => !t.Key.startsWith('aws:')).map(e => ({ Key: e.Key, Value: e.Value } ))
break;
}
}
let tags = {};
for (let tag of taglist) {
tags[tag.Key] = tag.Value;
}
if (typeof tags.Draco_Lifecycle === 'undefined') {
console.warn(`Source: ${source} has no Draco_Lifecycle tag. Skipped`);
continue;
}
const lifecycle = tags["Draco_Lifecycle"];
console.log(`Source: ${source} has lifecycle '${lifecycle}' with ${snapshots.length} snapshots. Youngest: ${JSON.stringify(youngest)}`);
let dry_run = (typeof process.env.DRY_RUN !== 'undefined' && process.env.DRY_RUN != "false")
let deletions = retention.implementPolicy(snapshots, lifecycle).filter(f => f.retain == false);
for (const snap of deletions) {
console.log(`${dry_run ? "Dry Run - Not ":""}Deleting: ${snap.id}`);
if (!dry_run) {
switch(snapshot_type) {
case 'RDS Cluster':
rsp = await rds.deleteDBClusterSnapshot({ DBClusterSnapshotIdentifier: snap.id }).promise();
break;
case 'RDS':
rsp =await rds.deleteDBSnapshot({ DBSnapshotIdentifier: snap.id }).promise();
break;
case 'EBS':
rsp =await ec2.deleteSnapshot({ SnapshotId: snap.id }).promise();
break;
}
}
}
}
}