rrrspec-server 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|