mobilize-base 1.298 → 1.351

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -162,9 +162,15 @@ the same domain, and all Users should have emails in this domain.
162
162
  * an owner name and password. You can set up separate owners
163
163
  for different environments as in the below file, which will keep your
164
164
  mission critical workers from getting rate-limit errors.
165
+ * one admin_group_name, which the owner and all admins should be added to -- this
166
+ group will need read permissions to read from and edit permissions to write
167
+ to files.
165
168
  * one or more admins with email attributes -- these will be for people
166
169
  who should be given write permissions to all Mobilize books in the
167
170
  environment for maintenance purposes.
171
+ * one worker_group_name, which the owner and all workers should be added to -- this
172
+ group will need read permissions to read from and edit permissions to write
173
+ to files.
168
174
  * one or more workers with name and pw attributes -- they will be used
169
175
  to queue up google reads and writes. This can be the same as the owner
170
176
  account for testing purposes or low-volume environments.
@@ -182,8 +188,10 @@ development:
182
188
  owner:
183
189
  name: owner_development
184
190
  pw: google_drive_password
191
+ admin_group_name: admins_development
185
192
  admins:
186
193
  - name: admin
194
+ worker_group_name: workers_development
187
195
  workers:
188
196
  - name: worker_development001
189
197
  pw: worker001_google_drive_password
@@ -194,8 +202,10 @@ test:
194
202
  owner:
195
203
  name: owner_test
196
204
  pw: google_drive_password
205
+ admin_group_name: admins_test
197
206
  admins:
198
207
  - name: admin
208
+ worker_group_name: workers_test
199
209
  workers:
200
210
  - name: worker_test001
201
211
  pw: worker001_google_drive_password
@@ -206,8 +216,10 @@ production:
206
216
  owner:
207
217
  name: owner_production
208
218
  pw: google_drive_password
219
+ admin_group_name: admins_production
209
220
  admins:
210
221
  - name: admin
222
+ worker_group_name: workers_production
211
223
  workers:
212
224
  - name: worker_production001
213
225
  pw: worker001_google_drive_password
@@ -266,6 +278,9 @@ The Jobtracker sits on your Resque and does 2 things:
266
278
 
267
279
  Emails are sent using ActionMailer, through the owner Google Drive
268
280
  account.
281
+ * errors are sent to the owner of the job/stage as well as the admin group.
282
+ * errors not specific to a job/stage are sent to the admin group only, as
283
+ given in gdrive.yml
269
284
 
270
285
  To this end, it needs these parameters, for which there is a sample
271
286
  below and in the [lib/samples][git_samples] folder:
@@ -278,24 +293,18 @@ development:
278
293
  runner_read_freq: 300 #5 min between runner reads
279
294
  max_run_time: 14400 # if a job runs for 4h+, notification will be sent
280
295
  extensions: [] #additional Mobilize modules to load workers with
281
- admins: #emails to send notifications to
282
- - email: admin@host.com
283
296
  test:
284
297
  cycle_freq: 10 #time between Jobtracker sweeps
285
298
  notification_freq: 3600 #1 hour between failure/timeout notifications
286
299
  runner_read_freq: 300 #5 min between runner reads
287
300
  max_run_time: 14400 # if a job runs for 4h+, notification will be sent
288
301
  extensions: [] #additional Mobilize modules to load workers with
289
- admins: #emails to send notifications to
290
- - email: admin@host.com
291
302
  production:
292
303
  cycle_freq: 10 #time between Jobtracker sweeps
293
304
  notification_freq: 3600 #1 hour between failure/timeout notifications
294
305
  runner_read_freq: 300 #5 min between runner reads
295
306
  max_run_time: 14400 # if a job runs for 4h+, notification will be sent
296
307
  extensions: [] #additional Mobilize modules to load workers with
