engineyard-serverside 2.7.8pre2 → 2.8.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. data/bin/engineyard-serverside +1 -1
  2. data/bin/engineyard-serverside-execute-hook +1 -1
  3. data/bin/engineyard-serverside-execute-service-hook +35 -0
  4. data/lib/engineyard-serverside.rb +0 -1
  5. data/lib/engineyard-serverside/about.rb +11 -8
  6. data/lib/engineyard-serverside/callbacks.rb +11 -0
  7. data/lib/engineyard-serverside/callbacks/collection.rb +17 -0
  8. data/lib/engineyard-serverside/callbacks/collection/base.rb +79 -0
  9. data/lib/engineyard-serverside/callbacks/collection/combined.rb +45 -0
  10. data/lib/engineyard-serverside/callbacks/collection/deploy_hooks.rb +21 -0
  11. data/lib/engineyard-serverside/callbacks/collection/service_hooks.rb +17 -0
  12. data/lib/engineyard-serverside/callbacks/collection/service_hooks/collection.rb +24 -0
  13. data/lib/engineyard-serverside/callbacks/collection/service_hooks/combined.rb +40 -0
  14. data/lib/engineyard-serverside/callbacks/distributor.rb +21 -0
  15. data/lib/engineyard-serverside/callbacks/distributor/remote.rb +76 -0
  16. data/lib/engineyard-serverside/callbacks/distributor/viability_filter.rb +66 -0
  17. data/lib/engineyard-serverside/callbacks/executor.rb +23 -0
  18. data/lib/engineyard-serverside/callbacks/executor/base.rb +44 -0
  19. data/lib/engineyard-serverside/callbacks/executor/executable.rb +123 -0
  20. data/lib/engineyard-serverside/callbacks/executor/ruby.rb +20 -0
  21. data/lib/engineyard-serverside/callbacks/executor/ruby/context.rb +81 -0
  22. data/lib/engineyard-serverside/callbacks/executor/ruby/executor.rb +118 -0
  23. data/{spec/fixtures/gitrepo/bar → lib/engineyard-serverside/callbacks/hooks.rb} +0 -0
  24. data/lib/engineyard-serverside/callbacks/hooks/app.rb +21 -0
  25. data/lib/engineyard-serverside/callbacks/hooks/base.rb +43 -0
  26. data/lib/engineyard-serverside/callbacks/hooks/service.rb +28 -0
  27. data/lib/engineyard-serverside/callbacks/service_hook.rb +20 -0
  28. data/lib/engineyard-serverside/cli.rb +4 -225
  29. data/lib/engineyard-serverside/cli/app.rb +136 -0
  30. data/lib/engineyard-serverside/cli/helpers.rb +58 -0
  31. data/lib/engineyard-serverside/cli/server_hash_extractor.rb +49 -0
  32. data/lib/engineyard-serverside/cli/workflows.rb +45 -0
  33. data/lib/engineyard-serverside/cli/workflows/base.rb +78 -0
  34. data/lib/engineyard-serverside/cli/workflows/calling_deploy_hooks.rb +31 -0
  35. data/lib/engineyard-serverside/cli/workflows/deploying_applications.rb +28 -0
  36. data/lib/engineyard-serverside/cli/workflows/disabling_maintenance.rb +29 -0
  37. data/lib/engineyard-serverside/cli/workflows/enabling_maintenance.rb +29 -0
  38. data/lib/engineyard-serverside/cli/workflows/errors.rb +13 -0
  39. data/lib/engineyard-serverside/cli/workflows/helpers.rb +21 -0
  40. data/lib/engineyard-serverside/cli/workflows/integrating_servers.rb +71 -0
  41. data/lib/engineyard-serverside/cli/workflows/restarting_applications.rb +36 -0
  42. data/lib/engineyard-serverside/cli/workflows/rolling_back_applications.rb +28 -0
  43. data/lib/engineyard-serverside/cli/workflows/showing_maintenance_status.rb +28 -0
  44. data/lib/engineyard-serverside/configuration.rb +1 -0
  45. data/lib/engineyard-serverside/dependency_manager/bundler.rb +46 -18
  46. data/lib/engineyard-serverside/dependency_manager/npm.rb +12 -1
  47. data/lib/engineyard-serverside/deploy.rb +7 -45
  48. data/lib/engineyard-serverside/maintenance.rb +1 -9
  49. data/lib/engineyard-serverside/paths.rb +11 -0
  50. data/lib/engineyard-serverside/propagator.rb +59 -0
  51. data/lib/engineyard-serverside/rails_assets.rb +2 -1
  52. data/lib/engineyard-serverside/slug.rb +7 -0
  53. data/lib/engineyard-serverside/slug/distributor.rb +58 -0
  54. data/lib/engineyard-serverside/slug/enabler.rb +100 -0
  55. data/lib/engineyard-serverside/slug/failure_handler.rb +24 -0
  56. data/lib/engineyard-serverside/slug/finalizer.rb +86 -0
  57. data/lib/engineyard-serverside/slug/generator.rb +29 -0
  58. data/lib/engineyard-serverside/slug/migrator.rb +41 -0
  59. data/lib/engineyard-serverside/slug/restarter.rb +103 -0
  60. data/lib/engineyard-serverside/slug/source.rb +16 -0
  61. data/lib/engineyard-serverside/slug/source/updater.rb +194 -0
  62. data/lib/engineyard-serverside/version.rb +1 -1
  63. data/lib/railway.rb +43 -0
  64. data/lib/result.rb +7 -0
  65. data/lib/result/base.rb +41 -0
  66. data/lib/result/dsl.rb +16 -0
  67. data/lib/result/failure.rb +29 -0
  68. data/lib/result/success.rb +24 -0
  69. data/lib/runner.rb +34 -0
  70. data/spec/archive_deploy_spec.rb +1 -1
  71. data/spec/bundler_deploy_spec.rb +22 -1
  72. data/spec/configuration_spec.rb +1 -0
  73. data/spec/deploy_hook_spec.rb +148 -132
  74. data/spec/fixtures/lockfiles/1.15.1-no-bundler +51 -0
  75. data/spec/fixtures/repos/assets_error/Gemfile +5 -0
  76. data/spec/fixtures/repos/assets_error/Gemfile.lock +88 -0
  77. data/spec/fixtures/repos/assets_error/README +1 -0
  78. data/spec/fixtures/repos/assets_error/Rakefile +4 -0
  79. data/spec/fixtures/{gitrepo/foo → repos/assets_error/app/assets/empty} +0 -0
  80. data/spec/fixtures/repos/assets_error/config/application.rb +5 -0
  81. data/spec/fixtures/repos/assets_error/config/ey.yml +4 -0
  82. data/spec/fixtures/repos/bundler_old/Gemfile +5 -0
  83. data/spec/fixtures/repos/bundler_old/Gemfile.lock +15 -0
  84. data/spec/fixtures/repos/bundler_old/README +1 -0
  85. data/spec/fixtures/repos/no_ey_config_no_warning/Gemfile +3 -0
  86. data/spec/fixtures/repos/no_ey_config_no_warning/Gemfile.lock +10 -0
  87. data/spec/fixtures/repos/no_ey_config_no_warning/README +1 -0
  88. data/spec/fixtures/repos/no_ey_config_no_warning/ey.yml +5 -0
  89. data/spec/lockfile_parser_spec.rb +5 -1
  90. data/spec/rails31_deploy_spec.rb +8 -0
  91. data/spec/rollback_spec.rb +1 -1
  92. data/spec/services_deploy_spec.rb +12 -0
  93. data/spec/spec_helper.rb +14 -8
  94. metadata +488 -429
  95. data/lib/engineyard-serverside/cli_helpers.rb +0 -53
  96. data/lib/engineyard-serverside/deploy_hook.rb +0 -142
