chef 12.4.3 → 12.5.0.alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -2
  3. data/lib/chef.rb +1 -1
  4. data/lib/chef/application/solo.rb +1 -1
  5. data/lib/chef/application/windows_service_manager.rb +17 -12
  6. data/lib/chef/chef_class.rb +7 -0
  7. data/lib/chef/chef_fs/config.rb +22 -24
  8. data/lib/chef/chef_fs/file_pattern.rb +4 -15
  9. data/lib/chef/chef_fs/file_system/cookbook_dir.rb +1 -0
  10. data/lib/chef/chef_fs/knife.rb +35 -7
  11. data/lib/chef/chef_fs/path_utils.rb +65 -34
  12. data/lib/chef/constants.rb +27 -0
  13. data/lib/chef/delayed_evaluator.rb +21 -0
  14. data/lib/chef/dsl/recipe.rb +20 -2
  15. data/lib/chef/event_dispatch/base.rb +40 -16
  16. data/lib/chef/event_dispatch/dsl.rb +64 -0
  17. data/lib/chef/exceptions.rb +6 -1
  18. data/lib/chef/formatters/doc.rb +3 -1
  19. data/lib/chef/guard_interpreter/resource_guard_interpreter.rb +3 -1
  20. data/lib/chef/http/http_request.rb +1 -1
  21. data/lib/chef/knife/bootstrap/templates/chef-full.erb +1 -1
  22. data/lib/chef/knife/ssl_check.rb +3 -2
  23. data/lib/chef/knife/user_edit.rb +1 -2
  24. data/lib/chef/mixin/params_validate.rb +362 -135
  25. data/lib/chef/node.rb +19 -0
  26. data/lib/chef/platform/handler_map.rb +0 -5
  27. data/lib/chef/platform/rebooter.rb +1 -1
  28. data/lib/chef/property.rb +539 -0
  29. data/lib/chef/provider.rb +129 -12
  30. data/lib/chef/provider/deploy.rb +3 -5
  31. data/lib/chef/provider/lwrp_base.rb +1 -75
  32. data/lib/chef/provider/package.rb +1 -1
  33. data/lib/chef/provider/powershell_script.rb +32 -19
  34. data/lib/chef/provider/registry_key.rb +5 -5
  35. data/lib/chef/provider/service/macosx.rb +5 -1
  36. data/lib/chef/recipe.rb +1 -8
  37. data/lib/chef/resource.rb +499 -84
  38. data/lib/chef/resource/file/verification.rb +7 -1
  39. data/lib/chef/resource/lwrp_base.rb +1 -7
  40. data/lib/chef/run_context.rb +404 -83
  41. data/lib/chef/version.rb +1 -1
  42. data/lib/chef/win32/registry.rb +10 -2
  43. data/lib/chef/workstation_config_loader.rb +3 -158
  44. data/spec/data/run_context/cookbooks/include/recipes/default.rb +24 -0
  45. data/spec/data/run_context/cookbooks/include/recipes/includee.rb +3 -0
  46. data/spec/functional/rebooter_spec.rb +1 -1
  47. data/spec/functional/resource/{powershell_spec.rb → powershell_script_spec.rb} +3 -3
  48. data/spec/functional/win32/registry_helper_spec.rb +12 -0
  49. data/spec/functional/win32/service_manager_spec.rb +2 -2
  50. data/spec/integration/knife/chef_repo_path_spec.rb +13 -11
  51. data/spec/integration/recipes/recipe_dsl_spec.rb +0 -15
  52. data/spec/integration/recipes/resource_action_spec.rb +343 -0
  53. data/spec/spec_helper.rb +1 -0
  54. data/spec/support/shared/functional/win32_service.rb +2 -1
  55. data/spec/unit/application/solo_spec.rb +4 -3
  56. data/spec/unit/chef_class_spec.rb +23 -0
  57. data/spec/unit/chef_fs/path_util_spec.rb +108 -0
  58. data/spec/unit/event_dispatch/dsl_spec.rb +87 -0
  59. data/spec/unit/json_compat_spec.rb +4 -3
  60. data/spec/unit/knife/ssl_check_spec.rb +4 -0
  61. data/spec/unit/mixin/params_validate_spec.rb +4 -2
  62. data/spec/unit/node_spec.rb +7 -0
  63. data/spec/unit/property/state_spec.rb +506 -0
  64. data/spec/unit/property/validation_spec.rb +658 -0
  65. data/spec/unit/property_spec.rb +968 -0
  66. data/spec/unit/provider/{powershell_spec.rb → powershell_script_spec.rb} +0 -0
  67. data/spec/unit/provider/registry_key_spec.rb +12 -0
  68. data/spec/unit/provider/service/macosx_spec.rb +4 -4
  69. data/spec/unit/provider_spec.rb +1 -3
  70. data/spec/unit/recipe_spec.rb +0 -4
  71. data/spec/unit/registry_helper_spec.rb +15 -1
  72. data/spec/unit/resource/file/verification_spec.rb +33 -5
  73. data/spec/unit/resource/{powershell_spec.rb → powershell_script_spec.rb} +0 -0
  74. data/spec/unit/resource_spec.rb +2 -2
  75. data/spec/unit/run_context/child_run_context_spec.rb +133 -0
  76. data/spec/unit/run_context_spec.rb +7 -0
  77. metadata +25 -25
  78. data/spec/unit/workstation_config_loader_spec.rb +0 -283
