diff options
author | Lamont Granquist <lamont@scriptkiddie.org> | 2017-03-17 09:05:46 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-03-17 09:05:46 -0700 |
commit | e1562c1153897368ed1c2efffd39ef85cc628f94 (patch) | |
tree | 30f91668b7f277b4289dfb26f0ef274021b10fc6 | |
parent | fdcd4a67a55b27eea7c798539c918c57df5cf1f6 (diff) | |
parent | e8485d595d2261cde8d82156ba73afc81e1b9360 (diff) | |
download | chef-e1562c1153897368ed1c2efffd39ef85cc628f94.tar.gz |
Merge pull request #5912 from chef/lcg/raise-on-method-redefinition
Chef-13: raise on properties redefining inherited methods
28 files changed, 77 insertions, 66 deletions
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 28468a1667..6ebd586b7b 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -92,3 +92,11 @@ now correctly use the `node.dup` or `node.to_hash` methods, or you should mutate ### The Chef::REST API has been removed It has been fully replaced with `Chef::ServerAPI` in chef-client code. + +### Properties overriding methods now raise an error + +Defining a property that overrides methods defined on the base ruby `Object` or on `Chef::Resource` itself can cause large amounts of +confusion. A simple example is `property :hash` which overrides the Object#hash method which will confuse ruby when the Custom Resource +is placed into the Chef::ResourceCollection which uses a Hash internally which expects to call Object#hash to get a unique id for the +object. Attempting to create `property :action` would also override the Chef::Resource#action method which is unlikely to end well for +the user. Overriding inherited properties is still supported. diff --git a/lib/chef/property.rb b/lib/chef/property.rb index 85e184497b..9f8fa599e5 100644 --- a/lib/chef/property.rb +++ b/lib/chef/property.rb @@ -493,12 +493,15 @@ class Chef # be using the existing getter/setter to manipulate it instead. return if !instance_variable_name - # We deprecate any attempt to create a property that already exists as a - # method in some Classes that we know would cause our users problems. - # For example, creating a `hash` property could cause issues when adding - # a Chef::Resource instance to an data structure that expects to be able - # to call the `#hash` method and get back an appropriate Fixnum. - emit_property_redefinition_deprecations + # Properties may override existing properties up the inheritance heirarchy, but + # properties must not override inherited methods like Object#hash. When the Resource is + # placed into the resource collection the ruby Hash object will call the + # Object#hash method on the resource, and overriding that with a property will cause + # very confusing results. + if property_redefines_method? + resource_name = declared_in.respond_to?(:resource_name) ? declared_in.resource_name : declared_in + raise ArgumentError, "Property `#{name}` of resource `#{resource_name}` overwrites an existing method." + end # We prefer this form because the property name won't show up in the # stack trace if you use `define_method`. @@ -614,28 +617,23 @@ class Chef private - def emit_property_redefinition_deprecations + def property_redefines_method? # We only emit deprecations if this property already exists as an instance method. # Weeding out class methods avoids unnecessary deprecations such Chef::Resource # defining a `name` property when there's an already-existing `name` method # for a Module. - return unless declared_in.instance_methods.include?(name) + return false unless declared_in.instance_methods.include?(name) # Only emit deprecations for some well-known classes. This will still # allow more advanced users to subclass their own custom resources and # override their own properties. - return unless [ Object, BasicObject, Kernel, Chef::Resource ].include?(declared_in.instance_method(name).owner) + return false unless [ Object, BasicObject, Kernel, Chef::Resource ].include?(declared_in.instance_method(name).owner) # Allow top-level Chef::Resource proprties, such as `name`, to be overridden. # As of this writing, `name` is the only Chef::Resource property created with the # `property` definition, but this will allow for future properties to be extended # as needed. - return if Chef::Resource.properties.keys.include?(name) - - # Emit the deprecation. - resource_name = declared_in.respond_to?(:resource_name) ? declared_in.resource_name : declared_in - Chef.deprecated(:property_name_collision, "Property `#{name}` of resource `#{resource_name}` overwrites an existing method. " \ - "Please use a different property name. This will raise an exception in Chef 13.") + !Chef::Resource.properties.keys.include?(name) end def exec_in_resource(resource, proc, *args) diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb index f8cea08076..b0e3a372e8 100644 --- a/lib/chef/resource.rb +++ b/lib/chef/resource.rb @@ -504,15 +504,6 @@ class Chef end # - # Since there are collisions with LWRP parameters named 'state' this - # method is not used by the resource_reporter and is most likely unused. - # It certainly cannot be relied upon and cannot be fixed. - # - # @deprecated - # - alias_method :state, :state_for_resource_reporter - - # # The value of the identity of this resource. # # - If there are no identity properties on the resource, `name` is returned. diff --git a/spec/unit/property_spec.rb b/spec/unit/property_spec.rb index 9f3ab43a2f..faeb774057 100644 --- a/spec/unit/property_spec.rb +++ b/spec/unit/property_spec.rb @@ -1099,6 +1099,20 @@ describe "Chef::Resource.property" do end + context "redefining Object methods" do + it "disallows redefining Object methods" do + expect { resource_class.class_eval { property :hash } }.to raise_error(ArgumentError) + end + + it "disallows redefining Chef::Resource methods" do + expect { resource_class.class_eval { property :action } }.to raise_error(ArgumentError) + end + + it "allows redefining properties on Chef::Resource" do + expect { resource_class.class_eval { property :sensitive } }.not_to raise_error + end + end + context "with a custom property type" do class CustomPropertyType < Chef::Property end diff --git a/spec/unit/resource/cookbook_file_spec.rb b/spec/unit/resource/cookbook_file_spec.rb index 6886ce1f31..05c37446a6 100644 --- a/spec/unit/resource/cookbook_file_spec.rb +++ b/spec/unit/resource/cookbook_file_spec.rb @@ -1,7 +1,7 @@ # # Author:: Daniel DeLeo (<dan@chef.io>) # Author:: Tyler Cloke (<tyler@chef.io>) -# Copyright:: Copyright 2010-2016, Chef Software, Inc. +# Copyright:: Copyright 2010-2017, Chef Software Inc. #p License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -64,7 +64,7 @@ describe Chef::Resource::CookbookFile do end it "describes the state" do - state = @cookbook_file.state + state = @cookbook_file.state_for_resource_reporter if Chef::Platform.windows? puts state expect(state[:rights]).to eq([{ :permissions => :read, :principals => "Everyone" }]) diff --git a/spec/unit/resource/cron_spec.rb b/spec/unit/resource/cron_spec.rb index 6e867b75e1..e2bfc321e8 100644 --- a/spec/unit/resource/cron_spec.rb +++ b/spec/unit/resource/cron_spec.rb @@ -170,7 +170,7 @@ describe Chef::Resource::Cron do end it "describes the state" do - state = @resource.state + state = @resource.state_for_resource_reporter expect(state[:minute]).to eq("1") expect(state[:hour]).to eq("2") expect(state[:day]).to eq("3") diff --git a/spec/unit/resource/deploy_spec.rb b/spec/unit/resource/deploy_spec.rb index a758638244..e008d79c29 100644 --- a/spec/unit/resource/deploy_spec.rb +++ b/spec/unit/resource/deploy_spec.rb @@ -270,7 +270,7 @@ describe Chef::Resource::Deploy do end it "describes its state" do - state = @resource.state + state = @resource.state_for_resource_reporter expect(state[:deploy_to]).to eq("/") expect(state[:revision]).to eq("1.2.3") end diff --git a/spec/unit/resource/directory_spec.rb b/spec/unit/resource/directory_spec.rb index cfb3ade135..b3a0134024 100644 --- a/spec/unit/resource/directory_spec.rb +++ b/spec/unit/resource/directory_spec.rb @@ -1,7 +1,7 @@ # # Author:: Adam Jacob (<adam@chef.io>) # Author:: Tyler Cloke (<tyler@chef.io>) -# Copyright:: Copyright 2008-2016, Chef Software Inc. +# Copyright:: Copyright 2008-2017, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -69,7 +69,7 @@ describe Chef::Resource::Directory do end it "describes its state" do - state = @resource.state + state = @resource.state_for_resource_reporter expect(state[:group]).to eq("wheel") expect(state[:mode]).to eq("0664") expect(state[:owner]).to eq("root") diff --git a/spec/unit/resource/env_spec.rb b/spec/unit/resource/env_spec.rb index cff862b69e..1c63ab519f 100644 --- a/spec/unit/resource/env_spec.rb +++ b/spec/unit/resource/env_spec.rb @@ -73,7 +73,7 @@ describe Chef::Resource::Env do end it "describes its state" do - state = @resource.state + state = @resource.state_for_resource_reporter expect(state[:value]).to eq("level7") end diff --git a/spec/unit/resource/file_spec.rb b/spec/unit/resource/file_spec.rb index 19304cb6b8..4004798dad 100644 --- a/spec/unit/resource/file_spec.rb +++ b/spec/unit/resource/file_spec.rb @@ -1,6 +1,6 @@ # # Author:: Adam Jacob (<adam@chef.io>) -# Copyright:: Copyright 2008-2016, Chef Software Inc. +# Copyright:: Copyright 2008-2017, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -101,7 +101,7 @@ describe Chef::Resource::File do context "on unix", :unix_only do it "describes its state" do - state = @resource.state + state = @resource.state_for_resource_reporter expect(state[:owner]).to eq("root") expect(state[:group]).to eq("wheel") expect(state[:mode]).to eq("0644") @@ -121,7 +121,7 @@ describe Chef::Resource::File do @resource.rights :full_control, "DOMAIN\User" end it "describes its state including windows ACL attributes" do - state = @resource.state + state = @resource.state_for_resource_reporter expect(state[:rights]).to eq([ { :permissions => :read, :principals => "Everyone" }, { :permissions => :full_control, :principals => "DOMAIN\User" } ]) end diff --git a/spec/unit/resource/group_spec.rb b/spec/unit/resource/group_spec.rb index 9d9b5c1111..8772f37a2b 100644 --- a/spec/unit/resource/group_spec.rb +++ b/spec/unit/resource/group_spec.rb @@ -1,7 +1,7 @@ # # Author:: AJ Christensen (<aj@chef.io>) # Author:: Tyler Cloke (<tyler@chef.io>); -# Copyright:: Copyright 2008-2016, Chef Software Inc. +# Copyright:: Copyright 2008-2017, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -151,7 +151,7 @@ describe Chef::Resource::Group, "append" do end it "describes its state" do - state = @resource.state + state = @resource.state_for_resource_reporter expect(state[:members]).to eql(%w{blastoise pikachu}) end diff --git a/spec/unit/resource/ifconfig_spec.rb b/spec/unit/resource/ifconfig_spec.rb index eceba0c319..699ebf1233 100644 --- a/spec/unit/resource/ifconfig_spec.rb +++ b/spec/unit/resource/ifconfig_spec.rb @@ -37,7 +37,7 @@ describe Chef::Resource::Ifconfig do end it "describes its state" do - state = @resource.state + state = @resource.state_for_resource_reporter expect(state[:inet_addr]).to eq("434.2343.23") expect(state[:mask]).to eq("255.255.545") end diff --git a/spec/unit/resource/link_spec.rb b/spec/unit/resource/link_spec.rb index bd0976d8ea..adfd0020f5 100644 --- a/spec/unit/resource/link_spec.rb +++ b/spec/unit/resource/link_spec.rb @@ -1,7 +1,7 @@ # # Author:: Adam Jacob (<adam@chef.io>) # Author:: Tyler Cloke (<tyler@chef.io>) -# Copyright:: Copyright 2008-2016, Chef Software Inc. +# Copyright:: Copyright 2008-2017, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -121,7 +121,7 @@ describe Chef::Resource::Link do end it "describes its state" do - state = @resource.state + state = @resource.state_for_resource_reporter expect(state[:to]).to eq("/to/dir/file.tar") expect(state[:owner]).to eq("root") expect(state[:group]).to eq("0664") diff --git a/spec/unit/resource/mdadm_spec.rb b/spec/unit/resource/mdadm_spec.rb index fe9acf807b..f3cadbe499 100644 --- a/spec/unit/resource/mdadm_spec.rb +++ b/spec/unit/resource/mdadm_spec.rb @@ -93,7 +93,7 @@ describe Chef::Resource::Mdadm do end it "describes its state" do - state = @resource.state + state = @resource.state_for_resource_reporter expect(state[:devices]).to eql(%w{device1 device2}) expect(state[:level]).to eq(1) expect(state[:chunk]).to eq(42) diff --git a/spec/unit/resource/mount_spec.rb b/spec/unit/resource/mount_spec.rb index 832f7644ac..466b6ac8c0 100644 --- a/spec/unit/resource/mount_spec.rb +++ b/spec/unit/resource/mount_spec.rb @@ -1,7 +1,7 @@ # # Author:: Joshua Timberman (<joshua@chef.io>) # Author:: Tyler Cloke (<tyler@chef.io>) -# Copyright:: Copyright 2009-2016, Chef Software Inc. +# Copyright:: Copyright 2009-2017, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -181,7 +181,7 @@ describe Chef::Resource::Mount do end it "describes its state" do - state = @resource.state + state = @resource.state_for_resource_reporter expect(state[:mount_point]).to eq("123.456") expect(state[:device_type]).to eql(:device) expect(state[:fstype]).to eq("ranked") @@ -202,7 +202,7 @@ describe Chef::Resource::Mount do end it "describes its state" do - state = @resource.state + state = @resource.state_for_resource_reporter expect(state[:mount_point]).to eq("T:") expect(state[:username]).to eq("Administrator") expect(state[:password]).to eq("Jetstream123!") diff --git a/spec/unit/resource/ohai_spec.rb b/spec/unit/resource/ohai_spec.rb index cf1748002b..574c09eeba 100644 --- a/spec/unit/resource/ohai_spec.rb +++ b/spec/unit/resource/ohai_spec.rb @@ -49,7 +49,7 @@ describe Chef::Resource::Ohai do end it "describes its state" do - state = @resource.state + state = @resource.state_for_resource_reporter expect(state[:plugin]).to eq("passwd") end diff --git a/spec/unit/resource/package_spec.rb b/spec/unit/resource/package_spec.rb index 7ec3c198e4..8c00ea2bdd 100644 --- a/spec/unit/resource/package_spec.rb +++ b/spec/unit/resource/package_spec.rb @@ -1,7 +1,7 @@ # # Author:: Adam Jacob (<adam@chef.io>) # Author:: Tyler Cloke (<tyler@chef.io>) -# Copyright:: Copyright 2008-2016, Chef Software Inc. +# Copyright:: Copyright 2008-2017, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -72,7 +72,7 @@ describe Chef::Resource::Package do end it "describes its state" do - state = @resource.state + state = @resource.state_for_resource_reporter expect(state[:version]).to eq("10.9.8") expect(state[:options]).to eq("-al") end diff --git a/spec/unit/resource/registry_key_spec.rb b/spec/unit/resource/registry_key_spec.rb index d378da3ed0..067f2da36a 100644 --- a/spec/unit/resource/registry_key_spec.rb +++ b/spec/unit/resource/registry_key_spec.rb @@ -1,6 +1,6 @@ # # Author:: Lamont Granquist (<lamont@chef.io>) -# Copyright:: Copyright 2012-2016, Chef Software Inc. +# Copyright:: Copyright 2012-2017, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -210,6 +210,6 @@ describe Chef::Resource::RegistryKey, "state" do it "should return scrubbed values" do @resource.values([ { :name => "poosh", :type => :binary, :data => 255.chr * 1 } ]) - expect(@resource.state).to eql( { :values => [{ :name => "poosh", :type => :binary, :data => "a8100ae6aa1940d0b663bb31cd466142ebbdbd5187131b92d93818987832eb89" }] } ) + expect(@resource.state_for_resource_reporter).to eql( { :values => [{ :name => "poosh", :type => :binary, :data => "a8100ae6aa1940d0b663bb31cd466142ebbdbd5187131b92d93818987832eb89" }] } ) end end diff --git a/spec/unit/resource/remote_directory_spec.rb b/spec/unit/resource/remote_directory_spec.rb index cdca214db6..370b8d8225 100644 --- a/spec/unit/resource/remote_directory_spec.rb +++ b/spec/unit/resource/remote_directory_spec.rb @@ -1,6 +1,6 @@ # # Author:: Adam Jacob (<adam@chef.io>) -# Copyright:: Copyright 2008-2016, Chef Software Inc. +# Copyright:: Copyright 2008-2017, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -84,7 +84,7 @@ describe Chef::Resource::RemoteDirectory do end it "describes its state" do - state = @resource.state + state = @resource.state_for_resource_reporter expect(state[:files_owner]).to eq("root") expect(state[:files_group]).to eq("supergroup") expect(state[:files_mode]).to eq("0664") diff --git a/spec/unit/resource/remote_file_spec.rb b/spec/unit/resource/remote_file_spec.rb index 274f98e7f4..eca3db3420 100644 --- a/spec/unit/resource/remote_file_spec.rb +++ b/spec/unit/resource/remote_file_spec.rb @@ -1,7 +1,7 @@ # # Author:: Adam Jacob (<adam@chef.io>) # Author:: Tyler Cloke (<tyler@chef.io>) -# Copyright:: Copyright 2008-2016, Chef Software Inc. +# Copyright:: Copyright 2008-2017, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -183,7 +183,7 @@ describe Chef::Resource::RemoteFile do end it "describes its state" do - state = @resource.state + state = @resource.state_for_resource_reporter if Chef::Platform.windows? puts state expect(state[:rights]).to eq([{ :permissions => :read, :principals => "Everyone" }]) diff --git a/spec/unit/resource/route_spec.rb b/spec/unit/resource/route_spec.rb index 259ccf7eab..884b477365 100644 --- a/spec/unit/resource/route_spec.rb +++ b/spec/unit/resource/route_spec.rb @@ -95,7 +95,7 @@ describe Chef::Resource::Route do end it "describes its state" do - state = @resource.state + state = @resource.state_for_resource_reporter expect(state[:netmask]).to eq("lemask") expect(state[:gateway]).to eq("111.111.111") end diff --git a/spec/unit/resource/scm_spec.rb b/spec/unit/resource/scm_spec.rb index f39334348e..679b3bc1fc 100644 --- a/spec/unit/resource/scm_spec.rb +++ b/spec/unit/resource/scm_spec.rb @@ -1,7 +1,7 @@ # # Author:: Daniel DeLeo (<dan@kallistec.com>) # Author:: Tyler Cloke (<tyler@chef.io>) -# Copyright:: Copyright 2008-2016, Chef Software Inc. +# Copyright:: Copyright 2008-2017, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -174,7 +174,7 @@ describe Chef::Resource::Scm do end it "describes its state" do - state = @resource.state + state = @resource.state_for_resource_reporter expect(state[:revision]).to eq("1.2.3") end diff --git a/spec/unit/resource/service_spec.rb b/spec/unit/resource/service_spec.rb index 8d661e2a7a..205282b99f 100644 --- a/spec/unit/resource/service_spec.rb +++ b/spec/unit/resource/service_spec.rb @@ -166,7 +166,7 @@ describe Chef::Resource::Service do end it "describes its state" do - state = @resource.state + state = @resource.state_for_resource_reporter expect(state[:enabled]).to eql(true) expect(state[:running]).to eql(false) end diff --git a/spec/unit/resource/systemd_unit_spec.rb b/spec/unit/resource/systemd_unit_spec.rb index 7e46872525..ab1004fab0 100644 --- a/spec/unit/resource/systemd_unit_spec.rb +++ b/spec/unit/resource/systemd_unit_spec.rb @@ -109,7 +109,7 @@ describe Chef::Resource::SystemdUnit do @resource.masked false @resource.static false @resource.content "test" - state = @resource.state + state = @resource.state_for_resource_reporter expect(state[:active]).to eq(true) expect(state[:enabled]).to eq(true) expect(state[:masked]).to eq(false) diff --git a/spec/unit/resource/template_spec.rb b/spec/unit/resource/template_spec.rb index 9060f02d29..966a86ba6b 100644 --- a/spec/unit/resource/template_spec.rb +++ b/spec/unit/resource/template_spec.rb @@ -1,6 +1,6 @@ # # Author:: Adam Jacob (<adam@chef.io>) -# Copyright:: Copyright 2008-2016, Chef Software Inc. +# Copyright:: Copyright 2008-2017, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -88,7 +88,7 @@ describe Chef::Resource::Template do context "on unix", :unix_only do it "describes its state" do - state = @resource.state + state = @resource.state_for_resource_reporter expect(state[:owner]).to eq("root") expect(state[:group]).to eq("wheel") expect(state[:mode]).to eq("0644") diff --git a/spec/unit/resource/user_spec.rb b/spec/unit/resource/user_spec.rb index afd3969164..1a3f0284e3 100644 --- a/spec/unit/resource/user_spec.rb +++ b/spec/unit/resource/user_spec.rb @@ -119,7 +119,7 @@ end end it "describes its state" do - state = @resource.state + state = @resource.state_for_resource_reporter expect(state[:uid]).to eq(123) expect(state[:gid]).to eq(456) expect(state[:home]).to eq("/usr/local/root/") diff --git a/spec/unit/resource_reporter_spec.rb b/spec/unit/resource_reporter_spec.rb index 84cfb52418..4fcfb01233 100644 --- a/spec/unit/resource_reporter_spec.rb +++ b/spec/unit/resource_reporter_spec.rb @@ -3,7 +3,7 @@ # Author:: Prajakta Purohit (<prajakta@chef.io>) # Author:: Tyler Cloke (<tyler@chef.io>) # -# Copyright:: Copyright 2012-2016, Chef Software Inc. +# Copyright:: Copyright 2012-2017, Chef Software Inc. # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -399,11 +399,11 @@ describe Chef::ResourceReporter do end it "includes an updated resource's initial state" do - expect(@first_update_report["before"]).to eq(current_resource.state) + expect(@first_update_report["before"]).to eq(current_resource.state_for_resource_reporter) end it "includes an updated resource's final state" do - expect(@first_update_report["after"]).to eq(new_resource.state) + expect(@first_update_report["after"]).to eq(new_resource.state_for_resource_reporter) end it "includes the resource's name" do @@ -540,11 +540,11 @@ describe Chef::ResourceReporter do end it "includes an updated resource's initial state" do - expect(@first_update_report["before"]).to eq(@current_resource.state) + expect(@first_update_report["before"]).to eq(@current_resource.state_for_resource_reporter) end it "includes an updated resource's final state" do - expect(@first_update_report["after"]).to eq(@new_resource.state) + expect(@first_update_report["after"]).to eq(@new_resource.state_for_resource_reporter) end it "includes the resource's name" do diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb index c1aeb256fa..11289a41c6 100644 --- a/spec/unit/resource_spec.rb +++ b/spec/unit/resource_spec.rb @@ -160,7 +160,7 @@ describe Chef::Resource do end it "describes its state" do - resource_state = file_resource.state + resource_state = file_resource.state_for_resource_reporter expect(resource_state.keys).to match_array([:checksum, :owner, :group, :mode]) expect(resource_state[:checksum]).to eq("abc123") expect(resource_state[:owner]).to eq("root") |