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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +5 -0
  3. data/Gemfile +1 -1
  4. data/README.md +14 -13
  5. data/cuboid.gemspec +1 -1
  6. data/lib/cuboid/application.rb +10 -10
  7. data/lib/cuboid/option_groups/agent.rb +54 -0
  8. data/lib/cuboid/option_groups/paths.rb +13 -4
  9. data/lib/cuboid/options.rb +1 -1
  10. data/lib/cuboid/processes/{dispatchers.rb → agents.rb} +40 -26
  11. data/lib/cuboid/processes/executables/agent.rb +5 -0
  12. data/lib/cuboid/processes/helpers/agents.rb +23 -0
  13. data/lib/cuboid/processes/helpers/instances.rb +4 -4
  14. data/lib/cuboid/processes/helpers.rb +1 -1
  15. data/lib/cuboid/processes/instances.rb +22 -10
  16. data/lib/cuboid/processes/schedulers.rb +16 -3
  17. data/lib/cuboid/processes.rb +2 -2
  18. data/lib/cuboid/rest/server/instance_helpers.rb +13 -13
  19. data/lib/cuboid/rest/server/routes/dispatcher.rb +11 -11
  20. data/lib/cuboid/rest/server/routes/grid.rb +8 -8
  21. data/lib/cuboid/rest/server/routes/instances.rb +1 -1
  22. data/lib/cuboid/rest/server.rb +5 -5
  23. data/lib/cuboid/rpc/client/{dispatcher.rb → agent.rb} +4 -4
  24. data/lib/cuboid/rpc/client/instance.rb +2 -2
  25. data/lib/cuboid/rpc/client.rb +1 -1
  26. data/lib/cuboid/rpc/server/agent/node.rb +247 -0
  27. data/lib/cuboid/rpc/server/{dispatcher → agent}/service.rb +13 -13
  28. data/lib/cuboid/rpc/server/{dispatcher.rb → agent.rb} +62 -32
  29. data/lib/cuboid/rpc/server/application_wrapper.rb +5 -4
  30. data/lib/cuboid/rpc/server/instance.rb +4 -4
  31. data/lib/cuboid/rpc/server/scheduler.rb +13 -12
  32. data/lib/version +1 -1
  33. data/spec/cuboid/option_groups/dispatcher_spec.rb +2 -2
  34. data/spec/cuboid/option_groups/paths_spec.rb +6 -3
  35. data/spec/cuboid/rest/server_spec.rb +45 -45
  36. data/spec/cuboid/rpc/client/dispatcher_spec.rb +2 -2
  37. data/spec/cuboid/rpc/server/dispatcher/node_spec.rb +65 -65
  38. data/spec/cuboid/rpc/server/dispatcher/service_spec.rb +16 -16
  39. data/spec/cuboid/rpc/server/dispatcher_spec.rb +187 -72
  40. data/spec/cuboid/rpc/server/scheduler_spec.rb +8 -8
  41. data/spec/support/fixtures/executables/node.rb +3 -3
  42. data/spec/support/fixtures/mock_app/test_service.rb +8 -8
  43. data/spec/support/fixtures/mock_app.rb +1 -1
  44. data/spec/support/fixtures/services/echo.rb +6 -6
  45. data/spec/support/helpers/resets.rb +1 -1
  46. data/spec/support/lib/web_server_client.rb +2 -2
  47. data/spec/support/lib/web_server_dispatcher.rb +1 -1
  48. metadata +18 -58
  49. data/lib/cuboid/option_groups/dispatcher.rb +0 -38
  50. data/lib/cuboid/processes/executables/dispatcher.rb +0 -5
  51. data/lib/cuboid/processes/helpers/dispatchers.rb +0 -23
  52. data/lib/cuboid/rpc/server/dispatcher/node.rb +0 -247
  53. data/spec/support/logs/Dispatcher - 51489-29703.log +0 -6
  54. data/spec/support/logs/Scheduler - 51474-42556.log +0 -3
  55. data/spec/support/logs/Scheduler - 51477-63074.log +0 -6
  56. data/spec/support/logs/Scheduler - 51496-16039.log +0 -3
  57. data/spec/support/logs/Scheduler - 51499-40309.log +0 -6
  58. data/spec/support/logs/Scheduler - 51521-54983.log +0 -4
  59. data/spec/support/logs/Scheduler - 51533-50145.log +0 -1
  60. data/spec/support/logs/Scheduler - 51537-26476.log +0 -3
  61. data/spec/support/logs/Scheduler - 51541-33347.log +0 -6
  62. data/spec/support/logs/Scheduler - 51556-5765.log +0 -3
  63. data/spec/support/logs/Scheduler - 51559-22349.log +0 -6
  64. data/spec/support/logs/Scheduler - 51567-20476.log +0 -3
  65. data/spec/support/logs/Scheduler - 51570-37548.log +0 -6
  66. data/spec/support/logs/Scheduler - 52668-26175.log +0 -3
  67. data/spec/support/reports/3480c2e4463df854d3457b247e3ba679.crf +0 -0
  68. data/spec/support/reports/45958408cb49a7f3391a973e05bf673b.crf +0 -0
  69. data/spec/support/reports/62a7f8d6c8914bb086e7e5f8c418d974.crf +0 -0
  70. data/spec/support/reports/6363927e13ec27b5cbd973b86bd8e52c.crf +0 -0
  71. data/spec/support/reports/b68f94be3aa96d0c27477dcfe1e25143.crf +0 -0
  72. 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 Dispatcher
