khronos 0.0.0 → 0.0.1.pre1

Sign up to get free protection for your applications and to get access to all the features.
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