├── .gitignore ├── .ruby-gemset ├── .ruby-version ├── .simplecov ├── .yardopts ├── CHANGELOG.md ├── Gemfile ├── LICENSE.md ├── README.md ├── Rakefile ├── config └── paths.yml ├── cuboid.gemspec ├── examples └── my_app │ ├── .ruby-gemset │ ├── .ruby-version │ ├── Gemfile │ ├── README.md │ ├── application.rb │ ├── lib │ ├── aggregator.rb │ ├── rest_api.rb │ └── rpc_api.rb │ └── runners │ ├── local.rb │ ├── rest.rb │ ├── rest │ └── helpers.rb │ └── rpc.rb ├── lib ├── cuboid.rb ├── cuboid │ ├── application.rb │ ├── application │ │ ├── parts │ │ │ ├── data.rb │ │ │ ├── report.rb │ │ │ └── state.rb │ │ └── runtime.rb │ ├── banner.rb │ ├── data.rb │ ├── data │ │ └── application.rb │ ├── error.rb │ ├── option_group.rb │ ├── option_groups.rb │ ├── option_groups │ │ ├── agent.rb │ │ ├── datastore.rb │ │ ├── output.rb │ │ ├── paths.rb │ │ ├── report.rb │ │ ├── rpc.rb │ │ ├── scheduler.rb │ │ ├── snapshot.rb │ │ └── system.rb │ ├── options.rb │ ├── processes.rb │ ├── processes │ │ ├── agents.rb │ │ ├── executables │ │ │ ├── agent.rb │ │ │ ├── base.rb │ │ │ ├── instance.rb │ │ │ ├── rest_service.rb │ │ │ └── scheduler.rb │ │ ├── helpers.rb │ │ ├── helpers │ │ │ ├── agents.rb │ │ │ ├── instances.rb │ │ │ ├── processes.rb │ │ │ └── schedulers.rb │ │ ├── instances.rb │ │ ├── manager.rb │ │ └── schedulers.rb │ ├── report.rb │ ├── rest │ │ ├── server.rb │ │ └── server │ │ │ ├── instance_helpers.rb │ │ │ └── routes │ │ │ ├── agent.rb │ │ │ ├── grid.rb │ │ │ ├── instances.rb │ │ │ └── scheduler.rb │ ├── rpc │ │ ├── client.rb │ │ ├── client │ │ │ ├── agent.rb │ │ │ ├── base.rb │ │ │ ├── instance.rb │ │ │ ├── instance │ │ │ │ └── service.rb │ │ │ └── scheduler.rb │ │ ├── serializer.rb │ │ └── server │ │ │ ├── active_options.rb │ │ │ ├── agent.rb │ │ │ ├── agent │ │ │ ├── node.rb │ │ │ └── service.rb │ │ │ ├── application_wrapper.rb │ │ │ ├── base.rb │ │ │ ├── instance.rb │ │ │ ├── instance │ │ │ ├── peers.rb │ │ │ └── service.rb │ │ │ ├── output.rb │ │ │ └── scheduler.rb │ ├── ruby.rb │ ├── ruby │ │ ├── array.rb │ │ ├── hash.rb │ │ └── object.rb │ ├── snapshot.rb │ ├── state.rb │ ├── state │ │ ├── application.rb │ │ └── options.rb │ ├── support.rb │ ├── support │ │ ├── buffer.rb │ │ ├── buffer │ │ │ ├── autoflush.rb │ │ │ └── base.rb │ │ ├── cache.rb │ │ ├── cache │ │ │ ├── base.rb │ │ │ ├── least_cost_replacement.rb │ │ │ ├── least_recently_pushed.rb │ │ │ ├── least_recently_used.rb │ │ │ ├── preference.rb │ │ │ └── random_replacement.rb │ │ ├── crypto.rb │ │ ├── crypto │ │ │ └── rsa_aes_cbc.rb │ │ ├── database.rb │ │ ├── database │ │ │ ├── base.rb │ │ │ ├── categorized_queue.rb │ │ │ ├── hash.rb │ │ │ └── queue.rb │ │ ├── filter.rb │ │ ├── filter │ │ │ ├── base.rb │ │ │ └── set.rb │ │ ├── glob.rb │ │ ├── mixins.rb │ │ └── mixins │ │ │ ├── observable.rb │ │ │ ├── parts.rb │ │ │ ├── profiler.rb │ │ │ ├── spec_instances.rb │ │ │ └── terminal.rb │ ├── system.rb │ ├── system │ │ ├── platforms.rb │ │ ├── platforms │ │ │ ├── linux.rb │ │ │ ├── mixins │ │ │ │ └── unix.rb │ │ │ ├── osx.rb │ │ │ └── windows.rb │ │ └── slots.rb │ ├── ui │ │ ├── output.rb │ │ ├── output_interface.rb │ │ └── output_interface │ │ │ ├── abstract.rb │ │ │ ├── controls.rb │ │ │ ├── error_logging.rb │ │ │ ├── implemented.rb │ │ │ └── personalization.rb │ ├── utilities.rb │ └── version.rb └── version ├── logs └── placeholder ├── profiles └── placeholder ├── reports └── placeholder ├── snapshots └── placeholder └── spec ├── cuboid ├── application │ ├── parts │ │ ├── data_spec.rb │ │ ├── report_spec.rb │ │ └── state_spec.rb │ └── runtime_spec.rb ├── application_spec.rb ├── data │ └── application_spec.rb ├── data_spec.rb ├── error_spec.rb ├── option_groups │ ├── datastore_spec.rb │ ├── dispatcher_spec.rb │ ├── output_spec.rb │ ├── paths_spec.rb │ ├── report_spec.rb │ ├── rpc_spec.rb │ ├── snapshot_spec.rb │ └── system.rb ├── options_spec.rb ├── report_spec.rb ├── rest │ └── server_spec.rb ├── rpc │ ├── client │ │ ├── base_spec.rb │ │ ├── dispatcher_spec.rb │ │ └── instance_spec.rb │ └── server │ │ ├── active_options_spec.rb │ │ ├── agent │ │ ├── node_spec.rb │ │ └── service_spec.rb │ │ ├── agent_spec.rb │ │ ├── base_spec.rb │ │ ├── instance_spec.rb │ │ ├── output_spec.rb │ │ └── scheduler_spec.rb ├── ruby │ ├── array_spec.rb │ ├── hash_spec.rb │ └── object_spec.rb ├── snapshot_spec.rb ├── state │ ├── application_spec.rb │ └── options_spec.rb ├── state_spec.rb ├── support │ ├── buffer │ │ ├── autoflush_spec.rb │ │ └── base_spec.rb │ ├── cache │ │ ├── least_cost_replacement_spec.rb │ │ ├── least_recently_pushed_spec.rb │ │ ├── least_recently_used_spec.rb │ │ ├── preference_spec.rb │ │ └── random_replacement_spec.rb │ ├── crypto │ │ └── rsa_aes_cbc_spec.rb │ ├── database │ │ ├── categorized_queue_spec.rb │ │ ├── hash_spec.rb │ │ └── scheduler_spec.rb │ ├── filter │ │ └── set_spec.rb │ ├── glob_spec.rb │ └── mixins │ │ └── observable_spec.rb ├── system │ ├── platforms │ │ ├── linux_spec.rb │ │ ├── osx_spec.rb │ │ └── windows_spec.rb │ └── slots_spec.rb ├── system_spec.rb └── utilities_spec.rb ├── spec_helper.rb └── support ├── factories ├── placeholder └── scan_report.rb ├── fixtures ├── empty │ └── placeholder ├── executables │ └── node.rb ├── mock_app.rb ├── mock_app │ └── test_service.rb └── services │ └── echo.rb ├── helpers ├── framework.rb ├── matchers.rb ├── misc.rb ├── paths.rb ├── request_helpers.rb ├── requires.rb ├── resets.rb └── web_server.rb ├── lib ├── factory.rb ├── web_server_client.rb ├── web_server_dispatcher.rb └── web_server_manager.rb ├── logs └── placeholder ├── pems ├── cacert.pem ├── client │ ├── cert.pem │ ├── foo-cert.pem │ ├── foo-key.pem │ └── key.pem └── server │ ├── cert.pem │ └── key.pem ├── reports └── placeholder ├── shared ├── application.rb ├── component.rb ├── component │ └── options │ │ └── base.rb ├── option_group.rb ├── support │ ├── cache.rb │ └── filter.rb └── system │ └── platforms │ ├── base.rb │ └── mixins │ └── unix.rb └── snapshots └── placeholder /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.gem 3 | /tmp 4 | /logs/*.log 5 | /reports/*.ser 6 | /snapshots/*.ses 7 | /spec/support/logs/*.log 8 | /spec/support/reports/*.crf 9 | /spec/support/snapshots/*.csf 10 | /private.pem 11 | error.log 12 | TODO 13 | .rvmrc 14 | coverage/ 15 | .env 16 | .bundle/ 17 | mkmf.log 18 | target 19 | *.dll 20 | *.dylib 21 | *.lock 22 | *.so 23 | *.tar.gz 24 | .rspec_status 25 | *.crf 26 | *.csf 27 | .rake_tasks~ 28 | -------------------------------------------------------------------------------- /.ruby-gemset: -------------------------------------------------------------------------------- 1 | cuboid 2 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | ruby-2.7.5 2 | -------------------------------------------------------------------------------- /.simplecov: -------------------------------------------------------------------------------- 1 | pid = Process.pid 2 | SimpleCov.at_exit do 3 | SimpleCov.result.format! if Process.pid == pid 4 | end 5 | 6 | SimpleCov.start do 7 | add_filter do |source_file| 8 | path = source_file.filename 9 | path.start_with?( "#{Dir.pwd}/spec" ) || 10 | # We can't monitor the server, they're forked. 11 | path.start_with?( "#{Dir.pwd}/lib/cuboid/rpc/server" ) 12 | end 13 | 14 | add_group 'Core' do |source_file| 15 | path = source_file.filename 16 | path.start_with?( "#{Dir.pwd}/lib/cuboid" ) && 17 | !path.start_with?( "#{Dir.pwd}/lib/cuboid/rpc" ) 18 | end 19 | 20 | add_group 'RPC' do |source_file| 21 | path = source_file.filename 22 | path.start_with?( "#{Dir.pwd}/lib/cuboid/rpc" ) 23 | end 24 | 25 | add_group 'Checks', 'components/checks' 26 | add_group 'Plugins', 'components/plugins' 27 | add_group 'Reports', 'components/reports' 28 | add_group 'Path extractors', 'components/path_extractors' 29 | add_group 'Fingerprinters', 'components/fingerprinters' 30 | add_group 'RPCD Handlers', 'components/rpcd_handlers' 31 | 32 | add_group 'UI', 'ui/' 33 | end 34 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --no-private 2 | --markup=markdown 3 | --verbose 4 | --title "Cuboid - Web Application Security Scanner" 5 | components/**/*.rb 6 | lib/**/*.rb 7 | ui/**/*.rb 8 | - 9 | CHANGELOG.md 10 | LICENSE.md 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.2.9 2 | 3 | * Updated dependencies. 4 | 5 | # 0.2.8 6 | 7 | * `File.exists?` => `File.exist?` 8 | * `Dir.exists?` => `Dir.exist?` 9 | 10 | # 0.2.7 11 | 12 | * `RPC::Server::Agent::Node#unplug` -- Use Raktr iterator. 13 | 14 | # 0.2.6 15 | 16 | * `Application.serializer` now defaults to JSON for REST API compatibility. 17 | 18 | # 0.2.5 19 | 20 | * `RPC::Server::Services::Base` => `RPC::Server::Instance::Service` 21 | * Added `RPC::Server::Instance::Peers` as a helper to iterate over peer `Instances`. 22 | 23 | # 0.2.4.2 24 | 25 | * Instance RPC services now decorated with `Server::Services::Base`. 26 | 27 | # 0.2.4.1 28 | 29 | * `Application`: Added `#application=` to help with inheritance. 30 | 31 | # 0.2.4 32 | 33 | * Made `Server::Instance` services accessible from `Application`. 34 | 35 | # 0.2.3 36 | 37 | * Simplified convergence of P2P mesh network. 38 | 39 | # 0.2.2 40 | 41 | * `Processes::Manager` 42 | * `#spawn` -- Handle `Interrupt` more gracefully. 43 | 44 | # 0.2.1 45 | 46 | * `Processes::Manager` 47 | * `#spawn` -- Handle exited child more gracefully. 48 | 49 | # 0.2 50 | 51 | * `Processes::Manager` 52 | * `#spawn` -- Daemonize servers optionally -- default turned to `false`. 53 | 54 | # 0.1.8 55 | 56 | * `Processes::Manager` 57 | * `#find_in_applications` -- Also include `PATH` search. 58 | 59 | # 0.1.7 60 | 61 | * `Application` 62 | * Added `#shutdown` callback method to handle RPC server shutdowns. 63 | 64 | # 0.1.6.1 65 | 66 | * `OptionGroups::RPC`: Added `#url`. 67 | 68 | # 0.1.5 69 | 70 | * Fixed relative path for application source path detection. 71 | 72 | # 0.1.3 73 | 74 | * Replaced `Arachni::RPC` and `Arachni::Reactor` with `Toq` and `Raktr`. 75 | 76 | # 0.1.0 77 | 78 | * `Dispatcher` => `Agent` 79 | * `Dispatcher#dispatch` => `Agent#spawn` 80 | * `neighbour` => `peer` 81 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rake', '13.0.3' 4 | 5 | group :docs do 6 | gem 'yard' 7 | gem 'redcarpet' 8 | end 9 | 10 | group :spec do 11 | gem 'simplecov', require: false, group: :test 12 | 13 | gem 'typhoeus' 14 | gem 'rspec' 15 | gem 'faker' 16 | end 17 | 18 | group :prof do 19 | gem 'benchmark-ips' 20 | gem 'memory_profiler' 21 | end 22 | 23 | if File.exist? '../toq' 24 | gem 'toq', path: '../toq' 25 | end 26 | 27 | gemspec 28 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | Copyright (c) 2023 Ecsypno Single Member P.C. . 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | =begin 2 | Copyright 2020 Alex Douckas , Tasos Laskos 3 | 4 | This file is part of the Engine Framework project and is subject to 5 | redistribution and commercial restrictions. Please see the Engine Framework 6 | web site for more information on licensing and terms of use. 7 | =end 8 | 9 | require File.expand_path(File.dirname(__FILE__)) + '/lib/cuboid' 10 | 11 | begin 12 | require 'rspec' 13 | require 'rspec/core/rake_task' 14 | 15 | namespace :spec do 16 | 17 | desc 'Run core library tests.' 18 | RSpec::Core::RakeTask.new( :core ) do |t| 19 | t.pattern = FileList[ 'spec/cuboid/**/*_spec.rb' ] 20 | end 21 | 22 | desc 'Run plugin tests.' 23 | RSpec::Core::RakeTask.new( :plugins ) do |t| 24 | t.pattern = FileList[ 'spec/components/plugins/**/*_spec.rb' ] 25 | end 26 | end 27 | 28 | RSpec::Core::RakeTask.new 29 | rescue LoadError 30 | puts 'If you want to run the tests please install rspec first:' 31 | puts ' gem install rspec' 32 | end 33 | 34 | desc 'Generate docs.' 35 | task :docs do 36 | outdir = "../cuboid-docs" 37 | sh "rm -rf #{outdir}" 38 | sh "mkdir -p #{outdir}" 39 | 40 | sh "yardoc -o #{outdir}" 41 | 42 | sh "rm -rf .yardoc" 43 | end 44 | 45 | desc 'Remove reporter and log files.' 46 | task :clean do 47 | files = %w(error.log *.crf *.csf *.yaml *.json *.marshal *.gem pkg/*.gem 48 | reports/*.crf snapshots/*.csf logs/*.log spec/support/logs/*.log 49 | spec/support/reports/*.crf spec/support/snapshots/*.csf 50 | ).map { |file| Dir.glob( file ) }.flatten 51 | 52 | next if files.empty? 53 | 54 | puts 'Removing:' 55 | files.each { |file| puts " * #{file}" } 56 | FileUtils.rm files 57 | end 58 | 59 | desc 'Build the gem.' 60 | task build: [ :clean ] do 61 | sh "gem build cuboid.gemspec" 62 | end 63 | 64 | desc 'Build and install the gem.' 65 | task install: [ :build ] do 66 | sh "gem install cuboid-#{Cuboid::VERSION}.gem" 67 | end 68 | 69 | desc 'Push a new version to Rubygems' 70 | task publish: [ :build ] do 71 | sh "git tag -a v#{Cuboid::VERSION} -m 'Version #{Cuboid::VERSION}'" 72 | sh "gem push cuboid-#{Cuboid::VERSION}.gem" 73 | end 74 | task release: [ :publish ] 75 | -------------------------------------------------------------------------------- /config/paths.yml: -------------------------------------------------------------------------------- 1 | # Sets default locations for writing different kinds of files. 2 | # 3 | # * '~' will be expanded to $HOME. 4 | # * Directories will be created if they don't already exist. 5 | 6 | # Error and RPC logs. 7 | logs: 8 | # Default directory for scan snapshots generated by RPC Instances. 9 | snapshots: 10 | # Default report location for the RPC Scheduler. 11 | reports: 12 | # Directory for temporary files -- like for excess workload that's been 13 | # offloaded to disk etc. 14 | # Will default to the OS temporary directory. 15 | tmpdir: 16 | -------------------------------------------------------------------------------- /cuboid.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | 3 | Gem::Specification.new do |s| 4 | require_relative File.expand_path( File.dirname( __FILE__ ) ) + '/lib/cuboid/version' 5 | 6 | s.name = 'cuboid' 7 | s.version = Cuboid::VERSION 8 | s.date = Time.now.strftime( '%Y-%m-%d' ) 9 | s.summary = 'An application-centric, decentralised and distributed computing solution. ' 10 | 11 | s.homepage = 'https://github.com/qadron/cuboid' 12 | s.email = 'tasos.laskos@gmail.com' 13 | s.authors = [ 'Tasos Laskos' ] 14 | s.licenses = ['MIT'] 15 | 16 | s.files += Dir.glob( 'config/**/**' ) 17 | s.files += Dir.glob( 'lib/**/**' ) 18 | s.files += Dir.glob( 'logs/**/**' ) 19 | s.files += Dir.glob( 'components/**/**' ) 20 | s.files += Dir.glob( 'spec/**/**' ) 21 | s.files += %w(Gemfile Rakefile cuboid.gemspec) 22 | s.test_files = Dir.glob( 'spec/**/**' ) 23 | 24 | s.extra_rdoc_files = %w(README.md LICENSE.md CHANGELOG.md) 25 | 26 | s.rdoc_options = [ '--charset=UTF-8' ] 27 | 28 | s.add_dependency 'awesome_print', '1.6.1' 29 | 30 | # Don't specify version, messes with the packages since they always grab the 31 | # latest one. 32 | s.add_dependency 'bundler' 33 | 34 | s.add_dependency 'concurrent-ruby', '~> 1.3.4' 35 | s.add_dependency 'concurrent-ruby-ext', '~> 1.3.4' 36 | 37 | # For compressing/decompressing system state archives. 38 | s.add_dependency 'rubyzip', '~> 2.4.1' 39 | 40 | s.add_dependency 'childprocess', '~> 5.1.0' 41 | 42 | # RPC serialization. 43 | s.add_dependency 'msgpack', '~> 1.7.5' 44 | 45 | # Web server 46 | s.add_dependency 'puma', '~> 6.5.0' 47 | 48 | s.add_dependency 'rack', '2.2.9' 49 | s.add_dependency 'rack-test' 50 | 51 | # REST API 52 | s.add_dependency 'sinatra', '2.2.3' 53 | s.add_dependency 'sinatra-contrib', '2.2.3' 54 | 55 | # RPC client/server implementation. 56 | s.add_dependency 'toq', '~> 0.1.0' 57 | 58 | s.add_dependency 'vmstat', '~> 2.3.1' 59 | s.add_dependency 'sys-proctable', '~> 1.3.0' 60 | 61 | s.description = < [1, 2, 3] 11 | } 12 | 13 | MyApp.run 14 | # :run 15 | # [ 16 | # [0] 1, 17 | # [1] 2, 18 | # [2] 3 19 | # ] 20 | 21 | ap MyApp.statistics 22 | # { 23 | # :runtime => 0.000982 24 | # } 25 | 26 | ap MyApp.generate_report.data 27 | # [ 28 | # [0] 1, 29 | # [1] 2, 30 | # [2] 3 31 | # ] 32 | -------------------------------------------------------------------------------- /examples/my_app/runners/rest.rb: -------------------------------------------------------------------------------- 1 | require_relative '../application' 2 | 3 | # Access to the #request and #response_* methods. 4 | require_relative 'rest/helpers' 5 | 6 | # Cuboid provides rudimentary process management for common entities, let's get them. 7 | include Cuboid 8 | 9 | # The REST API provides a nice and clean interface for Instances, Queues and 10 | # Agents -- as always, if the Agent is connected to a Grid you can 11 | # enjoy load-balancing. 12 | # 13 | # The REST interface can: 14 | # * spawn Instances on its own 15 | # * use a Agent to spawn Instances 16 | # * if the Agent is part of a Grid it enjoys load-balancing. 17 | # * use a Queue to spawn and manage Instances -- if the Queue is configured 18 | # with a Agent 19 | # * it will use it to spawn Instances 20 | # * if the Agent is part of a Grid it enjoys load-balancing. 21 | 22 | # Simple example, no Agent nor Queue. 23 | pid = MyApp.spawn( :rest, daemonize: true ) 24 | # Wait for the server to boot-up. 25 | sleep 1 while request( :get ).code == 0 26 | 27 | # None yet... 28 | request :get, 'instances' 29 | ap response_data 30 | # {} 31 | 32 | # Spawn an instance, configure it with :application options and run it. 33 | request :post, 'instances', id: [1,2,3] 34 | # :run 35 | # [ 36 | # [0] 1, 37 | # [1] 2, 38 | # [2] 3 39 | # ] 40 | 41 | # Unique identifier for this Instance. 42 | instance_id = response_data['id'] 43 | 44 | # Access to the custom REST API. 45 | request :get, "instances/#{instance_id}/custom/foo" 46 | ap response_data 47 | # "bar" 48 | 49 | # Access to the custom REST API. 50 | request :get, "instances/#{instance_id}/custom/rpc-foo" 51 | ap response_data 52 | # "bar" 53 | 54 | # Access to the custom REST API. 55 | request :get, "instances/#{instance_id}/custom/application_access" 56 | ap response_data 57 | # "foobar" 58 | 59 | # Get Instance runtime info. 60 | request :get, "instances/#{instance_id}" 61 | ap response_data 62 | # { 63 | # "status" => "done", 64 | # "busy" => false, 65 | # "application" => "CuboidApp", 66 | # "seed" => "e255b50f0f782052161423c5c3ddbdab", 67 | # "agent_url" => nil, 68 | # "queue_url" => nil, 69 | # "statistics" => { 70 | # "runtime" => 0.000547441 71 | # }, 72 | # "errors" => [], 73 | # "messages" => [] 74 | # } 75 | 76 | # Get the native report to access the Instances results. 77 | request :get, "instances/#{instance_id}/report.crf" 78 | ap Report.from_crf( response_data ).data 79 | # [ 80 | # [0] 1, 81 | # [1] 2, 82 | # [2] 3 83 | # ] 84 | 85 | # Get the REST API's JSON report to access results. 86 | request :get, "instances/#{instance_id}/report.json" 87 | ap MyApp.serializer.load( response_data['data'] ) 88 | # [ 89 | # [0] 1, 90 | # [1] 2, 91 | # [2] 3 92 | # ] 93 | 94 | # There it now is, on the list of all running Instances. 95 | request :get, 'instances' 96 | ap response_data 97 | # { 98 | # "a8630974a1bf4b8cc8a169fd1addbcc6" => {} 99 | # } 100 | 101 | # Shutdown the Instance, we're done with it. 102 | request :delete, "instances/#{instance_id}" 103 | ap response_data 104 | # nil 105 | 106 | # Gone. 107 | request :get, 'instances' 108 | ap response_data 109 | # {} 110 | 111 | Processes::Manager.kill pid 112 | -------------------------------------------------------------------------------- /examples/my_app/runners/rest/helpers.rb: -------------------------------------------------------------------------------- 1 | require 'typhoeus' 2 | require 'json' 3 | 4 | def response 5 | if @last_response.headers['Content-Type'].include? 'json' 6 | data = JSON.load( @last_response.body ) 7 | else 8 | data = @last_response.body 9 | end 10 | { 11 | code: @last_response.code, 12 | data: data 13 | } 14 | end 15 | 16 | def response_code 17 | response[:code] 18 | end 19 | 20 | def response_data 21 | response[:data] 22 | end 23 | 24 | def request( method, resource = nil, parameters = nil ) 25 | options = {} 26 | 27 | if parameters 28 | if method == :get 29 | options[:params] = parameters 30 | else 31 | options[:body] = parameters.to_json 32 | end 33 | end 34 | 35 | @last_response = Typhoeus.send( 36 | method, 37 | "http://127.0.0.1:7331/#{resource}", 38 | options 39 | ) 40 | end 41 | -------------------------------------------------------------------------------- /lib/cuboid.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'bundler/setup' 3 | require 'tmpdir' 4 | 5 | require_relative 'cuboid/version' 6 | 7 | require 'concurrent' 8 | require 'pp' 9 | require 'ap' 10 | 11 | def ap( obj ) 12 | super obj, raw: true 13 | end 14 | 15 | module Cuboid 16 | 17 | class < 7 | module Data 8 | 9 | # @return [Data::Application] 10 | def data 11 | Cuboid::Data.application 12 | end 13 | 14 | end 15 | 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/cuboid/application/parts/report.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | class Application 3 | module Parts 4 | 5 | # @author Tasos "Zapotek" Laskos 6 | module Report 7 | 8 | def report( results ) 9 | data.report = results 10 | end 11 | 12 | # @return [Cuboid::Report] 13 | # Scan results. 14 | def generate_report 15 | Cuboid::Report.new( 16 | application: self.class, 17 | status: state.status, 18 | options: Options.application, 19 | data: data.report, 20 | start_datetime: @start_datetime, 21 | finish_datetime: @finish_datetime 22 | ) 23 | end 24 | 25 | end 26 | 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/cuboid/application/runtime.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | class Application 3 | 4 | class Runtime 5 | 6 | def state 7 | Cuboid::Application.application.state.runtime 8 | end 9 | 10 | def state=( d ) 11 | Cuboid::Application.application.state.runtime = d 12 | end 13 | 14 | def data 15 | Cuboid::Application.application.data.runtime 16 | end 17 | 18 | def data=( d ) 19 | Cuboid::Application.application.data.runtime = d 20 | end 21 | 22 | end 23 | 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/cuboid/banner.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'bundler/setup' 3 | 4 | module Cuboid 5 | 6 | WEBSITE = 'http://placeholder.com' 7 | 8 | BANNER =< 11 | EOBANNER 12 | 13 | end 14 | -------------------------------------------------------------------------------- /lib/cuboid/data.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | 3 | # Stores and provides access to the data of the system. 4 | # 5 | # @author Tasos "Zapotek" Laskos 6 | class Data 7 | 8 | # {Data} error namespace. 9 | # 10 | # All {Data} errors inherit from and live under it. 11 | # 12 | # @author Tasos "Zapotek" Laskos 13 | class Error < Cuboid::Error 14 | end 15 | 16 | require_relative 'data/application' 17 | 18 | class < 7 | class Application 8 | 9 | # {Application} error namespace. 10 | # 11 | # All {Application} errors inherit from and live under it. 12 | # 13 | # @author Tasos "Zapotek" Laskos 14 | class Error < Data::Error 15 | end 16 | 17 | attr_accessor :runtime 18 | attr_accessor :report 19 | 20 | def statistics 21 | { 22 | runtime: !!@runtime, 23 | report: !!@report 24 | } 25 | end 26 | 27 | def dump( directory ) 28 | FileUtils.mkdir_p( directory ) 29 | 30 | d = Cuboid::Application.serializer.dump( @report ) 31 | IO.binwrite( "#{directory}/report", d ) 32 | 33 | d = Cuboid::Application.serializer.dump( @runtime ) 34 | IO.binwrite( "#{directory}/runtime", d ) 35 | end 36 | 37 | def self.load( directory ) 38 | application = new 39 | application.report = Cuboid::Application.serializer.load( IO.binread( "#{directory}/report" ) ) 40 | application.runtime = Cuboid::Application.serializer.load( IO.binread( "#{directory}/runtime" ) ) 41 | application 42 | end 43 | 44 | def clear 45 | @runtime = nil 46 | @report = nil 47 | end 48 | 49 | end 50 | 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /lib/cuboid/error.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | 3 | # It provides a namespace for all system errors. 4 | # 5 | # @author Tasos "Zapotek" Laskos 6 | class Error < StandardError 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /lib/cuboid/option_group.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | 3 | # @author Tasos "Zapotek" Laskos 4 | class OptionGroup 5 | 6 | # @author Tasos "Zapotek" Laskos 7 | class Error < Options::Error 8 | end 9 | 10 | class < 6 | class Agent < Cuboid::OptionGroup 7 | 8 | STRATEGIES = Set.new([:horizontal, :vertical, :direct]) 9 | 10 | # @return [String] 11 | # URL of a {RPC::Server::Agent}. 12 | attr_accessor :url 13 | 14 | # @return [Array] 15 | # Range of ports to use when spawning instances, first entry should be 16 | # the lowest port number, last the max port number. 17 | attr_accessor :instance_port_range 18 | 19 | # @return [String] 20 | # The URL of a peering {RPC::Server::Agent}, applicable when 21 | # {RPC::Server::Agent} are connected to each other to form a Grid. 22 | # 23 | # @see RPC::Server::Agent::Node 24 | attr_accessor :peer 25 | 26 | # @return [Float] 27 | # How regularly to check for peer statuses. 28 | attr_accessor :ping_interval 29 | 30 | # @return [String] 31 | # Agent name. 32 | attr_accessor :name 33 | 34 | attr_accessor :strategy 35 | 36 | set_defaults( 37 | strategy: :horizontal, 38 | ping_interval: 5.0, 39 | instance_port_range: [1025, 65535] 40 | ) 41 | 42 | def strategy=( type ) 43 | return @strategy = defaults[:strategy] if !type 44 | 45 | type = type.to_sym 46 | if !STRATEGIES.include? type 47 | fail ArgumentError, "Unknown strategy type: #{type}" 48 | end 49 | 50 | @strategy = type 51 | end 52 | 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/cuboid/option_groups/datastore.rb: -------------------------------------------------------------------------------- 1 | require 'ostruct' 2 | 3 | module Cuboid::OptionGroups 4 | 5 | # Generic OpenStruct-based class for general purpose data storage. 6 | # 7 | # @author Tasos "Zapotek" Laskos 8 | class Datastore < Cuboid::OptionGroup 9 | 10 | def initialize 11 | @source = OpenStruct.new 12 | end 13 | 14 | def method_missing( method, *args, &block ) 15 | @source.send( method, *args, &block ) 16 | end 17 | 18 | def to_h 19 | @source.marshal_dump 20 | end 21 | 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/cuboid/option_groups/output.rb: -------------------------------------------------------------------------------- 1 | module Cuboid::OptionGroups 2 | 3 | # {Cuboid::UI::Output} options. 4 | # 5 | # @author Tasos "Zapotek" Laskos 6 | class Output < Cuboid::OptionGroup 7 | 8 | # @return [Bool] 9 | # `true` if the output of the RPC instances should be redirected to a 10 | # file, `false` otherwise. 11 | attr_accessor :reroute_to_logfile 12 | 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/cuboid/option_groups/report.rb: -------------------------------------------------------------------------------- 1 | module Cuboid::OptionGroups 2 | 3 | # @author Tasos "Zapotek" Laskos 4 | class Report < Cuboid::OptionGroup 5 | 6 | # @return [String] 7 | # Directory or file path where to store the scan report. 8 | attr_accessor :path 9 | 10 | def initialize 11 | @default_path = self.path = default_path 12 | end 13 | 14 | def path=( path ) 15 | return @path = @default_path if !path 16 | 17 | if path.end_with?( '/' ) && !File.exist?( path ) 18 | raise ArgumentError, 19 | "Snapshot location does not exist: #{path}" 20 | end 21 | 22 | path = File.expand_path( path ) 23 | if File.directory? path 24 | path += '/' if !path.end_with? '/' 25 | end 26 | 27 | @path = path 28 | end 29 | 30 | def default_path 31 | Paths.config['reports'] 32 | end 33 | 34 | def defaults 35 | { path: default_path } 36 | end 37 | 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/cuboid/option_groups/rpc.rb: -------------------------------------------------------------------------------- 1 | module Cuboid::OptionGroups 2 | 3 | # Holds {Engine::RPC::Client} and {Engine::RPC::Server} options. 4 | # 5 | # @author Tasos "Zapotek" Laskos 6 | class RPC < Cuboid::OptionGroup 7 | 8 | # @return [String] 9 | # Path to the UNIX socket to use for RPC communication. 10 | # 11 | # @see RPC::Server::Base 12 | attr_accessor :server_socket 13 | 14 | # @return [String] 15 | # Hostname or IP address for the RPC server. 16 | # 17 | # @see RPC::Server::Base 18 | attr_accessor :server_address 19 | 20 | # @return [String] 21 | # External (hostname or IP) address for the RPC server to advertise. 22 | attr_accessor :server_external_address 23 | 24 | # @return [Integer] 25 | # RPC server port. 26 | # 27 | # @see RPC::Server::Base 28 | attr_accessor :server_port 29 | 30 | # @return [String] 31 | # Path to an SSL certificate authority file in PEM format. 32 | # 33 | # @see RPC::Server::Base 34 | # @see RPC::Client::Base 35 | attr_accessor :ssl_ca 36 | 37 | # @return [String] 38 | # Path to a server SSL private key in PEM format. 39 | # 40 | # @see RPC::Server::Base 41 | attr_accessor :server_ssl_private_key 42 | 43 | # @return [String] 44 | # Path to server SSL certificate in PEM format. 45 | # 46 | # @see RPC::Server::Base 47 | attr_accessor :server_ssl_certificate 48 | 49 | # @return [String] 50 | # Path to a client SSL private key in PEM format. 51 | # 52 | # @see RPC::Client::Base 53 | attr_accessor :client_ssl_private_key 54 | 55 | # @return [String] 56 | # Path to client SSL certificate in PEM format. 57 | # 58 | # @see RPC::Client::Base 59 | attr_accessor :client_ssl_certificate 60 | 61 | # @return [Integer] 62 | # Maximum retries for failed RPC calls. 63 | # 64 | # @see RPC::Client::Base 65 | attr_accessor :client_max_retries 66 | 67 | # @note This should be permanently set to `1`, otherwise it will cause issues 68 | # with the scheduling of the workload distribution of multi-Instance scans. 69 | # 70 | # @return [Integer] 71 | # Amount of concurrently open connections for each RPC client. 72 | # 73 | # @see RPC::Client::Base 74 | attr_accessor :connection_pool_size 75 | 76 | set_defaults( 77 | connection_pool_size: 1, 78 | server_address: '127.0.0.1', 79 | server_port: 7331 80 | ) 81 | 82 | def url 83 | "#{server_address}:#{server_port}" 84 | end 85 | 86 | def to_client_options 87 | { 88 | connection_pool_size: connection_pool_size, 89 | max_retries: client_max_retries, 90 | ssl_ca: ssl_ca, 91 | ssl_pkey: client_ssl_private_key, 92 | ssl_cert: client_ssl_certificate 93 | } 94 | end 95 | 96 | def to_server_options 97 | { 98 | host: server_address, 99 | external_address: server_external_address, 100 | port: server_port, 101 | socket: server_socket, 102 | ssl_ca: ssl_ca, 103 | ssl_pkey: server_ssl_private_key, 104 | ssl_cert: server_ssl_certificate 105 | } 106 | end 107 | 108 | end 109 | end 110 | -------------------------------------------------------------------------------- /lib/cuboid/option_groups/scheduler.rb: -------------------------------------------------------------------------------- 1 | module Cuboid::OptionGroups 2 | 3 | # Holds options for {RPC::Server::Scheduler} servers. 4 | # 5 | # @author Tasos "Zapotek" Laskos 6 | class Scheduler < Cuboid::OptionGroup 7 | 8 | # @return [String] 9 | # URL of a {RPC::Server::Scheduler}. 10 | attr_accessor :url 11 | 12 | # @return [Array] 13 | # Range of ports to use when spawning instances, first entry should be 14 | # the lowest port number, last the max port number. 15 | attr_accessor :instance_port_range 16 | 17 | # @return [Float] 18 | # How regularly to check for scan statuses. 19 | attr_accessor :ping_interval 20 | 21 | set_defaults( 22 | ping_interval: 5.0, 23 | instance_port_range: [1025, 65535] 24 | ) 25 | 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/cuboid/option_groups/snapshot.rb: -------------------------------------------------------------------------------- 1 | require_relative 'report' 2 | 3 | module Cuboid::OptionGroups 4 | 5 | # @author Tasos "Zapotek" Laskos 6 | class Snapshot < Report 7 | 8 | def default_path 9 | Paths.config['snapshots'] 10 | end 11 | 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/cuboid/option_groups/system.rb: -------------------------------------------------------------------------------- 1 | module Cuboid::OptionGroups 2 | 3 | # @author Tasos "Zapotek" Laskos 4 | class System < Cuboid::OptionGroup 5 | 6 | # @return [Integer] 7 | attr_accessor :max_slots 8 | 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/cuboid/processes.rb: -------------------------------------------------------------------------------- 1 | require 'singleton' 2 | require 'ostruct' 3 | 4 | lib = Cuboid::Options.paths.lib 5 | require lib + 'rpc/client/instance' 6 | require lib + 'rpc/client/agent' 7 | require lib + 'rpc/client/scheduler' 8 | 9 | lib = Cuboid::Options.paths.lib + 'processes/' 10 | require lib + 'manager' 11 | require lib + 'agents' 12 | require lib + 'instances' 13 | require lib + 'schedulers' 14 | -------------------------------------------------------------------------------- /lib/cuboid/processes/executables/agent.rb: -------------------------------------------------------------------------------- 1 | require Options.paths.lib + 'rpc/server/agent' 2 | 3 | Raktr.global.run do 4 | RPC::Server::Agent.new 5 | end 6 | -------------------------------------------------------------------------------- /lib/cuboid/processes/executables/base.rb: -------------------------------------------------------------------------------- 1 | require 'base64' 2 | 3 | $options = Marshal.load( Base64.strict_decode64( ARGV.pop ) ) 4 | 5 | if !$options[:without_cuboid] 6 | require 'cuboid' 7 | 8 | include Cuboid 9 | 10 | cuboid_options = Marshal.load( Base64.strict_decode64( ENV['CUBOID_SPAWN_OPTIONS'] ) ) 11 | Options.update cuboid_options 12 | 13 | require( Options.paths.application ) if Options.paths.application 14 | else 15 | if Gem.win_platform? 16 | require 'Win32API' 17 | require 'win32ole' 18 | end 19 | end 20 | 21 | def ppid 22 | $options[:ppid] 23 | end 24 | 25 | def parent_alive? 26 | # Windows is not big on POSIX so try it its own way if possible. 27 | if Gem.win_platform? 28 | begin 29 | alive = false 30 | wmi = WIN32OLE.connect( 'winmgmts://' ) 31 | processes = wmi.ExecQuery( "select ProcessId from win32_process where ProcessID='#{ppid}'") 32 | processes.each do |proc| 33 | proc.ole_free 34 | alive = true 35 | end 36 | processes.ole_free 37 | wmi.ole_free 38 | 39 | return alive 40 | rescue WIN32OLERuntimeError 41 | end 42 | end 43 | 44 | !!(Process.kill( 0, ppid ) rescue false) 45 | end 46 | 47 | def puts_stderr( str ) 48 | return if $stderr.closed? 49 | 50 | $stderr.puts str 51 | rescue 52 | end 53 | 54 | load ARGV.pop 55 | -------------------------------------------------------------------------------- /lib/cuboid/processes/executables/instance.rb: -------------------------------------------------------------------------------- 1 | require Options.paths.lib + 'rpc/server/instance' 2 | 3 | if (socket = $options[:socket]) 4 | Options.rpc.server_address = nil 5 | Options.rpc.server_external_address = nil 6 | Options.rpc.server_port = nil 7 | Options.rpc.server_socket = socket 8 | elsif (port = $options[:port]) 9 | Options.rpc.server_port = port 10 | end 11 | 12 | RPC::Server::Instance.new( Options, $options[:token] ) 13 | -------------------------------------------------------------------------------- /lib/cuboid/processes/executables/rest_service.rb: -------------------------------------------------------------------------------- 1 | require Options.paths.lib + 'rest/server' 2 | 3 | Rest::Server.run!( 4 | port: Options.rpc.server_port, 5 | bind: Options.rpc.server_address, 6 | 7 | username: Options.datastore['username'], 8 | password: Options.datastore['password'], 9 | 10 | ssl_ca: Options.rpc.ssl_ca, 11 | ssl_key: Options.rpc.server_ssl_private_key, 12 | ssl_certificate: Options.rpc.server_ssl_certificate 13 | ) 14 | -------------------------------------------------------------------------------- /lib/cuboid/processes/executables/scheduler.rb: -------------------------------------------------------------------------------- 1 | require Options.paths.lib + 'rpc/server/scheduler' 2 | 3 | Raktr.global.run do 4 | RPC::Server::Scheduler.new 5 | end 6 | -------------------------------------------------------------------------------- /lib/cuboid/processes/helpers.rb: -------------------------------------------------------------------------------- 1 | require_relative 'helpers/processes' 2 | require_relative 'helpers/agents' 3 | require_relative 'helpers/instances' 4 | require_relative 'helpers/schedulers' 5 | -------------------------------------------------------------------------------- /lib/cuboid/processes/helpers/agents.rb: -------------------------------------------------------------------------------- 1 | # @param (see Cuboid::Processes::Agents#spawn) 2 | # @return (see Cuboid::Processes::Agents#spawn) 3 | def agent_spawn( *args ) 4 | Cuboid::Processes::Agents.spawn( *args ) 5 | end 6 | 7 | # @param (see Cuboid::Processes::Agents#kill) 8 | # @return (see Cuboid::Processes::Agents#kill) 9 | def agent_kill( *args ) 10 | Cuboid::Processes::Agents.kill( *args ) 11 | end 12 | 13 | # @param (see Cuboid::Processes::Agents#killall) 14 | # @return (see Cuboid::Processes::Agents#killall) 15 | def agent_killall 16 | Cuboid::Processes::Agents.killall 17 | end 18 | 19 | # @param (see Cuboid::Processes::Agents#connect) 20 | # @return (see Cuboid::Processes::Agents#connect) 21 | def agent_connect( *args ) 22 | Cuboid::Processes::Agents.connect( *args ) 23 | end 24 | -------------------------------------------------------------------------------- /lib/cuboid/processes/helpers/instances.rb: -------------------------------------------------------------------------------- 1 | # @param (see Cuboid::Processes::Instances#spawn) 2 | # @return (see Cuboid::Processes::Instances#spawn) 3 | def instance_spawn( *args ) 4 | Cuboid::Processes::Instances.spawn( *args ) 5 | end 6 | 7 | # @param (see Cuboid::Processes::Instances#grid_spawn) 8 | # @return (see Cuboid::Processes::Instances#grid_spawn) 9 | def instance_grid_spawn( *args ) 10 | Cuboid::Processes::Instances.grid_spawn( *args ) 11 | end 12 | 13 | # @param (see Cuboid::Processes::Instances#agent_spawn) 14 | # @return (see Cuboid::Processes::Instances#agent_spawn) 15 | def instance_agent_spawn( *args ) 16 | Cuboid::Processes::Instances.agent.spawn( *args ) 17 | end 18 | 19 | def instance_kill( url ) 20 | Cuboid::Processes::Instances.kill url 21 | end 22 | 23 | # @param (see Cuboid::Processes::Instances#killall) 24 | # @return (see Cuboid::Processes::Instances#killall) 25 | def instance_killall 26 | Cuboid::Processes::Instances.killall 27 | end 28 | 29 | # @param (see Cuboid::Processes::Instances#connect) 30 | # @return (see Cuboid::Processes::Instances#connect) 31 | def instance_connect( *args ) 32 | Cuboid::Processes::Instances.connect( *args ) 33 | end 34 | 35 | # @param (see Cuboid::Processes::Instances#token_for) 36 | # @return (see Cuboid::Processes::Instances#token_for) 37 | def instance_token_for( *args ) 38 | Cuboid::Processes::Instances.token_for( *args ) 39 | end 40 | -------------------------------------------------------------------------------- /lib/cuboid/processes/helpers/processes.rb: -------------------------------------------------------------------------------- 1 | # @param (see Cuboid::Processes::Manager#kill_reactor) 2 | # @return (see Cuboid::Processes::Manager#kill_reactor) 3 | def process_kill_reactor( *args ) 4 | Cuboid::Processes::Manager.kill_reactor( *args ) 5 | end 6 | 7 | # @param (see Cuboid::Processes::Manager#kill) 8 | # @return (see Cuboid::Processes::Manager#kill) 9 | def process_kill( *args ) 10 | Cuboid::Processes::Manager.kill( *args ) 11 | end 12 | 13 | # @param (see Cuboid::Processes::Manager#killall) 14 | # @return (see Cuboid::Processes::Manager#killall) 15 | def process_killall( *args ) 16 | Cuboid::Processes::Manager.killall( *args ) 17 | end 18 | 19 | # @param (see Cuboid::Processes::Manager#kill_many) 20 | # @return (see Cuboid::Processes::Manager#kill_many) 21 | def process_kill_many( *args ) 22 | Cuboid::Processes::Manager.kill_many( *args ) 23 | end 24 | -------------------------------------------------------------------------------- /lib/cuboid/processes/helpers/schedulers.rb: -------------------------------------------------------------------------------- 1 | # @param (see Cuboid::Processes::Schedulers#spawn) 2 | # @return (see Cuboid::Processes::Schedulers#spawn) 3 | def scheduler_spawn( *args ) 4 | Cuboid::Processes::Schedulers.spawn( *args ) 5 | end 6 | 7 | # @param (see Cuboid::Processes::Schedulers#kill) 8 | # @return (see Cuboid::Processes::Schedulers#kill) 9 | def scheduler_kill( *args ) 10 | Cuboid::Processes::Schedulers.kill( *args ) 11 | end 12 | 13 | # @param (see Cuboid::Processes::Schedulers#killall) 14 | # @return (see Cuboid::Processes::Schedulers#killall) 15 | def scheduler_killall 16 | Cuboid::Processes::Schedulers.killall 17 | end 18 | 19 | # @param (see Cuboid::Processes::Schedulers#connect) 20 | # @return (see Cuboid::Processes::Schedulers#connect) 21 | def scheduler_connect( *args ) 22 | Cuboid::Processes::Schedulers.connect( *args ) 23 | end 24 | -------------------------------------------------------------------------------- /lib/cuboid/rest/server/instance_helpers.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | module Rest 3 | class Server 4 | 5 | module InstanceHelpers 6 | 7 | @@instances = {} 8 | @@agents = {} 9 | 10 | def get_instance 11 | if agent 12 | options = { 13 | owner: self.class.to_s, 14 | helpers: { 15 | owner: { 16 | url: env['HTTP_HOST'] 17 | } 18 | } 19 | } 20 | 21 | if (info = agent.spawn( options )) 22 | connect_to_instance( info['url'], info['token'] ) 23 | end 24 | else 25 | Processes::Instances.spawn( application: Options.paths.application, daemonize: true ) 26 | end 27 | end 28 | 29 | def agents 30 | @@agents.keys 31 | end 32 | 33 | def agent 34 | return if !Options.agent.url 35 | @agent ||= connect_to_agent( Options.agent.url ) 36 | end 37 | 38 | def unplug_agent( url ) 39 | connect_to_agent( url ).node.unplug 40 | 41 | c = @@agents.delete( url ) 42 | c.close if c 43 | end 44 | 45 | def connect_to_agent( url ) 46 | @@agents[url] ||= RPC::Client::Agent.new( url ) 47 | end 48 | 49 | def connect_to_instance( url, token ) 50 | RPC::Client::Instance.new( url, token ) 51 | end 52 | 53 | def update_from_scheduler 54 | return if !scheduler 55 | 56 | scheduler.running.each do |id, info| 57 | instances[id] ||= connect_to_instance( info['url'], info['token'] ) 58 | end 59 | 60 | (scheduler.failed.keys | scheduler.completed.keys).each do |id| 61 | session.delete id 62 | client = instances.delete( id ) 63 | client.close if client 64 | end 65 | end 66 | 67 | def scheduler 68 | return if !Options.scheduler.url 69 | @scheduler ||= connect_to_scheduler( Options.scheduler.url ) 70 | end 71 | 72 | def connect_to_scheduler( url ) 73 | RPC::Client::Scheduler.new( url ) 74 | end 75 | 76 | def instances 77 | @@instances 78 | end 79 | 80 | def instance_for( id, &block ) 81 | cleanup = proc do 82 | instances.delete( id ).close 83 | session.delete id 84 | end 85 | 86 | handle_error cleanup do 87 | block.call @@instances[id] 88 | end 89 | end 90 | 91 | def exist?( id ) 92 | instances.include? id 93 | end 94 | 95 | end 96 | 97 | end 98 | end 99 | end 100 | -------------------------------------------------------------------------------- /lib/cuboid/rest/server/routes/agent.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | module Rest 3 | class Server 4 | module Routes 5 | 6 | module Agent 7 | 8 | def self.registered( app ) 9 | 10 | app.get '/agent/url' do 11 | ensure_agent! 12 | 13 | json Options.agent.url 14 | end 15 | 16 | app.put '/agent/url' do 17 | url = ::JSON.load( request.body.read ) || {} 18 | 19 | handle_error do 20 | connect_to_agent( url ).alive? 21 | 22 | @agent = nil 23 | Options.agent.url = url 24 | json nil 25 | end 26 | end 27 | 28 | app.delete '/agent/url' do 29 | ensure_agent! 30 | 31 | json @agent = Options.agent.url = nil 32 | end 33 | 34 | end 35 | 36 | end 37 | 38 | end 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/cuboid/rest/server/routes/grid.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | module Rest 3 | class Server 4 | module Routes 5 | 6 | module Grid 7 | 8 | def self.registered( app ) 9 | 10 | app.get '/grid' do 11 | ensure_agent! 12 | 13 | handle_error do 14 | json [Options.agent.url] + agent.statistics['node']['peers'] 15 | end 16 | end 17 | 18 | app.get '/grid/:agent' do |url| 19 | ensure_agent! 20 | 21 | handle_error { json connect_to_agent( url ).statistics } 22 | end 23 | 24 | app.delete '/grid/:agent' do |url| 25 | ensure_agent! 26 | 27 | handle_error do 28 | unplug_agent( url ) 29 | end 30 | 31 | json nil 32 | end 33 | 34 | end 35 | 36 | end 37 | 38 | end 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/cuboid/rpc/client.rb: -------------------------------------------------------------------------------- 1 | require_relative 'client/instance' 2 | require_relative 'client/agent' 3 | require_relative 'client/scheduler' 4 | -------------------------------------------------------------------------------- /lib/cuboid/rpc/client/agent.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | 3 | require Options.paths.lib + 'rpc/client/base' 4 | 5 | module RPC 6 | class Client 7 | 8 | # RPC Agent client 9 | # 10 | # @author Tasos "Zapotek" Laskos 11 | class Agent 12 | # Not always available, set by the parent. 13 | attr_accessor :pid 14 | 15 | attr_reader :node 16 | 17 | def initialize( url, options = nil ) 18 | @client = Base.new( url, nil, options ) 19 | @node = Toq::Proxy.new( @client, 'node' ) 20 | 21 | Cuboid::Application.application.agent_services.keys.each do |name| 22 | self.class.send( :attr_reader, name.to_sym ) 23 | 24 | instance_variable_set( 25 | "@#{name}".to_sym, 26 | Toq::Proxy.new( @client, name ) 27 | ) 28 | end 29 | end 30 | 31 | def url 32 | @client.url 33 | end 34 | 35 | def address 36 | @client.address 37 | end 38 | 39 | def port 40 | @client.port 41 | end 42 | 43 | def close 44 | @client.close 45 | end 46 | 47 | private 48 | 49 | # Used to provide the illusion of locality for remote methods 50 | def method_missing( sym, *args, &block ) 51 | @client.call( "agent.#{sym.to_s}", *args, &block ) 52 | end 53 | 54 | end 55 | 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/cuboid/rpc/client/base.rb: -------------------------------------------------------------------------------- 1 | require 'toq' 2 | require_relative '../serializer' 3 | 4 | module Cuboid 5 | module RPC 6 | class Client 7 | 8 | # @author Tasos "Zapotek" Laskos 9 | class Base < Toq::Client 10 | attr_reader :url 11 | 12 | # @param [String] url 13 | # Server URL in `address:port` format. 14 | # @param [String] token 15 | # Optional authentication token. 16 | # @param [Hash] options 17 | # @option options [Integer] :connection_pool_size 18 | # @option options [Integer] :max_retries 19 | # @option options [Integer] :ssl_ca 20 | # @option options [Integer] :ssl_pkey 21 | # @option options [Integer] :ssl_cert 22 | def initialize( url, token = nil, options = nil ) 23 | @url = url 24 | 25 | socket, host, port = nil 26 | if url.include? ':' 27 | host, port = url.split( ':' ) 28 | else 29 | socket = url 30 | end 31 | 32 | @address = host 33 | @port = port 34 | 35 | # If given nil use the global defaults. 36 | options ||= Options.rpc.to_client_options 37 | 38 | super( options.merge( 39 | serializer: Serializer, 40 | host: host, 41 | port: port.to_i, 42 | socket: socket, 43 | token: token 44 | )) 45 | end 46 | 47 | def address 48 | @address 49 | end 50 | 51 | def port 52 | @port 53 | end 54 | 55 | end 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/cuboid/rpc/client/instance.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | 3 | require Options.paths.lib + 'rpc/client/base' 4 | 5 | module RPC 6 | class Client 7 | 8 | # RPC client for remote instances spawned by a remote agent 9 | # 10 | # @author Tasos "Zapotek" Laskos 11 | class Instance 12 | # Not always available, set by the parent. 13 | attr_accessor :pid 14 | attr_reader :options 15 | 16 | require_relative 'instance/service' 17 | 18 | class < 8 | class Proxy < Toq::Proxy 9 | 10 | def initialize( client ) 11 | super client, 'instance' 12 | end 13 | 14 | translate :status do |status| 15 | status.to_sym if status 16 | end 17 | 18 | translate :progress do |data| 19 | data = data.my_symbolize_keys 20 | data[:status] = data[:status].to_sym 21 | data 22 | end 23 | 24 | translate :abort_and_generate_report do |data| 25 | Report.from_rpc_data data 26 | end 27 | 28 | translate :generate_report do |data| 29 | Report.from_rpc_data data 30 | end 31 | 32 | end 33 | 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/cuboid/rpc/client/scheduler.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | 3 | require Options.paths.lib + 'rpc/client/base' 4 | 5 | module RPC 6 | class Client 7 | 8 | # RPC Scheduler client 9 | # 10 | # @author Tasos "Zapotek" Laskos 11 | class Scheduler 12 | # Not always available, set by the parent. 13 | attr_accessor :pid 14 | 15 | def initialize( url, options = nil ) 16 | @client = Base.new( url, nil, options ) 17 | end 18 | 19 | def url 20 | @client.url 21 | end 22 | 23 | def address 24 | @client.address 25 | end 26 | 27 | def port 28 | @client.port 29 | end 30 | 31 | def close 32 | @client.close 33 | end 34 | 35 | private 36 | 37 | # Used to provide the illusion of locality for remote methods 38 | def method_missing( sym, *args, &block ) 39 | @client.call( "scheduler.#{sym.to_s}", *args, &block ) 40 | end 41 | 42 | end 43 | 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/cuboid/rpc/serializer.rb: -------------------------------------------------------------------------------- 1 | require 'msgpack' 2 | 3 | module Cuboid 4 | module RPC 5 | 6 | # Used for serialization of {RPC} messages. 7 | # 8 | # It's simply a delegator for `MessagePack` with `Zlib` compression for messages 9 | # that are larger than {COMPRESS_LARGER_THAN}. 10 | # 11 | # @author Tasos "Zapotek" Laskos 12 | module Serializer 13 | 14 | # Compress object dumps larger than 1KB. 15 | COMPRESS_LARGER_THAN = 1_000 16 | 17 | # @param [#to_rpc_data] object 18 | # 19 | # @return [String] 20 | # {#compress Compressed} `object` dump. 21 | def dump( object ) 22 | # ap object 23 | compress( serializer.dump( object.to_rpc_data_or_self ) ) 24 | end 25 | 26 | # @param [String] dump 27 | # {#dump Dumped} object. 28 | # 29 | # @return [Object] 30 | def load( dump ) 31 | serializer.load( decompress( dump ) ) 32 | end 33 | 34 | # Simulates an object's over-the-wire transmission by {#dump dumping} 35 | # and then {#load loading}. 36 | # 37 | # @param [#to_rpc_data,.from_rpc_data] object 38 | # 39 | # @return [Object] 40 | # Data that the peer would receive. 41 | def rpc_data( object ) 42 | load( dump( object ) ) 43 | end 44 | 45 | # @param [#to_rpc_data,.from_rpc_data] object 46 | # 47 | # @return [Object] 48 | def deep_clone( object ) 49 | object.class.from_rpc_data rpc_data( object ) 50 | end 51 | 52 | # @note Ignores strings smaller than #{COMPRESS_LARGER_THAN}. 53 | # 54 | # @param [String] string 55 | # String to compress. 56 | # 57 | # @return [String] 58 | # Compressed (or not) `string`. 59 | def compress( string ) 60 | return string if string.size < COMPRESS_LARGER_THAN 61 | Zlib::Deflate.deflate string 62 | end 63 | 64 | # @note Will return the `string` as is if it was not compressed. 65 | # 66 | # @param [String] string 67 | # String to decompress. 68 | # 69 | # @return [String] 70 | # Decompressed string. 71 | def decompress( string ) 72 | return '' if string.to_s.empty? 73 | 74 | # Just an ID representing a serialized, empty data structure. 75 | return string if string.size == 1 76 | 77 | begin 78 | Zlib::Inflate.inflate string 79 | rescue Zlib::DataError 80 | string 81 | end 82 | end 83 | 84 | def serializer 85 | MessagePack 86 | end 87 | 88 | extend self 89 | end 90 | 91 | end 92 | end 93 | -------------------------------------------------------------------------------- /lib/cuboid/rpc/server/active_options.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | module RPC 3 | class Server 4 | 5 | # It, for the most part, forwards calls to {Cuboid::Options} and intercepts 6 | # a few that need to be updated at other places throughout the framework. 7 | # 8 | # @private 9 | # @author Tasos "Zapotek" Laskos 10 | class ActiveOptions 11 | 12 | def initialize 13 | @options = Cuboid::Options.instance 14 | 15 | (@options.public_methods( false ) - public_methods( false ) ).each do |m| 16 | self.class.class_eval do 17 | define_method m do |*args| 18 | @options.send( m, *args ) 19 | end 20 | end 21 | end 22 | end 23 | 24 | # @see Cuboid::Options#set 25 | def set( options ) 26 | @options.set( options ) 27 | true 28 | end 29 | 30 | def to_h 31 | @options.to_rpc_data 32 | end 33 | 34 | end 35 | 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/cuboid/rpc/server/base.rb: -------------------------------------------------------------------------------- 1 | require 'ostruct' 2 | require 'toq' 3 | require_relative '../serializer' 4 | 5 | module Cuboid 6 | module RPC 7 | class Server 8 | 9 | # RPC server class 10 | # 11 | # @private 12 | # @author Tasos "Zapotek" Laskos 13 | class Base < Toq::Server 14 | 15 | # @param [Hash] options 16 | # @option options [Integer] :host 17 | # @option options [Integer] :port 18 | # @option options [Integer] :socket 19 | # @option options [Integer] :ssl_ca 20 | # @option options [Integer] :ssl_pkey 21 | # @option options [Integer] :ssl_cert 22 | # @param [String] token 23 | # Optional authentication token. 24 | def initialize( options = nil, token = nil ) 25 | 26 | # If given nil use the global defaults. 27 | options ||= Options.rpc.to_server_options 28 | @options = options 29 | 30 | super(options.merge( 31 | serializer: Serializer, 32 | token: token 33 | )) 34 | end 35 | 36 | def address 37 | @options[:external_address] || @options[:host] 38 | end 39 | 40 | def port 41 | @options[:port] 42 | end 43 | 44 | def url 45 | return @options[:socket] if @options[:socket] 46 | 47 | "#{address}:#{port}" 48 | end 49 | 50 | def start 51 | super 52 | @ready = true 53 | end 54 | 55 | def ready? 56 | @ready ||= false 57 | end 58 | 59 | end 60 | 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /lib/cuboid/rpc/server/instance/peers.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | module RPC 3 | class Server 4 | class Instance 5 | 6 | class Peers 7 | include Enumerable 8 | 9 | def initialize 10 | @peers = {} 11 | end 12 | 13 | def set( peer_info ) 14 | peer_info.each do |url, token| 15 | next if url == self.self_url 16 | @peers[url] = Cuboid::Application.application.connect( url: url, token: token ) 17 | end 18 | 19 | nil 20 | end 21 | 22 | def each( &block ) 23 | @peers.each do |_, client| 24 | block.call client 25 | end 26 | end 27 | 28 | def self_url 29 | Cuboid::Options.rpc.url 30 | end 31 | 32 | end 33 | 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/cuboid/rpc/server/instance/service.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | module RPC 3 | class Server 4 | class Instance 5 | module Service 6 | 7 | attr_reader :name 8 | attr_reader :instance 9 | 10 | def initialize( name, instance ) 11 | @name = name 12 | @instance = instance 13 | end 14 | 15 | end 16 | 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/cuboid/rpc/server/output.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | module UI 3 | 4 | # RPC Output interface. 5 | # 6 | # @author Tasos "Zapotek" Laskos 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 93 | -------------------------------------------------------------------------------- /lib/cuboid/ruby.rb: -------------------------------------------------------------------------------- 1 | lib = Cuboid::Options.paths.lib 2 | require lib + 'ruby/object' 3 | require lib + 'ruby/hash' 4 | require lib + 'ruby/array' 5 | -------------------------------------------------------------------------------- /lib/cuboid/ruby/array.rb: -------------------------------------------------------------------------------- 1 | class Array 2 | 3 | # @param [#to_s, Array<#to_s>] tags 4 | # 5 | # @return [Bool] 6 | # `true` if `self` contains any of the `tags` when objects of both `self` 7 | # and `tags` are converted to `String`. 8 | def includes_tags?( tags ) 9 | return false if !tags 10 | 11 | tags = [tags].flatten.compact.map( &:to_s ) 12 | return false if tags.empty? 13 | 14 | (self.flatten.compact.map( &:to_s ) & tags).any? 15 | end 16 | 17 | end 18 | -------------------------------------------------------------------------------- /lib/cuboid/ruby/hash.rb: -------------------------------------------------------------------------------- 1 | class Hash 2 | 3 | if !method_defined?( :to_h ) 4 | alias :to_h :to_hash 5 | end 6 | 7 | # Converts the hash keys to strings. 8 | # 9 | # @param [Boolean] recursively 10 | # Go through the Hash recursively? 11 | # 12 | # @return [Hash] 13 | # Hash with +self+'s keys recursively converted to strings. 14 | def my_stringify_keys( recursively = true ) 15 | stringified = {} 16 | each do |k, v| 17 | stringified[k.to_s] = (recursively && v.is_a?( Hash ) ? 18 | v.my_stringify_keys : v) 19 | end 20 | stringified 21 | end 22 | 23 | # Converts the hash keys to symbols. 24 | # 25 | # @param [Boolean] recursively 26 | # Go through the Hash recursively? 27 | # 28 | # @return [Hash] 29 | # Hash with +self+'s keys recursively converted to symbols. 30 | def my_symbolize_keys( recursively = true ) 31 | symbolize = {} 32 | each do |k, v| 33 | k = k.respond_to?(:to_sym) ? k.to_sym : k 34 | 35 | symbolize[k] = (recursively && v.is_a?( Hash ) ? 36 | v.my_symbolize_keys : v) 37 | end 38 | symbolize 39 | end 40 | 41 | end 42 | -------------------------------------------------------------------------------- /lib/cuboid/ruby/object.rb: -------------------------------------------------------------------------------- 1 | # Overloads the {Object} class providing a {#deep_clone} method. 2 | # 3 | # @author Tasos "Zapotek" Laskos 4 | class Object 5 | 6 | # Deep-clones self using a Marshal dump-load. 7 | # 8 | # @return [Object] 9 | # Duplicate of self. 10 | def deep_clone 11 | Marshal.load( Marshal.dump( self ) ) 12 | end 13 | 14 | def rpc_clone 15 | if self.class.respond_to?( :from_rpc_data ) 16 | self.class.from_rpc_data( 17 | Cuboid::RPC::Serializer.serializer.load( 18 | Cuboid::RPC::Serializer.serializer.dump( to_rpc_data ) 19 | ) 20 | ) 21 | else 22 | Cuboid::RPC::Serializer.serializer.load( 23 | Cuboid::RPC::Serializer.serializer.dump( self ) 24 | ) 25 | end 26 | end 27 | 28 | def to_rpc_data_or_self 29 | respond_to?( :to_rpc_data ) ? to_rpc_data : self 30 | end 31 | 32 | end 33 | -------------------------------------------------------------------------------- /lib/cuboid/state.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | 3 | # Stores and provides access to the state of the system. 4 | # 5 | # @author Tasos "Zapotek" Laskos 6 | class State 7 | 8 | # {State} error namespace. 9 | # 10 | # All {State} errors inherit from and live under it. 11 | # 12 | # @author Tasos "Zapotek" Laskos 13 | class Error < Cuboid::Error 14 | end 15 | 16 | require_relative 'state/options' 17 | require_relative 'state/application' 18 | 19 | class < 5 | class Options 6 | 7 | def statistics 8 | {} 9 | end 10 | 11 | def dump( directory ) 12 | FileUtils.mkdir_p( directory ) 13 | Cuboid::Options.save( "#{directory}/options" ) 14 | end 15 | 16 | def self.load( directory ) 17 | Cuboid::Options.load( "#{directory}/options" ) 18 | new 19 | end 20 | 21 | def clear 22 | end 23 | 24 | end 25 | 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/cuboid/support.rb: -------------------------------------------------------------------------------- 1 | module Cuboid::Support 2 | end 3 | 4 | lib = Cuboid::Options.paths.support 5 | require lib + 'mixins' 6 | require lib + 'buffer' 7 | require lib + 'cache' 8 | require lib + 'crypto' 9 | require lib + 'database' 10 | require lib + 'filter' 11 | require lib + 'glob' 12 | -------------------------------------------------------------------------------- /lib/cuboid/support/buffer.rb: -------------------------------------------------------------------------------- 1 | buffers = Cuboid::Options.paths.support + 'buffer/' 2 | require buffers + 'base' 3 | require buffers + 'autoflush' 4 | -------------------------------------------------------------------------------- /lib/cuboid/support/buffer/autoflush.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | module Support::Buffer 3 | 4 | # A buffer implementation which flushes itself when it gets full or a number 5 | # of push attempts is reached between flushes. 6 | # 7 | # @author Tasos "Zapotek" Laskos 8 | class AutoFlush < Base 9 | 10 | attr_reader :max_pushes 11 | 12 | # @param [Integer] max_size 13 | # Maximum buffer size -- a flush will be triggered when that limit is 14 | # reached. 15 | # @param [Integer] max_pushes 16 | # Maximum number of pushes between flushes. 17 | # @param [#<<, #|, #clear, #size, #empty?] type 18 | # Internal storage class to use. 19 | def initialize( max_size = nil, max_pushes = nil, type = Array ) 20 | super( max_size, type ) 21 | 22 | @max_pushes = max_pushes 23 | @pushes = 0 24 | end 25 | 26 | def <<( *args ) 27 | super( *args ) 28 | ensure 29 | handle_push 30 | end 31 | 32 | def batch_push( *args ) 33 | super( *args ) 34 | ensure 35 | handle_push 36 | end 37 | 38 | def flush 39 | super 40 | ensure 41 | @pushes = 0 42 | end 43 | 44 | private 45 | 46 | def handle_push 47 | @pushes += 1 48 | flush if flush? 49 | end 50 | 51 | def flush? 52 | !!(full? || push_limit_reached?) 53 | end 54 | 55 | def push_limit_reached? 56 | max_pushes && @pushes >= max_pushes 57 | end 58 | 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/cuboid/support/buffer/base.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | module Support::Buffer 3 | 4 | # 5 | # Base buffer class to be extended by more specialised implementation. 6 | # 7 | # @author Tasos "Zapotek" Laskos 8 | # 9 | class Base 10 | include Support::Mixins::Observable 11 | 12 | # @!method on_push( &block ) 13 | # @param [Block] block block to call on {#push} 14 | advertise :on_push 15 | 16 | # @!method on_batch_push( &block ) 17 | # @param [Block] block block to call on {#batch_push} 18 | advertise :on_batch_push 19 | 20 | # @!method on_flush( &block ) 21 | # @param [Block] block block to call on {#flush} 22 | advertise :on_flush 23 | 24 | # @return [Integer] Maximum buffer size. 25 | attr_reader :max_size 26 | 27 | # @param [Integer] max_size 28 | # Maximum buffer size -- won't be enforced. 29 | # @param [#<<, #|, #clear, #size, #empty?] type 30 | # Internal storage class to use. 31 | def initialize( max_size = nil, type = Array ) 32 | super() 33 | @buffer = type.new 34 | @max_size = max_size 35 | end 36 | 37 | # @note Calls {#on_push} blocks with the given object and pushes an object 38 | # to the buffer. 39 | # 40 | # @param [Object] obj 41 | # Object to push. 42 | def <<( obj ) 43 | notify_on_push obj 44 | @buffer << obj 45 | self 46 | end 47 | alias :push :<< 48 | 49 | # @note Calls {#on_batch_push} blocks with the given list and merges the 50 | # buffer with the contents of a list. 51 | # 52 | # @param [#|] list 53 | # List of objects 54 | def batch_push( list ) 55 | notify_on_batch_push list 56 | @buffer |= list 57 | self 58 | end 59 | 60 | # @return [Integer] 61 | # Number of object in the buffer. 62 | def size 63 | @buffer.size 64 | end 65 | 66 | # @return [Bool] 67 | # `true` if the buffer is empty, `false` otherwise. 68 | def empty? 69 | @buffer.empty? 70 | end 71 | 72 | # @return [Bool] 73 | # `true` if the buffer is full, `false` otherwise. 74 | def full? 75 | !!(max_size && size >= max_size) 76 | end 77 | 78 | # @note Calls {#on_flush} blocks with the buffer and then empties it. 79 | # 80 | # @return current buffer 81 | def flush 82 | buffer = @buffer.dup 83 | notify_on_flush buffer 84 | buffer 85 | ensure 86 | @buffer.clear 87 | end 88 | 89 | end 90 | end 91 | end 92 | -------------------------------------------------------------------------------- /lib/cuboid/support/cache.rb: -------------------------------------------------------------------------------- 1 | lib = Cuboid::Options.paths.support + 'cache/' 2 | require lib + 'base' 3 | require lib + 'least_recently_pushed' 4 | require lib + 'least_recently_used' 5 | require lib + 'random_replacement' 6 | require lib + 'least_cost_replacement' 7 | require lib + 'preference' 8 | -------------------------------------------------------------------------------- /lib/cuboid/support/cache/least_cost_replacement.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | module Support::Cache 3 | 4 | # Least Cost Replacement cache implementation. 5 | # 6 | # Maintains 3 cost classes (low, medium, high) ) and discards entries from the 7 | # lowest cost classes in order to make room for new ones. 8 | # 9 | # @author Tasos "Zapotek" Laskos 10 | class LeastCostReplacement < Base 11 | 12 | VALID_COSTS = [ :low, :medium, :high ] 13 | 14 | # @see Cuboid::Cache::Base#initialize 15 | def initialize( * ) 16 | super 17 | reset_costs 18 | end 19 | 20 | # Storage method 21 | # 22 | # @param [Object] k 23 | # Entry key. 24 | # @param [Object] v 25 | # Object to store. 26 | # @param [Symbol] cost 27 | # 28 | # @return [Object] `v` 29 | # 30 | # @see VALID_COSTS 31 | def store( k, v, cost = :low ) 32 | fail( "invalid cost: #{cost}" ) if !valid_cost?( cost ) 33 | 34 | super( k, v ) 35 | ensure 36 | @costs[cost] << k 37 | end 38 | 39 | # @see Cuboid::Cache::Base#clear 40 | def clear 41 | super 42 | ensure 43 | reset_costs 44 | end 45 | 46 | private 47 | 48 | def reset_costs 49 | @costs = {} 50 | VALID_COSTS.each { |c| @costs[c] = [] } 51 | end 52 | 53 | def valid_cost?( cost ) 54 | VALID_COSTS.include?( cost ) 55 | end 56 | 57 | def candidate_from_cost_class( cost_class ) 58 | return if (costs = @costs[cost_class]).empty? 59 | costs.delete_at( rand( costs.size ) ) 60 | end 61 | 62 | def prune_candidate 63 | VALID_COSTS.each do |cost| 64 | if c = candidate_from_cost_class( cost ) 65 | return c 66 | end 67 | end 68 | end 69 | 70 | def prune 71 | delete( prune_candidate ) 72 | end 73 | 74 | end 75 | 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /lib/cuboid/support/cache/least_recently_pushed.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | module Support::Cache 3 | 4 | # Least Recently Pushed cache implementation. 5 | # 6 | # Discards the least recently pushed entries, in order to make room for newer ones. 7 | # 8 | # This is the cache with best performance across the board. 9 | # 10 | # @author Tasos "Zapotek" Laskos 11 | class LeastRecentlyPushed < Base 12 | 13 | private 14 | 15 | def prune 16 | @cache.delete( @cache.first.first ) 17 | end 18 | 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/cuboid/support/cache/least_recently_used.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | module Support::Cache 3 | 4 | # Least Recently Used cache implementation. 5 | # 6 | # Generally, the most desired mode under most circumstances. 7 | # Discards the least recently used entries in order to make room for newer ones. 8 | # 9 | # @author Tasos "Zapotek" Laskos 10 | class LeastRecentlyUsed < LeastRecentlyPushed 11 | 12 | private 13 | 14 | def get_with_internal_key( k ) 15 | if !@cache.include? k 16 | @misses += 1 17 | return 18 | end 19 | 20 | renew( k ) 21 | 22 | super k 23 | end 24 | 25 | def renew( internal_key ) 26 | @cache[internal_key] = @cache.delete( internal_key ) 27 | end 28 | 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/cuboid/support/cache/preference.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | module Support::Cache 3 | 4 | # @author Tasos "Zapotek" Laskos 5 | class Preference < Base 6 | 7 | def prefer( &block ) 8 | @preference = block 9 | end 10 | 11 | private 12 | 13 | def store_with_internal_key( k, v ) 14 | prune if capped? && (size > max_size - 1) 15 | 16 | _store( k, v ) 17 | end 18 | 19 | def find_preference 20 | @preference.call 21 | end 22 | 23 | def prune 24 | preferred = find_preference 25 | delete( preferred ) if preferred 26 | end 27 | 28 | end 29 | 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/cuboid/support/cache/random_replacement.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | module Support::Cache 3 | 4 | # Random Replacement cache implementation. 5 | # 6 | # Discards entries at random in order to make room for new ones. 7 | # 8 | # @author Tasos "Zapotek" Laskos 9 | class RandomReplacement < Base 10 | 11 | private 12 | 13 | def prune 14 | @cache.delete( @cache.keys.sample ) 15 | end 16 | 17 | end 18 | 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/cuboid/support/crypto.rb: -------------------------------------------------------------------------------- 1 | lib = Cuboid::Options.paths.support + 'crypto/' 2 | require lib + 'rsa_aes_cbc' 3 | -------------------------------------------------------------------------------- /lib/cuboid/support/crypto/rsa_aes_cbc.rb: -------------------------------------------------------------------------------- 1 | require 'openssl' 2 | require "base64" 3 | 4 | module Cuboid 5 | module Support::Crypto 6 | 7 | # Simple hybrid crypto class using RSA for public key encryption and AES with CBC 8 | # for bulk data encryption/decryption. 9 | # 10 | # RSA is used to encrypt the AES primitives which are used to encrypt the plaintext. 11 | # 12 | # @author Tasos "Zapotek" Laskos 13 | class RSA_AES_CBC 14 | 15 | # If only encryption is required the private key parameter can be omitted. 16 | # 17 | # @param [String] public_pem 18 | # Location of the Public key in PEM format. 19 | # @param [String] private_pem 20 | # Location of the Private key in PEM format. 21 | def initialize( public_pem, private_pem = nil ) 22 | @public_pem = public_pem 23 | @private_pem = private_pem 24 | end 25 | 26 | # Encrypts data and returns a Base64 representation of the ciphertext 27 | # and AES CBC primitives encrypted using the public key. 28 | # 29 | # @param [String] data 30 | # 31 | # @return [String] 32 | # Base64 representation of the ciphertext and AES CBC primitives encrypted 33 | # using the public key. 34 | def encrypt( data ) 35 | rsa = OpenSSL::PKey::RSA.new( File.read( @public_pem ) ) 36 | 37 | # encrypt with 256 bit AES with CBC 38 | aes = OpenSSL::Cipher::Cipher.new( 'aes-256-cbc' ) 39 | aes.encrypt 40 | 41 | # use random key and IV 42 | aes.key = key = aes.random_key 43 | aes.iv = iv = aes.random_iv 44 | 45 | # this will hold all primitives and ciphertext 46 | primitives = {} 47 | 48 | primitives['ciphertext'] = aes.update( data ) 49 | primitives['ciphertext'] << aes.final 50 | 51 | primitives['key'] = rsa.public_encrypt( key ) 52 | primitives['iv'] = rsa.public_encrypt( iv ) 53 | 54 | # serialize everything and base64 encode it 55 | Base64.encode64( primitives.to_yaml ) 56 | end 57 | 58 | # Decrypts data. 59 | # 60 | # @param [String] data 61 | # 62 | # @return [String] 63 | # Plaintext. 64 | def decrypt( data ) 65 | rsa = OpenSSL::PKey::RSA.new( File.read( @private_pem ) ) 66 | 67 | # decrypt with 256 bit AES with CBC 68 | aes = OpenSSL::Cipher::Cipher.new( 'aes-256-cbc' ) 69 | aes.decrypt 70 | 71 | # unencode and unserialize to get the primitives and ciphertext 72 | primitives = YAML::load( Base64.decode64( data ) ) 73 | 74 | aes.key = rsa.private_decrypt( primitives['key'] ) 75 | aes.iv = rsa.private_decrypt( primitives['iv'] ) 76 | 77 | plaintext = aes.update( primitives['ciphertext'] ) 78 | plaintext << aes.final 79 | 80 | plaintext 81 | end 82 | 83 | end 84 | 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /lib/cuboid/support/database.rb: -------------------------------------------------------------------------------- 1 | lib = Cuboid::Options.paths.support + '/database/' 2 | require lib + 'base' 3 | require lib + 'queue' 4 | require lib + 'categorized_queue' 5 | require lib + 'hash' 6 | -------------------------------------------------------------------------------- /lib/cuboid/support/filter.rb: -------------------------------------------------------------------------------- 1 | lib = Cuboid::Options.paths.support + 'filter/' 2 | require lib + 'base' 3 | require lib + 'set' 4 | -------------------------------------------------------------------------------- /lib/cuboid/support/filter/base.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | module Support::Filter 3 | 4 | # @author Tasos "Zapotek" Laskos 5 | # @abstract 6 | class Base 7 | 8 | attr_reader :collection 9 | 10 | DEFAULT_OPTIONS = { 11 | hasher: :hash 12 | } 13 | 14 | # @param [Set] options 15 | # @option options [Symbol] (:hasher) 16 | # Method to call on the item to obtain its hash. 17 | def initialize( options = {} ) 18 | @options = DEFAULT_OPTIONS.merge( options ) 19 | @hasher = @options[:hasher].to_sym 20 | end 21 | 22 | # @param [#persistent_hash] item 23 | # Item to insert. 24 | # 25 | # @return [Base] 26 | # `self` 27 | def <<( item ) 28 | @collection << calculate_hash( item ) 29 | self 30 | end 31 | 32 | # @param [#persistent_hash] item 33 | # Item to check. 34 | # 35 | # @return [Bool] 36 | def include?( item ) 37 | @collection.include? calculate_hash( item ) 38 | end 39 | 40 | def empty? 41 | @collection.empty? 42 | end 43 | 44 | def any? 45 | !empty? 46 | end 47 | 48 | def size 49 | @collection.size 50 | end 51 | 52 | def clear 53 | @collection.clear 54 | end 55 | 56 | def merge( other ) 57 | case other 58 | when self.class 59 | 60 | @collection.merge other.collection 61 | 62 | when Array 63 | 64 | other.each do |k| 65 | fail 'Cannot merge with unhashed entries' if !k.is_a?( Numeric ) 66 | @collection << k 67 | end 68 | 69 | else 70 | fail ArgumentError, 71 | "Don't know how to merge with: #{other.class}" 72 | end 73 | 74 | self 75 | end 76 | 77 | def ==( other ) 78 | hash == other.hash 79 | end 80 | 81 | def hash 82 | @collection.hash 83 | end 84 | 85 | def dup 86 | self.class.new( @options.dup ).merge self 87 | end 88 | 89 | def _dump( _ ) 90 | Marshal.dump( to_rpc_data ) 91 | end 92 | 93 | def self._load( data ) 94 | from_rpc_data Marshal.load( data ) 95 | end 96 | 97 | def collection=( c ) 98 | @collection = c 99 | end 100 | 101 | private 102 | 103 | def calculate_hash( item ) 104 | item.send @hasher 105 | end 106 | 107 | end 108 | 109 | end 110 | end 111 | -------------------------------------------------------------------------------- /lib/cuboid/support/filter/set.rb: -------------------------------------------------------------------------------- 1 | require 'set' 2 | 3 | module Cuboid 4 | module Support::Filter 5 | 6 | # Filter based on a Set. 7 | # 8 | # @author Tasos "Zapotek" Laskos 9 | class Set < Base 10 | 11 | # @param (see Base#initialize) 12 | def initialize(*) 13 | super 14 | @collection = ::Set.new 15 | end 16 | 17 | def to_rpc_data 18 | [@options, @collection.to_a] 19 | end 20 | 21 | def self.from_rpc_data( data ) 22 | options, items = data 23 | new( options ).merge items 24 | end 25 | 26 | end 27 | 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/cuboid/support/glob.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | module Support 3 | 4 | # @author Tasos "Zapotek" Laskos 5 | class Glob 6 | 7 | def self.to_regexp( glob ) 8 | escaped = Regexp.escape( glob ).gsub( '\*', '.*?' ) 9 | Regexp.new( "^#{escaped}$", Regexp::IGNORECASE ) 10 | end 11 | 12 | attr_reader :regexp 13 | 14 | def initialize( glob ) 15 | @regexp = self.class.to_regexp( glob ) 16 | end 17 | 18 | def =~( str ) 19 | @regexp.match? str 20 | end 21 | alias :matches? :=~ 22 | alias :match? :matches? 23 | 24 | end 25 | 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/cuboid/support/mixins.rb: -------------------------------------------------------------------------------- 1 | module Cuboid::Mixins 2 | end 3 | 4 | lib = Cuboid::Options.paths.mixins 5 | require lib + 'observable' 6 | require lib + 'terminal' 7 | require lib + 'profiler' 8 | require lib + 'parts' 9 | -------------------------------------------------------------------------------- /lib/cuboid/support/mixins/observable.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | module Support 3 | module Mixins 4 | 5 | # Provides a flexible way to make any object observable for multiple events. 6 | # 7 | # The observable classes use: 8 | # 9 | # * `notify_( *args )` 10 | # 11 | # to notify observers of events. 12 | # 13 | # The observers request notifications using: 14 | # 15 | # * `observable.( &block )` 16 | # 17 | # @author Tasos "Zapotek" Laskos 18 | module Observable 19 | include UI::Output 20 | include Utilities 21 | 22 | include MonitorMixin 23 | 24 | def self.included( base ) 25 | base.extend ClassMethods 26 | end 27 | 28 | module ClassMethods 29 | def advertise( *ad_events ) 30 | ad_events.each do |event| 31 | define_method event do |&block| 32 | add_observer( event, &block ) 33 | end 34 | 35 | define_method "notify_#{event}" do |*args| 36 | notify_observers( event, *args ) 37 | end 38 | 39 | private "notify_#{event}" 40 | end 41 | 42 | nil 43 | end 44 | end 45 | 46 | def initialize 47 | super 48 | 49 | @__observers = {} 50 | end 51 | 52 | private 53 | 54 | def observers 55 | @__observers 56 | end 57 | 58 | def add_observer( event, &block ) 59 | fail ArgumentError, 'Missing block' if !block 60 | synchronize do 61 | observers_for( event ) << block 62 | end 63 | 64 | self 65 | end 66 | 67 | def notify_observers( event, *args ) 68 | synchronize do 69 | observers_for( event ).each do |block| 70 | exception_jail( false ) { block.call( *args ) } 71 | end 72 | end 73 | 74 | nil 75 | end 76 | 77 | def dup_observers 78 | observers.inject({}) { |h, (k, v)| h[k] = v.dup; h } 79 | end 80 | 81 | def set_observers( obs ) 82 | @__observers = obs 83 | end 84 | 85 | def observers_for( event ) 86 | observers[event.to_sym] ||= [] 87 | end 88 | 89 | def clear_observers 90 | synchronize do 91 | observers.clear 92 | end 93 | end 94 | 95 | end 96 | 97 | end 98 | end 99 | end 100 | -------------------------------------------------------------------------------- /lib/cuboid/support/mixins/parts.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | module Support 3 | module Mixins 4 | 5 | module Parts 6 | 7 | def self.included( base ) 8 | dir = Utilities.caller_path( 3 ).split( '.rb', 2 ).first 9 | Dir.glob( "#{dir}/parts/**/*.rb" ).each { |f| require f } 10 | 11 | parts = base.const_get( :Parts ) 12 | parts.constants.each do |part_name| 13 | base.include parts.const_get( part_name ) 14 | end 15 | end 16 | 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/cuboid/support/mixins/profiler.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | module Support 3 | module Mixins 4 | 5 | module Profiler 6 | 7 | @@on = false 8 | 9 | class <] 9 | attr_reader :platforms 10 | 11 | # @return [Slots] 12 | attr_reader :slots 13 | 14 | def initialize 15 | @platforms = [] 16 | @slots = Slots.new( self ) 17 | end 18 | 19 | # @return [Float] 20 | # System utilization based on slots. 21 | # 22 | # * `0.0` => No utilization. 23 | # * `1.0` => Max utilization. 24 | def utilization 25 | total_slots = System.slots.total 26 | return 1.0 if total_slots == 0 27 | 28 | System.slots.used / Float( total_slots ) 29 | end 30 | 31 | # @return [Bool] 32 | def max_utilization? 33 | utilization == 1 34 | end 35 | 36 | # @return [Integer] 37 | # Amount of free RAM in bytes. 38 | def memory_free 39 | platform.memory_free 40 | end 41 | 42 | # @param [Integer] pgid 43 | # Process group ID. 44 | # 45 | # @return [Integer] 46 | # Amount of RAM in bytes used by the given GPID. 47 | def memory_for_process_group( pgid ) 48 | platform.memory_for_process_group( pgid ) 49 | end 50 | 51 | # @return [Integer] 52 | # Amount of free disk space in bytes. 53 | def disk_space_free 54 | platform.disk_space_free 55 | end 56 | 57 | # @return [String 58 | # Location for temporary file storage. 59 | def disk_directory 60 | platform.disk_directory 61 | end 62 | 63 | # @param [Integer] pid 64 | # Process ID. 65 | # 66 | # @return [Integer] 67 | # Amount of disk space in bytes used by the given PID. 68 | def disk_space_for_process( pid ) 69 | platform.disk_space_for_process( pid ) 70 | end 71 | 72 | # @return [Integer] 73 | # Amount of CPU cores. 74 | def cpu_count 75 | @cpu_count ||= platform.cpu_count 76 | end 77 | 78 | # @return [Platforms::Base] 79 | def platform 80 | return @platform if @platform 81 | 82 | platforms.each do |klass| 83 | next if !klass.current? 84 | 85 | return @platform = klass.new 86 | end 87 | 88 | raise "Unsupported platform: #{RUBY_PLATFORM}" 89 | end 90 | 91 | # @private 92 | def register_platform( platform ) 93 | platforms << platform 94 | end 95 | 96 | # @private 97 | def reset 98 | @cpu_count = nil 99 | @platform = nil 100 | end 101 | 102 | class < 11 | module Output 12 | include OutputInterface 13 | 14 | def print_error( message = '' ) 15 | msg = "#{caller_location} #{message}" 16 | 17 | $stderr.puts msg 18 | log_error msg 19 | end 20 | 21 | def print_bad(*) 22 | end 23 | 24 | def print_status(*) 25 | end 26 | 27 | def print_info(*) 28 | end 29 | 30 | def print_ok(*) 31 | end 32 | 33 | def print_debug(*) 34 | end 35 | 36 | def print_verbose(*) 37 | end 38 | 39 | def print_line(*) 40 | end 41 | 42 | private 43 | 44 | def output_provider_file 45 | __FILE__ 46 | end 47 | 48 | extend self 49 | end 50 | 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /lib/cuboid/ui/output_interface.rb: -------------------------------------------------------------------------------- 1 | require 'cuboid/error' 2 | 3 | module Cuboid 4 | module UI 5 | 6 | # @author Tasos "Zapotek" Laskos 7 | class Error < Cuboid::Error 8 | end 9 | 10 | # @author Tasos "Zapotek" Laskos 11 | module OutputInterface 12 | 13 | # @author Tasos "Zapotek" Laskos 14 | class Error < Cuboid::UI::Error 15 | end 16 | 17 | require_relative 'output_interface/abstract' 18 | require_relative 'output_interface/implemented' 19 | 20 | require_relative 'output_interface/error_logging' 21 | require_relative 'output_interface/controls' 22 | require_relative 'output_interface/personalization' 23 | 24 | # These output methods need to be implemented by the driving UI. 25 | include Abstract 26 | # These output method implementations depend on the Abstract ones. 27 | include Implemented 28 | 29 | include ErrorLogging 30 | include Controls 31 | include Personalization 32 | 33 | # Must be called after the entire {Cuboid} environment has been loaded. 34 | def self.initialize 35 | Controls.initialize 36 | ErrorLogging.initialize 37 | end 38 | 39 | extend self 40 | end 41 | 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/cuboid/ui/output_interface/abstract.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | module UI 3 | module OutputInterface 4 | 5 | # These methods need to be implemented by the driving UI. 6 | # 7 | # @author Tasos "Zapotek" Laskos 8 | module Abstract 9 | 10 | class Error < Cuboid::UI::OutputInterface::Error 11 | 12 | # Raised when trying to use an output method that has not been implemented. 13 | class MissingImplementation < Error 14 | end 15 | 16 | end 17 | 18 | # @abstract 19 | def print_error( message = '' ) 20 | fail Error::MissingImplementation 21 | end 22 | 23 | # @abstract 24 | def print_bad( message = '' ) 25 | fail Error::MissingImplementation 26 | end 27 | 28 | # @abstract 29 | def print_status( message = '' ) 30 | fail Error::MissingImplementation 31 | end 32 | 33 | # @abstract 34 | def print_info( message = '' ) 35 | fail Error::MissingImplementation 36 | end 37 | 38 | # @abstract 39 | def print_ok( message = '' ) 40 | fail Error::MissingImplementation 41 | end 42 | 43 | # @abstract 44 | def print_verbose( message = '' ) 45 | fail Error::MissingImplementation 46 | end 47 | 48 | # @abstract 49 | def print_line( message = '' ) 50 | fail Error::MissingImplementation 51 | end 52 | 53 | # @abstract 54 | def print_debug( message = '', level = 1 ) 55 | fail Error::MissingImplementation 56 | end 57 | 58 | # @abstract 59 | def output_provider_file 60 | # __FILE__ 61 | fail Error::MissingImplementation 62 | end 63 | 64 | end 65 | 66 | end 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /lib/cuboid/ui/output_interface/controls.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | module UI 3 | module OutputInterface 4 | 5 | # @author Tasos "Zapotek" Laskos 6 | module Controls 7 | 8 | def self.initialize 9 | @@verbose = false 10 | @@debug = 0 11 | end 12 | 13 | # Enables {#print_verbose} messages. 14 | # 15 | # @see #verbose? 16 | def verbose_on 17 | @@verbose = true 18 | end 19 | alias :verbose :verbose_on 20 | 21 | # Disables {#print_verbose} messages. 22 | # 23 | # @see #verbose? 24 | def verbose_off 25 | @@verbose = false 26 | end 27 | 28 | # @return [Bool] 29 | def verbose? 30 | @@verbose 31 | end 32 | 33 | # Enables {#print_debug} messages. 34 | # 35 | # @param [Integer] level 36 | # Sets the debugging level. 37 | # 38 | # @see #debug? 39 | def debug_on( level = 1 ) 40 | @@debug = level 41 | end 42 | alias :debug :debug_on 43 | 44 | # Disables {#print_debug} messages. 45 | # 46 | # @see #debug? 47 | def debug_off 48 | @@debug = 0 49 | end 50 | 51 | # @return [Integer] 52 | # Debugging level. 53 | def debug_level 54 | @@debug 55 | end 56 | 57 | # @param [Integer] level 58 | # Checks against this level. 59 | # 60 | # @return [Bool] 61 | # 62 | # @see #debug 63 | def debug?( level = 1 ) 64 | @@debug >= level 65 | end 66 | 67 | def debug_level_1? 68 | debug? 1 69 | end 70 | def debug_level_2? 71 | debug? 2 72 | end 73 | def debug_level_3? 74 | debug? 3 75 | end 76 | def debug_level_4? 77 | debug? 4 78 | end 79 | 80 | end 81 | 82 | end 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /lib/cuboid/ui/output_interface/error_logging.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | module UI 3 | module OutputInterface 4 | 5 | # @author Tasos "Zapotek" Laskos 6 | module ErrorLogging 7 | 8 | def self.initialize 9 | @@error_log_written_env = false 10 | 11 | @@error_fd ||= nil 12 | begin 13 | @@error_fd.close if @@error_fd 14 | rescue IOError 15 | end 16 | 17 | @@error_fd = nil 18 | @@error_buffer = [] 19 | @@error_logfile = "#{Cuboid::Options.paths.logs}error-#{Process.pid}.log" 20 | end 21 | 22 | # @param [String] logfile 23 | # Location of the error log file. 24 | def set_error_logfile( logfile ) 25 | @@error_logfile = logfile 26 | end 27 | 28 | # @return [String] 29 | # Location of the error log file. 30 | def error_logfile 31 | @@error_logfile 32 | end 33 | 34 | def has_error_log? 35 | File.exist? error_logfile 36 | end 37 | 38 | private 39 | 40 | def error_log_fd 41 | return @@error_fd if @@error_fd 42 | 43 | @@error_fd = File.open( error_logfile, 'a' ) 44 | @@error_fd.sync = true 45 | 46 | Kernel.at_exit do 47 | begin 48 | @@error_fd.close if @@error_fd 49 | rescue IOError 50 | end 51 | end 52 | 53 | @@error_fd 54 | 55 | # Errno::EMFILE (too many open files) or something, nothing we can do 56 | # about it except catch it to avoid a crash. 57 | rescue SystemCallError => e 58 | $stderr.puts "[#{e.class}] #{e}" 59 | e.backtrace.each { |line| $stderr.puts line } 60 | nil 61 | end 62 | 63 | # Logs an error message to the error log file. 64 | # 65 | # @param [String] str 66 | def log_error( str = '' ) 67 | fd = error_log_fd 68 | 69 | if !@@error_log_written_env 70 | @@error_log_written_env = true 71 | 72 | ['', "#{Time.now} " + ( '-' * 80 )].each do |s| 73 | 74 | if fd 75 | fd.puts s 76 | end 77 | 78 | @@error_buffer << s 79 | end 80 | 81 | begin 82 | h = {} 83 | ENV.each { |k, v| h[k] = v } 84 | 85 | options = Cuboid::Options.to_rpc_data.to_yaml 86 | 87 | ['ENV:', h.to_yaml, '-' * 80, 'OPTIONS:', options].each do |s| 88 | 89 | if fd 90 | fd.puts s 91 | end 92 | 93 | @@error_buffer += s.split("\n") 94 | end 95 | rescue 96 | end 97 | 98 | if fd 99 | fd.puts '-' * 80 100 | end 101 | 102 | @@error_buffer << '-' * 80 103 | end 104 | 105 | msg = "[#{Time.now}] #{str}" 106 | @@error_buffer << msg 107 | 108 | if fd 109 | fd.puts msg 110 | end 111 | 112 | nil 113 | end 114 | 115 | end 116 | 117 | end 118 | end 119 | end 120 | -------------------------------------------------------------------------------- /lib/cuboid/ui/output_interface/implemented.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | module UI 3 | module OutputInterface 4 | 5 | # @author Tasos "Zapotek" Laskos 6 | module Implemented 7 | 8 | # Prints the backtrace of an exception as error messages. 9 | # 10 | # @param [Exception] e 11 | def print_error_backtrace( e ) 12 | e.backtrace.each { |line| print_error( line ) } 13 | end 14 | 15 | def print_exception( e ) 16 | print_error "[#{e.class}] #{e}" 17 | print_error_backtrace( e ) 18 | end 19 | 20 | def print_debug_level_1( str = '' ) 21 | print_debug( str, 1 ) 22 | end 23 | 24 | def print_debug_level_2( str = '' ) 25 | print_debug( str, 2 ) 26 | end 27 | 28 | def print_debug_level_3( str = '' ) 29 | print_debug( str, 3 ) 30 | end 31 | 32 | def print_debug_level_4( str = '' ) 33 | print_debug( str, 4 ) 34 | end 35 | 36 | def print_debug_exception( e, level = 1 ) 37 | return if !debug? 38 | 39 | print_debug( "[#{e.class}] #{e}", level ) 40 | print_debug_backtrace( e, level ) 41 | end 42 | 43 | # Prints the backtrace of an exception as debugging messages. 44 | # 45 | # @param [Exception] e 46 | # 47 | # @see #debug? 48 | # @see #debug 49 | def print_debug_backtrace( e, level = 1 ) 50 | return if !debug? 51 | e.backtrace.each { |line| print_debug( line, level ) } 52 | end 53 | 54 | end 55 | 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/cuboid/ui/output_interface/personalization.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | module UI 3 | module OutputInterface 4 | 5 | # @author Tasos "Zapotek" Laskos 6 | module Personalization 7 | 8 | def included( base ) 9 | base.extend ClassMethods 10 | end 11 | 12 | module ClassMethods 13 | def personalize_output! 14 | @personalize_output = true 15 | end 16 | 17 | def personalize_output? 18 | @personalize_output 19 | end 20 | end 21 | 22 | private 23 | 24 | def personalize_output( message ) 25 | return message if !self.class.respond_to?( :personalize_output? ) 26 | 27 | self.class.personalize_output? ? 28 | "#{self.class.name.split('::').last}: #{message}" : message 29 | end 30 | 31 | def output_root 32 | @output_root ||= 33 | File.expand_path( File.dirname( __FILE__ ) + '/../../../../' ) + '/' 34 | end 35 | 36 | def caller_location 37 | file = nil 38 | line = nil 39 | caller_method = nil 40 | Kernel.caller.each do |c| 41 | file, line, method = *c.scan( /(.*):(\d+):in `(?:.*\s)?(.*)'/ ).flatten 42 | next if file == output_provider_file 43 | 44 | caller_method = method 45 | break 46 | end 47 | 48 | file.gsub!( output_root, '' ) 49 | 50 | context = nil 51 | if caller_method 52 | context = "[#{file}##{caller_method}:#{line}]" 53 | end 54 | 55 | context 56 | end 57 | 58 | end 59 | 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /lib/cuboid/version.rb: -------------------------------------------------------------------------------- 1 | module Cuboid 2 | 3 | # the universal system version 4 | VERSION = IO.read( File.dirname( __FILE__ ) + '/../version' ).strip 5 | 6 | end 7 | -------------------------------------------------------------------------------- /lib/version: -------------------------------------------------------------------------------- 1 | 0.2.13 2 | -------------------------------------------------------------------------------- /logs/placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qadron/cuboid/242af377b6196640d0901f4b0b9a145bbc445b82/logs/placeholder -------------------------------------------------------------------------------- /profiles/placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qadron/cuboid/242af377b6196640d0901f4b0b9a145bbc445b82/profiles/placeholder -------------------------------------------------------------------------------- /reports/placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qadron/cuboid/242af377b6196640d0901f4b0b9a145bbc445b82/reports/placeholder -------------------------------------------------------------------------------- /snapshots/placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qadron/cuboid/242af377b6196640d0901f4b0b9a145bbc445b82/snapshots/placeholder -------------------------------------------------------------------------------- /spec/cuboid/application/parts/data_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::Application::Parts::Data do 4 | include_examples 'application' 5 | 6 | describe '#data' do 7 | it "returns #{Cuboid::Data::Application}" do 8 | expect(subject.data).to be_kind_of Cuboid::Data::Application 9 | end 10 | end 11 | 12 | end 13 | -------------------------------------------------------------------------------- /spec/cuboid/application/parts/report_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::Application::Parts::Report do 4 | include_examples 'application' 5 | 6 | end 7 | -------------------------------------------------------------------------------- /spec/cuboid/application/runtime_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::Application::Runtime do 4 | include_examples 'application' 5 | 6 | describe '#state' do 7 | it 'provides access to the application runtime state' 8 | end 9 | 10 | describe '#state=' do 11 | it 'sets the application runtime state' 12 | end 13 | 14 | describe '#data' do 15 | it 'provides access to the application runtime data' 16 | end 17 | 18 | describe '#data=' do 19 | it 'sets the application runtime data' 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/cuboid/application_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::Application do 4 | include_examples 'application' 5 | 6 | describe '#version' do 7 | it "returns #{Cuboid::VERSION}" do 8 | expect(subject.version).to eq(Cuboid::VERSION) 9 | end 10 | end 11 | 12 | describe '#run' do 13 | context 'on invalid options' do 14 | it 'raises ArgumentError' 15 | end 16 | end 17 | 18 | describe '#statistics' do 19 | let(:statistics) { subject.statistics } 20 | 21 | describe ':runtime' do 22 | context 'when the app has been running' do 23 | it 'returns the runtime in seconds' do 24 | subject.run 25 | expect(statistics[:runtime]).to be > 0 26 | end 27 | end 28 | 29 | context 'when no scan has been running' do 30 | it 'returns 0' do 31 | expect(statistics[:runtime]).to eq(0) 32 | end 33 | end 34 | end 35 | end 36 | 37 | end 38 | -------------------------------------------------------------------------------- /spec/cuboid/data/application_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::Data::Application do 4 | 5 | subject { described_class.new } 6 | let(:dump_directory) do 7 | "#{Dir.tmpdir}/framework-#{Cuboid::Utilities.generate_token}" 8 | end 9 | 10 | describe '#statistics' do 11 | let(:statistics) { subject.statistics } 12 | end 13 | 14 | describe '#dump' do 15 | end 16 | 17 | describe '.load' do 18 | end 19 | 20 | describe '#clear' do 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /spec/cuboid/data_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::Data do 4 | 5 | subject { described_class } 6 | let(:dump_directory) do 7 | "#{Dir.tmpdir}/data-#{Cuboid::Utilities.generate_token}/" 8 | end 9 | 10 | describe '#application' do 11 | it "returns an instance of #{described_class::Application}" do 12 | expect(subject.application).to be_kind_of described_class::Application 13 | end 14 | end 15 | 16 | describe '#statistics' do 17 | %w(application).each do |name| 18 | it "includes :#{name} statistics" do 19 | expect(subject.statistics[name.to_sym]).to eq(subject.send(name).statistics) 20 | end 21 | end 22 | end 23 | 24 | describe '.dump' do 25 | %w(application).each do |name| 26 | it "stores ##{name} to disk" do 27 | previous_instance = subject.send(name) 28 | 29 | subject.dump( dump_directory ) 30 | 31 | new_instance = subject.load( dump_directory ).send(name) 32 | 33 | expect(new_instance).to be_kind_of subject.send(name).class 34 | expect(new_instance.object_id).not_to eq(previous_instance.object_id) 35 | end 36 | end 37 | end 38 | 39 | describe '#clear' do 40 | %w(application).each do |method| 41 | it "clears ##{method}" do 42 | expect(subject.send(method)).to receive(:clear).at_least(:once) 43 | subject.clear 44 | end 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /spec/cuboid/error_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::Error do 4 | it 'inherits from StandardError' do 5 | expect(Cuboid::Error <= StandardError).to be_truthy 6 | 7 | caught = false 8 | begin 9 | fail Cuboid::Error 10 | rescue StandardError => e 11 | caught = true 12 | end 13 | expect(caught).to be_truthy 14 | 15 | caught = false 16 | begin 17 | fail Cuboid::Error 18 | rescue 19 | caught = true 20 | end 21 | expect(caught).to be_truthy 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/cuboid/option_groups/datastore_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::OptionGroups::Datastore do 4 | include_examples 'option_group' 5 | subject { described_class.new } 6 | 7 | it 'creates attribute accessors on the fly' do 8 | subject.test = 1 9 | expect(subject.test).to eq(1) 10 | end 11 | 12 | describe '#to_h' do 13 | it 'only includes attributes with accessors' do 14 | method = :stuff= 15 | 16 | subject.instance_variable_set( :@blah, true ) 17 | 18 | value = subject.send( method, 'stuff' ) 19 | expect(subject.to_h).to eq({ method.to_s[0...-1].to_sym => value }) 20 | end 21 | end 22 | 23 | describe '#update' do 24 | it 'updates self with the values of the given hash' do 25 | method = :stuff 26 | value = 'stuff' 27 | 28 | subject.update( { method => value } ) 29 | expect(subject.send( method )).to include value 30 | end 31 | 32 | it 'returns self' do 33 | expect(subject.update({})).to eq(subject) 34 | end 35 | end 36 | 37 | describe '#merge' do 38 | it 'updates self with the values of the given OptionGroup' do 39 | method = :stuff 40 | value = 'stuff' 41 | 42 | group = described_class.new 43 | group.update( { method => value } ) 44 | 45 | subject.merge( group ) 46 | expect(subject.send( method )).to include value 47 | end 48 | 49 | it 'returns self' do 50 | group = described_class.new 51 | expect(subject.merge( group )).to eq(subject) 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /spec/cuboid/option_groups/dispatcher_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::OptionGroups::Agent do 4 | include_examples 'option_group' 5 | subject { described_class.new } 6 | 7 | %w(url instance_port_range peer ping_interval name).each do |method| 8 | it { is_expected.to respond_to method } 9 | it { is_expected.to respond_to "#{method}=" } 10 | end 11 | 12 | end 13 | -------------------------------------------------------------------------------- /spec/cuboid/option_groups/output_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::OptionGroups::Output do 4 | include_examples 'option_group' 5 | subject { described_class.new } 6 | 7 | %w(reroute_to_logfile).each do |method| 8 | it { is_expected.to respond_to method } 9 | it { is_expected.to respond_to "#{method}=" } 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /spec/cuboid/option_groups/report_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::OptionGroups::Report do 4 | include_examples 'option_group' 5 | subject { described_class.new } 6 | 7 | %w(path).each do |method| 8 | it { is_expected.to respond_to method } 9 | it { is_expected.to respond_to "#{method}=" } 10 | end 11 | 12 | describe '#path' do 13 | context "when #{Cuboid::OptionGroups::Paths}.config['reports']" do 14 | it 'returns it' do 15 | allow(Cuboid::OptionGroups::Paths).to receive(:config) do 16 | { 17 | 'reports' => Dir.tmpdir 18 | } 19 | end 20 | 21 | expect(subject.path).to eq(Dir.tmpdir + '/') 22 | end 23 | end 24 | end 25 | 26 | end 27 | -------------------------------------------------------------------------------- /spec/cuboid/option_groups/rpc_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::OptionGroups::RPC do 4 | include_examples 'option_group' 5 | subject { described_class.new } 6 | 7 | %w(server_socket server_external_address server_address server_port ssl_ca 8 | server_ssl_private_key server_ssl_certificate client_ssl_private_key 9 | client_ssl_certificate client_max_retries).each do |method| 10 | it { is_expected.to respond_to method } 11 | it { is_expected.to respond_to "#{method}=" } 12 | end 13 | 14 | describe '#to_client_options' do 15 | it 'returns RPC client options' do 16 | subject.connection_pool_size = 2 17 | subject.client_max_retries = 3 18 | subject.client_ssl_private_key = '2' 19 | subject.client_ssl_certificate = '3' 20 | subject.ssl_ca = '4' 21 | 22 | expect(subject.to_client_options).to eq( 23 | connection_pool_size: subject.connection_pool_size, 24 | max_retries: subject.client_max_retries, 25 | ssl_ca: subject.ssl_ca, 26 | ssl_pkey: subject.client_ssl_private_key, 27 | ssl_cert: subject.client_ssl_certificate 28 | ) 29 | end 30 | end 31 | 32 | describe '#to_server_options' do 33 | it 'returns RPC server options' do 34 | subject.server_address = 'blah' 35 | subject.server_external_address = 'fsfs' 36 | subject.server_port = 2 37 | subject.server_socket = '3' 38 | subject.ssl_ca = '4' 39 | subject.server_ssl_private_key = '4' 40 | subject.server_ssl_certificate = '4' 41 | 42 | expect(subject.to_server_options).to eq( 43 | host: subject.server_address, 44 | external_address: subject.server_external_address, 45 | port: subject.server_port, 46 | socket: subject.server_socket, 47 | ssl_ca: subject.ssl_ca, 48 | ssl_pkey: subject.server_ssl_private_key, 49 | ssl_cert: subject.server_ssl_certificate 50 | ) 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /spec/cuboid/option_groups/snapshot_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::OptionGroups::Snapshot do 4 | include_examples 'option_group' 5 | subject { described_class.new } 6 | 7 | %w(path).each do |method| 8 | it { is_expected.to respond_to method } 9 | it { is_expected.to respond_to "#{method}=" } 10 | end 11 | 12 | describe '#path' do 13 | context "when #{Cuboid::OptionGroups::Paths}.config['snapshots']" do 14 | it 'returns it' do 15 | allow(Cuboid::OptionGroups::Paths).to receive(:config) do 16 | { 17 | 'snapshots' => Dir.tmpdir 18 | } 19 | end 20 | 21 | expect(subject.path).to eq(Dir.tmpdir + '/') 22 | end 23 | end 24 | end 25 | 26 | end 27 | -------------------------------------------------------------------------------- /spec/cuboid/option_groups/system.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::OptionGroups::System do 4 | include_examples 'option_group' 5 | subject { described_class.new } 6 | 7 | %w(max_slots).each do |method| 8 | it { is_expected.to respond_to method } 9 | it { is_expected.to respond_to "#{method}=" } 10 | end 11 | 12 | end 13 | -------------------------------------------------------------------------------- /spec/cuboid/rpc/client/dispatcher_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'fileutils' 3 | 4 | describe Cuboid::RPC::Client::Agent do 5 | subject { agent_spawn application: "#{fixtures_path}/mock_app.rb", daemonize: true } 6 | 7 | describe '#node' do 8 | it 'provides access to the node data' do 9 | expect(subject.node.info.is_a?( Hash )).to be_truthy 10 | end 11 | end 12 | 13 | end 14 | -------------------------------------------------------------------------------- /spec/cuboid/rpc/client/instance_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::RPC::Client::Instance do 4 | 5 | let(:subject) { instance_spawn application: "#{fixtures_path}/mock_app.rb", daemonize: true } 6 | 7 | context 'when connecting to an instance' do 8 | context 'which requires a token' do 9 | context 'with a valid token' do 10 | it 'connects successfully' do 11 | expect(subject.alive?).to be_truthy 12 | end 13 | end 14 | 15 | context 'with an invalid token' do 16 | it 'should fail to connect' do 17 | expect do 18 | described_class.new( subject.url, 'blah' ).alive? 19 | end.to raise_error Toq::Exceptions::InvalidToken 20 | end 21 | end 22 | end 23 | end 24 | 25 | describe '#options' do 26 | let(:options) { subject.options } 27 | 28 | describe '#set' do 29 | let(:authorized_by) { 'tasos.laskos@gmail.com' } 30 | 31 | it 'allows batch assigning using a hash' do 32 | expect(options.set( authorized_by: authorized_by )).to be_truthy 33 | expect(options.authorized_by).to eq(authorized_by) 34 | end 35 | end 36 | end 37 | 38 | end 39 | -------------------------------------------------------------------------------- /spec/cuboid/rpc/server/active_options_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require Cuboid::Options.paths.lib + 'rpc/client/instance' 4 | require Cuboid::Options.paths.lib + 'rpc/server/instance' 5 | 6 | describe Cuboid::RPC::Server::ActiveOptions do 7 | let(:instance) { instance_spawn application: "#{fixtures_path}/mock_app.rb", daemonize: true } 8 | 9 | describe '#set' do 10 | it 'sets options by hash' do 11 | opts = { 12 | 'datastore' => { 'key' => 'val' }, 13 | } 14 | 15 | instance.options.set( opts ) 16 | h = instance.options.to_h 17 | 18 | expect(h['datastore']['key']).to eq(opts['datastore']['key']) 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /spec/cuboid/rpc/server/base_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require "#{Cuboid::Options.paths.lib}/rpc/server/base" 3 | 4 | describe Cuboid::RPC::Server::Base do 5 | before( :each ) do 6 | Raktr.global.run_in_thread 7 | end 8 | 9 | let(:subject) { Cuboid::RPC::Server::Base.new( 10 | host: 'localhost', port: port 11 | ) } 12 | let(:port) { available_port } 13 | 14 | it 'supports UNIX sockets', if: Raktr.supports_unix_sockets? do 15 | server = Cuboid::RPC::Server::Base.new( 16 | socket: "#{Dir.tmpdir}/cuboid-base-#{Cuboid::Utilities.generate_token}" 17 | ) 18 | 19 | server.start 20 | 21 | raised = false 22 | begin 23 | Timeout.timeout( 20 ){ 24 | sleep 0.1 while !server.ready? 25 | } 26 | rescue Exception => e 27 | raised = true 28 | end 29 | 30 | expect(server.ready?).to be_truthy 31 | expect(raised).to be_falsey 32 | end 33 | 34 | describe '#ready?' do 35 | context 'when the server is not ready' do 36 | it 'returns false' do 37 | expect(subject.ready?).to be_falsey 38 | end 39 | end 40 | 41 | context 'when the server is ready' do 42 | it 'returns true' do 43 | subject.start 44 | 45 | raised = false 46 | begin 47 | Timeout.timeout( 20 ){ 48 | sleep 0.1 while !subject.ready? 49 | } 50 | rescue Exception => e 51 | raised = true 52 | end 53 | 54 | expect(subject.ready?).to be_truthy 55 | expect(raised).to be_falsey 56 | end 57 | end 58 | end 59 | 60 | end 61 | -------------------------------------------------------------------------------- /spec/cuboid/rpc/server/output_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | require Cuboid::Options.paths.lib + 'rpc/server/output' 4 | 5 | class RPCOutput 6 | include Cuboid::UI::Output 7 | end 8 | 9 | describe Cuboid::UI::Output do 10 | 11 | subject { RPCOutput.new } 12 | let(:message) { 'This is a msg!' } 13 | let(:logfile ) do 14 | Cuboid::Options.paths.logs + "output_spec_#{Process.pid}.log" 15 | end 16 | let(:exception) do 17 | e = Exception.new( 'Stuff' ) 18 | e.set_backtrace( [ 'backtrace line1', 'backtrace line2' ] ) 19 | s 20 | end 21 | 22 | context 'when rerouting messages to a logfile' do 23 | before( :each ) do 24 | subject.reroute_to_file( logfile ) 25 | end 26 | 27 | it 'sends output to the logfile' do 28 | subject.print_line( 'blah' ) 29 | expect(IO.read( logfile ).split( "\n" ).size).to eq(1) 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/cuboid/ruby/hash_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Hash do 4 | let( :with_symbols ) do 5 | { 6 | stuff: 'blah', 7 | more: { 8 | stuff: { 9 | blah: 'stuff' 10 | } 11 | } 12 | } 13 | end 14 | 15 | let( :with_strings ) do 16 | { 17 | 'stuff' => 'blah', 18 | 'more' => { 19 | 'stuff' => { 20 | 'blah' => 'stuff' 21 | } 22 | } 23 | } 24 | end 25 | 26 | describe '#my_stringify_keys' do 27 | it 'recursively converts keys to strings' do 28 | expect(with_symbols.my_stringify_keys).to eq(with_strings) 29 | end 30 | 31 | context 'when the recursive is set to false' do 32 | it 'only converts the keys at depth 1' do 33 | expect(with_symbols.my_stringify_keys( false )).to eq({ 34 | 'stuff' => 'blah', 35 | 'more' => { 36 | stuff: { 37 | blah: 'stuff' 38 | } 39 | } 40 | }) 41 | end 42 | end 43 | end 44 | 45 | describe '#my_symbolize_keys' do 46 | it 'recursively converts keys to symbols' do 47 | expect(with_strings.my_symbolize_keys).to eq(with_symbols) 48 | end 49 | 50 | context 'when the recursive is set to false' do 51 | it 'only converts the keys at depth 1' do 52 | expect(with_strings.my_symbolize_keys( false )).to eq({ 53 | stuff: 'blah', 54 | more: { 55 | 'stuff' => { 56 | 'blah' => 'stuff' 57 | } 58 | } 59 | }) 60 | end 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /spec/cuboid/ruby/object_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | class Empty 4 | end 5 | 6 | class MyClass 7 | attr_accessor :stuff 8 | end 9 | 10 | describe Object do 11 | 12 | describe '#deep_clone' do 13 | it 'returns a deep copy of the object' do 14 | a = [ [1,2] ] 15 | b = a.deep_clone 16 | a[0] << 3 17 | 18 | expect(b).to eq([ [1,2] ]) 19 | end 20 | end 21 | 22 | end 23 | -------------------------------------------------------------------------------- /spec/cuboid/state/options_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::State::Options do 4 | 5 | subject { described_class.new } 6 | let(:dump_directory) do 7 | "#{Dir.tmpdir}/options-#{Cuboid::Utilities.generate_token}" 8 | end 9 | 10 | it { is_expected.to respond_to :clear} 11 | 12 | describe '#statistics' do 13 | let(:statistics) { subject.statistics } 14 | end 15 | 16 | describe '#dump' do 17 | it 'stores to disk' do 18 | Cuboid::Options.datastore.my_custom_option = 'my value' 19 | subject.dump( dump_directory ) 20 | 21 | expect(Cuboid::Options.load( "#{dump_directory}/options" ). 22 | datastore.my_custom_option).to eq('my value') 23 | end 24 | end 25 | 26 | describe '.load' do 27 | it 'restores from disk' do 28 | Cuboid::Options.datastore.my_custom_option = 'my value' 29 | subject.dump( dump_directory ) 30 | 31 | described_class.load( dump_directory ) 32 | 33 | expect(Cuboid::Options.datastore.my_custom_option).to eq('my value') 34 | end 35 | end 36 | 37 | end 38 | -------------------------------------------------------------------------------- /spec/cuboid/state_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::State do 4 | 5 | subject { described_class } 6 | let(:dump_directory) do 7 | "#{Dir.tmpdir}/state-#{Cuboid::Utilities.generate_token}" 8 | end 9 | 10 | describe '#application' do 11 | it "returns an instance of #{described_class::Application}" do 12 | expect(subject.application).to be_kind_of described_class::Application 13 | end 14 | end 15 | 16 | describe '#options' do 17 | it "returns an instance of #{described_class::Options}" do 18 | expect(subject.options).to be_kind_of described_class::Options 19 | end 20 | end 21 | 22 | describe '#statistics' do 23 | %w(options application).each do |name| 24 | it "includes :#{name} statistics" do 25 | expect(subject.statistics[name.to_sym]).to eq(subject.send(name).statistics) 26 | end 27 | end 28 | end 29 | 30 | describe '.dump' do 31 | %w(options application).each do |name| 32 | it "stores ##{name} to disk" do 33 | previous_instance = subject.send(name) 34 | 35 | subject.dump( dump_directory ) 36 | 37 | new_instance = subject.load( dump_directory ).send(name) 38 | 39 | expect(new_instance).to be_kind_of subject.send(name).class 40 | expect(new_instance.object_id).not_to eq(previous_instance.object_id) 41 | end 42 | end 43 | end 44 | 45 | describe '#clear' do 46 | %w(options application).each do |method| 47 | it "clears ##{method}" do 48 | expect(subject.send(method)).to receive(:clear).at_least(:once) 49 | subject.clear 50 | end 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /spec/cuboid/support/buffer/autoflush_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::Support::Buffer::AutoFlush do 4 | describe '#initialize' do 5 | context 'when passed a max_size' do 6 | context 'when the buffer reaches that size' do 7 | it 'forces the buffer to #flush itself' do 8 | b = described_class.new( 10 ) 9 | 10 | buffers = [] 11 | b.on_flush do |buffer| 12 | buffers << buffer 13 | end 14 | 15 | 20.times { |i| b << i } 16 | 17 | expect(buffers.size).to eq(2) 18 | expect(buffers.shift).to eq((0..9).to_a) 19 | expect(buffers.shift).to eq((10...20).to_a) 20 | 21 | expect(b).to be_empty 22 | end 23 | end 24 | end 25 | 26 | context 'when passed a max_pushes' do 27 | context 'when the amount of pushes reaches that size' do 28 | it 'forces the buffer to #flush itself' do 29 | b = described_class.new( 99999, 10 ) 30 | 31 | buffers = [] 32 | b.on_flush do |buffer| 33 | buffers << buffer 34 | end 35 | 36 | 20.times { |i| b << i } 37 | 38 | expect(buffers.size).to eq(2) 39 | expect(buffers.shift).to eq((0..9).to_a) 40 | expect(buffers.shift).to eq((10...20).to_a) 41 | expect(b).to be_empty 42 | 43 | b = described_class.new( 99999, 10 ) 44 | 45 | buffers = [] 46 | b.on_flush do |buffer| 47 | buffers << buffer 48 | end 49 | 50 | 20.times { |i| b.batch_push (0..1000).to_a } 51 | 52 | expect(buffers.size).to eq(2) 53 | expect(buffers.shift).to eq((0..1000).to_a) 54 | expect(buffers.shift).to eq((0..1000).to_a) 55 | expect(b).to be_empty 56 | end 57 | end 58 | end 59 | 60 | context 'when passed a type' do 61 | it 'should be used for internal storage' do 62 | b = described_class.new( 10, 999, Set ) 63 | b << 'test' 64 | b << 'test' 65 | expect(b.size).to eq(1) 66 | expect(b.flush.class).to eq(Set) 67 | 68 | b = described_class.new 69 | b << 'test' 70 | b << 'test' 71 | expect(b.size).to eq(2) 72 | 73 | expect(b.flush.class).to eq(Array) 74 | end 75 | end 76 | end 77 | 78 | end 79 | -------------------------------------------------------------------------------- /spec/cuboid/support/cache/least_cost_replacement_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::Support::Cache::LeastCostReplacement do 4 | it_behaves_like 'cache' 5 | 6 | it 'prunes itself by removing the least costly entries' do 7 | subject.max_size = 3 8 | 9 | subject.store( :k, '1', :high ) 10 | subject.store( :k2, '2', :high ) 11 | subject.store( :k3, '3', :medium ) 12 | subject.store( :k4, '4', :low ) 13 | expect(subject.size).to eq(3) 14 | 15 | expect(subject[:k4]).to be_truthy 16 | expect(subject[:k3]).to be_nil 17 | expect(subject[:k2]).to be_truthy 18 | expect(subject[:k]).to be_truthy 19 | 20 | subject.clear 21 | 22 | subject.max_size = 1 23 | 24 | subject.store( :k, '1', :medium ) 25 | subject.store( :k2, '2', :low ) 26 | subject.store( :k3, '3', :low ) 27 | subject.store( :k4, '4', :low ) 28 | expect(subject.size).to eq(1) 29 | 30 | expect(subject[:k4]).to be_truthy 31 | expect(subject[:k3]).to be_nil 32 | expect(subject[:k2]).to be_nil 33 | expect(subject[:k]).to be_nil 34 | end 35 | 36 | describe '#store' do 37 | it 'stores an object by key' do 38 | v = 'val' 39 | expect(subject.store( :key, v, :low )).to eq(v) 40 | expect(subject[:key]).to eq(v) 41 | end 42 | it 'assigns cost to object' do 43 | v = 'val' 44 | expect(subject.store( :key, v, :low )).to eq(v) 45 | expect(subject[:key]).to eq(v) 46 | end 47 | end 48 | 49 | describe '#[]=' do 50 | it 'stores an object' do 51 | v = 'val' 52 | expect(subject[:key] = v).to eq(v) 53 | expect(subject[:key]).to eq(v) 54 | end 55 | it 'alias of #store' do 56 | v = 'val2' 57 | expect(subject.store( :key2, v )).to eq(v) 58 | expect(subject[:key2]).to eq(v) 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /spec/cuboid/support/cache/least_recently_pushed_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::Support::Cache::LeastRecentlyPushed do 4 | it_behaves_like 'cache' 5 | 6 | it 'prunes itself by removing Least Recently Pushed entries' do 7 | subject.max_size = 3 8 | 9 | subject[:k] = '1' 10 | subject[:k2] = '2' 11 | subject[:k3] = '3' 12 | subject[:k4] = '4' 13 | expect(subject.size).to eq(3) 14 | 15 | expect(subject[:k4]).to be_truthy 16 | expect(subject[:k3]).to be_truthy 17 | expect(subject[:k2]).to be_truthy 18 | expect(subject[:k]).to be_nil 19 | 20 | subject.clear 21 | 22 | subject.max_size = 1 23 | subject[:k] = '1' 24 | subject[:k2] = '3' 25 | subject[:k3] = '4' 26 | expect(subject.size).to eq(1) 27 | 28 | expect(subject[:k3]).to be_truthy 29 | expect(subject[:k2]).to be_nil 30 | expect(subject[:k]).to be_nil 31 | end 32 | 33 | describe '#[]=' do 34 | it 'stores an object' do 35 | v = 'val' 36 | expect(subject[:key] = v).to eq(v) 37 | expect(subject[:key]).to eq(v) 38 | end 39 | it 'alias of #store' do 40 | v = 'val2' 41 | expect(subject.store( :key2, v )).to eq(v) 42 | expect(subject[:key2]).to eq(v) 43 | end 44 | end 45 | 46 | describe '#[]' do 47 | it 'retrieves an object by key' do 48 | v = 'val2' 49 | subject[:key] = v 50 | expect(subject[:key]).to eq(v) 51 | expect(subject.empty?).to be_falsey 52 | end 53 | 54 | context 'when the key does not exist' do 55 | it 'returns nil' do 56 | expect(subject[:some_key]).to be_nil 57 | end 58 | end 59 | end 60 | 61 | describe '#delete' do 62 | context 'when the key exists' do 63 | it 'deletes a key and return its value' do 64 | v = 'my_val' 65 | subject[:my_key] = v 66 | expect(subject.delete( :my_key )).to eq(v) 67 | expect(subject[:my_key]).to be_nil 68 | expect(subject.include?( :my_key )).to be_falsey 69 | end 70 | end 71 | context 'when the key does not exist' do 72 | it 'returns nil' do 73 | expect(subject.delete( :my_key2 )).to be_nil 74 | end 75 | end 76 | end 77 | 78 | describe '#clear' do 79 | it 'empties the cache' do 80 | subject[:my_key2] = 'v' 81 | expect(subject.size).to be > 0 82 | expect(subject.empty?).to be_falsey 83 | subject.clear 84 | 85 | expect(subject.size).to eq(0) 86 | expect(subject.empty?).to be_truthy 87 | end 88 | end 89 | 90 | end 91 | -------------------------------------------------------------------------------- /spec/cuboid/support/cache/least_recently_used_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::Support::Cache::LeastRecentlyUsed do 4 | it_behaves_like 'cache' 5 | 6 | it 'prunes itself by removing Least Recently Used entries' do 7 | subject.max_size = 3 8 | 9 | subject[:k] = '1' 10 | subject[:k2] = '2' 11 | subject[:k] 12 | subject[:k3] = '3' 13 | subject[:k4] = '4' 14 | 15 | expect(subject.size).to eq(3) 16 | 17 | expect(subject[:k]).to be_truthy 18 | expect(subject[:k4]).to be_truthy 19 | expect(subject[:k3]).to be_truthy 20 | expect(subject[:k2]).to be_nil 21 | end 22 | 23 | describe '#[]=' do 24 | it 'stores an object' do 25 | v = 'val' 26 | expect(subject[:key] = v).to eq(v) 27 | expect(subject[:key]).to eq(v) 28 | end 29 | it 'alias of #store' do 30 | v = 'val2' 31 | expect(subject.store( :key2, v )).to eq(v) 32 | expect(subject[:key2]).to eq(v) 33 | end 34 | end 35 | 36 | describe '#[]' do 37 | it 'retrieves an object by key' do 38 | v = 'val2' 39 | subject[:key] = v 40 | expect(subject[:key]).to eq(v) 41 | expect(subject.empty?).to be_falsey 42 | end 43 | 44 | context 'when the key does not exist' do 45 | it 'returns nil' do 46 | expect(subject[:some_key]).to be_nil 47 | end 48 | end 49 | end 50 | 51 | describe '#delete' do 52 | context 'when the key exists' do 53 | it 'deletes a key and return its value' do 54 | v = 'my_val' 55 | subject[:my_key] = v 56 | expect(subject.delete( :my_key )).to eq(v) 57 | expect(subject[:my_key]).to be_nil 58 | expect(subject.include?( :my_key )).to be_falsey 59 | end 60 | end 61 | context 'when the key does not exist' do 62 | it 'returns nil' do 63 | expect(subject.delete( :my_key2 )).to be_nil 64 | end 65 | end 66 | end 67 | 68 | describe '#clear' do 69 | it 'empties the cache' do 70 | subject[:my_key2] = 'v' 71 | expect(subject.size).to be > 0 72 | expect(subject.empty?).to be_falsey 73 | subject.clear 74 | 75 | expect(subject.size).to eq(0) 76 | expect(subject.empty?).to be_truthy 77 | end 78 | end 79 | 80 | end 81 | -------------------------------------------------------------------------------- /spec/cuboid/support/cache/preference_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::Support::Cache::Preference do 4 | it_behaves_like 'cache' 5 | 6 | it 'prunes itself by removing entries returned by the given block' do 7 | subject.max_size = 3 8 | 9 | subject.prefer { :k2 } 10 | 11 | k = [ :k, :k2, :k3, :k4 ] 12 | subject[k[0]] = '1' 13 | subject[k[1]] = '2' 14 | subject[k[2]] = '3' 15 | subject[k[3]] = '4' 16 | expect(subject.size).to eq(3) 17 | 18 | expect(k.map { |key| subject[key] }.count( nil )).to eq(1) 19 | 20 | subject.clear 21 | end 22 | 23 | it 'does not remove entries which are not preferred even if the max size has been exceeded' do 24 | subject.prefer { :k2 } 25 | 26 | k = [ :k, :k2, :k3, :k4 ] 27 | 28 | subject.max_size = 1 29 | subject[k[0]] = '1' 30 | subject[k[1]] = '3' 31 | subject[k[2]] = '4' 32 | expect(subject.size).to eq(2) 33 | 34 | expect(k[0...3].map { |key| subject[key] }.count( nil )).to eq(1) 35 | end 36 | 37 | end 38 | -------------------------------------------------------------------------------- /spec/cuboid/support/cache/random_replacement_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::Support::Cache::RandomReplacement do 4 | it_behaves_like 'cache' 5 | 6 | it 'prunes itself by removing random entries (Random Replacement)' do 7 | subject.max_size = 3 8 | 9 | k = [ :k, :k2, :k3, :k4 ] 10 | subject[k[0]] = '1' 11 | subject[k[1]] = '2' 12 | subject[k[2]] = '3' 13 | subject[k[3]] = '4' 14 | expect(subject.size).to eq(3) 15 | 16 | expect(k.map { |key| subject[key] }.count( nil )).to eq(1) 17 | 18 | subject.clear 19 | 20 | subject.max_size = 1 21 | subject[k[0]] = '1' 22 | subject[k[1]] = '3' 23 | subject[k[2]] = '4' 24 | expect(subject.size).to eq(1) 25 | 26 | expect(k[0...3].map { |key| subject[key] }.count( nil )).to eq(2) 27 | end 28 | 29 | describe '#[]=' do 30 | it 'stores an object' do 31 | v = 'val' 32 | expect(subject[:key] = v).to eq(v) 33 | expect(subject[:key]).to eq(v) 34 | end 35 | it 'alias of #store' do 36 | v = 'val2' 37 | expect(subject.store( :key2, v )).to eq(v) 38 | expect(subject[:key2]).to eq(v) 39 | end 40 | end 41 | 42 | end 43 | -------------------------------------------------------------------------------- /spec/cuboid/support/crypto/rsa_aes_cbc_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require 'tempfile' 3 | 4 | describe Cuboid::Support::Crypto::RSA_AES_CBC do 5 | 6 | SEED = 'seed data' 7 | 8 | let(:public_key_file_path) do 9 | key = private_key.public_key 10 | file = Tempfile.new( 'public_key.pem' ) 11 | file.write( key.to_pem ) 12 | file.close 13 | file.path 14 | end 15 | let(:private_key_file_path) do 16 | file = Tempfile.new( 'private_key.pem' ) 17 | file.write( private_key.to_pem ) 18 | file.close 19 | file.path 20 | end 21 | let(:private_key) { OpenSSL::PKey::RSA.generate( 1024 ) } 22 | subject { described_class.new( public_key_file_path, private_key_file_path ) } 23 | 24 | it 'generates matching encrypted and decrypted data' do 25 | expect(subject.decrypt( subject.encrypt( SEED ) )).to eq(SEED) 26 | end 27 | 28 | end 29 | -------------------------------------------------------------------------------- /spec/cuboid/support/filter/set_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::Support::Filter::Set do 4 | it_behaves_like 'filter' 5 | 6 | describe '#merge' do 7 | it 'merges 2 sets' do 8 | new = described_class.new 9 | 10 | subject << 'test' 11 | new << 'test2' 12 | 13 | subject.merge new 14 | expect(subject).to include 'test' 15 | expect(subject).to include 'test2' 16 | end 17 | end 18 | 19 | end 20 | -------------------------------------------------------------------------------- /spec/cuboid/support/glob_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::Support::Glob do 4 | 5 | let(:conversions) do 6 | { 7 | '*' => /^.*?$/i, 8 | 'test*' => /^test.*?$/i, 9 | '*test*' => /^.*?test.*?$/i, 10 | '*/*' => /^.*?\/.*?$/i 11 | } 12 | end 13 | 14 | let(:matches) do 15 | [ 16 | ['*', '', true], 17 | ['*', 'stuff', true], 18 | 19 | ['test*', 'stuff', false], 20 | ['test*', 'teststuff', true], 21 | ['test*', 'tEsTstuff', true], 22 | ['test*', 'stuffteststuff', false], 23 | 24 | ['*test*', 'stuffteststuff', true], 25 | ['*test*', 'stufftEsTstuff', true], 26 | ['*test*', 'stuff', false], 27 | ['*test*', 'teststuff', true], 28 | ['*test*', 'stufftest', true], 29 | 30 | ['*/*', 'test', false], 31 | ['*/*', 'test/', true], 32 | ['*/*', 'test/stuff', true] 33 | ] 34 | end 35 | 36 | describe '.to_regexp' do 37 | it 'converts a glog to a regexp' do 38 | conversions.each do |glob, regexp| 39 | expect(described_class.to_regexp( glob )).to eq regexp 40 | end 41 | end 42 | end 43 | 44 | describe '#regexp' do 45 | it 'returns the glob as a regexp' do 46 | conversions.each do |glob, regexp| 47 | expect(described_class.new( glob ).regexp).to eq regexp 48 | end 49 | end 50 | end 51 | 52 | describe '#match?' do 53 | it 'checks whether or not the glob matches the string' do 54 | matches.each do |glob, string, result| 55 | expect(described_class.new( glob ).match?( string )).to eq result 56 | end 57 | end 58 | end 59 | 60 | describe '#matches?' do 61 | it 'checks whether or not the glob matches the string' do 62 | matches.each do |glob, string, result| 63 | expect(described_class.new( glob ).matches?( string )).to eq result 64 | end 65 | end 66 | end 67 | 68 | describe '=~' do 69 | it 'checks whether or not the glob matches the string' do 70 | matches.each do |glob, string, result| 71 | expect(described_class.new( glob ) =~ string ).to eq result 72 | end 73 | end 74 | end 75 | end 76 | -------------------------------------------------------------------------------- /spec/cuboid/support/mixins/observable_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | class ObservableTest 4 | include Cuboid::Support::Mixins::Observable 5 | 6 | public :clear_observers 7 | 8 | advertise :my_event, :my_other_event 9 | 10 | def notify( event, *args ) 11 | send "notify_#{event}", *args 12 | end 13 | 14 | end 15 | 16 | describe Cuboid::Support::Mixins::Observable do 17 | 18 | subject{ ObservableTest.new } 19 | 20 | describe '#' do 21 | it 'adds an observer' do 22 | called = false 23 | subject.my_event { called = true } 24 | subject.notify :my_event 25 | 26 | expect(called).to be_truthy 27 | end 28 | 29 | it 'returns self' do 30 | expect(subject.my_event { }).to eq(subject) 31 | end 32 | 33 | context 'when no block is given' do 34 | it 'raises ArgumentError' do 35 | expect { subject.my_event }.to raise_error ArgumentError 36 | end 37 | end 38 | 39 | context 'when the observer expects arguments' do 40 | it 'forwards them' do 41 | received_args = nil 42 | sent_args = [ 1, 2, 3] 43 | 44 | subject.my_other_event do |one, two, three| 45 | received_args = [one, two, three] 46 | end 47 | subject.notify :my_other_event, sent_args 48 | 49 | expect(received_args).to eq(sent_args) 50 | end 51 | end 52 | 53 | describe 'when the event does not exist' do 54 | it "raises #{NoMethodError}" do 55 | expect { subject.blah_event }.to raise_error NoMethodError 56 | end 57 | end 58 | end 59 | 60 | describe '#notify' do 61 | it 'returns nil' do 62 | subject.my_event { } 63 | expect(subject.notify( :my_event )).to be_nil 64 | end 65 | 66 | context 'when a callback raises an exception' do 67 | it 'does not affect other callbacks' do 68 | called = [] 69 | 70 | subject.my_event { called << 1 } 71 | subject.my_event { called << 2; raise } 72 | subject.my_event { called << 3 } 73 | 74 | subject.notify( :my_event ) 75 | 76 | expect(called).to eq([1, 2, 3]) 77 | end 78 | end 79 | end 80 | 81 | describe '#clear_observers' do 82 | it 'removes all observers' do 83 | called = false 84 | 85 | subject.my_event { called = true } 86 | subject.clear_observers 87 | 88 | subject.notify :my_event 89 | 90 | expect(called).to be_falsey 91 | 92 | end 93 | end 94 | 95 | end 96 | -------------------------------------------------------------------------------- /spec/cuboid/system/platforms/linux_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::System::Platforms::Linux do 4 | it_should_behave_like 'Cuboid::System::Platforms::Mixins::Unix' 5 | 6 | describe '#memory_free' do 7 | it 'returns the amount of free memory' do 8 | o = Object.new 9 | expect(o).to receive(:available_bytes).and_return(1000) 10 | expect(subject).to receive(:memory).at_least(:once).and_return(o) 11 | 12 | expect(subject.memory_free).to eq 1000 13 | end 14 | end 15 | 16 | describe '.current?' do 17 | context 'when running on Linux' do 18 | it 'returns true' do 19 | expect(Cuboid).to receive(:linux?).and_return( true ) 20 | expect(described_class).to be_current 21 | end 22 | end 23 | 24 | context 'when not running on Linux' do 25 | it 'returns false' do 26 | expect(Cuboid).to receive(:linux?).and_return( false ) 27 | expect(described_class).to_not be_current 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/cuboid/system/platforms/osx_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::System::Platforms::OSX do 4 | it_should_behave_like 'Cuboid::System::Platforms::Mixins::Unix' 5 | 6 | describe '#memory_free' do 7 | it 'returns the amount of free memory' do 8 | o = Object.new 9 | expect(o).to receive(:free).and_return(1000) 10 | expect(o).to receive(:pagesize).and_return(4096) 11 | expect(subject).to receive(:memory).at_least(:once).and_return(o) 12 | 13 | expect(subject.memory_free).to eq 4096000 14 | end 15 | end 16 | 17 | describe '.current?' do 18 | context 'when running on OSX' do 19 | it 'returns true' do 20 | expect(Cuboid).to receive(:mac?).and_return( true ) 21 | expect(described_class).to be_current 22 | end 23 | end 24 | 25 | context 'when not running on OSX' do 26 | it 'returns false' do 27 | expect(Cuboid).to receive(:mac?).and_return( false ) 28 | expect(described_class).to_not be_current 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /spec/cuboid/system/platforms/windows_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Cuboid::System::Platforms::Windows, if: Cuboid.windows? do 4 | it_should_behave_like 'Cuboid::System::Platforms::Base' 5 | 6 | subject { described_class.new } 7 | 8 | describe '#memory_free' do 9 | it 'returns the amount of free memory' do 10 | expect(subject.memory_free).to be > 0 11 | end 12 | end 13 | 14 | describe '#disk_space_free' do 15 | it 'returns the amount of free disk space' do 16 | expect(subject.disk_space_free).to be > 0 17 | end 18 | end 19 | 20 | describe '#memory_for_process_group' do 21 | it 'returns bytes of memory used by the group' do 22 | expect(subject.memory_for_process_group( Process.pid )).to be > 0 23 | end 24 | end 25 | 26 | describe '.current?' do 27 | context 'when running on Windows' do 28 | it 'returns true'do 29 | expect(Cuboid).to receive(:windows?).and_return( true ) 30 | expect(described_class).to be_current 31 | end 32 | end 33 | 34 | context 'when not running on Windows' do 35 | it 'returns false' do 36 | expect(Cuboid).to receive(:windows?).and_return( false ) 37 | expect(described_class).to_not be_current 38 | end 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'rack/test' 2 | # require 'simplecov' 3 | require 'faker' 4 | 5 | require 'typhoeus' 6 | require_relative '../lib/cuboid' 7 | require_relative '../lib/cuboid/processes' 8 | require_relative '../lib/cuboid/processes/helpers' 9 | 10 | require_relative 'support/helpers/paths' 11 | require_relative 'support/helpers/requires' 12 | 13 | Dir.glob( "#{support_path}/{lib,helpers,shared,factories}/**/*.rb" ).each { |f| require f } 14 | 15 | # Uncomment to show output from spawned processes. 16 | Cuboid::Processes::Manager.preserve_output 17 | 18 | RSpec::Core::MemoizedHelpers.module_eval do 19 | alias to should 20 | alias to_not should_not 21 | end 22 | 23 | RSpec.configure do |config| 24 | config.example_status_persistence_file_path = '.rspec_status' 25 | config.run_all_when_everything_filtered = true 26 | config.color = true 27 | config.add_formatter :documentation 28 | config.alias_example_to :expect_it 29 | config.filter_run_when_matching focus: true 30 | 31 | config.mock_with :rspec do |mocks| 32 | mocks.yield_receiver_to_any_instance_implementation_blocks = true 33 | end 34 | 35 | config.before( :each ) do 36 | reset_all 37 | end 38 | 39 | config.after( :each ) do 40 | cleanup_instances 41 | processes_killall 42 | end 43 | config.after( :all ) do 44 | killall 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /spec/support/factories/placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qadron/cuboid/242af377b6196640d0901f4b0b9a145bbc445b82/spec/support/factories/placeholder -------------------------------------------------------------------------------- /spec/support/factories/scan_report.rb: -------------------------------------------------------------------------------- 1 | Factory.define :report_data do 2 | 3 | { 4 | application: MockApp, 5 | seed: Cuboid::Utilities.random_seed, 6 | options: Cuboid::Options.to_hash, 7 | start_datetime: Time.now - 10_000, 8 | finish_datetime: Time.now 9 | } 10 | end 11 | 12 | Factory.define :report do 13 | Cuboid::Report.new Factory[:report_data] 14 | end 15 | 16 | Factory.define :report_empty do 17 | Cuboid::Report.new 18 | end 19 | -------------------------------------------------------------------------------- /spec/support/fixtures/empty/placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qadron/cuboid/242af377b6196640d0901f4b0b9a145bbc445b82/spec/support/fixtures/empty/placeholder -------------------------------------------------------------------------------- /spec/support/fixtures/executables/node.rb: -------------------------------------------------------------------------------- 1 | require Options.paths.lib + 'ui/output' 2 | require Options.paths.lib + 'rpc/server/agent' 3 | require Options.paths.lib + 'processes/manager' 4 | 5 | class Node < Cuboid::RPC::Server::Agent::Node 6 | 7 | def initialize 8 | @options = Options.instance 9 | 10 | methods.each do |m| 11 | next if method( m ).owner != Cuboid::RPC::Server::Agent::Node 12 | self.class.send :private, m 13 | self.class.send :public, m 14 | end 15 | 16 | @server = Cuboid::RPC::Server::Base.new 17 | @server.add_async_check do |method| 18 | # methods that expect a block are async 19 | method.parameters.flatten.include?( :block ) 20 | end 21 | @server.add_handler( 'node', self ) 22 | 23 | super @options, @server 24 | 25 | @server.start 26 | end 27 | 28 | def url 29 | @server.url 30 | end 31 | 32 | def shutdown 33 | Raktr.global.delay 1 do 34 | Process.kill( 'KILL', Process.pid ) 35 | end 36 | end 37 | 38 | def connect_to_peer( url ) 39 | self.class.connect_to_peer( url ) 40 | end 41 | 42 | def self.connect_to_peer( url ) 43 | c = Cuboid::RPC::Client::Base.new( url ) 44 | Toq::Proxy.new( c, 'node' ) 45 | end 46 | end 47 | 48 | Raktr.global.run do 49 | Node.new 50 | end 51 | -------------------------------------------------------------------------------- /spec/support/fixtures/mock_app.rb: -------------------------------------------------------------------------------- 1 | require_relative 'mock_app/test_service' 2 | 3 | class MockApp < Cuboid::Application 4 | 5 | # This app is going to be using a max of 2 threads. 6 | provision_cores 2 7 | # This app is going to be using a max of 200MB of RAM. 8 | provision_memory 200 * 1024 * 1024 9 | # This app is going to be using a max of 2GB of disk space. 10 | provision_disk 2 * 1024 * 1024 * 1024 11 | 12 | validate_options_with :do_validate_options 13 | 14 | # Register event handlers. 15 | handler_for :pause, :do_pause 16 | handler_for :resume, :do_resume 17 | handler_for :suspend, :do_suspend 18 | handler_for :restore, :do_restore 19 | handler_for :abort, :do_abort 20 | 21 | # RPC, report and snapshot file. 22 | serialize_with Marshal 23 | 24 | agent_service_for :test_service, TestService 25 | 26 | # Execution entry point. 27 | def run 28 | ap __method__ 29 | report 'My results.' 30 | 31 | sleep 5 32 | end 33 | 34 | def do_validate_options( options ) 35 | return true if !options.is_a?( Hash ) 36 | !options.include?( 'invalid' ) 37 | end 38 | 39 | def do_pause 40 | ap __method__ 41 | end 42 | 43 | def do_resume 44 | ap __method__ 45 | end 46 | 47 | def do_suspend 48 | # Write pending state and data to Cuboid::Data::Application. 49 | ap __method__ 50 | end 51 | 52 | def do_restore 53 | # Restore special state and data from Cuboid::Data::Application. 54 | ap __method__ 55 | end 56 | 57 | def do_abort 58 | ap __method__ 59 | end 60 | 61 | end 62 | -------------------------------------------------------------------------------- /spec/support/fixtures/mock_app/test_service.rb: -------------------------------------------------------------------------------- 1 | require 'cuboid/rpc/server/agent' 2 | 3 | class TestService < Cuboid::RPC::Server::Agent::Service 4 | 5 | private :instances 6 | public :instances 7 | 8 | def test_agent 9 | agent.class == Cuboid::RPC::Server::Agent 10 | end 11 | 12 | def test_opts 13 | agent.instance_eval{ @options } == options 14 | end 15 | 16 | def test_node 17 | node.class == Cuboid::RPC::Server::Agent::Node 18 | end 19 | 20 | def test_map_instances( &block ) 21 | each = proc do |instance, iterator| 22 | iterator.return [instance.url, instance.token] 23 | end 24 | after = proc { |i| block.call Hash[i] } 25 | 26 | map_instances( each, after ) 27 | end 28 | 29 | def test_each_instance 30 | i = 0 31 | each_instance do |instance, iterator| 32 | i += 1 33 | instance.options.set( authorized_by: "test_#{i}@test.com") { |p| iterator.next } 34 | end 35 | true 36 | end 37 | 38 | def test_iterator_for 39 | iterator_for( instances ).class == Raktr::Iterator 40 | end 41 | 42 | def test_connect_to_agent( url, &block ) 43 | connect_to_agent( url ).alive? { |b| block.call b } 44 | end 45 | 46 | def test_connect_to_instance( *args, &block ) 47 | connect_to_instance( *args ).busy?{ |b| block.call !!b } 48 | end 49 | 50 | def test_defer( *args, &block ) 51 | defer do 52 | block.call args 53 | end 54 | end 55 | 56 | def test_run_asap( *args, &block ) 57 | run_asap { block.call args } 58 | end 59 | 60 | def echo( *args ) 61 | args 62 | end 63 | 64 | end 65 | -------------------------------------------------------------------------------- /spec/support/fixtures/services/echo.rb: -------------------------------------------------------------------------------- 1 | class Cuboid::RPC::Server::Agent 2 | class Service::Echo < Service 3 | 4 | private :instances 5 | public :instances 6 | 7 | def test_agent 8 | agent.class == Cuboid::RPC::Server::Agent 9 | end 10 | 11 | def test_opts 12 | agent.instance_eval{ @options } == options 13 | end 14 | 15 | def test_node 16 | node.class == Node 17 | end 18 | 19 | def test_map_instances( &block ) 20 | each = proc do |instance, iterator| 21 | iterator.return [instance.url, instance.token] 22 | end 23 | after = proc { |i| block.call Hash[i] } 24 | 25 | map_instances( each, after ) 26 | end 27 | 28 | def test_each_instance 29 | i = 0 30 | each_instance do |instance, iterator| 31 | i += 1 32 | instance.options.set( authorized_by: "test#{i}@test.test") { |p| iterator.next } 33 | end 34 | true 35 | end 36 | 37 | def test_iterator_for 38 | iterator_for( instances ).class == Raktr::Iterator 39 | end 40 | 41 | def test_connect_to_agent( url, &block ) 42 | connect_to_agent( url ).alive? { |b| block.call b } 43 | end 44 | 45 | def test_connect_to_instance( *args, &block ) 46 | connect_to_instance( *args ).busy?{ |b| block.call !!b } 47 | end 48 | 49 | def test_defer( *args, &block ) 50 | defer do 51 | block.call args 52 | end 53 | end 54 | 55 | def test_run_asap( *args, &block ) 56 | run_asap { block.call args } 57 | end 58 | 59 | def echo( *args ) 60 | args 61 | end 62 | 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /spec/support/helpers/framework.rb: -------------------------------------------------------------------------------- 1 | def issues 2 | Cuboid::Data.issues.all 3 | end 4 | -------------------------------------------------------------------------------- /spec/support/helpers/matchers.rb: -------------------------------------------------------------------------------- 1 | RSpec::Matchers.define :be_same_array_as do |expected_array| 2 | match do |actual_array| 3 | Set.new(actual_array) == Set.new(expected_array) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /spec/support/helpers/misc.rb: -------------------------------------------------------------------------------- 1 | def available_port 2 | Cuboid::Utilities.available_port 3 | end 4 | -------------------------------------------------------------------------------- /spec/support/helpers/paths.rb: -------------------------------------------------------------------------------- 1 | def name_from_filename 2 | File.basename( caller.first.split( ':' ).first, '_spec.rb' ) 3 | end 4 | 5 | def spec_path 6 | File.expand_path( File.dirname( File.absolute_path( __FILE__ ) ) + '/../../' ) + '/' 7 | end 8 | 9 | def support_path 10 | "#{spec_path}support/" 11 | end 12 | 13 | def fixtures_path 14 | "#{support_path}fixtures/" 15 | end 16 | -------------------------------------------------------------------------------- /spec/support/helpers/request_helpers.rb: -------------------------------------------------------------------------------- 1 | module RequestHelpers 2 | include Rack::Test::Methods 3 | 4 | def app 5 | Cuboid::Rest::Server 6 | end 7 | 8 | def response_data 9 | JSON.load response.body 10 | rescue => e 11 | ap response 12 | raise 13 | end 14 | 15 | def pretty_response_body 16 | JSON.pretty_generate( response_data ) 17 | end 18 | 19 | def response_body 20 | response.body 21 | end 22 | 23 | def response_code 24 | response.status 25 | end 26 | 27 | def response 28 | last_response 29 | end 30 | 31 | %w(get post put delete).each do |m| 32 | define_method m do |path, parameters = nil, headers = {}| 33 | super( path, (parameters.to_json if parameters), headers ) 34 | end 35 | end 36 | 37 | extend self 38 | end 39 | -------------------------------------------------------------------------------- /spec/support/helpers/requires.rb: -------------------------------------------------------------------------------- 1 | def require_lib( path ) 2 | require Cuboid::Options.paths.lib + path 3 | end 4 | 5 | def require_testee 6 | require Kernel.caller.first.split( ':' ).first. 7 | gsub( '/spec/cuboid', '/lib/cuboid' ).gsub( '_spec', '' ) 8 | end 9 | -------------------------------------------------------------------------------- /spec/support/helpers/resets.rb: -------------------------------------------------------------------------------- 1 | # Order is important. 2 | INSTANCES = [ 3 | Cuboid::Application 4 | ] 5 | INSTANCES.each(&:_spec_instances_collect!) 6 | 7 | def reset_options 8 | options = Cuboid::Options 9 | options.reset 10 | 11 | options.paths.logs = spec_path + 'support/logs/' 12 | options.paths.reports = spec_path + 'support/reports/' 13 | options.paths.snapshots = spec_path + 'support/snapshots/' 14 | options.snapshot.path = options.paths.snapshots 15 | 16 | options.rpc.server_address = '127.0.0.1' 17 | 18 | options 19 | end 20 | 21 | def cleanup_instances 22 | INSTANCES.each do |i| 23 | i._spec_instances_cleanup 24 | end 25 | end 26 | 27 | def reset_framework 28 | Cuboid::UI::OutputInterface.initialize 29 | # Cuboid::UI::Output.debug_on( 999999 ) 30 | # Cuboid::UI::Output.verbose_on 31 | # Cuboid::UI::Output.mute 32 | 33 | Cuboid::Application.reset 34 | end 35 | 36 | def reset_all 37 | reset_options 38 | reset_framework 39 | end 40 | 41 | def processes_killall 42 | instance_killall 43 | agent_killall 44 | scheduler_killall 45 | process_killall 46 | process_kill_reactor 47 | end 48 | 49 | def killall 50 | processes_killall 51 | web_server_killall 52 | end 53 | -------------------------------------------------------------------------------- /spec/support/helpers/web_server.rb: -------------------------------------------------------------------------------- 1 | def web_server_manager 2 | ENV['WEB_SERVER_DISPATCHER'] ? WebServerClient.instance : WebServerManager 3 | end 4 | 5 | def web_server_url_for( *args ) 6 | web_server_manager.url_for( *args ) 7 | end 8 | 9 | def web_server_spawn( *args ) 10 | web_server_manager.spawn( *args ) 11 | end 12 | 13 | def web_server_killall 14 | web_server_manager.killall 15 | end 16 | -------------------------------------------------------------------------------- /spec/support/lib/web_server_client.rb: -------------------------------------------------------------------------------- 1 | require 'toq' 2 | 3 | # @note Needs `ENV['WEB_SERVER_DISPATCHER']` in the format of `host:port`. 4 | # 5 | # {WebServerManager}-API-compatible client for the {WebServerAgent}. 6 | # 7 | # Delegates test webserver creation to the machine running {WebServerAgent}, 8 | # for hosts that lack support for fast servers (like Windows, which can't run 9 | # Thin, Puma etc.). 10 | # 11 | # @author Tasos "Zapotek" Laskos 12 | class WebServerClient < Toq::Proxy 13 | include Singleton 14 | 15 | def initialize( options = {} ) 16 | @host, port = ENV['WEB_SERVER_DISPATCHER'].split( ':' ) 17 | 18 | Raktr.global.run_in_thread if !Raktr.global.running? 19 | 20 | client = Toq::Client.new( host: @host, port: port ) 21 | super client, 'server' 22 | end 23 | 24 | def protocol_for( name ) 25 | name.to_s.include?( 'https' ) ? 'https' : 'http' 26 | end 27 | 28 | def address_for( name ) 29 | @host 30 | end 31 | 32 | def up?( name ) 33 | Typhoeus.get( 34 | url_for( name, false ), 35 | ssl_verifypeer: false, 36 | ssl_verifyhost: 0, 37 | forbid_reuse: true 38 | ).code != 0 39 | end 40 | 41 | end 42 | -------------------------------------------------------------------------------- /spec/support/lib/web_server_dispatcher.rb: -------------------------------------------------------------------------------- 1 | require_relative '../../../lib/cuboid/processes/manager' 2 | require_relative '../../../lib/cuboid/processes/helpers' 3 | require_relative '../../support/helpers/paths' 4 | require_relative 'web_server_manager' 5 | require 'toq' 6 | 7 | # @note Needs `ENV['WEB_SERVER_DISPATCHER']` in the format of `host:port`. 8 | # 9 | # Exposes the {WebServerManager} over RPC. 10 | # 11 | # @author Tasos "Zapotek" Laskos 12 | class WebServerAgent 13 | 14 | def initialize( options = {} ) 15 | host, port = ENV['WEB_SERVER_DISPATCHER'].split( ':' ) 16 | 17 | manager = WebServerManager.instance 18 | manager.address = host 19 | 20 | rpc = Cuboid::RPC::Server.new( host: host, port: port.to_i ) 21 | rpc.add_handler( 'server', manager ) 22 | rpc.run 23 | end 24 | 25 | end 26 | -------------------------------------------------------------------------------- /spec/support/lib/web_server_manager.rb: -------------------------------------------------------------------------------- 1 | class WebServerManager 2 | include Singleton 3 | include Cuboid::Utilities 4 | 5 | attr_reader :lib 6 | attr_accessor :address 7 | 8 | def initialize 9 | @lib = "#{support_path}/servers/" 10 | @servers = {} 11 | @address = Socket.gethostname 12 | 13 | Dir.glob( File.join( @lib + '**', '*.rb' ) ) do |path| 14 | @servers[normalize_name( File.basename( path, '.rb' ) )] = { 15 | path: path 16 | } 17 | end 18 | end 19 | 20 | def spawn( name ) 21 | server_info = data_for( name ) 22 | 23 | 10.times do 24 | server_info[:port] = Cuboid::Utilities.available_port 25 | server_info[:pid] = Process.spawn( 26 | RbConfig.ruby, server_info[:path], 27 | '-p', server_info[:port].to_s, 28 | '-o', address_for( name ) 29 | ) 30 | Process.detach server_info[:pid] 31 | 32 | begin 33 | Timeout.timeout( 10 ) { sleep 0.1 while !up?( name ) } 34 | 35 | return url_for( name, false ) 36 | rescue Timeout::Error 37 | kill name 38 | end 39 | end 40 | 41 | abort "Server '#{name}' never started!" 42 | end 43 | 44 | def url_for( name, lazy_start = true ) 45 | spawn( name ) if lazy_start && !up?( name ) 46 | 47 | "#{protocol_for( name )}://#{address_for( name )}:#{port_for( name )}" 48 | end 49 | 50 | def address_for( name ) 51 | @address 52 | end 53 | 54 | def port_for( name ) 55 | data_for( name )[:port] 56 | end 57 | 58 | def protocol_for( name ) 59 | name.to_s.include?( 'https' ) ? 'https' : 'http' 60 | end 61 | 62 | def kill( name ) 63 | server_info = data_for( name ) 64 | return if !server_info[:pid] 65 | 66 | r = process_kill( server_info[:pid] ) 67 | 68 | if r 69 | server_info.delete( :pid ) 70 | server_info.delete( :port ) 71 | end 72 | 73 | r 74 | end 75 | 76 | def killall 77 | @servers.keys.each { |name| kill name } 78 | nil 79 | end 80 | 81 | def up?( name ) 82 | return false if !data_for(name)[:port] 83 | 84 | Typhoeus.get( 85 | url_for( name, false ), 86 | ssl_verifypeer: false, 87 | ssl_verifyhost: 0, 88 | forbid_reuse: true 89 | ).code != 0 90 | end 91 | 92 | def self.method_missing( sym, *args, &block ) 93 | if instance.respond_to?( sym ) 94 | instance.send( sym, *args, &block ) 95 | else 96 | super( sym, *args, &block ) 97 | end 98 | end 99 | 100 | def self.respond_to?( m ) 101 | super( m ) || instance.respond_to?( m ) 102 | end 103 | 104 | private 105 | 106 | def normalize_name( name ) 107 | name.to_s.to_sym 108 | end 109 | 110 | def data_for( name ) 111 | @servers[normalize_name( name )] 112 | end 113 | 114 | def set_data_for(name, data) 115 | @servers[normalize_name(name)] = data 116 | end 117 | 118 | end 119 | -------------------------------------------------------------------------------- /spec/support/logs/placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qadron/cuboid/242af377b6196640d0901f4b0b9a145bbc445b82/spec/support/logs/placeholder -------------------------------------------------------------------------------- /spec/support/pems/cacert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIGaDCCBFCgAwIBAgIJAPBYwqAog9f1MA0GCSqGSIb3DQEBBQUAMFMxCzAJBgNV 3 | BAYTAkdSMRgwFgYDVQQDEw9BcmFjaG5pLXRlc3QtQ0ExKjAoBgkqhkiG9w0BCQEW 4 | G2FyYWNobmlAYXJhY2huaS1zY2FubmVyLmNvbTAeFw0xMjExMDIxMDIyNTNaFw0y 5 | MjEwMzExMDIyNTNaMFMxCzAJBgNVBAYTAkdSMRgwFgYDVQQDEw9BcmFjaG5pLXRl 6 | c3QtQ0ExKjAoBgkqhkiG9w0BCQEWG2FyYWNobmlAYXJhY2huaS1zY2FubmVyLmNv 7 | bTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANHbVRqyD7FT8KSCZLm1 8 | YrOwMj78OXRjMb+ucPR+fncXR1TEuPlEFojLtzqtsaA5OJXpj+0toFP+2T4DeFlu 9 | YT4TBrRQ3DxmeY2b5b6ZuurLMfMc1vU+dE92Tj4nuInBz3Z09aDQ8ZWXWzJ7uD3J 10 | eNjOcj7Jc94AGFx05Tii9VriUmX+jXQALv5S5WGVtKt4p67mLm/2xD4JhS+a0+B8 11 | s/Xo7l6EXaFeTsH5jgZDiY+f0Dpk6cM+pZ5AJVJiNonDJs8/nl9vdWPRH40GHsyN 12 | H0/lo/wTxuth/TvX3DBB5hTi/9V5eYbLTLtE1oyXgBxNKrjDu8FVn/jUl8DAIdJL 13 | n4vXwXI70wLyS6aF8uu9ApNnmSbTTc2scAKDmnlINLHqGGlpyleojqphDy3MfQYk 14 | YT9QSXNKHlO4NMLFqrqM1F3hvM3MEteeM12gAeeJLY6YYYJafMMMh/e7kK/y5u+J 15 | ype5t5N8GKskSRp0RvlRYfoH/lnyJd6FEyh9P0QHA4CKAadZBCfOcmwmTY/G0Kjn 16 | 3Y7r7BmJb4PIEcDqUjXwuyq6ZHjx7sawuGG6eXhIGln0JtSymy4j+h+xlh0S7O8B 17 | Ti6dMVxg+DNTkEJz2O00IIBcyToqZ26XovFkN5ueRNOROB3YVpldmpbLyuOQae/D 18 | 4Gc21bEWoR7OAaY2PRl4r563AgMBAAGjggE9MIIBOTAdBgNVHQ4EFgQU8VOtNUbZ 19 | rnbX0gRWIds5yaT+uCwwgYMGA1UdIwR8MHqAFPFTrTVG2a5219IEViHbOcmk/rgs 20 | oVekVTBTMQswCQYDVQQGEwJHUjEYMBYGA1UEAxMPQXJhY2huaS10ZXN0LUNBMSow 21 | KAYJKoZIhvcNAQkBFhthcmFjaG5pQGFyYWNobmktc2Nhbm5lci5jb22CCQDwWMKg 22 | KIPX9TAPBgNVHRMBAf8EBTADAQH/MBEGCWCGSAGG+EIBAQQEAwIBBjAJBgNVHRIE 23 | AjAAMCsGCWCGSAGG+EIBDQQeFhxUaW55Q0EgR2VuZXJhdGVkIENlcnRpZmljYXRl 24 | MCYGA1UdEQQfMB2BG2FyYWNobmlAYXJhY2huaS1zY2FubmVyLmNvbTAOBgNVHQ8B 25 | Af8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggIBAAAAebhmNydIUT+sZpvWM5JKNQdq 26 | YtnghnpZyM37cxLmFIeDKKPTw/3lhDViRRqQaaFTI7ukcqgloPmhMuymQT4o11Fw 27 | XLoQuIeSgS7Mg6eZF3CUKoEy1KnlluegDXtDI+WH/EQHlokBvoMaClltj6vsfqI/ 28 | K9K2MAXUKi8K7NRq6VYO1QPtrBfrX9VmLyndbYm8lSG5oDkGGh8NjVgHHZDgrQAQ 29 | 2EmsWbE9yMZ6yl+AaaE5XrbPWnBI8rK77WP93JYVAhmcaQiJzPfMw3sb2QojKdak 30 | 7fvJzAjBeXAoTP5Mu/E+BPPgELzB/DnRaWlrYsAQeRZBV+I3+k5CCVqdOAdJCk5Y 31 | dFNTppHfwVaDj5qKOmodzdUDcDL6ynl15t6WHgj2yBwsDVpWsvbqyitZkemLFwrf 32 | hAedR3dKr+IxrOynST1w4LorDorcGK/DqhD475bQ9EDel5nW18hotUeeeO+K3qc7 33 | iGgj7zTKfmhzL/KMotir/SQYYxQbbtLkkhXDaNVlWiYkHotOzrNbpKAFM776CI47 34 | KTXG88FydcycGHYU8SQLEbEDiowAikr2u+CUHKrJvz2Wr8jkWaMCSfgCyokcY6vR 35 | IIqnrpYHhX2FmKf+tRB8o3KXM6uiOSUvaW23LHcs0OKqcJ0XHOkhTMDFZ82eDZzl 36 | CXJQkVNhmc0Y9prF 37 | -----END CERTIFICATE----- 38 | -------------------------------------------------------------------------------- /spec/support/pems/client/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIGazCCBFOgAwIBAgIBAjANBgkqhkiG9w0BAQUFADBTMQswCQYDVQQGEwJHUjEY 3 | MBYGA1UEAxMPQXJhY2huaS10ZXN0LUNBMSowKAYJKoZIhvcNAQkBFhthcmFjaG5p 4 | QGFyYWNobmktc2Nhbm5lci5jb20wHhcNMTIxMTAyMTAyNDA4WhcNMjIxMDMwMTAy 5 | NDA4WjBKMQswCQYDVQQGEwJHUjEPMA0GA1UEAxMGQ2xpZW50MSowKAYJKoZIhvcN 6 | AQkBFhthcmFjaG5pQGFyYWNobmktc2Nhbm5lci5jb20wggIiMA0GCSqGSIb3DQEB 7 | AQUAA4ICDwAwggIKAoICAQDEl91flpFH2Y1b2voSsjYDEiC70ArJWrmgm/UFdtXT 8 | pCng9ACjJWuf/evBOa8SKmNFgM1NKMF7GaZghDid3npq4Pdz/C74Ci8Q3ORMPC25 9 | Cq5T8oLYr7OGRQG7cmAqq7fP7MbPikEoaV3sg9CYdCqeK5WqT4+2eWvJGZ6t3z/g 10 | A1WYoHMbhXS1MedPlJIboSUcUlvf2BEld4EzwjCAlF7IICAT37ijtlDqVZpByfii 11 | xj47wP/fx7AtHe7tog9h1MgM0ciJgvH9TT3Kc/iSHYwrhS6jcnlwju7P1YkaRjjO 12 | ODIt7HCf6ScMmK+XIJZRuI7I4zWSSUKI5vgj5BKtlyj71xHRcKX+wfUwhz92BwwE 13 | 9kakl+0L4C11aWKHsYpT8J3NFH8soFpyUPmVQTDiNDcmbxQYHuZbSdWG5+XLYrLZ 14 | MJ8am9HB7Z9uQugyZxki/AgQyT21eCJBrg83A0zd+SYezsDA/SVEZXCCrrO5/u/f 15 | 6+RGYZcWXaIk+18uai5ax0HwksN9AnIIYyH19Fkq516a231Hy37/RKn2W/vH7A/J 16 | YdleDe1wqeIApDC/jJncuO380BdzvvwbhTkp3KKjNcx0B9d917q+pguzotiVR19n 17 | /MyIZ2/EU6r+AGyt1jxFZQhbgL6ayVlMgflyCTcsfgN7kC6dW+7L5gfBt3MwoXgK 18 | lwIDAQABo4IBUTCCAU0wCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBLAwKwYJ 19 | YIZIAYb4QgENBB4WHFRpbnlDQSBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0O 20 | BBYEFNDF0OQgaYJqWYB9onMoDJrI6sEHMIGDBgNVHSMEfDB6gBTxU601RtmudtfS 21 | BFYh2znJpP64LKFXpFUwUzELMAkGA1UEBhMCR1IxGDAWBgNVBAMTD0FyYWNobmkt 22 | dGVzdC1DQTEqMCgGCSqGSIb3DQEJARYbYXJhY2huaUBhcmFjaG5pLXNjYW5uZXIu 23 | Y29tggkA8FjCoCiD1/UwJgYDVR0SBB8wHYEbYXJhY2huaUBhcmFjaG5pLXNjYW5u 24 | ZXIuY29tMCYGA1UdEQQfMB2BG2FyYWNobmlAYXJhY2huaS1zY2FubmVyLmNvbTAL 25 | BgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQEFBQADggIBAKOrRHhird4fhaVMHyNdTqQx 26 | tMuqYWrPb+L4vir7Gu9bfNXlS/b5QBBeFbg5YwVeMx2KTdJR9u3XK9PaQQCC0+ak 27 | tDy3beasiFvoThWmzaVLnPR2OpdRbbK9oFZuK7Y7Sdj6hFapbPcS3hG1M7uWtmiF 28 | Cwd3hmXzvyOH7DToQ5wRAtS3kE2I5J9kH8OVP/pIZ/OTnfDTHOilznOMmlJ+LuFh 29 | ECgxIFv9GRAb+J+AxFWBFNgm6yv6cGjAnT2rZR2B2WyKpmHs+mIxqmNSYldublKi 30 | OBVl13M2ETH9NKRkvNjYbD4nsZGWO85zGiv0AVaZxwEfIHuXQdWfSIhxno3UQpxJ 31 | UlCZTpGJ0wvHHrHC7GQZCCdr83Zq8MgcMgCuE78qNbYIE+rf6+MOhnxgg0sJ12Yr 32 | +++VrQx5HDOoChr39rFISRpi2vFxz5QsJONoNLvH0uJoxQ5UDlOXqeq6TlJBBNUk 33 | 77IkxGfqU7XmF9uT8nmcU4hx8yhKFJOZ5ORHX+U8+yaQN8RsxQadF7OzS1MMQXkl 34 | UwIeFEQNNv0JOIkjZILIDHz++PL5Fa+5/lgAV24X68ZIQ5FkgoLoOIWgbcARlsj0 35 | 62BJLPhrIbCUI7XDos+hTmMPWkzfGoIahL1mEMshlrKK/XGIFh9l1iKnI/4ze1bH 36 | 9nvAl0/txVluZtYXlf2E 37 | -----END CERTIFICATE----- 38 | -------------------------------------------------------------------------------- /spec/support/pems/client/foo-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIGzjCCBLagAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJGTzEM 3 | MAoGA1UECBMDZm9vMQwwCgYDVQQHEwNmb28xDDAKBgNVBAoTA2ZvbzEMMAoGA1UE 4 | CxMDZm9vMQwwCgYDVQQDEwNmb28xGjAYBgkqhkiG9w0BCQEWC2Zvb0Bmb28uZm9v 5 | MB4XDTExMDkyNjE0MDEzM1oXDTEyMDkyNTE0MDEzM1owgYMxCzAJBgNVBAYTAkZP 6 | MQwwCgYDVQQIEwNmb28xDDAKBgNVBAcTA2ZvbzEMMAoGA1UEChMDZm9vMQwwCgYD 7 | VQQLEwNmb28xETAPBgNVBAMTCHNvbWVuYW1lMSkwJwYJKoZIhvcNAQkBFhpzb21l 8 | bmFtZUBzb21lbmFtZS5zb21lbmFtZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC 9 | AgoCggIBAL0r5N/tveIx+Mvuwx5R1rFuK6I8udjkzj7N5zFs/JNsHA/9pVfEX6Y4 10 | NPlER/0MvPUNfX6quuAN3IdEW1g7ond/fuI4iARY/TSgPipIn6jk4v6Y2A6nOmUp 11 | O/32JWZBcM3IF0qMFGfYOq5+D20iWc4l/wPkcHz46/x8Qx3bnncXJix2M/AS9RwN 12 | Kt26Ue5tG8sPY/ckMsvMbfMQ+R+15k1x6RAnBIHeh8QsFlDFBGT+EmKzDgCI41zN 13 | akKiS+ZvJy2byKYT/r0P47Rzx6YP52+rIVh1SlkWwjD6EbVcnvDcW52rDrcqtsSg 14 | uqiZfr+2Td+iOQBtUS2Y5htcjdipRaVLLoCY0qwX6i4JKGiL6fPVTgIUReZPFdk7 15 | CHVCVAAEDl3yGLe6wpqGonKk6JjBZByiW3EE9T8f3uxS3pxD6BitpVCzwYiAJnFr 16 | DvV0xNBLriSJ+Ebcc7f0ng+L7CPO9C8ILX+vIfToURhrup4+vn7W4pyft3b588O2 17 | XBcax+XyBn1aQN6pwZrT/9wv85J94A/WBYkOvPl3Rv2uwjlzwfR/owLVPJkbowgF 18 | iUGQz9vl/0C+KcWkyjnXKXxq2EK7kszBH5m3vgDz2j3mMBGv0xfTZmDVM+Qs12xv 19 | wMwIfsOxo1oMmI6mWSx3NsJqP89ui650WAqORsWZ6O0jVv64tlnXAgMBAAGjggFe 20 | MIIBWjAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIEsDArBglghkgBhvhCAQ0E 21 | HhYcVGlueUNBIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUililQx5n 22 | hJGu4Zx3C8kDJEU08ogwgaEGA1UdIwSBmTCBloAUX93JoFY2qeY+HVPZ+jAIbA/Y 23 | rkGhc6RxMG8xCzAJBgNVBAYTAkZPMQwwCgYDVQQIEwNmb28xDDAKBgNVBAcTA2Zv 24 | bzEMMAoGA1UEChMDZm9vMQwwCgYDVQQLEwNmb28xDDAKBgNVBAMTA2ZvbzEaMBgG 25 | CSqGSIb3DQEJARYLZm9vQGZvby5mb2+CCQCmSX0QNZkjADAWBgNVHRIEDzANgQtm 26 | b29AZm9vLmZvbzAlBgNVHREEHjAcgRpzb21lbmFtZUBzb21lbmFtZS5zb21lbmFt 27 | ZTALBgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQEFBQADggIBAFlzE1hgfZ6Zhl8RpckD 28 | 0NDEZFAij+gQl91gcgAnkOE70JxbBiW3LXtdQvdNNpOUUdi10+UtbRyM1XayS7tQ 29 | v/yHvfTINtMXazDQiG+ClcCSapjAFhMJhA7HWMtZLtzXb/Pox2JciGdilbyPeHTb 30 | xitlun85TuTSseOkriND8OnjFolOBb/713adKE/p/UEbqZ9vbMyMgxOgemdPKKSB 31 | H8k/mdQtMbcK7Q01Z/3UMDyxiVoQk2x8SqG4NxKKTRhKN4U8DKCGO8b4awgQo+je 32 | mc01hW60ScVhBR+ha8NkvmReUm/pvIrYdaHnyJxwlY+RjIn2Y3OBlvBHkOVBCTJo 33 | MVHqVQGksEQH/U+zlcWrv8H/1JOhdnq4lXnWkFvOBRUVSXF3vw9q4aMYMB1bagAU 34 | ykciVW367xwj0HMzfAKT09uo0BhyqUuLec4/ksOeDGxLRn5KdrVwAp6b+quBHbkx 35 | BCGdLvBVxgx11E+YV+WbY0pvNNdqpsKq9oZxOLnTQek8YJ317WSzUeUPOT5zu4sM 36 | /B/i43wDiFWV1EcV8gEAy72l7jXi3++JbN56Cd03DML/BxSmfWXwrT3hKDP9mG5+ 37 | X4N8iCT+NBIMDYX1EAiBfD7ioxDpIv5yu+WNhTH7qsDPAE0Q3aTd1Un1Lkn7F/51 38 | bKDKa/6PCJlZOkZGTU7wxaCy 39 | -----END CERTIFICATE----- 40 | -------------------------------------------------------------------------------- /spec/support/pems/client/foo-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIJKAIBAAKCAgEAvSvk3+294jH4y+7DHlHWsW4rojy52OTOPs3nMWz8k2wcD/2l 3 | V8Rfpjg0+URH/Qy89Q19fqq64A3ch0RbWDuid39+4jiIBFj9NKA+KkifqOTi/pjY 4 | Dqc6ZSk7/fYlZkFwzcgXSowUZ9g6rn4PbSJZziX/A+RwfPjr/HxDHduedxcmLHYz 5 | 8BL1HA0q3bpR7m0byw9j9yQyy8xt8xD5H7XmTXHpECcEgd6HxCwWUMUEZP4SYrMO 6 | AIjjXM1qQqJL5m8nLZvIphP+vQ/jtHPHpg/nb6shWHVKWRbCMPoRtVye8NxbnasO 7 | tyq2xKC6qJl+v7ZN36I5AG1RLZjmG1yN2KlFpUsugJjSrBfqLgkoaIvp89VOAhRF 8 | 5k8V2TsIdUJUAAQOXfIYt7rCmoaicqTomMFkHKJbcQT1Px/e7FLenEPoGK2lULPB 9 | iIAmcWsO9XTE0EuuJIn4Rtxzt/SeD4vsI870Lwgtf68h9OhRGGu6nj6+ftbinJ+3 10 | dvnzw7ZcFxrH5fIGfVpA3qnBmtP/3C/zkn3gD9YFiQ68+XdG/a7COXPB9H+jAtU8 11 | mRujCAWJQZDP2+X/QL4pxaTKOdcpfGrYQruSzMEfmbe+APPaPeYwEa/TF9NmYNUz 12 | 5CzXbG/AzAh+w7GjWgyYjqZZLHc2wmo/z26LrnRYCo5GxZno7SNW/ri2WdcCAwEA 13 | AQKCAgAtE0h2EzArtbyS21Gi591Aaf/7qHygMeTzEh0EqfOFLT2Cke3T+/bOpJX6 14 | z0pYAbEEszIz/HqK6MKjeKZLX+pWjwqs3MuMPagX4hbt/GAO3ns4LzNMSoyfjjO9 15 | T+mZc/5tCkCCgt5Z4CwbZ+5FEupTNLqPMt+a57VTdEiJ1MrtlZaDAadejSXaiUit 16 | jLf5GAaHXCN1wCpaZVvNjz3NUIqP6ZSfRKzORTgUhmzK0ic56VB4NDIh7nw+oLOx 17 | LPro7ZNoSraE3D7WzfJ0DSKFb0S2VxCbA1ez4rhi8zNFA8zbl6Y864WWH5dtQt83 18 | TVvmUoo7v2vVrjappsUtv+AMwEjXsRlEH+tHEuoM6DcSXP0P9ZV+l+F/rz2P4k2x 19 | 313rZ1Yh9clgXSuVw4yxGnHTGtYT8v5pwynk55goZxzqLICHwl0UzjG2fQxmhWq7 20 | xw5Mrttw/kfKR8O+kdaGay3PvPeEZXDI0aWajZuVdQJrV/2pZ+2ucMH4dn6hznmG 21 | 2lM+LnGDrF9Y8fMXxN8Q7ooPQL23GXMCJBoYLoTECBnxuhz8r+UuZRxEWZTM4L1X 22 | 8iNxSgK9cJbwr8EM/awMGGnCFhhv0CvEhC2/Kwq9koitXecNJw2f2Z/VAJR0EYT4 23 | hZdRVKkeWOm8cOUeE2oNeCGF0loz7+BFNBsdcV3jQj8x7oLYAQKCAQEA5dgkkYX5 24 | LCDSOzCkGNxhc1LLL5qECY1TGov7mn89pRktVFVA1VxAd4Mw5z4QoHAb5fTqi4Dt 25 | dprJNySLclETyvCgFV1noi54sswThLyUKyHAYzu2WhWiU0artr0hGBKegI/0C1LP 26 | qMoiC8ywlu+GA/ehBDzKMXUQDSNGHWEuk3f8ND2iWAatpP2A5EedGbGI5DFU0GJ1 27 | v17w1o4Rro1DERXpbB5hLkxnL1Tqqgfk5ISweOwidjlNNaoTsW7/s+fEwoufG1MK 28 | vu3+f2KGqUFYWVSmzeHNxKVYspUMWhCTfLKPL5mt18iJyNcyRXi8NGTCeJT1jELc 29 | +FbmqBFEqcpA3QKCAQEA0rLdMH1fBClcjecRwomv+owx8+GOEBH7HNqSGOI415ac 30 | fIdMTAYKmQyOvRfy8AQF1UegrZdVkjt7QFxnvw3E1o+Wupy1FYcgS+muwsCRKEi4 31 | tpez7SdsW7/pDPuvILq4+RqwUkLhQtYgEeYNv0R1D2RSMLfmnJNJepwjjLYNX9+E 32 | 2vJcj28559CwDmB0KkOk/bqSSnsZnCc4sv2y+4Whe6Kirz0Dom8tmS82pc66dRW4 33 | WNdSSQeI+srKOZPYBBXzZPIQ6C8ahYwbP+F6p87xmzQ8iGAC/Yx+IgGd7zhN6X2Y 34 | eCRMb5avZMwjhIgoaNcUvHreHdA2ENwt0ta1HL7gQwKCAQBPKJi3kceWPhuJjSAG 35 | ++eIG0ylMXcl/wlPDET0Gbx2XuxwrgftM590SbeO/J4nU1UYZrhcoWOnRHnmRuzL 36 | y0agzyyjDw9BGPYyxfw26+evzyj+RDNyZR8JxT4gapS6QLdcGbf4KmYggLUnETEE 37 | WqvyM9e9qN4OGH4VuD9OoQi0e09eApgl7u7g8vnwJQXjMnSt+pt2RWK+LQmPK1DF 38 | qBsgCEbjGLdphaDH+Vv0gVGArn5EtzbLNsLzp4auxkbEhB8MzZ9XfsslpLvQzXMJ 39 | Wdr+sRvdrlX60uNwZcriPfE9shWEVE4+Ee/6Prsul/1hog4kD1FeJC0MTomT+paG 40 | T4T1AoIBAQChDaWzffGWLcOYmGrj5/lDK8y3Hc9Ii2YGPTB1ot5ONMrzCTyR1ABf 41 | 0tB8zASf4INQ7wpsBoSbXaotnTSUPoMaevF9PXHRvdM7E6nJJgcO4t+GetlGyt6y 42 | FzSd/vhzrSbdCsCCcKrdOu2SoOYbMMnF9So6ISg+wPrmpNkrorEAesuDzMRhw2Rg 43 | xQz+QE4rTD+ezvEpy4Tc7sIRV4lrZ07zQXLXPm07yX3yXLuJ7EZsXyjlh9lXB1J2 44 | WXJQpqj1Ho5IuBuiTvcX4+ukXccy5CWcPEaU+8btZmm3tsKxa7lqY92a4CwpE+aH 45 | xt7TXHx+/wDTvZtw8ImTQ6onQoV4LpMfAoIBAGqZ9HyADbomllKHw+Ks5oL66EYk 46 | IozJZ9I8Zw3pX+WBlG8z0KFlcVBfZjmcYOfH+tCouj/JQkhSaw1a1r7VU8WDu103 47 | KRWAMAlu9H9kuBa0NhS+iUv2qpFSH1ouAF41jufRFvd4hJW9NAzErI3Gy+mgFqUf 48 | D5HKDSX5PGIQqmtlXh9EZGAb9PzbV7YVw/QC9KRwjSl6KZbAN0yd9iBcht6JVL6m 49 | kxUFNvTqBwtck0h1gPlOBBEfm4WohOtlKcgZoVfNfZxP/9O1Eo8DnYLcJFv8w2q3 50 | wVNX5W5yqJcUQ9kTXrQ26XRHXLxQpW3f+fllypdw3Oh+N8+B9LUhJoACVCk= 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /spec/support/pems/client/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIJKAIBAAKCAgEAxJfdX5aRR9mNW9r6ErI2AxIgu9AKyVq5oJv1BXbV06Qp4PQA 3 | oyVrn/3rwTmvEipjRYDNTSjBexmmYIQ4nd56auD3c/wu+AovENzkTDwtuQquU/KC 4 | 2K+zhkUBu3JgKqu3z+zGz4pBKGld7IPQmHQqniuVqk+PtnlryRmerd8/4ANVmKBz 5 | G4V0tTHnT5SSG6ElHFJb39gRJXeBM8IwgJReyCAgE9+4o7ZQ6lWaQcn4osY+O8D/ 6 | 38ewLR3u7aIPYdTIDNHIiYLx/U09ynP4kh2MK4Uuo3J5cI7uz9WJGkY4zjgyLexw 7 | n+knDJivlyCWUbiOyOM1kklCiOb4I+QSrZco+9cR0XCl/sH1MIc/dgcMBPZGpJft 8 | C+AtdWlih7GKU/CdzRR/LKBaclD5lUEw4jQ3Jm8UGB7mW0nVhufly2Ky2TCfGpvR 9 | we2fbkLoMmcZIvwIEMk9tXgiQa4PNwNM3fkmHs7AwP0lRGVwgq6zuf7v3+vkRmGX 10 | Fl2iJPtfLmouWsdB8JLDfQJyCGMh9fRZKudemtt9R8t+/0Sp9lv7x+wPyWHZXg3t 11 | cKniAKQwv4yZ3Ljt/NAXc778G4U5KdyiozXMdAfXfde6vqYLs6LYlUdfZ/zMiGdv 12 | xFOq/gBsrdY8RWUIW4C+mslZTIH5cgk3LH4De5AunVvuy+YHwbdzMKF4CpcCAwEA 13 | AQKCAgBskz8VAtA78WAL4hWI69m+um1PrOe4Kx4oINoi6W3Q1HK4paoQcKNPGmrx 14 | LY2OJ9Dp2ugH/EFXXmQwG/Y04mGT06l26kl6fg1eb9C6deX+s3JyNJalW+x3dHcv 15 | ckzAZFRBRpDKoJ251u+jp202Nbov3vxqskQ50DeCOl8Twh/B9bV5dOv9wCgjxmrs 16 | 3a4QCmC3kpjhOLDHk4fM8SveZ5MtTejJcR7Fc/SeZyapvQVPyNMoJ0Bp9BxN3qFN 17 | ptp9+ol94wKxR5ukfNtqi8A12pHGm2iVpqyBSL+GE3YEB9JpukmkaVgOSTNi1pTr 18 | j3jVq2tYcXvtzf4sI/vZvqW+L6TW8hGwCSORF9H7etCB+56jdUxPzWP2DJtFuD+N 19 | cCuw4LaxmBJBOuwuyXpwoxvmJDeSYdUKayA3jgxSyuJ6DY5ENVa4k9yM7IfYFsgC 20 | nT0mZJmnXzd3MMMTV7APlYjusdnEL2g7GhL2Jy3+RYgItgSPejraC+lPKJOxb93j 21 | hcww9sRlCdp+jiNXUbmJcBiU0CLVoJmjmigAje5NmKDG5oM+puhW5G2oHVHtmL5D 22 | 4myOr7KxGaId+UKqhEYjMYU0YOSlhpMgIdamZsQrz5Oy/NnjOCZf5c10LR+xBiEY 23 | x1YaGvFdaiX+SOHf1OYLm/LLivyg2e16JY/AGlUizrlBvAnlwQKCAQEA5/NhJYtu 24 | y4PU7XwfcOwz7hr1PHp/tFLEfQ/JpOogLNe9+xeUnznrrnprRtm36CDTp/KgSgIv 25 | bwiwsKADcrmaepiytCHW7yFY3soQI+u+xdCvJjkD7dVlY4e1PxxPfghpvuz0ES60 26 | 8TqiVAY3bmrie4oIPhU7T2fmaawTwmrDjHZQOBtHOGIYR5LSJNrS1t18PtK02CzD 27 | HlYIzF0vpck7LzUnZhNoUvVlfuC3HCKY6WshC+kBJpeSqtBMUPDxbqmgSKmve1rI 28 | MsGfEeo1Fqq/VY5gMDkwcrV24BTuHhXGInxmamhCf/0bAXyXR5LJ1nB/TjzFzPsO 29 | VjurhFuzhb1+dwKCAQEA2Pn/s+Pd41aw9OVqmf7rnngiWVH5ZH2wRM1Q9A3Q7TO8 30 | FmK5vYskszjotpm5vzaUAecYMSiWuQrjOKgtoUBHXSYSRxAfqih4UixeW7UbYLsI 31 | 4ZJCgoLeH+mwiNowYPxhRrWWxSzpyWiK8Ho+oUJGnZSrTx2ZJutyPTLWDSeWcePW 32 | hBCGIcBmqyUjgH1+8+W7WWbvdtJPr22R+c7SDNfr6x1CQbdxV65EV/ZA47iaRhWA 33 | EK+2gXGrm/zPmbzmAoMT8e3Bw7IjziZLgmoGiBOubncZkRBFgg6kbiK5iq4hCvju 34 | u3S7zw5flTKfNBdg6L3iECZdNnx7NhAa1x+pX/g84QKCAQAJ17UOn80Sy8RUU4kO 35 | BKfrea9gYp5aq4x09h+LZPf6jykbp5OB8jZDECTPO9vm+MWigdQ1b8RNhOPiite6 36 | nY+llic3J5x0R0j7Iz3uGEnfIQzdpVu6UuzbfV2+kNf6tCawmGN++ylodyF+SUk+ 37 | 4UA9F81jvQjoRLNtVoT8IgWsRzT+PIIYLWl0WBGcyMBbp8hm7hAIgFXDSslSyr2D 38 | 3ncpeKr0VOx/YLRu3uBGTF2KwiYD2F5ZIeNyZiZm5OKJ3J7VloXAyUhnhvnWC8c2 39 | 8AXwfnsscLDSnUqvFZRJKIlg20CJUWVJAxeLR5svyVXRSLLOQhvup9Si6iNMGYwT 40 | p8ffAoIBACB933K6zsF6e+lQaZRB/lqutsYVZnlL8Rd8f4DsiLrcom/fvNzLd2V3 41 | QjIF9zDRJXzbdF34LAntvXUAikS5cXZbeyU1HujGQq5bgo3NIxprJ7tPwbH1kvnI 42 | bu0vb0s2wAJssvU38pJ8m0HDrJlNaXGzj6u7TPHtFCBh+nfh07+eVMNWDiADxdwf 43 | JZ/aKyau4k1TAs0SVWh5ygBUnlPaCbQdn4xjwg9VQ1rAv5raTA/urEzTY0sjEIW9 44 | aAEnrnH12wjBh+CcToxSY8BVzECYYeic4TE99IqzUqBDhvFjfgM8n9NmebFZ/6pB 45 | +GE+lv6DYNT2ScMdBP5ljv9j+GMss0ECggEBANSEzoqtnYKN+Ar4Klfa4VGa8Yy5 46 | v0EpIr5IqZ2dEMZWoXgxZthb1R68RD50tKI2X05Xrsw8gMlPGl5L0HkfCPdaGGhi 47 | +Hrn4ckeUId+NlKfxxdc5bISARP9vV/jdlJ37QOtm7LlLdhHw97Y8tN24E+N6gN0 48 | CjXGOEkAW/qT6wg7cFJvl8Ov6FTZrOCSxmzpwNaH04CM6G7lEWB/p/GiXSIULwZz 49 | UEuwDbRl+UnDihtR2R5E2BKbXSExFumCm+/34cGIFsd0+vc2x/r74R1FXxoF8Iwc 50 | OD6LesfpcEtvc1OckXup1WgQWdef3ESwkh+HRW+JhHCgsy7doLuJ4BfP5VA= 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /spec/support/pems/server/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIGYTCCBEmgAwIBAgIBBTANBgkqhkiG9w0BAQUFADBTMQswCQYDVQQGEwJHUjEY 3 | MBYGA1UEAxMPQXJhY2huaS10ZXN0LUNBMSowKAYJKoZIhvcNAQkBFhthcmFjaG5p 4 | QGFyYWNobmktc2Nhbm5lci5jb20wHhcNMTIxMTAyMTAzMzQxWhcNMjIxMDMwMTAz 5 | MzQxWjBNMQswCQYDVQQGEwJHUjESMBAGA1UEAxMJbG9jYWxob3N0MSowKAYJKoZI 6 | hvcNAQkBFhthcmFjaG5pQGFyYWNobmktc2Nhbm5lci5jb20wggIiMA0GCSqGSIb3 7 | DQEBAQUAA4ICDwAwggIKAoICAQCz+OAzIMwVNujjweU3Zhvk0ZGYqTdJ73Jz5v7F 8 | O5JE35hTzy1kf6EgMEqmZqKeBe95fviv+7UXcqtYoyxFPB/ssSLeUMyO0fUZZMMi 9 | 8EfqtZZveB1j7f0EfzSEG7eL5tCjb/Q8tXQvM33+0QfjnwcrSOr2fPPwbEiWU1d1 10 | cP1ZdRtgvAuo3a4Sga9xtJSFRe3iSpynJsQo482rSPpIcVfm5tArq8RVlztJPvXY 11 | A76iIgNddhdOM/3fBg+a6iXqkC7X0WPRoNt7XiVniwBIP1t5/2S7JyhRsN50Crfu 12 | 9N6lZd/fij0xwr5hLzl5lXKdUI5iYZZfTpNiIXnUICswFOUHwV/2W8XADu1HHLeX 13 | a4knqnmX0h7c+bJm3UrXMiwU61tVENogjNc/vJP9fjy/Klfvgn5BXmpyTze7Xzlj 14 | JcTt0VNNKUdieLjJsVKVF42j6RH9farshcHcCzd4701WkMEsbHZYncMUYNCBqUg7 15 | 5SuIFHIZfZ4wq06i8BLRXJRHK+P+kDp/cyJyr/UqtlJwFJIgtWYOO0SMhynBK8gC 16 | w/SE8Tlx0r/yIAyutyU6F4nE4wYfR3d8SyObzf5HD0rAtHBYUdPR5qFNePpEQlHR 17 | Kepgl/pMpPZ2X6e4HYqIBrrahMI0mAns285Z/iBKpv6MyJR1npEMNa8I0eugeAGO 18 | y0IcFQIDAQABo4IBRDCCAUAwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBkAw 19 | KwYJYIZIAYb4QgENBB4WHFRpbnlDQSBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYD 20 | VR0OBBYEFFQOlI4fxXxPl9UYd9MdoBsXt9ZWMIGDBgNVHSMEfDB6gBTxU601Rtmu 21 | dtfSBFYh2znJpP64LKFXpFUwUzELMAkGA1UEBhMCR1IxGDAWBgNVBAMTD0FyYWNo 22 | bmktdGVzdC1DQTEqMCgGCSqGSIb3DQEJARYbYXJhY2huaUBhcmFjaG5pLXNjYW5u 23 | ZXIuY29tggkA8FjCoCiD1/UwJgYDVR0SBB8wHYEbYXJhY2huaUBhcmFjaG5pLXNj 24 | YW5uZXIuY29tMCYGA1UdEQQfMB2BG2FyYWNobmlAYXJhY2huaS1zY2FubmVyLmNv 25 | bTANBgkqhkiG9w0BAQUFAAOCAgEAeKLkpVjOdO9r5FcZPhemKsL9AFmnbqWmSeFA 26 | Cmu7wyHwbDbgCS0tWnrq45qDbnAJxAqXQKzKS7QINxR80XMpgoaglx7qd1V5MEXK 27 | LXn4G6BJ5tAgEbLY7HnsUHQnLMK/KWCzMODweM2gtPTauChF9kIKWrBJuizfldAo 28 | ww8YOeZfPc77pEhC/vjttQOzfqcLjGvzpJJQVmd+i6JtH635hou9i0QXR//Nrf+l 29 | ymrCuh7sil/Z1bJdmrQ1yPb2fNgqwHnWHqwPSFb8heEGExKC6vNCS2A+e08HGQTR 30 | wNAQQYQRihtDC4lqNFcVdgZExXRypzbPE2l49RnDXCL/JOCisAErGd0kzBBj6SGD 31 | Z7AKH87esH1mNThWFCZJTYXFTthVpNbOpPQ78UbXz9tFbfVdbWAcOn9enNbVeZa5 32 | vbqIHQs7azy4I5NwQ2zK/+MO1egUbbCEhcUzoUgEx8NB3JeW2ziZDEzzyKqTcv0V 33 | EH+suZefvhHxI3tGg2UmFD1+SBEVNw/1jv1HsduTclI7ckGHLcFjt7hG1CFLfLZ3 34 | wEhN91GCsOrbckp8wBMsT1yiimasrVL2PKfa0ywUW0D6zRvBs5bgtGaWUPy1VX5W 35 | 3LLbhwMZyaHnjT9GGw2cW1hea8TRWapj+/On4w2tRy6KtnkLBHgM3E+0E9DFwwVZ 36 | jppXDh8= 37 | -----END CERTIFICATE----- 38 | -------------------------------------------------------------------------------- /spec/support/pems/server/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIJKgIBAAKCAgEAs/jgMyDMFTbo48HlN2Yb5NGRmKk3Se9yc+b+xTuSRN+YU88t 3 | ZH+hIDBKpmaingXveX74r/u1F3KrWKMsRTwf7LEi3lDMjtH1GWTDIvBH6rWWb3gd 4 | Y+39BH80hBu3i+bQo2/0PLV0LzN9/tEH458HK0jq9nzz8GxIllNXdXD9WXUbYLwL 5 | qN2uEoGvcbSUhUXt4kqcpybEKOPNq0j6SHFX5ubQK6vEVZc7ST712AO+oiIDXXYX 6 | TjP93wYPmuol6pAu19Fj0aDbe14lZ4sASD9bef9kuycoUbDedAq37vTepWXf34o9 7 | McK+YS85eZVynVCOYmGWX06TYiF51CArMBTlB8Ff9lvFwA7tRxy3l2uJJ6p5l9Ie 8 | 3PmyZt1K1zIsFOtbVRDaIIzXP7yT/X48vypX74J+QV5qck83u185YyXE7dFTTSlH 9 | Yni4ybFSlReNo+kR/X2q7IXB3As3eO9NVpDBLGx2WJ3DFGDQgalIO+UriBRyGX2e 10 | MKtOovAS0VyURyvj/pA6f3Micq/1KrZScBSSILVmDjtEjIcpwSvIAsP0hPE5cdK/ 11 | 8iAMrrclOheJxOMGH0d3fEsjm83+Rw9KwLRwWFHT0eahTXj6REJR0SnqYJf6TKT2 12 | dl+nuB2KiAa62oTCNJgJ7NvOWf4gSqb+jMiUdZ6RDDWvCNHroHgBjstCHBUCAwEA 13 | AQKCAgEAomQ78QZz/+R3CiQH7w/8tWCALq7pi5Y6wOg7FL7nAax754u2mssrGT9Z 14 | 7IIJ0+rpJcjzHuBRjCHVCHrb1HEnIUzIeK+RlIFQ1qP8C8eiLN/DKTvJ0USKHIen 15 | XFkiKmOP+Rlo2Wqltn2Q/9+fbb0uG9mE4frnphYe/T2gWgElKZYoqQWkw6+bhWlt 16 | ZH0b0pHBz4GhjpXFRFwhVZ/Y7osPVPMJ/Y3U76IxRatA67SdS/DC9o87Aj6Zoab3 17 | Z8JFijyld+mhVyL9SHkv6RkcnRRGN3PCcbkogWXFpBGT3FMpnzaoZi13gDIH1Otm 18 | whWewOJSR+VkaQUeqdoo8sZAEfeYm/14AgwJ0H7fX9iKNHfmbtji3iOcTMeeCas6 19 | Z0ODTzmIQcqA5MgHBjRhs0EacWSEdeEC9iEzpN8dyjm6J2MSqJk12vIlRPKNl5Lx 20 | 4bLFYZBGjSb2Q5SegnS2ktPKz9dsxb3zbaJ5PnrtMQ2TQNCK06F9NDFjeJuRpt2e 21 | PlnLB5vA/CEQ866zdVpwfWmaP/lbvYQKfiP0qDrUL6ws+cRWvWVj4MTiU4+Mzqdb 22 | 0hGTKMiAe2FIr7u73URabtMKlbPaiWuSQblikigRUDyEYwg7H/K+m8wsFm4Mc9AJ 23 | sT2GJW9cJ2QXAzkdGrVXsbIDWcijdN8ziLpIyP8043sHWWCRMHUCggEBAObMIc+Y 24 | MMurC7UFRNcn+hmJTRvhFsgYzds5jLhHHwNdFjvELbJYhOrRxOjzoKh65RDZ0U0T 25 | G5TgTFlGQE6q3Pca8dqFG2zncFownkkDm+AwdmdPZ/047kc4OgTYXTdaopJCWJqD 26 | sCYhnhVqE1+jpFyn+5KneBwJoCbSMDvdZAnXOVUY+aTkzmyd6IwWUPvlTtdwcNql 27 | XUQQudP6PEgDCquvAhlzhdIUaVGr9y2jdB0c055z6hsaCZJNqsUQpVm/QZAavety 28 | 2aDaU3/kuazFBtN6IviiSE1CSGDbVv7DuaBDeW32llwkGdD8WoNLLtwZXeYsWuz5 29 | Vo4ZAKaXoIr8gaMCggEBAMef8NebwQSm9B5XEwGzxOwdVNpTm75ApH8NUavjbvyk 30 | COOGSpLtdTkuRaUTOFJdjVTfOdzWzuMF+e2Y2BJe+1m32gYp5d4oGmf/LVrL75F3 31 | mhpNxl26g4Hnp38Bpz9LJc7+kWsM/za7bRMPIab5eIUa+yg3oq4pJsFZ9pry1sHS 32 | EZL0YDWHE1fiDKB7EW5iJeiKJz1CKjiQNdlEMo7nBigUiIaw8leNg+CSwf8RYBPt 33 | xbcZuQ1ihm1EjTfnkMCruVbPI0Ot4p5E2U9ByjmiHSgfj7gYg5Z+hQ498iwGXxCI 34 | PkCJkWZKeHp28ZCjKf3/46SZWgp5A7MBxygeBvY4ducCggEBAMaOi/ALIL+3kcTU 35 | mZr426OoidwYU/8lx8R4yGWpWjcMmJomdqHeoctbWKqJHoiT4goTOouyiqIULdsI 36 | Iz8KpDs6xSOYLDYPDoupFCQApNPFHbhXikFB/0zqRI/yjI+YvBBUX+HetWa4uUX3 37 | El/PaxIDut06koycg1mirrhyCSSmMr7RU8TWy/iKCYyrjlSHvMKAC7cvWQO72ANU 38 | XxQc+csmLwoyZsiLougOqAJtgvGg4TaKxrc2OVHS273aSkbh0cfmGco77psB+aAn 39 | 6M2IXA0IIJoDE2zUP2yEh116iXIlWoyN/mGa9VLkKAKYZeIZOL1TH46MHDj3raWl 40 | AQtRnT0CggEBAKx/SceWAIF7nyEEjxj+daCua99JNiB9jWd4Y+WLXyGSwVeSeKhj 41 | 5tKlaGsPo7CQxIrdZgut1xKX3HLyi2u8hafuVcjTtZ11u/O3rkJyTS6/Uft+z3W4 42 | sOC1A6idDh+EWUR398KwdWyfIs7AWY0Gr50dRoBnKv0h9CpKCe20/9x2INeCsgwv 43 | D4j2fYDDN7nRt/4sO2IU5ZUBEHcRFjsj6M8Dh0IUbyU21Qle56H4KrSt1xa0dJLb 44 | W1vaS8lOIWhMO5iG32shO90Vk06IhC2vJ1C/jRpAnJyT8XfwEW73hngrstdjyK/C 45 | lD8GvxED3UpuLxDRrtuz3H13gNfqsotbGWsCggEAAnt56fTLGmTu9pdM3FtTEaLN 46 | rmO9JbRDsxnXTN84hLci27HW0rU/AFGw+a6MHNiBNVfBqhMnZwPgAsNVf6TB//iE 47 | FZTotJWcTHkbiBwpXvRHHQ0BwxKluDicLqzzvEW8fmiNVjDig2JeS/45r2x0S4M/ 48 | 3TVHK7A/vXQWvBgQi9a2ooViRjRXdhzlIkjNNsYieAwX7793Nw2KqZbHKXvelEOQ 49 | I6nTIqNDZOZ7MmGKHnGpj4SIupCKdkalQgakkGFbpltsyKBC7gooHQuKjQsYnRgl 50 | XzOy39QzIn0WF9AoyLg/W8yPsnhc1S5Ed6Lq8KL3HWV6a8n9QRSHVCX6dafBcg== 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /spec/support/reports/placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qadron/cuboid/242af377b6196640d0901f4b0b9a145bbc445b82/spec/support/reports/placeholder -------------------------------------------------------------------------------- /spec/support/shared/application.rb: -------------------------------------------------------------------------------- 1 | require "#{fixtures_path}/mock_app" 2 | 3 | shared_examples_for 'application' do 4 | 5 | after(:each) { subject.reset } 6 | 7 | subject { MockApp.unsafe } 8 | let(:url) { web_server_url_for( :auditor ) } 9 | let(:f_url) { web_server_url_for( :framework ) } 10 | end 11 | -------------------------------------------------------------------------------- /spec/support/shared/component.rb: -------------------------------------------------------------------------------- 1 | shared_examples_for "component" do 2 | 3 | before :all do 4 | @name = self.class.metadata[:example_group][:description] 5 | end 6 | 7 | let(:name) { @name } 8 | let(:component_name) { name } 9 | let(:framework) { Cuboid::Framework.unsafe } 10 | let(:session) { framework.session } 11 | let(:http) { Cuboid::HTTP::Client } 12 | let(:options) { Cuboid::Options } 13 | 14 | def self.use_https 15 | @use_https = true 16 | end 17 | 18 | def url 19 | @url ||= web_server_url_for( @use_https ? "#{name}_https" : name ) + '/' 20 | rescue 21 | raise "Could not find server for '#{name}' component." 22 | end 23 | 24 | def yaml_load( yaml ) 25 | YAML.load yaml.gsub( '__URL__', url ) 26 | end 27 | 28 | def run 29 | framework.run 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/support/shared/option_group.rb: -------------------------------------------------------------------------------- 1 | shared_examples_for 'option_group' do 2 | it { is_expected.to respond_to :to_h } 3 | 4 | describe '#to_rpc_data' do 5 | let(:data) { subject.to_rpc_data } 6 | 7 | it 'converts self to a serializable hash' do 8 | expect(data).to be_kind_of Hash 9 | 10 | expect(Cuboid::RPC::Serializer.load( 11 | Cuboid::RPC::Serializer.dump( data ) 12 | )).to eq(data) 13 | end 14 | end 15 | 16 | described_class.defaults.each do |k, v| 17 | describe "##{k}" do 18 | it "defaults to #{v}" do 19 | expect(subject.instance_variable_get( "@#{k}".to_sym )).to eq(v) 20 | expect(subject.send( k )).to eq(v) 21 | end 22 | end 23 | end 24 | 25 | it 'honors default values for attributes' do 26 | subject.defaults.each do |k, v| 27 | subject.send "#{k}=", nil 28 | expect(subject.instance_variable_get( "@#{k}".to_sym )).to eq(v) 29 | expect(subject.send( k )).to eq(v) 30 | end 31 | end 32 | 33 | describe '#to_hash' do 34 | it 'returns a hash' do 35 | expect(subject.to_hash).to be_kind_of Hash 36 | end 37 | end 38 | 39 | describe '#to_h' do 40 | it 'returns a hash' do 41 | expect(subject.to_h).to be_kind_of Hash 42 | end 43 | 44 | it 'only includes attributes with accessors' do 45 | method = subject.methods.find { |m| m.to_s.end_with? '=' } 46 | if method == :=== || method == :== 47 | expect(subject.to_h).to be_empty 48 | next 49 | end 50 | 51 | subject.send( method, subject.defaults[method.to_s[0..-1].to_sym] ) 52 | 53 | hash = subject.to_h 54 | expect(hash).to be_any 55 | hash.each do |k, v| 56 | expect(subject).to respond_to "#{k}=" 57 | end 58 | end 59 | end 60 | 61 | describe '#update' do 62 | it 'updates self with the values of the given hash' do 63 | method = subject.methods.find { |m| m.to_s.end_with? '=' } 64 | next if method == :=== || method == :== 65 | 66 | method = method.to_s[0...-1].to_sym 67 | value = subject.defaults[method.to_s[0..-1].to_sym] 68 | 69 | subject.update( { method => value } ) 70 | expect(subject.send( method )).to eq(value) 71 | end 72 | 73 | it 'returns self' do 74 | expect(subject.update({})).to eq(subject) 75 | end 76 | end 77 | 78 | describe '#merge' do 79 | it 'updates self with the values of the given OptionGroup' do 80 | method = subject.methods.find { |m| m.to_s.end_with? '=' } 81 | next if method == :=== || method == :== 82 | 83 | method = method.to_s[0...-1].to_sym 84 | value = subject.defaults[method.to_s[0..-1].to_sym] 85 | 86 | group = described_class.new 87 | group.update( { method => value } ) 88 | 89 | subject.merge( group ) 90 | expect(subject.send( method )).to eq(value) 91 | end 92 | 93 | it 'returns self' do 94 | group = described_class.new 95 | expect(subject.merge( group )).to eq(subject) 96 | end 97 | end 98 | end 99 | -------------------------------------------------------------------------------- /spec/support/shared/system/platforms/base.rb: -------------------------------------------------------------------------------- 1 | shared_examples_for 'Cuboid::System::Platforms::Base' do 2 | subject { described_class.new } 3 | 4 | describe '#disk_directory' do 5 | it "delegates to #{Cuboid::OptionGroups::Paths}#os_tmpdir" do 6 | expect(subject.disk_directory).to be Cuboid::Options.paths.os_tmpdir 7 | end 8 | end 9 | 10 | describe '#disk_space_for_process' do 11 | it 'returns bytes of disk space used by the process' do 12 | expect(Cuboid::Support::Database::Base).to receive(:disk_space_for).with(123).and_return(1000) 13 | 14 | expect(subject.disk_space_for_process( 123 )).to eq 1000 15 | end 16 | end 17 | 18 | describe '#cpu_count' do 19 | it 'returns the amount of CPUs' do 20 | expect(Concurrent).to receive(:processor_count).and_return(99) 21 | expect(subject.cpu_count).to eq 99 22 | end 23 | end 24 | 25 | end 26 | -------------------------------------------------------------------------------- /spec/support/shared/system/platforms/mixins/unix.rb: -------------------------------------------------------------------------------- 1 | shared_examples_for 'Cuboid::System::Platforms::Mixins::Unix' do 2 | it_should_behave_like 'Cuboid::System::Platforms::Base' 3 | 4 | subject { described_class.new } 5 | 6 | describe '#memory_for_process_group' do 7 | let(:ps) do 8 | <