297
- admins: #emails to send notifications to
298
- - email: admin@host.com
299
308
  ```
300
309
 
301
310
  <a name='section_Configure_Resque'></a>
@@ -3,42 +3,16 @@ module GoogleDrive
3
3
 
4
4
  def add_worker_acl
5
5
  f = self
6
- return true if f.has_worker_acl?
7
- Mobilize::Gdrive.worker_emails.each do |a|
8
- f.update_acl(a)
9
- end
6
+ email = "#{Mobilize::Gdrive.worker_group_name}@#{Mobilize::Gdrive.domain}"
7
+ f.update_acl(email,"group")
10
8
  end
11
9
 
12
10
  def add_admin_acl
13
11
  f = self
14
- #admin includes workers
15
- return true if f.has_admin_acl?
16
- accounts = (Mobilize::Gdrive.admin_emails + Mobilize::Gdrive.worker_emails).uniq
17
- accounts.each do |email|
18
- f.update_acl(email)
19
- end
20
- end
21
-
22
- def has_admin_acl?
23
- f = self
24
- curr_emails = f.acls.map{|a| a.scope}.compact.sort
25
- admin_emails = (Mobilize::Gdrive.admin_emails + Mobilize::Gdrive.worker_emails).uniq
26
- if curr_emails == admin_emails or (curr_emails & admin_emails) == admin_emails
27
- return true
28
- else
29
- return false
30
- end
31
- end
32
-
33
- def has_worker_acl?
34
- f = self
35
- curr_emails = f.acls.map{|a| a.scope}.compact.sort
36
- worker_emails = Mobilize::Gdrive.worker_emails.sort
37
- if curr_emails == worker_emails or (curr_emails & worker_emails) == worker_emails
38
- return true
39
- else
40
- return false
41
- end
12
+ email = "#{Mobilize::Gdrive.admin_group_name}@#{Mobilize::Gdrive.domain}"
13
+ f.update_acl(email,"group")
14
+ #if adding acl ,must currently add workers as well
15
+ f.add_worker_acl
42
16
  end
43
17
 
44
18
  def read(user_name)
@@ -51,7 +25,7 @@ module GoogleDrive
51
25
  end
52
26
  end
53
27
 
54
- def update_acl(email,role="writer")
28
+ def update_acl(email,scope_type="user",role="writer")
55
29
  f = self
56
30
  #need these flags for HTTP retries
57
31
  #create req_acl hash to add to current acl
@@ -64,16 +38,16 @@ module GoogleDrive
64
38
  if entry.role != role
65
39
  #for whatever reason
66
40
  f.acl.delete(entry)
67
- f.acl.push({:scope_type=>"user",:scope=>email,:role=>role})
41
+ f.acl.push({:scope_type=>scope_type,:scope=>email,:role=>role})
68
42
  end
69
43
  elsif !['reader','writer','owner'].include?(role)
70
44
  raise "Invalid role #{role}"
71
45
  end
72
46
  else
73
47
  begin
74
- f.acl.push({:scope_type=>"user",:scope=>email,:role=>role})
48
+ f.acl.push({:scope_type=>scope_type,:scope=>email,:role=>role})
75
49
  rescue => exc
76
- raise exc unless exc.to_s.index("user already has access")
50
+ raise exc unless exc.to_s.index("already has access")
77
51
  end
78
52
  end
79
53
  return true
@@ -37,12 +37,16 @@ module Mobilize
37
37
  end
38
38
  end
39
39
 
40
- def Gdrive.worker_emails
41
- Gdrive.workers.map{|w| [w['name'],Gdrive.domain].join("@")}
40
+ def Gdrive.worker_group_name
41
+ Gdrive.config['worker_group_name']
42
+ end
43
+
44
+ def Gdrive.admin_group_name
45
+ Gdrive.config['admin_group_name']
42
46
  end
43
47
 
44
- def Gdrive.admin_emails
45
- Gdrive.admins.map{|w| [w['name'],Gdrive.domain].join("@")}
48
+ def Gdrive.worker_emails
49
+ Gdrive.workers.map{|w| [w['name'],Gdrive.domain].join("@")}
46
50
  end
47
51
 
48
52
  #email management - used to make sure not too many emails get used at the same time
@@ -58,7 +58,7 @@ module Mobilize
58
58
  def Gfile.update_acl_by_path(path,gdrive_slot,role="writer",target_email=nil)
59
59
  file = Gfile.find_by_path(path,target_email)
60
60
  raise "File #{path} not found" unless file
61
- file.update_acl(gdrive_slot,role)
61
+ file.update_acl(gdrive_slot,"user",role)
62
62
  end
63
63
 
64
64
  def Gfile.find_by_path(path)
@@ -109,7 +109,7 @@ module Mobilize
109
109
  #only give the user edit permissions if they're the ones
110
110
  #creating it
111
111
  target_sheet = Gsheet.find_or_create_by_path(target_path,gdrive_slot)
112
- target_sheet.spreadsheet.update_acl(u.email,"writer") unless target_sheet.spreadsheet.acl_entry(u.email).ie{|e| e and e.role=="owner"}
112
+ target_sheet.spreadsheet.update_acl(u.email) unless target_sheet.spreadsheet.acl_entry(u.email).ie{|e| e and e.role=="owner"}
113
113
  target_sheet.delete_sheet1
114
114
  end
115
115
  #pass it crop param to determine whether to shrink target sheet to fit data
@@ -19,13 +19,17 @@ module Mobilize
19
19
  def Resque.workers(state="all")
20
20
  workers = ::Resque.workers.select{|w| w.queues.first == Resque.queue_name}
21
21
  return workers if state == 'all'
22
- working_workers = workers.select{|w| w.job['queue']== Resque.queue_name}
22
+ working_workers = workers.select{|w| w.job['queue'] == Resque.queue_name}
23
23
  return working_workers if state == 'working'
24
24
  idle_workers = workers.select{|w| w.job['queue'].nil?}
25
25
  return idle_workers if state == 'idle'
26
26
  stale_workers = workers.select{|w| Time.parse(w.started) < Jobtracker.deployed_at}
27
27
  return stale_workers if state == 'stale'
28
- timeout_workers = workers.select{|w| w.job['payload'] and w.job['payload']['class']!='Jobtracker' and w.job['run_at'] < (Time.now.utc - Jobtracker.max_run_time)}
28
+ timeout_workers = workers.select do |w|
29
+ w.job['payload'] and
30
+ w.job['payload']['class']!='Jobtracker' and
31
+ w.job['run_at'] < (Time.now.utc - Jobtracker.max_run_time)
32
+ end
29
33
  return timeout_workers if state == 'timeout'
30
34
  raise "invalid state #{state}"
31
35
  end
@@ -115,14 +119,14 @@ module Mobilize
115
119
  s = Stage.where(:path=>stage_path).first
116
120
  if s.params['notify'].to_s=="false"
117
121
  next
118
- elsif s.params['notify'].index("@")
122
+ elsif s.params['notify'].to_s.index("@")
119
123
  s.params['notify']
120
124
  else
121
125
  s.job.runner.user.email
122
126
  end
123
- rescue
124
- #jobs without stages are sent to first admin
125
- Jobtracker.admin_emails.first
127
+ rescue ScriptError, StandardError
128
+ #jobs without stages are sent to admins
129
+ [Gdrive.admin_group_name,Gdrive.domain].join("@")
126
130
  end
127
131
  exc_to_s = f['error']
128
132
  if fjobs[email].nil?
@@ -0,0 +1,143 @@
1
+ module Mobilize
2
+ module Jobtracker
3
+ def Jobtracker.config
4
+ Base.config('jobtracker')
5
+ end
6
+
7
+ #modify this to increase the frequency of request cycles
8
+ def Jobtracker.cycle_freq
9
+ Jobtracker.config['cycle_freq']
10
+ end
11
+
12
+ #frequency of notifications
13
+ def Jobtracker.notification_freq
14
+ Jobtracker.config['notification_freq']
15
+ end
16
+
17
+ def Jobtracker.runner_read_freq
18
+ Jobtracker.config['runner_read_freq']
19
+ end
20
+
21
+ #long running tolerance
22
+ def Jobtracker.max_run_time
23
+ Jobtracker.config['max_run_time']
24
+ end
25
+
26
+ def Jobtracker.admins
27
+ Jobtracker.config['admins']
28
+ end
29
+
30
+ def Jobtracker.worker
31
+ Resque.find_worker_by_path("jobtracker")
32
+ end
33
+
34
+ def Jobtracker.workers(state="all")
35
+ Resque.workers(state)
36
+ end
37
+
38
+ def Jobtracker.status
39
+ args = Jobtracker.get_args
40
+ return args['status'] if args
41
+ job = Resque.jobs.select{|j| j['args'].first=='jobtracker'}.first
42
+ return 'queued' if job
43
+ return 'stopped'
44
+ end
45
+
46
+ def Jobtracker.update_status(msg)
47
+ #this is to keep jobtracker from resisting stop commands
48
+ return false if Jobtracker.status=="stopping"
49
+ #Jobtracker has no persistent database state
50
+ Resque.set_worker_args_by_path("jobtracker",{'status'=>msg})
51
+ return true
52
+ end
53
+
54
+ def Jobtracker.restart
55
+ Jobtracker.stop!
56
+ Jobtracker.start
57
+ end
58
+
59
+ def Jobtracker.set_args(args)
60
+ Resque.set_worker_args(Jobtracker.worker,args)
61
+ return true
62
+ end
63
+
64
+ def Jobtracker.get_args
65
+ Resque.get_worker_args(Jobtracker.worker)
66
+ end
67
+
68
+ def Jobtracker.kill_workers
69
+ Resque.kill_workers
70
+ end
71
+
72
+ def Jobtracker.kill_idle_workers
73
+ Resque.kill_idle_workers
74
+ end
75
+
76
+ def Jobtracker.kill_idle_and_stale_workers
77
+ Resque.kill_idle_and_stale_workers
78
+ end
79
+
80
+ def Jobtracker.prep_workers
81
+ Resque.prep_workers
82
+ end
83
+
84
+ def Jobtracker.failures
85
+ Resque.failures
86
+ end
87
+
88
+ def Jobtracker.start
89
+ if Jobtracker.status!='stopped'
90
+ Jobtracker.update_status("Jobtracker still #{Jobtracker.status}")
91
+ else
92
+ #make sure that workers are running and at the right number
93
+ #Resque.prep_workers
94
+ #queue up the jobtracker (starts the perform method)
95
+ Jobtracker.enqueue!
96
+ end
97
+ return true
98
+ end
99
+
100
+ def Jobtracker.enqueue!
101
+ ::Resque::Job.create(Resque.queue_name, Jobtracker, 'jobtracker',{})
102
+ end
103
+
104
+ def Jobtracker.restart!
105
+ Jobtracker.stop!
106
+ Jobtracker.start
107
+ return true
108
+ end
109
+
110
+ def Jobtracker.restart_workers!
111
+ Jobtracker.kill_workers
112
+ sleep 10
113
+ Jobtracker.prep_workers
114
+ Jobtracker.update_status("put workers back on the queue")
115
+ end
116
+
117
+ def Jobtracker.stop!
118
+ #send signal for Jobtracker to check for
119
+ Jobtracker.update_status('stopping')
120
+ sleep 5
121
+ i=0
122
+ while Jobtracker.status=='stopping'
123
+ puts "#{Jobtracker.to_s} still on queue, waiting"
124
+ sleep 5
125
+ i+=1
126
+ end
127
+ return true
128
+ end
129
+
130
+ def Jobtracker.last_notification
131
+ return Jobtracker.get_args["last_notification"] if Jobtracker.get_args
132
+ end
133
+
134
+ def Jobtracker.last_notification=(time)
135
+ Jobtracker.set_args({"last_notification"=>time})
136
+ end
137
+
138
+ def Jobtracker.notif_due?
139
+ last_duetime = Time.now.utc - Jobtracker.notification_freq
140
+ return (Jobtracker.last_notification.to_s.length==0 || Jobtracker.last_notification.to_datetime < last_duetime)
141
+ end
142
+ end
143
+ end
@@ -36,7 +36,7 @@ module Mobilize
36
36
  #creating it
37
37
  jobs_sheet = Gsheet.find_or_create_by_path(r.path,gdrive_slot)
38
38
  unless jobs_sheet.spreadsheet.acl_entry(u.email).ie{|e| e and e.role=="owner"}
39
- jobs_sheet.spreadsheet.update_acl(u.email,"writer")
39
+ jobs_sheet.spreadsheet.update_acl(u.email)
40
40
  end
41
41
  jobs_sheet.add_headers(r.headers)
42
42
  begin;jobs_sheet.delete_sheet1;rescue;end #don't care if sheet1 deletion fails
@@ -1,148 +1,7 @@
1
1
  module Mobilize
2
2
  module Jobtracker
3
- def Jobtracker.config
4
- Base.config('jobtracker')
5
- end
6
-
7
- #modify this to increase the frequency of request cycles
8
- def Jobtracker.cycle_freq
9
- Jobtracker.config['cycle_freq']
10
- end
11
-
12
- #frequency of notifications
13
- def Jobtracker.notification_freq
14
- Jobtracker.config['notification_freq']
15
- end
16
-
17
- def Jobtracker.runner_read_freq
18
- Jobtracker.config['runner_read_freq']
19
- end
20
-
21
- #long running tolerance
22
- def Jobtracker.max_run_time
23
- Jobtracker.config['max_run_time']
24
- end
25
-
26
- def Jobtracker.admins
27
- Jobtracker.config['admins']
28
- end
29
-
30
- def Jobtracker.admin_emails
31
- Jobtracker.admins.map{|a| a['email'] }
32
- end
33
-
34
- def Jobtracker.worker
35
- Resque.find_worker_by_path("jobtracker")
36
- end
37
-
38
- def Jobtracker.workers(state="all")
39
- Resque.workers(state)
40
- end
41
-
42
- def Jobtracker.status
43
- args = Jobtracker.get_args
44
- return args['status'] if args
45
- job = Resque.jobs.select{|j| j['args'].first=='jobtracker'}.first
46
- return 'queued' if job
47
- return 'stopped'
48
- end
49
-
50
- def Jobtracker.update_status(msg)
51
- #this is to keep jobtracker from resisting stop commands
52
- return false if Jobtracker.status=="stopping"
53
- #Jobtracker has no persistent database state
54
- Resque.set_worker_args_by_path("jobtracker",{'status'=>msg})
55
- return true
56
- end
57
-
58
- def Jobtracker.restart
59
- Jobtracker.stop!
60
- Jobtracker.start
61
- end
62
-
63
- def Jobtracker.set_args(args)
64
- Resque.set_worker_args(Jobtracker.worker,args)
65
- return true
66
- end
67
-
68
- def Jobtracker.get_args
69
- Resque.get_worker_args(Jobtracker.worker)
70
- end
71
-
72
- def Jobtracker.kill_workers
73
- Resque.kill_workers
74
- end
75
-
76
- def Jobtracker.kill_idle_workers
77
- Resque.kill_idle_workers
78
- end
79
-
80
- def Jobtracker.kill_idle_and_stale_workers
81
- Resque.kill_idle_and_stale_workers
82
- end
83
-
84
- def Jobtracker.prep_workers
85
- Resque.prep_workers
86
- end
87
-
88
- def Jobtracker.failures
89
- Resque.failures
90
- end
91
-
92
- def Jobtracker.start
93
- if Jobtracker.status!='stopped'
94
- Jobtracker.update_status("Jobtracker still #{Jobtracker.status}")
95
- else
96
- #make sure that workers are running and at the right number
97
- #Resque.prep_workers
98
- #queue up the jobtracker (starts the perform method)
99
- Jobtracker.enqueue!
100
- end
101
- return true
102
- end
103
-
104
- def Jobtracker.enqueue!
105
- ::Resque::Job.create(Resque.queue_name, Jobtracker, 'jobtracker',{})
106
- end
107
-
108
- def Jobtracker.restart!
109
- Jobtracker.stop!
110
- Jobtracker.start
111
- return true
112
- end
113
-
114
- def Jobtracker.restart_workers!
115
- Jobtracker.kill_workers
116
- sleep 10
117
- Jobtracker.prep_workers
118
- Jobtracker.update_status("put workers back on the queue")
119
- end
120
-
121
- def Jobtracker.stop!
122
- #send signal for Jobtracker to check for
123
- Jobtracker.update_status('stopping')
124
- sleep 5
125
- i=0
126
- while Jobtracker.status=='stopping'
127
- puts "#{Jobtracker.to_s} still on queue, waiting"
128
- sleep 5
129
- i+=1
130
- end
131
- return true
132
- end
133
-
134
- def Jobtracker.last_notification
135
- return Jobtracker.get_args["last_notification"] if Jobtracker.get_args
136
- end
137
-
138
- def Jobtracker.last_notification=(time)
139
- Jobtracker.set_args({"last_notification"=>time})
140
- end
141
-
142
- def Jobtracker.notif_due?
143
- last_duetime = Time.now.utc - Jobtracker.notification_freq
144
- return (Jobtracker.last_notification.to_s.length==0 || Jobtracker.last_notification.to_datetime < last_duetime)
145
- end
3
+ #adds convenience methods
4
+ require "#{File.dirname(__FILE__)}/helpers/jobtracker_helper"
146
5
 
147
6
  def Jobtracker.max_run_time_workers
148
7
  #return workers who have been cranking away for 6+ hours
@@ -176,19 +35,26 @@ module Mobilize
176
35
  end
177
36
  end.flatten.join("\n\n")
178
37
  u = User.where(:name=>email.split("@").first).first
179
- runner_dst = Dataset.find_by_url("gsheet://#{u.runner.path}")
180
- n['body'] += "\n\n#{runner_dst.http_url}" if runner_dst and runner_dst.http_url
38
+ if u
39
+ runner_dst = Dataset.find_by_url("gsheet://#{u.runner.path}")
40
+ n['body'] += "\n\n#{runner_dst.http_url}" if runner_dst and runner_dst.http_url
41
+ end
181
42
  n['to'] = email
182
- n['bcc'] = Jobtracker.admin_emails.join(",")
43
+ n['bcc'] = [Gdrive.admin_group_name,Gdrive.domain].join("@")
183
44
  notifs << n
184
45
  end
185
46
  end
186
47
  lws = Jobtracker.max_run_time_workers
187
48
  if lws.length>0
49
+ bod = begin
50
+ lws.map{|w| w.job['payload']['args']}.first.join("\n")
51
+ rescue
52
+ "Failed to get job names"
53
+ end
188
54
  n = {}
189
55
  n['subject'] = "#{lws.length.to_s} max run time jobs"
190
- n['body'] = lws.map{|w| %{spec:#{w['spec']} stg:#{w['stg']} run_at:#{w['run_at'].to_s}}}.join("\n\n")
191
- n['to'] = Jobtracker.admin_emails.join(",")
56
+ n['body'] = bod
57
+ n['to'] = [Gdrive.admin_group_name,Gdrive.domain].join("@")
192
58
  notifs << n
193
59
  end
194
60
  #deliver each email generated
@@ -250,53 +116,5 @@ module Mobilize
250
116
  end.to_s.strip
251
117
  Time.parse(deploy_time)
252
118
  end
253
-
254
- #test methods
255
- def Jobtracker.restart_test_redis
256
- Jobtracker.stop_test_redis
257
- if !system("which redis-server")
258
- raise "** can't find `redis-server` in your path, you need redis to run Resque and Mobilize"
259
- end
260
- "redis-server #{Base.root}/test/redis-test.conf".bash
261
- end
262
-
263
- def Jobtracker.stop_test_redis
264
- processes = `ps -A -o pid,command | grep [r]edis-test`.split($/)
265
- pids = processes.map { |process| process.split(" ")[0] }
266
- puts "Killing test redis server..."
267
- pids.each { |pid| Process.kill("TERM", pid.to_i) }
268
- puts "removing redis db dump file"
269
- sleep 5
270
- `rm -f #{Base.root}/test/dump.rdb #{Base.root}/test/dump-cluster.rdb`
271
- end
272
-
273
- def Jobtracker.set_test_env
274
- ENV['MOBILIZE_ENV']='test'
275
- ::Resque.redis="localhost:9736"
276
- mongoid_config_path = "#{Base.root}/config/mobilize/mongoid.yml"
277
- Mongoid.load!(mongoid_config_path, Base.env)
278
- end
279
-
280
- def Jobtracker.drop_test_db
281
- Jobtracker.set_test_env
282
- Mongoid.session(:default).collections.each do |collection|
283
- unless collection.name =~ /^system\./
284
- collection.drop
285
- end
286
- end
287
- end
288
-
289
- def Jobtracker.build_test_runner(user_name)
290
- Jobtracker.set_test_env
291
- u = User.where(:name=>user_name).first
292
- Jobtracker.update_status("delete old books and datasets")
293
- # delete any old runner from previous test runs
294
- gdrive_slot = Gdrive.owner_email
295
- u.runner.gsheet(gdrive_slot).spreadsheet.delete
296
- Dataset.find_by_handler_and_path('gbook',u.runner.title).delete
297
- Jobtracker.update_status("enqueue jobtracker, wait 45s")
298
- Mobilize::Jobtracker.start
299
- sleep 45
300
- end
301
119
  end
