inspec-core 5.18.14 → 5.22.3

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +19 -17
  3. data/inspec-core.gemspec +22 -22
  4. data/lib/inspec/base_cli.rb +19 -17
  5. data/lib/inspec/cli.rb +27 -25
  6. data/lib/inspec/dependencies/dependency_set.rb +2 -2
  7. data/lib/inspec/dsl.rb +9 -5
  8. data/lib/inspec/enhanced_outcomes.rb +19 -0
  9. data/lib/inspec/env_printer.rb +1 -1
  10. data/lib/inspec/exceptions.rb +2 -0
  11. data/lib/inspec/formatters/base.rb +69 -16
  12. data/lib/inspec/plugin/v2/loader.rb +19 -8
  13. data/lib/inspec/plugin/v2/plugin_types/reporter.rb +1 -0
  14. data/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb +54 -0
  15. data/lib/inspec/profile.rb +9 -8
  16. data/lib/inspec/reporters/base.rb +1 -0
  17. data/lib/inspec/reporters/cli.rb +94 -3
  18. data/lib/inspec/reporters/json.rb +3 -1
  19. data/lib/inspec/reporters/yaml.rb +3 -1
  20. data/lib/inspec/reporters.rb +2 -1
  21. data/lib/inspec/resources/file.rb +1 -1
  22. data/lib/inspec/resources/http.rb +5 -5
  23. data/lib/inspec/resources/lxc.rb +65 -9
  24. data/lib/inspec/resources/mongodb_session.rb +5 -0
  25. data/lib/inspec/resources/nftables.rb +251 -0
  26. data/lib/inspec/resources/oracledb_session.rb +13 -4
  27. data/lib/inspec/resources/podman.rb +353 -0
  28. data/lib/inspec/resources/podman_container.rb +84 -0
  29. data/lib/inspec/resources/podman_image.rb +108 -0
  30. data/lib/inspec/resources/podman_network.rb +81 -0
  31. data/lib/inspec/resources/podman_pod.rb +101 -0
  32. data/lib/inspec/resources/podman_volume.rb +87 -0
  33. data/lib/inspec/resources/postgres_session.rb +2 -1
  34. data/lib/inspec/resources/service.rb +1 -1
  35. data/lib/inspec/resources.rb +1 -0
  36. data/lib/inspec/rule.rb +54 -17
  37. data/lib/inspec/run_data/control.rb +6 -0
  38. data/lib/inspec/run_data/statistics.rb +8 -2
  39. data/lib/inspec/runner.rb +18 -8
  40. data/lib/inspec/runner_rspec.rb +3 -2
  41. data/lib/inspec/schema/exec_json.rb +78 -2
  42. data/lib/inspec/schema/output_schema.rb +4 -1
  43. data/lib/inspec/schema/profile_json.rb +46 -0
  44. data/lib/inspec/schema.rb +91 -0
  45. data/lib/inspec/utils/convert.rb +8 -0
  46. data/lib/inspec/utils/podman.rb +24 -0
  47. data/lib/inspec/utils/simpleconfig.rb +10 -2
  48. data/lib/inspec/utils/waivers/csv_file_reader.rb +34 -0
  49. data/lib/inspec/utils/waivers/excel_file_reader.rb +39 -0
  50. data/lib/inspec/utils/waivers/json_file_reader.rb +15 -0
  51. data/lib/inspec/version.rb +1 -1
  52. data/lib/inspec/waiver_file_reader.rb +61 -0
  53. data/lib/matchers/matchers.rb +15 -2
  54. data/lib/plugins/inspec-init/templates/profiles/alicloud/README.md +27 -0
  55. data/lib/plugins/inspec-init/templates/profiles/alicloud/controls/example.rb +10 -0
  56. data/lib/plugins/inspec-init/templates/profiles/alicloud/inputs.yml +1 -0
  57. data/lib/plugins/inspec-init/templates/profiles/alicloud/inspec.yml +14 -0
  58. data/lib/plugins/inspec-reporter-html2/README.md +1 -1
  59. data/lib/plugins/inspec-reporter-html2/templates/body.html.erb +7 -1
  60. data/lib/plugins/inspec-reporter-html2/templates/control.html.erb +10 -6
  61. data/lib/plugins/inspec-reporter-html2/templates/default.css +12 -0
  62. data/lib/plugins/inspec-reporter-html2/templates/selector.html.erb +7 -1
  63. data/lib/plugins/inspec-sign/lib/inspec-sign/base.rb +5 -2
  64. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +39 -13
  65. metadata +26 -9
