documentcloud-cloud-crowd 0.0.1 → 0.0.2

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.
@@ -11,8 +11,10 @@ class GraphicsMagick < CloudCrowd::Action
11
11
 
12
12
  # Download the initial image, and run each of the specified GraphicsMagick
13
13
  # commands against it, returning the aggregate output.
14
- def run
15
- return options['steps'].map {|step| run_step(step) }
14
+ def process
15
+ result = {}
16
+ options['steps'].each {|step| result[step['name']] = run_step(step) }
17
+ result.to_json
16
18
  end
17
19
 
18
20
  # Run an individual step (single GraphicsMagick command) in a shell-injection
@@ -20,11 +22,10 @@ class GraphicsMagick < CloudCrowd::Action
20
22
  # URL as the result.
21
23
  # TODO: +system+ wasn't working, figure out some other way to escape.
22
24
  def run_step(step)
23
- name, cmd, opts, ext = step['name'], step['command'], step['options'], step['extension']
25
+ cmd, opts = step['command'], step['options']
24
26
  in_path, out_path = input_path_for(step), output_path_for(step)
25
27
  `gm #{cmd} #{opts} #{in_path} #{out_path}`
26
- public_url = save(out_path)
27
- {'name' => name, 'url' => public_url}.to_json
28
+ save(out_path)
28
29
  end
29
30
 
30
31
  # Where should the starting image be located?
data/cloud-crowd.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'cloud-crowd'
3
- s.version = '0.0.1'
3
+ s.version = '0.0.2' # Keep version in sync with cloud-cloud.rb
4
4
  s.date = '2009-08-23'
5
5
 
6
6
  s.homepage = "http://documentcloud.org" # wiki page on github?
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.require_paths = ['lib']
20
20
  s.executables = ['crowd']
21
21
 
22
- s.post_install_message = "Run `crowd help` for information on using CloudCrowd."
22
+ # s.post_install_message = "Run `crowd --help` for information on using CloudCrowd."
23
23
  s.rubyforge_project = 'cloud-crowd'
24
24
  s.has_rdoc = true
25
25
 
@@ -51,6 +51,7 @@ lib/cloud_crowd/asset_store.rb
51
51
  lib/cloud_crowd/command_line.rb
52
52
  lib/cloud_crowd/core_ext.rb
53
53
  lib/cloud_crowd/daemon.rb
54
+ lib/cloud_crowd/helpers/authorization.rb
54
55
  lib/cloud_crowd/helpers/resources.rb
55
56
  lib/cloud_crowd/helpers/urls.rb
56
57
  lib/cloud_crowd/helpers.rb
@@ -62,8 +63,9 @@ lib/cloud_crowd/schema.rb
62
63
  lib/cloud_crowd/worker.rb
63
64
  test/acceptance/test_failing_work_units.rb
64
65
  test/blueprints.rb
65
- test/config/test_config.yml
66
- test/config/test_database.yml
66
+ test/config/config.yml
67
+ test/config/database.yml
68
+ test/config/actions/failure_testing.rb
67
69
  test/test_helper.rb
68
70
  test/unit/test_job.rb
69
71
  test/unit/test_work_unit.rb
@@ -8,6 +8,7 @@
8
8
 
9
9
  require 'rubygems'
10
10
  require 'cloud-crowd'
11
+ require 'cloud_crowd/app'
11
12
 
12
13
  CloudCrowd.configure(File.dirname(__FILE__) + '/config.yml')
13
14
  CloudCrowd.configure_database(File.dirname(__FILE__) + '/database.yml')
@@ -6,6 +6,11 @@
6
6
  :work_unit_retries: 3
7
7
 
8
8
  :central_server: http://localhost:9173
9
+ :use_http_authentication: no
10
+ :login: [your login name]
11
+ :password: [your password]
12
+
13
+ :use_s3_authentication: no
9
14
  :s3_bucket: [your CloudCrowd bucket]
10
15
  :aws_access_key: [your AWS access key]
11
- :aws_secret_key: [your AWS secret access key]
16
+ :aws_secret_key: [your AWS secret access key]
data/lib/cloud-crowd.rb CHANGED
@@ -1,25 +1,21 @@
1
1
  $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))
2
2
 
3
- # Standard Library:
4
- require 'tmpdir'
5
- require 'erb'
6
-
7
- # Gems:
8
- require 'sinatra'
9
- require 'activerecord'
3
+ # Common Gems:
10
4
  require 'json'
