sport_ngin_aws_auditor 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/.octopolo.yml +4 -0
  4. data/.rspec +2 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.soyuz.yml +13 -0
  8. data/.travis.yml +13 -0
  9. data/CHANGELOG.markdown +30 -0
  10. data/Gemfile +4 -0
  11. data/LICENSE.txt +22 -0
  12. data/README.md +103 -0
  13. data/Rakefile +2 -0
  14. data/bin/sport-ngin-aws-auditor +27 -0
  15. data/lib/sport_ngin_aws_auditor/aws.rb +44 -0
  16. data/lib/sport_ngin_aws_auditor/cache_instance.rb +69 -0
  17. data/lib/sport_ngin_aws_auditor/commands/audit.rb +17 -0
  18. data/lib/sport_ngin_aws_auditor/commands/export.rb +11 -0
  19. data/lib/sport_ngin_aws_auditor/commands/inspect.rb +12 -0
  20. data/lib/sport_ngin_aws_auditor/config.rb +51 -0
  21. data/lib/sport_ngin_aws_auditor/convenience_wrappers.rb +59 -0
  22. data/lib/sport_ngin_aws_auditor/ec2_instance.rb +119 -0
  23. data/lib/sport_ngin_aws_auditor/google.rb +62 -0
  24. data/lib/sport_ngin_aws_auditor/google_sheet.rb +80 -0
  25. data/lib/sport_ngin_aws_auditor/instance_helper.rb +71 -0
  26. data/lib/sport_ngin_aws_auditor/notify_slack.rb +31 -0
  27. data/lib/sport_ngin_aws_auditor/output.rb +13 -0
  28. data/lib/sport_ngin_aws_auditor/rds_instance.rb +78 -0
  29. data/lib/sport_ngin_aws_auditor/scripts/audit.rb +124 -0
  30. data/lib/sport_ngin_aws_auditor/scripts/export.rb +146 -0
  31. data/lib/sport_ngin_aws_auditor/scripts/inspect.rb +44 -0
  32. data/lib/sport_ngin_aws_auditor/stack.rb +63 -0
  33. data/lib/sport_ngin_aws_auditor/version.rb +3 -0
  34. data/lib/sport_ngin_aws_auditor.rb +14 -0
  35. data/spec/spec_helper.rb +45 -0
  36. data/spec/sport_ngin_aws_auditor/aws_spec.rb +48 -0
  37. data/spec/sport_ngin_aws_auditor/cache_instance_spec.rb +125 -0
  38. data/spec/sport_ngin_aws_auditor/config_spec.rb +44 -0
  39. data/spec/sport_ngin_aws_auditor/ec2_instance_spec.rb +181 -0
  40. data/spec/sport_ngin_aws_auditor/notify_slack_spec.rb +33 -0
  41. data/spec/sport_ngin_aws_auditor/rds_instance_spec.rb +140 -0
  42. data/sport_ngin_aws_auditor.gemspec +33 -0
  43. metadata +251 -0
