Class: App::AWSReports
- Inherits:
-
Object
- Object
- App::AWSReports
- Defined in:
- lib/aws/aws_reports.rb
Constant Summary collapse
- CONST_ALL =
'all'
- CONST_GLOBAL =
'Global'
- KEY_CLI =
'cli'
- KEY_COLUMNS =
'columns'
- KEY_COMMAND =
'command'
- KEY_EXPORT =
'export'
- KEY_REGION =
'region'
- KEY_REGIONS =
'regions'
- KEY_REGIONS_PREFERRED =
'regionsPreferred'
- YML_FILE =
'reports.yml'
Class Method Summary collapse
-
.get_aws_data(regions, resource, resource_title, silent: false) ⇒ Object
This runs the AWS GET and does various things depending on the the flags passed.
-
.get_lookups(data) ⇒ Object
Returns a Hash with all the resources that can be auto-fetched using a script.
-
.parse_metadata(regions) ⇒ Object
Parses the YML file and returns a bunch of Hashes.
-
.parse_results_for_prompt(resource, export_id, results) ⇒ Object
Takes AWS API results and translates it to an array of key/value pairs we can pass to Blufin::Terminal::prompt_select().
Class Method Details
.get_aws_data(regions, resource, resource_title, silent: false) ⇒ Object
This runs the AWS GET and does various things depending on the the flags passed.
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/aws/aws_reports.rb', line 119 def self.get_aws_data(regions, resource, resource_title, silent: false) response = {} results = [] threads = [] semaphore = Mutex.new regions = resource.has_key?(App::AWSReports::KEY_REGIONS) ? resource[App::AWSReports::KEY_REGIONS] : regions puts unless silent regions.each do |region| response[region] = {} if response[region].nil? sleep(0.01) unless silent threads << Thread.new { cmd = "aws #{resource[App::AWSReports::KEY_CLI][App::AWSReports::KEY_COMMAND]}#{region == App::AWSReports::CONST_GLOBAL ? '' : " --region #{region}"}#{App::AWS::get_profile_for_cli}" App::AWSOutputter::output_cli_command(cmd) unless silent json = `#{cmd}` begin semaphore.synchronize do if json.strip == '' response[region] = [] else response[region] = JSON.parse(json) end end rescue => e puts json.inspect raise RuntimeError, "JSON parsing (for: #{resource_title}) failed:\n\n#{e.}" end } end sleep(0.1) unless silent puts unless silent # Display spinner while waiting for threads to finish. Blufin::Terminal::execute_proc("AWS \xe2\x80\x94 Fetching: #{Blufin::Terminal::format_highlight(resource_title, false)}", Proc.new { threads.each { |thread| thread.join } }, verbose: !silent) puts unless silent # Extract the regional response(s) (from multi-calls to different regions) and aggregate into a single Array. root_keys = resource[App::AWSReports::KEY_CLI]['root'] if resource[App::AWSReports::KEY_REGIONS].length == 1 && resource[App::AWSReports::KEY_REGIONS][0] == App::AWSReports::CONST_GLOBAL results = recursively_get_results(App::AWSReports::CONST_GLOBAL, response[App::AWSReports::CONST_GLOBAL], root_keys) else response.each do |region, regional_response| recursively_get_results(region, regional_response, root_keys).each do |regional_result| results << regional_result end end end results end |
.get_lookups(data) ⇒ Object
Returns a Hash with all the resources that can be auto-fetched using a script.
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/aws/aws_reports.rb', line 173 def self.get_lookups(data) auto_fetch_resources = {} data.each do |resource| if resource[1].has_key?(App::AWSReports::KEY_EXPORT) resource[1][App::AWSReports::KEY_EXPORT].each do |export| if export.has_key?('id') auto_fetch_resources[export['id']] = { :resource_title => resource[0], :resource => resource[1] } end end end end auto_fetch_resources end |
.parse_metadata(regions) ⇒ Object
Parses the YML file and returns a bunch of Hashes. Throws straight-up runtime errors if something is wrong.
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/aws/aws_reports.rb', line 19 def self.(regions) yml_file = "#{App::Opt::get_base_path}#{App::Opt::OPT_PATH}/awx/#{YML_FILE}" # Throw error if the aws-report.yml isn't found in the /opt folder. Blufin::Terminal::error("File not found: #{Blufin::Terminal::format_directory(yml_file)}", 'This file should be located in the /opt folder of the my (ruby-gem) source-code.', true) unless Blufin::Files::file_exists(yml_file) columns = {} table_widths = {} export_map = {} begin data = YAML.load_file(File.(yml_file)) rescue => e Blufin::Terminal::error("Unable to parse #{Blufin::Terminal::format_action('YML')} file: #{Blufin::Terminal::format_directory(yml_file)}", e., true) end data.each do |resource, data| # Validates keys (generically). { KEY_REGIONS => [], KEY_REGIONS_PREFERRED => [], KEY_CLI => %w(command root) }.each do |required_key, required_nested_keys| raise RuntimeError, "Missing key: #{required_key} for \xe2\x86\x92 #{resource}" unless data.has_key?(required_key) required_nested_keys.each do |required_nested_key| raise RuntimeError, "Missing key: #{required_key}.#{required_nested_key} for \xe2\x86\x92 #{resource}" unless data[required_key].has_key?(required_nested_key) end end # Validate regions. validate_region_array(regions, data[KEY_REGIONS], resource) if data.has_key?(KEY_REGIONS) validate_region_array(regions, data[KEY_REGIONS_PREFERRED], resource) if data.has_key?(KEY_REGIONS_PREFERRED) # Validate (and parse) columns. if !data[KEY_COLUMNS].nil? && data[KEY_COLUMNS].any? if data.has_key?(KEY_COLUMNS) && data[KEY_COLUMNS].is_a?(Array) columns[resource] = {} unless columns.has_key?(resource) width_wildcard_count = 0 width_total = App::AWSOutputter::REGION_WIDTH # Start with 15 to account for hard-coded region column. columns[resource]['Region'] = { :key => 'region', :width => App::AWSOutputter::REGION_WIDTH, :formatter => 'region' } data[KEY_COLUMNS].each_with_index do |column, idx| title = column['title'] key = column['key'] width = column['width'] color = column['color'].nil? ? 'default' : column['color'] formatter = column['formatter'] ['Region'].each do |reserved_column| raise RuntimeError, "#{resource}.#{KEY_COLUMNS}[#{idx}] \xe2\x86\x92 #{reserved_column} is a reserved column." if title.downcase == reserved_column.downcase end raise RuntimeError, "#{resource}.#{KEY_COLUMNS}[#{idx}] \xe2\x86\x92 Missing: title" if title.nil? raise RuntimeError, "#{resource}.#{KEY_COLUMNS}[#{idx}] \xe2\x86\x92 Missing: key" if key.nil? raise RuntimeError, "#{resource}.#{KEY_COLUMNS}[#{idx}] \xe2\x86\x92 Missing: width" if width.nil? raise RuntimeError, "#{resource}.#{KEY_COLUMNS}[#{idx}] \xe2\x86\x92 Title already exists: #{title}" if columns[resource].has_key?(title) raise RuntimeError, "Expected width to be wildcard (*) or Integer greater than 0, instead got: (#{width.class}) #{width}" unless width == '*' || width.is_a?(Integer) raise RuntimeError, "#{resource}.#{KEY_COLUMNS}[#{idx}] \xe2\x86\x92 Title length (#{title.length}) is longer that column-width (#{width}) : #{title}" if title.length > width.to_i && width.to_s.strip != '*' raise RuntimeError, "#{resource}.#{KEY_COLUMNS}[#{idx}] \xe2\x86\x92 Formatter not recognized: #{formatter}, valid formatters are: #{App::AWSOutputter::get_formatter.join(', ')}" unless App::AWSOutputter::get_formatter.include?(formatter) || formatter.nil? raise RuntimeError, "#{resource}.#{KEY_COLUMNS}[#{idx}] \xe2\x86\x92 Color not recognized: #{color}, valid colors are: #{App::AWSOutputter::get_color.join(', ')}" unless App::AWSOutputter::get_color.include?(color) width_wildcard_count = width_wildcard_count + 1 if width == '*' width_total = width_total + width unless width == '*' columns[resource][title] = { :key => key, :width => width, :color => color, :formatter => formatter } end raise RuntimeError, "Wildcard width count must be exactly 1, got: #{width_wildcard_count}" unless width_wildcard_count == 1 raise RuntimeError, "#{resource}.#{KEY_COLUMNS} \xe2\x86\x92 Total width of columns (#{width_total}) minus wildcard (*) & region exceeds 210." if width_total > 210 table_widths[resource] = width_total end end # Validate Exports. unless data[KEY_EXPORT].nil? raise RuntimeError, "Expected #{resource}.export to be an Array, instead got: #{data[KEY_EXPORT].class}" unless data[KEY_EXPORT].is_a?(Array) raise RuntimeError, "Expected #{resource}.export to have at least 1 entry." unless data[KEY_EXPORT].any? data[KEY_EXPORT].each do |export| raise RuntimeError, "#{resource}.#{KEY_EXPORT} \xe2\x86\x92 Missing: id" unless export.has_key?('id') raise RuntimeError, "#{resource}.#{KEY_EXPORT} \xe2\x86\x92 Missing: value" unless export.has_key?('value') # Make sure formatter exists. App::AWSOutputter::get_formatter(export['valueFormatter']) if export.has_key?('valueFormatter') export.each { |key, val| raise RuntimeError, "Unexpected key: #{key} (#{val})" unless %w(id value valueFormatter description).include?(key) } export_map[resource] = [] unless export_map.has_key?(resource) export_map[resource] << export['id'] end end end # TODO - Maybe add "custom" resources here for WorkMailEmails and SES Identities, etc. here...?? If so, these need custom logic. [columns, data, export_map, table_widths] end |
.parse_results_for_prompt(resource, export_id, results) ⇒ Object
Takes AWS API results and translates it to an array of key/value pairs we can pass to Blufin::Terminal::prompt_select().
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/aws/aws_reports.rb', line 192 def self.parse_results_for_prompt(resource, export_id, results) = [] values = [] export = nil resource[App::AWSReports::KEY_EXPORT].each do |exp| if exp['id'] == export_id export = exp break end end raise RuntimeError, "Export with ID not found: #{export_id}" if export.nil? results.each do |result| value = result[export['value']] value = App::AWSOutputter::get_formatter(export['valueFormatter']).call(value)[0] if export.has_key?('valueFormatter') values << value end results.each do |result| value = result[export['value']] value = App::AWSOutputter::get_formatter(export['valueFormatter']).call(value)[0] if export.has_key?('valueFormatter') sort = export['description'] =~ /^Tags\.[A-Za-z0-9]+$/ ? extract_tag(result, export['description']) : result[export['description']] # Used for sorting. if export.has_key?('description') text = "#{value.rjust(values.max_by(&:length).length.to_i, ' ')} \x1B[38;5;246m\xe2\x80\x94 \x1B[38;5;240m#{sort}\x1B[0m" else text = value end << { :value => value, :text => text, :sort => sort, } end end |