engineyard-serverside 2.0.0.pre3 → 2.0.0.pre4
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/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
|