cps-property-generator 0.1.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/README.md +115 -0
- data/bin/cps-property-generator +45 -0
- data/lib/generator/config.rb +32 -0
- data/lib/generator/generator.rb +50 -0
- data/lib/generator/globals.rb +85 -0
- data/lib/generator/service.rb +89 -0
- data/lib/helpers/helpers.rb +81 -0
- data/lib/linter/config_linter.rb +106 -0
- data/lib/linter/globals_linter.rb +84 -0
- data/lib/linter/linter.rb +35 -0
- data/lib/linter/report.rb +76 -0
- data/lib/linter/services_linter.rb +186 -0
- data/spec/lib/config_spec.rb +35 -0
- data/spec/lib/global_spec.rb +28 -0
- data/spec/lib/service_spec.rb +35 -0
- data/spec/resources/config/config.yml +23 -0
- data/spec/resources/globals/accounts/123456789012/123456789012.yml +1 -0
- data/spec/resources/globals/accounts/123456789012/environments/my-test-env1.yml +1 -0
- data/spec/resources/globals/globals.yml +1 -0
- data/spec/resources/services/my-microservice-1.yml +16 -0
- data/spec/spec_helper.rb +85 -0
- metadata +136 -0
@@ -0,0 +1,106 @@
|
|
1
|
+
module PropertyGenerator
|
2
|
+
require 'yaml'
|
3
|
+
class ConfigLinter
|
4
|
+
|
5
|
+
attr_accessor :configs
|
6
|
+
|
7
|
+
def initialize(path)
|
8
|
+
@configs = check_for_config(path)
|
9
|
+
end
|
10
|
+
|
11
|
+
def run_config_tests
|
12
|
+
if @configs == {}
|
13
|
+
tests = ['config_file_is_present']
|
14
|
+
else
|
15
|
+
tests = ['config_has_correct_keys',
|
16
|
+
'environment_configs_match_environments_list',
|
17
|
+
'environment_configs_have_matching_region_and_account_values',
|
18
|
+
'environment_configs_have_well_formatted_interpolations',
|
19
|
+
'config_file_is_present']
|
20
|
+
end
|
21
|
+
results = PropertyGenerator.test_runner(self, tests)
|
22
|
+
results
|
23
|
+
end
|
24
|
+
|
25
|
+
def check_for_config(path)
|
26
|
+
#Tries to load the config file - if is unable to load config.yml it returns an empty hash
|
27
|
+
#Empty hash is returned so that the rest of the files are still able to be linted instead of stopping at this point.
|
28
|
+
begin
|
29
|
+
YAML.load_file(path)
|
30
|
+
rescue
|
31
|
+
{}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def config_file_is_present
|
36
|
+
status = {status: 'pass', error: ''}
|
37
|
+
if @configs == {}
|
38
|
+
status[:status] = 'fail'
|
39
|
+
status[:error] = "Config.yml file is missing, it is required."
|
40
|
+
end
|
41
|
+
status
|
42
|
+
end
|
43
|
+
|
44
|
+
def config_has_correct_keys
|
45
|
+
status = {status: 'pass', error: ''}
|
46
|
+
config_keys = ['environments', 'accounts',
|
47
|
+
'environment_configs']
|
48
|
+
if @configs.keys != config_keys
|
49
|
+
status[:status] = 'fail'
|
50
|
+
status[:error] = "Config keys should be 'environments', 'accounts', and 'environment_configs'."
|
51
|
+
end
|
52
|
+
status
|
53
|
+
end
|
54
|
+
|
55
|
+
def environment_configs_match_environments_list
|
56
|
+
status = {status: 'pass', error: ''}
|
57
|
+
if @configs['environments'] != @configs['environment_configs'].keys
|
58
|
+
status[:status] = 'fail'
|
59
|
+
status[:error] = "Environments in environment_configs do not match environments listed in config environments."
|
60
|
+
end
|
61
|
+
status
|
62
|
+
end
|
63
|
+
|
64
|
+
def environment_configs_have_matching_region_and_account_values
|
65
|
+
status = {status: 'pass', error: ''}
|
66
|
+
environments_missmatch_values = []
|
67
|
+
any_missmatches = false
|
68
|
+
@configs['environment_configs'].keys.each do |environment|
|
69
|
+
unless @configs['environments'].include?(@configs['environment_configs'][environment]['region']) && @configs['accounts'].include?(@configs['environment_configs'][environment]['account'])
|
70
|
+
environments_missmatch_values << environment
|
71
|
+
any_missmatches = true
|
72
|
+
end
|
73
|
+
if any_missmatches
|
74
|
+
status[:status] = 'fail'
|
75
|
+
status[:error] = "Environments: #{environments_missmatch_values} in environment_configs have a region or account value not listed in top level."
|
76
|
+
end
|
77
|
+
end
|
78
|
+
status
|
79
|
+
end
|
80
|
+
|
81
|
+
def environment_configs_have_well_formatted_interpolations
|
82
|
+
status = {status: 'pass', error: ''}
|
83
|
+
environments_with_bad_interpolations = []
|
84
|
+
any_mistakes = false
|
85
|
+
@configs['environment_configs'].keys.each do |environment|
|
86
|
+
if @configs['environment_configs'][environment]['interpolations'].class == Hash
|
87
|
+
@configs['environment_configs'][environment]['interpolations'].each do |interpolation, value|
|
88
|
+
if value.class != String && value.class != Integer && value.class != Float && value.class != Fixnum
|
89
|
+
environments_with_bad_interpolations << {environment => interpolation}
|
90
|
+
any_mistakes = true
|
91
|
+
end
|
92
|
+
end
|
93
|
+
else
|
94
|
+
environments_with_bad_interpolations << environment
|
95
|
+
any_mistakes = true
|
96
|
+
end
|
97
|
+
end
|
98
|
+
if any_mistakes
|
99
|
+
status[:status] = 'fail'
|
100
|
+
status[:error] = "Incorrectly formatted interpolations in Environments: #{environments_with_bad_interpolations}"
|
101
|
+
end
|
102
|
+
status
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module PropertyGenerator
|
2
|
+
require 'yaml'
|
3
|
+
class GlobalsLinter
|
4
|
+
|
5
|
+
def initialize(path, configs)
|
6
|
+
@configs = configs
|
7
|
+
@globals = {}
|
8
|
+
valid_paths = PropertyGenerator.valid_paths(path)
|
9
|
+
valid_paths.each do |file_path|
|
10
|
+
@globals[file_path] = YAML.load_file(file_path)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def run_globals_tests
|
15
|
+
tests = ['globals_load_as_hashes',
|
16
|
+
'globals_have_no_hashes_as_values',
|
17
|
+
'globals_are_defined_for_valid_environemnts',
|
18
|
+
]
|
19
|
+
results = PropertyGenerator.test_runner(self, tests)
|
20
|
+
results
|
21
|
+
end
|
22
|
+
|
23
|
+
def globals_load_as_hashes
|
24
|
+
status = {status: 'pass', error: ''}
|
25
|
+
non_hash_globals = []
|
26
|
+
@globals.each do |path, loaded|
|
27
|
+
if loaded.class != Hash
|
28
|
+
non_hash_globals << path
|
29
|
+
end
|
30
|
+
end
|
31
|
+
if non_hash_globals != []
|
32
|
+
status[:status] = 'fail'
|
33
|
+
status[:error] = "Global files #{non_hash_globals} are not being loaded as hashes."
|
34
|
+
end
|
35
|
+
status
|
36
|
+
end
|
37
|
+
|
38
|
+
def globals_have_no_hashes_as_values
|
39
|
+
status = {status: 'pass', error: ''}
|
40
|
+
globals_with_hash_props = []
|
41
|
+
@globals.each do |path, loaded|
|
42
|
+
if loaded.class == Hash
|
43
|
+
loaded.each do |key, value|
|
44
|
+
if value.class == Hash
|
45
|
+
globals_with_hash_props << {path => key}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
if globals_with_hash_props != []
|
51
|
+
status[:status] = 'fail'
|
52
|
+
status[:error] = "Globals #{globals_with_hash_props} have values that are hashes."
|
53
|
+
end
|
54
|
+
status
|
55
|
+
end
|
56
|
+
|
57
|
+
def globals_are_defined_for_valid_environemnts
|
58
|
+
status = {status: 'pass', error: ''}
|
59
|
+
ignore_list = ['globals.yml']
|
60
|
+
globals_with_invalid_environments = []
|
61
|
+
accounts = []
|
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
|
76
|
+
status[:status] = 'warn'
|
77
|
+
status[:error] = "Accounts list in config file is missing."
|
78
|
+
end
|
79
|
+
status
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module PropertyGenerator
|
2
|
+
require_relative 'config_linter.rb'
|
3
|
+
require_relative 'globals_linter.rb'
|
4
|
+
require_relative 'services_linter.rb'
|
5
|
+
require_relative 'report.rb'
|
6
|
+
class Linter
|
7
|
+
|
8
|
+
def initialize(path)
|
9
|
+
ignore_list = ['README.md']
|
10
|
+
invalid_paths = PropertyGenerator.invalid_paths(path, ignore_list)
|
11
|
+
@config_linter = PropertyGenerator::ConfigLinter.new(path+"/config/config.yml")
|
12
|
+
@globals_linter = PropertyGenerator::GlobalsLinter.new(path+"/globals/", @config_linter.configs)
|
13
|
+
@services_linter = PropertyGenerator::ServicesLinter.new(path+"/services/", @config_linter.configs)
|
14
|
+
@report = PropertyGenerator::Report.new(invalid_paths)
|
15
|
+
end
|
16
|
+
|
17
|
+
def run_tests
|
18
|
+
config_report = @config_linter.run_config_tests
|
19
|
+
globals_report = @globals_linter.run_globals_tests
|
20
|
+
service_linter = @services_linter.run_services_tests
|
21
|
+
@report.add_report(config_report)
|
22
|
+
@report.add_report(globals_report)
|
23
|
+
@report.add_report(service_linter)
|
24
|
+
end
|
25
|
+
|
26
|
+
def fail_check
|
27
|
+
@report.has_a_test_failed
|
28
|
+
end
|
29
|
+
|
30
|
+
def display_report
|
31
|
+
@report.display_report
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module PropertyGenerator
|
2
|
+
require 'terminal-table'
|
3
|
+
|
4
|
+
class Report
|
5
|
+
def initialize(files_failing_load)
|
6
|
+
@files_failing_load = files_failing_load
|
7
|
+
@full_report = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_report(report)
|
11
|
+
@full_report = @full_report.merge(report)
|
12
|
+
end
|
13
|
+
|
14
|
+
def display_report
|
15
|
+
if @files_failing_load != []
|
16
|
+
make_failing_to_load_table
|
17
|
+
end
|
18
|
+
make_pass_table
|
19
|
+
make_warn_table
|
20
|
+
make_fail_table
|
21
|
+
end
|
22
|
+
|
23
|
+
def has_a_test_failed
|
24
|
+
@full_report.each do |test, status|
|
25
|
+
if status[:status] == 'fail' || @files_failing_load != []
|
26
|
+
return true
|
27
|
+
else
|
28
|
+
return false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def make_failing_to_load_table
|
34
|
+
rows = []
|
35
|
+
@files_failing_load.each do |failed|
|
36
|
+
rows << [failed]
|
37
|
+
end
|
38
|
+
table = Terminal::Table.new :headings => ['Files'], :title => 'Files Failing to Load', :rows => rows, :style => {:width => 200}
|
39
|
+
puts table
|
40
|
+
end
|
41
|
+
|
42
|
+
def make_pass_table
|
43
|
+
rows = []
|
44
|
+
@full_report.each do |test,status|
|
45
|
+
if status[:status] == 'pass'
|
46
|
+
rows << [test.gsub('_', ' ')]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
table = Terminal::Table.new :headings => ['Test'], :title => 'Passing Tests', :rows => rows, :style => {:width => 200}
|
50
|
+
puts table
|
51
|
+
end
|
52
|
+
|
53
|
+
def make_warn_table
|
54
|
+
rows = []
|
55
|
+
@full_report.each do |test,status|
|
56
|
+
if status[:status] == 'warn'
|
57
|
+
rows << [test.gsub('_', ' '), status[:error].scan(/.{1,90}/).join("\n")]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
table = Terminal::Table.new :headings => ['Test', 'Error'], :title => 'Warning Tests', :rows => rows, :style => {:width => 200}
|
61
|
+
puts table
|
62
|
+
end
|
63
|
+
|
64
|
+
def make_fail_table
|
65
|
+
rows = []
|
66
|
+
@full_report.each do |test,status|
|
67
|
+
if status[:status] == 'fail'
|
68
|
+
rows << [test.gsub('_', ' '), status[:error].scan(/.{1,90}/).join("\n")]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
table = Terminal::Table.new :headings => ['Test', 'Error'], :title => 'Failing Tests', :rows => rows, :style => {:width => 200}
|
72
|
+
puts table
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
module PropertyGenerator
|
2
|
+
require 'yaml'
|
3
|
+
class ServicesLinter
|
4
|
+
|
5
|
+
def initialize(path, configs)
|
6
|
+
@configs = configs
|
7
|
+
@services = {}
|
8
|
+
valid_paths = PropertyGenerator.valid_paths(path)
|
9
|
+
valid_paths.each do |file_path|
|
10
|
+
@services[file_path] = YAML.load_file(file_path)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def run_services_tests
|
15
|
+
tests = ['services_have_accepted_keys',
|
16
|
+
'service_defaults_have_no_hashes_as_values',
|
17
|
+
'service_environments_match_config_environments',
|
18
|
+
'service_environments_have_no_hashes_as_values',
|
19
|
+
'service_encrypted_environments_match_config_environments',
|
20
|
+
'service_encrypted_fields_are_correct',
|
21
|
+
'service_encrypted_region_field_is_accepted']
|
22
|
+
results = PropertyGenerator.test_runner(self, tests)
|
23
|
+
results
|
24
|
+
end
|
25
|
+
|
26
|
+
def services_have_accepted_keys
|
27
|
+
status = {status: 'pass', error: ''}
|
28
|
+
accepted_keys = ['default', 'environments', 'encrypted']
|
29
|
+
services_with_unacceptable_keys = []
|
30
|
+
@services.each do |path, loaded|
|
31
|
+
loaded.keys.each do |service_key|
|
32
|
+
unless accepted_keys.include?(service_key)
|
33
|
+
services_with_unacceptable_keys << {path => service_key}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
if services_with_unacceptable_keys != []
|
38
|
+
status[:status] = 'fail'
|
39
|
+
status[:error] = "Service files: #{services_with_unacceptable_keys} have keys other than 'default', 'environments', or 'encrypted'."
|
40
|
+
end
|
41
|
+
status
|
42
|
+
end
|
43
|
+
|
44
|
+
def service_defaults_have_no_hashes_as_values
|
45
|
+
status = {status: 'pass', error: ''}
|
46
|
+
services_with_hashes_in_defaults = []
|
47
|
+
@services.each do |path, loaded|
|
48
|
+
unless loaded['default'] == nil
|
49
|
+
loaded['default'].each do |defaultkey, defaultvalue|
|
50
|
+
if defaultvalue.class == Hash
|
51
|
+
services_with_hashes_in_defaults << {path => defaultkey}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
if services_with_hashes_in_defaults != []
|
57
|
+
status[:status] = 'fail'
|
58
|
+
status[:error] = "Service files: #{services_with_hashes_in_defaults} have default properties with values as hashes."
|
59
|
+
end
|
60
|
+
status
|
61
|
+
end
|
62
|
+
|
63
|
+
def service_environments_match_config_environments
|
64
|
+
status = {status: 'pass', error: ''}
|
65
|
+
missmatched_environments = []
|
66
|
+
@services.each do |path, loaded|
|
67
|
+
if loaded['environments'] != nil
|
68
|
+
loaded['environments'].keys.each do |environment|
|
69
|
+
if @configs['environments'] != nil
|
70
|
+
unless @configs['environments'].include?(environment)
|
71
|
+
missmatched_environments << {path => environment}
|
72
|
+
end
|
73
|
+
else
|
74
|
+
status[:status] = 'warn'
|
75
|
+
status[:error] = "Environments list in config file is missing."
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
if missmatched_environments != []
|
81
|
+
status[:status] = 'warn'
|
82
|
+
status[:error] = "Service files: #{missmatched_environments} have environments not matching config list."
|
83
|
+
end
|
84
|
+
status
|
85
|
+
end
|
86
|
+
|
87
|
+
def service_environments_have_no_hashes_as_values
|
88
|
+
status = {status: 'pass', error: ''}
|
89
|
+
services_with_hashes_in_environments = []
|
90
|
+
@services.each do |path, loaded|
|
91
|
+
unless loaded['environments'] == nil
|
92
|
+
loaded['environments'].each do |environments, properties|
|
93
|
+
properties.each do |key, value|
|
94
|
+
if value.class == Hash
|
95
|
+
services_with_hashes_in_environments << {path => {environments => key}}
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
if services_with_hashes_in_environments != []
|
102
|
+
status[:status] = 'fail'
|
103
|
+
status[:error] = "Service files #{services_with_hashes_in_environments} have environment properties with values as hashes."
|
104
|
+
end
|
105
|
+
status
|
106
|
+
end
|
107
|
+
|
108
|
+
def service_encrypted_environments_match_config_environments
|
109
|
+
status = {status: 'pass', error: ''}
|
110
|
+
missmatched_environments = []
|
111
|
+
@services.each do |path, loaded|
|
112
|
+
if loaded['encrypted'] != nil
|
113
|
+
loaded['encrypted'].keys.each do |environment|
|
114
|
+
if @configs['environments'] != nil
|
115
|
+
unless @configs['environments'].include?(environment)
|
116
|
+
missmatched_environments << {path => environment}
|
117
|
+
end
|
118
|
+
else
|
119
|
+
status[:status] = 'warn'
|
120
|
+
status[:error] = "Environments list in config file is missing."
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
if missmatched_environments != []
|
126
|
+
status[:status] = 'warn'
|
127
|
+
status[:error] = "Service files: #{missmatched_environments} have encrypted environments not matching config list."
|
128
|
+
end
|
129
|
+
status
|
130
|
+
end
|
131
|
+
|
132
|
+
def service_encrypted_fields_are_correct
|
133
|
+
status = {status: 'pass', error: ''}
|
134
|
+
accepted_keys = ['region', 'encrypted']
|
135
|
+
services_with_unacceptable_keys = []
|
136
|
+
@services.each do |path, loaded|
|
137
|
+
if loaded['encrypted'] != nil
|
138
|
+
loaded['encrypted'].each do |environment, property|
|
139
|
+
if loaded['encrypted'][environment][property] != nil
|
140
|
+
loaded['encrypted'][environment][property].each do |ssm, keys|
|
141
|
+
unless loaded['encrypted'][environment][property]['$ssm'][keys].keys == accepted_keys
|
142
|
+
services_with_unacceptable_keys << {path => {environment => property}}
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
if services_with_unacceptable_keys != []
|
150
|
+
status[:status] = 'fail'
|
151
|
+
status[:error] = "Service files: #{services_with_unacceptable_keys} have encrypted properties with keys other than 'region' and 'encrypted'."
|
152
|
+
end
|
153
|
+
status
|
154
|
+
end
|
155
|
+
|
156
|
+
def service_encrypted_region_field_is_accepted
|
157
|
+
status = {status: 'pass', error: ''}
|
158
|
+
services_with_unacceptable_keys = []
|
159
|
+
@services.each do |path, loaded|
|
160
|
+
if loaded['encrypted'] != nil
|
161
|
+
loaded['encrypted'].each do |environment, property|
|
162
|
+
if loaded['encrypted'][environment][property] != nil
|
163
|
+
loaded['encrypted'][environment][property].each do |ssm, keys|
|
164
|
+
if @configs['environments'] != nil
|
165
|
+
unless @configs['environments'].include?(loaded['encrypted'][environment][property]['$ssm'][keys]['region'])
|
166
|
+
services_with_unacceptable_keys << {path => {environment => property}}
|
167
|
+
end
|
168
|
+
else
|
169
|
+
status[:status] = 'warn'
|
170
|
+
status[:error] = "Environments list in config file is missing."
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
if services_with_unacceptable_keys != []
|
178
|
+
status[:status] = 'warn'
|
179
|
+
status[:error] = "Service files: #{services_with_unacceptable_keys} have encrypted properties a region field not matching a declared environment in the configs."
|
180
|
+
end
|
181
|
+
status
|
182
|
+
end
|
183
|
+
|
184
|
+
|
185
|
+
end
|
186
|
+
end
|