cloud-crowd 0.6.2 → 0.7.0.pre
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.
- 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
|
-
|