ezmobius-nanite 0.4.0 → 0.4.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +70 -20
 - data/Rakefile +1 -1
 - data/bin/nanite-agent +34 -8
 - data/bin/nanite-mapper +18 -8
 - data/lib/nanite.rb +71 -0
 - data/lib/nanite/actor.rb +60 -0
 - data/lib/nanite/actor_registry.rb +24 -0
 - data/lib/nanite/admin.rb +138 -0
 - data/lib/nanite/agent.rb +250 -0
 - data/lib/nanite/amqp.rb +47 -0
 - data/lib/nanite/cluster.rb +203 -0
 - data/lib/nanite/config.rb +102 -0
 - data/lib/nanite/console.rb +39 -0
 - data/lib/nanite/daemonize.rb +13 -0
 - data/lib/nanite/dispatcher.rb +90 -0
 - data/lib/nanite/identity.rb +16 -0
 - data/lib/nanite/job.rb +104 -0
 - data/lib/nanite/local_state.rb +34 -0
 - data/lib/nanite/log.rb +64 -0
 - data/lib/nanite/log/formatter.rb +39 -0
 - data/lib/nanite/mapper.rb +277 -0
 - data/lib/nanite/mapper_proxy.rb +56 -0
 - data/lib/nanite/packets.rb +231 -0
 - data/lib/nanite/pid_file.rb +52 -0
 - data/lib/nanite/reaper.rb +38 -0
 - data/lib/nanite/security/cached_certificate_store_proxy.rb +24 -0
 - data/lib/nanite/security/certificate.rb +55 -0
 - data/lib/nanite/security/certificate_cache.rb +66 -0
 - data/lib/nanite/security/distinguished_name.rb +34 -0
 - data/lib/nanite/security/encrypted_document.rb +46 -0
 - data/lib/nanite/security/rsa_key_pair.rb +53 -0
 - data/lib/nanite/security/secure_serializer.rb +67 -0
 - data/lib/nanite/security/signature.rb +40 -0
 - data/lib/nanite/security/static_certificate_store.rb +35 -0
 - data/lib/nanite/security_provider.rb +47 -0
 - data/lib/nanite/serializer.rb +52 -0
 - data/lib/nanite/state.rb +164 -0
 - data/lib/nanite/streaming.rb +125 -0
 - data/lib/nanite/util.rb +51 -0
 - data/spec/actor_registry_spec.rb +62 -0
 - data/spec/actor_spec.rb +59 -0
 - data/spec/agent_spec.rb +235 -0
 - data/spec/cached_certificate_store_proxy_spec.rb +34 -0
 - data/spec/certificate_cache_spec.rb +49 -0
 - data/spec/certificate_spec.rb +27 -0
 - data/spec/cluster_spec.rb +300 -0
 - data/spec/dispatcher_spec.rb +136 -0
 - data/spec/distinguished_name_spec.rb +24 -0
 - data/spec/encrypted_document_spec.rb +21 -0
 - data/spec/job_spec.rb +219 -0
 - data/spec/local_state_spec.rb +112 -0
 - data/spec/packet_spec.rb +218 -0
 - data/spec/rsa_key_pair_spec.rb +33 -0
 - data/spec/secure_serializer_spec.rb +41 -0
 - data/spec/serializer_spec.rb +107 -0
 - data/spec/signature_spec.rb +30 -0
 - data/spec/spec_helper.rb +23 -0
 - data/spec/static_certificate_store_spec.rb +30 -0
 - data/spec/util_spec.rb +63 -0
 - metadata +63 -2
 
    
        data/lib/nanite/agent.rb
    ADDED
    
    | 
         @@ -0,0 +1,250 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Nanite
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Agent
         
     | 
| 
      
 3 
     | 
    
         
            +
                include AMQPHelper
         
     | 
| 
      
 4 
     | 
    
         
            +
                include FileStreaming
         
     | 
| 
      
 5 
     | 
    
         
            +
                include ConsoleHelper
         
     | 
| 
      
 6 
     | 
    
         
            +
                include DaemonizeHelper
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                attr_reader :identity, :options, :serializer, :dispatcher, :registry, :amq, :tags
         
     | 
| 
      
 9 
     | 
    
         
            +
                attr_accessor :status_proc
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                DEFAULT_OPTIONS = COMMON_DEFAULT_OPTIONS.merge({:user => 'nanite', :ping_time => 15,
         
     | 
| 
      
 12 
     | 
    
         
            +
                  :default_services => []}) unless defined?(DEFAULT_OPTIONS)
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                # Initializes a new agent and establishes AMQP connection.
         
     | 
| 
      
 15 
     | 
    
         
            +
                # This must be used inside EM.run block or if EventMachine reactor
         
     | 
| 
      
 16 
     | 
    
         
            +
                # is already started, for instance, by a Thin server that your Merb/Rails
         
     | 
| 
      
 17 
     | 
    
         
            +
                # application runs on.
         
     | 
| 
      
 18 
     | 
    
         
            +
                #
         
     | 
| 
      
 19 
     | 
    
         
            +
                # Agent options:
         
     | 
| 
      
 20 
     | 
    
         
            +
                #
         
     | 
| 
      
 21 
     | 
    
         
            +
                # identity    : identity of this agent, may be any string
         
     | 
| 
      
 22 
     | 
    
         
            +
                #
         
     | 
| 
      
 23 
     | 
    
         
            +
                # status_proc : a callable object that returns agent load as a string,
         
     | 
| 
      
 24 
     | 
    
         
            +
                #               defaults to load averages string extracted from `uptime`
         
     | 
| 
      
 25 
     | 
    
         
            +
                # format      : format to use for packets serialization. One of the three:
         
     | 
| 
      
 26 
     | 
    
         
            +
                #               :marshall, :json, or :yaml. Defaults to
         
     | 
