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