chef 12.4.3 → 12.5.0.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -315,6 +315,7 @@ class Chef
315
315
  # Consumes the combined run_list and other attributes in +attrs+
316
316
  def consume_attributes(attrs)
317
317
  normal_attrs_to_merge = consume_run_list(attrs)
318
+ normal_attrs_to_merge = consume_chef_environment(normal_attrs_to_merge)
318
319
  Chef::Log.debug("Applying attributes from json file")
319
320
  self.normal_attrs = Chef::Mixin::DeepMerge.merge(normal_attrs,normal_attrs_to_merge)
320
321
  self.tags # make sure they're defined
@@ -347,6 +348,24 @@ class Chef
347
348
  attrs
348
349
  end
349
350
 
351
+ # chef_environment when set in -j JSON will take precedence over
352
+ # -E ENVIRONMENT. Ideally, IMO, the order of precedence should be (lowest to
353
+ # highest):
354
+ # config_file
355
+ # -j JSON
356
+ # -E ENVIRONMENT
357
+ # so that users could reuse their JSON and override the chef_environment
358
+ # configured within it with -E ENVIRONMENT. Because command line options are
359
+ # merged with Chef::Config there is currently no way to distinguish between
360
+ # an environment set via config from an environment set via command line.
361
+ def consume_chef_environment(attrs)
362
+ attrs = attrs ? attrs.dup : {}
363
+ if env = attrs.delete("chef_environment")
364
+ chef_environment(env)
365
+ end
366
+ attrs
367
+ end
368
+
350
369
  # Clear defaults and overrides, so that any deleted attributes
351
370
  # between runs are still gone.
352
371
  def reset_defaults_and_overrides
@@ -31,11 +31,6 @@ class Chef
31
31
  # are exactly equal
32
32
  if new_matcher[:value].is_a?(Class) && !new_matcher[:override]
33
33
  cmp = compare_matcher_properties(new_matcher, matcher) { |m| m[:value].name }
34
- if cmp < 0
35
- Chef::Log.warn "You are overriding #{key} on #{new_matcher[:filters].inspect} with #{new_matcher[:value].inspect}: used to be #{matcher[:value].inspect}. Use override: true if this is what you intended."
36
- elsif cmp > 0
37
- Chef::Log.warn "You declared a new resource #{new_matcher[:value].inspect} for resource #{key}, but it comes alphabetically after #{matcher[:value].inspect} and has the same filters (#{new_matcher[:filters].inspect}), so it will not be used. Use override: true if you want to use it for #{key}."
38
- end
39
34
  end
40
35
  end
41
36
  cmp
@@ -32,7 +32,7 @@ class Chef
32
32
 
33
33
  cmd = if Chef::Platform.windows?
34
34
  # should this do /f as well? do we then need a minimum delay to let apps quit?
35
- "shutdown /r /t #{reboot_info[:delay_mins]} /c \"#{reboot_info[:reason]}\""
35
+ "shutdown /r /t #{reboot_info[:delay_mins]*60} /c \"#{reboot_info[:reason]}\""
36
36
  else
37
37
  # probably Linux-only.
38
38
  "shutdown -r +#{reboot_info[:delay_mins]} \"#{reboot_info[:reason]}\""
