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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ffdf010821a66bdb732d362cfd9d3c33acf9e6ad
4
- data.tar.gz: 06a8ab51406619a53ffa3131c8a356d630b6e2ec
3
+ metadata.gz: 3dd5351ecddcebbc6fbc7f89e1102c06b02dcb21
4
+ data.tar.gz: 33b7df062b10d6f6d9099372c931d85663bb68bc
5
5
  SHA512:
6
- metadata.gz: 3b86f71da951714df9558c4918e6bcf36b29815d97aeb907e71212762566f089baaf6e6fcf514df6e3569c8bdec17b11e50690c0df3d803eb4079805132c45d1
7
- data.tar.gz: 3c0d6cd34faa4fc5b2853775c890053e27761f5d7e1f9eed510323b487e33206c82b983ce93b07d53e7ab8a1b5c4bf1b12565ea0f45adb29f51b151e72d8076d
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 to check this file into your version control system.
12
+ # It's strongly recommended that you check this file into your version control system.
13
13
 
14
- ActiveRecord::Schema.define(:version => 20131105050718) do
14
+ ActiveRecord::Schema.define(version: 20140225062300) do
15
15
 
16
- create_table "slaves", :force => true do |t|
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"], :name => "index_slaves_on_key"
24
- add_index "slaves", ["taskset_id"], :name => "index_slaves_on_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", :force => true do |t|
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"], :name => "index_tasks_on_key"
35
- add_index "tasks", ["status"], :name => "index_tasks_on_status"
36
- add_index "tasks", ["taskset_id"], :name => "index_tasks_on_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", :force => true do |t|
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"], :name => "index_tasksets_on_created_at"
56
- add_index "tasksets", ["key"], :name => "index_tasksets_on_key"
57
- add_index "tasksets", ["rsync_name"], :name => "index_tasksets_on_rsync_name"
58
- add_index "tasksets", ["status"], :name => "index_tasksets_on_status"
59
- add_index "tasksets", ["taskset_class"], :name => "index_tasksets_on_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", :force => true do |t|
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"], :name => "index_trials_on_key"
76
- add_index "trials", ["slave_id"], :name => "index_trials_on_slave_id"
77
- add_index "trials", ["task_id"], :name => "index_trials_on_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", :force => true do |t|
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"], :name => "index_worker_logs_on_key"
91
- add_index "worker_logs", ["taskset_id"], :name => "index_worker_logs_on_taskset_id"
92
- add_index "worker_logs", ["worker_key"], :name => "index_worker_logs_on_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
@@ -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'
@@ -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
- auto_rebirth do
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
- auto_rebirth do
97
- worker = Worker.create(RRRSpec.configuration.worker_type)
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 rsync_server and rsync_options and rsync_dir
19
- $stderr.puts('The rsync options are not set')
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 ActiveModel::Serializers::JSON
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
- scope :full, includes(
12
- :tasks => [{:trials => [:task, :slave]}, :taskset],
13
- :slaves => [:trials],
14
- :worker_logs => [:taskset]
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
- def as_full_json
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 ActiveModel::Serializers::JSON
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 ActiveModel::Serializers::JSON
61
-
30
+ include JSONConstructor::TrialJSONConstructor
31
+ include LogFilePersister
62
32
  belongs_to :task
63
33
  belongs_to :slave
64
-
65
- def as_full_json
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 ActiveModel::Serializers::JSON
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 ActiveModel::Serializers::JSON
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
- Persistence::Task.import(taskset.tasks.map do |task|
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
- Persistence::WorkerLog.import(taskset.worker_logs.map do |worker_log|
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
 
@@ -1,5 +1,5 @@
1
1
  module RRRSpec
2
2
  module Server
3
- VERSION = "0.2.0"
3
+ VERSION = "0.2.1"
4
4
  end
5
5
  end
@@ -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 = "#{RSyncInfo.rsync_server}:#{File.join(RSyncInfo.rsync_dir, taskset.rsync_name)}"
28
- command = "rsync #{RSyncInfo.rsync_options} #{remote_path}/ #{working_path}"
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
@@ -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", "~> 3.0"
33
- spec.add_dependency "activerecord-import", "~> 0.3.1"
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 = RRRSpec::Configuration.new
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 = Configuration.new
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.0
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-02-18 00:00:00.000000000 Z
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: '3.0'
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: '3.0'
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.3.1
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.3.1
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.0.3
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