Merge pull request #648 from chef/dr/supports_inspec

specify required inspec version in inspec.yml
This commit is contained in:
Christoph Hartmann 2016-04-16 19:14:57 -04:00
commit b9b3428e63
8 changed files with 121 additions and 52 deletions

View file

@ -4,6 +4,8 @@
# author: Christoph Hartmann
require 'logger'
require 'rubygems/version'
require 'rubygems/requirement'
module Inspec
# Extract metadata.rb information
@ -40,8 +42,10 @@ module Inspec
# already.
end
def is_supported(os, entry)
name, family, release = support_fields(entry)
def is_supported?(os, entry)
name = entry[:'os-name'] || entry[:os]
family = entry[:'os-family']
release = entry[:release]
# return true if the backend matches the supported OS's
# fields act as masks, i.e. any value configured for os-name, os-family,
@ -66,34 +70,24 @@ module Inspec
name_ok && family_ok && release_ok
end
def support_fields(entry)
if entry.is_a?(Hash)
try_support = self.class.symbolize_keys(entry)
name = try_support[:'os-name'] || try_support[:os]
family = try_support[:'os-family']
release = try_support[:release]
elsif entry.is_a?(String)
@logger.warn(
"Do not use deprecated `supports: #{entry}` syntax. Instead use "\
"`supports: {os-family: #{entry}}`.")
family = entry
end
def inspec_requirement
inspec = params[:supports].find { |x| !x[:inspec].nil? } || {}
Gem::Requirement.create(inspec[:inspec])
end
[name, family, release]
def supports_runtime?
running = Gem::Version.new(Inspec::VERSION)
inspec_requirement.satisfied_by?(running)
end
def supports_transport?(backend)
# make sure the supports field is always an array
supp = params[:supports]
supp = supp.is_a?(Hash) ? [supp] : Array(supp)
# with no supports specified, always return true, as there are no
# constraints on the supported backend; it is equivalent to putting
# all fields into accept-all mode
return true if supp.empty?
return true if params[:supports].empty?
found = supp.find do |entry|
is_supported(backend.os, entry)
found = params[:supports].find do |entry|
is_supported?(backend.os, entry)
end
# finally, if we found a supported entry, we are good to go
@ -132,32 +126,51 @@ module Inspec
@missing_methods
end
def self.symbolize_keys(hash)
hash.each_with_object({}) {|(k, v), h|
def self.symbolize_keys(obj)
return obj.map { |i| symbolize_keys(i) } if obj.is_a?(Array)
return obj unless obj.is_a?(Hash)
obj.each_with_object({}) {|(k, v), h|
v = symbolize_keys(v) if v.is_a?(Hash)
v = symbolize_keys(v) if v.is_a?(Array)
h[k.to_sym] = v
}
end
def self.finalize(metadata, profile_id)
def self.finalize(metadata, profile_id, logger = nil)
return nil if metadata.nil?
param = metadata.params || {}
param['name'] = profile_id.to_s unless profile_id.to_s.empty?
param['version'] = param['version'].to_s unless param['version'].nil?
metadata.params = symbolize_keys(param)
# consolidate supports field with legacy mode
metadata.params[:supports] =
case x = metadata.params[:supports]
when Hash then [x]
when Array then x
when nil then []
else
logger ||= Logger.new(nil)
logger.warn(
"Do not use deprecated `supports: #{x}` syntax. Instead use "\
"`supports: {os-family: #{x}}`.")
[{ :'os-family' => x }]
end
metadata
end
def self.from_yaml(ref, contents, profile_id, logger = nil)
res = Metadata.new(ref, logger)
res.params = YAML.load(contents)
finalize(res, profile_id)
finalize(res, profile_id, logger)
end
def self.from_ruby(ref, contents, profile_id, logger = nil)
res = Metadata.new(ref, logger)
res.instance_eval(contents, ref, 1)
finalize(res, profile_id)
finalize(res, profile_id, logger)
end
def self.from_ref(ref, contents, profile_id, logger = nil)

View file

@ -53,6 +53,13 @@ module Inspec
end
def add_profile(profile, options = {})
# skip if not supported on this platform
if !profile.metadata.nil? && !profile.metadata.supports_runtime?
fail 'This profile requires InSpec version '\
"#{profile.metadata.inspec_requirement}. You are running "\
"InSpec v#{Inspec::VERSION}.\n"
end
@test_collector.add_profile(profile)
options[:metadata] = profile.metadata

View file

@ -69,7 +69,8 @@ module Inspec
targets.each { |target| runner.add_target(target, opts) }
exit runner.run
rescue RuntimeError => e
puts e.message
$stderr.puts e.message
exit 1
end
def diagnose

View file

@ -183,4 +183,22 @@ describe 'inspec exec' do
File.exist?('/tmp/inspec_test_DONT_CREATE').must_equal false
end
end
describe 'with a profile that is supported on this version of inspec' do
let(:out) { inspec('exec ' + File.join(profile_path, 'supported_inspec')) }
it 'exits cleanly' do
out.stderr.must_equal ''
out.exit_status.must_equal 0
end
end
describe 'with a profile that is not supported on this version of inspec' do
let(:out) { inspec('exec ' + File.join(profile_path, 'unsupported_inspec')) }
it 'does not support this profile' do
out.exit_status.must_equal 1
out.stderr.must_equal "This profile requires InSpec version >= 99.0.0. You are running InSpec v#{Inspec::VERSION}.\n"
end
end
end

View file

@ -8,6 +8,14 @@ require 'inspec/metadata'
describe 'metadata with supported operating systems' do
let(:logger) { Minitest::Mock.new }
def supports_meta(params)
res = Inspec::Metadata.from_yaml('mock', "---", nil, logger)
# manually inject supported parameters
res.params[:supports] = params
Inspec::Metadata.finalize(res, 'mock', logger)
res
end
describe 'running on ubuntu 14.04' do
let (:backend) { MockLoader.new(:ubuntu1404).backend }
@ -32,74 +40,92 @@ describe 'metadata with supported operating systems' do
it 'loads the support field from metadata' do
res = Inspec::Metadata.from_yaml('mock',
"---\nsupports:\n - os: ubuntu", nil)
res.params[:supports].must_equal([{ 'os' => 'ubuntu' }])
end
# Description of method
#
# @param [Type] params describe params
# @return [Type] description of returned object
def create_meta(params)
res = Inspec::Metadata.from_yaml('mock', "---", nil, logger)
# manually inject supported parameters
res.params[:supports] = params
Inspec::Metadata.finalize(res, 'mock')
res
res.params[:supports].must_equal([{ os: 'ubuntu' }])
end
it 'load a profile with empty supports clause' do
m = create_meta(nil)
m = supports_meta(nil)
m.supports_transport?(backend).must_equal true
end
it 'supports legacy simple support style, but warns' do
# i.e. setting this to something that would fail:
m = create_meta('linux')
logger.expect :warn, nil, [
'Do not use deprecated `supports: linux` '\
'syntax. Instead use `supports: {os-family: linux}`.']
m = supports_meta('linux')
m.supports_transport?(backend).must_equal true
logger.verify
end
it 'loads a profile which supports os ubuntu' do
m = create_meta({ 'os' => 'ubuntu' })
m = supports_meta({ 'os' => 'ubuntu' })
m.supports_transport?(backend).must_equal true
end
it 'loads a profile which supports os name ubuntu' do
m = create_meta({ 'os-name' => 'ubuntu' })
m = supports_meta({ 'os-name' => 'ubuntu' })
m.supports_transport?(backend).must_equal true
end
it 'loads a profile which supports os family linux' do
m = create_meta({ 'os-family' => 'linux' })
m = supports_meta({ 'os-family' => 'linux' })
m.supports_transport?(backend).must_equal true
end
it 'loads a profile which supports release 14.04' do
m = create_meta({ 'release' => '14.04' })
m = supports_meta({ 'release' => '14.04' })
m.supports_transport?(backend).must_equal true
end
it 'rejects a profile which supports release 12.04' do
m = create_meta({ 'release' => '12.04' })
m = supports_meta({ 'release' => '12.04' })
m.supports_transport?(backend).must_equal false
end
it 'loads a profile which supports ubuntu 14.04' do
m = create_meta({ 'os-name' => 'ubuntu', 'release' => '14.04' })
m = supports_meta({ 'os-name' => 'ubuntu', 'release' => '14.04' })
m.supports_transport?(backend).must_equal true
end
it 'rejects a profile which supports ubuntu 12.04' do
m = create_meta({ 'os-name' => 'ubuntu', 'release' => '12.04' })
m = supports_meta({ 'os-name' => 'ubuntu', 'release' => '12.04' })
m.supports_transport?(backend).must_equal false
end
it 'reject unsupported os' do
m = create_meta({ 'os-name' => 'windows' })
m = supports_meta({ 'os-name' => 'windows' })
m.supports_transport?(backend).must_equal false
end
end
describe 'testing the supported runtime' do
let(:current_version) { Inspec::VERSION }
let(:next_version) { current_version.sub(/\.\d+$/) { |num| num.to_i.next } }
it 'returns true on testing the current version' do
m = supports_meta({ 'inspec' => current_version })
m.supports_runtime?.must_equal true
end
it 'returns true on testing the current version' do
m = supports_meta({ 'inspec' => '= ' + current_version })
m.supports_runtime?.must_equal true
end
it 'returns true on testing >= current version' do
m = supports_meta({ 'inspec' => '>= ' + current_version })
m.supports_runtime?.must_equal true
end
it 'returns false on testing >= the next version' do
m = supports_meta({ 'inspec' => '>= ' + next_version })
m.supports_runtime?.must_equal false
end
it 'returns false on testing > the next version' do
m = supports_meta({ 'inspec' => '> ' + next_version })
m.supports_runtime?.must_equal false
end
end
end

View file

@ -0,0 +1,2 @@
supports:
- inspec: 0.18

View file

@ -0,0 +1,2 @@
supports:
- inspec: '>= 99.0.0'

View file

@ -172,7 +172,7 @@ describe Inspec::Profile do
end
it 'doesnt have constraints on supported systems' do
profile.metadata.params.wont_include(:supports)
profile.metadata.params[:supports].must_equal([])
end
end