cps-property-generator 0.2.21 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/cps-property-generator +3 -5
- data/lib/generator/config.rb +1 -2
- data/lib/generator/generator.rb +7 -9
- data/lib/generator/globals.rb +11 -11
- data/lib/generator/service.rb +9 -19
- data/lib/helpers/helpers.rb +22 -22
- data/lib/linter/config_linter.rb +36 -37
- data/lib/linter/globals_linter.rb +35 -30
- data/lib/linter/linter.rb +24 -10
- data/lib/linter/report.rb +24 -21
- data/lib/linter/services_linter.rb +109 -70
- data/spec/lib/config_spec.rb +23 -18
- data/spec/lib/global_spec.rb +55 -30
- data/spec/lib/service_spec.rb +76 -67
- data/spec/resources/services/my-microservice-1.yml +8 -0
- metadata +27 -13
@@ -1,27 +1,32 @@
|
|
1
1
|
require_relative '../helpers/helpers'
|
2
|
+
|
2
3
|
module PropertyGenerator
|
3
4
|
require 'yaml'
|
5
|
+
require 'pathname'
|
4
6
|
class GlobalsLinter
|
7
|
+
TESTS = [
|
8
|
+
'globals_load_as_hashes',
|
9
|
+
'globals_are_defined_for_valid_environemnts'
|
10
|
+
].freeze
|
5
11
|
|
6
|
-
def initialize(path, configs)
|
12
|
+
def initialize(path, configs, ignored_tests)
|
7
13
|
@configs = configs
|
8
14
|
@globals = {}
|
15
|
+
@ignored_tests = ignored_tests
|
9
16
|
valid_paths = PropertyGenerator.valid_paths(path)
|
10
17
|
valid_paths.each do |file_path|
|
11
|
-
|
18
|
+
@globals[file_path] = YAML.load_file(file_path)
|
12
19
|
end
|
13
20
|
end
|
14
21
|
|
15
22
|
def run_globals_tests
|
16
|
-
tests =
|
17
|
-
|
18
|
-
|
19
|
-
results = PropertyGenerator.test_runner(self, tests)
|
20
|
-
results
|
23
|
+
tests = TESTS - @ignored_tests
|
24
|
+
|
25
|
+
PropertyGenerator.test_runner(self, tests)
|
21
26
|
end
|
22
27
|
|
23
28
|
def globals_load_as_hashes
|
24
|
-
status = {status: 'pass', error: ''}
|
29
|
+
status = { status: 'pass', error: '' }
|
25
30
|
non_hash_globals = []
|
26
31
|
@globals.each do |path, loaded|
|
27
32
|
if loaded.class != Hash
|
@@ -36,13 +41,13 @@ module PropertyGenerator
|
|
36
41
|
end
|
37
42
|
|
38
43
|
def globals_have_no_hashes_as_values
|
39
|
-
status = {status: 'pass', error: ''}
|
44
|
+
status = { status: 'pass', error: '' }
|
40
45
|
globals_with_hash_props = []
|
41
46
|
@globals.each do |path, loaded|
|
42
47
|
if loaded.class == Hash
|
43
48
|
loaded.each do |key, value|
|
44
49
|
if value.class == Hash
|
45
|
-
globals_with_hash_props << {path => key}
|
50
|
+
globals_with_hash_props << { path => key }
|
46
51
|
end
|
47
52
|
end
|
48
53
|
end
|
@@ -55,30 +60,30 @@ module PropertyGenerator
|
|
55
60
|
end
|
56
61
|
|
57
62
|
def globals_are_defined_for_valid_environemnts
|
58
|
-
status = {status: 'pass', error: ''}
|
63
|
+
status = { status: 'pass', error: '' }
|
59
64
|
ignore_list = ['globals.yml']
|
60
|
-
|
61
|
-
|
62
|
-
if @configs['accounts'] != nil
|
63
|
-
@configs['accounts'].each do |account|
|
64
|
-
accounts << account.to_s
|
65
|
-
end
|
66
|
-
@globals.each do |path, loaded|
|
67
|
-
unless @configs['environments'].include?((path.split('/')[(path.split('/')).length - 1]).split('.')[0]) || accounts.include?((path.split('/')[(path.split('/')).length - 1]).split('.')[0])
|
68
|
-
globals_with_invalid_environments << path unless ignore_list.include?(path.split('/')[(path.split('/')).length - 1])
|
69
|
-
end
|
70
|
-
end
|
71
|
-
if globals_with_invalid_environments != []
|
72
|
-
status[:status] = 'warn'
|
73
|
-
status[:error] = "Files #{globals_with_invalid_environments} do not have names matching a declared environment."
|
74
|
-
end
|
75
|
-
else
|
65
|
+
|
66
|
+
if @configs['accounts'].nil?
|
76
67
|
status[:status] = 'warn'
|
77
|
-
status[:error] =
|
68
|
+
status[:error] = 'Accounts list in config file is missing.'
|
69
|
+
return status
|
70
|
+
end
|
71
|
+
|
72
|
+
accounts = @configs['accounts'].map(&:to_s)
|
73
|
+
globals_with_invalid_environments = @globals.keys.select do |path|
|
74
|
+
p = Pathname.new(path)
|
75
|
+
filename = p.basename(File.extname(path)).to_s
|
76
|
+
next if ignore_list.include?(p.basename.to_s)
|
77
|
+
|
78
|
+
!(@configs['environments'].include?(filename) || accounts.include?(filename))
|
78
79
|
end
|
79
|
-
status
|
80
|
-
end
|
81
80
|
|
81
|
+
unless globals_with_invalid_environments.empty?
|
82
|
+
status[:status] = 'warn'
|
83
|
+
status[:error] = "Files #{globals_with_invalid_environments} do not have names matching a declared environment."
|
84
|
+
end
|
82
85
|
|
86
|
+
status
|
87
|
+
end
|
83
88
|
end
|
84
89
|
end
|
data/lib/linter/linter.rb
CHANGED
@@ -1,17 +1,32 @@
|
|
1
1
|
module PropertyGenerator
|
2
|
-
require_relative 'config_linter
|
3
|
-
require_relative 'globals_linter
|
4
|
-
require_relative 'services_linter
|
5
|
-
require_relative 'report
|
2
|
+
require_relative 'config_linter'
|
3
|
+
require_relative 'globals_linter'
|
4
|
+
require_relative 'services_linter'
|
5
|
+
require_relative 'report'
|
6
6
|
class Linter
|
7
|
+
attr_accessor :ignored_tests, :services_linter, :globals_linter, :config_linter, :report
|
7
8
|
|
8
9
|
def initialize(path)
|
9
|
-
ignore_list = [
|
10
|
+
ignore_list = %w[README.md .cpsignore]
|
10
11
|
invalid_paths = PropertyGenerator.invalid_paths(path, ignore_list)
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
begin
|
13
|
+
@ignored_tests = YAML.load_file("#{path}/.cpsignore")
|
14
|
+
rescue StandardError
|
15
|
+
@ignored_tests = {}
|
16
|
+
end
|
17
|
+
@config_linter = PropertyGenerator::ConfigLinter.new("#{path}/config/config.yml", @ignored_tests['config'] || [])
|
18
|
+
@globals_linter = PropertyGenerator::GlobalsLinter.new("#{path}/globals/", @config_linter.configs, @ignored_tests['globals'] || [])
|
19
|
+
@services_linter = PropertyGenerator::ServicesLinter.new("#{path}/services/", @config_linter.configs, @ignored_tests['services'] || [])
|
20
|
+
|
21
|
+
unless @ignored_tests['display_skipped_tests'].nil?
|
22
|
+
if @ignored_tests['display_skipped_tests']
|
23
|
+
@ignored_tests.delete('display_skipped_tests')
|
24
|
+
else
|
25
|
+
@ignored_tests = []
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
@report = PropertyGenerator::Report.new(invalid_paths, @ignored_tests)
|
15
30
|
end
|
16
31
|
|
17
32
|
def run_tests
|
@@ -34,6 +49,5 @@ module PropertyGenerator
|
|
34
49
|
def display_report
|
35
50
|
@report.display_report
|
36
51
|
end
|
37
|
-
|
38
52
|
end
|
39
53
|
end
|
data/lib/linter/report.rb
CHANGED
@@ -2,9 +2,10 @@ module PropertyGenerator
|
|
2
2
|
require 'terminal-table'
|
3
3
|
|
4
4
|
class Report
|
5
|
-
def initialize(files_failing_load)
|
5
|
+
def initialize(files_failing_load, ignored_tests)
|
6
6
|
@files_failing_load = files_failing_load
|
7
7
|
@full_report = {}
|
8
|
+
@ignored_tests = ignored_tests
|
8
9
|
end
|
9
10
|
|
10
11
|
def add_report(report)
|
@@ -12,12 +13,16 @@ module PropertyGenerator
|
|
12
13
|
end
|
13
14
|
|
14
15
|
def display_report
|
15
|
-
|
16
|
-
make_failing_to_load_table
|
16
|
+
unless @files_failing_load.empty?
|
17
|
+
puts make_failing_to_load_table
|
18
|
+
puts '*****************'
|
19
|
+
puts "Check for property values that start with an interpolated value \nIf the first character of the value is a bracket yaml will fail to load \nPlace the value in quotes"
|
20
|
+
puts '*****************'
|
17
21
|
end
|
18
|
-
|
19
|
-
|
20
|
-
|
22
|
+
puts make_skip_table
|
23
|
+
puts make_pass_table
|
24
|
+
puts make_warn_table
|
25
|
+
puts make_fail_table
|
21
26
|
end
|
22
27
|
|
23
28
|
def has_a_test_failed
|
@@ -39,45 +44,43 @@ module PropertyGenerator
|
|
39
44
|
@files_failing_load.each do |failed|
|
40
45
|
rows << [failed]
|
41
46
|
end
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
+
Terminal::Table.new :headings => ['Files'], :title => 'Files Failing to Load', :rows => rows, :style => { :width => 200 }
|
48
|
+
end
|
49
|
+
|
50
|
+
def make_skip_table
|
51
|
+
return if @ignored_tests.empty?
|
52
|
+
|
53
|
+
Terminal::Table.new(headings: ['Test'], title: 'Skipped Tests', rows: @ignored_tests.values, style: { width: 200 })
|
47
54
|
end
|
48
55
|
|
49
56
|
def make_pass_table
|
50
57
|
rows = []
|
51
|
-
@full_report.each do |test,status|
|
58
|
+
@full_report.each do |test, status|
|
52
59
|
if status[:status] == 'pass'
|
53
60
|
rows << [test.gsub('_', ' ')]
|
54
61
|
end
|
55
62
|
end
|
56
|
-
|
57
|
-
puts table
|
63
|
+
Terminal::Table.new(headings: ['Test'], title: 'Passing Tests', rows: rows, style: { width: 200 })
|
58
64
|
end
|
59
65
|
|
60
66
|
def make_warn_table
|
61
67
|
rows = []
|
62
|
-
@full_report.each do |test,status|
|
68
|
+
@full_report.each do |test, status|
|
63
69
|
if status[:status] == 'warn'
|
64
70
|
rows << [test.gsub('_', ' '), status[:error].scan(/.{1,90}/).join("\n")]
|
65
71
|
end
|
66
72
|
end
|
67
|
-
|
68
|
-
puts table
|
73
|
+
Terminal::Table.new(headings: ['Test', 'Error'], title: 'Warning Tests', rows: rows, style: { width: 200 })
|
69
74
|
end
|
70
75
|
|
71
76
|
def make_fail_table
|
72
77
|
rows = []
|
73
|
-
@full_report.each do |test,status|
|
78
|
+
@full_report.each do |test, status|
|
74
79
|
if status[:status] == 'fail'
|
75
80
|
rows << [test.gsub('_', ' '), status[:error].scan(/.{1,90}/).join("\n")]
|
76
81
|
end
|
77
82
|
end
|
78
|
-
|
79
|
-
puts table
|
83
|
+
Terminal::Table.new(headings: ['Test', 'Error'], title: 'Failing Tests', rows: rows, style: { width: 200 })
|
80
84
|
end
|
81
|
-
|
82
85
|
end
|
83
86
|
end
|
@@ -2,10 +2,19 @@ require_relative '../helpers/helpers'
|
|
2
2
|
module PropertyGenerator
|
3
3
|
require 'yaml'
|
4
4
|
class ServicesLinter
|
5
|
+
TESTS = [
|
6
|
+
'services_have_accepted_keys',
|
7
|
+
'service_environments_are_not_empty',
|
8
|
+
'service_environments_match_config_environments',
|
9
|
+
'service_encrypted_environments_match_config_environments',
|
10
|
+
'service_encrypted_fields_are_correct',
|
11
|
+
'service_encrypted_region_field_is_accepted'
|
12
|
+
].freeze
|
5
13
|
|
6
|
-
def initialize(path, configs)
|
14
|
+
def initialize(path, configs, ignored_tests)
|
7
15
|
@configs = configs
|
8
16
|
@services = {}
|
17
|
+
@ignored_tests = ignored_tests
|
9
18
|
valid_paths = PropertyGenerator.valid_paths(path)
|
10
19
|
valid_paths.each do |file_path|
|
11
20
|
@services[file_path] = YAML.load_file(file_path)
|
@@ -13,26 +22,19 @@ module PropertyGenerator
|
|
13
22
|
end
|
14
23
|
|
15
24
|
def run_services_tests
|
16
|
-
tests =
|
17
|
-
|
18
|
-
|
19
|
-
'service_environments_match_config_environments',
|
20
|
-
'service_encrypted_environments_match_config_environments',
|
21
|
-
'service_encrypted_fields_are_correct',
|
22
|
-
'service_encrypted_region_field_is_accepted'
|
23
|
-
]
|
24
|
-
results = PropertyGenerator.test_runner(self, tests)
|
25
|
-
results
|
25
|
+
tests = TESTS - @ignored_tests
|
26
|
+
|
27
|
+
PropertyGenerator.test_runner(self, tests)
|
26
28
|
end
|
27
29
|
|
28
30
|
def service_environments_are_not_empty
|
29
|
-
status = {status: 'pass', error: ''}
|
31
|
+
status = { status: 'pass', error: '' }
|
30
32
|
services_empty_environments = []
|
31
33
|
@services.each do |path, loaded|
|
32
|
-
unless loaded['environments']
|
34
|
+
unless loaded['environments'].nil?
|
33
35
|
loaded['environments'].each do |environments, properties|
|
34
|
-
if properties
|
35
|
-
services_empty_environments << {path => environments}
|
36
|
+
if properties.nil?
|
37
|
+
services_empty_environments << { path => environments }
|
36
38
|
end
|
37
39
|
end
|
38
40
|
end
|
@@ -45,13 +47,13 @@ module PropertyGenerator
|
|
45
47
|
end
|
46
48
|
|
47
49
|
def services_have_accepted_keys
|
48
|
-
status = {status: 'pass', error: ''}
|
50
|
+
status = { status: 'pass', error: '' }
|
49
51
|
accepted_keys = ['default', 'environments', 'encrypted', 'configname', 'stringdata', 'configlabels', 'secretlabels', 'label']
|
50
52
|
services_with_unacceptable_keys = []
|
51
53
|
@services.each do |path, loaded|
|
52
54
|
loaded.keys.each do |service_key|
|
53
55
|
unless accepted_keys.include?(service_key)
|
54
|
-
services_with_unacceptable_keys << {path => service_key}
|
56
|
+
services_with_unacceptable_keys << { path => service_key }
|
55
57
|
end
|
56
58
|
end
|
57
59
|
end
|
@@ -63,18 +65,18 @@ module PropertyGenerator
|
|
63
65
|
end
|
64
66
|
|
65
67
|
def service_environments_match_config_environments
|
66
|
-
status = {status: 'pass', error: ''}
|
68
|
+
status = { status: 'pass', error: '' }
|
67
69
|
missmatched_environments = []
|
68
70
|
@services.each do |path, loaded|
|
69
|
-
if loaded['environments']
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
71
|
+
next if loaded['environments'].nil?
|
72
|
+
|
73
|
+
loaded['environments'].keys.each do |environment|
|
74
|
+
if @configs['environments'].nil?
|
75
|
+
status[:status] = 'warn'
|
76
|
+
status[:error] = 'Environments list in config file is missing.'
|
77
|
+
else
|
78
|
+
unless @configs['environments'].include?(environment)
|
79
|
+
missmatched_environments << { path => environment }
|
78
80
|
end
|
79
81
|
end
|
80
82
|
end
|
@@ -87,18 +89,18 @@ module PropertyGenerator
|
|
87
89
|
end
|
88
90
|
|
89
91
|
def service_encrypted_environments_match_config_environments
|
90
|
-
status = {status: 'pass', error: ''}
|
92
|
+
status = { status: 'pass', error: '' }
|
91
93
|
missmatched_environments = []
|
92
94
|
@services.each do |path, loaded|
|
93
|
-
if loaded['encrypted']
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
95
|
+
next if loaded['encrypted'].nil?
|
96
|
+
|
97
|
+
loaded['encrypted'].keys.each do |environment|
|
98
|
+
if @configs['environments'].nil?
|
99
|
+
status[:status] = 'warn'
|
100
|
+
status[:error] = 'Environments list in config file is missing.'
|
101
|
+
else
|
102
|
+
unless @configs['environments'].include?(environment)
|
103
|
+
missmatched_environments << { path => environment }
|
102
104
|
end
|
103
105
|
end
|
104
106
|
end
|
@@ -110,66 +112,103 @@ module PropertyGenerator
|
|
110
112
|
status
|
111
113
|
end
|
112
114
|
|
115
|
+
def recursive_find_keys(obj, key)
|
116
|
+
if obj.respond_to?(:key?) && obj.key?(key)
|
117
|
+
obj[key]
|
118
|
+
elsif obj.is_a?(Hash) or obj.is_a?(Array)
|
119
|
+
r = nil
|
120
|
+
obj.find{ |*a| r = recursive_find_keys(a.last,key) }
|
121
|
+
r
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
113
125
|
def service_encrypted_fields_are_correct
|
114
|
-
status = {status: 'pass', error: ''}
|
126
|
+
status = { status: 'pass', error: '' }
|
115
127
|
accepted_keys = ['region', 'encrypted', 'service', 'label']
|
116
128
|
services_with_unacceptable_keys = []
|
117
129
|
@services.each do |path, loaded|
|
118
|
-
if loaded['encrypted']
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
130
|
+
next if loaded['encrypted'].nil?
|
131
|
+
|
132
|
+
loaded['encrypted'].each do |environment, properties|
|
133
|
+
properties.each do |property, value|
|
134
|
+
if value.nil?
|
135
|
+
services_with_unacceptable_keys << { path => { environment => property } }
|
136
|
+
next
|
137
|
+
end
|
138
|
+
|
139
|
+
s = recursive_find_keys(value, '$ssm')
|
140
|
+
k = recursive_find_keys(value, '$kms')
|
141
|
+
if s.nil? && k.nil?
|
142
|
+
services_with_unacceptable_keys << { path => { environment => property } }
|
143
|
+
next
|
144
|
+
end
|
145
|
+
|
146
|
+
unless s.nil?
|
147
|
+
s.keys.each do |key|
|
148
|
+
unless accepted_keys.include?(key)
|
149
|
+
services_with_unacceptable_keys << { path => { environment => property } }
|
150
|
+
next
|
132
151
|
end
|
133
152
|
end
|
134
153
|
end
|
154
|
+
|
155
|
+
unless k.nil?
|
156
|
+
if (k.keys & ['region', 'encrypted']).count != 2
|
157
|
+
services_with_unacceptable_keys << { path => { environment => property } }
|
158
|
+
end
|
159
|
+
end
|
135
160
|
end
|
136
161
|
end
|
137
162
|
end
|
138
|
-
|
163
|
+
unless services_with_unacceptable_keys.empty?
|
139
164
|
status[:status] = 'fail'
|
140
|
-
status[:error] = "Service files: #{services_with_unacceptable_keys}
|
165
|
+
status[:error] = "Service files: #{services_with_unacceptable_keys} has an encrypted block without " +
|
166
|
+
"properties, encrypted properties without $kms or $ssm blocks, or encrypted blocks with either bad " +
|
167
|
+
"indentation or incorrect keys."
|
141
168
|
end
|
142
169
|
status
|
143
170
|
end
|
144
171
|
|
145
172
|
def service_encrypted_region_field_is_accepted
|
146
|
-
status = {status: 'pass', error: ''}
|
173
|
+
status = { status: 'pass', error: '' }
|
147
174
|
services_with_unacceptable_keys = []
|
148
175
|
@services.each do |path, loaded|
|
149
|
-
if loaded['encrypted']
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
176
|
+
next if loaded['encrypted'].nil?
|
177
|
+
|
178
|
+
loaded['encrypted'].each do |environment, properties|
|
179
|
+
if loaded['encrypted'][environment].nil?
|
180
|
+
status[:status] = 'error'
|
181
|
+
status[:error] = 'Encrypted properties are missing from the encrypted environment'
|
182
|
+
return status
|
183
|
+
end
|
184
|
+
|
185
|
+
encrypted_env = loaded['encrypted'][environment]
|
186
|
+
|
187
|
+
# next if encrypted_env[property].nil?
|
188
|
+
|
189
|
+
encrypted_env.each do |property, encrypted_values|
|
190
|
+
if @configs['environments'].nil?
|
191
|
+
status[:status] = 'warn'
|
192
|
+
status[:error] = 'Environments list in config file is missing.'
|
193
|
+
break
|
194
|
+
end
|
195
|
+
|
196
|
+
%w[$ssm $kms].each do |encryption_type|
|
197
|
+
values = recursive_find_keys(encrypted_values, encryption_type)
|
198
|
+
next if values.nil?
|
199
|
+
|
200
|
+
unless @configs['environments'].include?(values['region'])
|
201
|
+
services_with_unacceptable_keys << { path => { environment => property } }
|
161
202
|
end
|
162
203
|
end
|
163
204
|
end
|
164
205
|
end
|
165
206
|
end
|
166
|
-
if services_with_unacceptable_keys
|
207
|
+
if !services_with_unacceptable_keys.empty? && status[:status] == 'pass'
|
167
208
|
status[:status] = 'warn'
|
168
209
|
status[:error] = "Service files: #{services_with_unacceptable_keys} have encrypted properties a region field not matching a declared environment in the configs."
|
169
210
|
end
|
170
211
|
status
|
171
212
|
end
|
172
|
-
|
173
|
-
|
174
213
|
end
|
175
214
|
end
|