rightscale-nanite 0.4.1 → 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/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
|