rrrspec-server 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/db/migrate/20140221062741_expand_log.rb +11 -0
- data/db/migrate/20140225062300_delete_logs.rb +19 -0
- data/db/schema.rb +25 -30
- data/lib/rrrspec/server.rb +4 -0
- data/lib/rrrspec/server/cli.rb +14 -62
- data/lib/rrrspec/server/configuration.rb +12 -4
- data/lib/rrrspec/server/daemonizer.rb +87 -0
- data/lib/rrrspec/server/json_constructor.rb +187 -0
- data/lib/rrrspec/server/log_file_persister.rb +66 -0
- data/lib/rrrspec/server/persistent_models.rb +21 -79
- data/lib/rrrspec/server/persister.rb +11 -4
- data/lib/rrrspec/server/version.rb +1 -1
- data/lib/rrrspec/server/worker_runner.rb +4 -4
- data/rrrspec-server.gemspec +3 -2
- data/spec/rrrspec/server/log_file_persister_spec.rb +34 -0
- data/spec/rrrspec/server/persistent_models_spec.rb +2 -1
- data/spec/rrrspec/server/persister_spec.rb +38 -1
- metadata +57 -36
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3dd5351ecddcebbc6fbc7f89e1102c06b02dcb21
|
4
|
+
data.tar.gz: 33b7df062b10d6f6d9099372c931d85663bb68bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c5bd9cf986be28aa07d504e771a2be2fef1260c7e0a76f9e6bf91b405c81331b51f0d5c7ca551b9080b4383d22de9391c7551e8343b03c8392a75130e7bb204b
|
7
|
+
data.tar.gz: b85287965ddcf34ee7f64e1dc857bc2106257fac8b927ea2bd1862249a5f935473a4bfe7a690aa013ab9d435b4272e4177e4da626e187790b61423b5403ef9a3
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class ExpandLog < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
change_column(:tasksets, :setup_command, :text, limit: 4294967295)
|
4
|
+
change_column(:tasksets, :slave_command, :text, limit: 4294967295)
|
5
|
+
change_column(:tasksets, :log, :text, limit: 4294967295)
|
6
|
+
change_column(:trials, :stdout, :text, limit: 4294967295)
|
7
|
+
change_column(:trials, :stderr, :text, limit: 4294967295)
|
8
|
+
change_column(:worker_logs, :log, :text, limit: 4294967295)
|
9
|
+
change_column(:slaves, :log, :text, limit: 4294967295)
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class DeleteLogs < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
change_table :slaves do |t|
|
4
|
+
t.remove :log
|
5
|
+
end
|
6
|
+
|
7
|
+
change_table :tasksets do |t|
|
8
|
+
t.remove :log
|
9
|
+
end
|
10
|
+
|
11
|
+
change_table :trials do |t|
|
12
|
+
t.remove :stdout, :stderr
|
13
|
+
end
|
14
|
+
|
15
|
+
change_table :worker_logs do |t|
|
16
|
+
t.remove :log
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/db/schema.rb
CHANGED
@@ -9,21 +9,20 @@
|
|
9
9
|
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
|
10
10
|
# you'll amass, the slower it'll run and the greater likelihood for issues).
|
11
11
|
#
|
12
|
-
# It's strongly recommended
|
12
|
+
# It's strongly recommended that you check this file into your version control system.
|
13
13
|
|
14
|
-
ActiveRecord::Schema.define(:
|
14
|
+
ActiveRecord::Schema.define(version: 20140225062300) do
|
15
15
|
|
16
|
-
create_table "slaves", :
|
16
|
+
create_table "slaves", force: true do |t|
|
17
17
|
t.string "key"
|
18
18
|
t.integer "taskset_id"
|
19
19
|
t.string "status"
|
20
|
-
t.text "log"
|
21
20
|
end
|
22
21
|
|
23
|
-
add_index "slaves", ["key"], :
|
24
|
-
add_index "slaves", ["taskset_id"], :
|
22
|
+
add_index "slaves", ["key"], name: "index_slaves_on_key"
|
23
|
+
add_index "slaves", ["taskset_id"], name: "index_slaves_on_taskset_id"
|
25
24
|
|
26
|
-
create_table "tasks", :
|
25
|
+
create_table "tasks", force: true do |t|
|
27
26
|
t.string "key"
|
28
27
|
t.integer "taskset_id"
|
29
28
|
t.string "status"
|
@@ -31,15 +30,15 @@ ActiveRecord::Schema.define(:version => 20131105050718) do
|
|
31
30
|
t.string "spec_file"
|
32
31
|
end
|
33
32
|
|
34
|
-
add_index "tasks", ["key"], :
|
35
|
-
add_index "tasks", ["status"], :
|
36
|
-
add_index "tasks", ["taskset_id"], :
|
33
|
+
add_index "tasks", ["key"], name: "index_tasks_on_key"
|
34
|
+
add_index "tasks", ["status"], name: "index_tasks_on_status"
|
35
|
+
add_index "tasks", ["taskset_id"], name: "index_tasks_on_taskset_id"
|
37
36
|
|
38
|
-
create_table "tasksets", :
|
37
|
+
create_table "tasksets", force: true do |t|
|
39
38
|
t.string "key"
|
40
39
|
t.string "rsync_name"
|
41
|
-
t.text "setup_command"
|
42
|
-
t.text "slave_command"
|
40
|
+
t.text "setup_command", limit: 4294967295
|
41
|
+
t.text "slave_command", limit: 4294967295
|
43
42
|
t.string "worker_type"
|
44
43
|
t.integer "max_workers"
|
45
44
|
t.integer "max_trials"
|
@@ -49,34 +48,31 @@ ActiveRecord::Schema.define(:version => 20131105050718) do
|
|
49
48
|
t.datetime "created_at"
|
50
49
|
t.string "status"
|
51
50
|
t.datetime "finished_at"
|
52
|
-
t.text "log"
|
53
51
|
end
|
54
52
|
|
55
|
-
add_index "tasksets", ["created_at"], :
|
56
|
-
add_index "tasksets", ["key"], :
|
57
|
-
add_index "tasksets", ["rsync_name"], :
|
58
|
-
add_index "tasksets", ["status"], :
|
59
|
-
add_index "tasksets", ["taskset_class"], :
|
53
|
+
add_index "tasksets", ["created_at"], name: "index_tasksets_on_created_at"
|
54
|
+
add_index "tasksets", ["key"], name: "index_tasksets_on_key"
|
55
|
+
add_index "tasksets", ["rsync_name"], name: "index_tasksets_on_rsync_name"
|
56
|
+
add_index "tasksets", ["status"], name: "index_tasksets_on_status"
|
57
|
+
add_index "tasksets", ["taskset_class"], name: "index_tasksets_on_taskset_class"
|
60
58
|
|
61
|
-
create_table "trials", :
|
59
|
+
create_table "trials", force: true do |t|
|
62
60
|
t.string "key"
|
63
61
|
t.integer "task_id"
|
64
62
|
t.integer "slave_id"
|
65
63
|
t.datetime "started_at"
|
66
64
|
t.datetime "finished_at"
|
67
65
|
t.string "status"
|
68
|
-
t.text "stdout"
|
69
|
-
t.text "stderr"
|
70
66
|
t.integer "passed"
|
71
67
|
t.integer "pending"
|
72
68
|
t.integer "failed"
|
73
69
|
end
|
74
70
|
|
75
|
-
add_index "trials", ["key"], :
|
76
|
-
add_index "trials", ["slave_id"], :
|
77
|
-
add_index "trials", ["task_id"], :
|
71
|
+
add_index "trials", ["key"], name: "index_trials_on_key"
|
72
|
+
add_index "trials", ["slave_id"], name: "index_trials_on_slave_id"
|
73
|
+
add_index "trials", ["task_id"], name: "index_trials_on_task_id"
|
78
74
|
|
79
|
-
create_table "worker_logs", :
|
75
|
+
create_table "worker_logs", force: true do |t|
|
80
76
|
t.string "key"
|
81
77
|
t.string "worker_key"
|
82
78
|
t.integer "taskset_id"
|
@@ -84,11 +80,10 @@ ActiveRecord::Schema.define(:version => 20131105050718) do
|
|
84
80
|
t.datetime "rsync_finished_at"
|
85
81
|
t.datetime "setup_finished_at"
|
86
82
|
t.datetime "finished_at"
|
87
|
-
t.text "log"
|
88
83
|
end
|
89
84
|
|
90
|
-
add_index "worker_logs", ["key"], :
|
91
|
-
add_index "worker_logs", ["taskset_id"], :
|
92
|
-
add_index "worker_logs", ["worker_key"], :
|
85
|
+
add_index "worker_logs", ["key"], name: "index_worker_logs_on_key"
|
86
|
+
add_index "worker_logs", ["taskset_id"], name: "index_worker_logs_on_taskset_id"
|
87
|
+
add_index "worker_logs", ["worker_key"], name: "index_worker_logs_on_worker_key"
|
93
88
|
|
94
89
|
end
|
data/lib/rrrspec/server.rb
CHANGED
@@ -1,9 +1,13 @@
|
|
1
|
+
require 'fileutils'
|
1
2
|
require 'active_record'
|
2
3
|
|
3
4
|
require 'rrrspec'
|
4
5
|
require 'rrrspec/server/arbiter'
|
5
6
|
require 'rrrspec/server/configuration'
|
7
|
+
require 'rrrspec/server/daemonizer'
|
6
8
|
require 'rrrspec/server/dispatcher'
|
9
|
+
require 'rrrspec/server/json_constructor'
|
10
|
+
require 'rrrspec/server/log_file_persister'
|
7
11
|
require 'rrrspec/server/persistent_models'
|
8
12
|
require 'rrrspec/server/persister'
|
9
13
|
require 'rrrspec/server/worker_runner'
|
data/lib/rrrspec/server/cli.rb
CHANGED
@@ -12,73 +12,21 @@ module RRRSpec
|
|
12
12
|
def setup(conf)
|
13
13
|
RRRSpec.setup(conf, options[:config].split(':'))
|
14
14
|
end
|
15
|
-
|
16
|
-
def log_exception
|
17
|
-
yield
|
18
|
-
rescue
|
19
|
-
RRRSpec.logger.error($!)
|
20
|
-
raise
|
21
|
-
end
|
22
|
-
|
23
|
-
def auto_rebirth
|
24
|
-
Signal.trap('TERM') do
|
25
|
-
Signal.trap('TERM', 'DEFAULT')
|
26
|
-
Process.kill('TERM', 0)
|
27
|
-
end
|
28
|
-
loop do
|
29
|
-
pid = Process.fork do
|
30
|
-
log_exception { yield }
|
31
|
-
end
|
32
|
-
Process.waitpid(pid)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def daemonize
|
37
|
-
return unless options[:daemonize]
|
38
|
-
pidfile = options[:pidfile]
|
39
|
-
|
40
|
-
if pidfile
|
41
|
-
pidfile = File.absolute_path(pidfile)
|
42
|
-
if File.exists?(pidfile)
|
43
|
-
pid = open(pidfile, 'r') { |f| f.read.strip }
|
44
|
-
if pid
|
45
|
-
begin
|
46
|
-
if Process.kill(0, pid.to_i) == 1
|
47
|
-
$stderr.puts "Daemon process already exists: #{pid}"
|
48
|
-
exit 1
|
49
|
-
end
|
50
|
-
rescue Errno::ESRCH
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
Process.daemon
|
57
|
-
|
58
|
-
if pidfile
|
59
|
-
open(pidfile, 'w') { |f| f.write(Process.pid.to_s) }
|
60
|
-
|
61
|
-
parent_pid = Process.pid
|
62
|
-
at_exit do
|
63
|
-
if Process.pid == parent_pid
|
64
|
-
File.delete(pidfile)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
15
|
end
|
70
16
|
|
71
17
|
method_option :daemonize, type: :boolean
|
72
18
|
method_option :pidfile, type: :string
|
19
|
+
method_option :user, type: :string
|
73
20
|
desc 'server', 'Run RRRSpec as a server'
|
74
21
|
def server
|
75
|
-
$0 = 'rrrspec server'
|
76
22
|
setup(ServerConfiguration.new)
|
77
|
-
daemonize
|
78
|
-
|
23
|
+
RRRSpec.configuration.daemonize = options[:daemonize] unless options[:daemonize] == nil
|
24
|
+
RRRSpec.configuration.pidfile = options[:pidfile] unless options[:pidfile] == nil
|
25
|
+
RRRSpec.configuration.user = options[:user] unless options[:user] == nil
|
26
|
+
|
27
|
+
RRRSpec::Server.daemonizer('rrrspec-server server') do
|
79
28
|
ActiveRecord::Base.establish_connection(**RRRSpec.configuration.persistence_db)
|
80
29
|
Thread.abort_on_exception = true
|
81
|
-
Thread.fork { RRRSpec.pacemaker(RSyncInfo, 60, 5) }
|
82
30
|
Thread.fork { Dispatcher.work_loop }
|
83
31
|
Thread.fork { Arbiter.work_loop }
|
84
32
|
Thread.fork { Persister.work_loop }
|
@@ -86,15 +34,19 @@ module RRRSpec
|
|
86
34
|
end
|
87
35
|
end
|
88
36
|
|
37
|
+
method_option :'worker-type', type: :string
|
89
38
|
method_option :daemonize, type: :boolean
|
90
39
|
method_option :pidfile, type: :string
|
40
|
+
method_option :user, type: :string
|
91
41
|
desc 'worker', 'Run RRRSpec as a worker'
|
92
42
|
def worker
|
93
|
-
$0 = 'rrrspec worker'
|
94
43
|
setup(WorkerConfiguration.new)
|
95
|
-
daemonize
|
96
|
-
|
97
|
-
|
44
|
+
RRRSpec.configuration.daemonize = options[:daemonize] unless options[:daemonize] == nil
|
45
|
+
RRRSpec.configuration.pidfile = options[:pidfile] unless options[:pidfile] == nil
|
46
|
+
RRRSpec.configuration.user = options[:user] unless options[:user] == nil
|
47
|
+
|
48
|
+
RRRSpec::Server.daemonizer('rrrspec-server worker') do
|
49
|
+
worker = Worker.create(options[:'worker-type'] || RRRSpec.configuration.worker_type)
|
98
50
|
worker_runner = WorkerRunner.new(worker)
|
99
51
|
Thread.abort_on_exception = true
|
100
52
|
Thread.fork { RRRSpec.pacemaker(worker, 60, 5) }
|
@@ -3,9 +3,10 @@ require 'facter'
|
|
3
3
|
module RRRSpec
|
4
4
|
module Server
|
5
5
|
class ServerConfiguration < Configuration
|
6
|
-
attr_accessor :rsync_server, :rsync_dir, :rsync_options
|
7
6
|
attr_accessor :persistence_db
|
7
|
+
attr_accessor :execute_log_text_path
|
8
8
|
attr_accessor :json_cache_path
|
9
|
+
attr_accessor :daemonize, :pidfile, :user
|
9
10
|
|
10
11
|
def initialize
|
11
12
|
super()
|
@@ -15,8 +16,8 @@ module RRRSpec
|
|
15
16
|
def check_validity
|
16
17
|
validity = super
|
17
18
|
|
18
|
-
unless
|
19
|
-
$stderr.puts('The
|
19
|
+
unless execute_log_text_path
|
20
|
+
$stderr.puts('The path to save the log text should be set')
|
20
21
|
validity = false
|
21
22
|
end
|
22
23
|
|
@@ -30,11 +31,13 @@ module RRRSpec
|
|
30
31
|
end
|
31
32
|
|
32
33
|
class WorkerConfiguration < Configuration
|
34
|
+
attr_accessor :rsync_remote_path, :rsync_options
|
33
35
|
attr_accessor :working_dir, :worker_type, :slave_processes
|
36
|
+
attr_accessor :daemonize, :pidfile, :user
|
34
37
|
|
35
38
|
def initialize
|
36
39
|
super()
|
37
|
-
@slave_processes = Facter.processorcount.to_i
|
40
|
+
@slave_processes = Facter.value(:processorcount).to_i
|
38
41
|
@worker_type = 'default'
|
39
42
|
@type = :worker
|
40
43
|
end
|
@@ -42,6 +45,11 @@ module RRRSpec
|
|
42
45
|
def check_validity
|
43
46
|
validity = super
|
44
47
|
|
48
|
+
unless rsync_remote_path and rsync_options
|
49
|
+
$stderr.puts('The rsync options are not set')
|
50
|
+
validity = false
|
51
|
+
end
|
52
|
+
|
45
53
|
unless working_dir and worker_type
|
46
54
|
$stderr.puts('The worker options are not set')
|
47
55
|
validity = false
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module RRRSpec
|
2
|
+
module Server
|
3
|
+
RESPAWN_INTERVAL_LIMIT_SEC = 30
|
4
|
+
|
5
|
+
def self.daemonizer(process_name, &block)
|
6
|
+
$0 = process_name
|
7
|
+
return block.call unless should_daemonize?
|
8
|
+
|
9
|
+
pidfile = File.absolute_path(RRRSpec.configuration.pidfile || File.join("/var/run", "#{process_name}.pid"))
|
10
|
+
check_pidfile(pidfile)
|
11
|
+
|
12
|
+
Process.daemon
|
13
|
+
File.write(pidfile, Process.pid.to_s)
|
14
|
+
File.umask(0)
|
15
|
+
setup_signal_handlers
|
16
|
+
setup_atexit_handlers(pidfile)
|
17
|
+
monitor_fork do
|
18
|
+
Process::Sys.setuid(RRRSpec.configuration.user) if RRRSpec.configuration.user
|
19
|
+
block.call
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.should_daemonize?
|
24
|
+
RRRSpec.configuration.daemonize == nil || RRRSpec.configuration.daemonize
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.should_monitor?
|
28
|
+
RRRSpec.configuration.monitor == nil || RRRSpec.configuration.monitor
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.check_pidfile(pidfile)
|
32
|
+
if File.exist?(pidfile)
|
33
|
+
if File.readable?(pidfile)
|
34
|
+
pid = File.read(pidfile).to_i
|
35
|
+
begin
|
36
|
+
Process.kill(0, pid)
|
37
|
+
raise "Pid(#{pid}) is running"
|
38
|
+
rescue Errno::EPERM
|
39
|
+
raise "Pid(#{pid}) is running"
|
40
|
+
rescue Errno::ESRCH
|
41
|
+
end
|
42
|
+
else
|
43
|
+
raise "Cannot access #{pidfile}"
|
44
|
+
end
|
45
|
+
|
46
|
+
unless File.writable?(pidfile)
|
47
|
+
raise "Cannot access #{pidfile}"
|
48
|
+
end
|
49
|
+
else
|
50
|
+
unless File.writable?(File.dirname(pidfile))
|
51
|
+
raise "Cannot access #{pidfile}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.setup_signal_handlers
|
57
|
+
# Propagate the TERM signal to the children
|
58
|
+
Signal.trap('TERM') do
|
59
|
+
Signal.trap('TERM', 'DEFAULT')
|
60
|
+
Process.kill('TERM', 0)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.setup_atexit_handlers(pidfile)
|
65
|
+
# Delete pid at exit
|
66
|
+
current_pid = Process.pid
|
67
|
+
at_exit do
|
68
|
+
# Since at_exit handlers are inherited by child processes, it is the
|
69
|
+
# case that the handlers are invoked in the child processes. This guard
|
70
|
+
# is needed to avoid this.
|
71
|
+
File.delete(pidfile) if Process.pid == current_pid
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.monitor_fork(&block)
|
76
|
+
loop do
|
77
|
+
started_at = Time.now
|
78
|
+
pid = Process.fork(&block)
|
79
|
+
Process.waitpid(pid)
|
80
|
+
break unless should_monitor?
|
81
|
+
if Time.now - started_at < RESPAWN_INTERVAL_LIMIT_SEC
|
82
|
+
sleep RESPAWN_INTERVAL_LIMIT_SEC
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,187 @@
|
|
1
|
+
module RRRSpec
|
2
|
+
module Server
|
3
|
+
module JSONConstructor
|
4
|
+
module TasksetJSONConstructor
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
include ActiveModel::Serializers::JSON
|
7
|
+
|
8
|
+
def as_nodetail_json
|
9
|
+
as_json(except: :id, methods: :log)
|
10
|
+
end
|
11
|
+
|
12
|
+
def as_short_json
|
13
|
+
h = as_json(except: :id, methods: :log)
|
14
|
+
h['slaves'] = slaves.map { |slave| slave.as_json(only: :key) }
|
15
|
+
h['tasks'] = tasks.map { |task| task.as_json(only: :key) }
|
16
|
+
h['worker_logs'] = worker_logs.map { |worker_log| worker_log.as_json(only: :key) }
|
17
|
+
h
|
18
|
+
end
|
19
|
+
|
20
|
+
def as_full_json
|
21
|
+
h = as_json(except: :id, methods: :log)
|
22
|
+
h['slaves'] = slaves.map(&:as_full_json)
|
23
|
+
h['tasks'] = tasks.map(&:as_full_json)
|
24
|
+
h['worker_logs'] = worker_logs.map(&:as_full_json)
|
25
|
+
h
|
26
|
+
end
|
27
|
+
|
28
|
+
def as_json_for_index
|
29
|
+
{
|
30
|
+
'id' => id,
|
31
|
+
'key' => key,
|
32
|
+
'rsync_name' => rsync_name,
|
33
|
+
'setup_command' => setup_command,
|
34
|
+
'slave_command' => slave_command,
|
35
|
+
'worker_type' => worker_type,
|
36
|
+
'max_workers' => max_workers,
|
37
|
+
'max_trials' => max_trials,
|
38
|
+
'taskset_class' => taskset_class,
|
39
|
+
'created_at' => created_at,
|
40
|
+
'status' => status,
|
41
|
+
'finished_at' => finished_at,
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
def as_json_for_result_page
|
46
|
+
as_json_for_index.merge(
|
47
|
+
'tasks' => tasks.map(&:as_json_for_result_page),
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
def as_summary_json
|
52
|
+
statuses = tasks.pluck(:status)
|
53
|
+
groups = statuses.group_by { |status| status }
|
54
|
+
groups.default = []
|
55
|
+
|
56
|
+
{
|
57
|
+
'id' => id,
|
58
|
+
'key' => key,
|
59
|
+
'status' => status,
|
60
|
+
'created_at' => created_at,
|
61
|
+
'finished_at' => finished_at,
|
62
|
+
'task_count' => statuses.count,
|
63
|
+
'passed_count' => groups['passed'].size,
|
64
|
+
'pending_count' => groups['pending'].size,
|
65
|
+
'failed_count' => groups['failed'].size,
|
66
|
+
}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
module TaskJSONConstructor
|
71
|
+
extend ActiveSupport::Concern
|
72
|
+
include ActiveModel::Serializers::JSON
|
73
|
+
|
74
|
+
def as_short_json
|
75
|
+
h = as_json(except: [:id, :taskset_id, :trials],
|
76
|
+
include: { 'taskset' => { only: :key } })
|
77
|
+
h['trials'] = trials.map { |trial| trial.as_json(only: :key) }
|
78
|
+
h
|
79
|
+
end
|
80
|
+
|
81
|
+
def as_full_json
|
82
|
+
h = as_json(except: [:id, :taskset_id, :trials],
|
83
|
+
include: { 'taskset' => { only: :key } })
|
84
|
+
h['trials'] = trials.map(&:as_full_json)
|
85
|
+
h
|
86
|
+
end
|
87
|
+
|
88
|
+
def as_json_for_result_page
|
89
|
+
{
|
90
|
+
'id' => id,
|
91
|
+
'key' => key,
|
92
|
+
'status' => status,
|
93
|
+
'spec_path' => spec_file,
|
94
|
+
'estimate_sec' => estimate_sec,
|
95
|
+
'trials' => trials.map(&:as_json_for_result_page),
|
96
|
+
}
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
module TrialJSONConstructor
|
101
|
+
extend ActiveSupport::Concern
|
102
|
+
include ActiveModel::Serializers::JSON
|
103
|
+
|
104
|
+
def as_short_json
|
105
|
+
as_full_json
|
106
|
+
end
|
107
|
+
|
108
|
+
def as_full_json
|
109
|
+
as_json(except: [:id, :task_id, :slave_id],
|
110
|
+
include: { 'slave' => { only: :key }, 'task' => { only: :key } },
|
111
|
+
methods: [:stdout, :stderr])
|
112
|
+
end
|
113
|
+
|
114
|
+
def as_json_for_result_page
|
115
|
+
{
|
116
|
+
'id' => id,
|
117
|
+
'key' => key,
|
118
|
+
'task_id' => task_id,
|
119
|
+
'slave_id' => slave_id,
|
120
|
+
'started_at' => started_at,
|
121
|
+
'finished_at' => finished_at,
|
122
|
+
'status' => status,
|
123
|
+
'passed' => passed,
|
124
|
+
'pending' => pending,
|
125
|
+
'failed' => failed,
|
126
|
+
}
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
module WorkerLogJSONConstructor
|
131
|
+
extend ActiveSupport::Concern
|
132
|
+
include ActiveModel::Serializers::JSON
|
133
|
+
|
134
|
+
def as_short_json
|
135
|
+
as_full_json
|
136
|
+
end
|
137
|
+
|
138
|
+
def as_full_json
|
139
|
+
as_json(except: [:id, :taskset_id, :worker_key],
|
140
|
+
include: { 'taskset' => { only: :key } },
|
141
|
+
methods: [:worker, :log])
|
142
|
+
end
|
143
|
+
|
144
|
+
def worker
|
145
|
+
{ 'key' => worker_key }
|
146
|
+
end
|
147
|
+
|
148
|
+
def as_json_for_result_page
|
149
|
+
{
|
150
|
+
'id' => id,
|
151
|
+
'worker_name' => worker_key,
|
152
|
+
'started_at' => started_at,
|
153
|
+
'rsync_finished_at' => rsync_finished_at,
|
154
|
+
'setup_finished_at' => setup_finished_at,
|
155
|
+
'rspec_finished_at' => finished_at,
|
156
|
+
'log' => log.to_s,
|
157
|
+
}
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
module SlaveJSONConstructor
|
162
|
+
extend ActiveSupport::Concern
|
163
|
+
include ActiveModel::Serializers::JSON
|
164
|
+
|
165
|
+
def as_short_json
|
166
|
+
as_full_json
|
167
|
+
end
|
168
|
+
|
169
|
+
def as_full_json
|
170
|
+
as_json(except: [:id, :taskset_id],
|
171
|
+
include: { 'trials' => { only: :key } },
|
172
|
+
methods: [:log])
|
173
|
+
end
|
174
|
+
|
175
|
+
def as_json_for_result_page
|
176
|
+
{
|
177
|
+
'id' => id,
|
178
|
+
'name' => key,
|
179
|
+
'status' => status,
|
180
|
+
'trials' => trials.map { |trial| { id: trial.id, key: trial.key } },
|
181
|
+
'log' => log.to_s,
|
182
|
+
}
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module RRRSpec
|
2
|
+
module Server
|
3
|
+
# LogFilePersister
|
4
|
+
#
|
5
|
+
# class Taskset < ActiveRecord::Base
|
6
|
+
# include LogFilePersister
|
7
|
+
# save_as_file :log, suffix: 'log'
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
# Requirements:
|
11
|
+
#
|
12
|
+
# * Have a property named 'key'
|
13
|
+
# * Set RRRSpec.configuration.execute_log_text_path
|
14
|
+
# * Have a singleton method :after_save
|
15
|
+
#
|
16
|
+
# Provides:
|
17
|
+
#
|
18
|
+
# * Methods named 'log', 'log=', 'log_log_path'.
|
19
|
+
# * 'after_save' hook that persists a log to the file pointed by log_log_path.
|
20
|
+
# * Persist the value of the 'log' to File.join(execute_log_text_path, "#{key.gsub(/\//, '_').gsub(/:/, '/')}_log.log") if it is dirty.
|
21
|
+
module LogFilePersister
|
22
|
+
extend ActiveSupport::Concern
|
23
|
+
|
24
|
+
module ClassMethods
|
25
|
+
def save_as_file(name, suffix: nil)
|
26
|
+
suffix = "_#{suffix}" if suffix
|
27
|
+
instance_variable_set("@#{name}_dirty", false)
|
28
|
+
define_method("#{name}_log_path") do
|
29
|
+
return nil unless send(:key)
|
30
|
+
File.join(
|
31
|
+
RRRSpec.configuration.execute_log_text_path,
|
32
|
+
"#{send(:key).gsub(/\//, '_').gsub(/:/, '/')}#{suffix}.log"
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
define_method(name) do
|
37
|
+
contents = instance_variable_get("@#{name}")
|
38
|
+
if !contents
|
39
|
+
log_path = send("#{name}_log_path")
|
40
|
+
if log_path
|
41
|
+
contents = File.exist?(log_path) ? File.read(log_path) : nil
|
42
|
+
instance_variable_set("@#{name}", contents) if contents
|
43
|
+
end
|
44
|
+
end
|
45
|
+
contents
|
46
|
+
end
|
47
|
+
|
48
|
+
define_method("#{name}=") do |val|
|
49
|
+
if send(name) != val
|
50
|
+
instance_variable_set("@#{name}_dirty", true)
|
51
|
+
instance_variable_set("@#{name}", val)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
after_save do
|
56
|
+
if instance_variable_get("@#{name}")
|
57
|
+
log_path = send("#{name}_log_path")
|
58
|
+
FileUtils.mkdir_p(File.dirname(log_path))
|
59
|
+
File.open(log_path, 'w') { |fp| fp.write(send(name)) }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -2,109 +2,51 @@ module RRRSpec
|
|
2
2
|
module Server
|
3
3
|
module Persistence
|
4
4
|
class Taskset < ActiveRecord::Base
|
5
|
-
include
|
6
|
-
|
5
|
+
include JSONConstructor::TasksetJSONConstructor
|
6
|
+
include LogFilePersister
|
7
7
|
has_many :worker_logs
|
8
8
|
has_many :slaves
|
9
9
|
has_many :tasks
|
10
|
+
save_as_file :log, suffix: 'log'
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
def as_nodetail_json
|
18
|
-
as_json(except: :id)
|
19
|
-
end
|
20
|
-
|
21
|
-
def as_short_json
|
22
|
-
h = as_json(except: :id)
|
23
|
-
h['slaves'] = slaves.map { |slave| slave.as_json(only: :key) }
|
24
|
-
h['tasks'] = tasks.map { |task| task.as_json(only: :key) }
|
25
|
-
h['worker_logs'] = worker_logs.map { |worker_log| worker_log.as_json(only: :key) }
|
26
|
-
h
|
12
|
+
def self.full
|
13
|
+
includes(
|
14
|
+
:tasks => [{:trials => [:task, :slave]}, :taskset],
|
15
|
+
:slaves => [:trials],
|
16
|
+
:worker_logs => [:taskset]
|
17
|
+
)
|
27
18
|
end
|
28
19
|
|
29
|
-
|
30
|
-
h = as_json(except: :id)
|
31
|
-
h['slaves'] = slaves.map(&:as_full_json)
|
32
|
-
h['tasks'] = tasks.map(&:as_full_json)
|
33
|
-
h['worker_logs'] = worker_logs.map(&:as_full_json)
|
34
|
-
h
|
35
|
-
end
|
20
|
+
scope :by_redis_model, -> (_) { where(key: _.key) }
|
36
21
|
end
|
37
22
|
|
38
23
|
class Task < ActiveRecord::Base
|
39
|
-
include
|
40
|
-
|
24
|
+
include JSONConstructor::TaskJSONConstructor
|
41
25
|
belongs_to :taskset
|
42
26
|
has_many :trials
|
43
|
-
|
44
|
-
def as_short_json
|
45
|
-
h = as_json(except: [:id, :taskset_id, :trials],
|
46
|
-
include: { 'taskset' => { only: :key } })
|
47
|
-
h['trials'] = trials.map { |trial| trial.as_json(only: :key) }
|
48
|
-
h
|
49
|
-
end
|
50
|
-
|
51
|
-
def as_full_json
|
52
|
-
h = as_json(except: [:id, :taskset_id, :trials],
|
53
|
-
include: { 'taskset' => { only: :key } })
|
54
|
-
h['trials'] = trials.map(&:as_full_json)
|
55
|
-
h
|
56
|
-
end
|
57
27
|
end
|
58
28
|
|
59
29
|
class Trial < ActiveRecord::Base
|
60
|
-
include
|
61
|
-
|
30
|
+
include JSONConstructor::TrialJSONConstructor
|
31
|
+
include LogFilePersister
|
62
32
|
belongs_to :task
|
63
33
|
belongs_to :slave
|
64
|
-
|
65
|
-
|
66
|
-
as_json(except: [:id, :task_id, :slave_id],
|
67
|
-
include: { 'slave' => { only: :key }, 'task' => { only: :key } })
|
68
|
-
end
|
69
|
-
|
70
|
-
def as_short_json
|
71
|
-
as_full_json
|
72
|
-
end
|
34
|
+
save_as_file :stdout, suffix: 'stdout'
|
35
|
+
save_as_file :stderr, suffix: 'stderr'
|
73
36
|
end
|
74
37
|
|
75
38
|
class WorkerLog < ActiveRecord::Base
|
76
|
-
include
|
77
|
-
|
39
|
+
include JSONConstructor::WorkerLogJSONConstructor
|
40
|
+
include LogFilePersister
|
78
41
|
belongs_to :taskset
|
79
|
-
|
80
|
-
def as_full_json
|
81
|
-
as_json(except: [:id, :taskset_id, :worker_key],
|
82
|
-
include: { 'taskset' => { only: :key } },
|
83
|
-
methods: :worker)
|
84
|
-
end
|
85
|
-
|
86
|
-
def as_short_json
|
87
|
-
as_full_json
|
88
|
-
end
|
89
|
-
|
90
|
-
def worker
|
91
|
-
{ 'key' => worker_key }
|
92
|
-
end
|
42
|
+
save_as_file :log, suffix: 'worker_log'
|
93
43
|
end
|
94
44
|
|
95
45
|
class Slave < ActiveRecord::Base
|
96
|
-
include
|
97
|
-
|
46
|
+
include JSONConstructor::SlaveJSONConstructor
|
47
|
+
include LogFilePersister
|
98
48
|
has_many :trials
|
99
|
-
|
100
|
-
def as_full_json
|
101
|
-
as_json(except: [:id, :taskset_id],
|
102
|
-
include: { 'trials' => { only: :key } })
|
103
|
-
end
|
104
|
-
|
105
|
-
def as_short_json
|
106
|
-
as_full_json
|
107
|
-
end
|
49
|
+
save_as_file :log, suffix: 'slave_log'
|
108
50
|
end
|
109
51
|
end
|
110
52
|
end
|
@@ -62,17 +62,20 @@ module RRRSpec
|
|
62
62
|
p_slave
|
63
63
|
end
|
64
64
|
Persistence::Slave.import(p_slaves)
|
65
|
+
p_slaves.each { |p_slave| p_slave.run_callbacks(:save) {} }
|
65
66
|
end
|
66
67
|
|
67
68
|
ActiveRecord::Base.transaction do
|
68
|
-
|
69
|
+
p_tasks = taskset.tasks.map do |task|
|
69
70
|
h = task.to_h
|
70
71
|
h.delete('taskset')
|
71
72
|
h.delete('trials')
|
72
73
|
p_task = Persistence::Task.new(h)
|
73
74
|
p_task.taskset_id = p_taskset
|
74
75
|
p_task
|
75
|
-
end
|
76
|
+
end
|
77
|
+
Persistence::Task.import(p_tasks)
|
78
|
+
p_tasks.each { |p_task| p_task.run_callbacks(:save) {} }
|
76
79
|
end
|
77
80
|
|
78
81
|
p_slaves = {}
|
@@ -91,14 +94,16 @@ module RRRSpec
|
|
91
94
|
p_trial = Persistence::Trial.new(h)
|
92
95
|
p_trial.task_id = p_task
|
93
96
|
p_trial.slave_id = p_slaves[slave_key]
|
97
|
+
|
94
98
|
p_trials << p_trial
|
95
99
|
end
|
96
100
|
end
|
97
101
|
Persistence::Trial.import(p_trials)
|
102
|
+
p_trials.each { |p_trial| p_trial.run_callbacks(:save) {} }
|
98
103
|
end
|
99
104
|
|
100
105
|
ActiveRecord::Base.transaction do
|
101
|
-
|
106
|
+
p_worker_logs = taskset.worker_logs.map do |worker_log|
|
102
107
|
h = worker_log.to_h
|
103
108
|
h['worker_key'] = h['worker']['key']
|
104
109
|
h.delete('worker')
|
@@ -106,7 +111,9 @@ module RRRSpec
|
|
106
111
|
p_worker_log = Persistence::WorkerLog.new(h)
|
107
112
|
p_worker_log.taskset_id = p_taskset
|
108
113
|
p_worker_log
|
109
|
-
end
|
114
|
+
end
|
115
|
+
Persistence::WorkerLog.import(p_worker_logs)
|
116
|
+
p_worker_logs.each { |p_worker_log| p_worker_log.run_callbacks(:save) {} }
|
110
117
|
end
|
111
118
|
end
|
112
119
|
|
@@ -24,8 +24,8 @@ module RRRSpec
|
|
24
24
|
|
25
25
|
working_path = File.join(RRRSpec.configuration.working_dir, taskset.rsync_name)
|
26
26
|
FileUtils.mkdir_p(working_path) unless Dir.exists?(working_path)
|
27
|
-
remote_path =
|
28
|
-
command = "rsync #{
|
27
|
+
remote_path = File.join(RRRSpec.configuration.rsync_remote_path, taskset.rsync_name)
|
28
|
+
command = "rsync #{RRRSpec.configuration.rsync_options} #{remote_path}/ #{working_path}"
|
29
29
|
|
30
30
|
pid, out_rd, err_rd = execute_with_logs(working_path, command, {})
|
31
31
|
log_to_logger(logger, out_rd, err_rd)
|
@@ -150,7 +150,7 @@ module RRRSpec
|
|
150
150
|
(pid_to_slave_number.keys + [cancel_watcher_pid]).each do |pid|
|
151
151
|
begin
|
152
152
|
Process.kill("-TERM", pid)
|
153
|
-
rescue Errno::ESRCH
|
153
|
+
rescue Errno::ESRCH, Errno::EPERM
|
154
154
|
end
|
155
155
|
end
|
156
156
|
|
@@ -171,7 +171,7 @@ module RRRSpec
|
|
171
171
|
`ps aux | grep "rrrspec slave" | grep -v grep | awk '{print $2}'`.split("\n").map(&:to_i).each do |pid|
|
172
172
|
begin
|
173
173
|
Process.kill("KILL", pid)
|
174
|
-
rescue Errno::ESRCH
|
174
|
+
rescue Errno::ESRCH, Errno::EPERM
|
175
175
|
end
|
176
176
|
end
|
177
177
|
end
|
data/rrrspec-server.gemspec
CHANGED
@@ -29,10 +29,11 @@ Gem::Specification.new do |spec|
|
|
29
29
|
spec.add_development_dependency "rspec"
|
30
30
|
spec.add_development_dependency "sqlite3"
|
31
31
|
spec.add_development_dependency "timecop"
|
32
|
-
spec.add_dependency "activerecord", "~>
|
33
|
-
spec.add_dependency "activerecord-import", "~> 0.
|
32
|
+
spec.add_dependency "activerecord", "~> 4.0.2"
|
33
|
+
spec.add_dependency "activerecord-import", "~> 0.4.1"
|
34
34
|
spec.add_dependency "activesupport"
|
35
35
|
spec.add_dependency "bundler"
|
36
|
+
spec.add_dependency "facter"
|
36
37
|
spec.add_dependency "redis"
|
37
38
|
spec.add_dependency "rrrspec-client"
|
38
39
|
spec.add_dependency "thor"
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module RRRSpec
|
4
|
+
module Server
|
5
|
+
class TestClass
|
6
|
+
include LogFilePersister
|
7
|
+
def self.after_save(&block); end
|
8
|
+
save_as_file :log, suffix: 'log'
|
9
|
+
|
10
|
+
attr_accessor :key
|
11
|
+
end
|
12
|
+
|
13
|
+
describe LogFilePersister do
|
14
|
+
before do
|
15
|
+
RRRSpec.configuration = ServerConfiguration.new
|
16
|
+
RRRSpec.configuration.execute_log_text_path = '/tmp/log_path'
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#log_log_path' do
|
20
|
+
subject { TestClass.new }
|
21
|
+
|
22
|
+
it 'subsitutes colons in the key' do
|
23
|
+
subject.key = 'rrrspec:test'
|
24
|
+
expect(subject.log_log_path).to eq('/tmp/log_path/rrrspec/test_log.log')
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'subsitutes slashes in the key' do
|
28
|
+
subject.key = 'rrrspec:test/path'
|
29
|
+
expect(subject.log_log_path).to eq('/tmp/log_path/rrrspec/test_path_log.log')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -5,8 +5,9 @@ module RRRSpec
|
|
5
5
|
module Persistence
|
6
6
|
describe Taskset do
|
7
7
|
before do
|
8
|
-
RRRSpec.configuration =
|
8
|
+
RRRSpec.configuration = ServerConfiguration.new
|
9
9
|
RRRSpec.configuration.redis = @redis
|
10
|
+
RRRSpec.configuration.execute_log_text_path = Dir.mktmpdir
|
10
11
|
end
|
11
12
|
|
12
13
|
before do
|
@@ -4,8 +4,9 @@ module RRRSpec
|
|
4
4
|
module Server
|
5
5
|
describe Persister do
|
6
6
|
before do
|
7
|
-
RRRSpec.configuration =
|
7
|
+
RRRSpec.configuration = ServerConfiguration.new
|
8
8
|
RRRSpec.configuration.redis = @redis
|
9
|
+
RRRSpec.configuration.execute_log_text_path = Dir.mktmpdir
|
9
10
|
end
|
10
11
|
|
11
12
|
before do
|
@@ -13,6 +14,10 @@ module RRRSpec
|
|
13
14
|
RRRSpec.finished_fullset
|
14
15
|
end
|
15
16
|
|
17
|
+
after do
|
18
|
+
FileUtils.remove_entry_secure(RRRSpec.configuration.execute_log_text_path)
|
19
|
+
end
|
20
|
+
|
16
21
|
describe '.persist' do
|
17
22
|
let(:task_json) do
|
18
23
|
{
|
@@ -34,6 +39,16 @@ module RRRSpec
|
|
34
39
|
}
|
35
40
|
end
|
36
41
|
|
42
|
+
def log_path_of(filename)
|
43
|
+
File.join(RRRSpec.configuration.execute_log_text_path, filename)
|
44
|
+
end
|
45
|
+
|
46
|
+
let(:taskset_log_name) { @taskset.key.gsub(/\//, '_').gsub(/:/, '/') + "_log.log" }
|
47
|
+
let(:worker_log_name) { @worker_log.key.gsub(/\//, '_').gsub(/:/, '/') + "_worker_log.log" }
|
48
|
+
let(:slave_log_name) { @slave.key.gsub(/\//, '_').gsub(/:/, '/') + "_slave_log.log" }
|
49
|
+
let(:trial_stdout_name) { @trial.key.gsub(/\//, '_').gsub(/:/, '/') + "_stdout.log" }
|
50
|
+
let(:trial_stderr_name) { @trial.key.gsub(/\//, '_').gsub(/:/, '/') + "_stderr.log" }
|
51
|
+
|
37
52
|
def eq_json(actual, expected)
|
38
53
|
expect(
|
39
54
|
JSON.parse(JSON.generate(actual))
|
@@ -69,6 +84,28 @@ module RRRSpec
|
|
69
84
|
check_persistence
|
70
85
|
end
|
71
86
|
|
87
|
+
context 'when logs are too long' do
|
88
|
+
before do
|
89
|
+
RRRSpec.redis.hset(@trial.key, 'stdout', 'out' * 30000)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'saves full logs to the files' do
|
93
|
+
Persister.persist(@taskset)
|
94
|
+
expect(File.read(log_path_of(trial_stdout_name))).to eq(@trial.stdout)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
it 'creates log text files' do
|
100
|
+
Persister.persist(@taskset)
|
101
|
+
|
102
|
+
expect(File.read(log_path_of(taskset_log_name))).to eq(@taskset.log)
|
103
|
+
expect(File.read(log_path_of(worker_log_name))).to eq(@worker_log.log)
|
104
|
+
expect(File.read(log_path_of(slave_log_name))).to eq(@slave.log)
|
105
|
+
expect(File.read(log_path_of(trial_stdout_name))).to eq(@trial.stdout)
|
106
|
+
expect(File.read(log_path_of(trial_stderr_name))).to eq(@trial.stderr)
|
107
|
+
end
|
108
|
+
|
72
109
|
context "trial is finished after the taskset's finish" do
|
73
110
|
before do
|
74
111
|
@late_trial = Trial.create(@task, @slave)
|
metadata
CHANGED
@@ -1,195 +1,209 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rrrspec-server
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Masaya Suzuki
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-04-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: database_cleaner
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ~>
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: 1.2.0
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - ~>
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 1.2.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: mysql2
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- -
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: '0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rspec
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: sqlite3
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- -
|
73
|
+
- - ">="
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: '0'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- -
|
80
|
+
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: timecop
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- -
|
87
|
+
- - ">="
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: '0'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- -
|
94
|
+
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: activerecord
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- - ~>
|
101
|
+
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version:
|
103
|
+
version: 4.0.2
|
104
104
|
type: :runtime
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- - ~>
|
108
|
+
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version:
|
110
|
+
version: 4.0.2
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: activerecord-import
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
|
-
- - ~>
|
115
|
+
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: 0.
|
117
|
+
version: 0.4.1
|
118
118
|
type: :runtime
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
|
-
- - ~>
|
122
|
+
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: 0.
|
124
|
+
version: 0.4.1
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: activesupport
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
|
-
- -
|
129
|
+
- - ">="
|
130
130
|
- !ruby/object:Gem::Version
|
131
131
|
version: '0'
|
132
132
|
type: :runtime
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
|
-
- -
|
136
|
+
- - ">="
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '0'
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
140
|
name: bundler
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
142
142
|
requirements:
|
143
|
-
- -
|
143
|
+
- - ">="
|
144
144
|
- !ruby/object:Gem::Version
|
145
145
|
version: '0'
|
146
146
|
type: :runtime
|
147
147
|
prerelease: false
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
149
149
|
requirements:
|
150
|
-
- -
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: facter
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :runtime
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
151
165
|
- !ruby/object:Gem::Version
|
152
166
|
version: '0'
|
153
167
|
- !ruby/object:Gem::Dependency
|
154
168
|
name: redis
|
155
169
|
requirement: !ruby/object:Gem::Requirement
|
156
170
|
requirements:
|
157
|
-
- -
|
171
|
+
- - ">="
|
158
172
|
- !ruby/object:Gem::Version
|
159
173
|
version: '0'
|
160
174
|
type: :runtime
|
161
175
|
prerelease: false
|
162
176
|
version_requirements: !ruby/object:Gem::Requirement
|
163
177
|
requirements:
|
164
|
-
- -
|
178
|
+
- - ">="
|
165
179
|
- !ruby/object:Gem::Version
|
166
180
|
version: '0'
|
167
181
|
- !ruby/object:Gem::Dependency
|
168
182
|
name: rrrspec-client
|
169
183
|
requirement: !ruby/object:Gem::Requirement
|
170
184
|
requirements:
|
171
|
-
- -
|
185
|
+
- - ">="
|
172
186
|
- !ruby/object:Gem::Version
|
173
187
|
version: '0'
|
174
188
|
type: :runtime
|
175
189
|
prerelease: false
|
176
190
|
version_requirements: !ruby/object:Gem::Requirement
|
177
191
|
requirements:
|
178
|
-
- -
|
192
|
+
- - ">="
|
179
193
|
- !ruby/object:Gem::Version
|
180
194
|
version: '0'
|
181
195
|
- !ruby/object:Gem::Dependency
|
182
196
|
name: thor
|
183
197
|
requirement: !ruby/object:Gem::Requirement
|
184
198
|
requirements:
|
185
|
-
- -
|
199
|
+
- - ">="
|
186
200
|
- !ruby/object:Gem::Version
|
187
201
|
version: '0'
|
188
202
|
type: :runtime
|
189
203
|
prerelease: false
|
190
204
|
version_requirements: !ruby/object:Gem::Requirement
|
191
205
|
requirements:
|
192
|
-
- -
|
206
|
+
- - ">="
|
193
207
|
- !ruby/object:Gem::Version
|
194
208
|
version: '0'
|
195
209
|
description: Execute RSpec in a distributed manner
|
@@ -200,17 +214,22 @@ executables:
|
|
200
214
|
extensions: []
|
201
215
|
extra_rdoc_files: []
|
202
216
|
files:
|
203
|
-
- .rspec
|
217
|
+
- ".rspec"
|
204
218
|
- Gemfile
|
205
219
|
- Rakefile
|
206
220
|
- bin/rrrspec-server
|
207
221
|
- db/migrate/20131105050718_create_tables.rb
|
222
|
+
- db/migrate/20140221062741_expand_log.rb
|
223
|
+
- db/migrate/20140225062300_delete_logs.rb
|
208
224
|
- db/schema.rb
|
209
225
|
- lib/rrrspec/server.rb
|
210
226
|
- lib/rrrspec/server/arbiter.rb
|
211
227
|
- lib/rrrspec/server/cli.rb
|
212
228
|
- lib/rrrspec/server/configuration.rb
|
229
|
+
- lib/rrrspec/server/daemonizer.rb
|
213
230
|
- lib/rrrspec/server/dispatcher.rb
|
231
|
+
- lib/rrrspec/server/json_constructor.rb
|
232
|
+
- lib/rrrspec/server/log_file_persister.rb
|
214
233
|
- lib/rrrspec/server/persistent_models.rb
|
215
234
|
- lib/rrrspec/server/persister.rb
|
216
235
|
- lib/rrrspec/server/version.rb
|
@@ -219,6 +238,7 @@ files:
|
|
219
238
|
- spec/fixture.rb
|
220
239
|
- spec/rrrspec/server/arbiter_spec.rb
|
221
240
|
- spec/rrrspec/server/dispatcher_spec.rb
|
241
|
+
- spec/rrrspec/server/log_file_persister_spec.rb
|
222
242
|
- spec/rrrspec/server/persistent_models_spec.rb
|
223
243
|
- spec/rrrspec/server/persister_spec.rb
|
224
244
|
- spec/spec_helper.rb
|
@@ -233,17 +253,17 @@ require_paths:
|
|
233
253
|
- lib
|
234
254
|
required_ruby_version: !ruby/object:Gem::Requirement
|
235
255
|
requirements:
|
236
|
-
- -
|
256
|
+
- - ">="
|
237
257
|
- !ruby/object:Gem::Version
|
238
258
|
version: '0'
|
239
259
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
240
260
|
requirements:
|
241
|
-
- -
|
261
|
+
- - ">="
|
242
262
|
- !ruby/object:Gem::Version
|
243
263
|
version: '0'
|
244
264
|
requirements: []
|
245
265
|
rubyforge_project:
|
246
|
-
rubygems_version: 2.
|
266
|
+
rubygems_version: 2.2.2
|
247
267
|
signing_key:
|
248
268
|
specification_version: 4
|
249
269
|
summary: Execute RSpec in a distributed manner
|
@@ -251,6 +271,7 @@ test_files:
|
|
251
271
|
- spec/fixture.rb
|
252
272
|
- spec/rrrspec/server/arbiter_spec.rb
|
253
273
|
- spec/rrrspec/server/dispatcher_spec.rb
|
274
|
+
- spec/rrrspec/server/log_file_persister_spec.rb
|
254
275
|
- spec/rrrspec/server/persistent_models_spec.rb
|
255
276
|
- spec/rrrspec/server/persister_spec.rb
|
256
277
|
- spec/spec_helper.rb
|