Merge pull request #4365 from inspec/zenspider/the-need-for-speed

Make inspec much faster for most commands.
This commit is contained in:
Ryan Davis 2019-09-09 18:02:10 -07:00 committed by GitHub
commit f5bbfe5fac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 78 additions and 27 deletions

View file

@ -28,4 +28,3 @@ require "inspec/base_cli"
require "inspec/fetcher" require "inspec/fetcher"
require "inspec/source_reader" require "inspec/source_reader"
require "inspec/resource" require "inspec/resource"
require "inspec/resources"

View file

@ -4,6 +4,7 @@ require "train"
require "inspec/config" require "inspec/config"
require "inspec/version" require "inspec/version"
require "inspec/resource" require "inspec/resource"
require "inspec/dsl" # for method_missing_resource
module Inspec module Inspec
module Backend module Backend
@ -77,6 +78,12 @@ module Inspec
connection connection
end end
def method_missing(id, *args, &blk)
Inspec::DSL.method_missing_resource(self, id, *args)
rescue LoadError
super
end
Inspec::Resource.registry.each do |id, r| Inspec::Resource.registry.each do |id, r|
define_method id.to_sym do |*args| define_method id.to_sym do |*args|
r.new(self, id.to_s, *args) r.new(self, id.to_s, *args)

View file

@ -64,7 +64,6 @@ class Inspec::InspecCLI < Inspec::BaseCLI
desc: "A list of controls to include. Ignore all other tests." desc: "A list of controls to include. Ignore all other tests."
profile_options profile_options
def json(target) def json(target)
require "inspec/resources"
require "json" require "json"
o = config o = config
@ -103,8 +102,6 @@ class Inspec::InspecCLI < Inspec::BaseCLI
option :format, type: :string option :format, type: :string
profile_options profile_options
def check(path) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength def check(path) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
require "inspec/resources"
o = config o = config
diagnose(o) diagnose(o)
o["log_location"] ||= STDERR if o["format"] == "json" o["log_location"] ||= STDERR if o["format"] == "json"
@ -157,8 +154,6 @@ class Inspec::InspecCLI < Inspec::BaseCLI
option :overwrite, type: :boolean, default: false, option :overwrite, type: :boolean, default: false,
desc: "Overwrite existing vendored dependencies and lockfile." desc: "Overwrite existing vendored dependencies and lockfile."
def vendor(path = nil) def vendor(path = nil)
require "inspec/resources"
o = config o = config
configure_logger(o) configure_logger(o)
o[:logger] = Logger.new($stdout) o[:logger] = Logger.new($stdout)
@ -180,8 +175,6 @@ class Inspec::InspecCLI < Inspec::BaseCLI
option :ignore_errors, type: :boolean, default: false, option :ignore_errors, type: :boolean, default: false,
desc: "Ignore profile warnings." desc: "Ignore profile warnings."
def archive(path) def archive(path)
require "inspec/resources"
o = config o = config
diagnose(o) diagnose(o)

View file

@ -66,9 +66,11 @@ module Inspec
# We still haven't called it, so do so now. # We still haven't called it, so do so now.
send(method_name, *arguments, &block) send(method_name, *arguments, &block)
else else
# If we couldn't find a plugin to match, maybe something up above has it, begin
# or maybe it is just a unknown method error. Inspec::DSL.method_missing_resource(inspec, method_name, *arguments)
super rescue LoadError
super
end
end end
end end

View file

@ -3,6 +3,8 @@ require "inspec/log"
require "inspec/plugin/v2" require "inspec/plugin/v2"
module Inspec::DSL module Inspec::DSL
attr_accessor :backend
def require_controls(id, &block) def require_controls(id, &block)
opts = { profile_id: id, include_all: false, backend: @backend, conf: @conf, dependencies: @dependencies } opts = { profile_id: id, include_all: false, backend: @backend, conf: @conf, dependencies: @dependencies }
::Inspec::DSL.load_spec_files_for_profile(self, opts, &block) ::Inspec::DSL.load_spec_files_for_profile(self, opts, &block)
@ -25,6 +27,23 @@ module Inspec::DSL
add_resource(target_name, res) add_resource(target_name, res)
end end
##
# Try to load and instantiate a missing resource or raise LoadError
# if unable. Requiring the resource registers it and generates a
# method for it so you should only hit this once per missing
# resource.
def self.method_missing_resource(backend, id, *arguments)
begin
require "inspec/resources/#{id}"
rescue LoadError
require "resources/aws/#{id}"
end
klass = Inspec::Resource.registry[id.to_s]
klass.new(backend, id, *arguments)
end
# Support for Outer Profile DSL plugins # Support for Outer Profile DSL plugins
# This is called when an unknown method is encountered # This is called when an unknown method is encountered
# "bare" in a control file - outside of a control or describe block. # "bare" in a control file - outside of a control or describe block.
@ -44,7 +63,11 @@ module Inspec::DSL
# We still haven't called it, so do so now. # We still haven't called it, so do so now.
send(method_name, *arguments, &block) send(method_name, *arguments, &block)
else else
super begin
Inspec::DSL.method_missing_resource(backend, method_name, *arguments)
rescue LoadError
super
end
end end
end end

