Skip to content

Commit

Permalink
Merge pull request #8 from simp/cache_bugfix
Browse files Browse the repository at this point in the history
Fix a bug in component cache invalidation
  • Loading branch information
silug authored Apr 22, 2024
2 parents 76fcbf2 + 1276500 commit 2fd7575
Show file tree
Hide file tree
Showing 3 changed files with 236 additions and 15 deletions.
27 changes: 12 additions & 15 deletions lib/compliance_engine/component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,7 @@ def to_a
#
# @return [Hash] merged data
def to_h
return @to_h unless @to_h.nil?

@to_h ||= {}

fragments.each_value do |v|
@to_h = @to_h.deep_merge!(v)
end

@to_h
element
end

# Returns the key of the component
Expand Down Expand Up @@ -106,6 +98,14 @@ def ces
element['ces']
end

# Return a single key from the component
#
# @param key [String] the key of the value to return
# @return [Object] the value of the key
def [](key)
element[key]
end

private

# Get the context variables
Expand Down Expand Up @@ -240,15 +240,12 @@ def fragments
def element
return @element unless @element.nil?

@element = {}

fragments.each_value do |fragment|
@element = if @element.nil?
fragment
else
@element.deep_merge!(fragment)
end
@element = @element.deep_merge!(fragment)
end

@element = {} if @element.nil?
@element
end
end
224 changes: 224 additions & 0 deletions spec/classes/compliance_engine/data_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,230 @@ def test_data
end
end

context 'with complex confines' do
def test_data
{
'test_module_00' => {
'a/file.yaml' => <<~A_YAML,
---
version: 2.0.0
profiles:
custom_profile_1:
ces:
enable_widget_spinner_audit_logging.el7: true
confine:
os.release.major:
- '7'
os.name:
- CentOS
- OracleLinux
- RedHat
ce:
enable_widget_spinner_audit_logging.el7:
controls:
nist_800_53:rev4:AU-2: true
title: 'Ensure logging is enabled for Widget Spinner'
description: 'This setting enables usage and security logging for the Widget Spinner application.'
confine:
os.release.major:
- '7'
os.name:
- CentOS
- OracleLinux
- RedHat
checks:
widget_spinner_audit_logging_no:
type: 'puppet-class-parameter'
settings:
parameter: 'widget_spinner::audit_logging'
value: ['no']
ces:
- enable_widget_spinner_audit_logging.el7
A_YAML
'b/file.yaml' => <<~B_YAML,
---
version: 2.0.0
profiles:
custom_profile_1:
ces:
enable_widget_spinner_audit_logging.el8: true
confine:
os.release.major:
- '8'
os.name:
- CentOS
- OracleLinux
- RedHat
ce:
enable_widget_spinner_audit_logging.el8:
controls:
nist_800_53:rev4:AU-2: true
title: 'Ensure logging is enabled for Widget Spinner'
description: 'This setting enables usage and security logging for the Widget Spinner application.'
confine:
os.release.major:
- '8'
os.name:
- CentOS
- OracleLinux
- RedHat
checks:
widget_spinner_audit_logging_yes:
type: 'puppet-class-parameter'
settings:
parameter: 'widget_spinner::audit_logging'
value: ['yes']
ces:
- enable_widget_spinner_audit_logging.el8
B_YAML
'c/file.yaml' => <<~C_YAML,
---
version: 2.0.0
profiles:
custom_profile_1:
ces:
enable_widget_spinner_audit_logging.el9: true
confine:
os.release.major:
- '9'
os.name:
- CentOS
- OracleLinux
- RedHat
ce:
enable_widget_spinner_audit_logging.el9:
controls:
nist_800_53:rev4:AU-2: true
title: 'Ensure logging is enabled for Widget Spinner'
description: 'This setting enables usage and security logging for the Widget Spinner application.'
confine:
os.release.major:
- '9'
os.name:
- CentOS
- OracleLinux
- RedHat
checks:
widget_spinner_audit_logging_maybe:
type: 'puppet-class-parameter'
settings:
parameter: 'widget_spinner::audit_logging'
value: ['maybe']
ces:
- enable_widget_spinner_audit_logging.el9
C_YAML
},
}
end

subject(:compliance_engine) { described_class.new(*test_data.keys) }

