class Transaction::ResourceHarness
Constants
- NO_ACTION
- ResourceApplicationContext
@api private
Attributes
Public Class Methods
# File lib/puppet/transaction/resource_harness.rb 11 def initialize(transaction) 12 @transaction = transaction 13 @persistence = transaction.persistence 14 end
Public Instance Methods
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
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
# 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
# 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
# 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
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
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
# 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
# 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
# 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
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
# 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
# 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
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
# 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
# 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