@@ -178,7 +178,19 @@ module Inspec::Plugin::V2
178
178
  # TODO: enforce first-level version pinning
179
179
  plugin_deps = [Gem::Dependency.new(plugin_gem_name.to_s, version_constraint)]
180
180
  managed_gem_set = Gem::Resolver::VendorSet.new
181
- list_managed_gems.each { |spec| managed_gem_set.add_vendor_gem(spec.name, spec.gem_dir) }
181
+
182
+ list_managed_gems.each do |spec|
183
+ if Gem::Specification.load spec.gem_dir
184
+ managed_gem_set.add_vendor_gem(spec.name, spec.gem_dir)
185
+ else
186
+ # In case of invalid gemspec as mentioned in this PR https://github.com/brianmario/yajl-ruby/pull/223
187
+ # the add_vendor_gem breaks. So this is patch to fix the loading issue.
188
+ # Horribly, chdir to gemspec path to honor . in gemspec
189
+ Dir.chdir(spec.gem_dir) do |dir|
190
+ managed_gem_set.add_vendor_gem(spec.name, spec.gem_dir)
191
+ end
192
+ end
193
+ end
182
194
 
183
195
  # TODO: Next two lines merge our managed gems with the other gems available
184
196
  # in our "local universe" - which may be the system, or it could be in a Bundler microcosm,
@@ -278,7 +290,12 @@ module Inspec::Plugin::V2
278
290
  when :user_gem
279
291
  status.entry_point = status.name.to_s
280
292
  status.version = plugin_entry[:version]
281
- status.description = fetch_plugin_specs(status.name.to_s)&.summary
293
+ # Fetch the summary of the gem from local gemspec file instead of remote call using Gem::SpecFetcher.fetcher.
294
+ unless plugin_entry[:version].nil? # safe check very rare case.
295
+ version_string = plugin_entry[:version].gsub(/[=,~,>,<]/, "").strip
296
+ plugin_name_with_version = "#{status.name}-#{version_string}"
297
+ status.description = fetch_gemspec(File.join(plugin_gem_path, "gems", plugin_name_with_version, "/", status.name.to_s + ".gemspec"))&.summary
298
+ end
282
299
  when :path
283
300
  status.entry_point = plugin_entry[:installation_path]
284
301
  end
@@ -287,12 +304,6 @@ module Inspec::Plugin::V2
287
304
  end
288
305
  end
289
306
 
290
- def fetch_plugin_specs(plugin_name)
291
- fetcher = Gem::SpecFetcher.fetcher
292
- plugin_dependency = Gem::Dependency.new(plugin_name)
293
- fetcher.spec_for_dependency(plugin_dependency).flatten.first
294
- end
295
-
296
307
  def fixup_train_plugin_status(status)
297
308
  status.api_generation = :'train-1'
298
309
  if status.installation_type == :user_gem
@@ -7,6 +7,7 @@ module Inspec::Plugin::V2::PluginType
7
7
  include Inspec::Utils::RunDataFilters
8
8
 
9
9
  attr_reader :run_data
10
+ attr_accessor :enhanced_outcomes
10
11
 
11
12
  def initialize(config)
12
13
  @config = config
@@ -11,6 +11,7 @@ module Inspec::Plugin::V2::PluginType
11
11
  @running_controls_list = []
12
12
  @control_checks_count_map = {}
13
13
  @controls_count = nil
14
+ @notifications = {}
14
15
  end
15
16
 
16
17
  private
@@ -49,5 +50,58 @@ module Inspec::Plugin::V2::PluginType
49
50
  @control_checks_count_map = RSpec.configuration.formatters.grep(Inspec::Formatters::Base).first.get_control_checks_count_map
50
51
  end
51
52
  end
