Class: Puppet::Configurer

Inherits:
Object show all
Includes:
FactHandler, Util
Defined in:
lib/puppet/configurer.rb

Defined Under Namespace

Modules: FactHandler Classes: Downloader, PluginHandler

Constant Summary

Constants included from Util

Util::ALNUM, Util::ALPHA, Util::AbsolutePathPosix, Util::AbsolutePathWindows, Util::DEFAULT_POSIX_MODE, Util::DEFAULT_WINDOWS_MODE, Util::ESCAPED, Util::HEX, Util::HttpProxy, Util::PUPPET_STACK_INSERTION_FRAME, Util::RESERVED, Util::RFC_3986_URI_REGEX, Util::UNRESERVED, Util::UNSAFE

Constants included from Util::POSIX

Util::POSIX::LOCALE_ENV_VARS, Util::POSIX::USER_ENV_VARS

Constants included from Util::SymbolicFileMode

Util::SymbolicFileMode::SetGIDBit, Util::SymbolicFileMode::SetUIDBit, Util::SymbolicFileMode::StickyBit, Util::SymbolicFileMode::SymbolicMode, Util::SymbolicFileMode::SymbolicSpecialToBit

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Util

absolute_path?, benchmark, chuser, clear_environment, create_erb, default_env, deterministic_rand, deterministic_rand_int, exit_on_fail, format_backtrace_array, format_puppetstack_frame, get_env, get_environment, logmethods, merge_environment, path_to_uri, pretty_backtrace, replace_file, resolve_stackframe, rfc2396_escape, safe_posix_fork, set_env, skip_external_facts, symbolizehash, thinmark, uri_encode, uri_query_encode, uri_to_path, uri_unescape, which, withenv, withumask

Methods included from Util::POSIX

#get_posix_field, #gid, groups_of, #idfield, #methodbyid, #methodbyname, #search_posix_field, #uid

Methods included from Util::SymbolicFileMode

#display_mode, #normalize_symbolic_mode, #symbolic_mode_to_int, #valid_symbolic_mode?

Methods included from FactHandler

#encode_facts, #facts_for_uploading, #find_facts

Constructor Details

#initialize(transaction_uuid = nil, job_id = nil) ⇒ Configurer

Returns a new instance of Configurer.



55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/puppet/configurer.rb', line 55

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

Instance Attribute Details

#environmentObject (readonly)

Returns the value of attribute environment.



19
20
21
# File 'lib/puppet/configurer.rb', line 19

def environment
  @environment
end

Class Method Details

.should_pluginsync?Boolean

Returns:

  • (Boolean)


26
27
28
29
30
31
32
# File 'lib/puppet/configurer.rb', line 26

def self.should_pluginsync?
  if Puppet[:use_cached_catalog]
    false
  else
    true
  end
end

.to_sObject

Provide more helpful strings to the logging that the Agent does



22
23
24
# File 'lib/puppet/configurer.rb', line 22

def self.to_s
  _("Puppet configuration client")
end

Instance Method Details

#apply_catalog(catalog, options) ⇒ Object

Apply supplied catalog and return associated application report



282
283
284
285
286
287
288
289
290
291
292
293
294
# File 'lib/puppet/configurer.rb', line 282

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

#check_fact_name_length(name, number_of_dots) ⇒ Object



154
155
156
157
158
159
160
161
# File 'lib/puppet/configurer.rb', line 154

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

#check_fact_values_length(values) ⇒ Object



163
164
165
166
167
168
# File 'lib/puppet/configurer.rb', line 163

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

#check_facts_limits(facts) ⇒ Object



213
214
215
216
217
218
219
220
# File 'lib/puppet/configurer.rb', line 213

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

#check_payload_size(payload) ⇒ Object



184
185
186
187
188
189
190
# File 'lib/puppet/configurer.rb', line 184

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

#check_top_level_number_limit(size) ⇒ Object



170
171
172
173
174
175
# File 'lib/puppet/configurer.rb', line 170

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

#check_total_number_limit(size) ⇒ Object



177
178
179
180
181
182
# File 'lib/puppet/configurer.rb', line 177

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

#convert_catalog(result, duration, facts, options = {}) ⇒ Object

Convert a plain resource catalog into our full host catalog.



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/puppet/configurer.rb', line 111

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

#execute_postrun_commandObject



34
35
36
# File 'lib/puppet/configurer.rb', line 34

def execute_postrun_command
  execute_from_setting(:postrun_command)
end

#execute_prerun_commandObject



38
39
40
# File 'lib/puppet/configurer.rb', line 38

def execute_prerun_command
  execute_from_setting(:prerun_command)
end

#get_facts(options) ⇒ Object



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
# File 'lib/puppet/configurer.rb', line 222

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

#init_storageObject

Initialize and load storage



43
44
45
46
47
48
49
50
51
52
53
# File 'lib/puppet/configurer.rb', line 43

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

#parse_fact_name_and_value_limits(object, path = []) ⇒ Object



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/puppet/configurer.rb', line 192

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
    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)
    @number_of_facts += 1
  end
end

#prepare_and_retrieve_catalog(cached_catalog, facts, options, query_options) ⇒ Object



253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/puppet/configurer.rb', line 253

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

#prepare_and_retrieve_catalog_from_cache(options = {}) ⇒ Object



275
276
277
278
279
# File 'lib/puppet/configurer.rb', line 275

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

#resubmit_factstrue, false

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.

Returns:

  • (true)

    If fact submission succeeds.

  • (false)

    If an exception is raised during fact generation or submission.



663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
# File 'lib/puppet/configurer.rb', line 663

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

#retrieve_catalog(facts, query_options) ⇒ Object

Get the remote catalog, yo. Returns nil if no catalog can be found.



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
# File 'lib/puppet/configurer.rb', line 69

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

#run(options = {}) ⇒ Object

The code that actually runs the catalog. This just passes any options on to the catalog, which accepts :tags and :ignoreschedules.



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
# File 'lib/puppet/configurer.rb', line 299

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

#save_last_run_summary(report) ⇒ Object



646
647
648
649
650
651
652
653
# File 'lib/puppet/configurer.rb', line 646

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

#send_report(report) ⇒ Object



631
632
633
634
635
636
637
638
639
640
641
642
643
644
# File 'lib/puppet/configurer.rb', line 631

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

#valid_server_environment?Boolean

Returns:

  • (Boolean)


530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
# File 'lib/puppet/configurer.rb', line 530

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) # rubocop:disable Lint/BooleanSymbol
    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

#warn_fact_name_length(name, max_length) ⇒ Object



138
139
140
# File 'lib/puppet/configurer.rb', line 138

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

#warn_fact_payload_size(payload, max_size) ⇒ Object



150
151
152
# File 'lib/puppet/configurer.rb', line 150

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

#warn_fact_value_length(value, max_length) ⇒ Object



146
147
148
# File 'lib/puppet/configurer.rb', line 146

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

#warn_number_of_facts(size, max_number) ⇒ Object



134
135
136
# File 'lib/puppet/configurer.rb', line 134

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

#warn_number_of_top_level_facts(size, max_number) ⇒ Object



142
143
144
# File 'lib/puppet/configurer.rb', line 142

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