inspec 2.2.112 → 2.3.4

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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -2
  3. data/CHANGELOG.md +42 -19
  4. data/README.md +1 -1
  5. data/Rakefile +16 -3
  6. data/docs/dev/integration-testing.md +31 -0
  7. data/docs/dev/plugins.md +4 -2
  8. data/docs/dsl_inspec.md +104 -4
  9. data/docs/plugins.md +57 -0
  10. data/docs/resources/aws_ebs_volume.md.erb +76 -0
  11. data/docs/resources/aws_ebs_volumes.md.erb +86 -0
  12. data/docs/style.md +178 -0
  13. data/examples/plugins/inspec-resource-lister/Gemfile +12 -0
  14. data/examples/plugins/inspec-resource-lister/LICENSE +13 -0
  15. data/examples/plugins/inspec-resource-lister/README.md +62 -0
  16. data/examples/plugins/inspec-resource-lister/Rakefile +40 -0
  17. data/examples/plugins/inspec-resource-lister/inspec-resource-lister.gemspec +45 -0
  18. data/examples/plugins/inspec-resource-lister/lib/inspec-resource-lister.rb +16 -0
  19. data/examples/plugins/inspec-resource-lister/lib/inspec-resource-lister/cli_command.rb +70 -0
  20. data/examples/plugins/inspec-resource-lister/lib/inspec-resource-lister/plugin.rb +55 -0
  21. data/examples/plugins/inspec-resource-lister/lib/inspec-resource-lister/version.rb +10 -0
  22. data/examples/plugins/inspec-resource-lister/test/fixtures/README.md +24 -0
  23. data/examples/plugins/inspec-resource-lister/test/functional/README.md +18 -0
  24. data/examples/plugins/inspec-resource-lister/test/functional/inspec_resource_lister_test.rb +110 -0
  25. data/examples/plugins/inspec-resource-lister/test/helper.rb +26 -0
  26. data/examples/plugins/inspec-resource-lister/test/unit/README.md +17 -0
  27. data/examples/plugins/inspec-resource-lister/test/unit/cli_args_test.rb +64 -0
  28. data/examples/plugins/inspec-resource-lister/test/unit/plugin_def_test.rb +51 -0
  29. data/examples/profile/controls/example.rb +9 -8
  30. data/inspec.gemspec +2 -1
  31. data/lib/inspec/attribute_registry.rb +1 -1
  32. data/lib/inspec/globals.rb +4 -0
  33. data/lib/inspec/objects/control.rb +18 -3
  34. data/lib/inspec/plugin/v2.rb +14 -3
  35. data/lib/inspec/plugin/v2/activator.rb +7 -2
  36. data/lib/inspec/plugin/v2/installer.rb +426 -0
  37. data/lib/inspec/plugin/v2/loader.rb +137 -30
  38. data/lib/inspec/plugin/v2/registry.rb +13 -4
  39. data/lib/inspec/profile.rb +2 -1
  40. data/lib/inspec/reporters/json.rb +11 -1
  41. data/lib/inspec/resource.rb +6 -15
  42. data/lib/inspec/rule.rb +18 -9
  43. data/lib/inspec/runner_rspec.rb +1 -1
  44. data/lib/inspec/schema.rb +1 -0
  45. data/lib/inspec/version.rb +1 -1
  46. data/lib/plugins/inspec-plugin-manager-cli/README.md +6 -0
  47. data/lib/plugins/inspec-plugin-manager-cli/lib/inspec-plugin-manager-cli.rb +18 -0
  48. data/lib/plugins/inspec-plugin-manager-cli/lib/inspec-plugin-manager-cli/cli_command.rb +420 -0
  49. data/lib/plugins/inspec-plugin-manager-cli/lib/inspec-plugin-manager-cli/plugin.rb +12 -0
  50. data/lib/plugins/inspec-plugin-manager-cli/test/fixtures/config_dirs/empty/.gitkeep +0 -0
  51. data/lib/plugins/inspec-plugin-manager-cli/test/fixtures/plugins/inspec-egg-white-omelette/lib/inspec-egg-white-omelette.rb +2 -0
  52. data/lib/plugins/inspec-plugin-manager-cli/test/fixtures/plugins/inspec-egg-white-omelette/lib/inspec-egg-white-omelette/.gitkeep +0 -0
  53. data/lib/plugins/inspec-plugin-manager-cli/test/fixtures/plugins/inspec-wrong-structure/.gitkeep +0 -0
  54. data/lib/plugins/inspec-plugin-manager-cli/test/fixtures/plugins/wrong-name/lib/wrong-name.rb +1 -0
  55. data/lib/plugins/inspec-plugin-manager-cli/test/fixtures/plugins/wrong-name/lib/wrong-name/.gitkeep +0 -0
  56. data/lib/plugins/inspec-plugin-manager-cli/test/functional/inspec-plugin_test.rb +651 -0
  57. data/lib/plugins/inspec-plugin-manager-cli/test/unit/cli_args_test.rb +71 -0
  58. data/lib/plugins/inspec-plugin-manager-cli/test/unit/plugin_def_test.rb +20 -0
  59. data/lib/plugins/shared/core_plugin_test_helper.rb +101 -2
  60. data/lib/plugins/things-for-train-integration.rb +14 -0
  61. data/lib/resource_support/aws.rb +2 -0
  62. data/lib/resources/aws/aws_ebs_volume.rb +122 -0
  63. data/lib/resources/aws/aws_ebs_volumes.rb +63 -0
  64. data/lib/resources/port.rb +10 -6
  65. metadata +56 -11
  66. 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