11
- require 'daemons'
12
5
  require 'rest_client'
13
6
  require 'right_aws'
14
7
 
8
+ # Common CloudCrowd libs:
9
+ require 'cloud_crowd/core_ext'
10
+ require 'cloud_crowd/action'
11
+
15
12
  module CloudCrowd
16
13
 
17
- class App < Sinatra::Default
18
- set :root, File.expand_path(File.dirname(__FILE__) + '/..')
19
- end
14
+ # Root directory of the CloudCrowd gem.
15
+ ROOT = File.expand_path(File.dirname(__FILE__) + '/..')
20
16
 
21
17
  # Keep the version in sync with the gemspec.
22
- VERSION = '0.0.1'
18
+ VERSION = '0.0.2'
23
19
 
24
20
  # A Job is processing if its WorkUnits in the queue to be handled by workers.
25
21
  PROCESSING = 1
@@ -56,6 +52,7 @@ module CloudCrowd
56
52
 
57
53
  # Configure CloudCrowd by passing in the path to +config.yml+.
58
54
  def configure(config_path)
55
+ @config_path = File.expand_path(File.dirname(config_path))
59
56
  @config = YAML.load_file(config_path)
60
57
  end
61
58
 
@@ -77,20 +74,16 @@ module CloudCrowd
77
74
  def actions(name)
78
75
  action_class = name.camelize
79
76
  begin
77
+ raise NameError, "can't find the #{action_class} Action" unless Module.constants.include?(action_class)
80
78
  Module.const_get(action_class)
81
79
  rescue NameError => e
82
- require "#{CloudCrowd::App.root}/actions/#{name}"
83
- retry
80
+ user_action = "#{@config_path}/actions/#{name}"
81
+ default_action = "#{CloudCrowd::ROOT}/actions/#{name}"
82
+ require user_action and retry if File.exists? "#{user_action}.rb"
83
+ require default_action and retry if File.exists? "#{default_action}.rb"
84
+ raise e
84
85
  end
85
86
  end
86
87
  end
87
88
 
88
- end
89
-
90
- # CloudCrowd:
91
- require 'cloud_crowd/core_ext'
92
- require 'cloud_crowd/models'
93
- require 'cloud_crowd/asset_store'
94
- require 'cloud_crowd/action'
95
- require 'cloud_crowd/helpers'
96
- require 'cloud_crowd/app'
89
+ end
@@ -34,7 +34,7 @@ module CloudCrowd
34
34
 
35
35
  # Each CloudCrowd::Action must implement a +process+ method.
36
36
  def process
37
- raise NotImplementedError.new("CloudCrowd::Actions must override 'run' with their own processing code.")
37
+ raise NotImplementedError.new("CloudCrowd::Actions must override 'process' with their own processing code.")
38
38
  end
39
39
 
40
40
  # Download a file to the specified path using curl.
@@ -1,3 +1,8 @@
1
+ require 'erb'
2
+ require 'sinatra'
3
+ require 'cloud_crowd/models'
4
+ require 'cloud_crowd/helpers'
5
+
1
6
  module CloudCrowd
2
7
 
3
8
  class App < Sinatra::Default
@@ -5,8 +10,15 @@ module CloudCrowd
5
10
  # static serves files from /public, methodoverride allows the _method param.
6
11
  enable :static, :methodoverride
7
12
 
13
+ set :root, CloudCrowd::ROOT
14
+ set :authorization_realm, "CloudCrowd"
15
+
8
16
  helpers CloudCrowd::Helpers
9
17
 
18
+ before do
19
+ login_required if CloudCrowd.config[:use_http_authentication]
20
+ end
21
+
10
22
  # Start a new job. Accepts a JSON representation of the job-to-be.
11
23
  post '/jobs' do
12
24
  Job.create_from_request(JSON.parse(params[:json])).to_json
@@ -49,6 +61,12 @@ module CloudCrowd
49
61
  return status(204) && ''
50
62
  end
51
63
 
64
+ # To monitor the central server with Monit, God, Nagios, or another
65
+ # monitoring tool, you can hit /heartbeat to check.
66
+ get '/heartbeat' do
67
+ "buh-bump"
68
+ end
69
+
52
70
  end
53
71
 
54
72
  end
@@ -1,3 +1,5 @@
1
+ require 'tmpdir'
2
+
1
3
  module CloudCrowd
2
4
 
