mobilize-base 1.0.55 → 1.0.75

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -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, set from irb, as in:
393
+ Otherwise, it takes it from MOBILIZE_ENV parameter, as in:
394
394
 
395
395
  ``` ruby
396
396
  > ENV['MOBILIZE_ENV'] = 'production'
@@ -493,22 +493,24 @@ name>))` and enter values under each header:
493
493
  * Tasks have this syntax: <handler>.<call> <params>.
494
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 `"<Handler>.<call>_by_task_path"`
496
+ be called `"<handler>.<call>_by_task_path"`
497
497
  * the params the method accepts, which are custom to each
498
- task. These should be a comma-delimited list, with each param in
499
- quotes.
498
+ task. These should be of the for `<key1>: <value1>, <key2>: <value2>`, where
499
+ `<key>` is an unquoted string and `<value>` is a quoted string, an
500
+ integer, an array (delimited by square braces), or a hash (delimited by
501
+ curly braces).
500
502
  * 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
+ * gsheet.read `source: <input_gsheet_full_path>`, which reads the sheet.
504
+ * The gsheet_full_path should be of the form `<gbook_name>/<gsheet_name>`. The test uses
503
505
  "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
506
+ * gsheet.write `source: <task_relative_path>`,`target: <target_gsheet_path>`,
507
+ which writes the specified task output to the target_gsheet.
508
+ * The task_relative_path should be of the form `<task_column>` or
507
509
  `<job_name/task_column>`. The test uses "base1/task1" for the first test
508
510
  and simply "task1" for the second test. Both of these take the output
509
511
  from the first task.
510
512
  * The test uses "Requestor_mobilize(test)/base1.out" and
511
- "Requestor_mobilize(test)/base2.out" for output sheets.
513
+ "Requestor_mobilize(test)/base2.out" for target sheets.
512
514
 
513
515
  <a name='section_Start_Run_Test'></a>
514
516
  ### Run Test
@@ -1,6 +1,7 @@
1
1
  module GoogleDrive
2
2
  class ClientLoginFetcher
3
3
  def request_raw(method, url, data, extra_header, auth)
4
+ clf = self
4
5
  #this is patched to handle server errors due to http chaos
5
6
  uri = URI.parse(url)
6
7
  response = nil
@@ -14,22 +15,32 @@ module GoogleDrive
14
15
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
15
16
  #set 600 to allow for large downloads
16
17
  http.read_timeout = 600
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
18
+ response = begin
19
+ clf.http_call(http, method, uri, data, extra_header, auth)
20
+ rescue
21
+ #timeouts etc.
22
+ nil
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
22
34
  else
23
- sleep_time = (rand*100).to_i
35
+ sleep_time = 10
24
36
  end
25
- else
26
- sleep_time = 10
37
+ attempts += 1
38
+ puts "Sleeping for #{sleep_time.to_s} due to #{response.body}"
39
+ sleep sleep_time
27
40
  end
28
- attempts += 1
29
- puts "Sleeping for #{sleep_time.to_s} due to #{response.body}"
30
- sleep sleep_time
31
41
  end
32
42
  end
43
+ raise "No response after 5 attempts" if response.nil?
33
44
  raise response.body if response.code.ie{|rcode| rcode.starts_with?("4") or rcode.starts_with?("5")}
34
45
  return response
35
46
  end
@@ -54,7 +54,11 @@ module GoogleDrive
54
54
  raise "Invalid role #{role}"
55
55
  end
56
56
  else
57
- f.acl.push({:scope_type=>"user",:scope=>email,:role=>role})
57
+ begin
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
58
62
  end
59
63
  return true
60
64
  end
@@ -54,6 +54,15 @@ 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
+
57
66
  def Gdrive.root(gdrive_slot=nil)
58
67
  pw = Gdrive.password(gdrive_slot)
59
68
  GoogleDrive.login(gdrive_slot,pw)
@@ -27,7 +27,7 @@ module Mobilize
27
27
  gdrive_slot = Gdrive.slot_worker_by_path(t.path)
28
28
  return false unless gdrive_slot
29
29
  t = Task.where(:path=>task_path)
