Skip to content

Commit

Permalink
(SIMP-5636) Add confinement for checks in v2 format (#64)
Browse files Browse the repository at this point in the history
* Add 'compliance_markup::debug::hiera_backend_compile_time' virtual
parameter for use in debugging performance in production environments.
  * This is accessible both through puppet lookup and puppet catalog compilations

* Add 'confine' to version 2 checks, to limit checks based on
puppet facts, or special options:
  * module_name - fully qualified module name from metadata.json, ie puppetlabs-apache
  * module_version - SemanticPuppet VersionRange for checking module version, ie '>= 2.0.0'

SIMP-5636 #comment pupmod-simp-compliance_markup
SIMP-5600 #comment pupmod-simp-compliance_markup
  • Loading branch information
heliocentric authored and trevor-vaughan committed Aug 10, 2019
1 parent 268fd61 commit d322dd7
Show file tree
Hide file tree
Showing 7 changed files with 238 additions and 82 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
* Fri Aug 09 2019 Dylan Cochran <[email protected]> - 3.0.1-0
- Add confinement on modules and facts to SIMP Compliance Engine.

* Wed Aug 07 2019 Trevor Vaughan <[email protected]> - 3.0.0-0
- Converted the `compliance_map` function to a Puppet 4 function called
`compliance_markup::compliance_map()`
Expand Down
38 changes: 33 additions & 5 deletions lib/puppet/functions/compliance_markup/enforcement.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
param "String", :key
param "Hash", :options
param "Puppet::LookupContext", :context
end
dispatch :hiera_enforcement do
param "String", :key
param "Hash", :options
param "Undef", :context
end
def initialize(closure_scope, loader)
filename = File.expand_path('../../../../puppetx/simp/compliance_mapper.rb', __FILE__)
Expand All @@ -21,7 +26,7 @@ def hiera_enforcement(key, options, context)
unless (e.class.to_s == 'ArgumentError')
debug("Threw error #{e.to_s}")
end
context.not_found
not_found
end
retval
end
Expand All @@ -31,16 +36,39 @@ def codebase()
def environment()
closure_scope.environment.name.to_s
end
def not_found()
if (!@context.nil?)
@context.not_found
else
throw :no_such_key
end
end
def debug(message)
@context.explain() { "#{message}" }
if (!@context.nil?)
@context.explain() { "#{message}" }
end
end
def cache(key, value)
@context.cache(key, value)
if (!@context.nil?)
@context.cache(key, value)
end
end
def cached_value(key)
@context.cached_value(key)
if (!@context.nil?)
@context.cached_value(key)
end
end
def cache_has_key(key)
@context.cache_has_key(key)
if (!@context.nil?)
@context.cache_has_key(key)
else
false
end
end
def lookup_fact(fact)
closure_scope.lookupvar("facts")[fact]
end
def module_list
closure_scope.environment.modules.map { |obj| { "name" => obj.metadata["name"], "version" => obj.metadata["version"] } }
end
end
8 changes: 8 additions & 0 deletions lib/puppetx/simp/compliance_map.rb
Original file line number Diff line number Diff line change
Expand Up @@ -482,3 +482,11 @@ def codebase
def environment
@context.environment.name.to_s
end

def lookup_fact(fact)
@context.lookupvar("facts")[fact]
end

def module_list
@context.environment.modules.map { |obj| { "name" => obj.metadata["name"], "version" => obj.metadata["version"] } }
end
37 changes: 30 additions & 7 deletions lib/puppetx/simp/compliance_mapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def enforcement(key, options = {"mode" => "value"}, &block)
throw :no_such_key
when "compliance_markup::percent_sign"
throw :no_such_key
else
else
retval = :notfound
if cache_has_key("lock")
lock = cached_value("lock")
Expand Down Expand Up @@ -80,10 +80,11 @@ def enforcement(key, options = {"mode" => "value"}, &block)
end

cache("debug_output_#{profile}", debug_output)
cache("compliance_map_#{profile}", profile_map)

compile_end_time = Time.now

profile_map["compliance_markup::debug::hiera_backend_compile_time"] = (compile_end_time - compile_start_time)
cache("compliance_map_#{profile}", profile_map)
debug("debug: compiled compliance_map containing #{profile_map.size} keys in #{compile_end_time - compile_start_time} seconds")
end
if key == "compliance_markup::debug::dump"
Expand Down Expand Up @@ -240,7 +241,7 @@ def load(&block)
end
end

@v2 = v2_compiler.new()
@v2 = v2_compiler.new(callback)

@compliance_data.each do |filename, map|
if map.key?("version")
Expand Down Expand Up @@ -271,7 +272,7 @@ def profile

def v2_compiler()
Class.new do
def initialize()
def initialize(callback)
@control_list = {}
@configuration_element_list = {}
@check_list = {}
Expand All @@ -282,8 +283,11 @@ def initialize()
"controls" => {},
"checks" => {},
}
@callback = callback
end
def callback
@callback
end

def ce
@configuration_element_list
end
Expand Down Expand Up @@ -380,7 +384,6 @@ def list_puppet_params(profile_list)

if (specification["type"] == "puppet") || (specification["type"] == "puppet-class-parameter")
contain = false

if info.key?("checks")
if info["checks"].key?(check)
if info["checks"][check] == true
Expand All @@ -391,7 +394,27 @@ def list_puppet_params(profile_list)
end
end
end

