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
11 def initialize(transaction)
12   @transaction = transaction
13   @persistence = transaction.persistence
14 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
66 def cache(resource, name, value)
67   Puppet::Util::Storage.cache(resource)[name] = value
68 end
cached(resource, name) click to toggle source

Used mostly for scheduling and auditing at this point.

   # File lib/puppet/transaction/resource_harness.rb
61 def cached(resource, name)
62   Puppet::Util::Storage.cache(resource)[name]
63 end
evaluate(resource) click to toggle source
   # File lib/puppet/transaction/resource_harness.rb
16 def evaluate(resource)
17   status = Puppet::Resource::Status.new(resource)
18 
19   begin
20     context = ResourceApplicationContext.from_resource(resource, status)
21     perform_changes(resource, context)
22 
23     if status.changed? && ! resource.noop?
24       cache(resource, :synced, Time.now)
25       resource.flush if resource.respond_to?(:flush)
26     end
27   rescue => detail
28     status.failed_because(detail)
29   ensure
30     status.evaluation_time = Time.now - status.time
31   end
32 
33   status
34 end
schedule(resource) click to toggle source
   # File lib/puppet/transaction/resource_harness.rb
49 def schedule(resource)
50   unless resource.catalog
51     resource.warning _("Cannot schedule without a schedule-containing catalog")
52     return nil
53   end
54 
55   name = resource[:schedule]
56   return nil unless name
57   resource.catalog.resource(:schedule, name) || resource.fail(_("Could not find schedule %{name}") % { name: name })
58 end
scheduled?(resource) click to toggle source
   # File lib/puppet/transaction/resource_harness.rb
36 def scheduled?(resource)
37   return true if Puppet[:ignoreschedules]
38   schedule = schedule(resource)
39   return true unless schedule
40 
41   # We use 'checked' here instead of 'synced' because otherwise we'll
42   # end up checking most resources most times, because they will generally
43   # have been synced a long time ago (e.g., a file only gets updated
44   # once a month on the server and its schedule is daily; the last sync time
45   # will have been a month ago, so we'd end up checking every run).
46   schedule.match?(cached(resource, :checked).to_i)
47 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
198 def are_audited_values_equal(a, b)
199   a == b || (a.is_a?(Time) && b.is_a?(Time) && a.tv_sec == b.tv_sec && a.tv_usec == b.tv_usec)
200 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
210 def audit_event(event, property, context)
211   event.audited = true
212   event.status = "audit"
213 
214   # The event we've been provided might have been redacted so we need to use the state stored within
215   # the resource application context to see if an event was actually generated.
216   if !are_audited_values_equal(context.historical_values[property.name], context.current_values[property.name])
217     event.message = property.format(_("audit change: previously recorded value %s has been changed to %s"),
218                property.is_to_s(event.historical_value),
219                property.is_to_s(event.previous_value))
220   end
221 
222   event
223 end
audit_message(param, do_audit, historical_value, current_value) click to toggle source
    # File lib/puppet/transaction/resource_harness.rb
225 def audit_message(param, do_audit, historical_value, current_value)
226   if do_audit && historical_value && !are_audited_values_equal(historical_value, current_value)
227     param.format(_(" (previously recorded value was %s)"), param.is_to_s(historical_value))
228   else
229     ""
230   end
231 end
capture_audit_events(resource, context) click to toggle source
    # File lib/puppet/transaction/resource_harness.rb
252 def capture_audit_events(resource, context)
253   context.audited_params.each do |param_name|
254     if context.historical_values.include?(param_name)
255       if !are_audited_values_equal(context.historical_values[param_name], context.current_values[param_name]) && !context.synced_params.include?(param_name)
256         parameter = resource.parameter(param_name)
257         event = audit_event(create_change_event(parameter,
258                                                 context.current_values[param_name],
259                                                 context.historical_values[param_name]),
260                             parameter, context)
261         event.send_log
262         context.record(event)
263       end
264     else
265       property = resource.property(param_name)
266       property.notice(property.format(_("audit change: newly-recorded value %s"), context.current_values[param_name]))
267     end
268   end
269 end
create_change_event(property, current_value, historical_value) click to toggle source
    # File lib/puppet/transaction/resource_harness.rb
