class Transaction::ResourceHarness

Constants

NO_ACTION
ResourceApplicationContext

@api private

Attributes

transaction[R]

Public Class Methods

new(transaction) click to toggle source
   # File lib/puppet/transaction/resource_harness.rb
12 def initialize(transaction)
13   @transaction = transaction
14   @persistence = transaction.persistence
15 end

Public Instance Methods

cache(resource, name, value) click to toggle source

Used mostly for scheduling and auditing at this point.

   # File lib/puppet/transaction/resource_harness.rb
67 def cache(resource, name, value)
68   Puppet::Util::Storage.cache(resource)[name] = value
69 end
cached(resource, name) click to toggle source

Used mostly for scheduling and auditing at this point.

   # File lib/puppet/transaction/resource_harness.rb
62 def cached(resource, name)
63   Puppet::Util::Storage.cache(resource)[name]
64 end
evaluate(resource) click to toggle source
   # File lib/puppet/transaction/resource_harness.rb
17 def evaluate(resource)
18   status = Puppet::Resource::Status.new(resource)
19 
20   begin
21     context = ResourceApplicationContext.from_resource(resource, status)
22     perform_changes(resource, context)
23 
24     if status.changed? && ! resource.noop?
25       cache(resource, :synced, Time.now)
26       resource.flush if resource.respond_to?(:flush)
27     end
28   rescue => detail
29     status.failed_because(detail)
30   ensure
31     status.evaluation_time = Time.now - status.time
32   end
33 
34   status
35 end
schedule(resource) click to toggle source
   # File lib/puppet/transaction/resource_harness.rb
50 def schedule(resource)
51   unless resource.catalog
52     resource.warning _("Cannot schedule without a schedule-containing catalog")
53     return nil
54   end
55 
56   name = resource[:schedule]
57   return nil unless name
58   resource.catalog.resource(:schedule, name) || resource.fail(_("Could not find schedule %{name}") % { name: name })
59 end
scheduled?(resource) click to toggle source
   # File lib/puppet/transaction/resource_harness.rb
37 def scheduled?(resource)
38   return true if Puppet[:ignoreschedules]
39   schedule = schedule(resource)
40   return true unless schedule
41 
42   # We use 'checked' here instead of 'synced' because otherwise we'll
43   # end up checking most resources most times, because they will generally
44   # have been synced a long time ago (e.g., a file only gets updated
45   # once a month on the server and its schedule is daily; the last sync time
46   # will have been a month ago, so we'd end up checking every run).
47   schedule.match?(cached(resource, :checked).to_i)
48 end

Private Instance Methods

are_audited_values_equal(a, b) click to toggle source

This method is an ugly hack because, given a Time object with nanosecond resolution, roundtripped through YAML serialization, the Time object will be truncated to microseconds. For audit purposes, this code special cases this comparison, and compares the two objects by their second and microsecond components. tv_sec is the number of seconds since the epoch, and tv_usec is only the microsecond portion of time.

    # File lib/puppet/transaction/resource_harness.rb
199 def are_audited_values_equal(a, b)
200   a == b || (a.is_a?(Time) && b.is_a?(Time) && a.tv_sec == b.tv_sec && a.tv_usec == b.tv_usec)
201 end
audit_event(event, property, context) click to toggle source

Populate an existing event with audit information.

@param event [Puppet::Transaction::Event] The event to be populated. @param property [Puppet::Property] The property being audited. @param context [ResourceApplicationContext]

@return [Puppet::Transaction::Event] The given event, populated with the audit information.

    # File lib/puppet/transaction/resource_harness.rb
211 def audit_event(event, property, context)
212   event.audited = true
213   event.status = "audit"
214 
215   # The event we've been provided might have been redacted so we need to use the state stored within
216   # the resource application context to see if an event was actually generated.
217   if !are_audited_values_equal(context.historical_values[property.name], context.current_values[property.name])
218     event.message = property.format(_("audit change: previously recorded value %s has been changed to %s"),
219                property.is_to_s(event.historical_value),
220                property.is_to_s(event.previous_value))
221   end
222 
223   event
224 end
audit_message(param, do_audit, historical_value, current_value) click to toggle source
    # File lib/puppet/transaction/resource_harness.rb
