module ASF

The ASF module contains a set of classes which encapsulate access to a number of data sources such as LDAP, ICLAs, auth lists, etc. This code originally was developed as a part of separate tools and was later refactored out into a common library. Some of the older tools don’t fully make use of this refactoring.

Access to documents/* (except member_apps)

Find site image files

Constants

ETCLDAP

Directory where ldap.conf resides. Differs based on operating system.

PETRI_INFO

Represents a Petri culture currently defined in petri.apache.org/info.yaml

Public Class Methods

_init_ldap(reset = false, hosts = nil) click to toggle source

private entry point for establishing a connection safely

# File lib/whimsy/asf/ldap.rb, line 274
def self._init_ldap(reset = false, hosts = nil)
  ASF::LDAP::CONNECT_LOCK.synchronize do
    # fetch the default LDAP connection details
    if @ldap_dn.nil? || @ldap_pw.nil?
      Wunderbar.info("Reading #{ASF::LDAP::LDAP_CREDS}")
      File.open(ASF::LDAP::LDAP_CREDS) do |io|
        @ldap_dn = io.readline.strip
        @ldap_pw = io.readline.strip
      end
    end
    @ldap = nil if reset
    @ldap ||= ASF::LDAP.connect(hosts)
  end
end
committerids() click to toggle source

Obtain a list of committerids from LDAP cn=committers,ou=role,ou=groups,dc=apache,dc=org

# File lib/whimsy/asf/ldap.rb, line 407
def self.committerids
  weakref(:committerids) {RoleGroup.find('committers').memberids}
end
committers() click to toggle source

Obtain a list of committers from LDAP cn=committers,ou=role,ou=groups,dc=apache,dc=org

# File lib/whimsy/asf/ldap.rb, line 401
def self.committers
  weakref(:committers) {RoleGroup.find('committers').members}
end
dereference_weakref(object, attr, &block) click to toggle source

safely dereference a weakref array attribute. Block provided is used when reference is not set or has been reclaimed. N.B. dereference_weakref(object, :XYZ, block) stores the reference in @XYZ

# File lib/whimsy/asf/ldap.rb, line 358
def self.dereference_weakref(object, attr, &block)
  attr = "@#{attr}"
  value = object.instance_variable_get(attr) || block.call
  value[0..-1]
rescue WeakRef::RefError
  value = block.call
ensure
  if not value or RUBY_VERSION.start_with? '1'
    object.instance_variable_set(attr, value)
  elsif value and not value.instance_of? WeakRef
    object.instance_variable_set(attr, WeakRef.new(value))
  end

  # keep track of which weak references are saved
  @@weakrefs << attr if object == self
end
dup(obj) click to toggle source

duplicate an object, allowing for nested hashes

# File lib/whimsy/asf.rb, line 43
def self.dup(obj)
  obj.dup.tap do |new_obj|
    new_obj.each do |key, val|
      new_obj[key] = ASF.dup(val) if val.is_a?(Hash)
    end
  end
end
flush_weakrefs() click to toggle source
# File lib/whimsy/asf/ldap.rb, line 375
def self.flush_weakrefs
  @@weakrefs.each do |attr|
    self.remove_instance_variable(attr)
  end

  @@weakrefs.clear

  # run garbage collection
  GC.start
end
ldap() click to toggle source

Returns LDAP connection, creating and binding one if necessary.

# File lib/whimsy/asf/ldap.rb, line 297
def self.ldap
  @ldap || ASF._init_ldap
  # ensure the connection is bound
  unless @ldap.bound?
    Wunderbar.debug("#{@ldap.object_id}: bind as #{@ldap_dn} as #{@ldap}")
    @ldap.bind(@ldap_dn, @ldap_pw)
  end
  @ldap
end
library_gitinfo() click to toggle source

Last commit in this clone, and the date and time of that commit.

# File lib/whimsy/asf.rb, line 37
def self.library_gitinfo
  return @info if @info
  @info = `git show --format="%h  %ci"  -s HEAD`.strip
end
library_mtime() click to toggle source

Last modified time of any file in the entire source tree.

# File lib/whimsy/asf.rb, line 29
def self.library_mtime
  parent_dir = File.dirname(File.expand_path(__FILE__))
  sources = Dir.glob("#{parent_dir}/**/*")
  times = sources.map {|source| File.mtime(source)}
  times.max.gmtime