53
+
54
+ def enhanced_outcomes
55
+ @enhanced_outcomes ||= RSpec.configuration.formatters.grep(Inspec::Formatters::Base).first.enhanced_outcomes
56
+ end
57
+
58
+ def add_enhanced_outcomes(control_id)
59
+ if control_has_error(@notifications[control_id])
60
+ "error"
61
+ elsif control_has_impact_zero(@notifications[control_id])
62
+ "not_applicable"
63
+ elsif control_has_all_tests_skipped(@notifications[control_id])
64
+ "not_reviewed"
65
+ elsif control_has_any_tests_failed(@notifications[control_id])
66
+ "failed"
67
+ else
68
+ "passed"
69
+ end
70
+ end
71
+
72
+ def control_has_error(notifications)
73
+ notifications.any? do |notification_data|
74
+ notification, _status = notification_data
75
+ !notification.example.exception.nil? && !(notification.example.exception.is_a? RSpec::Expectations::ExpectationNotMetError) && !notification.example.exception.backtrace.nil? && (!notification.description.include? "No-op") # No-op exception occurs in case of not_applicable_if
76
+ end
77
+ end
78
+
79
+ def control_has_all_tests_skipped(notifications)
80
+ notifications.all? do |notification_data|
81
+ _notification, status = notification_data
82
+ status == "skipped"
83
+ end
84
+ end
85
+
86
+ def control_has_any_tests_failed(notifications)
87
+ notifications.any? do |notification_data|
88
+ _notification, status = notification_data
89
+ status == "failed"
90
+ end
91
+ end
92
+
93
+ def control_has_impact_zero(notifications)
94
+ notification_data = notifications.first
95
+ notification_impact = notification_data.first.example.metadata[:impact]
96
+ notification_data && !notification_impact.nil? && notification_impact.to_f == 0.0
97
+ end
98
+
99
+ def collect_notifications(notification, control_id, status)
100
+ if @notifications[control_id].nil?
101
+ @notifications[control_id] = [[notification, status]]
102
+ else
103
+ @notifications[control_id].push([notification, status])
104
+ end
105
+ end
52
106
  end
53
107
  end
@@ -383,24 +383,25 @@ module Inspec
383
383
  @runner_context
384
384
  end
385
385
 
386
- def collect_gem_dependencies(profile_context)
386
+ # This collects the gem dependencies data from parent and its dependent profiles
387
+ def collect_gem_dependencies
387
388
  gem_dependencies = []
388
- all_profiles = []
389
- profile_context.dependencies.list.values.each do |requirement|
390
- all_profiles << requirement.profile
391
- end
392
- all_profiles << self
393
- all_profiles.each do |profile|
389
+ # This collects the dependent profiles gem dependencies if any
390
+ locked_dependencies.dep_list.each do |_name, dep|
391
+ profile = dep.profile
394
392
  gem_dependencies << profile.metadata.gem_dependencies unless profile.metadata.gem_dependencies.empty?
395
393
  end
394
+ # Appends the parent profile gem dependencies which are available through metadata
395
+ gem_dependencies << metadata.gem_dependencies unless metadata.gem_dependencies.empty?
396
396
  gem_dependencies.flatten.uniq
397
397
  end
398
398
 
399
399
  # Loads the required gems specified in the Profile's metadata file from default inspec gems path i.e. ~/.inspec/gems
400
400
  # else installs and loads them.
401
401
  def load_gem_dependencies
402
- gem_dependencies = collect_gem_dependencies(load_libraries)
402
+ gem_dependencies = collect_gem_dependencies
403
403
  gem_dependencies.each do |gem_data|
404
+
404
405
  dependency_loader = DependencyLoader.new
405
406
  if dependency_loader.gem_version_installed?(gem_data[:name], gem_data[:version]) ||
406
407
  dependency_loader.gem_installed?(gem_data[:name])
@@ -5,6 +5,7 @@ module Inspec::Reporters
5
5
  include Inspec::Utils::RunDataFilters
6
6
 
7
7
  attr_reader :run_data
8
+ attr_accessor :enhanced_outcomes
8
9
 
9
10
  def initialize(config)
10
11
  @config = config
@@ -9,6 +9,9 @@ module Inspec::Reporters
9
9
  "passed" => "\033[0;1;32m",
