simonmenke-shuttle 0.1.07

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 (48) 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/shuttle +20 -0
  7. data/lib/rubygems_plugin.rb +1 -0
  8. data/lib/shuttle/actor/actions.rb +76 -0
  9. data/lib/shuttle/actor.rb +23 -0
  10. data/lib/shuttle/actors/apache_actor.rb +56 -0
  11. data/lib/shuttle/actors/base_actor.rb +276 -0
  12. data/lib/shuttle/actors/mysql_actor.rb +20 -0
  13. data/lib/shuttle/actors/passenger_actor.rb +19 -0
  14. data/lib/shuttle/actors/plesk_actor.rb +210 -0
  15. data/lib/shuttle/actors/sqlite3_actor.rb +44 -0
  16. data/lib/shuttle/app_runner.rb +118 -0
  17. data/lib/shuttle/apps/dev.rb +27 -0
  18. data/lib/shuttle/apps/engines.rb +33 -0
  19. data/lib/shuttle/apps/jobs.rb +35 -0
  20. data/lib/shuttle/apps/satellite.rb +32 -0
  21. data/lib/shuttle/apps/server.rb +70 -0
  22. data/lib/shuttle/client/auth_token.rb +98 -0
  23. data/lib/shuttle/client.rb +48 -0
  24. data/lib/shuttle/exception_handler.rb +53 -0
  25. data/lib/shuttle/extentions/rubygems_plugin.rb +27 -0
  26. data/lib/shuttle/extentions/thor_extentions.rb +32 -0
  27. data/lib/shuttle/job_queue.rb +199 -0
  28. data/lib/shuttle/satellite/actions.rb +35 -0
  29. data/lib/shuttle/satellite/dependency_loader.rb +79 -0
  30. data/lib/shuttle/satellite/persistence.rb +50 -0
  31. data/lib/shuttle/satellite.rb +49 -0
  32. data/lib/shuttle/server/daemon.rb +85 -0
  33. data/lib/shuttle/server/proxy.rb +25 -0
  34. data/lib/shuttle/server/security.rb +113 -0
  35. data/lib/shuttle/server.rb +113 -0
  36. data/lib/shuttle/system/config.rb +36 -0
  37. data/lib/shuttle/system/helper.rb +21 -0
  38. data/lib/shuttle/system/options.rb +79 -0
  39. data/lib/shuttle/system/process_user.rb +75 -0
  40. data/lib/shuttle/system/satellites.rb +44 -0
  41. data/lib/shuttle/system/shell.rb +80 -0
  42. data/lib/shuttle/system.rb +159 -0
  43. data/lib/shuttle/systems/centos_plesk_system.rb +28 -0
  44. data/lib/shuttle/systems/macports_system.rb +14 -0
  45. data/lib/shuttle.rb +82 -0
  46. data/spec/actor/actions_spec.rb +13 -0
  47. data/spec/spec_helper.rb +1 -0
  48. metadata +129 -0
