trusted-advisor-status 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/trusted-advisor-status +15 -0
- data/bin/wipe-trusted-advisor-history +9 -0
- data/lib/hash_util.rb +23 -0
- data/lib/results_dao.rb +84 -0
- data/lib/results_differencer.rb +69 -0
- data/lib/trusted_advisor_status.rb +135 -0
- metadata +80 -0
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
|
data/lib/results_dao.rb
ADDED
@@ -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: []
|