feature: attribute handling

This commit is contained in:
Christoph Hartmann 2016-05-07 13:04:22 +02:00
parent 753839f21f
commit c7a49056c4
11 changed files with 123 additions and 6 deletions

View file

@ -0,0 +1,2 @@
user: bob
password: secret

View file

@ -0,0 +1,3 @@
# Example InSpec Profile with Attributes
This profile uses InSpec attributes to parameterize a profile.

View file

@ -0,0 +1,20 @@
# encoding: utf-8
val_user = attribute('user', default: 'alice', required: true)
val_password = attribute('password', required: true)
# that works
describe 'bob' do
it { should eq val_user.value }
end
describe 'secret' do
it { should eq val_password.value }
end
describe val_user.value do
it { should eq 'bob' }
end
describe val_password.value do
it { should eq 'secret' }
end

View file

@ -0,0 +1,8 @@
name: profile-attribute
title: InSpec Profile
maintainer: The Authors
copyright: The Authors
copyright_email: you@example.com
license: All Rights Reserved
summary: An InSpec Compliance Profile
version: 0.1.0

View file

@ -0,0 +1,10 @@
name: inheritance
title: InSpec example inheritance
maintainer: Chef Software, Inc.
copyright: Chef Software, Inc.
copyright_email: support@chef.io
license: Apache 2 license
summary: Demonstrates the use of InSpec profile inheritance
version: 1.0.0
supports:
- os-family: unix

View file

@ -9,6 +9,7 @@ require 'json'
require 'pp'
require 'utils/base_cli'
require 'utils/json_log'
require 'yaml'
class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
class_option :diagnose, type: :boolean,
@ -106,9 +107,18 @@ class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
desc 'exec PATHS', 'run all test files at the specified PATH.'
exec_options
option :attr_file, type: :array
def exec(*targets)
diagnose
run_tests(targets, opts)
o = opts.dup
# parse attribute file
unless o['attr_file'].nil?
o['attrs'] = {}
o['attr_file'].each do |file|
o['attrs'] = o['attrs'].merge(YAML.load_file(file))
end
end
run_tests(targets, o)
end
desc 'detect', 'detect the target OS'

View file

@ -1,6 +1,7 @@
# encoding: utf-8
module Inspec
autoload :Attribute, 'inspec/objects/attribute'
autoload :Control, 'inspec/objects/control'
autoload :EachLoop, 'inspec/objects/each_loop'
autoload :List, 'inspec/objects/list'

View file

@ -0,0 +1,35 @@
# encoding:utf-8
module Inspec
class Attribute
attr_accessor :name
def initialize(name, options)
@name = name
@opts = options
@value = nil
end
# implicit call is done by inspec to determine the value of an attribute
def value(newvalue = nil)
unless newvalue.nil?
@value = newvalue
end
@value || default
end
def default
@opts[:default]
end
def to_hash
{
name: @name,
options: @opts,
}
end
def to_s
"Attribute #{@name} with #{@value}"
end
end
end

View file

@ -57,6 +57,7 @@ module Inspec
def info
res = params.dup
# add information about the controls
controls = res[:controls].map do |id, rule|
next if id.to_s.empty?
data = rule.dup
@ -67,6 +68,9 @@ module Inspec
[id, data]
end
res[:controls] = Hash[controls.compact]
# add information about the required attributes
res[:attributes] = res[:attributes].map(&:to_hash) unless res[:attributes].nil? || res[:attributes].empty?
res
end
@ -248,13 +252,16 @@ module Inspec
f = load_rule_filepath(prefix, rule)
load_rule(rule, f, controls, groups)
end
params[:attributes] = runner.attributes
else
# load from context
@runner_context.rules.values.each do |rule|
f = load_rule_filepath(prefix, rule)
load_rule(rule, f, controls, groups)
end
params[:attributes] = @runner_context.attributes
end
params
end
def load_rule_filepath(prefix, rule)

View file

@ -6,10 +6,12 @@ require 'inspec/rule'
require 'inspec/dsl'
require 'inspec/require_loader'
require 'securerandom'
require 'inspec/objects/attribute'
module Inspec
class ProfileContext # rubocop:disable Metrics/ClassLength
attr_reader :rules
attr_reader :attributes
def initialize(profile_id, backend, conf)
if backend.nil?
fail 'ProfileContext is initiated with a backend == nil. ' \
@ -21,7 +23,7 @@ module Inspec
@conf = conf.dup
@rules = {}
@require_loader = ::Inspec::RequireLoader.new
@attributes = []
reload_dsl
end
@ -84,6 +86,19 @@ module Inspec
end
end
def register_value(&block)
@values.push(block)
end
def register_attribute(name, options = {})
# we need to return an attribute object, in order to allow lazy access
attr = Attribute.new(name, options)
# set value
attr.value(@conf['attrs'][attr.name]) unless @conf['attrs'].nil?
@attributes.push(attr)
attr
end
def set_header(field, val)
@current_load[field] = val
end
@ -180,9 +195,9 @@ module Inspec
profile_context_owner.register_rule(control, &block) unless control.nil?
end
# TODO: mock method for attributes; import attribute handling
define_method :attributes do |_name, _options|
nil
# method for attributes; import attribute handling
define_method :attribute do |name, options|
profile_context_owner.register_attribute(name, options)
end
define_method :skip_control do |id|

View file

@ -15,7 +15,7 @@ require 'inspec/metadata'
module Inspec
class Runner # rubocop:disable Metrics/ClassLength
extend Forwardable
attr_reader :backend, :rules
attr_reader :backend, :rules, :attributes
def initialize(conf = {})
@rules = {}
@conf = conf.dup
@ -26,6 +26,9 @@ module Inspec
RunnerRspec.new(@conf)
end
# list of profile attributes
@attributes = []
load_attributes(@conf)
configure_transport
end
@ -110,6 +113,9 @@ module Inspec
tests = [tests] unless tests.is_a? Array
tests.each { |t| add_test_to_context(t, ctx) }
# merge all collect all attributes
@attributes |= ctx.attributes
# process the resulting rules
filter_controls(ctx.rules, options[:controls]).each do |rule_id, rule|
register_rule(rule_id, rule)