@@ -0,0 +1,539 @@
1
+ #
2
+ # Author:: John Keiser <jkeiser@chef.io>
3
+ # Copyright:: Copyright (c) 2015 John Keiser.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef/exceptions'
20
+ require 'chef/delayed_evaluator'
21
+
22
+ class Chef
23
+ #
24
+ # Type and validation information for a property on a resource.
25
+ #
26
+ # A property named "x" manipulates the "@x" instance variable on a
27
+ # resource. The *presence* of the variable (`instance_variable_defined?(@x)`)
28
+ # tells whether the variable is defined; it may have any actual value,
29
+ # constrained only by validation.
30
+ #
31
+ # Properties may have validation, defaults, and coercion, and have full
32
+ # support for lazy values.
33
+ #
34
+ # @see Chef::Resource.property
35
+ # @see Chef::DelayedEvaluator
36
+ #
37
+ class Property
38
+ #
39
+ # Create a reusable property type that can be used in multiple properties
40
+ # in different resources.
41
+ #
42
+ # @param options [Hash<Symbol,Object>] Validation options. See Chef::Resource.property for
43
+ # the list of options.
44
+ #
45
+ # @example
46
+ # Property.derive(default: 'hi')
47
+ #
48
+ def self.derive(**options)
49
+ new(**options)
50
+ end
51
+
52
+ #
53
+ # Create a new property.
54
+ #
55
+ # @param options [Hash<Symbol,Object>] Property options, including
56
+ # control options here, as well as validation options (see
57
+ # Chef::Mixin::ParamsValidate#validate for a description of validation
58
+ # options).
59
+ # @option options [Symbol] :name The name of this property.
60
+ # @option options [Class] :declared_in The class this property comes from.
61
+ # @option options [Symbol] :instance_variable_name The instance variable
62
+ # tied to this property. Must include a leading `@`. Defaults to `@<name>`.
63
+ # `nil` means the property is opaque and not tied to a specific instance
64
+ # variable.
65
+ # @option options [Boolean] :desired_state `true` if this property is part of desired
66
+ # state. Defaults to `true`.
67
+ # @option options [Boolean] :identity `true` if this property is part of object
68
+ # identity. Defaults to `false`.
69
+ # @option options [Boolean] :name_property `true` if this
70
+ # property defaults to the same value as `name`. Equivalent to
71
+ # `default: lazy { name }`, except that #property_is_set? will
72
+ # return `true` if the property is set *or* if `name` is set.
73
+ # @option options [Object] :default The value this property
74
+ # will return if the user does not set one. If this is `lazy`, it will
75
+ # be run in the context of the instance (and able to access other
76
+ # properties) and cached. If not, the value will be frozen with Object#freeze
77
+ # to prevent users from modifying it in an instance.
78
+ # @option options [Proc] :coerce A proc which will be called to
79
+ # transform the user input to canonical form. The value is passed in,
80
+ # and the transformed value returned as output. Lazy values will *not*
81
+ # be passed to this method until after they are evaluated. Called in the
82
+ # context of the resource (meaning you can access other properties).
83
+ # @option options [Boolean] :required `true` if this property
84
+ # must be present; `false` otherwise. This is checked after the resource
85
+ # is fully initialized.
86
+ #
87
+ def initialize(**options)
88
+ options.each { |k,v| options[k.to_sym] = v if k.is_a?(String) }
89
+ options[:name_property] = options.delete(:name_attribute) unless options.has_key?(:name_property)
90
+ @options = options
91
+
92
+ if options.has_key?(:default)
93
+ options[:default] = options[:default].freeze
94
+ end
95
+ options[:name] = options[:name].to_sym if options[:name]
96
+ options[:instance_variable_name] = options[:instance_variable_name].to_sym if options[:instance_variable_name]
97
+ end
98
+
99
+ #
100
+ # The name of this property.
101
+ #
102
+ # @return [String]
103
+ #
104
+ def name
105
+ options[:name]
106
+ end
107
+
108
+ #
109
+ # The class this property was defined in.
110
+ #
111
+ # @return [Class]
112
+ #
113
+ def declared_in
114
+ options[:declared_in]
115
+ end
116
+
117
+ #
118
+ # The instance variable associated with this property.
119
+ #
120
+ # Defaults to `@<name>`
121
+ #
122
+ # @return [Symbol]
123
+ #
124
+ def instance_variable_name
125
+ if options.has_key?(:instance_variable_name)
126
+ options[:instance_variable_name]
127
+ elsif name
128
+ :"@#{name}"
129
+ end
130
+ end
131
+
132
+ #
133
+ # The raw default value for this resource.
134
+ #
135
+ # Does not coerce or validate the default. Does not evaluate lazy values.
136
+ #
137
+ # Defaults to `lazy { name }` if name_property is true; otherwise defaults to
138
+ # `nil`
139
+ #
140
+ def default
141
+ return options[:default] if options.has_key?(:default)
142
+ return Chef::DelayedEvaluator.new { name } if name_property?
143
+ nil
144
+ end
145
+
146
+ #
147
+ # Whether this is part of the resource's natural identity or not.
148
+ #
149
+ # @return [Boolean]
150
+ #
151
+ def identity?
152
+ options[:identity]
153
+ end
154
+
155
+ #
156
+ # Whether this is part of desired state or not.
157
+ #
158
+ # Defaults to true.
159
+ #
160
+ # @return [Boolean]
161
+ #
162
+ def desired_state?
163
+ return true if !options.has_key?(:desired_state)
164
+ options[:desired_state]
165
+ end
166
+
167
+ #
168
+ # Whether this is name_property or not.
169
+ #
170
+ # @return [Boolean]
171
+ #
172
+ def name_property?
173
+ options[:name_property]
174
+ end
175
+
176
+ #
177
+ # Whether this property has a default value.
178
+ #
179
+ # @return [Boolean]
180
+ #
181
+ def has_default?
182
+ options.has_key?(:default) || name_property?
183
+ end
184
+
185
+ #
186
+ # Whether this property is required or not.
187
+ #
188
+ # @return [Boolean]
189
+ #
190
+ def required?
191
+ options[:required]
192
+ end
193
+
194
+ #
195
+ # Validation options. (See Chef::Mixin::ParamsValidate#validate.)
196
+ #
197
+ # @return [Hash<Symbol,Object>]
198
+ #
199
+ def validation_options
200
+ @validation_options ||= options.reject { |k,v|
201
+ [:declared_in,:name,:instance_variable_name,:desired_state,:identity,:default,:name_property,:coerce,:required].include?(k)
202
+ }
203
+ end
204
+
205
+ #
206
+ # Handle the property being called.
207
+ #
208
+ # The base implementation does the property get-or-set:
209
+ #
210
+ # ```ruby
211
+ # resource.myprop # get
212
+ # resource.myprop value # set
213
+ # ```
214
+ #
215
+ # Subclasses may implement this with any arguments they want, as long as
216
+ # the corresponding DSL calls it correctly.
217
+ #
218
+ # @param resource [Chef::Resource] The resource to get the property from.
219
+ # @param value The value to set (or NOT_PASSED if it is a get).
220
+ #
221
+ # @return The current value of the property. If it is a `set`, lazy values
222
+ # will be returned without running, validating or coercing. If it is a
223
+ # `get`, the non-lazy, coerced, validated value will always be returned.
224
+ #
225
+ def call(resource, value=NOT_PASSED)
226
+ if value == NOT_PASSED
227
+ return get(resource)
228
+ end
229
+
230
+ # myprop nil is sometimes a get (backcompat)
231
+ if value.nil? && !explicitly_accepts_nil?(resource)
232
+ # If you say "my_property nil" and the property explicitly accepts
233
+ # nil values, we consider this a get.
234
+ Chef::Log.deprecation("#{name} nil currently does not overwrite the value of #{name}. This will change in Chef 13, and the value will be set to nil instead. Please change your code to explicitly accept nil using \"property :#{name}, [MyType, nil]\", or stop setting this value to nil.")
235
+ return get(resource)
236
+ end
237
+
238
+ # Anything else (myprop value) is a set
239
+ set(resource, value)
240
+ end
241
+
242
+ #
243
+ # Get the property value from the resource, handling lazy values,
244
+ # defaults, and validation.
245
+ #
246
+ # - If the property's value is lazy, it is evaluated, coerced and validated.
247
+ # - If the property has no value, and is required, raises ValidationFailed.
248
+ # - If the property has no value, but has a lazy default, it is evaluated,
249
+ # coerced and validated. If the evaluated value is frozen, the resulting
250
+ # - If the property has no value, but has a default, the default value
251
+ # will be returned and frozen. If the default value is lazy, it will be
252
+ # evaluated, coerced and validated, and the result stored in the property.
253
+ # - If the property has no value, but is name_property, `resource.name`
254
+ # is retrieved, coerced, validated and stored in the property.
255
+ # - Otherwise, `nil` is returned.
256
+ #
257
+ # @param resource [Chef::Resource] The resource to get the property from.
258
+ #
259
+ # @return The value of the property.
260
+ #
261
+ # @raise Chef::Exceptions::ValidationFailed If the value is invalid for
262
+ # this property, or if the value is required and not set.
263
+ #
264
+ def get(resource)
265
+ if is_set?(resource)
266
+ value = get_value(resource)
267
+ if value.is_a?(DelayedEvaluator)
268
+ value = exec_in_resource(resource, value)
269
+ value = coerce(resource, value)
270
+ validate(resource, value)
271
+ end
272
+ value
273
+
274
+ else
275
+ if has_default?
276
+ value = default
277
+ if value.is_a?(DelayedEvaluator)
278
+ value = exec_in_resource(resource, value)
279
+ end
280
+
281
+ value = coerce(resource, value)
282
+
283
+ # We don't validate defaults
284
+
285
+ # If the value is mutable (non-frozen), we set it on the instance
286
+ # so that people can mutate it. (All constant default values are
287
+ # frozen.)
288
+ if !value.frozen?
289
+ set_value(resource, value)
290
+ end
291
+
292
+ value
293
+
294
+ elsif required?
295
+ raise Chef::Exceptions::ValidationFailed, "#{name} is required"
296
+ end
297
+ end
298
+ end
299
+
300
+ #
301
+ # Set the value of this property in the given resource.
302
+ #
303
+ # Non-lazy values are coerced and validated before being set. Coercion
304
+ # and validation of lazy values is delayed until they are first retrieved.
305
+ #
306
+ # @param resource [Chef::Resource] The resource to set this property in.
307
+ # @param value The value to set.
308
+ #
309
+ # @return The value that was set, after coercion (if lazy, still returns
310
+ # the lazy value)
311
+ #
312
+ # @raise Chef::Exceptions::ValidationFailed If the value is invalid for
313
+ # this property.
314
+ #
315
+ def set(resource, value)
316
+ unless value.is_a?(DelayedEvaluator)
317
+ value = coerce(resource, value)
318
+ validate(resource, value)
319
+ end
320
+ set_value(resource, value)
321
+ end
322
+
323
+ #
324
+ # Find out whether this property has been set.
325
+ #
326
+ # This will be true if:
327
+ # - The user explicitly set the value
328
+ # - The property has a default, and the value was retrieved.
329
+ #
330
+ # From this point of view, it is worth looking at this as "what does the
331
+ # user think this value should be." In order words, if the user grabbed
332
+ # the value, even if it was a default, they probably based calculations on
333
+ # it. If they based calculations on it and the value changes, the rest of
334
+ # the world gets inconsistent.
335
+ #
336
+ # @param resource [Chef::Resource] The resource to get the property from.
337
+ #
338
+ # @return [Boolean]
339
+ #
340
+ def is_set?(resource)
341
+ value_is_set?(resource)
342
+ end
343
+
344
+ #
345
+ # Reset the value of this property so that is_set? will return false and the
346
+ # default will be returned in the future.
347
+ #
348
+ # @param resource [Chef::Resource] The resource to get the property from.
349
+ #
350
+ def reset(resource)
351
+ reset_value(resource)
352
+ end
353
+
354
+ #
355
+ # Coerce an input value into canonical form for the property.
356
+ #
357
+ # After coercion, the value is suitable for storage in the resource.
358
+ # You must validate values after coercion, however.
359
+ #
360
+ # Does no special handling for lazy values.
361
+ #
362
+ # @param resource [Chef::Resource] The resource we're coercing against
363
+ # (to provide context for the coerce).
364
+ # @param value The value to coerce.
365
+ #
366
+ # @return The coerced value.
367
+ #
368
+ # @raise Chef::Exceptions::ValidationFailed If the value is invalid for
369
+ # this property.
370
+ #
371
+ def coerce(resource, value)
372
+ if options.has_key?(:coerce)
373
+ value = exec_in_resource(resource, options[:coerce], value)
374
+ end
375
+ value
376
+ end
377
+
378
+ #
379
+ # Validate a value.
380
+ #
381
+ # Calls Chef::Mixin::ParamsValidate#validate with #validation_options as
382
+ # options.
383
+ #
384
+ # @param resource [Chef::Resource] The resource we're validating against
385
+ # (to provide context for the validate).
386
+ # @param value The value to validate.
387
+ #
388
+ # @raise Chef::Exceptions::ValidationFailed If the value is invalid for
389
+ # this property.
390
+ #
391
+ def validate(resource, value)
392
+ resource.validate({ name => value }, { name => validation_options })
393
+ end
394
+
395
+ #
396
+ # Derive a new Property that is just like this one, except with some added or
397
+ # changed options.
398
+ #
399
+ # @param options [Hash<Symbol,Object>] List of options that would be passed
400
+ # to #initialize.
401
+ #
402
+ # @return [Property] The new property type.
403
+ #
404
+ def derive(**modified_options)
405
+ Property.new(**options.merge(**modified_options))
406
+ end
407
+
408
+ #
409
+ # Emit the DSL for this property into the resource class (`declared_in`).
410
+ #
411
+ # Creates a getter and setter for the property.
412
+ #
413
+ def emit_dsl
414
+ # We don't create the getter/setter if it's a custom property; we will
415
+ # be using the existing getter/setter to manipulate it instead.
416
+ return if !instance_variable_name
417
+
418
+ # We prefer this form because the property name won't show up in the
419
+ # stack trace if you use `define_method`.
420
+ declared_in.class_eval <<-EOM, __FILE__, __LINE__+1
421
+ def #{name}(value=NOT_PASSED)
422
+ self.class.properties[#{name.inspect}].call(self, value)
423
+ end
424
+ def #{name}=(value)
425
+ self.class.properties[#{name.inspect}].set(self, value)
426
+ end
427
+ EOM
428
+ rescue SyntaxError
429
+ # If the name is not a valid ruby name, we use define_method.
430
+ resource_class.define_method(name) do |value=NOT_PASSED|
431
+ self.class.properties[name].call(self, value)
432
+ end
433
+ resource_class.define_method("#{name}=") do |value|
434
+ self.class.properties[name].set(self, value)
435
+ end
436
+ end
437
+
438
+ protected
439
+
440
+ #
441
+ # The options this Property will use for get/set behavior and validation.
442
+ #
443
+ # @see #initialize for a list of valid options.
444
+ #
445
+ attr_reader :options
446
+
447
+ #
448
+ # Find out whether this type accepts nil explicitly.
449
+ #
450
+ # A type accepts nil explicitly if "is" allows nil, it validates as nil, *and* is not simply
451
+ # an empty type.
452
+ #
453
+ # These examples accept nil explicitly:
454
+ # ```ruby
455
+ # property :a, [ String, nil ]
456
+ # property :a, [ String, NilClass ]
457
+ # property :a, [ String, proc { |v| v.nil? } ]
458
+ # ```
459
+ #
460
+ # This does not (because the "is" doesn't exist or doesn't have nil):
461
+ #
462
+ # ```ruby
463
+ # property :x, String
464
+ # ```
465
+ #
466
+ # These do not, even though nil would validate fine (because they do not
467
+ # have "is"):
468
+ #
469
+ # ```ruby
470
+ # property :a
471
+ # property :a, equal_to: [ 1, 2, 3, nil ]
472
+ # property :a, kind_of: [ String, NilClass ]
473
+ # property :a, respond_to: [ ]
474
+ # property :a, callbacks: { "a" => proc { |v| v.nil? } }
475
+ # ```
476
+ #
477
+ # @param resource [Chef::Resource] The resource we're coercing against
478
+ # (to provide context for the coerce).
479
+ #
480
+ # @return [Boolean] Whether this value explicitly accepts nil.
481
+ #
482
+ # @api private
483
+ def explicitly_accepts_nil?(resource)
484
+ options.has_key?(:is) && resource.send(:_pv_is, { name => nil }, name, options[:is], raise_error: false)
485
+ end
486
+
487
+ def get_value(resource)
488
+ if instance_variable_name
489
+ resource.instance_variable_get(instance_variable_name)
490
+ else
491
+ resource.send(name)
492
+ end
493
+ end
494
+
495
+ def set_value(resource, value)
496
+ if instance_variable_name
497
+ resource.instance_variable_set(instance_variable_name, value)
498
+ else
499
+ resource.send(name, value)
500
+ end
501
+ end
502
+
503
+ def value_is_set?(resource)
504
+ if instance_variable_name
505
+ resource.instance_variable_defined?(instance_variable_name)
506
+ else
507
+ true
508
+ end
509
+ end
510
+
511
+ def reset_value(resource)
512
+ if instance_variable_name
513
+ if value_is_set?(resource)
514
+ resource.remove_instance_variable(instance_variable_name)
515
+ end
516
+ else
517
+ raise ArgumentError, "Property #{name} has no instance variable defined and cannot be reset"
518
+ end
519
+ end
520
+
521
+ def exec_in_resource(resource, proc, *args)
522
+ if resource
523
+ if proc.arity > args.size
524
+ value = proc.call(resource, *args)
525
+ else
526
+ value = resource.instance_exec(*args, &proc)
527
+ end
528
+ else
529
+ value = proc.call
530
+ end
531
+
532
+ if value.is_a?(DelayedEvaluator)
533
+ value = coerce(resource, value)
534
+ validate(resource, value)
535
+ end
536
+ value
537
+ end
538
+ end
539
+ end