3
5
  # The CloudCrowd::AssetStore should provide a common API for stashing and retrieving
@@ -7,6 +9,7 @@ module CloudCrowd
7
9
  include FileUtils
8
10
 
9
11
  def initialize
12
+ @use_auth = CloudCrowd.config[:use_s3_authentication]
10
13
  mkdir_p temp_storage_path unless File.exists? temp_storage_path
11
14
  end
12
15
 
@@ -15,10 +18,12 @@ module CloudCrowd
15
18
  "#{Dir.tmpdir}/cloud_crowd_tmp"
16
19
  end
17
20
 
18
- # Copy a finished file from our local storage to S3.
21
+ # Copy a finished file from our local storage to S3. Save it publicly if
22
+ # we're not configured to use S3 authentication.
19
23
  def save(local_path, save_path)
20
24
  ensure_s3_connection
21
- @bucket.put(save_path, File.open(local_path), {}, 'public-read')
25
+ permission = @use_auth ? 'private' : 'public-read'
26
+ @bucket.put(save_path, File.open(local_path), {}, permission)
22
27
  end
23
28
 
24
29
  # Cleanup all S3 files for a job that's been completed and retrieved.
@@ -27,9 +32,11 @@ module CloudCrowd
27
32
  @bucket.delete_folder("#{job.action}/job_#{job.id}")
28
33
  end
29
34
 
30
- # Return the S3 public URL for a finshed file.
35
+ # Return the S3 public URL for a finshed file. Authenticated links expire
36
+ # after one day by default.
31
37
  def url(save_path)
32
- @bucket.key(save_path).public_link
38
+ @use_auth ? @s3.interface.get_link(@bucket, save_path) :
39
+ @bucket.key(save_path).public_link
33
40
  end
34
41
 
35
42
  private
@@ -6,8 +6,11 @@ module CloudCrowd
6
6
  # Configuration files required for the `crowd` command to function.
7
7
  CONFIG_FILES = ['config.yml', 'config.ru', 'database.yml']
8
8
 
9
+ # Reference the absolute path to the root, because we're about to chdir.
10
+ CC_ROOT = File.expand_path(File.dirname(__FILE__) + '/../..')
11
+
9
12
  # Path to the Daemons gem script which launches workers.
10
- WORKER_RUNNER = File.expand_path("#{File.dirname(__FILE__)}/runner.rb")
13
+ WORKER_RUNNER = File.expand_path("#{CC_ROOT}/lib/cloud_crowd/runner.rb")
11
14
 
12
15
  # Command-line banner for the usage message.
13
16
  BANNER = <<-EOS
@@ -75,12 +78,11 @@ OPTIONS:
75
78
  def run_install
76
79
  require 'fileutils'
77
80
  install_path = ARGV.shift || '.'
78
- cc_root = File.dirname(__FILE__) + '/../..'
79
81
  FileUtils.mkdir_p install_path unless File.exists?(install_path)
80
- install_file "#{cc_root}/config/config.example.yml", "#{install_path}/config.yml"
81
- install_file "#{cc_root}/config/config.example.ru", "#{install_path}/config.ru"
82
- install_file "#{cc_root}/config/database.example.yml", "#{install_path}/database.yml"
83
- install_file "#{cc_root}/actions", "#{install_path}/actions", true
82
+ install_file "#{CC_ROOT}/config/config.example.yml", "#{install_path}/config.yml"
83
+ install_file "#{CC_ROOT}/config/config.example.ru", "#{install_path}/config.ru"
84
+ install_file "#{CC_ROOT}/config/database.example.yml", "#{install_path}/database.yml"
85
+ install_file "#{CC_ROOT}/actions", "#{install_path}/actions", true
84
86
  end
85
87
 
86
88
  # Manipulate worker daemons -- handles all commands that the Daemons gem
@@ -135,8 +137,7 @@ OPTIONS:
135
137
  # the CLOUD_CROWD_CONFIG environment variable. Exit if they're not found.
136
138
  def ensure_config
137
139
  return if @config_found
138
- config_dir = ENV['CLOUD_CROWD_CONFIG'] || '.'
139
- Dir.chdir config_dir
140
+ Dir.chdir @options[:config_path]
140
141
  CONFIG_FILES.all? {|f| File.exists? f } ? @config_dir = true : config_not_found
141
142
  end
142
143
 
@@ -144,16 +145,20 @@ OPTIONS:
144
145
  # TODO: Think about parsing options per sub-command separately.
