khronos 0.0.3.pre4 → 0.1.0

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.
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