class Puppet::Property

The Property class is the implementation of a resource's attributes of property kind. A Property is a specialized Resource Type Parameter that has both an 'is' (current) state, and a 'should' (wanted state). However, even if this is conceptually true, the current is value is obtained by asking the associated provider for the value, and hence it is not actually part of a property's state, and only available when a provider has been selected and can obtain the value (i.e. when running on an agent).

A Property (also in contrast to a parameter) is intended to describe a managed attribute of some system entity, such as the name or mode of a file.

The current value _(is)_ is read and written with the methods {#retrieve} and {#set}, and the wanted value _(should)_ is read and written with the methods {#value} and {#value=} which delegate to {#should} and {#should=}, i.e. when a property is used like any other parameter, it is the should value that is operated on.

All resource type properties in the puppet system are derived from this class.

The intention is that new parameters are created by using the DSL method {Puppet::Type.newproperty}.

@abstract @note Properties of Types are expressed using subclasses of this class. Such a class describes one

named property of a particular Type (as opposed to describing a type of property in general). This
limits the use of one (concrete) property class instance to occur only once for a given type's inheritance
chain. An instance of a Property class is the value holder of one instance of the resource type (e.g. the
mode of a file resource instance).
A Property class may server as the superclass _(parent)_ of another; e.g. a Size property that describes
handling of measurements such as kb, mb, gb. If a type requires two different size measurements it requires
one concrete class per such measure; e.g. MinSize (:parent => Size), and MaxSize (:parent => Size).

@see Puppet::Type @see Puppet::Parameter

@api public

Attributes

name[R]

@return [Symbol] The name of the property as given when the property was created.

unmanaged[RW]

@todo Figure out what this is used for. Can not find any logic in the puppet code base that

reads or writes this attribute.

??? Probably Unused

noop[W]

The noop mode for this property. By setting a property's noop mode to `true`, any management of this property is inhibited. Calculation and reporting still takes place, but if a change of the underlying managed entity's state should take place it will not be carried out. This noop setting overrides the overall `Puppet` mode as well as the noop mode in the _associated resource_

shouldorig[R]

Returns the original wanted value(s) _(should)_ unprocessed by munging/unmunging. The original values are set by {#value=} or {#should=}. @return (see should)

Public Class Methods

array_matching() click to toggle source

@!attribute [rw] array_matching @comment note that $#46; is a period - char code require to not terminate sentence. The `is` vs. `should` array matching mode; `:first`, or `:all`.

@comment there are two blank chars after the symbols to cause a break - do not remove these.

  • `:first` This is primarily used for single value properties. When matched against an array of values a match is true if the `is` value matches any of the values in the `should` array. When the `is` value is also an array, the matching is performed against the entire array as the `is` value.

  • `:all` : This is primarily used for multi-valued properties. When matched against an array of

    `should` values, the size of `is` and `should` must be the same, and all values in `is` must match
    a value in `should`.

@note The semantics of these modes are implemented by the method {#insync?}. That method is the default

implementation and it has a backwards compatible behavior that imposes additional constraints
on what constitutes a positive match. A derived property may override that method.

@return [Symbol] (:first) the mode in which matching is performed @see insync? @dsl type @api public

   # File lib/puppet/property.rb
90 def array_matching
91   @array_matching ||= :first
92 end
array_matching=(value) click to toggle source

@comment This is documented as an attribute - see the {array_matching} method.

    # File lib/puppet/property.rb
 96 def array_matching=(value)
 97   value = value.intern if value.is_a?(String)
 98   #TRANSLATORS 'Property#array_matching', 'first', and 'all' should not be translated
 99   raise ArgumentError, _("Supported values for Property#array_matching are 'first' and 'all'") unless [:first, :all].include?(value)
100   @array_matching = value
101 end
idempotent() click to toggle source

Used to mark a type property as having or lacking idempotency (on purpose generally). This is used to avoid marking the property as a corrective_change when there is known idempotency issues with the property rendering a corrective_change flag as useless. @return [Boolean] true if the property is marked as idempotent

    # File lib/puppet/property.rb
108 def idempotent
109   @idempotent.nil? ? @idempotent = true : @idempotent
110 end
idempotent=(value) click to toggle source

Attribute setter for the idempotent attribute. @param [bool] value boolean indicating if the property is idempotent. @see idempotent

    # File lib/puppet/property.rb
115 def idempotent=(value)
116   @idempotent = value
117 end
method_added(sym) click to toggle source

Protects against override of the {#safe_insync?} method. @raise [RuntimeError] if the added method is `:safe_insync?` @api private

    # File lib/puppet/property.rb
283 def self.method_added(sym)
284   raise "Puppet::Property#safe_insync? shouldn't be overridden; please override insync? instead" if sym == :safe_insync?
285 end
newvalue(name, options = {}, &block) click to toggle source

Defines a new valid value for this property. A valid value is specified as a literal (typically a Symbol), but can also be specified with a Regexp.

@param name [Symbol, Regexp] a valid literal value, or a regexp that matches a value @param options [Hash] a hash with options @option options [Symbol] :event The event that should be emitted when this value is set. @todo Option :event original comment says “event should be returned…”, is “returned” the correct word

to use?

@option options [Symbol] :invalidate_refreshes Indicates a change on this property should invalidate and

remove any scheduled refreshes (from notify or subscribe) targeted at the same resource. For example, if
a change in this property takes into account any changes that a scheduled refresh would have performed,
then the scheduled refresh would be deleted.

@option options [Object] any Any other option is treated as a call to a setter having the given

option name (e.g. `:required_features` calls `required_features=` with the option's value as an
argument).

@dsl type @api public

    # File lib/puppet/property.rb
162 def self.newvalue(name, options = {}, &block)
163   value = value_collection.newvalue(name, options, &block)
164 
165   unless value.method.nil?
166     method = value.method.to_sym
167     if value.block
168       if instance_methods(false).include?(method)
169         raise ArgumentError, _("Attempt to redefine method %{method} with block") % { method: method }
170       end
171       define_method(method, &value.block)
172     else
173       # Let the method be an alias for calling the providers setter unless we already have this method
174       alias_method(method, :call_provider) unless method_defined?(method)
175     end
176   end
177   value
178 end
value_name(name) click to toggle source

Looks up a value's name among valid values, to enable option lookup with result as a key. @param name [Object] the parameter value to match against valid values (names). @return {Symbol, Regexp} a value matching predicate @api private

    # File lib/puppet/property.rb
125 def self.value_name(name)
126   value = value_collection.match?(name)
127   value.name if value
128 end
value_option(name, option) click to toggle source

Returns the value of the given option (set when a valid value with the given “name” was defined). @param name [Symbol, Regexp] the valid value predicate as returned by {value_name} @param option [Symbol] the name of the wanted option @return [Object] value of the option @raise [NoMethodError] if the option is not supported @todo Guessing on result of passing a non supported option (it performs send(option)). @api private

    # File lib/puppet/property.rb
138 def self.value_option(name, option)
139   value = value_collection.value(name)
140   value.send(option) if value
141 end

Public Instance Methods

call_provider(value) click to toggle source

Calls the provider setter method for this property with the given value as argument. @return [Object] what the provider returns when calling a setter for this property's name @raise [Puppet::Error] when the provider can not handle this property. @see set @api private

    # File lib/puppet/property.rb
186 def call_provider(value)
187   # We have no idea how to handle this unless our parent have a provider
188   self.fail "#{self.class.name} cannot handle values of type #{value.inspect}" unless @resource.provider
189   method = self.class.name.to_s + "="
190   unless provider.respond_to? method
191     self.fail "The #{provider.class.name} provider can not handle attribute #{self.class.name}"
192   end
193   provider.send(method, value)
194 end
change_to_s(current_value, newvalue) click to toggle source

Formats a message for a property change from the given `current_value` to the given `newvalue`. @return [String] a message describing the property change. @note If called with equal values, this is reported as a change. @raise [Puppet::DevError] if there were issues formatting the message

    # File lib/puppet/property.rb
201 def change_to_s(current_value, newvalue)
202   begin
203     if current_value == :absent
204       return "defined '#{name}' as #{should_to_s(newvalue)}"
205     elsif newvalue == :absent or newvalue == [:absent]
206       return "undefined '#{name}' from #{is_to_s(current_value)}"
207     else
208       return "#{name} changed #{is_to_s(current_value)} to #{should_to_s(newvalue)}"
209     end
210   rescue Puppet::Error
211     raise
212   rescue => detail
213     message = _("Could not convert change '%{name}' to string: %{detail}") % { name: name, detail: detail }
214     Puppet.log_exception(detail, message)
215     raise Puppet::DevError, message, detail.backtrace
216   end
217 end
event(options = {}) click to toggle source

Produces an event describing a change of this property. In addition to the event attributes set by the resource type, this method adds:

  • `:name` - the event_name

  • `:desired_value` - a.k.a should or _wanted value_

  • `:property` - reference to this property

  • `:source_description` - The containment path of this property, indicating what resource this

    property is associated with and in what stage and class that resource
    was declared, e.g. "/Stage[main]/Myclass/File[/tmp/example]/ensure"
  • `:invalidate_refreshes` - if scheduled refreshes should be invalidated

  • `:redacted` - if the event will be redacted (due to this property being sensitive)

@return [Puppet::Transaction::Event] the created event @see Puppet::Type#event

    # File lib/puppet/property.rb
254 def event(options = {})
255   attrs = { :name => event_name, :desired_value => should, :property => self, :source_description => path }.merge(options)
256   value = self.class.value_collection.match?(should) if should
257   
258   attrs[:invalidate_refreshes] = true if value && value.invalidate_refreshes 
259   attrs[:redacted] = @sensitive
260   resource.event attrs
261 end
event_name() click to toggle source

Produces the name of the event to use to describe a change of this property's value. The produced event name is either the event name configured for this property, or a generic event based on the name of the property with suffix `_changed`, or if the property is `:ensure`, the name of the resource type and one of the suffixes `_created`, `_removed`, or `_changed`. @return [String] the name of the event that describes the change

    # File lib/puppet/property.rb
225 def event_name
226   value = self.should
227 
228   event_name = self.class.value_option(value, :event) and return event_name
229 
230   name == :ensure or return (name.to_s + "_changed").to_sym
231 
232   return (resource.type.to_s + case value
233   when :present; "_created"
234   when :absent; "_removed"
235   else
236     "_changed"
237   end).to_sym
238 end
idempotent?() click to toggle source

@return [Boolean] whether the property is marked as idempotent for the purposes

of calculating corrective change.
    # File lib/puppet/property.rb
429 def idempotent?
430   self.class.idempotent
431 end
insync?(is) click to toggle source

Checks if the current _(is)_ value is in sync with the wanted _(should)_ value. The check if the two values are in sync is controlled by the result of {#match_all?} which specifies a match of `:first` or `:all`). The matching of the is value against the entire should value or each of the should values (as controlled by {#match_all?} is performed by {#property_matches?}.

A derived property typically only needs to override the {#property_matches?} method, but may also override this method if there is a need to have more control over the array matching logic.

@note The array matching logic in this method contains backwards compatible logic that performs the

comparison in `:all` mode by checking equality and equality of _is_ against _should_ converted to array of String,
and that the lengths are equal, and in `:first` mode by checking if one of the _should_ values
is included in the _is_ values. This means that the _is_ value needs to be carefully arranged to
match the _should_.

@todo The implementation should really do return is.zip(@should).all? {|a, b| property_matches?(a, b) }

instead of using equality check and then check against an array with converted strings.

@param is [Object] The current _(is)_ value to check if it is in sync with the wanted _(should)_ value(s) @return [Boolean] whether the values are in sync or not. @raise [Puppet::DevError] if wanted value _(should)_ is not an array. @api public

    # File lib/puppet/property.rb
307 def insync?(is)
308   self.devfail "#{self.class.name}'s should is not array" unless @should.is_a?(Array)
309 
310   # an empty array is analogous to no should values
311   return true if @should.empty?
312 
313   # Look for a matching value, either for all the @should values, or any of
314   # them, depending on the configuration of this property.
315   if match_all? then
316     # Emulate Array#== using our own comparison function.
317     # A non-array was not equal to an array, which @should always is.
318     return false unless is.is_a? Array
319 
320     # If they were different lengths, they are not equal.
321     return false unless is.length == @should.length
322 
323     # Finally, are all the elements equal?  In order to preserve the
324     # behaviour of previous 2.7.x releases, we need to impose some fun rules
325     # on "equality" here.
326     #
327     # Specifically, we need to implement *this* comparison: the two arrays
328     # are identical if the is values are == the should values, or if the is
329     # values are == the should values, stringified.
330     #
331     # This does mean that property equality is not commutative, and will not
332     # work unless the `is` value is carefully arranged to match the should.
333     return (is == @should or is == @should.map(&:to_s))
334 
335     # When we stop being idiots about this, and actually have meaningful
336     # semantics, this version is the thing we actually want to do.
337     #
338     # return is.zip(@should).all? {|a, b| property_matches?(a, b) }
339   else
340     return @should.any? {|want| property_matches?(is, want) }
341   end
342 end
insync_values?(should, is) click to toggle source

This method tests if two values are insync? outside of the properties current should value. This works around the requirement for corrective_change analysis that requires two older values to be compared with the properties potentially custom insync? code.

@param [Object] should the value it should be @param [Object] is the value it is @return [Boolean] whether or not the values are in sync or not @api private

    # File lib/puppet/property.rb
353 def insync_values?(should, is)
354   # Here be dragons. We're setting the should value of a property purely just to
355   # call its insync? method, as it lacks a way to pass in a should.
356   # Unfortunately there isn't an API compatible way of avoiding this, as both should
357   # an insync? behaviours are part of the public API. Future API work should factor
358   # this kind of arbitrary comparisons into the API to remove this complexity. -ken
359 
360   # Backup old should, set it to the new value, then call insync? on the property.
361   old_should = @should
362 
363   begin
364     @should = should
365     insync?(is)
366   rescue
367     # Certain operations may fail, but we don't want to fail the transaction if we can
368     # avoid it
369     #TRANSLATORS 'insync_values?' should not be translated
370     msg = _("Unknown failure using insync_values? on type: %{type} / property: %{name} to compare values %{should} and %{is}") %
371         { type: self.resource.ref, name: self.name, should: should, is: is }
372     Puppet.info(msg)
373 
374     # Return nil, ie. unknown
375     nil
376   ensure
377     # Always restore old should
378     @should = old_should
379   end
380 end
is_to_s(value) click to toggle source

Produces a pretty printing string for the given value. This default implementation calls {#format_value_for_display} on the class. A derived implementation may perform property specific pretty printing when the is values are not already in suitable form. @param value [Object] the value to format as a string @return [String] a pretty printing string

    # File lib/puppet/property.rb
405 def is_to_s(value)
406   self.class.format_value_for_display(value)
407 end
log(msg) click to toggle source

Emits a log message at the log level specified for the associated resource. The log entry is associated with this property. @param msg [String] the message to log @return [void]

    # File lib/puppet/property.rb
414 def log(msg)
415   Puppet::Util::Log.create(
416     :level   => resource[:loglevel],
417     :message => msg,
418     :source  => self
419   )
420 end
match_all?() click to toggle source

@return [Boolean] whether the {array_matching} mode is set to `:all` or not

    # File lib/puppet/property.rb
423 def match_all?
424   self.class.array_matching == :all
425 end
name() click to toggle source

@return [Symbol] the name of the property as stated when the property was created. @note A property class (just like a parameter class) describes one specific property and

can only be used once within one type's inheritance chain.
    # File lib/puppet/property.rb
436 def name
437   self.class.name
438 end
noop() click to toggle source

@return [Boolean] whether this property is in noop mode or not. Returns whether this property is in noop mode or not; if a difference between the is and should values should be acted on or not. The noop mode is a transitive setting. The mode is checked in this property, then in the _associated resource_ and finally in Puppet. @todo This logic is different than Parameter#noop in that the resource noop mode overrides

the property's mode - in parameter it is the other way around. Bug or feature?
    # File lib/puppet/property.rb
448 def noop
449   # This is only here to make testing easier.
450   if @resource.respond_to?(:noop?)
451     @resource.noop?
452   else
453     if defined?(@noop)
454       @noop
455     else
456       Puppet[:noop]
457     end
458   end
459 end
property_matches?(current, desired) click to toggle source

Checks if the given current and desired values are equal. This default implementation performs this check in a backwards compatible way where the equality of the two values is checked, and then the equality of current with desired converted to a string.

A derived implementation may override this method to perform a property specific equality check.

The intent of this method is to provide an equality check suitable for checking if the property value is in sync or not. It is typically called from {#insync?}.

    # File lib/puppet/property.rb
392 def property_matches?(current, desired)
393   # This preserves the older Puppet behaviour of doing raw and string
394   # equality comparisons for all equality.  I am not clear this is globally
395   # desirable, but at least it is not a breaking change. --daniel 2011-11-11
396   current == desired or current == desired.to_s
397 end
retrieve() click to toggle source

Retrieves the current value _(is)_ of this property from the provider. This implementation performs this operation by calling a provider method with the same name as this property (i.e. if the property name is 'gid', a call to the 'provider.gid' is expected to return the current value. @return [Object] what the provider returns as the current value of the property

    # File lib/puppet/property.rb
467 def retrieve
468   provider.send(self.class.name)
469 end
safe_insync?(is) click to toggle source

Determines whether the property is in-sync or not in a way that is protected against missing value. @note If the wanted value _(should)_ is not defined or is set to a non-true value then this is

a state that can not be fixed and the property is reported to be in sync.

@return [Boolean] the protected result of `true` or the result of calling {#insync?}.

@api private @note Do not override this method.

    # File lib/puppet/property.rb
271 def safe_insync?(is)
272   # If there is no @should value, consider the property to be in sync.
273   return true unless @should
274 
275   # Otherwise delegate to the (possibly derived) insync? method.
276   insync?(is)
277 end
set(value) click to toggle source

Sets the current _(is)_ value of this property. The name associated with the value is first obtained by calling {value_name}. A dynamically created setter method associated with this name is called if it exists, otherwise the value is set using using the provider's setter method for this property by calling ({#call_provider}).

@param value [Object] the value to set @return [Object] returns the result of calling the setter method or {#call_provider} @raise [Puppet::Error] if there were problems setting the value using the setter method or when the provider

setter should be used but there is no provider in the associated resource_

@raise [Puppet::ResourceError] if there was a problem setting the value and it was not raised

as a Puppet::Error. The original exception is wrapped and logged.

@api public

    # File lib/puppet/property.rb
484 def set(value)
485   # Set a name for looking up associated options like the event.
486   name = self.class.value_name(value)
487   method = self.class.value_option(name, :method)
488   if method && self.respond_to?(method)
489     begin
490       self.send(method)
491     rescue Puppet::Error
492       raise
493     rescue => detail
494       error = Puppet::ResourceError.new(_("Could not set '%{value}' on %{class_name}: %{detail}") %
495                                             { value: value, class_name: self.class.name, detail: detail }, @resource.file, @resource.line, detail)
496       error.set_backtrace detail.backtrace
497       Puppet.log_exception(detail, error.message)
498       raise error
499     end
500   else
501     block = self.class.value_option(name, :block)
502     if block
503       # FIXME It'd be better here to define a method, so that
504       # the blocks could return values.
505       self.instance_eval(&block)
506     else
507       call_provider(value)
508     end
509   end
510 end
should() click to toggle source

Returns the wanted _(should)_ value of this property. If the _array matching mode_ {#match_all?} is true, an array of the wanted values in unmunged format is returned, else the first value in the array of wanted values in unmunged format is returned. @return [Array<Object>, Object, nil] Array of values if {#match_all?} else a single value, or nil if there are no

wanted values.

@raise [Puppet::DevError] if the wanted value is non nil and not an array

@note This method will potentially return different values than the original values as they are

converted via munging/unmunging. If the original values are wanted, call {#shouldorig}.

@see shouldorig @api public

    # File lib/puppet/property.rb
525 def should
526   return nil unless defined?(@should)
527 
528   self.devfail "should for #{self.class.name} on #{resource.name} is not an array" unless @should.is_a?(Array)
529 
530   if match_all?
531     return @should.collect { |val| self.unmunge(val) }
532   else
533     return self.unmunge(@should[0])
534   end
535 end
should=(values) click to toggle source

Sets the wanted _(should)_ value of this property. If the given value is not already an Array, it will be wrapped in one before being set. This method also sets the cached original should values returned by {#shouldorig}.

@param values [Array<Object>, Object] the value(s) to set as the wanted value(s) @raise [StandardError] when validation of a value fails (see {#validate}). @api public

    # File lib/puppet/property.rb
545 def should=(values)
546   values = [values] unless values.is_a?(Array)
547 
548   @shouldorig = values
549 
550   values.each { |val| validate(val) }
551   @should = values.collect { |val| self.munge(val) }
552 end
should_to_s(value) click to toggle source

Produces a pretty printing string for the given value. This default implementation calls {#format_value_for_display} on the class. A derived implementation may perform property specific pretty printing when the should values are not already in suitable form. @param value [Object] the value to format as a string @return [String] a pretty printing string

    # File lib/puppet/property.rb
560 def should_to_s(value)
561   self.class.format_value_for_display(value)
562 end
sync() click to toggle source

Synchronizes the current value _(is)_ and the wanted value _(should)_ by calling {#set}. @raise [Puppet::DevError] if {#should} is nil @todo The implementation of this method is somewhat inefficient as it computes the should

array twice.
    # File lib/puppet/property.rb
568 def sync
569   devfail "Got a nil value for should" unless should
570   set(should)
571 end
unsafe_validate(value) click to toggle source

Asserts that the given value is valid. If the developer uses a 'validate' hook, this method will get overridden. @raise [Exception] if the value is invalid, or value can not be handled. @return [void] @api private

Calls superclass method Puppet::Parameter#unsafe_validate
    # File lib/puppet/property.rb
579 def unsafe_validate(value)
580   super
581   validate_features_per_value(value)
582 end
validate_features_per_value(value) click to toggle source

Asserts that all required provider features are present for the given property value. @raise [ArgumentError] if a required feature is not present @return [void] @api private

    # File lib/puppet/property.rb
589 def validate_features_per_value(value)
590   features = self.class.value_option(self.class.value_name(value), :required_features)
591   if features
592     features = Array(features)
593     needed_features = features.collect { |f| f.to_s }.join(", ")
594     unless provider.satisfies?(features)
595       #TRANSLATORS 'Provider' refers to a Puppet provider class
596       raise ArgumentError, _("Provider %{provider} must have features '%{needed_features}' to set '%{property}' to '%{value}'") %
597           { provider: provider.class.name, needed_features: needed_features, property: self.class.name, value: value }
598     end
599   end
600 end
value() click to toggle source

@return [Object, nil] Returns the wanted _(should)_ value of this property.

    # File lib/puppet/property.rb
603 def value
604   self.should
605 end
value=(values) click to toggle source

(see should=)

    # File lib/puppet/property.rb
608 def value=(values)
609   self.should = values
610 end