khronos 0.0.3.pre4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -12,7 +12,7 @@ group :test do
12
12
  gem "delorean", "~> 2.0.0"
13
13
  gem "factory_girl", "~> 4.0.0"
14
14
  gem "webmock", "~> 1.8.9"
15
- #gem "em-spec", ''
15
+ gem "database_cleaner"
16
16
 
17
17
  #
18
18
  # Used for time functions
data/Gemfile.lock ADDED
@@ -0,0 +1,129 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ khronos (0.1.0)
5
+ activerecord (~> 3.2.8)
6
+ activesupport (~> 3.2.8)
7
+ bson_ext (~> 1.6.4)
8
+ em-http-request (~> 1.0.3)
9
+ eventmachine (~> 1.0.0.beta.4)
10
+ girl_friday (~> 0.10.0)
11
+ json (~> 1.7.5)
12
+ mongoid (~> 3.0.5)
13
+ rest-client (~> 1.6.7)
14
+ sinatra (~> 1.3.3)
15
+
16
+ GEM
17
+ remote: http://rubygems.org/
18
+ specs:
19
+ activemodel (3.2.8)
20
+ activesupport (= 3.2.8)
21
+ builder (~> 3.0.0)
22
+ activerecord (3.2.8)
23
+ activemodel (= 3.2.8)
24
+ activesupport (= 3.2.8)
25
+ arel (~> 3.0.2)
26
+ tzinfo (~> 0.3.29)
27
+ activesupport (3.2.8)
28
+ i18n (~> 0.6)
29
+ multi_json (~> 1.0)
30
+ addressable (2.3.2)
31
+ arel (3.0.2)
32
+ bson (1.6.4)
33
+ bson_ext (1.6.4)
34
+ bson (~> 1.6.4)
35
+ builder (3.0.3)
36
+ chronic (0.7.0)
37
+ connection_pool (0.9.2)
38
+ cookiejar (0.3.0)
39
+ crack (0.3.1)
40
+ daemons (1.1.8)
41
+ database_cleaner (0.8.0)
42
+ delorean (2.0.0)
43
+ chronic
44
+ diff-lcs (1.1.3)
45
+ em-http-request (1.0.3)
46
+ addressable (>= 2.2.3)
47
+ cookiejar
48
+ em-socksify
49
+ eventmachine (>= 1.0.0.beta.4)
50
+ http_parser.rb (>= 0.5.3)
51
+ em-socksify (0.2.1)
52
+ eventmachine (>= 1.0.0.beta.4)
53
+ eventmachine (1.0.0.rc.4)
54
+ factory_girl (4.0.0)
55
+ activesupport (>= 3.0.0)
56
+ fakefs (0.4.0)
57
+ girl_friday (0.10.0)
58
+ connection_pool (~> 0.9.0)
59
+ http_parser.rb (0.5.3)
60
+ i18n (0.6.1)
61
+ json (1.7.5)
62
+ mime-types (1.19)
63
+ mongo (1.6.4)
64
+ bson (~> 1.6.4)
65
+ mongoid (3.0.5)
66
+ activemodel (~> 3.1)
67
+ moped (~> 1.1)
68
+ origin (~> 1.0)
69
+ tzinfo (~> 0.3.22)
70
+ moped (1.2.1)
71
+ multi_json (1.3.6)
72
+ mysql2 (0.3.11)
73
+ netrc (0.7.7)
74
+ origin (1.0.8)
75
+ pg (0.14.1)
76
+ rack (1.4.1)
77
+ rack-protection (1.2.0)
78
+ rack
79
+ rack-test (0.6.1)
80
+ rack (>= 1.0)
81
+ rest-client (1.6.8)
82
+ mime-types (>= 1.16)
83
+ netrc
84
+ rspec (2.11.0)
85
+ rspec-core (~> 2.11.0)
86
+ rspec-expectations (~> 2.11.0)
87
+ rspec-mocks (~> 2.11.0)
88
+ rspec-core (2.11.1)
89
+ rspec-expectations (2.11.2)
90
+ diff-lcs (~> 1.1.3)
91
+ rspec-mocks (2.11.2)
92
+ simplecov (0.6.4)
93
+ multi_json (~> 1.0)
94
+ simplecov-html (~> 0.5.3)
95
+ simplecov-html (0.5.3)
96
+ sinatra (1.3.3)
97
+ rack (~> 1.3, >= 1.3.6)
98
+ rack-protection (~> 1.2)
99
+ tilt (~> 1.3, >= 1.3.3)
100
+ sqlite3 (1.3.6)
101
+ thin (1.4.1)
102
+ daemons (>= 1.0.9)
103
+ eventmachine (>= 0.12.6)
104
+ rack (>= 1.0.0)
105
+ tilt (1.3.3)
106
+ tzinfo (0.3.33)
107
+ webmock (1.8.9)
108
+ addressable (>= 2.2.7)
109
+ crack (>= 0.1.7)
110
+
111
+ PLATFORMS
112
+ ruby
113
+
114
+ DEPENDENCIES
115
+ activesupport (~> 3.2.8)
116
+ database_cleaner
117
+ delorean (~> 2.0.0)
118
+ factory_girl (~> 4.0.0)
119
+ fakefs (~> 0.4.0)
120
+ khronos!
121
+ mongo
122
+ mysql2 (~> 0.3.11)
123
+ pg
124
+ rack-test (~> 0.6.1)
125
+ rspec (~> 2.11.0)
126
+ simplecov (= 0.6.4)
127
+ sqlite3
128
+ thin
129
+ webmock (~> 1.8.9)
data/README.md CHANGED
@@ -1,8 +1,54 @@
1
1
  khronos
