Chef Inspec Ruby DSL调用自定义方法报未定义错误的解决咨询
问题:Chef InSpec中复用文件检查逻辑报错
需要编写多个Chef InSpec控制项,针对不同文件执行相同检查,且需通过only_if按需运行。为减少重复代码,定义了file_checks方法封装检查逻辑,但在control块中调用时出现undefined method 'file_checks'错误。移除only_if后问题依旧,相关代码如下:
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
执行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
尝试将结构体与file_checks整合为类时,又出现describe未定义的错误。请问是否可以在Chef InSpec控制项中这样定义方法?如何以最少的重复代码实现需求?
解决方案
原因说明
InSpec的control块运行在Inspec::Rule实例的上下文里,顶级定义的file_checks方法不在该实例的方法查找路径中,因此会报未定义错误。而将方法放到类中时,类的上下文没有InSpec DSL(如describe、it)的调用权限,所以会出现describe未定义的问题。
可行方案
方案1:单个control内复用(最简方式)
直接在control块内部定义file_checks方法,让方法处于Rule实例的上下文范围内:
control "test" do impact 0.7 title "Test" desc "Test" 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 unless file.sha256sum.nil? it "should match the known sha256 checksum" do expect(subject.sha256sum).to(eq file.sha256sum) end end end end test_files = [ File_attribs.new("/etc/os-release", "0644", "root", "root", "fe133101dac304ceb134e47dea186e9d74d2a439cd32ae5452cc4f5b3c1eba0e") ] test_files.each do |test_file| file_checks test_file # 按需添加only_if条件示例 # only_if { inspec.os.name == 'ubuntu' } end end
方案2:跨control复用(扩展Rule DSL)
通过扩展Inspec::Rule的DSL模块,让file_checks方法能在所有control块中调用:
File_attribs = Struct.new(:path, :mode, :owner, :group, :sha256sum) # 扩展InSpec的Rule DSL,让所有control都能调用该方法 Inspec::Rule.include(Module.new do 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 unless file.sha256sum.nil? it "should match the known sha256 checksum" do expect(subject.sha256sum).to(eq file.sha256sum) end 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 # 按需添加only_if条件 # only_if { 你的条件判断逻辑 } end end
方案3:官方推荐方式(自定义资源)
创建自定义InSpec资源封装检查逻辑,扩展性更强,符合框架规范:
- 在profile的
libraries/目录下创建file_checks.rb:
class FileChecks < Inspec.resource(1) name 'file_checks' desc '自定义资源:验证文件属性' def initialize(path, mode, owner, group, sha256sum = nil) @path = path @mode = mode @owner = owner @group = group @sha256sum = sha256sum @file = inspec.file(path) end def exists? @file.exist? end def is_file? @file.file? end def correct_mode? @file.mode.cmp(@mode) end def correct_owner? @file.owner == @owner end def correct_group? @file.group == @group end def correct_sha256? return true if @sha256sum.nil? @file.sha256sum == @sha256sum end end
- 在控制文件中使用该自定义资源:
control "test" do impact 0.7 title "Test" desc "Test" test_files = [ { path: "/etc/os-release", mode: "0644", owner: "root", group: "root", sha256sum: "fe133101dac304ceb134e47dea186e9d74d2a439cd32ae5452cc4f5b3c1eba0e" } ] test_files.each do |test_file| describe file_checks(test_file[:path], test_file[:mode], test_file[:owner], test_file[:group], test_file[:sha256sum]) do it "should exist" do expect(subject).to exist end it "should be a file" do expect(subject.is_file?).to be true end it "should have correct mode" do expect(subject.correct_mode?).to be true end it "should have correct owner" do expect(subject.correct_owner?).to be true end it "should have correct group" do expect(subject.correct_group?).to be true end unless test_file[:sha256sum].nil? it "should have correct sha256sum" do expect(subject.correct_sha256?).to be true end end # 按需添加only_if条件 # only_if { 你的条件判断逻辑 } end end end
方案选择建议
- 仅单个control需要复用:选方案1,实现最简单;
- 多个control需要复用:选方案2或方案3,其中方案3是InSpec官方推荐的自定义资源方式,更利于后续维护和扩展。
内容的提问来源于stack exchange,提问作者Lauren Minnema
相关产品推荐
相关产品推荐

