foreplay 0.0.2 → 0.0.3
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/README.md +9 -5
- data/features/deploy.feature +1 -1
- data/lib/foreplay/deploy.rb +29 -23
- data/lib/foreplay/version.rb +1 -1
- data/spec/lib/foreplay/deploy_spec.rb +6 -46
- metadata +4 -5
- data/foreplay.rb +0 -246
data/README.md
CHANGED
|
@@ -103,8 +103,12 @@ end
|
|
|
103
103
|
|
|
104
104
|
## Contributing
|
|
105
105
|
|
|
106
|
-
1.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
106
|
+
1. Fork it
|
|
107
|
+
1. Create your feature branch (`git checkout -b my-new-feature`)
|
|
108
|
+
1. Commit your changes (`git commit -am 'Add some feature'`)
|
|
109
|
+
1. Push to the branch (`git push origin my-new-feature`)
|
|
110
|
+
1. Create new Pull Request
|
|
111
|
+
|
|
112
|
+
## Acknowledgements
|
|
113
|
+
|
|
114
|
+
1. Thanks to Ryan Bigg for the guide to making your first gem https://github.com/radar/guides/blob/master/gem-development.md
|
data/features/deploy.feature
CHANGED
|
@@ -91,6 +91,6 @@ Feature: deploy
|
|
|
91
91
|
Scenario: deploy
|
|
92
92
|
When I run `foreplay setup -r git@github.com:Xenapto/foreplay.git -s web.example.com --password "top-secret"`
|
|
93
93
|
And I run `foreplay deploy production`
|
|
94
|
-
Then the output should contain "Deploying aruba to web.example.com
|
|
94
|
+
Then the output should contain "Deploying aruba to web.example.com for the web role in the production environment"
|
|
95
95
|
And the output should contain "Connecting to web.example.com"
|
|
96
96
|
And the output should contain "There was a problem starting an ssh session on web.example.com"
|
data/lib/foreplay/deploy.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require 'thor/group'
|
|
2
2
|
require 'yaml'
|
|
3
3
|
require 'net/ssh'
|
|
4
|
+
require 'net/ssh/shell'
|
|
4
5
|
require 'active_support/inflector'
|
|
5
6
|
require 'active_support/core_ext/object'
|
|
6
7
|
require 'active_support/core_ext/hash'
|
|
@@ -15,7 +16,8 @@ module Foreplay
|
|
|
15
16
|
argument :environment, :type => :string, :required => true
|
|
16
17
|
argument :filters, :type => :hash, :required => false
|
|
17
18
|
|
|
18
|
-
DEFAULTS_KEY
|
|
19
|
+
DEFAULTS_KEY = 'defaults'
|
|
20
|
+
INDENT = ' ' * 4
|
|
19
21
|
|
|
20
22
|
def parse
|
|
21
23
|
# Explain what we're going to do
|
|
@@ -73,7 +75,7 @@ module Foreplay
|
|
|
73
75
|
servers = instructions[:servers]
|
|
74
76
|
preposition = mode == :deploy ? 'to' : 'for'
|
|
75
77
|
|
|
76
|
-
puts "#{mode.capitalize}ing #{instructions[:name].yellow} #{preposition} #{servers.join(', ').yellow}
|
|
78
|
+
puts "#{mode.capitalize}ing #{instructions[:name].yellow} #{preposition} #{servers.join(', ').yellow} for the #{instructions[:role].dup.yellow} role in the #{environment.dup.yellow} environment..." if servers.length > 1
|
|
77
79
|
servers.each { |server| deploy_to_server server, instructions }
|
|
78
80
|
end
|
|
79
81
|
|
|
@@ -88,7 +90,7 @@ module Foreplay
|
|
|
88
90
|
|
|
89
91
|
instructions[:server] = server
|
|
90
92
|
|
|
91
|
-
puts "#{mode.capitalize}ing #{name.yellow} #{preposition} #{server.yellow}
|
|
93
|
+
puts "#{mode.capitalize}ing #{name.yellow} #{preposition} #{server.yellow} for the #{role.dup.yellow} role in the #{environment.dup.yellow} environment"
|
|
92
94
|
|
|
93
95
|
# Substitute variables in the path
|
|
94
96
|
path.gsub! '%u', user
|
|
@@ -98,7 +100,7 @@ module Foreplay
|
|
|
98
100
|
steps = [ { :command => 'mkdir -p .foreplay && touch .foreplay/current_port && cat .foreplay/current_port', :silent => true } ]
|
|
99
101
|
|
|
100
102
|
current_port_string = execute_on_server(steps, instructions).strip!
|
|
101
|
-
puts current_port_string.blank? ?
|
|
103
|
+
puts current_port_string.blank? ? "#{INDENT}No instance is currently deployed" : "#{INDENT}Current instance is using port #{current_port_string}"
|
|
102
104
|
|
|
103
105
|
current_port = current_port_string.to_i
|
|
104
106
|
|
|
@@ -129,6 +131,8 @@ module Foreplay
|
|
|
129
131
|
:commentary => 'Trusting the .rvmrc file for the new instance' },
|
|
130
132
|
{ :command => "cd #{current_port}",
|
|
131
133
|
:commentary => 'Configuring the new instance' },
|
|
134
|
+
{ :command => 'mkdir -p config',
|
|
135
|
+
:commentary => "Making sure the config directory exists" },
|
|
132
136
|
{ :key => :env,
|
|
133
137
|
:delimiter => '=',
|
|
134
138
|
:prefix => '.',
|
|
@@ -144,29 +148,31 @@ module Foreplay
|
|
|
144
148
|
:before => ' ',
|
|
145
149
|
:header => "#{environment}:",
|
|
146
150
|
:path => 'config/' },
|
|
147
|
-
{ :command =>
|
|
151
|
+
{ :command => 'bundle install',
|
|
148
152
|
:commentary => 'Using bundler to install the required gems' },
|
|
149
|
-
{ :command =>
|
|
153
|
+
{ :command => 'sudo ln -f `which foreman` /usr/bin/foreman',
|
|
150
154
|
:commentary => 'Setting the current version of foreman to be the default' },
|
|
151
|
-
{ :command =>
|
|
155
|
+
{ :command => 'sudo foreman export upstart /etc/init',
|
|
152
156
|
:commentary => "Converting #{current_service} to an upstart service" },
|
|
153
157
|
{ :command => "sudo start #{current_service} || sudo restart #{current_service}",
|
|
154
158
|
:commentary => 'Starting the service',
|
|
155
159
|
:ignore_error => true },
|
|
156
160
|
{ :command => 'sleep 60',
|
|
157
161
|
:commentary => 'Waiting 60s to give service time to start' },
|
|
158
|
-
{ :command => "sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{current_port
|
|
162
|
+
{ :command => "sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{current_port}",
|
|
159
163
|
:commentary => "Adding firewall rule to direct incoming traffic on port 80 to port #{current_port}" },
|
|
160
|
-
{ :command => "sudo iptables -t nat -D PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{former_port
|
|
161
|
-
:commentary => "Removing previous firewall directing traffic to port #{former_port}",
|
|
164
|
+
{ :command => "sudo iptables -t nat -D PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{former_port}",
|
|
165
|
+
:commentary => "Removing previous firewall rule directing traffic to port #{former_port}",
|
|
162
166
|
:ignore_error => true },
|
|
163
|
-
{ :command =>
|
|
164
|
-
:commentary =>
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
167
|
+
{ :command => 'sudo iptables-save > /etc/iptables/rules.v4',
|
|
168
|
+
:commentary => 'Attempting to save firewall rules to /etc/iptables/rules.v4',
|
|
169
|
+
:ignore_error => true },
|
|
170
|
+
{ :command => 'sudo iptables-save > /etc/iptables.up.rules',
|
|
171
|
+
:commentary => 'Attempting to save firewall rules to /etc/iptables.up.rules',
|
|
172
|
+
:ignore_error => true },
|
|
173
|
+
{ :command => 'sudo iptables-save -c | egrep REDIRECT --color=never',
|
|
168
174
|
:ignore_error => true,
|
|
169
|
-
:commentary =>
|
|
175
|
+
:commentary => 'Current firewall NAT configuration:' },
|
|
170
176
|
{ :command => "sudo stop #{former_service} || echo 'No previous instance running'",
|
|
171
177
|
:commentary => 'Stopping the previous instance',
|
|
172
178
|
:ignore_error => true },
|
|
@@ -192,10 +198,10 @@ module Foreplay
|
|
|
192
198
|
if private_key.blank?
|
|
193
199
|
terminate('No authentication methods supplied. You must supply a private key, key file or password in the configuration file') if keyfile.blank?
|
|
194
200
|
# Get the key from the key file
|
|
195
|
-
puts "
|
|
201
|
+
puts "#{INDENT}Using private key from #{keyfile}"
|
|
196
202
|
private_key = File.read keyfile
|
|
197
203
|
else
|
|
198
|
-
puts "
|
|
204
|
+
puts "#{INDENT}Using private key from the configuration file"
|
|
199
205
|
end
|
|
200
206
|
|
|
201
207
|
options[:key_data] = [private_key]
|
|
@@ -208,12 +214,12 @@ module Foreplay
|
|
|
208
214
|
output = ''
|
|
209
215
|
|
|
210
216
|
if mode == :deploy
|
|
211
|
-
puts "
|
|
217
|
+
puts "#{INDENT}Connecting to #{server}"
|
|
212
218
|
|
|
213
219
|
# SSH connection
|
|
214
220
|
begin
|
|
215
221
|
Net::SSH.start(server, user, options) do |session|
|
|
216
|
-
puts "
|
|
222
|
+
puts "#{INDENT}Successfully connected to #{server}"
|
|
217
223
|
|
|
218
224
|
session.shell do |sh|
|
|
219
225
|
steps.each do |step|
|
|
@@ -234,7 +240,7 @@ module Foreplay
|
|
|
234
240
|
sh.wait!
|
|
235
241
|
|
|
236
242
|
if step[:ignore_error] == true || process.exit_status == 0
|
|
237
|
-
print output.gsub!(/^/,
|
|
243
|
+
print output.gsub!(/^/, INDENT * 2) unless step[:silent] == true || output.blank?
|
|
238
244
|
else
|
|
239
245
|
terminate(output)
|
|
240
246
|
end
|
|
@@ -250,7 +256,7 @@ module Foreplay
|
|
|
250
256
|
steps.each do |step|
|
|
251
257
|
commands = build_commands step, instructions
|
|
252
258
|
|
|
253
|
-
commands.each { |command| puts "
|
|
259
|
+
commands.each { |command| puts "#{INDENT * 2}#{command}" unless step[:silent] }
|
|
254
260
|
end
|
|
255
261
|
end
|
|
256
262
|
|
|
@@ -258,7 +264,7 @@ module Foreplay
|
|
|
258
264
|
end
|
|
259
265
|
|
|
260
266
|
def build_commands step, instructions
|
|
261
|
-
puts "
|
|
267
|
+
puts "#{INDENT}#{(step[:commentary] || step[:command]).yellow}" unless step[:silent] == true
|
|
262
268
|
|
|
263
269
|
# Each step can be (1) a command or (2) a series of values to add to a file
|
|
264
270
|
if step.has_key? :key
|
data/lib/foreplay/version.rb
CHANGED
|
@@ -54,50 +54,10 @@ describe Foreplay::Deploy do
|
|
|
54
54
|
expect { Foreplay::Deploy.start([:deploy, 'production', '']) }.to raise_error(RuntimeError, /.*There was a problem starting an ssh session on web.example.com*/)
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
No instance is currently deployed
|
|
62
|
-
\e[0;33;49mSetting the port for the new instance to 50000\e[0m
|
|
63
|
-
echo 50000 > .foreplay/current_port
|
|
64
|
-
\e[0;33;49mCloning repository git@github.com:Xenapto/foreplay.git\e[0m
|
|
65
|
-
mkdir -p apps/foreplay && cd apps/foreplay && rm -rf 50000 && git clone git@github.com:Xenapto/foreplay.git 50000
|
|
66
|
-
\e[0;33;49mTrusting the .rvmrc file for the new instance\e[0m
|
|
67
|
-
rvm rvmrc trust 50000
|
|
68
|
-
\e[0;33;49mConfiguring the new instance\e[0m
|
|
69
|
-
cd 50000
|
|
70
|
-
\e[0;33;49mBuilding .env\e[0m
|
|
71
|
-
\e[0;33;49mBuilding .foreman\e[0m
|
|
72
|
-
\e[0;33;49mBuilding config/database.yml\e[0m
|
|
73
|
-
\e[0;33;49mUsing bundler to install the required gems\e[0m
|
|
74
|
-
bundle install
|
|
75
|
-
\e[0;33;49mSetting the current version of foreman to be the default\e[0m
|
|
76
|
-
sudo ln -f `which foreman` /usr/bin/foreman
|
|
77
|
-
\e[0;33;49mConverting foreplay-50000 to an upstart service\e[0m
|
|
78
|
-
sudo foreman export upstart /etc/init
|
|
79
|
-
\e[0;33;49mStarting the service\e[0m
|
|
80
|
-
sudo start foreplay-50000 || sudo restart foreplay-50000
|
|
81
|
-
\e[0;33;49mWaiting 60s to give service time to start\e[0m
|
|
82
|
-
sleep 60
|
|
83
|
-
\e[0;33;49mAdding firewall rule to direct incoming traffic on port 80 to port 50000\e[0m
|
|
84
|
-
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 50100
|
|
85
|
-
\e[0;33;49mRemoving previous firewall directing traffic to port 51000\e[0m
|
|
86
|
-
sudo iptables -t nat -D PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 51100
|
|
87
|
-
\e[0;33;49mSaving iptables rules to /etc/iptables/rules.v4\e[0m
|
|
88
|
-
sudo iptables-save > /etc/iptables/rules.v4
|
|
89
|
-
\e[0;33;49mSaving iptables rules to /etc/iptables.up.rules\e[0m
|
|
90
|
-
sudo iptables-save > /etc/iptables.up.rules
|
|
91
|
-
\e[0;33;49mCurrent firewall NAT configuration:\e[0m
|
|
92
|
-
sudo iptables-save -c | egrep REDIRECT --color=never
|
|
93
|
-
\e[0;33;49mStopping the previous instance\e[0m
|
|
94
|
-
sudo stop foreplay-51000 || echo 'No previous instance running'
|
|
95
|
-
Deployment configuration check was successful
|
|
96
|
-
OUTPUT
|
|
97
|
-
|
|
98
|
-
output.split("\n").reverse.each { |line| $stdout.should_receive(:puts).with(line) }
|
|
99
|
-
Foreplay::Deploy.start [:check, 'production', '']
|
|
100
|
-
end
|
|
57
|
+
it "should check the config" do
|
|
58
|
+
$stdout.should_receive(:puts).at_least(:once)
|
|
59
|
+
Foreplay::Deploy.start [:check, 'production', '']
|
|
60
|
+
end
|
|
101
61
|
|
|
102
62
|
it "should deploy to the environment" do
|
|
103
63
|
Net::SSH.should_receive(:start).with('web.example.com', 'fred', { :verbose => :warn, :password => 'trollope' }).and_yield(session)
|
|
@@ -126,8 +86,8 @@ OUTPUT
|
|
|
126
86
|
'sudo foreman export upstart /etc/init',
|
|
127
87
|
'sudo start foreplay-50000 || sudo restart foreplay-50000',
|
|
128
88
|
'sleep 60',
|
|
129
|
-
'sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port
|
|
130
|
-
'sudo iptables -t nat -D PREROUTING -p tcp --dport 80 -j REDIRECT --to-port
|
|
89
|
+
'sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 50000',
|
|
90
|
+
'sudo iptables -t nat -D PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 51000',
|
|
131
91
|
'sudo iptables-save > /etc/iptables/rules.v4',
|
|
132
92
|
'sudo iptables-save > /etc/iptables.up.rules',
|
|
133
93
|
'sudo iptables-save -c | egrep REDIRECT --color=never',
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: foreplay
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.3
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2013-11-
|
|
12
|
+
date: 2013-11-10 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: activesupport
|
|
@@ -224,7 +224,6 @@ files:
|
|
|
224
224
|
- features/support/env.rb
|
|
225
225
|
- foreplay.gemspec
|
|
226
226
|
- foreplay.rake
|
|
227
|
-
- foreplay.rb
|
|
228
227
|
- lib/foreplay.rb
|
|
229
228
|
- lib/foreplay/cli.rb
|
|
230
229
|
- lib/foreplay/deploy.rb
|
|
@@ -251,7 +250,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
251
250
|
version: '0'
|
|
252
251
|
segments:
|
|
253
252
|
- 0
|
|
254
|
-
hash: -
|
|
253
|
+
hash: -3290811328696495946
|
|
255
254
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
256
255
|
none: false
|
|
257
256
|
requirements:
|
|
@@ -260,7 +259,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
260
259
|
version: '0'
|
|
261
260
|
segments:
|
|
262
261
|
- 0
|
|
263
|
-
hash: -
|
|
262
|
+
hash: -3290811328696495946
|
|
264
263
|
requirements: []
|
|
265
264
|
rubyforge_project:
|
|
266
265
|
rubygems_version: 1.8.25
|
data/foreplay.rb
DELETED
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
require 'yaml'
|
|
3
|
-
require 'net/ssh'
|
|
4
|
-
|
|
5
|
-
class Foreplay
|
|
6
|
-
class << self
|
|
7
|
-
def push check_only = false
|
|
8
|
-
environment = ENV['ENV']
|
|
9
|
-
config_file = "#{Rails.root}/config/foreplay.yml"
|
|
10
|
-
config_all = YAML.load(File.read(config_file))
|
|
11
|
-
|
|
12
|
-
# This environment
|
|
13
|
-
raise RuntimeError, "No deployment environment defined. Set the ENV environment variable." if environment.blank?
|
|
14
|
-
raise RuntimeError, "No deployment configuration defined for #{environment} environment. Check #{config_file}" unless config_all.has_key? environment
|
|
15
|
-
config = config_all[environment]
|
|
16
|
-
|
|
17
|
-
# Establish defaults
|
|
18
|
-
# First the default defaults
|
|
19
|
-
defaults = {
|
|
20
|
-
'name' => Rails.application.class.parent_name.underscore,
|
|
21
|
-
'environment' => environment,
|
|
22
|
-
'env' => { 'RAILS_ENV' => environment }
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
defaults = deep_merge_with_arrays(defaults, config_all['defaults']) if config_all.has_key? 'defaults' # Then the global defaults
|
|
26
|
-
defaults = deep_merge_with_arrays(defaults, config['defaults']) if config.has_key? 'defaults' # Then the defaults for this environment
|
|
27
|
-
|
|
28
|
-
config.each do |role, additional_instructions|
|
|
29
|
-
next if role == 'defaults' # 'defaults' is not a role
|
|
30
|
-
next unless ENV['ROLE'].blank? || ENV['ROLE'] == role # Only deploy to the role we've specified (or all roles if none is specified)
|
|
31
|
-
|
|
32
|
-
instructions = deep_merge_with_arrays(defaults, additional_instructions).symbolize_keys
|
|
33
|
-
instructions[:role] = role
|
|
34
|
-
required_keys = [:name, :environment, :role, :servers, :path, :repository]
|
|
35
|
-
|
|
36
|
-
required_keys.each { |key| raise RuntimeError, "Required key #{key} not found in instructions for #{environment} environment. Check #{config_file}" unless instructions.has_key? key }
|
|
37
|
-
|
|
38
|
-
deploy_role instructions unless check_only
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
puts check_only ? 'Deployment configuration check was successful' : 'Finished deployment'
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def deploy_role instructions
|
|
45
|
-
servers = instructions[:servers]
|
|
46
|
-
puts "Deploying #{instructions[:name]} to #{servers.join(', ')} for the #{instructions[:role]} role in the #{instructions[:environment]} environment..." if servers.length > 1
|
|
47
|
-
servers.each { |server| deploy_to_server server, instructions }
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def deploy_to_server server, instructions
|
|
51
|
-
name = instructions[:name]
|
|
52
|
-
environment = instructions[:environment]
|
|
53
|
-
role = instructions[:role]
|
|
54
|
-
path = instructions[:path]
|
|
55
|
-
repository = instructions[:repository]
|
|
56
|
-
user = instructions[:user]
|
|
57
|
-
|
|
58
|
-
instructions[:server] = server
|
|
59
|
-
|
|
60
|
-
puts "Deploying #{name} to #{server} for the #{role} role in the #{environment} environment"
|
|
61
|
-
|
|
62
|
-
# Substitute variables in the path
|
|
63
|
-
path.sub! '%u', user
|
|
64
|
-
path.sub! '%a', name
|
|
65
|
-
|
|
66
|
-
# Find out which port we're currently running on
|
|
67
|
-
steps = [ { :command => 'mkdir -p .foreplay && touch .foreplay/current_port && cat .foreplay/current_port', :silent => true } ]
|
|
68
|
-
|
|
69
|
-
current_port = execute_on_server(steps, instructions).strip!
|
|
70
|
-
puts "Current instance is using port #{current_port}"
|
|
71
|
-
|
|
72
|
-
# Switch ports
|
|
73
|
-
if current_port == '50000'
|
|
74
|
-
current_port = '51000'
|
|
75
|
-
former_port = '50000'
|
|
76
|
-
else
|
|
77
|
-
current_port = '50000'
|
|
78
|
-
former_port = '51000'
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
# Contents of .foreman file
|
|
82
|
-
current_service = '%s-%s' % [name, current_port]
|
|
83
|
-
former_service = '%s-%s' % [name, former_port]
|
|
84
|
-
|
|
85
|
-
instructions[:foreman]['app'] = current_service
|
|
86
|
-
instructions[:foreman]['port'] = current_port
|
|
87
|
-
instructions[:foreman]['user'] = user
|
|
88
|
-
|
|
89
|
-
# Commands to execute on remote server
|
|
90
|
-
steps = [
|
|
91
|
-
{ :command => "echo #{current_port} > .foreplay/current_port" },
|
|
92
|
-
{ :command => "mkdir -p #{path} && cd #{path} && rm -rf #{current_port} && git clone #{repository} #{current_port}",
|
|
93
|
-
:commentary => "Cloning repository #{repository}" },
|
|
94
|
-
{ :command => "rvm rvmrc trust #{current_port}" },
|
|
95
|
-
{ :command => "cd #{current_port}" },
|
|
96
|
-
{ :key => :env,
|
|
97
|
-
:delimiter => '=',
|
|
98
|
-
:prefix => '.',
|
|
99
|
-
:commentary => 'Building .env' },
|
|
100
|
-
{ :key => :foreman,
|
|
101
|
-
:delimiter => ': ',
|
|
102
|
-
:prefix => '.',
|
|
103
|
-
:commentary => 'Building .foreman' },
|
|
104
|
-
{ :key => :database,
|
|
105
|
-
:delimiter => ': ',
|
|
106
|
-
:suffix => '.yml',
|
|
107
|
-
:commentary => 'Building config/database.yml',
|
|
108
|
-
:before => ' ',
|
|
109
|
-
:header => "#{environment}:",
|
|
110
|
-
:path => 'config/' },
|
|
111
|
-
{ :command => "bundle install" },
|
|
112
|
-
{ :command => "sudo ln -f `which foreman` /usr/bin/foreman" },
|
|
113
|
-
{ :command => "sudo foreman export upstart /etc/init" },
|
|
114
|
-
{ :command => "sudo start #{current_service} || sudo restart #{current_service}",
|
|
115
|
-
:ignore_error => true },
|
|
116
|
-
{ :command => 'sleep 60',
|
|
117
|
-
:commentary => 'Waiting 60s to give service time to start' },
|
|
118
|
-
{ :command => "sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{current_port.to_i + 100}",
|
|
119
|
-
:commentary => "Adding firewall rule to direct incoming traffic on port 80 to port #{current_port}" },
|
|
120
|
-
{ :command => "sudo iptables -t nat -D PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{former_port.to_i + 100}",
|
|
121
|
-
:commentary => "Removing previous firewall directing traffic to port #{former_port}",
|
|
122
|
-
:ignore_error => true },
|
|
123
|
-
{ :command => "sudo iptables -t nat -L | grep REDIRECT",
|
|
124
|
-
:ignore_error => true,
|
|
125
|
-
:commentary => "Current firewall NAT configuration:" },
|
|
126
|
-
{ :command => "sudo stop #{former_service} || echo 'No previous instance running'",
|
|
127
|
-
:ignore_error => true },
|
|
128
|
-
]
|
|
129
|
-
|
|
130
|
-
execute_on_server steps, instructions
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
def execute_on_server steps, instructions
|
|
134
|
-
server = instructions[:server]
|
|
135
|
-
user = instructions[:user]
|
|
136
|
-
password = instructions[:password]
|
|
137
|
-
keyfile = instructions[:keyfile]
|
|
138
|
-
key = instructions[:key]
|
|
139
|
-
|
|
140
|
-
keyfile.sub! '~' , ENV['HOME'] # Remote shell won't expand this for us
|
|
141
|
-
|
|
142
|
-
# SSH authentication methods
|
|
143
|
-
options = { :verbose => :warn }
|
|
144
|
-
|
|
145
|
-
if password.blank?
|
|
146
|
-
# If there's no password we must supply a private key
|
|
147
|
-
if key.blank?
|
|
148
|
-
raise RuntimeError, "No authentication methods supplied. You must supply a private key, key file or password in the configuration file" if keyfile.blank?
|
|
149
|
-
# Get the key from the key file
|
|
150
|
-
puts "Using private key from #{keyfile}"
|
|
151
|
-
key = File.read keyfile
|
|
152
|
-
else
|
|
153
|
-
puts "Using private key from the configuration file"
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
options[:key_data] = [key]
|
|
157
|
-
else
|
|
158
|
-
# Use the password supplied
|
|
159
|
-
options[:password] = password
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
# Capture output of last command to return to the calling routine
|
|
163
|
-
output = ''
|
|
164
|
-
|
|
165
|
-
# SSH connection
|
|
166
|
-
Net::SSH.start(server, user, options) do |ssh|
|
|
167
|
-
puts "Successfully connected to #{server}"
|
|
168
|
-
|
|
169
|
-
ssh.shell do |sh|
|
|
170
|
-
steps.each do |step|
|
|
171
|
-
# Output from this step
|
|
172
|
-
output = ''
|
|
173
|
-
previous = '' # We don't need or want the final CRLF
|
|
174
|
-
|
|
175
|
-
puts step[:commentary] || step[:command] unless step[:silent] == true
|
|
176
|
-
|
|
177
|
-
# Each step can be (1) a command or (2) a series of values to add to a file
|
|
178
|
-
if step.has_key? :key
|
|
179
|
-
step[:silent] = true
|
|
180
|
-
|
|
181
|
-
# Add values from the config file to a file on the remote machine
|
|
182
|
-
key = step[:key]
|
|
183
|
-
prefix = step[:prefix] || ''
|
|
184
|
-
suffix = step[:suffix] || ''
|
|
185
|
-
path = step[:path] || ''
|
|
186
|
-
before = step[:before] || ''
|
|
187
|
-
delimiter = step[:delimiter] || ''
|
|
188
|
-
after = step[:after] || ''
|
|
189
|
-
|
|
190
|
-
filename = '%s%s%s%s' % [path, prefix, key, suffix]
|
|
191
|
-
commands = step.has_key?(:header) ? ['echo "%s" >> %s' % [step[:header], filename]] : []
|
|
192
|
-
|
|
193
|
-
instructions[key].each { |k, v| commands << 'echo "%s%s%s%s%s" >> %s' % [before, k, delimiter, v, after, filename] }
|
|
194
|
-
else
|
|
195
|
-
# ...or just execute the command specified
|
|
196
|
-
commands = [step[:command]]
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
commands.each do |command|
|
|
200
|
-
process = sh.execute command
|
|
201
|
-
|
|
202
|
-
process.on_output do |p, o|
|
|
203
|
-
previous = o
|
|
204
|
-
output += previous
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
sh.wait!
|
|
208
|
-
|
|
209
|
-
if step[:ignore_error] == true || process.exit_status == 0
|
|
210
|
-
print "#{output}" unless step[:silent] == true
|
|
211
|
-
else
|
|
212
|
-
raise RuntimeError, output
|
|
213
|
-
end
|
|
214
|
-
end
|
|
215
|
-
end
|
|
216
|
-
end
|
|
217
|
-
end
|
|
218
|
-
|
|
219
|
-
output
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
# Returns a new hash with +hash+ and +other_hash+ merged recursively, including arrays.
|
|
223
|
-
#
|
|
224
|
-
# h1 = { x: { y: [4,5,6] }, z: [7,8,9] }
|
|
225
|
-
# h2 = { x: { y: [7,8,9] }, z: 'xyz' }
|
|
226
|
-
# h1.deep_merge_with_arrays(h2)
|
|
227
|
-
# #=> {:x=>{:y=>[4, 5, 6, 7, 8, 9]}, :z=>[7, 8, 9, "xyz"]}
|
|
228
|
-
def deep_merge_with_arrays(hash, other_hash)
|
|
229
|
-
new_hash = hash.deep_dup
|
|
230
|
-
|
|
231
|
-
other_hash.each_pair do |k,v|
|
|
232
|
-
tv = new_hash[k]
|
|
233
|
-
|
|
234
|
-
if tv.is_a?(Hash) && v.is_a?(Hash)
|
|
235
|
-
new_hash[k] = deep_merge_with_arrays(tv, v)
|
|
236
|
-
elsif tv.is_a?(Array) || v.is_a?(Array)
|
|
237
|
-
new_hash[k] = Array.wrap(tv) + Array.wrap(v)
|
|
238
|
-
else
|
|
239
|
-
new_hash[k] = v
|
|
240
|
-
end
|
|
241
|
-
end
|
|
242
|
-
|
|
243
|
-
new_hash
|
|
244
|
-
end
|
|
245
|
-
end
|
|
246
|
-
end
|