@@ -0,0 +1,87 @@
1
+ #
2
+ # Author:: Ranjib Dey (<ranjib@linux.com>)
3
+ #
4
+ # Copyright:: Copyright (c) 2015 Ranjib Dey
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+ require 'spec_helper'
20
+ require 'chef/event_dispatch/dsl'
21
+
22
+ describe Chef::EventDispatch::DSL do
23
+ let(:events) do
24
+ Chef::EventDispatch::Dispatcher.new
25
+ end
26
+
27
+ let(:run_context) do
28
+ Chef::RunContext.new(Chef::Node.new, nil, events)
29
+ end
30
+
31
+ before do
32
+ Chef.set_run_context(run_context)
33
+ end
34
+
35
+ after do
36
+ Chef.reset!
37
+ end
38
+
39
+ subject{ described_class.new('test') }
40
+
41
+ it 'set handler name' do
42
+ subject.on(:run_started) {}
43
+ expect(events.subscribers.first.name).to eq('test')
44
+ end
45
+
46
+ it 'raise error when invalid event type is supplied' do
47
+ expect do
48
+ subject.on(:foo_bar) {}
49
+ end.to raise_error(Chef::Exceptions::InvalidEventType)
50
+ end
51
+
52
+ it 'register user hooks against valid event type' do
53
+ subject.on(:run_failed) {'testhook'}
54
+ expect(events.subscribers.first.run_failed).to eq('testhook')
55
+ end
56
+
57
+ it 'preserve state across event hooks' do
58
+ calls = []
59
+ Chef.event_handler do
60
+ on :resource_updated do
61
+ calls << :updated
62
+ end
63
+ on :resource_action_start do
64
+ calls << :started
65
+ end
66
+ end
67
+ resource = Chef::Resource::RubyBlock.new('foo', run_context)
68
+ resource.block { }
69
+ resource.run_action(:run)
70
+ expect(calls).to eq([:started, :updated])
71
+ end
72
+
73
+ it 'preserve instance variables across handler callbacks' do
74
+ Chef.event_handler do
75
+ on :resource_action_start do
76
+ @ivar = [1]
77
+ end
78
+ on :resource_updated do
79
+ @ivar << 2
80
+ end
81
+ end
82
+ resource = Chef::Resource::RubyBlock.new('foo', run_context)
83
+ resource.block { }
84
+ resource.run_action(:run)
85
+ expect(events.subscribers.first.instance_variable_get(:@ivar)).to eq([1, 2])
86
+ end
87
+ end
@@ -72,8 +72,6 @@ describe Chef::JSONCompat do
72
72
  end