| 
      
 27 
     | 
    
         
            +
                #               Ruby's Marshall format. For interoperability with
         
     | 
| 
      
 28 
     | 
    
         
            +
                #               AMQP clients implemented in other languages, use JSON.
         
     | 
| 
      
 29 
     | 
    
         
            +
                #
         
     | 
| 
      
 30 
     | 
    
         
            +
                #               Note that Nanite uses JSON gem,
         
     | 
| 
      
 31 
     | 
    
         
            +
                #               and ActiveSupport's JSON encoder may cause clashes
         
     | 
| 
      
 32 
     | 
    
         
            +
                #               if ActiveSupport is loaded after JSON gem.
         
     | 
| 
      
 33 
     | 
    
         
            +
                #
         
     | 
| 
      
 34 
     | 
    
         
            +
                # root        : application root for this agent, defaults to Dir.pwd
         
     | 
| 
      
 35 
     | 
    
         
            +
                #
         
     | 
| 
      
 36 
     | 
    
         
            +
                # log_dir     : path to directory where agent stores it's log file
         
     | 
| 
      
 37 
     | 
    
         
            +
                #               if not given, app_root is used.
         
     | 
| 
      
 38 
     | 
    
         
            +
                #
         
     | 
| 
      
 39 
     | 
    
         
            +
                # file_root   : path to directory to files this agent provides
         
     | 
| 
      
 40 
     | 
    
         
            +
                #               defaults to app_root/files
         
     | 
| 
      
 41 
     | 
    
         
            +
                #
         
     | 
| 
      
 42 
     | 
    
         
            +
                # ping_time   : time interval in seconds between two subsequent heartbeat messages
         
     | 
| 
      
 43 
     | 
    
         
            +
                #               this agent broadcasts. Default value is 15.
         
     | 
| 
      
 44 
     | 
    
         
            +
                #
         
     | 
| 
      
 45 
     | 
    
         
            +
                # console     : true tells Nanite to start interactive console
         
     | 
| 
      
 46 
     | 
    
         
            +
                #
         
     | 
| 
      
 47 
     | 
    
         
            +
                # daemonize   : true tells Nanite to daemonize
         
     | 
| 
      
 48 
     | 
    
         
            +
                #
         
     | 
| 
      
 49 
     | 
    
         
            +
                # pid_dir     : path to the directory where the agent stores its pid file (only if daemonized)
         
     | 
| 
      
 50 
     | 
    
         
            +
                #               defaults to the root or the current working directory.
         
     | 
| 
      
 51 
     | 
    
         
            +
                #
         
     | 
| 
      
 52 
     | 
    
         
            +
                # services    : list of services provided by this agent, by default
         
     | 
| 
      
 53 
     | 
    
         
            +
                #               all methods exposed by actors are listed
         
     | 
| 
      
 54 
     | 
    
         
            +
                #
         
     | 
| 
      
 55 
     | 
    
         
            +
                # single_threaded: Run all operations in one thread
         
     | 
| 
      
 56 
     | 
    
         
            +
                #
         
     | 
| 
      
 57 
     | 
    
         
            +
                # Connection options:
         
     | 
| 
      
 58 
     | 
    
         
            +
                #
         
     | 
| 
      
 59 
     | 
    
         
            +
                # vhost    : AMQP broker vhost that should be used
         
     | 
| 
      
 60 
     | 
    
         
            +
                #
         
     | 
| 
      
 61 
     | 
    
         
            +
                # user     : AMQP broker user
         
     | 
| 
      
 62 
     | 
    
         
            +
                #
         
     | 
| 
      
 63 
     | 
    
         
            +
                # pass     : AMQP broker password
         
     | 
| 
      
 64 
     | 
    
         
            +
                #
         
     | 
| 
      
 65 
     | 
    
         
            +
                # host     : host AMQP broker (or node of interest) runs on,
         
     | 
| 
      
 66 
     | 
    
         
            +
                #            defaults to 0.0.0.0
         
     | 
| 
      
 67 
     | 
    
         
            +
                #
         
     | 
| 
      
 68 
     | 
    
         
            +
                # port     : port AMQP broker (or node of interest) runs on,
         
     | 
| 
      
 69 
     | 
    
         
            +
                #            this defaults to 5672, port used by some widely
         
     | 
| 
      
 70 
     | 
    
         
            +
                #            used AMQP brokers (RabbitMQ and ZeroMQ)
         
     | 
| 
      
 71 
     | 
    
         
            +
                #
         
     | 
| 
      
 72 
     | 
    
         
            +
                # On start Nanite reads config.yml, so it is common to specify
         
     | 
| 
      
 73 
     | 
    
         
            +
                # options in the YAML file. However, when both Ruby code options
         
     | 
| 
      
 74 
     | 
    
         
            +
                # and YAML file specify option, Ruby code options take precedence.
         
     | 
| 
      
 75 
     | 
    
         
            +
                def self.start(options = {})
         
     | 
| 
      
 76 
     | 
    
         
            +
                  agent = new(options)
         
     | 
| 
      
 77 
     | 
    
         
            +
                  agent.run
         
     | 
| 
      
 78 
     | 
    
         
            +
                  agent
         
     | 
| 
      
 79 
     | 
    
         
            +
                end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                def initialize(opts)
         
     | 
| 
      
 82 
     | 
    
         
            +
                  set_configuration(opts)
         
     | 
| 
      
 83 
     | 
    
         
            +
                  @tags = []
         
     | 
| 
      
 84 
     | 
    
         
            +
                  @tags << opts[:tag]
         
     | 
| 
      
 85 
     | 
    
         
            +
                  @tags.flatten!
         
     | 
| 
      
 86 
     | 
    
         
            +
                  @options.freeze
         
     | 
| 
      
 87 
     | 
    
         
            +
                end
         
     | 
| 
      
 88 
     | 
    
         
            +
                
         
     | 
