mobilize-base 1.0.75 → 1.0.81
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/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