Skip to content
This repository has been archived by the owner on Jan 22, 2019. It is now read-only.

Fix #24: Add an Amazon task to see instances infos #35

Merged
merged 1 commit into from
Jul 10, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .albot.json.template
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,10 @@
},
"changelog": {
"gistId": ""
},
"amazon": {
"key": "",
"secret": "",
"region": ""
}
}
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ You will just need Coffeescript

pulls [ <url> | without <filter> | with <filter> | recent [<unit>] | last [<number> | <filter>]] List all Pull Requests of the organisation
deploy <project> [ | <alias>] [<branch>] Deploy your projects with the configured command
changelog <project> [ | <alias>] <pr> <number> | <since> <number> <period> | <between> <tag-range> ["save"] List changelog for a given PR, period, range
changelog <project> [ | <alias>] <pr> <number> | <since> <number> <period> | <between> <tag-range> [save] List changelog for a given PR, period, range
amazon [ instances [ with <term> ] ] Display various informations about your Amazon infrastructure
help Display a list of available commands
server Start albot to listen on Hipchat instead of the command line

Expand Down Expand Up @@ -146,6 +147,23 @@ Available in all flavour

$ albot changelog webapp between 43..45 save

### Amazon

Display substantial informations about your Amazon infrastructure.
Like the list of your EC2 instances.

$ albot amazon instances

Will display all the instances available on your account.

Interestingly, if you have DNS configured in Route53, this route will be displayed instead of the Public DNS.

If some of your instances are behind an ElasticLoadBalancer, more details will be provided for it.

You can, as always, filter the result list

$ albot amazon instances with live

### Server

Albot can also be use as an Hipchat bot.
Expand Down
24 changes: 0 additions & 24 deletions TODO

This file was deleted.

119 changes: 119 additions & 0 deletions lib/amazon.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
Configuration = require './configuration'
_ = require('underscore')._
_.str = require 'underscore.string'
_.mixin _.str.exports()

Async = require 'async'

Utils = require './utils'
AwsHelpers = require './aws_helpers'

amazon = (fallback, keyword, filter, filterValue) ->

Route53 = new Configuration.Amazon.aws.Route53()
Ec2 = new Configuration.Amazon.aws.EC2()
Elb = new Configuration.Amazon.aws.ELB()

params = {}

if (keyword == 'instances')
AwsHelpers.getAllRecordSets Route53, (err, recordSets) ->
if (not recordSets?)
Utils.fallback_printError fallback, err
else
dnsRoutePairs = mappingDnsWithRoute(recordSets)

findInstancesBehindLoadBalancer Elb, dnsRoutePairs, (err, idInstances) ->
if (not idInstances?)
Utils.fallback_printError fallback, err
else
if (filter == 'with')
display fallback, Ec2, params, dnsRoutePairs, idInstances, filterValue
else
display fallback, Ec2, params, dnsRoutePairs, idInstances, filter


display = (fallback, Ec2, params, dnsRoutePairs, idInstances, filterValue) ->
AwsHelpers.getInstancesByParams Ec2, params, (err, results) ->
list = _.map results, (instance) ->

tag = _.findWhere instance.Tags, {"Key": "Name"}
security = instance.SecurityGroups[0]
role = if instance.IamInstanceProfile? then " / Role: " + instance.IamInstanceProfile.Arn.split('/')[1] else ""
route = _.findWhere dnsRoutePairs, {"Dns": instance.PublicDnsName}
title =
if (route?)
route.Route
else if instance.PublicDnsName
instance.PublicDnsName
else
"No route or public dns"

behinds = _.findWhere idInstances, {"Id": instance.InstanceId}
lb = if behinds? and behinds.LoadBalancer? then " - behind: #{behinds.LoadBalancer.LoadBalancerName}" else ""

{
title: title
#TODO: Should remove the trailing dot
url: "http://#{title}"
infos: if tag? then tag.Value + ' / ' + instance.InstanceType else instance.InstanceType
comments: if security? then "Security: #{security.GroupName}#{role}"
status: instance.State.Name == 'running'
tails: if behinds? then _.map _.pluck(behinds.Routes, 'Route'), (route) -> "via #{route}#{lb}"
}

Utils.fallback_printList fallback, list, (list) ->
if (filterValue?)
_.filter list, (o) ->
stuff = o.title + o.infos + o.comments
stuff += _.reduce o.tails, (memo, tail) ->
memo.concat(tail)
, []

_.str.include(stuff, filterValue)
else
list

mappingDnsWithRoute = (recordSets) ->
_.reduce recordSets, (memo, recordSet) ->
if (not _.isEmpty(recordSet.ResourceRecords))
memo.concat _.map recordSet.ResourceRecords, (record) ->
{ Dns: record.Value, Route: recordSet.Name }
else
memo.concat [
{ Dns: recordSet.AliasTarget.DNSName, Route: recordSet.Name }
]
, []

filterByUrl = (dnsRoutePairs, url) ->
if (url?)
_.filter dnsRoutePairs, (name) -> name.Route.indexOf(url) > -1
else
dnsRoutePairs

findInstancesBehindLoadBalancer = (Elb, routes, callback) ->
names = _.reduce routes, (memo, dnsRoutePair) ->
match = dnsRoutePair.Dns.match("^(.*lb)-")

if match then memo.concat(match[1]) else memo
, []