30
- gfile_path = t.params.first
30
+ gfile_path = t.params['file']
31
31
  Gfile.find_by_path(gfile_path,gdrive_slot).read
32
32
  end
33
33
  end
@@ -37,7 +37,7 @@ module Mobilize
37
37
  gdrive_slot = Gdrive.slot_worker_by_path(task_path)
38
38
  return false unless gdrive_slot
39
39
  t = Task.where(:path=>task_path).first
40
- gsheet_path = t.params.first
40
+ gsheet_path = t.params['source']
41
41
  Gsheet.find_by_path(gsheet_path,gdrive_slot).to_tsv
42
42
  end
43
43
 
@@ -46,8 +46,8 @@ module Mobilize
46
46
  #return false if there are no emails available
47
47
  return false unless gdrive_slot
48
48
  t = Task.where(:path=>task_path).first
49
- source = t.params.first
50
- target_path = t.params.second
49
+ source = t.params['source']
50
+ target_path = t.params['target']
51
51
  source_job_name, source_task_name = if source.index("/")
52
52
  source.split("/")
53
53
  else
@@ -5,8 +5,6 @@ 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
10
8
 
11
9
  index({ path: 1})
12
10
 
@@ -26,6 +24,32 @@ module Mobilize
26
24
  return j
27
25
  end
28
26
 
27
+ def status
28
+ #last task status
29
+ j = self
30
+ j.active_task.status
31
+ end
32
+
33
+ def active_task
34
+ j = self
35
+ #latest started at or first
36
+ j.tasks.select{|t| t.started_at}.sort_by{|t| t.started_at}.last || j.tasks.first
37
+ end
38
+
39
+ def completed_at
40
+ j = self
41
+ j.tasks.last.completed_at
42
+ end
43
+
44
+ def failed_at
45
+ j = self
46
+ j.tasks.last.failed_at
47
+ end
48
+
49
+ def status_at
50
+ j.active_task.status_at
51
+ end
52
+
29
53
  #convenience methods
30
54
  def runner
31
55
  j = self
@@ -41,7 +65,7 @@ module Mobilize
41
65
  def is_due?
42
66
  j = self
43
67
  return false if j.is_working? or j.active == false or j.trigger.to_s.starts_with?("after")
44
- last_run = j.last_completed_at
68
+ last_run = j.completed_at
45
69
  #check trigger
46
70
  trigger = j.trigger
47
71
  return true if trigger == 'once'
@@ -5,7 +5,9 @@ module Mobilize
5
5
  field :path, type: String
6
6
  field :active, type: Boolean
7
7
  field :status, type: String
8
- field :last_run, type: Time
8
+ field :started_at, type: Time
9
+ field :status_at, type: Time
10
+ field :completed_at, type: Time
9
11
 
10
12
  index({ path: 1})
11
13
 
@@ -13,9 +15,9 @@ module Mobilize
13
15
  %w{name active trigger status task1 task2 task3 task4 task5}
14
16
  end
15
17
 
16
- def last_cached_at
18
+ def cached_at
17
19
  r = self
18
- Dataset.find_or_create_by_path(r.path).last_cached_at
20
+ Dataset.find_or_create_by_path(r.path).cached_at
19
21
  end
20
22
 
21
23
  def worker
@@ -51,12 +53,12 @@ module Mobilize
51
53
  j.tasks.first.enqueue!
52
54
  end
53
55
  rescue ScriptError, StandardError => exc
54
- j.update_status("Failed to enqueue with #{exc.to_s}")
56
+ r.update_status("Failed to enqueue #{j.path} with #{exc.to_s}")
55
57
  j.update_attributes(:active=>false)
56
58
  end
57
59
  end
58
60
  r.update_gsheet(gdrive_slot)
59
- r.update_attributes(:last_run=>Time.now.utc)
61
+ r.update_attributes(:completed_at=>Time.now.utc)
60
62
  end
61
63
 
62
64
  def dataset
@@ -77,7 +79,7 @@ module Mobilize
77
79
  r = self
78
80
  jobs_sheet = Gsheet.find_or_create_by_path(r.path,gdrive_slot)
79
81
  jobs_sheet.add_headers(r.headers)
