khronos 0.0.0 → 0.0.1.pre1

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.
Files changed (80) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +4 -0
  3. data/Gemfile +18 -7
  4. data/Rakefile +36 -0
  5. data/bin/khronos +2 -1
  6. data/config.ru +9 -1
  7. data/config/environment.yml +27 -5
  8. data/{lib/khronos/storage/adapter/mongo/.schedule_log.rb.un~ → db/test.db} +0 -0
  9. data/khronos.gemspec +10 -7
  10. data/lib/khronos.rb +4 -7
  11. data/lib/khronos/config.rb +0 -2
  12. data/lib/khronos/logger.rb +16 -0
  13. data/lib/khronos/scheduler.rb +17 -3
  14. data/lib/khronos/server.rb +4 -31
  15. data/lib/khronos/server/controller.rb +31 -0
  16. data/lib/khronos/server/runner.rb +50 -0
  17. data/lib/khronos/server/scheduler.rb +110 -0
  18. data/lib/khronos/storage.rb +34 -13
  19. data/lib/khronos/storage/adapter.rb +44 -0
  20. data/lib/khronos/storage/adapter/activerecord.rb +39 -0
  21. data/lib/khronos/storage/adapter/activerecord/migrations/schedule.rb +29 -0
  22. data/lib/khronos/storage/adapter/activerecord/migrations/schedule_log.rb +23 -0
  23. data/lib/khronos/storage/adapter/activerecord/schedule.rb +24 -0
  24. data/lib/khronos/storage/adapter/activerecord/schedule_log.rb +14 -0
  25. data/lib/khronos/storage/adapter/mongoid.rb +37 -0
  26. data/lib/khronos/storage/adapter/{mongo → mongoid}/schedule.rb +6 -4
  27. data/lib/khronos/storage/adapter/{mongo → mongoid}/schedule_log.rb +3 -4
  28. data/lib/khronos/tasks/db.rake +25 -0
  29. data/lib/khronos/version.rb +1 -1
  30. data/server.rb +29 -0
  31. data/spec/functional/adapters_spec.rb +61 -0
  32. data/spec/functional/controller_spec.rb +11 -0
  33. data/spec/functional/scheduler_spec.rb +30 -0
  34. data/spec/integration/runner_server_spec.rb +26 -0
  35. data/spec/integration/scheduler_server_spec.rb +70 -0
  36. data/spec/spec_helper.rb +17 -1
  37. data/spec/support/factories.rb +12 -0
  38. data/spec/support/mocks.rb +23 -0
  39. data/{lib/khronos/storage/adapter/postgres/.schedule.rb.un~ → spec/tmp/scheduler.db} +0 -0
  40. data/{lib/khronos/.version.rb.swp → spec/tmp/sqlite3.db} +0 -0
  41. metadata +92 -55
  42. data/config/.config.yml.un~ +0 -0
  43. data/config/.environment.yml.un~ +0 -0
  44. data/config/.redis.yml.un~ +0 -0
  45. data/config/.storage.yml.un~ +0 -0
  46. data/lib/.khronos.rb.un~ +0 -0
  47. data/lib/.scheduler.rb.un~ +0 -0
  48. data/lib/khronos/.config.rb.un~ +0 -0
  49. data/lib/khronos/.controller.rb.un~ +0 -0
  50. data/lib/khronos/.runner.rb.un~ +0 -0
  51. data/lib/khronos/.schedule.rb.un~ +0 -0
  52. data/lib/khronos/.scheduler.rb.un~ +0 -0
  53. data/lib/khronos/.server.rb.un~ +0 -0
  54. data/lib/khronos/.storage.rb.un~ +0 -0
  55. data/lib/khronos/.task.rb.un~ +0 -0
  56. data/lib/khronos/.version.rb.un~ +0 -0
  57. data/lib/khronos/controller.rb +0 -26
  58. data/lib/khronos/runner.rb +0 -11
  59. data/lib/khronos/storage/adapter/.client.rb.un~ +0 -0
  60. data/lib/khronos/storage/adapter/.memcached.rb.un~ +0 -0
  61. data/lib/khronos/storage/adapter/.mongo.rb.un~ +0 -0
  62. data/lib/khronos/storage/adapter/.mongodb.rb.un~ +0 -0
  63. data/lib/khronos/storage/adapter/.postgres.rb.un~ +0 -0
  64. data/lib/khronos/storage/adapter/.redis.rb.un~ +0 -0
  65. data/lib/khronos/storage/adapter/mongo.rb +0 -13
  66. data/lib/khronos/storage/adapter/mongo/.mongo.rb.un~ +0 -0
  67. data/lib/khronos/storage/adapter/mongo/.schedule.rb.un~ +0 -0
  68. data/lib/khronos/storage/adapter/postgres.rb +0 -15
  69. data/lib/khronos/storage/adapter/postgres/.schedule_log.rb.un~ +0 -0
  70. data/lib/khronos/storage/adapter/postgres/schedule.rb +0 -13
  71. data/lib/khronos/storage/adapter/postgres/schedule_log.rb +0 -13
  72. data/lib/khronos/storage/db/migrate/.schedule.rb.un~ +0 -0
  73. data/lib/khronos/storage/db/migrate/.schedule_log.rb.un~ +0 -0
  74. data/lib/khronos/storage/db/migrate/schedule.rb +0 -15
  75. data/lib/khronos/storage/db/migrate/schedule_log.rb +0 -16
  76. data/spec/.spec_helper.rb.un~ +0 -0
  77. data/spec/functional/.client_spec.rb.un~ +0 -0
  78. data/spec/functional/client_spec.rb +0 -24
  79. data/spec/integration/.server_spec.rb.un~ +0 -0
  80. data/spec/integration/server_spec.rb +0 -31