10
10
  "skipped" => "\033[0;37m",
11
11
  "reset" => "\033[0m",
12
+ "error" => "\033[34m",
13
+ "not_applicable" => "\033[36m",
14
+ "not_reviewed" => "\033[33m",
12
15
  }.freeze
13
16
 
14
17
  # Most currently available Windows terminals have poor support
@@ -18,6 +21,9 @@ module Inspec::Reporters
18
21
  "skipped" => "[SKIP]",
19
22
  "passed" => "[PASS]",
20
23
  "unknown" => "[UNKN]",
24
+ "error" => "[ERR]",
25
+ "not_applicable" => "[N/A]",
26
+ "not_reviewed" => "[N/R]",
21
27
  }.freeze
22
28
  else
23
29
  # Extended colors for everyone else
@@ -26,6 +32,9 @@ module Inspec::Reporters
26
32
  "passed" => "\033[38;5;41m",
27
33
  "skipped" => "\033[38;5;247m",
28
34
  "reset" => "\033[0m",
35
+ "error" => "\033[0;38;5;21m",
36
+ "not_applicable" => "\033[0;38;5;117m",
37
+ "not_reviewed" => "\033[0;38;5;214m",
29
38
  }.freeze
30
39
 
31
40
  # Groovy UTF-8 characters for everyone else...
@@ -35,6 +44,9 @@ module Inspec::Reporters
35
44
  "skipped" => "↺",
36
45
  "passed" => "✔",
37
46
  "unknown" => "?",
47
+ "error" => "ERR",
48
+ "not_applicable" => "N/A",
49
+ "not_reviewed" => "N/R",
38
50
  }.freeze
39
51
  end
40
52
 
@@ -63,7 +75,11 @@ module Inspec::Reporters
63
75
  end
64
76
 
65
77
  output("")
66
- print_profile_summary
78
+ if enhanced_outcomes
79
+ print_control_outcomes_summary
80
+ else
81
+ print_profile_summary
82
+ end
67
83
  print_tests_summary
68
84
  end
69
85
 
@@ -88,6 +104,7 @@ module Inspec::Reporters
88
104
  def print_standard_control_results(profile)
89
105
  standard_controls_from_profile(profile).each do |control_from_profile|
90
106
  control = Control.new(control_from_profile)
107
+ control.enhanced_outcomes = enhanced_outcomes
91
108
  next if control.results.nil?
92
109
 
93
110
  output(format_control_header(control))
@@ -122,7 +139,7 @@ module Inspec::Reporters
122
139
  end
123
140
 
124
141
  def format_control_header(control)
125
- impact = control.impact_string
142
+ impact = enhanced_outcomes ? control.impact_string_for_enhanced_outcomes : control.impact_string
126
143
  format_message(
127
144
  color: impact,
128
145
  indicator: impact,
@@ -292,6 +309,68 @@ module Inspec::Reporters
292
309
  }
293
310
  end
294
311
 