before(:each) do
test_data.each do |module_path, file_data|
allow(File).to receive(:directory?).with(module_path).and_return(true)
allow(Dir).to receive(:exist?).with("#{module_path}/SIMP/compliance_profiles").and_return(true)
allow(Dir).to receive(:exist?).with("#{module_path}/simp/compliance_profiles").and_return(false)
allow(Dir).to receive(:glob).with(
[
"#{module_path}/SIMP/compliance_profiles/**/*.yaml",
"#{module_path}/SIMP/compliance_profiles/**/*.json",
],
).and_return(
file_data.map { |name, _contents| "#{module_path}/SIMP/compliance_profiles/#{name}" },
)

file_data.each do |name, contents|
filename = "#{module_path}/SIMP/compliance_profiles/#{name}"
allow(File).to receive(:size).with(filename).and_return(contents.length)
allow(File).to receive(:mtime).with(filename).and_return(Time.now)
allow(File).to receive(:read).with(filename).and_return(contents)
end
end
end

it 'initializes' do
expect(compliance_engine).not_to be_nil
expect(compliance_engine).to be_instance_of(described_class)
end

it 'returns a list of files' do
expect(compliance_engine.files).to eq(test_data.map { |module_path, files| files.map { |name, _| "#{module_path}/SIMP/compliance_profiles/#{name}" } }.flatten)
end

it 'returns a list of profiles' do
profiles = compliance_engine.profiles
expect(profiles).to be_instance_of(ComplianceEngine::Profiles)
expect(profiles.keys).to eq(['custom_profile_1'])
end

it 'returns a list of ces' do
ces = compliance_engine.ces
expect(ces).to be_instance_of(ComplianceEngine::Ces)
expect(ces.keys).to eq(['enable_widget_spinner_audit_logging.el7', 'enable_widget_spinner_audit_logging.el8', 'enable_widget_spinner_audit_logging.el9'])
end

it 'returns a hash of confines' do
confines = compliance_engine.confines
expect(confines).to be_instance_of(Hash)
expect(confines.keys).to eq(['os.release.major', 'os.name'])
end

it 'returns no hiera data when there are no profiles' do
hiera = compliance_engine.hiera
expect(hiera).to be_instance_of(Hash)
expect(hiera).to eq({})
end

it 'returns no hiera data when there are no valid profiles' do
hiera = compliance_engine.hiera(['invalid_profile'])
expect(hiera).to be_instance_of(Hash)
expect(hiera).to eq({})
end

it 'returns confined hiera data' do
compliance_engine.facts = { 'os' => { 'release' => { 'major' => '7' }, 'name' => 'CentOS' } }
hiera = compliance_engine.hiera(['custom_profile_1'])
expect(hiera).to be_instance_of(Hash)
expect(hiera).to eq({ 'widget_spinner::audit_logging' => ['no'] })
end

it 'skips hiera data when there is no match' do
compliance_engine.facts = { 'os' => { 'release' => { 'major' => '12' }, 'name' => 'Debian' } }
hiera = compliance_engine.hiera(['custom_profile_1'])
expect(hiera).to be_instance_of(Hash)
expect(hiera).to eq({})
end

it 'returns unconfined hiera data' do
compliance_engine.facts = nil
hiera = compliance_engine.hiera(['custom_profile_1'])
expect(hiera).to be_instance_of(Hash)
expect(hiera).to eq({ 'widget_spinner::audit_logging' => ['no', 'yes', 'maybe'] })
end

it 'correctly invalidates cached data' do
compliance_engine.facts = { 'os' => { 'release' => { 'major' => '7' }, 'name' => 'CentOS' } }
hiera = compliance_engine.hiera(['custom_profile_1'])
expect(hiera).to be_instance_of(Hash)
expect(hiera).to eq({ 'widget_spinner::audit_logging' => ['no'] })

compliance_engine.facts = { 'os' => { 'release' => { 'major' => '8' }, 'name' => 'RedHat' } }
hiera = compliance_engine.hiera(['custom_profile_1'])
expect(hiera).to be_instance_of(Hash)
expect(hiera).to eq({ 'widget_spinner::audit_logging' => ['yes'] })

compliance_engine.facts = nil
hiera = compliance_engine.hiera(['custom_profile_1'])
expect(hiera).to be_instance_of(Hash)
expect(hiera).to eq({ 'widget_spinner::audit_logging' => ['no', 'yes', 'maybe'] })

compliance_engine.facts = { 'os' => { 'release' => { 'major' => '9' }, 'name' => 'RedHat' } }
hiera = compliance_engine.hiera(['custom_profile_1'])
expect(hiera).to be_instance_of(Hash)
expect(hiera).to eq({ 'widget_spinner::audit_logging' => ['maybe'] })
end
end

context 'with mapping based on ce' do
def test_data
{
Expand Down
File renamed without changes.

0 comments on commit 2fd7575

Please sign in to comment.