cuboid 0.0.0 → 0.0.1alpha
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 +0 -0
- data/Gemfile +20 -5
- data/LICENSE.md +22 -0
- data/README.md +158 -19
- data/Rakefile +56 -3
- data/config/paths.yml +15 -0
- data/cuboid.gemspec +61 -23
- data/lib/cuboid.rb +96 -4
- data/lib/cuboid/application.rb +326 -0
- data/lib/cuboid/application/parts/data.rb +18 -0
- data/lib/cuboid/application/parts/report.rb +29 -0
- data/lib/cuboid/application/parts/state.rb +274 -0
- data/lib/cuboid/application/runtime.rb +25 -0
- data/lib/cuboid/banner.rb +13 -0
- data/lib/cuboid/data.rb +86 -0
- data/lib/cuboid/data/application.rb +52 -0
- data/lib/cuboid/error.rb +9 -0
- data/lib/cuboid/option_group.rb +129 -0
- data/lib/cuboid/option_groups.rb +8 -0
- data/lib/cuboid/option_groups/datastore.rb +23 -0
- data/lib/cuboid/option_groups/dispatcher.rb +38 -0
- data/lib/cuboid/option_groups/output.rb +14 -0
- data/lib/cuboid/option_groups/paths.rb +184 -0
- data/lib/cuboid/option_groups/report.rb +39 -0
- data/lib/cuboid/option_groups/rpc.rb +105 -0
- data/lib/cuboid/option_groups/scheduler.rb +27 -0
- data/lib/cuboid/option_groups/snapshot.rb +13 -0
- data/lib/cuboid/option_groups/system.rb +10 -0
- data/lib/cuboid/options.rb +254 -0
- data/lib/cuboid/processes.rb +13 -0
- data/lib/cuboid/processes/dispatchers.rb +140 -0
- data/lib/cuboid/processes/executables/base.rb +54 -0
- data/lib/cuboid/processes/executables/dispatcher.rb +5 -0
- data/lib/cuboid/processes/executables/instance.rb +12 -0
- data/lib/cuboid/processes/executables/rest_service.rb +13 -0
- data/lib/cuboid/processes/executables/scheduler.rb +5 -0
- data/lib/cuboid/processes/helpers.rb +4 -0
- data/lib/cuboid/processes/helpers/dispatchers.rb +23 -0
- data/lib/cuboid/processes/helpers/instances.rb +39 -0
- data/lib/cuboid/processes/helpers/processes.rb +23 -0
- data/lib/cuboid/processes/helpers/schedulers.rb +23 -0
- data/lib/cuboid/processes/instances.rb +203 -0
- data/lib/cuboid/processes/manager.rb +262 -0
- data/lib/cuboid/processes/schedulers.rb +128 -0
- data/lib/cuboid/report.rb +220 -0
- data/lib/cuboid/rest/server.rb +165 -0
- data/lib/cuboid/rest/server/instance_helpers.rb +99 -0
- data/lib/cuboid/rest/server/routes/dispatcher.rb +41 -0
- data/lib/cuboid/rest/server/routes/grid.rb +41 -0
- data/lib/cuboid/rest/server/routes/instances.rb +131 -0
- data/lib/cuboid/rest/server/routes/scheduler.rb +140 -0
- data/lib/cuboid/rpc/client.rb +3 -0
- data/lib/cuboid/rpc/client/base.rb +58 -0
- data/lib/cuboid/rpc/client/dispatcher.rb +58 -0
- data/lib/cuboid/rpc/client/instance.rb +100 -0
- data/lib/cuboid/rpc/client/instance/service.rb +37 -0
- data/lib/cuboid/rpc/client/scheduler.rb +46 -0
- data/lib/cuboid/rpc/serializer.rb +92 -0
- data/lib/cuboid/rpc/server/active_options.rb +38 -0
- data/lib/cuboid/rpc/server/application_wrapper.rb +138 -0
- data/lib/cuboid/rpc/server/base.rb +63 -0
- data/lib/cuboid/rpc/server/dispatcher.rb +317 -0
- data/lib/cuboid/rpc/server/dispatcher/node.rb +247 -0
- data/lib/cuboid/rpc/server/dispatcher/service.rb +145 -0
- data/lib/cuboid/rpc/server/instance.rb +338 -0
- data/lib/cuboid/rpc/server/output.rb +92 -0
- data/lib/cuboid/rpc/server/scheduler.rb +482 -0
- data/lib/cuboid/ruby.rb +4 -0
- data/lib/cuboid/ruby/array.rb +17 -0
- data/lib/cuboid/ruby/hash.rb +41 -0
- data/lib/cuboid/ruby/object.rb +32 -0
- data/lib/cuboid/snapshot.rb +186 -0
- data/lib/cuboid/state.rb +94 -0
- data/lib/cuboid/state/application.rb +309 -0
- data/lib/cuboid/state/options.rb +27 -0
- data/lib/cuboid/support.rb +11 -0
- data/lib/cuboid/support/buffer.rb +3 -0
- data/lib/cuboid/support/buffer/autoflush.rb +61 -0
- data/lib/cuboid/support/buffer/base.rb +91 -0
- data/lib/cuboid/support/cache.rb +7 -0
- data/lib/cuboid/support/cache/base.rb +226 -0
- data/lib/cuboid/support/cache/least_cost_replacement.rb +77 -0
- data/lib/cuboid/support/cache/least_recently_pushed.rb +21 -0
- data/lib/cuboid/support/cache/least_recently_used.rb +31 -0
- data/lib/cuboid/support/cache/preference.rb +31 -0
- data/lib/cuboid/support/cache/random_replacement.rb +20 -0
- data/lib/cuboid/support/crypto.rb +2 -0
- data/lib/cuboid/support/crypto/rsa_aes_cbc.rb +86 -0
- data/lib/cuboid/support/database.rb +5 -0
- data/lib/cuboid/support/database/base.rb +177 -0
- data/lib/cuboid/support/database/categorized_queue.rb +195 -0
- data/lib/cuboid/support/database/hash.rb +300 -0
- data/lib/cuboid/support/database/queue.rb +149 -0
- data/lib/cuboid/support/filter.rb +3 -0
- data/lib/cuboid/support/filter/base.rb +110 -0
- data/lib/cuboid/support/filter/set.rb +29 -0
- data/lib/cuboid/support/glob.rb +27 -0
- data/lib/cuboid/support/mixins.rb +8 -0
- data/lib/cuboid/support/mixins/observable.rb +99 -0
- data/lib/cuboid/support/mixins/parts.rb +20 -0
- data/lib/cuboid/support/mixins/profiler.rb +93 -0
- data/lib/cuboid/support/mixins/spec_instances.rb +65 -0
- data/lib/cuboid/support/mixins/terminal.rb +57 -0
- data/lib/cuboid/system.rb +119 -0
- data/lib/cuboid/system/platforms.rb +84 -0
- data/lib/cuboid/system/platforms/linux.rb +26 -0
- data/lib/cuboid/system/platforms/mixins/unix.rb +46 -0
- data/lib/cuboid/system/platforms/osx.rb +25 -0
- data/lib/cuboid/system/platforms/windows.rb +81 -0
- data/lib/cuboid/system/slots.rb +143 -0
- data/lib/cuboid/ui/output.rb +52 -0
- data/lib/cuboid/ui/output_interface.rb +43 -0
- data/lib/cuboid/ui/output_interface/abstract.rb +68 -0
- data/lib/cuboid/ui/output_interface/controls.rb +84 -0
- data/lib/cuboid/ui/output_interface/error_logging.rb +119 -0
- data/lib/cuboid/ui/output_interface/implemented.rb +58 -0
- data/lib/cuboid/ui/output_interface/personalization.rb +62 -0
- data/lib/cuboid/utilities.rb +155 -0
- data/lib/cuboid/version.rb +4 -3
- data/lib/version +1 -0
- data/logs/placeholder +0 -0
- data/spec/cuboid/application/parts/data_spec.rb +12 -0
- data/spec/cuboid/application/parts/report_spec.rb +6 -0
- data/spec/cuboid/application/parts/state_spec.rb +192 -0
- data/spec/cuboid/application/runtime_spec.rb +21 -0
- data/spec/cuboid/application_spec.rb +37 -0
- data/spec/cuboid/data/application_spec.rb +22 -0
- data/spec/cuboid/data_spec.rb +47 -0
- data/spec/cuboid/error_spec.rb +23 -0
- data/spec/cuboid/option_groups/datastore_spec.rb +54 -0
- data/spec/cuboid/option_groups/dispatcher_spec.rb +12 -0
- data/spec/cuboid/option_groups/output_spec.rb +11 -0
- data/spec/cuboid/option_groups/paths_spec.rb +184 -0
- data/spec/cuboid/option_groups/report_spec.rb +26 -0
- data/spec/cuboid/option_groups/rpc_spec.rb +53 -0
- data/spec/cuboid/option_groups/snapshot_spec.rb +26 -0
- data/spec/cuboid/option_groups/system.rb +12 -0
- data/spec/cuboid/options_spec.rb +218 -0
- data/spec/cuboid/report_spec.rb +221 -0
- data/spec/cuboid/rest/server_spec.rb +1205 -0
- data/spec/cuboid/rpc/client/base_spec.rb +151 -0
- data/spec/cuboid/rpc/client/dispatcher_spec.rb +13 -0
- data/spec/cuboid/rpc/client/instance_spec.rb +38 -0
- data/spec/cuboid/rpc/server/active_options_spec.rb +21 -0
- data/spec/cuboid/rpc/server/base_spec.rb +60 -0
- data/spec/cuboid/rpc/server/dispatcher/node_spec.rb +222 -0
- data/spec/cuboid/rpc/server/dispatcher/service_spec.rb +112 -0
- data/spec/cuboid/rpc/server/dispatcher_spec.rb +317 -0
- data/spec/cuboid/rpc/server/instance_spec.rb +307 -0
- data/spec/cuboid/rpc/server/output_spec.rb +32 -0
- data/spec/cuboid/rpc/server/scheduler_spec.rb +400 -0
- data/spec/cuboid/ruby/array_spec.rb +77 -0
- data/spec/cuboid/ruby/hash_spec.rb +63 -0
- data/spec/cuboid/ruby/object_spec.rb +22 -0
- data/spec/cuboid/snapshot_spec.rb +123 -0
- data/spec/cuboid/state/application_spec.rb +538 -0
- data/spec/cuboid/state/options_spec.rb +37 -0
- data/spec/cuboid/state_spec.rb +53 -0
- data/spec/cuboid/support/buffer/autoflush_spec.rb +78 -0
- data/spec/cuboid/support/buffer/base_spec.rb +193 -0
- data/spec/cuboid/support/cache/least_cost_replacement_spec.rb +61 -0
- data/spec/cuboid/support/cache/least_recently_pushed_spec.rb +90 -0
- data/spec/cuboid/support/cache/least_recently_used_spec.rb +80 -0
- data/spec/cuboid/support/cache/preference_spec.rb +37 -0
- data/spec/cuboid/support/cache/random_replacement_spec.rb +42 -0
- data/spec/cuboid/support/crypto/rsa_aes_cbc_spec.rb +28 -0
- data/spec/cuboid/support/database/categorized_queue_spec.rb +327 -0
- data/spec/cuboid/support/database/hash_spec.rb +204 -0
- data/spec/cuboid/support/database/scheduler_spec.rb +325 -0
- data/spec/cuboid/support/filter/set_spec.rb +19 -0
- data/spec/cuboid/support/glob_spec.rb +75 -0
- data/spec/cuboid/support/mixins/observable_spec.rb +95 -0
- data/spec/cuboid/system/platforms/linux_spec.rb +31 -0
- data/spec/cuboid/system/platforms/osx_spec.rb +32 -0
- data/spec/cuboid/system/platforms/windows_spec.rb +41 -0
- data/spec/cuboid/system/slots_spec.rb +202 -0
- data/spec/cuboid/system_spec.rb +105 -0
- data/spec/cuboid/utilities_spec.rb +131 -0
- data/spec/spec_helper.rb +46 -0
- data/spec/support/factories/placeholder +0 -0
- data/spec/support/factories/scan_report.rb +18 -0
- data/spec/support/fixtures/empty/placeholder +0 -0
- data/spec/support/fixtures/executables/node.rb +50 -0
- data/spec/support/fixtures/mock_app.rb +61 -0
- data/spec/support/fixtures/mock_app/test_service.rb +64 -0
- data/spec/support/fixtures/services/echo.rb +64 -0
- data/spec/support/helpers/framework.rb +3 -0
- data/spec/support/helpers/matchers.rb +5 -0
- data/spec/support/helpers/misc.rb +3 -0
- data/spec/support/helpers/paths.rb +15 -0
- data/spec/support/helpers/request_helpers.rb +38 -0
- data/spec/support/helpers/requires.rb +8 -0
- data/spec/support/helpers/resets.rb +52 -0
- data/spec/support/helpers/web_server.rb +15 -0
- data/spec/support/lib/factory.rb +107 -0
- data/spec/support/lib/web_server_client.rb +41 -0
- data/spec/support/lib/web_server_dispatcher.rb +25 -0
- data/spec/support/lib/web_server_manager.rb +118 -0
- data/spec/support/logs/placeholder +0 -0
- data/spec/support/pems/cacert.pem +37 -0
- data/spec/support/pems/client/cert.pem +37 -0
- data/spec/support/pems/client/foo-cert.pem +39 -0
- data/spec/support/pems/client/foo-key.pem +51 -0
- data/spec/support/pems/client/key.pem +51 -0
- data/spec/support/pems/server/cert.pem +37 -0
- data/spec/support/pems/server/key.pem +51 -0
- data/spec/support/reports/placeholder +0 -0
- data/spec/support/shared/application.rb +10 -0
- data/spec/support/shared/component.rb +31 -0
- data/spec/support/shared/component/options/base.rb +187 -0
- data/spec/support/shared/option_group.rb +98 -0
- data/spec/support/shared/support/cache.rb +419 -0
- data/spec/support/shared/support/filter.rb +143 -0
- data/spec/support/shared/system/platforms/base.rb +25 -0
- data/spec/support/shared/system/platforms/mixins/unix.rb +37 -0
- data/spec/support/snapshots/placeholder +0 -0
- metadata +566 -21
- data/.gitignore +0 -8
- data/bin/console +0 -15
- data/bin/setup +0 -8
@@ -0,0 +1,92 @@
|
|
1
|
+
module Cuboid
|
2
|
+
module UI
|
3
|
+
|
4
|
+
# RPC Output interface.
|
5
|
+
#
|
6
|
+
# @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
|
7
|
+
module Output
|
8
|
+
include OutputInterface
|
9
|
+
|
10
|
+
def self.initialize
|
11
|
+
@@reroute_to_file = false
|
12
|
+
@@error_buffer = []
|
13
|
+
end
|
14
|
+
initialize
|
15
|
+
|
16
|
+
# @param [String] str
|
17
|
+
def log_error( str = '' )
|
18
|
+
super( str )
|
19
|
+
@@error_buffer << str
|
20
|
+
end
|
21
|
+
|
22
|
+
def error_buffer
|
23
|
+
@@error_buffer
|
24
|
+
end
|
25
|
+
|
26
|
+
def print_error( str = '' )
|
27
|
+
log_error( str )
|
28
|
+
push_to_output_buffer( error: str )
|
29
|
+
end
|
30
|
+
|
31
|
+
def print_bad( str = '' )
|
32
|
+
push_to_output_buffer( bad: str )
|
33
|
+
end
|
34
|
+
|
35
|
+
def print_status( str = '' )
|
36
|
+
push_to_output_buffer( status: str )
|
37
|
+
end
|
38
|
+
|
39
|
+
def print_info( str = '' )
|
40
|
+
push_to_output_buffer( info: str )
|
41
|
+
end
|
42
|
+
|
43
|
+
def print_ok( str = '' )
|
44
|
+
push_to_output_buffer( ok: str )
|
45
|
+
end
|
46
|
+
|
47
|
+
def print_debug( str = '', level = 1 )
|
48
|
+
return if !debug?( level )
|
49
|
+
push_to_output_buffer( debug: str )
|
50
|
+
end
|
51
|
+
|
52
|
+
def print_verbose( str = '' )
|
53
|
+
push_to_output_buffer( verbose: str )
|
54
|
+
end
|
55
|
+
|
56
|
+
def print_line( str = '' )
|
57
|
+
push_to_output_buffer( line: str )
|
58
|
+
end
|
59
|
+
|
60
|
+
def reroute_to_file( file )
|
61
|
+
@@reroute_to_file = file
|
62
|
+
end
|
63
|
+
|
64
|
+
def reroute_to_file?
|
65
|
+
@@reroute_to_file
|
66
|
+
end
|
67
|
+
|
68
|
+
def output_provider_file
|
69
|
+
__FILE__
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def push_to_output_buffer( msg )
|
75
|
+
return if !@@reroute_to_file
|
76
|
+
|
77
|
+
# This is stupid, keep a handle open and close it on exit like with the
|
78
|
+
# error log file.
|
79
|
+
File.open( @@reroute_to_file, 'a+' ) do |f|
|
80
|
+
type = msg.keys[0]
|
81
|
+
str = msg.values[0]
|
82
|
+
|
83
|
+
f.write( "[#{Time.now.asctime}] [#{type}] #{str}\n" )
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
extend self
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,482 @@
|
|
1
|
+
module Cuboid
|
2
|
+
|
3
|
+
lib = Options.paths.lib
|
4
|
+
|
5
|
+
require lib + 'processes/manager'
|
6
|
+
require lib + 'processes/instances'
|
7
|
+
|
8
|
+
require lib + 'rpc/client/instance'
|
9
|
+
require lib + 'rpc/client/dispatcher'
|
10
|
+
|
11
|
+
require lib + 'rpc/server/base'
|
12
|
+
require lib + 'rpc/server/output'
|
13
|
+
|
14
|
+
module RPC
|
15
|
+
class Server
|
16
|
+
|
17
|
+
# RPC scheduler service which:
|
18
|
+
#
|
19
|
+
# * Maintains a priority queue of Instance jobs.
|
20
|
+
# * {#push Accepts jobs.}
|
21
|
+
# * Runs them once a slot is available -- determined by
|
22
|
+
# {System#utilization system utilization}.
|
23
|
+
# * Monitors {#running} Instances, retrieves and
|
24
|
+
# {OptionGroups::Paths#reports stores} their {Report reports} and shuts down
|
25
|
+
# their {Instance} to free its slot.
|
26
|
+
# * Makes available information on {#completed} and {#failed} Instances.
|
27
|
+
#
|
28
|
+
# In addition to the purely queue functionality, it also allows for running
|
29
|
+
# Instances to be:
|
30
|
+
#
|
31
|
+
# * {#detach Detached} from the queue monitor and transfer the management
|
32
|
+
# responsibility to the client.
|
33
|
+
# * {#attach Attached} to the queue monitor and transfer the management
|
34
|
+
# responsibility to the queue.
|
35
|
+
#
|
36
|
+
# If a {Dispatcher} has been provided, {Instance instances} will be
|
37
|
+
# {Dispatcher#dispatch provided} by it.
|
38
|
+
# If no {Dispatcher} has been given, {Instance instances} will be spawned on the
|
39
|
+
# Scheduler machine.
|
40
|
+
#
|
41
|
+
# @author Tasos "Zapotek" Laskos <tasos.laskos@gmail.com>
|
42
|
+
class Scheduler
|
43
|
+
include UI::Output
|
44
|
+
include Utilities
|
45
|
+
|
46
|
+
TICK_CONSUME = 0.1
|
47
|
+
|
48
|
+
def initialize
|
49
|
+
@options = Options.instance
|
50
|
+
|
51
|
+
@options.snapshot.path ||= @options.paths.snapshots
|
52
|
+
@options.report.path ||= @options.paths.reports
|
53
|
+
|
54
|
+
@server = Base.new( @options.rpc.to_server_options )
|
55
|
+
@server.logger.level = @options.datastore.log_level if @options.datastore.log_level
|
56
|
+
|
57
|
+
Options.scheduler.url = @url = @server.url
|
58
|
+
|
59
|
+
prep_logging
|
60
|
+
|
61
|
+
@queue = {}
|
62
|
+
@id_to_priority = {}
|
63
|
+
@by_priority = {}
|
64
|
+
|
65
|
+
@running = {}
|
66
|
+
@completed = {}
|
67
|
+
@failed = {}
|
68
|
+
|
69
|
+
set_handlers( @server )
|
70
|
+
trap_interrupts { Thread.new { shutdown } }
|
71
|
+
|
72
|
+
monitor_instances
|
73
|
+
consume_queue
|
74
|
+
|
75
|
+
run
|
76
|
+
end
|
77
|
+
|
78
|
+
# @return [Bool]
|
79
|
+
def empty?
|
80
|
+
self.size == 0
|
81
|
+
end
|
82
|
+
|
83
|
+
# @return [Bool]
|
84
|
+
def any?
|
85
|
+
!empty?
|
86
|
+
end
|
87
|
+
|
88
|
+
# @return [Integer]
|
89
|
+
def size
|
90
|
+
@queue.size
|
91
|
+
end
|
92
|
+
|
93
|
+
# @return [Hash<Integer,Array>]
|
94
|
+
# Queued Instances grouped and sorted by priority.
|
95
|
+
def list
|
96
|
+
@by_priority
|
97
|
+
end
|
98
|
+
|
99
|
+
# @return [Hash]
|
100
|
+
# {RPC::Client::Instance RPC connection information} on running Instances.
|
101
|
+
def running
|
102
|
+
@running.inject( {} ) do |h, (id, client)|
|
103
|
+
h.merge! id => { url: client.url, token: client.token, pid: client.pid }
|
104
|
+
h
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# @return [Hash]
|
109
|
+
# Completed Instances and their report location.
|
110
|
+
def completed
|
111
|
+
@completed
|
112
|
+
end
|
113
|
+
|
114
|
+
# @return [Hash]
|
115
|
+
# Failed Instances and the associated error.
|
116
|
+
def failed
|
117
|
+
@failed
|
118
|
+
end
|
119
|
+
|
120
|
+
# @note Only returns info for queued Instances, once a Instance has passed through
|
121
|
+
# the {#running} stage it's no longer part of the queue.
|
122
|
+
#
|
123
|
+
# @param [String] id
|
124
|
+
# ID for a queued Instance.
|
125
|
+
#
|
126
|
+
# @return [Hash, nil]
|
127
|
+
# * Instance options and priority.
|
128
|
+
# * `nil` if a Instance with the given ID could not be found.
|
129
|
+
def get( id )
|
130
|
+
return if !@queue.include? id
|
131
|
+
|
132
|
+
{
|
133
|
+
options: @queue[id],
|
134
|
+
priority: @id_to_priority[id]
|
135
|
+
}
|
136
|
+
end
|
137
|
+
|
138
|
+
# @param [Hash] options
|
139
|
+
# {Instance#run Instance options} with an extra `priority` option which
|
140
|
+
# defaults to `0` (higher is more urgent).
|
141
|
+
#
|
142
|
+
# @return [String]
|
143
|
+
# Instance ID used to reference the Instance from then on.
|
144
|
+
def push( options, queue_options = {} )
|
145
|
+
priority = queue_options.delete('priority') || 0
|
146
|
+
|
147
|
+
if !Cuboid::Application.application.valid_options?( options )
|
148
|
+
fail ArgumentError, 'Invalid options!'
|
149
|
+
end
|
150
|
+
|
151
|
+
id = Utilities.generate_token
|
152
|
+
|
153
|
+
@queue[id] = options
|
154
|
+
@id_to_priority[id] = priority
|
155
|
+
|
156
|
+
(@by_priority[priority] ||= []) << id
|
157
|
+
@by_priority = Hash[@by_priority.sort_by { |k, _| -k }]
|
158
|
+
|
159
|
+
id
|
160
|
+
end
|
161
|
+
|
162
|
+
# @note Only affects queued Instances, once a Instance has passed through
|
163
|
+
# the {#running} stage it's no longer part of the queue.
|
164
|
+
#
|
165
|
+
# @param [String] id
|
166
|
+
# Instance ID to remove from the queue.
|
167
|
+
def remove( id )
|
168
|
+
return false if !@queue.include? id
|
169
|
+
|
170
|
+
@queue.delete( id )
|
171
|
+
@by_priority[@id_to_priority.delete( id )].delete( id )
|
172
|
+
|
173
|
+
true
|
174
|
+
end
|
175
|
+
|
176
|
+
# @param [String] id
|
177
|
+
# Running Instance to detach from the queue monitor.
|
178
|
+
#
|
179
|
+
# Once a Instance is detached it becomes someone else's responsibility to
|
180
|
+
# monitor, manage and shutdown to free its slot.
|
181
|
+
#
|
182
|
+
# @return [Hash, nil]
|
183
|
+
# * {RPC::Client::Instance RPC connection information} for the Instance.
|
184
|
+
# * `nil` if no running Instance with that ID is found.
|
185
|
+
def detach( id, &block )
|
186
|
+
client = @running.delete( id )
|
187
|
+
return block.call if !client
|
188
|
+
|
189
|
+
client.options.set( scheduler: { url: nil } ) do
|
190
|
+
block.call( url: client.url, token: client.token, pid: client.pid )
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# Attaches a running Instance to the queue monitor.
|
195
|
+
#
|
196
|
+
# @param [String] url
|
197
|
+
# Instance URL for a running Instance.
|
198
|
+
# @param [String] token
|
199
|
+
# Authentication token for the Instance.
|
200
|
+
#
|
201
|
+
# @return [String, false, nil]
|
202
|
+
# * Instance ID for further queue reference.
|
203
|
+
# * `false` if the Instance is already attached to a Scheduler.
|
204
|
+
# * `nil` if the Instance could not be reached.
|
205
|
+
def attach( url, token, &block )
|
206
|
+
client = connect_to_instance( url, token )
|
207
|
+
client.alive? do |bool|
|
208
|
+
if bool.rpc_exception?
|
209
|
+
block.call
|
210
|
+
next
|
211
|
+
end
|
212
|
+
|
213
|
+
client.scheduler_url do |scheduler_url|
|
214
|
+
if scheduler_url
|
215
|
+
block.call false
|
216
|
+
next
|
217
|
+
end
|
218
|
+
|
219
|
+
client.options.set( scheduler: { url: @options.scheduler.url } ) do
|
220
|
+
@running[token] = client
|
221
|
+
block.call token
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
# @note Only affects queued Instances, once a Instance has passed through the
|
228
|
+
# {#running} stage it's no longer part of the queue.
|
229
|
+
#
|
230
|
+
# Empties the queue.
|
231
|
+
def clear
|
232
|
+
@queue.clear
|
233
|
+
@by_priority.clear
|
234
|
+
@id_to_priority.clear
|
235
|
+
|
236
|
+
nil
|
237
|
+
end
|
238
|
+
|
239
|
+
# Shuts down the service.
|
240
|
+
def shutdown
|
241
|
+
print_status 'Shutting down...'
|
242
|
+
reactor.delay 2 do
|
243
|
+
reactor.stop
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
# @param [Integer] starting_line
|
248
|
+
# Sets the starting line for the range of errors to return.
|
249
|
+
#
|
250
|
+
# @return [Array<String>]
|
251
|
+
def errors( starting_line = 0 )
|
252
|
+
return [] if self.error_buffer.empty?
|
253
|
+
|
254
|
+
error_strings = self.error_buffer
|
255
|
+
|
256
|
+
if starting_line != 0
|
257
|
+
error_strings = error_strings[starting_line..-1]
|
258
|
+
end
|
259
|
+
|
260
|
+
error_strings
|
261
|
+
end
|
262
|
+
|
263
|
+
# @return [TrueClass]
|
264
|
+
def alive?
|
265
|
+
@server.alive?
|
266
|
+
end
|
267
|
+
|
268
|
+
protected
|
269
|
+
|
270
|
+
def pop
|
271
|
+
return if @queue.empty?
|
272
|
+
|
273
|
+
top_priority, q = @by_priority.first
|
274
|
+
|
275
|
+
id = q.pop
|
276
|
+
r = [id, @queue.delete( id )]
|
277
|
+
|
278
|
+
@by_priority.delete( top_priority ) if q.empty?
|
279
|
+
|
280
|
+
r
|
281
|
+
end
|
282
|
+
|
283
|
+
private
|
284
|
+
|
285
|
+
def consume_queue
|
286
|
+
spawning = false
|
287
|
+
reactor.at_interval( TICK_CONSUME ) do
|
288
|
+
next if self.empty? || spawning
|
289
|
+
|
290
|
+
spawning = true
|
291
|
+
spawn_instance do |client|
|
292
|
+
if client == :error
|
293
|
+
spawning = false
|
294
|
+
next
|
295
|
+
end
|
296
|
+
|
297
|
+
if !client
|
298
|
+
print_debug 'Could not get Instance, all systems are at max utilization.'
|
299
|
+
spawning = false
|
300
|
+
next
|
301
|
+
end
|
302
|
+
|
303
|
+
id, options = self.pop
|
304
|
+
|
305
|
+
print_status "[#{id}] Got Instance: #{client.url}/#{client.token}"
|
306
|
+
|
307
|
+
client.run( options ) do
|
308
|
+
spawning = false
|
309
|
+
|
310
|
+
print_status "[#{id}] Instance started."
|
311
|
+
@running[id] = client
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
def monitor_instances
|
318
|
+
checking = false
|
319
|
+
reactor.at_interval( Options.scheduler.ping_interval ) do
|
320
|
+
next if checking
|
321
|
+
print_debug 'Checking running Instances.'
|
322
|
+
checking = true
|
323
|
+
|
324
|
+
each = proc { |(id, c), i| check_instance( id, c ) { i.next } }
|
325
|
+
after = proc { checking = false }
|
326
|
+
reactor.create_iterator( @running ).each( each, after )
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
def check_instance( id, client, &block )
|
331
|
+
print_debug "[#{id}] Checking status."
|
332
|
+
|
333
|
+
client.busy? do |busy|
|
334
|
+
if busy.rpc_exception?
|
335
|
+
handle_rpc_error( id, busy )
|
336
|
+
block.call
|
337
|
+
elsif busy
|
338
|
+
print_debug "[#{id}] Busy."
|
339
|
+
block.call
|
340
|
+
else
|
341
|
+
get_report_and_shutdown( id, client, &block )
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
def handle_rpc_error( id, error )
|
347
|
+
print_error "[#{id}] Failed: [#{error.class}] #{error.to_s}"
|
348
|
+
|
349
|
+
@failed[id] = {
|
350
|
+
error: error.class.to_s,
|
351
|
+
description: error.to_s
|
352
|
+
}
|
353
|
+
c = @running.delete( id )
|
354
|
+
c.close if c
|
355
|
+
end
|
356
|
+
|
357
|
+
def get_report_and_shutdown( id, client, &block )
|
358
|
+
print_status "[#{id}] Grabbing report."
|
359
|
+
|
360
|
+
client.generate_report do |report|
|
361
|
+
if report.rpc_exception?
|
362
|
+
handle_rpc_error( id, report )
|
363
|
+
block.call
|
364
|
+
next
|
365
|
+
end
|
366
|
+
|
367
|
+
path = report.save( "#{Options.report.path}/#{id}.#{Report::EXTENSION}" )
|
368
|
+
|
369
|
+
print_status "[#{id}] Report saved at: #{path}"
|
370
|
+
|
371
|
+
client.shutdown do
|
372
|
+
print_status "[#{id}] Completed."
|
373
|
+
|
374
|
+
@running.delete( id ).close
|
375
|
+
@completed[id] = path
|
376
|
+
|
377
|
+
block.call
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
# Starts the dispatcher's server
|
383
|
+
def run
|
384
|
+
reactor.on_error do |_, e|
|
385
|
+
print_error "Reactor: #{e}"
|
386
|
+
|
387
|
+
e.backtrace.each do |l|
|
388
|
+
print_error "Reactor: #{l}"
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
print_status 'Ready'
|
393
|
+
@server.start
|
394
|
+
rescue => e
|
395
|
+
print_exception e
|
396
|
+
|
397
|
+
$stderr.puts "Could not start server, for details see: #{@logfile}"
|
398
|
+
exit 1
|
399
|
+
end
|
400
|
+
|
401
|
+
def reactor
|
402
|
+
Arachni::Reactor.global
|
403
|
+
end
|
404
|
+
|
405
|
+
def trap_interrupts( &block )
|
406
|
+
%w(QUIT INT).each do |signal|
|
407
|
+
trap( signal, &block || Proc.new{ } ) if Signal.list.has_key?( signal )
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
def prep_logging
|
412
|
+
# reroute all output to a logfile
|
413
|
+
@logfile ||= reroute_to_file(
|
414
|
+
@options.paths.logs + "/Scheduler - #{Process.pid}-#{@options.rpc.server_port}.log"
|
415
|
+
)
|
416
|
+
end
|
417
|
+
|
418
|
+
def dispatcher
|
419
|
+
return if !Options.dispatcher.url
|
420
|
+
@dispatcher ||= RPC::Client::Dispatcher.new( Options.dispatcher.url )
|
421
|
+
end
|
422
|
+
|
423
|
+
def spawn_instance( &block )
|
424
|
+
if dispatcher
|
425
|
+
options = {
|
426
|
+
owner: self.class.to_s,
|
427
|
+
helpers: {
|
428
|
+
owner: {
|
429
|
+
url: @url
|
430
|
+
}
|
431
|
+
}
|
432
|
+
}
|
433
|
+
|
434
|
+
dispatcher.dispatch options do |info|
|
435
|
+
if info.rpc_exception?
|
436
|
+
print_error "Failed to contact Dispatcher at: #{dispatcher.url}"
|
437
|
+
print_error "[#{info.class}] #{info.to_s}"
|
438
|
+
block.call :error
|
439
|
+
next
|
440
|
+
end
|
441
|
+
|
442
|
+
if info
|
443
|
+
client = connect_to_instance( info['url'], info['token'] )
|
444
|
+
client.options.set( scheduler: { url: @options.scheduler.url } ) do
|
445
|
+
block.call( client )
|
446
|
+
end
|
447
|
+
|
448
|
+
else
|
449
|
+
block.call
|
450
|
+
end
|
451
|
+
end
|
452
|
+
else
|
453
|
+
return block.call if System.max_utilization?
|
454
|
+
|
455
|
+
Processes::Instances.spawn(
|
456
|
+
application: Options.paths.application,
|
457
|
+
port_range: Options.scheduler.instance_port_range,
|
458
|
+
&block
|
459
|
+
)
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
def connect_to_instance( url, token )
|
464
|
+
RPC::Client::Instance.new( url, token )
|
465
|
+
end
|
466
|
+
|
467
|
+
# @param [Base] server
|
468
|
+
# Prepares all the RPC handlers for the given `server`.
|
469
|
+
def set_handlers( server )
|
470
|
+
server.add_async_check do |method|
|
471
|
+
# methods that expect a block are async
|
472
|
+
method.parameters.flatten.include? :block
|
473
|
+
end
|
474
|
+
|
475
|
+
server.add_handler( 'scheduler', self )
|
476
|
+
end
|
477
|
+
|
478
|
+
end
|
479
|
+
|
480
|
+
end
|
481
|
+
end
|
482
|
+
end
|