312
+ def control_outcomes_summary
313
+ failed = 0
314
+ passed = 0
315
+ error = 0
316
+ not_reviewed = 0
317
+ not_applicable = 0
318
+
319
+ all_unique_controls.each do |control|
320
+ next if control[:status].empty?
321
+
322
+ if control[:status] == "failed"
323
+ failed += 1
324
+ elsif control[:status] == "error"
325
+ error += 1
326
+ elsif control[:status] == "not_reviewed"
327
+ not_reviewed += 1
328
+ elsif control[:status] == "not_applicable"
329
+ not_applicable += 1
330
+ else
331
+ passed += 1
332
+ end
333
+ end
334
+
335
+ total = failed + passed + error + not_reviewed + not_applicable
336
+
337
+ {
338
+ "total" => total,
339
+ "failed" => failed,
340
+ "passed" => passed,
341
+ "error" => error,
342
+ "not_reviewed" => not_reviewed,
343
+ "not_applicable" => not_applicable,
344
+ }
345
+ end
346
+
347
+ def print_control_outcomes_summary
348
+ summary = control_outcomes_summary
349
+ return unless summary["total"] > 0
350
+
351
+ success_str = summary["passed"] == 1 ? "1 successful control" : "#{summary["passed"]} successful controls"
352
+ failed_str = summary["failed"] == 1 ? "1 control failure" : "#{summary["failed"]} control failures"
353
+ error_str = summary["error"] == 1 ? "1 control has error" : "#{summary["error"]} controls have error"
354
+ not_rev_str = summary["not_reviewed"] == 1 ? "1 control not reviewed" : "#{summary["not_reviewed"]} controls not reviewed"
355
+ not_app_str = summary["not_applicable"] == 1 ? "1 control not applicable" : "#{summary["not_applicable"]} controls not applicable"
356
+
357
+ success_color = summary["passed"] > 0 ? "passed" : "no_color"
358
+ failed_color = summary["failed"] > 0 ? "failed" : "no_color"
359
+ error_color = summary["error"] > 0 ? "error" : "no_color"
360
+ not_rev_color = summary["not_reviewed"] > 0 ? "not_reviewed" : "no_color"
361
+ not_app_color = summary["not_applicable"] > 0 ? "not_applicable" : "no_color"
362
+
363
+ s = format(
364
+ "Profile Summary: %s, %s, %s, %s, %s",
365
+ format_with_color(success_color, success_str),
366
+ format_with_color(failed_color, failed_str),
367
+ format_with_color(not_rev_color, not_rev_str),
368
+ format_with_color(not_app_color, not_app_str),
369
+ format_with_color(error_color, error_str)
370
+ )
371
+ output(s) if summary["total"] > 0
372
+ end
373
+
295
374
  def print_profile_summary
296
375
  summary = profile_summary
297
376
  return unless summary["total"] > 0
@@ -350,6 +429,7 @@ module Inspec::Reporters
350
429
 
351
430
  class Control
352
431
  attr_reader :data
432
+ attr_accessor :enhanced_outcomes
353
433
 
354
434
  def initialize(control_hash)
355
435
  @data = control_hash
@@ -379,6 +459,10 @@ module Inspec::Reporters
379
459
  id.start_with?("(generated from ")
380
460
  end
381
461
 
462
+ def status
463
+ data[:status]
464
+ end
465
+
382
466
  def title_for_report
383
467
  # if this is an anonymous control, just grab the resource title from any result entry
384
468
  return results.first[:resource_title] if anonymous?
@@ -392,10 +476,17 @@ module Inspec::Reporters
392
476
  # append a failure summary if appropriate.
393
477
  title_for_report += " (#{failure_count} failed)" if failure_count > 0
394
478
  title_for_report += " (#{skipped_count} skipped)" if skipped_count > 0
395
-
396
479
  title_for_report
397
480
  end
398
481
 
482
+ def impact_string_for_enhanced_outcomes
483
+ if impact.nil?
484
+ "unknown"
485
+ else
486
+ status
487
+ end
488
+ end
489
+
399
490
  def impact_string
400
491
  if anonymous?
401
492
  nil
@@ -114,7 +114,7 @@ module Inspec::Reporters
114
114
 
115
115
  def profile_controls(profile)
