Flucti-flucti-cli 0.1.16
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +7 -0
- data/README.mdown +24 -0
- data/Rakefile +56 -0
- data/TODO.txt +19 -0
- data/bin/flucti +4 -0
- data/flucti-cli.gemspec +46 -0
- data/lib/flucti.rb +20 -0
- data/lib/flucti/api_access.rb +56 -0
- data/lib/flucti/cli.rb +107 -0
- data/lib/flucti/parameters.rb +37 -0
- data/lib/flucti/resources.rb +41 -0
- data/lib/flucti/resources/account.rb +7 -0
- data/lib/flucti/resources/app_type.rb +18 -0
- data/lib/flucti/resources/backend.rb +28 -0
- data/lib/flucti/resources/basic_resource.rb +16 -0
- data/lib/flucti/resources/container.rb +15 -0
- data/lib/flucti/resources/db_server.rb +38 -0
- data/lib/flucti/resources/domain.rb +8 -0
- data/lib/flucti/resources/general.rb +14 -0
- data/lib/flucti/resources/mail_client.rb +7 -0
- data/lib/flucti/resources/mail_server.rb +7 -0
- data/lib/flucti/resources/port_forwarding.rb +15 -0
- data/lib/flucti/resources/port_forwarding/services +13921 -0
- data/lib/flucti/resources/ssh_details.rb +96 -0
- data/lib/flucti/resources/webserver.rb +9 -0
- data/lib/flucti/resources/website.rb +9 -0
- data/lib/flucti/tasks.rb +3 -0
- data/lib/flucti/tasks/apikey_tasks.rb +57 -0
- data/lib/flucti/tasks/apptype_tasks.rb +264 -0
- data/lib/flucti/tasks/connect_pack.rb +161 -0
- data/lib/flucti/tasks/db_tasks.rb +158 -0
- data/lib/flucti/tasks/mail_tasks.rb +104 -0
- data/lib/flucti/tasks/miscellaneous_tasks.rb +6 -0
- data/lib/flucti/tasks/progress_tasks.rb +24 -0
- data/lib/flucti/tasks/sshkey_tasks.rb +151 -0
- data/lib/flucti/tasks/vps/firewall_tasks.rb +84 -0
- data/lib/flucti/tasks/vps_tasks.rb +37 -0
- data/lib/flucti/tasks/webserver_tasks.rb +154 -0
- data/lib/flucti/tasks/website/apptype_tasks.rb +42 -0
- data/lib/flucti/tasks/website/backends/instances_tasks.rb +37 -0
- data/lib/flucti/tasks/website/backends_tasks.rb +254 -0
- data/lib/flucti/tasks/website/capfile_tasks.rb +41 -0
- data/lib/flucti/tasks/website/domains_tasks.rb +107 -0
- data/lib/flucti/tasks/website_tasks.rb +221 -0
- data/lib/flucti/utilities.rb +20 -0
- data/lib/flucti/utilities/connection_error_handling.rb +50 -0
- data/lib/flucti/utilities/core_ext.rb +35 -0
- data/lib/flucti/utilities/list_displayer.rb +57 -0
- data/lib/flucti/utilities/miscellaneous.rb +65 -0
- data/lib/flucti/utilities/progress_bar.rb +17 -0
- data/lib/flucti/utilities/table.rb +25 -0
- data/lib/flucti/utilities/task_packing.rb +10 -0
- data/lib/flucti/utilities/user_interface.rb +117 -0
- data/lib/vendor/ruby-progressbar-0.9/lib/ChangeLog +113 -0
- data/lib/vendor/ruby-progressbar-0.9/lib/progressbar.en.rd +103 -0
- data/lib/vendor/ruby-progressbar-0.9/lib/progressbar.ja.rd +100 -0
- data/lib/vendor/ruby-progressbar-0.9/lib/progressbar.rb +236 -0
- data/lib/vendor/ruby-progressbar-0.9/lib/test.rb +105 -0
- data/test/flucti/resources_test.rb +32 -0
- data/test/flucti/tasks_test.rb +28 -0
- data/test/flucti/utilities/miscellaneous_test.rb +54 -0
- data/test/flucti/utilities/table_test.rb +28 -0
- data/test/flucti/utilities/user_interface_test.rb +161 -0
- data/test/test_helper.rb +5 -0
- metadata +221 -0
@@ -0,0 +1,96 @@
|
|
1
|
+
module Flucti
|
2
|
+
module Resources
|
3
|
+
class SshDetails < BasicResource
|
4
|
+
def as(login)
|
5
|
+
AccountBinding.new(self, login)
|
6
|
+
end
|
7
|
+
|
8
|
+
def connection
|
9
|
+
as(login)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
class AccountBinding < Struct.new(:details, :login)
|
15
|
+
delegate :host, :port, :to => :details
|
16
|
+
|
17
|
+
def connection_command
|
18
|
+
"ssh -p #{port} #{login}@#{host}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def connect
|
22
|
+
check_for_presence_of_ssh do
|
23
|
+
command = connection_command
|
24
|
+
$stderr.puts "Connecting to VPS with: `#{command}'"
|
25
|
+
exec(command)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def execute_command(command)
|
30
|
+
%(#{connection_command} 'echo "#{command.gsub /"/, '\"'}" | bash -l -s')
|
31
|
+
end
|
32
|
+
|
33
|
+
def execute(command)
|
34
|
+
check_for_presence_of_ssh do
|
35
|
+
command = execute_command(command)
|
36
|
+
$stderr.puts "Executing: #{command}"
|
37
|
+
|
38
|
+
# For some reason, `exec' won't work, it throws the following error:
|
39
|
+
# `Operation not supported' (Errno::E045). `system' causes this too
|
40
|
+
# sometimes, but so much less often that it's bearable.
|
41
|
+
system(command)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
alias run :execute
|
45
|
+
|
46
|
+
def upload(*files)
|
47
|
+
check_for_presence_of_scp do
|
48
|
+
command = %(scp -P #{port} #{files * ' '} #{login}@#{host}:~)
|
49
|
+
$stderr.puts "Executing: #{command}"
|
50
|
+
system(command)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def check_for_presence_of_ssh
|
57
|
+
if has_command? 'ssh'
|
58
|
+
yield
|
59
|
+
else
|
60
|
+
suggest_windows_alternative "ssh", "PuTTY", "http://www.chiark.greenend.org.uk/~sgtatham/putty"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def check_for_presence_of_scp
|
65
|
+
if has_command? 'scp'
|
66
|
+
yield
|
67
|
+
else
|
68
|
+
suggest_windows_alternative "scp", "WinSCP", "http://winscp.net"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def has_command?(command)
|
73
|
+
case RUBY_PLATFORM
|
74
|
+
when /mswin/
|
75
|
+
false
|
76
|
+
else
|
77
|
+
system("which ssh > /dev/null 2>&1")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def suggest_windows_alternative(missing, alternative, url)
|
82
|
+
Utilities.error! <<-MSG
|
83
|
+
The necessary `#{missing}' utility seems missing on your system. You
|
84
|
+
should connect to the VPS manually with #{alternative} (for Windows,
|
85
|
+
available at #{url}) using the following connection details :
|
86
|
+
|
87
|
+
* Host: #{host}
|
88
|
+
* Port: #{port}
|
89
|
+
* Login: #{login}
|
90
|
+
* Password: (not needed -- specify your public SSH key instead)
|
91
|
+
MSG
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/flucti/tasks.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
namespace :apikey do
|
2
|
+
desc <<-DESC
|
3
|
+
Reset your API key and generate a new one. The new key will be sent to you
|
4
|
+
by e-mail.
|
5
|
+
DESC
|
6
|
+
task :reset do
|
7
|
+
APIKey.put
|
8
|
+
|
9
|
+
puts_title "API key reset"
|
10
|
+
puts_long <<-MSG
|
11
|
+
Your API key has been reset. You will receive an e-mail with your new
|
12
|
+
API key shortly. After that, you will need to reconfigure this utility
|
13
|
+
to use the new key, by running #{qcommand "apikey:switch"}.
|
14
|
+
MSG
|
15
|
+
end
|
16
|
+
|
17
|
+
desc <<-DESC
|
18
|
+
Change the current API key. You should use this task after #{qcommand "apikey:reset"}
|
19
|
+
to use a newly generated API key.
|
20
|
+
DESC
|
21
|
+
task :switch do
|
22
|
+
puts_title "API key"
|
23
|
+
puts_long <<-MSG
|
24
|
+
In order to use this utility, you must provide your API key that must
|
25
|
+
have been sent to you in an e-mail after creating your account. Please
|
26
|
+
paste it at the prompt below and press <enter>.
|
27
|
+
MSG
|
28
|
+
prompt_and_store_key
|
29
|
+
end
|
30
|
+
|
31
|
+
def prompt_and_store_key
|
32
|
+
loop do
|
33
|
+
puts
|
34
|
+
print "API key: "
|
35
|
+
entered = $stdin.gets.strip
|
36
|
+
begin
|
37
|
+
APIKey.site = APIAccess.site_with_api_key(entered)
|
38
|
+
begin
|
39
|
+
APIKey.get
|
40
|
+
ensure
|
41
|
+
APIKey.site = nil
|
42
|
+
end
|
43
|
+
rescue WebService::UnauthorizedAccess
|
44
|
+
puts
|
45
|
+
puts_long <<-MSG
|
46
|
+
The key you entered apears to be invalid. Please double check it
|
47
|
+
and retry, or let us help you: #{SUPPORT_EMAIL_ADDR}.
|
48
|
+
MSG
|
49
|
+
else
|
50
|
+
Parameters.store(:api_key, entered)
|
51
|
+
puts
|
52
|
+
puts "* Key valid and saved."
|
53
|
+
break
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,264 @@
|
|
1
|
+
namespace :apptype do
|
2
|
+
task(:default) { list }
|
3
|
+
|
4
|
+
desc <<-DESC
|
5
|
+
Display a list of all the stock application types.
|
6
|
+
|
7
|
+
These are only suggestions that a website can be automatically configured
|
8
|
+
for running. Of course, you can configure your VPS's to run any kind of
|
9
|
+
site you want, either by customizing existing types (see #{qcommand \
|
10
|
+
'apptype:import'} and #{qcommand 'apptype:push'}), or by configuring
|
11
|
+
everything manually.
|
12
|
+
DESC
|
13
|
+
task :list do
|
14
|
+
puts_title "Available application types"
|
15
|
+
puts_list AppType.all, :id => :short, :table => true do |t|
|
16
|
+
t.col("Name", :name)
|
17
|
+
t.col("Stock?") { |t| '*' if t.stock? }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
desc <<-DESC
|
22
|
+
Display brief details about an application type.
|
23
|
+
DESC
|
24
|
+
task :show do
|
25
|
+
id = require_id!
|
26
|
+
display(AppType.find(id))
|
27
|
+
end
|
28
|
+
|
29
|
+
desc <<-DESC
|
30
|
+
Import files from an existing type into the current working directory.
|
31
|
+
|
32
|
+
This is usually the first step in creating a custom type. It's usually
|
33
|
+
followed by the customisation of the imported files, and finalised with a
|
34
|
+
#{qcommand 'apptype:push'}.
|
35
|
+
DESC
|
36
|
+
task :import do
|
37
|
+
id = require_id!
|
38
|
+
|
39
|
+
type = AppType.find(id)
|
40
|
+
puts_title "Importing type #{q type}"
|
41
|
+
|
42
|
+
# (utilities)
|
43
|
+
secure_write = lambda do |path, contents|
|
44
|
+
path = path.gsub(/\{(.*?)\}/) { $1.split(',').first }
|
45
|
+
if File.exist?(path) || File.exist?(path.downcase)
|
46
|
+
$stderr.puts "Warning: skipping #{q path} as it already exists"
|
47
|
+
else
|
48
|
+
puts "Writing #{q path}"
|
49
|
+
File.open(path, 'w') { |f| f << contents }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Files
|
54
|
+
file_map_for_type.each do |attribute, glob|
|
55
|
+
secure_write[glob, type.send(attribute)]
|
56
|
+
end
|
57
|
+
|
58
|
+
# Metadata
|
59
|
+
filter_out = ['id', 'stock?', *file_map_for_type.stringify_keys.keys]
|
60
|
+
data =
|
61
|
+
type.attributes.
|
62
|
+
stringify_keys.
|
63
|
+
except(*filter_out).
|
64
|
+
merge('name' => name_of_current, 'short' => name_of_current)
|
65
|
+
secure_write["meta.yml", YAML.dump(data)]
|
66
|
+
|
67
|
+
# Services
|
68
|
+
type.service_configs.each do |service|
|
69
|
+
file_map_for_service.each do |attribute, glob|
|
70
|
+
path = ([service.name, glob] - %w(main)).join('_')
|
71
|
+
data = service.send(attribute)
|
72
|
+
secure_write[path, data]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
desc <<-DESC
|
78
|
+
Create a new application type or update an existing one. The files in the
|
79
|
+
current working directory are used to build a new or updated type which is
|
80
|
+
then registered under the name of that directory.
|
81
|
+
|
82
|
+
Creating a new type usually goes somewhat like this:
|
83
|
+
$ mkdir foo-type && cd foo-type
|
84
|
+
$ #{command "apptype:import"} ID=sinatra
|
85
|
+
# ...Edit the imported files...
|
86
|
+
$ #{command "apptype:push"}
|
87
|
+
# ...Edit the files..
|
88
|
+
$ #{command "apptype:push"}
|
89
|
+
|
90
|
+
Custom types can be used like the stock ones. For a new site:
|
91
|
+
$ #{command "website:declare"} TYPE=foo
|
92
|
+
|
93
|
+
Or, for an existing site:
|
94
|
+
$ cd path/to/some-site
|
95
|
+
$ #{command "website:apptype:switch"} ID=foo
|
96
|
+
$ #{command "website:backends:reprepare"}
|
97
|
+
|
98
|
+
To update a type:
|
99
|
+
$ cd foo-type
|
100
|
+
# ...Edit the files...
|
101
|
+
$ #{command "apptype:push"}
|
102
|
+
# ...For each website of that type...
|
103
|
+
$ #{command "website:backends:reprepare"}
|
104
|
+
DESC
|
105
|
+
task :push do
|
106
|
+
if type = AppType[name_of_current]
|
107
|
+
update type
|
108
|
+
else
|
109
|
+
create type
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
desc <<-DESC
|
114
|
+
Delete an existing type. All website of this type must have been deleted
|
115
|
+
before running this tasks.
|
116
|
+
DESC
|
117
|
+
task :delete do
|
118
|
+
id = require_id!
|
119
|
+
type = AppType.find(id)
|
120
|
+
begin
|
121
|
+
type.destroy
|
122
|
+
rescue WebService::ResourceConflict
|
123
|
+
error! $!.response.data['errors']
|
124
|
+
else
|
125
|
+
puts_title "Application type deleted"
|
126
|
+
puts "Application type #{q type} deleted successfully."
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def require_id!
|
131
|
+
ENV["ID"] or
|
132
|
+
error! <<-MSG
|
133
|
+
The ID or short name of the application type to deal with must be
|
134
|
+
specified in the $ID environment variable. To list all application
|
135
|
+
types, run #{qcommand "apptype:list"}.
|
136
|
+
MSG
|
137
|
+
end
|
138
|
+
|
139
|
+
def build_type_from_cwd
|
140
|
+
type = AppType.new
|
141
|
+
dir = Pathname.pwd
|
142
|
+
|
143
|
+
# (utilities)
|
144
|
+
assign_to = lambda do |object, map|
|
145
|
+
map.each do |attribute, glob|
|
146
|
+
file = Pathname.glob(dir.join(glob)).first
|
147
|
+
object.write_attribute(attribute, file.read) if file
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Metadata
|
152
|
+
meta = dir.join("meta.yml")
|
153
|
+
type.attributes = YAML.load(meta.read) if meta.file?
|
154
|
+
|
155
|
+
# Here is not really the place to assign a default name and short name but
|
156
|
+
# since callers of this method expect these attributes to be set, I think
|
157
|
+
# it's OK.
|
158
|
+
type.name = name_of_current unless type.attribute_set?(:name)
|
159
|
+
type.short = type.name unless type.attribute_set?(:short)
|
160
|
+
|
161
|
+
# Files
|
162
|
+
assign_to[type, file_map_for_type]
|
163
|
+
|
164
|
+
# Build the main ServiceConfig:
|
165
|
+
# start.sh => main.start_script
|
166
|
+
# finish.sh => main.finish_script
|
167
|
+
if file_map_for_service.values.any? { |f| File.file? f }
|
168
|
+
main = ServiceConfig.new(:name => "main")
|
169
|
+
assign_to[main, file_map_for_service]
|
170
|
+
end
|
171
|
+
|
172
|
+
# Build other ServiceConfig's:
|
173
|
+
# foo_start.sh => foo.start_script
|
174
|
+
# foo_install.sh => foo.finish_script
|
175
|
+
other =
|
176
|
+
Pathname.glob(dir.join("*_{#{file_map_for_service.values * ','}}")).
|
177
|
+
group_by { |p| p.basename.to_s.split('_', 2).first }.
|
178
|
+
map do |name, files|
|
179
|
+
service = ServiceConfig.new(:name => name)
|
180
|
+
file_map =
|
181
|
+
file_map_for_service.inject({}) do |m, (attr, file)|
|
182
|
+
if path = files.find { |path| path.basename.to_s.split('_', 2).last == file }
|
183
|
+
m[attr] = path
|
184
|
+
end
|
185
|
+
m
|
186
|
+
end
|
187
|
+
assign_to[service, file_map]
|
188
|
+
service
|
189
|
+
end
|
190
|
+
|
191
|
+
return type, [main, *other].compact
|
192
|
+
end
|
193
|
+
|
194
|
+
def display(type)
|
195
|
+
puts "Name: #{type.name}"
|
196
|
+
puts "Short name: #{type.short}"
|
197
|
+
puts "Stock?: #{type.stock? ? 'Yes' : 'No'}"
|
198
|
+
puts "Created: #{type.created_at}"
|
199
|
+
puts "Updated: #{type.updated_at}"
|
200
|
+
puts "Detection: #{(d = type.detection) ? "test -#{d}" : '(none)'}"
|
201
|
+
puts "Install script: (#{type.install_script.to_s.length} bytes)"
|
202
|
+
puts "Webserver conf.: (#{type.webserver_conf.to_s.length} bytes)"
|
203
|
+
puts "Capfile tmpl.: (#{type.capfile.to_s.length} bytes)"
|
204
|
+
if type.service_configs.any?
|
205
|
+
puts "Services:"
|
206
|
+
type.service_configs.each do |service|
|
207
|
+
puts "- #{service}"
|
208
|
+
end
|
209
|
+
else
|
210
|
+
puts "Services: (none)"
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def name_of_current
|
215
|
+
ENV["NAME"] || clean_name(Pathname.pwd.expand_path.basename.to_s.gsub(/^type-|-type$/, ""))
|
216
|
+
end
|
217
|
+
|
218
|
+
def file_map_for_type
|
219
|
+
{ :root_install_script => "root_install.sh", :install_script => "install.sh",
|
220
|
+
:webserver_conf => "nginx.conf", :capfile => "{C,c}apfile" }
|
221
|
+
end
|
222
|
+
|
223
|
+
def file_map_for_service
|
224
|
+
{ :start_script => "start.sh", :finish_script => "finish.sh" }
|
225
|
+
end
|
226
|
+
|
227
|
+
def create(type)
|
228
|
+
type, services = build_type_from_cwd
|
229
|
+
|
230
|
+
try_save type do
|
231
|
+
puts_title "Application type created"
|
232
|
+
puts "Creating associated service configurations..."
|
233
|
+
services.each do |service|
|
234
|
+
service.app_type = type
|
235
|
+
try_save service do
|
236
|
+
puts "- Created service #{q service}"
|
237
|
+
end
|
238
|
+
end
|
239
|
+
puts
|
240
|
+
display(type.reload)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def update(type)
|
245
|
+
updated, services = build_type_from_cwd
|
246
|
+
updated.attributes.except('id').each do |attribute, value|
|
247
|
+
type.write_attribute(attribute, value)
|
248
|
+
end
|
249
|
+
|
250
|
+
try_save type do
|
251
|
+
puts_title "Application type updated"
|
252
|
+
puts "Updating associated service configurations..."
|
253
|
+
type.service_configs.each &:destroy
|
254
|
+
services.each do |service|
|
255
|
+
service.app_type = type
|
256
|
+
try_save service do
|
257
|
+
puts "- Updated service #{q service}"
|
258
|
+
end
|
259
|
+
end
|
260
|
+
puts
|
261
|
+
display(type.reload)
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
desc <<-DESC
|
2
|
+
Open a new shell in the specified #{resource_type}'s UNIX account.
|
3
|
+
For password-less access, be sure to run #{qcommand "sshkey:register"}
|
4
|
+
first.
|
5
|
+
|
6
|
+
Environment variables:
|
7
|
+
(optional) $ID: the ID of the #{resource_type} to connect to.
|
8
|
+
(optional) $AS: the login of the UNIX account to connect as.
|
9
|
+
DESC
|
10
|
+
namespace :connect do
|
11
|
+
task :default do
|
12
|
+
resource = fetch_current
|
13
|
+
ssh_details_for(resource).connect
|
14
|
+
end
|
15
|
+
|
16
|
+
desc <<-DESC
|
17
|
+
Display SSH connection details. You can then use these details to connect
|
18
|
+
to the #{resource_type} either from the command line (using the standard
|
19
|
+
`ssh', `scp' or `sftp' tool suite), programmatically using Net::SSH, or
|
20
|
+
graphically using WinSCP or Cyberduck.
|
21
|
+
|
22
|
+
Environment variables:
|
23
|
+
(optional) $ID: the ID of the #{resource_type} to get the
|
24
|
+
connection details for.
|
25
|
+
DESC
|
26
|
+
task :details do
|
27
|
+
resource = fetch_current
|
28
|
+
details = ssh_details_for(resource)
|
29
|
+
|
30
|
+
puts_long <<-EOS
|
31
|
+
SSH connection details for #{resource_type} #{resource}:
|
32
|
+
* Host: #{details.host}
|
33
|
+
* Port: #{details.port}
|
34
|
+
* Login: #{details.login}
|
35
|
+
|
36
|
+
Remote command execution:
|
37
|
+
$ ssh -p #{details.port} #{details.login}@#{details.host} <command>
|
38
|
+
|
39
|
+
File upload:
|
40
|
+
$ scp -P #{details.port} #{details.login}@#{details.host} <local-path> <remote-path>
|
41
|
+
EOS
|
42
|
+
end
|
43
|
+
|
44
|
+
# Allow for defining a method named `command'.
|
45
|
+
class << self
|
46
|
+
undef_method :command if method_defined? :command
|
47
|
+
end
|
48
|
+
|
49
|
+
desc <<-DESC
|
50
|
+
Output an SSH connection command, ready to run.
|
51
|
+
|
52
|
+
Environment variables:
|
53
|
+
(optional) $ID: the ID of the #{resource_type} to get the connection
|
54
|
+
command for.
|
55
|
+
DESC
|
56
|
+
task :command do
|
57
|
+
resource = fetch_current
|
58
|
+
puts ssh_details_for(resource).connection_command
|
59
|
+
end
|
60
|
+
|
61
|
+
def ssh_details_for(resource)
|
62
|
+
details = resource.ssh_details
|
63
|
+
details.login = ENV["AS"] || 'root' unless details.attribute_set? :login
|
64
|
+
|
65
|
+
# Return `details.connection' instead of `details' so that methods
|
66
|
+
# of both SshDetails and SshDetails::AccountBinding are made
|
67
|
+
# available from the same object.
|
68
|
+
details.connection
|
69
|
+
rescue WebService::NotAcceptable
|
70
|
+
vps = resource.attribute_set?(:vps) ? resource.vps : resource
|
71
|
+
reference = vps == resource ? "VPS" : "#{resource_type}'s VPS"
|
72
|
+
error! <<-MSG
|
73
|
+
The SSH port of the #{reference} (#{q vps}) is not accessible
|
74
|
+
from the Internet. You need to open it in order to be able
|
75
|
+
to connect to the #{resource_type}'s UNIX account. To do so,
|
76
|
+
run #{qcommand "vps:firewall:open PORT=22"}.
|
77
|
+
MSG
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
desc "Alias for task `connect'."
|
82
|
+
task(:enter) { connect.default }
|
83
|
+
|
84
|
+
desc <<-DESC
|
85
|
+
Run a command in the #{resource_type} environment. Default working
|
86
|
+
directory is the home directory.
|
87
|
+
|
88
|
+
Environment variables:
|
89
|
+
(mandatory) $CMD: the command to run.
|
90
|
+
|
91
|
+
(optional) $ID: the ID of the #{resource_type} to run the command
|
92
|
+
in.
|
93
|
+
DESC
|
94
|
+
task :run do
|
95
|
+
cmd = ENV['CMD'] or error! <<-MSG
|
96
|
+
The command to execute must be specified in the $CMD environment
|
97
|
+
variable.
|
98
|
+
MSG
|
99
|
+
resource = fetch_current
|
100
|
+
connect.ssh_details_for(resource).run(cmd)
|
101
|
+
end
|
102
|
+
|
103
|
+
desc <<-DESC
|
104
|
+
Upload a local file to the home directory of the #{resource_type}.
|
105
|
+
|
106
|
+
Environment variables:
|
107
|
+
(mandatory) $FILE: the path to the file to upload.
|
108
|
+
|
109
|
+
(optional) $ID: the ID of the #{resource_type} to upload the
|
110
|
+
file to.
|
111
|
+
DESC
|
112
|
+
task :upload do
|
113
|
+
files = ENV['FILE'] || ENV['FILES'] or error! <<-MSG
|
114
|
+
The file to upload must be specified in the $FILE environment
|
115
|
+
variable.
|
116
|
+
MSG
|
117
|
+
resource = fetch_current
|
118
|
+
connect.ssh_details_for(resource).upload(*Dir[files])
|
119
|
+
end
|
120
|
+
|
121
|
+
namespace :rubygems do
|
122
|
+
desc <<-DESC
|
123
|
+
Install or update RubyGems (latest version) on the UNIX account of the
|
124
|
+
specified #{resource_type}. Ruby will be installed if necessary (latest
|
125
|
+
1.8).
|
126
|
+
|
127
|
+
Once you have done that, you can connect to the #{resource_type}'s
|
128
|
+
UNIX account and start installing gems. For example:
|
129
|
+
$ #{command "db:server:rubygems:install"}
|
130
|
+
$ #{command "progress"}
|
131
|
+
$ #{command "db:server:enter"}
|
132
|
+
mysql@filiberto ~ $ gem install rails
|
133
|
+
|
134
|
+
Environment variables:
|
135
|
+
(optional) $ID: the ID of the #{resource_type} to install RubyGems onto.
|
136
|
+
Default: the last #{resource_type}.
|
137
|
+
DESC
|
138
|
+
task :install do
|
139
|
+
resource = fetch_current
|
140
|
+
|
141
|
+
resource.post(:rubygems)
|
142
|
+
|
143
|
+
puts_title "Request sent"
|
144
|
+
puts "RubyGems (latest version) has been scheduled for installation or update on #{resource_type} #{resource}."
|
145
|
+
end
|
146
|
+
|
147
|
+
desc "Alias for `install'."
|
148
|
+
task(:setup) { install }
|
149
|
+
|
150
|
+
desc <<-DESC
|
151
|
+
Update RubyGems to the latest version. Only the package management
|
152
|
+
system is updated, not the gems themselves.
|
153
|
+
|
154
|
+
Environment variables:
|
155
|
+
(optional) $ID: the ID of the #{resource_type} to update RubyGems on.
|
156
|
+
Default: the last #{resource_type}.
|
157
|
+
DESC
|
158
|
+
task(:update) do
|
159
|
+
install
|
160
|
+
end
|
161
|
+
end
|