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.
- data/app_generators/engine/engine_generator.rb +39 -0
- data/app_generators/engine/templates/config/routes.rb +2 -0
- data/app_generators/engine/templates/init.rb +1 -0
- data/app_generators/engine/templates/lib/engine.rb +1 -0
- data/app_generators/engine/templates/rails/init.rb +1 -0
- data/bin/shuttle +20 -0
- data/lib/rubygems_plugin.rb +1 -0
- data/lib/shuttle/actor/actions.rb +76 -0
- data/lib/shuttle/actor.rb +23 -0
- data/lib/shuttle/actors/apache_actor.rb +56 -0
- data/lib/shuttle/actors/base_actor.rb +276 -0
- data/lib/shuttle/actors/mysql_actor.rb +20 -0
- data/lib/shuttle/actors/passenger_actor.rb +19 -0
- data/lib/shuttle/actors/plesk_actor.rb +210 -0
- data/lib/shuttle/actors/sqlite3_actor.rb +44 -0
- data/lib/shuttle/app_runner.rb +118 -0
- data/lib/shuttle/apps/dev.rb +27 -0
- data/lib/shuttle/apps/engines.rb +33 -0
- data/lib/shuttle/apps/jobs.rb +35 -0
- data/lib/shuttle/apps/satellite.rb +32 -0
- data/lib/shuttle/apps/server.rb +70 -0
- data/lib/shuttle/client/auth_token.rb +98 -0
- data/lib/shuttle/client.rb +48 -0
- data/lib/shuttle/exception_handler.rb +53 -0
- data/lib/shuttle/extentions/rubygems_plugin.rb +27 -0
- data/lib/shuttle/extentions/thor_extentions.rb +32 -0
- data/lib/shuttle/job_queue.rb +199 -0
- data/lib/shuttle/satellite/actions.rb +35 -0
- data/lib/shuttle/satellite/dependency_loader.rb +79 -0
- data/lib/shuttle/satellite/persistence.rb +50 -0
- data/lib/shuttle/satellite.rb +49 -0
- data/lib/shuttle/server/daemon.rb +85 -0
- data/lib/shuttle/server/proxy.rb +25 -0
- data/lib/shuttle/server/security.rb +113 -0
- data/lib/shuttle/server.rb +113 -0
- data/lib/shuttle/system/config.rb +36 -0
- data/lib/shuttle/system/helper.rb +21 -0
- data/lib/shuttle/system/options.rb +79 -0
- data/lib/shuttle/system/process_user.rb +75 -0
- data/lib/shuttle/system/satellites.rb +44 -0
- data/lib/shuttle/system/shell.rb +80 -0
- data/lib/shuttle/system.rb +159 -0
- data/lib/shuttle/systems/centos_plesk_system.rb +28 -0
- data/lib/shuttle/systems/macports_system.rb +14 -0
- data/lib/shuttle.rb +82 -0
- data/spec/actor/actions_spec.rb +13 -0
- data/spec/spec_helper.rb +1 -0
- metadata +129 -0
@@ -0,0 +1,210 @@
|
|
1
|
+
|
2
|
+
module Shuttle
|
3
|
+
module Actors # :nodoc:
|
4
|
+
class PleskActor < Shuttle::Actor
|
5
|
+
|
6
|
+
before_install_satellite :create_client
|
7
|
+
before_install_satellite :create_domain
|
8
|
+
after_install_satellite :link_htdocs
|
9
|
+
after_install_satellite :reload_subdomains
|
10
|
+
after_install_satellite :restart_apache
|
11
|
+
on_install_satellite :create_database
|
12
|
+
on_uninstall_satellite :drop_databases
|
13
|
+
|
14
|
+
def create_client
|
15
|
+
# only do this if this is needed
|
16
|
+
if false
|
17
|
+
unless system.client?(system.plesk_client)
|
18
|
+
system.create_client(system.plesk_client, system.plesk_client, 'mypwd')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def create_domain
|
24
|
+
unless system.domain?(satellite.basedomain)
|
25
|
+
system.create_domain(system.plesk_client, satellite.basedomain,
|
26
|
+
system.web_user[0,20], (rand(1_000_000_000) + 10_000).to_s)
|
27
|
+
else
|
28
|
+
httpdocs_path = "/var/www/vhosts/#{satellite.basedomain}/httpdocs"
|
29
|
+
system.set_satellite_option(:web_user,
|
30
|
+
system.get_user_name(File.stat(httpdocs_path).uid))
|
31
|
+
end
|
32
|
+
|
33
|
+
if satellite.subdomain? and !system.subdomain?(satellite.basedomain, satellite.subdomain)
|
34
|
+
system.create_subdomain(satellite.basedomain, satellite.subdomain)
|
35
|
+
end
|
36
|
+
|
37
|
+
FileUtils.mkdir_p(system.satellite_root)
|
38
|
+
FileUtils.mkdir_p(system.shared_root)
|
39
|
+
FileUtils.chown_R(system.web_user, system.web_group, system.satellite_root)
|
40
|
+
FileUtils.chown_R(system.web_user, system.web_group, system.shared_root)
|
41
|
+
end
|
42
|
+
|
43
|
+
def link_htdocs
|
44
|
+
httpdocs_path = File.join(File.dirname(system.satellite_root), 'httpdocs')
|
45
|
+
FileUtils.rm_rf(httpdocs_path)
|
46
|
+
FileUtils.ln_s(system.satellite_root, httpdocs_path)
|
47
|
+
FileUtils.chown_R(system.web_user, system.web_group, httpdocs_path)
|
48
|
+
|
49
|
+
File.open(File.join(system.satellite_root, '../conf/vhost.conf'), 'w+') do |f|
|
50
|
+
f.puts "DocumentRoot #{httpdocs_path}/public"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def reload_subdomains
|
55
|
+
system.run("#{system.plesk_websrvmng_bin} --reconfigure-vhost --vhost-name=#{satellite.basedomain}")
|
56
|
+
end
|
57
|
+
|
58
|
+
def restart_apache
|
59
|
+
system.run("#{system.plesk_httpd_bin} restart")
|
60
|
+
end
|
61
|
+
|
62
|
+
def create_database
|
63
|
+
db_name = satellite.domain.downcase.gsub(/[^a-z]+/, '_')[0,63]
|
64
|
+
db_user = db_name[0,13]
|
65
|
+
db_pswd = (rand(1_000_000_000) + 10_000).to_s
|
66
|
+
|
67
|
+
system.create_database(satellite.basedomain, "#{db_name}_d", "#{db_user}_ud", db_pswd)
|
68
|
+
system.create_database(satellite.basedomain, "#{db_name}_t", "#{db_user}_ut", db_pswd)
|
69
|
+
system.create_database(satellite.basedomain, "#{db_name}_p", "#{db_user}_up", db_pswd)
|
70
|
+
|
71
|
+
|
72
|
+
system.as_user(system.web_user, system.web_group) do
|
73
|
+
config = %{
|
74
|
+
development:
|
75
|
+
adapter: mysql
|
76
|
+
database: #{db_name}_d
|
77
|
+
username: #{db_user}_ud
|
78
|
+
password: #{db_pswd}
|
79
|
+
host: localhost
|
80
|
+
encoding: utf8
|
81
|
+
socket: /var/lib/mysql/mysql.sock
|
82
|
+
|
83
|
+
test:
|
84
|
+
adapter: mysql
|
85
|
+
database: #{db_name}_t
|
86
|
+
username: #{db_user}_ut
|
87
|
+
password: #{db_pswd}
|
88
|
+
host: localhost
|
89
|
+
encoding: utf8
|
90
|
+
socket: /var/lib/mysql/mysql.sock
|
91
|
+
|
92
|
+
production:
|
93
|
+
adapter: mysql
|
94
|
+
database: #{db_name}_p
|
95
|
+
username: #{db_user}_up
|
96
|
+
password: #{db_pswd}
|
97
|
+
host: localhost
|
98
|
+
encoding: utf8
|
99
|
+
socket: /var/lib/mysql/mysql.sock
|
100
|
+
}
|
101
|
+
|
102
|
+
db_file = File.join(system.satellite_root, 'config', 'database.yml')
|
103
|
+
File.open(db_file, 'w+') { |f| f.write config }
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def drop_databases
|
108
|
+
db_name = satellite.domain.downcase.gsub(/[^a-z]+/, '_')[0,63]
|
109
|
+
|
110
|
+
system.drop_database("#{db_name}_d")
|
111
|
+
system.drop_database("#{db_name}_t")
|
112
|
+
system.drop_database("#{db_name}_p")
|
113
|
+
end
|
114
|
+
|
115
|
+
module Helper
|
116
|
+
|
117
|
+
def create_client(name, login, pwd)
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
# check is the client exists
|
122
|
+
def client?(login)
|
123
|
+
!(run("#{plesk_client_bin} -i #{login}") =~ /Object not found: Client/)
|
124
|
+
end
|
125
|
+
|
126
|
+
# create a domain through plesk
|
127
|
+
def create_domain(client, domain, user, passwd)
|
128
|
+
run("#{plesk_domain_bin} -c #{domain} -clogin #{client} -status enabled -hosting true -hst_type phys -dns true -www true -login #{user} -passwd #{passwd} -shell /bin/bash")
|
129
|
+
end
|
130
|
+
|
131
|
+
# check if a domain exists with plesk
|
132
|
+
def domain?(domain)
|
133
|
+
!(run("#{plesk_domain_bin} -i #{domain}") =~ /Object not found: Domain/)
|
134
|
+
end
|
135
|
+
|
136
|
+
# create a subdomain through plesk
|
137
|
+
def create_subdomain(basedomain, subdomain)
|
138
|
+
run("#{plesk_subdomain_bin} -c #{subdomain} -d #{basedomain}")
|
139
|
+
end
|
140
|
+
|
141
|
+
# check if a subdomain exists with plesk
|
142
|
+
def subdomain?(basedomain, subdomain)
|
143
|
+
run("#{plesk_subdomain_bin} -i -s #{subdomain} -d #{basedomain}") =~ /SUCCESS: Gathering/
|
144
|
+
end
|
145
|
+
|
146
|
+
# create a database through plesk
|
147
|
+
def create_database(basedomain, db_name, db_user, pwd)
|
148
|
+
o = run("#{plesk_database_bin} -c #{db_name} -domain #{basedomain} -server #{plesk_database_server} -add_user #{db_user} -passwd #{pwd}")
|
149
|
+
if o =~ /Database with requested name already exists/
|
150
|
+
o = run("#{plesk_database_bin} -u #{db_name} -add_user #{db_user} -passwd #{pwd}")
|
151
|
+
if o =~ /Unable to create database user: User/
|
152
|
+
o = run("#{plesk_database_bin} -u #{db_name} -update_user #{db_user} -passwd #{pwd}")
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# drop a database through plesk
|
158
|
+
def drop_database(db_name)
|
159
|
+
run("#{plesk_database_bin} -r #{db_name}")
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
|
164
|
+
module Config
|
165
|
+
|
166
|
+
# set the plesk client to be used for this satellite.
|
167
|
+
def plesk_client(&block)
|
168
|
+
satellite_option(:plesk_client, block)
|
169
|
+
end
|
170
|
+
|
171
|
+
# path to the client tool.
|
172
|
+
def plesk_client_bin(&block)
|
173
|
+
option(:plesk_client_bin, block) { |v| v or '/usr/local/psa/bin/client' }
|
174
|
+
end
|
175
|
+
|
176
|
+
# path to the domain tool.
|
177
|
+
def plesk_domain_bin(&block)
|
178
|
+
option(:plesk_domain_bin, block) { |v| v or '/usr/local/psa/bin/domain' }
|
179
|
+
end
|
180
|
+
|
181
|
+
# path to the subdomain tool.
|
182
|
+
def plesk_subdomain_bin(&block)
|
183
|
+
option(:plesk_subdomain_bin, block) { |v| v or '/usr/local/psa/bin/subdomain' }
|
184
|
+
end
|
185
|
+
|
186
|
+
# path to the database tool.
|
187
|
+
def plesk_database_bin(&block)
|
188
|
+
option(:plesk_database_bin, block) { |v| v or '/usr/local/psa/bin/database' }
|
189
|
+
end
|
190
|
+
|
191
|
+
# path to the websrvmng tool.
|
192
|
+
def plesk_websrvmng_bin(&block)
|
193
|
+
option(:plesk_websrvmng_bin, block) { |v| v or '/usr/local/psa/admin/sbin/websrvmng' }
|
194
|
+
end
|
195
|
+
|
196
|
+
# path to the httpd tool.
|
197
|
+
def plesk_httpd_bin(&block)
|
198
|
+
option(:plesk_httpd_bin, block) { |v| v or '/etc/init.d/httpd' }
|
199
|
+
end
|
200
|
+
|
201
|
+
# host and port of database server.
|
202
|
+
def plesk_database_server(&block)
|
203
|
+
option(:plesk_database_server, block)
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
|
2
|
+
module Shuttle
|
3
|
+
module Actors # :nodoc:
|
4
|
+
class Sqlite3Actor < Shuttle::Actor
|
5
|
+
|
6
|
+
after_install_satellite :write_config_file
|
7
|
+
|
8
|
+
# write the +database.yml+ config file.
|
9
|
+
def write_config_file
|
10
|
+
system.as_user(system.web_user, system.web_group) do
|
11
|
+
|
12
|
+
config = %{# SQLite version 3.x
|
13
|
+
# gem install sqlite3-ruby (not necessary on OS X Leopard)
|
14
|
+
development:
|
15
|
+
adapter: sqlite3
|
16
|
+
database: db/system/development.sqlite3
|
17
|
+
pool: 5
|
18
|
+
timeout: 5000
|
19
|
+
|
20
|
+
# Warning: The database defined as "test" will be erased and
|
21
|
+
# re-generated from your development database when you run "rake".
|
22
|
+
# Do not set this db to the same as development or production.
|
23
|
+
test:
|
24
|
+
adapter: sqlite3
|
25
|
+
database: db/system/test.sqlite3
|
26
|
+
pool: 5
|
27
|
+
timeout: 5000
|
28
|
+
|
29
|
+
production:
|
30
|
+
adapter: sqlite3
|
31
|
+
database: db/system/production.sqlite3
|
32
|
+
pool: 5
|
33
|
+
timeout: 5000
|
34
|
+
}
|
35
|
+
|
36
|
+
db_file = File.join(system.satellite_root, 'config', 'database.yml')
|
37
|
+
File.open(db_file, 'w+') { |f| f.write config }
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require File.dirname(__FILE__)+'/extentions/thor_extentions'
|
3
|
+
|
4
|
+
module Shuttle
|
5
|
+
|
6
|
+
# AppRunner allows us to have multiple apps in different namespaces
|
7
|
+
class AppRunner < Thor
|
8
|
+
|
9
|
+
# register a subapplication with the app runner.
|
10
|
+
def self.use(app)
|
11
|
+
Shuttle::Apps.const_get(app)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Override Thor#help so we can give info about not-yet-loaded tasks
|
15
|
+
def help(task = nil)
|
16
|
+
super
|
17
|
+
|
18
|
+
unless task
|
19
|
+
search = ".*#{search}" if options["substring"]
|
20
|
+
search = /^#{search}.*/i
|
21
|
+
group = options[:group] || "standard"
|
22
|
+
|
23
|
+
classes = Thor.subclasses.select do |k|
|
24
|
+
(options[:all] || k.group_name == group) &&
|
25
|
+
Thor::Util.constant_to_thor_path(k.name) =~ search
|
26
|
+
end
|
27
|
+
display_klasses(false, classes)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# forward actions to the sub apps
|
32
|
+
def method_missing(meth, *args)
|
33
|
+
meth = meth.to_s
|
34
|
+
super(meth.to_sym, *args) unless meth.include?(?:)
|
35
|
+
|
36
|
+
begin
|
37
|
+
task = Thor[meth]
|
38
|
+
task.parse(task.klass.new, ARGV[1..-1])
|
39
|
+
rescue => e
|
40
|
+
puts e.message
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def display_klasses(with_modules = false, klasses = Thor.subclasses)
|
47
|
+
klasses -= [Thor, Shuttle::AppRunner] unless with_modules
|
48
|
+
raise Error, "No Thor tasks available" if klasses.empty?
|
49
|
+
|
50
|
+
if with_modules && !thor_yaml.empty?
|
51
|
+
max_name = thor_yaml.max { |(xk, xv), (yk, yv)| xk.to_s.size <=> yk.to_s.size }.first.size
|
52
|
+
modules_label = "Modules"
|
53
|
+
namespaces_label = "Namespaces"
|
54
|
+
column_width = [max_name + 4, modules_label.size + 1].max
|
55
|
+
|
56
|
+
print "%-#{column_width}s" % modules_label
|
57
|
+
puts namespaces_label
|
58
|
+
print "%-#{column_width}s" % ("-" * modules_label.size)
|
59
|
+
puts "-" * namespaces_label.size
|
60
|
+
|
61
|
+
thor_yaml.each do |name, info|
|
62
|
+
print "%-#{column_width}s" % name
|
63
|
+
puts info[:constants].map { |c| Thor::Util.constant_to_thor_path(c) }.join(", ")
|
64
|
+
end
|
65
|
+
|
66
|
+
puts
|
67
|
+
end
|
68
|
+
|
69
|
+
# Calculate the largest base class name
|
70
|
+
max_base = klasses.max do |x,y|
|
71
|
+
Thor::Util.constant_to_thor_path(x.name).size <=> Thor::Util.constant_to_thor_path(y.name).size
|
72
|
+
end.name.size
|
73
|
+
|
74
|
+
# Calculate the size of the largest option description
|
75
|
+
max_left_item = klasses.max do |x,y|
|
76
|
+
(x.maxima.usage + x.maxima.opt).to_i <=> (y.maxima.usage + y.maxima.opt).to_i
|
77
|
+
end
|
78
|
+
|
79
|
+
max_left = max_left_item.maxima.usage + max_left_item.maxima.opt
|
80
|
+
|
81
|
+
unless klasses.empty?
|
82
|
+
puts # add some spacing
|
83
|
+
klasses.each { |k| display_tasks(k, max_base, max_left); }
|
84
|
+
else
|
85
|
+
puts "\033[1;34mNo Thor tasks available\033[0m"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def display_tasks(klass, max_base, max_left)
|
90
|
+
if klass.tasks.values.length > 1
|
91
|
+
|
92
|
+
base = Thor::Util.constant_to_thor_path(klass.name)
|
93
|
+
|
94
|
+
if base.to_a.empty?
|
95
|
+
base = 'default'
|
96
|
+
puts "\033[1;35m#{base}\033[0m"
|
97
|
+
else
|
98
|
+
puts "\033[1;34m#{base}\033[0m"
|
99
|
+
end
|
100
|
+
|
101
|
+
puts "-" * base.length
|
102
|
+
|
103
|
+
klass.tasks.each true do |name, task|
|
104
|
+
format_string = "%-#{max_left + max_base + 5}s"
|
105
|
+
print format_string % task.formatted_usage(true)
|
106
|
+
puts task.description
|
107
|
+
end
|
108
|
+
|
109
|
+
unless klass.opts.empty?
|
110
|
+
puts "\nglobal options: #{Options.new(klass.opts)}"
|
111
|
+
end
|
112
|
+
|
113
|
+
puts # add some spacing
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Shuttle
|
4
|
+
module Apps # :nodoc:
|
5
|
+
|
6
|
+
class Dev < Thor
|
7
|
+
desc "create NAME", "create a new engine"
|
8
|
+
def create(name)
|
9
|
+
require 'rubigen'
|
10
|
+
system("rails #{name}")
|
11
|
+
|
12
|
+
FileUtils.rm_r("#{name}/doc", :verbose => true)
|
13
|
+
FileUtils.rm_r("#{name}/README", :verbose => true)
|
14
|
+
FileUtils.rm_r("#{name}/public/javascripts", :verbose => true)
|
15
|
+
|
16
|
+
require 'rubigen/scripts/generate'
|
17
|
+
RubiGen::Base.use_application_sources!
|
18
|
+
RubiGen::Scripts::Generate.new.run(["-f", name], :generator => 'engine')
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "link", "link the current development app"
|
22
|
+
def link
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Shuttle
|
4
|
+
module Apps # :nodoc:
|
5
|
+
|
6
|
+
class Engines < Thor
|
7
|
+
desc 'install DOMAIN NAME', 'install an engine'
|
8
|
+
method_options :version => :required, :lib => :optional, :source => :optional, :token => :optional
|
9
|
+
def install(domain, name)
|
10
|
+
desc = { :version => options[:version] }
|
11
|
+
desc[:lib] = options[:lib] if options[:lib]
|
12
|
+
desc[:source] = options[:source] if options[:source]
|
13
|
+
Shuttle.client(options[:token]).install_engine(domain, name, desc)
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'update DOMAIN NAME', 'update an engine'
|
17
|
+
method_options :version => :required, :lib => :optional, :source => :optional, :token => :optional
|
18
|
+
def update(domain, name)
|
19
|
+
desc = { :version => options[:version] }
|
20
|
+
desc[:lib] = options[:lib] if options[:lib]
|
21
|
+
desc[:source] = options[:source] if options[:source]
|
22
|
+
Shuttle.client(options[:token]).update_engine(domain, name, desc)
|
23
|
+
end
|
24
|
+
|
25
|
+
desc 'uninstall DOMAIN NAME', 'uninstall an engine'
|
26
|
+
method_options :token => :optional
|
27
|
+
def uninstall(domain, name)
|
28
|
+
Shuttle.client(options[:token]).uninstall_engine(domain, name)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Shuttle
|
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 = Shuttle.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
|
+
Shuttle.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
|
+
Shuttle.client(options[:token]).immediate_job(id.to_i)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Shuttle
|
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
|
+
Shuttle.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
|
+
Shuttle.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
|
+
Shuttle.client(options[:token]).uninstall_satellite(domain)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'simple-daemon'
|
3
|
+
|
4
|
+
module Shuttle
|
5
|
+
module Apps
|
6
|
+
|
7
|
+
class Server < Thor
|
8
|
+
|
9
|
+
desc "start", 'start the server'
|
10
|
+
method_options :foreground => :boolean, :config => :optional
|
11
|
+
def start
|
12
|
+
Shuttle.server? true
|
13
|
+
Shuttle::System.load!(options[:root_path])
|
14
|
+
|
15
|
+
begin
|
16
|
+
FileUtils.mkdir_p(Shuttle.system.root)
|
17
|
+
rescue Errno::EACCES
|
18
|
+
Shuttle.logger.out.fatal "must be executed as root"
|
19
|
+
exit(1)
|
20
|
+
end
|
21
|
+
|
22
|
+
unless Shuttle.system.is_user('root')
|
23
|
+
Shuttle.logger.out.fatal "must be executed as root"
|
24
|
+
exit(1)
|
25
|
+
end
|
26
|
+
|
27
|
+
SimpleDaemon.const_set 'WORKING_DIRECTORY', Shuttle.system.root
|
28
|
+
if options[:foreground]
|
29
|
+
Shuttle::Server.start
|
30
|
+
else
|
31
|
+
ARGV.clear and ARGV.concat(%w( start ))
|
32
|
+
Shuttle::Server.daemonize
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
desc "stop", 'stop the server'
|
37
|
+
method_options :token => :optional
|
38
|
+
def stop
|
39
|
+
Shuttle.client(options[:token]).stop_server
|
40
|
+
end
|
41
|
+
|
42
|
+
desc "restart", 'restart the server'
|
43
|
+
method_options :token => :optional
|
44
|
+
def restart
|
45
|
+
Shuttle.client(options[:token]).restart_server
|
46
|
+
end
|
47
|
+
|
48
|
+
desc "reload", 'reload the server'
|
49
|
+
method_options :token => :optional
|
50
|
+
def reload
|
51
|
+
Shuttle.client(options[:token]).reload_server
|
52
|
+
end
|
53
|
+
|
54
|
+
desc "update", 'update the shuttle'
|
55
|
+
method_options :token => :optional
|
56
|
+
def update
|
57
|
+
Shuttle.client(options[:token]).update_server
|
58
|
+
end
|
59
|
+
|
60
|
+
desc "version", 'version of the server'
|
61
|
+
method_options :token => :optional
|
62
|
+
def version
|
63
|
+
puts "Client: #{Shuttle.version}"
|
64
|
+
puts "Server: #{Shuttle.client(options[:token]).server_version}"
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Shuttle
|
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 shuttle server can be accessed.
|
18
|
+
attr_reader :target_uri
|
19
|
+
# the SSL verification mode used by the shuttle server
|
20
|
+
attr_reader :verify_mode
|
21
|
+
# the optional CA certificate used by the shuttle 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 = Shuttle::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
|