80
- jobs_sheet.delete_sheet1
82
+ begin;jobs_sheet.delete_sheet1;rescue;end #don't care if sheet1 deletion fails
81
83
  return jobs_sheet
82
84
  end
83
85
 
@@ -128,7 +130,12 @@ module Mobilize
128
130
  def update_gsheet(gdrive_slot)
129
131
  r = self
130
132
  jobs_gsheet = r.gsheet(gdrive_slot)
131
- upd_rows = r.jobs.map{|j| {'name'=>j.name, 'active'=>j.active, 'status'=>j.status} }
133
+ upd_jobs = r.jobs.select do |j|
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
138
+ upd_rows = upd_jobs.map{|j| {'name'=>j.name, 'active'=>j.active, 'status'=>j.status}}
132
139
  jobs_gsheet.add_or_update_rows(upd_rows)
133
140
  r.update_status("gsheet updated")
134
141
  return true
@@ -152,7 +159,7 @@ module Mobilize
152
159
 
153
160
  def update_status(msg)
154
161
  r = self
155
- r.update_attributes(:status=>msg)
162
+ r.update_attributes(:status=>msg, :status_at=>Time.now.utc)
156
163
  Mobilize::Resque.set_worker_args_by_path(r.path,{'status'=>msg})
157
164
  return true
158
165
  end
@@ -165,12 +172,13 @@ module Mobilize
165
172
  def is_due?
166
173
  r = self.reload
167
174
  return false if r.is_working?
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
175
+ prev_due_time = Time.now.utc - Jobtracker.runner_read_freq
176
+ return true if r.started_at.nil? or r.started_at < prev_due_time
170
177
  end
171
178
 
172
179
  def enqueue!
173
180
  r = self
181
+ r.update_attributes(:started_at=>Time.now.utc)
174
182
  ::Resque::Job.create("mobilize",Runner,r.path,{})
175
183
  return true
176
184
  end
@@ -7,8 +7,10 @@ module Mobilize
7
7
  field :call, type: String
8
8
  field :param_string, type: Array
9
9
  field :status, type: String
10
- field :last_completed_at, type: Time
11
- field :last_run_at, type: Time
10
+ field :completed_at, type: Time
11
+ field :started_at, type: Time
12
+ field :failed_at, type: Time
13
+ field :status_at, type: Time
12
14
 
13
15
  index({ path: 1})
14
16
 
@@ -27,13 +29,22 @@ module Mobilize
27
29
  Dataset.find_or_create_by_handler_and_path("gridfs","#{t.path}/stderr")
28
30
  end
29
31
 
32
+ def log_dataset
33
+ t = self
34
+ Dataset.find_or_create_by_handler_and_path("gridfs","#{t.path}/log")
35
+ end
36
+
30
37
  def params
31
38
  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
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}}")
37
48
  end
38
49
  end
39
50
 
@@ -69,12 +80,16 @@ module Mobilize
69
80
  t.update_status(%{Starting at #{Time.now.utc}})
70
81
  stdout, stderr = [nil,nil]
71
82
  begin
72
- stdout = "Mobilize::#{t.handler.humanize}".constantize.send("#{t.call}_by_task_path",t.path).to_s
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
73
86
  rescue ScriptError, StandardError => exc
74
87
  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}")
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}")
78
93
  raise exc
79
94
  end
80
95
  if stdout == false
@@ -86,12 +101,13 @@ module Mobilize
86
101
  t.stdout_dataset.write_cache(stdout)
87
102
  t.update_attributes(:status=>"Completed at #{Time.now.utc.to_s}")
88
103
  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)
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}")
92
108
  #check for any dependent jobs, if there are, enqueue them
93
109
  r = j.runner
94
- dep_jobs = r.jobs.select{|dj| dj.active==true and dj.trigger=="after #{j.name}"}
110
+ dep_jobs = r.jobs.select{|dj| dj.active==true and dj.trigger.strip.downcase == "after #{j.name}"}
95
111
  #put begin/rescue so all dependencies run
96
112
  dep_jobs.each{|dj| begin;dj.tasks.first.enqueue! unless dj.is_working?;rescue;end}