| 
      
 89 
     | 
    
         
            +
                def run
         
     | 
| 
      
 90 
     | 
    
         
            +
                  log_path = false
         
     | 
| 
      
 91 
     | 
    
         
            +
                  if @options[:daemonize]
         
     | 
| 
      
 92 
     | 
    
         
            +
                    log_path = (@options[:log_dir] || @options[:root] || Dir.pwd)
         
     | 
| 
      
 93 
     | 
    
         
            +
                  end
         
     | 
| 
      
 94 
     | 
    
         
            +
                  Log.init(@identity, log_path)
         
     | 
| 
      
 95 
     | 
    
         
            +
                  Log.level = @options[:log_level] if @options[:log_level]
         
     | 
| 
      
 96 
     | 
    
         
            +
                  @serializer = Serializer.new(@options[:format])
         
     | 
| 
      
 97 
     | 
    
         
            +
                  @status_proc = lambda { parse_uptime(`uptime`) rescue 'no status' }
         
     | 
| 
      
 98 
     | 
    
         
            +
                  pid_file = PidFile.new(@identity, @options)
         
     | 
| 
      
 99 
     | 
    
         
            +
                  pid_file.check
         
     | 
| 
      
 100 
     | 
    
         
            +
                  if @options[:daemonize]
         
     | 
| 
      
 101 
     | 
    
         
            +
                    daemonize
         
     | 
| 
      
 102 
     | 
    
         
            +
                    pid_file.write
         
     | 
| 
      
 103 
     | 
    
         
            +
                    at_exit { pid_file.remove }
         
     | 
| 
      
 104 
     | 
    
         
            +
                  end
         
     | 
| 
      
 105 
     | 
    
         
            +
                  @amq = start_amqp(@options)
         
     | 
| 
      
 106 
     | 
    
         
            +
                  @registry = ActorRegistry.new
         
     | 
| 
      
 107 
     | 
    
         
            +
                  @dispatcher = Dispatcher.new(@amq, @registry, @serializer, @identity, @options)
         
     | 
| 
      
 108 
     | 
    
         
            +
                  setup_mapper_proxy
         
     | 
| 
      
 109 
     | 
    
         
            +
                  load_actors
         
     | 
| 
      
 110 
     | 
    
         
            +
                  setup_traps
         
     | 
| 
      
 111 
     | 
    
         
            +
                  setup_queue
         
     | 
| 
      
 112 
     | 
    
         
            +
                  advertise_services
         
     | 
| 
      
 113 
     | 
    
         
            +
                  setup_heartbeat
         
     | 
| 
      
 114 
     | 
    
         
            +
                  at_exit { un_register } unless $TESTING
         
     | 
| 
      
 115 
     | 
    
         
            +
                  start_console if @options[:console] && !@options[:daemonize]
         
     | 
| 
      
 116 
     | 
    
         
            +
                end
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
                def register(actor, prefix = nil)
         
     | 
| 
      
 119 
     | 
    
         
            +
                  registry.register(actor, prefix)
         
     | 
| 
      
 120 
     | 
    
         
            +
                end
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                # Can be used in agent's initialization file to register a security module
         
     | 
| 
      
 123 
     | 
    
         
            +
                # This security module 'authorize' method will be called back whenever the
         
     | 
| 
      
 124 
     | 
    
         
            +
                # agent receives a request and will be given the corresponding deliverable.
         
     | 
| 
      
 125 
     | 
    
         
            +
                # It should return 'true' for the request to proceed.
         
     | 
| 
      
 126 
     | 
    
         
            +
                # Requests will return 'deny_token' or the string "Denied" by default when
         
     | 
| 
      
 127 
     | 
    
         
            +
                # 'authorize' does not return 'true'.
         
     | 
| 
      
 128 
     | 
    
         
            +
                def register_security(security, deny_token = "Denied")
         
     | 
| 
      
 129 
     | 
    
         
            +
                  @security = security
         
     | 
| 
      
 130 
     | 
    
         
            +
                  @deny_token = deny_token
         
     | 
| 
      
 131 
     | 
    
         
            +
                end
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
                protected
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
                def set_configuration(opts)
         
     | 
| 
      
 136 
     | 
    
         
            +
                  @options = DEFAULT_OPTIONS.clone
         
     | 
| 
      
 137 
     | 
    
         
            +
                  root = opts[:root] || @options[:root]
         
     | 
| 
      
 138 
     | 
    
         
            +
                  custom_config = if root
         
     | 
| 
      
 139 
     | 
    
         
            +
                    file = File.expand_path(File.join(root, 'config.yml'))
         
     | 
| 
      
 140 
     | 
    
         
            +
                    File.exists?(file) ? (YAML.load(IO.read(file)) || {}) : {}
         
     | 
| 
      
 141 
     | 
    
         
            +
                  else
         
     | 
| 
      
 142 
     | 
    
         
            +
                    {}
         
     | 
| 
      
 143 
     | 
    
         
            +
                  end
         
     | 
| 
      
 144 
     | 
    
         
            +
                  opts.delete(:identity) unless opts[:identity]
         
     | 
| 
      
 145 
     | 
    
         
            +
                  @options.update(custom_config.merge(opts))
         
     | 
| 
      
 146 
     | 
    
         
            +
                  @options[:file_root] ||= File.join(@options[:root], 'files')
         
     | 
| 
      
 147 
     | 
    
         
            +
                  return @identity = "nanite-#{@options[:identity]}" if @options[:identity]
         
     | 
| 
      
 148 
     | 
    
         
            +
                  token = Identity.generate
         
     | 
| 
      
 149 
     | 
    
         
            +
                  @identity = "nanite-#{token}"
         
     | 
| 
      
 150 
     | 
    
         
            +
                  File.open(File.expand_path(File.join(@options[:root], 'config.yml')), 'w') do |fd|
         
     | 
