mirror of
https://github.com/inspec/inspec
synced 2024-12-18 00:53:22 +00:00
Merge pull request #447 from chef/dr/target-helper-dirs
rework target to resolver connection
This commit is contained in:
commit
a42b19414d
6 changed files with 114 additions and 82 deletions
|
@ -8,40 +8,26 @@ require 'inspec/targets/dir'
|
|||
|
||||
module Inspec::Targets
|
||||
class ArchiveHelper < DirsResolver
|
||||
def get_files(target)
|
||||
files, rootdir = structure(target)
|
||||
attr_reader :files
|
||||
|
||||
def initialize(target, _opts = {})
|
||||
@target = target
|
||||
files, @rootdir = structure(target)
|
||||
|
||||
# remove trailing slashes
|
||||
files = files.collect { |f| f.chomp('/') }
|
||||
|
||||
# remove leading directory
|
||||
files.collect { |f|
|
||||
Pathname(f).relative_path_from(Pathname(rootdir)).to_s
|
||||
root = Pathname(@rootdir)
|
||||
@files = files.collect { |f|
|
||||
Pathname(f).relative_path_from(root).to_s
|
||||
}
|
||||
end
|
||||
|
||||
def resolve(target, _opts = {})
|
||||
files = get_files(target)
|
||||
helper = DirsHelper.get_handler(files) ||
|
||||
fail("Don't know how to handle folder #{target}")
|
||||
|
||||
_, rootdir = structure(target)
|
||||
|
||||
res = {
|
||||
test: collect(helper, files, :get_filenames),
|
||||
library: collect(helper, files, :get_libraries),
|
||||
metadata: collect(helper, files, :get_metadata),
|
||||
}.map { |as, list|
|
||||
content(target, list, rootdir, base_folder: target, as: as)
|
||||
}
|
||||
|
||||
res.flatten
|
||||
end
|
||||
|
||||
# FIXME(sr) dedup inspec/targets/folder
|
||||
def collect(helper, files, getter)
|
||||
return [] unless helper.respond_to? getter
|
||||
helper.method(getter).call(files)
|
||||
def resolve(path, opts = {})
|
||||
o = (opts || {})
|
||||
o[:base_folder] = @target
|
||||
content(@target, path, @rootdir, o)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -8,17 +8,42 @@ module Inspec
|
|||
module Targets
|
||||
extend Modulator
|
||||
|
||||
# Retrieve the resolver for a given target. Resolvers are any type of
|
||||
# instance, that can take a target in the #resolve method, and turn
|
||||
# it into raw InSpec code. It will actually retrieve all file's
|
||||
# contents, including libraries and metadata.
|
||||
#
|
||||
# @param [any] target any target you are pointing to; typically a string
|
||||
# @return [Inspec::Targets::*] the resolver handling this target
|
||||
def self.find_resolver(target)
|
||||
modules.values.find { |m| m.handles?(target) }
|
||||
end
|
||||
|
||||
# Retrieve the target handler, i.e. the component that is going to handle
|
||||
# all aspects of a given target. Unlike the resolver, this provides
|
||||
# access to an object, that understands the target and is able to read
|
||||
# all contents, while providing additional methods which InSpec itself
|
||||
# does not care about (think: plugins).
|
||||
#
|
||||
# There is a special case, where you might be pointing to a URL containing
|
||||
# a ZIP file which has a full InSpec Profile embedded. This will be
|
||||
# resolved to a directory helper (Inspec::Targets::DirsResolver). This
|
||||
# method will retrieve the right handler for this directory and provide it
|
||||
# with full access all contents (i.e. the DirsResolver will be embedded).
|
||||
#
|
||||
# @param [any] target any target you are pointing to; typically a string
|
||||
# @return [Inspec::Targets::*] the handler for this target
|
||||
def self.find_handler(target)
|
||||
resolver = find_resolver(target)
|
||||
return resolver unless resolver.is_a?DirsResolver
|
||||
files = resolver.get_files(target)
|
||||
DirsHelper.get_handler(files)
|
||||
return resolver unless resolver.ancestors.include? DirsResolver
|
||||
resolver.from_target(target).handler
|
||||
end
|
||||
|
||||
# Turn targets into actionable InSpec code, library-data and metadata.
|
||||
#
|
||||
# @param [any] targets any targets you are pointing to; typically strings
|
||||
# @param [Hash] additional options for loading
|
||||
# @return [Array] an array of resolved data, with :content, :ref, :type
|
||||
def self.resolve(targets, opts = {})
|
||||
Array(targets).map do |target|
|
||||
handler = find_resolver(target) ||
|
||||
|
|
|
@ -18,7 +18,7 @@ module Inspec::Targets
|
|||
extend Modulator
|
||||
|
||||
def self.get_handler(paths)
|
||||
modules.values.find { |x| x.handles?(paths) }
|
||||
modules.values.reverse.find { |x| x.handles?(paths) }
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -27,8 +27,46 @@ module Inspec::Targets
|
|||
#
|
||||
# These resolvers must implement the required methods of this class.
|
||||
class DirsResolver
|
||||
def get_files(_target)
|
||||
fail NotImplementedError, "Directory resolver #{self.class} must implement #get_files"
|
||||
def files
|
||||
fail NotImplementedError, "Directory resolver #{self.class} must implement #files"
|
||||
end
|
||||
|
||||
def resolve(_path)
|
||||
fail NotImplementedError, "Directory resolver #{self.class} must implement #content"
|
||||
end
|
||||
|
||||
def handler
|
||||
DirsHelper.get_handler(files) ||
|
||||
fail("Don't know how to handle #{self}")
|
||||
end
|
||||
|
||||
def self.from_target(target)
|
||||
new(target)
|
||||
end
|
||||
|
||||
def self.resolve(target, opts)
|
||||
r = new(target, opts)
|
||||
handler = r.handler
|
||||
files = r.files
|
||||
|
||||
res = []
|
||||
{
|
||||
test: __collect(handler, :get_filenames, files),
|
||||
library: __collect(handler, :get_libraries, files),
|
||||
metadata: __collect(handler, :get_metadata, files),
|
||||
}.each do |as, list|
|
||||
Array(list).each { |path|
|
||||
res.push(r.resolve(path, as: as))
|
||||
}
|
||||
end
|
||||
|
||||
return handler.resolve_contents(res) if handler.respond_to?(:resolve_contents)
|
||||
res
|
||||
end
|
||||
|
||||
def self.__collect(handler, getter, files)
|
||||
return [] unless handler.respond_to? getter
|
||||
handler.method(getter).call(files)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -97,7 +135,9 @@ module Inspec::Targets
|
|||
end
|
||||
|
||||
def get_filenames(paths)
|
||||
paths.find_all { |x| x.end_with?('.rb') and !x.include?('/') }
|
||||
# TODO: eventually remove the metadata.rb exception here
|
||||
# when we have fully phased out metadata.rb in 1.0
|
||||
paths.find_all { |x| x.end_with?('.rb') && !x.include?('/') && x != 'metadata.rb' }
|
||||
end
|
||||
end
|
||||
DirsHelper.add_module('flat-ruby-folder', FlatDir.new)
|
||||
|
|
|
@ -7,49 +7,32 @@ require 'inspec/targets/file'
|
|||
|
||||
module Inspec::Targets
|
||||
class FolderHelper < DirsResolver
|
||||
def handles?(target)
|
||||
def self.handles?(target)
|
||||
File.directory?(target)
|
||||
end
|
||||
|
||||
def get_files(target)
|
||||
attr_reader :files
|
||||
|
||||
def initialize(target, _opts = {})
|
||||
@base_folder = target
|
||||
# find all files in the folder
|
||||
files = Dir[File.join(target, '**', '*')]
|
||||
# remove the prefix
|
||||
prefix = File.join(target, '')
|
||||
files.map { |x| x.sub(prefix, '') }
|
||||
@files = files.map { |x| x.sub(prefix, '') }
|
||||
@file_handler = Inspec::Targets.modules['file']
|
||||
end
|
||||
|
||||
def resolve(target, _opts = {})
|
||||
# get the dirs helper
|
||||
files = get_files(target)
|
||||
helper = DirsHelper.get_handler(files) ||
|
||||
fail("Don't know how to handle folder #{target}")
|
||||
|
||||
# get all test file contents
|
||||
file_handler = Inspec::Targets.modules['file']
|
||||
res = {
|
||||
test: collect(helper, files, :get_filenames),
|
||||
library: collect(helper, files, :get_libraries),
|
||||
metadata: collect(helper, files, :get_metadata),
|
||||
}.map { |as, list|
|
||||
file_handler.resolve_all(list, base_folder: target, as: as)
|
||||
}
|
||||
|
||||
# flatten the outer list layer
|
||||
res.inject(&:+)
|
||||
def resolve(path, opts = {})
|
||||
o = (opts || {})
|
||||
o[:base_folder] = @base_folder
|
||||
@file_handler.resolve(path, o)
|
||||
end
|
||||
|
||||
def to_s
|
||||
'Folder Loader'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def collect(helper, files, getter)
|
||||
return [] unless helper.respond_to? getter
|
||||
helper.method(getter).call(files)
|
||||
end
|
||||
end
|
||||
|
||||
Inspec::Targets.add_module('folder', FolderHelper.new)
|
||||
Inspec::Targets.add_module('folder', FolderHelper)
|
||||
end
|
||||
|
|
|
@ -8,7 +8,7 @@ require 'inspec/targets/archive'
|
|||
|
||||
module Inspec::Targets
|
||||
class TarHelper < ArchiveHelper
|
||||
def handles?(target)
|
||||
def self.handles?(target)
|
||||
File.file?(target) && target.end_with?('.tar.gz', '.tgz')
|
||||
end
|
||||
|
||||
|
@ -30,22 +30,20 @@ module Inspec::Targets
|
|||
[files, rootdir]
|
||||
end
|
||||
|
||||
def content(input, files, rootdir = nil, opts = {})
|
||||
content = []
|
||||
def content(input, path, rootdir = nil, opts = {})
|
||||
content = nil
|
||||
Gem::Package::TarReader.new(Zlib::GzipReader.open(input)) do |tar|
|
||||
tar.each do |entry|
|
||||
if entry.directory?
|
||||
# nothing to do
|
||||
elsif entry.file?
|
||||
if files.include?(entry.full_name.gsub(rootdir, ''))
|
||||
h = {
|
||||
# NB if some file is empty, return empty-string, not nil
|
||||
content: entry.read || '',
|
||||
type: opts[:as] || :test,
|
||||
ref: entry.full_name,
|
||||
}
|
||||
content.push(h)
|
||||
end
|
||||
next unless path == entry.full_name.gsub(rootdir, '')
|
||||
content = {
|
||||
# NB if some file is empty, return empty-string, not nil
|
||||
content: entry.read || '',
|
||||
type: opts[:as] || :test,
|
||||
ref: entry.full_name,
|
||||
}
|
||||
elsif entry.header.typeflag == '2'
|
||||
# ignore symlinks for now
|
||||
end
|
||||
|
@ -59,5 +57,5 @@ module Inspec::Targets
|
|||
end
|
||||
end
|
||||
|
||||
Inspec::Targets.add_module('tar', TarHelper.new)
|
||||
Inspec::Targets.add_module('tar', TarHelper)
|
||||
end
|
||||
|
|
|
@ -8,22 +8,22 @@ require 'inspec/targets/archive'
|
|||
|
||||
module Inspec::Targets
|
||||
class ZipHelper < ArchiveHelper
|
||||
def handles?(target)
|
||||
def self.handles?(target)
|
||||
File.file?(target) and target.end_with?('.zip')
|
||||
end
|
||||
|
||||
def content(input, files, rootdir = nil, opts = {})
|
||||
content = []
|
||||
def content(input, path, rootdir = nil, opts = {})
|
||||
content = nil
|
||||
::Zip::InputStream.open(input) do |io|
|
||||
while (entry = io.get_next_entry)
|
||||
next if !files.include?(entry.name.gsub(rootdir, ''))
|
||||
h = {
|
||||
next unless path == entry.name.gsub(rootdir, '')
|
||||
content = {
|
||||
# NB if some file is empty, return empty-string, not nil
|
||||
content: io.read || '',
|
||||
type: opts[:as] || :test,
|
||||
ref: entry.name,
|
||||
}
|
||||
content.push(h)
|
||||
abort
|
||||
end
|
||||
end
|
||||
content
|
||||
|
@ -51,5 +51,5 @@ module Inspec::Targets
|
|||
end
|
||||
end
|
||||
|
||||
Inspec::Targets.add_module('zip', ZipHelper.new)
|
||||
Inspec::Targets.add_module('zip', ZipHelper)
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue