mobilize-base 1.0.75 → 1.0.81
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +14 -14
- data/Rakefile +1 -1
- data/lib/mobilize-base.rb +1 -1
- data/lib/mobilize-base/extensions/google_drive/worksheet.rb +3 -2
- data/lib/mobilize-base/handlers/gfile.rb +4 -4
- data/lib/mobilize-base/handlers/gsheet.rb +14 -14
- data/lib/mobilize-base/handlers/resque.rb +1 -1
- data/lib/mobilize-base/jobtracker.rb +3 -1
- data/lib/mobilize-base/models/job.rb +11 -10
- data/lib/mobilize-base/models/runner.rb +15 -19
- data/lib/mobilize-base/models/stage.rb +153 -0
- data/lib/mobilize-base/{rakes.rb → tasks.rb} +15 -0
- data/lib/mobilize-base/version.rb +1 -1
- data/test/base1_stage1.yml +3 -0
- data/test/base_job_rows.yml +4 -4
- data/test/mobilize-base_test.rb +6 -7
- metadata +8 -8
- data/lib/mobilize-base/models/task.rb +0 -154
- data/test/base1_task1.yml +0 -3
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/tasks'
|
124
124
|
```
|
125
125
|
|
126
126
|
This defines rake tasks essential to run the environment.
|
@@ -489,26 +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
|
+
* stage1..stage5 List of stages to be performed by the job.
|
493
|
+
* Stages have this syntax: <handler>.<call> <params>.
|
494
|
+
* handler specifies the file that should receive the stage
|
495
495
|
* the call specifies the method within the file. The method should
|
496
|
-
be called `"<handler>.<call>
|
496
|
+
be called `"<handler>.<call>_by_stage_path"`
|
497
497
|
* the params the method accepts, which are custom to each
|
498
|
-
|
498
|
+
stage. These should be of the for `<key1>: <value1>, <key2>: <value2>`, where
|
499
499
|
`<key>` is an unquoted string and `<value>` is a quoted string, an
|
500
500
|
integer, an array (delimited by square braces), or a hash (delimited by
|
501
501
|
curly braces).
|
502
|
-
* For mobilize-base, the following
|
502
|
+
* For mobilize-base, the following stages are available:
|
503
503
|
* gsheet.read `source: <input_gsheet_full_path>`, which reads the sheet.
|
504
504
|
* The gsheet_full_path should be of the form `<gbook_name>/<gsheet_name>`. The test uses
|
505
|
-
"Requestor_mobilize(test)/
|
506
|
-
* gsheet.write `source: <
|
507
|
-
which writes the specified
|
508
|
-
* The
|
509
|
-
`<job_name/
|
510
|
-
and simply "
|
511
|
-
from the first
|
505
|
+
"Requestor_mobilize(test)/base1_stage1.in".
|
506
|
+
* gsheet.write `source: <stage_relative_path>`,`target: <target_gsheet_path>`,
|
507
|
+
which writes the specified stage output to the target_gsheet.
|
508
|
+
* The stage_relative_path should be of the form `<stage_column>` or
|
509
|
+
`<job_name/stage_column>`. The test uses "base1/stage1" for the first test
|
510
|
+
and simply "stage1" for the second test. Both of these take the output
|
511
|
+
from the first stage.
|
512
512
|
* The test uses "Requestor_mobilize(test)/base1.out" and
|
513
513
|
"Requestor_mobilize(test)/base2.out" for target sheets.
|
514
514
|
|
data/Rakefile
CHANGED
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/stage"
|
70
70
|
end
|
71
71
|
require 'google_drive'
|
72
72
|
require 'resque'
|
@@ -137,8 +137,9 @@ 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
|
-
#
|
140
|
+
elsif loc_v.to_s.downcase.gsub("-","").gsub(" ","")==rem_v.to_s.downcase.gsub("-","").gsub(" ","")
|
141
|
+
#supported currency, silently converted whether it's an actual currency or not
|
142
|
+
#put a backtick on it.
|
142
143
|
sheet[row_i+1,col_i+1] = %{'#{loc_v}}
|
143
144
|
re_col_vs << {'row_i'=>row_i+1,'col_i'=>col_i+1,'col_v'=>%{'#{loc_v}}}
|
144
145
|
elsif (loc_v.to_s.count('e')==1 or loc_v.to_s.count('e')==0) and
|
@@ -22,12 +22,12 @@ 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_stage_path(stage_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(s.path)
|
28
28
|
return false unless gdrive_slot
|
29
|
-
|
30
|
-
gfile_path =
|
29
|
+
s = Stage.where(:path=>stage_path)
|
30
|
+
gfile_path = s.params['file']
|
31
31
|
Gfile.find_by_path(gfile_path,gdrive_slot).read
|
32
32
|
end
|
33
33
|
end
|
@@ -32,32 +32,32 @@ module Mobilize
|
|
32
32
|
return sheet
|
33
33
|
end
|
34
34
|
|
35
|
-
def Gsheet.
|
35
|
+
def Gsheet.read_by_stage_path(stage_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(stage_path)
|
38
38
|
return false unless gdrive_slot
|
39
|
-
|
40
|
-
gsheet_path =
|
39
|
+
s = Stage.where(:path=>stage_path).first
|
40
|
+
gsheet_path = s.params['source']
|
41
41
|
Gsheet.find_by_path(gsheet_path,gdrive_slot).to_tsv
|
42
42
|
end
|
43
43
|
|
44
|
-
def Gsheet.
|
45
|
-
gdrive_slot = Gdrive.slot_worker_by_path(
|
44
|
+
def Gsheet.write_by_stage_path(stage_path)
|
45
|
+
gdrive_slot = Gdrive.slot_worker_by_path(stage_path)
|
46
46
|
#return false if there are no emails available
|
47
47
|
return false unless gdrive_slot
|
48
|
-
|
49
|
-
source =
|
50
|
-
target_path =
|
51
|
-
source_job_name,
|
48
|
+
s = Stage.where(:path=>stage_path).first
|
49
|
+
source = s.params['source']
|
50
|
+
target_path = s.params['target']
|
51
|
+
source_job_name, source_stage_name = if source.index("/")
|
52
52
|
source.split("/")
|
53
53
|
else
|
54
54
|
[nil, source]
|
55
55
|
end
|
56
|
-
|
57
|
-
|
58
|
-
tsv =
|
56
|
+
source_stage_path = "#{s.job.runner.path}/#{source_job_name || s.job.name}/#{source_stage_name}"
|
57
|
+
source_stage = Stage.where(:path=>source_stage_path).first
|
58
|
+
tsv = source_stage.stdout_dataset.read_cache
|
59
59
|
sheet_name = target_path.split("/").last
|
60
|
-
temp_path = [
|
60
|
+
temp_path = [stage_path.gridsafe,sheet_name].join("/")
|
61
61
|
temp_sheet = Gsheet.find_or_create_by_path(temp_path,gdrive_slot)
|
62
62
|
temp_sheet.write(tsv)
|
63
63
|
temp_sheet.check_and_fix(tsv)
|
@@ -50,7 +50,7 @@ module Mobilize
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def Resque.active_paths
|
53
|
-
#first argument of the payload is the runner /
|
53
|
+
#first argument of the payload is the runner / stage path unless the worker is Jobtracker
|
54
54
|
Resque.jobs('active').map{|j| j['args'].first unless j['class']=='Jobtracker'}.compact
|
55
55
|
end
|
56
56
|
|
@@ -190,7 +190,9 @@ module Mobilize
|
|
190
190
|
while Jobtracker.status != 'stopping'
|
191
191
|
users = User.all
|
192
192
|
Jobtracker.run_notifications
|
193
|
-
users
|
193
|
+
#run throush all users randomly
|
194
|
+
#so none are privileged on JT restarts
|
195
|
+
users.sort_by{rand}.each do |u|
|
194
196
|
r = u.runner
|
195
197
|
Jobtracker.update_status("Checking #{r.path}")
|
196
198
|
if r.is_due?
|
@@ -13,9 +13,9 @@ module Mobilize
|
|
13
13
|
j.path.split("/").last
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
16
|
+
def stages
|
17
17
|
j = self
|
18
|
-
|
18
|
+
Stage.where(:path=>/^#{j.path.escape_regex}/).to_a.sort_by{|s| s.path}
|
19
19
|
end
|
20
20
|
|
21
21
|
def Job.find_or_create_by_path(path)
|
@@ -25,29 +25,30 @@ module Mobilize
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def status
|
28
|
-
#last
|
28
|
+
#last stage status
|
29
29
|
j = self
|
30
|
-
j.
|
30
|
+
j.active_stage.status if j.active_stage
|
31
31
|
end
|
32
32
|
|
33
|
-
def
|
33
|
+
def active_stage
|
34
34
|
j = self
|
35
35
|
#latest started at or first
|
36
|
-
j.
|
36
|
+
j.stages.select{|s| s.started_at}.sort_by{|s| s.started_at}.last || j.stages.first
|
37
37
|
end
|
38
38
|
|
39
39
|
def completed_at
|
40
40
|
j = self
|
41
|
-
j.
|
41
|
+
j.stages.last.completed_at if j.stages.last
|
42
42
|
end
|
43
43
|
|
44
44
|
def failed_at
|
45
45
|
j = self
|
46
|
-
j.
|
46
|
+
j.active_stage.failed_at if j.active_stage
|
47
47
|
end
|
48
48
|
|
49
49
|
def status_at
|
50
|
-
j
|
50
|
+
j = self
|
51
|
+
j.active_stage.status_at if j.active_stage
|
51
52
|
end
|
52
53
|
|
53
54
|
#convenience methods
|
@@ -59,7 +60,7 @@ module Mobilize
|
|
59
60
|
|
60
61
|
def is_working?
|
61
62
|
j = self
|
62
|
-
j.
|
63
|
+
j.stages.select{|s| s.is_working?}.compact.length>0
|
63
64
|
end
|
64
65
|
|
65
66
|
def is_due?
|
@@ -12,7 +12,7 @@ module Mobilize
|
|
12
12
|
index({ path: 1})
|
13
13
|
|
14
14
|
def headers
|
15
|
-
%w{name active trigger status
|
15
|
+
%w{name active trigger status stage1 stage2 stage3 stage4 stage5}
|
16
16
|
end
|
17
17
|
|
18
18
|
def cached_at
|
@@ -41,6 +41,7 @@ module Mobilize
|
|
41
41
|
r.update_status("no gdrive slot available")
|
42
42
|
return false
|
43
43
|
end
|
44
|
+
r.update_attributes(:started_at=>Time.now.utc)
|
44
45
|
#make sure any updates to activity are processed first
|
45
46
|
#as in when someone runs a "once" job that has completed
|
46
47
|
r.update_gsheet(gdrive_slot)
|
@@ -50,7 +51,7 @@ module Mobilize
|
|
50
51
|
r.jobs.each do |j|
|
51
52
|
begin
|
52
53
|
if j.is_due?
|
53
|
-
j.
|
54
|
+
j.stages.first.enqueue!
|
54
55
|
end
|
55
56
|
rescue ScriptError, StandardError => exc
|
56
57
|
r.update_status("Failed to enqueue #{j.path} with #{exc.to_s}")
|
@@ -95,25 +96,25 @@ module Mobilize
|
|
95
96
|
#parse out the jobs and update the Job collection
|
96
97
|
gsheet_jobs.each_with_index do |rj,rj_i|
|
97
98
|
#skip non-jobs or jobs without required values
|
98
|
-
next if (rj['name'].to_s.first == "#" or ['name','active','trigger','
|
99
|
+
next if (rj['name'].to_s.first == "#" or ['name','active','trigger','stage1'].select{|c| rj[c].to_s.strip==""}.length>0)
|
99
100
|
j = Job.find_or_create_by_path("#{r.path}/#{rj['name']}")
|
100
101
|
#update top line params
|
101
102
|
j.update_attributes(:active => rj['active'],
|
102
103
|
:trigger => rj['trigger'])
|
103
|
-
(1..5).to_a.each do |
|
104
|
-
|
105
|
-
break if
|
106
|
-
|
107
|
-
#parse command string, update
|
108
|
-
|
109
|
-
|
110
|
-
|
104
|
+
(1..5).to_a.each do |s_idx|
|
105
|
+
stage_string = rj["stage#{s_idx.to_s}"]
|
106
|
+
break if stage_string.to_s.length==0
|
107
|
+
s = Stage.find_or_create_by_path("#{j.path}/stage#{s_idx.to_s}")
|
108
|
+
#parse command string, update stage with it
|
109
|
+
s_handler, call, param_string = [""*3]
|
110
|
+
stage_string.split(" ").ie do |spls|
|
111
|
+
s_handler = spls.first.split(".").first
|
111
112
|
call = spls.first.split(".").last
|
112
113
|
param_string = spls[1..-1].join(" ").strip
|
113
114
|
end
|
114
|
-
|
115
|
+
s.update_attributes(:call=>call, :handler=>s_handler, :param_string=>param_string)
|
115
116
|
end
|
116
|
-
r.update_status("Updated #{j.path}
|
117
|
+
r.update_status("Updated #{j.path} stages at #{Time.now.utc}")
|
117
118
|
#add this job to list of read ones
|
118
119
|
done_jobs << j
|
119
120
|
end
|
@@ -130,11 +131,7 @@ module Mobilize
|
|
130
131
|
def update_gsheet(gdrive_slot)
|
131
132
|
r = self
|
132
133
|
jobs_gsheet = r.gsheet(gdrive_slot)
|
133
|
-
upd_jobs = r.jobs.select
|
134
|
-
j.completed_at.nil? ||
|
135
|
-
j.completed_at > j.runner.completed_at ||
|
136
|
-
(j.failed_at and j.failed_at > j.runner.completed_at)
|
137
|
-
end
|
134
|
+
upd_jobs = r.jobs.select{|j| j.status_at and j.status_at > j.runner.completed_at}
|
138
135
|
upd_rows = upd_jobs.map{|j| {'name'=>j.name, 'active'=>j.active, 'status'=>j.status}}
|
139
136
|
jobs_gsheet.add_or_update_rows(upd_rows)
|
140
137
|
r.update_status("gsheet updated")
|
@@ -178,7 +175,6 @@ module Mobilize
|
|
178
175
|
|
179
176
|
def enqueue!
|
180
177
|
r = self
|
181
|
-
r.update_attributes(:started_at=>Time.now.utc)
|
182
178
|
::Resque::Job.create("mobilize",Runner,r.path,{})
|
183
179
|
return true
|
184
180
|
end
|
@@ -0,0 +1,153 @@
|
|
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 :completed_at, type: Time
|
11
|
+
field :started_at, type: Time
|
12
|
+
field :failed_at, type: Time
|
13
|
+
field :status_at, type: Time
|
14
|
+
|
15
|
+
index({ path: 1})
|
16
|
+
|
17
|
+
def idx
|
18
|
+
s = self
|
19
|
+
s.path.split("/").last.gsub("stage","").to_i
|
20
|
+
end
|
21
|
+
|
22
|
+
def stdout_dataset
|
23
|
+
s = self
|
24
|
+
Dataset.find_or_create_by_handler_and_path("gridfs","#{s.path}/stdout")
|
25
|
+
end
|
26
|
+
|
27
|
+
def stderr_dataset
|
28
|
+
s = self
|
29
|
+
Dataset.find_or_create_by_handler_and_path("gridfs","#{s.path}/stderr")
|
30
|
+
end
|
31
|
+
|
32
|
+
def log_dataset
|
33
|
+
s = self
|
34
|
+
Dataset.find_or_create_by_handler_and_path("gridfs","#{s.path}/log")
|
35
|
+
end
|
36
|
+
|
37
|
+
def params
|
38
|
+
s = self
|
39
|
+
#evaluates param_string to ruby hash
|
40
|
+
#using YAML parser
|
41
|
+
#TODO: eliminate ridiculousness
|
42
|
+
begin
|
43
|
+
YAML.load(s.param_string)
|
44
|
+
raise "Must resolve to Hash" unless result.class==Hash
|
45
|
+
rescue
|
46
|
+
sub_param_string = s.param_string.gsub(":\"",": \"").gsub(":'",": '").gsub(":[",": [").gsub(":{",": {").gsub(/(:[0-9])/,'stageparamsgsub\1').gsub('stageparamsgsub:',': ')
|
47
|
+
YAML.load("{#{sub_param_string}}")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def job
|
52
|
+
s = self
|
53
|
+
job_path = s.path.split("/")[0..-2].join("/")
|
54
|
+
Job.where(:path=>job_path).first
|
55
|
+
end
|
56
|
+
|
57
|
+
def Stage.find_or_create_by_path(path)
|
58
|
+
s = Stage.where(:path=>path).first
|
59
|
+
s = Stage.create(:path=>path) unless s
|
60
|
+
return s
|
61
|
+
end
|
62
|
+
|
63
|
+
def prior
|
64
|
+
s = self
|
65
|
+
j = s.job
|
66
|
+
return nil if s.idx==1
|
67
|
+
return j.stages[s.idx-2]
|
68
|
+
end
|
69
|
+
|
70
|
+
def next
|
71
|
+
s = self
|
72
|
+
j = s.job
|
73
|
+
return nil if s.idx == j.stages.length
|
74
|
+
return j.stages[s.idx]
|
75
|
+
end
|
76
|
+
|
77
|
+
def Stage.perform(id,*args)
|
78
|
+
s = Stage.where(:path=>id).first
|
79
|
+
j = s.job
|
80
|
+
s.update_attributes(:started_at=>Time.now.utc)
|
81
|
+
s.update_status(%{Starting at #{Time.now.utc}})
|
82
|
+
stdout, stderr = [nil,nil]
|
83
|
+
begin
|
84
|
+
stdout,log = "Mobilize::#{s.handler.humanize}".constantize.send("#{s.call}_by_stage_path",s.path).to_s
|
85
|
+
#write to log if method returns an array w 2 members
|
86
|
+
s.log_dataset.write_cache(log) if log
|
87
|
+
rescue ScriptError, StandardError => exc
|
88
|
+
stderr = [exc.to_s,exc.backtrace.to_s].join("\n")
|
89
|
+
#record the failure in Job so it appears on Runner, turn it off
|
90
|
+
#so it doesn't run again
|
91
|
+
j.update_attributes(:active=>false)
|
92
|
+
s.update_attributes(:failed_at=>Time.now.utc)
|
93
|
+
s.update_status("Failed at #{Time.now.utc.to_s}")
|
94
|
+
raise exc
|
95
|
+
end
|
96
|
+
if stdout == false
|
97
|
+
#re-queue self if output is false
|
98
|
+
s.enqueue!
|
99
|
+
return false
|
100
|
+
end
|
101
|
+
#write output to cache
|
102
|
+
s.stdout_dataset.write_cache(stdout)
|
103
|
+
s.update_attributes(:completed_at=>Time.now.utc)
|
104
|
+
s.update_status("Completed at #{Time.now.utc.to_s}")
|
105
|
+
if s.idx == j.stages.length
|
106
|
+
#job has completed
|
107
|
+
j.update_attributes(:active=>false) if j.trigger.strip.downcase == "once"
|
108
|
+
#check for any dependent jobs, if there are, enqueue them
|
109
|
+
r = j.runner
|
110
|
+
dep_jobs = r.jobs.select{|dj| dj.active==true and dj.trigger.strip.downcase == "after #{j.name}"}
|
111
|
+
#put begin/rescue so all dependencies run
|
112
|
+
dep_jobs.each{|dj| begin;dj.stages.first.enqueue! unless dj.is_working?;rescue;end}
|
113
|
+
else
|
114
|
+
#queue up next stage
|
115
|
+
s.next.enqueue!
|
116
|
+
end
|
117
|
+
return true
|
118
|
+
end
|
119
|
+
|
120
|
+
def enqueue!
|
121
|
+
s = self
|
122
|
+
::Resque::Job.create("mobilize",Stage,s.path,{})
|
123
|
+
return true
|
124
|
+
end
|
125
|
+
|
126
|
+
def worker
|
127
|
+
s = self
|
128
|
+
Mobilize::Resque.find_worker_by_path(s.path)
|
129
|
+
end
|
130
|
+
|
131
|
+
def worker_args
|
132
|
+
s = self
|
133
|
+
Jobtracker.get_worker_args(s.worker)
|
134
|
+
end
|
135
|
+
|
136
|
+
def set_worker_args(args)
|
137
|
+
s = self
|
138
|
+
Jobtracker.set_worker_args(s.worker,args)
|
139
|
+
end
|
140
|
+
|
141
|
+
def update_status(msg)
|
142
|
+
s = self
|
143
|
+
s.update_attributes(:status=>msg,:status_at=>Time.now.utc)
|
144
|
+
Mobilize::Resque.set_worker_args_by_path(s.path,{'status'=>msg})
|
145
|
+
return true
|
146
|
+
end
|
147
|
+
|
148
|
+
def is_working?
|
149
|
+
s = self
|
150
|
+
Mobilize::Resque.active_paths.include?(s.path)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -35,6 +35,21 @@ namespace :mobilize_base do
|
|
35
35
|
require 'mobilize-base'
|
36
36
|
Mobilize::Jobtracker.prep_workers
|
37
37
|
end
|
38
|
+
desc "Stop Jobtracker"
|
39
|
+
task :stop do
|
40
|
+
require 'mobilize-base'
|
41
|
+
Mobilize::Jobtracker.stop!
|
42
|
+
end
|
43
|
+
desc "Start Jobtracker"
|
44
|
+
task :start do
|
45
|
+
require 'mobilize-base'
|
46
|
+
Mobilize::Jobtracker.start
|
47
|
+
end
|
48
|
+
desc "Restart Jobtracker"
|
49
|
+
task :restart do
|
50
|
+
require 'mobilize-base'
|
51
|
+
Mobilize::Jobtracker.restart!
|
52
|
+
end
|
38
53
|
desc "kill all old resque web processes, start new one with resque_web.rb extension file"
|
39
54
|
task :resque_web do
|
40
55
|
require 'mobilize-base'
|
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
|
+
stage1: 'gsheet.read source:"Runner_mobilize(test)/base1_stage1.in"'
|
6
|
+
stage2: 'gsheet.write source:"base1/stage1", target:"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
|
+
stage1: 'gsheet.read source:"Runner_mobilize(test)/base1.out"'
|
13
|
+
stage2: 'gsheet.write source:"stage1", target:"Runner_mobilize(test)/base2.out"'
|
data/test/mobilize-base_test.rb
CHANGED
@@ -32,12 +32,12 @@ 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 == 61 #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_stage1 input sheet"
|
38
|
+
test_source_sheet = Mobilize::Gsheet.find_or_create_by_path("#{r.path.split("/")[0..-2].join("/")}/base1_stage1.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_stage1.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
|
|
@@ -48,13 +48,12 @@ describe "Mobilize" do
|
|
48
48
|
|
49
49
|
puts "jobtracker posted test sheet data to test destination, and checksum succeeded?"
|
50
50
|
test_target_sheet_1 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/base1.out",gdrive_slot)
|
51
|
-
|
52
|
-
|
51
|
+
test_target_sheet_2 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/base2.out",gdrive_slot)
|
53
52
|
|
54
53
|
assert test_target_sheet_1.to_tsv == test_source_sheet.to_tsv
|
55
54
|
|
56
55
|
puts "delete both output sheets, set first job to active=true"
|
57
|
-
test_target_sheet_1.delete
|
56
|
+
[test_target_sheet_1,test_target_sheet_2].each{|s| s.delete}
|
58
57
|
|
59
58
|
jobs_sheet.add_or_update_rows([{'name'=>'base1','active'=>true}])
|
60
59
|
sleep 90
|
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.81
|
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-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -207,9 +207,9 @@ files:
|
|
207
207
|
- lib/mobilize-base/models/dataset.rb
|
208
208
|
- lib/mobilize-base/models/job.rb
|
209
209
|
- lib/mobilize-base/models/runner.rb
|
210
|
-
- lib/mobilize-base/models/
|
210
|
+
- lib/mobilize-base/models/stage.rb
|
211
211
|
- lib/mobilize-base/models/user.rb
|
212
|
-
- lib/mobilize-base/
|
212
|
+
- lib/mobilize-base/tasks.rb
|
213
213
|
- lib/mobilize-base/version.rb
|
214
214
|
- lib/samples/gdrive.yml
|
215
215
|
- lib/samples/gridfs.yml
|
@@ -219,7 +219,7 @@ files:
|
|
219
219
|
- lib/samples/resque.yml
|
220
220
|
- lib/samples/resque_web.rb
|
221
221
|
- mobilize-base.gemspec
|
222
|
-
- test/
|
222
|
+
- test/base1_stage1.yml
|
223
223
|
- test/base_job_rows.yml
|
224
224
|
- test/mobilize-base_test.rb
|
225
225
|
- test/redis-test.conf
|
@@ -238,7 +238,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
238
238
|
version: '0'
|
239
239
|
segments:
|
240
240
|
- 0
|
241
|
-
hash:
|
241
|
+
hash: -1990483530432854751
|
242
242
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
243
243
|
none: false
|
244
244
|
requirements:
|
@@ -247,7 +247,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
247
247
|
version: '0'
|
248
248
|
segments:
|
249
249
|
- 0
|
250
|
-
hash:
|
250
|
+
hash: -1990483530432854751
|
251
251
|
requirements: []
|
252
252
|
rubyforge_project: mobilize-base
|
253
253
|
rubygems_version: 1.8.24
|
@@ -256,7 +256,7 @@ specification_version: 3
|
|
256
256
|
summary: Moves datasets and schedules data transfers using MongoDB, Resque and Google
|
257
257
|
Docs
|
258
258
|
test_files:
|
259
|
-
- test/
|
259
|
+
- test/base1_stage1.yml
|
260
260
|
- test/base_job_rows.yml
|
261
261
|
- test/mobilize-base_test.rb
|
262
262
|
- test/redis-test.conf
|
@@ -1,154 +0,0 @@
|
|
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 :completed_at, type: Time
|
11
|
-
field :started_at, type: Time
|
12
|
-
field :failed_at, type: Time
|
13
|
-
field :status_at, type: Time
|
14
|
-
|
15
|
-
index({ path: 1})
|
16
|
-
|
17
|
-
def idx
|
18
|
-
t = self
|
19
|
-
t.path.split("/").last.gsub("task","").to_i
|
20
|
-
end
|
21
|
-
|
22
|
-
def stdout_dataset
|
23
|
-
t = self
|
24
|
-
Dataset.find_or_create_by_handler_and_path("gridfs","#{t.path}/stdout")
|
25
|
-
end
|
26
|
-
|
27
|
-
def stderr_dataset
|
28
|
-
t = self
|
29
|
-
Dataset.find_or_create_by_handler_and_path("gridfs","#{t.path}/stderr")
|
30
|
-
end
|
31
|
-
|
32
|
-
def log_dataset
|
33
|
-
t = self
|
34
|
-
Dataset.find_or_create_by_handler_and_path("gridfs","#{t.path}/log")
|
35
|
-
end
|
36
|
-
|
37
|
-
def params
|
38
|
-
t = self
|
39
|
-
#evaluates param_string to ruby hash
|
40
|
-
#using YAML parser
|
41
|
-
#TODO: eliminate ridiculousness
|
42
|
-
begin
|
43
|
-
YAML.load(t.param_string)
|
44
|
-
raise "Must resolve to Hash" unless result.class==Hash
|
45
|
-
rescue
|
46
|
-
sub_param_string = t.param_string.gsub(":\"",": \"").gsub(":'",": '").gsub(":[",": [").gsub(":{",": {").gsub(/(:[0-9])/,'taskparamsgsub\1').gsub('taskparamsgsub:',': ')
|
47
|
-
YAML.load("{#{sub_param_string}}")
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def job
|
52
|
-
t = self
|
53
|
-
job_path = t.path.split("/")[0..-2].join("/")
|
54
|
-
Job.where(:path=>job_path).first
|
55
|
-
end
|
56
|
-
|
57
|
-
def Task.find_or_create_by_path(path)
|
58
|
-
t = Task.where(:path=>path).first
|
59
|
-
t = Task.create(:path=>path) unless t
|
60
|
-
return t
|
61
|
-
end
|
62
|
-
|
63
|
-
def prior
|
64
|
-
t = self
|
65
|
-
j = t.job
|
66
|
-
return nil if t.idx==1
|
67
|
-
return j.tasks[t.idx-2]
|
68
|
-
end
|
69
|
-
|
70
|
-
def next
|
71
|
-
t = self
|
72
|
-
j = t.job
|
73
|
-
return nil if t.idx == j.tasks.length
|
74
|
-
return j.tasks[t.idx]
|
75
|
-
end
|
76
|
-
|
77
|
-
def Task.perform(id,*args)
|
78
|
-
t = Task.where(:path=>id).first
|
79
|
-
j = t.job
|
80
|
-
t.update_status(%{Starting at #{Time.now.utc}})
|
81
|
-
stdout, stderr = [nil,nil]
|
82
|
-
begin
|
83
|
-
stdout,log = "Mobilize::#{t.handler.humanize}".constantize.send("#{t.call}_by_task_path",t.path).to_s
|
84
|
-
#write to log if method returns an array w 2 members
|
85
|
-
t.log_dataset.write_cache(log) if log
|
86
|
-
rescue ScriptError, StandardError => exc
|
87
|
-
stderr = [exc.to_s,exc.backtrace.to_s].join("\n")
|
88
|
-
#record the failure in Job so it appears on Runner, turn it off
|
89
|
-
#so it doesn't run again
|
90
|
-
j.update_attributes(:active=>false)
|
91
|
-
t.update_attributes(:failed_at=>Time.now.utc)
|
92
|
-
t.update_status("Failed at #{Time.now.utc.to_s}")
|
93
|
-
raise exc
|
94
|
-
end
|
95
|
-
if stdout == false
|
96
|
-
#re-queue self if output is false
|
97
|
-
t.enqueue!
|
98
|
-
return false
|
99
|
-
end
|
100
|
-
#write output to cache
|
101
|
-
t.stdout_dataset.write_cache(stdout)
|
102
|
-
t.update_attributes(:status=>"Completed at #{Time.now.utc.to_s}")
|
103
|
-
if t.idx == j.tasks.length
|
104
|
-
#job has completed
|
105
|
-
j.update_attributes(:active=>false) if j.trigger.strip.downcase == "once"
|
106
|
-
t.update_attributes(:completed_at=>Time.now.utc)
|
107
|
-
t.update_status("Completed at #{Time.now.utc.to_s}")
|
108
|
-
#check for any dependent jobs, if there are, enqueue them
|
109
|
-
r = j.runner
|
110
|
-
dep_jobs = r.jobs.select{|dj| dj.active==true and dj.trigger.strip.downcase == "after #{j.name}"}
|
111
|
-
#put begin/rescue so all dependencies run
|
112
|
-
dep_jobs.each{|dj| begin;dj.tasks.first.enqueue! unless dj.is_working?;rescue;end}
|
113
|
-
else
|
114
|
-
#queue up next task
|
115
|
-
t.next.enqueue!
|
116
|
-
end
|
117
|
-
return true
|
118
|
-
end
|
119
|
-
|
120
|
-
def enqueue!
|
121
|
-
t = self
|
122
|
-
t.update_attributes(:started_at=>Time.now.utc)
|
123
|
-
::Resque::Job.create("mobilize",Task,t.path,{})
|
124
|
-
return true
|
125
|
-
end
|
126
|
-
|
127
|
-
def worker
|
128
|
-
t = self
|
129
|
-
Mobilize::Resque.find_worker_by_path(t.path)
|
130
|
-
end
|
131
|
-
|
132
|
-
def worker_args
|
133
|
-
t = self
|
134
|
-
Jobtracker.get_worker_args(t.worker)
|
135
|
-
end
|
136
|
-
|
137
|
-
def set_worker_args(args)
|
138
|
-
t = self
|
139
|
-
Jobtracker.set_worker_args(t.worker,args)
|
140
|
-
end
|
141
|
-
|
142
|
-
def update_status(msg)
|
143
|
-
t = self
|
144
|
-
t.update_attributes(:status=>msg,:status_at=>Time.now.utc)
|
145
|
-
Mobilize::Resque.set_worker_args_by_path(t.path,{'status'=>msg})
|
146
|
-
return true
|
147
|
-
end
|
148
|
-
|
149
|
-
def is_working?
|
150
|
-
t = self
|
151
|
-
Mobilize::Resque.active_paths.include?(t.path)
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
data/test/base1_task1.yml
DELETED