@@ -0,0 +1,66 @@
1
+ require 'railway'
2
+
3
+ module EY
4
+ module Serverside
5
+ module Callbacks
6
+ module Distributor
7
+
8
+ class ViabilityFilter
9
+ include Railway
10
+
11
+ step :normalize_input
12
+ step :check_ruby_candidates
13
+ step :check_executable_candidates
14
+ step :calculate_callback_name
15
+
16
+ def normalize_input(input = {})
17
+ input[:viable] = []
18
+
19
+ unless input[:candidates].respond_to?(:each)
20
+ input[:candidates] = [input[:candidates]]
21
+ end
22
+
23
+ Success(input)
24
+ end
25
+
26
+ def check_ruby_candidates(input = {})
27
+ hooks = input[:candidates].
28
+ select {|hook| hook.flavor == :ruby}
29
+
30
+ hooks.each do |hook|
31
+ input[:viable].push(hook)
32
+ end
33
+
34
+ Success(input)
35
+ end
36
+
37
+ def check_executable_candidates(input = {})
38
+ hooks = input[:candidates].
39
+ select {|hook| hook.flavor == :executable}
40
+
41
+ hooks.each do |hook|
42
+ if hook.path.executable?
43
+ input[:viable].push(hook)
44
+ else
45
+ input[:shell].warning(
46
+ "Skipping possible deploy hook #{hook} because it is not executable."
47
+ )
48
+ end
49
+ end
50
+
51
+ Success(input)
52
+ end
53
+
54
+ def calculate_callback_name(input = {})
55
+ if input[:viable].empty?
56
+ return Failure(input.merge({:reason => :no_viable_hooks}))
57
+ end
58
+
59
+ Success(input[:viable].first.callback_name)
60
+ end
61
+ end
62
+
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,23 @@
1
+ require 'engineyard-serverside/callbacks/executor/executable'
2
+ require 'engineyard-serverside/callbacks/executor/ruby'
3
+
4
+ module EY
5
+ module Serverside
6
+ module Callbacks
7
+
8
+ module Executor
9
+ FLAVORS = {
10
+ :ruby => Ruby,
11
+ :executable => Executable
12
+ }
13
+
14
+ def self.execute(config, shell, hooks)
15
+ hooks.each do |hook|
16
+ FLAVORS[hook.flavor].execute(config, shell, hook)
17
+ end
18
+ end
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,44 @@
1
+ require 'railway'
2
+
3
+ module EY
4
+ module Serverside
5
+ module Callbacks
6
+ module Executor
7
+
8
+ class Base
9
+ include Railway
10
+
11
+ attr_reader :config, :shell, :hook
12
+
13
+ def self.execute(config, shell, hook)
14
+ new(config, shell, hook).execute
15
+ end
16
+
17
+ def initialize(config, shell, hook)
18
+ @config = config
19
+ @shell = shell
20
+ @hook = hook
21
+ end
22
+
23
+ def execute
24
+ call.or_else {|payload| handle_failure(payload)}
25
+ end
26
+
27
+ def handle_failure(payload = {})
28
+ raise "Unimplemented Hook Executor!"
29
+ end
30
+
31
+ def paths
32
+ config.paths
33
+ end
34
+
35
+ def hook_path
36
+ hook.path
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,123 @@
1
+ require 'escape'
2
+ require 'runner'
3
+ require 'engineyard-serverside/callbacks/executor/base'
4
+
5
+ module EY
6
+ module Serverside
7
+ module Callbacks
8
+ module Executor
9
+
10
+ class Executable < Base
11
+ include Runner
12
+
13
+ step :validate_hook
14
+ step :populate_environment
15
+ step :calculate_wrapper
16
+ step :run_hook
17
+
18
+ def handle_failure(payload = {})
19
+ case payload[:reason]
20
+
21
+ when :not_executable
22
+ true
23
+ when :execution_failed
24
+ abort "*** [Error] Hook failed to exit cleanly: #{hook_path} ***\n"
25
+ else
26
+ abort "*** [Error] An unknown error occurred for hook: #{hook_path} ***\n"
27
+ end
28
+ end
29
+
30
+ def validate_hook(input = {})
31
+ unless hook_path.executable?
32
+ return Failure(
33
+ input.merge(
34
+ {
35
+ :reason => :not_executable
36
+ }
37
+ )
38
+ )
39
+ end
40
+
41
+ Success(input)
42
+ end
43
+
44
+ def calculate_wrapper(input = {})
45
+ Success(
46
+ input.merge(
47
+ {
48
+ :wrapper => hook.respond_to?(:service_name) ?
49
+ About.service_hook_executor :
50
+ About.hook_executor
51
+ }
52
+ )
53
+ )
54
+ end
55
+
56
+ def run_hook(input = {})
57
+ env = "#{input[:environment]} #{config.framework_envs}"
58
+ wrapper = input[:wrapper]
59
+ name = hook.short_name
60
+
61
+ result = run("#{env} #{wrapper} #{name}")
62
+
63
+ unless result.success?
64
+ return Failure(
65
+ input.merge(
66
+ {
67
+ :reason => :execution_failed
68
+ }
69
+ )
70
+ )
71
+ end
72
+
73
+ Success(input)
74
+ end
75
+
76
+ def populate_environment(input = {})
77
+ env = {
78
+ 'EY_DEPLOY_ACCOUNT_NAME' => config.account_name,
79
+ 'EY_DEPLOY_APP' => config.app,
80
+ 'EY_DEPLOY_CONFIG' => config.to_json,
81
+ 'EY_DEPLOY_CURRENT_ROLES' => current_roles,
82
+ 'EY_DEPLOY_CURRENT_NAME' => current_name,
83
+ 'EY_DEPLOY_ENVIRONMENT_NAME' => config.environment_name,
84
+ 'EY_DEPLOY_FRAMEWORK_ENV' => config.framework_env.to_s,
85
+ 'EY_DEPLOY_RELEASE_PATH' => paths.active_release.to_s,
86
+ 'EY_DEPLOY_VERBOSE' => verbose,
87
+ }
88
+
89
+ Success(
90
+ input.merge(
91
+ {
92
+ :environment => env.
93
+ reject {|name, value| value.nil?}.
94
+ map {|name, value| "#{name}=#{Escape.shell_command([value])}"}.
95
+ join(' ')
96
+ }
97
+ )
98
+ )
99
+ end
100
+
101
+ def verbose
102
+ config.verbose ? '1' : '0'
103
+ end
104
+
105
+ def current_roles
106
+ config.current_roles.to_a.join(' ')
107
+ end
108
+
109
+ def current_name
110
+ name = config.current_name
111
+
112
+ unless name
113
+ return nil
114
+ end
115
+
116
+ name.to_s
117
+ end
118
+ end
119
+
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,20 @@
1
+ require 'rbconfig'
2
+ require 'railway'
3
+
4
+ require 'engineyard-serverside/callbacks/executor/ruby/executor'
5
+
6
+ module EY
7
+ module Serverside
8
+ module Callbacks
9
+ module Executor
10
+
11
+ module Ruby
12
+ def self.execute(config, shell, hook)
13
+ Executor.execute(config, shell,hook)
14
+ end
15
+ end
16
+
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,81 @@
1
+ require 'engineyard-serverside/shell/helpers'
2
+
3
+ module EY
4
+ module Serverside
5
+ module Callbacks
6
+ module Executor
7
+ module Ruby
8
+
9
+ class Context
10
+ include EY::Serverside::Shell::Helpers
11
+
12
+ attr_reader :configuration, :shell, :hook
13
+
14
+ def initialize(config, shell, hook)
15
+ @configuration = config
16
+ @configuration.set_framework_envs
17
+ @shell = shell
18
+ @node = config.node
19
+ @hook = hook
20
+ end
21
+
22
+ def config
23
+ @configuration
24
+ end
25
+
26
+ def inspect
27
+ "#<Callbacks::Executor::Ruby::Context #{hook.path.inspect}>"
28
+ end
29
+
30
+ def method_missing(meth, *args, &blk)
31
+ if config.respond_to?(meth)
32
+ shell.warning "Use of `#{meth}` (via method_missing) is deprecated in favor of `config.#{meth}` for improved error messages and compatibility.\n\tin #{hook.path}"
33
+ config.send(meth, *args, &blk)
34
+ else
35
+ super
36
+ end
37
+ end
38
+
39
+ def respond_to?(*a)
40
+ config.respond_to?(*a) || super
41
+ end
42
+
43
+ def run(cmd)
44
+ shell.logged_system(Escape.shell_command(["sh", "-l", "-c", cmd])).success?
45
+ end
46
+
47
+ def run!(cmd)
48
+ run(cmd) or raise("run!: Command failed. #{cmd}")
49
+ end
50
+
51
+ def sudo(cmd)
52
+ shell.logged_system(Escape.shell_command(["sudo", "sh", "-l", "-c", cmd])).success?
53
+ end
54
+
55
+ def sudo!(cmd)
56
+ sudo(cmd) or raise("sudo!: Command failed. #{cmd}")
57
+ end
58
+
59
+ # convenience functions for running on certain instance types
60
+ def on_app_master(&blk) on_roles(%w[solo app_master], &blk) end
61
+ def on_app_servers(&blk) on_roles(%w[solo app_master app], &blk) end
62
+ def on_app_servers_and_utilities(&blk) on_roles(%w[solo app_master app util], &blk) end
63
+
64
+ def on_utilities(*names, &blk)
65
+ names.flatten!
66
+ on_roles(%w[util]) do
67
+ blk.call if names.empty? || names.include?(config.current_name)
68
+ end
69
+ end
70
+
71
+ private
72
+ def on_roles(desired_roles)
73
+ yield if desired_roles.any? { |role| config.current_roles.include?(role) }
74
+ end
75
+ end
76
+
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,118 @@
1
+ require 'rbconfig'
2
+ require 'railway'
3
+ require 'engineyard-serverside/callbacks/executor/base'
4
+ require 'engineyard-serverside/callbacks/executor/ruby/context'
5
+
6
+ module EY
7
+ module Serverside
8
+ module Callbacks
9
+ module Executor
10
+ module Ruby
11
+
12
+ # An executor for Ruby hooks
13
+ class Executor < Base
14
+ step :validate_hook
15
+ step :display_deprecation_warnings
16
+ step :announce_execution
17
+ step :context_eval
18
+
19
+ def handle_failure(payload = {})
20
+ case payload[:reason]
21
+
22
+ # We tried to execute the hook, but doing so raised an exception.
23
+ # So, let us tell you all about that and propagate the error to
24
+ # the caller.
25
+ when :execution_failed
26
+ exception = payload[:exception]
27
+ display_hook_error(exception)
28
+ raise exception
29
+
30
+ # A syntax error was detected in the hook, so rather than trying
31
+ # to run it, we bail with some information for the user.
32
+ when :syntax_error
33
+ abort "*** [Error] Invalid Ruby syntax in hook: #{hook_path} ***\n*** #{payload[:syntax_error]} ***"
34
+
35
+ # Something most out of the ordinary happened, to the point that
36
+ # we don't know how to handle it. That being the case, we're going
37
+ # to just flat out bail.
38
+ else
39
+ abort "*** [Error] An unknown error occurred for hook: #{hook_path} ***"
40
+ end
41
+ end
42
+
43
+ def display_deprecation_warnings(input = {})
44
+ code = input[:code]
45
+
46
+ if code =~ /@configuration/
47
+ shell.warning("Use of `@configuration` in deploy hooks is deprecated.\nPlease use `config`, which provides access to the same object.\n\tin #{hook_path}")
48
+ end
49
+
50
+ if code =~ /@node/
51
+ shell.warning("Use of `@node` in deploy hooks is deprecated.\nPlease use `config.node`, which provides access to the same object.\n\tin #{hook_path}")
52
+ end
53
+
54
+ Success(input)
55
+ end
56
+
57
+ def announce_execution(input = {})
58
+ shell.info "Executing #{hook.path} ..."
59
+ Success(input)
60
+ end
61
+
62
+ def context_eval(input = {})
63
+ Dir.chdir(paths.active_release.to_s) do
64
+ begin
65
+ Context.new(config, shell, hook).instance_eval(input[:code])
66
+ rescue Exception => exception
67
+ return Failure(
68
+ input.merge(
69
+ {
70
+ :reason => :execution_failed,
71
+ :exception => exception
72
+ }
73
+ )
74
+ )
75
+ end
76
+ end
77
+
78
+ Success(input)
79
+ end
80
+
81
+ def validate_hook(input = {})
82
+ output = `#{ruby_bin} -c #{hook_path} 2>&1`
83
+ unless output =~ /Syntax OK/
84
+ return Failure(
85
+ input.merge(
86
+ {
87
+ :reason => :syntax_error,
88
+ :syntax_error => output
89
+ }
90
+ )
91
+ )
92
+ end
93
+
94
+ Success(input.merge({:code => hook.read}))
95
+ end
96
+
97
+ def ruby_bin
98
+ # Ideally, we'd use RbConfig.ruby, but that doesn't work on 1.8.7
99
+ File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'])
100
+ end
101
+
102
+ def display_hook_error(exception)
103
+ shell.fatal <<-ERROR
104
+ Exception raised in hook #{hook_path}.
105
+
106
+ #{exception.class}: #{exception.to_s}
107
+
108
+ Please fix this error before retrying.
109
+ ERROR
110
+ end
111
+
112
+ end
113
+
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end