hubot-aws-cfpb
Version:
CFPB-specific Hubot aws commands. Forked from the excellent https://github.com/yoheimuta/hubot-aws/ and heavily modified for our particular usage needs
285 lines (225 loc) • 9.62 kB
text/coffeescript
# Description:
# List ec2 instances info
# Show detail about an instance if specified an instance id
# Filter ec2 instances info if specified an instance name
# Configurations:
# HUBOT_AWS_DEFAULT_CREATOR_EMAIL: [required] An email address to be used for tagging the new instance
#
# Commands:
# hubot ec2 ls - Displays Instances
# hubot ec2 ls instance_id - Displays details about 'instance_id'
# hubot ec2 mine - Displays Instances I've created, based on user email
# hubot ec2 chat - Displays Instances created via chat
# hubot ec2 filter sometext - Filters instances whose name (name tag value) contains 'sometext'
# hubot ec2 expired - Displays instances that have expired
# hubot ec2 expiring - Displays instances that are expiring within 2 days
# hubot ec2 windows - Displays instances running the Windows OS
#
gist = require 'quick-gist'
mdTable = require('markdown-table')
moment = require 'moment'
_ = require 'underscore'
tsv = require 'tsv'
tags = require './tags'
util = require 'util'
EXPIRED_MESSAGE = "Instances that have expired \n"
EXPIRES_SOON_MESSAGE = "Instances that will expire soon \n"
USER_EXPIRES_SOON_MESSAGE = "List of your instances that will expire soon: \n"
EXTEND_COMMAND = "\nIf you wish to extend run 'cfpbot ec2 extend [instanceIds]'"
DAYS_CONSIDERED_SOON = 2
ec2 = require('../../ec2.coffee')
getArgParams = (arg, filter = "all", opt_arg = "") ->
filterValues = []
if arg
for av in arg.split /\s+/
if av and not av.match(/^--/)
filterValues.push(av)
#console.log "filterValues are: "
#console.log util.inspect(filterValues, false, null)
params = {}
if filter == "mine"
params['Filters'] = [{Name: 'tag:Creator', Values: [opt_arg]}]
else if filter == "chat"
params['Filters'] = [{Name: 'tag:CreatedByApplication', Values: [filter]}]
else if filter == "windows"
params['Filters'] = [{Name: 'platform', Values: [filter]}]
else if filter == "filter" and filterValues.length
params['Filters'] = [{Name: 'tag-value', Values: ["*#{filterValues[0]}*"]}]
else if filterValues.length
params['InstanceIds'] = filterValues
if Object.keys(params).length > 0
return params
else
return null
listEC2Instances = (params, complete, error) ->
console.log util.inspect(params, false, null)
ec2.describeInstances params, (err, res) ->
if err
error(err)
else
instances = []
for reservation in res.Reservations
for instance in reservation.Instances
instances.push(instance)
complete(instances)
get_expiration_tag = (tag) ->
return tag.Key == "ExpireDate"
instance_will_expire_soon = (instance) ->
expiration_tag = instance.Tags.filter get_expiration_tag
if expiration_tag.length == 0
return false
expiration_moment = moment(expiration_tag[0].Value).format('YYYY-MM-DD')
will_be_expired_in_x_days = expiration_moment <= moment().add(DAYS_CONSIDERED_SOON, 'days').format('YYYY-MM-DD')
is_not_expired_now = expiration_moment < moment().format('YYYY-MM-DD')
return will_be_expired_in_x_days and not is_not_expired_now and instance.State.Name == "running"
instance_has_expired = (instance) ->
expiration_tag = instance.Tags.filter get_expiration_tag
if expiration_tag.length == 0
return false
expiration_moment = moment(expiration_tag[0].Value).format('YYYY-MM-DD')
is_not_expired_now = expiration_moment < moment().format('YYYY-MM-DD')
return is_not_expired_now and instance.State.Name == "running"
extract_message = (instances, msg)->
return msg + messages_from_ec2_instances(instances)
get_instance_tag = (instance, key, default_value = "")->
tags_ = _.filter(instance.Tags, (tag)-> return tag.Key == key)
if not _.isEmpty(tags_)
return tags_[0].Value
else
return default_value
list_expiring_msg = (msg)->
return (instances)->
instances_that_will_expire = instances.filter instance_will_expire_soon
handle_sending_message msg, extract_message(instances_that_will_expire, EXPIRES_SOON_MESSAGE)
list_expired_msg = (msg)->
return (instances)->
instances_that_expired = instances.filter instance_has_expired
handle_sending_message msg, extract_message(instances_that_expired, EXPIRED_MESSAGE)
handle_instances = (robot) ->
msg_room = (msg_text, room = process.env.HUBOT_EC2_MENTION_ROOM) ->
robot.messageRoom room, msg_text
return (instances) ->
instances_that_expired = instances.filter instance_has_expired
instances_that_will_expire = instances.filter instance_will_expire_soon
if instances_that_will_expire.length
msg_text_expire_soon = extract_message(instances_that_will_expire, EXPIRES_SOON_MESSAGE)
msg_room(msg_text_expire_soon)
for user in _.values(robot.brain.data.users)
creator_email = user.email_address || ""
user_id = user.id
user_instances = _.filter(instances_that_will_expire, (this_instance)-> return get_instance_tag(this_instance, 'Creator').toLowerCase() == creator_email.toLowerCase())
if user_instances.length
msg_text_expire_soon = extract_message(user_instances, USER_EXPIRES_SOON_MESSAGE) + EXTEND_COMMAND
msg_room(msg_text_expire_soon, user.name)
instanceIdsToStop = _.pluck(instances_that_expired, 'InstanceId')
if instanceIdsToStop.length
console.log "Stopping expired EC2 instances: #{instanceIdsToStop}"
ec2.stopInstances {InstanceIds: instanceIdsToStop}, (err, res) ->
if err
msg_room(err)
else
tags.removeSchedule(null, instanceIdsToStop)
handle_ec2_instance = (robot) ->
if process.env.HUBOT_EC2_MENTION_ROOM
params = getArgParams(null, filter = "chat")
listEC2Instances(params, handle_instances(robot), ->)
ec2_setup_polling = (robot) ->
setInterval ->
handle_ec2_instance(robot)?
, 1000 * 60 * 60 * 8
messages_from_ec2_instances = (instances) ->
messages = []
getTag = (_tags, tag) -> _.first(t.Value for t in _tags when t.Key is tag) || ''
for instance in instances
messages.push({
time: moment(instance.LaunchTime).format('YYYY-MM-DD')
state: instance.State.Name
id: instance.InstanceId
type: instance.InstanceType
ip: instance.PrivateIpAddress
name: getTag(instance.Tags, 'Name') || '[NoName]'
description: getTag(instance.Tags, 'Description') || ''
expiration: getTag(instance.Tags, 'ExpireDate') || ''
schedule: getTag(instance.Tags, tags.SCHEDULE_TAG) || ''
backup: getTag(instance.Tags, 'Backup') || '[None]'
creator: getTag(instance.Tags, 'Creator') || '[None]'
})
messages.sort (a, b) ->
moment(a.time) - moment(b.time)
if messages.length
tableHead = [
'id',
'ip',
'name',
'creator',
'state',
'description',
'type',
'launched',
'expires',
'schedule',
'backup',
]
tableRows = (
[
m.id,
m.ip,
m.name,
m.creator,
m.state,
m.description,
m.type,
m.time,
m.expiration,
m.schedule,
m.backup,
] for m in messages
)
tableRows.unshift tableHead
table = mdTable tableRows, {align: 'l'}
return "\n#{table}"
else
return "\n[None]\n"
error_ec2_instances = (msg, err) ->
return (err) ->
msg.send "DescribeInstancesError: #{err}"
handle_sending_message = (msg, messages) ->
if messages.length < 1000
msg.send messages
else
gistOpts = {content: messages, enterpriseOnly: true, fileExtension: 'md'}
gist gistOpts, (err, resp, data) ->
url = data.html_url
msg.send "View instances at: " + url
complete_ec2_instances = (msg, instances) ->
return (instances) ->
handle_sending_message msg, messages_from_ec2_instances(instances)
module.exports = (robot) ->
ec2_setup_polling(robot)
robot.respond /ec2 ls(.*)$/i, (msg) ->
arg_params = getArgParams(arg = msg.match[1])
msg.send "Fetching instances..."
listEC2Instances(arg_params, complete_ec2_instances(msg), error_ec2_instances(msg))
robot.respond /ec2 filter(.*)$/i, (msg) ->
arg_params = getArgParams(arg = msg.match[1], filter = "filter")
msg.send "Fetching filtered instances..."
listEC2Instances(arg_params, complete_ec2_instances(msg), error_ec2_instances(msg))
robot.respond /ec2 mine$/i, (msg) ->
creator_email = msg.message.user["email_address"] || process.env.HUBOT_AWS_DEFAULT_CREATOR_EMAIL || "unknown"
msg.send "Fetching instances created by #{creator_email} ..."
arg_params = getArgParams(arg = msg.match[1], filter = "mine", opt_arg = creator_email)
listEC2Instances(arg_params, complete_ec2_instances(msg), error_ec2_instances(msg))
robot.respond /ec2 chat$/i, (msg) ->
msg.send "Fetching instances created via chat ..."
arg_params = getArgParams(arg = msg.match[1], filter = "chat")
listEC2Instances(arg_params, complete_ec2_instances(msg), error_ec2_instances(msg))
robot.respond /ec2 expiring$/i, (msg)->
msg.send "Fetching all instances expiring within #{DAYS_CONSIDERED_SOON} days"
listEC2Instances(null, list_expiring_msg(msg), error_ec2_instances(msg))
robot.respond /ec2 expired$/i, (msg)->
msg.send "Fetching all instances that are expired"
listEC2Instances(null, list_expired_msg(msg), error_ec2_instances(msg))
robot.respond /ec2 windows$/i, (msg)->
msg.send "Fetching all windows instances ..."
arg_params = getArgParams(arg = msg.match[1], filter = "windows")
listEC2Instances(arg_params, complete_ec2_instances(msg), error_ec2_instances(msg))