| 
      
 151 
     | 
    
         
            +
                    fd.write(YAML.dump(custom_config.merge(:identity => token)))
         
     | 
| 
      
 152 
     | 
    
         
            +
                  end
         
     | 
| 
      
 153 
     | 
    
         
            +
                end
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
                def load_actors
         
     | 
| 
      
 156 
     | 
    
         
            +
                  return unless options[:root]
         
     | 
| 
      
 157 
     | 
    
         
            +
                  actors_dir = @options[:actors_dir] || "#{@options[:root]}/actors"
         
     | 
| 
      
 158 
     | 
    
         
            +
                  actors = @options[:actors]
         
     | 
| 
      
 159 
     | 
    
         
            +
                  Dir["#{actors_dir}/*.rb"].each do |actor|
         
     | 
| 
      
 160 
     | 
    
         
            +
                    next if actors && !actors.include?(File.basename(actor, ".rb"))
         
     | 
| 
      
 161 
     | 
    
         
            +
                    Nanite::Log.info("loading actor: #{actor}")
         
     | 
| 
      
 162 
     | 
    
         
            +
                    require actor
         
     | 
| 
      
 163 
     | 
    
         
            +
                  end
         
     | 
| 
      
 164 
     | 
    
         
            +
                  init_path = @options[:initrb] || File.join(options[:root], 'init.rb')
         
     | 
| 
      
 165 
     | 
    
         
            +
                  instance_eval(File.read(init_path), init_path) if File.exist?(init_path)
         
     | 
| 
      
 166 
     | 
    
         
            +
                end
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
                def receive(packet)
         
     | 
| 
      
 169 
     | 
    
         
            +
                  case packet
         
     | 
| 
      
 170 
     | 
    
         
            +
                  when Advertise
         
     | 
| 
      
 171 
     | 
    
         
            +
                    Nanite::Log.debug("handling Advertise: #{packet.inspect}")
         
     | 
| 
      
 172 
     | 
    
         
            +
                    advertise_services
         
     | 
| 
      
 173 
     | 
    
         
            +
                  when Request, Push
         
     | 
| 
      
 174 
     | 
    
         
            +
                    Nanite::Log.debug("handling Request: #{packet.inspect}")
         
     | 
| 
      
 175 
     | 
    
         
            +
                    if @security && !@security.authorize(packet)
         
     | 
| 
      
 176 
     | 
    
         
            +
                      if packet.kind_of?(Request)
         
     | 
| 
      
 177 
     | 
    
         
            +
                        r = Result.new(packet.token, packet.reply_to, @deny_token, identity)
         
     | 
| 
      
 178 
     | 
    
         
            +
                        amq.queue(packet.reply_to, :no_declare => options[:secure]).publish(serializer.dump(r))
         
     | 
| 
      
 179 
     | 
    
         
            +
                      end
         
     | 
| 
      
 180 
     | 
    
         
            +
                    else
         
     | 
| 
      
 181 
     | 
    
         
            +
                      dispatcher.dispatch(packet)
         
     | 
| 
      
 182 
     | 
    
         
            +
                    end
         
     | 
| 
      
 183 
     | 
    
         
            +
                  when Result
         
     | 
| 
      
 184 
     | 
    
         
            +
                    Nanite::Log.debug("handling Result: #{packet.inspect}")
         
     | 
| 
      
 185 
     | 
    
         
            +
                    @mapper_proxy.handle_result(packet)
         
     | 
| 
      
 186 
     | 
    
         
            +
                  when IntermediateMessage
         
     | 
| 
      
 187 
     | 
    
         
            +
                    Nanite::Log.debug("handling Intermediate Result: #{packet.inspect}")
         
     | 
| 
      
 188 
     | 
    
         
            +
                    @mapper_proxy.handle_intermediate_result(packet)
         
     | 
| 
      
 189 
     | 
    
         
            +
                  end
         
     | 
| 
      
 190 
     | 
    
         
            +
                end
         
     | 
| 
      
 191 
     | 
    
         
            +
                
         
     | 
| 
      
 192 
     | 
    
         
            +
                def tag(*tags)
         
     | 
| 
      
 193 
     | 
    
         
            +
                  tags.each {|t| @tags << t}
         
     | 
| 
      
 194 
     | 
    
         
            +
                  @tags.uniq!
         
     | 
| 
      
 195 
     | 
    
         
            +
                end
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
      
 197 
     | 
    
         
            +
                def setup_queue
         
     | 
| 
      
 198 
     | 
    
         
            +
                  amq.queue(identity, :durable => true).subscribe(:ack => true) do |info, msg|
         
     | 
| 
      
 199 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 200 
     | 
    
         
            +
                      info.ack
         
     | 
| 
      
 201 
     | 
    
         
            +
                      packet = serializer.load(msg)
         
     | 
| 
      
 202 
     | 
    
         
            +
                      receive(packet)
         
     | 
| 
      
 203 
     | 
    
         
            +
                    rescue Exception => e
         
     | 
| 
      
 204 
     | 
    
         
            +
                      Nanite::Log.error("Error handling packet: #{e.message}")
         
     | 
| 
      
 205 
     | 
    
         
            +
                    end
         
     | 
| 
      
 206 
     | 
    
         
            +
                  end
         
     | 
| 
      
 207 
     | 
    
         
            +
                end
         
     | 
| 
      
 208 
     | 
    
         
            +
             
     | 
| 
      
 209 
     | 
    
         
            +
                def setup_heartbeat
         
     | 
| 
      
 210 
     | 
    
         
            +
                  EM.add_periodic_timer(options[:ping_time]) do
         
     | 
| 
      
 211 
     | 
    
         
            +
                    amq.fanout('heartbeat', :no_declare => options[:secure]).publish(serializer.dump(Ping.new(identity, status_proc.call)))
         
     | 
| 
      
 212 
     | 
    
         
            +
                  end
         
     | 