174 def create_change_event(property, current_value, historical_value)
175   options = {}
176   should = property.should
177 
178   if property.sensitive
179     options[:previous_value] = current_value.nil? ? nil : '[redacted]'
180     options[:desired_value] = should.nil? ? nil : '[redacted]'
181     options[:historical_value] = historical_value.nil? ? nil : '[redacted]'
182   else
183     options[:previous_value] = current_value
184     options[:desired_value] = should
185     options[:historical_value] = historical_value
186   end
187 
188   property.event(options)
189 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
277 def new_system_value(property, event, old_system_value)
278   if event && event.status != "success"
279     # For non-success events, we persist the old_system_value if it is defined,
280     # or use the event previous_value.
281     # If we're using the event previous_value, we ensure that it's
282     # an array. This is needed because properties assume that their
283     # `should` value is an array, and we will use this value later
284     # on in property insync? logic.
285     event_value = [event.previous_value] unless event.previous_value.is_a?(Array)
286     old_system_value.nil? ? event_value : old_system_value
287   else
288     # For non events, or for success cases, we just want to store
289     # the parameters agent value.
290     # We use instance_variable_get here because we want this process to bypass any
291     # munging/unmunging or validation that the property might try to do, since those
292     # operations may not be correctly implemented for custom types.
293     property.instance_variable_get(:@should)
294   end
295 end
noop(event, param, current_value, audit_message) click to toggle source
    # File lib/puppet/transaction/resource_harness.rb
233 def noop(event, param, current_value, audit_message)
234   event.message = param.format(_("current_value %s, should be %s (noop)"),
235                                param.is_to_s(current_value),
236                                param.should_to_s(param.should)) + audit_message.to_s
237   event.status = "noop"
238 end
perform_changes(resource, context) click to toggle source
   # File lib/puppet/transaction/resource_harness.rb
72 def perform_changes(resource, context)
73   cache(resource, :checked, Time.now)
74 
75   # Record the current state in state.yml.
76   context.audited_params.each do |param|
77     cache(resource, param, context.current_values[param])
78   end
79 
80   ensure_param = resource.parameter(:ensure)
81   if ensure_param && ensure_param.should
82     ensure_event = sync_if_needed(ensure_param, context)
83   else
84     ensure_event = NO_ACTION
85   end
86 
87   if ensure_event == NO_ACTION
88     if context.resource_present?
89       resource.properties.each do |param|
90         sync_if_needed(param, context)
91       end
92     else
93       resource.debug("Nothing to manage: no ensure and the resource doesn't exist")
94     end
95   end
96 
97   capture_audit_events(resource, context)
98   persist_system_values(resource, context)
99 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
105 def persist_system_values(resource, context)
106   param_to_event = {}
107   context.status.events.each do |ev|
108     param_to_event[ev.property] = ev
109   end
110 
111   context.system_value_params.each do |pname, param|
112     @persistence.set_system_value(resource.ref, pname.to_s,
113                                   new_system_value(param,
114                                                    param_to_event[pname.to_s],
115                                                    @persistence.get_system_value(resource.ref, pname.to_s)))
116   end
117 end
sync(event, param, current_value, audit_message) click to toggle source
    # File lib/puppet/transaction/resource_harness.rb
240 def sync(event, param, current_value, audit_message)
241   param.sync
242   if param.sensitive
243     event.message = param.format(_("changed %s to %s"),
244                                  param.is_to_s(current_value),
245                                  param.should_to_s(param.should)) + audit_message.to_s
246   else
247     event.message = "#{param.change_to_s(current_value, param.should)}#{audit_message}"
248   end
249   event.status = "success"
250 end
sync_if_needed(param, context) click to toggle source
    # File lib/puppet/transaction/resource_harness.rb
119 def sync_if_needed(param, context)
120   historical_value = context.historical_values[param.name]
121   current_value = context.current_values[param.name]
122   do_audit = context.audited_params.include?(param.name)
123 
124   begin
125     if param.should && !param.safe_insync?(current_value)
126       event = create_change_event(param, current_value, historical_value)
127       if do_audit
128         event = audit_event(event, param, context)
129       end
130 
131       brief_audit_message = audit_message(param, do_audit, historical_value, current_value)
132 
133       if param.noop
134         noop(event, param, current_value, brief_audit_message)
135       else
136         sync(event, param, current_value, brief_audit_message)
137       end
138 
139       event
140     else
141       NO_ACTION
142     end
143   rescue => detail
144     # Execution will continue on StandardErrors, just store the event
145     Puppet.log_exception(detail)
146 
147     event = create_change_event(param, current_value, historical_value)
148     event.status = "failure"
149     event.message = param.format(_("change from %s to %s failed: "),
150                                  param.is_to_s(current_value),
151                                  param.should_to_s(param.should)) + detail.to_s
152     event
153   rescue Exception => detail
154     # Execution will halt on Exceptions, they get raised to the application
155     event = create_change_event(param, current_value, historical_value)
156     event.status = "failure"
157     event.message = param.format(_("change from %s to %s failed: "),
158                                  param.is_to_s(current_value),
159                                  param.should_to_s(param.should)) + detail.to_s
160     raise
161   ensure
162     if event
163       name = param.name.to_s
164       event.message ||= _("could not create change error message for %{name}") % { name: name }
165       event.calculate_corrective_change(@persistence.get_system_value(context.resource.ref, name))
166       event.message << ' (corrective)' if event.corrective_change
167       context.record(event)
168       event.send_log
169       context.synced_params << param.name
170     end
171   end
172 end