116
116
  (profile[:controls] || []).map { |c|
117
- {
117
+ control_hash = {
118
118
  id: c[:id],
119
119
  title: c[:title],
120
120
  desc: c.dig(:descriptions, :default),
@@ -130,6 +130,8 @@ module Inspec::Reporters
130
130
  waiver_data: c[:waiver_data] || {},
131
131
  results: profile_results(c),
132
132
  }
133
+ control_hash.merge!({ status: c[:status] }) if enhanced_outcomes
134
+ control_hash
133
135
  }
134
136
  end
135
137
 
@@ -3,7 +3,9 @@ require "yaml"
3
3
  module Inspec::Reporters
4
4
  class Yaml < Base
5
5
  def render
6
- output(Inspec::Reporters::Json.new({ run_data: run_data }).report.to_yaml, false)
6
+ json_reporter_obj = Inspec::Reporters::Json.new({ run_data: run_data })
7
+ json_reporter_obj.enhanced_outcomes = enhanced_outcomes
8
+ output(json_reporter_obj.report.to_yaml, false)
7
9
  end
8
10
 
9
11
  def report
@@ -7,7 +7,7 @@ require "inspec/reporters/yaml"
7
7
 
8
8
  module Inspec::Reporters
9
9
  # rubocop:disable Metrics/CyclomaticComplexity
10
- def self.render(reporter, run_data)
10
+ def self.render(reporter, run_data, enhanced_outcomes = false)
11
11
  name, config = reporter.dup
12
12
  config[:run_data] = run_data
13
13
  case name
@@ -29,6 +29,7 @@ module Inspec::Reporters
29
29
  activator.activate!
30
30
  reporter = activator.implementation_class.new(config)
31
31
  end
32
+ reporter.enhanced_outcomes = enhanced_outcomes
32
33
 
33
34
  # optional send_report method on reporter
34
35
  return reporter.send_report if defined?(reporter.send_report)
@@ -66,7 +66,7 @@ module Inspec::Resources
66
66
  def user_permissions
67
67
  return {} unless exist?
68
68
 
69
- return skip_reource"`user_permissions` is not supported on your OS yet." unless inspec.os.windows?
69
+ return skip_resource "`user_permissions` is not supported on your OS yet." unless inspec.os.windows?
70
70
 
71
71
  @perms_provider.user_permissions(file)
72
72
  end
@@ -4,7 +4,7 @@
4
4
 
5
5
  require "inspec/resources/command"
6
6
  require "faraday" unless defined?(Faraday)
7
- require "faraday_middleware"
7
+ require "faraday/follow_redirects"
8
8
  require "hashie"
9
9
 
10
10
  module Inspec::Resources
@@ -153,7 +153,7 @@ module Inspec::Resources
153
153
 
154
154
  conn = Faraday.new(url: url, headers: request_headers, params: params, ssl: { verify: ssl_verify? }) do |builder|
155
155
  builder.request :url_encoded
156
- builder.use FaradayMiddleware::FollowRedirects, limit: max_redirects unless max_redirects.nil?
156
+ builder.use Faraday::FollowRedirects::Middleware, limit: max_redirects unless max_redirects.nil?
157
157
  builder.adapter Faraday.default_adapter
158
158
  end
159
159
 
@@ -304,11 +304,11 @@ module Inspec::Resources
304
304
  # Insecure not supported simply https://stackoverflow.com/questions/11696944/powershell-v3-invoke-webrequest-https-error
305
305
  cmd << "-MaximumRedirection #{max_redirects}" unless max_redirects.nil?
306
306
  request_headers["Authorization"] = """ '\"Basic ' + [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(\"#{username}:#{password}\")) +'\"' """ unless username.nil? || password.nil?
307
- request_header_string = nil
307
+ request_header_array = []
308
308
  request_headers.each do |k, v|
309
- request_header_string << " #{k} = #{v}"
309
+ request_header_array << " '#{k}' = '#{v}'"
310
310
  end
311
- cmd << "-Headers @{#{request_header_string.join(";")}}" unless request_header_string.nil?
311
+ cmd << "-Headers @{#{request_header_array.join(";")}}" unless request_header_array.empty?
312
312
  if params.nil?
313
313
  cmd << "'#{url}'"
314
314
  else
@@ -9,14 +9,26 @@ module Inspec::Resources
9
9
  describe lxc("ubuntu-container") do
10
10
  it { should exist }
11
11
  it { should be_running }
12
+ its("name") { should eq "ubuntu-container" }
13
+ its("status") { should cmp "Running" }
14
+ its("type") { should eq "container" }
15
+ its("architecture") { should eq "x86_64" }
16
+ its("pid") { should eq 1378 }
17
+ its("created_at") { should eq "2022/08/16 12:07 UTC" }
18
+ its("last_used_at") { should eq "2022/08/17 05:06 UTC" }
19
+ its("resources") { should include "Disk usage" }
12
20
  end
13
21
  EXAMPLE
14
22
 
23
+ attr_reader :container_info, :container_name
24
+
15
25
  # Resource initialization.
16
26
  def initialize(container_name)
17
27
  @container_name = container_name
18
28
 
19
29
  raise Inspec::Exceptions::ResourceSkipped, "The `lxc` resource is not supported on your OS yet." unless inspec.os.linux?
30
+
31
+ @container_info = populate_container_info
20
32
  end
21
33
 
22
34
  def resource_id
@@ -28,17 +40,60 @@ module Inspec::Resources
28
40
  end
29
41
 
30
42
  def exists?
31
- lxc_info_cmd.exit_status.to_i == 0
43
+ !@container_info.empty?
32
44
  end
33
45
 
34
46
  def running?
35
- container_info = lxc_info_cmd.stdout.split(":").map(&:strip)
36
- container_info[0] == "Status" && container_info[1] == "Running"
47
+ @container_info.key?("Status") && @container_info["Status"].casecmp("Running") == 0
48
+ end
49
+
50
+ def name
51
+ @container_info["Name"]
52
+ end
53
+
54
+ def status
55
+ @container_info["Status"]
56
+ end
57
+
58
+ def type
59
+ @container_info["Type"]
60
+ end
61
+
62
+ def architecture
63
+ @container_info["Architecture"]
64
+ end
65
+
66
+ def pid
67
+ @container_info["PID"]
68
+ end
69
+
70
+ def created_at
71
+ @container_info["Created"]
72
+ end
73
+
74
+ def last_used_at
75
+ @container_info["Last Used"]
76
+ end
77
+
78
+ def resources
79
+ @container_info["Resources"]
37
80
  end
38
81
 
39
82
  private
40
83
 
41
- # Method to find lxc
84
+ def populate_container_info
85
+ lxc_util = find_lxc_or_error
86
+ lxc_info_cmd = inspec.command("#{lxc_util} info #{@container_name}")
87
+
88
+ if lxc_info_cmd.exit_status.to_i == 0
89
+ parse_command_output(lxc_info_cmd.stdout)
90
+ elsif lxc_info_cmd.stderr =~ /Error: Instance not found/
91
+ {}
92
+ else
93
+ raise Inspec::Exceptions::ResourceFailed, "Unable to retrieve information for #{container_name}.\n#{lxc_info_cmd.stderr}"
94
+ end
95
+ end
96
+
42
97
  def find_lxc_or_error
43
98
  %w{/usr/sbin/lxc /sbin/lxc lxc}.each do |cmd|
44
99
  return cmd if inspec.command(cmd).exist?
@@ -47,11 +102,12 @@ module Inspec::Resources
47
102
  raise Inspec::Exceptions::ResourceFailed, "Could not find `lxc`"
48
103
  end
49
104
 
50
- def lxc_info_cmd
51
- bin = find_lxc_or_error
52
- info_cmd = "info #{@container_name} | grep -i Status"
53
- lxc_cmd = format("%s %s", bin, info_cmd).strip
54
- inspec.command(lxc_cmd)
105
+ def parse_command_output(output)
106
+ require "yaml" unless defined?(YAML)
107
+ YAML.load(output)
108
+ rescue Psych::SyntaxError => e
109
+ warn "Could not parse the command output.\n#{e.message}"
110
+ {}
55
111
  end
56
112
  end
57
113
  end
@@ -84,6 +84,11 @@ module Inspec::Resources
84
84
  options[:ssl_cert] = @ssl_cert unless @ssl_cert.nil?
85
85
  options[:ssl_ca_cert] = @ssl_ca_cert unless @ssl_ca_cert.nil?
86
86
 
87
+ # Setting the logger level to INFO as mongo gem version 2.13.2 is using DEBUG as the log level Ref: https://github.com/mongodb/mongo-ruby-driver/blob/v2.13.2/lib/mongo/logger.rb#L79
88
+ # Latest version of the mongo gem don't have this issue as it set to INFO level Ref: https://github.com/mongodb/mongo-ruby-driver/blob/master/lib/mongo/logger.rb#L82
89
+ # We pinned the version to 2.13.2 as the latest version of the mongo gem has broken symlink https://jira.mongodb.org/browse/RUBY-2546 which causes omnibus build failure.
90
+ # Once we get the latest version working we can remove logger level set here.
91
+ Mongo::Logger.logger.level = Logger::INFO
87
92
  @client = Mongo::Client.new([ "#{host}:#{port}" ], options)
88
93
 
89
94
  rescue => e