| 
      
 213 
     | 
    
         
            +
                end
         
     | 
| 
      
 214 
     | 
    
         
            +
                
         
     | 
| 
      
 215 
     | 
    
         
            +
                def setup_mapper_proxy
         
     | 
| 
      
 216 
     | 
    
         
            +
                  @mapper_proxy = MapperProxy.new(identity, options)
         
     | 
| 
      
 217 
     | 
    
         
            +
                end
         
     | 
| 
      
 218 
     | 
    
         
            +
                
         
     | 
| 
      
 219 
     | 
    
         
            +
                def setup_traps
         
     | 
| 
      
 220 
     | 
    
         
            +
                  ['INT', 'TERM'].each do |sig|
         
     | 
| 
      
 221 
     | 
    
         
            +
                    old = trap(sig) do
         
     | 
| 
      
 222 
     | 
    
         
            +
                      un_register
         
     | 
| 
      
 223 
     | 
    
         
            +
                      amq.instance_variable_get('@connection').close do
         
     | 
| 
      
 224 
     | 
    
         
            +
                        EM.stop
         
     | 
| 
      
 225 
     | 
    
         
            +
                        old.call if old.is_a? Proc
         
     | 
| 
      
 226 
     | 
    
         
            +
                      end
         
     | 
| 
      
 227 
     | 
    
         
            +
                    end
         
     | 
| 
      
 228 
     | 
    
         
            +
                  end
         
     | 
| 
      
 229 
     | 
    
         
            +
                end
         
     | 
| 
      
 230 
     | 
    
         
            +
                
         
     | 
| 
      
 231 
     | 
    
         
            +
                def un_register
         
     | 
| 
      
 232 
     | 
    
         
            +
                  unless @unregistered
         
     | 
| 
      
 233 
     | 
    
         
            +
                    @unregistered = true
         
     | 
| 
      
 234 
     | 
    
         
            +
                    amq.fanout('registration', :no_declare => options[:secure]).publish(serializer.dump(UnRegister.new(identity)))
         
     | 
| 
      
 235 
     | 
    
         
            +
                  end
         
     | 
| 
      
 236 
     | 
    
         
            +
                end
         
     | 
| 
      
 237 
     | 
    
         
            +
             
     | 
| 
      
 238 
     | 
    
         
            +
                def advertise_services
         
     | 
| 
      
 239 
     | 
    
         
            +
                  Nanite::Log.debug("advertise_services: #{registry.services.inspect}")
         
     | 
| 
      
 240 
     | 
    
         
            +
                  amq.fanout('registration', :no_declare => options[:secure]).publish(serializer.dump(Register.new(identity, registry.services, status_proc.call, self.tags)))
         
     | 
| 
      
 241 
     | 
    
         
            +
                end
         
     | 
| 
      
 242 
     | 
    
         
            +
             
     | 
| 
      
 243 
     | 
    
         
            +
                def parse_uptime(up)
         
     | 
| 
      
 244 
     | 
    
         
            +
                  if up =~ /load averages?: (.*)/
         
     | 
| 
      
 245 
     | 
    
         
            +
                    a,b,c = $1.split(/\s+|,\s+/)
         
     | 
| 
      
 246 
     | 
    
         
            +
                    (a.to_f + b.to_f + c.to_f) / 3
         
     | 
| 
      
 247 
     | 
    
         
            +
                  end
         
     | 
| 
      
 248 
     | 
    
         
            +
                end
         
     | 
| 
      
 249 
     | 
    
         
            +
              end
         
     | 
| 
      
 250 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/nanite/amqp.rb
    ADDED
    
    | 
         @@ -0,0 +1,47 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            class MQ
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Queue
         
     | 
| 
      
 3 
     | 
    
         
            +
                # Asks the broker to redeliver all unacknowledged messages on a
         
     | 
| 
      
 4 
     | 
    
         
            +
                # specifieid channel. Zero or more messages may be redelivered.
         
     | 
| 
      
 5 
     | 
    
         
            +
                #
         
     | 
| 
      
 6 
     | 
    
         
            +
                # * requeue (default false)
         
     | 
| 
      
 7 
     | 
    
         
            +
                # If this parameter is false, the message will be redelivered to the original recipient.
         
     | 
| 
      
 8 
     | 
    
         
            +
                # If this flag is true, the server will attempt to requeue the message, potentially then
         
     | 
| 
      
 9 
     | 
    
         
            +
                # delivering it to an alternative subscriber.
         
     | 
| 
      
 10 
     | 
    
         
            +
                #
         
     | 
| 
      
 11 
     | 
    
         
            +
                def recover requeue = false
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @mq.callback{
         
     | 
| 
      
 13 
     | 
    
         
            +
                    @mq.send Protocol::Basic::Recover.new({ :requeue => requeue })
         
     | 
| 
      
 14 
     | 
    
         
            +
                  }
         
     | 
| 
      
 15 
     | 
    
         
            +
                  self
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            # monkey patch to the amqp gem that adds :no_declare => true option for new 
         
     | 
| 
      
 21 
     | 
    
         
            +
            # Exchange objects. This allows us to send messeages to exchanges that are
         
     | 
| 
      
 22 
     | 
    
         
            +
            # declared by the mappers and that we have no configuration priviledges on.
         
     | 
| 
      
 23 
     | 
    
         
            +
            # temporary until we get this into amqp proper
         
     | 
| 
      
 24 
     | 
    
         
            +
            MQ::Exchange.class_eval do
         
     | 
| 
      
 25 
     | 
    
         
            +
              def initialize mq, type, name, opts = {}
         
     | 
| 
      
 26 
     | 
    
         
            +
                @mq = mq
         
     | 
| 
      
 27 
     | 
    
         
            +
                @type, @name, @opts = type, name, opts
         
     | 
