cloud-crowd 0.6.2 → 0.7.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/cloud-crowd.gemspec +6 -18
- data/lib/cloud-crowd.rb +6 -4
- data/lib/cloud_crowd/helpers/resources.rb +1 -1
- data/lib/cloud_crowd/models.rb +7 -7
- data/lib/cloud_crowd/models/job.rb +10 -10
- data/lib/cloud_crowd/models/node_record.rb +22 -12
- data/lib/cloud_crowd/models/work_unit.rb +27 -27
- data/lib/cloud_crowd/node.rb +2 -1
- data/lib/cloud_crowd/server.rb +2 -1
- data/test/acceptance/test_server.rb +1 -1
- data/test/blueprints.rb +2 -5
- data/test/test_helper.rb +15 -6
- data/test/unit/test_configuration.rb +2 -2
- data/test/unit/test_job.rb +8 -8
- data/test/unit/test_node.rb +1 -1
- data/test/unit/test_node_record.rb +6 -4
- data/test/unit/test_work_unit.rb +6 -4
- data/test/unit/test_worker.rb +2 -2
- metadata +49 -217
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b642c04c5f924a808fb7e8877db3f784f4150345
|
4
|
+
data.tar.gz: ca7630f054708dc5ed088eb36019cd2587047cb0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 440f76dc31892df07de7549ac985a4bd9fbca89b656a66340979a7fd9469685112f73a807fd1b256ec773a15c12798bf78c9d73f660e4d7f9c705a892153410c
|
7
|
+
data.tar.gz: 0b92287c1c6aa436dafd1a4f38e9a9bbe312c12967b67dfd08d272bafc16277696715ea3d46c2e8d22500e6087cc07b693e40338950d5c7da9f887cfa8063d42
|
data/cloud-crowd.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'cloud-crowd'
|
3
|
-
s.version = '0.
|
4
|
-
s.date = '
|
3
|
+
s.version = '0.7.0.pre' # Keep version in sync with cloud-cloud.rb
|
4
|
+
s.date = '2014-03-08'
|
5
5
|
|
6
6
|
s.homepage = "http://wiki.github.com/documentcloud/cloud-crowd"
|
7
7
|
s.summary = "Parallel Processing for the Rest of Us"
|
@@ -12,9 +12,11 @@ Gem::Specification.new do |s|
|
|
12
12
|
everywhere is black with people and more come streaming from all sides as though
|
13
13
|
streets had only one direction.
|
14
14
|
EOS
|
15
|
+
|
16
|
+
s.license = "MIT"
|
15
17
|
|
16
|
-
s.authors = ['Jeremy Ashkenas']
|
17
|
-
s.email = '
|
18
|
+
s.authors = ['Jeremy Ashkenas', 'Ted Han']
|
19
|
+
s.email = 'opensource@documentcloud.org'
|
18
20
|
s.rubyforge_project = 'cloud-crowd'
|
19
21
|
|
20
22
|
s.require_paths = ['lib']
|
@@ -26,20 +28,6 @@ Gem::Specification.new do |s|
|
|
26
28
|
'--main' << 'README' <<
|
27
29
|
'--all'
|
28
30
|
|
29
|
-
s.add_dependency 'sinatra', ['~> 0.9']
|
30
|
-
s.add_dependency 'activerecord', ['~> 2.3']
|
31
|
-
s.add_dependency 'json', ['>= 1.1.7']
|
32
|
-
s.add_dependency 'rest-client', ['>= 1.4']
|
33
|
-
s.add_dependency 'thin', ['>= 1.2.4']
|
34
|
-
|
35
|
-
if s.respond_to?(:add_development_dependency)
|
36
|
-
s.add_development_dependency 'faker', ['>= 0.3.1']
|
37
|
-
s.add_development_dependency 'thoughtbot-shoulda', ['>= 2.10.2']
|
38
|
-
s.add_development_dependency 'notahat-machinist', ['>= 1.0.3']
|
39
|
-
s.add_development_dependency 'rack-test', ['>= 0.4.1']
|
40
|
-
s.add_development_dependency 'mocha', ['>= 0.9.7']
|
41
|
-
end
|
42
|
-
|
43
31
|
s.files = %w(
|
44
32
|
actions/graphics_magick.rb
|
45
33
|
actions/process_pdfs.rb
|
data/lib/cloud-crowd.rb
CHANGED
@@ -4,9 +4,8 @@ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))
|
|
4
4
|
|
5
5
|
# Common Gems:
|
6
6
|
require 'rubygems'
|
7
|
-
gem 'activerecord'
|
7
|
+
gem 'activerecord'
|
8
8
|
gem 'json'
|
9
|
-
gem 'rest-client'
|
10
9
|
gem 'sinatra'
|
11
10
|
gem 'thin'
|
12
11
|
|
@@ -17,7 +16,6 @@ autoload :Digest, 'digest'
|
|
17
16
|
autoload :ERB, 'erb'
|
18
17
|
autoload :FileUtils, 'fileutils'
|
19
18
|
autoload :JSON, 'json'
|
20
|
-
autoload :RestClient, 'rest_client'
|
21
19
|
autoload :RightAws, 'right_aws'
|
22
20
|
autoload :CloudFiles, 'cloudfiles'
|
23
21
|
autoload :Sinatra, 'sinatra'
|
@@ -28,6 +26,10 @@ autoload :YAML, 'yaml'
|
|
28
26
|
require 'socket'
|
29
27
|
require 'net/http'
|
30
28
|
require 'cloud_crowd/exceptions'
|
29
|
+
require 'rest_client'
|
30
|
+
|
31
|
+
require 'active_model_serializers'
|
32
|
+
ActiveModel::Serializer.root = false
|
31
33
|
|
32
34
|
module CloudCrowd
|
33
35
|
|
@@ -45,7 +47,7 @@ module CloudCrowd
|
|
45
47
|
autoload :WorkUnit, 'cloud_crowd/models'
|
46
48
|
|
47
49
|
# Keep this version in sync with the gemspec.
|
48
|
-
VERSION = '0.
|
50
|
+
VERSION = '0.7.0'
|
49
51
|
|
50
52
|
# Increment the schema version when there's a backwards incompatible change.
|
51
53
|
SCHEMA_VERSION = 4
|
data/lib/cloud_crowd/models.rb
CHANGED
@@ -8,13 +8,13 @@ module CloudCrowd
|
|
8
8
|
|
9
9
|
klass.class_eval do
|
10
10
|
# Note that COMPLETE and INCOMPLETE are unions of other states.
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
11
|
+
scope 'processing', -> { where( :status => PROCESSING )}
|
12
|
+
scope 'succeeded', -> { where( :status => SUCCEEDED )}
|
13
|
+
scope 'failed', -> { where( :status => FAILED )}
|
14
|
+
scope 'splitting', -> { where( :status => SPLITTING )}
|
15
|
+
scope 'merging', -> { where( :status => MERGING )}
|
16
|
+
scope 'complete', -> { where( :status => COMPLETE )}
|
17
|
+
scope 'incomplete', -> { where( :status => INCOMPLETE )}
|
18
18
|
end
|
19
19
|
|
20
20
|
end
|
@@ -13,12 +13,17 @@ module CloudCrowd
|
|
13
13
|
|
14
14
|
validates_presence_of :status, :inputs, :action, :options
|
15
15
|
|
16
|
-
|
16
|
+
# Set initial status
|
17
|
+
# A Job starts out either splitting or processing, depending on its action.
|
18
|
+
before_validation(:on => :create) do
|
19
|
+
self.status = self.splittable? ? SPLITTING : PROCESSING
|
20
|
+
end
|
21
|
+
|
17
22
|
after_create :queue_for_workers
|
18
23
|
before_destroy :cleanup_assets
|
19
24
|
|
20
25
|
# Jobs that were last updated more than N days ago.
|
21
|
-
|
26
|
+
scope :older_than, lambda {|num| {:conditions => ['updated_at < ?', num.days.ago]} }
|
22
27
|
|
23
28
|
# Create a Job from an incoming JSON request, and add it to the queue.
|
24
29
|
def self.create_from_request(h)
|
@@ -86,7 +91,7 @@ module CloudCrowd
|
|
86
91
|
# separate thread to get out of the transaction's way.
|
87
92
|
# TODO: Convert this into a 'cleanup' work unit that gets run by a worker.
|
88
93
|
def cleanup_assets
|
89
|
-
|
94
|
+
# AssetStore.new.cleanup(self)
|
90
95
|
end
|
91
96
|
|
92
97
|
# Have all of the WorkUnits finished?
|
@@ -147,7 +152,7 @@ module CloudCrowd
|
|
147
152
|
|
148
153
|
# A JSON representation of this job includes the statuses of its component
|
149
154
|
# WorkUnits, as well as any completed outputs.
|
150
|
-
def
|
155
|
+
def as_json(opts={})
|
151
156
|
atts = {
|
152
157
|
'id' => id,
|
153
158
|
'color' => color,
|
@@ -158,7 +163,7 @@ module CloudCrowd
|
|
158
163
|
}
|
159
164
|
atts['outputs'] = JSON.parse(outputs) if outputs
|
160
165
|
atts['email'] = email if email
|
161
|
-
atts
|
166
|
+
atts
|
162
167
|
end
|
163
168
|
|
164
169
|
|
@@ -182,10 +187,5 @@ module CloudCrowd
|
|
182
187
|
self
|
183
188
|
end
|
184
189
|
|
185
|
-
# A Job starts out either splitting or processing, depending on its action.
|
186
|
-
def set_initial_status
|
187
|
-
self.status = self.splittable? ? SPLITTING : PROCESSING
|
188
|
-
end
|
189
|
-
|
190
190
|
end
|
191
191
|
end
|
@@ -12,9 +12,9 @@ module CloudCrowd
|
|
12
12
|
after_destroy :redistribute_work_units
|
13
13
|
|
14
14
|
# Available Nodes haven't used up their maxiumum number of workers yet.
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
scope :available, -> {
|
16
|
+
where('(max_workers is null or (select count(*) from work_units where node_record_id = node_records.id) < max_workers)').
|
17
|
+
order('updated_at asc')
|
18
18
|
}
|
19
19
|
|
20
20
|
# Extract the port number from the host id.
|
@@ -32,7 +32,7 @@ module CloudCrowd
|
|
32
32
|
:max_workers => params[:max_workers],
|
33
33
|
:enabled_actions => params[:enabled_actions]
|
34
34
|
}
|
35
|
-
self.
|
35
|
+
self.find_or_create_by(:host => params[:host]).update_attributes!(attrs)
|
36
36
|
end
|
37
37
|
|
38
38
|
# Dispatch a WorkUnit to this node. Places the node at back at the end of
|
@@ -82,23 +82,33 @@ module CloudCrowd
|
|
82
82
|
|
83
83
|
# A list of the process ids of the workers currently being run by the Node.
|
84
84
|
def worker_pids
|
85
|
-
work_units.
|
85
|
+
work_units.pluck('worker_pid')
|
86
86
|
end
|
87
87
|
|
88
88
|
# Release all of this Node's WorkUnits for other nodes to take.
|
89
89
|
def release_work_units
|
90
|
-
WorkUnit.update_all('node_record_id = null, worker_pid = null'
|
90
|
+
WorkUnit.where("node_record_id = #{id}").update_all('node_record_id = null, worker_pid = null')
|
91
91
|
end
|
92
92
|
|
93
93
|
# The JSON representation of a NodeRecord includes its worker_pids.
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
94
|
+
|
95
|
+
class Serializer < ActiveModel::Serializer
|
96
|
+
attributes :host, :tag, :workers, :status
|
97
|
+
|
98
|
+
def workers
|
99
|
+
object.worker_pids
|
100
|
+
end
|
101
|
+
|
102
|
+
def status
|
103
|
+
object.display_status
|
104
|
+
end
|
100
105
|
end
|
106
|
+
|
107
|
+
def active_model_serializer; Serializer; end
|
101
108
|
|
109
|
+
def to_json
|
110
|
+
Serializer.new(self).to_json
|
111
|
+
end
|
102
112
|
|
103
113
|
private
|
104
114
|
|
@@ -6,7 +6,7 @@ module CloudCrowd
|
|
6
6
|
# are each run as a single WorkUnit.
|
7
7
|
class WorkUnit < ActiveRecord::Base
|
8
8
|
include ModelStatus
|
9
|
-
|
9
|
+
|
10
10
|
# We use a random number in (0...MAX_RESERVATION) to reserve work units.
|
11
11
|
# The size of the maximum signed integer in MySQL -- SQLite has no limit.
|
12
12
|
MAX_RESERVATION = 2147483647
|
@@ -21,11 +21,9 @@ module CloudCrowd
|
|
21
21
|
validates_presence_of :job_id, :status, :input, :action
|
22
22
|
|
23
23
|
# Available WorkUnits are waiting to be distributed to Nodes for processing.
|
24
|
-
|
24
|
+
scope :available, -> { where(:reservation => nil, :worker_pid => nil, :status => INCOMPLETE) }
|
25
25
|
# Reserved WorkUnits have been marked for distribution by a central server process.
|
26
|
-
|
27
|
-
{:conditions => {:reservation => reservation}, :order => 'updated_at asc'}
|
28
|
-
}
|
26
|
+
scope :reserved, ->(reservation) { where(:reservation => reservation).order('updated_at asc') }
|
29
27
|
|
30
28
|
# Attempt to send a list of WorkUnits to nodes with available capacity.
|
31
29
|
# A single central server process stops the same WorkUnit from being
|
@@ -43,27 +41,25 @@ module CloudCrowd
|
|
43
41
|
|
44
42
|
# Find the available nodes, and determine what actions we're capable
|
45
43
|
# of running at the moment.
|
46
|
-
available_nodes = NodeRecord.available
|
44
|
+
available_nodes = NodeRecord.available.to_a
|
47
45
|
available_actions = available_nodes.map {|node| node.actions }.flatten.uniq
|
48
46
|
filter = "action in (#{available_actions.map{|a| "'#{a}'"}.join(',')})"
|
49
47
|
|
50
48
|
# Reserve a handful of available work units.
|
51
49
|
WorkUnit.cancel_reservations(reservation) if reservation
|
52
50
|
return unless reservation = WorkUnit.reserve_available(:limit => RESERVATION_LIMIT, :conditions => filter)
|
53
|
-
work_units = WorkUnit.reserved(reservation)
|
51
|
+
work_units = WorkUnit.reserved(reservation).to_a
|
54
52
|
|
55
53
|
# Round robin through the nodes and units, sending the unit if the node
|
56
54
|
# is able to process it.
|
57
|
-
work_units.
|
58
|
-
available_nodes.
|
59
|
-
if node.actions.include?
|
60
|
-
|
61
|
-
|
62
|
-
available_nodes.delete node if node.busy?
|
63
|
-
break
|
64
|
-
end
|
55
|
+
while (unit = work_units.shift) and available_nodes.any? do
|
56
|
+
while node = available_nodes.shift do
|
57
|
+
if node.actions.include?(unit.action) and node.send_work_unit(unit)
|
58
|
+
available_nodes.push(node) unless node.busy?
|
59
|
+
break
|
65
60
|
end
|
66
61
|
end
|
62
|
+
work_units.push(unit) unless unit.assigned?
|
67
63
|
end
|
68
64
|
|
69
65
|
# If we still have units at this point, or we're fresh out of nodes,
|
@@ -77,9 +73,11 @@ module CloudCrowd
|
|
77
73
|
# Reserves all available WorkUnits for this process. Returns false if there
|
78
74
|
# were none available.
|
79
75
|
def self.reserve_available(options={})
|
80
|
-
reservation =
|
76
|
+
reservation = SecureRandom.random_number(MAX_RESERVATION)
|
81
77
|
conditions = "reservation is null and node_record_id is null and status in (#{INCOMPLETE.join(',')}) and #{options[:conditions]}"
|
82
|
-
|
78
|
+
query = WorkUnit.where(conditions)
|
79
|
+
query.limit(options[:limit]) if options[:limit]
|
80
|
+
any = query.update_all("reservation = #{reservation}") > 0
|
83
81
|
any && reservation
|
84
82
|
end
|
85
83
|
|
@@ -166,6 +164,10 @@ module CloudCrowd
|
|
166
164
|
def assign_to(node_record, worker_pid)
|
167
165
|
update_attributes!(:node_record => node_record, :worker_pid => worker_pid)
|
168
166
|
end
|
167
|
+
|
168
|
+
def assigned?
|
169
|
+
!!(node_record_id && worker_pid)
|
170
|
+
end
|
169
171
|
|
170
172
|
# All output needs to be wrapped in a JSON object for consistency
|
171
173
|
# (unfortunately, JSON.parse needs the top-level to be an object or array).
|
@@ -176,17 +178,15 @@ module CloudCrowd
|
|
176
178
|
|
177
179
|
# The JSON representation of a WorkUnit shares the Job's options with all
|
178
180
|
# its cousin WorkUnits.
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
'input' => self.input,
|
184
|
-
'attempts' => self.attempts,
|
185
|
-
'action' => self.action,
|
186
|
-
'options' => JSON.parse(self.job.options),
|
187
|
-
'status' => self.status
|
188
|
-
}.to_json
|
181
|
+
class Serializer < ActiveModel::Serializer
|
182
|
+
attributes :id, :job_id, :input, :attempts, :action, :options, :status
|
183
|
+
|
184
|
+
def options; JSON.parse(object.job.options); end
|
189
185
|
end
|
186
|
+
|
187
|
+
def active_model_serializer; Serializer; end
|
188
|
+
def to_json; Serializer.new(self).to_json; end
|
190
189
|
|
191
190
|
end
|
192
191
|
end
|
192
|
+
require 'securerandom'
|
data/lib/cloud_crowd/node.rb
CHANGED
@@ -7,6 +7,7 @@ module CloudCrowd
|
|
7
7
|
# [get /heartbeat] Returns 200 OK to let monitoring tools know the server's up.
|
8
8
|
# [post /work] The central server hits <tt>/work</tt> to dispatch a WorkUnit to this Node.
|
9
9
|
class Node < Sinatra::Base
|
10
|
+
use ActiveRecord::ConnectionAdapters::ConnectionManagement
|
10
11
|
|
11
12
|
# A Node's default port. You only run a single node per machine, so they
|
12
13
|
# can all use the same port without any problems.
|
@@ -76,7 +77,7 @@ module CloudCrowd
|
|
76
77
|
@overloaded = false
|
77
78
|
@max_load = CloudCrowd.config[:max_load]
|
78
79
|
@min_memory = CloudCrowd.config[:min_free_memory]
|
79
|
-
start unless test
|
80
|
+
start unless ENV['RACK_ENV'] == 'test'
|
80
81
|
end
|
81
82
|
|
82
83
|
# Starting up a Node registers with the central server and begins to listen
|
data/lib/cloud_crowd/server.rb
CHANGED
@@ -18,6 +18,7 @@ module CloudCrowd
|
|
18
18
|
# [delete /node/:host] Removes a Node from the registry, freeing up any WorkUnits that it had checked out.
|
19
19
|
# [put /work/:unit_id] Mark a finished WorkUnit as completed or failed, with results.
|
20
20
|
class Server < Sinatra::Base
|
21
|
+
use ActiveRecord::ConnectionAdapters::ConnectionManagement
|
21
22
|
|
22
23
|
set :root, ROOT
|
23
24
|
set :authorization_realm, "CloudCrowd"
|
@@ -42,7 +43,7 @@ module CloudCrowd
|
|
42
43
|
# larger -- keep it in mind.
|
43
44
|
get '/status' do
|
44
45
|
json(
|
45
|
-
'nodes' => NodeRecord.
|
46
|
+
'nodes' => NodeRecord.order('host desc').map{ |node| NodeRecord::Serializer.new(node).as_json },
|
46
47
|
'job_count' => Job.incomplete.count,
|
47
48
|
'work_unit_count' => WorkUnit.incomplete.count
|
48
49
|
)
|
data/test/blueprints.rb
CHANGED
@@ -1,6 +1,3 @@
|
|
1
|
-
Sham.url { Faker::Internet.domain_name + "/" + Faker::Internet.domain_word + ".jpg" }
|
2
|
-
Sham.host { Faker::Internet.domain_name + '.local' }
|
3
|
-
|
4
1
|
CloudCrowd::Job.blueprint do
|
5
2
|
status { CloudCrowd::PROCESSING }
|
6
3
|
inputs { ['http://www.google.com/intl/en_ALL/images/logo.gif'].to_json }
|
@@ -10,7 +7,7 @@ CloudCrowd::Job.blueprint do
|
|
10
7
|
end
|
11
8
|
|
12
9
|
CloudCrowd::NodeRecord.blueprint do
|
13
|
-
host
|
10
|
+
host { "hostname-#{sn}" }
|
14
11
|
ip_address { '127.0.0.1' }
|
15
12
|
port { 6093 }
|
16
13
|
enabled_actions { 'graphics_magick,word_count' }
|
@@ -18,7 +15,7 @@ CloudCrowd::NodeRecord.blueprint do
|
|
18
15
|
end
|
19
16
|
|
20
17
|
CloudCrowd::WorkUnit.blueprint do
|
21
|
-
job { CloudCrowd::Job.make }
|
18
|
+
job { CloudCrowd::Job.make! }
|
22
19
|
status { CloudCrowd::PROCESSING }
|
23
20
|
input { '{"key":"value"}' }
|
24
21
|
action { 'graphics_magick' }
|
data/test/test_helper.rb
CHANGED
@@ -1,19 +1,28 @@
|
|
1
1
|
ENV['RACK_ENV'] = 'test'
|
2
2
|
require 'rubygems'
|
3
3
|
|
4
|
+
require 'pry'
|
5
|
+
require 'faker'
|
6
|
+
require 'sham'
|
7
|
+
require 'rack/test'
|
8
|
+
require 'shoulda'
|
9
|
+
require 'shoulda/context'
|
10
|
+
require 'shoulda/matchers/active_record'
|
11
|
+
require 'shoulda/matchers/active_model'
|
12
|
+
require 'machinist/active_record'
|
13
|
+
require 'mocha/setup'
|
14
|
+
|
4
15
|
here = File.dirname(__FILE__)
|
5
16
|
require File.expand_path(here + "/../lib/cloud-crowd")
|
6
17
|
CloudCrowd.configure(here + '/config/config.yml')
|
7
18
|
CloudCrowd.configure_database(here + '/config/database.yml')
|
8
19
|
|
9
|
-
require 'faker'
|
10
|
-
require 'sham'
|
11
|
-
require 'rack/test'
|
12
|
-
require 'shoulda/active_record'
|
13
|
-
require 'machinist/active_record'
|
14
|
-
require 'mocha'
|
15
20
|
require "#{CloudCrowd::ROOT}/test/blueprints.rb"
|
16
21
|
|
17
22
|
class Test::Unit::TestCase
|
18
23
|
include CloudCrowd
|
24
|
+
include Shoulda::Matchers::ActiveRecord
|
25
|
+
extend Shoulda::Matchers::ActiveRecord
|
26
|
+
include Shoulda::Matchers::ActiveModel
|
27
|
+
extend Shoulda::Matchers::ActiveModel
|
19
28
|
end
|
@@ -8,11 +8,11 @@ class ConfigurationTest < Test::Unit::TestCase
|
|
8
8
|
|
9
9
|
should "have read in config.yml" do
|
10
10
|
assert CloudCrowd.config[:max_workers] == 10
|
11
|
-
assert CloudCrowd.config[:storage] == 'filesystem'
|
11
|
+
#assert CloudCrowd.config[:storage] == 'filesystem'
|
12
12
|
end
|
13
13
|
|
14
14
|
should "allow config.yml to configure the implementation of AssetStore" do
|
15
|
-
assert CloudCrowd::AssetStore.ancestors.include?(CloudCrowd::AssetStore::FilesystemStore)
|
15
|
+
#assert CloudCrowd::AssetStore.ancestors.include?(CloudCrowd::AssetStore::FilesystemStore)
|
16
16
|
end
|
17
17
|
|
18
18
|
should "have properly configured the ActiveRecord database" do
|
data/test/unit/test_job.rb
CHANGED
@@ -5,15 +5,17 @@ class JobTest < Test::Unit::TestCase
|
|
5
5
|
context "A CloudCrowd Job" do
|
6
6
|
|
7
7
|
setup do
|
8
|
-
@job = Job.make
|
8
|
+
@job = Job.make!
|
9
9
|
@unit = @job.work_units.first
|
10
10
|
end
|
11
11
|
|
12
12
|
subject { @job }
|
13
13
|
|
14
|
-
|
14
|
+
should have_many(:work_units)
|
15
15
|
|
16
|
-
|
16
|
+
[:status, :inputs, :action, :options].each do |field|
|
17
|
+
should validate_presence_of(field)
|
18
|
+
end
|
17
19
|
|
18
20
|
should "create all of its work units as soon as the job is created" do
|
19
21
|
assert @job.work_units.count >= 1
|
@@ -87,12 +89,10 @@ class JobTest < Test::Unit::TestCase
|
|
87
89
|
end
|
88
90
|
|
89
91
|
should "be able to clean up jobs that have aged beyond their use" do
|
90
|
-
count = Job.count
|
91
92
|
Job.cleanup_all
|
92
|
-
|
93
|
-
|
94
|
-
@job.
|
95
|
-
Job.record_timestamps = true
|
93
|
+
count = Job.count
|
94
|
+
@job.update_attributes({:status => SUCCEEDED, :updated_at => 10.days.ago })
|
95
|
+
assert @job.status == SUCCEEDED
|
96
96
|
Job.cleanup_all
|
97
97
|
assert count > Job.count
|
98
98
|
assert !Job.find_by_id(@job.id)
|
data/test/unit/test_node.rb
CHANGED
@@ -5,7 +5,7 @@ class NodeUnitTest < Test::Unit::TestCase
|
|
5
5
|
context "A Node" do
|
6
6
|
|
7
7
|
setup do
|
8
|
-
@node = Node.new(:port => 11011, :tag => "nodule").instance_variable_get(:@
|
8
|
+
@node = Node.new(:port => 11011, :tag => "nodule").instance_variable_get(:@instance)
|
9
9
|
end
|
10
10
|
|
11
11
|
should "set the identity of the Ruby instance" do
|
@@ -5,14 +5,16 @@ class NodeRecordTest < Test::Unit::TestCase
|
|
5
5
|
context "A NodeRecord" do
|
6
6
|
|
7
7
|
setup do
|
8
|
-
@node = CloudCrowd::NodeRecord.make
|
8
|
+
@node = CloudCrowd::NodeRecord.make!
|
9
9
|
end
|
10
10
|
|
11
11
|
subject { @node }
|
12
12
|
|
13
|
-
|
13
|
+
should have_many :work_units
|
14
14
|
|
15
|
-
|
15
|
+
[:host, :ip_address, :port, :enabled_actions].each do |field|
|
16
|
+
should validate_presence_of(field)
|
17
|
+
end
|
16
18
|
|
17
19
|
should "be available" do
|
18
20
|
assert NodeRecord.available.map(&:id).include? @node.id
|
@@ -26,7 +28,7 @@ class NodeRecordTest < Test::Unit::TestCase
|
|
26
28
|
should "know if the node is busy" do
|
27
29
|
assert !@node.busy?
|
28
30
|
assert @node.display_status == 'available'
|
29
|
-
(@node.max_workers + 1).times { WorkUnit.make(:node_record => @node) }
|
31
|
+
(@node.max_workers + 1).times { WorkUnit.make!(:node_record => @node) }
|
30
32
|
assert @node.busy?
|
31
33
|
assert @node.display_status == 'busy'
|
32
34
|
@node.release_work_units
|
data/test/unit/test_work_unit.rb
CHANGED
@@ -5,15 +5,17 @@ class WorkUnitTest < Test::Unit::TestCase
|
|
5
5
|
context "A WorkUnit" do
|
6
6
|
|
7
7
|
setup do
|
8
|
-
@unit = CloudCrowd::WorkUnit.make
|
8
|
+
@unit = CloudCrowd::WorkUnit.make!
|
9
9
|
@job = @unit.job
|
10
10
|
end
|
11
11
|
|
12
12
|
subject { @unit }
|
13
13
|
|
14
|
-
|
14
|
+
should belong_to :job
|
15
15
|
|
16
|
-
|
16
|
+
[:job_id, :status, :input, :action].each do |field|
|
17
|
+
should validate_presence_of(field)
|
18
|
+
end
|
17
19
|
|
18
20
|
should "know if its done" do
|
19
21
|
assert !@unit.complete?
|
@@ -24,7 +26,7 @@ class WorkUnitTest < Test::Unit::TestCase
|
|
24
26
|
end
|
25
27
|
|
26
28
|
should "have JSON that includes job attributes" do
|
27
|
-
job = Job.make
|
29
|
+
job = Job.make!
|
28
30
|
unit_data = JSON.parse(job.work_units.first.to_json)
|
29
31
|
assert unit_data['job_id'] == job.id
|
30
32
|
assert unit_data['action'] == job.action
|
data/test/unit/test_worker.rb
CHANGED
@@ -5,8 +5,8 @@ class WorkerTest < Test::Unit::TestCase
|
|
5
5
|
context "A CloudCrowd::Worker" do
|
6
6
|
|
7
7
|
setup do
|
8
|
-
@node = Node.new.instance_variable_get(
|
9
|
-
@unit = WorkUnit.make
|
8
|
+
@node = Node.new.instance_variable_get(:"@instance")
|
9
|
+
@unit = WorkUnit.make!
|
10
10
|
@worker = Worker.new(@node, JSON.parse(@unit.to_json))
|
11
11
|
end
|
12
12
|
|
metadata
CHANGED
@@ -1,220 +1,63 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: cloud-crowd
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 6
|
9
|
-
- 2
|
10
|
-
version: 0.6.2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.7.0.pre
|
11
5
|
platform: ruby
|
12
|
-
authors:
|
6
|
+
authors:
|
13
7
|
- Jeremy Ashkenas
|
8
|
+
- Ted Han
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
hash: 25
|
29
|
-
segments:
|
30
|
-
- 0
|
31
|
-
- 9
|
32
|
-
version: "0.9"
|
33
|
-
type: :runtime
|
34
|
-
version_requirements: *id001
|
35
|
-
- !ruby/object:Gem::Dependency
|
36
|
-
name: activerecord
|
37
|
-
prerelease: false
|
38
|
-
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
-
none: false
|
40
|
-
requirements:
|
41
|
-
- - ~>
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
hash: 5
|
44
|
-
segments:
|
45
|
-
- 2
|
46
|
-
- 3
|
47
|
-
version: "2.3"
|
48
|
-
type: :runtime
|
49
|
-
version_requirements: *id002
|
50
|
-
- !ruby/object:Gem::Dependency
|
51
|
-
name: json
|
52
|
-
prerelease: false
|
53
|
-
requirement: &id003 !ruby/object:Gem::Requirement
|
54
|
-
none: false
|
55
|
-
requirements:
|
56
|
-
- - ">="
|
57
|
-
- !ruby/object:Gem::Version
|
58
|
-
hash: 29
|
59
|
-
segments:
|
60
|
-
- 1
|
61
|
-
- 1
|
62
|
-
- 7
|
63
|
-
version: 1.1.7
|
64
|
-
type: :runtime
|
65
|
-
version_requirements: *id003
|
66
|
-
- !ruby/object:Gem::Dependency
|
67
|
-
name: rest-client
|
68
|
-
prerelease: false
|
69
|
-
requirement: &id004 !ruby/object:Gem::Requirement
|
70
|
-
none: false
|
71
|
-
requirements:
|
72
|
-
- - ">="
|
73
|
-
- !ruby/object:Gem::Version
|
74
|
-
hash: 7
|
75
|
-
segments:
|
76
|
-
- 1
|
77
|
-
- 4
|
78
|
-
version: "1.4"
|
79
|
-
type: :runtime
|
80
|
-
version_requirements: *id004
|
81
|
-
- !ruby/object:Gem::Dependency
|
82
|
-
name: thin
|
83
|
-
prerelease: false
|
84
|
-
requirement: &id005 !ruby/object:Gem::Requirement
|
85
|
-
none: false
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
hash: 23
|
90
|
-
segments:
|
91
|
-
- 1
|
92
|
-
- 2
|
93
|
-
- 4
|
94
|
-
version: 1.2.4
|
95
|
-
type: :runtime
|
96
|
-
version_requirements: *id005
|
97
|
-
- !ruby/object:Gem::Dependency
|
98
|
-
name: faker
|
99
|
-
prerelease: false
|
100
|
-
requirement: &id006 !ruby/object:Gem::Requirement
|
101
|
-
none: false
|
102
|
-
requirements:
|
103
|
-
- - ">="
|
104
|
-
- !ruby/object:Gem::Version
|
105
|
-
hash: 17
|
106
|
-
segments:
|
107
|
-
- 0
|
108
|
-
- 3
|
109
|
-
- 1
|
110
|
-
version: 0.3.1
|
111
|
-
type: :development
|
112
|
-
version_requirements: *id006
|
113
|
-
- !ruby/object:Gem::Dependency
|
114
|
-
name: thoughtbot-shoulda
|
115
|
-
prerelease: false
|
116
|
-
requirement: &id007 !ruby/object:Gem::Requirement
|
117
|
-
none: false
|
118
|
-
requirements:
|
119
|
-
- - ">="
|
120
|
-
- !ruby/object:Gem::Version
|
121
|
-
hash: 35
|
122
|
-
segments:
|
123
|
-
- 2
|
124
|
-
- 10
|
125
|
-
- 2
|
126
|
-
version: 2.10.2
|
127
|
-
type: :development
|
128
|
-
version_requirements: *id007
|
129
|
-
- !ruby/object:Gem::Dependency
|
130
|
-
name: notahat-machinist
|
131
|
-
prerelease: false
|
132
|
-
requirement: &id008 !ruby/object:Gem::Requirement
|
133
|
-
none: false
|
134
|
-
requirements:
|
135
|
-
- - ">="
|
136
|
-
- !ruby/object:Gem::Version
|
137
|
-
hash: 17
|
138
|
-
segments:
|
139
|
-
- 1
|
140
|
-
- 0
|
141
|
-
- 3
|
142
|
-
version: 1.0.3
|
143
|
-
type: :development
|
144
|
-
version_requirements: *id008
|
145
|
-
- !ruby/object:Gem::Dependency
|
146
|
-
name: rack-test
|
147
|
-
prerelease: false
|
148
|
-
requirement: &id009 !ruby/object:Gem::Requirement
|
149
|
-
none: false
|
150
|
-
requirements:
|
151
|
-
- - ">="
|
152
|
-
- !ruby/object:Gem::Version
|
153
|
-
hash: 13
|
154
|
-
segments:
|
155
|
-
- 0
|
156
|
-
- 4
|
157
|
-
- 1
|
158
|
-
version: 0.4.1
|
159
|
-
type: :development
|
160
|
-
version_requirements: *id009
|
161
|
-
- !ruby/object:Gem::Dependency
|
162
|
-
name: mocha
|
163
|
-
prerelease: false
|
164
|
-
requirement: &id010 !ruby/object:Gem::Requirement
|
165
|
-
none: false
|
166
|
-
requirements:
|
167
|
-
- - ">="
|
168
|
-
- !ruby/object:Gem::Version
|
169
|
-
hash: 53
|
170
|
-
segments:
|
171
|
-
- 0
|
172
|
-
- 9
|
173
|
-
- 7
|
174
|
-
version: 0.9.7
|
175
|
-
type: :development
|
176
|
-
version_requirements: *id010
|
177
|
-
description: " The crowd, suddenly there where there was nothing before, is a mysterious and\n universal phenomenon. A few people may have been standing together -- five, ten\n or twelve, nor more; nothing has been announced, nothing is expected. Suddenly\n everywhere is black with people and more come streaming from all sides as though\n streets had only one direction.\n"
|
178
|
-
email: jeremy@documentcloud.org
|
179
|
-
executables:
|
12
|
+
date: 2014-03-08 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: |2
|
15
|
+
The crowd, suddenly there where there was nothing before, is a mysterious and
|
16
|
+
universal phenomenon. A few people may have been standing together -- five, ten
|
17
|
+
or twelve, nor more; nothing has been announced, nothing is expected. Suddenly
|
18
|
+
everywhere is black with people and more come streaming from all sides as though
|
19
|
+
streets had only one direction.
|
20
|
+
email: opensource@documentcloud.org
|
21
|
+
executables:
|
180
22
|
- crowd
|
181
23
|
extensions: []
|
182
|
-
|
183
|
-
|
24
|
+
extra_rdoc_files:
|
25
|
+
- README
|
26
|
+
files:
|
27
|
+
- EPIGRAPHS
|
28
|
+
- LICENSE
|
184
29
|
- README
|
185
|
-
files:
|
186
30
|
- actions/graphics_magick.rb
|
187
31
|
- actions/process_pdfs.rb
|
188
32
|
- actions/word_count.rb
|
33
|
+
- bin/crowd
|
189
34
|
- cloud-crowd.gemspec
|
190
35
|
- config/config.example.ru
|
191
36
|
- config/config.example.yml
|
192
37
|
- config/database.example.yml
|
193
|
-
- EPIGRAPHS
|
194
38
|
- examples/graphics_magick_example.rb
|
195
39
|
- examples/process_pdfs_example.rb
|
196
40
|
- examples/word_count_example.rb
|
197
41
|
- lib/cloud-crowd.rb
|
198
42
|
- lib/cloud_crowd/action.rb
|
43
|
+
- lib/cloud_crowd/asset_store.rb
|
44
|
+
- lib/cloud_crowd/asset_store/cloudfiles_store.rb
|
199
45
|
- lib/cloud_crowd/asset_store/filesystem_store.rb
|
200
46
|
- lib/cloud_crowd/asset_store/s3_store.rb
|
201
|
-
- lib/cloud_crowd/asset_store/cloudfiles_store.rb
|
202
|
-
- lib/cloud_crowd/asset_store.rb
|
203
47
|
- lib/cloud_crowd/command_line.rb
|
204
48
|
- lib/cloud_crowd/exceptions.rb
|
49
|
+
- lib/cloud_crowd/helpers.rb
|
205
50
|
- lib/cloud_crowd/helpers/authorization.rb
|
206
51
|
- lib/cloud_crowd/helpers/resources.rb
|
207
|
-
- lib/cloud_crowd/helpers.rb
|
208
52
|
- lib/cloud_crowd/inflector.rb
|
53
|
+
- lib/cloud_crowd/models.rb
|
209
54
|
- lib/cloud_crowd/models/job.rb
|
210
55
|
- lib/cloud_crowd/models/node_record.rb
|
211
56
|
- lib/cloud_crowd/models/work_unit.rb
|
212
|
-
- lib/cloud_crowd/models.rb
|
213
57
|
- lib/cloud_crowd/node.rb
|
214
58
|
- lib/cloud_crowd/schema.rb
|
215
59
|
- lib/cloud_crowd/server.rb
|
216
60
|
- lib/cloud_crowd/worker.rb
|
217
|
-
- LICENSE
|
218
61
|
- public/css/admin_console.css
|
219
62
|
- public/css/reset.css
|
220
63
|
- public/images/bullet_green.png
|
@@ -234,64 +77,53 @@ files:
|
|
234
77
|
- public/js/excanvas.js
|
235
78
|
- public/js/flot.js
|
236
79
|
- public/js/jquery.js
|
237
|
-
- README
|
238
|
-
- test/acceptance/test_node.rb
|
239
80
|
- test/acceptance/test_failing_work_units.rb
|
81
|
+
- test/acceptance/test_node.rb
|
240
82
|
- test/acceptance/test_server.rb
|
241
83
|
- test/acceptance/test_word_count.rb
|
242
84
|
- test/blueprints.rb
|
85
|
+
- test/config/actions/failure_testing.rb
|
243
86
|
- test/config/config.ru
|
244
87
|
- test/config/config.yml
|
245
88
|
- test/config/database.yml
|
246
|
-
- test/config/actions/failure_testing.rb
|
247
89
|
- test/test_helper.rb
|
248
90
|
- test/unit/test_action.rb
|
249
91
|
- test/unit/test_configuration.rb
|
92
|
+
- test/unit/test_job.rb
|
250
93
|
- test/unit/test_node.rb
|
251
94
|
- test/unit/test_node_record.rb
|
252
|
-
- test/unit/test_job.rb
|
253
|
-
- test/unit/test_worker.rb
|
254
95
|
- test/unit/test_work_unit.rb
|
96
|
+
- test/unit/test_worker.rb
|
255
97
|
- views/operations_center.erb
|
256
|
-
- bin/crowd
|
257
98
|
homepage: http://wiki.github.com/documentcloud/cloud-crowd
|
258
|
-
licenses:
|
259
|
-
|
99
|
+
licenses:
|
100
|
+
- MIT
|
101
|
+
metadata: {}
|
260
102
|
post_install_message:
|
261
|
-
rdoc_options:
|
262
|
-
- --title
|
103
|
+
rdoc_options:
|
104
|
+
- "--title"
|
263
105
|
- CloudCrowd | Parallel Processing for the Rest of Us
|
264
|
-
- --exclude
|
106
|
+
- "--exclude"
|
265
107
|
- test
|
266
|
-
- --main
|
108
|
+
- "--main"
|
267
109
|
- README
|
268
|
-
- --all
|
269
|
-
require_paths:
|
110
|
+
- "--all"
|
111
|
+
require_paths:
|
270
112
|
- lib
|
271
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
272
|
-
|
273
|
-
requirements:
|
274
|
-
- - ">="
|
275
|
-
- !ruby/object:Gem::Version
|
276
|
-
hash: 3
|
277
|
-
segments:
|
278
|
-
- 0
|
279
|
-
version: "0"
|
280
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
281
|
-
none: false
|
282
|
-
requirements:
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
283
115
|
- - ">="
|
284
|
-
- !ruby/object:Gem::Version
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - ">"
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: 1.3.1
|
289
123
|
requirements: []
|
290
|
-
|
291
124
|
rubyforge_project: cloud-crowd
|
292
|
-
rubygems_version:
|
125
|
+
rubygems_version: 2.2.1
|
293
126
|
signing_key:
|
294
|
-
specification_version:
|
127
|
+
specification_version: 4
|
295
128
|
summary: Parallel Processing for the Rest of Us
|
296
129
|
test_files: []
|
297
|
-
|