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.
@@ -2,92 +2,95 @@ require 'open-uri'
2
2
  require 'engineyard-serverside/logged_output'
3
3
 
4
4
  module EY
5
- class Server < Struct.new(:hostname, :roles, :name, :user)
6
- include LoggedOutput
5
+ module Serverside
6
+ class Server < Struct.new(:hostname, :roles, :name, :user)
7
+ include LoggedOutput
8
+
9
+ class DuplicateHostname < StandardError
10
+ def initialize(hostname)
11
+ super "There is already an EY::Serverside::Server with hostname '#{hostname}'"
12
+ end
13
+ end
7
14
 
8
- class DuplicateHostname < StandardError
9
- def initialize(hostname)
10
- super "There is already an EY::Server with hostname '#{hostname}'"
15
+ def initialize(*fields)
16
+ super
17
+ self.roles = self.roles.map { |r| r.to_sym } if self.roles
11
18
  end
12
- end
13
19
 
14
- def initialize(*fields)
15
- super
16
- self.roles = self.roles.map { |r| r.to_sym } if self.roles
17
- end
20
+ attr_writer :default_task
18
21
 
19
- attr_writer :default_task
22
+ def self.from_roles(*want_roles)
23
+ want_roles = want_roles.flatten.compact.map{|r| r.to_sym}
24
+ return all if !want_roles || want_roles.include?(:all) || want_roles.empty?
20
25
 
21
- def self.from_roles(*want_roles)
22
- want_roles = want_roles.flatten.compact.map{|r| r.to_sym}
23
- return all if !want_roles || want_roles.include?(:all) || want_roles.empty?
26
+ all.select do |s|
27
+ !(s.roles & want_roles).empty?
28
+ end
29
+ end
24
30
 
25
- all.select do |s|
26
- !(s.roles & want_roles).empty?
31
+ def role
32
+ roles.first
27
33
  end
28
- end
29
34
 
30
- def role
31
- roles.first
32
- end
35
+ def self.load_all_from_array(server_hashes)
36
+ server_hashes.each do |instance_hash|
37
+ add(instance_hash)
38
+ end
39
+ end
33
40
 
34
- def self.load_all_from_array(server_hashes)
35
- server_hashes.each do |instance_hash|
36
- add(instance_hash)
41
+ def self.all
42
+ @all
37
43
  end
38
- end
39
44
 
40
- def self.all
41
- @all
42
- end
45
+ def self.by_hostname(hostname)
46
+ all.find{|s| s.hostname == hostname}
47
+ end
43
48
 
44
- def self.by_hostname(hostname)
45
- all.find{|s| s.hostname == hostname}
46
- end
49
+ def self.add(server_hash)
50
+ hostname = server_hash[:hostname]
51
+ if by_hostname(hostname)
52
+ raise DuplicateHostname.new(hostname)
53
+ end
54
+ server = new(hostname, server_hash[:roles], server_hash[:name], server_hash[:user])
55
+ @all << server
56
+ server
57
+ end
47
58
 
48
- def self.add(server_hash)
49
- hostname = server_hash[:hostname]
50
- if by_hostname(hostname)
51
- raise DuplicateHostname.new(hostname)
59
+ def self.current
60
+ all.find {|s| s.local? }
52
61
  end
53
- server = new(hostname, server_hash[:roles], server_hash[:name], server_hash[:user])
54
- @all << server
55
- server
56
- end
57
62
 
58
- def self.current
59
- all.find {|s| s.local? }
60
- end
63
+ def self.reset
64
+ @all = []
65
+ end
66
+ reset
61
67
 
62
- def self.reset
63
- @all = []
64
- end
65
- reset
68
+ def roles=(roles)
69
+ super(roles.map{|r| r.to_sym})
70
+ end
66
71
 
67
- def roles=(roles)
68
- super(roles.map{|r| r.to_sym})
69
- end
72
+ def local?
73
+ hostname == 'localhost'
74
+ end
70
75
 