| 
      
 28 
     | 
    
         
            +
                @mq.exchanges[@name = name] ||= self
         
     | 
| 
      
 29 
     | 
    
         
            +
                @key = opts[:key]
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                @mq.callback{
         
     | 
| 
      
 32 
     | 
    
         
            +
                  @mq.send AMQP::Protocol::Exchange::Declare.new({ :exchange => name,
         
     | 
| 
      
 33 
     | 
    
         
            +
                                                             :type => type,
         
     | 
| 
      
 34 
     | 
    
         
            +
                                                             :nowait => true }.merge(opts))
         
     | 
| 
      
 35 
     | 
    
         
            +
                } unless name == "amq.#{type}" or name == ''  or opts[:no_declare]
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
            end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
            module Nanite
         
     | 
| 
      
 40 
     | 
    
         
            +
              module AMQPHelper
         
     | 
| 
      
 41 
     | 
    
         
            +
                def start_amqp(options)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  connection = AMQP.connect(:user => options[:user], :pass => options[:pass], :vhost => options[:vhost],
         
     | 
| 
      
 43 
     | 
    
         
            +
                    :host => options[:host], :port => (options[:port] || ::AMQP::PORT).to_i, :insist => options[:insist] || false)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  MQ.new(connection)
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
              end
         
     | 
| 
      
 47 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,203 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Nanite
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Cluster
         
     | 
| 
      
 3 
     | 
    
         
            +
                attr_reader :agent_timeout, :nanites, :reaper, :serializer, :identity, :amq, :redis, :mapper
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                def initialize(amq, agent_timeout, identity, serializer, mapper, redis=nil)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  @amq = amq
         
     | 
| 
      
 7 
     | 
    
         
            +
                  @agent_timeout = agent_timeout
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @identity = identity
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @serializer = serializer
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @mapper = mapper
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @redis = redis
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @security = SecurityProvider.get
         
     | 
| 
      
 13 
     | 
    
         
            +
                  if redis
         
     | 
| 
      
 14 
     | 
    
         
            +
                    Nanite::Log.info("using redis for state storage")
         
     | 
| 
      
 15 
     | 
    
         
            +
                    require 'nanite/state'
         
     | 
| 
      
 16 
     | 
    
         
            +
                    @nanites = ::Nanite::State.new(redis)
         
     | 
| 
      
 17 
     | 
    
         
            +
                  else
         
     | 
| 
      
 18 
     | 
    
         
            +
                    require 'nanite/local_state'
         
     | 
| 
      
 19 
     | 
    
         
            +
                    @nanites = Nanite::LocalState.new
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @reaper = Reaper.new(agent_timeout)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  setup_queues
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                # determine which nanites should receive the given request
         
     | 
| 
      
 26 
     | 
    
         
            +
                def targets_for(request)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  return [request.target] if request.target
         
     | 
| 
      
 28 
     | 
    
         
            +
                  __send__(request.selector, request.type, request.tags).collect {|name, state| name }
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                # adds nanite to nanites map: key is nanite's identity
         
     | 
| 
      
 32 
     | 
    
         
            +
                # and value is a services/status pair implemented
         
     | 
| 
      
 33 
     | 
    
         
            +
                # as a hash
         
     | 
| 
      
 34 
     | 
    
         
            +
                def register(reg)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  case reg
         
     | 
| 
      
 36 
     | 
    
         
            +
                  when Register
         
     | 
| 
      
 37 
     | 
    
         
            +
                    if @security.authorize_registration(reg)
         
     | 
| 
      
 38 
     | 
    
         
            +
                      nanites[reg.identity] = { :services => reg.services, :status => reg.status, :tags => reg.tags }
         
     | 
| 
      
 39 
     | 
    
         
            +
                      reaper.timeout(reg.identity, agent_timeout + 1) { nanites.delete(reg.identity) }
         
     | 
| 
      
 40 
     | 
    
         
            +
                      Nanite::Log.info("registered: #{reg.identity}, #{nanites[reg.identity].inspect}")
         
     | 
| 
      
 41 
     | 
    
         
            +
                    else
         
     | 
| 
      
 42 
     | 
    
         
            +
                      Nanite::Log.warning("registration of #{reg.inspect} not authorized")
         
     | 
| 
      
 43 
     | 
    
         
            +
                    end
         
     | 
| 
      
 44 
     | 
    
         
            +
                  when UnRegister
         
     | 
| 
      
 45 
     | 
    
         
            +
                    nanites.delete(reg.identity)
         
     | 
| 
      
 46 
     | 
    
         
            +
                    Nanite::Log.info("un-registering: #{reg.identity}")
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                def route(request, targets)
         
     | 
| 
      
 51 
     | 
    
         
            +
                  EM.next_tick { targets.map { |target| publish(request, target) } }
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                def publish(request, target)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  # We need to initialize the 'target' field of the request object so that the serializer has
         
     | 
| 
      
 56 
     | 
    
         
            +
                  # access to it.
         
     | 
| 
      
 57 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 58 
     | 
    
         
            +
                    old_target = request.target
         
     | 
| 
      
 59 
     | 
    
         
            +
                    request.target = target unless target == 'mapper-offline'
         
     | 
| 
      
 60 
     | 
    
         
            +
                    amq.queue(target).publish(serializer.dump(request), :persistent => request.persistent)
         
     | 
| 
      
 61 
     | 
    
         
            +
                  ensure
         
     | 
| 
      
 62 
     | 
    
         
            +
                    request.target = old_target
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                protected
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                # updates nanite information (last ping timestamps, status)
         
     | 
| 
      
 69 
     | 
    
         
            +
                # when heartbeat message is received
         
     | 
| 
      
 70 
     | 
    
         
            +
                def handle_ping(ping)
         
     | 
| 
      
 71 
     | 
    
         
            +
                  if nanite = nanites[ping.identity]
         
     | 