145
146
  def parse_options
146
147
  @options = {
147
- :db_config => 'database.yml',
148
- :port => 9173,
148
+ :db_config => 'database.yml',
149
+ :port => 9173,
150
+ :config_path => ENV['CLOUD_CROWD_CONFIG'] || '.',
149
151
  }
150
152
  @option_parser = OptionParser.new do |opts|
151
- opts.on('-n', '--num-workers NUM', OptionParser::DecimalInteger, 'number of worker processes') do |num|
152
- @options[:num_workers] = num
153
+ opts.on('-c', '--config PATH', 'path to configuration directory') do |conf_path|
154
+ @options[:config_path] = conf_path
153
155
  end
154
156
  opts.on('-d', '--database-config PATH', 'path to database.yml') do |conf_path|
155
157
  @options[:db_config] = conf_path
156
158
  end
159
+ opts.on('-n', '--num-workers NUM', OptionParser::DecimalInteger, 'number of worker processes') do |num|
160
+ @options[:num_workers] = num
161
+ end
157
162
  opts.on('-p', '--port PORT', 'central server port number') do |port_num|
158
163
  @options[:port] = port_num
159
164
  end
@@ -172,19 +177,20 @@ OPTIONS:
172
177
  def load_code
173
178
  ensure_config
174
179
  require 'rubygems'
175
- require File.dirname(__FILE__) + '/../cloud-crowd'
180
+ require "#{CC_ROOT}/lib/cloud-crowd"
176
181
  CloudCrowd.configure('config.yml')
177
182
  end
178
183
 
179
184
  # Establish a connection to the central server's database. Not all commands
180
185
  # require this.
181
186
  def connect_to_database
187
+ require 'cloud_crowd/models'
182
188
  CloudCrowd.configure_database(@options[:db_config])
183
189
  end
184
190
 
185
191
  # Exit with an explanation if the configuration files couldn't be found.
186
192
  def config_not_found
187
- puts "`crowd` can't find the CloudCrowd configuration directory. Please either run `crowd` from inside of the configuration directory, or add a CLOUD_CROWD_CONFIG variable to your environment."
193
+ puts "`crowd` can't find the CloudCrowd configuration directory. Please either run `crowd` from inside of the configuration directory, or use `crowd -c path/to/config`"
188
194
  exit(1)
189
195
  end
190
196
 
@@ -0,0 +1,46 @@
1
+ # After sinatra-authorization...
2
+
3
+ module CloudCrowd
4
+ module Helpers
5
+ module Authorization
6
+
7
+ def login_required
8
+ return if authorized?
9
+ unauthorized! unless auth.provided?
10
+ bad_request! unless auth.basic?
11
+ unauthorized! unless authorize(*auth.credentials)
12
+ request.env['REMOTE_USER'] = auth.username
13
+ end
14
+
15
+ def authorized?
16
+ !!request.env['REMOTE_USER']
17
+ end
18
+
19
+ def current_user
20
+ request.env['REMOTE_USER']
21
+ end
22
+
23
+ def authorize(login, password)
24
+ return true unless CloudCrowd.config[:use_http_authentication]
25
+ return CloudCrowd.config[:login] == login &&
26
+ CloudCrowd.config[:password] == password
27
+ end
28
+
29
+
30
+ private
31
+
32
+ def auth
33
+ @auth ||= Rack::Auth::Basic::Request.new(request.env)
34
+ end
35
+
36
+ def unauthorized!(realm = CloudCrowd::App.authorization_realm)
37
+ response['WWW-Authenticate'] = "Basic realm=\"#{realm}\""
38
+ halt 401, 'Authorization Required'
39
+ end
40
+
41
+ def bad_request!
42
+ halt 400, 'Bad Request'
43
+ end
44
+ end
45
+ end
46
+ end
@@ -1,8 +1,9 @@
1
+ require 'cloud_crowd/helpers/authorization'
1
2
  require 'cloud_crowd/helpers/resources'
2
3
  require 'cloud_crowd/helpers/urls'
3
4
 
4
5
  module CloudCrowd
5
6
  module Helpers
6
- include Resources, Urls #, Rack::Utils
7
+ include Authorization, Resources, Urls #, Rack::Utils
7
8
  end
8
9
  end
@@ -113,7 +113,7 @@ class Job < ActiveRecord::Base
113
113
  # WorkUnits, as well as any completed outputs.