@@ -0,0 +1 @@
1
+ deploy
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 1.9.3
data/Gemfile CHANGED
@@ -2,17 +2,28 @@ source :rubygems
2
2
  gemspec
3
3
 
4
4
  group :test do
5
- # Used for time functions
6
- gem 'activesupport', "~> 3.2.8"
7
-
5
+ gem 'thin'
8
6
  gem 'simplecov', '= 0.6.4'
9
7
 
10
- gem 'rspec'
11
- gem 'rack-test'
8
+ # Testing
9
+ gem "rspec", "~> 2.11.0"
10
+ gem 'rack-test', '~> 0.6.1'
12
11
  gem "fakefs", "~> 0.4.0"
13
12
  gem "delorean", "~> 2.0.0"
13
+ gem "factory_girl", "~> 4.0.0"
14
+ gem "webmock", "~> 1.8.9"
15
+ #gem "em-spec", ''
16
+
17
+ #
18
+ # Used for time functions
19
+ #
20
+ gem 'activesupport', "~> 3.2.8"
14
21
 
15
- gem 'redis'
16
- gem 'memcached'
22
+ #
23
+ # Database support testing
24
+ #
17
25
  gem 'mongo'
26
+ gem "mysql2", "~> 0.3.11"
27
+ gem 'sqlite3'
28
+ gem 'pg'
18
29
  end
@@ -0,0 +1,36 @@
1
+ $: << 'lib'
2
+
3
+ require 'bundler/setup'
4
+
5
+ ENV['RACK_ENV'] = ENV['ENV'] || 'test'
6
+ require 'khronos'
7
+
8
+ def load_migrations!
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
24
+
25
+ desc 'Destroy entire database.'
26
+ task :drop do
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
34
+
35
+ end
36
+
@@ -6,4 +6,5 @@ ENV['RACK_ENV'] ||= 'production'
6
6
  require 'readline'
7
7
  require 'khronos'
8
8
 
9
- run Khronos::Server
9
+
10
+ run Khronos::Server::Scheduler
data/config.ru CHANGED
@@ -4,4 +4,12 @@ require 'bundler/setup'
4
4
  require 'active_support/all'
5
5
  require 'khronos'
6
6
 
7
- run Khronos::Server
7
+ #controller = Khronos::Controller.new
8
+ #controller.start!
9
+
10
+ run Khronos::Server::Scheduler
11
+
12
+ # Note that this will block current thread.
13
+ #EventMachine.run {
14
+ #EventMachine.start_server "127.0.0.1", 8081, Khronos::Server::Runner
15
+ #}
@@ -1,11 +1,33 @@
1
1
  production:
