cuboid 0.0.3alpha → 0.1.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/Gemfile +1 -1
- data/README.md +14 -13
- data/cuboid.gemspec +1 -1
- data/lib/cuboid/application.rb +10 -10
- data/lib/cuboid/option_groups/agent.rb +54 -0
- data/lib/cuboid/option_groups/paths.rb +13 -4
- data/lib/cuboid/options.rb +1 -1
- data/lib/cuboid/processes/{dispatchers.rb → agents.rb} +40 -26
- data/lib/cuboid/processes/executables/agent.rb +5 -0
- data/lib/cuboid/processes/helpers/agents.rb +23 -0
- data/lib/cuboid/processes/helpers/instances.rb +4 -4
- data/lib/cuboid/processes/helpers.rb +1 -1
- data/lib/cuboid/processes/instances.rb +22 -10
- data/lib/cuboid/processes/schedulers.rb +16 -3
- data/lib/cuboid/processes.rb +2 -2
- data/lib/cuboid/rest/server/instance_helpers.rb +13 -13
- data/lib/cuboid/rest/server/routes/dispatcher.rb +11 -11
- data/lib/cuboid/rest/server/routes/grid.rb +8 -8
- data/lib/cuboid/rest/server/routes/instances.rb +1 -1
- data/lib/cuboid/rest/server.rb +5 -5
- data/lib/cuboid/rpc/client/{dispatcher.rb → agent.rb} +4 -4
- data/lib/cuboid/rpc/client/instance.rb +2 -2
- data/lib/cuboid/rpc/client.rb +1 -1
- data/lib/cuboid/rpc/server/agent/node.rb +247 -0
- data/lib/cuboid/rpc/server/{dispatcher → agent}/service.rb +13 -13
- data/lib/cuboid/rpc/server/{dispatcher.rb → agent.rb} +62 -32
- data/lib/cuboid/rpc/server/application_wrapper.rb +5 -4
- data/lib/cuboid/rpc/server/instance.rb +4 -4
- data/lib/cuboid/rpc/server/scheduler.rb +13 -12
- data/lib/version +1 -1
- data/spec/cuboid/option_groups/dispatcher_spec.rb +2 -2
- data/spec/cuboid/option_groups/paths_spec.rb +6 -3
- data/spec/cuboid/rest/server_spec.rb +45 -45
- data/spec/cuboid/rpc/client/dispatcher_spec.rb +2 -2
- data/spec/cuboid/rpc/server/dispatcher/node_spec.rb +65 -65
- data/spec/cuboid/rpc/server/dispatcher/service_spec.rb +16 -16
- data/spec/cuboid/rpc/server/dispatcher_spec.rb +187 -72
- data/spec/cuboid/rpc/server/scheduler_spec.rb +8 -8
- data/spec/support/fixtures/executables/node.rb +3 -3
- data/spec/support/fixtures/mock_app/test_service.rb +8 -8
- data/spec/support/fixtures/mock_app.rb +1 -1
- data/spec/support/fixtures/services/echo.rb +6 -6
- data/spec/support/helpers/resets.rb +1 -1
- data/spec/support/lib/web_server_client.rb +2 -2
- data/spec/support/lib/web_server_dispatcher.rb +1 -1
- metadata +18 -58
- data/lib/cuboid/option_groups/dispatcher.rb +0 -38
- data/lib/cuboid/processes/executables/dispatcher.rb +0 -5
- data/lib/cuboid/processes/helpers/dispatchers.rb +0 -23
- data/lib/cuboid/rpc/server/dispatcher/node.rb +0 -247
- data/spec/support/logs/Dispatcher - 51489-29703.log +0 -6
- data/spec/support/logs/Scheduler - 51474-42556.log +0 -3
- data/spec/support/logs/Scheduler - 51477-63074.log +0 -6
- data/spec/support/logs/Scheduler - 51496-16039.log +0 -3
- data/spec/support/logs/Scheduler - 51499-40309.log +0 -6
- data/spec/support/logs/Scheduler - 51521-54983.log +0 -4
- data/spec/support/logs/Scheduler - 51533-50145.log +0 -1
- data/spec/support/logs/Scheduler - 51537-26476.log +0 -3
- data/spec/support/logs/Scheduler - 51541-33347.log +0 -6
- data/spec/support/logs/Scheduler - 51556-5765.log +0 -3
- data/spec/support/logs/Scheduler - 51559-22349.log +0 -6
- data/spec/support/logs/Scheduler - 51567-20476.log +0 -3
- data/spec/support/logs/Scheduler - 51570-37548.log +0 -6
- data/spec/support/logs/Scheduler - 52668-26175.log +0 -3
- data/spec/support/reports/3480c2e4463df854d3457b247e3ba679.crf +0 -0
- data/spec/support/reports/45958408cb49a7f3391a973e05bf673b.crf +0 -0
- data/spec/support/reports/62a7f8d6c8914bb086e7e5f8c418d974.crf +0 -0
- data/spec/support/reports/6363927e13ec27b5cbd973b86bd8e52c.crf +0 -0
- data/spec/support/reports/b68f94be3aa96d0c27477dcfe1e25143.crf +0 -0
- data/spec/support/reports/bbb7496056393de17e72855a63d3acfb.crf +0 -0
@@ -3,32 +3,32 @@ module Rest
|
|
3
3
|
class Server
|
4
4
|
module Routes
|
5
5
|
|
6
|
-
module
|
6
|
+
module Agent
|
7
7
|
|
8
8
|
def self.registered( app )
|
9
9
|
|
10
|
-
app.get '/
|
11
|
-
|
10
|
+
app.get '/agent/url' do
|
11
|
+
ensure_agent!
|
12
12
|
|
13
|
-
json Options.
|
13
|
+
json Options.agent.url
|
14
14
|
end
|
15
15
|
|
16
|
-
app.put '/
|
16
|
+
app.put '/agent/url' do
|
17
17
|
url = ::JSON.load( request.body.read ) || {}
|
18
18
|
|
19
19
|
handle_error do
|
20
|
-
|
20
|
+
connect_to_agent( url ).alive?
|
21
21
|
|
22
|
-
@
|
23
|
-
Options.
|
22
|
+
@agent = nil
|
23
|
+
Options.agent.url = url
|
24
24
|
json nil
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
app.delete '/
|
29
|
-
|
28
|
+
app.delete '/agent/url' do
|
29
|
+
ensure_agent!
|
30
30
|
|
31
|
-
json @
|
31
|
+
json @agent = Options.agent.url = nil
|
32
32
|
end
|
33
33
|
|
34
34
|
end
|
@@ -8,24 +8,24 @@ module Grid
|
|
8
8
|
def self.registered( app )
|
9
9
|
|
10
10
|
app.get '/grid' do
|
11
|
-
|
11
|
+
ensure_agent!
|
12
12
|
|
13
13
|
handle_error do
|
14
|
-
json [Options.
|
14
|
+
json [Options.agent.url] + agent.statistics['node']['peers']
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
app.get '/grid/:
|
19
|
-
|
18
|
+
app.get '/grid/:agent' do |url|
|
19
|
+
ensure_agent!
|
20
20
|
|
21
|
-
handle_error { json
|
21
|
+
handle_error { json connect_to_agent( url ).statistics }
|
22
22
|
end
|
23
23
|
|
24
|
-
app.delete '/grid/:
|
25
|
-
|
24
|
+
app.delete '/grid/:agent' do |url|
|
25
|
+
ensure_agent!
|
26
26
|
|
27
27
|
handle_error do
|
28
|
-
|
28
|
+
unplug_agent( url )
|
29
29
|
end
|
30
30
|
|
31
31
|
json nil
|
data/lib/cuboid/rest/server.rb
CHANGED
@@ -23,7 +23,7 @@ class Server < Sinatra::Base
|
|
23
23
|
end
|
24
24
|
|
25
25
|
register Routes::Instances
|
26
|
-
register Routes::
|
26
|
+
register Routes::Agent
|
27
27
|
register Routes::Grid
|
28
28
|
register Routes::Scheduler
|
29
29
|
|
@@ -81,9 +81,9 @@ class Server < Sinatra::Base
|
|
81
81
|
halt 404, json( "Scan not found for id: #{h id}." )
|
82
82
|
end
|
83
83
|
|
84
|
-
def
|
85
|
-
return if
|
86
|
-
halt 501, json( 'No
|
84
|
+
def ensure_agent!
|
85
|
+
return if agent
|
86
|
+
halt 501, json( 'No Agent has been set.' )
|
87
87
|
end
|
88
88
|
|
89
89
|
def ensure_scheduler!
|
@@ -114,7 +114,7 @@ class Server < Sinatra::Base
|
|
114
114
|
|
115
115
|
def reset
|
116
116
|
@@instances.clear
|
117
|
-
@@
|
117
|
+
@@agents.clear
|
118
118
|
end
|
119
119
|
|
120
120
|
def run!( options )
|
@@ -5,10 +5,10 @@ require Options.paths.lib + 'rpc/client/base'
|
|
5
5
|
module RPC
|
6
6
|
class Client
|
7
7
|
|
8
|
-
# RPC
|
8
|
+
# RPC Agent client
|
9
9
|
#
|
10
10
|
# @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
|
11
|
-
class
|
11
|
+
class Agent
|
12
12
|
# Not always available, set by the parent.
|
13
13
|
attr_accessor :pid
|
14
14
|
|
@@ -18,7 +18,7 @@ class Dispatcher
|
|
18
18
|
@client = Base.new( url, nil, options )
|
19
19
|
@node = Arachni::RPC::Proxy.new( @client, 'node' )
|
20
20
|
|
21
|
-
Cuboid::Application.application.
|
21
|
+
Cuboid::Application.application.agent_services.keys.each do |name|
|
22
22
|
self.class.send( :attr_reader, name.to_sym )
|
23
23
|
|
24
24
|
instance_variable_set(
|
@@ -48,7 +48,7 @@ class Dispatcher
|
|
48
48
|
|
49
49
|
# Used to provide the illusion of locality for remote methods
|
50
50
|
def method_missing( sym, *args, &block )
|
51
|
-
@client.call( "
|
51
|
+
@client.call( "agent.#{sym.to_s}", *args, &block )
|
52
52
|
end
|
53
53
|
|
54
54
|
end
|
@@ -5,7 +5,7 @@ require Options.paths.lib + 'rpc/client/base'
|
|
5
5
|
module RPC
|
6
6
|
class Client
|
7
7
|
|
8
|
-
# RPC client for remote instances spawned by a remote
|
8
|
+
# RPC client for remote instances spawned by a remote agent
|
9
9
|
#
|
10
10
|
# @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
|
11
11
|
class Instance
|
@@ -47,7 +47,7 @@ class Instance
|
|
47
47
|
@instance = Proxy.new( @client )
|
48
48
|
@options = Arachni::RPC::Proxy.new( @client, 'options' )
|
49
49
|
|
50
|
-
# map
|
50
|
+
# map Agent handlers
|
51
51
|
Cuboid::Application.application.instance_services.keys.each do |name|
|
52
52
|
self.class.send( :attr_reader, name.to_sym )
|
53
53
|
|
data/lib/cuboid/rpc/client.rb
CHANGED
@@ -0,0 +1,247 @@
|
|
1
|
+
module Cuboid
|
2
|
+
|
3
|
+
require Options.paths.lib + 'rpc/server/output'
|
4
|
+
|
5
|
+
module RPC
|
6
|
+
|
7
|
+
# Agent node class, helps maintain a list of all available Agents in
|
8
|
+
# the grid and announce itself to peering Agents.
|
9
|
+
#
|
10
|
+
# As soon as a new Node is fired up it checks-in with its peer and grabs
|
11
|
+
# a list of all available peers.
|
12
|
+
#
|
13
|
+
# As soon as it receives the peer list it then announces itself to them.
|
14
|
+
#
|
15
|
+
# Upon convergence there will be a grid of Agents each one with its own
|
16
|
+
# copy of all available Agent URLs.
|
17
|
+
#
|
18
|
+
# @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
|
19
|
+
class Server::Agent::Node
|
20
|
+
include UI::Output
|
21
|
+
|
22
|
+
# Initializes the node by:
|
23
|
+
#
|
24
|
+
# * Adding the peer (if the user has supplied one) to the peer list.
|
25
|
+
# * Getting the peer's peer list and appending them to its own.
|
26
|
+
# * Announces itself to the peer and instructs it to propagate our URL
|
27
|
+
# to the others.
|
28
|
+
#
|
29
|
+
# @param [Cuboid::Options] options
|
30
|
+
# @param [Server::Base] server
|
31
|
+
# Agent's RPC server.
|
32
|
+
# @param [String] logfile
|
33
|
+
# Where to send the output.
|
34
|
+
def initialize( options, server, logfile = nil )
|
35
|
+
@options = options
|
36
|
+
@server = server
|
37
|
+
@url = @server.url
|
38
|
+
|
39
|
+
reroute_to_file( logfile ) if logfile
|
40
|
+
|
41
|
+
print_status 'Initializing grid node...'
|
42
|
+
|
43
|
+
@dead_nodes = Set.new
|
44
|
+
@peers = Set.new
|
45
|
+
@nodes_info_cache = []
|
46
|
+
|
47
|
+
if (peer = @options.agent.peer)
|
48
|
+
# Grab the peer's peers.
|
49
|
+
connect_to_peer( peer ).peers do |urls|
|
50
|
+
if urls.rpc_exception?
|
51
|
+
add_dead_peer( peer )
|
52
|
+
print_info "Neighbour seems dead: #{peer}"
|
53
|
+
add_dead_peer( peer )
|
54
|
+
next
|
55
|
+
end
|
56
|
+
|
57
|
+
# Add peer and announce it to everyone.
|
58
|
+
add_peer( peer, true )
|
59
|
+
|
60
|
+
urls.each { |url| @peers << url if url != @url }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
print_status 'Node ready.'
|
65
|
+
|
66
|
+
log_updated_peers
|
67
|
+
|
68
|
+
Arachni::Reactor.global.at_interval( @options.agent.ping_interval ) do
|
69
|
+
ping
|
70
|
+
check_for_comebacks
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# @return [Boolean]
|
75
|
+
# `true` if grid member, `false` otherwise.
|
76
|
+
def grid_member?
|
77
|
+
@peers.any?
|
78
|
+
end
|
79
|
+
|
80
|
+
def unplug
|
81
|
+
@peers.each do |peer|
|
82
|
+
connect_to_peer( peer ).remove_peer( @url ) {}
|
83
|
+
end
|
84
|
+
|
85
|
+
@peers.clear
|
86
|
+
@dead_nodes.clear
|
87
|
+
|
88
|
+
nil
|
89
|
+
end
|
90
|
+
|
91
|
+
# Adds a peer to the peer list.
|
92
|
+
#
|
93
|
+
# @param [String] node_url
|
94
|
+
# URL of a peering node.
|
95
|
+
# @param [Boolean] propagate
|
96
|
+
# Whether or not to announce the new node to the peers.
|
97
|
+
def add_peer( node_url, propagate = false )
|
98
|
+
# we don't want ourselves in the Set
|
99
|
+
return false if node_url == @url
|
100
|
+
return false if @peers.include?( node_url )
|
101
|
+
|
102
|
+
print_status "Adding peer: #{node_url}"
|
103
|
+
|
104
|
+
@peers << node_url
|
105
|
+
log_updated_peers
|
106
|
+
announce( node_url ) if propagate
|
107
|
+
|
108
|
+
connect_to_peer( node_url ).add_peer( @url, propagate ) do |res|
|
109
|
+
next if !res.rpc_exception?
|
110
|
+
add_dead_peer( node_url )
|
111
|
+
print_status "Neighbour seems dead: #{node_url}"
|
112
|
+
end
|
113
|
+
true
|
114
|
+
end
|
115
|
+
|
116
|
+
def remove_peer( url )
|
117
|
+
@peers.delete url
|
118
|
+
@dead_nodes.delete url
|
119
|
+
nil
|
120
|
+
end
|
121
|
+
|
122
|
+
# @return [Array]
|
123
|
+
# Neighbour/node/peer URLs.
|
124
|
+
def peers
|
125
|
+
@peers.to_a
|
126
|
+
end
|
127
|
+
|
128
|
+
def peers_with_info( &block )
|
129
|
+
fail 'This method requires a block!' if !block_given?
|
130
|
+
|
131
|
+
@peers_cmp = ''
|
132
|
+
|
133
|
+
if @nodes_info_cache.empty? || @peers_cmp != peers.to_s
|
134
|
+
@peers_cmp = peers.to_s
|
135
|
+
|
136
|
+
each = proc do |peer, iter|
|
137
|
+
connect_to_peer( peer ).info do |info|
|
138
|
+
if info.rpc_exception?
|
139
|
+
print_info "Neighbour seems dead: #{peer}"
|
140
|
+
add_dead_peer( peer )
|
141
|
+
log_updated_peers
|
142
|
+
|
143
|
+
iter.return( nil )
|
144
|
+
else
|
145
|
+
iter.return( info )
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
after = proc do |nodes|
|
151
|
+
@nodes_info_cache = nodes.compact
|
152
|
+
block.call( @nodes_info_cache )
|
153
|
+
end
|
154
|
+
|
155
|
+
Arachni::Reactor.global.create_iterator( peers ).map( each, after )
|
156
|
+
else
|
157
|
+
block.call( @nodes_info_cache )
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# @return [Hash]
|
162
|
+
#
|
163
|
+
# * `url` -- This node's URL.
|
164
|
+
# * `name` -- Nickname
|
165
|
+
# * `peers` -- Array of peers.
|
166
|
+
def info
|
167
|
+
{
|
168
|
+
'url' => @url,
|
169
|
+
'name' => @options.agent.name,
|
170
|
+
'peers' => @peers.to_a,
|
171
|
+
'unreachable_peers' => @dead_nodes.to_a
|
172
|
+
}
|
173
|
+
end
|
174
|
+
|
175
|
+
def alive?
|
176
|
+
true
|
177
|
+
end
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
def add_dead_peer( url )
|
182
|
+
remove_peer( url )
|
183
|
+
@dead_nodes << url
|
184
|
+
end
|
185
|
+
|
186
|
+
def log_updated_peers
|
187
|
+
print_info 'Updated peers:'
|
188
|
+
|
189
|
+
if !peers.empty?
|
190
|
+
peers.each { |node| print_info( '---- ' + node ) }
|
191
|
+
else
|
192
|
+
print_info '<empty>'
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def ping
|
197
|
+
peers.each do |peer|
|
198
|
+
connect_to_peer( peer ).alive? do |res|
|
199
|
+
next if !res.rpc_exception?
|
200
|
+
add_dead_peer( peer )
|
201
|
+
print_status "Found dead peer: #{peer} "
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def check_for_comebacks
|
207
|
+
@dead_nodes.dup.each do |url|
|
208
|
+
peer = connect_to_peer( url )
|
209
|
+
peer.alive? do |res|
|
210
|
+
next if res.rpc_exception?
|
211
|
+
|
212
|
+
print_status "Agent came back to life: #{url}"
|
213
|
+
([@url] | peers).each do |node|
|
214
|
+
peer.add_peer( node ){}
|
215
|
+
end
|
216
|
+
|
217
|
+
add_peer( url )
|
218
|
+
@dead_nodes.delete url
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
# Announces the node to the ones in the peer list
|
224
|
+
#
|
225
|
+
# @param [String] node
|
226
|
+
# URL
|
227
|
+
def announce( node )
|
228
|
+
print_status "Advertising: #{node}"
|
229
|
+
|
230
|
+
peers.each do |peer|
|
231
|
+
next if peer == node
|
232
|
+
|
233
|
+
print_info '---- to: ' + peer
|
234
|
+
connect_to_peer( peer ).add_peer( node ) do |res|
|
235
|
+
add_dead_peer( peer ) if res.rpc_exception?
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def connect_to_peer( url )
|
241
|
+
@rpc_clients ||= {}
|
242
|
+
@rpc_clients[url] ||= Client::Agent.new( url ).node
|
243
|
+
end
|
244
|
+
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Cuboid
|
2
2
|
module RPC
|
3
3
|
|
4
|
-
# Base class and namespace for all
|
4
|
+
# Base class and namespace for all Agent services.
|
5
5
|
#
|
6
6
|
# # RPC accessibility
|
7
7
|
#
|
@@ -26,20 +26,20 @@ module RPC
|
|
26
26
|
# results to that block instead of returning a value.
|
27
27
|
#
|
28
28
|
# @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
|
29
|
-
class Server::
|
29
|
+
class Server::Agent::Service
|
30
30
|
|
31
31
|
attr_reader :options
|
32
|
-
attr_reader :
|
32
|
+
attr_reader :agent
|
33
33
|
|
34
|
-
def initialize( options,
|
34
|
+
def initialize( options, agent )
|
35
35
|
@options = options
|
36
|
-
@
|
36
|
+
@agent = agent
|
37
37
|
end
|
38
38
|
|
39
|
-
# @return [Server::
|
39
|
+
# @return [Server::Agent::Node]
|
40
40
|
# Local node.
|
41
41
|
def node
|
42
|
-
|
42
|
+
agent.instance_eval { @node }
|
43
43
|
end
|
44
44
|
|
45
45
|
# Performs an asynchronous map operation over all running instances.
|
@@ -102,17 +102,17 @@ class Server::Dispatcher::Service
|
|
102
102
|
# @return [Array<Hash>]
|
103
103
|
# Alive instances.
|
104
104
|
def instances
|
105
|
-
|
105
|
+
agent.running_instances
|
106
106
|
end
|
107
107
|
|
108
|
-
# Connects to a
|
108
|
+
# Connects to a Agent by `url`
|
109
109
|
#
|
110
110
|
# @param [String] url
|
111
111
|
#
|
112
|
-
# @return [Client::
|
113
|
-
def
|
114
|
-
@
|
115
|
-
@
|
112
|
+
# @return [Client::Agent]
|
113
|
+
def connect_to_agent( url )
|
114
|
+
@agent_connections ||= {}
|
115
|
+
@agent_connections[url] ||= Client::Agent.new( url )
|
116
116
|
end
|
117
117
|
|
118
118
|
# Connects to an Instance by `url`.
|