97
113
  else
@@ -103,6 +119,7 @@ module Mobilize
103
119
 
104
120
  def enqueue!
105
121
  t = self
122
+ t.update_attributes(:started_at=>Time.now.utc)
106
123
  ::Resque::Job.create("mobilize",Task,t.path,{})
107
124
  return true
108
125
  end
@@ -124,7 +141,7 @@ module Mobilize
124
141
 
125
142
  def update_status(msg)
126
143
  t = self
127
- t.update_attributes(:status=>msg)
144
+ t.update_attributes(:status=>msg,:status_at=>Time.now.utc)
128
145
  Mobilize::Resque.set_worker_args_by_path(t.path,{'status'=>msg})
129
146
  return true
130
147
  end
@@ -1,5 +1,5 @@
1
1
  module Mobilize
2
2
  module Base
3
- VERSION = "1.0.55"
3
+ VERSION = "1.0.75"
4
4
  end
5
5
  end
@@ -29,7 +29,6 @@ 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'
33
32
  s.add_runtime_dependency 'popen4','0.1.2'
34
33
  s.add_runtime_dependency 'actionmailer','3.1.1'
35
34
  end
@@ -2,12 +2,12 @@
2
2
  active: true
3
3
  trigger: once
4
4
  status: ""
5
- task1: 'gsheet.read "Runner_mobilize(test)/base1_task1.in"'
6
- task2: 'gsheet.write "base1/task1", "Runner_mobilize(test)/base1.out"'
5
+ task1: 'gsheet.read source:"Runner_mobilize(test)/base1_task1.in"'
6
+ task2: 'gsheet.write source:"base1/task1", 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
- task1: 'gsheet.read "Runner_mobilize(test)/base1.out"'
13
- task2: 'gsheet.write "task1", "Runner_mobilize(test)/base2.out"'
12
+ task1: 'gsheet.read source:"Runner_mobilize(test)/base1.out"'
13
+ task2: 'gsheet.write source:"task1", target:"Runner_mobilize(test)/base2.out"'
@@ -44,15 +44,24 @@ describe "Mobilize" do
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!
50
47
  sleep 120
51
48
 
52
49
  puts "jobtracker posted test sheet data to test destination, and checksum succeeded?"
53
- test_target_sheet = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/base1.out",gdrive_slot)
50
+ test_target_sheet_1 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/base1.out",gdrive_slot)
51
+ test_target_sheet_1 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/base1.out",gdrive_slot)
52
+
53
+
54
+ assert test_target_sheet_1.to_tsv == test_source_sheet.to_tsv
54
55
 
55
- assert test_target_sheet.to_tsv == test_source_sheet.to_tsv
56
+ puts "delete both output sheets, set first job to active=true"
57
+ test_target_sheet_1.delete
58
+
59
+ jobs_sheet.add_or_update_rows([{'name'=>'base1','active'=>true}])
60
+ sleep 90
61
+
62
+ test_target_sheet_2 = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/base1.out",gdrive_slot)
63
+ puts "jobtracker posted test sheet data to test destination, and checksum succeeded?"
64
+ assert test_target_sheet_2.to_tsv == test_source_sheet.to_tsv
56
65
 
57
66
  end
58
67
 
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.55
4
+ version: 1.0.75
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-13 00:00:00.000000000 Z
12
+ date: 2012-12-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -139,22 +139,6 @@ 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
158
142
  - !ruby/object:Gem::Dependency
159
143
  name: popen4
160
144
  requirement: !ruby/object:Gem::Requirement
@@ -254,7 +238,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
254
238
  version: '0'
255
239
  segments:
256
240
  - 0
257
- hash: -3858742184331837226
241
+ hash: 2930191387184106525
258
242
  required_rubygems_version: !ruby/object:Gem::Requirement
259
243
  none: false
260
244
  requirements:
@@ -263,7 +247,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
263
247
  version: '0'
264
248
  segments:
265
249
  - 0
266
- hash: -3858742184331837226
250
+ hash: 2930191387184106525
267
251
  requirements: []
268
252
  rubyforge_project: mobilize-base
269
253
  rubygems_version: 1.8.24