226 def audit_message(param, do_audit, historical_value, current_value)
227   if do_audit && historical_value && !are_audited_values_equal(historical_value, current_value)
228     param.format(_(" (previously recorded value was %s)"), param.is_to_s(historical_value))
229   else
230     ""
231   end
232 end
capture_audit_events(resource, context) click to toggle source
    # File lib/puppet/transaction/resource_harness.rb
253 def capture_audit_events(resource, context)
254   context.audited_params.each do |param_name|
255     if context.historical_values.include?(param_name)
256       if !are_audited_values_equal(context.historical_values[param_name], context.current_values[param_name]) && !context.synced_params.include?(param_name)
257         parameter = resource.parameter(param_name)
258         event = audit_event(create_change_event(parameter,
259                                                 context.current_values[param_name],
260                                                 context.historical_values[param_name]),
261                             parameter, context)
262         event.send_log
263         context.record(event)
264       end
265     else
266       property = resource.property(param_name)
267       property.notice(property.format(_("audit change: newly-recorded value %s"), context.current_values[param_name]))
268     end
269   end
270 end
create_change_event(property, current_value, historical_value) click to toggle source
    # File lib/puppet/transaction/resource_harness.rb
175 def create_change_event(property, current_value, historical_value)
176   options = {}
177   should = property.should
178 
179   if property.sensitive
180     options[:previous_value] = current_value.nil? ? nil : '[redacted]'
181     options[:desired_value] = should.nil? ? nil : '[redacted]'
182     options[:historical_value] = historical_value.nil? ? nil : '[redacted]'
183   else
184     options[:previous_value] = current_value
185     options[:desired_value] = should
186     options[:historical_value] = historical_value
187   end
188 
189   property.event(options)
190 end
new_system_value(property, event, old_system_value) click to toggle source

Given an event and its property, calculate the system_value to persist for future calculations. @param [Puppet::Transaction::Event] event event to use for processing @param [Puppet::Property] property correlating property @param [Object] old_system_value system_value from last transaction @return [Object] system_value to be used for next transaction

    # File lib/puppet/transaction/resource_harness.rb
278 def new_system_value(property, event, old_system_value)
279   if event && event.status != "success"
280     # For non-success events, we persist the old_system_value if it is defined,
281     # or use the event previous_value.
282     # If we're using the event previous_value, we ensure that it's
283     # an array. This is needed because properties assume that their
284     # `should` value is an array, and we will use this value later
285     # on in property insync? logic.
286     event_value = [event.previous_value] unless event.previous_value.is_a?(Array)
287     old_system_value.nil? ? event_value : old_system_value
288   else
289     # For non events, or for success cases, we just want to store
290     # the parameters agent value.
291     # We use instance_variable_get here because we want this process to bypass any
292     # munging/unmunging or validation that the property might try to do, since those
293     # operations may not be correctly implemented for custom types.
294     property.instance_variable_get(:@should)
295   end
296 end
noop(event, param, current_value, audit_message) click to toggle source
    # File lib/puppet/transaction/resource_harness.rb