2
- storage: redis://localhost:6379/0
2
+ storage: mongodb://localhost:27017/khronos
3
3
  interval: 5
4
+ controller:
5
+ interval: 1
6
+ runner:
7
+ host: 'localhost'
8
+ port: '7243'
9
+ scheduler:
10
+ host: 'localhost'
11
+ port: '8080'
4
12
 
5
13
  development:
6
- storage: redis://localhost:6379/0
7
- interval: 1
14
+ storage: mongodb://localhost:27017/khronos_development
15
+ controller:
16
+ interval: 1
17
+ runner:
18
+ host: 'localhost'
19
+ port: '7243'
20
+ scheduler:
21
+ host: 'localhost'
22
+ port: '8080'
8
23
 
9
24
  test:
10
- storage: redis://localhost:6379/10
11
- interval: 1
25
+ storage: sqlite3://localhost/db/test.db
26
+ controller:
27
+ interval: 1
28
+ runner:
29
+ host: 'localhost'
30
+ port: '7243'
31
+ scheduler:
32
+ host: 'localhost'
33
+ port: '8080'
@@ -8,17 +8,20 @@ Gem::Specification.new do |s|
8
8
  s.platform = Gem::Platform::RUBY
9
9
  s.authors = ["Endel Dreyer"]
10
10
  s.email = ["endel@ocapi.com.br"]
11
- s.homepage = "http://github.com/ocapi/scraper"
11
+ s.homepage = "http://github.com/ocapi/khronos"
12
12
 
13
13
  s.summary = "Ruby HTTP Job Scheduler Interface."
14
- s.description = "Advanced Cron replacement for the cloud."
14
+ s.description = "Ruby HTTP Job Scheduler Interface. An advanced Cron replacement for the cloud."
15
15
  s.licenses = ['MIT']
16
16
 
17
- s.add_dependency "sinatra", '~> 1.3.3'
18
- s.add_dependency 'mongoid', '~> 3.0.5'
19
- s.add_dependency 'activerecord', '~> 3.2.8'
20
- s.add_dependency 'json', '~> 1.7.5'
21
- s.add_dependency 'activesupport', '~> 3.2.8'
17
+ s.add_dependency "sinatra", "~> 1.3.3"
18
+ s.add_dependency "mongoid", "~> 3.0.5"
19
+ s.add_dependency "bson_ext", "~> 1.6.4"
20
+ s.add_dependency "activerecord", "~> 3.2.8"
21
+ s.add_dependency "json", "~> 1.7.5"
22
+ s.add_dependency "activesupport", "~> 3.2.8"
23
+ s.add_dependency "eventmachine", "~> 1.0.0.beta.4"
24
+ s.add_dependency "em-http-request", "~> 1.0.3"
22
25
 
23
26
  s.files = `git ls-files`.split("\n")
24
27
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -2,19 +2,16 @@ require 'rubygems'
2
2
  require 'bundler'
3
3
  require 'bundler/setup'
4
4
  require 'json'
5
+ require 'logger'
5
6
 
6
7
  module Khronos
7
8
  autoload :Config, 'khronos/config'
9
+ autoload :Logger, 'khronos/logger'
10
+ autoload :Scheduler, 'khronos/scheduler'
8
11
 
9
12
  autoload :Storage, 'khronos/storage'
10
13
  autoload :Server, 'khronos/server'
11
-
12
- autoload :Scheduler, 'khronos/scheduler'
13
- autoload :Runner, 'khronos/runner'
14
- autoload :Controller, 'khronos/controller'
15
14
  end
16
15
 
16
+ Khronos::Logger.setup!(Logger.new(STDOUT))
17
17
  Khronos::Config.instance.load!('config/environment.yml', ENV['RACK_ENV'])
18
-
19
- controller = Khronos::Controller.new
20
- controller.start!
@@ -24,5 +24,3 @@ module Khronos
24
24
  end
25
25
  end
26
26
  end