@@ -0,0 +1,62 @@
1
+ require "google/api_client"
2
+ require "google_drive"
3
+
4
+ module SportNginAwsAuditor
5
+ class GoogleConfig < Hash
6
+ include Hashie::Extensions::IndifferentAccess
7
+ end
8
+
9
+ class Google
10
+ FILE_NAMES = %w[.google.yml]
11
+
12
+ def self.configuration
13
+ GoogleDrive.login_with_oauth(get_authorization)
14
+ end
15
+
16
+ def self.get_authorization
17
+ creds = load_config[:credentials]
18
+ client = ::Google::APIClient.new
19
+ auth = client.authorization
20
+ auth.client_id = creds[:client_id]
21
+ auth.client_secret = creds[:client_secret]
22
+ auth.scope =
23
+ "https://www.googleapis.com/auth/drive " +
24
+ "https://docs.google.com/feeds/ " +
25
+ "https://docs.googleusercontent.com/ " +
26
+ "https://spreadsheets.google.com/feeds/"
27
+ auth.redirect_uri = "urn:ietf:wg:oauth:2.0:oob"
28
+ print("1. If it doesn't automatically open, open this page:\n%s\n\n" % auth.authorization_uri)
29
+ `open "#{auth.authorization_uri}"`
30
+ print("2. Enter the authorization code shown in the page: ")
31
+ auth.code = $stdin.gets.chomp
32
+ auth.fetch_access_token!
33
+ access_token = auth.access_token
34
+ end
35
+
36
+ def self.file
37
+ load_config[:file]
38
+ end
39
+
40
+ def self.load_config
41
+ return @config if @config
42
+ @config = GoogleConfig[YAML.load_file(config_path)]
43
+ end
44
+
45
+ def self.config_path
46
+ if filepath = FILE_NAMES.detect {|filename| File.exists?(filename)}
47
+ File.join(Dir.pwd, filepath)
48
+ else
49
+ old_dir = Dir.pwd
50
+ Dir.chdir('..')
51
+ if old_dir != Dir.pwd
52
+ config_path
53
+ else
54
+ puts "Could not find #{FILE_NAMES.join(' or ')}"
55
+ exit
56
+ end
57
+ end
58
+ end
59
+
60
+ end
61
+ end
62
+
@@ -0,0 +1,80 @@
1
+ require 'google_drive'
2
+
3
+ module SportNginAwsAuditor
4
+ class GoogleSheet
5
+ extend GoogleWrapper
6
+
7
+ attr_accessor :sheet, :worksheet, :path
8
+ def initialize(title, path, environment)
9
+ @sheet = self.class.create_sheet(title, path)
10
+ @worksheet = self.class.worksheet(sheet, environment)
11
+ end
12
+
13
+ def write_header(header)
14
+ worksheet.list.keys = header.unshift('name')
15
+ worksheet.save
16
+ end
17
+
18
+ def write_row(value_hash)
19
+ worksheet.list.push(value_hash)
20
+ worksheet.save
21
+ end
22
+
23
+ def self.first_or_create(title)
24
+ spreadsheet = google.root_collection.files("title" => title, "title-exact" => true).first
25
+ spreadsheet ? spreadsheet : google.create_spreadsheet(title)
26
+ end
27
+
28
+ #returns a spreadsheet object
29
+ def self.create_sheet(title, path)
30
+ folder = go_to_collection(path) if path
31
+ if folder
32
+ spreadsheet = folder.files("title" => title, "title-exact" => true).first
33
+ if spreadsheet
34
+ return spreadsheet
35
+ else
36
+ file = first_or_create(title)
37
+ folder.add(file)
38
+ google.root_collection.remove(file)
39
+ return folder.files("title" => title, "title-exact" => true).first
40
+ end
41
+ else
42
+ first_or_create(title)
43
+ end
44
+ end
45
+
46
+ #returns a worksheet object
47
+ def self.worksheet(spreadsheet, title)
48
+ worksheet = spreadsheet.worksheet_by_title(title)
49
+ worksheet ? delete_all_rows(worksheet) : spreadsheet.add_worksheet(title)
50
+ end
51
+
52
+ #returns a collection object
53
+ def self.go_to_collection(directory)
54
+ if directory
55
+ path = directory.split('/')
56
+ go_to_subcollection(google.collection_by_title(path.first),path[1..-1])
57
+ end
58
+ end
59
+
60
+ #returns a collection object
61
+ def self.go_to_subcollection(base, subs)
62
+ puts "Folder doesn't exist in specified path" and exit if base.nil?
63
+ if subs.empty?
64
+ return base
65
+ else
66
+ base = base.subcollection_by_title(subs.first)
67
+ go_to_subcollection(base,subs[1..-1])
68
+ end
69
+ end
70
+
71
+ def self.delete_all_rows(worksheet)
72
+ worksheet.list.each do |row|
73
+ row.clear
74
+ end
75
+ worksheet.save
76
+ worksheet
77
+ end
78
+
79
+ end
80
+ end
@@ -0,0 +1,71 @@
1
+ module SportNginAwsAuditor
2
+ module InstanceHelper
3
+
4
+ def instance_hash
5
+ Hash[get_instances.map { |instance| instance.nil? ? next : [instance.id, instance]}.compact]
6
+ end
7
+
8
+ def reserved_instance_hash
9
+ Hash[get_reserved_instances.map { |instance| instance.nil? ? next : [instance.id, instance]}.compact]
10
+ end
11
+
12
+ def instance_count_hash(instances)
13
+ instance_hash = Hash.new()
14
+ instances.each do |instance|
15
+ next if instance.nil?
16
+ instance_hash[instance.to_s] = instance_hash.has_key?(instance.to_s) ? instance_hash[instance.to_s] + instance.count : instance.count
17
+ end if instances
18
+ instance_hash
19
+ end
20
+
21
+ def add_instances_with_tag_to_hash(instances_to_add, instance_hash)
22
+ instances_to_add.each do |instance|
23
+ next if instance.nil?
24
+ key = instance.to_s << " with tag"
25
+ instance_hash[key] = instance_hash.has_key?(key) ? instance_hash[key] + 1 : 1
26
+ end if instances_to_add
27
+ instance_hash
28
+ end
29
+
30
+ def compare(tag_name)
31
+ differences = Hash.new()
32
+ instances = get_instances(tag_name)
33
+ instances_with_tag = filter_instances_with_tags(instances)
34
+ instances_without_tag = filter_instance_without_tags(instances)
35
+ instance_hash = instance_count_hash(instances_without_tag)
36
+ ris = instance_count_hash(get_reserved_instances)
37
+ instance_hash.keys.concat(ris.keys).uniq.each do |key|
38
+ instance_count = instance_hash.has_key?(key) ? instance_hash[key] : 0
39
+ ris_count = ris.has_key?(key) ? ris[key] : 0
40
+ differences[key] = ris_count - instance_count
41
+ end
42
+ add_instances_with_tag_to_hash(instances_with_tag, differences)
43
+ differences
44
+ end
45
+
46
+ # assuming the value of the tag is in the form: 01/01/2000 like a date
47
+ def filter_instances_with_tags(instances)
48
+ instances.select do |instance|
49
+ value = gather_instance_tag_date(instance)
50
+ value && (Date.today.to_s < value.to_s)
51
+ end
52
+ end
53
+
54
+ # assuming the value of the tag is in the form: 01/01/2000 like a date
55
+ def filter_instance_without_tags(instances)
56
+ instances.select do |instance|
57
+ value = gather_instance_tag_date(instance)
58
+ value.nil? || (Date.today.to_s >= value.to_s)
59
+ end
60
+ end
61
+
62
+ def gather_instance_tag_date(instance)
63
+ value = instance.no_reserved_instance_tag_value
64
+ unless value.nil?
65
+ date_hash = Date._strptime(value, '%m/%d/%Y')
66
+ value = Date.new(date_hash[:year], date_hash[:mon], date_hash[:mday]) if date_hash
67
+ end
68
+ value
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,31 @@
1
+ require 'slack-notifier'
2
+
3
+ module SportNginAwsAuditor
4
+ class NotifySlack
5
+ attr_accessor :text, :channel, :webhook, :username, :icon_url, :icon_emoji
6
+
7
+ def initialize(text)
8
+ self.text = text
9
+ if SportNginAwsAuditor::Config.slack
10
+ self.channel = SportNginAwsAuditor::Config.slack[:channel]
11
+ self.username = SportNginAwsAuditor::Config.slack[:username]
12
+ self.webhook = SportNginAwsAuditor::Config.slack[:webhook]
13
+ self.icon_url = SportNginAwsAuditor::Config.slack[:icon_url]
14
+ else
15
+ puts "To use Slack, you must provide a separate config file. See the README for more information."
16
+ end
17
+ end
18
+
19
+ def perform
20
+ if SportNginAwsAuditor::Config.slack
21
+ options = {webhook: webhook,
22
+ channel: channel,
23
+ username: username,
24
+ icon_url: icon_url,
25
+ http_options: {open_timeout: 10}
26
+ }
27
+ Slack::Notifier.new(webhook, options).ping(text)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,13 @@
1
+ require 'highline'
2
+
3
+ module SportNginAwsAuditor
4
+ module Output
5
+ def self.terminal
6
+ @terminal ||= HighLine.new
7
+ end
8
+
9
+ def self.ask(*args, &block)
10
+ terminal.ask(*args, &block)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,78 @@
1
+ require_relative './instance_helper'
2
+
3
+ module SportNginAwsAuditor
4
+ class RDSInstance
5
+ extend InstanceHelper
6
+ extend RDSWrapper
7
+ extend AWSWrapper
8
+
9
+ class << self
10
+ attr_accessor :instances, :reserved_instances
11
+ end
12
+
13
+ attr_accessor :id, :name, :multi_az, :instance_type, :engine, :count, :tag_value
14
+ def initialize(rds_instance, account_id=nil, tag_name=nil, rds=nil)
15
+ if rds_instance.class.to_s == "Aws::RDS::Types::ReservedDBInstance"
16
+ self.id = rds_instance.reserved_db_instances_offering_id
17
+ self.multi_az = rds_instance.multi_az ? "Multi-AZ" : "Single-AZ"
18
+ self.instance_type = rds_instance.db_instance_class
19
+ self.engine = rds_instance.product_description
20
+ self.count = 1
21
+ elsif rds_instance.class.to_s == "Aws::RDS::Types::DBInstance"
22
+ self.id = rds_instance.db_instance_identifier
23
+ self.multi_az = rds_instance.multi_az ? "Multi-AZ" : "Single-AZ"
24
+ self.instance_type = rds_instance.db_instance_class
25
+ self.engine = rds_instance.engine
26
+ self.count = 1
27
+
28
+ if tag_name
29
+ region = rds_instance.availability_zone.split(//).first(9).join
30
+ region = "us-east-1" if region == "Multiple"
31
+ arn = "arn:aws:rds:#{region}:#{account_id}:db:#{self.id}"
32
+
33
+ # go through to see if the tag we're looking for is one of them
34
+ rds.list_tags_for_resource(resource_name: arn).tag_list.each do |tag|
35
+ if tag.key == tag_name
36
+ self.tag_value = tag.value
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ def to_s
44
+ "#{engine_helper} #{multi_az} #{instance_type}"
45
+ end
46
+
47
+ def self.get_instances(tag_name=nil)
48
+ return @instances if @instances
49
+ account_id = get_account_id
50
+ @instances = rds.describe_db_instances.db_instances.map do |instance|
51
+ next unless instance.db_instance_status.to_s == 'available'
52
+ new(instance, account_id, tag_name, rds)
53
+ end.compact
54
+ end
55
+
56
+ def self.get_reserved_instances
57
+ return @reserved_instances if @reserved_instances
58
+ @reserved_instances = rds.describe_reserved_db_instances.reserved_db_instances.map do |instance|
59
+ next unless instance.state.to_s == 'active'
60
+ new(instance)
61
+ end.compact
62
+ end
63
+
64
+ def no_reserved_instance_tag_value
65
+ @tag_value
66
+ end
67
+
68
+ def engine_helper
69
+ if engine.downcase.include? "post"
70
+ return "PostgreSQL"
71
+ elsif engine.downcase.include? "mysql"
72
+ return "MySQL"
73
+ end
74
+ end
75
+ private :engine_helper
76
+
77
+ end
78
+ end
@@ -0,0 +1,124 @@
1
+ require 'highline/import'
2
+ require_relative "../notify_slack"
3
+
4
+ module SportNginAwsAuditor
5
+ module Scripts
6
+ class Audit
7
+ extend AWSWrapper
8
+
9
+ class << self
10
+ attr_accessor :options
11
+ end
12
+
13
+ def self.execute(environment, options=nil)
14
+ aws(environment)
15
+ @options = options
16
+ slack = options[:slack]
17
+ no_selection = !(options[:ec2] || options[:rds] || options[:cache])
18
+
19
+ if options[:no_tag]
20
+ tag_name = nil
21
+ else
22
+ tag_name = options[:tag]
23
+ end
24
+
25
+ if !slack
26
+ print "Gathering info, please wait..."; print "\r"
27
+ else
28
+ puts "Condensed results from this audit will print into Slack instead of directly to an output."
29
+ end
30
+
31
+ data = gather_data("EC2Instance", tag_name) if options[:ec2] || no_selection
32
+ print_data(slack, environment, data, "EC2Instance") if options[:ec2] || no_selection
33
+
34
+ data = gather_data("RDSInstance", tag_name) if options[:rds] || no_selection
35
+ print_data(slack, environment, data, "RDSInstance") if options[:ec2] || no_selection
36
+
37
+ data = gather_data("CacheInstance", tag_name) if options[:cache] || no_selection
38
+ print_data(slack, environment, data, "CacheInstance") if options[:ec2] || no_selection
39
+ end
40
+
41
+ def self.gather_data(class_type, tag_name)
42
+ klass = SportNginAwsAuditor.const_get(class_type)
43
+
44
+ if options[:instances]
45
+ instances = klass.get_instances(tag_name)
46
+ instances_with_tag = klass.filter_instances_with_tags(instances)
47
+ instances_without_tag = klass.filter_instance_without_tags(instances)
48
+ instance_hash = klass.instance_count_hash(instances_without_tag)
49
+ klass.add_instances_with_tag_to_hash(instances_with_tag, instance_hash)
50
+ return instance_hash
51
+ elsif options[:reserved]
52
+ return klass.instance_count_hash(klass.get_reserved_instances)
53
+ else
54
+ return klass.compare(tag_name)
55
+ end
56
+ end
57
+
58
+ def self.print_data(slack, environment, data, class_type)
59
+ if slack
60
+ print_to_slack(data, class_type, environment)
61
+ elsif options[:reserved] || options[:instances]
62
+ puts header(class_type)
63
+ data.each{ |key, value| say "<%= color('#{key}: #{value}', :white) %>" }
64
+ else
65
+ puts header(class_type)
66
+ data.each{ |key, value| colorize(key, value) }
67
+ end
68
+ end
69
+
70
+ def self.colorize(key, value)
71
+ if key.include?(" with tag")
72
+ k = key.dup # because key is a frozen string right now
73
+ k.slice!(" with tag")
74
+ say "<%= color('#{k}: #{"*" << value.to_s}', :blue) %>"
75
+ elsif value < 0
76
+ say "<%= color('#{key}: #{value}', :yellow) %>"
77
+ elsif value == 0
78
+ say "<%= color('#{key}: #{value}', :green) %>"
79
+ elsif value > 0
80
+ say "<%= color('#{key}: #{value}', :red) %>"
81
+ end
82
+ end
83
+
84
+ def self.print_to_slack(instances_hash, class_type, environment)
85
+ discrepancy_hash = Hash.new
86
+ instances_hash.each do |key, value|
87
+ if !(value == 0) && !(key.include?(" with tag"))
88
+ discrepancy_hash[key] = value
89
+ end
90
+ end
91
+
92
+ if discrepancy_hash.empty?
93
+ slack_job = NotifySlack.new("All #{class_type} instances for #{environment} are up to date.")
94
+ slack_job.perform
95
+ else
96
+ print_discrepancies(discrepancy_hash, class_type, environment)
97
+ end
98
+ end
99
+
100
+ def self.print_discrepancies(discrepancy_hash, class_type, environment)
101
+ to_print = "Some #{class_type} instances for #{environment} are out of sync:\n"
102
+ to_print << "#{header(class_type)}\n"
103
+
104
+ discrepancy_hash.each do |key, value|
105
+ to_print << "#{key}: #{value}\n"
106
+ end
107
+
108
+ slack_job = NotifySlack.new(to_print)
109
+ slack_job.perform
110
+ end
111
+
112
+ def self.header(type, length = 50)
113
+ type.upcase!.slice! "INSTANCE"
114
+ half_length = (length - type.length)/2.0 - 1
115
+ [
116
+ "*" * length,
117
+ "*" * half_length.floor + " #{type} " + "*" * half_length.ceil,
118
+ "*" * length
119
+ ].join("\n")
120
+ end
121
+
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,146 @@
1
+ require 'csv'
2
+ require_relative "../google"
3
+
4
+ module SportNginAwsAuditor
5
+ module Scripts
6
+ class Export
7
+ extend GoogleWrapper
8
+ extend AWSWrapper
9
+
10
+ class << self
11
+ attr_accessor :ec2_instances, :rds_instances, :cache_instances, :options, :file, :keys_hash, :environment
12
+ end
13
+
14
+ CLASS_TYPES = %w[EC2Instance RDSInstance CacheInstance]
15
+
16
+ def self.execute(environment, options = nil)
17
+ @environment = environment
18
+ (puts "Must specify either --drive or --csv"; exit) unless options[:csv] || options[:drive]
19
+ aws(environment)
20
+ print "Gathering info, please wait..."
21
+ all_keys = get_all_keys
22
+ all_info = prepare
23
+ print "\r" + " " * 50 + "\r"
24
+
25
+ create_csv(all_keys,all_info) if options[:csv]
26
+ upload_to_drive(all_keys,all_info) if options[:drive]
27
+ end
28
+
29
+ def self.create_csv(keys, info)
30
+ CSV.open("#{environment}.csv", "wb") do |csv|
31
+ csv << ["name",keys].flatten
32
+ info.each do |hash|
33
+ csv << all_keys_hash.merge(hash).values
34
+ end
35
+ end
36
+
37
+ `open "#{environment}.csv"`
38
+ end
39
+
40
+ def self.upload_to_drive(keys, info)
41
+ @file = GoogleSheet.new(Google.file[:name], Google.file[:path], environment)
42
+ print "Exporting to Google Drive, please wait..."
43
+ file.write_header(keys)
44
+ info.each do |value_hash|
45
+ response = file.worksheet.list.push(value_hash)
46
+ puts response unless response.is_a? GoogleDrive::ListRow
47
+ end
48
+ file.worksheet.save
49
+ print "\r" + " " * 50 + "\r"
50
+ puts "Exporting Complete."
51
+ `open #{file.sheet.human_url}`
52
+ end
53
+
54
+ def self.prepare
55
+ [get_all_arrays,get_all_counts].flatten
56
+ end
57
+
58
+ def self.get_all_keys
59
+ return @keys if @keys
60
+ @keys = [
61
+ [ec2_reserved_instances.values,ec2_instances.values].flatten.map{ |x| x.to_s }.uniq.sort! { |a,b| a.downcase <=> b.downcase },
62
+ [rds_reserved_instances.values,rds_instances.values].flatten.map{ |x| x.to_s }.uniq.sort! { |a,b| a.downcase <=> b.downcase },
63
+ [cache_reserved_instances.values,cache_instances.values].flatten.map{ |x| x.to_s }.uniq.sort! { |a,b| a.downcase <=> b.downcase }
64
+ ].flatten
65
+ end
66
+
67
+ def self.all_keys_hash(name = nil, value = nil)
68
+ return @keys_hash if @keys_hash && @keys_hash[:name] == name
69
+ @keys_hash = {:name => name}
70
+ get_all_keys.each{ |key| @keys_hash[key] = value }
71
+ @keys_hash
72
+ end
73
+
74
+ def self.get_all_arrays
75
+ return @all_array if @all_array
76
+ @all_array = [ec2_array,rds_array,cache_array].flatten
77
+ end
78
+
79
+ def self.ec2_array
80
+ instance_array = [{name: "OPSWORKS"}]
81
+ EC2Instance.bucketize.map do |stack_name, stack_instances|
82
+ instance_array << {:name => stack_name}.merge(EC2Instance.instance_count_hash(stack_instances))
83
+ end
84
+ instance_array
85
+ end
86
+
87
+ def self.rds_array
88
+ instance_array = [{name: "RDS"}]
89
+ rds_instances.each do |db_name, db|
90
+ instance_array << Hash({:name => db_name, "#{db.to_s}" => "#{db.count}"})
91
+ end
92
+ instance_array
93
+ end
94
+
95
+ def self.cache_array
96
+ instance_array = [{name: "CACHE"}]
97
+ cache_instances.each do |cache_name, cache|
98
+ instance_array << Hash({:name => cache_name, "#{cache.to_s}" => "#{cache.count}"})
99
+ end
100
+ instance_array
101
+ end
102
+
103
+ def self.get_all_counts
104
+ total_array = [{:name => "TOTALS"}]
105
+ total_array << all_keys_hash("Running Instances").merge(counts(:instance => true))
106
+ total_array << all_keys_hash("Reserved Instances", 0).merge(counts(:reserved => true))
107
+ total_array << all_keys_hash("Differences").merge(counts(:compare => true))
108
+ end
109
+
110
+ def self.counts(options = {:instance => false, :reserved => false, :compare => false })
111
+ CLASS_TYPES.map do |class_type|
112
+ klass = SportNginAwsAuditor.const_get(class_type)
113
+ instances = klass.instance_count_hash(klass.get_instances) if options[:instance]
114
+ instances = klass.instance_count_hash(klass.get_reserved_instances) if options[:reserved]
115
+ instances = klass.compare if options[:compare]
116
+ instances
117
+ end.inject(:merge)
118
+ end
119
+
120
+ def self.ec2_instances
121
+ @ec2_instances ||= EC2Instance.instance_hash
122
+ end
123
+
124
+ def self.ec2_reserved_instances
125
+ @ec2_reserved_instances ||= EC2Instance.reserved_instance_hash
126
+ end
127
+
128
+ def self.rds_instances
129
+ @rds_instances ||= RDSInstance.instance_hash
130
+ end
131
+
132
+ def self.rds_reserved_instances
133
+ @rds_reserved_instances ||= RDSInstance.reserved_instance_hash
134
+ end
135
+
136
+ def self.cache_instances
137
+ @cache_instances ||= CacheInstance.instance_hash
138
+ end
139
+
140
+ def self.cache_reserved_instances
141
+ @cache_reserved_instances ||= CacheInstance.reserved_instance_hash
142
+ end
143
+
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,44 @@
1
+ module SportNginAwsAuditor
2
+ module Scripts
3
+ class Inspect
4
+ extend AWSWrapper
5
+ extend OpsWorksWrapper
6
+
7
+ def self.execute(environment, options=nil)
8
+ aws(environment)
9
+ no_selection = options.values.uniq == [false]
10
+ output("EC2Instance") if options[:ec2] || no_selection
11
+ output("RDSInstance") if options[:rds] || no_selection
12
+ output("CacheInstance") if options[:cache] || no_selection
13
+ end
14
+
15
+ def self.output(class_type)
16
+ klass = SportNginAwsAuditor.const_get(class_type)
17
+ print "Gathering info, please wait..."; print "\r"
18
+ instances = class_type == "EC2Instance" ? klass.bucketize : klass.instance_hash
19
+ say "<%= color('#{header(class_type)}', :white) %>"
20
+ instances.each do |key, value|
21
+ pretty_print(key, klass.instance_count_hash(Array(value)))
22
+ end
23
+ end
24
+
25
+ def self.header(type, length = 50)
26
+ type.upcase!.slice! "INSTANCE"
27
+ half_length = (length - type.length)/2.0 - 1
28
+ [
29
+ "*" * length,
30
+ "*" * half_length.floor + " #{type} " + "*" * half_length.ceil,
31
+ "*" * length
32
+ ].join("\n")
33
+ end
34
+
35
+ def self.pretty_print(title, body)
36
+ puts "======================================="
37
+ puts "#{title}"
38
+ puts "======================================="
39
+ body.each{ |key, value| say "<%= color('#{key}: #{value}', :white) %>" }
40
+ puts "\n"
41
+ end
42
+ end
43
+ end
44
+ end