Permalink
Browse files

embryonic you-eye

  • Loading branch information...
1 parent 66999f8 commit 10880e640c651c0f5b421103a6e7131db246756a @jashkenas jashkenas committed Aug 30, 2009
View
@@ -1,6 +1,6 @@
.autotest
log
-hidden
+raws
tmp
db/*.sqlite3
raw
View
@@ -50,6 +50,7 @@ lib/cloud_crowd/app.rb
lib/cloud_crowd/asset_store.rb
lib/cloud_crowd/command_line.rb
lib/cloud_crowd/daemon.rb
+lib/cloud_crowd/exceptions.rb
lib/cloud_crowd/helpers/authorization.rb
lib/cloud_crowd/helpers/resources.rb
lib/cloud_crowd/helpers.rb
@@ -60,6 +61,12 @@ lib/cloud_crowd/models.rb
lib/cloud_crowd/runner.rb
lib/cloud_crowd/schema.rb
lib/cloud_crowd/worker.rb
+public/css/admin_console.css
+public/css/reset.css
+public/images/queue_fill.png
+public/js/admin_console.js
+public/js/jquery-1.3.2.js
+views/index.erb
test/acceptance/test_failing_work_units.rb
test/blueprints.rb
test/config/config.yml
View
@@ -15,28 +15,46 @@ class App < Sinatra::Default
login_required if CloudCrowd.config[:use_http_authentication]
end
+ # Render the admin console.
+ get '/' do
+ erb :index
+ end
+
+ # Get the JSON for every active job in the queue.
+ get '/jobs' do
+ json Job.all
+ end
+
+ # To monitor the central server with Monit, God, Nagios, or another
+ # monitoring tool, you can hit /heartbeat to make sure.
+ get '/heartbeat' do
+ "buh-bump"
+ end
+
+ # PUBLIC API:
+
# Start a new job. Accepts a JSON representation of the job-to-be.
post '/jobs' do
- Job.create_from_request(JSON.parse(params[:json])).to_json
+ json Job.create_from_request(JSON.parse(params[:json]))
end
# Check the status of a job, returning the output if finished, and the
# number of work units remaining otherwise.
get '/jobs/:job_id' do
- current_job.to_json
+ json current_job
end
# Cleans up a Job's saved S3 files. Delete a Job after you're done
# downloading the results.
delete '/jobs/:job_id' do
current_job.cleanup
- ''
+ json nil
end
# Internal method for worker daemons to fetch the work unit at the front
# of the queue. Work unit is marked as taken and handed off to the worker.
post '/work' do
- dequeue_work_unit
+ json dequeue_work_unit
end
# When workers are done with their unit, either successfully on in failure,
@@ -47,22 +65,16 @@ class App < Sinatra::Default
case params[:status]
when 'succeeded'
current_work_unit.finish(params[:output], params[:time])
- dequeue_work_unit
+ json dequeue_work_unit
when 'failed'
current_work_unit.fail(params[:output], params[:time])
- dequeue_work_unit(1)
+ json dequeue_work_unit(1)
else
error(500, "Completing a work unit must specify status.")
end
end
end
- # To monitor the central server with Monit, God, Nagios, or another
- # monitoring tool, you can hit /heartbeat to make sure.
- get '/heartbeat' do
- "buh-bump"
- end
-
end
end
@@ -144,7 +144,6 @@ def ensure_config
end
# Parse all options for all actions.
- # TODO: Think about parsing options per sub-command separately.
def parse_options
@options = {
:port => 9173,
@@ -0,0 +1,7 @@
+module CloudCrowd
+
+ class Error < RuntimeError; end;
+ class ActionNotFound < Error; end;
+ class StatusUnspecified < Error; end;
+
+end
@@ -2,6 +2,13 @@ module CloudCrowd
module Helpers
module Resources
+ # Convenience for responding with JSON.
+ def json(obj)
+ content_type :json
+ return status(204) && '{}' unless obj
+ obj.to_json
+ end
+
def current_job
@job ||= Job.find_by_id(params[:job_id]) or raise Sinatra::NotFound
end
@@ -15,9 +22,7 @@ def current_work_unit
def dequeue_work_unit(offset=0)
handle_conflicts do
actions = params[:enabled_actions].split(',')
- unit = WorkUnit.dequeue(actions, offset)
- return status(204) && '' unless unit
- unit.to_json
+ WorkUnit.dequeue(actions, offset)
end
end
@@ -99,9 +99,11 @@ def mergeable?
self.processing? && self.action_class.public_instance_methods.include?('merge')
end
- # Retrieve the class for this Job's Action, loading it if necessary.
+ # Retrieve the class for this Job's Action.
def action_class
- CloudCrowd.actions[self.action]
+ klass = CloudCrowd.actions[self.action]
+ return klass if klass
+ raise ActionNotFound, "no action named: '#{self.action}' could be found"
end
# When the WorkUnits are all finished, gather all their outputs together
@@ -124,6 +126,12 @@ def percent_complete
return 99 if merging?
(work_units.complete.count / work_units.count.to_f * 100).round
end
+
+ # How long has this Job taken?
+ def time_taken
+ return self.time if self.time
+ Time.now - self.created_at
+ end
# When starting a new job, or moving to a new stage, split up the inputs
# into WorkUnits, and queue them.
@@ -141,9 +149,14 @@ def queue_for_workers(input)
# A JSON representation of this job includes the statuses of its component
# WorkUnits, as well as any completed outputs.
def to_json(opts={})
- atts = {'id' => self.id, 'status' => self.display_status, 'percent_complete' => self.percent_complete}
+ atts = {
+ 'id' => self.id,
+ 'status' => self.display_status,
+ 'percent_complete' => self.percent_complete,
+ 'work_units' => self.work_units.count,
+ 'time_taken' => self.time_taken
+ }
atts.merge!({'outputs' => JSON.parse(self.outputs)}) if self.outputs
- atts.merge!({'time' => self.time}) if self.time
atts.to_json
end
@@ -76,7 +76,7 @@ def run_work_unit
when PROCESSING then @action.process
when SPLITTING then @action.split
when MERGING then @action.merge
- else raise "Work units must specify their status."
+ else raise StatusUnspecified, "work units must specify their status"
end
complete_work_unit({'output' => result}.to_json)
rescue Exception => e
@@ -0,0 +1,35 @@
+body {
+ background: #c7c7c7;
+ font-family: Arial;
+ font-size: 12px;
+}
+
+#queue {
+ position: absolute;
+ top: 100px; left: 100px; right: 100px;
+ height: 77px;
+}
+ #queue_fill {
+ position: absolute;
+ width: 100%; height: 75px;
+ border: 1px solid #5c5c5c;
+ -moz-border-radius: 10px; -webkit-border-radius: 10px;
+ background: transparent url(/images/queue_fill.png) repeat-x 0px -1px;
+ }
+ #queue .job {
+ position: relative;
+ margin-top: 1px;
+ height: 75px;
+ background: blue;
+ float: left;
+ overflow: hidden;
+ -moz-border-radius: 10px;
+ -webkit-border-radius: 10px;
+ }
+ #queue .job_id {
+ color: #c7c7c7;
+ font-size: 14px;
+ position: absolute;
+ bottom: 8px; left: 8px;
+ z-index: 10;
+ }
View
@@ -0,0 +1,52 @@
+/*------------------------------ RESET + DEFAULT STYLES ---------------------------------*/
+
+/*
+Eric Meyer's final reset.css
+Source: http://meyerweb.com/eric/thoughts/2007/05/01/reset-reloaded/
+*/
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, font, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ font-weight: inherit;
+ font-style: inherit;
+ font-size: 100%;
+ font-family: inherit;
+ vertical-align: baseline;
+}
+/* remember to define focus styles! */
+:focus {
+ outline: 0;
+}
+body {
+ line-height: 1;
+ color: black;
+ background: white;
+}
+ol, ul {
+ list-style: none;
+}
+/* tables still need 'cellspacing="0"' in the markup */
+table {
+ border-collapse: separate;
+ border-spacing: 0;
+}
+caption, th, td {
+ text-align: left;
+ font-weight: normal;
+}
+blockquote:before, blockquote:after,
+q:before, q:after {
+ content: "";
+}
+blockquote, q {
+ quotes: "" "";
+}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
@@ -0,0 +1,34 @@
+window.Console = {
+
+ initialize : function() {
+ this._queue = $('#jobs');
+ this.getJobs();
+ },
+
+ getJobs : function() {
+ $.get('/jobs', null, function(resp) {
+ Console._jobs = resp;
+ Console.renderJobs();
+ }, 'json');
+ },
+
+ renderJobs : function() {
+ var queue = this._queue;
+ var totalUnits = 0;
+ var totalWidth = queue.width();
+ $.each(this._jobs, function() {
+ totalUnits += this.work_units;
+ });
+ $.each(this._jobs, function() {
+ this.width = (this.work_units / totalUnits) * 100;
+ this.color = [Math.round(Math.random() * 255), Math.round(Math.random() * 255), Math.round(Math.random() * 255)];
+ });
+ queue.html('');
+ $.each(this._jobs.reverse(), function() {
+ queue.append('<div class="job" style="width:' + this.width + '%; background:rgb(' + this.color.join(',') + ');"><div class="job_id">#' + this.id + '</div></div>');
+ });
+ }
+
+};
+
+$(document).ready(function() { Console.initialize(); });
Oops, something went wrong.

0 comments on commit 10880e6

Please sign in to comment.