73
73
  end
74
74
 
75
- # On FreeBSD 10.1 i386 rspec fails with a SystemStackError loading the expect line with more that 252 entries
76
- # https://github.com/chef/chef/issues/3101
77
75
  describe "with the file with 252 or less nested entries" do
78
76
  let(:json) { IO.read(File.join(CHEF_SPEC_DATA, 'nested.json')) }
79
77
  let(:hash) { Chef::JSONCompat.from_json(json) }
@@ -84,7 +82,10 @@ describe Chef::JSONCompat do
84
82
  end
85
83
 
86
84
  it "should has 'test' as a 252 nested value" do
87
- expect(hash['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']['key']).to eq('test')
85
+ v = 252.times.inject(hash) do |memo, _|
86
+ memo['key']
87
+ end
88
+ expect(v).to eq('test')
88
89
  end
89
90
  end
90
91
  end
@@ -163,6 +163,7 @@ E
163
163
  expect(ssl_check).to receive(:verify_X509).and_return(true) # X509 valid certs (no warn)
164
164
  expect(ssl_socket).to receive(:connect) # no error
165
165
  expect(ssl_socket).to receive(:post_connection_check).with("foo.example.com") # no error
166
+ expect(ssl_socket).to receive(:hostname=).with("foo.example.com") # no error
166
167
  end
167
168
 
168
169
  it "prints a success message" do
@@ -197,6 +198,7 @@ E
197
198
  expect(ssl_socket).to receive(:post_connection_check).
198
199
  with("foo.example.com").
199
200
  and_raise(OpenSSL::SSL::SSLError)
201
+ expect(ssl_socket).to receive(:hostname=).with("foo.example.com") # no error
200
202
  expect(ssl_socket_for_debug).to receive(:connect)
201
203
  expect(ssl_socket_for_debug).to receive(:peer_cert).and_return(self_signed_crt)
202
204
  end
@@ -215,6 +217,8 @@ E
215
217
  expect(ssl_check).to receive(:verify_X509).and_return(true) # X509 valid certs
216
218
  expect(ssl_socket).to receive(:connect).
217
219
  and_raise(OpenSSL::SSL::SSLError)
220
+ expect(ssl_socket).to receive(:hostname=).
221
+ with("foo.example.com") # no error
218
222
  expect(ssl_socket_for_debug).to receive(:connect)
219
223
  expect(ssl_socket_for_debug).to receive(:peer_cert).and_return(self_signed_crt)
220
224
  end
@@ -21,6 +21,8 @@ require 'spec_helper'
21
21
  class TinyClass
22
22
  include Chef::Mixin::ParamsValidate
23
23
 
24
+ attr_reader :name
25
+
24
26
  def music(is_good=true)
25
27
  is_good
26
28
  end
@@ -331,11 +333,11 @@ describe Chef::Mixin::ParamsValidate do
331
333
  it "asserts that a value returns false from a predicate method" do
332
334
  expect do
333
335
  @vo.validate({:not_blank => "should pass"},
334
- {:not_blank => {:cannot_be => :nil, :cannot_be => :empty}})
336
+ {:not_blank => {:cannot_be => [ :nil, :empty ]}})
335
337
  end.not_to raise_error
336
338
  expect do
337
339
  @vo.validate({:not_blank => ""},
338
- {:not_blank => {:cannot_be => :nil, :cannot_be => :empty}})
340
+ {:not_blank => {:cannot_be => [ :nil, :empty ]}})
339
341
  end.to raise_error(Chef::Exceptions::ValidationFailed)
340
342
  end
341
343
 
@@ -672,6 +672,13 @@ describe Chef::Node do
672
672
  expect(node.run_list).to eq([ "role[base]", "recipe[chef::server]" ])
673
673
  end
674
674
 
675
+ it "sets the node chef_environment" do
676
+ attrs = { "chef_environment" => "foo_environment", "bar" => "baz" }
677
+ expect(node.consume_chef_environment(attrs)).to eq({ "bar" => "baz" })
678
+ expect(node.chef_environment).to eq("foo_environment")
679
+ expect(node['chef_environment']).to be nil
680
+ end
681
+
675
682
  it "should overwrites the run list with the run list it consumes" do
