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.
@@ -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