AwsHelpers.getLoadBalancersByNames Elb, _.uniq(names), (err, lbDescriptions) ->
if (not lbDescriptions?)
callback err
else
Async.reduce lbDescriptions, [], (memo, description, cb) ->
cb null, memo.concat _.map description.Instances, (instance) ->
{
Id: instance.InstanceId,
LoadBalancer: description,
Routes: _.where(routes, {"Dns": description.DNSName + "."})
}
, (err, idInstances) ->
callback null, idInstances

module.exports = {
name: "Amazon",
description: "[ instances [ with -term- ] ] Display various informations about your Amazon infrastructure",
action: amazon
}
71 changes: 71 additions & 0 deletions lib/aws_helpers.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
Configuration = require './configuration'
_ = require('underscore')._

Async = require 'async'

prepareFilters = (name, value, sensitive) ->
if (_.isString(value) or _.isArray(value))
if (sensitive)
value = [
"\*" + value.toUpperCase() + "\*",
"\*" + value.toLowerCase() + "\*"
]
else if (_.isString(value))
value = ["\*" + value + "\*"]

{"Filters": [{
"Name": name,
"Values": value
}]}

#TODO: More than 100 instances
getInstancesByParams = (Ec2, params, callback) ->
Ec2.describeInstances params, (err, data) ->
if (not data?)
callback(err)
else
callback null, _.reduceRight data.Reservations, (memo, reservation) ->
memo.concat(reservation.Instances)
, []

getAllRecordSets = (Route53, callback) ->
Route53.listHostedZones {}, (err, hostedZones) ->
if (not hostedZones?)
callback(err)
else
Async.concat hostedZones.HostedZones, (hz, cb) ->
getAllRecordSetsAcc Route53, hz, [], null, null, cb
, callback

getAllRecordSetsAcc = (Route53, hz, acc, recordName, recordType, callback) ->
if (recordName? and recordType?)
params = {"HostedZoneId": hz.Id, "StartRecordName": recordName, "StartRecordType": recordType}
else
params = {"HostedZoneId": hz.Id}

Route53.listResourceRecordSets params, (err, records) ->
if (not records?)
callback(err, [])
else
all = acc.concat records.ResourceRecordSets

if (records.IsTruncated)
getAllRecordSetsAcc(Route53, hz, all, records.NextRecordName, records.NextRecordType, callback)
else
callback(null, all)

getLoadBalancersByNames = (Elb, names, callback) ->
params = {"LoadBalancerNames": names}

Elb.describeLoadBalancers params, (err, loadBalancers) ->
if (not loadBalancers?)
callback err
else
callback null, loadBalancers.LoadBalancerDescriptions

module.exports = {
prepareFilters: prepareFilters,
getInstancesByParams: getInstancesByParams,
getAllRecordSets: getAllRecordSets,
getLoadBalancersByNames: getLoadBalancersByNames
}
2 changes: 1 addition & 1 deletion lib/changelog.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,6 @@ module.exports = {
name: "Changelog",
description: "-project- [ | -alias-] -pr- -number-
| -since- -number- -period- | -between- -tag-range-
[\"save\"] List changelog for a given PR, period, range ",
[save] List changelog for a given PR, period, range ",
action: changelog
}
1 change: 1 addition & 0 deletions lib/commands.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ list = {
pulls: require('./pulls'),
deploy: require('./deploy'),
changelog: require('./changelog'),
amazon: require('./amazon'),
help: {
name: "Help"
description: "Display a list of available commands",
Expand Down
29 changes: 28 additions & 1 deletion lib/configuration.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Path = require 'path'
HipchatApi = require 'hipchat'
GithubApi = require 'github'
Winston = require 'winston'
AWS = require 'aws-sdk'

userHome = () ->
process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE
Expand Down Expand Up @@ -35,6 +36,11 @@ Nconf
"deploy": {
"args": [],
"env": []
},
"amazon": {
"key": "",
"secret": "",
"region": ""
}
}

Expand All @@ -55,6 +61,23 @@ initLogger =

@logger

initAws =
(aws) ->
if (aws?)
@aws = aws
else
credentials =
{
accessKeyId: Nconf.get("amazon").key,
secretAccessKey: Nconf.get("amazon").secret,
region: Nconf.get("amazon").region
}

AWS.config.update credentials
@aws = AWS

@aws

module.exports =
Nconf: Nconf,
Nickname: Nconf.get('nickname'),
Expand All @@ -69,8 +92,12 @@ module.exports =
Channel: Nconf.get('hipchat').channel,
Frequency: Nconf.get('hipchat').frequency
},
Amazon: {
initAws: initAws,
aws: @aws || initAws()
},
Winston: {
initLogger: initLogger,
logger: @logger || initLogger
logger: @logger || initLogger()
},
Version: JSON.parse(Fs.readFileSync(Path.resolve(__dirname, '../package.json'), 'utf8')).version
4 changes: 2 additions & 2 deletions lib/server.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ server = (frequency, testCallback) ->
freq = if _.isString(frequency) then frequency else Hipchat.Frequency

Hipchat.Rooms.history Hipchat.Channel, (error, lines) ->
if (error?) then console.log("An error occured while fetching history: #{JSON.stringify(error)}")
if (error?) then Winston.logger.error("An error occured while fetching history: #{JSON.stringify(error)}")
else if(lines)
Cache.store(lines.messages)

intervalId = setInterval () ->
Hipchat.Rooms.history Hipchat.Channel, (error, lines) ->
if (error?) then console.log("An error occured while fetching history: #{JSON.stringify(error)}")
if (error?) then Winston.logger.error("An error occured while fetching history: #{JSON.stringify(error)}")
else if (lines)
Async.each lines.messages, (line, cb) ->
if (not Cache.cached(line))
Expand Down
Loading