27
-
28
- Khronos::Config.instance.load('config.yml')
@@ -0,0 +1,16 @@
1
+ module Khronos
2
+ class Logger
3
+
4
+ class << self
5
+ def setup!(logger)
6
+ @logger = logger
7
+ end
8
+
9
+ def method_missing(method, *args, &block)
10
+ @logger.send(method, *args, &block)
11
+ end
12
+
13
+ end
14
+
15
+ end
16
+ end
@@ -1,12 +1,26 @@
1
- require 'observer'
1
+ require 'socket'
2
+ require 'json'
2
3
 
3
4
  module Khronos
4
5
  class Scheduler
5
- include Observable
6
+ module Methods
7
+ def run(schedule)
8
+ client = TCPSocket.new( Config.instance.runner['host'], Config.instance.runner['port'] )
9
+ client.puts( schedule.to_json )
6
10
 
7
- def run
11
+ while !(client.closed?) && (message = client.gets)
12
+ puts message.inspect
13
+ client.close
14
+ end
15
+ end
16
+
17
+ def fetch(target_time=Time.now)
18
+ Storage::Schedule.where(['at <= ?', target_time]).where(:active => true)
19
+ end
8
20
  end
9
21
 
22
+ include Methods
23
+ extend Methods
10
24
  end
11
25
  end
12
26
 
@@ -1,34 +1,7 @@
1
- require 'sinatra'
2
-
3
1
  module Khronos
4
- class Server < Sinatra::Base
5
- set :storage, Storage.new
6
-
7
- # Retrieves scheduling data from a context
8
- #
9
- # @return [Hash] JSON
10
- get '/' do
11
- context = retrieve_context!
12
- settings.storage.get(context).to_json
13
- end
14
-
15
- # Creates or updates a schedule from a context
16
- #
17
- # @param [String]
18
- #
19
- # @return [Hash] Context JSON status hash
20
- put '/' do
21
- context = retrieve_context!
22
- puts params.inspect
23
- context.to_json
24
- end
25
-
26
- private
27
-
28
- def retrieve_context!
29
- halt 400, "Missing 'context' on query string." unless params[:context]
30
- (params[:namespace].nil?) ? params[:context] : "#{params[:namespace]}:#{params[:context]}"
31
- end
32
-
2
+ module Server
3
+ autoload :Runner, 'khronos/server/runner'
4
+ autoload :Scheduler, 'khronos/server/scheduler'
5
+ autoload :Controller, 'khronos/server/controller'
33
6
  end
34
7
  end
