capricorn 0.2.00

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 (52) hide show
  1. data/app_generators/engine/engine_generator.rb +39 -0
  2. data/app_generators/engine/templates/Gmfile +20 -0
  3. data/app_generators/engine/templates/MIT-LICENSE.txt +20 -0
  4. data/app_generators/engine/templates/README.rdoc +7 -0
  5. data/app_generators/engine/templates/config/routes.rb +2 -0
  6. data/app_generators/engine/templates/gitignore +3 -0
  7. data/app_generators/engine/templates/init.rb +1 -0
  8. data/app_generators/engine/templates/lib/engine.rb +1 -0
  9. data/app_generators/engine/templates/rails/init.rb +1 -0
  10. data/app_generators/engine/templates/tasks/engine_tasks.rake +4 -0
  11. data/bin/capricorn +20 -0
  12. data/lib/capricorn.rb +100 -0
  13. data/lib/capricorn/actor.rb +23 -0
  14. data/lib/capricorn/actor/actions.rb +76 -0
  15. data/lib/capricorn/actors/apache_actor.rb +56 -0
  16. data/lib/capricorn/actors/base_actor.rb +276 -0
  17. data/lib/capricorn/actors/mysql_actor.rb +20 -0
  18. data/lib/capricorn/actors/passenger_actor.rb +19 -0
  19. data/lib/capricorn/actors/plesk_actor.rb +210 -0
  20. data/lib/capricorn/actors/sqlite3_actor.rb +44 -0
  21. data/lib/capricorn/app_runner.rb +119 -0
  22. data/lib/capricorn/apps/dev.rb +15 -0
  23. data/lib/capricorn/apps/engines.rb +33 -0
  24. data/lib/capricorn/apps/jobs.rb +35 -0
  25. data/lib/capricorn/apps/satellite.rb +32 -0
  26. data/lib/capricorn/apps/server.rb +67 -0
  27. data/lib/capricorn/client.rb +48 -0
  28. data/lib/capricorn/client/auth_token.rb +98 -0
  29. data/lib/capricorn/daemon.rb +71 -0
  30. data/lib/capricorn/exception_handler.rb +79 -0
  31. data/lib/capricorn/extentions/rubygems_plugin.rb +27 -0
  32. data/lib/capricorn/extentions/thor_extentions.rb +32 -0
  33. data/lib/capricorn/job_queue.rb +199 -0
  34. data/lib/capricorn/satellite.rb +50 -0
  35. data/lib/capricorn/satellite/actions.rb +35 -0
  36. data/lib/capricorn/satellite/dependency_loader.rb +78 -0
  37. data/lib/capricorn/satellite/persistence.rb +50 -0
  38. data/lib/capricorn/server.rb +122 -0
  39. data/lib/capricorn/server/daemon.rb +88 -0
  40. data/lib/capricorn/server/proxy.rb +25 -0
  41. data/lib/capricorn/server/security.rb +113 -0
  42. data/lib/capricorn/system.rb +184 -0
  43. data/lib/capricorn/system/config.rb +49 -0
  44. data/lib/capricorn/system/helper.rb +21 -0
  45. data/lib/capricorn/system/options.rb +79 -0
  46. data/lib/capricorn/system/process_user.rb +73 -0
  47. data/lib/capricorn/system/satellites.rb +44 -0
  48. data/lib/capricorn/system/shell.rb +80 -0
  49. data/lib/rubygems_plugin.rb +1 -0
  50. data/spec/actor/actions_spec.rb +13 -0
  51. data/spec/spec_helper.rb +1 -0
  52. metadata +108 -0
