inspec-core 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +42 -19
- data/README.md +1 -1
- data/docs/dev/integration-testing.md +31 -0
- data/docs/dev/plugins.md +4 -2
- data/docs/dsl_inspec.md +104 -4
- data/docs/plugins.md +57 -0
- data/docs/style.md +178 -0
- data/examples/plugins/inspec-resource-lister/Gemfile +12 -0
- data/examples/plugins/inspec-resource-lister/LICENSE +13 -0
- data/examples/plugins/inspec-resource-lister/README.md +62 -0
- data/examples/plugins/inspec-resource-lister/Rakefile +40 -0
- data/examples/plugins/inspec-resource-lister/inspec-resource-lister.gemspec +45 -0
- data/examples/plugins/inspec-resource-lister/lib/inspec-resource-lister.rb +16 -0
- data/examples/plugins/inspec-resource-lister/lib/inspec-resource-lister/cli_command.rb +70 -0
- data/examples/plugins/inspec-resource-lister/lib/inspec-resource-lister/plugin.rb +55 -0
- data/examples/plugins/inspec-resource-lister/lib/inspec-resource-lister/version.rb +10 -0
- data/examples/plugins/inspec-resource-lister/test/fixtures/README.md +24 -0
- data/examples/plugins/inspec-resource-lister/test/functional/README.md +18 -0
- data/examples/plugins/inspec-resource-lister/test/functional/inspec_resource_lister_test.rb +110 -0
- data/examples/plugins/inspec-resource-lister/test/helper.rb +26 -0
- data/examples/plugins/inspec-resource-lister/test/unit/README.md +17 -0
- data/examples/plugins/inspec-resource-lister/test/unit/cli_args_test.rb +64 -0
- data/examples/plugins/inspec-resource-lister/test/unit/plugin_def_test.rb +51 -0
- data/examples/profile/controls/example.rb +9 -8
- data/inspec-core.gemspec +1 -1
- data/lib/inspec/attribute_registry.rb +1 -1
- data/lib/inspec/globals.rb +4 -0
- data/lib/inspec/objects/control.rb +18 -3
- data/lib/inspec/plugin/v2.rb +14 -3
- data/lib/inspec/plugin/v2/activator.rb +7 -2
- data/lib/inspec/plugin/v2/installer.rb +426 -0
- data/lib/inspec/plugin/v2/loader.rb +137 -30
- data/lib/inspec/plugin/v2/registry.rb +13 -4
- data/lib/inspec/profile.rb +2 -1
- data/lib/inspec/reporters/json.rb +11 -1
- data/lib/inspec/resource.rb +6 -15
- data/lib/inspec/rule.rb +18 -9
- data/lib/inspec/runner_rspec.rb +1 -1
- data/lib/inspec/schema.rb +1 -0
- data/lib/inspec/version.rb +1 -1
- data/lib/plugins/inspec-plugin-manager-cli/README.md +6 -0
- data/lib/plugins/inspec-plugin-manager-cli/lib/inspec-plugin-manager-cli.rb +18 -0
- data/lib/plugins/inspec-plugin-manager-cli/lib/inspec-plugin-manager-cli/cli_command.rb +420 -0
- data/lib/plugins/inspec-plugin-manager-cli/lib/inspec-plugin-manager-cli/plugin.rb +12 -0
- data/lib/plugins/inspec-plugin-manager-cli/test/fixtures/config_dirs/empty/.gitkeep +0 -0
- data/lib/plugins/inspec-plugin-manager-cli/test/fixtures/plugins/inspec-egg-white-omelette/lib/inspec-egg-white-omelette.rb +2 -0
- data/lib/plugins/inspec-plugin-manager-cli/test/fixtures/plugins/inspec-egg-white-omelette/lib/inspec-egg-white-omelette/.gitkeep +0 -0
- data/lib/plugins/inspec-plugin-manager-cli/test/fixtures/plugins/inspec-wrong-structure/.gitkeep +0 -0
- data/lib/plugins/inspec-plugin-manager-cli/test/fixtures/plugins/wrong-name/lib/wrong-name.rb +1 -0
- data/lib/plugins/inspec-plugin-manager-cli/test/fixtures/plugins/wrong-name/lib/wrong-name/.gitkeep +0 -0
- data/lib/plugins/inspec-plugin-manager-cli/test/functional/inspec-plugin_test.rb +651 -0
- data/lib/plugins/inspec-plugin-manager-cli/test/unit/cli_args_test.rb +71 -0
- data/lib/plugins/inspec-plugin-manager-cli/test/unit/plugin_def_test.rb +20 -0
- data/lib/plugins/shared/core_plugin_test_helper.rb +101 -2
- data/lib/plugins/things-for-train-integration.rb +14 -0
- data/lib/resources/port.rb +10 -6
- metadata +38 -11
- data/docs/ruby_usage.md +0 -204
data/lib/inspec/version.rb
CHANGED
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
# Because this is a core plugin, we place the plugin definition here in the entry point.
|
3
|
+
# This is needed because under core testing, the entry point may be reloaded multiple times,
|
4
|
+
# and we need plugin registration to properly occur each time.
|
5
|
+
# More typically, the entry point would just load a plugin definition file.
|
6
|
+
|
7
|
+
module InspecPlugins
|
8
|
+
module PluginManager
|
9
|
+
class Plugin < Inspec.plugin(2)
|
10
|
+
plugin_name :'inspec-plugin-manager-cli'
|
11
|
+
|
12
|
+
cli_command :plugin do
|
13
|
+
require_relative 'inspec-plugin-manager-cli/cli_command'
|
14
|
+
InspecPlugins::PluginManager::CliCommand
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,420 @@
|
|
1
|
+
require 'term/ansicolor'
|
2
|
+
require 'pathname'
|
3
|
+
require 'inspec/plugin/v2/installer'
|
4
|
+
|
5
|
+
module InspecPlugins
|
6
|
+
module PluginManager
|
7
|
+
class CliCommand < Inspec.plugin(2, :cli_command)
|
8
|
+
include Term::ANSIColor
|
9
|
+
|
10
|
+
subcommand_desc 'plugin SUBCOMMAND', 'Manage InSpec and Train plugins'
|
11
|
+
|
12
|
+
#==================================================================#
|
13
|
+
# inspec plugin list
|
14
|
+
#==================================================================#
|
15
|
+
|
16
|
+
desc 'list [options]', 'Lists user-installed InSpec plugins.'
|
17
|
+
option :all, desc: 'Include plugins shipped with InSpec as well.', type: :boolean, aliases: [:a]
|
18
|
+
def list
|
19
|
+
plugin_statuses = Inspec::Plugin::V2::Registry.instance.plugin_statuses
|
20
|
+
plugin_statuses.reject! { |s| [:core, :bundle].include?(s.installation_type) } unless options[:all]
|
21
|
+
|
22
|
+
# TODO: ui object support
|
23
|
+
puts
|
24
|
+
puts(bold { format(' %-30s%-10s%-8s%-6s', 'Plugin Name', 'Version', 'Via', 'ApiVer') })
|
25
|
+
puts '-' * 55
|
26
|
+
plugin_statuses.sort_by(&:name).each do |status|
|
27
|
+
puts(format(' %-30s%-10s%-8s%-6s', status.name, make_pretty_version(status), status.installation_type, status.api_generation.to_s))
|
28
|
+
end
|
29
|
+
puts '-' * 55
|
30
|
+
puts(" #{plugin_statuses.count} plugin(s) total")
|
31
|
+
puts
|
32
|
+
end
|
33
|
+
|
34
|
+
#==================================================================#
|
35
|
+
# inspec plugin search
|
36
|
+
#==================================================================#
|
37
|
+
|
38
|
+
desc 'search [options] PATTERN', 'Searches rubygems.org for InSpec plugins. Exits 0 on a search hit, exits 2 on a search miss.'
|
39
|
+
option :all, desc: 'List all available versions, not just the latest one.', type: :boolean, aliases: [:a]
|
40
|
+
option :exact, desc: 'Assume PATTERN is exact; do not add a wildcard to the end', type: :boolean, aliases: [:e]
|
41
|
+
# Justification for disabling ABC: currently at 33.51/33
|
42
|
+
def search(search_term) # rubocop: disable Metrics/AbcSize
|
43
|
+
search_results = installer.search(search_term, exact: options[:exact])
|
44
|
+
|
45
|
+
# TODO: ui object support
|
46
|
+
puts
|
47
|
+
puts(bold { format(' %-30s%-50s%', 'Plugin Name', 'Versions Available') })
|
48
|
+
puts '-' * 55
|
49
|
+
search_results.keys.sort.each do |plugin_name|
|
50
|
+
versions = options[:all] ? search_results[plugin_name] : [search_results[plugin_name].first]
|
51
|
+
versions = '(' + versions.join(', ') + ')'
|
52
|
+
puts(format(' %-30s%-50s', plugin_name, versions))
|
53
|
+
end
|
54
|
+
puts '-' * 55
|
55
|
+
puts(" #{search_results.count} plugin(s) found")
|
56
|
+
puts
|
57
|
+
|
58
|
+
exit 2 if search_results.empty?
|
59
|
+
rescue Inspec::Plugin::V2::SearchError => ex
|
60
|
+
Inspec::Log.error ex.message
|
61
|
+
exit 1
|
62
|
+
end
|
63
|
+
|
64
|
+
#==================================================================#
|
65
|
+
# inspec plugin install
|
66
|
+
#==================================================================#
|
67
|
+
desc 'install [-v VERSION] PLUGIN', 'Installs a plugin from rubygems.org, a gemfile, or a path to local source.'
|
68
|
+
long_desc <<~EOLD
|
69
|
+
PLUGIN may be the name of a gem on rubygems.org that begins with inspec- or train-.
|
70
|
+
PLUGIN may also be the path to a local gemfile, which will then be installed like
|
71
|
+
any other gem. Finally, if PLUGIN is a path ending in .rb, it is taken to be a
|
72
|
+
local file that will act as athe entry point for a plugin (this mode is provided
|
73
|
+
for local plugin development). Exit codes are 0 on success, 2 if the plugin is
|
74
|
+
already installed, and 1 if any other error occurs.
|
75
|
+
EOLD
|
76
|
+
option :version, desc: 'When installing from rubygems.org, specifies a specific version to install.', aliases: [:v]
|
77
|
+
def install(plugin_id_arg)
|
78
|
+
if plugin_id_arg =~ /\.gem$/ # Does it end in .gem?
|
79
|
+
install_from_gemfile(plugin_id_arg)
|
80
|
+
elsif plugin_id_arg =~ %r{[\/\\]} || Dir.exist?(plugin_id_arg) # Does the argument have a slash, or exist as dir in the local directory?
|
81
|
+
install_from_path(plugin_id_arg)
|
82
|
+
else
|
83
|
+
install_from_remote_gem(plugin_id_arg)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
#--------------------------
|
88
|
+
# update
|
89
|
+
#--------------------------
|
90
|
+
desc 'update PLUGIN', 'Updates a plugin to the latest from from rubygems.org'
|
91
|
+
long_desc <<~EOLD
|
92
|
+
PLUGIN may be the name of a gem on rubygems.org that begins with inspec- or train-.
|
93
|
+
Exit codes are 0 on success, 2 if the plugin is already up to date, and 1 if any
|
94
|
+
other error occurs.
|
95
|
+
EOLD
|
96
|
+
def update(plugin_name)
|
97
|
+
pre_update_versions = installer.list_installed_plugin_gems.select { |spec| spec.name == plugin_name }.map { |spec| spec.version.to_s }
|
98
|
+
old_version = pre_update_versions.join(', ')
|
99
|
+
|
100
|
+
update_preflight_check(plugin_name, pre_update_versions)
|
101
|
+
|
102
|
+
begin
|
103
|
+
installer.update(plugin_name)
|
104
|
+
rescue Inspec::Plugin::V2::UpdateError => ex
|
105
|
+
puts(red { 'Update error: ' } + ex.message + ' - update failed')
|
106
|
+
exit 1
|
107
|
+
end
|
108
|
+
post_update_versions = installer.list_installed_plugin_gems.select { |spec| spec.name == plugin_name }.map { |spec| spec.version.to_s }
|
109
|
+
new_version = (post_update_versions - pre_update_versions).first
|
110
|
+
|
111
|
+
puts(bold { plugin_name } + " plugin, version #{old_version} -> #{new_version}, updated from rubygems.org")
|
112
|
+
end
|
113
|
+
|
114
|
+
#--------------------------
|
115
|
+
# uninstall
|
116
|
+
#--------------------------
|
117
|
+
desc 'uninstall PLUGIN_NAME', 'Uninstalls a gem- or path- based plugin'
|
118
|
+
long_desc <<~EOLD
|
119
|
+
Removes a plugin from the users configuration.
|
120
|
+
In the case of a gem plugin (by far the most common), the plugin gem is removed, along
|
121
|
+
with any of its dependencies that are no longer needed by anything else. Finally, the
|
122
|
+
plugin configuration file is updated to reflect that the plugin is no longer present.
|
123
|
+
In the case of a path-based plugin (often used for plugin development), no changes
|
124
|
+
are made to the referenced plugin source code. Rather, the plugin's entry is simply removed
|
125
|
+
from the plugin config file.
|
126
|
+
EOLD
|
127
|
+
def uninstall(plugin_name)
|
128
|
+
status = Inspec::Plugin::V2::Registry.instance[plugin_name.to_sym]
|
129
|
+
unless status
|
130
|
+
puts(red { 'No such plugin installed: ' } + "#{plugin_name} is not installed - uninstall failed")
|
131
|
+
|
132
|
+
exit 1
|
133
|
+
end
|
134
|
+
installer = Inspec::Plugin::V2::Installer.instance
|
135
|
+
|
136
|
+
pre_uninstall_versions = installer.list_installed_plugin_gems.select { |spec| spec.name == plugin_name }.map { |spec| spec.version.to_s }
|
137
|
+
old_version = pre_uninstall_versions.join(', ')
|
138
|
+
|
139
|
+
installer.uninstall(plugin_name)
|
140
|
+
|
141
|
+
if status.installation_type == :path
|
142
|
+
puts(bold { plugin_name } + ' path-based plugin install has been uninstalled')
|
143
|
+
else
|
144
|
+
puts(bold { plugin_name } + " plugin, version #{old_version}, has been uninstalled")
|
145
|
+
end
|
146
|
+
exit 0
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
|
151
|
+
#==================================================================#
|
152
|
+
# install breakdown
|
153
|
+
#==================================================================#
|
154
|
+
# These are broken down because rubocop complained.
|
155
|
+
|
156
|
+
def install_from_gemfile(gem_file)
|
157
|
+
unless File.exist? gem_file
|
158
|
+
puts(red { 'No such plugin gem file ' } + gem_file + ' - installation failed.')
|
159
|
+
exit 1
|
160
|
+
end
|
161
|
+
|
162
|
+
plugin_name_parts = File.basename(gem_file, '.gem').split('-')
|
163
|
+
version = plugin_name_parts.pop
|
164
|
+
plugin_name = plugin_name_parts.join('-')
|
165
|
+
check_plugin_name(plugin_name, 'installation')
|
166
|
+
|
167
|
+
installer.install(plugin_name, gem_file: gem_file)
|
168
|
+
|
169
|
+
puts(bold { plugin_name } + " plugin, version #{version}, installed from local .gem file")
|
170
|
+
exit 0
|
171
|
+
end
|
172
|
+
|
173
|
+
def install_from_path(path)
|
174
|
+
unless File.exist? path
|
175
|
+
puts(red { 'No such source code path ' } + path + ' - installation failed.')
|
176
|
+
exit 1
|
177
|
+
end
|
178
|
+
|
179
|
+
plugin_name = File.basename(path, '.rb')
|
180
|
+
|
181
|
+
# While installer.install does some rudimentary checking,
|
182
|
+
# this file has good UI access, so we promise to validate the
|
183
|
+
# input a lot and hand the installer a sure-thing.
|
184
|
+
|
185
|
+
# Name OK?
|
186
|
+
check_plugin_name(plugin_name, 'installation')
|
187
|
+
|
188
|
+
# Already installed?
|
189
|
+
if registry.known_plugin?(plugin_name.to_sym)
|
190
|
+
puts(red { 'Plugin already installed' } + " - #{plugin_name} - Use 'inspec plugin list' to see previously installed plugin - installation failed.")
|
191
|
+
exit 2
|
192
|
+
end
|
193
|
+
|
194
|
+
# Can we figure out how to load it?
|
195
|
+
entry_point = install_from_path__apply_entry_point_heuristics(path)
|
196
|
+
|
197
|
+
# If you load it, does it act like a plugin?
|
198
|
+
install_from_path__probe_load(entry_point, plugin_name)
|
199
|
+
|
200
|
+
# OK, install it!
|
201
|
+
installer.install(plugin_name, path: entry_point)
|
202
|
+
|
203
|
+
puts(bold { plugin_name } + ' plugin installed via source path reference, resolved to entry point ' + entry_point)
|
204
|
+
exit 0
|
205
|
+
end
|
206
|
+
|
207
|
+
# Rationale for rubocop variances: It's a heuristics method, and will be full of
|
208
|
+
# conditionals. The code is well-commented; refactoring into sub-methods would
|
209
|
+
# reduce clarity.
|
210
|
+
def install_from_path__apply_entry_point_heuristics(path) # rubocop: disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
211
|
+
given = Pathname.new(path)
|
212
|
+
given = given.expand_path # Resolve any relative paths
|
213
|
+
name_regex = /^(inspec|train)-/
|
214
|
+
versioned_regex = /^(inspec|train)-[a-z0-9\-\_]+-\d+\.\d+\.\d+$/
|
215
|
+
|
216
|
+
# What are the last four things like?
|
217
|
+
parts = [
|
218
|
+
given.parent.parent.basename,
|
219
|
+
given.parent.basename,
|
220
|
+
given.basename('.rb'),
|
221
|
+
given.extname,
|
222
|
+
].map(&:to_s)
|
223
|
+
|
224
|
+
# Case 1: Simplest case: it was a full entry point, as presented.
|
225
|
+
# /home/you/projects/inspec-something/lib/inspec-something.rb
|
226
|
+
# parts index: ^0^ ^1^ ^2^ ^3^
|
227
|
+
if parts[0] =~ name_regex && parts[1] == 'lib' && parts[2] == parts[0] && parts[3] == '.rb'
|
228
|
+
return given.to_s
|
229
|
+
end
|
230
|
+
|
231
|
+
# Case 2: Also easy: they either referred to the internal library directory,
|
232
|
+
# or left the extansion off. Those are the same to us.
|
233
|
+
# /home/you/projects/inspec-something/lib/inspec-something
|
234
|
+
# parts index: ^0^ ^1^ ^2^ (3 is empty)
|
235
|
+
if parts[0] =~ name_regex && parts[1] == 'lib' && parts[2] == parts[0] && parts[3].empty?
|
236
|
+
return given.to_s + '.rb'
|
237
|
+
end
|
238
|
+
|
239
|
+
# Case 3: Maybe they were refering to a path that is inside a gem installation, or an exploded gem?
|
240
|
+
# In that case, we'll have a version on the plugin name in part 0
|
241
|
+
# /home/you/.gems/2.4.0/gems/inspec-something-3.45.1/lib/inspec-something.rb
|
242
|
+
# parts index: ^0^ ^1^ ^2^ ^3^
|
243
|
+
if parts[0] =~ versioned_regex && parts[1] == 'lib' && parts[0].start_with?(parts[2]) && parts[3] == '.rb'
|
244
|
+
return given.to_s
|
245
|
+
end
|
246
|
+
|
247
|
+
# Case 4: Like case 3, but missing the .rb
|
248
|
+
# /home/you/.gems/2.4.0/gems/inspec-something-3.45.1/lib/inspec-something
|
249
|
+
# parts index: ^0^ ^1^ ^2^ ^3^ (empty)
|
250
|
+
if parts[0] =~ versioned_regex && parts[1] == 'lib' && parts[0].start_with?(parts[2]) && parts[3].empty?
|
251
|
+
return given.to_s + '.rb'
|
252
|
+
end
|
253
|
+
|
254
|
+
# Case 5: Easy to recognize, but harder to handle: they referred to the project root.
|
255
|
+
# /home/you/projects/inspec-something
|
256
|
+
# parts index: ^0^ ^1^ ^2^ (3 is empty)
|
257
|
+
# 0 and 1 are not meaningful to us, but we hope to find a parts[2]/lib/inspec-something.rb.
|
258
|
+
entry_point_guess = File.join(given.to_s, 'lib', parts[2] + '.rb')
|
259
|
+
if parts[2] =~ name_regex && File.exist?(entry_point_guess)
|
260
|
+
return entry_point_guess
|
261
|
+
end
|
262
|
+
|
263
|
+
# Well, if we got here, parts[2] matches an inspec/train prefix, but we have no idea about anything.
|
264
|
+
# Give up.
|
265
|
+
puts(red { 'Unrecognizable plugin structure' } + " - #{parts[2]} - When installing from a path, please provide the path of the entry point file - installation failed.")
|
266
|
+
exit 1
|
267
|
+
end
|
268
|
+
|
269
|
+
def install_from_path__probe_load(entry_point, plugin_name)
|
270
|
+
# Brazenly attempt to load a file, and see if it registers a plugin.
|
271
|
+
begin
|
272
|
+
require entry_point
|
273
|
+
rescue LoadError => ex
|
274
|
+
puts(red { 'Plugin contains errors' } + " - #{plugin_name} - Encountered errors while trying to test load the plugin entry point, resolved to #{entry_point} - installation failed")
|
275
|
+
puts ex.message
|
276
|
+
exit 1
|
277
|
+
end
|
278
|
+
|
279
|
+
# OK, the wheels didn't fall off. But is it a plugin?
|
280
|
+
if plugin_name.to_s.start_with?('train')
|
281
|
+
# Train internal names do not include the prix in their registry entries
|
282
|
+
# And the registry is keyed on Strings
|
283
|
+
registry_key = plugin_name.to_s.sub(/^train-/, '')
|
284
|
+
unless Train::Plugins.registry.key?(registry_key)
|
285
|
+
puts(red { 'Does not appear to be a plugin' } + " - #{plugin_name} - After probe-loading the supposed plugin, it did not register itself to Train. Ensure something inherits from 'Train.plugin(1)' - installation failed.")
|
286
|
+
exit 1
|
287
|
+
end
|
288
|
+
else
|
289
|
+
unless registry.known_plugin?(plugin_name.to_sym)
|
290
|
+
puts(red { 'Does not appear to be a plugin' } + " - #{plugin_name} - After probe-loading the supposed plugin, it did not register itself to InSpec. Ensure something inherits from 'Inspec.plugin(2)' - installation failed.")
|
291
|
+
exit 1
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
def install_from_remote_gem(plugin_name)
|
297
|
+
requested_version = options[:version]
|
298
|
+
|
299
|
+
check_plugin_name(plugin_name, 'installation')
|
300
|
+
|
301
|
+
# Version pre-flighting
|
302
|
+
pre_installed_versions = installer.list_installed_plugin_gems.select { |spec| spec.name == plugin_name }.map { |spec| spec.version.to_s }
|
303
|
+
install_from_remote_gem_verson_preflight_check(plugin_name, requested_version, pre_installed_versions)
|
304
|
+
|
305
|
+
install_attempt_install(plugin_name)
|
306
|
+
|
307
|
+
# Success messaging. What did we actually install?
|
308
|
+
post_installed_versions = installer.list_installed_plugin_gems.select { |spec| spec.name == plugin_name }.map { |spec| spec.version.to_s }
|
309
|
+
new_version = (post_installed_versions - pre_installed_versions).first
|
310
|
+
|
311
|
+
puts(bold { plugin_name } + " plugin, version #{new_version}, installed from rubygems.org")
|
312
|
+
exit 0
|
313
|
+
end
|
314
|
+
|
315
|
+
def install_from_remote_gem_verson_preflight_check(plugin_name, requested_version, pre_installed_versions)
|
316
|
+
return if pre_installed_versions.empty?
|
317
|
+
|
318
|
+
# Everything past here in the block is a code 2 error
|
319
|
+
|
320
|
+
# If they didn't ask for a specific version, they implicitly ask for the latest.
|
321
|
+
# Do an expensive search to determine the latest version.
|
322
|
+
unless requested_version
|
323
|
+
latest_version = installer.search(plugin_name, exact: true, scope: :latest)
|
324
|
+
latest_version = latest_version[plugin_name]&.last
|
325
|
+
if latest_version && !requested_version
|
326
|
+
requested_version = latest_version
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
# Check for already-installed at desired version conditions
|
331
|
+
they_explicitly_asked_for_a_version = !options[:version].nil?
|
332
|
+
what_we_would_install_is_already_installed = pre_installed_versions.include?(requested_version)
|
333
|
+
if what_we_would_install_is_already_installed && they_explicitly_asked_for_a_version
|
334
|
+
puts(red { 'Plugin already installed at requested version' } + " - plugin #{plugin_name} #{requested_version} - refusing to install.")
|
335
|
+
elsif what_we_would_install_is_already_installed && !they_explicitly_asked_for_a_version
|
336
|
+
puts(red { 'Plugin already installed at latest version' } + " - plugin #{plugin_name} #{requested_version} - refusing to install.")
|
337
|
+
else
|
338
|
+
# There are existing versions installed, but none of them are what was requested
|
339
|
+
puts(red { 'Update required' } + " - plugin #{plugin_name}, requested #{requested_version}, have #{pre_installed_versions.join(', ')}; use `inspec plugin update` - refusing to install.")
|
340
|
+
end
|
341
|
+
|
342
|
+
exit 2
|
343
|
+
end
|
344
|
+
|
345
|
+
def install_attempt_install(plugin_name)
|
346
|
+
installer.install(plugin_name, version: options[:version])
|
347
|
+
rescue Inspec::Plugin::V2::InstallError
|
348
|
+
results = installer.search(plugin_name, exact: true)
|
349
|
+
if results.empty?
|
350
|
+
puts(red { 'No such plugin gem ' } + plugin_name + ' could be found on rubygems.org - installation failed.')
|
351
|
+
elsif options[:version] && !results[plugin_name].include?(options[:version])
|
352
|
+
puts(red { 'No such version' } + ' - ' + plugin_name + " exists, but no such version #{options[:version]} found on rubygems.org - installation failed.")
|
353
|
+
else
|
354
|
+
puts(red { 'Unknown error occured ' } + ' - installation failed.')
|
355
|
+
end
|
356
|
+
exit 1
|
357
|
+
end
|
358
|
+
|
359
|
+
#==================================================================#
|
360
|
+
# update breakdown
|
361
|
+
#==================================================================#
|
362
|
+
def update_preflight_check(plugin_name, pre_update_versions)
|
363
|
+
if pre_update_versions.empty?
|
364
|
+
# Check for path install
|
365
|
+
status = Inspec::Plugin::V2::Registry.instance[plugin_name.to_sym]
|
366
|
+
if !status
|
367
|
+
puts(red { 'No such plugin installed: ' } + "#{plugin_name} - update failed")
|
368
|
+
exit 1
|
369
|
+
elsif status.installation_type == :path
|
370
|
+
puts(red { 'Cannot update path-based install: ' } + "#{plugin_name} is installed via path reference; use `inspec plugin uninstall` to remove - refusing to update")
|
371
|
+
exit 2
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
# Check for latest version (and implicitly, existance)
|
376
|
+
latest_version = installer.search(plugin_name, exact: true, scope: :latest)
|
377
|
+
latest_version = latest_version[plugin_name]&.last
|
378
|
+
|
379
|
+
if pre_update_versions.include?(latest_version)
|
380
|
+
puts(red { 'Already installed at latest version: ' } + "#{plugin_name} is at #{latest_version}, which the latest - refusing to update")
|
381
|
+
exit 2
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
#==================================================================#
|
386
|
+
# utilities
|
387
|
+
#==================================================================#
|
388
|
+
def installer
|
389
|
+
Inspec::Plugin::V2::Installer.instance
|
390
|
+
end
|
391
|
+
|
392
|
+
def registry
|
393
|
+
Inspec::Plugin::V2::Registry.instance
|
394
|
+
end
|
395
|
+
|
396
|
+
def check_plugin_name(plugin_name, action)
|
397
|
+
unless plugin_name =~ /^(inspec|train)-/
|
398
|
+
puts(red { 'Invalid plugin name' } + " - #{plugin_name} - All inspec plugins must begin with either 'inspec-' or 'train-' - #{action} failed.")
|
399
|
+
exit 1
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
def make_pretty_version(status)
|
404
|
+
case status.installation_type
|
405
|
+
when :core, :bundle
|
406
|
+
Inspec::VERSION
|
407
|
+
when :gem
|
408
|
+
# TODO: this is naive, and assumes the latest version is the one that will be used. Logged on #3317
|
409
|
+
# In fact, the logic to determine "what version would be used" belongs in the Loader.
|
410
|
+
Inspec::Plugin::V2::Loader.list_installed_plugin_gems
|
411
|
+
.select { |spec| spec.name == status.name.to_s }
|
412
|
+
.sort_by(&:version)
|
413
|
+
.last.version
|
414
|
+
when :path
|
415
|
+
'src'
|
416
|
+
end
|
417
|
+
end
|
418
|
+
end
|
419
|
+
end
|
420
|
+
end
|