@@ -0,0 +1,31 @@
1
+ module Khronos
2
+ module Server
3
+ class Controller
4
+ attr_reader :storage, :scheduler
5
+
6
+ def initialize
7
+ @storage = Storage.new
8
+ @scheduler = Scheduler.new
9
+ end
10
+
11
+ def logger=(logger)
12
+ puts "WARNING: Not implemented yet."
13
+ end
14
+
15
+ def start!
16
+ loop do
17
+ @scheduler.fetch(Time.now).each do |schedule|
18
+ schedule.update_attributes(:status => false)
19
+ schedule.save
20
+ @scheduler.run(schedule)
21
+ end
22
+
23
+ #
24
+ # Sleep 'interval' seconds
25
+ #
26
+ sleep(Config.instance.controller['interval'])
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,50 @@
1
+ require 'json'
2
+ require 'eventmachine'
3
+ require 'em-http'
4
+
5
+ module Khronos
6
+ module Server
7
+
8
+ class Runner < EventMachine::Connection
9
+ def post_init
10
+ puts "-- someone connected to the server!"
11
+ end
12
+
13
+ def receive_data json
14
+ schedule = JSON.parse(json)
15
+ send_data ">>> you sent: #{schedule.inspect}"
16
+
17
+ # Close connection with client immediatelly
18
+ close_connection
19
+
20
+ if (url = schedule['task_url'])
21
+ http = EventMachine::HttpRequest.new(url).get :redirects => 5
22
+ http.callback do
23
+ puts "#{url}\n#{http.response_header.status} - #{http.response.length} bytes\n"
24
+ puts http.response
25
+ end
26
+
27
+ http.errback do
28
+ puts "#{url}\n" + http.error
29
+ end
30
+
31
+ enqueue_recurrency!(schedule)
32
+ end
33
+
34
+ end
35
+
36
+ def enqueue_recurrency!(schedule)
37
+ url = "http://#{Config.instance.scheduler['host']}"
38
+ url += ":#{Config.instance.scheduler['port']}" if Config.instance.scheduler['port']
39
+ url += "/task?id=#{schedule['id']}"
40
+ EventMachine::HttpRequest.new(url).patch :redirects => 2
41
+ end
42
+
43
+ def unbind
44
+ puts "-- someone disconnected from the echo server!"
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,110 @@
1
+ require 'sinatra'
2
+
3
+ module Khronos
4
+ module Server
5
+
6
+ class Scheduler < Sinatra::Base
7
+ set :storage, Storage.new
8
+
9
+ # Creates a schedule
10
+ #
11
+ # @param [String] context application-level identifier
12
+ # @param [Integer] at timestamp which will run for the first time
13
+ # @param [Integer] recurrency next execution interval, in seconds
14
+ # @param [String] task_url url of the task that will run
15
+ # @param [String, JSON] callbacks callback urls (e.g. '{"success" : ... , "error" : ...}')
16
+ #
17
+ # @return [Hash] created schedule data
18
+ post '/task' do
19
+ Storage::Schedule.create(params).to_json
20
+ end
21
+
22
+ # Retrieves scheduling tasks from a context
23
+ #
24
+ # @param [Integer] id
25
+ # @param [String] context
26
+ #
27
+ # @return [Hash] JSON
28
+ get '/task' do
29
+ schedule = (!params.empty?) ? Storage::Schedule.where(params).first : nil
30
+ if schedule.nil?
31
+ # Requested task not found
32
+ 404
33
+ else
34
+ schedule.to_json
35
+ end
36
+ end
37
+
38
+ # Retrieve a list of scheduling tasks
39
+ get '/tasks' do
40
+ if Storage::Schedule.name =~ /ActiveRecord/
41
+ criteria = Storage::Schedule
42
+ params.each_pair do |field, value|
43
+ if field == 'context'
44
+ field = "#{field} LIKE ?"
45
+ else
46
+ field = "#{field} = ?"
47
+ end
48
+ criteria = criteria.where(field, value)
49
+ end
50
+ criteria.to_json
51
+ else
52
+ Storage::Schedule.where(params).to_json
53
+ end
54
+ end
55
+
56
+ # Creates or updates a schedule from a context
57
+ #
58
+ # @param [String]
59
+ #
60
+ # @return [Hash] Context JSON status hash
61
+ put '/task' do
62
+ schedule = Storage::Schedule.where({:id => params.delete(:id)}).first
63
+
64
+ # No schedule found for this params.
65
+ return {}.to_json unless schedule
66
+
67
+ schedule.update_attributes(params)
68
+ schedule.save
69
+
70
+ schedule.to_json
71
+ end
72
+
73
+ # Checks recurrency and reactivates a schedule if necessary
74
+ #
75
+ # @param [String] id
76
+ #
77
+ # @return [Hash] data
78
+ patch '/task' do
79
+ schedule = Storage::Schedule.where(params).first
80
+
81
+ # No schedule found for this params.
82
+ return {}.to_json unless schedule
83
+
84
+ # Is recurrency check requested
85
+ if schedule.recurrency > 0
86
+ schedule.at += schedule.recurrency
87
+ schedule.active = true
88
+ end
89
+ schedule.save
90
+
91
+ schedule.to_json
92
+ end
93
+
94
+ # Force a task to be scheduled right now
95
+ #
96
+ # @param [Integer] id
97
+ #
98
+ # @return [Hash] queued
99
+ post '/task/run' do
100
+ schedule = Storage::Schedule.where(:id => params[:id]).first
101
+ Khronos::Scheduler.run(schedule) if schedule
102
+ {:queued => !schedule.nil?}.to_json
103
+ end
104
+
105
+ # Log requests
106
+
107
+ end
108
+
109
+ end
110
+ end