class Puppet::HTTP::DNS

Public Class Methods

new(resolver = Resolv::DNS.new) click to toggle source
   # File lib/puppet/http/dns.rb
27 def initialize(resolver = Resolv::DNS.new)
28   @resolver = resolver
29 
30   # Stores DNS records per service, along with their TTL
31   # and the time at which they were resolved, for cache
32   # eviction.
33   @record_cache = {}
34 end

Public Instance Methods

each_srv_record(domain, service_name = :puppet) { |target.to_s, port| ... } click to toggle source

Iterate through the list of records for this service and yield each server and port pair. Records are only fetched via DNS query the first time and cached for the duration of their service's TTL thereafter. @param [String] domain the domain to search for @param [Symbol] service_name the key of the service we are querying @yields [String, Integer] server and port of selected record

   # File lib/puppet/http/dns.rb
43 def each_srv_record(domain, service_name = :puppet, &block)
44   if (domain.nil? or domain.empty?)
45     Puppet.debug "Domain not known; skipping SRV lookup"
46     return
47   end
48 
49   Puppet.debug "Searching for SRV records for domain: #{domain}"
50 
51   case service_name
52     when :puppet then service = '_x-puppet'
53     when :file   then service = '_x-puppet-fileserver'
54     else              service = "_x-puppet-#{service_name}"
55   end
56   record_name = "#{service}._tcp.#{domain}"
57 
58   if @record_cache.has_key?(service_name) && !expired?(service_name)
59     records = @record_cache[service_name].records
60     Puppet.debug "Using cached record for #{record_name}"
61   else
62     records = @resolver.getresources(record_name, Resolv::DNS::Resource::IN::SRV)
63     if records.size > 0
64       @record_cache[service_name] = CacheEntry.new(records)
65     end
66     Puppet.debug "Found #{records.size} SRV records for: #{record_name}"
67   end
68 
69   if records.size == 0 && service_name != :puppet
70     # Try the generic :puppet service if no SRV records were found
71     # for the specific service.
72     each_srv_record(domain, :puppet, &block)
73   else
74     each_priority(records) do |recs|
75       while next_rr = recs.delete(find_weighted_server(recs)) #rubocop:disable Lint/AssignmentInCondition
76         Puppet.debug "Yielding next server of #{next_rr.target}:#{next_rr.port}"
77         yield next_rr.target.to_s, next_rr.port
78       end
79     end
80   end
81 end
expired?(service_name) click to toggle source

Checks if the cached entry for the given service has expired. @param [String] service_name the name of the service to check @return [Boolean] true if the entry has expired, false otherwise.

Always returns true if the record had no TTL.
    # File lib/puppet/http/dns.rb
128 def expired?(service_name)
129   entry = @record_cache[service_name]
130   if entry
131     return Time.now > (entry.resolution_time + entry.ttl)
132   else
133     return true
134   end
135 end
find_weighted_server(records) click to toggle source

Given a list of records of the same priority, chooses a random one from among them, favoring those with higher weights. @param [[Resolv::DNS::Resource::IN::SRV]] records a list of records

of the same priority

@return [Resolv::DNS::Resource::IN:SRV] the chosen record

    # File lib/puppet/http/dns.rb
 88 def find_weighted_server(records)
 89   return nil if records.nil? || records.empty?
 90   return records.first if records.size == 1
 91 
 92   # Calculate the sum of all weights in the list of resource records,
 93   # This is used to then select hosts until the weight exceeds what
 94   # random number we selected.  For example, if we have weights of 1 8 and 3:
 95   #
 96   # |-|--------|---|
 97   #        ^
 98   # We generate a random number 5, and iterate through the records, adding
 99   # the current record's weight to the accumulator until the weight of the
100   # current record plus previous records is greater than the random number.
101   total_weight = records.inject(0) { |sum,record|
102     sum + weight(record)
103   }
104   current_weight = 0
105   chosen_weight  = 1 + Kernel.rand(total_weight)
106 
107   records.each do |record|
108     current_weight += weight(record)
109     return record if current_weight >= chosen_weight
110   end
111 end
ttl(service_name) click to toggle source

Returns TTL for the cached records for this service. @param [String] service_name the service whose TTL we want @return [Integer] the TTL for this service, in seconds

    # File lib/puppet/http/dns.rb
120 def ttl(service_name)
121   return @record_cache[service_name].ttl
122 end
weight(record) click to toggle source
    # File lib/puppet/http/dns.rb
113 def weight(record)
114   record.weight == 0 ? 1 : record.weight * 10
115 end

Private Instance Methods

each_priority(records) { |pri_hash| ... } click to toggle source

Groups the records by their priority and yields the groups in order of highest to lowest priority (lowest to highest numbers), one at a time. { 1 => [records], 2 => [records], etc. }

@param [[Resolv::DNS::Resource::IN::SRV]] records the list of

records for a given service

@yields [[Resolv::DNS::Resource::IN::SRV]] a group of records of

the same priority
    # File lib/puppet/http/dns.rb
148 def each_priority(records)
149   pri_hash = records.inject({}) do |groups, element|
150     groups[element.priority] ||= []
151     groups[element.priority] << element
152     groups
153   end
154 
155   pri_hash.keys.sort.each do |key|
156     yield pri_hash[key]
157   end
158 end