regurgitator.git  about / heads / tags
Read-only Rack endpoints for MogileFS and BARFS
blob d986d8b6b4eb26cf8f4df7d0327d87dd73bc6b75 2707 bytes (raw)
$ git show HEAD:lib/regurgitator/local.rb	# shows this blob on the CLI

 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
 
# -*- encoding: binary -*-
require "socket"
require "set"
require_relative 'local_file'

module Regurgitator::Local # :nodoc:

  # This is used to register local storage endpoints so we can
  # short-circuit and avoid making HTTP requests to devices
  # Must be configured by the user
  STORE_PATHS = Hash.new do |h,port|
    h[port] = {} # key: directory root path, value: whatever...
  end.compare_by_identity

  @local_addrs = {}
  @local_addrs_lock = Mutex.new

  def self.include?(addr)
    @local_addrs_lock.synchronize { @local_addrs.include?(addr) }
  end

  def self.addr
    (@local_addrs_lock.synchronize { @local_addrs.first })[0]
  end

  def self.addrs
    @local_addrs_lock.synchronize { @local_addrs.keys }
  end

  # Normally not needed unless you dynamically bring up/down network devices.
  # It may be useful to call this periodically or to integrate with
  # some network device state monitoring system.
  def self.refresh_addrs!
    @local_addrs_lock.synchronize do
      tmp = {}
      Socket.ip_address_list.keep_if do |ip|
        ip.ipv4? && ! ip.ipv4_multicast?
      end.each { |ip| tmp[ip.ip_address.freeze] = true }
      @local_addrs = tmp
    end
  end
  refresh_addrs!

  # registers a local path for a given +tcp_port+ and +directory_root+
  def self.register(tcp_port, directory_root)
    directory_root = directory_root.gsub(%r{/+\z}, "")
    dev_dirs = Dir.foreach(directory_root).grep(/\Adev\d+\z/)
    raise ArgumentError, 'no /dev\d+/ directories found' if dev_dirs.empty?
    STORE_PATHS[tcp_port][directory_root] = Set.new(dev_dirs)
  end

  # returns +nil+ if nothing was found
  # returns a path and associated File::Stat to the local FS if a
  # matching path is possible
  def device_path_stat(uri)
    Regurgitator::Local.include?(uri.host) or return
    (roots = STORE_PATHS[uri.port]).empty? and return
    uri.path =~ %r{\A/(dev\d+)/} or
            raise "BUG: path needs to match '\\A/dev\d+/' (#{uri})"
    devN = $1
    rv = nil
    roots.each do |root, dev_dirs|
      dev_dirs.include?(devN) or next
      begin
        path = "#{root}#{uri.path}"
        stat = File.stat(path)

        # multiple "/devN" paths for the same ports would be ambiguous,
        # so we cannot optimize away the HTTP request for those
        return nil if rv

        rv = [ path, stat ]
      rescue => e
        warn "E: #{e.message}, root=#{root} failed for #{uri}"
      end
    end

    rv
  end

  def trylocal(env, uri_group)
    STORE_PATHS.empty? and return
    uri_group.flatten.each do |uri|
      path_stat = device_path_stat(uri) or next
      path, stat = path_stat
      return Regurgitator::LocalFile.new(env, path, uri, stat)
    end
    nil
  end
end

git clone https://yhbt.net/regurgitator.git