| 
      
 72 
     | 
    
         
            +
                    nanite[:status] = ping.status
         
     | 
| 
      
 73 
     | 
    
         
            +
                    reaper.reset_with_autoregister_hack(ping.identity, agent_timeout + 1) { nanites.delete(ping.identity) }
         
     | 
| 
      
 74 
     | 
    
         
            +
                  else
         
     | 
| 
      
 75 
     | 
    
         
            +
                    amq.queue(ping.identity).publish(serializer.dump(Advertise.new))
         
     | 
| 
      
 76 
     | 
    
         
            +
                  end
         
     | 
| 
      
 77 
     | 
    
         
            +
                end
         
     | 
| 
      
 78 
     | 
    
         
            +
                
         
     | 
| 
      
 79 
     | 
    
         
            +
                # forward request coming from agent
         
     | 
| 
      
 80 
     | 
    
         
            +
                def handle_request(request)
         
     | 
| 
      
 81 
     | 
    
         
            +
                  if @security.authorize_request(request)
         
     | 
| 
      
 82 
     | 
    
         
            +
                    result = Result.new(request.token, request.from, nil, mapper.identity)
         
     | 
| 
      
 83 
     | 
    
         
            +
                    intm_handler = lambda do |res|
         
     | 
| 
      
 84 
     | 
    
         
            +
                      result.results = res
         
     | 
| 
      
 85 
     | 
    
         
            +
                      forward_response(result, request.persistent)
         
     | 
| 
      
 86 
     | 
    
         
            +
                    end
         
     | 
| 
      
 87 
     | 
    
         
            +
                    ok = mapper.send_request(request, :intermediate_handler => intm_handler) do |res|
         
     | 
| 
      
 88 
     | 
    
         
            +
                      result.results = res
         
     | 
| 
      
 89 
     | 
    
         
            +
                      forward_response(result, request.persistent)
         
     | 
| 
      
 90 
     | 
    
         
            +
                    end
         
     | 
| 
      
 91 
     | 
    
         
            +
                    if ok == false
         
     | 
| 
      
 92 
     | 
    
         
            +
                      forward_response(result, request.persistent)
         
     | 
| 
      
 93 
     | 
    
         
            +
                    end
         
     | 
| 
      
 94 
     | 
    
         
            +
                  else
         
     | 
| 
      
 95 
     | 
    
         
            +
                    Nanite::Log.warning("request #{request.inspect} not authorized")
         
     | 
| 
      
 96 
     | 
    
         
            +
                  end
         
     | 
| 
      
 97 
     | 
    
         
            +
                end
         
     | 
| 
      
 98 
     | 
    
         
            +
                
         
     | 
| 
      
 99 
     | 
    
         
            +
                # forward response back to agent that originally made the request
         
     | 
| 
      
 100 
     | 
    
         
            +
                def forward_response(res, persistent)
         
     | 
| 
      
 101 
     | 
    
         
            +
                  amq.queue(res.to).publish(serializer.dump(res), :persistent => persistent)
         
     | 
| 
      
 102 
     | 
    
         
            +
                end
         
     | 
| 
      
 103 
     | 
    
         
            +
                
         
     | 
| 
      
 104 
     | 
    
         
            +
                # returns least loaded nanite that provides given service
         
     | 
| 
      
 105 
     | 
    
         
            +
                def least_loaded(service, tags=[])
         
     | 
| 
      
 106 
     | 
    
         
            +
                  candidates = nanites_providing(service,tags)
         
     | 
| 
      
 107 
     | 
    
         
            +
                  return [] if candidates.empty?
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                  [candidates.min { |a,b| a[1][:status] <=> b[1][:status] }]
         
     | 
| 
      
 110 
     | 
    
         
            +
                end
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                # returns all nanites that provide given service
         
     | 
| 
      
 113 
     | 
    
         
            +
                def all(service, tags=[])
         
     | 
| 
      
 114 
     | 
    
         
            +
                  nanites_providing(service,tags)
         
     | 
| 
      
 115 
     | 
    
         
            +
                end
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
                # returns a random nanite
         
     | 
| 
      
 118 
     | 
    
         
            +
                def random(service, tags=[])
         
     | 
| 
      
 119 
     | 
    
         
            +
                  candidates = nanites_providing(service,tags)
         
     | 
| 
      
 120 
     | 
    
         
            +
                  return [] if candidates.empty?
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                  [candidates[rand(candidates.size)]]
         
     | 
| 
      
 123 
     | 
    
         
            +
                end
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                # selects next nanite that provides given service
         
     | 
| 
      
 126 
     | 
    
         
            +
                # using round robin rotation
         
     | 
| 
      
 127 
     | 
    
         
            +
                def rr(service, tags=[])
         
     | 
| 
      
 128 
     | 
    
         
            +
                  @last ||= {}
         
     | 
| 
      
 129 
     | 
    
         
            +
                  @last[service] ||= 0
         
     | 
| 
      
 130 
     | 
    
         
            +
                  candidates = nanites_providing(service,tags)
         
     | 
| 
      
 131 
     | 
    
         
            +
                  return [] if candidates.empty?
         
     | 
| 
      
 132 
     | 
    
         
            +
                  @last[service] = 0 if @last[service] >= candidates.size
         
     | 
| 
      
 133 
     | 
    
         
            +
                  candidate = candidates[@last[service]]
         
     | 
| 
      
 134 
     | 
    
         
            +
                  @last[service] += 1
         
     | 
| 
      
 135 
     | 
    
         
            +
                  [candidate]
         
     | 
| 
      
 136 
     | 
    
         
            +
                end
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                # returns all nanites that provide the given service
         
     | 
| 
      
 139 
     | 
    
         
            +
                def nanites_providing(service, *tags)
         
     | 
| 
      
 140 
     | 
    
         
            +
                  nanites.nanites_for(service, *tags)
         
     | 
