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