6
+ module Agent
7
7
 
8
8
  def self.registered( app )
9
9
 
10
- app.get '/dispatcher/url' do
11
- ensure_dispatcher!
10
+ app.get '/agent/url' do
11
+ ensure_agent!
12
12
 
13
- json Options.dispatcher.url
13
+ json Options.agent.url
14
14
  end
15
15
 
16
- app.put '/dispatcher/url' do
16
+ app.put '/agent/url' do
17
17
  url = ::JSON.load( request.body.read ) || {}
18
18
 
19
19
  handle_error do
20
- connect_to_dispatcher( url ).alive?
20
+ connect_to_agent( url ).alive?
21
21
 
22
- @dispatcher = nil
23
- Options.dispatcher.url = url
22
+ @agent = nil
23
+ Options.agent.url = url
24
24
  json nil
25
25
  end
26
26
  end
27
27
 
28
- app.delete '/dispatcher/url' do
29
- ensure_dispatcher!
28
+ app.delete '/agent/url' do
29
+ ensure_agent!
30
30
 
31
- json @dispatcher = Options.dispatcher.url = nil
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
- ensure_dispatcher!
11
+ ensure_agent!
12
12
 
13
13
  handle_error do
14
- json [Options.dispatcher.url] + dispatcher.statistics['node']['neighbours']
14
+ json [Options.agent.url] + agent.statistics['node']['peers']
15
15
  end
16
16
  end
17
17
 
18
- app.get '/grid/:dispatcher' do |url|
19
- ensure_dispatcher!
18
+ app.get '/grid/:agent' do |url|
19
+ ensure_agent!
20
20
 
21
- handle_error { json connect_to_dispatcher( url ).statistics }
21
+ handle_error { json connect_to_agent( url ).statistics }
22
22
  end
23
23
 
24
- app.delete '/grid/:dispatcher' do |url|
25
- ensure_dispatcher!
24
+ app.delete '/grid/:agent' do |url|
25
+ ensure_agent!
26
26
 
27
27
  handle_error do
28
- unplug_dispatcher( url )
28
+ unplug_agent( url )
29
29
  end
30
30
 
31
31
  json nil
@@ -16,7 +16,7 @@ module Instances
16
16
 
17
17
  # Create
18
18
  app.post '/instances' do
19
- max_utilization! if !dispatcher && System.max_utilization?
19
+ max_utilization! if !agent && System.max_utilization?
20
20
 
21
21
  options = ::JSON.load( request.body.read ) || {}
22
22
 
@@ -23,7 +23,7 @@ class Server < Sinatra::Base
23
23
  end
24
24
 
25
25
  register Routes::Instances
26
- register Routes::Dispatcher
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 ensure_dispatcher!
85
- return if dispatcher
86
- halt 501, json( 'No Dispatcher has been set.' )
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
- @@dispatchers.clear
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 Dispatcher client
8
+ # RPC Agent client
9
9
  #
10
10
  # @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
11
- class Dispatcher
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.dispatcher_services.keys.each do |name|
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( "dispatcher.#{sym.to_s}", *args, &block )
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 dispatcher
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 Dispatcher handlers
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
 
@@ -1,3 +1,3 @@
1
1
  require_relative 'client/instance'
2
- require_relative 'client/dispatcher'
2
+ require_relative 'client/agent'
3
3
  require_relative 'client/scheduler'
@@ -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 Dispatcher services.
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::Dispatcher::Service
29
+ class Server::Agent::Service
30
30
 
31
31
  attr_reader :options
32
- attr_reader :dispatcher
32
+ attr_reader :agent
33
33
 
34
- def initialize( options, dispatcher )
34
+ def initialize( options, agent )
35
35
  @options = options
36
- @dispatcher = dispatcher
36
+ @agent = agent
37
37
  end
38
38
 
39
- # @return [Server::Dispatcher::Node]
39
+ # @return [Server::Agent::Node]
40
40
  # Local node.
41
41
  def node
42
- dispatcher.instance_eval { @node }
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
- dispatcher.running_instances
105
+ agent.running_instances
106
106
  end
107
107
 
108
- # Connects to a Dispatcher by `url`
108
+ # Connects to a Agent by `url`
109
109
  #
110
110
  # @param [String] url
111
111
  #
112
- # @return [Client::Dispatcher]
113
- def connect_to_dispatcher( url )
114
- @dispatcher_connections ||= {}
115
- @dispatcher_connections[url] ||= Client::Dispatcher.new( url )
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`.