114
114
  def to_json(opts={})
115
115
  atts = {'id' => self.id, 'status' => self.display_status, 'work_units_remaining' => self.work_units_remaining}
116
- atts.merge!({'output' => JSON.parse(self.outputs)}) if self.outputs
116
+ atts.merge!({'outputs' => JSON.parse(self.outputs)}) if self.outputs
117
117
  atts.merge!({'time' => self.time}) if self.time
118
118
  atts.to_json
119
119
  end
@@ -1,3 +1,5 @@
1
+ require 'activerecord'
2
+
1
3
  module CloudCrowd
2
4
  module ModelStatus
3
5
 
@@ -2,24 +2,23 @@
2
2
  # daemons don't load the entire rails stack, this file functions like a mini
3
3
  # environment.rb, loading all the common gems that we need.
4
4
 
5
- # CloudCrowd::App.root = File.expand_path(File.dirname(__FILE__) + '/../..') unless defined?(CloudCrowd::App.root)
6
-
7
- # Standard Lib and Gems
5
+ # Standard Libs
8
6
  require 'fileutils'
7
+ require 'benchmark'
8
+ require 'socket'
9
+
10
+ # Gems
9
11
  require 'rubygems'
10
12
  require 'daemons'
11
- require 'socket'
12
13
  require 'yaml'
13
- require 'json'
14
- require 'rest_client'
15
- require 'right_aws'
16
14
 
17
15
  FileUtils.mkdir('log') unless File.exists?('log')
18
16
 
19
17
  # Daemon/Worker Dependencies.
20
18
  require "#{File.dirname(__FILE__)}/../cloud-crowd"
19
+ require 'cloud_crowd/asset_store'
21
20
 
