simonmenke-capricorn 0.2.00

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/app_generators/engine/engine_generator.rb +39 -0
  2. data/app_generators/engine/templates/config/routes.rb +2 -0
  3. data/app_generators/engine/templates/init.rb +1 -0
  4. data/app_generators/engine/templates/lib/engine.rb +1 -0
  5. data/app_generators/engine/templates/rails/init.rb +1 -0
  6. data/bin/capricorn +20 -0
  7. data/lib/capricorn/actor/actions.rb +76 -0
  8. data/lib/capricorn/actor.rb +23 -0
  9. data/lib/capricorn/actors/apache_actor.rb +56 -0
  10. data/lib/capricorn/actors/base_actor.rb +276 -0
  11. data/lib/capricorn/actors/mysql_actor.rb +20 -0
  12. data/lib/capricorn/actors/passenger_actor.rb +19 -0
  13. data/lib/capricorn/actors/plesk_actor.rb +210 -0
  14. data/lib/capricorn/actors/sqlite3_actor.rb +44 -0
  15. data/lib/capricorn/app_runner.rb +119 -0
  16. data/lib/capricorn/apps/dev.rb +24 -0
  17. data/lib/capricorn/apps/engines.rb +33 -0
  18. data/lib/capricorn/apps/jobs.rb +35 -0
  19. data/lib/capricorn/apps/satellite.rb +32 -0
  20. data/lib/capricorn/apps/server.rb +67 -0
  21. data/lib/capricorn/client/auth_token.rb +98 -0
  22. data/lib/capricorn/client.rb +48 -0
  23. data/lib/capricorn/daemon.rb +71 -0
  24. data/lib/capricorn/exception_handler.rb +78 -0
  25. data/lib/capricorn/extentions/rubygems_plugin.rb +27 -0
  26. data/lib/capricorn/extentions/thor_extentions.rb +32 -0
  27. data/lib/capricorn/job_queue.rb +199 -0
  28. data/lib/capricorn/satellite/actions.rb +35 -0
  29. data/lib/capricorn/satellite/dependency_loader.rb +78 -0
  30. data/lib/capricorn/satellite/persistence.rb +50 -0
  31. data/lib/capricorn/satellite.rb +49 -0
  32. data/lib/capricorn/server/daemon.rb +88 -0
  33. data/lib/capricorn/server/proxy.rb +25 -0
  34. data/lib/capricorn/server/security.rb +113 -0
  35. data/lib/capricorn/server.rb +113 -0
  36. data/lib/capricorn/system/config.rb +49 -0
  37. data/lib/capricorn/system/helper.rb +21 -0
  38. data/lib/capricorn/system/options.rb +79 -0
  39. data/lib/capricorn/system/process_user.rb +73 -0
  40. data/lib/capricorn/system/satellites.rb +44 -0
  41. data/lib/capricorn/system/shell.rb +80 -0
  42. data/lib/capricorn/system.rb +159 -0
  43. data/lib/capricorn.rb +100 -0
  44. data/lib/rubygems_plugin.rb +1 -0
  45. data/spec/actor/actions_spec.rb +13 -0
  46. data/spec/spec_helper.rb +1 -0
  47. metadata +99 -0
