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
@@ -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