I need to write several Chef Inspec controls that basically do the same checks but against different files and conditionally depending on certain factors (using the only_if syntax). I've written a method to do these checks and so I can call the method instead of repeatedly writing the same code over and over again in each control block. However this generates an error stating that the method was undefined.
I've omitted the "only_if" condition so that the control always runs
File_attribs = Struct.new(:path, :mode, :owner, :group, :sha256sum)
def file_checks (file)
describe "#{file.path}" do
subject { file(file.path) }
it "should exist" do
expect(subject).to(exist)
end
it "should be a file" do
expect(subject).to(be_file)
end
it "should have mode #{file.mode}" do
expect(subject.mode).to(cmp file.mode)
end
it "should be owned by #{file.owner} user" do
expect(subject.owner).to(eq file.owner)
end
it "should be owned by #{file.group} group" do
expect(subject.group).to(eq file.group)
end
if ! file.sha256sum.nil?
it "should match the known sha256 checksum" do
expect(subject.sha256sum).to(eq file.sha256sum)
end
end
end
end
control "test" do
impact 0.7
title "Test"
desc "Test"
test_files = [
File_attribs.new("/etc/os-release", "0644", "root", "root", "fe133101dac304ceb134e47dea186e9d74d2a439cd32ae5452cc4f5b3c1eba0e")
]
test_files.each do |test_file|
file_checks test_file
end
end
This results in the following error when I try to run it with inspec exec:
Profile: tests from /hab/svc/core-tools/test/integration/default/core-tools.rb (tests from .hab.svc.core-tools.test.integration.default.core-tools.rb)
Version: (not specified)
Target: local://
Target ID: d98404f5-a6f9-5bfb-b3ac-3e75561c700f
× test: Test
× Control Source Code Error /hab/svc/core-tools/test/integration/default/core-tools.rb:30
undefined method `file_checks' for #<Inspec::Rule:0x0000000006c0ee90 @impact=0.7, @title="Test", @descriptions={:default=>"Test"}, @refs=[], @tags={}, @resource_dsl=#<Module:0x0000000006c15cb8>, @__code=nil, @__block=#<Proc:0x0000000006c0ecd8 /hab/svc/core-tools/test/integration/default/core-tools.rb:30>, @__source_location={:ref=>"/hab/svc/core-tools/test/integration/default/core-tools.rb", :line=>30}, @__rule_id="test", @__profile_id="tests from .hab.svc.core-tools.test.integration.default.core-tools.rb", @__checks=[["describe", ["Control Source Code Error"], #<Proc:0x0000000006d24398 /hab/pkgs/chef/inspec/5.17.4/20220629103022/lib/gems/inspec-core-5.17.4/lib/inspec/rule.rb:407>]], @__skip_rule={}, @__merge_count=0, @__merge_changes=[], @__skip_only_if_eval=false, @__file="/hab/svc/core-tools/test/integration/default/core-tools.rb", @__group_title=nil>
Profile Summary: 0 successful controls, 1 control failure, 0 controls skipped
Test Summary: 0 successful, 1 failure, 0 skipped
I have tried numerous other approaches such as combining the struct members and file_checks function into a class, but then I get errors about "describe" being an undefined method.
Is it possible to define methods for use in Chef Inspec controls like this, or am I completely off base with what I'm trying to do? How can I accomplish what I want with the minimal amount of duplicated code?