302
120
  end
@@ -101,6 +101,11 @@ module Mobilize
101
101
  return true
102
102
  end
103
103
 
104
+ def worker
105
+ r = self
106
+ Mobilize::Resque.find_worker_by_path(r.path)
107
+ end
108
+
104
109
  def enqueue!
105
110
  r = self
106
111
  ::Resque::Job.create("mobilize",Runner,r.path,{})
@@ -44,6 +44,14 @@ module Mobilize
44
44
 
45
45
  def Stage.perform(id,*args)
46
46
  s = Stage.where(:path=>id).first
47
+ #check to make sure params are parsable
48
+ begin
49
+ param_hash = s.params
50
+ raise ScriptError if param_hash.class!=Hash
51
+ rescue StandardError, ScriptError
52
+ s.fail({'signal'=>500,
53
+ 'err_str'=>"Unable to parse stage params, make sure you don't have issues with your quotes, commas, or colons."})
54
+ end
47
55
  s.update_attributes(:started_at=>Time.now.utc)
48
56
  s.update_status(%{Starting at #{Time.now.utc}})
49
57
  #get response by running method
@@ -112,7 +120,11 @@ module Mobilize
112
120
  j = s.job
113
121
  r = j.runner
114
122
  u = r.user
115
- j.update_attributes(:active=>false) if s.params['always_on'].to_s=="false"
123
+ begin
124
+ j.update_attributes(:active=>false) if s.params['always_on'].to_s=="false"
125
+ rescue StandardError, ScriptError
126
+ #skip due to parse error on params
127
+ end
116
128
  s.update_attributes(:failed_at=>Time.now.utc,:response=>response)
117
129
  stage_name = "#{j.name}_stage#{s.idx.to_s}.err"
118
130
  target_path = (r.path.split("/")[0..-2] + [stage_name]).join("/")
@@ -1,5 +1,5 @@
1
1
  module Mobilize
2
2
  module Base
3
- VERSION = "1.298"
3
+ VERSION = "1.351"
4
4
  end
5
5
  end
@@ -4,8 +4,10 @@ development:
4
4
  owner:
5
5
  name: owner_development
6
6
  pw: google_drive_password
7
+ admin_group_name: admins_development
7
8
  admins:
8
9
  - name: admin
10
+ worker_group_name: workers_development
9
11
  workers:
10
12
  - name: worker_development001
11
13
  pw: worker001_google_drive_password
@@ -16,8 +18,10 @@ test:
16
18
  owner:
17
19
  name: owner_test
18
20
  pw: google_drive_password
21
+ admin_group_name: admins_test
19
22
  admins:
20
23
  - name: admin
24
+ worker_group_name: workers_test
21
25
  workers:
22
26
  - name: worker_test001
23
27
  pw: worker001_google_drive_password
@@ -28,8 +32,10 @@ production:
28
32
  owner:
29
33
  name: owner_production
30
34
  pw: google_drive_password
35
+ admin_group_name: admins_production
31
36
  admins:
32
37
  - name: admin
38
+ worker_group_name: workers_production
33
39
  workers:
34
40
  - name: worker_production001
35
41
  pw: worker001_google_drive_password
@@ -5,21 +5,15 @@ development:
5
5
  runner_read_freq: 300 #5 min between runner reads
6
6
  max_run_time: 14400 # if a job runs for 4h+, notification will be sent
7
7
  extensions: [] #additional Mobilize modules to load workers with
8
- admins: #emails to send notifications to
9
- - email: admin@host.com
10
8
  test:
11
9
  cycle_freq: 10 #time between Jobtracker sweeps
12
10
  notification_freq: 3600 #1 hour between failure/timeout notifications
13
11
  runner_read_freq: 300 #5 min between runner reads
14
12
  max_run_time: 14400 # if a job runs for 4h+, notification will be sent
15
13
  extensions: [] #additional Mobilize modules to load workers with
16
- admins: #emails to send notifications to
17
- - email: admin@host.com
18
14
  production:
19
15
  cycle_freq: 10 #time between Jobtracker sweeps
20
16
  notification_freq: 3600 #1 hour between failure/timeout notifications
21
17
  runner_read_freq: 300 #5 min between runner reads
22
18
  max_run_time: 14400 # if a job runs for 4h+, notification will be sent
23
19
  extensions: [] #additional Mobilize modules to load workers with
24
- admins: #emails to send notifications to
25
- - email: admin@host.com
@@ -3,9 +3,13 @@
3
3
  trigger: once
4
4
  status: ""
5
5
  stage1: gsheet.write source:"gfile://test_base_1.tsv", target:base1.out
6
-
7
6
  - name: base2
8
7
  active: true
9
8
  trigger: after base1
10
9
  status: ""
11
10
  stage1: gsheet.write source:base1.out, target:base2.out
11
+ - name: base3
12
+ active: true
13
+ trigger: after base2
14
+ status: ""
15
+ stage1: gsheet.write source:"base1."_"", target:base2.out
@@ -1,43 +1,34 @@
1
1
  require 'test_helper'
2
2
 
3
3
  describe "Mobilize" do
4
-
5
- def before
6
- puts 'nothing before'
7
- end
8
-
9
- # enqueues 4 workers on Resque
10
4
  it "runs integration test" do
11
5
 
12
6
  puts "restart test redis"
13
- Mobilize::Jobtracker.restart_test_redis
7
+ TestHelper.restart_test_redis
14
8
 
15
9
  puts "clear out test db"
16
- Mobilize::Jobtracker.drop_test_db
10
+ TestHelper.drop_test_db
17
11
 
18
12
  puts "restart workers"
19
13
  Mobilize::Jobtracker.restart_workers!
20
14
 
21
- puts "build test runner"
22
- gdrive_slot = Mobilize::Gdrive.owner_email
23
- puts "create user 'mobilize'"
24
- user_name = gdrive_slot.split("@").first
25
- u = Mobilize::User.find_or_create_by_name(user_name)
26
- assert u.email == gdrive_slot
15
+ u = TestHelper.owner_user
16
+ user_name = u.name
17
+ gdrive_slot = u.email
27
18
 
28
- Mobilize::Jobtracker.build_test_runner(user_name)
19
+ puts "build test runner"
20
+ TestHelper.build_test_runner(user_name)
29
21
  assert Mobilize::Jobtracker.workers.length == Mobilize::Resque.config['max_workers'].to_i
30
22
 
31
23
  puts "Jobtracker created runner with 'jobs' sheet?"
24
+
32
25
  r = u.runner
33
26
  jobs_sheet_url = "gsheet://#{r.path}"
34
27
  jobs_sheet = Mobilize::Gsheet.find_by_path(r.path,gdrive_slot)
35
28
  jobs_sheet_dst = Mobilize::Dataset.find_or_create_by_url(jobs_sheet_url)
36
29
  jobs_sheet_tsv = jobs_sheet_dst.read(user_name,gdrive_slot)
37
- assert jobs_sheet_tsv.tsv_header_array.join.length == 53 #total header length
38
30
 
39
- #stop Jobtracker, if you're doing this by queueing runners
40
- #Mobilize::Jobtracker.stop!
31
+ assert jobs_sheet_tsv.tsv_header_array.join.length == 53 #total header length
41
32
 
42
33
  puts "add base1 input file"
43
34
  test_filename = "test_base_1"
@@ -53,55 +44,22 @@ describe "Mobilize" do
53
44
  jobs_sheet.reload
54
45
  jobs_sheet.add_or_update_rows(test_job_rows)
55
46
  #wait for stages to complete
56
- #r.enqueue!
57
- wait_for_stages
47
+ TestHelper.wait_for_stages
58
48
 
59
49
  puts "jobtracker posted test sheet data to test destination, and checksum succeeded?"
60
- test_target_sheet_1_url = "gsheet://#{r.title}/base1.out"
61
- test_target_sheet_2_url = "gsheet://#{r.title}/base2.out"
62
- test_error_sheet_url = "gsheet://#{r.title}/base1_stage1.err"
63
-
64
- test_1_tsv = Mobilize::Dataset.read_by_url(test_target_sheet_1_url,user_name,gdrive_slot)
65
- test_2_tsv = Mobilize::Dataset.read_by_url(test_target_sheet_1_url,user_name,gdrive_slot)
50
+ tsv_hash = {}
51
+ ["base1.out", "base2.out", "base3_stage1.err"].each do |sheet_name|
52
+ url = "gsheet://#{r.title}/#{sheet_name}"
53
+ data = Mobilize::Dataset.read_by_url(url,user_name,gdrive_slot)
54
+ tsv_hash[sheet_name] = data
55
+ end
66
56
 
67
- assert test_1_tsv.to_s.length>0
68
- assert test_1_tsv == test_2_tsv
57
+ assert tsv_hash["base1.out"].to_s.length>0
58
+ assert tsv_hash["base2.out"] == tsv_hash["base1.out"]
69
59
 
70
- puts "change first job to fail, wait for stages"
71
- test_job_rows.first['stage1'] = %{gsheet.write source:"gfile://test_base_1.fail", target:base1.out, retries:3}
72
- Mobilize::Dataset.write_by_url(test_error_sheet_url," ",user_name,gdrive_slot)
73
- jobs_sheet.add_or_update_rows(test_job_rows)
60
+ base3_response = tsv_hash["base3_stage1.err"].tsv_to_hash_array.first['response']
61
+ assert base3_response == "Unable to parse stage params, make sure you don't have issues with your quotes, commas, or colons."
74
62
 
75
- #wait for stages to complete
76
- wait_for_stages
77
-
78
- test_error_sheet = Mobilize::Gsheet.find_by_path("#{r.path.split("/")[0..-2].join("/")}/base1_stage1.err",gdrive_slot)
79
- puts "jobtracker posted failing test error to sheet "
80
- error_rows = test_error_sheet.read(user_name).tsv_to_hash_array
81
- assert error_rows.first['response'] == "Could not get gfile://test_base_1.fail with error: unable to find test_base_1.fail"
82
63
  Mobilize::Jobtracker.stop!
83
64
  end
84
-
85
- def wait_for_stages(time_limit=600,stage_limit=120,wait_length=10)
86
- time = 0
87
- time_since_stage = 0
88
- #check for 10 min
89
- while time < time_limit and time_since_stage < stage_limit
90
- sleep wait_length
91
- job_classes = Mobilize::Resque.jobs.map{|j| j['class']}
92
- if job_classes.include?("Mobilize::Stage")
93
- time_since_stage = 0
94
- puts "saw stage at #{time.to_s} seconds"
95
- else
96
- time_since_stage += wait_length
97
- puts "#{time_since_stage.to_s} seconds since stage seen"
98
- end
99
- time += wait_length
100
- puts "total wait time #{time.to_s} seconds"
101
- end
102
-
103
- if time >= time_limit
104
- raise "Timed out before stage completion"
105
- end
106
- end
107
65
  end
data/test/test_helper.rb CHANGED
@@ -8,3 +8,82 @@ $dir = File.dirname(File.expand_path(__FILE__))
8
8
  ENV['MOBILIZE_ENV'] = 'test'
9
9
  require 'mobilize-base'
10
10
  $TESTING = true
11
+ module TestHelper
12
+ def TestHelper.wait_for_stages(time_limit=600,stage_limit=120,wait_length=10)
13
+ time = 0
14
+ time_since_stage = 0
15
+ #check for 10 min
16
+ while time < time_limit and time_since_stage < stage_limit
17
+ sleep wait_length
18
+ job_classes = Mobilize::Resque.jobs.map{|j| j['class']}
19
+ if job_classes.include?("Mobilize::Stage")
20
+ time_since_stage = 0
21
+ puts "saw stage at #{time.to_s} seconds"
22
+ else
23
+ time_since_stage += wait_length
24
+ puts "#{time_since_stage.to_s} seconds since stage seen"
25
+ end
26
+ time += wait_length
27
+ puts "total wait time #{time.to_s} seconds"
28
+ end
29
+
30
+ if time >= time_limit
31
+ raise "Timed out before stage completion"
32
+ end
33
+ end
34
+
35
+ #test methods
36
+ def TestHelper.restart_test_redis
37
+ TestHelper.stop_test_redis
38
+ if !system("which redis-server")
39
+ raise "** can't find `redis-server` in your path, you need redis to run Resque and Mobilize"
40
+ end
41
+ "redis-server #{Mobilize::Base.root}/test/redis-test.conf".bash
42
+ end
43
+
44
+ def TestHelper.stop_test_redis
45
+ processes = `ps -A -o pid,command | grep [r]edis-test`.split($/)
46
+ pids = processes.map { |process| process.split(" ")[0] }
47
+ puts "Killing test redis server..."
48
+ pids.each { |pid| Process.kill("TERM", pid.to_i) }
49
+ puts "removing redis db dump file"
50
+ sleep 5
51
+ `rm -f #{Mobilize::Base.root}/test/dump.rdb #{Mobilize::Base.root}/test/dump-cluster.rdb`
52
+ end
53
+
54
+ def TestHelper.set_test_env
55
+ ENV['MOBILIZE_ENV']='test'
56
+ ::Resque.redis="localhost:9736"
57
+ mongoid_config_path = "#{Mobilize::Base.root}/config/mobilize/mongoid.yml"
58
+ Mongoid.load!(mongoid_config_path, Mobilize::Base.env)
59
+ end
60
+
61
+ def TestHelper.drop_test_db
62
+ TestHelper.set_test_env
63
+ Mongoid.session(:default).collections.each do |collection|
64
+ unless collection.name =~ /^system\./
65
+ collection.drop
66
+ end
67
+ end
68
+ end
69
+
70
+ def TestHelper.build_test_runner(user_name)
71
+ TestHelper.set_test_env
72
+ u = Mobilize::User.where(:name=>user_name).first
73
+ Mobilize::Jobtracker.update_status("delete old books and datasets")
74
+ # delete any old runner from previous test runs
75
+ gdrive_slot = Mobilize::Gdrive.owner_email
76
+ u.runner.gsheet(gdrive_slot).spreadsheet.delete
77
+ Mobilize::Dataset.find_by_handler_and_path('gbook',u.runner.title).delete
78
+ Mobilize::Jobtracker.update_status("enqueue jobtracker, wait 45s")
79
+ Mobilize::Jobtracker.start
80
+ sleep 45
81
+ end
82
+
83
+ def TestHelper.owner_user
84
+ gdrive_slot = Mobilize::Gdrive.owner_email
85
+ puts "create user 'mobilize'"
86
+ user_name = gdrive_slot.split("@").first
87
+ return Mobilize::User.find_or_create_by_name(user_name)
88
+ end
89
+ end
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.298'
4
+ version: '1.351'
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: 2013-04-17 00:00:00.000000000 Z
12
+ date: 2013-04-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -191,6 +191,7 @@ files:
191
191
  - lib/mobilize-base/handlers/gsheet.rb
192
192
  - lib/mobilize-base/handlers/resque.rb
193
193
  - lib/mobilize-base/helpers/job_helper.rb
194
+ - lib/mobilize-base/helpers/jobtracker_helper.rb
194
195
  - lib/mobilize-base/helpers/runner_helper.rb
195
196
  - lib/mobilize-base/helpers/stage_helper.rb
196
197
  - lib/mobilize-base/jobtracker.rb
@@ -228,7 +229,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
228
229
  version: '0'
229
230
  segments:
230
231
  - 0
231
- hash: 2488340168623489502
232
+ hash: 4420073828570035753
232
233
  required_rubygems_version: !ruby/object:Gem::Requirement
233
234
  none: false
234
235
  requirements:
@@ -237,7 +238,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
237
238
  version: '0'
238
239
  segments:
239
240
  - 0
240
- hash: 2488340168623489502
241
+ hash: 4420073828570035753
241
242
  requirements: []
242
243
  rubyforge_project: mobilize-base
243
244
  rubygems_version: 1.8.25