# Check confinement, if we don't match any confinement die early.
if specification.key?("confine")
confine = specification["confine"]
if confine != {}
continue = confine.all? do |confinement_setting, confinement_value|
case confinement_setting
when "module_name"
@callback.module_list.map { |obj| obj["name"] }.include?(confinement_value)
when "module_version"
require 'semantic_puppet'
rvalue = @callback.module_list.select { |obj| obj["name"] == confine["module_name"] }
currentver = SemanticPuppet::Version.parse(rvalue.first["version"])
requiredver = SemanticPuppet::VersionRange.parse(confinement_value)
requiredver.include?(currentver)
else
rvalue = @callback.lookup_fact(confinement_setting)
rvalue == confinement_value
end
end
end
end
if continue
if specification.key?("controls")

Expand Down
2 changes: 1 addition & 1 deletion metadata.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "simp-compliance_markup",
"version": "3.0.0",
"version": "3.0.1",
"author": "SIMP Team",
"summary": "Compliance-mapping annotation for Puppet code",
"license": "Apache-2.0",
Expand Down
163 changes: 163 additions & 0 deletions spec/functions/compliance_markup/enforcement_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
#!/usr/bin/env ruby -S rspec

require 'spec_helper'
require 'semantic_puppet'
require 'puppet/pops/lookup/context'
puppetver = SemanticPuppet::Version.parse(Puppet.version)
requiredver = SemanticPuppet::Version.parse("4.10.0")

describe 'compliance_markup::hiera_backend' do
skip 'requires function and test changes to handle incomplete LookupContext in rspec' do
if (puppetver > requiredver)
context "when key doesn't exist in the compliance map" do
let(:hieradata) {"test_spec"}
it 'should throw an error' do
errored = false
ex = nil
begin
result = subject.execute("compliance_markup::test::nonexistent", {}, Puppet::LookupContext.new('rp_env', 'compliance_markup'))
rescue Exception => e
ex = e
errored = true
end
expect(errored).to eql(true)
end
it 'should throw a :no_such_key error' do
errored = false
ex = nil
begin
result = subject.execute("compliance_markup::test::nonexistent", {}, Puppet::LookupContext.new('rp_env', 'compliance_markup'))
rescue Exception => e
ex = e
errored = true
end
expect(ex.to_s).to match(/no_such_key/)
end
end
context "when key does exist in the compliance map" do
let(:hieradata) {"test_spec"}
it 'should not throw an error' do
errored = false
ex = nil
begin
result = subject.execute("compliance_markup::test::testvariable", {}, Puppet::LookupContext.new('rp_env', 'compliance_markup'))
rescue Exception => e
ex = e
errored = true
end
expect(errored).to eql(false)
end
it 'should not throw a :no_such_key error' do
errored = false
ex = nil
begin
result = subject.execute("compliance_markup::test::testvariable", {}, Puppet::LookupContext.new('rp_env', 'compliance_markup'))
rescue Exception => e
ex = e
errored = true
end
expect(ex.to_s).to_not match(/no_such_key/)
end
it 'should return "disa"' do
errored = false
ex = nil
begin
result = subject.execute("compliance_markup::test::testvariable", {}, Puppet::LookupContext.new('m'))
rescue Exception => e
ex = e
errored = true
end
expect(ex.to_s).to_not match(/no_such_key/)
end

context "when key == compliance_markup::debug::hiera_backend_compile_time" do
let(:hieradata) {"test_spec"}
it 'should return an number' do
errored = false
ex = nil
begin
result = subject.execute("compliance_markup::debug::hiera_backend_compile_time", {}, nil)
rescue Exception => e
ex = e
errored = true
end
puts " completed in #{result} seconds"
expect(result).to be_a(Float)
end
end
end
end
context "when key == compliance_markup::test::confined_by_module" do
let(:hieradata){ "test_spec" }
it 'should return "confined"' do
errored = false
ex = nil
begin
result = subject.execute("compliance_markup::test::confined_by_module", {}, nil)
rescue Exception => e
ex = e
errored = true
end
expect(result).to eql("confined")
end
end
context "when key == compliance_markup::test::unconfined" do
let(:hieradata){ "test_spec" }
it 'should return "confined"' do
errored = false
ex = nil
begin
result = subject.execute("compliance_markup::test::unconfined", {}, nil)
rescue Exception => e
ex = e
errored = true
end
expect(result).to eql("confined")
end
end
context "when key == compliance_markup::test::confined_with_matching_fact" do
let(:hieradata){ "test_spec" }
it 'should return "confined"' do
errored = false
ex = nil
begin
result = subject.execute("compliance_markup::test::confined_with_matching_fact", {}, nil)
rescue Exception => e
ex = e
errored = true
end
expect(result).to eql("confined")
end
end
context "when key == compliance_markup::test::confined_with_not_matching_fact" do
let(:hieradata){ "test_spec" }
it 'should return "confined"' do
errored = false
ex = nil
begin
result = subject.execute("compliance_markup::test::confined_with_not_matching_fact", {}, nil)
rescue Exception => e
ex = e
errored = true
end
expect(result).to_not eql("confined")
end
end
context "when key == compliance_markup::test::confined_with_wrong_module_version" do
let(:hieradata){ "test_spec" }
it 'should not return "confined"' do
errored = false
ex = nil
begin
$pry_debug = true
result = subject.execute("compliance_markup::test::confined_with_wrong_module_version", {}, nil)
rescue Exception => e
ex = e
errored = true
end
expect(result).to_not eql("confined")
end
end
end
end

Loading

0 comments on commit d322dd7

Please sign in to comment.