inspec-core 2.2.112 → 2.3.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +42 -19
  3. data/README.md +1 -1
  4. data/docs/dev/integration-testing.md +31 -0
  5. data/docs/dev/plugins.md +4 -2
  6. data/docs/dsl_inspec.md +104 -4
  7. data/docs/plugins.md +57 -0
  8. data/docs/style.md +178 -0
  9. data/examples/plugins/inspec-resource-lister/Gemfile +12 -0
  10. data/examples/plugins/inspec-resource-lister/LICENSE +13 -0
  11. data/examples/plugins/inspec-resource-lister/README.md +62 -0
  12. data/examples/plugins/inspec-resource-lister/Rakefile +40 -0
  13. data/examples/plugins/inspec-resource-lister/inspec-resource-lister.gemspec +45 -0
  14. data/examples/plugins/inspec-resource-lister/lib/inspec-resource-lister.rb +16 -0
  15. data/examples/plugins/inspec-resource-lister/lib/inspec-resource-lister/cli_command.rb +70 -0
  16. data/examples/plugins/inspec-resource-lister/lib/inspec-resource-lister/plugin.rb +55 -0
  17. data/examples/plugins/inspec-resource-lister/lib/inspec-resource-lister/version.rb +10 -0
  18. data/examples/plugins/inspec-resource-lister/test/fixtures/README.md +24 -0
  19. data/examples/plugins/inspec-resource-lister/test/functional/README.md +18 -0
  20. data/examples/plugins/inspec-resource-lister/test/functional/inspec_resource_lister_test.rb +110 -0
  21. data/examples/plugins/inspec-resource-lister/test/helper.rb +26 -0
  22. data/examples/plugins/inspec-resource-lister/test/unit/README.md +17 -0
  23. data/examples/plugins/inspec-resource-lister/test/unit/cli_args_test.rb +64 -0
  24. data/examples/plugins/inspec-resource-lister/test/unit/plugin_def_test.rb +51 -0
  25. data/examples/profile/controls/example.rb +9 -8
  26. data/inspec-core.gemspec +1 -1
  27. data/lib/inspec/attribute_registry.rb +1 -1
  28. data/lib/inspec/globals.rb +4 -0
  29. data/lib/inspec/objects/control.rb +18 -3
  30. data/lib/inspec/plugin/v2.rb +14 -3
  31. data/lib/inspec/plugin/v2/activator.rb +7 -2
  32. data/lib/inspec/plugin/v2/installer.rb +426 -0
  33. data/lib/inspec/plugin/v2/loader.rb +137 -30
  34. data/lib/inspec/plugin/v2/registry.rb +13 -4
  35. data/lib/inspec/profile.rb +2 -1
  36. data/lib/inspec/reporters/json.rb +11 -1
  37. data/lib/inspec/resource.rb +6 -15
  38. data/lib/inspec/rule.rb +18 -9
  39. data/lib/inspec/runner_rspec.rb +1 -1
  40. data/lib/inspec/schema.rb +1 -0
  41. data/lib/inspec/version.rb +1 -1
  42. data/lib/plugins/inspec-plugin-manager-cli/README.md +6 -0
  43. data/lib/plugins/inspec-plugin-manager-cli/lib/inspec-plugin-manager-cli.rb +18 -0
  44. data/lib/plugins/inspec-plugin-manager-cli/lib/inspec-plugin-manager-cli/cli_command.rb +420 -0
  45. data/lib/plugins/inspec-plugin-manager-cli/lib/inspec-plugin-manager-cli/plugin.rb +12 -0
  46. data/lib/plugins/inspec-plugin-manager-cli/test/fixtures/config_dirs/empty/.gitkeep +0 -0
  47. data/lib/plugins/inspec-plugin-manager-cli/test/fixtures/plugins/inspec-egg-white-omelette/lib/inspec-egg-white-omelette.rb +2 -0
  48. data/lib/plugins/inspec-plugin-manager-cli/test/fixtures/plugins/inspec-egg-white-omelette/lib/inspec-egg-white-omelette/.gitkeep +0 -0
  49. data/lib/plugins/inspec-plugin-manager-cli/test/fixtures/plugins/inspec-wrong-structure/.gitkeep +0 -0
  50. data/lib/plugins/inspec-plugin-manager-cli/test/fixtures/plugins/wrong-name/lib/wrong-name.rb +1 -0
  51. data/lib/plugins/inspec-plugin-manager-cli/test/fixtures/plugins/wrong-name/lib/wrong-name/.gitkeep +0 -0
  52. data/lib/plugins/inspec-plugin-manager-cli/test/functional/inspec-plugin_test.rb +651 -0
  53. data/lib/plugins/inspec-plugin-manager-cli/test/unit/cli_args_test.rb +71 -0
  54. data/lib/plugins/inspec-plugin-manager-cli/test/unit/plugin_def_test.rb +20 -0
  55. data/lib/plugins/shared/core_plugin_test_helper.rb +101 -2
  56. data/lib/plugins/things-for-train-integration.rb +14 -0
  57. data/lib/resources/port.rb +10 -6
  58. metadata +38 -11
  59. data/docs/ruby_usage.md +0 -204
