engineyard-serverside 1.3.7 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|