mirror of
https://github.com/inspec/inspec
synced 2024-12-18 09:03:12 +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
|
module Inspec::Targets
|
||||||
class ArchiveHelper < DirsResolver
|
class ArchiveHelper < DirsResolver
|
||||||
def get_files(target)
|
attr_reader :files
|
||||||
files, rootdir = structure(target)
|
|
||||||
|
def initialize(target, _opts = {})
|
||||||
|
@target = target
|
||||||
|
files, @rootdir = structure(target)
|
||||||
|
|
||||||
# remove trailing slashes
|
# remove trailing slashes
|
||||||
files = files.collect { |f| f.chomp('/') }
|
files = files.collect { |f| f.chomp('/') }
|
||||||
|
|
||||||
# remove leading directory
|
# remove leading directory
|
||||||
files.collect { |f|
|
root = Pathname(@rootdir)
|
||||||
Pathname(f).relative_path_from(Pathname(rootdir)).to_s
|
@files = files.collect { |f|
|
||||||
|
Pathname(f).relative_path_from(root).to_s
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def resolve(target, _opts = {})
|
def resolve(path, opts = {})
|
||||||
files = get_files(target)
|
o = (opts || {})
|
||||||
helper = DirsHelper.get_handler(files) ||
|
o[:base_folder] = @target
|
||||||
fail("Don't know how to handle folder #{target}")
|
content(@target, path, @rootdir, o)
|
||||||
|
|
||||||
_, 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)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,17 +8,42 @@ module Inspec
|
||||||
module Targets
|
module Targets
|
||||||
extend Modulator
|
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)
|
def self.find_resolver(target)
|
||||||
modules.values.find { |m| m.handles?(target) }
|
modules.values.find { |m| m.handles?(target) }
|
||||||
end
|
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)
|
def self.find_handler(target)
|
||||||
resolver = find_resolver(target)
|
resolver = find_resolver(target)
|
||||||
return resolver unless resolver.is_a?DirsResolver
|
return resolver unless resolver.ancestors.include? DirsResolver
|
||||||
files = resolver.get_files(target)
|
resolver.from_target(target).handler
|
||||||
DirsHelper.get_handler(files)
|
|
||||||
end
|
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 = {})
|
def self.resolve(targets, opts = {})
|
||||||
Array(targets).map do |target|
|
Array(targets).map do |target|
|
||||||
handler = find_resolver(target) ||
|
handler = find_resolver(target) ||
|
||||||
|
|
|
@ -18,7 +18,7 @@ module Inspec::Targets
|
||||||
extend Modulator
|
extend Modulator
|
||||||
|
|
||||||
def self.get_handler(paths)
|
def self.get_handler(paths)
|
||||||
modules.values.find { |x| x.handles?(paths) }
|
modules.values.reverse.find { |x| x.handles?(paths) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -27,8 +27,46 @@ module Inspec::Targets
|
||||||
#
|
#
|
||||||
# These resolvers must implement the required methods of this class.
|
# These resolvers must implement the required methods of this class.
|
||||||
class DirsResolver
|
class DirsResolver
|
||||||
def get_files(_target)
|
def files
|
||||||
fail NotImplementedError, "Directory resolver #{self.class} must implement #get_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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -97,7 +135,9 @@ module Inspec::Targets
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_filenames(paths)
|
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
|
||||||
end
|
end
|
||||||
DirsHelper.add_module('flat-ruby-folder', FlatDir.new)
|
DirsHelper.add_module('flat-ruby-folder', FlatDir.new)
|
||||||
|
|
|
@ -7,49 +7,32 @@ require 'inspec/targets/file'
|
||||||
|
|
||||||
module Inspec::Targets
|
module Inspec::Targets
|
||||||
class FolderHelper < DirsResolver
|
class FolderHelper < DirsResolver
|
||||||
def handles?(target)
|
def self.handles?(target)
|
||||||
File.directory?(target)
|
File.directory?(target)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_files(target)
|
attr_reader :files
|
||||||
|
|
||||||
|
def initialize(target, _opts = {})
|
||||||
|
@base_folder = target
|
||||||
# find all files in the folder
|
# find all files in the folder
|
||||||
files = Dir[File.join(target, '**', '*')]
|
files = Dir[File.join(target, '**', '*')]
|
||||||
# remove the prefix
|
# remove the prefix
|
||||||
prefix = File.join(target, '')
|
prefix = File.join(target, '')
|
||||||
files.map { |x| x.sub(prefix, '') }
|
@files = files.map { |x| x.sub(prefix, '') }
|
||||||
|
@file_handler = Inspec::Targets.modules['file']
|
||||||
end
|
end
|
||||||
|
|
||||||
def resolve(target, _opts = {})
|
def resolve(path, opts = {})
|
||||||
# get the dirs helper
|
o = (opts || {})
|
||||||
files = get_files(target)
|
o[:base_folder] = @base_folder
|
||||||
helper = DirsHelper.get_handler(files) ||
|
@file_handler.resolve(path, o)
|
||||||
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(&:+)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_s
|
def to_s
|
||||||
'Folder Loader'
|
'Folder Loader'
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def collect(helper, files, getter)
|
|
||||||
return [] unless helper.respond_to? getter
|
|
||||||
helper.method(getter).call(files)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
Inspec::Targets.add_module('folder', FolderHelper.new)
|
Inspec::Targets.add_module('folder', FolderHelper)
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,7 +8,7 @@ require 'inspec/targets/archive'
|
||||||
|
|
||||||
module Inspec::Targets
|
module Inspec::Targets
|
||||||
class TarHelper < ArchiveHelper
|
class TarHelper < ArchiveHelper
|
||||||
def handles?(target)
|
def self.handles?(target)
|
||||||
File.file?(target) && target.end_with?('.tar.gz', '.tgz')
|
File.file?(target) && target.end_with?('.tar.gz', '.tgz')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -30,22 +30,20 @@ module Inspec::Targets
|
||||||
[files, rootdir]
|
[files, rootdir]
|
||||||
end
|
end
|
||||||
|
|
||||||
def content(input, files, rootdir = nil, opts = {})
|
def content(input, path, rootdir = nil, opts = {})
|
||||||
content = []
|
content = nil
|
||||||
Gem::Package::TarReader.new(Zlib::GzipReader.open(input)) do |tar|
|
Gem::Package::TarReader.new(Zlib::GzipReader.open(input)) do |tar|
|
||||||
tar.each do |entry|
|
tar.each do |entry|
|
||||||
if entry.directory?
|
if entry.directory?
|
||||||
# nothing to do
|
# nothing to do
|
||||||
elsif entry.file?
|
elsif entry.file?
|
||||||
if files.include?(entry.full_name.gsub(rootdir, ''))
|
next unless path == entry.full_name.gsub(rootdir, '')
|
||||||
h = {
|
content = {
|
||||||
# NB if some file is empty, return empty-string, not nil
|
# NB if some file is empty, return empty-string, not nil
|
||||||
content: entry.read || '',
|
content: entry.read || '',
|
||||||
type: opts[:as] || :test,
|
type: opts[:as] || :test,
|
||||||
ref: entry.full_name,
|
ref: entry.full_name,
|
||||||
}
|
}
|
||||||
content.push(h)
|
|
||||||
end
|
|
||||||
elsif entry.header.typeflag == '2'
|
elsif entry.header.typeflag == '2'
|
||||||
# ignore symlinks for now
|
# ignore symlinks for now
|
||||||
end
|
end
|
||||||
|
@ -59,5 +57,5 @@ module Inspec::Targets
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Inspec::Targets.add_module('tar', TarHelper.new)
|
Inspec::Targets.add_module('tar', TarHelper)
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,22 +8,22 @@ require 'inspec/targets/archive'
|
||||||
|
|
||||||
module Inspec::Targets
|
module Inspec::Targets
|
||||||
class ZipHelper < ArchiveHelper
|
class ZipHelper < ArchiveHelper
|
||||||
def handles?(target)
|
def self.handles?(target)
|
||||||
File.file?(target) and target.end_with?('.zip')
|
File.file?(target) and target.end_with?('.zip')
|
||||||
end
|
end
|
||||||
|
|
||||||
def content(input, files, rootdir = nil, opts = {})
|
def content(input, path, rootdir = nil, opts = {})
|
||||||
content = []
|
content = nil
|
||||||
::Zip::InputStream.open(input) do |io|
|
::Zip::InputStream.open(input) do |io|
|
||||||
while (entry = io.get_next_entry)
|
while (entry = io.get_next_entry)
|
||||||
next if !files.include?(entry.name.gsub(rootdir, ''))
|
next unless path == entry.name.gsub(rootdir, '')
|
||||||
h = {
|
content = {
|
||||||
# NB if some file is empty, return empty-string, not nil
|
# NB if some file is empty, return empty-string, not nil
|
||||||
content: io.read || '',
|
content: io.read || '',
|
||||||
type: opts[:as] || :test,
|
type: opts[:as] || :test,
|
||||||
ref: entry.name,
|
ref: entry.name,
|
||||||
}
|
}
|
||||||
content.push(h)
|
abort
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
content
|
content
|
||||||
|
@ -51,5 +51,5 @@ module Inspec::Targets
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Inspec::Targets.add_module('zip', ZipHelper.new)
|
Inspec::Targets.add_module('zip', ZipHelper)
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue