Skip to content

Commit f8197aa

Browse files
authored
Merge pull request #16 from nib-health-funds/dry-run
Implements dry run feature
2 parents 52cfb0c + f29cbe0 commit f8197aa

File tree

8 files changed

+108
-60
lines changed

8 files changed

+108
-60
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,15 @@ Hammertime is intended to be run in response to a Lambda scheduled event, e.g
3232

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

35+
### Enabling/Disabling
36+
37+
You can enable/disable hammertime using the environment variable `HAMMERTIME_ENABLED` at the time of deployment. 'true' enables hammertime.
38+
39+
### Dry run
40+
41+
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.
42+
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.
43+
3544
## Deployment
3645

3746
Refer to the [serverless framework](!https://serverless.com/) for detailed instructions, but should be as simple as

hammertime.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
const start = require('./src/start');
22
const stop = require('./src/stop');
33

4+
// Try and use env var, otherwise default to false.
5+
const dryRun = process.env.hammerTimeDryRun === 'true' || false;
6+
47
module.exports = {
5-
start,
6-
stop,
8+
start: (event, context, callback) => start({ event, context, callback, dryRun }),
9+
stop: (event, context, callback) => stop({ event, context, callback, dryRun }),
710
};

isDryRun.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports.isDryRun = () => process.env.HAMMERTIME_DRY_RUN === 'true';

isEnabled.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports.isEnabled = () => process.env.HAMMERTIME_ENABLED === 'true';

schedule.js

Lines changed: 0 additions & 1 deletion
This file was deleted.

serverless.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ provider:
2626
- "logs:CreateLogStream"
2727
- "logs:PutLogEvents"
2828
Resource: "arn:aws:logs:*:*:*"
29+
environment:
30+
hammerTimeDryRun: ${file(./isDryRun.js):isDryRun}
31+
custom:
32+
enabled: ${file(./isEnabled.js):isEnabled}
2933

3034
functions:
3135
stop-hammertime:
@@ -34,12 +38,12 @@ functions:
3438
events:
3539
- schedule:
3640
rate: cron(30 9 * * ? *) # ADST 8:30PM, AEST 7:30PM every day
37-
enabled: ${file(./schedule.js):enabled}
41+
enabled: ${self:custom:enabled}
3842

3943
start-hammertime:
4044
handler: hammertime.start
4145
timeout: 180
4246
events:
4347
- schedule:
4448
rate: cron(30 19 * * ? *) # ADST 6:30AM, AEST 5:30AM every day
45-
enabled: ${file(./schedule.js):enabled}
49+
enabled: ${self:custom:enabled}

src/start.js

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,53 +5,67 @@ const startInstances = require('./instances/startInstances');
55
const listInstancesToStart = require('./instances/listInstancesToStart');
66
const untagInstances = require('./instances/untagInstances');
77

8-
function startAllInstances() {
8+
function startAllInstances(dryRun) {
99
return listInstancesToStart()
1010
.then((startableInstances) => {
11-
console.log(`Found the following ${startableInstances.length} instances to start up...`);
11+
if (dryRun) {
12+
console.log('Dry run is enabled, will not start or untag any instances.');
13+
return [];
14+
}
15+
1216
if (startableInstances.length === 0) {
13-
console.log('None! Moving on.');
14-
return 'No instances to turn on';
17+
console.log('No instances found to start, moving on...');
18+
return [];
1519
}
1620

21+
console.log(`Found the following ${startableInstances.length} instances to start up...`);
1722
startableInstances.forEach((instance) => {
1823
console.log(instance);
1924
});
20-
return startInstances(startableInstances);
21-
})
22-
.then((startedInstances) => {
23-
console.log('Finished starting instances. Moving on to untag them.');
24-
return untagInstances(startedInstances);
25+
26+
return startInstances(startableInstances).then((startedInstanceIds) => {
27+
console.log('Finished starting instances. Moving on to untag them.');
28+
return untagInstances(startedInstanceIds);
29+
});
2530
});
2631
}
2732

28-
function spinUpASGs() {
33+
function spinUpASGs(dryRun) {
2934
return listASGsToStart()
3035
.then((startableASGs) => {
31-
console.log(`Found the following ${startableASGs.length} instances to start up...`);
36+
if (dryRun) {
37+
console.log('Dry run is enabled, will not start or untag any ASGs.');
38+
return [];
39+
}
40+
3241
if (startableASGs.length === 0) {
33-
console.log('None! Moving on.');
34-
return 'No ASGs to spin up';
42+
console.log('No ASGs found to spin up, moving on...');
43+
return [];
3544
}
3645

46+
console.log(`Found the following ${startableASGs.length} instances to start up...`);
47+
// Log startableASGs for easy debugging
3748
startableASGs.forEach((asg) => {
3849
console.log(asg.AutoScalingGroupName);
3950
});
40-
return startASGs(startableASGs);
41-
})
42-
.then((startedASGs) => {
43-
console.log(`Finished spinning up ASGs. Moving on to untag ${startedASGs.length} of them.`);
44-
return untagASGs(startedASGs);
51+
52+
return startASGs(startableASGs).then((startedASGs) => {
53+
console.log(`Finished spinning up ASGs. Moving on to untag ${startedASGs.length} of them.`);
54+
return untagASGs(startedASGs);
55+
});
4556
});
4657
}
4758

48-
module.exports = function start(event, context, callback) {
59+
module.exports = function start(options) {
60+
const { event, callback, dryRun } = options;
4961
console.log('Break it down!');
5062
Promise.all([
51-
startAllInstances(),
52-
spinUpASGs(),
63+
startAllInstances(dryRun),
64+
spinUpASGs(dryRun),
5365
]).then(() => {
54-
console.log('All instances and ASGs started successfully. Good morning!');
66+
if (!dryRun) {
67+
console.log('All instances and ASGs started successfully. Good morning!');
68+
}
5569
callback(null, { message: 'Start: Hammertime successfully completed.' }, event);
5670
}).catch((err) => {
5771
console.error(err);

src/stop.js

Lines changed: 50 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,57 +5,74 @@ const listInstancesToStop = require('./instances/listInstancesToStop');
55
const tagInstances = require('./instances/tagInstances');
66
const stopInstances = require('./instances/stopInstances');
77

8-
function spinDownASGs() {
8+
function stopAllInstances(dryRun) {
9+
return listInstancesToStop()
10+
.then((stoppableInstances) => {
11+
if (dryRun) {
12+
console.log('Dry run is enabled, will not stop or tag any instances.');
13+
return [];
14+
}
15+
16+
if (stoppableInstances.length === 0) {
17+
console.log('No instances found to stop, moving on...');
18+
return [];
19+
}
20+
21+
console.log('Found the following instances to shut down...');
22+
stoppableInstances.forEach((instance) => {
23+
console.log(instance);
24+
});
25+
26+
return tagInstances(stoppableInstances).then((taggedInstances) => {
27+
if (taggedInstances.length > 0) {
28+
console.log('Finished tagging instances. Moving on to stop them.');
29+
return stopInstances(taggedInstances);
30+
}
31+
32+
return [];
33+
});
34+
});
35+
}
36+
37+
function spinDownASGs(dryRun) {
938
return listASGsToStop()
1039
.then((stoppableASGs) => {
11-
console.log(`Found the following ${stoppableASGs.length} instances to spin down...`);
40+
if (dryRun) {
41+
console.log('Dry run is enabled, will not stop or tag any ASGs.');
42+
return [];
43+
}
44+
1245
if (stoppableASGs.length === 0) {
13-
console.log('None! Moving on.');
46+
console.log('No ASGs to spin down, moving on...');
1447
return [];
1548
}
1649

50+
console.log(`Found the following ${stoppableASGs.length} instances to spin down...`);
1751
stoppableASGs.forEach((asg) => {
1852
console.log(asg.AutoScalingGroupName);
1953
});
20-
return tagASGs(stoppableASGs);
21-
})
22-
.then((taggedASGs) => {
23-
if (taggedASGs.length > 0) {
24-
console.log(`Finished tagging ASGs. Moving on to spin down ${taggedASGs.length} of them.`);
25-
return stopASGs(taggedASGs);
26-
}
27-
});
28-
}
2954

30-
function stopAllInstances() {
31-
return listInstancesToStop()
32-
.then((stoppableInstances) => {
33-
console.log('Found the following instances to shut down...');
34-
if (stoppableInstances.length === 0) {
35-
console.log('None! Moving on.');
36-
return [];
37-
}
55+
return tagASGs(stoppableASGs).then((taggedASGs) => {
56+
if (taggedASGs.length > 0) {
57+
console.log(`Finished tagging ASGs. Moving on to spin down ${taggedASGs.length} of them.`);
58+
return stopASGs(taggedASGs);
59+
}
3860

39-
stoppableInstances.forEach((instance) => {
40-
console.log(instance);
61+
return [];
4162
});
42-
return tagInstances(stoppableInstances);
43-
})
44-
.then((taggedInstances) => {
45-
if (taggedInstances.length > 0) {
46-
console.log('Finished tagging instances. Moving on to stop them.');
47-
return stopInstances(taggedInstances);
48-
}
4963
});
5064
}
5165

52-
module.exports = function stop(event, context, callback) {
66+
module.exports = function stop(options) {
67+
const { event, callback, dryRun } = options;
5368
console.log('Stop. Hammertime!');
5469
Promise.all([
55-
stopAllInstances(),
56-
spinDownASGs(),
70+
stopAllInstances(dryRun),
71+
spinDownASGs(dryRun),
5772
]).then(() => {
58-
console.log('All instances and ASGs stopped successfully. Good night!');
73+
if (!dryRun) {
74+
console.log('All instances and ASGs stopped successfully. Good night!');
75+
}
5976
callback(null, { message: 'Stop: Hammertime successfully completed.' }, event);
6077
}).catch((err) => {
6178
console.error(err);

0 commit comments

Comments
 (0)