| 
      
 141 
     | 
    
         
            +
                end
         
     | 
| 
      
 142 
     | 
    
         
            +
             
     | 
| 
      
 143 
     | 
    
         
            +
                def setup_queues
         
     | 
| 
      
 144 
     | 
    
         
            +
                  setup_heartbeat_queue
         
     | 
| 
      
 145 
     | 
    
         
            +
                  setup_registration_queue
         
     | 
| 
      
 146 
     | 
    
         
            +
                  setup_request_queue
         
     | 
| 
      
 147 
     | 
    
         
            +
                end
         
     | 
| 
      
 148 
     | 
    
         
            +
             
     | 
| 
      
 149 
     | 
    
         
            +
                def setup_heartbeat_queue
         
     | 
| 
      
 150 
     | 
    
         
            +
                  handler = lambda do |ping|
         
     | 
| 
      
 151 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 152 
     | 
    
         
            +
                      ping = serializer.load(ping)
         
     | 
| 
      
 153 
     | 
    
         
            +
                      Nanite::Log.debug("got heartbeat from #{ping.identity}") if ping.respond_to?(:identity)
         
     | 
| 
      
 154 
     | 
    
         
            +
                      handle_ping(ping)
         
     | 
| 
      
 155 
     | 
    
         
            +
                    rescue Exception => e
         
     | 
| 
      
 156 
     | 
    
         
            +
                      Nanite::Log.error("Error handling heartbeat: #{e.message}")
         
     | 
| 
      
 157 
     | 
    
         
            +
                    end
         
     | 
| 
      
 158 
     | 
    
         
            +
                  end
         
     | 
| 
      
 159 
     | 
    
         
            +
                  hb_fanout = amq.fanout('heartbeat', :durable => true)
         
     | 
| 
      
 160 
     | 
    
         
            +
                  if @redis
         
     | 
| 
      
 161 
     | 
    
         
            +
                    amq.queue("heartbeat").bind(hb_fanout).subscribe &handler
         
     | 
| 
      
 162 
     | 
    
         
            +
                  else
         
     | 
| 
      
 163 
     | 
    
         
            +
                    amq.queue("heartbeat-#{identity}", :exclusive => true).bind(hb_fanout).subscribe &handler
         
     | 
| 
      
 164 
     | 
    
         
            +
                  end
         
     | 
| 
      
 165 
     | 
    
         
            +
                end
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
      
 167 
     | 
    
         
            +
                def setup_registration_queue
         
     | 
| 
      
 168 
     | 
    
         
            +
                  handler = lambda do |msg|
         
     | 
| 
      
 169 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 170 
     | 
    
         
            +
                      msg = serializer.load(msg)
         
     | 
| 
      
 171 
     | 
    
         
            +
                      Nanite::Log.debug("got registration from #{msg.identity}")
         
     | 
| 
      
 172 
     | 
    
         
            +
                      register(msg)
         
     | 
| 
      
 173 
     | 
    
         
            +
                    rescue Exception => e
         
     | 
| 
      
 174 
     | 
    
         
            +
                      Nanite::Log.error("Error handling registration: #{e.message}")
         
     | 
| 
      
 175 
     | 
    
         
            +
                    end
         
     | 
| 
      
 176 
     | 
    
         
            +
                  end
         
     | 
| 
      
 177 
     | 
    
         
            +
                  reg_fanout = amq.fanout('registration', :durable => true)
         
     | 
| 
      
 178 
     | 
    
         
            +
                  if @redis
         
     | 
| 
      
 179 
     | 
    
         
            +
                    amq.queue("registration").bind(reg_fanout).subscribe &handler
         
     | 
| 
      
 180 
     | 
    
         
            +
                  else
         
     | 
| 
      
 181 
     | 
    
         
            +
                    amq.queue("registration-#{identity}", :exclusive => true).bind(reg_fanout).subscribe &handler
         
     | 
| 
      
 182 
     | 
    
         
            +
                  end
         
     | 
| 
      
 183 
     | 
    
         
            +
                end
         
     | 
| 
      
 184 
     | 
    
         
            +
                
         
     | 
| 
      
 185 
     | 
    
         
            +
                def setup_request_queue
         
     | 
| 
      
 186 
     | 
    
         
            +
                  handler = lambda do |msg|
         
     | 
| 
      
 187 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 188 
     | 
    
         
            +
                      msg = serializer.load(msg)
         
     | 
| 
      
 189 
     | 
    
         
            +
                      Nanite::Log.debug("got request from #{msg.from} of type #{msg.type}")
         
     | 
| 
      
 190 
     | 
    
         
            +
                      handle_request(msg)
         
     | 
| 
      
 191 
     | 
    
         
            +
                    rescue Exception => e
         
     | 
| 
      
 192 
     | 
    
         
            +
                      Nanite::Log.error("Error handling request: #{e.message}")
         
     | 
| 
      
 193 
     | 
    
         
            +
                    end
         
     | 
| 
      
 194 
     | 
    
         
            +
                  end
         
     | 
| 
      
 195 
     | 
    
         
            +
                  req_fanout = amq.fanout('request', :durable => true)
         
     | 
| 
      
 196 
     | 
    
         
            +
                  if @redis
         
     | 
| 
      
 197 
     | 
    
         
            +
                    amq.queue("request").bind(req_fanout).subscribe &handler
         
     | 
| 
      
 198 
     | 
    
         
            +
                  else
         
     | 
| 
      
 199 
     | 
    
         
            +
                    amq.queue("request-#{identity}", :exclusive => true).bind(req_fanout).subscribe &handler
         
     | 
| 
      
 200 
     | 
    
         
            +
                  end
         
     | 
| 
      
 201 
     | 
    
         
            +
                end
         
     | 
| 
      
 202 
     | 
    
         
            +
              end
         
     | 
| 
      
 203 
     | 
    
         
            +
            end
         
     |