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 +1 -1
- data/Gemfile.lock +129 -0
- data/README.md +47 -1
- data/examples/runner_server.ru +17 -0
- data/examples/scheduler_server.ru +8 -0
- data/khronos.gemspec +4 -4
- data/lib/khronos/scheduler.rb +1 -1
- data/lib/khronos/server/runner.rb +25 -16
- data/lib/khronos/server/scheduler.rb +33 -24
- data/lib/khronos/storage/adapter/activerecord/migrations/schedule.rb +1 -1
- data/lib/khronos/storage/adapter/activerecord/migrations/schedule_log.rb +2 -1
- data/lib/khronos/storage/adapter/activerecord/schedule_log.rb +1 -1
- data/lib/khronos/storage/adapter/mongoid/schedule_log.rb +2 -1
- data/lib/khronos/version.rb +1 -1
- data/spec/functional/scheduler_spec.rb +1 -1
- data/spec/integration/runner_server_spec.rb +29 -12
- data/spec/integration/scheduler_server_spec.rb +114 -51
- data/spec/support/mocks.rb +0 -23
- data/spec/tmp/scheduler.db +0 -0
- data/spec/tmp/sqlite3.db +0 -0
- metadata +12 -11
- data/lib/khronos/server/em_runner.rb +0 -56
data/Gemfile
CHANGED
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!
|
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@
|
11
|
-
s.homepage = "http://github.com/
|
10
|
+
s.email = ["endel.dreyer@gmail.com"]
|
11
|
+
s.homepage = "http://github.com/endel/khronos"
|
12
12
|
|
13
|
-
s.summary = "
|
14
|
-
s.description = "
|
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"
|
data/lib/khronos/scheduler.rb
CHANGED
@@ -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
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
-
|
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
|
-
#
|
10
|
+
# Greetings
|
11
|
+
#
|
10
12
|
get '/' do
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
-
#
|
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 =>
|
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
|
data/lib/khronos/version.rb
CHANGED
@@ -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
|
6
|
-
let(:
|
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
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
20
|
-
|
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
|
-
|
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
|
-
|
11
|
-
|
12
|
-
|
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
|
-
|
15
|
-
|
15
|
+
get('/task', {:context => "invalid"})
|
16
|
+
last_response.status.should == 404
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
get('/task', {:id => 99})
|
19
|
+
last_response.status.should == 404
|
20
|
+
end
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
:
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
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
|
data/spec/support/mocks.rb
CHANGED
@@ -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
|
data/spec/tmp/scheduler.db
CHANGED
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
|
5
|
-
prerelease:
|
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-
|
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:
|
175
|
-
cloud.
|
174
|
+
description: Simple HTTP-based Job scheduling for the cloud.
|
176
175
|
email:
|
177
|
-
- endel@
|
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/
|
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:
|
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:
|
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
|