foreplay 0.1.5 → 0.1.6
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +11 -2
- data/Rakefile +1 -1
- data/foreplay.gemspec +6 -6
- data/lib/foreplay/cli.rb +48 -48
- data/lib/foreplay/deploy.rb +361 -344
- data/lib/foreplay/setup.rb +33 -33
- data/lib/foreplay/utility.rb +3 -3
- data/lib/foreplay/version.rb +1 -1
- data/spec/lib/foreplay/deploy_spec.rb +130 -122
- data/spec/lib/foreplay/setup_spec.rb +13 -13
- data/spec/lib/foreplay/utility_spec.rb +28 -28
- data/spec/spec_helper.rb +2 -2
- metadata +2 -2
data/lib/foreplay/deploy.rb
CHANGED
|
@@ -1,344 +1,361 @@
|
|
|
1
|
-
require 'thor/group'
|
|
2
|
-
require 'yaml'
|
|
3
|
-
require 'net/ssh'
|
|
4
|
-
require 'net/ssh/shell'
|
|
5
|
-
require 'active_support/inflector'
|
|
6
|
-
require 'active_support/core_ext/object'
|
|
7
|
-
require 'active_support/core_ext/hash'
|
|
8
|
-
require 'colorize'
|
|
9
|
-
require 'foreplay/utility'
|
|
10
|
-
|
|
11
|
-
module Foreplay
|
|
12
|
-
class Deploy < Thor::Group
|
|
13
|
-
include Thor::Actions
|
|
14
|
-
|
|
15
|
-
argument :mode, :
|
|
16
|
-
argument :environment, :
|
|
17
|
-
argument :filters, :
|
|
18
|
-
|
|
19
|
-
DEFAULTS_KEY = 'defaults'
|
|
20
|
-
INDENT = ' ' * 4
|
|
21
|
-
|
|
22
|
-
def parse
|
|
23
|
-
# Explain what we're going to do
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
#
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
:
|
|
49
|
-
:
|
|
50
|
-
:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
defaults = Foreplay::Utility
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
next if filters.
|
|
60
|
-
|
|
61
|
-
instructions = Foreplay::Utility
|
|
62
|
-
instructions[:role] = role
|
|
63
|
-
required_keys = [:name, :environment, :role, :servers, :path, :repository]
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
servers
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
puts
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
{
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
{
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
:
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
:
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
:
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
{
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
{
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
{
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
end
|
|
1
|
+
require 'thor/group'
|
|
2
|
+
require 'yaml'
|
|
3
|
+
require 'net/ssh'
|
|
4
|
+
require 'net/ssh/shell'
|
|
5
|
+
require 'active_support/inflector'
|
|
6
|
+
require 'active_support/core_ext/object'
|
|
7
|
+
require 'active_support/core_ext/hash'
|
|
8
|
+
require 'colorize'
|
|
9
|
+
require 'foreplay/utility'
|
|
10
|
+
|
|
11
|
+
module Foreplay
|
|
12
|
+
class Deploy < Thor::Group
|
|
13
|
+
include Thor::Actions
|
|
14
|
+
|
|
15
|
+
argument :mode, type: :string, required: true
|
|
16
|
+
argument :environment, type: :string, required: true
|
|
17
|
+
argument :filters, type: :hash, required: false
|
|
18
|
+
|
|
19
|
+
DEFAULTS_KEY = 'defaults'
|
|
20
|
+
INDENT = ' ' * 4
|
|
21
|
+
|
|
22
|
+
def parse
|
|
23
|
+
# Explain what we're going to do
|
|
24
|
+
message = "#{mode.capitalize}ing #{environment.dup.yellow} environment, "
|
|
25
|
+
message += "#{explanatory_text(filters, 'role')}, #{explanatory_text(filters, 'server')}"
|
|
26
|
+
puts message
|
|
27
|
+
|
|
28
|
+
config_file = "#{Dir.getwd}/config/foreplay.yml"
|
|
29
|
+
|
|
30
|
+
begin
|
|
31
|
+
config_yml = File.read config_file
|
|
32
|
+
rescue Errno::ENOENT
|
|
33
|
+
terminate "Can't find configuration file #{config_file}.\nPlease run foreplay setup or create the file manually."
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
config_all = YAML.load(config_yml)
|
|
37
|
+
config_env = config_all[environment] || {}
|
|
38
|
+
|
|
39
|
+
# This environment
|
|
40
|
+
unless config_all.key? environment
|
|
41
|
+
terminate("No deployment configuration defined for #{environment} environment.\nCheck #{config_file}")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Establish defaults
|
|
45
|
+
# First the default defaults
|
|
46
|
+
defaults = {
|
|
47
|
+
name: File.basename(Dir.getwd),
|
|
48
|
+
environment: environment,
|
|
49
|
+
env: { 'RAILS_ENV' => environment },
|
|
50
|
+
port: 50_000
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
defaults = Foreplay::Utility.supermerge(defaults, config_all[DEFAULTS_KEY]) if config_all.key? DEFAULTS_KEY
|
|
54
|
+
defaults = Foreplay::Utility.supermerge(defaults, config_env[DEFAULTS_KEY]) if config_env.key? DEFAULTS_KEY
|
|
55
|
+
|
|
56
|
+
config_env.each do |role, additional_instructions|
|
|
57
|
+
next if role == DEFAULTS_KEY # 'defaults' is not a role
|
|
58
|
+
# Only deploy to the role we've specified (or all roles if none is specified)
|
|
59
|
+
next if filters.key?('role') && filters['role'] != role
|
|
60
|
+
|
|
61
|
+
instructions = Foreplay::Utility.supermerge(defaults, additional_instructions).symbolize_keys
|
|
62
|
+
instructions[:role] = role
|
|
63
|
+
required_keys = [:name, :environment, :role, :servers, :path, :repository]
|
|
64
|
+
|
|
65
|
+
required_keys.each do |key|
|
|
66
|
+
next if instructions.key? key
|
|
67
|
+
terminate("Required key #{key} not found in instructions for #{environment} environment.\nCheck #{config_file}")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
deploy_role instructions
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
puts mode == :deploy ? 'Finished deployment' : 'Deployment configuration check was successful'
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def deploy_role(instructions)
|
|
79
|
+
servers = instructions[:servers]
|
|
80
|
+
preposition = mode == :deploy ? 'to' : 'for'
|
|
81
|
+
|
|
82
|
+
if servers.length > 1
|
|
83
|
+
message = "#{mode.capitalize}ing #{instructions[:name].yellow} #{preposition} #{servers.join(', ').yellow} for the "
|
|
84
|
+
message += "#{instructions[:role].dup.yellow} role in the #{environment.dup.yellow} environment..."
|
|
85
|
+
puts message
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
servers.each { |server| deploy_to_server server, instructions }
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def deploy_to_server(server, instructions)
|
|
92
|
+
name = instructions[:name]
|
|
93
|
+
role = instructions[:role]
|
|
94
|
+
path = instructions[:path]
|
|
95
|
+
repository = instructions[:repository]
|
|
96
|
+
user = instructions[:user]
|
|
97
|
+
port = instructions[:port]
|
|
98
|
+
preposition = mode == :deploy ? 'to' : 'for'
|
|
99
|
+
|
|
100
|
+
instructions[:server] = server
|
|
101
|
+
|
|
102
|
+
message = "#{mode.capitalize}ing #{name.yellow} #{preposition} #{server.yellow} "
|
|
103
|
+
message += "for the #{role.dup.yellow} role in the #{environment.dup.yellow} environment"
|
|
104
|
+
puts message
|
|
105
|
+
|
|
106
|
+
# Substitute variables in the path
|
|
107
|
+
path.gsub! '%u', user
|
|
108
|
+
path.gsub! '%a', name
|
|
109
|
+
|
|
110
|
+
# Find out which port we're currently running on
|
|
111
|
+
current_port_file = ".foreplay/#{name}/current_port"
|
|
112
|
+
steps = [{ command: "mkdir -p .foreplay/#{name} && touch #{current_port_file} && cat #{current_port_file}", silent: true }]
|
|
113
|
+
|
|
114
|
+
current_port_string = execute_on_server(steps, instructions).strip!
|
|
115
|
+
|
|
116
|
+
if current_port_string.blank?
|
|
117
|
+
puts "#{INDENT}No instance is currently deployed"
|
|
118
|
+
else
|
|
119
|
+
"#{INDENT}Current instance is using port #{current_port_string}"
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
current_port = current_port_string.to_i
|
|
123
|
+
|
|
124
|
+
# Switch ports
|
|
125
|
+
if current_port == port
|
|
126
|
+
current_port = port + 1000
|
|
127
|
+
former_port = port
|
|
128
|
+
else
|
|
129
|
+
current_port = port
|
|
130
|
+
former_port = port + 1000
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Contents of .foreman file
|
|
134
|
+
current_service = "#{name}-#{current_port}"
|
|
135
|
+
former_service = "#{name}-#{former_port}"
|
|
136
|
+
|
|
137
|
+
instructions[:foreman]['app'] = current_service
|
|
138
|
+
instructions[:foreman]['port'] = current_port
|
|
139
|
+
instructions[:foreman]['user'] = user
|
|
140
|
+
|
|
141
|
+
# Commands to execute on remote server
|
|
142
|
+
steps = [
|
|
143
|
+
{ command: "mkdir -p #{path} && cd #{path} && rm -rf #{current_port} && git clone #{repository} #{current_port}",
|
|
144
|
+
commentary: "Cloning repository #{repository}" },
|
|
145
|
+
{ command: "rvm rvmrc trust #{current_port}",
|
|
146
|
+
commentary: 'Trusting the .rvmrc file for the new instance' },
|
|
147
|
+
{ command: "rvm rvmrc warning ignore #{current_port}",
|
|
148
|
+
commentary: 'Ignoring the .rvmrc warning for the new instance' },
|
|
149
|
+
{ command: "cd #{current_port}",
|
|
150
|
+
commentary: 'If you have a .rvmrc file there may be a delay now while we install a new ruby' },
|
|
151
|
+
{ command: 'if [ -f .ruby-version ] ; then rvm install `cat .ruby-version` ; else echo "No .ruby-version file found" ; fi',
|
|
152
|
+
commentary: 'If you have a .ruby-version file there may be a delay now while we install a new ruby' },
|
|
153
|
+
{ command: 'mkdir -p config',
|
|
154
|
+
commentary: 'Making sure the config directory exists' },
|
|
155
|
+
{ key: :env,
|
|
156
|
+
delimiter: '=',
|
|
157
|
+
prefix: '.',
|
|
158
|
+
commentary: 'Building .env' },
|
|
159
|
+
{ key: :foreman,
|
|
160
|
+
delimiter: ': ',
|
|
161
|
+
prefix: '.',
|
|
162
|
+
commentary: 'Building .foreman' },
|
|
163
|
+
{ key: :database,
|
|
164
|
+
delimiter: ': ',
|
|
165
|
+
suffix: '.yml',
|
|
166
|
+
commentary: 'Building config/database.yml',
|
|
167
|
+
before: ' ',
|
|
168
|
+
header: "#{environment}:",
|
|
169
|
+
path: 'config/' },
|
|
170
|
+
{ key: :resque,
|
|
171
|
+
delimiter: ': ',
|
|
172
|
+
suffix: '.yml',
|
|
173
|
+
commentary: 'Building config/resque.yml',
|
|
174
|
+
before: environment,
|
|
175
|
+
path: 'config/' },
|
|
176
|
+
{ command: 'bundle install --deployment --without development test',
|
|
177
|
+
commentary: 'Using bundler to install the required gems in deployment mode' },
|
|
178
|
+
{ command: 'sudo ln -f `which foreman` /usr/bin/foreman || echo Using default version of foreman',
|
|
179
|
+
commentary: 'Setting the current version of foreman to be the default' },
|
|
180
|
+
{ command: 'echo HOME="$HOME" >> .env',
|
|
181
|
+
commentary: 'Adding home path to .env (foreplay issue #443)' },
|
|
182
|
+
{ command: 'echo SHELL="$SHELL" >> .env',
|
|
183
|
+
commentary: 'Adding shell path to .env (foreplay issue #443)' },
|
|
184
|
+
{ command: 'echo PATH="$PATH:`which bundle`" >> .env',
|
|
185
|
+
commentary: 'Adding bundler path to .env (foreplay issue #443)' },
|
|
186
|
+
{ command: 'sudo foreman export upstart /etc/init',
|
|
187
|
+
commentary: "Converting #{current_service} to an upstart service" },
|
|
188
|
+
{ command: "sudo start #{current_service} || sudo restart #{current_service}",
|
|
189
|
+
commentary: 'Starting the service',
|
|
190
|
+
ignore_error: true },
|
|
191
|
+
{ command: "echo #{current_port} > $HOME/#{current_port_file}",
|
|
192
|
+
commentary: "Setting the port for the new instance to #{current_port}" },
|
|
193
|
+
{ command: 'sleep 60',
|
|
194
|
+
commentary: 'Waiting 60s to give service time to start' },
|
|
195
|
+
{ command: "sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{current_port}",
|
|
196
|
+
commentary: "Adding firewall rule to direct incoming traffic on port 80 to port #{current_port}" },
|
|
197
|
+
{ command: "sudo iptables -t nat -D PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{former_port}",
|
|
198
|
+
commentary: "Removing previous firewall rule directing traffic to port #{former_port}",
|
|
199
|
+
ignore_error: true },
|
|
200
|
+
{ command: 'sudo iptables-save > /etc/iptables/rules.v4',
|
|
201
|
+
commentary: 'Attempting to save firewall rules to /etc/iptables/rules.v4',
|
|
202
|
+
ignore_error: true },
|
|
203
|
+
{ command: 'sudo iptables-save > /etc/iptables.up.rules',
|
|
204
|
+
commentary: 'Attempting to save firewall rules to /etc/iptables.up.rules',
|
|
205
|
+
ignore_error: true },
|
|
206
|
+
{ command: 'sudo iptables-save -c | egrep REDIRECT --color=never',
|
|
207
|
+
ignore_error: true,
|
|
208
|
+
commentary: 'Current firewall NAT configuration:' },
|
|
209
|
+
{ command: "sudo stop #{former_service} || echo 'No previous instance running'",
|
|
210
|
+
commentary: 'Stopping the previous instance',
|
|
211
|
+
ignore_error: true }
|
|
212
|
+
]
|
|
213
|
+
|
|
214
|
+
execute_on_server steps, instructions
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def execute_on_server(steps, instructions)
|
|
218
|
+
server_port = instructions[:server]
|
|
219
|
+
user = instructions[:user]
|
|
220
|
+
password = instructions[:password]
|
|
221
|
+
keyfile = instructions[:keyfile]
|
|
222
|
+
private_key = instructions[:private_key]
|
|
223
|
+
|
|
224
|
+
keyfile.sub! '~', ENV['HOME'] || '/' unless keyfile.blank? # Remote shell won't expand this for us
|
|
225
|
+
|
|
226
|
+
# Parse server + port
|
|
227
|
+
server, port = server_port.split(':')
|
|
228
|
+
port ||= 22
|
|
229
|
+
|
|
230
|
+
# SSH authentication methods
|
|
231
|
+
options = { verbose: :warn, port: port }
|
|
232
|
+
|
|
233
|
+
if password.blank?
|
|
234
|
+
# If there's no password we must supply a private key
|
|
235
|
+
if private_key.blank?
|
|
236
|
+
message = 'No authentication methods supplied. You must supply a private key, key file or password in the configuration file'
|
|
237
|
+
terminate(message) if keyfile.blank?
|
|
238
|
+
# Get the key from the key file
|
|
239
|
+
puts "#{INDENT}Using private key from #{keyfile}"
|
|
240
|
+
private_key = File.read keyfile
|
|
241
|
+
else
|
|
242
|
+
puts "#{INDENT}Using private key from the configuration file"
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
options[:key_data] = [private_key]
|
|
246
|
+
else
|
|
247
|
+
# Use the password supplied
|
|
248
|
+
options[:password] = password
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# Capture output of last command to return to the calling routine
|
|
252
|
+
output = ''
|
|
253
|
+
|
|
254
|
+
if mode == :deploy
|
|
255
|
+
puts "#{INDENT}Connecting to #{server} on port #{port}"
|
|
256
|
+
|
|
257
|
+
# SSH connection
|
|
258
|
+
begin
|
|
259
|
+
Net::SSH.start(server, user, options) do |session|
|
|
260
|
+
puts "#{INDENT}Successfully connected to #{server} on port #{port}"
|
|
261
|
+
|
|
262
|
+
session.shell do |sh|
|
|
263
|
+
steps.each do |step|
|
|
264
|
+
# Output from this step
|
|
265
|
+
output = ''
|
|
266
|
+
previous = '' # We don't need or want the final CRLF
|
|
267
|
+
commands = build_step step, instructions
|
|
268
|
+
|
|
269
|
+
commands.each do |command|
|
|
270
|
+
process = sh.execute command
|
|
271
|
+
|
|
272
|
+
process.on_output do |_, o|
|
|
273
|
+
previous = o
|
|
274
|
+
output += previous
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
sh.wait!
|
|
278
|
+
|
|
279
|
+
if step[:ignore_error] == true || process.exit_status == 0
|
|
280
|
+
print output.gsub!(/^/, INDENT * 2) unless step[:silent] == true || output.blank?
|
|
281
|
+
else
|
|
282
|
+
terminate(output)
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
rescue SocketError => e
|
|
289
|
+
terminate "There was a problem starting an ssh session on #{server_port}:\n#{e.message}"
|
|
290
|
+
end
|
|
291
|
+
else
|
|
292
|
+
# Deployment check: just say what we would have done
|
|
293
|
+
steps.each do |step|
|
|
294
|
+
commands = build_step step, instructions
|
|
295
|
+
|
|
296
|
+
commands.each { |command| puts "#{INDENT * 2}#{command}" unless step[:silent] }
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
output
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def build_step(step, instructions)
|
|
304
|
+
puts "#{INDENT}#{(step[:commentary] || step[:command]).yellow}" unless step[:silent] == true
|
|
305
|
+
|
|
306
|
+
# Each step can be (1) a command or (2) a series of values to add to a file
|
|
307
|
+
if step.key?(:key)
|
|
308
|
+
if instructions.key?(step[:key])
|
|
309
|
+
build_commands step, instructions
|
|
310
|
+
else
|
|
311
|
+
[]
|
|
312
|
+
end
|
|
313
|
+
else
|
|
314
|
+
# ...or just execute the command specified
|
|
315
|
+
[step[:command]]
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def build_commands(step, instructions)
|
|
320
|
+
# Add values from the config file to a file on the remote machine
|
|
321
|
+
key = step[:key]
|
|
322
|
+
prefix = step[:prefix] || ''
|
|
323
|
+
suffix = step[:suffix] || ''
|
|
324
|
+
path = step[:path] || ''
|
|
325
|
+
before = step[:before] || ''
|
|
326
|
+
delimiter = step[:delimiter] || ''
|
|
327
|
+
after = step[:after] || ''
|
|
328
|
+
|
|
329
|
+
step[:silent] = true
|
|
330
|
+
filename = "#{path}#{prefix}#{key}#{suffix}"
|
|
331
|
+
|
|
332
|
+
if step.key?(:header)
|
|
333
|
+
commands = ["echo \"#{step[:header]}\" > #{filename}"]
|
|
334
|
+
redirect = '>>'
|
|
335
|
+
else
|
|
336
|
+
commands = []
|
|
337
|
+
redirect = '>'
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
if instructions[key].kind_of?(Hash)
|
|
341
|
+
instructions[key].each do |k, v|
|
|
342
|
+
commands << "echo \"#{before}#{k}#{delimiter}#{v}#{after}\" #{redirect} #{filename}"
|
|
343
|
+
redirect = '>>'
|
|
344
|
+
end
|
|
345
|
+
else
|
|
346
|
+
commands << "echo \"#{before}#{delimiter}#{instructions[key]}#{after}\" #{redirect} #{filename}"
|
|
347
|
+
redirect = '>>'
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
commands
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def explanatory_text(hsh, key)
|
|
354
|
+
hsh.key?(key) ? "#{hsh[key].dup.yellow} #{key}" : "all #{key.pluralize}"
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def terminate(message)
|
|
358
|
+
fail message
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
end
|