engineyard-serverside 1.3.7 → 1.4.0
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/bin/engineyard-serverside +1 -1
- data/lib/engineyard-serverside.rb +37 -33
- data/lib/engineyard-serverside/bundle_installer.rb +3 -1
- data/lib/engineyard-serverside/cli.rb +196 -194
- data/lib/engineyard-serverside/configuration.rb +109 -107
- data/lib/engineyard-serverside/deploy.rb +273 -271
- data/lib/engineyard-serverside/deploy_hook.rb +57 -55
- data/lib/engineyard-serverside/deprecation.rb +27 -0
- data/lib/engineyard-serverside/lockfile_parser.rb +80 -78
- data/lib/engineyard-serverside/logged_output.rb +56 -54
- data/lib/engineyard-serverside/server.rb +67 -64
- data/lib/engineyard-serverside/strategies/git.rb +110 -108
- data/lib/engineyard-serverside/task.rb +48 -45
- data/lib/engineyard-serverside/version.rb +3 -1
- data/spec/custom_deploy_spec.rb +5 -5
- data/spec/deploy_hook_spec.rb +3 -3
- data/spec/deprecation_spec.rb +25 -0
- data/spec/git_strategy_spec.rb +1 -1
- data/spec/lockfile_parser_spec.rb +4 -4
- data/spec/real_deploy_spec.rb +13 -7
- data/spec/restart_spec.rb +4 -4
- data/spec/server_spec.rb +24 -24
- data/spec/spec_helper.rb +15 -13
- metadata +8 -5
@@ -1,77 +1,79 @@
|
|
1
1
|
module EY
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
2
|
+
module Serverside
|
3
|
+
class DeployHook < Task
|
4
|
+
def initialize(options)
|
5
|
+
super(EY::Serverside::Deploy::Configuration.new(options))
|
6
|
+
end
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
def callback_context
|
9
|
+
@context ||= CallbackContext.new(config)
|
10
|
+
end
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
12
|
+
def run(hook)
|
13
|
+
hook_path = "#{c.release_path}/deploy/#{hook}.rb"
|
14
|
+
if File.exist?(hook_path)
|
15
|
+
Dir.chdir(c.release_path) do
|
16
|
+
puts "~> running deploy hook: deploy/#{hook}.rb"
|
17
|
+
if desc = syntax_error(hook_path)
|
18
|
+
hook_name = File.basename(hook_path)
|
19
|
+
abort "*** [Error] Invalid Ruby syntax in hook: #{hook_name} ***\n*** #{desc.chomp} ***"
|
20
|
+
else
|
21
|
+
callback_context.instance_eval(IO.read(hook_path))
|
22
|
+
end
|
21
23
|
end
|
22
24
|
end
|
23
25
|
end
|
24
|
-
end
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
|
-
class CallbackContext
|
32
|
-
def initialize(config)
|
33
|
-
@configuration = config
|
34
|
-
@node = node
|
27
|
+
def syntax_error(file)
|
28
|
+
valid = Kernel.system("ruby -c #{file} 2>/tmp/ey_invalid_deploy_hook | grep 'Syntax OK' --quiet")
|
29
|
+
File.new("/tmp/ey_invalid_deploy_hook").gets unless valid
|
35
30
|
end
|
36
31
|
|
37
|
-
|
38
|
-
|
39
|
-
@configuration
|
40
|
-
|
41
|
-
super
|
32
|
+
class CallbackContext
|
33
|
+
def initialize(config)
|
34
|
+
@configuration = config
|
35
|
+
@node = node
|
42
36
|
end
|
43
|
-
end
|
44
37
|
|
45
|
-
|
46
|
-
|
47
|
-
|
38
|
+
def method_missing(meth, *args, &blk)
|
39
|
+
if @configuration.respond_to?(meth)
|
40
|
+
@configuration.send(meth, *args, &blk)
|
41
|
+
else
|
42
|
+
super
|
43
|
+
end
|
44
|
+
end
|
48
45
|
|
49
|
-
|
50
|
-
|
51
|
-
|
46
|
+
def respond_to?(meth, include_private=false)
|
47
|
+
@configuration.respond_to?(meth, include_private) || super
|
48
|
+
end
|
52
49
|
|
53
|
-
|
54
|
-
|
55
|
-
|
50
|
+
def run(cmd)
|
51
|
+
system(Escape.shell_command(["sh", "-l", "-c", cmd]))
|
52
|
+
end
|
53
|
+
|
54
|
+
def sudo(cmd)
|
55
|
+
system(Escape.shell_command(["sudo", "sh", "-l", "-c", cmd]))
|
56
|
+
end
|
56
57
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
58
|
+
# convenience functions for running on certain instance types
|
59
|
+
def on_app_master(&blk) on_roles(%w[solo app_master], &blk) end
|
60
|
+
def on_app_servers(&blk) on_roles(%w[solo app_master app], &blk) end
|
61
|
+
def on_app_servers_and_utilities(&blk) on_roles(%w[solo app_master app util], &blk) end
|
61
62
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
63
|
+
def on_utilities(*names, &blk)
|
64
|
+
names.flatten!
|
65
|
+
on_roles(%w[util]) do
|
66
|
+
blk.call if names.empty? || names.include?(current_name)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
def on_roles(desired_roles)
|
72
|
+
yield if desired_roles.any? { |role| current_roles.include?(role) }
|
66
73
|
end
|
67
|
-
end
|
68
74
|
|
69
|
-
private
|
70
|
-
def on_roles(desired_roles)
|
71
|
-
yield if desired_roles.any? { |role| current_roles.include?(role) }
|
72
75
|
end
|
73
76
|
|
74
77
|
end
|
75
|
-
|
76
78
|
end
|
77
79
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module EY
|
2
|
+
module Serverside
|
3
|
+
def self.deprecation_warning(msg)
|
4
|
+
STDERR.puts "DEPRECATION WARNING: #{msg}"
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.const_missing(const)
|
9
|
+
if EY::Serverside.const_defined?(const)
|
10
|
+
EY::Serverside.deprecation_warning("EY::#{const} has been deprecated. use EY::Serverside::#{const} instead")
|
11
|
+
EY::Serverside.class_eval(const.to_s)
|
12
|
+
else
|
13
|
+
super
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.node
|
18
|
+
EY::Serverside.deprecation_warning("EY.node has been deprecated. use EY::Serverside.node instead")
|
19
|
+
EY::Serverside.node
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.dna_json
|
23
|
+
EY::Serverside.deprecation_warning("EY.dna_json has been deprecated. use EY::Serverside.dna_json instead")
|
24
|
+
EY::Serverside.dna_json
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -1,101 +1,103 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
module EY
|
3
|
-
|
3
|
+
module Serverside
|
4
|
+
class LockfileParser
|
4
5
|
|
5
|
-
|
6
|
+
attr_reader :bundler_version, :lockfile_version
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
def initialize(lockfile_contents)
|
9
|
+
@lockfile_version, @bundler_version = Parse106.new(lockfile_contents).parse
|
10
|
+
end
|
10
11
|
|
11
|
-
|
12
|
+
private
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
class BaseParser
|
15
|
+
def initialize(contents)
|
16
|
+
@contents = contents
|
17
|
+
end
|
18
|
+
def parse
|
19
|
+
raise "Unknown lockfile format #{@contents[0,50]}..."
|
20
|
+
end
|
19
21
|
end
|
20
|
-
end
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
28
|
-
bundler_version = from_yaml['specs'].map do |spec|
|
29
|
-
# spec is a one-element hash: the key is the gem name, and
|
30
|
-
# the value is {"version" => the-version}.
|
31
|
-
if spec.keys.first == "bundler"
|
32
|
-
spec.values.first["version"]
|
23
|
+
class Parse09 < BaseParser
|
24
|
+
def parse
|
25
|
+
from_yaml = safe_yaml_load(@contents)
|
26
|
+
unless from_yaml.is_a?(Hash)
|
27
|
+
return super
|
33
28
|
end
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
29
|
+
bundler_version = from_yaml['specs'].map do |spec|
|
30
|
+
# spec is a one-element hash: the key is the gem name, and
|
31
|
+
# the value is {"version" => the-version}.
|
32
|
+
if spec.keys.first == "bundler"
|
33
|
+
spec.values.first["version"]
|
34
|
+
end
|
35
|
+
end.compact.first
|
36
|
+
[:bundler09, bundler_version]
|
37
|
+
end
|
38
|
+
def safe_yaml_load(loadable)
|
39
|
+
YAML.load(loadable) #won't always raise... soemtimes parses the contents as 1 big string
|
40
|
+
rescue ArgumentError => e # not yaml
|
41
|
+
nil
|
42
|
+
end
|
41
43
|
end
|
42
|
-
end
|
43
44
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
45
|
+
class Parse10 < Parse09
|
46
|
+
def parse
|
47
|
+
unless @contents.index(/^DEPENDENCIES/)
|
48
|
+
return super
|
49
|
+
end
|
50
|
+
dep_section = ""
|
51
|
+
in_dependencies_section = false
|
52
|
+
@contents.each_line do |line|
|
53
|
+
if line =~ /^DEPENDENCIES/
|
54
|
+
in_dependencies_section = true
|
55
|
+
elsif line =~ /^\S/
|
56
|
+
in_dependencies_section = false
|
57
|
+
elsif in_dependencies_section
|
58
|
+
dep_section << line
|
59
|
+
end
|
58
60
|
end
|
59
|
-
end
|
60
61
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
62
|
+
unless dep_section.length > 0
|
63
|
+
raise "Couldn't parse #{@contents}; exiting"
|
64
|
+
exit(1)
|
65
|
+
end
|
65
66
|
|
66
|
-
|
67
|
-
|
68
|
-
|
67
|
+
result = dep_section.scan(/^\s*bundler\s*\(=\s*([^\)]+)\)/).first
|
68
|
+
bundler_version = result ? result.first : nil
|
69
|
+
[:bundler10, bundler_version]
|
70
|
+
end
|
69
71
|
end
|
70
|
-
end
|
71
72
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
73
|
+
class Parse106 < Parse10
|
74
|
+
def parse
|
75
|
+
unless @contents.index(/^METADATA/)
|
76
|
+
return super
|
77
|
+
end
|
78
|
+
meta_section = ""
|
79
|
+
in_meta_section = false
|
80
|
+
@contents.each_line do |line|
|
81
|
+
if line =~ /^METADATA/
|
82
|
+
in_meta_section = true
|
83
|
+
elsif line =~ /^\S/
|
84
|
+
in_meta_section = false
|
85
|
+
elsif in_meta_section
|
86
|
+
meta_section << line
|
87
|
+
end
|
86
88
|
end
|
87
|
-
end
|
88
89
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
90
|
+
unless meta_section.length > 0
|
91
|
+
raise "Couldn't parse #{@contents}; exiting"
|
92
|
+
exit(1)
|
93
|
+
end
|
93
94
|
|
94
|
-
|
95
|
-
|
96
|
-
|
95
|
+
result = meta_section.scan(/^\s*version:\s*(.*)$/).first
|
96
|
+
bundler_version = result ? result.first : nil
|
97
|
+
[:bundler10, bundler_version]
|
98
|
+
end
|
97
99
|
end
|
98
|
-
end
|
99
100
|
|
101
|
+
end
|
100
102
|
end
|
101
103
|
end
|
@@ -1,78 +1,80 @@
|
|
1
1
|
require 'open4'
|
2
2
|
|
3
3
|
module EY
|
4
|
-
module
|
4
|
+
module Serverside
|
5
|
+
module LoggedOutput
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
class Tee
|
8
|
+
def initialize(*streams)
|
9
|
+
@streams = streams.flatten
|
10
|
+
end
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
def <<(output)
|
13
|
+
@streams.each do |s|
|
14
|
+
s << output
|
15
|
+
s.flush
|
16
|
+
end
|
17
|
+
self
|
15
18
|
end
|
16
|
-
|
17
|
-
end
|
18
|
-
end # Tee
|
19
|
+
end # Tee
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
@@logfile = File.join(ENV['HOME'], 'ey.log')
|
22
|
+
def self.logfile=(filename)
|
23
|
+
File.unlink filename if File.exist?(filename) # start fresh
|
24
|
+
@@logfile = filename
|
25
|
+
end
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
27
|
+
def self.logfile
|
28
|
+
@@logfile
|
29
|
+
end
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
31
|
+
@@verbose = false
|
32
|
+
def self.verbose=(v)
|
33
|
+
@@verbose = !!v
|
34
|
+
end
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
36
|
+
def self.verbose?
|
37
|
+
@@verbose
|
38
|
+
end
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
|
40
|
+
def verbose?
|
41
|
+
EY::Serverside::LoggedOutput.verbose?
|
42
|
+
end
|
42
43
|
|
43
|
-
|
44
|
-
|
45
|
-
|
44
|
+
def info(msg)
|
45
|
+
with_logfile do |log|
|
46
|
+
Tee.new($stdout, log) << (msg + "\n")
|
47
|
+
end
|
46
48
|
end
|
47
|
-
end
|
48
49
|
|
49
|
-
|
50
|
-
|
51
|
-
|
50
|
+
def debug(msg)
|
51
|
+
with_logfile do |log|
|
52
|
+
log << "#{msg}\n"
|
53
|
+
end
|
52
54
|
end
|
53
|
-
end
|
54
55
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
56
|
+
def logged_system(cmd)
|
57
|
+
with_logfile do |log|
|
58
|
+
out = verbose? ? Tee.new($stdout, log) : log
|
59
|
+
err = Tee.new($stderr, log) # we always want to see errors
|
60
|
+
|
61
|
+
out << ":: running #{cmd}\n"
|
59
62
|
|
60
|
-
|
63
|
+
# :quiet means don't raise an error on nonzero exit status
|
64
|
+
status = Open4.spawn cmd, 0 => '', 1 => out, 2 => err, :quiet => true
|
65
|
+
status.exitstatus == 0
|
66
|
+
end
|
67
|
+
end
|
61
68
|
|
62
|
-
|
63
|
-
|
64
|
-
|
69
|
+
private
|
70
|
+
def with_logfile
|
71
|
+
File.open(logfile, 'a') {|f| yield f }
|
65
72
|
end
|
66
|
-
end
|
67
73
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
end
|
74
|
+
def logfile
|
75
|
+
EY::Serverside::LoggedOutput.logfile
|
76
|
+
end
|
72
77
|
|
73
|
-
def logfile
|
74
|
-
EY::LoggedOutput.logfile
|
75
78
|
end
|
76
|
-
|
77
79
|
end
|
78
80
|
end
|