#!/usr/bin/env node

var AWS = require('aws-sdk');
const n = '<%= instance_name %>';
var jobs = [];

var now = ()=>{ return new Date().toISOString(); };
var retention = ()=>{ var d = new Date(); d.setDate(d.getDate() + <%= retention %>); return d.toISOString(); };
var pastRetention = (dstr)=>{
  var sd = new Date(dstr); var d = new Date();
  return (sd < d);
};

var description = (volumeId)=>{ return `Scheduled snapshot for ${n} volume ${volumeId} at ${now()}`; };

var findTag = (tagKey, tags) => {
  for(var tag in tags) { if(tagKey === tags[tag].Key) return tags[tag].Value; }
  return false;
};

var createSnapshot = (volumeId) => { return new Promise((ful,rej) => {
  console.log(`Creating snapshot of ${volumeId}`);
  ec2.createSnapshot({ VolumeId: volumeId, Description: description(volumeId) }, (err, data) => {
    if(err) rej(['Unable to create snapshot', err]);
    else {
      ec2.createTags({Resources: [data.SnapshotId], Tags: [{Key:'Expires', Value:retention()}]}, (err, data) => {
        if(err) rej(['Unable to create tag', err]);
        else ful(`Created and tagged snapshot of ${volumeId}`);
      });
    }
  });
})};

var cleanupSnapshot = (snapshotId) => { return new Promise((ful,rej) => {
  ec2.deleteSnapshot({ SnapshotId: snapshotId }, (err, data) => {
    if(err) rej(['Unable to delete snapshot', err]);
    else ful(`Deleted snapshot ${snapshotId} per retention policy`);
  });
})};

var cleanupSnapshots = (volumeId) => { return new Promise((ful,rej) => {
  console.log(`Cleaning up snapshots of ${volumeId}`);
  ec2.describeSnapshots({ Filters: [{ Name: 'volume-id', Values: [volumeId] }, {Name: 'tag-key', Values: ['Expires'] }] }, (err, data) => {
    if(err) {
      console.error('Unable to describe snapshots');
    } else { data.Snapshots.forEach((snap) => {
      val = findTag('DeleteAfter', snap.Tags);
      if(val && pastRetention(val)) jobs.push(cleanupSnapshot(snap.SnapshotId));
    })}
  });
})};

var outputJobs = () => {
  for (var j in jobs) {
    jobs[j].then((val) => { console.log(val); }, (err) => { console.error(err); });
  }
};

AWS.config.region = '<%= instance_region %>';
var ec2 = new AWS.EC2();

exports.handler = function (event, context, callback) {
  ec2.describeInstances({ Filters: [{Name:'tag:Name', Values:[n]}] },(err,data) => {
      if(err) {
        console.error('Unable to describe instances');
        context.done(err);
      } else {
        console.log(`Creating and cleaning snapshot(s) of ${n}`);
        data.Reservations.forEach((res) => { res.Instances.forEach((inst) => { inst.BlockDeviceMappings.forEach((bd) => {
          if(bd.DeviceName === inst.RootDeviceName) {
            // always back up the root volume
            jobs.push(createSnapshot(bd.Ebs.VolumeId));
            cleanupSnapshots(bd.Ebs.VolumeId);
          } else {
            ec2.describeVolumes({ VolumeIds: [bd.Ebs.VolumeId] },(err,data) => {
              if(err) { console.error('Unable to describe volumes'); } else {
                val = findTag('InstanceSnapshotInterval', data.Volumes[0].Tags);
                if(val) {
                  jobs.push(createSnapshot(bd.Ebs.VolumeId));
                  cleanupSnapshots(bd.Ebs.VolumeId);
                }
              }
              sc = true;
            });
          }
        })})});
        Promise.all(jobs).then((vals) => {
          msg = `Finished processing snapshots of ${n}`;
          console.log(msg);
          context.succeed(msg);
          outputJobs();
        }, (err) => {
          console.error(err[1]);
          context.fail(err[0]);
          outputJobs();
        });
        callback();
      }
  });
};
