khronos 0.1.3 → 0.1.4
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/.rspec +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +4 -1
- data/README.md +8 -7
- data/Rakefile +9 -25
- data/lib/khronos.rb +0 -1
- data/lib/khronos/server/controller.rb +4 -2
- data/lib/khronos/server/scheduler.rb +2 -3
- data/lib/khronos/storage.rb +0 -20
- data/lib/khronos/storage/adapter.rb +1 -5
- data/lib/khronos/storage/adapter/activerecord.rb +21 -13
- data/lib/khronos/storage/adapter/activerecord/schedule.rb +8 -0
- data/lib/khronos/version.rb +1 -1
- data/spec/functional/adapters_spec.rb +1 -1
- data/spec/functional/storage_spec.rb +46 -0
- data/spec/integration/scheduler_server_spec.rb +1 -1
- metadata +4 -5
- data/lib/khronos/scheduler.rb +0 -19
- data/spec/functional/scheduler_spec.rb +0 -30
data/.rspec
CHANGED
data/Gemfile
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
source :rubygems
|
2
2
|
gemspec
|
3
3
|
|
4
|
+
gem 'rake'
|
5
|
+
|
4
6
|
group :test do
|
5
7
|
gem 'thin'
|
6
8
|
gem 'simplecov', '= 0.6.4'
|
@@ -13,6 +15,7 @@ group :test do
|
|
13
15
|
gem "factory_girl", "~> 4.0.0"
|
14
16
|
gem "webmock", "~> 1.8.9"
|
15
17
|
gem "database_cleaner"
|
18
|
+
gem 'netrc', '= 0.7.7'
|
16
19
|
|
17
20
|
#
|
18
21
|
# Used for time functions
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
khronos (0.1.
|
4
|
+
khronos (0.1.4)
|
5
5
|
activerecord (~> 3.2.8)
|
6
6
|
girl_friday (~> 0.10.0)
|
7
7
|
json (~> 1.7.5)
|
@@ -63,6 +63,7 @@ GEM
|
|
63
63
|
rack
|
64
64
|
rack-test (0.6.1)
|
65
65
|
rack (>= 1.0)
|
66
|
+
rake (0.9.2.2)
|
66
67
|
rest-client (1.6.8)
|
67
68
|
mime-types (>= 1.16)
|
68
69
|
netrc
|
@@ -107,8 +108,10 @@ DEPENDENCIES
|
|
107
108
|
mongo
|
108
109
|
mongoid (~> 3.0.5)
|
109
110
|
mysql2 (~> 0.3.11)
|
111
|
+
netrc (= 0.7.7)
|
110
112
|
pg
|
111
113
|
rack-test (~> 0.6.1)
|
114
|
+
rake
|
112
115
|
rspec (~> 2.11.0)
|
113
116
|
simplecov (= 0.6.4)
|
114
117
|
sqlite3
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
khronos
|
1
|
+
khronos [](http://travis-ci.org/endel/khronos)
|
2
2
|
===
|
3
3
|
|
4
4
|
Simple HTTP-based Job scheduling for the cloud.
|
@@ -10,7 +10,8 @@ Features
|
|
10
10
|
- Configure recurrency per request
|
11
11
|
- Log HTTP status code for every request made
|
12
12
|
- Query the database via REST API
|
13
|
-
|
13
|
+
|
14
|
+
Currently only PostgreSQL is supported. Patches for other databases are welcome.
|
14
15
|
|
15
16
|
How it works
|
16
17
|
---
|
@@ -31,24 +32,24 @@ Create a scheduled task:
|
|
31
32
|
:task_url => "http://myapp.com/do-something-awesome",
|
32
33
|
:recurrency => 12.hours
|
33
34
|
})
|
34
|
-
# =>
|
35
|
+
# => {"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
|
|
36
37
|
Query for a scheduled task:
|
37
38
|
|
38
39
|
RestClient.get('http://localhost:3000/task', :params => { :context => 'test' })
|
39
|
-
# =>
|
40
|
+
# => {"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
|
|
41
42
|
Delete a scheduled task by query:
|
42
43
|
|
43
44
|
RestClient.delete('http://localhost:3000/task', :params => { :status_code => 404 })
|
44
|
-
# =>
|
45
|
+
# => {"deleted":3}
|
45
46
|
RestClient.delete('http://localhost:3000/task', :params => { :id => 9 })
|
46
|
-
# =>
|
47
|
+
# => {"deleted":1}
|
47
48
|
|
48
49
|
Query for logs for tasks that already ran.
|
49
50
|
|
50
51
|
RestClient.get('http://localhost:3000/schedule/logs', :params => { :status_code => 500 })
|
51
|
-
# =>
|
52
|
+
# => [{"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}]
|
52
53
|
|
53
54
|
Note: these examples are using [rest-client](https://github.com/archiloque/rest-client/) and [activesupport](https://github.com/rails/rails/tree/master/activesupport).
|
54
55
|
|
data/Rakefile
CHANGED
@@ -1,36 +1,20 @@
|
|
1
1
|
$: << 'lib'
|
2
2
|
|
3
|
+
require 'rake'
|
3
4
|
require 'bundler/setup'
|
4
5
|
|
5
6
|
ENV['RACK_ENV'] = ENV['ENV'] || 'test'
|
6
7
|
require 'khronos'
|
7
8
|
|
8
|
-
|
9
|
-
require 'khronos/storage/adapter/activerecord/migrations/schedule'
|
10
|
-
require 'khronos/storage/adapter/activerecord/migrations/schedule_log'
|
11
|
-
end
|
12
|
-
|
13
|
-
namespace :db do
|
14
|
-
|
15
|
-
desc 'Create the database.'
|
16
|
-
task :create do
|
17
|
-
adapter = Khronos::Storage::Adapter.get(ENV['KHRONOS_STORAGE'])
|
18
|
-
if adapter.name =~ /ActiveRecord/
|
19
|
-
load_migrations!
|
20
|
-
CreateSchedule.up
|
21
|
-
CreateScheduleLog.up
|
22
|
-
end
|
23
|
-
end
|
9
|
+
require "rspec/core/rake_task"
|
24
10
|
|
25
|
-
|
26
|
-
|
27
|
-
adapter = Khronos::Storage::Adapter.get(ENV['KHRONOS_STORAGE'])
|
28
|
-
if adapter.name =~ /ActiveRecord/
|
29
|
-
load_migrations!
|
30
|
-
CreateSchedule.down
|
31
|
-
CreateScheduleLog.down
|
32
|
-
end
|
33
|
-
end
|
11
|
+
desc "Run all specs"
|
12
|
+
task :spec => ["spec:all"]
|
34
13
|
|
14
|
+
desc "Run unit specs"
|
15
|
+
RSpec::Core::RakeTask.new(:'spec:all') do |t|
|
16
|
+
t.rspec_opts = ['--colour']
|
17
|
+
t.pattern = 'spec/**/*_spec.rb'
|
35
18
|
end
|
36
19
|
|
20
|
+
task :default => :spec
|
data/lib/khronos.rb
CHANGED
@@ -15,10 +15,12 @@ module Khronos
|
|
15
15
|
def check_schedule!
|
16
16
|
puts "Checking... #{Time.now}"
|
17
17
|
count = 0
|
18
|
-
|
19
|
-
|
18
|
+
|
19
|
+
Storage::Schedule.fetch(Time.now).each do |schedule|
|
20
|
+
@runner.enqueue(schedule)
|
20
21
|
count += 1
|
21
22
|
end
|
23
|
+
|
22
24
|
puts "Tick. #{count} jobs to run."
|
23
25
|
end
|
24
26
|
|
@@ -111,9 +111,8 @@ module Khronos
|
|
111
111
|
#
|
112
112
|
# @return [Hash] queued
|
113
113
|
post '/task/run' do
|
114
|
-
schedule = Storage::Schedule.where(:id => params[:id]).
|
115
|
-
|
116
|
-
{:queued => !schedule.nil?}.to_json
|
114
|
+
schedule = Storage::Schedule.where(:id => params[:id]).update_all(:at => Time.now)
|
115
|
+
{:queued => schedule}.to_json
|
117
116
|
end
|
118
117
|
|
119
118
|
# Schedule logs querying interface
|
data/lib/khronos/storage.rb
CHANGED
@@ -8,7 +8,6 @@ module Khronos
|
|
8
8
|
raise RuntimeError.new("Please configure 'KHRONOS_STORAGE' on your environment variables.") if uri.nil?
|
9
9
|
|
10
10
|
@adapter = Adapter.get(uri)
|
11
|
-
self.migrate! if @adapter.name =~ /ActiveRecord/
|
12
11
|
self.class.send(:include, @adapter)
|
13
12
|
end
|
14
13
|
|
@@ -24,24 +23,5 @@ module Khronos
|
|
24
23
|
ScheduleLog.delete_all
|
25
24
|
end
|
26
25
|
|
27
|
-
protected
|
28
|
-
|
29
|
-
def migrate!
|
30
|
-
require 'khronos/storage/adapter/activerecord/migrations/schedule'
|
31
|
-
require 'khronos/storage/adapter/activerecord/migrations/schedule_log'
|
32
|
-
|
33
|
-
unless ActiveRecord::Base.connection.table_exists?(:schedules)
|
34
|
-
Adapter::ActiveRecord::CreateSchedule.up
|
35
|
-
else
|
36
|
-
Logger.debug "Schedules table already exists."
|
37
|
-
end
|
38
|
-
|
39
|
-
unless ActiveRecord::Base.connection.table_exists?(:schedule_logs)
|
40
|
-
Adapter::ActiveRecord::CreateScheduleLog.up
|
41
|
-
else
|
42
|
-
Logger.debug "ScheduleLog table already exists."
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
26
|
end
|
47
27
|
end
|
@@ -29,14 +29,10 @@ module Khronos
|
|
29
29
|
def self.get(url)
|
30
30
|
uri = parse_uri(url)
|
31
31
|
framework = @frameworks[uri[:scheme]]
|
32
|
-
|
33
32
|
require "khronos/storage/adapter/#{framework}"
|
34
33
|
|
35
34
|
# Get and connect with the adapter class.
|
36
|
-
|
37
|
-
|
38
|
-
# uri.merge(:adapter => @adapters[uri[:scheme]] || uri[:scheme])
|
39
|
-
adapter.connect!(url)
|
35
|
+
const_get(@classes[framework]).connect!(url)
|
40
36
|
end
|
41
37
|
|
42
38
|
end
|
@@ -10,25 +10,33 @@ module Khronos
|
|
10
10
|
def self.connect!(url)
|
11
11
|
require 'active_record'
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
#else
|
16
|
-
::ActiveRecord::Base.establish_connection(url)
|
17
|
-
::ActiveRecord::Base.include_root_in_json = false
|
18
|
-
#end
|
19
|
-
|
20
|
-
#
|
21
|
-
# ::ActiveRecord::Base.logger = ::Logger.new(STDOUT)
|
22
|
-
#
|
13
|
+
::ActiveRecord::Base.establish_connection(url)
|
14
|
+
::ActiveRecord::Base.include_root_in_json = false
|
23
15
|
self
|
24
16
|
end
|
25
17
|
|
26
18
|
def self.included(base)
|
27
|
-
|
19
|
+
self.migrate!
|
28
20
|
end
|
29
21
|
|
30
|
-
|
31
|
-
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
def self.migrate!
|
26
|
+
require 'khronos/storage/adapter/activerecord/migrations/schedule'
|
27
|
+
require 'khronos/storage/adapter/activerecord/migrations/schedule_log'
|
28
|
+
|
29
|
+
unless ::ActiveRecord::Base.connection.table_exists?(:schedules)
|
30
|
+
ActiveRecord::CreateSchedule.up
|
31
|
+
else
|
32
|
+
Logger.debug "Schedules table already exists."
|
33
|
+
end
|
34
|
+
|
35
|
+
unless ::ActiveRecord::Base.connection.table_exists?(:schedule_logs)
|
36
|
+
ActiveRecord::CreateScheduleLog.up
|
37
|
+
else
|
38
|
+
Logger.debug "ScheduleLog table already exists."
|
39
|
+
end
|
32
40
|
end
|
33
41
|
|
34
42
|
end
|
@@ -9,6 +9,14 @@ module Khronos
|
|
9
9
|
attr_accessible :context, :at, :recurrency, :task_url, :callbacks, :active
|
10
10
|
has_many :logs, :class_name => ScheduleLog
|
11
11
|
|
12
|
+
class << self
|
13
|
+
def fetch(time)
|
14
|
+
self.find_by_sql([
|
15
|
+
"UPDATE #{self.table_name} SET active = false WHERE at <= ? AND active = true RETURNING *", time
|
16
|
+
])
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
12
20
|
def callbacks=(options)
|
13
21
|
write_attribute(:callbacks, options.to_json)
|
14
22
|
end
|
data/lib/khronos/version.rb
CHANGED
@@ -31,7 +31,7 @@ describe Khronos::Storage do
|
|
31
31
|
Khronos::Storage::Schedule.last.task_url.should == "http://some-service.com/task"
|
32
32
|
end
|
33
33
|
|
34
|
-
|
34
|
+
xit "should identify activerecord adapter for sqlite3" do
|
35
35
|
client = subject.new('sqlite3://localhost/spec/tmp/sqlite3.db')
|
36
36
|
Khronos::Storage::Schedule.should == Khronos::Storage::Adapter::ActiveRecord::Schedule
|
37
37
|
Khronos::Storage::ScheduleLog.should == Khronos::Storage::Adapter::ActiveRecord::ScheduleLog
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Khronos::Storage do
|
4
|
+
subject { Khronos::Storage::Schedule }
|
5
|
+
let(:schedule) { subject }
|
6
|
+
|
7
|
+
before(:all) do
|
8
|
+
ENV['KHRONOS_STORAGE'] = 'postgresql://localhost:5432/khronos'
|
9
|
+
|
10
|
+
@storage = Khronos::Storage.new
|
11
|
+
@storage.truncate!
|
12
|
+
|
13
|
+
load_factory_girl!
|
14
|
+
end
|
15
|
+
|
16
|
+
it "fetch schedules by time" do
|
17
|
+
FactoryGirl.create(:schedule, :at => 1.minutes.from_now)
|
18
|
+
schedule.fetch(1.minutes.from_now).length.should == 1
|
19
|
+
|
20
|
+
FactoryGirl.create(:schedule, :at => 1.minutes.from_now)
|
21
|
+
FactoryGirl.create(:schedule, :at => 5.minutes.from_now)
|
22
|
+
schedule.fetch(5.minutes.from_now).length.should == 2
|
23
|
+
|
24
|
+
FactoryGirl.create(:schedule, :at => 1.minutes.from_now)
|
25
|
+
FactoryGirl.create(:schedule, :at => 5.minutes.from_now)
|
26
|
+
FactoryGirl.create(:schedule, :at => 10.minutes.from_now)
|
27
|
+
schedule.fetch(10.minutes.from_now).length.should == 3
|
28
|
+
|
29
|
+
FactoryGirl.create(:schedule, :at => 1.minutes.from_now)
|
30
|
+
FactoryGirl.create(:schedule, :at => 5.minutes.from_now)
|
31
|
+
FactoryGirl.create(:schedule, :at => 10.minutes.from_now)
|
32
|
+
FactoryGirl.create(:schedule, :at => 30.minutes.from_now)
|
33
|
+
schedule.fetch(30.minutes.from_now).length.should == 4
|
34
|
+
|
35
|
+
FactoryGirl.create(:schedule, :at => 1.minutes.from_now)
|
36
|
+
FactoryGirl.create(:schedule, :at => 5.minutes.from_now)
|
37
|
+
FactoryGirl.create(:schedule, :at => 10.minutes.from_now)
|
38
|
+
FactoryGirl.create(:schedule, :at => 30.minutes.from_now)
|
39
|
+
FactoryGirl.create(:schedule, :at => 60.minutes.from_now)
|
40
|
+
schedule.fetch(60.minutes.from_now).length.should == 5
|
41
|
+
|
42
|
+
schedule.fetch(60.minutes.from_now).length.should == 0
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
@@ -44,7 +44,7 @@ describe Khronos::Server::Scheduler do
|
|
44
44
|
dummy_schedule = FactoryGirl.create(:schedule)
|
45
45
|
post('/task/run', :id => dummy_schedule.id)
|
46
46
|
last_response.status.should == 200
|
47
|
-
JSON.parse(last_response.body).should == {'queued' =>
|
47
|
+
JSON.parse(last_response.body).should == {'queued' => 1}
|
48
48
|
end
|
49
49
|
|
50
50
|
it "should update schedule data" do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: khronos
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-09-
|
12
|
+
date: 2012-09-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sinatra
|
@@ -165,7 +165,6 @@ files:
|
|
165
165
|
- lib/khronos.rb
|
166
166
|
- lib/khronos/config.rb
|
167
167
|
- lib/khronos/logger.rb
|
168
|
-
- lib/khronos/scheduler.rb
|
169
168
|
- lib/khronos/server.rb
|
170
169
|
- lib/khronos/server/controller.rb
|
171
170
|
- lib/khronos/server/runner.rb
|
@@ -189,7 +188,7 @@ files:
|
|
189
188
|
- spec/functional/adapters_spec.rb
|
190
189
|
- spec/functional/controller_spec.rb
|
191
190
|
- spec/functional/misc_spec.rb
|
192
|
-
- spec/functional/
|
191
|
+
- spec/functional/storage_spec.rb
|
193
192
|
- spec/integration/runner_server_spec.rb
|
194
193
|
- spec/integration/scheduler_server_spec.rb
|
195
194
|
- spec/spec_helper.rb
|
@@ -224,7 +223,7 @@ test_files:
|
|
224
223
|
- spec/functional/adapters_spec.rb
|
225
224
|
- spec/functional/controller_spec.rb
|
226
225
|
- spec/functional/misc_spec.rb
|
227
|
-
- spec/functional/
|
226
|
+
- spec/functional/storage_spec.rb
|
228
227
|
- spec/integration/runner_server_spec.rb
|
229
228
|
- spec/integration/scheduler_server_spec.rb
|
230
229
|
- spec/spec_helper.rb
|
data/lib/khronos/scheduler.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
require 'socket'
|
2
|
-
require 'json'
|
3
|
-
|
4
|
-
module Khronos
|
5
|
-
class Scheduler
|
6
|
-
|
7
|
-
def self.run(schedule, runner=nil)
|
8
|
-
schedule.update_attributes(:active => false)
|
9
|
-
schedule.save
|
10
|
-
runner.enqueue(schedule) if runner
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.fetch(target_time=Time.now)
|
14
|
-
Storage::Schedule.where(['at <= ?', target_time]).where(:active => true)
|
15
|
-
end
|
16
|
-
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
@@ -1,30 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Khronos::Scheduler do
|
4
|
-
subject { Khronos::Scheduler }
|
5
|
-
let(:scheduler) { subject }
|
6
|
-
|
7
|
-
before(:all) do
|
8
|
-
ENV['KHRONOS_STORAGE'] = 'sqlite3://localhost/spec/tmp/scheduler.db'
|
9
|
-
|
10
|
-
@storage = Khronos::Storage.new
|
11
|
-
@storage.truncate!
|
12
|
-
|
13
|
-
load_factory_girl!
|
14
|
-
FactoryGirl.create(:schedule, :at => 1.minutes.from_now)
|
15
|
-
FactoryGirl.create(:schedule, :at => 5.minutes.from_now)
|
16
|
-
FactoryGirl.create(:schedule, :at => 10.minutes.from_now)
|
17
|
-
FactoryGirl.create(:schedule, :at => 30.minutes.from_now)
|
18
|
-
FactoryGirl.create(:schedule, :at => 60.minutes.from_now)
|
19
|
-
end
|
20
|
-
|
21
|
-
it "fetch schedules by time" do
|
22
|
-
scheduler.fetch(1.minutes.from_now).length.should == 1
|
23
|
-
scheduler.fetch(5.minutes.from_now).length.should == 2
|
24
|
-
scheduler.fetch(10.minutes.from_now).length.should == 3
|
25
|
-
scheduler.fetch(30.minutes.from_now).length.should == 4
|
26
|
-
scheduler.fetch(60.minutes.from_now).length.should == 5
|
27
|
-
end
|
28
|
-
|
29
|
-
end
|
30
|
-
|