@@ -0,0 +1,35 @@
1
+ Capricorn.runtime_gem('thor', Capricorn::THOR_VERSION)
2
+
3
+ module Capricorn
4
+ module Apps
5
+ class Jobs < Thor
6
+
7
+ desc "list", 'list the jobs in the queue'
8
+ method_options :token => :optional
9
+ def list
10
+ queued_jobs = Capricorn.client(options[:token]).queued_jobs
11
+ queued_jobs.each do |id, name, canceled, immediated, running, waiting, delay|
12
+ status = []
13
+ status.push canceled ? 'c' : ' '
14
+ status.push immediated ? 'i' : ' '
15
+ status.push running ? 'r' : ' '
16
+ status.push waiting ? 'w' : ' '
17
+ puts("% 8d % 8d % 8s %s" % [id, delay.to_i, status.join, name])
18
+ end
19
+ end
20
+
21
+ desc "cancel ID", 'cancel the job with ID'
22
+ method_options :token => :optional
23
+ def cancel(id)
24
+ Capricorn.client(options[:token]).cancel_job(id.to_i)
25
+ end
26
+
27
+ desc "immediate ID", 'immediately run the job with ID'
28
+ method_options :token => :optional
29
+ def immediate(id)
30
+ Capricorn.client(options[:token]).immediate_job(id.to_i)
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,32 @@
1
+ Capricorn.runtime_gem('thor', Capricorn::THOR_VERSION)
2
+
3
+ module Capricorn
4
+ module Apps # :nodoc:
5
+
6
+ class Satellite < Thor
7
+ desc 'list', 'show all managed satellites'
8
+ method_options :token => :optional
9
+ def list
10
+ Capricorn.client(options[:token]).satellites.each do |sat|
11
+ puts sat.domain
12
+ sat.engines.each do |name, options|
13
+ puts "- #{name} #{options.inspect}"
14
+ end
15
+ end
16
+ end
17
+
18
+ desc 'install DOMAIN', 'install a satellite'
19
+ method_options :token => :optional
20
+ def install(domain)
21
+ Capricorn.client(options[:token]).install_satellite(domain)
22
+ end
23
+
24
+ desc 'uninstall DOMAIN', 'uninstall a satellite'
25
+ method_options :token => :optional
26
+ def uninstall(domain)
27
+ Capricorn.client(options[:token]).uninstall_satellite(domain)
28
+ end
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,67 @@
1
+ Capricorn.runtime_gem('thor', Capricorn::THOR_VERSION)
2
+
3
+ module Capricorn
4
+ module Apps
5
+
6
+ class Server < Thor
7
+
8
+ desc "start", 'start the server'
9
+ method_options :foreground => :boolean, :config => :optional
10
+ def start
11
+ Capricorn.server? true
12
+ Capricorn::System.load!(options[:root_path])
13
+
14
+ begin
15
+ FileUtils.mkdir_p(Capricorn.system.root)
16
+ rescue Errno::EACCES
17
+ Capricorn.logger.out.fatal "must be executed as root"
18
+ exit(1)
19
+ end
20
+
21
+ unless Capricorn.system.is_user('root')
22
+ Capricorn.logger.out.fatal "must be executed as root"
23
+ exit(1)
24
+ end
25
+
26
+ if options[:foreground]
27
+ Capricorn::Server.start
28
+ else
29
+ Capricorn::Server.daemonize
30
+ end
31
+ end
32
+
33
+ desc "stop", 'stop the server'
34
+ method_options :token => :optional
35
+ def stop
36
+ Capricorn.client(options[:token]).stop_server
37
+ end
38
+
39
+ desc "restart", 'restart the server'
40
+ method_options :token => :optional
41
+ def restart
42
+ Capricorn.client(options[:token]).restart_server
43
+ end
44
+
45
+ desc "reload", 'reload the server'
46
+ method_options :token => :optional
47
+ def reload
48
+ Capricorn.client(options[:token]).reload_server
49
+ end
50
+
51
+ desc "update", 'update the capricorn'
52
+ method_options :token => :optional
53
+ def update
54
+ Capricorn.client(options[:token]).update_server
55
+ end
56
+
57
+ desc "version", 'version of the server'
58
+ method_options :token => :optional
59
+ def version
60
+ puts "Client: #{Capricorn.version}"
61
+ puts "Server: #{Capricorn.client(options[:token]).server_version}"
62
+ end
63
+
64
+ end
65
+
66
+ end
67
+ end
@@ -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,98 @@
1
+ require 'yaml'
2
+
3
+ module Capricorn
4
+ class Client
5
+ class AuthToken
6
+
7
+ # load a token from the passed IO.
8
+ def self.load(io)
9
+ self.new YAML.load(io)
10
+ end
11
+
12
+ # load a token from a file referenced by the given +path+.
13
+ def self.load_file(path)
14
+ self.new YAML.load_file(path)
15
+ end
16
+
17
+ # the uri at which the capricorn server can be accessed.
18
+ attr_reader :target_uri
19
+ # the SSL verification mode used by the capricorn server
20
+ attr_reader :verify_mode
21
+ # the optional CA certificate used by the capricorn server
22
+ attr_reader :ca_certificate_data
23
+ # the private key used by the client
24
+ attr_reader :private_key_data
25
+ # the certificate used by the client
26
+ attr_reader :certificate_data
27
+
28
+ # create a new token from the given options
29
+ #
30
+ # +:target_uri+, +:verify_mode+, +:ca_certificate_data+, +:private_key_data+, +:certificate_data+
31
+ def initialize(options={})
32
+ @target_uri = options[:target_uri]
33
+ @verify_mode = options[:verify_mode]
34
+ @ca_certificate_data = options[:ca_certificate_data]
35
+ @private_key_data = options[:private_key_data]
36
+ @certificate_data = options[:certificate_data]
37
+ end
38
+
39
+ # get the parsed and initialized OpenSSL::X509::Certificate
40
+ def ca_certificate
41
+ @ca_certificate ||= OpenSSL::X509::Certificate.new(@ca_certificate_data)
42
+ end
43
+
44
+ # get the parsed and initialized OpenSSL::X509::Certificate
45
+ def certificate
46
+ @certificate ||= OpenSSL::X509::Certificate.new(@certificate_data)
47
+ end
48
+
49
+ # get the parsed and initialized OpenSSL::PKey::RSA
50
+ def private_key
51
+ @private_key ||= OpenSSL::PKey::RSA.new(@private_key_data)
52
+ end
53
+
54
+ # connect to the server and return the server handle.
55
+ def connect
56
+ use_ssl, uri = Capricorn::Client.parse_uri(self.target_uri)
57
+ if use_ssl
58
+ DRb.start_service nil, nil, self.options_for_drb
59
+ else
60
+ DRb.start_service
61
+ end
62
+ DRbObject.new nil, uri
63
+ end
64
+
65
+ # return options for use with DRb
66
+ def options_for_drb
67
+ @options_for_drb ||= {
68
+ :SSLVerifyMode => self.verify_mode,
69
+ :SSLCACertificate => self.ca_certificate,
70
+ :SSLPrivateKey => self.private_key,
71
+ :SSLCertificate => self.certificate
72
+ }
73
+ end
74
+
75
+ # dump this token to the given IO or return the content as a String
76
+ def dump(io=nil)
77
+ data = {
78
+ :target_uri => self.target_uri,
79
+ :verify_mode => self.verify_mode,
80
+ :ca_certificate_data => self.ca_certificate_data,
81
+ :private_key_data => self.private_key_data,
82
+ :certificate_data => self.certificate_data
83
+ }
84
+ if io
85
+ io.write YAML.dump(data)
86
+ else
87
+ YAML.dump(data)
88
+ end
89
+ end
90
+
91
+ # dump this token to a file at the given +path+.
92
+ def dump_file(path)
93
+ File.open(path, 'w+') { |f| dump(f) }
94
+ end
95
+
96
+ end
97
+ end
98
+ 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,79 @@
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
+ args = [args.inspect] if args.size == 1 and Array === args.first
60
+ logger.out.info(*args, &block)
61
+ end
62
+
63
+ def report
64
+ yield
65
+ rescue Exception => e
66
+ if StandardError === e
67
+ logger.err.error(e)
68
+ else
69
+ logger.err.fatal(e)
70
+ end
71
+ raise e
72
+ end
73
+
74
+ end
75
+ end
76
+
77
+ def FileUtils.fu_output_message(msg)
78
+ Capricorn::ExceptionHandler.out.info(msg)
79
+ 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