@@ -0,0 +1,113 @@
1
+
2
+ module Shuttle
3
+ class Server
4
+ module Security # :nodoc:
5
+
6
+ def self.included(base)
7
+ base.extend Shuttle::Server::Security::ClassMethods
8
+ end
9
+
10
+ module ClassMethods
11
+
12
+ def options_for_server
13
+
14
+ config = {}
15
+ if Shuttle.system.use_ssl?
16
+ install_quick_cert!
17
+ dump_quick_cert_config!
18
+ run_quick_cert!
19
+
20
+ keypair = Shuttle.system.path('quick_cert', 'shuttle', 'shuttle_keypair.pem')
21
+ cert = Shuttle.system.path('quick_cert', 'shuttle', 'cert_shuttle.pem')
22
+
23
+ config = {
24
+ :SSLPrivateKey => OpenSSL::PKey::RSA.new(File.read(keypair)),
25
+ :SSLCertificate => OpenSSL::X509::Certificate.new(File.read(cert)),
26
+ :SSLVerifyMode => OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT,
27
+ :SSLCACertificateFile => Shuttle.system.path('quick_cert', 'CA', 'cacert.pem')
28
+ }
29
+ end
30
+
31
+ config
32
+ end
33
+
34
+ def install_quick_cert!
35
+ unless Shuttle.system.find_bin('QuickCert')
36
+ FileUtils.mkdir_p('/tmp/quick_cert')
37
+ File.chmod(0700, '/tmp/quick_cert')
38
+
39
+ Dir.chdir('/tmp/quick_cert') do
40
+ Shuttle.system.run "curl -O #{Shuttle::QUICK_CERT}"
41
+ Shuttle.system.run "tar xzf QuickCert-1.0.2.tar.gz"
42
+
43
+ Dir.chdir('/tmp/quick_cert/QuickCert-1.0.2') do
44
+ Shuttle.system.run "#{Shuttle.system.ruby_path} ./setup.rb config"
45
+ Shuttle.system.run "#{Shuttle.system.ruby_path} ./setup.rb setup"
46
+ Shuttle.system.run "#{Shuttle.system.ruby_path} ./setup.rb install"
47
+ end
48
+ end
49
+
50
+ FileUtils.rm_rf('/tmp/quick_cert')
51
+ end
52
+ end
53
+
54
+ def dump_quick_cert_config!
55
+ return if File.file? Shuttle.system.path('quick_cert', 'qc_config')
56
+
57
+ config = %{
58
+ full_hostname = `hostname`.strip
59
+ domainname = full_hostname.split('.')[1..-1].join('.')
60
+ hostname = full_hostname.split('.')[0]
61
+
62
+ CA[:hostname] = hostname
63
+ CA[:domainname] = domainname
64
+ CA[:CA_dir] = File.join Dir.pwd, "CA"
65
+ CA[:password] = '#{rand(100_000)}'
66
+
67
+ CERTS << {
68
+ :type => 'server',
69
+ :hostname => 'shuttle',
70
+ # :password => '#{rand(100_000)}',
71
+ }
72
+
73
+ CERTS << {
74
+ :type => 'client',
75
+ :user => 'core',
76
+ :email => 'core@mrhenry.be',
77
+ }
78
+ }
79
+ FileUtils.mkdir_p(Shuttle.system.path('quick_cert'))
80
+ File.chmod(0700, Shuttle.system.path('quick_cert'))
81
+ File.open(Shuttle.system.path('quick_cert', 'qc_config'), 'w+') { |f| f.write config }
82
+ end
83
+
84
+ def run_quick_cert!
85
+ return if File.directory? Shuttle.system.path('quick_cert', 'CA')
86
+
87
+ Dir.chdir(Shuttle.system.path('quick_cert')) do
88
+ Shuttle.system.run Shuttle.system.find_bin('QuickCert')
89
+ end
90
+ end
91
+
92
+ def make_client_cert_public!
93
+ token = nil
94
+ if Shuttle.system.use_ssl?
95
+ token = Shuttle::Client::AuthToken.new(
96
+ :target_uri => self.construct_uri(DRb.uri),
97
+ :verify_mode => OpenSSL::SSL::VERIFY_PEER,
98
+ :private_key_data => File.read(Shuttle.system.path('quick_cert', 'core', 'core_keypair.pem')),
99
+ :certificate_data => File.read(Shuttle.system.path('quick_cert', 'core', 'cert_core.pem')),
100
+ :ca_certificate_data => File.read(Shuttle.system.path('quick_cert', 'CA', 'cacert.pem'))
101
+ )
102
+ else
103
+ token = Shuttle::Client::AuthToken.new(
104
+ :target_uri => self.construct_uri(DRb.uri))
105
+ end
106
+ token.dump_file(Shuttle.system.path('core.token'))
107
+ FileUtils.chmod_R(0775, Shuttle.system.path('core.token'))
108
+ end
109
+
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,113 @@
1
+ require 'drb'
2
+ require 'drb/ssl'
3
+ require 'simple-daemon'
4
+
5
+ module Shuttle
6
+
7
+ # the Server is supposed to be a simple DRb interface to the System.
8
+ class Server < SimpleDaemon::Base
9
+ include DRbUndumped
10
+
11
+ autoload :Proxy, File.dirname(__FILE__)+'/server/proxy'
12
+ autoload :Daemon, File.dirname(__FILE__)+'/server/daemon'
13
+ autoload :Security, File.dirname(__FILE__)+'/server/security'
14
+
15
+ include Shuttle::Server::Daemon
16
+ include Shuttle::Server::Security
17
+
18
+ def self.shared
19
+ @shared ||= new
20
+ end
21
+
22
+ def self.proxy
23
+ @proxy ||= Shuttle::Server::Proxy.new(self.shared)
24
+ end
25
+
26
+ def stop_server
27
+ Shuttle.log "stopping the server..."
28
+ Thread.new { sleep(1) and exit(Shuttle::STOP_STATUS) }
29
+ end
30
+
31
+ def restart_server
32
+ Shuttle.log "restarting the server..."
33
+ Thread.new { sleep(1) and exit(Shuttle::RESTART_STATUS) }
34
+ end
35
+
36
+ def reload_server
37
+ Shuttle.log "reloading the server..."
38
+ Thread.new { sleep(1) and exit(Shuttle::RELOAD_STATUS) }
39
+ end
40
+
41
+ def server_version
42
+ Shuttle.version
43
+ end
44
+
45
+ def update_server
46
+ if Shuttle.system.gem_update('shuttle', :user => 'root')
47
+ reload_server
48
+ end
49
+ end
50
+
51
+ def install_satellite(domain)
52
+ Shuttle.system.install_satellite(domain)
53
+ end
54
+
55
+ def uninstall_satellite(domain)
56
+ satellite = Shuttle.system.find_satellite(domain)
57
+ if satellite
58
+ Shuttle.system.uninstall_satellite(satellite)
59
+ else
60
+ Shuttle.log "Satellite not found (#{domain})"
61
+ end
62
+ end
63
+
64
+ def install_engine(domain, name, options={})
65
+ satellite = Shuttle.system.find_satellite(domain)
66
+ if satellite
67
+ Shuttle.system.install_engine(satellite, name, options)
68
+ else
69
+ Shuttle.log "Satellite not found (#{domain})"
70
+ end
71
+ end
72
+
73
+ def update_engine(domain, name, options={})
74
+ satellite = Shuttle.system.find_satellite(domain)
75
+ if satellite
76
+ Shuttle.system.update_engine(satellite, name, options)
77
+ else
78
+ Shuttle.log "Satellite not found (#{domain})"
79
+ end
80
+ end
81
+
82
+ def uninstall_engine(domain, name)
83
+ satellite = Shuttle.system.find_satellite(domain)
84
+ if satellite
85
+ Shuttle.system.uninstall_engine(satellite, name)
86
+ else
87
+ Shuttle.log "Satellite not found (#{domain})"
88
+ end
89
+ end
90
+
91
+ def satellites
92
+ Shuttle.system.satellites
93
+ end
94
+
95
+ def queued_jobs
96
+ queued_jobs = []
97
+ Shuttle.system.queue.each do |job, canceled, immediated|
98
+ job_delay = job.delay
99
+ queued_jobs.push([job.id, job.name, canceled, immediated, job.running?, job.waiting?, job_delay])
100
+ end
101
+ queued_jobs
102
+ end
103
+
104
+ def cancel_job(id)
105
+ Shuttle.system.queue.cancel(id)
106
+ end
107
+
108
+ def immediate_job(id)
109
+ Shuttle.system.queue.immediate(id)
110
+ end
111
+
112
+ end
113
+ end
@@ -0,0 +1,36 @@
1
+
2
+ module Shuttle
3
+ class System
4
+ module Config
5
+
6
+ def use_development!
7
+ environment { 'development' }
8
+ end
9
+
10
+ def development?
11
+ environment == 'development'
12
+ end
13
+
14
+ def use_production!
15
+ environment { 'production' }
16
+ end
17
+
18
+ def production?
19
+ environment == 'production'
20
+ end
21
+
22
+ def environment(&block)
23
+ option(:environment, block) { |s,v| v or 'production' }
24
+ end
25
+
26
+ def use_ssl!
27
+ option(:use_ssl, lambda { true })
28
+ end
29
+
30
+ def use_ssl?
31
+ option(:use_ssl, nil)
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,21 @@
1
+
2
+ module Shuttle
3
+ class System
4
+ module Helper
5
+
6
+ def use(actor)
7
+ actor_klass = (Shuttle::Actors.const_get(actor) rescue nil)
8
+ raise "Actor not found! (#{actor})" unless actor_klass
9
+
10
+ actor_helper = (actor_klass.const_get('Helper') rescue nil)
11
+ extend actor_helper if actor_helper
12
+
13
+ actor_config = (actor_klass.const_get('Config') rescue nil)
14
+ extend actor_config if actor_config
15
+
16
+ @actors.push(actor_klass)
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,79 @@
1
+
2
+ module Shuttle
3
+ class System
4
+ module Options
5
+
6
+ def option(name, proc, &handler)
7
+ if proc
8
+ @option_descriptors.push({
9
+ :name => name.to_sym,
10
+ :proc => proc,
11
+ :handler => handler
12
+ })
13
+ else
14
+ if handler && !@options.key?(name.to_sym)
15
+ @options[name.to_sym] = handler.call(nil)
16
+ end
17
+ @options[name.to_sym]
18
+ end
19
+ end
20
+
21
+ def satellite_option(name, proc, &handler)
22
+ if proc
23
+ @satellite_option_descriptors.push({
24
+ :name => name.to_sym,
25
+ :proc => proc,
26
+ :handler => handler
27
+ })
28
+ else
29
+ if handler && !@satellite_options.key?(name.to_sym)
30
+ @satellite_options[name.to_sym] = handler.call(@current_satellite, nil)
31
+ end
32
+ @satellite_options[name.to_sym]
33
+ end
34
+ end
35
+
36
+ def set_satellite_option(name, value)
37
+ @satellite_options[name.to_sym] = value
38
+ end
39
+
40
+ def resolve_options_with(satellite)
41
+ @current_satellite = satellite
42
+ resolve_options!
43
+
44
+ result = yield
45
+
46
+ @current_satellite = nil
47
+ resolve_options!
48
+
49
+ result
50
+ end
51
+
52
+ def resolve_options!
53
+ resolve_options_for_system!
54
+ resolve_options_for_satellite!
55
+ end
56
+
57
+ def resolve_options_for_system!
58
+ @options = {}
59
+ @option_descriptors.each do |option|
60
+ value = option[:proc].call if option[:proc]
61
+ value = option[:handler].call(value) if option[:handler]
62
+ @options[option[:name]] = value
63
+ end
64
+ end
65
+
66
+ def resolve_options_for_satellite!
67
+ @satellite_options = {}
68
+ if @current_satellite
69
+ @satellite_option_descriptors.each do |option|
70
+ value = option[:proc].call(@current_satellite) if option[:proc]
71
+ value = option[:handler].call(@current_satellite, value) if option[:handler]
72
+ @satellite_options[option[:name]] = value
73
+ end
74
+ end
75
+ end
76
+
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,75 @@
1
+
2
+ autoload :Etc, 'etc'
3
+
4
+ module Shuttle
5
+ class System
6
+ module ProcessUser
7
+
8
+ # get the uid for a user name
9
+ def get_uid(username)
10
+ return username if Numeric === username
11
+ Etc.getpwnam(username).uid
12
+ end
13
+
14
+ # get the gid for a group name
15
+ def get_gid(groupname)
16
+ return groupname if Numeric === groupname
17
+ Etc.getgrnam(groupname).gid
18
+ end
19
+
20
+ # get the user name for a uid (or the current process user)
21
+ def get_user_name(uid=Process.uid)
22
+ return uid if String === uid
23
+ Etc.getpwuid(uid).name
24
+ end
25
+
26
+ # get the group name for a gid (or the current process group)
27
+ def get_group_name(gid=Process.gid)
28
+ return gid if String === gid
29
+ Etc.getgrgid(gid).name
30
+ end
31
+
32
+ # is this process running as this user?
33
+ def is_user(username)
34
+ uid = get_uid(username)
35
+ Process.euid == uid and Process.uid == uid
36
+ end
37
+
38
+ # is this process running as this group?
39
+ def is_group(groupname)
40
+ gid = get_gid(groupname)
41
+ Process.egid == gid and Process.gid == gid
42
+ end
43
+
44
+ # switch this user to the specified user and group
45
+ def switch_to_user(username, groupname=nil)
46
+ different_uid = (Process.euid != get_uid(username))
47
+ different_gid = (Process.egid != get_gid(groupname)) if groupname
48
+
49
+ if groupname and different_gid
50
+ Process.gid = Process.egid = get_gid(groupname)
51
+ end
52
+
53
+ if different_uid
54
+ Process.uid = Process.euid = get_uid(username)
55
+ end
56
+ end
57
+
58
+ # run the passed block as the specified user and group
59
+ def as_user(username, groupname=nil, &block)
60
+ euid = Process.euid
61
+ egid = Process.egid
62
+
63
+ value = nil
64
+ begin
65
+ switch_to_user(username, groupname)
66
+ value = block.call
67
+ ensure
68
+ switch_to_user(euid, egid)
69
+ end
70
+ value
71
+ end
72
+
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,44 @@
1
+
2
+ module Shuttle
3
+ class System
4
+ module Satellites
5
+
6
+ def satellites_hash
7
+ @satellites || load_satellites
8
+ end
9
+
10
+ def satellites
11
+ satellites_hash.values
12
+ end
13
+
14
+ def find_satellite(domain)
15
+ satellites_hash[domain]
16
+ end
17
+
18
+ def save_satellite!(satellite)
19
+ satellites_hash[satellite.domain] = satellite
20
+ satellite.dump_file(self.path('satellites', "#{satellite.domain}.yml"))
21
+ end
22
+
23
+ def destroy_satellite!(satellite)
24
+ satellites_hash.delete(satellite.domain)
25
+ FileUtils.rm_f(self.path('satellites', "#{satellite.domain}.yml"))
26
+ end
27
+
28
+ private
29
+
30
+ def load_satellites
31
+ @satellites = {}
32
+
33
+ FileUtils.mkdir_p(self.path('satellites'))
34
+ Dir.glob(self.path('satellites', '*.yml')).each do |yml|
35
+ satellite = Shuttle::Satellite.load_file(yml)
36
+ @satellites[satellite.domain] = satellite
37
+ end
38
+
39
+ @satellites
40
+ end
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,80 @@
1
+
2
+ module Shuttle
3
+ class System
4
+ module Shell
5
+
6
+ def run(cmd)
7
+ as_user 'root', 'wheel' do
8
+ Shuttle.log "[#{get_user_name}]> "+cmd
9
+ cmd = "bash -l -c #{cmd.inspect}"
10
+ output = %x[#{cmd} 2>&1]
11
+ Shuttle.log output if development?
12
+ output
13
+ end
14
+ end
15
+
16
+ def user_run(username, cmd)
17
+ as_user 'root', 'wheel' do
18
+ Shuttle.log "[#{username}]> "+cmd
19
+ cmd = "su #{username} -l -c #{cmd.inspect}"
20
+ output = %x[#{cmd} 2>&1]
21
+ Shuttle.log output if development?
22
+ output
23
+ end
24
+ end
25
+
26
+ def popen(cmd, *args, &block)
27
+ as_user 'root', 'wheel' do
28
+ Shuttle.log cmd
29
+ cmd = "bash -l -c #{cmd.inspect}"
30
+ IO.popen(cmd, *args, &block)
31
+ end
32
+ end
33
+
34
+ def user_popen(username, cmd, *args, &block)
35
+ as_user 'root', 'wheel' do
36
+ Shuttle.log cmd
37
+ cmd = "su #{username} -l -c #{cmd.inspect}"
38
+ IO.popen(cmd, *args, &block)
39
+ end
40
+ end
41
+
42
+ def find_bin(*names)
43
+ names = names.flatten.compact.uniq
44
+ unless names.empty?
45
+ names = names.join(' ')
46
+ popen("which #{names}", 'r') do |f|
47
+
48
+ until f.eof?
49
+ path = f.readline
50
+ next unless path
51
+ path = path.strip
52
+ next if path =~ /(Last login)|(You have new)|(Using ruby)/
53
+ return path
54
+ end
55
+
56
+ end
57
+ end
58
+ end
59
+
60
+ def user_find_bin(username, *names)
61
+ names = names.flatten.compact.uniq
62
+ unless names.empty?
63
+ names = names.join(' ')
64
+ user_popen(username, "which #{names}", 'r') do |f|
65
+
66
+ until f.eof?
67
+ path = f.readline
68
+ next unless path
69
+ path = path.strip
70
+ next if path =~ /(Last login)|(You have new)|(Using ruby)/
71
+ return path
72
+ end
73
+
74
+ end
75
+ end
76
+ end
77
+
78
+ end
79
+ end
80
+ end