rightscale-nanite 0.4.1 → 0.4.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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 +153 -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 +62 -1
data/lib/nanite.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'amqp'
|
3
|
+
require 'mq'
|
4
|
+
require 'json'
|
5
|
+
require 'logger'
|
6
|
+
require 'yaml'
|
7
|
+
require 'openssl'
|
8
|
+
|
9
|
+
$:.unshift File.dirname(__FILE__)
|
10
|
+
require 'nanite/amqp'
|
11
|
+
require 'nanite/util'
|
12
|
+
require 'nanite/config'
|
13
|
+
require 'nanite/packets'
|
14
|
+
require 'nanite/identity'
|
15
|
+
require 'nanite/console'
|
16
|
+
require 'nanite/daemonize'
|
17
|
+
require 'nanite/pid_file'
|
18
|
+
require 'nanite/job'
|
19
|
+
require 'nanite/mapper'
|
20
|
+
require 'nanite/actor'
|
21
|
+
require 'nanite/actor_registry'
|
22
|
+
require 'nanite/streaming'
|
23
|
+
require 'nanite/dispatcher'
|
24
|
+
require 'nanite/agent'
|
25
|
+
require 'nanite/cluster'
|
26
|
+
require 'nanite/reaper'
|
27
|
+
require 'nanite/log'
|
28
|
+
require 'nanite/mapper_proxy'
|
29
|
+
require 'nanite/security_provider'
|
30
|
+
require 'nanite/security/cached_certificate_store_proxy'
|
31
|
+
require 'nanite/security/certificate'
|
32
|
+
require 'nanite/security/certificate_cache'
|
33
|
+
require 'nanite/security/distinguished_name'
|
34
|
+
require 'nanite/security/encrypted_document'
|
35
|
+
require 'nanite/security/rsa_key_pair'
|
36
|
+
require 'nanite/security/secure_serializer'
|
37
|
+
require 'nanite/security/signature'
|
38
|
+
require 'nanite/security/static_certificate_store'
|
39
|
+
require 'nanite/serializer'
|
40
|
+
|
41
|
+
module Nanite
|
42
|
+
VERSION = '0.4.0' unless defined?(Nanite::VERSION)
|
43
|
+
|
44
|
+
class MapperNotRunning < StandardError; end
|
45
|
+
|
46
|
+
class << self
|
47
|
+
attr_reader :mapper, :agent
|
48
|
+
|
49
|
+
def start_agent(options = {})
|
50
|
+
@agent = Nanite::Agent.start(options)
|
51
|
+
end
|
52
|
+
|
53
|
+
def start_mapper(options = {})
|
54
|
+
@mapper = Nanite::Mapper.start(options)
|
55
|
+
end
|
56
|
+
|
57
|
+
def request(*args, &blk)
|
58
|
+
ensure_mapper
|
59
|
+
@mapper.request(*args, &blk)
|
60
|
+
end
|
61
|
+
|
62
|
+
def push(*args)
|
63
|
+
ensure_mapper
|
64
|
+
@mapper.push(*args)
|
65
|
+
end
|
66
|
+
|
67
|
+
def ensure_mapper
|
68
|
+
raise MapperNotRunning.new('A mapper needs to be started via Nanite.start_mapper') unless @mapper
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/nanite/actor.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
module Nanite
|
2
|
+
# This mixin provides Nanite actor functionality.
|
3
|
+
#
|
4
|
+
# To use it simply include it your class containing the functionality to be exposed:
|
5
|
+
#
|
6
|
+
# class Foo
|
7
|
+
# include Nanite::Actor
|
8
|
+
# expose :bar
|
9
|
+
#
|
10
|
+
# def bar(payload)
|
11
|
+
# # ...
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# end
|
15
|
+
module Actor
|
16
|
+
|
17
|
+
def self.included(base)
|
18
|
+
base.class_eval do
|
19
|
+
include Nanite::Actor::InstanceMethods
|
20
|
+
extend Nanite::Actor::ClassMethods
|
21
|
+
end # base.class_eval
|
22
|
+
end # self.included
|
23
|
+
|
24
|
+
module ClassMethods
|
25
|
+
def default_prefix
|
26
|
+
to_s.to_const_path
|
27
|
+
end
|
28
|
+
|
29
|
+
def expose(*meths)
|
30
|
+
@exposed ||= []
|
31
|
+
meths.each do |meth|
|
32
|
+
@exposed << meth unless @exposed.include?(meth)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def provides_for(prefix)
|
37
|
+
return [] unless @exposed
|
38
|
+
@exposed.map {|meth| "/#{prefix}/#{meth}".squeeze('/')}
|
39
|
+
end
|
40
|
+
|
41
|
+
def on_exception(proc = nil, &blk)
|
42
|
+
raise 'No callback provided for on_exception' unless proc || blk
|
43
|
+
@exception_callback = proc || blk
|
44
|
+
end
|
45
|
+
|
46
|
+
def exception_callback
|
47
|
+
@exception_callback
|
48
|
+
end
|
49
|
+
|
50
|
+
end # ClassMethods
|
51
|
+
|
52
|
+
module InstanceMethods
|
53
|
+
# send nanite request to another agent (through the mapper)
|
54
|
+
def request(*args, &blk)
|
55
|
+
MapperProxy.instance.request(*args, &blk)
|
56
|
+
end
|
57
|
+
end # InstanceMethods
|
58
|
+
|
59
|
+
end # Actor
|
60
|
+
end # Nanite
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Nanite
|
2
|
+
class ActorRegistry
|
3
|
+
attr_reader :actors
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@actors = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def register(actor, prefix)
|
10
|
+
raise ArgumentError, "#{actor.inspect} is not a Nanite::Actor subclass instance" unless Nanite::Actor === actor
|
11
|
+
Nanite::Log.info("Registering #{actor.inspect} with prefix #{prefix.inspect}")
|
12
|
+
prefix ||= actor.class.default_prefix
|
13
|
+
actors[prefix.to_s] = actor
|
14
|
+
end
|
15
|
+
|
16
|
+
def services
|
17
|
+
actors.map {|prefix, actor| actor.class.provides_for(prefix) }.flatten.uniq
|
18
|
+
end
|
19
|
+
|
20
|
+
def actor_for(prefix)
|
21
|
+
actor = actors[prefix]
|
22
|
+
end
|
23
|
+
end # ActorRegistry
|
24
|
+
end # Nanite
|
data/lib/nanite/admin.rb
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
require 'rack'
|
2
|
+
|
3
|
+
module Nanite
|
4
|
+
# This is a Rack app for nanite-admin. You need to have an async capable
|
5
|
+
# version of Thin installed for this to work. See bin/nanite-admin for install
|
6
|
+
# instructions.
|
7
|
+
class Admin
|
8
|
+
def initialize(mapper)
|
9
|
+
@mapper = mapper
|
10
|
+
end
|
11
|
+
|
12
|
+
AsyncResponse = [-1, {}, []].freeze
|
13
|
+
|
14
|
+
def call(env)
|
15
|
+
req = Rack::Request.new(env)
|
16
|
+
if cmd = req.params['command']
|
17
|
+
@command = cmd
|
18
|
+
@selection = req.params['type'] if req.params['type']
|
19
|
+
|
20
|
+
options = {}
|
21
|
+
case @selection
|
22
|
+
when 'least_loaded', 'random', 'all', 'rr'
|
23
|
+
options[:selector] = @selection
|
24
|
+
else
|
25
|
+
options[:target] = @selection
|
26
|
+
end
|
27
|
+
|
28
|
+
@mapper.request(cmd, req.params['payload'], options) do |response, responsejob|
|
29
|
+
env['async.callback'].call [200, {'Content-Type' => 'text/html'}, [layout(ul(response, responsejob))]]
|
30
|
+
end
|
31
|
+
AsyncResponse
|
32
|
+
else
|
33
|
+
[200, {'Content-Type' => 'text/html'}, layout]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def services
|
38
|
+
buf = "<select name='command'>"
|
39
|
+
@mapper.cluster.nanites.all_services.each do |srv|
|
40
|
+
buf << "<option value='#{srv}' #{@command == srv ? 'selected="true"' : ''}>#{srv}</option>"
|
41
|
+
end
|
42
|
+
buf << "</select>"
|
43
|
+
buf
|
44
|
+
end
|
45
|
+
|
46
|
+
def ul(hash, job)
|
47
|
+
buf = "<ul>"
|
48
|
+
hash.each do |k,v|
|
49
|
+
buf << "<li><div class=\"nanite\">#{k}:</div><div class=\"response\">#{v.inspect}</div>"
|
50
|
+
if job.intermediate_state && job.intermediate_state[k]
|
51
|
+
buf << "<div class=\"intermediatestates\"><span class=\"statenote\">intermediate state:</span> #{job.intermediate_state[k].inspect}</div>"
|
52
|
+
end
|
53
|
+
buf << "</li>"
|
54
|
+
end
|
55
|
+
buf << "</ul>"
|
56
|
+
buf
|
57
|
+
end
|
58
|
+
|
59
|
+
def layout(content=nil)
|
60
|
+
%Q{
|
61
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
62
|
+
<html xmlns='http://www.w3.org/1999/xhtml'>
|
63
|
+
<head>
|
64
|
+
<meta content='text/html; charset=utf-8' http-equiv='Content-Type' />
|
65
|
+
<meta content='en' http-equiv='Content-Language' />
|
66
|
+
<meta content='Engineyard' name='author' />
|
67
|
+
<title>Nanite Control Tower</title>
|
68
|
+
|
69
|
+
<!-- Google AJAX Libraries API -->
|
70
|
+
<script src="http://www.google.com/jsapi"></script>
|
71
|
+
<script type="text/javascript">
|
72
|
+
google.load("jquery", "1");
|
73
|
+
</script>
|
74
|
+
|
75
|
+
<script type="text/javascript">
|
76
|
+
$(document).ready(function(){
|
77
|
+
|
78
|
+
// set the focus to the payload field
|
79
|
+
$("#payload").focus();
|
80
|
+
|
81
|
+
});
|
82
|
+
</script>
|
83
|
+
|
84
|
+
<style>
|
85
|
+
body {margin: 0; font-family: verdana; background-color: #fcfcfc;}
|
86
|
+
ul {margin: 0; padding: 0; margin-left: 10px}
|
87
|
+
li {list-style-type: none; margin-bottom: 6px}
|
88
|
+
li .nanite {font-weight: bold; font-size: 12px}
|
89
|
+
li .response {padding: 8px}
|
90
|
+
li .intermediatestates {padding: 8px; font-size: 10px;}
|
91
|
+
li .intermediatestates span.statenote {font-style: italic;}
|
92
|
+
h1, h2, h3 {margin-top: none; padding: none; margin-left: 40px;}
|
93
|
+
h1 {font-size: 22px; margin-top: 40px; margin-bottom: 30px; border-bottom: 1px solid #ddd; padding-bottom: 6px;
|
94
|
+
margin-right: 40px}
|
95
|
+
h2 {font-size: 16px;}
|
96
|
+
h3 {margin-left: 0; font-size: 14px}
|
97
|
+
.section {border: 1px solid #ccc; background-color: #fefefe; padding: 10px; margin: 20px 40px; padding: 20px;
|
98
|
+
font-size: 14px}
|
99
|
+
#footer {text-align: center; color: #AAA; font-size: 12px}
|
100
|
+
</style>
|
101
|
+
|
102
|
+
</head>
|
103
|
+
|
104
|
+
<body>
|
105
|
+
<div id="header">
|
106
|
+
<h1>Nanite Control Tower</h1>
|
107
|
+
</div>
|
108
|
+
|
109
|
+
<h2>#{@mapper.options[:vhost]}</h2>
|
110
|
+
<div class="section">
|
111
|
+
<form method="post" action="/">
|
112
|
+
<input type="hidden" value="POST" name="_method"/>
|
113
|
+
|
114
|
+
<label>Send</label>
|
115
|
+
<select name="type">
|
116
|
+
<option #{@selection == 'least_loaded' ? 'selected="true"' : ''} value="least_loaded">the least loaded nanite</option>
|
117
|
+
<option #{@selection == 'random' ? 'selected="true"' : ''} value="random">a random nanite</option>
|
118
|
+
<option #{@selection == 'all' ? 'selected="true"' : ''} value="all">all nanites</option>
|
119
|
+
<option #{@selection == 'rr' ? 'selected="true"' : ''} value="rr">a nanite chosen by round robin</option>
|
120
|
+
#{@mapper.cluster.nanites.map {|k,v| "<option #{@selection == k ? 'selected="true"' : ''} value='#{k}'>#{k}</option>" }.join}
|
121
|
+
</select>
|
122
|
+
|
123
|
+
<label>providing service</label>
|
124
|
+
#{services}
|
125
|
+
|
126
|
+
<label>the payload</label>
|
127
|
+
<input type="text" class="text" name="payload" id="payload"/>
|
128
|
+
|
129
|
+
<input type="submit" class="submit" value="Go!" name="submit"/>
|
130
|
+
</form>
|
131
|
+
|
132
|
+
#{"<h3>Responses</h3>" if content}
|
133
|
+
#{content}
|
134
|
+
</div>
|
135
|
+
|
136
|
+
<h2>Running nanites</h2>
|
137
|
+
<div class="section">
|
138
|
+
#{"No nanites online." if @mapper.cluster.nanites.size == 0}
|
139
|
+
<ul>
|
140
|
+
#{@mapper.cluster.nanites.map {|k,v| "<li>identity : #{k}<br />load : #{v[:status]}<br />services : #{v[:services].to_a.inspect}</li>" }.join}
|
141
|
+
</ul>
|
142
|
+
</div>
|
143
|
+
<div id="footer">
|
144
|
+
Nanite #{Nanite::VERSION}
|
145
|
+
<br />
|
146
|
+
© 2009 a bunch of random geeks
|
147
|
+
</div>
|
148
|
+
</body>
|
149
|
+
</html>
|
150
|
+
}
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
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
|