Flucti-flucti-cli 0.1.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/LICENSE +7 -0
  2. data/README.mdown +24 -0
  3. data/Rakefile +56 -0
  4. data/TODO.txt +19 -0
  5. data/bin/flucti +4 -0
  6. data/flucti-cli.gemspec +46 -0
  7. data/lib/flucti.rb +20 -0
  8. data/lib/flucti/api_access.rb +56 -0
  9. data/lib/flucti/cli.rb +107 -0
  10. data/lib/flucti/parameters.rb +37 -0
  11. data/lib/flucti/resources.rb +41 -0
  12. data/lib/flucti/resources/account.rb +7 -0
  13. data/lib/flucti/resources/app_type.rb +18 -0
  14. data/lib/flucti/resources/backend.rb +28 -0
  15. data/lib/flucti/resources/basic_resource.rb +16 -0
  16. data/lib/flucti/resources/container.rb +15 -0
  17. data/lib/flucti/resources/db_server.rb +38 -0
  18. data/lib/flucti/resources/domain.rb +8 -0
  19. data/lib/flucti/resources/general.rb +14 -0
  20. data/lib/flucti/resources/mail_client.rb +7 -0
  21. data/lib/flucti/resources/mail_server.rb +7 -0
  22. data/lib/flucti/resources/port_forwarding.rb +15 -0
  23. data/lib/flucti/resources/port_forwarding/services +13921 -0
  24. data/lib/flucti/resources/ssh_details.rb +96 -0
  25. data/lib/flucti/resources/webserver.rb +9 -0
  26. data/lib/flucti/resources/website.rb +9 -0
  27. data/lib/flucti/tasks.rb +3 -0
  28. data/lib/flucti/tasks/apikey_tasks.rb +57 -0
  29. data/lib/flucti/tasks/apptype_tasks.rb +264 -0
  30. data/lib/flucti/tasks/connect_pack.rb +161 -0
  31. data/lib/flucti/tasks/db_tasks.rb +158 -0
  32. data/lib/flucti/tasks/mail_tasks.rb +104 -0
  33. data/lib/flucti/tasks/miscellaneous_tasks.rb +6 -0
  34. data/lib/flucti/tasks/progress_tasks.rb +24 -0
  35. data/lib/flucti/tasks/sshkey_tasks.rb +151 -0
  36. data/lib/flucti/tasks/vps/firewall_tasks.rb +84 -0
  37. data/lib/flucti/tasks/vps_tasks.rb +37 -0
  38. data/lib/flucti/tasks/webserver_tasks.rb +154 -0
  39. data/lib/flucti/tasks/website/apptype_tasks.rb +42 -0
  40. data/lib/flucti/tasks/website/backends/instances_tasks.rb +37 -0
  41. data/lib/flucti/tasks/website/backends_tasks.rb +254 -0
  42. data/lib/flucti/tasks/website/capfile_tasks.rb +41 -0
  43. data/lib/flucti/tasks/website/domains_tasks.rb +107 -0
  44. data/lib/flucti/tasks/website_tasks.rb +221 -0
  45. data/lib/flucti/utilities.rb +20 -0
  46. data/lib/flucti/utilities/connection_error_handling.rb +50 -0
  47. data/lib/flucti/utilities/core_ext.rb +35 -0
  48. data/lib/flucti/utilities/list_displayer.rb +57 -0
  49. data/lib/flucti/utilities/miscellaneous.rb +65 -0
  50. data/lib/flucti/utilities/progress_bar.rb +17 -0
  51. data/lib/flucti/utilities/table.rb +25 -0
  52. data/lib/flucti/utilities/task_packing.rb +10 -0
  53. data/lib/flucti/utilities/user_interface.rb +117 -0
  54. data/lib/vendor/ruby-progressbar-0.9/lib/ChangeLog +113 -0
  55. data/lib/vendor/ruby-progressbar-0.9/lib/progressbar.en.rd +103 -0
  56. data/lib/vendor/ruby-progressbar-0.9/lib/progressbar.ja.rd +100 -0
  57. data/lib/vendor/ruby-progressbar-0.9/lib/progressbar.rb +236 -0
  58. data/lib/vendor/ruby-progressbar-0.9/lib/test.rb +105 -0
  59. data/test/flucti/resources_test.rb +32 -0
  60. data/test/flucti/tasks_test.rb +28 -0
  61. data/test/flucti/utilities/miscellaneous_test.rb +54 -0
  62. data/test/flucti/utilities/table_test.rb +28 -0
  63. data/test/flucti/utilities/user_interface_test.rb +161 -0
  64. data/test/test_helper.rb +5 -0
  65. metadata +221 -0