@@ -0,0 +1,12 @@
1
+ module InspecPlugins
2
+ module PluginManager
3
+ class Plugin < Inspec.plugin(2)
4
+ plugin_name :'inspec-plugin-manager-cli'
5
+
6
+ cli_command :plugin do
7
+ require_relative 'cli'
8
+ InspecPlugins::PluginManager::CliCommand
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,2 @@
1
+ # Despite having the right file structure, and the right name,
2
+ # this is not actually an InSpec plugin.
@@ -0,0 +1 @@
1
+ # This should should never even be loaded.
@@ -0,0 +1,651 @@
1
+ #=========================================================================================#
2
+ # `inspec plugin SUBCOMMAND` facility
3
+ #=========================================================================================#
4
+ require_relative '../../../shared/core_plugin_test_helper.rb'
5
+
6
+ #-----------------------------------------------------------------------------------------#
7
+ # utilities
8
+ #-----------------------------------------------------------------------------------------#
9
+ module PluginManagerHelpers
10
+ let(:project_repo_path) { File.expand_path(File.join(__FILE__, '..', '..', '..')) }
11
+ let(:project_fixtures_path) { File.join(project_repo_path, 'test', 'fixtures') }
12
+ let(:project_config_dirs_path) { File.join(project_fixtures_path, 'config_dirs') }
13
+ let(:empty_config_dir_path) { File.join(project_config_dirs_path, 'empty') }
14
+
15
+ let(:list_after_run) do
16
+ Proc.new do |run_result, tmp_dir|
17
+ # After installing/uninstalling/whatevering, run list with config in the same dir, and capture it.
18
+ run_result.payload.list_result = run_inspec_process('plugin list', env: { INSPEC_CONFIG_DIR: tmp_dir })
19
+ end
20
+ end
21
+
22
+ def copy_in_project_config_dir(fixture_name, dest = nil)
23
+ src = Dir.glob(File.join(project_config_dirs_path, fixture_name, '*'))
24
+ dest ||= File.join(project_config_dirs_path, 'empty')
25
+ src.each { |path| FileUtils.cp_r(path, dest) }
26
+ end
27
+
28
+ def copy_in_core_config_dir(fixture_name, dest = nil)
29
+ src = Dir.glob(File.join(core_config_dir_path, fixture_name, '*'))
30
+ dest ||= File.join(project_config_dirs_path, 'empty')
31
+ src.each { |path| FileUtils.cp_r(path, dest) }
32
+ end
33
+
34
+ def clear_empty_config_dir
35
+ Dir.glob(File.join(project_config_dirs_path, 'empty', '*')).each do |path|
36
+ next if path.end_with? '.gitkeep'
37
+ FileUtils.rm_rf(path)
38
+ end
39
+ end
40
+
41
+ def teardown
42
+ clear_empty_config_dir
43
+ end
44
+ end
45
+
46
+ #-----------------------------------------------------------------------------------------#
47
+ # inspec help
48
+ #-----------------------------------------------------------------------------------------#
49
+ class PluginManagerCliHelp < MiniTest::Test
50
+ include CorePluginFunctionalHelper
51
+
52
+ # Main inspec help subcommand listing
53
+ def test_inspec_help_includes_plugin
54
+ result = run_inspec_process_with_this_plugin('help')
55
+ assert_includes result.stdout, 'inspec plugin'
56
+ end
57
+
58
+ # inspec plugin help subcommand listing
59
+ def test_inspec_plugin_help_includes_plugin
60
+ result = run_inspec_process_with_this_plugin('plugin help')
61
+ assert_includes result.stdout, 'inspec plugin list'
62
+ assert_includes result.stdout, 'inspec plugin search'
63
+ assert_includes result.stdout, 'inspec plugin install'
64
+ assert_includes result.stdout, 'inspec plugin update'
65
+ assert_includes result.stdout, 'inspec plugin uninstall'
66
+ end
67
+ end
68
+
69
+ #-----------------------------------------------------------------------------------------#
70
+ # inspec plugin list
71
+ #-----------------------------------------------------------------------------------------#
72
+ class PluginManagerCliList < MiniTest::Test
73
+ include CorePluginFunctionalHelper
74
+ include PluginManagerHelpers
75
+
76
+ def test_list_when_no_user_plugins_installed
77
+ result = run_inspec_process_with_this_plugin('plugin list')
78
+ assert_equal 0, result.exit_status, 'exist status must be 0'
79
+ assert_includes result.stdout, '0 plugin(s) total', 'Empty list should include zero count'
80
+ end
81
+
82
+ def test_list_all_when_no_user_plugins_installed
83
+ result = run_inspec_process_with_this_plugin('plugin list --all')
84
+ assert_equal 0, result.exit_status, 'exist status must be 0'
85
+ assert_includes result.stdout, '6 plugin(s) total', '--all list should find six'
86
+ assert_includes result.stdout, 'inspec-plugin-manager-cli', '--all list should find inspec-plugin-manager-cli'
87
+ assert_includes result.stdout, 'habitat', '--all list should find habitat'
88
+
89
+ result = run_inspec_process_with_this_plugin('plugin list -a')
90
+ assert_equal 0, result.exit_status, 'exist status must be 0'
91
+ assert_includes result.stdout, '6 plugin(s) total', '-a list should find six'
92
+ end
93
+
94
+ def test_list_when_gem_and_path_plugins_installed
95
+ pre_block = Proc.new do |plugin_statefile_data, tmp_dir|
96
+ plugin_statefile_data.clear # Signal not to write a file, we'll provide one.
97
+ copy_in_core_config_dir('test-fixture-1-float', tmp_dir)
98
+ end
99
+
100
+ result = run_inspec_process_with_this_plugin('plugin list', pre_run: pre_block)
101
+ assert_equal 0, result.exit_status, 'exist status must be 0'
102
+ assert_includes result.stdout, '2 plugin(s) total', 'gem+path should show two plugins'
103
+
104
+ # Plugin Name Version Via ApiVer
105
+ # -------------------------------------------------------
106
+ # inspec-meaning-of-life src path 2
107
+ # inspec-test-fixture 0.1.0 gem 2
108
+ # -------------------------------------------------------
109
+ # 2 plugin(s) total
110
+ gem_line = result.stdout.split("\n").grep(/gem/).first
111
+ assert_match(/\s*inspec-\S+\s+\d+\.\d+\.\d+\s+gem\s+2/, gem_line)
112
+ path_line = result.stdout.split("\n").grep(/path/).first
113
+ assert_match(/\s*inspec-\S+\s+src\s+path\s+2/, path_line)
114
+ end
115
+
116
+ def test_list_when_a_train_plugin_is_installed
117
+ pre_block = Proc.new do |plugin_statefile_data, tmp_dir|
118
+ plugin_statefile_data.clear # Signal not to write a file, we'll provide one.
119
+ copy_in_core_config_dir('train-test-fixture', tmp_dir)
120
+ end
121
+
122
+ result = run_inspec_process_with_this_plugin('plugin list', pre_run: pre_block)
123
+ assert_equal 0, result.exit_status, 'exist status must be 0'
124
+ assert_includes result.stdout, '1 plugin(s) total', 'list train should show one plugins'
125
+
126
+ # Plugin Name Version Via ApiVer
127
+ # -------------------------------------------------------
128
+ # train-test-fixture 0.1.0 gem train-1
129
+ # -------------------------------------------------------
130
+ # 1 plugin(s) total
131
+ train_line = result.stdout.split("\n").grep(/train/).first
132
+ assert_includes(train_line, 'train-test-fixture')
133
+ assert_includes(train_line, '0.1.0')
134
+ assert_includes(train_line, 'gem')
135
+ assert_includes(train_line, 'train-1')
136
+ end
137
+ end
138
+
139
+ #-----------------------------------------------------------------------------------------#
140
+ # inspec plugin search
141
+ #-----------------------------------------------------------------------------------------#
142
+ class PluginManagerCliSearch < MiniTest::Test
143
+ include CorePluginFunctionalHelper
144
+ include PluginManagerHelpers
145
+
146
+ def test_search_for_a_real_gem_with_full_name_no_options
147
+ result = run_inspec_process('plugin search inspec-test-fixture')
148
+ assert_equal 0, result.exit_status, 'Search should exit 0 on a hit'
149
+ assert_includes result.stdout, 'inspec-test-fixture', 'Search result should contain the gem name'
150
+ assert_includes result.stdout, '1 plugin(s) found', 'Search result should find 1 plugin'
151
+ line = result.stdout.split("\n").grep(/inspec-test-fixture/).first
152
+ assert_match(/\s*inspec-test-fixture\s+\((\d+\.\d+\.\d+){1}\)/,line,'Plugin line should include name and exactly one version')
153
+ end
154
+
155
+ def test_search_for_a_real_gem_with_stub_name_no_options
156
+ result = run_inspec_process('plugin search inspec-test-')
157
+ assert_equal 0, result.exit_status, 'Search should exit 0 on a hit'
158
+ assert_includes result.stdout, 'inspec-test-fixture', 'Search result should contain the gem name'
159
+ assert_includes result.stdout, '1 plugin(s) found', 'Search result should find 1 plugin'
160
+
161
+ line = result.stdout.split("\n").grep(/inspec-test-fixture/).first
162
+ assert_match(/\s*inspec-test-fixture\s+\((\d+\.\d+\.\d+){1}\)/,line,'Plugin line should include name and exactly one version')
163
+ end
164
+
165
+ def test_search_for_a_real_gem_with_full_name_and_exact_option
166
+ result = run_inspec_process('plugin search --exact inspec-test-fixture')
167
+ assert_equal 0, result.exit_status, 'Search should exit 0 on a hit'
168
+ assert_includes result.stdout, 'inspec-test-fixture', 'Search result should contain the gem name'
169
+ assert_includes result.stdout, '1 plugin(s) found', 'Search result should find 1 plugin'
170
+
171
+ result = run_inspec_process('plugin search -e inspec-test-fixture')
172
+ assert_equal 0, result.exit_status, 'Search should exit 0 on a hit'
173
+ end
174
+
175
+ def test_search_for_a_real_gem_with_stub_name_and_exact_option
176
+ result = run_inspec_process('plugin search --exact inspec-test-')
177
+ assert_equal 2, result.exit_status, 'Search should exit 2 on a miss'
178
+ assert_includes result.stdout, '0 plugin(s) found', 'Search result should find 0 plugins'
179
+
180
+ result = run_inspec_process('plugin search -e inspec-test-')
181
+ assert_equal 2, result.exit_status, 'Search should exit 2 on a miss'
182
+ end
183
+
184
+ def test_search_for_a_real_gem_with_full_name_and_all_option
185
+ result = run_inspec_process('plugin search --all inspec-test-fixture')
186
+ assert_equal 0, result.exit_status, 'Search should exit 0 on a hit'
187
+ assert_includes result.stdout, 'inspec-test-fixture', 'Search result should contain the gem name'
188
+ assert_includes result.stdout, '1 plugin(s) found', 'Search result should find 1 plugin'
189
+
190
+ line = result.stdout.split("\n").grep(/inspec-test-fixture/).first
191
+ assert_match(/\s*inspec-test-fixture\s+\((\d+\.\d+\.\d+(,\s)?){2,}\)/,line,'Plugin line should include name and at least two versions')
192
+
193
+ result = run_inspec_process('plugin search -a inspec-test-fixture')
194
+ assert_equal 0, result.exit_status, 'Search should exit 0 on a hit'
195
+ end
196
+
197
+ def test_search_for_a_gem_with_missing_prefix
198
+ result = run_inspec_process('plugin search test-fixture')
199
+ assert_equal 1, result.exit_status, 'Search should exit 1 on user error'
200
+ assert_includes result.stdout, "All inspec plugins must begin with either 'inspec-' or 'train-'"
201
+ end
202
+
203
+ def test_search_for_a_gem_that_does_not_exist
204
+ result = run_inspec_process('plugin search inspec-test-fixture-nonesuch')
205
+ assert_equal 2, result.exit_status, 'Search should exit 2 on a miss'
206
+ assert_includes result.stdout, '0 plugin(s) found', 'Search result should find 0 plugins'
207
+ end
208
+
209
+ def test_search_for_a_real_gem_with_full_name_no_options_and_train_name
210
+ result = run_inspec_process('plugin search train-test-fixture')
211
+ assert_equal 0, result.exit_status, 'Search should exit 0 on a hit'
212
+ assert_includes result.stdout, 'train-test-fixture', 'Search result should contain the gem name'
213
+ assert_includes result.stdout, '1 plugin(s) found', 'Search result should find 1 plugin'
214
+ line = result.stdout.split("\n").grep(/train-test-fixture/).first
215
+ assert_match(/\s*train-test-fixture\s+\((\d+\.\d+\.\d+){1}\)/,line,'Plugin line should include name and exactly one version')
216
+ end
217
+
218
+ end
219
+
220
+ #-----------------------------------------------------------------------------------------#
221
+ # inspec plugin install
222
+ #-----------------------------------------------------------------------------------------#
223
+ class PluginManagerCliInstall < MiniTest::Test
224
+ include CorePluginFunctionalHelper # gives us instance methods, like `let` aliases inside test methods
225
+ extend CorePluginFunctionalHelper # gives us class methods, like `let` aliases out here outside test methods
226
+
227
+ include PluginManagerHelpers
228
+ ruby_abi_version = (Gem.ruby_version.segments[0, 2] << 0).join('.')
229
+ # Test multiple hueristics of the path-mode install.
230
+ # These are all positive tests; they should resolve the entry point to the same path in each case.
231
+ {
232
+ 'is_perfect' => {
233
+ given: File.join(core_fixture_plugins_path, 'inspec-test-fixture', 'lib', 'inspec-test-fixture.rb'),
234
+ },
235
+ 'refers_to_the_entry_point_with_no_extension' => {
236
+ given: File.join(core_fixture_plugins_path, 'inspec-test-fixture', 'lib', 'inspec-test-fixture'),
237
+ },
238
+ 'refers_to_the_src_root_of_a_plugin' => {
239
+ given: File.join(core_fixture_plugins_path, 'inspec-test-fixture'),
240
+ },
241
+ 'refers_to_a_versioned_gem_install' => {
242
+ given: File.join(core_config_dir_path, 'test-fixture-1-float', 'gems', ruby_abi_version, 'gems', 'inspec-test-fixture-0.1.0', 'lib', 'inspec-test-fixture.rb'),
243
+ resolved_path: File.join(core_config_dir_path, 'test-fixture-1-float', 'gems', ruby_abi_version, 'gems', 'inspec-test-fixture-0.1.0', 'lib', 'inspec-test-fixture.rb'),
244
+ },
245
+ 'refers_to_a_versioned_gem_install_missing_extension' => {
246
+ given: File.join(core_config_dir_path, 'test-fixture-1-float', 'gems', ruby_abi_version, 'gems', 'inspec-test-fixture-0.1.0', 'lib', 'inspec-test-fixture'),
247
+ resolved_path: File.join(core_config_dir_path, 'test-fixture-1-float', 'gems', ruby_abi_version, 'gems', 'inspec-test-fixture-0.1.0', 'lib', 'inspec-test-fixture.rb'),
248
+ },
249
+ 'refers_to_a_relative_path' => {
250
+ given: File.join('test', 'unit', 'mock', 'plugins', 'inspec-test-fixture', 'lib', 'inspec-test-fixture.rb'),
251
+ },
252
+ 'refers_to_a_train_plugin' => {
253
+ given: File.join(core_config_dir_path, 'train-test-fixture', 'gems', ruby_abi_version, 'gems', 'train-test-fixture-0.1.0', 'lib', 'train-test-fixture.rb'),
254
+ plugin_name: 'train-test-fixture',
255
+ resolved_path: File.join(core_config_dir_path, 'train-test-fixture', 'gems', ruby_abi_version, 'gems', 'train-test-fixture-0.1.0', 'lib', 'train-test-fixture.rb'),
256
+ },
257
+ }.each do |test_name, fixture_info|
258
+ define_method(('test_install_from_path_when_path_' + test_name).to_sym) do
259
+ fixture_info = {
260
+ plugin_name: 'inspec-test-fixture',
261
+ resolved_path: File.join(core_fixture_plugins_path, 'inspec-test-fixture', 'lib', 'inspec-test-fixture.rb')
262
+ }.merge(fixture_info)
263
+
264
+ install_result = run_inspec_process_with_this_plugin("plugin install #{fixture_info[:given]}", post_run: list_after_run)
265
+
266
+ assert_empty install_result.stderr
267
+ assert_equal 0, install_result.exit_status, 'Exit status should be 0'
268
+
269
+ # Check UX messaging
270
+ success_message = install_result.stdout.split("\n").grep(/installed/).last
271
+ refute_nil success_message, 'Should find a success message at the end'
272
+ assert_includes success_message, fixture_info[:plugin_name]
273
+ assert_includes success_message, 'plugin installed via source path reference'
274
+
275
+ # Check round-trip UX via list
276
+ list_result = install_result.payload.list_result
277
+ itf_line = list_result.stdout.split("\n").grep(Regexp.new(fixture_info[:plugin_name])).first
278
+ refute_nil itf_line, 'plugin name should now appear in the output of inspec list'
279
+ assert_match(/\s*(inspec|train)-test-fixture\s+src\s+path\s+/, itf_line, 'list output should show that it is a path installation')
280
+
281
+ # Check plugin statefile. Extra important in this case, since all should resolve to the same entry point.
282
+ plugin_data = install_result.payload.plugin_data
283
+ entry = plugin_data['plugins'].detect { |e| e['name'] == fixture_info[:plugin_name] }
284
+ assert_equal fixture_info[:resolved_path], entry['installation_path'], 'Regardless of input, the entry point should be correct.'
285
+ end
286
+ end
287
+
288
+ def test_fail_install_from_nonexistant_path
289
+ bad_path = File.join(project_fixtures_path, 'none', 'such', 'inspec-test-fixture-nonesuch.rb')
290
+ install_result = run_inspec_process_with_this_plugin("plugin install #{bad_path}")
291
+
292
+ assert_empty install_result.stderr
293
+ assert_equal 1, install_result.exit_status, 'Exit status should be 1'
294
+
295
+ error_message = install_result.stdout.split("\n").last
296
+ assert_includes error_message, "No such source code path"
297
+ assert_includes error_message, 'inspec-test-fixture-nonesuch.rb'
298
+ assert_includes error_message, 'installation failed'
299
+ end
300
+
301
+ def test_fail_install_from_path_with_wrong_name
302
+ bad_path = File.join(project_fixtures_path, 'plugins', 'wrong-name', 'lib', 'wrong-name.rb')
303
+ install_result = run_inspec_process_with_this_plugin("plugin install #{bad_path}")
304
+
305
+ assert_empty install_result.stderr
306
+ assert_equal 1, install_result.exit_status, 'Exit status should be 1'
307
+
308
+ error_message = install_result.stdout.split("\n").last
309
+ assert_includes error_message, "Invalid plugin name"
310
+ assert_includes error_message, 'wrong-name'
311
+ assert_includes error_message, "All inspec plugins must begin with either 'inspec-' or 'train-'"
312
+ assert_includes error_message, 'installation failed'
313
+ end
314
+
315
+ def test_fail_install_from_path_when_it_is_not_a_plugin
316
+ bad_path = File.join(project_fixtures_path, 'plugins', 'inspec-egg-white-omelette', 'lib', 'inspec-egg-white-omelette.rb')
317
+ install_result = run_inspec_process_with_this_plugin("plugin install #{bad_path}")
318
+
319
+ assert_empty install_result.stderr
320
+ assert_equal 1, install_result.exit_status, 'Exit status should be 1'
321
+
322
+ error_message = install_result.stdout.split("\n").last
323
+ assert_includes error_message, "Does not appear to be a plugin"
324
+ assert_includes error_message, 'inspec-egg-white-omelette'
325
+ assert_includes error_message, "After probe-loading the supposed plugin, it did not register"
326
+ assert_includes error_message, "Ensure something inherits from 'Inspec.plugin(2)'"
327
+ assert_includes error_message, 'installation failed'
328
+ end
329
+
330
+ def test_fail_install_from_path_when_it_is_already_installed
331
+ plugin_path = File.join(core_fixture_plugins_path, 'inspec-test-fixture', 'lib', 'inspec-test-fixture.rb')
332
+ pre_block = Proc.new do |plugin_data, _tmp_dir|
333
+ plugin_data["plugins"] << {
334
+ "name" => "inspec-test-fixture",
335
+ "installation_type" => "path",
336
+ "installation_path" => plugin_path,
337
+ }
338
+ end
339
+
340
+ install_result = run_inspec_process_with_this_plugin("plugin install #{plugin_path}", pre_run: pre_block)
341
+ assert_empty install_result.stderr
342
+ assert_equal 2, install_result.exit_status, 'Exit status on second install should be 2'
343
+
344
+ error_message = install_result.stdout.split("\n").last
345
+ assert_includes error_message, "Plugin already installed"
346
+ assert_includes error_message, 'inspec-test-fixture'
347
+ assert_includes error_message, "Use 'inspec plugin list' to see previously installed plugin"
348
+ assert_includes error_message, 'installation failed'
349
+ end
350
+
351
+ def test_fail_install_from_path_when_the_dir_structure_is_wrong
352
+ bad_path = File.join(project_fixtures_path, 'plugins', 'inspec-wrong-structure')
353
+ install_result = run_inspec_process_with_this_plugin("plugin install #{bad_path}")
354
+
355
+ assert_empty install_result.stderr
356
+ assert_equal 1, install_result.exit_status, 'Exit status should be 1'
357
+
358
+ error_message = install_result.stdout.split("\n").last
359
+ assert_includes error_message, "Unrecognizable plugin structure"
360
+ assert_includes error_message, 'inspec-wrong-structure'
361
+ assert_includes error_message, ' When installing from a path, please provide the path of the entry point file'
362
+ assert_includes error_message, 'installation failed'
363
+ end
364
+
365
+ def test_install_from_gemfile
366
+ fixture_gemfile_path = File.join(core_fixture_plugins_path, 'inspec-test-fixture', 'pkg', 'inspec-test-fixture-0.1.0.gem')
367
+ install_result = run_inspec_process_with_this_plugin("plugin install #{fixture_gemfile_path}", post_run: list_after_run)
368
+
369
+ assert_empty install_result.stderr
370
+ assert_equal 0, install_result.exit_status, 'Exit status should be 0'
371
+
372
+ success_message = install_result.stdout.split("\n").grep(/installed/).last
373
+ refute_nil success_message, 'Should find a success message at the end'
374
+ assert_includes success_message, 'inspec-test-fixture'
375
+ assert_includes success_message, '0.1.0'
376
+ assert_includes success_message, 'installed from local .gem file'
377
+
378
+ list_result = install_result.payload.list_result
379
+ itf_line = list_result.stdout.split("\n").grep(/inspec-test-fixture/).first
380
+ refute_nil itf_line, 'inspec-test-fixture should now appear in the output of inspec list'
381
+ assert_match(/\s*inspec-test-fixture\s+0.1.0\s+gem\s+/, itf_line, 'list output should show that it is a gem installation with version')
382
+ end
383
+
384
+ def test_fail_install_from_nonexistant_gemfile
385
+ bad_path = File.join(project_fixtures_path, 'none', 'such', 'inspec-test-fixture-nonesuch-0.3.0.gem')
386
+ install_result = run_inspec_process_with_this_plugin("plugin install #{bad_path}")
387
+
388
+ assert_empty install_result.stderr
389
+ assert_equal 1, install_result.exit_status, 'Exit status should be 1'
390
+ assert_match(/No such plugin gem file .+ - installation failed./, install_result.stdout)
391
+ end
392
+
393
+ def test_install_from_rubygems_latest
394
+ install_result = run_inspec_process_with_this_plugin('plugin install inspec-test-fixture', post_run: list_after_run)
395
+
396
+ assert_empty install_result.stderr
397
+ assert_equal 0, install_result.exit_status, 'Exit status should be 0'
398
+
399
+ success_message = install_result.stdout.split("\n").grep(/installed/).last
400
+ refute_nil success_message, 'Should find a success message at the end'
401
+ assert_includes success_message, 'inspec-test-fixture'
402
+ assert_includes success_message, '0.2.0'
403
+ assert_includes success_message, 'installed from rubygems.org'
404
+
405
+ list_result = install_result.payload.list_result
406
+ itf_line = list_result.stdout.split("\n").grep(/inspec-test-fixture/).first
407
+ refute_nil itf_line, 'inspec-test-fixture should now appear in the output of inspec list'
408
+ assert_match(/\s*inspec-test-fixture\s+0.2.0\s+gem\s+/, itf_line, 'list output should show that it is a gem installation with version')
409
+ end
410
+
411
+ def test_fail_install_from_nonexistant_remote_rubygem
412
+ install_result = run_inspec_process_with_this_plugin('plugin install inspec-test-fixture-nonesuch')
413
+
414
+ assert_empty install_result.stderr
415
+ assert_equal 1, install_result.exit_status, 'Exit status should be 1'
416
+ assert_match(/No such plugin gem .+ could be found on rubygems.org - installation failed./, install_result.stdout)
417
+ end
418
+
419
+ def test_install_from_rubygems_with_pinned_version
420
+ install_result = run_inspec_process_with_this_plugin('plugin install inspec-test-fixture -v 0.1.0', post_run: list_after_run)
421
+
422
+ assert_empty install_result.stderr
423
+ assert_equal 0, install_result.exit_status, 'Exit status should be 0'
424
+
425
+ success_message = install_result.stdout.split("\n").grep(/installed/).last
426
+ refute_nil success_message, 'Should find a success message at the end'
427
+ assert_includes success_message, 'inspec-test-fixture'
428
+ assert_includes success_message, '0.1.0'
429
+ assert_includes success_message, 'installed from rubygems.org'
430
+
431
+ list_result = install_result.payload.list_result
432
+ itf_line = list_result.stdout.split("\n").grep(/inspec-test-fixture/).first
433
+ refute_nil itf_line, 'inspec-test-fixture should now appear in the output of inspec list'
434
+ assert_match(/\s*inspec-test-fixture\s+0.1.0\s+gem\s+/, itf_line, 'list output should show that it is a gem installation with version')
435
+ end
436
+
437
+ def test_fail_install_from_nonexistant_rubygem_version
438
+ install_result = run_inspec_process_with_this_plugin('plugin install inspec-test-fixture -v 99.99.99')
439
+
440
+ assert_empty install_result.stderr
441
+ assert_equal 1, install_result.exit_status, 'Exit status should be 1'
442
+
443
+ fail_message = install_result.stdout.split("\n").grep(/failed/).last
444
+ refute_nil fail_message, 'Should find a failure message at the end'
445
+ assert_includes fail_message, 'inspec-test-fixture'
446
+ assert_includes fail_message, '99.99.99'
447
+ assert_includes fail_message, 'no such version'
448
+ assert_includes fail_message, 'on rubygems.org'
449
+ end
450
+
451
+ def test_refuse_install_when_missing_prefix
452
+ install_result = run_inspec_process_with_this_plugin('plugin install test-fixture')
453
+
454
+ assert_empty install_result.stderr
455
+ assert_equal 1, install_result.exit_status, 'Exit status should be 1'
456
+
457
+ fail_message = install_result.stdout.split("\n").grep(/failed/).last
458
+ refute_nil fail_message, 'Should find a failure message at the end'
459
+ assert_includes fail_message, 'test-fixture'
460
+ assert_includes fail_message, "All inspec plugins must begin with either 'inspec-' or 'train-'"
461
+ end
462
+
463
+ def test_refuse_install_when_already_installed_same_version
464
+ pre_block = Proc.new do |plugin_statefile_data, tmp_dir|
465
+ plugin_statefile_data.clear # Signal not to write a file, we'll provide one.
466
+ copy_in_core_config_dir('test-fixture-2-float', tmp_dir)
467
+ end
468
+
469
+ install_result = run_inspec_process_with_this_plugin('plugin install inspec-test-fixture', pre_run: pre_block)
470
+ assert_empty install_result.stderr
471
+ assert_equal 2, install_result.exit_status, 'Exit status should be 2'
472
+
473
+ refusal_message = install_result.stdout.split("\n").grep(/refusing/).last
474
+ refute_nil refusal_message, 'Should find a failure message at the end'
475
+ assert_includes refusal_message, 'inspec-test-fixture'
476
+ assert_includes refusal_message, '0.2.0'
477
+ assert_includes refusal_message, 'Plugin already installed at latest version'
478
+ end
479
+
480
+ def test_refuse_install_when_already_installed_can_update
481
+ pre_block = Proc.new do |plugin_statefile_data, tmp_dir|
482
+ plugin_statefile_data.clear # Signal not to write a file, we'll provide one.
483
+ copy_in_core_config_dir('test-fixture-1-float', tmp_dir)
484
+ end
485
+
486
+ install_result = run_inspec_process_with_this_plugin('plugin install inspec-test-fixture', pre_run: pre_block)
487
+ assert_empty install_result.stderr
488
+ assert_equal 2, install_result.exit_status, 'Exit status should be 2'
489
+
490
+ refusal_message = install_result.stdout.split("\n").grep(/refusing/).last
491
+ refute_nil refusal_message, 'Should find a failure message at the end'
492
+ assert_includes refusal_message, 'inspec-test-fixture'
493
+ assert_includes refusal_message, '0.1.0'
494
+ assert_includes refusal_message, '0.2.0'
495
+ assert_includes refusal_message, 'Update required'
496
+ assert_includes refusal_message, 'inspec plugin update'
497
+ end
498
+
499
+ def test_install_from_rubygems_latest_with_train_plugin
500
+ install_result = run_inspec_process_with_this_plugin('plugin install train-test-fixture', post_run: list_after_run)
501
+
502
+ assert_empty install_result.stderr
503
+ assert_equal 0, install_result.exit_status, 'Exit status should be 0'
504
+
505
+ success_message = install_result.stdout.split("\n").grep(/installed/).last
506
+ refute_nil success_message, 'Should find a success message at the end'
507
+ assert_includes success_message, 'train-test-fixture'
508
+ assert_includes success_message, '0.1.0'
509
+ assert_includes success_message, 'installed from rubygems.org'
510
+
511
+ list_result = install_result.payload.list_result
512
+ itf_line = list_result.stdout.split("\n").grep(/train-test-fixture/).first
513
+ refute_nil itf_line, 'train-test-fixture should now appear in the output of inspec list'
514
+ assert_match(/\s*train-test-fixture\s+0.1.0\s+gem\s+/, itf_line, 'list output should show that it is a gem installation with version')
515
+ end
516
+ end
517
+
518
+
519
+ #-----------------------------------------------------------------------------------------#
520
+ # inspec plugin update
521
+ #-----------------------------------------------------------------------------------------#
522
+ class PluginManagerCliUpdate < MiniTest::Test
523
+ include CorePluginFunctionalHelper
524
+ include PluginManagerHelpers
525
+
526
+ def test_when_a_plugin_can_be_updated
527
+ pre_block = Proc.new do |plugin_statefile_data, tmp_dir|
528
+ plugin_statefile_data.clear # Signal not to write a file, we'll provide one.
529
+ copy_in_core_config_dir('test-fixture-1-float', tmp_dir)
530
+ end
531
+
532
+ update_result = run_inspec_process_with_this_plugin('plugin update inspec-test-fixture', pre_run: pre_block, post_run: list_after_run)
533
+ assert_empty update_result.stderr
534
+ assert_equal 0, update_result.exit_status, 'Exit status should be 0'
535
+
536
+ success_message = update_result.stdout.split("\n").grep(/updated/).last
537
+ refute_nil success_message, 'Should find a success message at the end'
538
+ assert_includes success_message, 'inspec-test-fixture'
539
+ assert_includes success_message, '0.1.0'
540
+ assert_includes success_message, '0.2.0'
541
+ assert_includes success_message, 'updated from rubygems.org'
542
+
543
+ list_result = update_result.payload.list_result
544
+ itf_line = list_result.stdout.split("\n").grep(/inspec-test-fixture/).first
545
+ refute_nil itf_line, 'inspec-test-fixture should appear in the output of inspec list'
546
+ assert_match(/\s*inspec-test-fixture\s+0.2.0\s+gem\s+/, itf_line, 'list output should show that it is a gem installation with version 0.2.0')
547
+ end
548
+
549
+ def test_refuse_update_when_already_current
550
+ pre_block = Proc.new do |plugin_statefile_data, tmp_dir|
551
+ plugin_statefile_data.clear # Signal not to write a file, we'll provide one.
552
+ copy_in_core_config_dir('test-fixture-2-float', tmp_dir)
553
+ end
554
+
555
+ update_result = run_inspec_process_with_this_plugin('plugin update inspec-test-fixture', pre_run: pre_block)
556
+ assert_empty update_result.stderr
557
+ assert_equal 2, update_result.exit_status, 'Exit status should be 2'
558
+
559
+ refusal_message = update_result.stdout.split("\n").grep(/refusing/).last
560
+ refute_nil refusal_message, 'Should find a failure message at the end'
561
+ assert_includes refusal_message, 'inspec-test-fixture'
562
+ assert_includes refusal_message, '0.2.0'
563
+ assert_includes refusal_message, 'Already installed at latest version'
564
+ end
565
+
566
+ def test_fail_update_from_nonexistant_gem
567
+ update_result = run_inspec_process_with_this_plugin('plugin update inspec-test-fixture-nonesuch')
568
+
569
+ assert_empty update_result.stderr
570
+ assert_equal 1, update_result.exit_status, 'Exit status should be 1'
571
+ assert_match(/No such plugin installed: .+ - update failed/, update_result.stdout)
572
+ end
573
+
574
+ def test_fail_update_path
575
+ pre_block = Proc.new do |plugin_statefile_data, tmp_dir|
576
+ plugin_statefile_data.clear # Signal not to write a file, we'll provide one.
577
+ copy_in_core_config_dir('meaning_by_path', tmp_dir)
578
+ end
579
+
580
+ update_result = run_inspec_process_with_this_plugin('plugin update inspec-meaning-of-life', pre_run: pre_block)
581
+ assert_empty update_result.stderr
582
+ assert_equal 2, update_result.exit_status, 'Exit status should be 2'
583
+
584
+ refusal_message = update_result.stdout.split("\n").grep(/refusing/).last
585
+ refute_nil refusal_message, 'Should find a failure message at the end'
586
+ assert_includes refusal_message, 'inspec-meaning-of-life'
587
+ assert_includes refusal_message, 'inspec plugin uninstall'
588
+ assert_includes refusal_message, 'Cannot update path-based install'
589
+ end
590
+ end
591
+
592
+ #-----------------------------------------------------------------------------------------#
593
+ # inspec plugin uninstall
594
+ #-----------------------------------------------------------------------------------------#
595
+ class PluginManagerCliUninstall < MiniTest::Test
596
+ include CorePluginFunctionalHelper
597
+ include PluginManagerHelpers
598
+
599
+ def test_when_a_gem_plugin_can_be_uninstalled
600
+ pre_block = Proc.new do |plugin_statefile_data, tmp_dir|
601
+ plugin_statefile_data.clear # Signal not to write a file, we'll provide one.
602
+ copy_in_core_config_dir('test-fixture-1-float', tmp_dir)
603
+ end
604
+
605
+ # Attempt uninstall
606
+ uninstall_result = run_inspec_process_with_this_plugin('plugin uninstall inspec-test-fixture', pre_run: pre_block, post_run: list_after_run)
607
+ assert_empty uninstall_result.stderr
608
+ assert_equal 0, uninstall_result.exit_status, 'Exit status should be 0'
609
+
610
+ success_message = uninstall_result.stdout.split("\n").grep(/uninstalled/).last
611
+ refute_nil success_message, 'Should find a success message at the end'
612
+ assert_includes success_message, 'inspec-test-fixture'
613
+ assert_includes success_message, '0.1.0'
614
+ assert_includes success_message, 'has been uninstalled'
615
+
616
+ list_result = uninstall_result.payload.list_result
617
+ itf_line = list_result.stdout.split("\n").grep(/inspec-test-fixture/).first
618
+ assert_nil itf_line, 'inspec-test-fixture should not appear in the output of inspec list'
619
+ end
620
+
621
+ def test_when_a_path_plugin_can_be_uninstalled
622
+ pre_block = Proc.new do |plugin_statefile_data, tmp_dir|
623
+ plugin_statefile_data.clear # Signal not to write a file, we'll provide one.
624
+ # This fixture includes a path install for inspec-meaning-of-life
625
+ copy_in_core_config_dir('test-fixture-1-float', tmp_dir)
626
+ end
627
+
628
+ uninstall_result = run_inspec_process_with_this_plugin('plugin uninstall inspec-meaning-of-life', pre_run: pre_block, post_run: list_after_run)
629
+ assert_empty uninstall_result.stderr
630
+ assert_equal 0, uninstall_result.exit_status, 'Exit status should be 0'
631
+
632
+ success_message = uninstall_result.stdout.split("\n").grep(/uninstalled/).last
633
+ refute_nil success_message, 'Should find a success message at the end'
634
+ assert_includes success_message, 'inspec-meaning-of-life'
635
+ assert_includes success_message, 'path-based plugin install'
636
+ assert_includes success_message, 'has been uninstalled'
637
+
638
+ list_result = uninstall_result.payload.list_result
639
+ itf_line = list_result.stdout.split("\n").grep(/inspec-meaning-of-life/).first
640
+ assert_nil itf_line, 'inspec-meaning-of-life should not appear in the output of inspec list'
641
+ end
642
+
643
+ def test_fail_uninstall_from_plugin_that_is_not_installed
644
+ uninstall_result = run_inspec_process_with_this_plugin('plugin uninstall inspec-test-fixture-nonesuch')
645
+
646
+ assert_empty uninstall_result.stderr
647
+ assert_equal 1, uninstall_result.exit_status, 'Exit status should be 1'
648
+ refute_includes 'Inspec::Plugin::V2::UnInstallError', uninstall_result.stdout # Stacktrace marker
649
+ assert_match(/No such plugin installed: .+ - uninstall failed/, uninstall_result.stdout)
650
+ end
651
+ end