class Puppet::HTTP::DNS

Public Class Methods

new(resolver = Resolv::DNS.new) click to toggle source
   # File lib/puppet/http/dns.rb
26 def initialize(resolver = Resolv::DNS.new)
27   @resolver = resolver
28 
29   # Stores DNS records per service, along with their TTL
30   # and the time at which they were resolved, for cache
31   # eviction.
32   @record_cache = {}
33 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
42 def each_srv_record(domain, service_name = :puppet, &block)
43   if (domain.nil? or domain.empty?)
44     Puppet.debug "Domain not known; skipping SRV lookup"
45     return
46   end
47 
48   Puppet.debug "Searching for SRV records for domain: #{domain}"
49 
50   case service_name
51     when :puppet then service = '_x-puppet'
52     when :file   then service = '_x-puppet-fileserver'
53     else              service = "_x-puppet-#{service_name}"
54   end
55   record_name = "#{service}._tcp.#{domain}"
56 
57   if @record_cache.has_key?(service_name) && !expired?(service_name)
58     records = @record_cache[service_name].records
59     Puppet.debug "Using cached record for #{record_name}"
60   else
61     records = @resolver.getresources(record_name, Resolv::DNS::Resource::IN::SRV)
62     if records.size > 0
63       @record_cache[service_name] = CacheEntry.new(records)
64     end
65     Puppet.debug "Found #{records.size} SRV records for: #{record_name}"
66   end
67 
68   if records.size == 0 && service_name != :puppet
69     # Try the generic :puppet service if no SRV records were found
70     # for the specific service.
71     each_srv_record(domain, :puppet, &block)
72   else
73     each_priority(records) do |recs|
74       while next_rr = recs.delete(find_weighted_server(recs)) #rubocop:disable Lint/AssignmentInCondition
75         Puppet.debug "Yielding next server of #{next_rr.target}:#{next_rr.port}"
76         yield next_rr.target.to_s, next_rr.port
77       end
78     end
79   end
80 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
127 def expired?(service_name)
128   entry = @record_cache[service_name]
129   if entry
130     return Time.now > (entry.resolution_time + entry.ttl)
131   else
132     return true
133   end
134 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
 87 def find_weighted_server(records)
 88   return nil if records.nil? || records.empty?
 89   return records.first if records.size == 1
 90 
 91   # Calculate the sum of all weights in the list of resource records,
 92   # This is used to then select hosts until the weight exceeds what
 93   # random number we selected.  For example, if we have weights of 1 8 and 3:
 94   #
 95   # |-|--------|---|
 96   #        ^
 97   # We generate a random number 5, and iterate through the records, adding
 98   # the current record's weight to the accumulator until the weight of the
 99   # current record plus previous records is greater than the random number.
100   total_weight = records.inject(0) { |sum,record|
101     sum + weight(record)
102   }
103   current_weight = 0
104   chosen_weight  = 1 + Kernel.rand(total_weight)
105 
106   records.each do |record|
107     current_weight += weight(record)
108     return record if current_weight >= chosen_weight
109   end
110 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
119 def ttl(service_name)
120   return @record_cache[service_name].ttl
121 end
weight(record) click to toggle source
    # File lib/puppet/http/dns.rb
112 def weight(record)
113   record.weight == 0 ? 1 : record.weight * 10
114 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
147 def each_priority(records)
148   pri_hash = records.inject({}) do |groups, element|
149     groups[element.priority] ||= []
150     groups[element.priority] << element
151     groups
152   end
153 
154   pri_hash.keys.sort.each do |key|
155     yield pri_hash[key]
156   end
157 end