676
683
  node.consume_run_list "recipes" => [ "one", "two" ]
677
684
  node.consume_run_list "recipes" => [ "three" ]
@@ -0,0 +1,506 @@
1
+ require 'support/shared/integration/integration_helper'
2
+
3
+ describe "Chef::Resource#identity and #state" do
4
+ include IntegrationSupport
5
+
6
+ class NewResourceNamer
7
+ @i = 0
8
+ def self.next
9
+ "chef_resource_property_spec_#{@i += 1}"
10
+ end
11
+ end
12
+
13
+ def self.new_resource_name
14
+ NewResourceNamer.next
15
+ end
16
+
17
+ let(:resource_class) do
18
+ new_resource_name = self.class.new_resource_name
19
+ Class.new(Chef::Resource) do
20
+ resource_name new_resource_name
21
+ end
22
+ end
23
+
24
+ let(:resource) do
25
+ resource_class.new("blah")
26
+ end
27
+
28
+ def self.english_join(values)
29
+ return '<nothing>' if values.size == 0
30
+ return values[0].inspect if values.size == 1
31
+ "#{values[0..-2].map { |v| v.inspect }.join(", ")} and #{values[-1].inspect}"
32
+ end
33
+
34
+ def self.with_property(*properties, &block)
35
+ tags_index = properties.find_index { |p| !p.is_a?(String)}
36
+ if tags_index
37
+ properties, tags = properties[0..tags_index-1], properties[tags_index..-1]
38
+ else
39
+ tags = []
40
+ end
41
+ properties = properties.map { |property| "property #{property}" }
42
+ context "With properties #{english_join(properties)}", *tags do
43
+ before do
44
+ properties.each do |property_str|
45
+ resource_class.class_eval(property_str, __FILE__, __LINE__)
46
+ end
47
+ end
48
+ instance_eval(&block)
49
+ end
50
+ end
51
+
52
+ # identity
53
+ context "Chef::Resource#identity_properties" do
54
+ with_property ":x" do
55
+ it "name is the default identity" do
56
+ expect(resource_class.identity_properties).to eq [ Chef::Resource.properties[:name] ]
57
+ expect(Chef::Resource.properties[:name].identity?).to be_falsey
58
+ expect(resource.name).to eq 'blah'
59
+ expect(resource.identity).to eq 'blah'
60
+ end
61
+
62
+ it "identity_properties :x changes the identity" do
63
+ expect(resource_class.identity_properties :x).to eq [ resource_class.properties[:x] ]
64
+ expect(resource_class.identity_properties).to eq [ resource_class.properties[:x] ]
65
+ expect(Chef::Resource.properties[:name].identity?).to be_falsey
66
+ expect(resource_class.properties[:x].identity?).to be_truthy
67
+
68
+ expect(resource.x 'woo').to eq 'woo'
69
+ expect(resource.x).to eq 'woo'
70
+
71
+ expect(resource.name).to eq 'blah'
72
+ expect(resource.identity).to eq 'woo'
73
+ end
74
+
75
+ with_property ":y, identity: true" do
76
+ context "and identity_properties :x" do
77
+ before do
78
+ resource_class.class_eval do
79
+ identity_properties :x
80
+ end
81
+ end
82
+
83
+ it "only returns :x as identity" do
84
+ resource.x 'foo'
85
+ resource.y 'bar'
86
+ expect(resource_class.identity_properties).to eq [ resource_class.properties[:x] ]
87
+ expect(resource.identity).to eq 'foo'
88
+ end
89
+ it "does not flip y.desired_state off" do
90
+ resource.x 'foo'
91
+ resource.y 'bar'
92
+ expect(resource_class.state_properties).to eq [
93
+ resource_class.properties[:x],
94
+ resource_class.properties[:y]
95
+ ]
96
+ expect(resource.state_for_resource_reporter).to eq(x: 'foo', y: 'bar')
97
+ end
98
+ end
99
+ end
100
+
101
+ context "With a subclass" do
102
+ let(:subresource_class) do
103
+ new_resource_name = self.class.new_resource_name
104
+ Class.new(resource_class) do
105
+ resource_name new_resource_name
106
+ end
107
+ end
108
+ let(:subresource) do
109
+ subresource_class.new('sub')
110
+ end
111
+
112
+ it "name is the default identity on the subclass" do
113
+ expect(subresource_class.identity_properties).to eq [ Chef::Resource.properties[:name] ]
114
+ expect(Chef::Resource.properties[:name].identity?).to be_falsey
115
+ expect(subresource.name).to eq 'sub'
116
+ expect(subresource.identity).to eq 'sub'
117
+ end
118
+
119
+ context "With identity_properties :x on the superclass" do
120
+ before do
121
+ resource_class.class_eval do
122
+ identity_properties :x
123
+ end
124
+ end
125
+
126
+ it "The subclass inherits :x as identity" do
127
+ expect(subresource_class.identity_properties).to eq [ subresource_class.properties[:x] ]
128
+ expect(Chef::Resource.properties[:name].identity?).to be_falsey
129
+ expect(subresource_class.properties[:x].identity?).to be_truthy
130
+
131
+ subresource.x 'foo'
132
+ expect(subresource.identity).to eq 'foo'
133
+ end
134
+
135
+ context "With property :y, identity: true on the subclass" do
136
+ before do
137
+ subresource_class.class_eval do
138
+ property :y, identity: true
139
+ end
140
+ end
141
+ it "The subclass's identity includes both x and y" do
142
+ expect(subresource_class.identity_properties).to eq [
143
+ subresource_class.properties[:x],
144
+ subresource_class.properties[:y]
145
+ ]
146
+ subresource.x 'foo'
147
+ subresource.y 'bar'
148
+ expect(subresource.identity).to eq(x: 'foo', y: 'bar')
149
+ end
150
+ end
151
+
152
+ with_property ":y, String" do
153
+ context "With identity_properties :y on the subclass" do
154
+ before do
155
+ subresource_class.class_eval do
156
+ identity_properties :y
157
+ end
158
+ end
159
+ it "y is part of state" do
160
+ subresource.x 'foo'
161
+ subresource.y 'bar'
162
+ expect(subresource.state_for_resource_reporter).to eq(x: 'foo', y: 'bar')
163
+ expect(subresource_class.state_properties).to eq [
164
+ subresource_class.properties[:x],
165
+ subresource_class.properties[:y]
166
+ ]
167
+ end
168
+ it "y is the identity" do
169
+ expect(subresource_class.identity_properties).to eq [ subresource_class.properties[:y] ]
170
+ subresource.x 'foo'
171
+ subresource.y 'bar'
172
+ expect(subresource.identity).to eq 'bar'
173
+ end
174
+ it "y still has validation" do
175
+ expect { subresource.y 12 }.to raise_error Chef::Exceptions::ValidationFailed
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
182
+
183
+ with_property ":string_only, String, identity: true", ":string_only2, String" do
184
+ it "identity_properties does not change validation" do
185
+ resource_class.identity_properties :string_only
186
+ expect { resource.string_only 12 }.to raise_error Chef::Exceptions::ValidationFailed
187
+ expect { resource.string_only2 12 }.to raise_error Chef::Exceptions::ValidationFailed
188
+ end
189
+ end
190
+
191
+ with_property ":x, desired_state: false" do
192
+ it "identity_properties does not change desired_state" do
193
+ resource_class.identity_properties :x
194
+ resource.x 'hi'
195
+ expect(resource.identity).to eq 'hi'
196
+ expect(resource_class.properties[:x].desired_state?).to be_falsey
197
+ expect(resource_class.state_properties).to eq []
198
+ expect(resource.state_for_resource_reporter).to eq({})
199
+ end
200
+ end
201
+
202
+ context "With custom property custom_property defined only as methods, using different variables for storage" do
203
+ before do
204
+ resource_class.class_eval do
205
+ def custom_property
206
+ @blarghle ? @blarghle*3 : nil
207
+ end
208
+ def custom_property=(x)
209
+ @blarghle = x*2
210
+ end
211
+ end
212
+ end
213
+
214
+ context "And identity_properties :custom_property" do
215
+ before do
216
+ resource_class.class_eval do
217
+ identity_properties :custom_property
218
+ end
219
+ end
220
+
221
+ it "identity_properties comes back as :custom_property" do
222
+ expect(resource_class.properties[:custom_property].identity?).to be_truthy
223
+ expect(resource_class.identity_properties).to eq [ resource_class.properties[:custom_property] ]
224
+ end
225
+ it "custom_property becomes part of desired_state" do
226
+ resource.custom_property = 1
227
+ expect(resource.state_for_resource_reporter).to eq(custom_property: 6)
228
+ expect(resource_class.properties[:custom_property].desired_state?).to be_truthy
229
+ expect(resource_class.state_properties).to eq [
230
+ resource_class.properties[:custom_property]
231
+ ]
232
+ end
233
+ it "identity_properties does not change custom_property's getter or setter" do
234
+ resource.custom_property = 1
235
+ expect(resource.custom_property).to eq 6
236
+ end
237
+ it "custom_property is returned as the identity" do
238
+ expect(resource.identity).to be_nil
239
+ resource.custom_property = 1
240
+ expect(resource.identity).to eq 6
241
+ end
242
+ end
243
+ end
244
+ end
245
+
246
+ context "Property#identity" do
247
+ with_property ":x, identity: true" do
248
+ it "name is only part of the identity if an identity attribute is defined" do
249
+ expect(resource_class.identity_properties).to eq [ resource_class.properties[:x] ]
250
+ resource.x 'woo'
251
+ expect(resource.identity).to eq 'woo'
252
+ end
253
+ end
254
+
255
+ with_property ":x, identity: true, default: 'xxx'",
256
+ ":y, identity: true, default: 'yyy'",
257
+ ":z, identity: true, default: 'zzz'" do
258
+ it "identity_property raises an error if multiple identity values are defined" do
259
+ expect { resource_class.identity_property }.to raise_error Chef::Exceptions::MultipleIdentityError
260
+ end
261
+ it "identity_attr raises an error if multiple identity values are defined" do
262
+ expect { resource_class.identity_attr }.to raise_error Chef::Exceptions::MultipleIdentityError
263
+ end
264
+ it "identity returns all identity values in a hash if multiple are defined" do
265
+ resource.x 'foo'
266
+ resource.y 'bar'
267
+ resource.z 'baz'
268
+ expect(resource.identity).to eq(x: 'foo', y: 'bar', z: 'baz')
269
+ end
270
+ it "identity returns all values whether any value is set or not" do
271
+ expect(resource.identity).to eq(x: 'xxx', y: 'yyy', z: 'zzz')
272
+ end
273
+ it "identity_properties wipes out any other identity attributes if multiple are defined" do
274
+ resource_class.identity_properties :y
275
+ resource.x 'foo'
276
+ resource.y 'bar'
277
+ resource.z 'baz'
278
+ expect(resource.identity).to eq 'bar'
279
+ end
280
+ end
281
+
282
+ with_property ":x, identity: true, name_property: true" do
283
+ it "identity when x is not defined returns the value of x" do
284
+ expect(resource.identity).to eq 'blah'
285
+ end
286
+ it "state when x is not defined returns the value of x" do
287
+ expect(resource.state_for_resource_reporter).to eq(x: 'blah')
288
+ end
289
+ end
290
+ end
291
+
292
+ # state_properties
293
+ context "Chef::Resource#state_properties" do
294
+ it "state_properties is empty by default" do
295
+ expect(Chef::Resource.state_properties).to eq []
296
+ expect(resource.state_for_resource_reporter).to eq({})
297
+ end
298
+
299
+ with_property ":x", ":y", ":z" do
300
+ it "x, y and z are state attributes" do
301
+ resource.x 1
302
+ resource.y 2
303
+ resource.z 3
304
+ expect(resource_class.state_properties).to eq [
305
+ resource_class.properties[:x],
306
+ resource_class.properties[:y],
307
+ resource_class.properties[:z]
308
+ ]
309
+ expect(resource.state_for_resource_reporter).to eq(x: 1, y: 2, z: 3)
310
+ end
311
+ it "values that are not set are not included in state" do
312
+ resource.x 1
313
+ expect(resource.state_for_resource_reporter).to eq(x: 1)
314
+ end
315
+ it "when no values are set, nothing is included in state" do
316
+ end
317
+ end
318
+
319
+ with_property ":x", ":y, desired_state: false", ":z, desired_state: true" do
320
+ it "x and z are state attributes, and y is not" do
321
+ resource.x 1
322
+ resource.y 2
323
+ resource.z 3
324
+ expect(resource_class.state_properties).to eq [
325
+ resource_class.properties[:x],
326
+ resource_class.properties[:z]
327
+ ]
328
+ expect(resource.state_for_resource_reporter).to eq(x: 1, z: 3)
329
+ end
330
+ end
331
+
332
+ with_property ":x, name_property: true" do
333
+ # it "Unset values with name_property are included in state" do
334
+ # expect(resource.state_for_resource_reporter).to eq({ x: 'blah' })
335
+ # end
336
+ it "Set values with name_property are included in state" do
337
+ resource.x 1
338
+ expect(resource.state_for_resource_reporter).to eq(x: 1)
339
+ end
340
+ end
341
+
342
+ with_property ":x, default: 1" do
343
+ it "Unset values with defaults are not included in state" do
344
+ expect(resource.state_for_resource_reporter).to eq({})
345
+ end
346
+ it "Set values with defaults are included in state" do
347
+ resource.x 1
348
+ expect(resource.state_for_resource_reporter).to eq(x: 1)
349
+ end
350
+ end
351
+
352
+ context "With a class with a normal getter and setter" do
353
+ before do
354
+ resource_class.class_eval do
355
+ def x
356
+ @blah*3
357
+ end
358
+ def x=(value)
359
+ @blah = value*2
360
+ end
361
+ end
362
+ end
363
+ it "state_properties(:x) causes the value to be included in properties" do
364
+ resource_class.state_properties(:x)
365
+ resource.x = 1
366
+
367
+ expect(resource.x).to eq 6
368
+ expect(resource.state_for_resource_reporter).to eq(x: 6)
369
+ end
370
+ end
371
+
372
+ context "When state_properties happens before properties are declared" do
373
+ before do
374
+ resource_class.class_eval do
375
+ state_properties :x
376
+ property :x
377
+ end
378
+ end
379
+ it "the property works and is in state_properties" do
380
+ expect(resource_class.state_properties).to include(resource_class.properties[:x])
381
+ resource.x = 1
382
+ expect(resource.x).to eq 1
383
+ expect(resource.state_for_resource_reporter).to eq(x: 1)
384
+ end
385
+ end
386
+
387
+ with_property ":x, Integer, identity: true" do
388
+ it "state_properties(:x) leaves the property in desired_state" do
389
+ resource_class.state_properties(:x)
390
+ resource.x 10
391
+
392
+ expect(resource_class.properties[:x].desired_state?).to be_truthy
393
+ expect(resource_class.state_properties).to eq [
394
+ resource_class.properties[:x]
395
+ ]
396
+ expect(resource.state_for_resource_reporter).to eq(x: 10)
397
+ end
398
+ it "state_properties(:x) does not turn off validation" do
399
+ resource_class.state_properties(:x)
400
+ expect { resource.x 'ouch' }.to raise_error Chef::Exceptions::ValidationFailed
401
+ end
402
+ it "state_properties(:x) does not turn off identity" do
403
+ resource_class.state_properties(:x)
404
+ resource.x 10
405
+
406
+ expect(resource_class.identity_properties).to eq [ resource_class.properties[:x] ]
407
+ expect(resource_class.properties[:x].identity?).to be_truthy
408
+ expect(resource.identity).to eq 10
409
+ end
410
+ end
411
+
412
+ with_property ":x, Integer, identity: true, desired_state: false" do
413
+ before do
414
+ resource_class.class_eval do
415
+ def y
416
+ 20
417
+ end
418
+ end
419
+ end
420
+
421
+ it "state_properties(:x) leaves x identical" do
422
+ old_value = resource_class.properties[:y]
423
+ resource_class.state_properties(:x)
424
+ resource.x 10
425
+
426
+ expect(resource_class.properties[:y].object_id).to eq old_value.object_id
427
+
428
+ expect(resource_class.properties[:x].desired_state?).to be_truthy
429
+ expect(resource_class.properties[:x].identity?).to be_truthy
430
+ expect(resource_class.identity_properties).to eq [
431
+ resource_class.properties[:x]
432
+ ]
433
+ expect(resource.identity).to eq(10)
434
+ expect(resource_class.state_properties).to eq [
435
+ resource_class.properties[:x]
436
+ ]
437
+ expect(resource.state_for_resource_reporter).to eq(x: 10)
438
+ end
439
+
440
+ it "state_properties(:y) adds y to desired state" do
441
+ old_value = resource_class.properties[:x]
442
+ resource_class.state_properties(:y)
443
+ resource.x 10
444
+
445
+ expect(resource_class.properties[:x].object_id).to eq old_value.object_id
446
+ expect(resource_class.properties[:x].desired_state?).to be_falsey
447
+ expect(resource_class.properties[:y].desired_state?).to be_truthy
448
+ expect(resource_class.state_properties).to eq [
449
+ resource_class.properties[:y]
450
+ ]
451
+ expect(resource.state_for_resource_reporter).to eq(y: 20)
452
+ end
453
+
454
+ context "With a subclassed resource" do
455
+ let(:subresource_class) do
456
+ new_resource_name = self.class.new_resource_name
457
+ Class.new(resource_class) do
458
+ resource_name new_resource_name
459
+ end
460
+ end
461
+ let(:subresource) do
462
+ subresource_class.new('blah')
463
+ end
464
+
465
+ it "state_properties(:x) adds x to desired state" do
466
+ old_value = resource_class.properties[:y]
467
+ subresource_class.state_properties(:x)
468
+ subresource.x 10
469
+
470
+ expect(subresource_class.properties[:y].object_id).to eq old_value.object_id
471
+
472
+ expect(subresource_class.properties[:x].desired_state?).to be_truthy
473
+ expect(subresource_class.properties[:x].identity?).to be_truthy
474
+ expect(subresource_class.identity_properties).to eq [
475
+ subresource_class.properties[:x]
476
+ ]
477
+ expect(subresource.identity).to eq(10)
478
+ expect(subresource_class.state_properties).to eq [
479
+ subresource_class.properties[:x]
480
+ ]
481
+ expect(subresource.state_for_resource_reporter).to eq(x: 10)
482
+ end
483
+
484
+ it "state_properties(:y) adds y to desired state" do
485
+ old_value = resource_class.properties[:x]
486
+ subresource_class.state_properties(:y)
487
+ subresource.x 10
488
+
489
+ expect(subresource_class.properties[:x].object_id).to eq old_value.object_id
490
+ expect(subresource_class.properties[:y].desired_state?).to be_truthy
491
+ expect(subresource_class.state_properties).to eq [
492
+ subresource_class.properties[:y]
493
+ ]
494
+ expect(subresource.state_for_resource_reporter).to eq(y: 20)
495
+
496
+ expect(subresource_class.properties[:x].identity?).to be_truthy
497
+ expect(subresource_class.identity_properties).to eq [
498
+ subresource_class.properties[:x]
499
+ ]
500
+ expect(subresource.identity).to eq(10)
501
+ end
502
+ end
503
+ end
504
+ end
505
+
506
+ end