2
2
  ===
3
3
 
4
- Job scheduling for the cloud.
4
+ Simple HTTP-based Job scheduling for the cloud.
5
5
 
6
+ Features
7
+ ---
8
+
9
+ - Schedule of HTTP Requests
10
+ - Configure recurrency per request
11
+ - Log HTTP status code for every request made
12
+ - Query the database via REST API
13
+ - Postgresql, MySQL and SQLite supported. (mongodb will be supported soon)
14
+
15
+ How it works
16
+ ---
17
+
18
+ Khronos use a rack app to schedule and query for scheduled tasks, and a worker
19
+ process to execute them in the background.
20
+
21
+ At 'examples' directory you find two rackup files for these processes.
22
+
23
+ How to use
24
+ ---
25
+
26
+ Create a scheduled task:
27
+
28
+ RestClient.post('http://localhost:3000/task', {
29
+ :context => 'test',
30
+ :at => 24.hours.from_now,
31
+ :task_url => "http://myapp.com/do-something-awesome",
32
+ :recurrency => 12.hours
33
+ })
34
+ # => "{\"active\":true,\"at\":\"2012-09-15T21:24:56-03:00\",\"context\":\"test\",\"id\":1,\"recurrency\":1,\"task_url\":\"http://myapp.com/do-something-awesome\"}"
35
+
36
+ Query for a scheduled task:
37
+
38
+ RestClient.get('http://localhost:3000/task', :params => { :context => 'test' })
39
+ # => "{\"active\":true,\"at\":\"2012-09-15T21:24:56-03:00\",\"context\":\"test\",\"id\":1,\"recurrency\":1,\"task_url\":\"http://myapp.com/do-something-awesome\"}"
40
+
41
+ Query for logs for tasks that already ran.
42
+
43
+ RestClient.get('http://localhost:3000/schedule/logs', :params => { :status_code => 500 })
44
+ # => "[{\"id\":3,\"schedule_id\":1,\"started_at\":\"2012-09-15T13:38:48-03:00\",\"status_code\":500},{\"id\":5,\"schedule_id\":2,\"started_at\":\"2012-09-15T13:38:48-03:00\",\"status_code\":500}]"
45
+
46
+ Note: these examples are using [rest-client](https://github.com/archiloque/rest-client/) and [activesupport](https://github.com/rails/rails/tree/master/activesupport).
47
+
48
+ Contributing
49
+ ---
50
+
51
+ Feel free to fork and send pull requests with features and/or bug fixes.
6
52
 
7
53
  License
8
54
  ---
@@ -0,0 +1,17 @@
1
+ ENV['RACK_ENV'] = 'production'
2
+
3
+ require 'rubygems'
4
+ require 'khronos'
5
+ require 'khronos/version'
6
+
7
+ require 'girl_friday'
8
+ require 'redis'
9
+ require 'connection_pool'
10
+
11
+ ENV['REDISTOGO_URL'] ||= "redis://127.0.0.1:6379/0"
12
+
13
+ $redis = ConnectionPool::Wrapper.new(:size => 2, :timeout => 3) { Redis.connect(:url => ENV["REDISTOGO_URL"]) }
14
+ runner = Khronos::Server::Runner.new(:runner, :store => GirlFriday::Store::Redis, :store_config => { :pool => $redis })
15
+
16
+ Khronos::Config.instance.load!('config/environment.yml', ENV['RACK_ENV'])
17
+ Khronos::Server::Controller.new(runner).start!
@@ -0,0 +1,8 @@
1
+ ENV['RACK_ENV'] = 'production'
2
+
3
+ require 'rubygems'
4
+ require 'khronos'
5
+
6
+ Khronos::Config.instance.load!('config/environment.yml', ENV['RACK_ENV'])
7
+ run Khronos::Server::Scheduler
8
+
data/khronos.gemspec CHANGED
@@ -7,11 +7,11 @@ Gem::Specification.new do |s|
7
7
  s.version = Khronos::VERSION
8
8
  s.platform = Gem::Platform::RUBY
9
9
  s.authors = ["Endel Dreyer"]
10
- s.email = ["endel@ocapi.com.br"]
11
- s.homepage = "http://github.com/ocapi/khronos"
10
+ s.email = ["endel.dreyer@gmail.com"]
11
+ s.homepage = "http://github.com/endel/khronos"
12
12
 
13
- s.summary = "Ruby HTTP Job Scheduler Interface."
14
- s.description = "Ruby HTTP Job Scheduler Interface. An advanced Cron replacement for the cloud."
13
+ s.summary = "Simple HTTP-based Job scheduling for the cloud."
14
+ s.description = "Simple HTTP-based Job scheduling for the cloud."
15
15
  s.licenses = ['MIT']
16
16
 
17
17
  s.add_dependency "sinatra", "~> 1.3.3"
@@ -8,7 +8,7 @@ module Khronos
8
8
  puts "Khronos::Scheduler#run => #{schedule.inspect}"
9
9
  schedule.update_attributes(:active => false)
10
10
  schedule.save
11
- runner.enqueue(schedule)
11
+ runner.enqueue(schedule) if runner
12
12
  end
13
13
 
14
14
  def self.fetch(target_time=Time.now)
@@ -16,32 +16,41 @@ module Khronos
16
16
  end
17
17
 
18
18
  def enqueue(schedule)
19
- puts "Khronos::Server::Runner#enqueue => #{schedule.inspect}"
20
19
  @queue.push(schedule.to_json)
21
20
  end
22
21
 
23
22
  def process(json)
24
23
  schedule = JSON.parse(json)
25
- puts "Khronos::Server::Runner#process => #{schedule.inspect}"
26
-
27
- if (url = schedule['task_url'])
28
- begin
29
- response = RestClient.get(url)
30
- puts "Callback: success. response length: #{response.length.inspect}"
31
- rescue Exception => e
32
- puts "Callback: error. (#{e.inspect})"
33
- end
34
- calculate_recurrency!(schedule)
35
- end
24
+ schedule_log = { :started_at => Time.now, :schedule_id => schedule['id'] }
25
+
26
+ response = RestClient.get(schedule['task_url'])
27
+ schedule_log[:status_code] = response.code
28
+
29
+ rescue RestClient::Exception => e
30
+ schedule_log[:status_code] = e.http_code
31
+
32
+ ensure
33
+ log_schedule!(schedule_log)
34
+ calculate_recurrency!(schedule) if schedule['recurrency'].to_i > 0
36
35
  end
37
36
 
38
37
  def calculate_recurrency!(schedule)
39
- url = "http://#{Config.instance.scheduler['host']}"
40
- url += ":#{Config.instance.scheduler['port']}" if Config.instance.scheduler['port']
41
- url += "/task"
42
- RestClient.put(url, :id => schedule['id'], :patch => true)
38
+ RestClient.put(scheduler_route('/task'), :id => schedule['id'], :patch => true)
43
39
  end
44
40
 
41
+ def log_schedule!(schedule_log)
42
+ puts "Log schedule! #{schedule_log.inspect}"
43
+ RestClient.post( scheduler_route('/schedule/log'), schedule_log )
44
+ end
45
+
46
+ protected
47
+
48
+ def scheduler_route(route)
49
+ url = "http://#{Config.instance.scheduler['host']}"
50
+ url += ":#{Config.instance.scheduler['port']}" if Config.instance.scheduler['port']
51
+ url += route
52
+ end
53
+
45
54
  end
46
55
 
47
56
  end
@@ -5,23 +5,16 @@ module Khronos
5
5
 
6
6
  class Scheduler < Sinatra::Base
7
7
  set :storage, Storage.new
8
+ before { content_type 'application/json' }
8
9
 
9
- # Introduction
10
+ # Greetings
11
+ #
10
12
  get '/' do
11
- <<-EOF
12
- <html>
13
- <head>
14
- <title>Khronos #{Khronos::VERSION}</title>
15
- </head>
16
- <body>
17
- <h1>HTTP Job Scheduler Interface.</h1>
18
- <p>
19
- <a href="http://rubygems.org/gems/khronos">Khronos #{Khronos::VERSION}</a><br />
20
- by <a href="https://github.com/endel">Endel Dreyer</a>
21
- </p>
22
- </body>
23
- </html>
24
- EOF
13
+ {
14
+ :name => "Khronos - HTTP Job Scheduler Interface.",
15
+ :version => Khronos::VERSION,
16
+ :link => "https://github.com/endel/khronos"
17
+ }
25
18
  end
26
19
 
27
20
  # Creates a schedule
@@ -97,14 +90,6 @@ module Khronos
97
90
  schedule.to_json
98
91
  end
99
92
 
100
- # Checks recurrency and reactivates a schedule if necessary
101
- #
102
- # @param [String] id
103
- #
104
- # @return [Hash] data
105
- patch '/task' do
106
- end
107
-
108
93
  # Force a task to be scheduled right now
109
94
  #
110
95
  # @param [Integer] id
@@ -116,7 +101,31 @@ module Khronos
116
101
  {:queued => !schedule.nil?}.to_json
117
102
  end
118
103
 
119
- # Log requests
104
+ # Schedule logs querying interface
105
+ #
106
+ # @param [Integer] started_at
107
+ # @param [Integer] schedule_id
108
+ # @param [Integer] status_code
109
+ #
110
+ # @return [Array] list of schedule logs
111
+ #
112
+ get '/schedule/logs' do
113
+ limit = params.delete('limit')
114
+ offset = params.delete('offset')
115
+
116
+ relation = Storage::ScheduleLog.where(params)
117
+ relation = relation.limit(limit) if limit
118
+ relation = relation.offset(offset) if offset
119
+ relation.to_json
120
+ end
121
+
122
+ # Create a schedule log
123
+ #
124
+ # @param [Integer] started_at
125
+ # @param [Integer] status_code
126
+ post '/schedule/log' do
127
+ Storage::ScheduleLog.create(params).to_json
128
+ end
120
129
 
121
130
  end
122
131
 
@@ -9,7 +9,7 @@ module Khronos
9
9
  t.string :context, :null => false, :limit => 100
10
10
  t.datetime :at, :null => false
11
11
  t.string :task_url, :null => false
12
- t.integer :recurrency, :null => false
12
+ t.integer :recurrency, :null => true, :default => 0
13
13
  t.string :callbacks, :null => true, :limit => 500
14
14
  t.boolean :active, :null => false, :default => true
15
15
  end
@@ -8,7 +8,8 @@ module Khronos
8
8
  create_table :schedule_logs do |t|
9
9
  t.integer :schedule_id, :null => false
10
10
  t.datetime :started_at, :null => false
11
- t.datetime :finished_at, :null => true
11
+ t.integer :status_code, :null => false
12
+ t.string :callbacks
12
13
  end
13
14
  end
14
15
 
@@ -4,7 +4,7 @@ module Khronos
4
4
 
5
5
  module ActiveRecord
6
6
  class ScheduleLog < ::ActiveRecord::Base
7
- attr_accessible :schedule_id, :started_at, :finished_at
7
+ attr_accessible :schedule_id, :started_at, :status_code
8
8
  belongs_to :schedule
9
9
  end
10
10
  end
@@ -7,7 +7,8 @@ module Khronos
7
7
  include ::Mongoid::Document
8
8
 
9
9
  field :started_at, :type => DateTime
10
- field :finished_at, :type => DateTime
10
+ field :status_code, :type => Integer
11
+ field :callbacks, :type => String
11
12
 
12
13
  belongs_to :schedule
13
14
  end
@@ -1,3 +1,3 @@
1
1
  module Khronos
2
- VERSION = '0.0.3.pre4'
2
+ VERSION = '0.1.0'
3
3
  end
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe Khronos::Scheduler do
4
4
  subject { Khronos::Scheduler }
5
- let(:scheduler) { subject.new }
5
+ let(:scheduler) { subject }
6
6
 
7
7
  before(:all) do
8
8
  ENV['KHRONOS_STORAGE'] = 'sqlite3://localhost/spec/tmp/scheduler.db'
@@ -2,25 +2,42 @@ require 'spec_helper'
2
2
 
3
3
  describe Khronos::Server::Runner do
4
4
  subject { Khronos::Server::Runner }
5
- let(:recurrency_check_url) { "http://localhost:8080/task?id=1" }
6
- let(:task_url) { 'http://test.com' }
5
+ let(:recurrency_check_url) { "http://localhost:8080/task" }
6
+ let(:schedule_log_url) { "http://localhost:8080/schedule/log" }
7
+ let(:valid_task_url) { 'http://test.com' }
8
+ let(:invalid_task_url) { 'http://test.com/404' }
7
9
 
8
10
  before(:each) do
9
- Khronos::Server::Runner.any_instance.stub(:new)
10
- Khronos::Server::Runner.any_instance.stub(:send_data)
11
- Khronos::Server::Runner.any_instance.stub(:close_connection)
12
-
13
- stub_request(:get, task_url).to_return(:body => 'it works!', :status => 200)
14
- stub_request(:patch, recurrency_check_url).to_return(:status => 200, :body => "", :headers => {})
11
+ stub_request(:get, valid_task_url).to_return(:body => 'it works!', :status => 200)
12
+ stub_request(:get, invalid_task_url).to_return(:status => 404)
13
+ stub_request(:put, recurrency_check_url).with(:id => 1, :patch => true).to_return(:status => 200, :body => "", :headers => {})
15
14
  end
16
15
 
17
16
  it "should run a task" do
17
+ stub_request(:post, schedule_log_url).with(:schedule_id => 1).to_return(:status => 200, :body => "", :headers => {})
18
+ stub_request(:post, schedule_log_url).with(:schedule_id => 2, :status_code => 404).to_return(:status => 200, :body => "", :headers => {})
19
+
18
20
  runner = subject.new(nil)
19
- EM.run_block do
20
- runner.receive_data({:id => 1, :task_url => task_url, :recurrency => 0}.to_json)
21
+ runner.process({:id => 1, :task_url => valid_task_url, :recurrency => 0}.to_json)
22
+ runner.process({:id => 2, :task_url => invalid_task_url, :recurrency => 60}.to_json)
23
+
24
+ a_request(:get, valid_task_url).should have_been_made
25
+ a_request(:get, invalid_task_url).should have_been_made
26
+ a_request(:post, schedule_log_url).with {|r| r.body =~ /schedule_id=2/ && r.body =~ /status_code=404/ }.should have_been_made
27
+ a_request(:post, schedule_log_url).with {|r| r.body =~ /schedule_id=1/ && r.body =~ /status_code=200/ }.should have_been_made
28
+ a_request(:put, recurrency_check_url).should have_been_made
29
+ end
30
+
31
+ context "callbacks" do
32
+
33
+ xit "should trigger error callback" do
34
+ # not implemented yet
35
+ end
36
+
37
+ xit "should trigger success callback" do
38
+ # not implemented yet
21
39
  end
22
- a_request(:get, task_url).should have_been_made
23
- a_request(:patch, recurrency_check_url).should have_been_made
40
+
24
41
  end
25
42
 
26
43
  end
@@ -7,64 +7,127 @@ describe Khronos::Server::Scheduler do
7
7
  Khronos::Server::Scheduler
8
8
  end
9
9
 
10
- it "should return 404 for invalid task requests" do
11
- get('/task')
12
- last_response.status.should == 404
10
+ context "tasks" do
11
+ it "should return 404 for invalid task requests" do
12
+ get('/task')
13
+ last_response.status.should == 404
13
14
 
14
- get('/task', {:context => "invalid"})
15
- last_response.status.should == 404
15
+ get('/task', {:context => "invalid"})
16
+ last_response.status.should == 404
16
17
 
17
- get('/task', {:id => 99})
18
- last_response.status.should == 404
19
- end
18
+ get('/task', {:id => 99})
19
+ last_response.status.should == 404
20
+ end
20
21
 
21
- it "should schedule context to 1 week" do
22
- post('/task', {
23
- :context => "1-week-test",
24
- :schedule => 1.week,
25
- :at => Time.now,
26
- :recurrency => 1.week,
27
- :task_url => "http://fake"
28
- })
29
- last_response.status.should == 200
30
- Khronos::Storage::Schedule.where(:context => "1-week-test").count.should == 1
31
- end
22
+ it "should schedule context to 1 week" do
23
+ post('/task', {
24
+ :context => "1-week-test",
25
+ :schedule => 1.week,
26
+ :at => Time.now,
27
+ :recurrency => 1.week,
28
+ :task_url => "http://fake"
29
+ })
30
+ last_response.status.should == 200
31
+ Khronos::Storage::Schedule.where(:context => "1-week-test").count.should == 1
32
32
 
33
- it "should enqueue schedule to run immediatelly" do
34
- mock_tcp_next_request("")
35
- dummy_schedule = FactoryGirl.create(:schedule)
36
- post('/task/run', :id => dummy_schedule.id)
37
- last_response.status.should == 200
38
- JSON.parse(last_response.body).should == {'queued' => true}
39
- end
33
+ post('/task', {
34
+ :context => "2-weeks-test",
35
+ :schedule => 2.weeks,
36
+ :at => Time.now,
37
+ :task_url => "http://fake"
38
+ })
39
+ last_response.status.should == 200
40
+ Khronos::Storage::Schedule.where(:context => "2-weeks-test").count.should == 1
41
+ end
42
+
43
+ it "should enqueue schedule to run immediatelly" do
44
+ dummy_schedule = FactoryGirl.create(:schedule)
45
+ post('/task/run', :id => dummy_schedule.id)
46
+ last_response.status.should == 200
47
+ JSON.parse(last_response.body).should == {'queued' => true}
48
+ end
49
+
50
+ it "should update schedule data" do
51
+ dummy_schedule = FactoryGirl.create(:schedule, {
52
+ :context => "will be updated",
53
+ :at => Time.now,
54
+ :active => true,
55
+ :recurrency => 0
56
+ })
57
+ request('/task', :method => 'PUT', :params => {:id => dummy_schedule.id, :context => "updated"})
58
+ schedule = Khronos::Storage::Schedule.find(dummy_schedule.id)
59
+ schedule.active.should == true
60
+ schedule.context.should == 'updated'
61
+ end
62
+
63
+ it "should update recurring time" do
64
+ dummy_schedule = FactoryGirl.create(:schedule, {
65
+ :at => Time.now,
66
+ :active => false,
67
+ :recurrency => 1.day
68
+ })
69
+ request('/task', :method => 'PUT', :params => {:id => dummy_schedule.id, :patch => true})
70
+ schedule = Khronos::Storage::Schedule.find(dummy_schedule.id)
71
+ schedule.active.should == true
72
+ schedule.at.to_i.should == 1.day.from_now.to_i
73
+ end
74
+
75
+ it "should retrieve a list of schedules by context pattern" do
76
+ FactoryGirl.create(:schedule, {:context => "namespaced"})
77
+ FactoryGirl.create(:schedule, {:context => "namespaced:1"})
78
+ FactoryGirl.create(:schedule, {:context => "namespaced:2"})
79
+ FactoryGirl.create(:schedule, {:context => "namespaced:3"})
80
+ FactoryGirl.create(:schedule, {:context => "namespaced:4"})
81
+ FactoryGirl.create(:schedule, {:context => "namespaced:5"})
40
82
 
41
- it "should update recurring time" do
42
- dummy_schedule = FactoryGirl.create(:schedule, {
43
- :at => Time.now,
44
- :active => false,
45
- :recurrency => 1.day
46
- })
47
- request('/task', :method => 'PATCH', :params => {:id => dummy_schedule.id})
48
- schedule = Khronos::Storage::Schedule.find(dummy_schedule.id)
49
- schedule.active.should == true
50
- schedule.at.to_i.should == 1.day.from_now.to_i
83
+ get('/tasks', {:context => "namespaced%"})
84
+ last_response.status.should == 200
85
+ JSON.parse(last_response.body).length.should == 6
86
+
87
+ get('/tasks', {:context => "namespaced"})
88
+ last_response.status.should == 200
89
+ JSON.parse(last_response.body).length.should == 1
90
+ end
51
91
  end
52
92
 
53
- it "should retrieve a list of schedules by context pattern" do
54
- FactoryGirl.create(:schedule, {:context => "namespaced"})
55
- FactoryGirl.create(:schedule, {:context => "namespaced:1"})
56
- FactoryGirl.create(:schedule, {:context => "namespaced:2"})
57
- FactoryGirl.create(:schedule, {:context => "namespaced:3"})
58
- FactoryGirl.create(:schedule, {:context => "namespaced:4"})
59
- FactoryGirl.create(:schedule, {:context => "namespaced:5"})
60
-
61
- get('/tasks', {:context => "namespaced%"})
62
- last_response.status.should == 200
63
- JSON.parse(last_response.body).length.should == 6
64
-
65
- get('/tasks', {:context => "namespaced"})
66
- last_response.status.should == 200
67
- JSON.parse(last_response.body).length.should == 1
93
+ context "logs" do
94
+ it "should record schedule logs" do
95
+ data = { :schedule_id => 1, :started_at => Time.at(1347727128), :status_code => 404 }
96
+ post('/schedule/log', data)
97
+ last_response.status.should == 200
98
+
99
+ schedule_log = Khronos::Storage::ScheduleLog.find(JSON.parse(last_response.body)['id'])
100
+ schedule_log.schedule_id.should == data[:schedule_id]
101
+ schedule_log.started_at.should == data[:started_at]
102
+ schedule_log.status_code.should == data[:status_code]
103
+ end
104
+
105
+ it "should query for schedule logs" do
106
+ FactoryGirl.create(:schedule_log, {:started_at => Time.at(1347727128), :status_code => 200, :schedule_id => 1})
107
+ FactoryGirl.create(:schedule_log, {:started_at => Time.at(1347727128), :status_code => 500, :schedule_id => 1})
108
+ FactoryGirl.create(:schedule_log, {:started_at => Time.at(1347727128), :status_code => 200, :schedule_id => 1})
109
+ FactoryGirl.create(:schedule_log, {:started_at => Time.at(1347727128), :status_code => 500, :schedule_id => 2})
110
+ FactoryGirl.create(:schedule_log, {:started_at => Time.at(1347727128), :status_code => 200, :schedule_id => 2})
111
+ FactoryGirl.create(:schedule_log, {:started_at => Time.at(1347727128), :status_code => 404, :schedule_id => 2})
112
+ FactoryGirl.create(:schedule_log, {:started_at => Time.at(1347727128), :status_code => 200, :schedule_id => 3})
113
+
114
+ get('/schedule/logs', {:status_code => 200})
115
+ last_response.status.should == 200
116
+ JSON.parse(last_response.body).length.should == 4
117
+
118
+ get('/schedule/logs', {:schedule_id => 2})
119
+ last_response.status.should == 200
120
+ logs = JSON.parse(last_response.body)
121
+ logs.length.should == 3
122
+
123
+ get('/schedule/logs', {:status_code => 200, :limit => 1})
124
+ JSON.parse(last_response.body).length.should == 1
125
+
126
+ get('/schedule/logs', {:status_code => 500})
127
+ puts "status_code => #{last_response.body.inspect}"
128
+ #logs = JSON.parse(last_response.body)
129
+ #logs.first['schedule_id'].should == 2
130
+ end
68
131
  end
69
132
 
70
133
  end
@@ -1,23 +0,0 @@
1
- require 'socket'
2
- TCP_NEW = TCPSocket.method(:new) unless defined? TCP_NEW
3
-
4
- #
5
- # Example:
6
- # mock_tcp_next_request("<xml>junk</xml>")
7
- #
8
- class FakeTCPSocket
9
- def puts(*args); end
10
- def closed?; true; end
11
- def write(some_text = nil); end
12
- end
13
-
14
- def mock_tcp_next_request(string)
15
- TCPSocket.stub!(:new).and_return {
16
- cm = FakeTCPSocket.new
17
- cm
18
- }
19
- end
20
-
21
- def unmock_tcp
22
- TCPSocket.stub!(:new).and_return { TCP_NEW.call }
23
- end
Binary file
data/spec/tmp/sqlite3.db CHANGED
Binary file
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: khronos
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3.pre4
5
- prerelease: 6
4
+ version: 0.1.0
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Endel Dreyer
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-10 00:00:00.000000000 Z
12
+ date: 2012-09-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sinatra
@@ -171,10 +171,9 @@ dependencies:
171
171
  - - ~>
172
172
  - !ruby/object:Gem::Version
173
173
  version: 0.10.0
174
- description: Ruby HTTP Job Scheduler Interface. An advanced Cron replacement for the
175
- cloud.
174
+ description: Simple HTTP-based Job scheduling for the cloud.
176
175
  email:
177
- - endel@ocapi.com.br
176
+ - endel.dreyer@gmail.com
178
177
  executables: []
179
178
  extensions: []
180
179
  extra_rdoc_files: []
@@ -183,6 +182,7 @@ files:
183
182
  - .rspec
184
183
  - .travis.yml
185
184
  - Gemfile
185
+ - Gemfile.lock
186
186
  - LICENSE
187
187
  - README.md
188
188
  - Rakefile
@@ -190,6 +190,8 @@ files:
190
190
  - config.ru
191
191
  - config/environment.yml
192
192
  - db/test.db
193
+ - examples/runner_server.ru
194
+ - examples/scheduler_server.ru
193
195
  - khronos.gemspec
194
196
  - lib/.scheduler.rb.swp
195
197
  - lib/khronos.rb
@@ -198,7 +200,6 @@ files:
198
200
  - lib/khronos/scheduler.rb
199
201
  - lib/khronos/server.rb
200
202
  - lib/khronos/server/controller.rb
201
- - lib/khronos/server/em_runner.rb
202
203
  - lib/khronos/server/runner.rb
203
204
  - lib/khronos/server/scheduler.rb
204
205
  - lib/khronos/storage.rb
@@ -228,7 +229,7 @@ files:
228
229
  - spec/support/mocks.rb
229
230
  - spec/tmp/scheduler.db
230
231
  - spec/tmp/sqlite3.db
231
- homepage: http://github.com/ocapi/khronos
232
+ homepage: http://github.com/endel/khronos
232
233
  licenses:
233
234
  - MIT
234
235
  post_install_message:
@@ -244,15 +245,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
244
245
  required_rubygems_version: !ruby/object:Gem::Requirement
245
246
  none: false
246
247
  requirements:
247
- - - ! '>'
248
+ - - ! '>='
248
249
  - !ruby/object:Gem::Version
249
- version: 1.3.1
250
+ version: '0'
250
251
  requirements: []
251
252
  rubyforge_project:
252
253
  rubygems_version: 1.8.24
253
254
  signing_key:
254
255
  specification_version: 3
255
- summary: Ruby HTTP Job Scheduler Interface.
256
+ summary: Simple HTTP-based Job scheduling for the cloud.
256
257
  test_files:
257
258
  - spec/functional/adapters_spec.rb
258
259
  - spec/functional/controller_spec.rb
@@ -1,56 +0,0 @@
1
- require 'json'
2
- require 'eventmachine'
3
- require 'em-http'
4
-
5
- #
6
- # DEPRECATED:
7
- # This class was used just for testing purpose.
8
- #
9
-
10
- module Khronos
11
- module Server
12
-
13
- class Runner < EventMachine::Connection
14
- def post_init
15
- puts "-- someone connected to the server!"
16
- end
17
-
18
- def receive_data json
19
- puts "Receive data to run: #{json}"
20
- schedule = JSON.parse(json)
21
- send_data ">>> you sent: #{schedule.inspect}"
22
-
23
- # Close connection with client immediatelly
24
- close_connection
25
-
26
- if (url = schedule['task_url'])
27
- http = EventMachine::HttpRequest.new(url).get :redirects => 5
28
- http.callback do
29
- puts "#{url}\n#{http.response_header.status} - #{http.response.length} bytes\n"
30
- puts http.response
31
- end
32
-
33
- http.errback do
34
- puts "#{url}\n" + http.error
35
- end
36
-
37
- enqueue_recurrency!(schedule)
38
- end
39
-
40
- end
41
-
42
- def enqueue_recurrency!(schedule)
43
- url = "http://#{Config.instance.scheduler['host']}"
44
- url += ":#{Config.instance.scheduler['port']}" if Config.instance.scheduler['port']
45
- url += "/task?id=#{schedule['id']}"
46
- EventMachine::HttpRequest.new(url).patch :redirects => 2
47
- end
48
-
49
- def unbind
50
- puts "-- someone disconnected from the echo server!"
51
- end
52
-
53
- end
54
-
55
- end
56
- end