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,112 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require "#{Cuboid::Options.paths.lib}/rpc/server/dispatcher"
|
3
|
+
|
4
|
+
describe Cuboid::RPC::Server::Dispatcher::Service do
|
5
|
+
before( :each ) do
|
6
|
+
Cuboid::Options.paths.services = "#{fixtures_path}services/"
|
7
|
+
Cuboid::Options.system.max_slots = 10
|
8
|
+
end
|
9
|
+
let(:instance_count) { 3 }
|
10
|
+
let(:dispatcher) { dispatcher_spawn application: "#{fixtures_path}/mock_app.rb" }
|
11
|
+
let(:subject) { dispatcher.test_service }
|
12
|
+
|
13
|
+
describe '#dispatcher' do
|
14
|
+
it 'provides access to the parent Dispatcher' do
|
15
|
+
expect(subject.test_dispatcher).to be_truthy
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#opts' do
|
20
|
+
it 'provides access to the Dispatcher\'s options' do
|
21
|
+
expect(subject.test_opts).to be_truthy
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#node' do
|
26
|
+
it 'provides access to the Dispatcher\'s node' do
|
27
|
+
expect(subject.test_node).to be_truthy
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe '#instances' do
|
32
|
+
before(:each) do
|
33
|
+
instance_count.times { dispatcher.dispatch }
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'provides access to the running instances' do
|
37
|
+
expect(subject.instances.map{ |i| i['pid'] }).to eq(subject.instances.map{ |j| j['pid'] })
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe '#map_instances' do
|
42
|
+
before(:each) do
|
43
|
+
instance_count.times { dispatcher.dispatch }
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'asynchronously maps all running instances' do
|
47
|
+
expect(subject.test_map_instances).to eq(
|
48
|
+
Hash[subject.instances.map { |j| [j['url'], j['token']] }]
|
49
|
+
)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '#each_instance' do
|
54
|
+
before(:each) do
|
55
|
+
instance_count.times { dispatcher.dispatch }
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'asynchronously iterates over all running instances' do
|
59
|
+
subject.test_each_instance
|
60
|
+
auths = subject.instances.map do |j|
|
61
|
+
Cuboid::RPC::Client::Instance.new(
|
62
|
+
j['url'], j['token']
|
63
|
+
).options.authorized_by
|
64
|
+
end
|
65
|
+
|
66
|
+
expect(auths.size).to eq(instance_count)
|
67
|
+
auths.sort!
|
68
|
+
|
69
|
+
1.upto( instance_count ).each do |i|
|
70
|
+
expect(auths[i-1]).to eq "test_#{i}@test.com"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#defer' do
|
76
|
+
it 'defers execution of the given block' do
|
77
|
+
args = [1, 'stuff']
|
78
|
+
expect(subject.test_defer( *args )).to eq(args)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe '#run_asap' do
|
83
|
+
it 'runs the given block as soon as possible' do
|
84
|
+
args = [1, 'stuff']
|
85
|
+
expect(subject.test_run_asap( *args )).to eq(args)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe '#iterator_for' do
|
90
|
+
it 'provides an asynchronous iterator' do
|
91
|
+
expect(subject.test_iterator_for).to be_truthy
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe '#connect_to_dispatcher' do
|
96
|
+
it 'connects to the a dispatcher by url' do
|
97
|
+
expect(subject.test_connect_to_dispatcher( dispatcher.url )).to be_truthy
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe '#connect_to_instance' do
|
102
|
+
it 'connects to an instance' do
|
103
|
+
dispatcher.dispatch
|
104
|
+
instance = subject.instances.first
|
105
|
+
|
106
|
+
expect(subject.test_connect_to_instance( instance )).to be_falsey
|
107
|
+
expect(subject.test_connect_to_instance( instance['url'], instance['token'] )).to be_falsey
|
108
|
+
expect(subject.test_connect_to_instance( url: instance['url'], token: instance['token'] )).to be_falsey
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
@@ -0,0 +1,317 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
require "#{Cuboid::Options.paths.lib}/rpc/server/dispatcher"
|
5
|
+
|
6
|
+
describe Cuboid::RPC::Server::Dispatcher do
|
7
|
+
before( :each ) do
|
8
|
+
Cuboid::Options.system.max_slots = slots
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:instance_info_keys) { %w(token application pid url owner birthdate helpers now age) }
|
12
|
+
let(:slots) { 3 }
|
13
|
+
let(:subject) { dispatcher_spawn( application: "#{fixtures_path}/mock_app.rb" ) }
|
14
|
+
|
15
|
+
describe '#alive?' do
|
16
|
+
it 'returns true' do
|
17
|
+
expect(subject.alive?).to eq(true)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#preferred' do
|
22
|
+
context 'when the dispatcher is a grid member' do
|
23
|
+
it 'returns the URL of least burdened Dispatcher' do
|
24
|
+
dispatcher_spawn( neighbour: subject.url ).dispatch( load_balance: false )
|
25
|
+
dispatcher_spawn( neighbour: subject.url ).dispatch( load_balance: false )
|
26
|
+
|
27
|
+
expect(subject.preferred).to eq(subject.url)
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'and all Dispatchers are at max utilization' do
|
31
|
+
before :each do
|
32
|
+
subject.dispatch( load_balance: false )
|
33
|
+
end
|
34
|
+
|
35
|
+
let(:slots) { 1 }
|
36
|
+
|
37
|
+
it 'returns nil' do
|
38
|
+
dispatcher_spawn( neighbour: subject.url ).dispatch( load_balance: false )
|
39
|
+
dispatcher_spawn( neighbour: subject.url ).dispatch( load_balance: false )
|
40
|
+
|
41
|
+
expect(subject.preferred).to be_nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'when the dispatcher is not a grid member' do
|
47
|
+
it 'returns the URL of the Dispatcher' do
|
48
|
+
expect(subject.preferred).to eq(subject.url)
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'and it is at max utilization' do
|
52
|
+
before :each do
|
53
|
+
subject.dispatch( load_balance: false )
|
54
|
+
end
|
55
|
+
|
56
|
+
let(:slots) { 1 }
|
57
|
+
|
58
|
+
it 'returns nil' do
|
59
|
+
expect(subject.preferred).to be_nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#handlers' do
|
66
|
+
it 'returns an array of loaded handlers' do
|
67
|
+
expect(subject.services.include?( 'test_service' )).to be_truthy
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe '#dispatch' do
|
72
|
+
it 'does not leak Instances' do
|
73
|
+
slots.times do
|
74
|
+
subject.dispatch
|
75
|
+
end
|
76
|
+
|
77
|
+
expect(subject.instances.size).to eq(slots)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'sets OptionGroups::Dispatcher#url' do
|
81
|
+
info = subject.dispatch
|
82
|
+
instance = instance_connect( info['url'], info['token'] )
|
83
|
+
|
84
|
+
expect(instance.dispatcher_url).to eq subject.url
|
85
|
+
end
|
86
|
+
|
87
|
+
context "when #{Cuboid::OptionGroups::RPC}#server_external_address has been set" do
|
88
|
+
before :each do
|
89
|
+
Cuboid::Options.rpc.server_external_address = address
|
90
|
+
end
|
91
|
+
|
92
|
+
let(:address) { '127.0.0.2' }
|
93
|
+
|
94
|
+
it 'advertises that address' do
|
95
|
+
expect(subject.dispatch['url']).to start_with "#{address}:"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context 'when not a Grid member' do
|
100
|
+
it 'returns Instance info' do
|
101
|
+
info = subject.dispatch( owner: 'rspec' )
|
102
|
+
|
103
|
+
%w(token application pid url owner birthdate helpers).each do |k|
|
104
|
+
expect(info[k]).to be_truthy
|
105
|
+
end
|
106
|
+
|
107
|
+
instance = instance_connect( info['url'], info['token'] )
|
108
|
+
expect(instance.alive?).to be_truthy
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'assigns an optional owner' do
|
112
|
+
owner = 'blah'
|
113
|
+
expect(subject.dispatch( owner: owner )['owner']).to eq(owner)
|
114
|
+
end
|
115
|
+
|
116
|
+
context 'when the there are no available slots' do
|
117
|
+
let(:slots) { 5 }
|
118
|
+
before :each do
|
119
|
+
slots.times do
|
120
|
+
subject.dispatch
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'returns nil' do
|
125
|
+
expect(subject.dispatch).to be nil
|
126
|
+
end
|
127
|
+
|
128
|
+
context 'and slots are freed' do
|
129
|
+
let(:free) { slots - 1 }
|
130
|
+
|
131
|
+
it 'returns Instance info' do
|
132
|
+
subject.instances[0...free].each do |info|
|
133
|
+
service = instance_connect( info['url'], info['token'] )
|
134
|
+
service.shutdown
|
135
|
+
|
136
|
+
while sleep 0.1
|
137
|
+
service.alive? rescue break
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
instances = []
|
142
|
+
free.times do
|
143
|
+
instances << subject.dispatch
|
144
|
+
end
|
145
|
+
instances.compact!
|
146
|
+
|
147
|
+
expect(instances.size).to eq free
|
148
|
+
expect(subject.dispatch).to be nil
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
context 'when a Grid member' do
|
155
|
+
let(:slots) { 4 }
|
156
|
+
|
157
|
+
it 'returns Instance info from the least burdened Dispatcher' do
|
158
|
+
d1 = dispatcher_spawn(
|
159
|
+
address: '127.0.0.1',
|
160
|
+
application: "#{fixtures_path}/mock_app.rb"
|
161
|
+
)
|
162
|
+
|
163
|
+
3.times do
|
164
|
+
d1.dispatch( load_balance: false )
|
165
|
+
end
|
166
|
+
|
167
|
+
d2 = dispatcher_spawn(
|
168
|
+
address: '127.0.0.2',
|
169
|
+
neighbour: d1.url,
|
170
|
+
application: "#{fixtures_path}/mock_app.rb"
|
171
|
+
)
|
172
|
+
|
173
|
+
2.times do
|
174
|
+
d2.dispatch( load_balance: false )
|
175
|
+
end
|
176
|
+
|
177
|
+
d3 = dispatcher_spawn(
|
178
|
+
address: '127.0.0.3',
|
179
|
+
neighbour: d1.url,
|
180
|
+
application: "#{fixtures_path}/mock_app.rb"
|
181
|
+
)
|
182
|
+
d3.dispatch( load_balance: false )
|
183
|
+
preferred = d3.url.split( ':' ).first
|
184
|
+
|
185
|
+
expect(d3.dispatch['url'].split( ':' ).first).to eq(preferred)
|
186
|
+
expect(%W{127.0.0.3 127.0.0.2}).to include d1.dispatch['url'].split( ':' ).first
|
187
|
+
expect(d2.dispatch['url'].split( ':' ).first).to eq(preferred)
|
188
|
+
expect(%W{127.0.0.1 127.0.0.3}).to include d3.dispatch['url'].split( ':' ).first
|
189
|
+
expect(%W{127.0.0.2 127.0.0.3}).to include d3.dispatch['url'].split( ':' ).first
|
190
|
+
expect(%W{127.0.0.2 127.0.0.3}).to include d1.dispatch['url'].split( ':' ).first
|
191
|
+
end
|
192
|
+
|
193
|
+
context 'when the load-balance option is set to false' do
|
194
|
+
it 'returns an Instance from the requested Dispatcher' do
|
195
|
+
d1 = dispatcher_spawn(
|
196
|
+
address: '127.0.0.1',
|
197
|
+
application: "#{fixtures_path}/mock_app.rb"
|
198
|
+
)
|
199
|
+
|
200
|
+
d1.dispatch( load_balance: false )
|
201
|
+
|
202
|
+
d2 = dispatcher_spawn(
|
203
|
+
address: '127.0.0.2',
|
204
|
+
neighbour: d1.url,
|
205
|
+
application: "#{fixtures_path}/mock_app.rb"
|
206
|
+
)
|
207
|
+
d2.dispatch( load_balance: false )
|
208
|
+
|
209
|
+
d3 = dispatcher_spawn(
|
210
|
+
address: '127.0.0.3',
|
211
|
+
neighbour: d1.url,
|
212
|
+
application: "#{fixtures_path}/mock_app.rb"
|
213
|
+
)
|
214
|
+
2.times do
|
215
|
+
d3.dispatch( load_balance: false )
|
216
|
+
end
|
217
|
+
|
218
|
+
expect(d3.dispatch( load_balance: false )['url'].
|
219
|
+
split( ':' ).first).to eq('127.0.0.3')
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
describe '#instance' do
|
226
|
+
it 'returns proc info by PID' do
|
227
|
+
instance = subject.dispatch( owner: 'rspec' )
|
228
|
+
info = subject.instance( instance['pid'] )
|
229
|
+
instance_info_keys.each do |k|
|
230
|
+
expect(info[k]).to be_truthy
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
describe '#instances' do
|
236
|
+
it 'returns proc info by PID for all instances' do
|
237
|
+
slots.times { subject.dispatch( owner: 'rspec' ) }
|
238
|
+
|
239
|
+
subject.instances.each do |instance|
|
240
|
+
instance_info_keys.each do |k|
|
241
|
+
expect(instance[k]).to be_truthy
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
describe '#running_instances' do
|
248
|
+
it 'returns proc info for running instances' do
|
249
|
+
slots.times { subject.dispatch }
|
250
|
+
|
251
|
+
expect(subject.running_instances.size).to eq(slots)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
describe '#finished_instances' do
|
256
|
+
it 'returns proc info for finished instances' do
|
257
|
+
3.times { Cuboid::Processes::Manager.kill subject.dispatch['pid'] }
|
258
|
+
|
259
|
+
expect(subject.finished_instances.size).to eq(3)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
describe '#utilization' do
|
264
|
+
it 'returns a float signifying the amount of workload' do
|
265
|
+
3.times do
|
266
|
+
subject.dispatch
|
267
|
+
end
|
268
|
+
|
269
|
+
expect(subject.utilization).to eq(3 / Float(slots))
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
describe '#statistics' do
|
274
|
+
it 'returns general statistics' do
|
275
|
+
subject.dispatch
|
276
|
+
instances = subject.instances
|
277
|
+
Cuboid::Processes::Manager.kill( instances.first['pid'] )
|
278
|
+
|
279
|
+
stats = subject.statistics
|
280
|
+
|
281
|
+
%w(utilization running_instances finished_instances node
|
282
|
+
consumed_pids snapshots).each do |k|
|
283
|
+
expect(stats[k]).to be_truthy
|
284
|
+
end
|
285
|
+
|
286
|
+
finished = stats['finished_instances']
|
287
|
+
expect(finished.size).to eq(1)
|
288
|
+
|
289
|
+
expect(stats['node']).to eq(subject.node.info)
|
290
|
+
end
|
291
|
+
|
292
|
+
context 'when there are snapshots' do
|
293
|
+
it 'lists them' do
|
294
|
+
info = subject.dispatch
|
295
|
+
|
296
|
+
instance = Cuboid::RPC::Client::Instance.new(
|
297
|
+
info['url'], info['token']
|
298
|
+
)
|
299
|
+
|
300
|
+
instance.run
|
301
|
+
instance.suspend!
|
302
|
+
sleep 1 while !instance.suspended?
|
303
|
+
snapshot_path = instance.snapshot_path
|
304
|
+
instance.shutdown
|
305
|
+
|
306
|
+
expect(subject.statistics['snapshots']).to include snapshot_path
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
describe '#log' do
|
312
|
+
it 'returns the contents of the log file' do
|
313
|
+
expect(subject.log).to be_truthy
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
end
|
@@ -0,0 +1,307 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'spec_helper'
|
3
|
+
require "#{fixtures_path}/mock_app"
|
4
|
+
|
5
|
+
describe 'Cuboid::RPC::Server::Instance' do
|
6
|
+
let(:subject) { instance_spawn( application: "#{fixtures_path}/mock_app.rb" ) }
|
7
|
+
|
8
|
+
it 'supports UNIX sockets', if: Arachni::Reactor.supports_unix_sockets? do
|
9
|
+
socket = "#{Dir.tmpdir}/cuboid-instance-#{Cuboid::Utilities.generate_token}"
|
10
|
+
subject = instance_spawn(
|
11
|
+
socket: socket,
|
12
|
+
application: "#{fixtures_path}/mock_app.rb"
|
13
|
+
)
|
14
|
+
|
15
|
+
expect(subject.url).to eq(socket)
|
16
|
+
expect(subject.alive?).to be_truthy
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#application' do
|
20
|
+
it 'returns the application name' do
|
21
|
+
expect(subject.application).to eq 'MockApp'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#snapshot_path' do
|
26
|
+
it 'returns the path to the future/current snapshot' do
|
27
|
+
subject.run
|
28
|
+
|
29
|
+
Timeout.timeout 5 do
|
30
|
+
sleep 1 while !subject.running?
|
31
|
+
end
|
32
|
+
|
33
|
+
expect(subject.snapshot_path).to end_with '.csf'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#suspend!' do
|
38
|
+
it 'suspends the Instance to disk' do
|
39
|
+
subject.run
|
40
|
+
|
41
|
+
Timeout.timeout 5 do
|
42
|
+
sleep 1 while !subject.running?
|
43
|
+
end
|
44
|
+
|
45
|
+
subject.suspend!
|
46
|
+
|
47
|
+
Timeout.timeout 5 do
|
48
|
+
sleep 1 while !subject.suspended?
|
49
|
+
end
|
50
|
+
|
51
|
+
expect(File.exists?( subject.snapshot_path )).to be_truthy
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe '#suspended?' do
|
56
|
+
context 'when the Instance has not been suspended' do
|
57
|
+
it 'returns false' do
|
58
|
+
expect(subject.suspended?).to be_falsey
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'when the Instance has been suspended' do
|
63
|
+
it 'returns true' do
|
64
|
+
subject.run
|
65
|
+
|
66
|
+
Timeout.timeout 5 do
|
67
|
+
sleep 1 while !subject.running?
|
68
|
+
end
|
69
|
+
|
70
|
+
subject.suspend!
|
71
|
+
|
72
|
+
Timeout.timeout 5 do
|
73
|
+
sleep 1 while !subject.suspended?
|
74
|
+
end
|
75
|
+
|
76
|
+
expect(subject.suspended?).to be_truthy
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe '#busy?' do
|
82
|
+
context 'when the Instance is not running' do
|
83
|
+
it 'returns false' do
|
84
|
+
expect(subject.busy?).to be_falsey
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'when the Instance is running' do
|
89
|
+
it 'returns true' do
|
90
|
+
subject.run
|
91
|
+
expect(subject.busy?).to be_truthy
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe '#restore!' do
|
97
|
+
it 'suspends the Instance to disk' do
|
98
|
+
subject.run
|
99
|
+
|
100
|
+
options = subject.generate_report.options
|
101
|
+
|
102
|
+
subject.suspend!
|
103
|
+
|
104
|
+
Timeout.timeout 5 do
|
105
|
+
sleep 1 while subject.status != :suspended
|
106
|
+
end
|
107
|
+
|
108
|
+
snapshot_path = subject.snapshot_path
|
109
|
+
subject.shutdown
|
110
|
+
|
111
|
+
subject = instance_spawn
|
112
|
+
subject.restore! snapshot_path
|
113
|
+
|
114
|
+
File.delete snapshot_path
|
115
|
+
|
116
|
+
sleep 1 while subject.status != :done
|
117
|
+
|
118
|
+
expect(subject.generate_report.options).to eq(options)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe '#errors' do
|
123
|
+
before(:each) do
|
124
|
+
subject.error_test error
|
125
|
+
end
|
126
|
+
let(:error) { "My error #{rand(9999)}" }
|
127
|
+
|
128
|
+
context 'when no argument has been provided' do
|
129
|
+
it 'returns all logged errors' do
|
130
|
+
expect(subject.errors.last).to end_with error
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
context 'when a start line-range has been provided' do
|
135
|
+
it 'returns all logged errors after that line' do
|
136
|
+
initial_errors = subject.errors
|
137
|
+
errors = subject.errors( 10 )
|
138
|
+
|
139
|
+
expect(initial_errors[10..-1]).to eq(errors)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe '#error_logfile' do
|
145
|
+
before(:each) do
|
146
|
+
subject.error_test error
|
147
|
+
end
|
148
|
+
let(:error) { "My error #{rand(9999)}" }
|
149
|
+
|
150
|
+
it 'returns the path to the error logfile' do
|
151
|
+
errors = IO.read( subject.error_logfile )
|
152
|
+
|
153
|
+
subject.errors.each do |error|
|
154
|
+
expect(errors).to include error
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
describe '#alive?' do
|
160
|
+
it 'returns true' do
|
161
|
+
expect(subject.alive?).to eq(true)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe '#paused?' do
|
166
|
+
context 'when not paused' do
|
167
|
+
it 'returns false' do
|
168
|
+
expect(subject.paused?).to be_falsey
|
169
|
+
end
|
170
|
+
end
|
171
|
+
context 'when paused' do
|
172
|
+
it 'returns true' do
|
173
|
+
subject.run
|
174
|
+
|
175
|
+
subject.pause!
|
176
|
+
Timeout.timeout 5 do
|
177
|
+
sleep 1 while !subject.paused?
|
178
|
+
end
|
179
|
+
|
180
|
+
expect(subject.paused?).to be_truthy
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
describe '#resume!' do
|
186
|
+
it 'resumes the Instance' do
|
187
|
+
subject.run
|
188
|
+
|
189
|
+
subject.pause!
|
190
|
+
Timeout.timeout 5 do
|
191
|
+
sleep 1 while !subject.paused?
|
192
|
+
end
|
193
|
+
|
194
|
+
expect(subject.paused?).to be_truthy
|
195
|
+
expect(subject.resume!).to be_truthy
|
196
|
+
|
197
|
+
Timeout.timeout 5 do
|
198
|
+
sleep 1 while subject.paused?
|
199
|
+
end
|
200
|
+
|
201
|
+
expect(subject.paused?).to be_falsey
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
describe '#abort_and_generate_report' do
|
206
|
+
it "cleans-up and returns the report as #{Cuboid::Report}" do
|
207
|
+
expect(subject.abort_and_generate_report).to be_kind_of Cuboid::Report
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
describe '#status' do
|
212
|
+
context 'after initialization' do
|
213
|
+
it 'returns :ready' do
|
214
|
+
expect(subject.status).to eq(:ready)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
context 'after #run has been called' do
|
219
|
+
it 'returns :running' do
|
220
|
+
subject.run
|
221
|
+
|
222
|
+
sleep 2
|
223
|
+
expect(subject.status).to eq(:running)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
context 'once the Instance has completed' do
|
228
|
+
it 'returns :done' do
|
229
|
+
subject.run
|
230
|
+
|
231
|
+
sleep 1 while subject.busy?
|
232
|
+
expect(subject.status).to eq(:done)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
describe '#run' do
|
238
|
+
context 'on invalid options' do
|
239
|
+
it 'raises ArgumentError' do
|
240
|
+
expect do
|
241
|
+
subject.run invalid: :stuff
|
242
|
+
end.to raise_error Arachni::RPC::Exceptions::RemoteException
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'configures and starts a job' do
|
247
|
+
expect(subject.busy?).to be false
|
248
|
+
expect(subject.status).to be :ready
|
249
|
+
|
250
|
+
subject.run
|
251
|
+
|
252
|
+
# if a run in already running it should just bail out early
|
253
|
+
expect(subject.run).to be_falsey
|
254
|
+
|
255
|
+
sleep 1 while subject.busy?
|
256
|
+
|
257
|
+
expect(subject.busy?).to be false
|
258
|
+
expect(subject.status).to be :done
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
describe '#progress' do
|
263
|
+
before( :each ) do
|
264
|
+
subject.run
|
265
|
+
sleep 1 while subject.busy?
|
266
|
+
end
|
267
|
+
|
268
|
+
it 'returns progress information' do
|
269
|
+
p = subject.progress
|
270
|
+
expect(p[:busy]).to be false
|
271
|
+
expect(p[:status]).to be :done
|
272
|
+
expect(p[:statistics]).to be_any
|
273
|
+
|
274
|
+
expect(p[:seed]).not_to be_empty
|
275
|
+
end
|
276
|
+
|
277
|
+
describe ':without' do
|
278
|
+
describe ':statistics' do
|
279
|
+
it 'includes statistics' do
|
280
|
+
expect(subject.progress(
|
281
|
+
without: :statistics
|
282
|
+
)).not_to include :statistics
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
context 'with an array of things to be excluded' do
|
287
|
+
it 'excludes those things'
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
describe ':with' do
|
292
|
+
context 'with an array of things to be included' do
|
293
|
+
it 'includes those things'
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
describe '#shutdown' do
|
299
|
+
it 'shuts-down the instance' do
|
300
|
+
expect(subject.shutdown).to be_truthy
|
301
|
+
sleep 4
|
302
|
+
|
303
|
+
expect { subject.alive? }.to raise_error Arachni::RPC::Exceptions::ConnectionError
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
end
|