Skip to content

Commit

Permalink
Merge pull request #16 from nib-health-funds/dry-run
Browse files Browse the repository at this point in the history
Implements dry run feature
  • Loading branch information
krutisfood authored May 30, 2017
2 parents 52cfb0c + f29cbe0 commit f8197aa
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 60 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ Hammertime is intended to be run in response to a Lambda scheduled event, e.g

Note when constructing schedule events in AWS, that times are in UTC.

### Enabling/Disabling

You can enable/disable hammertime using the environment variable `HAMMERTIME_ENABLED` at the time of deployment. 'true' enables hammertime.

### Dry run

Hammertime has a dry-run feature for when you are not quite ready to unleash the [hammer pants](https://en.wikipedia.org/wiki/Hammer_pants) on your entire fleet of EC2s just yet.
By setting `HAMMERTIME_DRY_RUN` to 'true', you enable dry-run in which hammertime does not touch your EC2s but will still log what it _would_ have touched.

## Deployment

Refer to the [serverless framework](!https://serverless.com/) for detailed instructions, but should be as simple as
Expand Down
7 changes: 5 additions & 2 deletions hammertime.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
const start = require('./src/start');
const stop = require('./src/stop');

// Try and use env var, otherwise default to false.
const dryRun = process.env.hammerTimeDryRun === 'true' || false;

module.exports = {
start,
stop,
start: (event, context, callback) => start({ event, context, callback, dryRun }),
stop: (event, context, callback) => stop({ event, context, callback, dryRun }),
};
1 change: 1 addition & 0 deletions isDryRun.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports.isDryRun = () => process.env.HAMMERTIME_DRY_RUN === 'true';
1 change: 1 addition & 0 deletions isEnabled.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports.isEnabled = () => process.env.HAMMERTIME_ENABLED === 'true';
1 change: 0 additions & 1 deletion schedule.js

This file was deleted.

8 changes: 6 additions & 2 deletions serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ provider:
- "logs:CreateLogStream"
- "logs:PutLogEvents"
Resource: "arn:aws:logs:*:*:*"
environment:
hammerTimeDryRun: ${file(./isDryRun.js):isDryRun}
custom:
enabled: ${file(./isEnabled.js):isEnabled}

functions:
stop-hammertime:
Expand All @@ -34,12 +38,12 @@ functions:
events:
- schedule:
rate: cron(30 9 * * ? *) # ADST 8:30PM, AEST 7:30PM every day
enabled: ${file(./schedule.js):enabled}
enabled: ${self:custom:enabled}

start-hammertime:
handler: hammertime.start
timeout: 180
events:
- schedule:
rate: cron(30 19 * * ? *) # ADST 6:30AM, AEST 5:30AM every day
enabled: ${file(./schedule.js):enabled}
enabled: ${self:custom:enabled}
58 changes: 36 additions & 22 deletions src/start.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,53 +5,67 @@ const startInstances = require('./instances/startInstances');
const listInstancesToStart = require('./instances/listInstancesToStart');
const untagInstances = require('./instances/untagInstances');

function startAllInstances() {
function startAllInstances(dryRun) {
return listInstancesToStart()
.then((startableInstances) => {
console.log(`Found the following ${startableInstances.length} instances to start up...`);
if (dryRun) {
console.log('Dry run is enabled, will not start or untag any instances.');
return [];
}

if (startableInstances.length === 0) {
console.log('None! Moving on.');
return 'No instances to turn on';
console.log('No instances found to start, moving on...');
return [];
}

console.log(`Found the following ${startableInstances.length} instances to start up...`);
startableInstances.forEach((instance) => {
console.log(instance);
});
return startInstances(startableInstances);
})
.then((startedInstances) => {
console.log('Finished starting instances. Moving on to untag them.');
return untagInstances(startedInstances);

return startInstances(startableInstances).then((startedInstanceIds) => {
console.log('Finished starting instances. Moving on to untag them.');
return untagInstances(startedInstanceIds);
});
});
}

function spinUpASGs() {
function spinUpASGs(dryRun) {
return listASGsToStart()
.then((startableASGs) => {
console.log(`Found the following ${startableASGs.length} instances to start up...`);
if (dryRun) {
console.log('Dry run is enabled, will not start or untag any ASGs.');
return [];
}

if (startableASGs.length === 0) {
console.log('None! Moving on.');
return 'No ASGs to spin up';
console.log('No ASGs found to spin up, moving on...');
return [];
}

console.log(`Found the following ${startableASGs.length} instances to start up...`);
// Log startableASGs for easy debugging
startableASGs.forEach((asg) => {
console.log(asg.AutoScalingGroupName);
});
return startASGs(startableASGs);
})
.then((startedASGs) => {
console.log(`Finished spinning up ASGs. Moving on to untag ${startedASGs.length} of them.`);
return untagASGs(startedASGs);

return startASGs(startableASGs).then((startedASGs) => {
console.log(`Finished spinning up ASGs. Moving on to untag ${startedASGs.length} of them.`);
return untagASGs(startedASGs);
});
});
}

module.exports = function start(event, context, callback) {
module.exports = function start(options) {
const { event, callback, dryRun } = options;
console.log('Break it down!');
Promise.all([
startAllInstances(),
spinUpASGs(),
startAllInstances(dryRun),
spinUpASGs(dryRun),
]).then(() => {
console.log('All instances and ASGs started successfully. Good morning!');
if (!dryRun) {
console.log('All instances and ASGs started successfully. Good morning!');
}
callback(null, { message: 'Start: Hammertime successfully completed.' }, event);
}).catch((err) => {
console.error(err);
Expand Down
83 changes: 50 additions & 33 deletions src/stop.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,57 +5,74 @@ const listInstancesToStop = require('./instances/listInstancesToStop');
const tagInstances = require('./instances/tagInstances');
const stopInstances = require('./instances/stopInstances');

function spinDownASGs() {
function stopAllInstances(dryRun) {
return listInstancesToStop()
.then((stoppableInstances) => {
if (dryRun) {
console.log('Dry run is enabled, will not stop or tag any instances.');
return [];
}

if (stoppableInstances.length === 0) {
console.log('No instances found to stop, moving on...');
return [];
}

console.log('Found the following instances to shut down...');
stoppableInstances.forEach((instance) => {
console.log(instance);
});

return tagInstances(stoppableInstances).then((taggedInstances) => {
if (taggedInstances.length > 0) {
console.log('Finished tagging instances. Moving on to stop them.');
return stopInstances(taggedInstances);
}

return [];
});
});
}

function spinDownASGs(dryRun) {
return listASGsToStop()
.then((stoppableASGs) => {
console.log(`Found the following ${stoppableASGs.length} instances to spin down...`);
if (dryRun) {
console.log('Dry run is enabled, will not stop or tag any ASGs.');
return [];
}

if (stoppableASGs.length === 0) {
console.log('None! Moving on.');
console.log('No ASGs to spin down, moving on...');
return [];
}

console.log(`Found the following ${stoppableASGs.length} instances to spin down...`);
stoppableASGs.forEach((asg) => {
console.log(asg.AutoScalingGroupName);
});
return tagASGs(stoppableASGs);
})
.then((taggedASGs) => {
if (taggedASGs.length > 0) {
console.log(`Finished tagging ASGs. Moving on to spin down ${taggedASGs.length} of them.`);
return stopASGs(taggedASGs);
}
});
}

function stopAllInstances() {
return listInstancesToStop()
.then((stoppableInstances) => {
console.log('Found the following instances to shut down...');
if (stoppableInstances.length === 0) {
console.log('None! Moving on.');
return [];
}
return tagASGs(stoppableASGs).then((taggedASGs) => {
if (taggedASGs.length > 0) {
console.log(`Finished tagging ASGs. Moving on to spin down ${taggedASGs.length} of them.`);
return stopASGs(taggedASGs);
}

stoppableInstances.forEach((instance) => {
console.log(instance);
return [];
});
return tagInstances(stoppableInstances);
})
.then((taggedInstances) => {
if (taggedInstances.length > 0) {
console.log('Finished tagging instances. Moving on to stop them.');
return stopInstances(taggedInstances);
}
});
}

module.exports = function stop(event, context, callback) {
module.exports = function stop(options) {
const { event, callback, dryRun } = options;
console.log('Stop. Hammertime!');
Promise.all([
stopAllInstances(),
spinDownASGs(),
stopAllInstances(dryRun),
spinDownASGs(dryRun),
]).then(() => {
console.log('All instances and ASGs stopped successfully. Good night!');
if (!dryRun) {
console.log('All instances and ASGs stopped successfully. Good night!');
}
callback(null, { message: 'Stop: Hammertime successfully completed.' }, event);
}).catch((err) => {
console.error(err);
Expand Down

0 comments on commit f8197aa

Please sign in to comment.