234 def noop(event, param, current_value, audit_message)
235   event.message = param.format(_("current_value %s, should be %s (noop)"),
236                                param.is_to_s(current_value),
237                                param.should_to_s(param.should)) + audit_message.to_s
238   event.status = "noop"
239 end
perform_changes(resource, context) click to toggle source
    # File lib/puppet/transaction/resource_harness.rb
 73 def perform_changes(resource, context)
 74   cache(resource, :checked, Time.now)
 75 
 76   # Record the current state in state.yml.
 77   context.audited_params.each do |param|
 78     cache(resource, param, context.current_values[param])
 79   end
 80 
 81   ensure_param = resource.parameter(:ensure)
 82   if ensure_param && ensure_param.should
 83     ensure_event = sync_if_needed(ensure_param, context)
 84   else
 85     ensure_event = NO_ACTION
 86   end
 87 
 88   if ensure_event == NO_ACTION
 89     if context.resource_present?
 90       resource.properties.each do |param|
 91         sync_if_needed(param, context)
 92       end
 93     else
 94       resource.debug("Nothing to manage: no ensure and the resource doesn't exist")
 95     end
 96   end
 97 
 98   capture_audit_events(resource, context)
 99   persist_system_values(resource, context)
100 end
persist_system_values(resource, context) click to toggle source

We persist the last known values for the properties of a resource after resource application. @param [Puppet::Type] resource resource whose values we are to persist. @param [ResourceApplicationContext] context the application context to operate on.

    # File lib/puppet/transaction/resource_harness.rb
106 def persist_system_values(resource, context)
107   param_to_event = {}
108   context.status.events.each do |ev|
109     param_to_event[ev.property] = ev
110   end
111 
112   context.system_value_params.each do |pname, param|
113     @persistence.set_system_value(resource.ref, pname.to_s,
114                                   new_system_value(param,
115                                                    param_to_event[pname.to_s],
116                                                    @persistence.get_system_value(resource.ref, pname.to_s)))
117   end
118 end
sync(event, param, current_value, audit_message) click to toggle source
    # File lib/puppet/transaction/resource_harness.rb
241 def sync(event, param, current_value, audit_message)
242   param.sync
243   if param.sensitive
244     event.message = param.format(_("changed %s to %s"),
245                                  param.is_to_s(current_value),
246                                  param.should_to_s(param.should)) + audit_message.to_s
247   else
248     event.message = "#{param.change_to_s(current_value, param.should)}#{audit_message}"
249   end
250   event.status = "success"
251 end
sync_if_needed(param, context) click to toggle source
    # File lib/puppet/transaction/resource_harness.rb
120 def sync_if_needed(param, context)
121   historical_value = context.historical_values[param.name]
122   current_value = context.current_values[param.name]
123   do_audit = context.audited_params.include?(param.name)
124 
125   begin
126     if param.should && !param.safe_insync?(current_value)
127       event = create_change_event(param, current_value, historical_value)
128       if do_audit
129         event = audit_event(event, param, context)
130       end
131 
132       brief_audit_message = audit_message(param, do_audit, historical_value, current_value)
133 
134       if param.noop
135         noop(event, param, current_value, brief_audit_message)
136       else
137         sync(event, param, current_value, brief_audit_message)
138       end
139 
140       event
141     else
142       NO_ACTION
143     end
144   rescue => detail
145     # Execution will continue on StandardErrors, just store the event
146     Puppet.log_exception(detail)
147 
148     event = create_change_event(param, current_value, historical_value)
149     event.status = "failure"
150     event.message = param.format(_("change from %s to %s failed: "),
151                                  param.is_to_s(current_value),
152                                  param.should_to_s(param.should)) + detail.to_s
153     event
154   rescue Exception => detail
155     # Execution will halt on Exceptions, they get raised to the application
156     event = create_change_event(param, current_value, historical_value)
157     event.status = "failure"
158     event.message = param.format(_("change from %s to %s failed: "),
159                                  param.is_to_s(current_value),
160                                  param.should_to_s(param.should)) + detail.to_s
161     raise
162   ensure
163     if event
164       name = param.name.to_s
165       event.message ||= _("could not create change error message for %{name}") % { name: name }
166       event.calculate_corrective_change(@persistence.get_system_value(context.resource.ref, name))
167       event.message << ' (corrective)' if event.corrective_change
168       context.record(event)
169       event.send_log
170       context.synced_params << param.name
171     end
172   end
173 end