71
- def local?
72
- hostname == 'localhost'
73
- end
76
+ def sync_directory(directory)
77
+ return if local?
78
+ run "mkdir -p #{directory}"
79
+ logged_system(%|rsync --delete -aq -e "#{ssh_command}" #{directory}/ #{user}@#{hostname}:#{directory}|)
80
+ end
74
81
 
75
- def sync_directory(directory)
76
- return if local?
77
- run "mkdir -p #{directory}"
78
- logged_system(%|rsync --delete -aq -e "#{ssh_command}" #{directory}/ #{user}@#{hostname}:#{directory}|)
79
- end
82
+ def run(command)
83
+ if local?
84
+ logged_system(command)
85
+ else
86
+ logged_system(ssh_command + " " + Escape.shell_command(["#{user}@#{hostname}", command]))
87
+ end
88
+ end
80
89
 
81
- def run(command)
82
- if local?
83
- logged_system(command)
84
- else
85
- logged_system(ssh_command + " " + Escape.shell_command(["#{user}@#{hostname}", command]))
90
+ def ssh_command
91
+ "ssh -i #{ENV['HOME']}/.ssh/internal -o StrictHostKeyChecking=no -o PasswordAuthentication=no"
86
92
  end
87
- end
88
93
 
89
- def ssh_command
90
- "ssh -i #{ENV['HOME']}/.ssh/internal -o StrictHostKeyChecking=no -o PasswordAuthentication=no"
91
94
  end
92
95
  end
93
96
  end
@@ -1,138 +1,140 @@
1
1
  require 'engineyard-serverside/logged_output'
2
2
 
3
3
  module EY
4
- module Strategies
5
- class Git
6
- module Helpers
4
+ module Serverside
5
+ module Strategies
6
+ class Git
7
+ module Helpers
8
+
9
+ def update_repository_cache
10
+ unless strategy.fetch && strategy.checkout
11
+ abort "*** [Error] Git could not checkout (#{strategy.to_checkout}) ***"
12
+ end
13
+ end
7
14
 
8
- def update_repository_cache
9
- unless strategy.fetch && strategy.checkout
10
- abort "*** [Error] Git could not checkout (#{strategy.to_checkout}) ***"
15
+ def create_revision_file_command
16
+ strategy.create_revision_file_command(c.release_path)
11
17
  end
12
- end
13
18
 
14
- def create_revision_file_command
15
- strategy.create_revision_file_command(c.release_path)
16
- end
19
+ def short_log_message(revision)
20
+ strategy.short_log_message(revision)
21
+ end
17
22
 
18
- def short_log_message(revision)
19
- strategy.short_log_message(revision)
23
+ def strategy
24
+ klass = Module.nesting[1]
25
+ # Use [] to access attributes instead of calling methods so
26
+ # that we get nils instead of NoMethodError.
27
+ #
28
+ # Rollback doesn't know about the repository location (nor
29
+ # should it need to), but it would like to use #short_log_message.
30
+ klass.new(
31
+ :repository_cache => c[:repository_cache],
32
+ :app => c[:app],
33
+ :repo => c[:repo],
34
+ :ref => c[:branch]
35
+ )
36
+ end
20
37
  end
21
38
 
22
- def strategy
23
- klass = Module.nesting[1]
24
- # Use [] to access attributes instead of calling methods so
25
- # that we get nils instead of NoMethodError.
26
- #
27
- # Rollback doesn't know about the repository location (nor
28
- # should it need to), but it would like to use #short_log_message.
29
- klass.new(
30
- :repository_cache => c[:repository_cache],
31
- :app => c[:app],
32
- :repo => c[:repo],
33
- :ref => c[:branch]
34
- )
35
- end
36
- end
39
+ include LoggedOutput
37
40
 
38
- include LoggedOutput
41
+ attr_reader :opts
39
42
 
40
- attr_reader :opts
43
+ def initialize(opts)
44
+ @opts = opts
45
+ set_up_git_ssh(@opts[:app])
46
+ end
41
47
 
42
- def initialize(opts)
43
- @opts = opts
44
- set_up_git_ssh(@opts[:app])
45
- end
48
+ def usable_repository?
49
+ File.directory?(opts[:repository_cache]) && `#{git} remote -v | grep origin`[opts[:repo]]
50
+ end
46
51
 
47
- def usable_repository?
48
- File.directory?(opts[:repository_cache]) && `#{git} remote -v | grep origin`[opts[:repo]]
49
- end
52
+ def fetch
53
+ if usable_repository?
54
+ logged_system("#{git} fetch -q origin 2>&1")
55
+ else
56
+ FileUtils.rm_rf(opts[:repository_cache])
57
+ logged_system("git clone -q #{opts[:repo]} #{opts[:repository_cache]} 2>&1")
58
+ end
59
+ end
50
60
 
51
- def fetch
52
- if usable_repository?
53
- logged_system("#{git} fetch -q origin 2>&1")
54
- else
55
- FileUtils.rm_rf(opts[:repository_cache])
56
- logged_system("git clone -q #{opts[:repo]} #{opts[:repository_cache]} 2>&1")
61
+ def checkout
62
+ info "~> Deploying revision #{short_log_message(to_checkout)}"
63
+ in_git_work_tree do
64
+ (logged_system("git checkout -q '#{to_checkout}'") ||
65
+ logged_system("git reset -q --hard '#{to_checkout}'")) &&
66
+ logged_system("git submodule sync") &&
67
+ logged_system("git submodule update --init") &&
68
+ logged_system("git clean -dfq")
69
+ end
57
70
  end
58
- end
59
71
 
60
- def checkout
61
- info "~> Deploying revision #{short_log_message(to_checkout)}"
62
- in_git_work_tree do
63
- (logged_system("git checkout -q '#{to_checkout}'") ||
64
- logged_system("git reset -q --hard '#{to_checkout}'")) &&
65
- logged_system("git submodule sync") &&
66
- logged_system("git submodule update --init") &&
67
- logged_system("git clean -dfq")
72
+ def to_checkout
73
+ return @to_checkout if @opts_ref == opts[:ref]
74
+ @opts_ref = opts[:ref]
75
+ @to_checkout = if branch?(opts[:ref])
76
+ "origin/#{opts[:ref]}"
77
+ else
78
+ opts[:ref]
79
+ end
68
80
  end
69
- end
70
81
 
71
- def to_checkout
72
- return @to_checkout if @opts_ref == opts[:ref]
73
- @opts_ref = opts[:ref]
74
- @to_checkout = if branch?(opts[:ref])
75
- "origin/#{opts[:ref]}"
76
- else
77
- opts[:ref]
82
+ def create_revision_file_command(dir)
83
+ %Q{#{git} show --pretty=format:"%H" | head -1 > "#{dir}/REVISION"}
78
84
  end
79
- end
80
85
 
81
- def create_revision_file_command(dir)
82
- %Q{#{git} show --pretty=format:"%H" | head -1 > "#{dir}/REVISION"}
83
- end
86
+ def short_log_message(rev)
87
+ `#{git} log --pretty=oneline --abbrev-commit '#{rev}^..#{rev}'`.strip
88
+ end
84
89
 
85
- def short_log_message(rev)
86
- `#{git} log --pretty=oneline --abbrev-commit '#{rev}^..#{rev}'`.strip
87
- end
90
+ private
91
+ def in_git_work_tree
92
+ Dir.chdir(git_work_tree) { yield }
93
+ end
88
94
 
89
- private
90
- def in_git_work_tree
91
- Dir.chdir(git_work_tree) { yield }
92
- end
95
+ def git_work_tree
96
+ opts[:repository_cache]
97
+ end
93
98
 
94
- def git_work_tree
95
- opts[:repository_cache]
96
- end
99
+ def git
100
+ "git --git-dir #{git_work_tree}/.git --work-tree #{git_work_tree}"
101
+ end
97
102
 
98
- def git
99
- "git --git-dir #{git_work_tree}/.git --work-tree #{git_work_tree}"
100
- end
103
+ def branch?(ref)
104
+ `#{git} branch -r`.map { |x| x.strip }.include?("origin/#{ref}")
105
+ end
101
106
 
102
- def branch?(ref)
103
- `#{git} branch -r`.map { |x| x.strip }.include?("origin/#{ref}")
104
- end
107
+ def set_up_git_ssh(app)
108
+ # hold references to the tempfiles so they don't get finalized
109
+ # unexpectedly; tempfile finalization unlinks the files
110
+ @git_ssh = Tempfile.open("git-ssh")
111
+ @config = Tempfile.open("git-ssh-config")
112
+
113
+ @config.write "StrictHostKeyChecking no\n"
114
+ @config.write "CheckHostIP no\n"
115
+ @config.write "PasswordAuthentication no\n"
116
+ @config.write "LogLevel DEBUG\n"
117
+ @config.write "IdentityFile ~/.ssh/#{app}-deploy-key\n"
118
+ @config.chmod(0600)
119
+ @config.close
120
+
121
+ @git_ssh.write "#!/bin/sh\n"
122
+ @git_ssh.write "unset SSH_AUTH_SOCK\n"
123
+ @git_ssh.write "ssh -F \"#{@config.path}\" $*\n"
124
+ @git_ssh.chmod(0700)
125
+ # NB: this file _must_ be closed before git looks at it.
126
+ #
127
+ # Linux won't let you execve a file that's open for writing,
128
+ # so if this file stays open, then git will complain about
129
+ # being unable to exec it and will exit with a message like
130
+ #
131
+ # fatal: exec /tmp/git-ssh20100417-21417-d040rm-0 failed.
132
+ @git_ssh.close
105
133
 
106
- def set_up_git_ssh(app)
107
- # hold references to the tempfiles so they don't get finalized
108
- # unexpectedly; tempfile finalization unlinks the files
109
- @git_ssh = Tempfile.open("git-ssh")
110
- @config = Tempfile.open("git-ssh-config")
111
-
112
- @config.write "StrictHostKeyChecking no\n"
113
- @config.write "CheckHostIP no\n"
114
- @config.write "PasswordAuthentication no\n"
115
- @config.write "LogLevel DEBUG\n"
116
- @config.write "IdentityFile ~/.ssh/#{app}-deploy-key\n"
117
- @config.chmod(0600)
118
- @config.close
119
-
120
- @git_ssh.write "#!/bin/sh\n"
121
- @git_ssh.write "unset SSH_AUTH_SOCK\n"
122
- @git_ssh.write "ssh -F \"#{@config.path}\" $*\n"
123
- @git_ssh.chmod(0700)
124
- # NB: this file _must_ be closed before git looks at it.
125
- #
126
- # Linux won't let you execve a file that's open for writing,
127
- # so if this file stays open, then git will complain about
128
- # being unable to exec it and will exit with a message like
129
- #
130
- # fatal: exec /tmp/git-ssh20100417-21417-d040rm-0 failed.
131
- @git_ssh.close
132
-
133
- ENV['GIT_SSH'] = @git_ssh.path
134
- end
134
+ ENV['GIT_SSH'] = @git_ssh.path
135
+ end
135
136
 
137
+ end
136
138
  end
137
139
  end
138
140
  end
@@ -1,62 +1,65 @@
1
1
  module EY
2
- class Task
3
- include Dataflow
2
+ module Serverside
3
+ class Task
4
+ include Dataflow
4
5
 
5
- attr_reader :config
6
- alias :c :config
6
+ attr_reader :config
7
+ alias :c :config
7
8
 
8
- def initialize(conf)
9
- @config = conf
10
- @roles = :all
11
- end
12
-
13
- def require_custom_tasks
14
- deploy_file = ["config/eydeploy.rb", "eydeploy.rb"].map do |short_file|
15
- File.join(c.repository_cache, short_file)
16
- end.detect do |file|
17
- File.exist?(file)
9
+ def initialize(conf)
10
+ @config = conf
11
+ @roles = :all
18
12
  end
19
13
 
20
- if deploy_file
21
- puts "~> Loading deployment task overrides from #{deploy_file}"
22
- instance_eval(File.read(deploy_file))
23
- true
24
- else
25
- false
14
+ def require_custom_tasks
15
+ deploy_file = ["config/eydeploy.rb", "eydeploy.rb"].map do |short_file|
16
+ File.join(c.repository_cache, short_file)
17
+ end.detect do |file|
18
+ File.exist?(file)
19
+ end
20
+
21
+ if deploy_file
22
+ puts "~> Loading deployment task overrides from #{deploy_file}"
23
+ instance_eval(File.read(deploy_file))
24
+ true
25
+ else
26
+ false
27
+ end
26
28
  end
27
- end
28
29
 
29
- def roles(*task_roles)
30
- raise "Roles must be passed a block" unless block_given?
30
+ def roles(*task_roles)
31
+ raise "Roles must be passed a block" unless block_given?
31
32
 
32
- begin
33
- @roles = task_roles
34
- yield
35
- ensure
36
- @roles = :all
33
+ begin
34
+ @roles = task_roles
35
+ yield
36
+ ensure
37
+ @roles = :all
38
+ end
37
39
  end
38
- end
39
40
 
40
- def run(cmd, &blk)
41
- run_on_roles(cmd, &blk)
42
- end
41
+ def run(cmd, &blk)
42
+ run_on_roles(cmd, &blk)
43
+ end
43
44
 
44
- def sudo(cmd, &blk)
45
- run_on_roles(cmd, %w[sudo sh -l -c], &blk)
46
- end
45
+ def sudo(cmd, &blk)
46
+ run_on_roles(cmd, %w[sudo sh -l -c], &blk)
47
+ end
47
48
 
48
- private
49
+ private
49
50
 
50
- def run_on_roles(cmd, wrapper=%w[sh -l -c])
51
- results = EY::Server.from_roles(@roles).map do |server|
52
- to_run = block_given? ? yield(server, cmd.dup) : cmd
53
- need_later { server.run(Escape.shell_command(wrapper + [to_run])) }
51
+ def run_on_roles(cmd, wrapper=%w[sh -l -c])
52
+ results = EY::Serverside::Server.from_roles(@roles).map do |server|
53
+ to_run = block_given? ? yield(server, cmd.dup) : cmd
54
+ need_later { server.run(Escape.shell_command(wrapper + [to_run])) }
55
+ end
56
+ barrier *results
57
+ # MRI's truthiness check is an internal C thing that does not call
58
+ # any methods... so Dataflow cannot proxy it & we must "x == true"
59
+ # Rubinius, wherefore art thou!?
60
+ results.all?{|x| x == true } || raise(EY::Serverside::RemoteFailure.new(cmd))
54
61
  end
55
- barrier *results
56
- # MRI's truthiness check is an internal C thing that does not call
57
- # any methods... so Dataflow cannot proxy it & we must "x == true"
58
- # Rubinius, wherefore art thou!?
59
- results.all?{|x| x == true } || raise(EY::RemoteFailure.new(cmd))
62
+
60
63
  end
61
64
  end
62
65
  end