22
- Daemons.run("#{CloudCrowd::App.root}/lib/cloud_crowd/daemon.rb", {
21
+ Daemons.run("#{CloudCrowd::ROOT}/lib/cloud_crowd/daemon.rb", {
23
22
  :app_name => "cloud_crowd_worker",
24
23
  :dir_mode => :normal,
25
24
  :dir => 'log',
@@ -11,15 +11,17 @@ module CloudCrowd
11
11
  # connection to S3. This AssetStore gets passed into each action, for use
12
12
  # as it is run.
13
13
  def initialize
14
- @id = $$
14
+ @id = $$
15
15
  @hostname = Socket.gethostname
16
- @store = CloudCrowd::AssetStore.new
16
+ @store = CloudCrowd::AssetStore.new
17
+ @server = central_server_resource
18
+ log 'started'
17
19
  end
18
20
 
19
21
  # Ask the central server for a new WorkUnit.
20
22
  def fetch_work_unit
21
23
  keep_trying_to "fetch a new work unit" do
22
- unit_json = RestClient.get("#{CENTRAL_URL}/work")
24
+ unit_json = @server['/work'].get
23
25
  return unless unit_json # No content means no work for us.
24
26
  @start_time = Time.now
25
27
  parse_work_unit unit_json
@@ -31,7 +33,7 @@ module CloudCrowd
31
33
  def complete_work_unit(result)
32
34
  keep_trying_to "complete work unit" do
33
35
  data = completion_params.merge({:status => 'succeeded', :output => result})
34
- RestClient.put("#{CENTRAL_URL}/work/#{data[:id]}", data)
36
+ @server["/work/#{data[:id]}"].put(data)
35
37
  log "finished #{@action_name} in #{data[:time]} seconds"
36
38
  end
37
39
  end
@@ -40,7 +42,7 @@ module CloudCrowd
40
42
  def fail_work_unit(exception)
41
43
  keep_trying_to "mark work unit as failed" do
42
44
  data = completion_params.merge({:status => 'failed', :output => exception.message})
43
- RestClient.put("#{CENTRAL_URL}/work/#{data[:id]}", data)
45
+ @server["/work/#{data[:id]}"].put(data)
44
46
  log "failed #{@action_name} in #{data[:time]} seconds\n#{exception.message}\n#{exception.backtrace}"
45
47
  end
46
48
  end
@@ -63,7 +65,7 @@ module CloudCrowd
63
65
  end
64
66
 
65
67
  # Executes the current work unit, catching all exceptions as failures.
66
- def run
68
+ def run_work_unit
67
69
  begin
68
70
  @action = CloudCrowd.actions(@action_name).new
69
71
  @action.configure(@status, @input, @options, @store)
@@ -81,9 +83,24 @@ module CloudCrowd
81
83
  end
82
84
  end
83
85
 
86
+ # Wraps +run_work_unit+ to benchmark the execution time, if requested.
87
+ def run
88
+ return run_work_unit unless @options['benchmark']
89
+ status = CloudCrowd.display_status(@status)
90
+ log("ran #{@action_name}/#{status} in " + Benchmark.measure { run_work_unit }.to_s)
91
+ end
92
+
84
93
 
85
94
  private
86
95
 
96
+ # Keep an authenticated (if configured to enable authentication) resource
97
+ # for the central server.
98
+ def central_server_resource
99
+ params = [CENTRAL_URL]
100
+ params += [CloudCrowd.config[:login], CloudCrowd.config[:password]] if CloudCrowd.config[:use_http_authentication]
101
+ RestClient::Resource.new(*params)
102
+ end
103
+
87
104
  # Common parameters to send back to central, regardless of success or failure.
88
105
  def completion_params
89
106
  {:id => @options['work_unit_id'], :time => Time.now - @start_time}
@@ -0,0 +1,13 @@
1
+ # Simple Action that fails the work unit until it is just about to exhaust
2
+ # all of its retries.
3
+ class FailureTesting < CloudCrowd::Action
4
+
5
+ def run
6
+ if options['attempts'] + 1 >= CloudCrowd.config[:work_unit_retries]
7
+ return 'made it!'
8
+ else
9
+ raise 'hell'
10
+ end
11
+ end
12
+
13
+ end
File without changes
File without changes
data/test/test_helper.rb CHANGED
@@ -2,8 +2,10 @@ require 'rubygems'
2
2
 
3
3
  here = File.dirname(__FILE__)
4
4
  require File.expand_path(here + "/../lib/cloud-crowd")
5
- CloudCrowd.configure(here + '/config/test_config.yml')
6
- CloudCrowd.configure_database(here + '/config/test_database.yml')
5
+ require 'cloud_crowd/app'
6
+
7
+ CloudCrowd.configure(here + '/config/config.yml')
8
+ CloudCrowd.configure_database(here + '/config/database.yml')
7
9
 
8
10
  require 'faker'
9
11
  require 'sham'
@@ -11,7 +13,7 @@ require 'rack/test'
11
13
  require 'shoulda/active_record'
12
14
  require 'machinist/active_record'
13
15
  require 'mocha'
14
- require "#{CloudCrowd::App.root}/test/blueprints.rb"
16
+ require "#{CloudCrowd::ROOT}/test/blueprints.rb"
15
17
 
16
18
  class Test::Unit::TestCase
17
19
  include CloudCrowd
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: documentcloud-cloud-crowd
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Ashkenas
@@ -143,6 +143,7 @@ files:
143
143
  - lib/cloud_crowd/command_line.rb
144
144
  - lib/cloud_crowd/core_ext.rb
145
145
  - lib/cloud_crowd/daemon.rb
146
+ - lib/cloud_crowd/helpers/authorization.rb
146
147
  - lib/cloud_crowd/helpers/resources.rb
147
148
  - lib/cloud_crowd/helpers/urls.rb
148
149
  - lib/cloud_crowd/helpers.rb
@@ -154,15 +155,15 @@ files:
154
155
  - lib/cloud_crowd/worker.rb
155
156
  - test/acceptance/test_failing_work_units.rb
156
157
  - test/blueprints.rb
157
- - test/config/test_config.yml
158
- - test/config/test_database.yml
158
+ - test/config/config.yml
159
+ - test/config/database.yml
160
+ - test/config/actions/failure_testing.rb
159
161
  - test/test_helper.rb
160
162
  - test/unit/test_job.rb
161
163
  - test/unit/test_work_unit.rb
162
164
  has_rdoc: true
163
165
  homepage: http://documentcloud.org
164
- licenses:
165
- post_install_message: Run `crowd help` for information on using CloudCrowd.
166
+ post_install_message:
166
167
  rdoc_options: []
167
168
 
168
169
  require_paths:
@@ -182,7 +183,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
182
183
  requirements: []
183
184
 
184
185
  rubyforge_project: cloud-crowd
185
- rubygems_version: 1.3.5
186
+ rubygems_version: 1.2.0
186
187
  signing_key:
187
188
  specification_version: 2
188
189
  summary: Better living through Map --> Ruby --> Reduce