inspec 4.7.24 → 4.10.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -2
- data/lib/fetchers/git.rb +49 -10
- data/lib/inspec/cli.rb +23 -34
- data/lib/inspec/resources/windows_task.rb +5 -0
- data/lib/inspec/version.rb +1 -1
- data/lib/plugins/inspec-artifact/test/functional/inspec_artifact_test.rb +7 -8
- data/lib/plugins/inspec-compliance/test/functional/inspec_compliance_test.rb +18 -6
- data/lib/plugins/inspec-habitat/lib/inspec-habitat/profile.rb +0 -12
- data/lib/plugins/inspec-habitat/templates/habitat/plan.sh.erb +1 -78
- data/lib/plugins/inspec-habitat/test/functional/inspec_habitat_test.rb +5 -10
- data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/test/functional/inspec_plugin_template_test.rb +4 -4
- data/lib/plugins/inspec-init/test/functional/inspec_init_plugin_test.rb +17 -10
- data/lib/plugins/inspec-init/test/functional/inspec_init_profile_test.rb +31 -12
- data/lib/plugins/inspec-plugin-manager-cli/lib/inspec-plugin-manager-cli/cli_command.rb +106 -69
- data/lib/plugins/inspec-plugin-manager-cli/test/functional/inspec-plugin_test.rb +180 -87
- metadata +2 -6
- data/lib/inspec/utils/latest_version.rb +0 -13
- data/lib/plugins/inspec-habitat/templates/habitat/config/inspec_exec_config.json.erb +0 -25
- data/lib/plugins/inspec-habitat/templates/habitat/default.toml.erb +0 -9
- data/lib/plugins/inspec-habitat/templates/habitat/hooks/run.erb +0 -32
@@ -5,7 +5,6 @@ class ProfileCli < Minitest::Test
|
|
5
5
|
include CorePluginFunctionalHelper
|
6
6
|
|
7
7
|
def setup
|
8
|
-
skip_windows!
|
9
8
|
@tmpdir = Dir.mktmpdir
|
10
9
|
@habitat_profile = File.join(@tmpdir, "habitat-profile")
|
11
10
|
run_inspec_process("init profile " + @habitat_profile)
|
@@ -18,22 +17,18 @@ class ProfileCli < Minitest::Test
|
|
18
17
|
def test_setup_subcommand
|
19
18
|
result = run_inspec_process("habitat profile setup " + @habitat_profile + " --log-level debug")
|
20
19
|
|
21
|
-
# Command runs without error
|
22
|
-
assert_empty result.stderr
|
23
|
-
assert_equal 0, result.exit_status
|
24
|
-
|
25
20
|
# Command creates only expected files
|
26
21
|
base_dir = File.join(@tmpdir, "habitat-profile", "habitat")
|
27
22
|
files = %w{
|
28
|
-
default.toml
|
29
23
|
plan.sh
|
30
|
-
config
|
31
|
-
config/inspec_exec_config.json
|
32
|
-
hooks
|
33
|
-
hooks/run
|
34
24
|
}
|
35
25
|
actual_files = Dir.glob(File.join(base_dir, "**/*"))
|
36
26
|
expected_files = files.map { |x| File.join(base_dir, x) }
|
37
27
|
assert_equal actual_files.sort, expected_files.sort
|
28
|
+
|
29
|
+
# Command runs without error
|
30
|
+
assert_empty result.stderr
|
31
|
+
|
32
|
+
assert_exit_code 0, result
|
38
33
|
end
|
39
34
|
end
|
@@ -35,7 +35,7 @@ describe 'inspec list-resources core' do
|
|
35
35
|
# Some tests through here use minitest Expectations, which attach to all
|
36
36
|
# Objects, and begin with 'must' (positive) or 'wont' (negative)
|
37
37
|
# See http://docs.seattlerb.org/minitest/Minitest/Expectations.html
|
38
|
-
it("should exit successfully") {
|
38
|
+
it("should exit successfully") { assert_exit_code 0, outcome }
|
39
39
|
it("should be silent on stderr") { outcome.stderr.must_be_empty }
|
40
40
|
|
41
41
|
# A selection of core resources, just spot checking.
|
@@ -59,7 +59,7 @@ describe 'inspec list-resources core' do
|
|
59
59
|
let(:outcome) { run_inspec_process_with_this_plugin("listresources core user") }
|
60
60
|
|
61
61
|
# Should be well-behaved...
|
62
|
-
it("should exit successfully") {
|
62
|
+
it("should exit successfully") { assert_exit_code 0, outcome }
|
63
63
|
it("should be silent on stderr") { outcome.stderr.must_be_empty }
|
64
64
|
|
65
65
|
# Here, we want to know it DID match some things, and NOT some others.
|
@@ -79,7 +79,7 @@ describe 'inspec list-resources core' do
|
|
79
79
|
let(:outcome) { run_inspec_process_with_this_plugin("listresources core autogyro") }
|
80
80
|
|
81
81
|
# Should be well-behaved...
|
82
|
-
it("should exit successfully") {
|
82
|
+
it("should exit successfully") { assert_exit_code 0, outcome }
|
83
83
|
it("should be silent on stderr") { outcome.stderr.must_be_empty }
|
84
84
|
|
85
85
|
# Output lines should be just two, for the summary.
|
@@ -99,7 +99,7 @@ describe 'inspec list-resources core' do
|
|
99
99
|
let(:outcome) { run_inspec_process_with_this_plugin('listresources core --no-summary') }
|
100
100
|
|
101
101
|
# Should be well-behaved...
|
102
|
-
it("should exit successfully") {
|
102
|
+
it("should exit successfully") { assert_exit_code 0, outcome }
|
103
103
|
it("should be silent on stderr") { outcome.stderr.must_be_empty }
|
104
104
|
|
105
105
|
# Check for the summary
|
@@ -3,18 +3,18 @@ require_relative "../../../shared/core_plugin_test_helper.rb"
|
|
3
3
|
class InitPluginCli < Minitest::Test
|
4
4
|
include CorePluginFunctionalHelper
|
5
5
|
|
6
|
-
def setup
|
7
|
-
skip_windows!
|
8
|
-
end
|
9
|
-
|
10
6
|
def test_generating_inspec_plugin_correct_prefix_required
|
11
7
|
Dir.mktmpdir do |dir|
|
12
8
|
plugin = "wacky-name"
|
13
9
|
run_result = run_inspec_process("init plugin --no-prompt #{plugin} ", prefix: "cd #{dir} &&")
|
14
|
-
|
15
|
-
|
10
|
+
|
11
|
+
skip_windows!
|
16
12
|
assert_includes run_result.stdout, "ERROR"
|
17
13
|
assert_includes run_result.stdout, "Plugin names must begin with"
|
14
|
+
|
15
|
+
assert_empty run_result.stderr
|
16
|
+
|
17
|
+
assert_exit_code 1, run_result
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
@@ -25,12 +25,15 @@ class InitPluginCli < Minitest::Test
|
|
25
25
|
module_name = plugin.sub(/^inspec\-/, "").split("-").map(&:capitalize).join("")
|
26
26
|
|
27
27
|
run_result = run_inspec_process("init plugin --no-prompt #{plugin}", prefix: "cd #{dir} &&")
|
28
|
-
assert_empty run_result.stderr
|
29
28
|
|
30
|
-
|
29
|
+
skip_windows!
|
31
30
|
assert_includes run_result.stdout, "Creating new inspec plugin at"
|
32
31
|
assert_includes run_result.stdout, plugin
|
33
32
|
|
33
|
+
assert_empty run_result.stderr
|
34
|
+
|
35
|
+
assert_exit_code 0, run_result
|
36
|
+
|
34
37
|
# Check generated files and contents.
|
35
38
|
# Each file must exist, and its contents must match each of the regexen given.
|
36
39
|
{
|
@@ -128,11 +131,15 @@ class InitPluginCli < Minitest::Test
|
|
128
131
|
opts += " --module_name FunPlugin"
|
129
132
|
|
130
133
|
run_result = run_inspec_process("init plugin #{plugin} --no-prompt #{opts}", prefix: "cd #{dir} &&")
|
131
|
-
|
132
|
-
|
134
|
+
|
135
|
+
skip_windows!
|
133
136
|
assert_includes run_result.stdout, "Creating new inspec plugin at"
|
134
137
|
assert_includes run_result.stdout, plugin
|
135
138
|
|
139
|
+
assert_empty run_result.stderr
|
140
|
+
|
141
|
+
assert_exit_code 0, run_result
|
142
|
+
|
136
143
|
# Check generated files and contents.
|
137
144
|
# Each file must exist, and its contents must match each of the regexen given.
|
138
145
|
{
|
@@ -4,19 +4,18 @@ require_relative "../../../shared/core_plugin_test_helper.rb"
|
|
4
4
|
class InitCli < Minitest::Test
|
5
5
|
include CorePluginFunctionalHelper
|
6
6
|
|
7
|
-
def setup
|
8
|
-
skip_windows!
|
9
|
-
end
|
10
|
-
|
11
7
|
def test_generating_inspec_profile
|
12
8
|
Dir.mktmpdir do |dir|
|
13
9
|
profile = File.join(dir, "test-profile")
|
14
10
|
out = run_inspec_process("init profile test-profile", prefix: "cd #{dir} &&")
|
15
|
-
|
11
|
+
|
12
|
+
skip_windows!
|
16
13
|
assert_includes out.stdout, "Creating new profile at"
|
17
14
|
assert_includes out.stdout, profile
|
18
15
|
assert_includes Dir.entries(profile).join, "inspec.yml"
|
19
16
|
assert_includes Dir.entries(profile).join, "README.md"
|
17
|
+
|
18
|
+
assert_exit_code 0, out
|
20
19
|
end
|
21
20
|
end
|
22
21
|
|
@@ -24,20 +23,26 @@ class InitCli < Minitest::Test
|
|
24
23
|
Dir.mktmpdir do |dir|
|
25
24
|
profile = File.join(dir, "test-profile")
|
26
25
|
out = run_inspec_process("init profile --platform os test-profile", prefix: "cd #{dir} &&")
|
27
|
-
|
26
|
+
|
27
|
+
skip_windows!
|
28
28
|
assert_includes out.stdout, "Creating new profile at"
|
29
29
|
assert_includes out.stdout, profile
|
30
30
|
assert_includes Dir.entries(profile).join, "inspec.yml"
|
31
31
|
assert_includes Dir.entries(profile).join, "README.md"
|
32
|
+
|
33
|
+
assert_exit_code 0, out
|
32
34
|
end
|
33
35
|
end
|
34
36
|
|
35
37
|
def test_generating_inspec_profile_with_bad_platform
|
36
38
|
Dir.mktmpdir do |dir|
|
37
39
|
out = run_inspec_process("init profile --platform nonesuch test-profile", prefix: "cd #{dir} &&")
|
38
|
-
|
40
|
+
|
41
|
+
skip_windows!
|
39
42
|
assert_includes out.stdout, "Unable to generate profile"
|
40
43
|
assert_includes out.stdout, "No template available for platform 'nonesuch'"
|
44
|
+
|
45
|
+
assert_exit_code 1, out
|
41
46
|
end
|
42
47
|
end
|
43
48
|
|
@@ -45,10 +50,12 @@ class InitCli < Minitest::Test
|
|
45
50
|
Dir.mktmpdir do |dir|
|
46
51
|
profile = dir + "/test/deeper/profile"
|
47
52
|
out = run_inspec_process("init profile test/deeper/profile", prefix: "cd #{dir} &&")
|
48
|
-
|
53
|
+
skip_windows!
|
49
54
|
assert_equal true, File.exist?(profile)
|
50
55
|
profile = YAML.load_file("#{profile}/inspec.yml")
|
51
56
|
assert_equal "profile", profile["name"]
|
57
|
+
|
58
|
+
assert_exit_code 0, out
|
52
59
|
end
|
53
60
|
end
|
54
61
|
|
@@ -56,11 +63,14 @@ class InitCli < Minitest::Test
|
|
56
63
|
Dir.mktmpdir do |dir|
|
57
64
|
profile = File.join(dir, "test-gcp-profile")
|
58
65
|
out = run_inspec_process("init profile --platform gcp test-gcp-profile", prefix: "cd #{dir} &&")
|
59
|
-
|
66
|
+
|
67
|
+
skip_windows!
|
60
68
|
assert_includes out.stdout, "Creating new profile at"
|
61
69
|
assert_includes out.stdout, profile
|
62
70
|
assert_includes Dir.entries(profile).join, "inspec.yml"
|
63
71
|
assert_includes Dir.entries(profile).join, "README.md"
|
72
|
+
|
73
|
+
assert_exit_code 0, out
|
64
74
|
end
|
65
75
|
end
|
66
76
|
|
@@ -68,11 +78,14 @@ class InitCli < Minitest::Test
|
|
68
78
|
Dir.mktmpdir do |dir|
|
69
79
|
profile = File.join(dir, "test-aws-profile")
|
70
80
|
out = run_inspec_process("init profile --platform aws test-aws-profile", prefix: "cd #{dir} &&")
|
71
|
-
|
81
|
+
|
82
|
+
skip_windows!
|
72
83
|
assert_includes out.stdout, "Creating new profile at"
|
73
84
|
assert_includes out.stdout, profile
|
74
85
|
assert_includes Dir.entries(profile).join, "inspec.yml"
|
75
86
|
assert_includes Dir.entries(profile).join, "README.md"
|
87
|
+
|
88
|
+
assert_exit_code 0, out
|
76
89
|
end
|
77
90
|
end
|
78
91
|
|
@@ -80,11 +93,14 @@ class InitCli < Minitest::Test
|
|
80
93
|
Dir.mktmpdir do |dir|
|
81
94
|
profile = File.join(dir, "test-azure-profile")
|
82
95
|
out = run_inspec_process("init profile --platform azure test-azure-profile", prefix: "cd #{dir} &&")
|
83
|
-
|
96
|
+
|
97
|
+
skip_windows!
|
84
98
|
assert_includes out.stdout, "Creating new profile at"
|
85
99
|
assert_includes out.stdout, profile
|
86
100
|
assert_includes Dir.entries(profile).join, "inspec.yml"
|
87
101
|
assert_includes Dir.entries(profile).join, "README.md"
|
102
|
+
|
103
|
+
assert_exit_code 0, out
|
88
104
|
end
|
89
105
|
end
|
90
106
|
|
@@ -92,11 +108,14 @@ class InitCli < Minitest::Test
|
|
92
108
|
Dir.mktmpdir do |dir|
|
93
109
|
profile = File.join(dir, "test-os-profile")
|
94
110
|
out = run_inspec_process("init profile --platform os test-os-profile", prefix: "cd #{dir} &&")
|
95
|
-
|
111
|
+
|
112
|
+
skip_windows!
|
96
113
|
assert_includes out.stdout, "Creating new profile at"
|
97
114
|
assert_includes out.stdout, profile
|
98
115
|
assert_includes Dir.entries(profile).join, "inspec.yml"
|
99
116
|
assert_includes Dir.entries(profile).join, "README.md"
|
117
|
+
|
118
|
+
assert_exit_code 0, out
|
100
119
|
end
|
101
120
|
end
|
102
121
|
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require "term/ansicolor"
|
2
1
|
require "pathname"
|
3
2
|
require "inspec/plugin/v2"
|
4
3
|
require "inspec/plugin/v2/installer"
|
@@ -7,7 +6,6 @@ require "inspec/dist"
|
|
7
6
|
module InspecPlugins
|
8
7
|
module PluginManager
|
9
8
|
class CliCommand < Inspec.plugin(2, :cli_command)
|
10
|
-
include Term::ANSIColor
|
11
9
|
include Inspec::Dist
|
12
10
|
|
13
11
|
subcommand_desc "plugin SUBCOMMAND", "Manage #{PRODUCT_NAME} and Train plugins"
|
@@ -22,15 +20,17 @@ module InspecPlugins
|
|
22
20
|
plugin_statuses = Inspec::Plugin::V2::Registry.instance.plugin_statuses
|
23
21
|
plugin_statuses.reject! { |s| %i{core bundle}.include?(s.installation_type) } unless options[:all]
|
24
22
|
|
25
|
-
# TODO: ui object support
|
26
23
|
puts
|
27
|
-
|
28
|
-
|
24
|
+
ui.bold(format(" %-30s%-10s%-8s%-6s", "Plugin Name", "Version", "Via", "ApiVer"))
|
25
|
+
ui.line
|
29
26
|
plugin_statuses.sort_by(&:name).each do |status|
|
30
|
-
|
27
|
+
ui.plain(format(" %-30s%-10s%-8s%-6s", status.name,
|
28
|
+
make_pretty_version(status),
|
29
|
+
status.installation_type,
|
30
|
+
status.api_generation.to_s))
|
31
31
|
end
|
32
|
-
|
33
|
-
|
32
|
+
ui.line
|
33
|
+
ui.plain(" #{plugin_statuses.count} plugin(s) total")
|
34
34
|
puts
|
35
35
|
end
|
36
36
|
|
@@ -59,23 +59,22 @@ module InspecPlugins
|
|
59
59
|
search_results.delete("train-test-fixture")
|
60
60
|
end
|
61
61
|
|
62
|
-
# TODO: ui object support
|
63
62
|
puts
|
64
|
-
|
65
|
-
|
63
|
+
ui.bold(format(" %-30s%-50s", "Plugin Name", "Versions Available"))
|
64
|
+
ui.line
|
66
65
|
search_results.keys.sort.each do |plugin_name|
|
67
66
|
versions = options[:all] ? search_results[plugin_name] : [search_results[plugin_name].first]
|
68
67
|
versions = "(" + versions.join(", ") + ")"
|
69
|
-
|
68
|
+
ui.plain(format(" %-30s%-50s", plugin_name, versions))
|
70
69
|
end
|
71
|
-
|
72
|
-
|
70
|
+
ui.line
|
71
|
+
ui.plain(" #{search_results.count} plugin(s) found")
|
73
72
|
puts
|
74
73
|
|
75
|
-
exit
|
74
|
+
ui.exit Inspec::UI::EXIT_PLUGIN_ERROR if search_results.empty?
|
76
75
|
rescue Inspec::Plugin::V2::SearchError => ex
|
77
76
|
Inspec::Log.error ex.message
|
78
|
-
exit
|
77
|
+
ui.exit Inspec::UI::EXIT_USAGE_ERROR
|
79
78
|
end
|
80
79
|
|
81
80
|
#==================================================================#
|
@@ -119,13 +118,14 @@ module InspecPlugins
|
|
119
118
|
begin
|
120
119
|
installer.update(plugin_name)
|
121
120
|
rescue Inspec::Plugin::V2::UpdateError => ex
|
122
|
-
|
123
|
-
exit
|
121
|
+
ui.plain("#{ui.red('Update error:')} #{ex.message} - update failed")
|
122
|
+
ui.exit Inspec::UI::EXIT_USAGE_ERROR
|
124
123
|
end
|
125
124
|
post_update_versions = installer.list_installed_plugin_gems.select { |spec| spec.name == plugin_name }.map { |spec| spec.version.to_s }
|
126
125
|
new_version = (post_update_versions - pre_update_versions).first
|
127
126
|
|
128
|
-
|
127
|
+
ui.bold(plugin_name + " plugin, version #{old_version} -> " \
|
128
|
+
"#{new_version}, updated from rubygems.org")
|
129
129
|
end
|
130
130
|
|
131
131
|
#--------------------------
|
@@ -144,9 +144,9 @@ module InspecPlugins
|
|
144
144
|
def uninstall(plugin_name)
|
145
145
|
status = Inspec::Plugin::V2::Registry.instance[plugin_name.to_sym]
|
146
146
|
unless status
|
147
|
-
|
148
|
-
|
149
|
-
exit
|
147
|
+
ui.plain("#{ui.red('No such plugin installed:')} #{plugin_name} is not " \
|
148
|
+
"installed - uninstall failed")
|
149
|
+
ui.exit Inspec::UI::EXIT_USAGE_ERROR
|
150
150
|
end
|
151
151
|
installer = Inspec::Plugin::V2::Installer.instance
|
152
152
|
|
@@ -156,11 +156,13 @@ module InspecPlugins
|
|
156
156
|
installer.uninstall(plugin_name)
|
157
157
|
|
158
158
|
if status.installation_type == :path
|
159
|
-
|
159
|
+
ui.bold(plugin_name + " path-based plugin install has been " \
|
160
|
+
"uninstalled")
|
160
161
|
else
|
161
|
-
|
162
|
+
ui.bold(plugin_name + " plugin, version #{old_version}, has " \
|
163
|
+
"been uninstalled")
|
162
164
|
end
|
163
|
-
exit
|
165
|
+
ui.exit Inspec::UI::EXIT_NORMAL
|
164
166
|
end
|
165
167
|
|
166
168
|
private
|
@@ -172,8 +174,8 @@ module InspecPlugins
|
|
172
174
|
|
173
175
|
def install_from_gemfile(gem_file)
|
174
176
|
unless File.exist? gem_file
|
175
|
-
|
176
|
-
exit
|
177
|
+
ui.red("No such plugin gem file #{gem_file} - installation failed.")
|
178
|
+
ui.exit Inspec::UI::EXIT_USAGE_ERROR
|
177
179
|
end
|
178
180
|
|
179
181
|
plugin_name_parts = File.basename(gem_file, ".gem").split("-")
|
@@ -183,14 +185,15 @@ module InspecPlugins
|
|
183
185
|
|
184
186
|
installer.install(plugin_name, gem_file: gem_file)
|
185
187
|
|
186
|
-
|
187
|
-
|
188
|
+
ui.bold("#{plugin_name} plugin, version #{version}, installed from " \
|
189
|
+
"local .gem file")
|
190
|
+
ui.exit Inspec::UI::EXIT_NORMAL
|
188
191
|
end
|
189
192
|
|
190
193
|
def install_from_path(path)
|
191
194
|
unless File.exist? path
|
192
|
-
|
193
|
-
exit
|
195
|
+
ui.red("No such source code path #{path} - installation failed.")
|
196
|
+
ui.exit Inspec::UI::EXIT_USAGE_ERROR
|
194
197
|
end
|
195
198
|
|
196
199
|
plugin_name = File.basename(path, ".rb")
|
@@ -204,8 +207,10 @@ module InspecPlugins
|
|
204
207
|
|
205
208
|
# Already installed?
|
206
209
|
if registry.known_plugin?(plugin_name.to_sym)
|
207
|
-
|
208
|
-
|
210
|
+
ui.red("Plugin already installed - #{plugin_name} - Use '#{EXEC_NAME} " \
|
211
|
+
"plugin list' to see previously installed plugin - " \
|
212
|
+
"installation failed.")
|
213
|
+
ui.exit Inspec::UI::EXIT_PLUGIN_ERROR
|
209
214
|
end
|
210
215
|
|
211
216
|
# Can we figure out how to load it?
|
@@ -217,8 +222,9 @@ module InspecPlugins
|
|
217
222
|
# OK, install it!
|
218
223
|
installer.install(plugin_name, path: entry_point)
|
219
224
|
|
220
|
-
|
221
|
-
|
225
|
+
ui.bold("#{plugin_name} plugin installed via source path reference, " \
|
226
|
+
"resolved to entry point #{entry_point}")
|
227
|
+
ui.exit Inspec::UI::EXIT_NORMAL
|
222
228
|
end
|
223
229
|
|
224
230
|
# Rationale for rubocop variances: It's a heuristics method, and will be full of
|
@@ -229,6 +235,7 @@ module InspecPlugins
|
|
229
235
|
given = given.expand_path # Resolve any relative paths
|
230
236
|
name_regex = /^(inspec|train)-/
|
231
237
|
versioned_regex = /^(inspec|train)-[a-z0-9\-\_]+-\d+\.\d+\.\d+$/
|
238
|
+
sha_ref_regex = /^(inspec|train)-[a-z0-9\-\_]+-[0-9a-f]{5,40}$/
|
232
239
|
|
233
240
|
# What are the last four things like?
|
234
241
|
parts = [
|
@@ -257,14 +264,14 @@ module InspecPlugins
|
|
257
264
|
# In that case, we'll have a version on the plugin name in part 0
|
258
265
|
# /home/you/.gems/2.4.0/gems/inspec-something-3.45.1/lib/inspec-something.rb
|
259
266
|
# parts index: ^0^ ^1^ ^2^ ^3^
|
260
|
-
if parts[0] =~ versioned_regex && parts[1] == "lib" && parts[0].start_with?(parts[2]) && parts[3] == ".rb"
|
267
|
+
if (parts[0] =~ versioned_regex || parts[0] =~ sha_ref_regex) && parts[1] == "lib" && parts[0].start_with?(parts[2]) && parts[3] == ".rb"
|
261
268
|
return given.to_s
|
262
269
|
end
|
263
270
|
|
264
271
|
# Case 4: Like case 3, but missing the .rb
|
265
272
|
# /home/you/.gems/2.4.0/gems/inspec-something-3.45.1/lib/inspec-something
|
266
273
|
# parts index: ^0^ ^1^ ^2^ ^3^ (empty)
|
267
|
-
if parts[0] =~ versioned_regex && parts[1] == "lib" && parts[0].start_with?(parts[2]) && parts[3].empty?
|
274
|
+
if (parts[0] =~ versioned_regex || parts[0] =~ sha_ref_regex) && parts[1] == "lib" && parts[0].start_with?(parts[2]) && parts[3].empty?
|
268
275
|
return given.to_s + ".rb"
|
269
276
|
end
|
270
277
|
|
@@ -279,8 +286,10 @@ module InspecPlugins
|
|
279
286
|
|
280
287
|
# Well, if we got here, parts[2] matches an inspec/train prefix, but we have no idea about anything.
|
281
288
|
# Give up.
|
282
|
-
|
283
|
-
|
289
|
+
ui.red("Unrecognizable plugin structure - #{parts[2]} - When " \
|
290
|
+
"installing from a path, please provide the path of the " \
|
291
|
+
"entry point file - installation failed.")
|
292
|
+
ui.exit Inspec::UI::EXIT_USAGE_ERROR
|
284
293
|
end
|
285
294
|
|
286
295
|
def install_from_path__probe_load(entry_point, plugin_name)
|
@@ -288,9 +297,11 @@ module InspecPlugins
|
|
288
297
|
begin
|
289
298
|
require entry_point
|
290
299
|
rescue LoadError => ex
|
291
|
-
|
292
|
-
|
293
|
-
|
300
|
+
ui.red("Plugin contains errors - #{plugin_name} - Encountered " \
|
301
|
+
"errors while trying to test load the plugin entry point, " \
|
302
|
+
"resolved to #{entry_point} - installation failed")
|
303
|
+
ui.plain ex.message
|
304
|
+
ui.exit Inspec::UI::EXIT_USAGE_ERROR
|
294
305
|
end
|
295
306
|
|
296
307
|
# OK, the wheels didn't fall off. But is it a plugin?
|
@@ -299,13 +310,19 @@ module InspecPlugins
|
|
299
310
|
# And the registry is keyed on Strings
|
300
311
|
registry_key = plugin_name.to_s.sub(/^train-/, "")
|
301
312
|
unless Train::Plugins.registry.key?(registry_key)
|
302
|
-
|
303
|
-
|
313
|
+
ui.red("Does not appear to be a plugin - #{plugin_name} - After " \
|
314
|
+
"probe-loading the supposed plugin, it did not register " \
|
315
|
+
"itself to Train. Ensure something inherits from " \
|
316
|
+
"'Train.plugin(1)' - installation failed.")
|
317
|
+
ui.exit Inspec::UI::EXIT_USAGE_ERROR
|
304
318
|
end
|
305
319
|
else
|
306
320
|
unless registry.known_plugin?(plugin_name.to_sym)
|
307
|
-
|
308
|
-
|
321
|
+
ui.red("Does not appear to be a plugin - #{plugin_name} - After " \
|
322
|
+
"probe-loading the supposed plugin, it did not register " \
|
323
|
+
"itself to InSpec. Ensure something inherits from " \
|
324
|
+
"'Inspec.plugin(2)' - installation failed.")
|
325
|
+
ui.exit Inspec::UI::EXIT_USAGE_ERROR
|
309
326
|
end
|
310
327
|
end
|
311
328
|
end
|
@@ -325,8 +342,9 @@ module InspecPlugins
|
|
325
342
|
post_installed_versions = installer.list_installed_plugin_gems.select { |spec| spec.name == plugin_name }.map { |spec| spec.version.to_s }
|
326
343
|
new_version = (post_installed_versions - pre_installed_versions).first
|
327
344
|
|
328
|
-
|
329
|
-
|
345
|
+
ui.bold("#{plugin_name} plugin, version #{new_version}, installed " \
|
346
|
+
"from rubygems.org")
|
347
|
+
ui.exit Inspec::UI::EXIT_NORMAL
|
330
348
|
end
|
331
349
|
|
332
350
|
def install_from_remote_gem_verson_preflight_check(plugin_name, requested_version, pre_installed_versions)
|
@@ -348,38 +366,50 @@ module InspecPlugins
|
|
348
366
|
they_explicitly_asked_for_a_version = !options[:version].nil?
|
349
367
|
what_we_would_install_is_already_installed = pre_installed_versions.include?(requested_version)
|
350
368
|
if what_we_would_install_is_already_installed && they_explicitly_asked_for_a_version
|
351
|
-
|
369
|
+
ui.red("Plugin already installed at requested version - plugin " \
|
370
|
+
"#{plugin_name} #{requested_version} - refusing to install.")
|
352
371
|
elsif what_we_would_install_is_already_installed && !they_explicitly_asked_for_a_version
|
353
|
-
|
372
|
+
ui.red("Plugin already installed at latest version - plugin " \
|
373
|
+
"#{plugin_name} #{requested_version} - refusing to install.")
|
354
374
|
else
|
355
375
|
# There are existing versions installed, but none of them are what was requested
|
356
|
-
|
376
|
+
ui.red("Update required - plugin #{plugin_name}, requested " \
|
377
|
+
"#{requested_version}, have " \
|
378
|
+
"#{pre_installed_versions.join(', ')}; use `inspec " \
|
379
|
+
"plugin update` - refusing to install.")
|
357
380
|
end
|
358
381
|
|
359
|
-
exit
|
382
|
+
ui.exit Inspec::UI::EXIT_PLUGIN_ERROR
|
360
383
|
end
|
361
384
|
|
362
385
|
# Rationale for RuboCop variance: This is a one-line method with heavy UX-focused error handling.
|
363
386
|
def install_attempt_install(plugin_name) # rubocop: disable Metrics/AbcSize
|
364
387
|
installer.install(plugin_name, version: options[:version])
|
365
388
|
rescue Inspec::Plugin::V2::PluginExcludedError => ex
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
389
|
+
ui.red("Plugin on Exclusion List - #{plugin_name} is listed as an " \
|
390
|
+
"incompatible gem - refusing to install.")
|
391
|
+
ui.plain("Rationale: #{ex.details.rationale}")
|
392
|
+
ui.plain("Exclusion list location: " +
|
393
|
+
File.join(Inspec.src_root, "etc", "plugin_filters.json"))
|
394
|
+
ui.plain("If you disagree with this determination, please accept " \
|
395
|
+
"our apologies for the misunderstanding, and open an issue " \
|
396
|
+
"at https://github.com/inspec/inspec/issues/new")
|
397
|
+
ui.exit Inspec::UI::EXIT_PLUGIN_ERROR
|
371
398
|
rescue Inspec::Plugin::V2::InstallError
|
372
399
|
raise if Inspec::Log.level == :debug
|
373
400
|
|
374
401
|
results = installer.search(plugin_name, exact: true)
|
375
402
|
if results.empty?
|
376
|
-
|
403
|
+
ui.red("No such plugin gem #{plugin_name} could be found on " \
|
404
|
+
"rubygems.org - installation failed.")
|
377
405
|
elsif options[:version] && !results[plugin_name].include?(options[:version])
|
378
|
-
|
406
|
+
ui.red("No such version - #{plugin_name} exists, but no such " \
|
407
|
+
"version #{options[:version]} found on rubygems.org - " \
|
408
|
+
"installation failed.")
|
379
409
|
else
|
380
|
-
|
410
|
+
ui.red("Unknown error occured - installation failed.")
|
381
411
|
end
|
382
|
-
exit
|
412
|
+
ui.exit Inspec::UI::EXIT_USAGE_ERROR
|
383
413
|
end
|
384
414
|
|
385
415
|
#==================================================================#
|
@@ -390,11 +420,14 @@ module InspecPlugins
|
|
390
420
|
# Check for path install
|
391
421
|
status = Inspec::Plugin::V2::Registry.instance[plugin_name.to_sym]
|
392
422
|
if !status
|
393
|
-
|
394
|
-
exit
|
423
|
+
ui.plain("#{ui.red('No such plugin installed:')} #{plugin_name} - update failed")
|
424
|
+
ui.exit Inspec::UI::EXIT_USAGE_ERROR
|
395
425
|
elsif status.installation_type == :path
|
396
|
-
|
397
|
-
|
426
|
+
ui.plain("#{ui.red('Cannot update path-based install:')} " \
|
427
|
+
"#{plugin_name} is installed via path reference; " \
|
428
|
+
"use `inspec plugin uninstall` to remove - refusing to" \
|
429
|
+
"update")
|
430
|
+
ui.exit Inspec::UI::EXIT_PLUGIN_ERROR
|
398
431
|
end
|
399
432
|
end
|
400
433
|
|
@@ -403,8 +436,10 @@ module InspecPlugins
|
|
403
436
|
latest_version = latest_version[plugin_name]&.last
|
404
437
|
|
405
438
|
if pre_update_versions.include?(latest_version)
|
406
|
-
|
407
|
-
|
439
|
+
ui.plain("#{ui.red('Already installed at latest version:')} " \
|
440
|
+
"#{plugin_name} is at #{latest_version}, which the " \
|
441
|
+
"latest - refusing to update")
|
442
|
+
ui.exit Inspec::UI::EXIT_PLUGIN_ERROR
|
408
443
|
end
|
409
444
|
end
|
410
445
|
|
@@ -421,8 +456,10 @@ module InspecPlugins
|
|
421
456
|
|
422
457
|
def check_plugin_name(plugin_name, action)
|
423
458
|
unless plugin_name =~ /^(inspec|train)-/
|
424
|
-
|
425
|
-
|
459
|
+
ui.red("Invalid plugin name - #{plugin_name} - All inspec " \
|
460
|
+
"plugins must begin with either 'inspec-' or 'train-' " \
|
461
|
+
"- #{action} failed.")
|
462
|
+
ui.exit Inspec::UI::EXIT_USAGE_ERROR
|
426
463
|
end
|
427
464
|
end
|
428
465
|
|