end
memberids() click to toggle source

Obtain a list of memberids from LDAP cn=member,ou=groups,dc=apache,dc=org Note: includes some non-ASF member infrastructure contractors TODO: convert to RoleGroup at some point?

# File lib/whimsy/asf/ldap.rb, line 423
def self.memberids
  weakref(:memberids) {Group.find('member').memberids}
end
members() click to toggle source

Obtain a list of members from LDAP cn=member,ou=groups,dc=apache,dc=org Note: includes some non-ASF member infrastructure contractors TODO: convert to RoleGroup at some point?

# File lib/whimsy/asf/ldap.rb, line 415
def self.members
  weakref(:members) {Group.find('member').members}
end
pmc_chairs() click to toggle source

Obtain a list of PMC chairs from LDAP cn=pmc-chairs,ou=groups,ou=services,dc=apache,dc=org Note: this list may include non-PMC VPs.

# File lib/whimsy/asf/ldap.rb, line 395
def self.pmc_chairs
  weakref(:pmc_chairs) {Service.find('pmc-chairs').members}
end
search_archive_by_id(id) click to toggle source

Search archive for historical records of people who were committers but never submitted an ICLA (some of which are still ASF members or members of a PMC).

# File lib/whimsy/asf/icla.rb, line 329
def self.search_archive_by_id(id)
  name = JSON.parse(File.read(File.join(ASF::SVN['officers_historic'], 'committers.json')))[id]
  name = id if name and name.empty?
  name
end
search_one(base, filter, attrs=nil) click to toggle source

search with a scope of one, with automatic retry/failover

# File lib/whimsy/asf/ldap.rb, line 308
def self.search_one(base, filter, attrs=nil)
  self.search_scope(::LDAP::LDAP_SCOPE_ONELEVEL, base, filter, attrs)
end
search_scope(scope, base, filter, attrs=nil) click to toggle source

search with a specified scope, with automatic retry/failover

# File lib/whimsy/asf/ldap.rb, line 318
def self.search_scope(scope, base, filter, attrs=nil)

  # Dummy command, used for logging purposes only
  sname = %w(base one sub children)[scope] rescue scope
  cmd = "ldapsearch -x -LLL -b #{base} -s #{sname} #{filter} " +
    [attrs].flatten.join(' ')

  # try once per host, with a minimum of two tries
  attempts_left = [ASF::LDAP.hosts.length, 2].max
  target = nil # ensure access from rescue block
  begin
    attempts_left -= 1
    ASF.ldap # creates connection if necessary and binds it
    return [] unless @ldap

    target = @ldap.get_option(::LDAP::LDAP_OPT_HOST_NAME) rescue '?'
    Wunderbar.info "[#{target}] #{cmd}"

    result = @ldap.search2(base, scope, filter, attrs)
  rescue Exception => re
    if attempts_left <= 0
      Wunderbar.error "[#{target}] => #{re.inspect} for #{cmd}"
      raise
    else
      Wunderbar.warn "[#{target}] => #{re.inspect} for #{cmd}, retrying ..."
      @ldap.unbind if @ldap.bound? rescue nil
      @ldap = nil # force new connection
      sleep 1
      retry
    end
  end

  result.map! {|hash| hash[attrs]} if attrs.is_a? String

  result.compact
end
search_subtree(base, filter, attrs=nil) click to toggle source

search with a scope of subtree, with automatic retry/failover

# File lib/whimsy/asf/ldap.rb, line 313
def self.search_subtree(base, filter, attrs=nil)
  self.search_scope(::LDAP::LDAP_SCOPE_SUBTREE, base, filter, attrs)
end
weakref(attr, &block) click to toggle source

shortcut for dereference weakref N.B. weakref(:XYZ) stores the reference in @XYZ

# File lib/whimsy/asf/ldap.rb, line 388
def self.weakref(attr, &block)
  self.dereference_weakref(self, attr, &block)
end