inspec-core 2.2.112 → 2.3.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|