engineyard-serverside 2.0.0.pre3 → 2.0.0.pre4
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/engineyard-serverside/cli.rb +46 -153
- data/lib/engineyard-serverside/cli_helpers.rb +53 -0
- data/lib/engineyard-serverside/configuration.rb +131 -161
- data/lib/engineyard-serverside/deploy.rb +69 -37
- data/lib/engineyard-serverside/deploy_hook.rb +31 -6
- data/lib/engineyard-serverside/paths.rb +106 -0
- data/lib/engineyard-serverside/server.rb +7 -59
- data/lib/engineyard-serverside/servers.rb +93 -0
- data/lib/engineyard-serverside/task.rb +4 -3
- data/lib/engineyard-serverside/version.rb +1 -1
- data/spec/basic_deploy_spec.rb +2 -2
- data/spec/bundler_deploy_spec.rb +6 -6
- data/spec/configuration_spec.rb +2 -1
- data/spec/custom_deploy_spec.rb +9 -4
- data/spec/deploy_hook_spec.rb +31 -15
- data/spec/ey_yml_customized_deploy_spec.rb +17 -15
- data/spec/fixtures/repos/assets_disabled/app/assets/empty +0 -0
- data/spec/fixtures/repos/assets_disabled_in_ey_yml/app/assets/empty +0 -0
- data/spec/fixtures/repos/assets_enabled/app/assets/empty +0 -0
- data/spec/fixtures/repos/assets_in_hook/app/assets/empty +0 -0
- data/spec/rails31_deploy_spec.rb +8 -8
- data/spec/restart_spec.rb +3 -2
- data/spec/rollback_spec.rb +61 -0
- data/spec/server_spec.rb +44 -50
- data/spec/spec_helper.rb +19 -12
- metadata +17 -4
@@ -44,7 +44,7 @@ module EY
|
|
44
44
|
|
45
45
|
callback(:after_symlink)
|
46
46
|
run_with_callbacks(:restart)
|
47
|
-
|
47
|
+
conditionally_disable_maintenance_page
|
48
48
|
|
49
49
|
cleanup_old_releases
|
50
50
|
shell.status "Finished deploy at #{Time.now.asctime}"
|
@@ -67,7 +67,7 @@ module EY
|
|
67
67
|
end
|
68
68
|
|
69
69
|
def parse_configured_services
|
70
|
-
result = YAML.load_file "#{c.
|
70
|
+
result = YAML.load_file "#{c.paths.shared_config}/ey_services_config_deploy.yml"
|
71
71
|
return {} unless result.is_a?(Hash)
|
72
72
|
result
|
73
73
|
rescue
|
@@ -138,6 +138,7 @@ To fix this problem, commit your Gemfile.lock to your repository and redeploy.
|
|
138
138
|
File.exists?(file)
|
139
139
|
end
|
140
140
|
|
141
|
+
shell.status "Enabling maintenance page."
|
141
142
|
@maintenance_up = true
|
142
143
|
roles :app_master, :app, :solo do
|
143
144
|
run Escape.shell_command(['mkdir', '-p', File.dirname(c.maintenance_page_enabled_path)])
|
@@ -148,22 +149,49 @@ To fix this problem, commit your Gemfile.lock to your repository and redeploy.
|
|
148
149
|
def conditionally_enable_maintenance_page
|
149
150
|
if c.enable_maintenance_page?
|
150
151
|
enable_maintenance_page
|
152
|
+
else
|
153
|
+
explain_not_enabling_maintenance_page
|
151
154
|
end
|
152
155
|
end
|
153
156
|
|
154
|
-
def
|
155
|
-
if c.
|
156
|
-
|
157
|
-
|
158
|
-
|
157
|
+
def explain_not_enabling_maintenance_page
|
158
|
+
if c.migrate?
|
159
|
+
if !c.maintenance_on_migrate? && !c.maintenance_on_restart?
|
160
|
+
shell.status "Skipping maintenance page. (maintenance_on_migrate is false in ey.yml)"
|
161
|
+
shell.notice "[Caution] No maintenance migrations must be non-destructive!"
|
162
|
+
shell.notice "Requests may be served during a partially migrated state."
|
159
163
|
end
|
160
164
|
else
|
161
|
-
if
|
162
|
-
shell.
|
165
|
+
if c.required_downtime_stack? && !c.maintenance_on_restart?
|
166
|
+
shell.status "Skipping maintenance page. (maintenance_on_restart is false in ey.yml, overriding recommended default)"
|
167
|
+
unless File.exist?(c.maintenance_page_enabled_path)
|
168
|
+
shell.warning <<-WARN
|
169
|
+
No maintenance page! Brief downtime may be possible during restart.
|
170
|
+
This application stack does not support no-downtime restarts.
|
171
|
+
WARN
|
172
|
+
end
|
173
|
+
elsif !c.required_downtime_stack?
|
174
|
+
shell.status "Skipping maintenance page. (no-downtime restarts supported)"
|
163
175
|
end
|
164
176
|
end
|
165
177
|
end
|
166
178
|
|
179
|
+
def disable_maintenance_page
|
180
|
+
shell.status "Removing maintenance page."
|
181
|
+
@maintenance_up = false
|
182
|
+
roles :app_master, :app, :solo do
|
183
|
+
run "rm -f #{c.maintenance_page_enabled_path}"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def conditionally_disable_maintenance_page
|
188
|
+
if c.disable_maintenance_page?
|
189
|
+
disable_maintenance_page
|
190
|
+
elsif File.exists?(c.maintenance_page_enabled_path)
|
191
|
+
shell.notice "[Attention] Maintenance page is still up.\nYou must remove it manually using `ey web enable`."
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
167
195
|
def run_with_callbacks(task)
|
168
196
|
callback("before_#{task}")
|
169
197
|
send(task)
|
@@ -173,7 +201,7 @@ To fix this problem, commit your Gemfile.lock to your repository and redeploy.
|
|
173
201
|
# task
|
174
202
|
def push_code
|
175
203
|
shell.status "Pushing code to all servers"
|
176
|
-
commands =
|
204
|
+
commands = servers.remote.map do |server|
|
177
205
|
cmd = server.sync_directory_command(config.repository_cache)
|
178
206
|
proc { shell.logged_system(cmd) }
|
179
207
|
end
|
@@ -229,7 +257,7 @@ chmod 0700 #{path}
|
|
229
257
|
end
|
230
258
|
|
231
259
|
def ssh_wrapper_path
|
232
|
-
"#{c.
|
260
|
+
"#{c.paths.shared_config}/#{c.app}-ssh-wrapper"
|
233
261
|
end
|
234
262
|
|
235
263
|
# task
|
@@ -260,20 +288,23 @@ chmod 0700 #{path}
|
|
260
288
|
|
261
289
|
# task
|
262
290
|
def rollback
|
263
|
-
if c.
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
291
|
+
if c.rollback_paths!
|
292
|
+
begin
|
293
|
+
rolled_back_release = c.paths.latest_release
|
294
|
+
shell.status "Rolling back to previous release: #{short_log_message(c.active_revision)}"
|
295
|
+
run_with_callbacks(:symlink)
|
296
|
+
sudo "rm -rf #{rolled_back_release}"
|
297
|
+
bundle
|
298
|
+
shell.status "Restarting with previous release."
|
299
|
+
with_maintenance_page { run_with_callbacks(:restart) }
|
300
|
+
shell.status "Finished rollback at #{Time.now.asctime}"
|
301
|
+
rescue Exception
|
302
|
+
shell.status "Failed to rollback at #{Time.now.asctime}"
|
303
|
+
puts_deploy_failure
|
304
|
+
raise
|
305
|
+
end
|
275
306
|
else
|
276
|
-
shell.
|
307
|
+
shell.fatal "Already at oldest release, nothing to roll back to."
|
277
308
|
exit(1)
|
278
309
|
end
|
279
310
|
end
|
@@ -282,8 +313,8 @@ chmod 0700 #{path}
|
|
282
313
|
def migrate
|
283
314
|
return unless c.migrate?
|
284
315
|
@migrations_reached = true
|
316
|
+
cmd = "cd #{c.release_path} && PATH=#{c.binstubs_path}:$PATH #{c.framework_envs} #{c.migration_command}"
|
285
317
|
roles :app_master, :solo do
|
286
|
-
cmd = "cd #{c.release_path} && PATH=#{c.binstubs_path}:$PATH #{c.framework_envs} #{c.migration_command}"
|
287
318
|
shell.status "Migrating: #{cmd}"
|
288
319
|
run(cmd)
|
289
320
|
end
|
@@ -292,7 +323,8 @@ chmod 0700 #{path}
|
|
292
323
|
# task
|
293
324
|
def copy_repository_cache
|
294
325
|
shell.status "Copying to #{c.release_path}"
|
295
|
-
|
326
|
+
exclusions = Array(c.copy_exclude).map { |e| %|--exclude="#{e}"| }.join(' ')
|
327
|
+
run("mkdir -p #{c.release_path} #{c.failed_release_dir} #{c.paths.shared_config} && rsync -aq #{exclusions} #{c.repository_cache}/ #{c.release_path}")
|
296
328
|
|
297
329
|
shell.status "Ensuring proper ownership."
|
298
330
|
sudo("chown -R #{c.user}:#{c.group} #{c.release_path} #{c.failed_release_dir}")
|
@@ -337,7 +369,7 @@ Deploy again if your services configuration appears incomplete or out of date.
|
|
337
369
|
["Creating SQLite database if needed", "touch #{c.shared_path}/databases/#{c.framework_env}.sqlite3"],
|
338
370
|
["Create config directory if needed", "mkdir -p #{c.release_path}/config"],
|
339
371
|
["Generating SQLite config", <<-WRAP],
|
340
|
-
cat > #{c.
|
372
|
+
cat > #{c.paths.shared_config}/database.sqlite3.yml<<'YML'
|
341
373
|
#{c.framework_env}:
|
342
374
|
adapter: sqlite3
|
343
375
|
database: #{c.shared_path}/databases/#{c.framework_env}.sqlite3
|
@@ -345,7 +377,7 @@ cat > #{c.shared_path}/config/database.sqlite3.yml<<'YML'
|
|
345
377
|
timeout: 5000
|
346
378
|
YML
|
347
379
|
WRAP
|
348
|
-
["Symlink database.yml", "ln -nfs #{c.
|
380
|
+
["Symlink database.yml", "ln -nfs #{c.paths.shared_config}/database.sqlite3.yml #{c.release_path}/config/database.yml"],
|
349
381
|
].each do |what, cmd|
|
350
382
|
shell.status "#{what}"
|
351
383
|
run(cmd)
|
@@ -373,15 +405,15 @@ WRAP
|
|
373
405
|
["Set group write permissions", "chmod -R g+w #{release_to_link}"],
|
374
406
|
["Remove revision-tracked shared directories from deployment", "rm -rf #{release_to_link}/log #{release_to_link}/public/system #{release_to_link}/tmp/pids"],
|
375
407
|
["Create tmp directory", "mkdir -p #{release_to_link}/tmp"],
|
376
|
-
["Symlink shared log directory", "ln -nfs #{c.
|
408
|
+
["Symlink shared log directory", "ln -nfs #{c.paths.shared_log} #{release_to_link}/log"],
|
377
409
|
["Create public directory if needed", "mkdir -p #{release_to_link}/public"],
|
378
410
|
["Create config directory if needed", "mkdir -p #{release_to_link}/config"],
|
379
|
-
["Create system directory if needed", "ln -nfs #{c.
|
411
|
+
["Create system directory if needed", "ln -nfs #{c.paths.shared_system} #{release_to_link}/public/system"],
|
380
412
|
["Symlink shared pids directory", "ln -nfs #{c.shared_path}/pids #{release_to_link}/tmp/pids"],
|
381
|
-
["Symlink other shared config files", "find #{c.
|
382
|
-
["Symlink mongrel_cluster.yml", "ln -nfs #{c.
|
383
|
-
["Symlink database.yml", "ln -nfs #{c.
|
384
|
-
["Symlink newrelic.yml if needed", "if [ -f \"#{c.
|
413
|
+
["Symlink other shared config files", "find #{c.paths.shared_config} -type f -not -name 'database.yml' -exec ln -s {} #{release_to_link}/config \\;"],
|
414
|
+
["Symlink mongrel_cluster.yml", "ln -nfs #{c.paths.shared_config}/mongrel_cluster.yml #{release_to_link}/config/mongrel_cluster.yml"],
|
415
|
+
["Symlink database.yml", "ln -nfs #{c.paths.shared_config}/database.yml #{release_to_link}/config/database.yml"],
|
416
|
+
["Symlink newrelic.yml if needed", "if [ -f \"#{c.paths.shared_config}/newrelic.yml\" ]; then ln -nfs #{c.paths.shared_config}/newrelic.yml #{release_to_link}/config/newrelic.yml; fi"],
|
385
417
|
]
|
386
418
|
end
|
387
419
|
|
@@ -402,7 +434,7 @@ WRAP
|
|
402
434
|
shell.status "Running deploy hook: deploy/#{what}.rb"
|
403
435
|
run Escape.shell_command(base_callback_command_for(what)) do |server, cmd|
|
404
436
|
per_instance_args = []
|
405
|
-
per_instance_args << '--current-roles' << server.roles.join(' ')
|
437
|
+
per_instance_args << '--current-roles' << server.roles.to_a.join(' ')
|
406
438
|
per_instance_args << '--current-name' << server.name.to_s if server.name
|
407
439
|
per_instance_args << '--config' << c.to_json
|
408
440
|
cmd << " " << Escape.shell_command(per_instance_args)
|
@@ -438,7 +470,7 @@ WRAP
|
|
438
470
|
cmd << '--environment-name' << config.environment_name
|
439
471
|
cmd << '--account-name' << config.account_name
|
440
472
|
cmd << '--release-path' << config.release_path.to_s
|
441
|
-
cmd << '--framework-env' << config.
|
473
|
+
cmd << '--framework-env' << config.framework_env.to_s
|
442
474
|
cmd << '--verbose' if config.verbose
|
443
475
|
cmd
|
444
476
|
end
|
@@ -472,7 +504,7 @@ WRAP
|
|
472
504
|
def with_maintenance_page
|
473
505
|
conditionally_enable_maintenance_page
|
474
506
|
yield if block_given?
|
475
|
-
|
507
|
+
conditionally_disable_maintenance_page
|
476
508
|
end
|
477
509
|
|
478
510
|
def with_failed_release_cleanup
|
@@ -2,15 +2,22 @@ require 'engineyard-serverside/shell/helpers'
|
|
2
2
|
|
3
3
|
module EY
|
4
4
|
module Serverside
|
5
|
-
class DeployHook
|
5
|
+
class DeployHook
|
6
|
+
def initialize(config, shell, hook_name)
|
7
|
+
@config, @shell, @hook_name = config, shell, hook_name
|
8
|
+
end
|
9
|
+
|
10
|
+
def hook_path
|
11
|
+
"#{@config.release_path}/deploy/#{@hook_name}.rb"
|
12
|
+
end
|
13
|
+
|
6
14
|
def callback_context
|
7
|
-
@context ||= CallbackContext.new(config, shell)
|
15
|
+
@context ||= CallbackContext.new(@config, @shell, hook_path)
|
8
16
|
end
|
9
17
|
|
10
|
-
def
|
11
|
-
hook_path = "#{c.release_path}/deploy/#{hook}.rb"
|
18
|
+
def call
|
12
19
|
if File.exist?(hook_path)
|
13
|
-
Dir.chdir(
|
20
|
+
Dir.chdir(@config.release_path) do
|
14
21
|
if desc = syntax_error(hook_path)
|
15
22
|
hook_name = File.basename(hook_path)
|
16
23
|
abort "*** [Error] Invalid Ruby syntax in hook: #{hook_name} ***\n*** #{desc.chomp} ***"
|
@@ -23,6 +30,19 @@ module EY
|
|
23
30
|
|
24
31
|
def eval_hook(code)
|
25
32
|
callback_context.instance_eval(code)
|
33
|
+
rescue Exception => exception
|
34
|
+
display_hook_error(exception, code, hook_path)
|
35
|
+
raise exception
|
36
|
+
end
|
37
|
+
|
38
|
+
def display_hook_error(exception, code, hook_path)
|
39
|
+
@shell.fatal <<-ERROR
|
40
|
+
Exception raised in deploy hook #{hook_path.inspect}.
|
41
|
+
|
42
|
+
#{exception.class}: #{exception.to_s}
|
43
|
+
|
44
|
+
Please fix this error before retrying.
|
45
|
+
ERROR
|
26
46
|
end
|
27
47
|
|
28
48
|
def syntax_error(file)
|
@@ -35,17 +55,22 @@ module EY
|
|
35
55
|
|
36
56
|
attr_reader :shell
|
37
57
|
|
38
|
-
def initialize(config, shell)
|
58
|
+
def initialize(config, shell, hook_path)
|
39
59
|
@configuration = config
|
40
60
|
@configuration.set_framework_envs
|
41
61
|
@shell = shell
|
42
62
|
@node = node
|
63
|
+
@hook_path = hook_path
|
43
64
|
end
|
44
65
|
|
45
66
|
def config
|
46
67
|
@configuration
|
47
68
|
end
|
48
69
|
|
70
|
+
def inspect
|
71
|
+
"#<DeployHook::CallbackContext #{hook_path.inspect}>"
|
72
|
+
end
|
73
|
+
|
49
74
|
def method_missing(meth, *args, &blk)
|
50
75
|
if @configuration.respond_to?(meth)
|
51
76
|
@configuration.send(meth, *args, &blk)
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module EY
|
4
|
+
module Serverside
|
5
|
+
class Paths
|
6
|
+
|
7
|
+
module LegacyHelpers
|
8
|
+
def deploy_to() paths.deploy_root.to_s end
|
9
|
+
def release_dir() paths.releases.to_s end
|
10
|
+
def failed_release_dir() paths.releases_failed.to_s end
|
11
|
+
def release_path() paths.active_release.to_s end
|
12
|
+
def all_releases() paths.all_releases.map { |path| path.to_s } end
|
13
|
+
def previous_release(*a) paths.previous_release(*a).to_s end
|
14
|
+
def latest_release() paths.latest_release.to_s end
|
15
|
+
def current_path() paths.current.to_s end
|
16
|
+
def shared_path() paths.shared.to_s end
|
17
|
+
def maintenance_page_enabled_path() paths.enabled_maintenance_page.to_s end
|
18
|
+
def repository_cache() paths.repository_cache.to_s end
|
19
|
+
def bundled_gems_path() paths.bundled_gems.to_s end
|
20
|
+
def ruby_version_file() paths.ruby_version.to_s end
|
21
|
+
def system_version_file() paths.system_version.to_s end
|
22
|
+
def binstubs_path() paths.binstubs.to_s end
|
23
|
+
def gemfile_path() paths.gemfile.to_s end
|
24
|
+
def active_revision() paths.active_revision.read.strip end
|
25
|
+
def latest_revision() paths.latest_revision.read.strip end
|
26
|
+
alias revision latest_revision
|
27
|
+
def ssh_identity_file() paths.ssh_identity.to_s end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.def_path(name, parts)
|
31
|
+
define_method(name.to_sym) { path(*parts) }
|
32
|
+
end
|
33
|
+
|
34
|
+
def path(root, *parts)
|
35
|
+
send(root).join(*parts)
|
36
|
+
end
|
37
|
+
|
38
|
+
attr_reader :home, :deploy_root
|
39
|
+
|
40
|
+
def_path :current, [:deploy_root, 'current']
|
41
|
+
def_path :releases, [:deploy_root, 'releases']
|
42
|
+
def_path :releases_failed, [:deploy_root, 'releases_failed']
|
43
|
+
def_path :shared, [:deploy_root, 'shared']
|
44
|
+
def_path :shared_log, [:shared, 'log']
|
45
|
+
def_path :shared_config, [:shared, 'config']
|
46
|
+
def_path :shared_system, [:shared, 'system']
|
47
|
+
def_path :enabled_maintenance_page, [:shared_system, 'maintenance.html']
|
48
|
+
def_path :bundled_gems, [:shared, 'bundled_gems']
|
49
|
+
def_path :ruby_version, [:bundled_gems, 'RUBY_VERSION']
|
50
|
+
def_path :system_version, [:bundled_gems, 'SYSTEM_VERSION']
|
51
|
+
def_path :latest_revision, [:latest_release, 'REVISION']
|
52
|
+
def_path :active_revision, [:active_release, 'REVISION']
|
53
|
+
def_path :binstubs, [:active_release, 'ey_bundler_binstubs']
|
54
|
+
def_path :gemfile, [:active_release, 'Gemfile']
|
55
|
+
|
56
|
+
def initialize(opts)
|
57
|
+
@opts = opts
|
58
|
+
@home = Pathname.new(@opts[:hame] || ENV['HOME'])
|
59
|
+
@app_name = @opts[:app_name]
|
60
|
+
@active_release = Pathname.new(@opts[:active_release]) if @opts[:active_release]
|
61
|
+
@repository_cache = Pathname.new(@opts[:repository_cache]) if @opts[:repository_cache]
|
62
|
+
@deploy_root = Pathname.new(@opts[:deploy_root] || "/data/#{@app_name}")
|
63
|
+
end
|
64
|
+
|
65
|
+
def ssh_identity
|
66
|
+
path(:home, '.ssh', "#{@app_name}-deploy-key")
|
67
|
+
end
|
68
|
+
|
69
|
+
def repository_cache
|
70
|
+
@repository_cache ||= path(:shared, 'cached-copy')
|
71
|
+
end
|
72
|
+
|
73
|
+
def active_release
|
74
|
+
@active_release ||= path(:releases, Time.now.utc.strftime("%Y%m%d%H%M%S"))
|
75
|
+
end
|
76
|
+
|
77
|
+
def all_releases
|
78
|
+
@all_releases ||= Pathname.glob(releases.join('*')).sort
|
79
|
+
end
|
80
|
+
|
81
|
+
# deploy_root/releases/<release before argument release path>
|
82
|
+
def previous_release(current=latest_release)
|
83
|
+
index = all_releases.index(current)
|
84
|
+
if index && index > 0
|
85
|
+
all_releases[index-1]
|
86
|
+
else
|
87
|
+
nil
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# deploy_root/releases/<latest timestamp>
|
92
|
+
def latest_release
|
93
|
+
all_releases.last
|
94
|
+
end
|
95
|
+
|
96
|
+
def rollback
|
97
|
+
if previous_release
|
98
|
+
self.class.new(@opts.dup.merge(:active_release => previous_release))
|
99
|
+
else
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
@@ -1,69 +1,27 @@
|
|
1
|
-
require '
|
1
|
+
require 'set'
|
2
2
|
|
3
3
|
module EY
|
4
4
|
module Serverside
|
5
5
|
class Server < Struct.new(:hostname, :roles, :name, :user)
|
6
|
-
|
7
|
-
|
8
|
-
super "There is already an EY::Serverside::Server with hostname '#{hostname}'"
|
9
|
-
end
|
6
|
+
def self.from_hash(server_hash)
|
7
|
+
new(server_hash[:hostname], Set.new(server_hash[:roles].map{|r|r.to_sym}), server_hash[:name], server_hash[:user])
|
10
8
|
end
|
11
9
|
|
12
10
|
def initialize(*fields)
|
13
11
|
super
|
14
|
-
self.roles = self.roles.map { |r| r.to_sym } if self.roles
|
15
|
-
end
|
16
|
-
|
17
|
-
attr_writer :default_task
|
18
|
-
|
19
|
-
def self.from_roles(*want_roles)
|
20
|
-
want_roles = want_roles.flatten.compact.map{|r| r.to_sym}
|
21
|
-
return all if !want_roles || want_roles.include?(:all) || want_roles.empty?
|
22
|
-
|
23
|
-
all.select do |s|
|
24
|
-
!(s.roles & want_roles).empty?
|
25
|
-
end
|
26
12
|
end
|
27
13
|
|
28
14
|
def role
|
29
15
|
roles.first
|
30
16
|
end
|
31
17
|
|
32
|
-
def
|
33
|
-
|
34
|
-
add(instance_hash)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.all
|
39
|
-
@all
|
40
|
-
end
|
41
|
-
|
42
|
-
def self.by_hostname(hostname)
|
43
|
-
all.find{|s| s.hostname == hostname}
|
44
|
-
end
|
45
|
-
|
46
|
-
def self.add(server_hash)
|
47
|
-
hostname = server_hash[:hostname]
|
48
|
-
if by_hostname(hostname)
|
49
|
-
raise DuplicateHostname.new(hostname)
|
50
|
-
end
|
51
|
-
server = new(hostname, server_hash[:roles], server_hash[:name], server_hash[:user])
|
52
|
-
@all << server
|
53
|
-
server
|
54
|
-
end
|
55
|
-
|
56
|
-
def self.current
|
57
|
-
all.find {|s| s.local? }
|
18
|
+
def matches_roles?(set)
|
19
|
+
(roles & set).any?
|
58
20
|
end
|
59
21
|
|
60
|
-
def self.reset
|
61
|
-
@all = []
|
62
|
-
end
|
63
|
-
reset
|
64
|
-
|
65
22
|
def roles=(roles)
|
66
|
-
|
23
|
+
roles_set = Set.new roles.map{|r| r.to_sym}
|
24
|
+
super roles_set
|
67
25
|
end
|
68
26
|
|
69
27
|
def local?
|
@@ -102,16 +60,6 @@ module EY
|
|
102
60
|
@known_hosts_file ||= Tempfile.new('ey-ss-known-hosts')
|
103
61
|
end
|
104
62
|
|
105
|
-
# Make a known hosts tempfile to absorb host fingerprints so we don't show
|
106
|
-
#
|
107
|
-
# Warning: Permanently added 'xxx' (RSA) to the list of known hosts.
|
108
|
-
#
|
109
|
-
# for every ssh command.
|
110
|
-
# (even with StrictHostKeyChecking=no, the warning output is annoying)
|
111
|
-
def self.known_hosts_file
|
112
|
-
@known_hosts_file ||= Tempfile.new('ey-ss-known-hosts')
|
113
|
-
end
|
114
|
-
|
115
63
|
def ssh_command
|
116
64
|
"ssh -i #{ENV['HOME']}/.ssh/internal -o StrictHostKeyChecking=no -o UserKnownHostsFile=#{self.class.known_hosts_file.path} -o PasswordAuthentication=no "
|
117
65
|
end
|