@@ -0,0 +1,48 @@
1
+ require 'drb'
2
+ require 'drb/ssl'
3
+ require 'uri'
4
+
5
+ module Capricorn
6
+ class Client
7
+
8
+ autoload :AuthToken, File.dirname(__FILE__)+'/client/auth_token'
9
+
10
+ # return a DRb uri for the given Capricorn uri.
11
+ def self.parse_uri(uri)
12
+ uri = URI.parse(uri)
13
+ use_ssl = (uri.scheme == 'ssl+capricorn')
14
+ uri.scheme = 'druby'
15
+ return use_ssl, uri.to_s
16
+ end
17
+
18
+ # return an potentialy initialize the client to the given token.
19
+ def self.current(token=nil)
20
+ @client = connect(token) unless @client
21
+ @client
22
+ end
23
+
24
+ # connect to the server referenced by the given token.
25
+ def self.connect(token=nil)
26
+ token ||= 'core.token'
27
+
28
+ [Capricorn::DEFAULT_ROOT_SYSTEM_DIR,
29
+ Capricorn::DEFAULT_USER_SYSTEM_DIR,
30
+ File.join(Capricorn::DEFAULT_USER_SYSTEM_DIR, 'tokens'),
31
+ '.'].each do |path|
32
+ path = File.expand_path(File.join(path, token))
33
+ if File.file? path
34
+ token = path
35
+ break
36
+ end
37
+ end
38
+
39
+ unless File.file? token
40
+ raise "Unable to read the token at: #{token}"
41
+ end
42
+
43
+ token = Capricorn::Client::AuthToken.load_file(token) if String === token
44
+ token.connect if token
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,71 @@
1
+
2
+ module Capricorn
3
+ module Daemon
4
+ class Base
5
+
6
+ def self.daemonize(&block)
7
+ Capricorn::Daemon::Controller.start(self,&block)
8
+ end
9
+
10
+ end
11
+
12
+ module PidFile
13
+ def self.store(pid)
14
+ File.open(self.pid_file, 'w') {|f| f << pid}
15
+ end
16
+
17
+ def self.recall
18
+ IO.read(self.pid_file).to_i rescue nil
19
+ end
20
+
21
+ def self.destroy
22
+ FileUtils.rm(self.pid_file) if self.exist?
23
+ end
24
+
25
+ def self.pid_file
26
+ Capricorn.system.path("Server.pid")
27
+ end
28
+
29
+ def self.exist?
30
+ File.file?(self.pid_file)
31
+ end
32
+ end
33
+
34
+ module Controller
35
+
36
+ def self.start(daemon, &block)
37
+ fork do
38
+ Process.setsid
39
+ exit if fork
40
+ if PidFile.exist?
41
+ puts "Pid file #{PidFile.pid_file} already exists. Not starting."
42
+ exit 1
43
+ end
44
+ Capricorn::Daemon::PidFile.store(Process.pid)
45
+ Dir.chdir Capricorn.system.root
46
+ File.umask 0000
47
+
48
+ Capricorn::ExceptionHandler.redirect_std
49
+
50
+ trap("TERM") { daemon.stop; exit }
51
+ at_exit { Capricorn::Daemon::PidFile.destroy if $master }
52
+ at_exit(&block) if block
53
+ daemon.start
54
+ end
55
+ puts "Daemon started."
56
+ end
57
+
58
+ def self.stop
59
+ if !Capricorn::Daemon::PidFile.exist?
60
+ puts "Pid file not found. Is the daemon started?"
61
+ exit
62
+ end
63
+ pid = Capricorn::Daemon::PidFile.recall
64
+ pid && Process.kill("TERM", pid)
65
+ rescue Errno::ESRCH
66
+ puts "Pid file found, but process was not running. The daemon may have died."
67
+ end
68
+
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,78 @@
1
+
2
+ module Capricorn
3
+ module ExceptionHandler
4
+
5
+ def self.setup(out=STDOUT, err=STDERR)
6
+ if String === out
7
+ @out = Logger.new(out, 'daily')
8
+ @stdout = @out.instance_variable_get('@logdev').instance_variable_get('@dev')
9
+ else
10
+ @out = Logger.new(out)
11
+ @stdout = out
12
+ end
13
+
14
+ if String === err
15
+ @err = Logger.new(err, 'daily')
16
+ @stderr = @err.instance_variable_get('@logdev').instance_variable_get('@dev')
17
+ else
18
+ @err = Logger.new(err)
19
+ @stderr = err
20
+ end
21
+
22
+ @out.level = Logger::DEBUG
23
+ @err.level = Logger::DEBUG
24
+ end
25
+
26
+ def self.err
27
+ @err
28
+ end
29
+
30
+ def self.out
31
+ @out
32
+ end
33
+
34
+ def self.stderr
35
+ @stderr
36
+ end
37
+
38
+ def self.stdout
39
+ @stdout
40
+ end
41
+
42
+ def self.redirect_std
43
+ if STDOUT != self.stdout
44
+ STDOUT.reopen self.stdout
45
+ end
46
+
47
+ if STDERR != self.stderr
48
+ STDERR.reopen self.stderr
49
+ end
50
+
51
+ STDIN.reopen "/dev/null"
52
+ end
53
+
54
+ def logger
55
+ Capricorn::ExceptionHandler
56
+ end
57
+
58
+ def log(*args, &block)
59
+ logger.out.info(*args, &block)
60
+ end
61
+
62
+ def report
63
+ yield
64
+ rescue Exception => e
65
+ if StandardError === e
66
+ logger.err.error(e)
67
+ else
68
+ logger.err.fatal(e)
69
+ end
70
+ raise e
71
+ end
72
+
73
+ end
74
+ end
75
+
76
+ def FileUtils.fu_output_message(msg)
77
+ Capricorn::ExceptionHandler.out.info(msg)
78
+ end
@@ -0,0 +1,27 @@
1
+ require 'rubygems/specification'
2
+
3
+ module Gem # :nodoc:
4
+ class Specification
5
+ attribute :engine_dependencies, {}
6
+
7
+ def add_engine_dependency(name, options={})
8
+ name = name.to_s
9
+ add_runtime_dependency(name, *[options[:version]].compact)
10
+ @engine_dependencies ||= {}
11
+ @engine_dependencies[name] = options
12
+ end
13
+
14
+ alias_method :ruby_code_without_engines, :ruby_code # :nodoc:
15
+ def ruby_code(obj) # :nodoc:
16
+ return obj.inspect if Hash === obj
17
+ return ruby_code_without_engines(obj)
18
+ end
19
+
20
+ alias_method :to_ruby_without_engines, :to_ruby # :nodoc:
21
+ def to_ruby # :nodoc:
22
+ code = to_ruby_without_engines
23
+ code.gsub! /s\.engine_dependencies\s+[=]([^\n]+)/, 's.instance_variable_set(:@engine_dependencies,\1)'
24
+ code
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,32 @@
1
+ Capricorn.runtime_gem('thor', Capricorn::THOR_VERSION)
2
+
3
+ class Thor
4
+ class << self
5
+ attr_accessor :real_namespace # :nodoc:
6
+ end
7
+ def self.namespace=(value)
8
+ Thor.real_namespace = value
9
+ end
10
+ def self.namespace
11
+ Thor.real_namespace
12
+ end
13
+ self.namespace = nil
14
+ end
15
+
16
+ module Thor::Util # :nodoc:
17
+ class << self
18
+ alias_method :old_constant_to_thor_path, :constant_to_thor_path
19
+ alias_method :old_constant_from_thor_path, :constant_from_thor_path
20
+ end
21
+
22
+ def self.constant_to_thor_path(*args) # :nodoc:
23
+ path = old_constant_to_thor_path(*args)
24
+ path.sub! /^#{Thor.namespace}:/, '' if Thor.namespace
25
+ path
26
+ end
27
+
28
+ def self.constant_from_thor_path(path) # :nodoc:
29
+ path = "#{Thor.namespace}:"+path if Thor.namespace
30
+ old_constant_from_thor_path(path)
31
+ end
32
+ end
@@ -0,0 +1,199 @@
1
+ require 'thread'
2
+
3
+ module Capricorn
4
+ class JobQueue
5
+ include DRbUndumped
6
+
7
+ # create a new job queue
8
+ def initialize
9
+ @immediated_jobs = Array.new
10
+ @canceled_jobs = Array.new
11
+ @job_queue = Array.new
12
+ @jobs = Hash.new
13
+ @mutex = Mutex.new
14
+ @next_id = 1
15
+
16
+ @worker = Thread.new(self) do |job_queue|
17
+ while job_queue.running? or job_queue.peek
18
+
19
+ if job = job_queue.peek
20
+ job.run(job_queue)
21
+ job_queue.delete(job.id)
22
+ else
23
+ sleep(1)
24
+ end
25
+
26
+ end
27
+ end
28
+ end
29
+
30
+ # enqueue a new job with the given +name+, +options+ and +proc+
31
+ def enqueue(name, options={}, &proc)
32
+ @mutex.synchronize do
33
+ job = Job.new(@next_id, name, options, &proc)
34
+ @next_id += 1
35
+ @jobs[job.id] = job
36
+ @job_queue.push job.id
37
+ return job.id
38
+ end
39
+ end
40
+
41
+ # dequeue the next job of the queue.
42
+ def dequeue
43
+ @mutex.synchronize do
44
+ id = @job_queue.shift
45
+ return @jobs.delete(id) if id
46
+ end
47
+ end
48
+
49
+ # delete the job associated with the given +id+.
50
+ def delete(id)
51
+ @mutex.synchronize do
52
+ id = @job_queue.delete(id)
53
+ return @jobs.delete(id) if id
54
+ end
55
+ end
56
+
57
+ # peek at the next job in the queue
58
+ def peek
59
+ job = nil
60
+ @mutex.synchronize do
61
+ id = @job_queue.first
62
+ job = @jobs[id] if id
63
+ end
64
+ job
65
+ end
66
+
67
+ # get the size of the job queue
68
+ def size
69
+ @mutex.synchronize do
70
+ @job_queue.size
71
+ end
72
+ end
73
+
74
+ # cancel the job associated with the given +id+.
75
+ def cancel(id)
76
+ @mutex.synchronize do
77
+ id = @job_queue.delete(id)
78
+ if id
79
+ @jobs.delete(id)
80
+ @canceled_jobs.push(id)
81
+ end
82
+ end
83
+ end
84
+
85
+ # is the job associated with the given +id+ canceled.
86
+ def canceled?(id)
87
+ @mutex.synchronize do
88
+ return !@canceled_jobs.delete(id).nil?
89
+ end
90
+ end
91
+
92
+ # run the job associated with the given +id+ immediately.
93
+ def immediate(id)
94
+ @mutex.synchronize do
95
+ @immediated_jobs.push(id) if @jobs[id]
96
+ end
97
+ end
98
+
99
+ # should the job associated with the given +id+ be run immediately.
100
+ def immediated?(id)
101
+ @mutex.synchronize do
102
+ return !@immediated_jobs.delete(id).nil?
103
+ end
104
+ end
105
+
106
+ # join the worker thread
107
+ def join!
108
+ @worker.join
109
+ end
110
+
111
+ # wait until the queue is empty then stop the worker
112
+ def stop!
113
+ @mutex.synchronize do
114
+ @stopped = true
115
+ end
116
+ join!
117
+ end
118
+
119
+ # is the queue stopping or stopped?
120
+ def stopped?
121
+ @mutex.synchronize do
122
+ return !!@stopped
123
+ end
124
+ end
125
+
126
+ # is the queue running
127
+ def running?
128
+ !stopped?
129
+ end
130
+
131
+ # iterate through all the jobs on the queue
132
+ def each
133
+ @mutex.synchronize do
134
+ @job_queue.each do |id|
135
+ job = @jobs[id]
136
+ canceled = @canceled_jobs.include?(id)
137
+ immediated = @immediated_jobs.include?(id)
138
+
139
+ yield(job, canceled, immediated)
140
+ end
141
+ end
142
+ end
143
+
144
+ class Job
145
+ include DRbUndumped
146
+
147
+ attr_accessor :id, :name, :options, :proc
148
+
149
+ def initialize(id, name, options={}, &proc)
150
+ @id = id.to_i
151
+ @mutex = Mutex.new
152
+ @name, @options, @proc = name, options, proc
153
+ @run_at = Time.now + (options.delete(:delay) || 30)
154
+ end
155
+
156
+ def delay
157
+ @mutex.synchronize do
158
+ delay = @run_at - Time.now
159
+ delay = 0 if delay < 0
160
+ return delay
161
+ end
162
+ end
163
+
164
+ def running?
165
+ @mutex.synchronize do
166
+ return @running
167
+ end
168
+ end
169
+
170
+ def waiting?
171
+ @mutex.synchronize do
172
+ return @waiting
173
+ end
174
+ end
175
+
176
+ def run(job_queue)
177
+ Capricorn.report do
178
+ @waiting = true
179
+ immediated = canceled = false
180
+ Capricorn.log "waiting #{@run_at - Time.now}s."
181
+ until immediated or canceled or @run_at <= Time.now
182
+ sleep(1)
183
+ canceled = job_queue.canceled?(self.id)
184
+ immediated = job_queue.immediated?(self.id)
185
+ end
186
+
187
+ unless canceled
188
+ @waiting = false
189
+ @running = true
190
+ Capricorn.log("[queue]> #{@name}")
191
+ @proc.call(@options)
192
+ end
193
+ end
194
+ end
195
+
196
+ end
197
+
198
+ end
199
+ end
@@ -0,0 +1,35 @@
1
+
2
+ module Capricorn
3
+ class Satellite
4
+ module Actions
5
+
6
+ def add_engine(name, options={})
7
+ unless @engines.key? name
8
+ @engines[name] = options
9
+ true
10
+ else
11
+ false
12
+ end
13
+ end
14
+
15
+ def update_engine(name, options={})
16
+ if @engines.key? name
17
+ @engines[name] = options
18
+ true
19
+ else
20
+ false
21
+ end
22
+ end
23
+
24
+ def remove_engine(name)
25
+ if @engines.key? name
26
+ @engines.delete(name)
27
+ true
28
+ else
29
+ false
30
+ end
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,78 @@
1
+
2
+
3
+ module Capricorn
4
+ class Satellite
5
+ class DependencyLoader
6
+
7
+ attr_reader :names, :specs, :engines
8
+
9
+ def self.load_for(engines)
10
+ dependency_loader = self.new(engines)
11
+ dependency_loader.add_dependecies!
12
+ dependency_loader.order_by_dependecies!
13
+
14
+ return dependency_loader
15
+ end
16
+
17
+ def initialize(engines)
18
+ specs = engines.collect do |k,r|
19
+ s = Gem.source_index.find_name(k, Gem::Requirement.new(r[:version] || ">= 0.0.0"))
20
+ s.last
21
+ end.compact
22
+
23
+ @names = specs.collect { |spec| spec.name }
24
+ @specs = specs.inject({}) { |h, spec| h[spec.name] = spec ; h }
25
+ @engines = engines
26
+ end
27
+
28
+ def add_dependecies!
29
+ @specs.values.each do |spec|
30
+ add_dependecies_for spec
31
+ end
32
+ end
33
+
34
+ def order_by_dependecies!
35
+ @names = tsort
36
+ end
37
+
38
+ def each
39
+ @names.each do |name|
40
+ yield(@specs[name])
41
+ end
42
+ end
43
+
44
+ def reverse_each
45
+ @names.reverse.each do |name|
46
+ yield(@specs[name])
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ include TSort
53
+
54
+ def add_dependecies_for(spec)
55
+ engine_dependencies = spec.engine_dependencies || {}
56
+ engine_dependencies.each do |name, options|
57
+ gems = Gem.source_index.find_name(name, [options[:version]].compact)
58
+ next unless gem = gems.last
59
+ next if @names.include?(name) and gem.version <= @specs[gem.name].version
60
+ @specs[gem.name] = gem
61
+ @names.push(gem.name)
62
+ add_dependecies_for(gem)
63
+ end
64
+ @engines = @engines.merge(engine_dependencies)
65
+ end
66
+
67
+ def tsort_each_node(&block)
68
+ @names.each(&block)
69
+ end
70
+
71
+ def tsort_each_child(node, &block)
72
+ engine_dependencies = @specs[node].engine_dependencies || {}
73
+ engine_dependencies.keys.each(&block)
74
+ end
75
+
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,50 @@
1
+ require 'yaml'
2
+
3
+ module Capricorn
4
+ class Satellite
5
+ module Persistence
6
+
7
+ def self.included(base)
8
+ base.extend Capricorn::Satellite::Persistence::ClassMethods
9
+ end
10
+
11
+ module ClassMethods
12
+
13
+ def load(data)
14
+ Capricorn::Satellite.new(YAML.load(data))
15
+ end
16
+
17
+ def load_file(path)
18
+ return nil unless File.exist?(path)
19
+ Capricorn::Satellite.new(YAML.load_file(path))
20
+ end
21
+
22
+ end
23
+
24
+ def dump(io=nil)
25
+ data = {}
26
+
27
+ private_vars = %w( basedomain subdomain )
28
+ instance_variables.each do |ivar_name|
29
+ ivar_name = ivar_name.to_s
30
+ ivar_name =~ /^@(.+)$/
31
+ name = $1
32
+ unless private_vars.include? name
33
+ data[name] = instance_variable_get(ivar_name.to_sym)
34
+ end
35
+ end
36
+
37
+ if io
38
+ io.write YAML.dump(data)
39
+ else
40
+ YAML.dump(data)
41
+ end
42
+ end
43
+
44
+ def dump_file(path)
45
+ File.open(path, 'w+') { |f| dump(f) }
46
+ end
47
+
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,49 @@
1
+
2
+ module Capricorn
3
+ class Satellite
4
+
5
+ autoload :Actions, File.dirname(__FILE__)+'/satellite/actions'
6
+ autoload :Persistence, File.dirname(__FILE__)+'/satellite/persistence'
7
+ autoload :DependencyLoader, File.dirname(__FILE__)+'/satellite/dependency_loader'
8
+
9
+ include Capricorn::Satellite::Actions
10
+ include Capricorn::Satellite::Persistence
11
+
12
+ attr_reader :domain, :engines
13
+
14
+ def initialize(domain)
15
+ if Hash === domain
16
+ domain.each do |name, value|
17
+ instance_variable_set("@#{name}".to_sym, value)
18
+ end
19
+ else
20
+ @domain = domain
21
+ @engines = {}
22
+ end
23
+ @domain.gsub! /^www\./, ''
24
+ end
25
+
26
+ def basedomain
27
+ unless @basedomain
28
+ parts = self.domain.split('.')
29
+ parts = parts[-2..-1]
30
+ @basedomain = parts.join('.')
31
+ end
32
+ @basedomain
33
+ end
34
+
35
+ def subdomain
36
+ unless @subdomain
37
+ parts = self.domain.split('.')
38
+ parts = parts[0..-3]
39
+ @subdomain = parts.join('.')
40
+ end
41
+ @subdomain unless @subdomain == ''
42
+ end
43
+
44
+ def subdomain?
45
+ !self.subdomain.nil?
46
+ end
47
+
48
+ end
49
+ end