Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
173 lines (143 sloc) 6.295 kb
# Description:
# Queries for the status of AWS services
#
# Dependencies:
# "aws2js": "0.6.12"
# "underscore": "1.3.3"
# "moment": "1.6.2"
#
# Configuration:
# HUBOT_AWS_ACCESS_KEY_ID
# HUBOT_AWS_SECRET_ACCESS_KEY
# HUBOT_AWS_SQS_REGIONS
# HUBOT_AWS_EC2_REGIONS
#
# Commands:
# hubot sqs status - Returns the status of SQS queues
# hubot ec2 status - Returns the status of EC2 instances
#
# Notes:
# It's highly recommended to use a read-only IAM account for this purpose
# https://console.aws.amazon.com/iam/home?
# SQS - requires ListQueues, GetQueueAttributes and ReceiveMessage
# EC2 - requires EC2:Describe*, elasticloadbalancing:Describe*, cloudwatch:ListMetrics,
# cloudwatch:GetMetricStatistics, cloudwatch:Describe*, autoscaling:Describe*
#
# Author:
# Iristyle
key = process.env.HUBOT_AWS_ACCESS_KEY_ID
secret = process.env.HUBOT_AWS_SECRET_ACCESS_KEY
_ = require 'underscore'
moment = require 'moment'
aws = require 'aws2js'
sqs = aws
.load('sqs', key, secret)
.setApiVersion('2011-10-01')
ec2 = aws
.load('ec2', key, secret)
.setApiVersion('2012-05-01')
getRegionInstances = (region, msg) ->
ec2.setRegion(region).request 'DescribeInstances', (error, reservations) ->
if error?
msg.send "Failed to describe instances for region #{region} - error #{error}"
return
ec2.setRegion(region).request 'DescribeInstanceStatus', (error, allStatuses) ->
statuses = if error? then [] else allStatuses.instanceStatusSet.item
instances = _.flatten [reservations?.reservationSet?.item ? []]
instances = _.pluck instances, 'instancesSet'
instances = _.flatten _.pluck instances, 'item'
msg.send "Found #{instances.length} instances for region #{region}..."
for instance in instances
do (instance) ->
status = _.find statuses, (s) ->
instance.instanceId == s.instanceId
suffix = ''
state = instance.instanceState.name
excl = String.fromCharCode 0x203C
dexcl = excl + excl
switch state
when 'pending' then prefix = String.fromCharCode 0x25B2
when 'running' then prefix = String.fromCharCode 0x25BA
when 'shutting-down' then prefix = String.fromCharCode 0x25BC
when 'terminated' then prefix = String.fromCharCode 0x25AA
when 'stopping' then prefix = String.fromCharCode 0x25A1
when 'stopped' then prefix = String.fromCharCode 0x25A0
else prefix = dexcl
if status?
bad = _.filter [status.systemStatus, status.instanceStatus],
(s) -> s.status != 'ok'
if bad.length > 0
prefix = dexcl
badStrings = _.map bad, (b) ->
b.details.item.name + ' ' + b.details.item.status
concat = (memo, s) -> memo + s
suffix = _.reduce badStrings, concat, ''
iEvents = _.flatten [status.eventsSet?.item ? []]
if not _.isEmpty iEvents then prefix = dexcl
desc = (memo, e) -> "#{memo} #{dexcl}#{e.code} : #{e.description}"
suffix += _.reduce iEvents, desc, ''
id = instance.instanceId ? 'N/A'
type = instance.instanceType
dnsName = if _.isEmpty instance.dnsName then 'N/A' \
else instance.dnsName
launchTime = moment(instance.launchTime)
.format 'ddd, L LT'
arch = instance.architecture
devType = instance.rootDeviceType
tags = _.flatten [instance.tagSet?.item ? []]
name = (_.find tags, (t) -> t.key == 'Name')?.value ? 'missing'
msg.send "#{prefix} [#{state}] - #{name} / #{type} [#{devType} #{arch}] / #{dnsName} / #{region} / #{id} - started #{launchTime} #{suffix}"
getRegionQueues = (region, msg) ->
sqs.setRegion(region).request 'ListQueues', {}, (error, queues) ->
if error?
msg.send "Failed to list queues for region #{region} - error #{error}"
return
urls = _.flatten [queues.ListQueuesResult?.QueueUrl ? []]
msg.send "Found #{urls.length} queues for region #{region}..."
urls.forEach (url) ->
url = url['#']
name = url.split '/'
name = name[name.length - 1]
path = url.replace "https://sqs.#{region}.amazonaws.com", ''
queue =
Version: '2011-10-01'
AttributeName: ['All']
sqs.setRegion(region).setQueue(path + '/')
.request 'GetQueueAttributes', queue, (error, attributes) ->
if error?
msg.send "Can't read queue attributes [#{name}] (path #{path})" +
" - #{url} - #{error}"
return
info = attributes.GetQueueAttributesResult.Attribute
for index, attr of info
switch attr.Name
when 'ApproximateNumberOfMessages' \
then msgCount = attr.Value
when 'ApproximateNumberOfMessagesNotVisible' \
then inFlight = attr.Value
queue.MaxNumberOfMessages = 1
queue.VisibilityTimeout = 0
sqs.setRegion(region).setQueue(path + '/')
.request 'ReceiveMessage', queue, (error, result) ->
queueDesc = "[SQS: #{name}] - [#{msgCount}] total msgs" +
" / [#{inFlight}] in flight"
if error?
timestamp = "unavailable - #{error}"
else
sqsmsg = result.ReceiveMessageResult.Message
if sqsmsg? and sqsmsg.Attribute?
timestamp = (att for att in sqsmsg.Attribute \
when att.Name == 'SentTimestamp')
timestamp = (moment parseFloat timestamp[0].Value)
.format 'ddd, L LT'
else
timestamp = 'none available'
msg.send "#{queueDesc} / oldest msg ~[#{timestamp}] / #{url}"
defaultRegions = 'us-east-1,us-west-1,us-west-2,eu-west-1,ap-southeast-1,ap-northeast-1,sa-east-1'
module.exports = (robot) ->
robot.respond /(^|\W)sqs status(\z|\W|$)/i, (msg) ->
regions = process.env?.HUBOT_AWS_SQS_REGIONS ? defaultRegions
getRegionQueues region, msg for region in regions.split ','
robot.respond /(^|\W)ec2 status(\z|\W|$)/i, (msg) ->
regions = process.env?.HUBOT_AWS_EC2_REGIONS ? defaultRegions
getRegionInstances region, msg for region in regions.split ','
Jump to Line
Something went wrong with that request. Please try again.