-
Notifications
You must be signed in to change notification settings - Fork 2.2k
/
configurer.rb
756 lines (640 loc) · 28.7 KB
/
configurer.rb
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
756
# frozen_string_literal: true
# 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