mobilize-base 1.0.9 → 1.0.51
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.
- data/.gitignore +0 -1
- data/README.md +19 -21
- data/Rakefile +1 -1
- data/lib/mobilize-base/extensions/google_drive/client_login_fetcher.rb +11 -22
- data/lib/mobilize-base/extensions/google_drive/file.rb +1 -5
- data/lib/mobilize-base/extensions/google_drive/worksheet.rb +2 -3
- data/lib/mobilize-base/handlers/gbook.rb +4 -4
- data/lib/mobilize-base/handlers/gdrive.rb +0 -9
- data/lib/mobilize-base/handlers/gfile.rb +5 -8
- data/lib/mobilize-base/handlers/gridfs.rb +7 -7
- data/lib/mobilize-base/handlers/gsheet.rb +19 -25
- data/lib/mobilize-base/handlers/resque.rb +6 -6
- data/lib/mobilize-base/jobtracker.rb +10 -22
- data/lib/mobilize-base/models/dataset.rb +26 -25
- data/lib/mobilize-base/models/job.rb +6 -31
- data/lib/mobilize-base/models/runner.rb +27 -31
- data/lib/mobilize-base/models/task.rb +137 -0
- data/lib/mobilize-base/models/user.rb +1 -1
- data/lib/mobilize-base/{tasks.rb → rakes.rb} +8 -29
- data/lib/mobilize-base/tasks/mobilize-base.rake +2 -0
- data/lib/mobilize-base/version.rb +1 -1
- data/lib/mobilize-base.rb +1 -1
- data/mobilize-base.gemspec +1 -0
- data/test/base1_task1.yml +3 -0
- data/test/base_job_rows.yml +4 -4
- data/test/mobilize-base_test.rb +9 -17
- metadata +25 -8
- data/lib/mobilize-base/models/stage.rb +0 -141
- data/test/base1_stage1.yml +0 -3
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -120,7 +120,7 @@ same one that contains your Rakefile)
|
|
120
120
|
Inside the Rakefile in your project's root folder, make sure you have:
|
121
121
|
|
122
122
|
``` ruby
|
123
|
-
require 'mobilize-base/
|
123
|
+
require 'mobilize-base/rakes'
|
124
124
|
```
|
125
125
|
|
126
126
|
This defines rake tasks essential to run the environment.
|
@@ -390,7 +390,7 @@ Mobilize takes the environment from your Rails.env if you're running
|
|
390
390
|
Rails, or assumes "development." You can specify "development", "test",
|
391
391
|
or "production," as per the yml files.
|
392
392
|
|
393
|
-
Otherwise, it takes it from MOBILIZE_ENV parameter, as in:
|
393
|
+
Otherwise, it takes it from MOBILIZE_ENV parameter, set from irb, as in:
|
394
394
|
|
395
395
|
``` ruby
|
396
396
|
> ENV['MOBILIZE_ENV'] = 'production'
|
@@ -489,28 +489,26 @@ name>))` and enter values under each header:
|
|
489
489
|
|
490
490
|
* status Mobilize writes this field with the last status returned by the job
|
491
491
|
|
492
|
-
*
|
493
|
-
*
|
494
|
-
* handler specifies the file that should receive the
|
492
|
+
* task1..task5 List of tasks to be performed by the job.
|
493
|
+
* Tasks have this syntax: <handler>.<call> <params>.
|
494
|
+
* handler specifies the file that should receive the task
|
495
495
|
* the call specifies the method within the file. The method should
|
496
|
-
be called `"<
|
496
|
+
be called `"<Handler>.<call>_by_task_path"`
|
497
497
|
* the params the method accepts, which are custom to each
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
and simply "stage1" for the second test. Both of these take the output
|
511
|
-
from the first stage.
|
498
|
+
task. These should be a comma-delimited list, with each param in
|
499
|
+
quotes.
|
500
|
+
* For mobilize-base, the following tasks are available:
|
501
|
+
* gsheet.read `<input_gsheet_full_path>`, which reads the sheet.
|
502
|
+
* The gsheet_path should be of the form `<gbook_name>/<gsheet_name>`. The test uses
|
503
|
+
"Requestor_mobilize(test)/base1_task1.in".
|
504
|
+
* gsheet.write `<task_relative_path>`,`<output_gsheet_path>`,
|
505
|
+
which writes the specified task output to the output_gsheet.
|
506
|
+
* The task_path should be of the form `<task_column>` or
|
507
|
+
`<job_name/task_column>`. The test uses "base1/task1" for the first test
|
508
|
+
and simply "task1" for the second test. Both of these take the output
|
509
|
+
from the first task.
|
512
510
|
* The test uses "Requestor_mobilize(test)/base1.out" and
|
513
|
-
"Requestor_mobilize(test)/base2.out" for
|
511
|
+
"Requestor_mobilize(test)/base2.out" for output sheets.
|
514
512
|
|
515
513
|
<a name='section_Start_Run_Test'></a>
|
516
514
|
### Run Test
|
data/Rakefile
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
module GoogleDrive
|
2
2
|
class ClientLoginFetcher
|
3
3
|
def request_raw(method, url, data, extra_header, auth)
|
4
|
-
clf = self
|
5
4
|
#this is patched to handle server errors due to http chaos
|
6
5
|
uri = URI.parse(url)
|
7
6
|
response = nil
|
@@ -15,32 +14,22 @@ module GoogleDrive
|
|
15
14
|
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
16
15
|
#set 600 to allow for large downloads
|
17
16
|
http.read_timeout = 600
|
18
|
-
response =
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
end
|
24
|
-
if response.nil?
|
25
|
-
attempts +=1
|
26
|
-
else
|
27
|
-
if response.code.ie{|rcode| rcode.starts_with?("4") or rcode.starts_with?("5")}
|
28
|
-
if response.body.downcase.index("rate limit") or response.body.downcase.index("captcha")
|
29
|
-
if sleep_time
|
30
|
-
sleep_time = sleep_time * attempts
|
31
|
-
else
|
32
|
-
sleep_time = (rand*100).to_i
|
33
|
-
end
|
17
|
+
response = self.http_call(http, method, uri, data, extra_header, auth)
|
18
|
+
if response.code.ie{|rcode| rcode.starts_with?("4") or rcode.starts_with?("5")}
|
19
|
+
if response.body.downcase.index("rate limit") or response.body.downcase.index("captcha")
|
20
|
+
if sleep_time
|
21
|
+
sleep_time = sleep_time * attempts
|
34
22
|
else
|
35
|
-
sleep_time =
|
23
|
+
sleep_time = (rand*100).to_i
|
36
24
|
end
|
37
|
-
|
38
|
-
|
39
|
-
sleep sleep_time
|
25
|
+
else
|
26
|
+
sleep_time = 10
|
40
27
|
end
|
28
|
+
attempts += 1
|
29
|
+
puts "Sleeping for #{sleep_time.to_s} due to #{response.body}"
|
30
|
+
sleep sleep_time
|
41
31
|
end
|
42
32
|
end
|
43
|
-
raise "No response after 5 attempts" if response.nil?
|
44
33
|
raise response.body if response.code.ie{|rcode| rcode.starts_with?("4") or rcode.starts_with?("5")}
|
45
34
|
return response
|
46
35
|
end
|
@@ -54,11 +54,7 @@ module GoogleDrive
|
|
54
54
|
raise "Invalid role #{role}"
|
55
55
|
end
|
56
56
|
else
|
57
|
-
|
58
|
-
f.acl.push({:scope_type=>"user",:scope=>email,:role=>role})
|
59
|
-
rescue => exc
|
60
|
-
raise exc unless exc.to_s.index("user already has access")
|
61
|
-
end
|
57
|
+
f.acl.push({:scope_type=>"user",:scope=>email,:role=>role})
|
62
58
|
end
|
63
59
|
return true
|
64
60
|
end
|
@@ -137,9 +137,8 @@ module GoogleDrive
|
|
137
137
|
if loc_v != rem_v
|
138
138
|
if ['true','false'].include?(loc_v.downcase)
|
139
139
|
#google sheet upcases true and false. ignore
|
140
|
-
elsif loc_v.
|
141
|
-
#
|
142
|
-
#put a backtick on it.
|
140
|
+
elsif loc_v.starts_with?('rp') and rem_v.starts_with?('Rp')
|
141
|
+
# some other math bs
|
143
142
|
sheet[row_i+1,col_i+1] = %{'#{loc_v}}
|
144
143
|
re_col_vs << {'row_i'=>row_i+1,'col_i'=>col_i+1,'col_v'=>%{'#{loc_v}}}
|
145
144
|
elsif (loc_v.to_s.count('e')==1 or loc_v.to_s.count('e')==0) and
|
@@ -8,10 +8,10 @@ module Mobilize
|
|
8
8
|
dst = Dataset.find_or_create_by_handler_and_path('gbook',path)
|
9
9
|
#there should only be one book with each path, otherwise we have fail
|
10
10
|
book = nil
|
11
|
-
if books.length>1 and dst.
|
11
|
+
if books.length>1 and dst.url.to_s.length>0
|
12
12
|
#some idiot process created a duplicate book.
|
13
13
|
#Fix by renaming all but one with dst entry's key
|
14
|
-
dkey = dst.
|
14
|
+
dkey = dst.url.split("key=").last
|
15
15
|
books.each do |b|
|
16
16
|
bkey = b.resource_id.split(":").last
|
17
17
|
if bkey == dkey
|
@@ -30,9 +30,9 @@ module Mobilize
|
|
30
30
|
book = Gdrive.root(Gdrive.owner_email).create_spreadsheet(path)
|
31
31
|
("Created book #{path} at #{Time.now.utc.to_s}; Access at #{book.human_url}").oputs
|
32
32
|
end
|
33
|
-
#always make sure book dataset
|
33
|
+
#always make sure book dataset URL is up to date
|
34
34
|
#and that book has admin acl
|
35
|
-
dst.update_attributes(:
|
35
|
+
dst.update_attributes(:url=>book.human_url)
|
36
36
|
book.add_admin_acl
|
37
37
|
return book
|
38
38
|
end
|
@@ -54,15 +54,6 @@ module Mobilize
|
|
54
54
|
return false
|
55
55
|
end
|
56
56
|
|
57
|
-
def Gdrive.unslot_worker_by_path(path)
|
58
|
-
begin
|
59
|
-
Mobilize::Resque.set_worker_args_by_path(path,{'gdrive_slot'=>nil})
|
60
|
-
return true
|
61
|
-
rescue
|
62
|
-
return false
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
57
|
def Gdrive.root(gdrive_slot=nil)
|
67
58
|
pw = Gdrive.password(gdrive_slot)
|
68
59
|
GoogleDrive.login(gdrive_slot,pw)
|
@@ -22,16 +22,13 @@ module Mobilize
|
|
22
22
|
Gdrive.files(gdrive_slot,{"title"=>path,"title-exact"=>"true"}).first
|
23
23
|
end
|
24
24
|
|
25
|
-
def Gfile.
|
25
|
+
def Gfile.read_by_task_path(task_path)
|
26
26
|
#reserve gdrive_slot account for read
|
27
|
-
gdrive_slot = Gdrive.slot_worker_by_path(
|
27
|
+
gdrive_slot = Gdrive.slot_worker_by_path(t.path)
|
28
28
|
return false unless gdrive_slot
|
29
|
-
|
30
|
-
gfile_path =
|
31
|
-
|
32
|
-
#use Gridfs to cache result
|
33
|
-
out_url = "gridfs://#{s.path}/out"
|
34
|
-
Dataset.write_to_url(out_url,out_tsv)
|
29
|
+
t = Task.where(:path=>task_path)
|
30
|
+
gfile_path = t.params.first
|
31
|
+
Gfile.find_by_path(gfile_path,gdrive_slot).read
|
35
32
|
end
|
36
33
|
end
|
37
34
|
end
|
@@ -11,29 +11,29 @@ module Mobilize
|
|
11
11
|
return ::Mongo::GridFileSystem.new(::Mongo::Connection.new(host,port).db(database_name))
|
12
12
|
end
|
13
13
|
|
14
|
-
def Gridfs.
|
14
|
+
def Gridfs.read(path)
|
15
15
|
begin
|
16
|
-
zs=Gridfs.grid.open(
|
16
|
+
zs=Gridfs.grid.open(path.gridsafe,'r').read
|
17
17
|
return ::Zlib::Inflate.inflate(zs)
|
18
18
|
rescue
|
19
19
|
return nil
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
def Gridfs.
|
23
|
+
def Gridfs.write(path,string)
|
24
24
|
zs = ::Zlib::Deflate.deflate(string)
|
25
25
|
raise "compressed string too large for Gridfs write" if zs.length > Gridfs.config['max_compressed_write_size']
|
26
|
-
curr_zs = Gridfs.
|
26
|
+
curr_zs = Gridfs.read(path.gridsafe).to_s
|
27
27
|
#write a new version when there is a change
|
28
28
|
if curr_zs != zs
|
29
|
-
Gridfs.grid.open(
|
29
|
+
Gridfs.grid.open(path.gridsafe,'w',:versions => Gridfs.config['max_versions']){|f| f.write(zs)}
|
30
30
|
end
|
31
31
|
return true
|
32
32
|
end
|
33
33
|
|
34
|
-
def Gridfs.delete(
|
34
|
+
def Gridfs.delete(path)
|
35
35
|
begin
|
36
|
-
Gridfs.grid.delete(
|
36
|
+
Gridfs.grid.delete(path.gridsafe)
|
37
37
|
return true
|
38
38
|
rescue
|
39
39
|
return nil
|
@@ -32,35 +32,32 @@ module Mobilize
|
|
32
32
|
return sheet
|
33
33
|
end
|
34
34
|
|
35
|
-
def Gsheet.
|
35
|
+
def Gsheet.read_by_task_path(task_path)
|
36
36
|
#reserve gdrive_slot account for read
|
37
|
-
gdrive_slot = Gdrive.slot_worker_by_path(
|
37
|
+
gdrive_slot = Gdrive.slot_worker_by_path(task_path)
|
38
38
|
return false unless gdrive_slot
|
39
|
-
|
40
|
-
gsheet_path =
|
41
|
-
|
42
|
-
#use Gridfs to cache result
|
43
|
-
out_url = "gridfs://#{s.path}/out"
|
44
|
-
Dataset.write_to_url(out_url,out_tsv)
|
39
|
+
t = Task.where(:path=>task_path).first
|
40
|
+
gsheet_path = t.params.first
|
41
|
+
Gsheet.find_by_path(gsheet_path,gdrive_slot).to_tsv
|
45
42
|
end
|
46
43
|
|
47
|
-
def Gsheet.
|
48
|
-
gdrive_slot = Gdrive.slot_worker_by_path(
|
49
|
-
#return
|
50
|
-
return
|
51
|
-
|
52
|
-
source =
|
53
|
-
target_path =
|
54
|
-
source_job_name,
|
44
|
+
def Gsheet.write_by_task_path(task_path)
|
45
|
+
gdrive_slot = Gdrive.slot_worker_by_path(task_path)
|
46
|
+
#return false if there are no emails available
|
47
|
+
return false unless gdrive_slot
|
48
|
+
t = Task.where(:path=>task_path).first
|
49
|
+
source = t.params.first
|
50
|
+
target_path = t.params.second
|
51
|
+
source_job_name, source_task_name = if source.index("/")
|
55
52
|
source.split("/")
|
56
53
|
else
|
57
54
|
[nil, source]
|
58
55
|
end
|
59
|
-
|
60
|
-
|
61
|
-
tsv =
|
56
|
+
source_task_path = "#{t.job.runner.path}/#{source_job_name || t.job.name}/#{source_task_name}"
|
57
|
+
source_task = Task.where(:path=>source_task_path).first
|
58
|
+
tsv = source_task.stdout_dataset.read_cache
|
62
59
|
sheet_name = target_path.split("/").last
|
63
|
-
temp_path = [
|
60
|
+
temp_path = [task_path.gridsafe,sheet_name].join("/")
|
64
61
|
temp_sheet = Gsheet.find_or_create_by_path(temp_path,gdrive_slot)
|
65
62
|
temp_sheet.write(tsv)
|
66
63
|
temp_sheet.check_and_fix(tsv)
|
@@ -68,11 +65,8 @@ module Mobilize
|
|
68
65
|
target_sheet.merge(temp_sheet)
|
69
66
|
#delete the temp sheet's book
|
70
67
|
temp_sheet.spreadsheet.delete
|
71
|
-
|
72
|
-
|
73
|
-
#use Gridfs to cache result
|
74
|
-
out_url = "gridfs://#{s.path}/out"
|
75
|
-
Dataset.write_to_url(out_url,status)
|
68
|
+
"Write successful for #{target_path}".oputs
|
69
|
+
return true
|
76
70
|
end
|
77
71
|
end
|
78
72
|
end
|
@@ -17,6 +17,7 @@ module Mobilize
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def Resque.workers(state="all")
|
20
|
+
raise "invalid state #{state}" unless ['all','idle','working','timeout'].include?(state)
|
20
21
|
workers = ::Resque.workers.select{|w| w.queues.first == Resque.queue_name}
|
21
22
|
return workers if state == 'all'
|
22
23
|
working_workers = workers.select{|w| w.job['queue']== Resque.queue_name}
|
@@ -27,7 +28,6 @@ module Mobilize
|
|
27
28
|
return stale_workers if state == 'stale'
|
28
29
|
timeout_workers = workers.select{|w| w.job['payload'] and w.job['payload']['class']!='Jobtracker' and w.job['runat'] < (Time.now.utc - Jobtracker.max_run_time)}
|
29
30
|
return timeout_workers if state == 'timeout'
|
30
|
-
raise "invalid state #{state}"
|
31
31
|
end
|
32
32
|
|
33
33
|
def Resque.failures
|
@@ -36,6 +36,7 @@ module Mobilize
|
|
36
36
|
|
37
37
|
#active state refers to jobs that are either queued or working
|
38
38
|
def Resque.jobs(state="active")
|
39
|
+
raise "invalid state #{state}" unless ['all','queued','working','active','timeout','failed'].include?(state)
|
39
40
|
working_jobs = Resque.workers('working').map{|w| w.job['payload']}
|
40
41
|
return working_jobs if state == 'working'
|
41
42
|
queued_jobs = ::Resque.peek(Resque.queue_name,0,0).to_a
|
@@ -46,17 +47,16 @@ module Mobilize
|
|
46
47
|
timeout_jobs = Resque.workers("timeout").map{|w| w.job['payload']}
|
47
48
|
return timeout_jobs if state == 'timeout'
|
48
49
|
return working_jobs + queued_jobs + failed_jobs if state == 'all'
|
49
|
-
raise "invalid state #{state}"
|
50
50
|
end
|
51
51
|
|
52
52
|
def Resque.active_paths
|
53
|
-
#first argument of the payload is the runner /
|
54
|
-
Resque.jobs('active').
|
53
|
+
#first argument of the payload is the runner / task path unless the worker is Jobtracker
|
54
|
+
Resque.jobs('active').map{|j| j['args'].first unless j['class']=='Jobtracker'}.compact
|
55
55
|
end
|
56
56
|
|
57
57
|
#Resque workers and methods to find
|
58
58
|
def Resque.find_worker_by_path(path)
|
59
|
-
Resque.workers('working').select{|w| w.job
|
59
|
+
Resque.workers('working').select{|w| w.job['payload'] and w.job['payload']['args'].first == path}.first
|
60
60
|
end
|
61
61
|
|
62
62
|
def Resque.set_worker_args_by_path(path,args)
|
@@ -141,7 +141,7 @@ module Mobilize
|
|
141
141
|
return true
|
142
142
|
end
|
143
143
|
|
144
|
-
def Resque.
|
144
|
+
def Resque.kill_idle_stale_workers
|
145
145
|
idle_pids = Resque.workers('idle').select{|w| w.job=={}}.map{|w| w.to_s.split(":").second}
|
146
146
|
stale_pids = Resque.workers('stale').select{|w| w.job=={}}.map{|w| w.to_s.split(":").second}
|
147
147
|
idle_stale_pids = (idle_pids & stale_pids)
|
@@ -89,7 +89,7 @@ module Mobilize
|
|
89
89
|
|
90
90
|
def Jobtracker.start
|
91
91
|
if Jobtracker.status!='stopped'
|
92
|
-
|
92
|
+
raise "Jobtracker still #{Jobtracker.status}"
|
93
93
|
else
|
94
94
|
#make sure that workers are running and at the right number
|
95
95
|
#Resque.prep_workers
|
@@ -190,9 +190,7 @@ module Mobilize
|
|
190
190
|
while Jobtracker.status != 'stopping'
|
191
191
|
users = User.all
|
192
192
|
Jobtracker.run_notifications
|
193
|
-
|
194
|
-
#so none are privileged on JT restarts
|
195
|
-
users.sort_by{rand}.each do |u|
|
193
|
+
users.each do |u|
|
196
194
|
r = u.runner
|
197
195
|
Jobtracker.update_status("Checking #{r.path}")
|
198
196
|
if r.is_due?
|
@@ -208,25 +206,15 @@ module Mobilize
|
|
208
206
|
|
209
207
|
def Jobtracker.deployed_at
|
210
208
|
#assumes deploy is as of last commit, or as of last deploy time
|
211
|
-
#as given by the
|
209
|
+
#as given by the least recently updated file in the root folder
|
212
210
|
deploy_time = begin
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
if lsr.length == 8
|
221
|
-
#ubuntu
|
222
|
-
lsr[5..6].join(" ")
|
223
|
-
elsif lsr.length == 9
|
224
|
-
#osx
|
225
|
-
lsr[5..7].join(" ")
|
226
|
-
end
|
227
|
-
end.first
|
228
|
-
mod_time
|
229
|
-
end.to_s.strip
|
211
|
+
%{git log -1 --format="%cd"}.bash
|
212
|
+
rescue
|
213
|
+
ls_string = "ls -l #{ENV['PWD']}/*".bash
|
214
|
+
ls_rows = ls_string.split("\n").map{|lss| lss.strip.split(" ")}
|
215
|
+
mod_times = ls_rows.select{|lsr| lsr.length == 8}.map{|lsr| lsr[5..6].join(" ")}
|
216
|
+
mod_times.min
|
217
|
+
end
|
230
218
|
Time.parse(deploy_time)
|
231
219
|
end
|
232
220
|
|
@@ -4,7 +4,7 @@ module Mobilize
|
|
4
4
|
include Mongoid::Timestamps
|
5
5
|
field :handler, type: String
|
6
6
|
field :path, type: String
|
7
|
-
field :
|
7
|
+
field :url, type: String
|
8
8
|
field :raw_size, type: Fixnum
|
9
9
|
field :last_cached_at, type: Time
|
10
10
|
field :last_cache_handler, type: String
|
@@ -18,16 +18,6 @@ module Mobilize
|
|
18
18
|
return "Mobilize::#{dst.handler.humanize}".constantize.read_by_path(dst.path)
|
19
19
|
end
|
20
20
|
|
21
|
-
def Dataset.find_by_url(url)
|
22
|
-
handler,path = url.split("://")
|
23
|
-
Dataset.find_by_handler_and_path(handler,path)
|
24
|
-
end
|
25
|
-
|
26
|
-
def Dataset.find_or_create_by_url(url)
|
27
|
-
handler,path = url.split("://")
|
28
|
-
Dataset.find_or_create_by_handler_and_path(handler,path)
|
29
|
-
end
|
30
|
-
|
31
21
|
def Dataset.find_by_handler_and_path(handler,path)
|
32
22
|
Dataset.where(handler: handler, path: path).first
|
33
23
|
end
|
@@ -38,30 +28,41 @@ module Mobilize
|
|
38
28
|
return dst
|
39
29
|
end
|
40
30
|
|
41
|
-
def
|
42
|
-
dst =
|
43
|
-
dst.
|
44
|
-
|
31
|
+
def write(string)
|
32
|
+
dst = self
|
33
|
+
"Mobilize::#{dst.handler.humanize}".constantize.write_by_path(dst.path,string)
|
34
|
+
dst.raw_size = string.length
|
35
|
+
dst.save!
|
36
|
+
return true
|
45
37
|
end
|
46
38
|
|
47
|
-
def
|
39
|
+
def cache_valid?
|
48
40
|
dst = self
|
49
|
-
dst.
|
50
|
-
"Mobilize::#{dst.handler.humanize}".constantize.read_by_dataset_path(dst.path)
|
41
|
+
return true if dst.last_cached_at and (dst.cache_expire_at.nil? or dst.cache_expire_at > Time.now.utc)
|
51
42
|
end
|
52
43
|
|
53
|
-
def
|
44
|
+
def read_cache(cache_handler="gridfs")
|
54
45
|
dst = self
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
46
|
+
if cache_valid?
|
47
|
+
dst.update_attributes(:last_read_at=>Time.now.utc)
|
48
|
+
return "Mobilize::#{cache_handler.humanize}".constantize.read([dst.handler,dst.path].join("://"))
|
49
|
+
else
|
50
|
+
raise "Cache invalid or not found for #{cache_handler}://#{dst.path}"
|
51
|
+
end
|
59
52
|
end
|
60
53
|
|
61
|
-
def
|
54
|
+
def write_cache(string,expire_at=nil,cache_handler="gridfs")
|
62
55
|
dst = self
|
63
|
-
"Mobilize::#{
|
56
|
+
"Mobilize::#{cache_handler.humanize}".constantize.write([dst.handler,dst.path].join("://"),string)
|
57
|
+
dst.update_attributes(:last_cached_at=>Time.now.utc,
|
58
|
+
:last_cache_handler=>cache_handler.to_s.downcase,
|
59
|
+
:cache_expire_at=>expire_at,
|
60
|
+
:size=>string.length)
|
64
61
|
return true
|
65
62
|
end
|
63
|
+
|
64
|
+
def delete_cache(cache_handler="gridfs")
|
65
|
+
return "Mobilize::#{cache_handler.humanize}".constantize.delete(dst.handler, dst.path)
|
66
|
+
end
|
66
67
|
end
|
67
68
|
end
|
@@ -5,6 +5,8 @@ module Mobilize
|
|
5
5
|
field :path, type: String
|
6
6
|
field :active, type: Boolean
|
7
7
|
field :trigger, type: String
|
8
|
+
field :status, type: String
|
9
|
+
field :last_completed_at, type: Time
|
8
10
|
|
9
11
|
index({ path: 1})
|
10
12
|
|
@@ -13,9 +15,9 @@ module Mobilize
|
|
13
15
|
j.path.split("/").last
|
14
16
|
end
|
15
17
|
|
16
|
-
def
|
18
|
+
def tasks
|
17
19
|
j = self
|
18
|
-
|
20
|
+
Task.where(:path=>/^#{j.path.escape_regex}/).to_a.sort_by{|t| t.path}
|
19
21
|
end
|
20
22
|
|
21
23
|
def Job.find_or_create_by_path(path)
|
@@ -24,33 +26,6 @@ module Mobilize
|
|
24
26
|
return j
|
25
27
|
end
|
26
28
|
|
27
|
-
def status
|
28
|
-
#last stage status
|
29
|
-
j = self
|
30
|
-
j.active_stage.status if j.active_stage
|
31
|
-
end
|
32
|
-
|
33
|
-
def active_stage
|
34
|
-
j = self
|
35
|
-
#latest started at or first
|
36
|
-
j.stages.select{|s| s.started_at}.sort_by{|s| s.started_at}.last || j.stages.first
|
37
|
-
end
|
38
|
-
|
39
|
-
def completed_at
|
40
|
-
j = self
|
41
|
-
j.stages.last.completed_at if j.stages.last
|
42
|
-
end
|
43
|
-
|
44
|
-
def failed_at
|
45
|
-
j = self
|
46
|
-
j.active_stage.failed_at if j.active_stage
|
47
|
-
end
|
48
|
-
|
49
|
-
def status_at
|
50
|
-
j = self
|
51
|
-
j.active_stage.status_at if j.active_stage
|
52
|
-
end
|
53
|
-
|
54
29
|
#convenience methods
|
55
30
|
def runner
|
56
31
|
j = self
|
@@ -60,13 +35,13 @@ module Mobilize
|
|
60
35
|
|
61
36
|
def is_working?
|
62
37
|
j = self
|
63
|
-
j.
|
38
|
+
j.tasks.select{|t| t.is_working?}.compact.length>0
|
64
39
|
end
|
65
40
|
|
66
41
|
def is_due?
|
67
42
|
j = self
|
68
43
|
return false if j.is_working? or j.active == false or j.trigger.to_s.starts_with?("after")
|
69
|
-
last_run = j.
|
44
|
+
last_run = j.last_completed_at
|
70
45
|
#check trigger
|
71
46
|
trigger = j.trigger
|
72
47
|
return true if trigger == 'once'
|
@@ -5,19 +5,17 @@ module Mobilize
|
|
5
5
|
field :path, type: String
|
6
6
|
field :active, type: Boolean
|
7
7
|
field :status, type: String
|
8
|
-
field :
|
9
|
-
field :status_at, type: Time
|
10
|
-
field :completed_at, type: Time
|
8
|
+
field :last_run, type: Time
|
11
9
|
|
12
10
|
index({ path: 1})
|
13
11
|
|
14
12
|
def headers
|
15
|
-
%w{name active trigger status
|
13
|
+
%w{name active trigger status task1 task2 task3 task4 task5}
|
16
14
|
end
|
17
15
|
|
18
|
-
def
|
16
|
+
def last_cached_at
|
19
17
|
r = self
|
20
|
-
Dataset.find_or_create_by_path(r.path).
|
18
|
+
Dataset.find_or_create_by_path(r.path).last_cached_at
|
21
19
|
end
|
22
20
|
|
23
21
|
def worker
|
@@ -41,7 +39,6 @@ module Mobilize
|
|
41
39
|
r.update_status("no gdrive slot available")
|
42
40
|
return false
|
43
41
|
end
|
44
|
-
r.update_attributes(:started_at=>Time.now.utc)
|
45
42
|
#make sure any updates to activity are processed first
|
46
43
|
#as in when someone runs a "once" job that has completed
|
47
44
|
r.update_gsheet(gdrive_slot)
|
@@ -51,15 +48,15 @@ module Mobilize
|
|
51
48
|
r.jobs.each do |j|
|
52
49
|
begin
|
53
50
|
if j.is_due?
|
54
|
-
j.
|
51
|
+
j.tasks.first.enqueue!
|
55
52
|
end
|
56
53
|
rescue ScriptError, StandardError => exc
|
57
|
-
|
54
|
+
j.update_status("Failed to enqueue with #{exc.to_s}")
|
58
55
|
j.update_attributes(:active=>false)
|
59
56
|
end
|
60
57
|
end
|
61
58
|
r.update_gsheet(gdrive_slot)
|
62
|
-
r.update_attributes(:
|
59
|
+
r.update_attributes(:last_run=>Time.now.utc)
|
63
60
|
end
|
64
61
|
|
65
62
|
def dataset
|
@@ -71,16 +68,16 @@ module Mobilize
|
|
71
68
|
Runner.where(:path=>path).first || Runner.create(:path=>path,:active=>true)
|
72
69
|
end
|
73
70
|
|
74
|
-
def
|
71
|
+
def read_cache
|
75
72
|
r = self
|
76
|
-
|
73
|
+
r.dataset.read_cache
|
77
74
|
end
|
78
75
|
|
79
76
|
def gsheet(gdrive_slot)
|
80
77
|
r = self
|
81
78
|
jobs_sheet = Gsheet.find_or_create_by_path(r.path,gdrive_slot)
|
82
79
|
jobs_sheet.add_headers(r.headers)
|
83
|
-
|
80
|
+
jobs_sheet.delete_sheet1
|
84
81
|
return jobs_sheet
|
85
82
|
end
|
86
83
|
|
@@ -88,7 +85,7 @@ module Mobilize
|
|
88
85
|
r = self
|
89
86
|
gsheet_tsv = r.gsheet(gdrive_slot).to_tsv
|
90
87
|
#cache in DB
|
91
|
-
r.
|
88
|
+
r.dataset.write_cache(gsheet_tsv)
|
92
89
|
#turn it into a hash array
|
93
90
|
gsheet_jobs = gsheet_tsv.tsv_to_hash_array
|
94
91
|
#go through each job, update relevant job with its params
|
@@ -96,25 +93,25 @@ module Mobilize
|
|
96
93
|
#parse out the jobs and update the Job collection
|
97
94
|
gsheet_jobs.each_with_index do |rj,rj_i|
|
98
95
|
#skip non-jobs or jobs without required values
|
99
|
-
next if (rj['name'].to_s.first == "#" or ['name','active','trigger','
|
96
|
+
next if (rj['name'].to_s.first == "#" or ['name','active','trigger','task1'].select{|c| rj[c].to_s.strip==""}.length>0)
|
100
97
|
j = Job.find_or_create_by_path("#{r.path}/#{rj['name']}")
|
101
98
|
#update top line params
|
102
99
|
j.update_attributes(:active => rj['active'],
|
103
100
|
:trigger => rj['trigger'])
|
104
|
-
(1..5).to_a.each do |
|
105
|
-
|
106
|
-
break if
|
107
|
-
|
108
|
-
#parse command string, update
|
109
|
-
|
110
|
-
|
111
|
-
|
101
|
+
(1..5).to_a.each do |t_idx|
|
102
|
+
task_string = rj["task#{t_idx.to_s}"]
|
103
|
+
break if task_string.to_s.length==0
|
104
|
+
t = Task.find_or_create_by_path("#{j.path}/task#{t_idx.to_s}")
|
105
|
+
#parse command string, update task with it
|
106
|
+
t_handler, call, param_string = [""*3]
|
107
|
+
task_string.split(" ").ie do |spls|
|
108
|
+
t_handler = spls.first.split(".").first
|
112
109
|
call = spls.first.split(".").last
|
113
110
|
param_string = spls[1..-1].join(" ").strip
|
114
111
|
end
|
115
|
-
|
112
|
+
t.update_attributes(:call=>call, :handler=>t_handler, :param_string=>param_string)
|
116
113
|
end
|
117
|
-
r.update_status("Updated #{j.path}
|
114
|
+
r.update_status("Updated #{j.path} tasks at #{Time.now.utc}")
|
118
115
|
#add this job to list of read ones
|
119
116
|
done_jobs << j
|
120
117
|
end
|
@@ -131,8 +128,7 @@ module Mobilize
|
|
131
128
|
def update_gsheet(gdrive_slot)
|
132
129
|
r = self
|
133
130
|
jobs_gsheet = r.gsheet(gdrive_slot)
|
134
|
-
|
135
|
-
upd_rows = upd_jobs.map{|j| {'name'=>j.name, 'active'=>j.active, 'status'=>j.status}}
|
131
|
+
upd_rows = r.jobs.map{|j| {'name'=>j.name, 'active'=>j.active, 'status'=>j.status} }
|
136
132
|
jobs_gsheet.add_or_update_rows(upd_rows)
|
137
133
|
r.update_status("gsheet updated")
|
138
134
|
return true
|
@@ -151,12 +147,12 @@ module Mobilize
|
|
151
147
|
def user
|
152
148
|
r = self
|
153
149
|
user_name = r.path.split("_").second.split("(").first.split("/").first
|
154
|
-
User.where(:
|
150
|
+
User.where(:email=>[user_name,Gdrive.domain].join("@")).first
|
155
151
|
end
|
156
152
|
|
157
153
|
def update_status(msg)
|
158
154
|
r = self
|
159
|
-
r.update_attributes(:status=>msg
|
155
|
+
r.update_attributes(:status=>msg)
|
160
156
|
Mobilize::Resque.set_worker_args_by_path(r.path,{'status'=>msg})
|
161
157
|
return true
|
162
158
|
end
|
@@ -169,8 +165,8 @@ module Mobilize
|
|
169
165
|
def is_due?
|
170
166
|
r = self.reload
|
171
167
|
return false if r.is_working?
|
172
|
-
|
173
|
-
return true if r.
|
168
|
+
last_due_time = Time.now.utc - Jobtracker.runner_read_freq
|
169
|
+
return true if r.last_run.nil? or r.last_run < last_due_time
|
174
170
|
end
|
175
171
|
|
176
172
|
def enqueue!
|
@@ -0,0 +1,137 @@
|
|
1
|
+
module Mobilize
|
2
|
+
class Task
|
3
|
+
include Mongoid::Document
|
4
|
+
include Mongoid::Timestamps
|
5
|
+
field :path, type: String
|
6
|
+
field :handler, type: String
|
7
|
+
field :call, type: String
|
8
|
+
field :param_string, type: Array
|
9
|
+
field :status, type: String
|
10
|
+
field :last_completed_at, type: Time
|
11
|
+
field :last_run_at, type: Time
|
12
|
+
|
13
|
+
index({ path: 1})
|
14
|
+
|
15
|
+
def idx
|
16
|
+
t = self
|
17
|
+
t.path.split("/").last.gsub("task","").to_i
|
18
|
+
end
|
19
|
+
|
20
|
+
def stdout_dataset
|
21
|
+
t = self
|
22
|
+
Dataset.find_or_create_by_handler_and_path("gridfs","#{t.path}/stdout")
|
23
|
+
end
|
24
|
+
|
25
|
+
def stderr_dataset
|
26
|
+
t = self
|
27
|
+
Dataset.find_or_create_by_handler_and_path("gridfs","#{t.path}/stderr")
|
28
|
+
end
|
29
|
+
|
30
|
+
def params
|
31
|
+
t = self
|
32
|
+
t.param_string.split(",").map do |p|
|
33
|
+
ps = p.strip
|
34
|
+
ps = ps[1..-1] if ['"',"'"].include?(ps[0])
|
35
|
+
ps = ps[0..-2] if ['"',"'"].include?(ps[-1])
|
36
|
+
ps
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def job
|
41
|
+
t = self
|
42
|
+
job_path = t.path.split("/")[0..-2].join("/")
|
43
|
+
Job.where(:path=>job_path).first
|
44
|
+
end
|
45
|
+
|
46
|
+
def Task.find_or_create_by_path(path)
|
47
|
+
t = Task.where(:path=>path).first
|
48
|
+
t = Task.create(:path=>path) unless t
|
49
|
+
return t
|
50
|
+
end
|
51
|
+
|
52
|
+
def prior
|
53
|
+
t = self
|
54
|
+
j = t.job
|
55
|
+
return nil if t.idx==1
|
56
|
+
return j.tasks[t.idx-2]
|
57
|
+
end
|
58
|
+
|
59
|
+
def next
|
60
|
+
t = self
|
61
|
+
j = t.job
|
62
|
+
return nil if t.idx == j.tasks.length
|
63
|
+
return j.tasks[t.idx]
|
64
|
+
end
|
65
|
+
|
66
|
+
def Task.perform(id,*args)
|
67
|
+
t = Task.where(:path=>id).first
|
68
|
+
j = t.job
|
69
|
+
t.update_status(%{Starting at #{Time.now.utc}})
|
70
|
+
stdout, stderr = [nil,nil]
|
71
|
+
begin
|
72
|
+
stdout = "Mobilize::#{t.handler.humanize}".constantize.send("#{t.call}_by_task_path",t.path).to_s
|
73
|
+
rescue ScriptError, StandardError => exc
|
74
|
+
stderr = [exc.to_s,exc.backtrace.to_s].join("\n")
|
75
|
+
#record the failure in Job so it appears on Runner
|
76
|
+
j.update_attributes(:status=>"Failed at #{Time.now.utc.to_s}")
|
77
|
+
t.update_attributes(:status=>"Failed at #{Time.now.utc.to_s}")
|
78
|
+
raise exc
|
79
|
+
end
|
80
|
+
if stdout == false
|
81
|
+
#re-queue self if output is false
|
82
|
+
t.enqueue!
|
83
|
+
return false
|
84
|
+
end
|
85
|
+
#write output to cache
|
86
|
+
t.stdout_dataset.write_cache(stdout)
|
87
|
+
t.update_attributes(:status=>"Completed at #{Time.now.utc.to_s}")
|
88
|
+
if t.idx == j.tasks.length
|
89
|
+
j.update_attributes(:status=>"Completed at #{Time.now.utc.to_s}",:last_completed_at=>Time.now.utc)
|
90
|
+
j.update_attributes(:active=>false) if j.trigger.strip == "once"
|
91
|
+
t.update_attributes(:status=>"Completed at #{Time.now.utc.to_s}",:last_completed_at=>Time.now.utc)
|
92
|
+
#check for any dependent jobs, if there are, enqueue them
|
93
|
+
r = j.runner
|
94
|
+
dep_jobs = r.jobs.select{|dj| dj.active==true and dj.trigger=="after #{j.name}"}
|
95
|
+
#put begin/rescue so all dependencies run
|
96
|
+
dep_jobs.each{|dj| begin;dj.tasks.first.enqueue! unless dj.is_working?;rescue;end}
|
97
|
+
else
|
98
|
+
#queue up next task
|
99
|
+
t.next.enqueue!
|
100
|
+
end
|
101
|
+
return true
|
102
|
+
end
|
103
|
+
|
104
|
+
def enqueue!
|
105
|
+
t = self
|
106
|
+
::Resque::Job.create("mobilize",Task,t.path,{})
|
107
|
+
return true
|
108
|
+
end
|
109
|
+
|
110
|
+
def worker
|
111
|
+
t = self
|
112
|
+
Mobilize::Resque.find_worker_by_path(t.path)
|
113
|
+
end
|
114
|
+
|
115
|
+
def worker_args
|
116
|
+
t = self
|
117
|
+
Jobtracker.get_worker_args(t.worker)
|
118
|
+
end
|
119
|
+
|
120
|
+
def set_worker_args(args)
|
121
|
+
t = self
|
122
|
+
Jobtracker.set_worker_args(t.worker,args)
|
123
|
+
end
|
124
|
+
|
125
|
+
def update_status(msg)
|
126
|
+
t = self
|
127
|
+
t.update_attributes(:status=>msg)
|
128
|
+
Mobilize::Resque.set_worker_args_by_path(t.path,{'status'=>msg})
|
129
|
+
return true
|
130
|
+
end
|
131
|
+
|
132
|
+
def is_working?
|
133
|
+
t = self
|
134
|
+
Mobilize::Resque.active_paths.include?(t.path)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -1,3 +1,6 @@
|
|
1
|
+
# require 'resque/tasks'
|
2
|
+
# will give you the resque tasks
|
3
|
+
|
1
4
|
namespace :mobilize_base do
|
2
5
|
desc "Start a Resque worker"
|
3
6
|
task :work do
|
@@ -8,7 +11,6 @@ namespace :mobilize_base do
|
|
8
11
|
require e
|
9
12
|
end
|
10
13
|
rescue Exception=>exc
|
11
|
-
#do nothing
|
12
14
|
end
|
13
15
|
|
14
16
|
begin
|
@@ -21,42 +23,17 @@ namespace :mobilize_base do
|
|
21
23
|
|
22
24
|
worker.work(ENV['INTERVAL'] || 5) # interval, will block
|
23
25
|
end
|
24
|
-
desc "Kill all Resque workers"
|
25
|
-
task :kill_workers do
|
26
|
-
require 'mobilize-base'
|
27
|
-
Mobilize::Jobtracker.kill_workers
|
28
|
-
end
|
29
26
|
desc "Kill idle workers not in sync with repo"
|
30
27
|
task :kill_idle_and_stale_workers do
|
31
28
|
require 'mobilize-base'
|
32
29
|
Mobilize::Jobtracker.kill_idle_and_stale_workers
|
33
30
|
end
|
34
|
-
|
35
|
-
task :kill_idle_workers do
|
36
|
-
require 'mobilize-base'
|
37
|
-
Mobilize::Jobtracker.kill_idle_workers
|
38
|
-
end
|
39
|
-
desc "Make sure there are the correct # of workers, kill if too many"
|
31
|
+
desc "Make sure workers are prepped"
|
40
32
|
task :prep_workers do
|
41
33
|
require 'mobilize-base'
|
42
34
|
Mobilize::Jobtracker.prep_workers
|
43
35
|
end
|
44
|
-
desc "
|
45
|
-
task :stop do
|
46
|
-
require 'mobilize-base'
|
47
|
-
Mobilize::Jobtracker.stop!
|
48
|
-
end
|
49
|
-
desc "Start Jobtracker"
|
50
|
-
task :start do
|
51
|
-
require 'mobilize-base'
|
52
|
-
Mobilize::Jobtracker.start
|
53
|
-
end
|
54
|
-
desc "Restart Jobtracker"
|
55
|
-
task :restart do
|
56
|
-
require 'mobilize-base'
|
57
|
-
Mobilize::Jobtracker.restart!
|
58
|
-
end
|
59
|
-
desc "kill all old resque web processes, start new one with resque_web.rb extension file"
|
36
|
+
desc "kill all old resque web, start new one with env params"
|
60
37
|
task :resque_web do
|
61
38
|
require 'mobilize-base'
|
62
39
|
port = Mobilize::Base.config('resque')['web_port']
|
@@ -75,7 +52,9 @@ namespace :mobilize_base do
|
|
75
52
|
"Mobilize::#{m}".constantize.create_indexes
|
76
53
|
end
|
77
54
|
end
|
78
|
-
|
55
|
+
end
|
56
|
+
namespace :mobilize do
|
57
|
+
desc "Set up config and log folders and files"
|
79
58
|
task :setup do
|
80
59
|
config_dir = (ENV['MOBILIZE_CONFIG_DIR'] ||= "config/mobilize/")
|
81
60
|
log_dir = (ENV['MOBILIZE_LOG_DIR'] ||= "log/")
|
data/lib/mobilize-base.rb
CHANGED
@@ -66,7 +66,7 @@ if File.exists?(mongoid_config_path)
|
|
66
66
|
require "mobilize-base/models/user"
|
67
67
|
require "mobilize-base/models/runner"
|
68
68
|
require "mobilize-base/models/job"
|
69
|
-
require "mobilize-base/models/
|
69
|
+
require "mobilize-base/models/task"
|
70
70
|
end
|
71
71
|
require 'google_drive'
|
72
72
|
require 'resque'
|
data/mobilize-base.gemspec
CHANGED
@@ -29,6 +29,7 @@ Gem::Specification.new do |s|
|
|
29
29
|
s.add_runtime_dependency 'redis',"~>3.0.0"
|
30
30
|
s.add_runtime_dependency 'resque','1.21.0'
|
31
31
|
s.add_runtime_dependency 'google_drive','0.3.2'
|
32
|
+
s.add_runtime_dependency 'bluepill','0.0.60'
|
32
33
|
s.add_runtime_dependency 'popen4','0.1.2'
|
33
34
|
s.add_runtime_dependency 'actionmailer','3.1.1'
|
34
35
|
end
|
data/test/base_job_rows.yml
CHANGED
@@ -2,12 +2,12 @@
|
|
2
2
|
active: true
|
3
3
|
trigger: once
|
4
4
|
status: ""
|
5
|
-
|
6
|
-
|
5
|
+
task1: 'gsheet.read "Runner_mobilize(test)/base1_task1.in"'
|
6
|
+
task2: 'gsheet.write "base1/task1", "Runner_mobilize(test)/base1.out"'
|
7
7
|
|
8
8
|
- name: "base2"
|
9
9
|
active: true
|
10
10
|
trigger: "after base1"
|
11
11
|
status: ""
|
12
|
-
|
13
|
-
|
12
|
+
task1: 'gsheet.read "Runner_mobilize(test)/base1.out"'
|
13
|
+
task2: 'gsheet.write "task1", "Runner_mobilize(test)/base2.out"'
|
data/test/mobilize-base_test.rb
CHANGED
@@ -32,35 +32,27 @@ describe "Mobilize" do
|
|
32
32
|
r = u.runner
|
33
33
|
jobs_sheet = r.gsheet(gdrive_slot)
|
34
34
|
tsv = jobs_sheet.to_tsv
|
35
|
-
assert tsv.length ==
|
35
|
+
assert tsv.length == 56 #headers only
|
36
36
|
|
37
|
-
puts "add
|
38
|
-
test_source_sheet = Mobilize::Gsheet.find_or_create_by_path("#{r.path.split("/")[0..-2].join("/")}/
|
37
|
+
puts "add base1_task1 input sheet"
|
38
|
+
test_source_sheet = Mobilize::Gsheet.find_or_create_by_path("#{r.path.split("/")[0..-2].join("/")}/base1_task1.in",gdrive_slot)
|
39
39
|
|
40
|
-
test_source_ha = ::YAML.load_file("#{Mobilize::Base.root}/test/
|
40
|
+
test_source_ha = ::YAML.load_file("#{Mobilize::Base.root}/test/base1_task1.yml")*40
|
41
41
|
test_source_tsv = test_source_ha.hash_array_to_tsv
|
42
42
|
test_source_sheet.write(test_source_tsv)
|
43
43
|
|
44
44
|
puts "add row to jobs sheet, wait 120s"
|
45
45
|
test_job_rows = ::YAML.load_file("#{Mobilize::Base.root}/test/base_job_rows.yml")
|
46
46
|
jobs_sheet.add_or_update_rows(test_job_rows)
|
47
|
+
|
48
|
+
puts "job row added, force enqueued runner"
|
49
|
+
r.enqueue!
|
47
50
|
sleep 120
|
48
51
|
|
49
52
|
puts "jobtracker posted test sheet data to test destination, and checksum succeeded?"
|
50
|
-
|
51
|
-
test_target_sheet_2 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/base2.out",gdrive_slot)
|
52
|
-
|
53
|
-
assert test_target_sheet_1.to_tsv == test_source_sheet.to_tsv
|
53
|
+
test_target_sheet = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/base1.out",gdrive_slot)
|
54
54
|
|
55
|
-
|
56
|
-
[test_target_sheet_1,test_target_sheet_2].each{|s| s.delete}
|
57
|
-
|
58
|
-
jobs_sheet.add_or_update_rows([{'name'=>'base1','active'=>true}])
|
59
|
-
sleep 90
|
60
|
-
|
61
|
-
test_target_sheet_2 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/base1.out",gdrive_slot)
|
62
|
-
puts "jobtracker posted test sheet data to test destination, and checksum succeeded?"
|
63
|
-
assert test_target_sheet_2.to_tsv == test_source_sheet.to_tsv
|
55
|
+
assert test_target_sheet.to_tsv == test_source_sheet.to_tsv
|
64
56
|
|
65
57
|
end
|
66
58
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mobilize-base
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.51
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-12-
|
12
|
+
date: 2012-12-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -139,6 +139,22 @@ dependencies:
|
|
139
139
|
- - '='
|
140
140
|
- !ruby/object:Gem::Version
|
141
141
|
version: 0.3.2
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: bluepill
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - '='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: 0.0.60
|
150
|
+
type: :runtime
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - '='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: 0.0.60
|
142
158
|
- !ruby/object:Gem::Dependency
|
143
159
|
name: popen4
|
144
160
|
requirement: !ruby/object:Gem::Requirement
|
@@ -207,9 +223,10 @@ files:
|
|
207
223
|
- lib/mobilize-base/models/dataset.rb
|
208
224
|
- lib/mobilize-base/models/job.rb
|
209
225
|
- lib/mobilize-base/models/runner.rb
|
210
|
-
- lib/mobilize-base/models/
|
226
|
+
- lib/mobilize-base/models/task.rb
|
211
227
|
- lib/mobilize-base/models/user.rb
|
212
|
-
- lib/mobilize-base/
|
228
|
+
- lib/mobilize-base/rakes.rb
|
229
|
+
- lib/mobilize-base/tasks/mobilize-base.rake
|
213
230
|
- lib/mobilize-base/version.rb
|
214
231
|
- lib/samples/gdrive.yml
|
215
232
|
- lib/samples/gridfs.yml
|
@@ -219,7 +236,7 @@ files:
|
|
219
236
|
- lib/samples/resque.yml
|
220
237
|
- lib/samples/resque_web.rb
|
221
238
|
- mobilize-base.gemspec
|
222
|
-
- test/
|
239
|
+
- test/base1_task1.yml
|
223
240
|
- test/base_job_rows.yml
|
224
241
|
- test/mobilize-base_test.rb
|
225
242
|
- test/redis-test.conf
|
@@ -238,7 +255,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
238
255
|
version: '0'
|
239
256
|
segments:
|
240
257
|
- 0
|
241
|
-
hash:
|
258
|
+
hash: -989061027210314242
|
242
259
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
243
260
|
none: false
|
244
261
|
requirements:
|
@@ -247,7 +264,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
247
264
|
version: '0'
|
248
265
|
segments:
|
249
266
|
- 0
|
250
|
-
hash:
|
267
|
+
hash: -989061027210314242
|
251
268
|
requirements: []
|
252
269
|
rubyforge_project: mobilize-base
|
253
270
|
rubygems_version: 1.8.24
|
@@ -256,7 +273,7 @@ specification_version: 3
|
|
256
273
|
summary: Moves datasets and schedules data transfers using MongoDB, Resque and Google
|
257
274
|
Docs
|
258
275
|
test_files:
|
259
|
-
- test/
|
276
|
+
- test/base1_task1.yml
|
260
277
|
- test/base_job_rows.yml
|
261
278
|
- test/mobilize-base_test.rb
|
262
279
|
- test/redis-test.conf
|
@@ -1,141 +0,0 @@
|
|
1
|
-
module Mobilize
|
2
|
-
class Stage
|
3
|
-
include Mongoid::Document
|
4
|
-
include Mongoid::Timestamps
|
5
|
-
field :path, type: String
|
6
|
-
field :handler, type: String
|
7
|
-
field :call, type: String
|
8
|
-
field :param_string, type: Array
|
9
|
-
field :status, type: String
|
10
|
-
field :out_url, type: String
|
11
|
-
field :completed_at, type: Time
|
12
|
-
field :started_at, type: Time
|
13
|
-
field :failed_at, type: Time
|
14
|
-
field :status_at, type: Time
|
15
|
-
|
16
|
-
index({ path: 1})
|
17
|
-
|
18
|
-
def idx
|
19
|
-
s = self
|
20
|
-
s.path.split("/").last.gsub("stage","").to_i
|
21
|
-
end
|
22
|
-
|
23
|
-
def out_dst
|
24
|
-
#this gives a dataset that points to the output
|
25
|
-
#allowing you to determine its size
|
26
|
-
#before committing to a read or write
|
27
|
-
s = self
|
28
|
-
Dataset.find_by_url(s.out_url) if s.out_url
|
29
|
-
end
|
30
|
-
|
31
|
-
def params
|
32
|
-
s = self
|
33
|
-
#evaluates param_string to ruby hash
|
34
|
-
#using YAML parser
|
35
|
-
#TODO: eliminate ridiculousness
|
36
|
-
begin
|
37
|
-
YAML.load(s.param_string)
|
38
|
-
raise "Must resolve to Hash" unless result.class==Hash
|
39
|
-
rescue
|
40
|
-
sub_param_string = s.param_string.gsub(":\"",": \"").gsub(":'",": '").gsub(":[",": [").gsub(":{",": {").gsub(/(:[0-9])/,'stageparamsgsub\1').gsub('stageparamsgsub:',': ')
|
41
|
-
YAML.load("{#{sub_param_string}}")
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def job
|
46
|
-
s = self
|
47
|
-
job_path = s.path.split("/")[0..-2].join("/")
|
48
|
-
Job.where(:path=>job_path).first
|
49
|
-
end
|
50
|
-
|
51
|
-
def Stage.find_or_create_by_path(path)
|
52
|
-
s = Stage.where(:path=>path).first
|
53
|
-
s = Stage.create(:path=>path) unless s
|
54
|
-
return s
|
55
|
-
end
|
56
|
-
|
57
|
-
def prior
|
58
|
-
s = self
|
59
|
-
j = s.job
|
60
|
-
return nil if s.idx==1
|
61
|
-
return j.stages[s.idx-2]
|
62
|
-
end
|
63
|
-
|
64
|
-
def next
|
65
|
-
s = self
|
66
|
-
j = s.job
|
67
|
-
return nil if s.idx == j.stages.length
|
68
|
-
return j.stages[s.idx]
|
69
|
-
end
|
70
|
-
|
71
|
-
def Stage.perform(id,*args)
|
72
|
-
s = Stage.where(:path=>id).first
|
73
|
-
j = s.job
|
74
|
-
s.update_attributes(:started_at=>Time.now.utc)
|
75
|
-
s.update_status(%{Starting at #{Time.now.utc}})
|
76
|
-
begin
|
77
|
-
#get response by running method
|
78
|
-
s.out_url = "Mobilize::#{s.handler.humanize}".constantize.send("#{s.call}_by_stage_path",s.path)
|
79
|
-
s.save!
|
80
|
-
unless s.out_url
|
81
|
-
#re-queue self if no response
|
82
|
-
s.enqueue!
|
83
|
-
return false
|
84
|
-
end
|
85
|
-
rescue ScriptError, StandardError => exc
|
86
|
-
j.update_attributes(:active=>false)
|
87
|
-
s.update_attributes(:failed_at=>Time.now.utc)
|
88
|
-
s.update_status("Failed at #{Time.now.utc.to_s}")
|
89
|
-
raise exc
|
90
|
-
end
|
91
|
-
s.update_attributes(:completed_at=>Time.now.utc)
|
92
|
-
s.update_status("Completed at #{Time.now.utc.to_s}")
|
93
|
-
if s.idx == j.stages.length
|
94
|
-
#job has completed
|
95
|
-
j.update_attributes(:active=>false) if j.trigger.strip.downcase == "once"
|
96
|
-
#check for any dependent jobs, if there are, enqueue them
|
97
|
-
r = j.runner
|
98
|
-
dep_jobs = r.jobs.select{|dj| dj.active==true and dj.trigger.strip.downcase == "after #{j.name}"}
|
99
|
-
#put begin/rescue so all dependencies run
|
100
|
-
dep_jobs.each{|dj| begin;dj.stages.first.enqueue! unless dj.is_working?;rescue;end}
|
101
|
-
else
|
102
|
-
#queue up next stage
|
103
|
-
s.next.enqueue!
|
104
|
-
end
|
105
|
-
return true
|
106
|
-
end
|
107
|
-
|
108
|
-
def enqueue!
|
109
|
-
s = self
|
110
|
-
::Resque::Job.create("mobilize",Stage,s.path,{})
|
111
|
-
return true
|
112
|
-
end
|
113
|
-
|
114
|
-
def worker
|
115
|
-
s = self
|
116
|
-
Mobilize::Resque.find_worker_by_path(s.path)
|
117
|
-
end
|
118
|
-
|
119
|
-
def worker_args
|
120
|
-
s = self
|
121
|
-
Jobtracker.get_worker_args(s.worker)
|
122
|
-
end
|
123
|
-
|
124
|
-
def set_worker_args(args)
|
125
|
-
s = self
|
126
|
-
Jobtracker.set_worker_args(s.worker,args)
|
127
|
-
end
|
128
|
-
|
129
|
-
def update_status(msg)
|
130
|
-
s = self
|
131
|
-
s.update_attributes(:status=>msg,:status_at=>Time.now.utc)
|
132
|
-
Mobilize::Resque.set_worker_args_by_path(s.path,{'status'=>msg})
|
133
|
-
return true
|
134
|
-
end
|
135
|
-
|
136
|
-
def is_working?
|
137
|
-
s = self
|
138
|
-
Mobilize::Resque.active_paths.include?(s.path)
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
data/test/base1_stage1.yml
DELETED