trusted-advisor-status 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 629b80755ac7f3bb7ddf9f9a28495c274a0ecd14
4
+ data.tar.gz: f8335b6cc73fe74e0e45c556fdf9f3d4751d18e9
5
+ SHA512:
6
+ metadata.gz: 1337b5c13fa5ce08df3638123367a1473489df23cf53828327bba5fcad2519415c89018a088c315369a22a48063189906e2a537d22d9347d2aec40cdb45c7916
7
+ data.tar.gz: ae6ca68c5c3305b2b2a3e1e7c86ecae36e198ee1b35d004196ad5b4adc44671ab195ccb0e738134b0602370a8119034613c29653904776413f4b49c7b1d897de
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ require 'trollop'
3
+ require 'trusted_advisor_status'
4
+
5
+ opts = Trollop::options do
6
+ opt :categories, '', type: :strings, required: false, default: %w(security performance)
7
+ opt :fail_on_warn, '', type: :boolean, required: false, default: false, conflicts: :fail_on_error
8
+ opt :fail_on_error, '', type: :boolean, required: false, default: false, conflicts: :fail_on_warn
9
+ opt :delta_name, 'Given a name here, the results will be the delta of results already stored against the name', type: :string, required: false
10
+ end
11
+
12
+ exit TrustedAdvisorStatus.new.check_status categories: opts[:categories],
13
+ fail_on_warn: opts[:fail_on_warn],
14
+ fail_on_error: opts[:fail_on_error],
15
+ delta_name: opts[:delta_name]
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ require 'trollop'
3
+ require 'results_dao'
4
+
5
+ opts = Trollop::options do
6
+ opt :delta_name, 'Given a name here, the results will be the delta of results already stored against the name', type: :string, required: true
7
+ end
8
+
9
+ ResultsDAO.new.nuke_results opts[:delta_name]
data/lib/hash_util.rb ADDED
@@ -0,0 +1,23 @@
1
+ module HashUtil
2
+
3
+ def self.stringify_keys(hash)
4
+ new_hash = {}
5
+ hash.each do |k,v|
6
+ if v.is_a? Hash
7
+ new_hash[k.to_s] = stringify_keys(v)
8
+ elsif v.is_a? Array
9
+ new_array = v.map do |element|
10
+ if element.is_a? Hash
11
+ stringify_keys(element)
12
+ else
13
+ element
14
+ end
15
+ end
16
+ new_hash[k.to_s] = new_array
17
+ else
18
+ new_hash[k.to_s] = v
19
+ end
20
+ end
21
+ new_hash
22
+ end
23
+ end
@@ -0,0 +1,84 @@
1
+ require 'aws-sdk'
2
+
3
+ class ResultsDAO
4
+
5
+ def nuke_results(delta_name)
6
+ table = dynamo_db.table(delta_name)
7
+
8
+ table.delete
9
+
10
+ client.wait_until(:table_not_exists, table_name: delta_name)
11
+ end
12
+
13
+ def retrieve_prior_results(delta_name:)
14
+ if table_exists? delta_name
15
+ table = dynamo_db.table(delta_name)
16
+
17
+ item_response = table.get_item key: {
18
+ 'result_label' => 'previous_result'
19
+ }
20
+ unless item_response.item.nil?
21
+ item = item_response.item['result']
22
+ if item.nil?
23
+ raise "Result key must have value in prior results: #{item_response.item}"
24
+ else
25
+ item
26
+ end
27
+ end
28
+ else
29
+ nil
30
+ end
31
+ end
32
+
33
+ def update_prior_result(delta_name:, results:)
34
+ conditionally_create_table table_name: delta_name
35
+
36
+ table = dynamo_db.table(delta_name)
37
+ result_item = {
38
+ 'result_label' => 'previous_result',
39
+ 'result' => results
40
+ }
41
+
42
+ table.put_item item: result_item
43
+ end
44
+
45
+ private
46
+
47
+ def conditionally_create_table(table_name:)
48
+ unless table_exists? table_name
49
+ dynamo_db.create_table attribute_definitions: [
50
+ {
51
+ attribute_name: 'result_label',
52
+ attribute_type: 'S'
53
+ }
54
+ ],
55
+ table_name: table_name,
56
+ key_schema: [
57
+ {
58
+ attribute_name: 'result_label',
59
+ key_type: 'HASH'
60
+ }
61
+ ],
62
+ provisioned_throughput: {
63
+ read_capacity_units: 1,
64
+ write_capacity_units: 1
65
+ }
66
+
67
+ client.wait_until(:table_exists, table_name: table_name)
68
+ end
69
+ end
70
+
71
+
72
+ def table_exists?(table_name)
73
+ found_table = dynamo_db.tables.find { |table| table.name == table_name }
74
+ not found_table.nil?
75
+ end
76
+
77
+ def client
78
+ Aws::DynamoDB::Client.new
79
+ end
80
+
81
+ def dynamo_db
82
+ Aws::DynamoDB::Resource.new(client: client)
83
+ end
84
+ end
@@ -0,0 +1,69 @@
1
+ require 'set'
2
+
3
+ class ResultsDifferencer
4
+
5
+ def fixed(prior:,
6
+ current:)
7
+
8
+ prior_hash = {}
9
+ prior.each { |result| prior_hash[result['check_id']] = result }
10
+
11
+ current_hash = {}
12
+ current.each { |result| current_hash[result['check_id']] = result }
13
+
14
+ prior_ids = prior.map { |result| result['check_id'] }
15
+ current_ids = current.map { |result| result['check_id'] }
16
+
17
+ fixed_check_ids = prior_ids - current_ids
18
+ same_check_ids = prior_ids - fixed_check_ids
19
+
20
+ delta = []
21
+
22
+ same_check_ids.each do |check_id|
23
+ fixed_resources = prior_hash[check_id]['flagged_resources'] - current_hash[check_id]['flagged_resources']
24
+ if fixed_resources != []
25
+ delta_result = prior_hash[check_id].dup
26
+ delta_result['flagged_resources'] = fixed_resources
27
+ delta << delta_result
28
+ else
29
+
30
+ end
31
+ end
32
+
33
+ fixed_check_ids.each { |check_id| delta << prior_hash[check_id] }
34
+
35
+ delta
36
+ end
37
+
38
+ def new_violations(prior:,
39
+ current:)
40
+
41
+ prior_hash = {}
42
+ prior.each { |result| prior_hash[result['check_id']] = result }
43
+
44
+ current_hash = {}
45
+ current.each { |result| current_hash[result['check_id']] = result }
46
+
47
+ prior_ids = prior.map { |result| result['check_id'] }
48
+ current_ids = current.map { |result| result['check_id'] }
49
+
50
+ new_check_ids = current_ids - prior_ids
51
+ same_check_ids = current_ids - new_check_ids
52
+
53
+ delta = []
54
+
55
+ same_check_ids.each do |check_id|
56
+ new_resources = current_hash[check_id]['flagged_resources'] - prior_hash[check_id]['flagged_resources']
57
+ if new_resources != []
58
+ delta_result = current_hash[check_id].dup
59
+ delta_result['flagged_resources'] = new_resources
60
+ delta << delta_result
61
+ end
62
+ end
63
+
64
+ new_check_ids.each { |check_id| delta << current_hash[check_id] }
65
+
66
+ delta
67
+ end
68
+
69
+ end
@@ -0,0 +1,135 @@
1
+ require 'aws-sdk'
2
+ require 'json'
3
+ require_relative 'results_dao'
4
+ require_relative 'results_differencer'
5
+ require_relative 'hash_util'
6
+
7
+ class TrustedAdvisorStatus
8
+
9
+
10
+ def check_status(categories: %w(security performance),
11
+ fail_on_warn: false,
12
+ fail_on_error: false,
13
+ delta_name: nil)
14
+
15
+ results = discover_results(categories: categories,
16
+ delta_name: delta_name)
17
+
18
+ render_results(results)
19
+
20
+ if results.is_a? Array
21
+ new_violations = results
22
+ elsif results.is_a? Hash
23
+ new_violations = results['new_violation']
24
+ end
25
+
26
+ if fail_on_error
27
+ error_found = new_violations.find { |result| result['status'] == 'error' }
28
+ error_found.nil? ? 0 : 1
29
+ elsif fail_on_warn
30
+ warning_fond = new_violations.find { |result| result['status'] == 'error' or result['status'] == 'warning' }
31
+ warning_fond.nil? ? 0 : 1
32
+ else
33
+ 0
34
+ end
35
+ end
36
+
37
+ def discover_results(categories:, delta_name:)
38
+ results_dao = ResultsDAO.new
39
+
40
+ full_results = not_ok_check_results(categories: categories)
41
+ if delta_name.nil?
42
+ full_results
43
+ else
44
+ prior_results = results_dao.retrieve_prior_results delta_name: delta_name
45
+ if prior_results.nil?
46
+ delta_results = full_results
47
+ else
48
+ diff = ResultsDifferencer.new
49
+ new_violations = diff.new_violations(prior: prior_results,
50
+ current: full_results)
51
+ fixes = diff.fixed(prior: prior_results,
52
+ current: full_results)
53
+
54
+ delta_results = {
55
+ 'new_violation' => new_violations,
56
+ 'fixes' => fixes
57
+ }
58
+ end
59
+ results_dao.update_prior_result(delta_name: delta_name, results: full_results)
60
+
61
+ delta_results
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ # resp.result.check_id #=> String
68
+ # resp.result.timestamp #=> String
69
+ # resp.result.status #=> String
70
+
71
+ # resp.result.resources_summary.resources_processed #=> Integer
72
+ # resp.result.resources_summary.resources_flagged #=> Integer
73
+ # resp.result.resources_summary.resources_ignored #=> Integer
74
+ # resp.result.resources_summary.resources_suppressed #=> Integer
75
+
76
+ # resp.result.flagged_resources #=> Array
77
+ # resp.result.flagged_resources[0].status #=> String
78
+ # resp.result.flagged_resources[0].region #=> String
79
+ # resp.result.flagged_resources[0].resource_id #=> String
80
+ # resp.result.flagged_resources[0].is_suppressed #=> true/false
81
+ # resp.result.flagged_resources[0].metadata #=> Array
82
+ # resp.result.flagged_resources[0].metadata[0] #=> String
83
+ def not_ok_check_results(categories:)
84
+
85
+ # the region is on purpose - support intfc is global, but can't find endpoint outside of us-east-1
86
+ support = Aws::Support::Client.new region: 'us-east-1'
87
+
88
+ describe_trusted_advisor_checks_response = support.describe_trusted_advisor_checks language: 'en'
89
+
90
+ if categories.nil?
91
+ checks = describe_trusted_advisor_checks_response.checks
92
+ else
93
+ checks = describe_trusted_advisor_checks_response.checks.select { |check| categories.include? check.category }
94
+ end
95
+
96
+ checks.reduce([]) do |aggregate, check|
97
+ describe_trusted_advisor_check_result_response = support.describe_trusted_advisor_check_result check_id: check.id,
98
+ language: 'en'
99
+
100
+ if describe_trusted_advisor_check_result_response.result.status != 'ok'
101
+ aggregate << convert_check_result_into_hash(check.name,
102
+ describe_trusted_advisor_check_result_response.result)
103
+ end
104
+
105
+ aggregate
106
+ end
107
+ end
108
+
109
+ def convert_check_result_into_hash(check_name, check_result)
110
+ hash_result = check_result.to_h
111
+
112
+ hash_result.delete :timestamp
113
+ hash_result.delete :resources_summary
114
+ hash_result.delete :category_specific_summary
115
+
116
+ hash_result[:description] = check_name
117
+
118
+ unless hash_result[:flagged_resources].nil?
119
+ hash_result[:flagged_resources] = hash_result[:flagged_resources].reject do |flagged_resource|
120
+ is_suppressed = flagged_resource[:is_suppressed]
121
+
122
+ flagged_resource.delete :resource_id
123
+ flagged_resource.delete :is_suppressed
124
+
125
+ is_suppressed
126
+ end
127
+ end
128
+
129
+ HashUtil::stringify_keys(hash_result)
130
+ end
131
+
132
+ def render_results(results)
133
+ puts JSON.pretty_generate(results)
134
+ end
135
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: trusted-advisor-status
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - someguy
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-03-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aws-sdk
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 2.2.17
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 2.2.17
27
+ - !ruby/object:Gem::Dependency
28
+ name: trollop
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 2.1.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 2.1.2
41
+ description: Some scripting around the AWS Trusted Advisor interface for convenience
42
+ in calling from pipeline
43
+ email:
44
+ executables:
45
+ - trusted-advisor-status
46
+ - wipe-trusted-advisor-history
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - bin/trusted-advisor-status
51
+ - bin/wipe-trusted-advisor-history
52
+ - lib/hash_util.rb
53
+ - lib/results_dao.rb
54
+ - lib/results_differencer.rb
55
+ - lib/trusted_advisor_status.rb
56
+ homepage: https://github.com/stelligent/trusted-advisor-status
57
+ licenses: []
58
+ metadata: {}
59
+ post_install_message:
60
+ rdoc_options: []
61
+ require_paths:
62
+ - lib
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 2.1.0
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ requirements: []
75
+ rubyforge_project:
76
+ rubygems_version: 2.6.0
77
+ signing_key:
78
+ specification_version: 4
79
+ summary: ''
80
+ test_files: []