@@ -0,0 +1,84 @@
1
+ namespace :vps do
2
+ namespace :firewall do
3
+ # Allow for defining task named `open':
4
+ class << self
5
+ undef_method :open if private_method_defined? :open
6
+ end
7
+
8
+ task(:default) { rules }
9
+
10
+ desc <<-DESC
11
+ Open a port from the Internet to a VPS. For example, if you choose to
12
+ open port 443 of a VPS foo, it will be accessible from the Internet on a
13
+ port greater than or equal to 1025, like 1047. As a result, packets
14
+ would arrive like this:
15
+
16
+ Internet <=> (Firewall:1047) <=> VPS:443
17
+
18
+ Following on this example:
19
+ $ #{command 'vps:firewall:open'} PORT=443
20
+
21
+ As 443 is the default HTTP SSL port number, you can a host site over SSL
22
+ and it will be accessible from the Internet on port 1047, for example at
23
+ https://www.example.com:1047.
24
+
25
+ By default, a port is open from the Internet. But you can restrict the
26
+ access to only one or several of your VPS's by specifying the $FROM
27
+ variable.
28
+
29
+ For example, to open port 3306 (MySQL) of VPS #{q 'trever'} to another
30
+ VPS #{q 'christina'}:
31
+ $ #{command 'vps:firewall:open'} FROM=christina ID=trever PORT=3306
32
+
33
+ Environment variables:
34
+ (mandatory) $PORT: the destination port number on the VPS, in the form
35
+ of <port>[:<protocol>], where <protocol> is optional
36
+ and is one of TCP or UDP, TCP being the default if
37
+ none is specified. For example:
38
+ $ #{command 'vps:firewall:open'} PORT=443
39
+ Is equivalent to:
40
+ $ #{command 'vps:firewall:open'} PORT=443:TCP
41
+
42
+ (optional) $FROM: the origin to grant access to. It can be either a
43
+ VPS identified by its hostname, or the Internet,
44
+ identified by #{q 'internet'}. Valid values
45
+ include:
46
+ FROM=internet # default
47
+ Or:
48
+ FROM=foo # VPS whose hostname is `foo'
49
+
50
+ (optional) $ID: the ID or hostname of the destination VPS.
51
+ Default: the newest VPS.
52
+ DESC
53
+ task :open do
54
+ vps = top.vps.fetch_current
55
+ port = ENV["PORT"] or error! <<-MSG
56
+ The destination port must be specified in the $PORT environment
57
+ variable, in the form of <port>[:<protocol>].
58
+ Examples: PORT=443, PORT=443:TCP, PORT=53:UDP
59
+ MSG
60
+ sources = (ENV['FROM'] || 'internet').split(',')
61
+ number, protocol = port.to_s.split(':')
62
+ protocol ||= 'TCP'
63
+ fwd = vps.port_forwardings.build :sources => sources, :to => number, :protocol => protocol
64
+ try_save fwd do
65
+ puts_title "Request sent"
66
+ puts "#{fwd.protocol.to_s.upcase} port #{fwd.to} has been scheduled for opening on VPS #{vps}."
67
+ end
68
+ end
69
+
70
+ desc <<-DESC
71
+ List all firewall rules defined for a given VPS.
72
+ DESC
73
+ task :rules do
74
+ vps = top.vps.fetch_current
75
+
76
+ puts_title "Firewall rules for VPS #{q vps}"
77
+ puts_list vps.port_forwardings.all, :table => true do |t|
78
+ t.col("Service") { |f| f.service_name || "(#{f.protocol})" }
79
+ t.col("Sources") { |f| "{#{f.simplified_sources * ', '}}:#{f.from}" }
80
+ t.col("Destination") { |f| "#{f.target}:#{f.to}" }
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,37 @@
1
+ namespace :vps do
2
+ task(:default) { list }
3
+ import_pack(:connect, self, "VPS")
4
+
5
+ desc <<-DESC
6
+ Display a list of all your VPS's.
7
+ DESC
8
+ task :list do
9
+ puts_title "All VPS's"
10
+ puts_list VPS.all do |t|
11
+ t.col("Hostname", :hostname)
12
+ t.col("Memory capacity") { |s| "#{s.memory_capacity} MB" }
13
+ t.col("Disk capacity") { |s| "#{s.disk_capacity} GB" }
14
+ t.col("Status") { |s| s.status.to_s.capitalize }
15
+ end
16
+ end
17
+
18
+ desc <<-DESC
19
+ Display a list of hostsnames of all your VPS's. Useful for refering to
20
+ these programmatically.
21
+ DESC
22
+ task :hostnames do
23
+ VPS.all.each { |vps| puts vps.hostname }
24
+ end
25
+
26
+ desc <<-DESC
27
+ List all users (UNIX accounts) that have a home folder.
28
+ DESC
29
+ task :users do
30
+ ENV['CMD'] = "ls /home | grep -v ^_"
31
+ run
32
+ end
33
+
34
+ def fetch_current
35
+ (id = ENV["ID"] || ENV["ON"]) ? VPS.find(id) : VPS.last
36
+ end
37
+ end
@@ -0,0 +1,154 @@
1
+ namespace :webserver do
2
+ task(:default) { list }
3
+ import_pack(:connect, self, "webserver")
4
+
5
+ desc <<-DESC
6
+ List all webservers on either a particular VPS, or all VPS's.
7
+
8
+ Environment variables:
9
+ (optional) $ON: hostname of the VPS to list the webservers of.
10
+ Default: the newest VPS.
11
+ DESC
12
+ task :list do
13
+ puts_title "All webservers"
14
+ puts_list particular_set_of_webservers || Webserver.all, :title => :vps do |t|
15
+ t.col("Running on", :vps)
16
+ t.col("Running as", :user)
17
+ end
18
+ end
19
+
20
+ desc <<-DESC
21
+ Make sure that at least one webserver is present. Failing that, one is set
22
+ up automatically on the newest VPS.
23
+ DESC
24
+ task :ensure_any do
25
+ if (count = Webserver.all.size).zero?
26
+ setup
27
+ else
28
+ puts_title "Webserver present"
29
+ puts "There exists #{count} webservers."
30
+ end
31
+ end
32
+
33
+ desc <<-DESC
34
+ Set up a new webserver on a given VPS. Automatically installs and
35
+ configures the necessary software, running as its own UNIX user for
36
+ security purposes.
37
+
38
+ Environment variables:
39
+ (optional) $ON: hostname of the VPS to set up the webserver onto.
40
+ Default: the newest VPS.
41
+ DESC
42
+ task :setup do
43
+ vps = particular_vps || VPS.last
44
+ webserver = vps.webservers.build
45
+
46
+ try_save webserver do
47
+ puts_title "Request sent"
48
+ puts "Webserver successfully scheduled for setup on VPS #{q vps}."
49
+ end
50
+ end
51
+
52
+ desc <<-DESC
53
+ Force the setup procedure of a webserver to be re-run. You might want to
54
+ do that if you think you messed up the automatic configuration, UNIX
55
+ account or software installations.
56
+
57
+ Environment variables:
58
+ (optional) $ID: the ID of the webserver (get the list with #{qcommand "webserver:list"}).
59
+ Default: the webserver last set up.
60
+ DESC
61
+ task :reprepare do
62
+ fetch_current.put(:reprepare)
63
+ puts_title "Request sent"
64
+ puts "Webserver has been scheduled for re-preparation."
65
+ end
66
+
67
+ desc <<-DESC
68
+ Force the reconfiguration of a webserver. You might want to do that if you
69
+ think you messed up the automatic configuration.
70
+
71
+ Environment variables:
72
+ (optional) $ID: the ID of the webserver (get the list with #{qcommand "webserver:list"}).
73
+ Default: the webserver last set up.
74
+ DESC
75
+ task :reconfigure do
76
+ fetch_current.put(:reconfigure)
77
+ puts_title "Request sent"
78
+ puts "Webserver has been scheduled for re-configuration."
79
+ end
80
+
81
+ desc <<-DESC
82
+ List all websites hosted by a given webserver. That is, websites served
83
+ via the webserver by having one of their backends running on the same VPS
84
+ as the webserver.
85
+
86
+ Environment variables:
87
+ $ID: the ID of the webserver (get the list with #{qcommand "webserver:list"}).
88
+ DESC
89
+ task :hosted do
90
+ webserver = fetch_current!
91
+
92
+ puts_title "Hosted websites"
93
+ top.website.backends.show_list(webserver.backends.all)
94
+ end
95
+
96
+ desc <<-DESC
97
+ Remove a webserver. This is accomplished by stopping all services run as
98
+ the UNIX user of the webserver, and then suppressing that user.
99
+
100
+ No data is lost: as with any automated suppression of a UNIX user, the
101
+ home folder is backed up at /home/_foo, where "foo" is the name newly
102
+ suppressed user.
103
+
104
+ The removal of a webserver is only allowed once all the backends of the
105
+ sites it serves have been removed (or switched webservers by way of
106
+ removal followed by re-creation). Get the list of all these remaining
107
+ backends with #{qcommand "webserver:hosted"}.
108
+
109
+ Environment variables:
110
+ $ID: the ID of the webserver (get the list with #{qcommand "webserver:list"}).
111
+ DESC
112
+ task :remove do
113
+ webserver = fetch_current!
114
+
115
+ begin
116
+ webserver.destroy
117
+ rescue WebService::ResourceConflict
118
+ error! $!.response.data['errors']
119
+ else
120
+ puts_title "Request sent"
121
+ puts "Webserver on VPS #{q webserver.vps} has been scheduled for removal."
122
+ end
123
+ end
124
+
125
+ def require_id!
126
+ ENV["ID"] or
127
+ error! <<-MSG
128
+ The ID of the webserver to deal with must be specified by setting the
129
+ $ID environment variable. To list all webservers, run
130
+ #{qcommand "webserver:list"}.
131
+ MSG
132
+ end
133
+
134
+ def fetch_current
135
+ particular_webserver || Webserver.last
136
+ end
137
+
138
+ def fetch_current!
139
+ id = require_id!
140
+ Webserver.find(id)
141
+ end
142
+
143
+ def particular_vps
144
+ VPS.find(ENV["ON"]) if ENV["ON"]
145
+ end
146
+
147
+ def particular_webserver
148
+ Webserver.find(ENV["ID"]) if ENV["ID"]
149
+ end
150
+
151
+ def particular_set_of_webservers
152
+ vps = particular_vps and vps.webservers
153
+ end
154
+ end
@@ -0,0 +1,42 @@
1
+ namespace :website do
2
+ namespace :apptype do
3
+ desc <<-DESC
4
+ Change the application type of the website in the current working
5
+ directory.
6
+
7
+ When declaring a website, a type is (automatically or explicitely)
8
+ assigned to it. It is possible to change that initial choice with this
9
+ task:
10
+ $ cd path/to/foo
11
+ $ #{command "website:apptype:switch"} ID=custom
12
+ $ #{command "website:backends:reprepare"}
13
+
14
+ Environment variables:
15
+ $ID: the ID or short name of the application type to switch to.
16
+ DESC
17
+ task :switch do
18
+ website, type_id = fetch_current!, require_id!
19
+
20
+ website.app_type = AppType.find(type_id)
21
+
22
+ try_save website do
23
+ puts_title "Application type switched"
24
+ puts_long <<-MSG
25
+ * The application type of website #{q website} has been successfully
26
+ switched to #{q website.app_type}.
27
+
28
+ * Remember to re-reprepare all backends with
29
+ #{qcommand "website:backends:reprepare"}.
30
+ MSG
31
+ end
32
+ end
33
+
34
+ def require_id!
35
+ ENV["ID"] || ENV["TO"] or
36
+ error! <<-MSG
37
+ The ID or short name of the application type to switch to must be
38
+ specified by setting the $ID environment variable.
39
+ MSG
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,37 @@
1
+ namespace :website do
2
+ namespace :backends do
3
+ namespace :instances do
4
+ environment_variables = <<-EOS
5
+ Environment variables:
6
+ (optional) $ID: the ID of the backend to alter.
7
+ Default: the backend last added.
8
+ EOS
9
+
10
+ desc <<-DESC
11
+ Bump up the number of instances a backend. This adds one more process to
12
+ the backend, allowing for processing more requests per second, but also
13
+ consuming more memory.
14
+
15
+ The default is 1. This tasks increments the current number by 1.
16
+
17
+ #{environment_variables}
18
+ DESC
19
+ task :more do
20
+ backend = backends.fetch_current
21
+ backend.post(:instances)
22
+ puts "Number of instances of backend #{backend} scheduled for increase."
23
+ end
24
+
25
+ desc <<-DESC
26
+ Decrease the number of instances of a backend.
27
+
28
+ #{environment_variables}
29
+ DESC
30
+ task :less do
31
+ backend = backends.fetch_current
32
+ backend.delete(:instances)
33
+ puts "Number of instances of backend #{backend} scheduled for decrease."
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,254 @@
1
+ namespace :website do
2
+ namespace :backends do
3
+ # Allow for defining tasks named `test' and `try':
4
+ class << self
5
+ undef_method :test if private_method_defined? :test
6
+ undef_method :try if public_method_defined? :try
7
+ end
8
+
9
+ task(:default) { list }
10
+ import_pack(:connect, self, "backend")
11
+
12
+ desc <<-DESC
13
+ List all the backends currently running the site.
14
+ DESC
15
+ task :list do
16
+ site = website.fetch_current!
17
+
18
+ puts_title "Backends of website: #{site}"
19
+ show_list(site.backends.all)
20
+ end
21
+
22
+ desc <<-DESC
23
+ Prepare a VPS for hosting the current website. So is done by creating a
24
+ dedicated UNIX user to run the site as, enclosing processes related to
25
+ the site in a tight, secure environment. This prevents potential
26
+ security flaws to have an impact on other services run within the same
27
+ VPS.
28
+
29
+ Environment variables:
30
+ (optional) $ON: hostname of the VPS. Default: last webserver set up.
31
+ (optional) $AS: UNIX account to run the services as. Default: the
32
+ name of the website.
33
+ DESC
34
+ task :add do
35
+ site = website.fetch_current!
36
+
37
+ unless webserver = (ENV["ON"] ? (vps = VPS.find(ENV["ON"])).webservers : Webserver).last
38
+ message = vps ? "No webserver seems to be running on VPS #{q vps}." : "You have no webserver set up yet."
39
+ message << " Set one up first by running #{qcommand "webserver:setup"}."
40
+ error! message
41
+ end
42
+
43
+ # Backend: ties together the site to the webserver
44
+ backend = site.backends.build(:webserver => webserver, :user => ENV["AS"])
45
+
46
+ try_save backend do
47
+ puts_title "Request sent"
48
+ puts_long <<-INFO
49
+ * A new backend for website #{q site} has been scheduled for setup
50
+ on VPS #{q backend.vps}.
51
+
52
+ * If you are using an automatically generated Capfile, you should
53
+ download an updated version with #{qcommand "website:capfile:download"}.
54
+
55
+ * Once the new backend is ready (check that #{qcommand "progress"}
56
+ is at 100%), you should re-deploy to push the site to it. If you
57
+ are using a Capfile, this is done with #{q "cap deploy:setup"}
58
+ followed by #{q "cap deploy"}.
59
+ INFO
60
+ end
61
+ end
62
+
63
+ desc <<-DESC
64
+ Force a re-configuration of a backend. Checks the UNIX user,
65
+ re-generates configuration files and reloads the webserver's
66
+ configuration if necessary.
67
+
68
+ Environment variables:
69
+ (optional) $ID: the ID of a particular backend (get the list with
70
+ #{qcommand "website:backends:list"}).
71
+
72
+ Default: all backends of the current websites are
73
+ scheduled for reconfiguration.
74
+ DESC
75
+ task :reconfigure do
76
+ backends = fetch_several
77
+
78
+ puts_title "Sending requests"
79
+ backends.each do |backend|
80
+ backend.put(:reconfigure)
81
+ puts "Backend #{backend} has been scheduled for reconfiguration."
82
+ end
83
+ end
84
+
85
+ desc <<-DESC
86
+ Force a re-preparation of a backend.
87
+
88
+ This re-runs the whole preparation process that was performed at
89
+ addition time. It's necessary to perform the preparation again when
90
+ switching application types of a website. For example:
91
+ $ cd path/to/foo
92
+ $ #{command "website:apptype:switch"} ID=custom
93
+ $ #{command "website:backends:reprepare"}
94
+ DESC
95
+ task :reprepare do
96
+ backends = fetch_several
97
+
98
+ puts_title "Sending requests"
99
+ backends.each do |backend|
100
+ backend.put(:reprepare)
101
+ puts "Backend #{backend} has been scheduled for re-preparation."
102
+ end
103
+ end
104
+
105
+ task :test do
106
+ backend = fetch_current
107
+ execute = lambda { |command| top.webserver.connect.ssh_details_for(webserver).execute(command) }
108
+
109
+ # Check for errors during installations
110
+ puts_title "Installations"
111
+ none, display = true, lambda do |error, script_name|
112
+ begin
113
+ error = backend.send(error)
114
+ puts_subtitle "Error while executing #{q script_name}"
115
+ puts error.output
116
+ rescue WebService::ResourceNotFound
117
+ # ignore
118
+ else
119
+ none = false
120
+ end
121
+ end
122
+ display[:root_install_error, 'root_install.sh']
123
+ display[:install_error, 'install.sh']
124
+ puts "No error occured during installations." if none
125
+
126
+ # Check syntax of webserver configuration file
127
+ puts_title "Webserver configuration"
128
+ webserver = backend.webserver
129
+ command = "/usr/sbin/nginx -c $HOME/webserver/nginx_main.conf -t"
130
+ top.webserver.connect.ssh_details_for(webserver).execute(command)
131
+
132
+ # Fetch the home page locally
133
+ puts_title "Home page (beginning)"
134
+ command = "wget -q -O - localhost:#{backend.port} | head -15"
135
+ top.website.backends.connect.ssh_details_for(backend).execute(command)
136
+ end
137
+
138
+ desc <<-DESC
139
+ Detect errors on service startup. Try to run the service script
140
+ (*_start.sh of the app. type) like the service manager would, in order
141
+ to spot errors output during startup, that would otherwise land in an
142
+ error log. Eases the task of debugging why an application progress
143
+ won't start like when forgetting to install a necessary RubyGem.
144
+
145
+ Environment variables:
146
+ (optional) $ID: the ID of the backend to try to start the service within.
147
+ Default: the backend last added.
148
+ DESC
149
+ task :try do
150
+ backend = fetch_current
151
+
152
+ if type = backend.website.app_type
153
+ type.service_configs.each do |service|
154
+ run = lambda { |cmd| connect.ssh_details_for(backend).execute(cmd) }
155
+
156
+ # Display service status
157
+ dir = "$HOME/service/#{backend.website.name}-#{service.name}-1"
158
+ run["sv status #{dir}"]
159
+
160
+ # Try to start the service in the foreground to spot errors on startup
161
+ command = "#{dir}/run"
162
+ command = "p=$$; ((sleep 2; kill $p) &); #{command}" # timeout
163
+ run[command]
164
+ end
165
+ end
166
+ end
167
+
168
+ desc <<-DESC
169
+ Stop a backend and clear site-related files in a soft way.
170
+
171
+ All the files related to the associated website are not deleted, but
172
+ instead moved in a backup directory in the home folder of the backends's
173
+ UNIX account.
174
+
175
+ This UNIX account is then soft-cleared in a similar fashion. That is, it
176
+ is unregistered from the system, and its files are moved to a backup
177
+ directory under /home.
178
+
179
+ Example backup directory for a backend running as "foo":
180
+ /home/_foo
181
+
182
+ Example backup directory for a website named "bar" on this backend:
183
+ /home/_foo/_bar
184
+
185
+ That way, the VPS is kept in a clean state, and your files are still
186
+ available for you to get them back if you need them. This would most
187
+ likely be the case if the site accepts uploads stored on the filesystem,
188
+ in a subdirectory of the site's root, like RAILS_ROOT/public/images/ for
189
+ a Rails application, or wp-content/ for a WordPress blog.
190
+
191
+ Environment variables:
192
+ $ID: the ID of the backend to remove.
193
+ DESC
194
+ task :remove do
195
+ id = require_id!
196
+
197
+ website = top.website.fetch_current!
198
+ backend = website.backends.find(id)
199
+
200
+ if website.backends.all.size <= 1
201
+ confirm <<-WARN
202
+ You are about to remove the only backend left for this website. If
203
+ you do proceed with the removal, the site will become offline until
204
+ you set up a new one with #{qcommand "website:backends:add"} and
205
+ re-deploy.
206
+ WARN
207
+ end
208
+
209
+ backend.destroy
210
+
211
+ puts_title "Request sent"
212
+ puts_long <<-INFO
213
+ * Backend #{backend} has been scheduled for removal.
214
+
215
+ * If you are using the automatically generated Capfile, you should
216
+ download the updated version with #{qcommand "website:capfile:download"}.
217
+ INFO
218
+ end
219
+
220
+ def require_id!
221
+ ENV["ID"] or
222
+ error! <<-MSG
223
+ The ID of the backend to deal with must be specified by setting the
224
+ $ID environment variable. To list all backends of the current
225
+ website, run #{qcommand "website:backends"}.
226
+ MSG
227
+ end
228
+
229
+ def fetch_current!
230
+ id = require_id!
231
+ website.fetch_current!.backends.find(id)
232
+ end
233
+
234
+ def fetch_current
235
+ backends = website.fetch_current!.backends
236
+ (id = ENV["ID"]) ? backends.find(id) : backends.last
237
+ end
238
+
239
+ def fetch_several
240
+ backends = website.fetch_current!.backends
241
+ (id = ENV["ID"]) ? [backends.find(id)] : backends.all
242
+ end
243
+
244
+ def show_list(backends)
245
+ puts_list backends do |t|
246
+ t.col("Website") { |b| b.website.name }
247
+ t.col("Running on", :vps)
248
+ t.col("Running as", :user)
249
+ t.col("Instances", :num_instances)
250
+ t.col("Internal web port", :port)
251
+ end
252
+ end
253
+ end
254
+ end