class Puppet::Configurer
Attributes
environment[R]
Public Class Methods
new(transaction_uuid = nil, job_id = nil)
click to toggle source
# File lib/puppet/configurer.rb 53 def initialize(transaction_uuid = nil, job_id = nil) 54 @running = false 55 @splayed = false 56 @running_failure = false 57 @cached_catalog_status = 'not_used' 58 @environment = Puppet[:environment] 59 @transaction_uuid = transaction_uuid || SecureRandom.uuid 60 @job_id = job_id 61 @static_catalog = true 62 @checksum_type = Puppet[:supported_checksum_types] 63 @handler = Puppet::Configurer::PluginHandler.new() 64 end
should_pluginsync?()
click to toggle source
# File lib/puppet/configurer.rb 24 def self.should_pluginsync? 25 if Puppet[:use_cached_catalog] 26 false 27 else 28 true 29 end 30 end
to_s()
click to toggle source
Provide more helpful strings to the logging that the Agent does
# File lib/puppet/configurer.rb 20 def self.to_s 21 _("Puppet configuration client") 22 end
Public Instance Methods
apply_catalog(catalog, options)
click to toggle source
Apply supplied catalog and return associated application report
# File lib/puppet/configurer.rb 277 def apply_catalog(catalog, options) 278 report = options[:report] 279 report.configuration_version = catalog.version 280 281 benchmark(:notice, _("Applied catalog in %{seconds} seconds")) do 282 apply_catalog_time = thinmark do 283 catalog.apply(options) 284 end 285 options[:report].add_times(:catalog_application, apply_catalog_time) 286 end 287 288 report 289 end
check_fact_name_length(name, number_of_dots)
click to toggle source
# File lib/puppet/configurer.rb 149 def check_fact_name_length(name, number_of_dots) 150 max_length = Puppet[:fact_name_length_soft_limit] 151 return if max_length.zero? 152 153 # rough byte size estimations of fact path as a postgresql btree index 154 size_as_btree_index = 8 + (number_of_dots * 2) + name.to_s.bytesize 155 warn_fact_name_length(name, max_length) if size_as_btree_index > max_length 156 end
check_fact_values_length(values)
click to toggle source
# File lib/puppet/configurer.rb 158 def check_fact_values_length(values) 159 max_length = Puppet[:fact_value_length_soft_limit] 160 return if max_length.zero? 161 162 warn_fact_value_length(values, max_length) if values.to_s.bytesize > max_length 163 end
check_facts_limits(facts)
click to toggle source
# File lib/puppet/configurer.rb 208 def check_facts_limits(facts) 209 @number_of_facts = 0 210 check_top_level_number_limit(facts.size) 211 212 parse_fact_name_and_value_limits(facts) 213 check_total_number_limit(@number_of_facts) 214 Puppet.debug _("The total number of facts registered is %{number_of_facts}") % {number_of_facts: @number_of_facts} 215 end
check_payload_size(payload)
click to toggle source
# File lib/puppet/configurer.rb 179 def check_payload_size(payload) 180 max_size = Puppet[:payload_soft_limit] 181 return if max_size.zero? 182 183 warn_fact_payload_size(payload, max_size) if payload > max_size 184 Puppet.debug _("The size of the payload is %{payload}") % {payload: payload} 185 end
check_top_level_number_limit(size)
click to toggle source
# File lib/puppet/configurer.rb 165 def check_top_level_number_limit(size) 166 max_size = Puppet[:top_level_facts_soft_limit] 167 return if max_size.zero? 168 169 warn_number_of_top_level_facts(size, max_size) if size > max_size 170 end
check_total_number_limit(size)
click to toggle source
# File lib/puppet/configurer.rb 172 def check_total_number_limit(size) 173 max_size = Puppet[:number_of_facts_soft_limit] 174 return if max_size.zero? 175 176 warn_number_of_facts(size, max_size) if size > max_size 177 end
convert_catalog(result, duration, facts, options = {})
click to toggle source
Convert a plain resource catalog into our full host catalog.
# File lib/puppet/configurer.rb 109 def convert_catalog(result, duration, facts, options = {}) 110 catalog = nil 111 112 catalog_conversion_time = thinmark do 113 # Will mutate the result and replace all Deferred values with resolved values 114 if facts 115 Puppet::Pops::Evaluator::DeferredResolver.resolve_and_replace(facts, result, Puppet.lookup(:current_environment)) 116 end 117 118 catalog = result.to_ral 119 catalog.finalize 120 catalog.retrieval_duration = duration 121 catalog.write_class_file 122 catalog.write_resource_file 123 end 124 options[:report].add_times(:convert_catalog, catalog_conversion_time) if options[:report] 125 126 catalog 127 end
execute_postrun_command()
click to toggle source
# File lib/puppet/configurer.rb 32 def execute_postrun_command 33 execute_from_setting(:postrun_command) 34 end
execute_prerun_command()
click to toggle source
# File lib/puppet/configurer.rb 36 def execute_prerun_command 37 execute_from_setting(:prerun_command) 38 end
get_facts(options)
click to toggle source
# File lib/puppet/configurer.rb 217 def get_facts(options) 218 if options[:pluginsync] 219 plugin_sync_time = thinmark do 220 remote_environment_for_plugins = Puppet::Node::Environment.remote(@environment) 221 download_plugins(remote_environment_for_plugins) 222 223 Puppet::GettextConfig.reset_text_domain('agent') 224 Puppet::ModuleTranslations.load_from_vardir(Puppet[:vardir]) 225 end 226 options[:report].add_times(:plugin_sync, plugin_sync_time) if options[:report] 227 end 228 229 facts_hash = {} 230 facts = nil 231 if Puppet::Resource::Catalog.indirection.terminus_class == :rest 232 # This is a bit complicated. We need the serialized and escaped facts, 233 # and we need to know which format they're encoded in. Thus, we 234 # get a hash with both of these pieces of information. 235 # 236 # facts_for_uploading may set Puppet[:node_name_value] as a side effect 237 facter_time = thinmark do 238 facts = find_facts 239 check_facts_limits(facts.to_data_hash['values']) 240 facts_hash = encode_facts(facts) # encode for uploading # was: facts_for_uploading 241 check_payload_size(facts_hash[:facts].bytesize) 242 end 243 options[:report].add_times(:fact_generation, facter_time) if options[:report] 244 end 245 [facts_hash, facts] 246 end
init_storage()
click to toggle source
Initialize and load storage
# File lib/puppet/configurer.rb 41 def init_storage 42 Puppet::Util::Storage.load 43 rescue => detail 44 Puppet.log_exception(detail, _("Removing corrupt state file %{file}: %{detail}") % { file: Puppet[:statefile], detail: detail }) 45 begin 46 Puppet::FileSystem.unlink(Puppet[:statefile]) 47 retry 48 rescue => detail 49 raise Puppet::Error.new(_("Cannot remove %{file}: %{detail}") % { file: Puppet[:statefile], detail: detail }, detail) 50 end 51 end
parse_fact_name_and_value_limits(object, path = [])
click to toggle source
# File lib/puppet/configurer.rb 187 def parse_fact_name_and_value_limits(object, path = []) 188 case object 189 when Hash 190 object.each do |key, value| 191 path.push(key) 192 parse_fact_name_and_value_limits(value, path) 193 path.pop 194 @number_of_facts += 1 195 end 196 when Array 197 object.each_with_index do |e, idx| 198 path.push(idx) 199 parse_fact_name_and_value_limits(e, path) 200 path.pop 201 end 202 else 203 check_fact_name_length(path.join(), path.size) 204 check_fact_values_length(object) 205 end 206 end
prepare_and_retrieve_catalog(cached_catalog, facts, options, query_options)
click to toggle source
# File lib/puppet/configurer.rb 248 def prepare_and_retrieve_catalog(cached_catalog, facts, options, query_options) 249 # set report host name now that we have the fact 250 options[:report].host = Puppet[:node_name_value] 251 252 query_options[:transaction_uuid] = @transaction_uuid 253 query_options[:job_id] = @job_id 254 query_options[:static_catalog] = @static_catalog 255 256 # Query params don't enforce ordered evaluation, so munge this list into a 257 # dot-separated string. 258 query_options[:checksum_type] = @checksum_type.join('.') 259 260 # apply passes in ral catalog 261 catalog = cached_catalog || options[:catalog] 262 unless catalog 263 # retrieve_catalog returns resource catalog 264 catalog = retrieve_catalog(facts, query_options) 265 Puppet.err _("Could not retrieve catalog; skipping run") unless catalog 266 end 267 catalog 268 end
prepare_and_retrieve_catalog_from_cache(options = {})
click to toggle source
# File lib/puppet/configurer.rb 270 def prepare_and_retrieve_catalog_from_cache(options = {}) 271 result = retrieve_catalog_from_cache({:transaction_uuid => @transaction_uuid, :static_catalog => @static_catalog}) 272 Puppet.info _("Using cached catalog from environment '%{catalog_env}'") % { catalog_env: result.environment } if result 273 result 274 end
resubmit_facts()
click to toggle source
Submit updated facts to the Puppet
Server
This method will clear all current fact values, load a fresh set of fact data, and then submit it to the Puppet
Server.
@return [true] If fact submission succeeds. @return [false] If an exception is raised during fact generation or
submission.
# File lib/puppet/configurer.rb 606 def resubmit_facts 607 ::Facter.clear 608 facts = find_facts 609 610 client = Puppet.runtime[:http] 611 session = client.create_session 612 puppet = session.route_to(:puppet) 613 614 Puppet.info(_("Uploading facts for %{node} to %{server}") % { 615 node: facts.name, 616 server: puppet.url.hostname}) 617 618 puppet.put_facts(facts.name, facts: facts, environment: Puppet.lookup(:current_environment).name.to_s) 619 620 return true 621 rescue => detail 622 Puppet.log_exception(detail, _("Failed to submit facts: %{detail}") % 623 { detail: detail }) 624 625 return false 626 end
retrieve_catalog(facts, query_options)
click to toggle source
Get the remote catalog, yo. Returns nil if no catalog can be found.
# File lib/puppet/configurer.rb 67 def retrieve_catalog(facts, query_options) 68 query_options ||= {} 69 if Puppet[:use_cached_catalog] || @running_failure 70 result = retrieve_catalog_from_cache(query_options) 71 end 72 73 if result 74 if Puppet[:use_cached_catalog] 75 @cached_catalog_status = 'explicitly_requested' 76 elsif @running_failure 77 @cached_catalog_status = 'on_failure' 78 end 79 80 Puppet.info _("Using cached catalog from environment '%{environment}'") % { environment: result.environment } 81 else 82 result = retrieve_new_catalog(facts, query_options) 83 84 if !result 85 if !Puppet[:usecacheonfailure] 86 Puppet.warning _("Not using cache on failed catalog") 87 return nil 88 end 89 90 result = retrieve_catalog_from_cache(query_options) 91 92 if result 93 # don't use use cached catalog if it doesn't match server specified environment 94 if result.environment != @environment 95 Puppet.err _("Not using cached catalog because its environment '%{catalog_env}' does not match '%{local_env}'") % { catalog_env: result.environment, local_env: @environment } 96 return nil 97 end 98 99 @cached_catalog_status = 'on_failure' 100 Puppet.info _("Using cached catalog from environment '%{catalog_env}'") % { catalog_env: result.environment } 101 end 102 end 103 end 104 105 result 106 end
run(options = {})
click to toggle source
The code that actually runs the catalog. This just passes any options on to the catalog, which accepts :tags and :ignoreschedules.
# File lib/puppet/configurer.rb 294 def run(options = {}) 295 # We create the report pre-populated with default settings for 296 # environment and transaction_uuid very early, this is to ensure 297 # they are sent regardless of any catalog compilation failures or 298 # exceptions. 299 options[:report] ||= Puppet::Transaction::Report.new(nil, @environment, @transaction_uuid, @job_id, options[:start_time] || Time.now) 300 report = options[:report] 301 init_storage 302 303 Puppet::Util::Log.newdestination(report) 304 305 completed = nil 306 begin 307 # Skip failover logic if the server_list setting is empty 308 do_failover = Puppet.settings[:server_list] && !Puppet.settings[:server_list].empty? 309 310 # When we are passed a catalog, that means we're in apply 311 # mode. We shouldn't try to do any failover in that case. 312 if options[:catalog].nil? && do_failover 313 server, port = find_functional_server 314 if server.nil? 315 detail = _("Could not select a functional puppet server from server_list: '%{server_list}'") % { server_list: Puppet.settings.value(:server_list, Puppet[:environment].to_sym, true) } 316 if Puppet[:usecacheonfailure] 317 options[:pluginsync] = false 318 @running_failure = true 319 320 server = Puppet[:server_list].first[0] 321 port = Puppet[:server_list].first[1] || Puppet[:serverport] 322 323 Puppet.err(detail) 324 else 325 raise Puppet::Error, detail 326 end 327 else 328 #TRANSLATORS 'server_list' is the name of a setting and should not be translated 329 Puppet.debug _("Selected puppet server from the `server_list` setting: %{server}:%{port}") % { server: server, port: port } 330 report.server_used = "#{server}:#{port}" 331 end 332 Puppet.override(server: server, serverport: port) do 333 completed = run_internal(options) 334 end 335 else 336 completed = run_internal(options) 337 end 338 ensure 339 # we may sleep for awhile, close connections now 340 Puppet.runtime[:http].close 341 end 342 343 completed ? report.exit_status : nil 344 end
save_last_run_summary(report)
click to toggle source
# File lib/puppet/configurer.rb 589 def save_last_run_summary(report) 590 mode = Puppet.settings.setting(:lastrunfile).mode 591 Puppet::Util.replace_file(Puppet[:lastrunfile], mode) do |fh| 592 fh.print YAML.dump(report.raw_summary) 593 end 594 rescue => detail 595 Puppet.log_exception(detail, _("Could not save last run local report: %{detail}") % { detail: detail }) 596 end
send_report(report)
click to toggle source
# File lib/puppet/configurer.rb 581 def send_report(report) 582 puts report.summary if Puppet[:summarize] 583 save_last_run_summary(report) 584 Puppet::Transaction::Report.indirection.save(report, nil, :environment => Puppet::Node::Environment.remote(@environment)) if Puppet[:report] 585 rescue => detail 586 Puppet.log_exception(detail, _("Could not send report: %{detail}") % { detail: detail }) 587 end
valid_server_environment?()
click to toggle source
# File lib/puppet/configurer.rb 528 def valid_server_environment? 529 session = Puppet.lookup(:http_session) 530 begin 531 fs = session.route_to(:fileserver) 532 fs.get_file_metadatas(path: URI(Puppet[:pluginsource]).path, recurse: :false, environment: @environment) 533 true 534 rescue Puppet::HTTP::ResponseError => detail 535 if detail.response.code == 404 536 Puppet.notice(_("Environment '%{environment}' not found on server, skipping initial pluginsync.") % { environment: @environment }) 537 else 538 Puppet.log_exception(detail, detail.message) 539 end 540 false 541 rescue => detail 542 Puppet.log_exception(detail, detail.message) 543 false 544 end 545 end
warn_fact_name_length(name, max_length)
click to toggle source
# File lib/puppet/configurer.rb 133 def warn_fact_name_length(name, max_length) 134 Puppet.warning _("Fact %{name} with length: '%{length}' exceeds the length limit: %{limit}") % { name: name, length: name.to_s.bytesize, limit: max_length } 135 end
warn_fact_payload_size(payload, max_size)
click to toggle source
# File lib/puppet/configurer.rb 145 def warn_fact_payload_size(payload, max_size) 146 Puppet.warning _("Payload with the current size of: '%{payload}' exceeds the payload size limit: %{max_size}") % { payload: payload, max_size: max_size } 147 end
warn_fact_value_length(value, max_length)
click to toggle source
# File lib/puppet/configurer.rb 141 def warn_fact_value_length(value, max_length) 142 Puppet.warning _("Fact value '%{value}' with the value length: '%{length}' exceeds the value length limit: %{max_length}") % { value: value, length:value.to_s.bytesize, max_length: max_length } 143 end
warn_number_of_facts(size, max_number)
click to toggle source
# File lib/puppet/configurer.rb 129 def warn_number_of_facts(size, max_number) 130 Puppet.warning _("The current total number of facts: %{size} exceeds the number of facts limit: %{max_size}") % { size: size, max_size: max_number } 131 end
warn_number_of_top_level_facts(size, max_number)
click to toggle source
# File lib/puppet/configurer.rb 137 def warn_number_of_top_level_facts(size, max_number) 138 Puppet.warning _("The current number of top level facts: %{size} exceeds the top facts limit: %{max_size}") % { size: size, max_size: max_number } 139 end
Private Instance Methods
download_plugins(remote_environment_for_plugins)
click to toggle source
# File lib/puppet/configurer.rb 681 def download_plugins(remote_environment_for_plugins) 682 begin 683 @handler.download_plugins(remote_environment_for_plugins) 684 rescue Puppet::Error => detail 685 if !Puppet[:ignore_plugin_errors] && Puppet[:usecacheonfailure] 686 @running_failure = true 687 else 688 raise detail 689 end 690 end 691 end
execute_from_setting(setting)
click to toggle source
# File lib/puppet/configurer.rb 630 def execute_from_setting(setting) 631 return true if (command = Puppet[setting]) == "" 632 633 begin 634 Puppet::Util::Execution.execute([command]) 635 true 636 rescue => detail 637 Puppet.log_exception(detail, _("Could not run command from %{setting}: %{detail}") % { setting: setting, detail: detail }) 638 false 639 end 640 end
find_functional_server()
click to toggle source
# File lib/puppet/configurer.rb 547 def find_functional_server 548 begin 549 session = Puppet.lookup(:http_session) 550 service = session.route_to(:puppet) 551 return [service.url.host, service.url.port] 552 rescue Puppet::HTTP::ResponseError => e 553 Puppet.debug(_("Puppet server %{host}:%{port} is unavailable: %{code} %{reason}") % 554 { host: e.response.url.host, port: e.response.url.port, code: e.response.code, reason: e.response.reason }) 555 rescue => detail 556 #TRANSLATORS 'server_list' is the name of a setting and should not be translated 557 Puppet.debug _("Unable to connect to server from server_list setting: %{detail}") % {detail: detail} 558 end 559 [nil, nil] 560 end
last_server_specified_environment()
click to toggle source
# File lib/puppet/configurer.rb 563 def last_server_specified_environment 564 return @last_server_specified_environment if @last_server_specified_environment 565 if Puppet::FileSystem.exist?(Puppet[:lastrunfile]) 566 summary = Puppet::Util::Yaml.safe_load_file(Puppet[:lastrunfile]) 567 return unless summary.dig('application', 'run_mode') == 'agent' 568 initial_environment = summary.dig('application', 'initial_environment') 569 converged_environment = summary.dig('application', 'converged_environment') 570 @last_server_specified_environment = converged_environment if initial_environment != converged_environment 571 end 572 573 Puppet.debug(_("Found last server-specified environment: %{environment}") % { environment: @last_server_specified_environment }) if @last_server_specified_environment 574 @last_server_specified_environment 575 rescue => detail 576 Puppet.debug(_("Could not find last server-specified environment: %{detail}") % { detail: detail }) 577 nil 578 end
retrieve_catalog_from_cache(query_options)
click to toggle source
# File lib/puppet/configurer.rb 642 def retrieve_catalog_from_cache(query_options) 643 result = nil 644 @duration = thinmark do 645 result = Puppet::Resource::Catalog.indirection.find( 646 Puppet[:node_name_value], 647 query_options.merge( 648 :ignore_terminus => true, 649 :environment => Puppet::Node::Environment.remote(@environment) 650 ) 651 ) 652 end 653 result 654 rescue => detail 655 Puppet.log_exception(detail, _("Could not retrieve catalog from cache: %{detail}") % { detail: detail }) 656 return nil 657 end
retrieve_new_catalog(facts, query_options)
click to toggle source
# File lib/puppet/configurer.rb 659 def retrieve_new_catalog(facts, query_options) 660 result = nil 661 @duration = thinmark do 662 result = Puppet::Resource::Catalog.indirection.find( 663 Puppet[:node_name_value], 664 query_options.merge( 665 :ignore_cache => true, 666 # don't update cache until after environment converges 667 :ignore_cache_save => true, 668 :environment => Puppet::Node::Environment.remote(@environment), 669 :check_environment => true, 670 :fail_on_404 => true, 671 :facts_for_catalog => facts 672 ) 673 ) 674 end 675 result 676 rescue StandardError => detail 677 Puppet.log_exception(detail, _("Could not retrieve catalog from remote server: %{detail}") % { detail: detail }) 678 return nil 679 end
run_internal(options)
click to toggle source
# File lib/puppet/configurer.rb 346 def run_internal(options) 347 report = options[:report] 348 report.initial_environment = Puppet[:environment] 349 350 if options[:start_time] 351 startup_time = Time.now - options[:start_time] 352 report.add_times(:startup_time, startup_time) 353 end 354 355 # If a cached catalog is explicitly requested, attempt to retrieve it. Skip the node request, 356 # don't pluginsync and switch to the catalog's environment if we successfully retrieve it. 357 if Puppet[:use_cached_catalog] 358 Puppet::GettextConfig.reset_text_domain('agent') 359 Puppet::ModuleTranslations.load_from_vardir(Puppet[:vardir]) 360 361 cached_catalog = prepare_and_retrieve_catalog_from_cache(options) 362 if cached_catalog 363 @cached_catalog_status = 'explicitly_requested' 364 365 if @environment != cached_catalog.environment && !Puppet[:strict_environment_mode] 366 Puppet.notice _("Local environment: '%{local_env}' doesn't match the environment of the cached catalog '%{catalog_env}', switching agent to '%{catalog_env}'.") % { local_env: @environment, catalog_env: cached_catalog.environment } 367 @environment = cached_catalog.environment 368 end 369 370 report.environment = @environment 371 else 372 # Don't try to retrieve a catalog from the cache again after we've already 373 # failed to do so the first time. 374 Puppet[:use_cached_catalog] = false 375 Puppet[:usecacheonfailure] = false 376 options[:pluginsync] = Puppet::Configurer.should_pluginsync? 377 end 378 end 379 380 begin 381 unless Puppet[:node_name_fact].empty? 382 query_options, facts = get_facts(options) 383 end 384 385 configured_environment = Puppet[:environment] if Puppet.settings.set_by_config?(:environment) 386 387 # We only need to find out the environment to run in if we don't already have a catalog 388 unless (cached_catalog || options[:catalog] || Puppet.settings.set_by_cli?(:environment) || Puppet[:strict_environment_mode]) 389 Puppet.debug(_("Environment not passed via CLI and no catalog was given, attempting to find out the last server-specified environment")) 390 if last_server_specified_environment 391 @environment = last_server_specified_environment 392 report.environment = last_server_specified_environment 393 else 394 Puppet.debug(_("Could not find a usable environment in the lastrunfile. Either the file does not exist, does not have the required keys, or the values of 'initial_environment' and 'converged_environment' are identical.")) 395 end 396 end 397 398 Puppet.info _("Using environment '%{env}'") % { env: @environment } 399 400 # This is to maintain compatibility with anyone using this class 401 # aside from agent, apply, device. 402 unless Puppet.lookup(:loaders) { nil } 403 new_env = Puppet::Node::Environment.remote(@environment) 404 Puppet.push_context( 405 { 406 current_environment: new_env, 407 loaders: Puppet::Pops::Loaders.new(new_env, true) 408 }, 409 "Local node environment #{@environment} for configurer transaction" 410 ) 411 end 412 413 temp_value = options[:pluginsync] 414 415 # only validate server environment if pluginsync is requested 416 options[:pluginsync] = valid_server_environment? if options[:pluginsync] == true 417 418 query_options, facts = get_facts(options) unless query_options 419 options[:pluginsync] = temp_value 420 421 query_options[:configured_environment] = configured_environment 422 423 catalog = prepare_and_retrieve_catalog(cached_catalog, facts, options, query_options) 424 unless catalog 425 return nil 426 end 427 428 if Puppet[:strict_environment_mode] && catalog.environment != @environment 429 Puppet.err _("Not using catalog because its environment '%{catalog_env}' does not match agent specified environment '%{local_env}' and strict_environment_mode is set") % { catalog_env: catalog.environment, local_env: @environment } 430 return nil 431 end 432 433 # Here we set the local environment based on what we get from the 434 # catalog. Since a change in environment means a change in facts, and 435 # facts may be used to determine which catalog we get, we need to 436 # rerun the process if the environment is changed. 437 tries = 0 438 while catalog.environment and not catalog.environment.empty? and catalog.environment != @environment 439 if tries > 3 440 raise Puppet::Error, _("Catalog environment didn't stabilize after %{tries} fetches, aborting run") % { tries: tries } 441 end 442 Puppet.notice _("Local environment: '%{local_env}' doesn't match server specified environment '%{catalog_env}', restarting agent run with environment '%{catalog_env}'") % { local_env: @environment, catalog_env: catalog.environment } 443 @environment = catalog.environment 444 report.environment = @environment 445 446 new_env = Puppet::Node::Environment.remote(@environment) 447 Puppet.push_context( 448 { 449 :current_environment => new_env, 450 :loaders => Puppet::Pops::Loaders.new(new_env, true) 451 }, 452 "Local node environment #{@environment} for configurer transaction" 453 ) 454 455 query_options, facts = get_facts(options) 456 query_options[:configured_environment] = configured_environment 457 458 # if we get here, ignore the cached catalog 459 catalog = prepare_and_retrieve_catalog(nil, facts, options, query_options) 460 return nil unless catalog 461 tries += 1 462 end 463 464 # now that environment has converged, convert resource catalog into ral catalog 465 # unless we were given a RAL catalog 466 if !cached_catalog && options[:catalog] 467 ral_catalog = options[:catalog] 468 else 469 # Ordering here matters. We have to resolve deferred resources in the 470 # resource catalog, convert the resource catalog to a RAL catalog (which 471 # triggers type/provider validation), and only if that is successful, 472 # should we cache the *original* resource catalog. However, deferred 473 # evaluation mutates the resource catalog, so we need to make a copy of 474 # it here. If PUP-9323 is ever implemented so that we resolve deferred 475 # resources in the RAL catalog as they are needed, then we could eliminate 476 # this step. 477 catalog_to_cache = Puppet.override(:rich_data => Puppet[:rich_data]) do 478 Puppet::Resource::Catalog.from_data_hash(catalog.to_data_hash) 479 end 480 481 # REMIND @duration is the time spent loading the last catalog, and doesn't 482 # account for things like we failed to download and fell back to the cache 483 ral_catalog = convert_catalog(catalog, @duration, facts, options) 484 485 # Validation succeeded, so commit the `catalog_to_cache` for non-noop runs. Don't 486 # commit `catalog` since it contains the result of deferred evaluation. Ideally 487 # we'd just copy the downloaded response body, instead of serializing the 488 # in-memory catalog, but that's hard due to the indirector. 489 indirection = Puppet::Resource::Catalog.indirection 490 if !Puppet[:noop] && indirection.cache? 491 request = indirection.request(:save, nil, catalog_to_cache, environment: Puppet::Node::Environment.remote(catalog_to_cache.environment)) 492 Puppet.info("Caching catalog for #{request.key}") 493 indirection.cache.save(request) 494 end 495 end 496 497 execute_prerun_command or return nil 498 499 options[:report].code_id = ral_catalog.code_id 500 options[:report].catalog_uuid = ral_catalog.catalog_uuid 501 options[:report].cached_catalog_status = @cached_catalog_status 502 apply_catalog(ral_catalog, options) 503 true 504 rescue => detail 505 Puppet.log_exception(detail, _("Failed to apply catalog: %{detail}") % { detail: detail }) 506 return nil 507 ensure 508 execute_postrun_command or return nil 509 end 510 ensure 511 if Puppet[:resubmit_facts] 512 # TODO: Should mark the report as "failed" if an error occurs and 513 # resubmit_facts returns false. There is currently no API for this. 514 resubmit_facts_time = thinmark { resubmit_facts } 515 516 report.add_times(:resubmit_facts, resubmit_facts_time) 517 end 518 519 report.cached_catalog_status ||= @cached_catalog_status 520 report.add_times(:total, Time.now - report.time) 521 report.finalize_report 522 Puppet::Util::Log.close(report) 523 send_report(report) 524 Puppet.pop_context 525 end