View file

@ -1,3 +1,5 @@
require "inspec/errors"
# Impact scores based off CVSS 3.0 # Impact scores based off CVSS 3.0
module Inspec::Impact module Inspec::Impact
IMPACT_SCORES = { IMPACT_SCORES = {

View file

@ -31,8 +31,10 @@ module Inspec
def supports(criteria = nil) def supports(criteria = nil)
return if criteria.nil? return if criteria.nil?
Inspec::Resource.supports[@name] ||= [] key = @name.to_sym
Inspec::Resource.supports[@name].push(criteria)
Inspec::Resource.supports[key] ||= []
Inspec::Resource.supports[key].push(criteria)
end end
def example(example = nil) def example(example = nil)
@ -80,7 +82,7 @@ module Inspec
def initialize(backend, name, *args) def initialize(backend, name, *args)
@resource_skipped = false @resource_skipped = false
@resource_failed = false @resource_failed = false
@supports = Inspec::Resource.supports[name] @supports = Inspec::Resource.supports[name.to_sym]
@resource_exception_message = nil @resource_exception_message = nil
# attach the backend to this instance # attach the backend to this instance
@ -124,6 +126,7 @@ module Inspec
end end
def check_supports def check_supports
require "inspec/resources/platform"
status = inspec.platform.supported?(@supports) status = inspec.platform.supported?(@supports)
fail_msg = "Resource `#{@__resource_name__}` is not supported on platform #{inspec.platform.name}/#{inspec.platform.release}." fail_msg = "Resource `#{@__resource_name__}` is not supported on platform #{inspec.platform.name}/#{inspec.platform.release}."
fail_resource(fail_msg) unless status fail_resource(fail_msg) unless status
@ -165,7 +168,7 @@ module Inspec
end end
module Plugins module Plugins
class Resource class Resource # TODO: possibly push up to inspec/resource.rb
extend Inspec::ResourceDSL extend Inspec::ResourceDSL
include Inspec::ResourceBehaviors include Inspec::ResourceBehaviors
end end

View file

@ -74,6 +74,7 @@ module Inspec
# @return [Resource] base class for creating a new resource # @return [Resource] base class for creating a new resource
def self.resource(version) def self.resource(version)
validate_resource_dsl_version!(version) validate_resource_dsl_version!(version)
require "inspec/plugin/v1/plugin_types/resource"
Inspec::Plugins::Resource Inspec::Plugins::Resource
end end

View file

@ -1,11 +1,18 @@
##
# Now that resources are lazily loaded, this file is ONLY here for one
# reason at this point, to load all the resources in order to populate
# the registry for `inspec shell`'s `help commands`. There has to be a
# cheaper way to do this, but this will do for now.
#
# NOTE: I intentionally didn't convert this to a loop over a simple
# glob so this remains a sort of manifest for our resources.
require "inspec/resource" require "inspec/resource"
# Detect if we are running the stripped-down inspec-core # Detect if we are running the stripped-down inspec-core
# This relies on AWS being stripped from the inspec-core gem # This relies on AWS being stripped from the inspec-core gem
inspec_core_only = ENV["NO_AWS"] || !File.exist?(File.join(File.dirname(__FILE__), "..", "resource_support", "aws.rb")) inspec_core_only = ENV["NO_AWS"] || !File.exist?(File.join(File.dirname(__FILE__), "..", "resource_support", "aws.rb"))
require "rspec/matchers"
# Do not attempt to load cloud resources if we are in inspec-core mode # Do not attempt to load cloud resources if we are in inspec-core mode
unless inspec_core_only unless inspec_core_only
require "resource_support/aws" require "resource_support/aws"

View file

@ -1,3 +1,5 @@
require "inspec/resource"
module Inspec::Resources module Inspec::Resources
class PlatformResource < Inspec.resource(1) class PlatformResource < Inspec.resource(1)
name "platform" name "platform"

View file

@ -0,0 +1 @@
require "inspec/resources/users"

View file

@ -9,7 +9,6 @@ require "inspec/metadata"
require "inspec/config" require "inspec/config"
require "inspec/dependencies/cache" require "inspec/dependencies/cache"
require "inspec/dist" require "inspec/dist"
require "inspec/resources"
require "inspec/reporters" require "inspec/reporters"
require "inspec/runner_rspec" require "inspec/runner_rspec"
# spec requirements # spec requirements

View file

@ -112,6 +112,7 @@ module Inspec
#{print_target_info} #{print_target_info}
EOF EOF
elsif topic == "resources" elsif topic == "resources"
require "inspec/resources"
resources.sort.each do |resource| resources.sort.each do |resource|
puts " - #{resource}" puts " - #{resource}"
end end
@ -134,7 +135,13 @@ module Inspec
info += "https://www.inspec.io/docs/reference/resources/#{topic}\n\n" info += "https://www.inspec.io/docs/reference/resources/#{topic}\n\n"
puts info puts info
else else
puts "The resource #{topic} does not exist. For a list of valid resources, type: help resources" begin
require "inspec/resources/#{topic}"
help topic
rescue LoadError
# TODO: stderr!
puts "The resource #{topic} does not exist. For a list of valid resources, type: help resources"
end
end end
end end

View file

@ -1,5 +1,7 @@
# copyright: 2015, Vulcano Security GmbH # copyright: 2015, Vulcano Security GmbH
require "rspec/matchers"
RSpec::Matchers.define :be_readable do RSpec::Matchers.define :be_readable do
match do |file| match do |file|
file.readable?(@by, @by_user) file.readable?(@by, @by_user)

View file

@ -1,5 +1,3 @@
require "inspec/resources"
class MockLoader class MockLoader
# collects emulation operating systems # collects emulation operating systems
OPERATING_SYSTEMS = { OPERATING_SYSTEMS = {

View file

@ -57,10 +57,15 @@ describe Inspec::Plugins::Resource do
describe "supported platform" do describe "supported platform" do
def supports_meta(supports) def supports_meta(supports)
Inspec::Resource.supports["os"] = supports @old = Inspec::Resource.supports[:os]
Inspec::Resource.supports[:os] = supports
load_resource("os") load_resource("os")
end end
after do
Inspec::Resource.supports[:os] = @old
end
it "loads a profile which supports multiple families" do it "loads a profile which supports multiple families" do
m = supports_meta([ m = supports_meta([
{ os_family: "windows" }, { os_family: "windows" },

View file

@ -1,6 +1,7 @@
require "helper" require "helper"
require "inspec/resource" require "inspec/resource"
require "inspec/resources/command" require "inspec/resources/command"
require "inspec/resources/os"
describe Inspec::Resources::Cmd do describe Inspec::Resources::Cmd do
let(:x) { rand.to_s } let(:x) { rand.to_s }

View file

@ -125,7 +125,7 @@ describe "Inspec::Resources::Package" do
end end
# darwin (brew) # darwin (brew)
it "can parse ouptut from `brew` when package is installed" do it "can parse ouptut from 'brew' when package is installed" do
resource = MockLoader.new(:osx104).load_resource("package", "curl") resource = MockLoader.new(:osx104).load_resource("package", "curl")
pkg = { name: "curl", installed: true, version: "7.52.1", type: "brew" } pkg = { name: "curl", installed: true, version: "7.52.1", type: "brew" }
_(resource.installed?).must_equal true _(resource.installed?).must_equal true
@ -133,7 +133,7 @@ describe "Inspec::Resources::Package" do
_(resource.info).must_equal pkg _(resource.info).must_equal pkg
end end
it "can parse ouptut from `brew` when package is not installed but exists" do it "can parse ouptut from 'brew' when package is not installed but exists" do
resource = MockLoader.new(:osx104).load_resource("package", "nginx") resource = MockLoader.new(:osx104).load_resource("package", "nginx")
pkg = {} pkg = {}
_(resource.installed?).must_equal false _(resource.installed?).must_equal false
@ -141,7 +141,7 @@ describe "Inspec::Resources::Package" do
_(resource.info).must_equal pkg _(resource.info).must_equal pkg
end end
it "returns {} when `brew` exits non-zero" do it "returns {} when 'brew' exits non-zero" do
resource = MockLoader.new(:osx104).load_resource("package", "nope") resource = MockLoader.new(:osx104).load_resource("package", "nope")
pkg = {} pkg = {}
_(resource.installed?).must_equal false _(resource.installed?).must_equal false

View file

@ -1,5 +1,4 @@
require "helper" require "helper"
require "inspec/resource"
require "inspec/resources/ssl" require "inspec/resources/ssl"
require "sslshake" require "sslshake"