tdd_deploy 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/Capfile +12 -0
  2. data/Gemfile +9 -0
  3. data/bin/tdd_deploy_context +132 -0
  4. data/bin/tdd_deploy_server.rb +6 -0
  5. data/config.ru +6 -0
  6. data/lib/tasks/tdd_deploy.rake +24 -0
  7. data/lib/tdd_deploy/assertions.rb +146 -0
  8. data/lib/tdd_deploy/base.rb +62 -0
  9. data/lib/tdd_deploy/deploy_test_methods.rb +65 -0
  10. data/lib/tdd_deploy/environ.rb +272 -0
  11. data/lib/tdd_deploy/host_tests/host_connection.rb +29 -0
  12. data/lib/tdd_deploy/host_tests/remote_ip_tables.rb +34 -0
  13. data/lib/tdd_deploy/host_tests/remote_monit.rb +17 -0
  14. data/lib/tdd_deploy/host_tests/remote_nginx.rb +17 -0
  15. data/lib/tdd_deploy/host_tests/remote_postfix.rb +23 -0
  16. data/lib/tdd_deploy/host_tests/remote_postgresql.rb +17 -0
  17. data/lib/tdd_deploy/railengine.rb +10 -0
  18. data/lib/tdd_deploy/run_methods.rb +126 -0
  19. data/lib/tdd_deploy/server.rb +58 -0
  20. data/lib/tdd_deploy/site_tests/site_layout.rb +46 -0
  21. data/lib/tdd_deploy/site_tests/site_user.rb +14 -0
  22. data/lib/tdd_deploy/version.rb +3 -0
  23. data/lib/tdd_deploy.rb +12 -0
  24. data/tests/test_assertions.rb +62 -0
  25. data/tests/test_base.rb +20 -0
  26. data/tests/test_colored_tests.rb +47 -0
  27. data/tests/test_environ.rb +153 -0
  28. data/tests/test_helpers.rb +10 -0
  29. data/tests/test_host_tests.rb +31 -0
  30. data/tests/test_remote_ip_tables.rb +27 -0
  31. data/tests/test_remote_monit.rb +27 -0
  32. data/tests/test_remote_nginx.rb +27 -0
  33. data/tests/test_remote_postfix.rb +27 -0
  34. data/tests/test_remote_postgresql.rb +27 -0
  35. data/tests/test_run_methods.rb +89 -0
  36. data/tests/test_server.rb +46 -0
  37. data/tests/test_site_layout.rb +34 -0
  38. data/tests/test_site_user.rb +21 -0
  39. data/tests/test_tdd_deploy.rb +17 -0
  40. data/tests/test_tdd_deploy_context.rb +35 -0
  41. data/tests/test_tdd_deploy_server.rb +59 -0
  42. data/tests/test_test_deploy_methods.rb +97 -0
  43. metadata +146 -0
@@ -0,0 +1,272 @@
1
+ module TddDeploy
2
+
3
+ # == module TddDeploy::Environ
4
+ #
5
+ # provides management for host provisioning and deployment variables - aka 'the environment'.
6
+ # The 'enviornment' - as used here - is not the Ruby ENV hash
7
+ #
8
+ # three types of variables are supported
9
+ # :int - which are integers
10
+ # :string - which are strings
11
+ # :list - which are input as comma separated strings [the actual separator is /[\s,]+/ ]
12
+ # which are used internally as arrays of strings. So, to set a :list variable you
13
+ # write 'foo = bar,baz,boop', and when you use it you get back ['bar', 'baz', 'boop']
14
+ # (this inanity was done so we could save the environment as a simple ascii file)
15
+ #
16
+ # Environment variables:
17
+ #
18
+ # === Integer variables
19
+ # * 'ssh_timeout' - timeout in seconds used to terminate remote commands fired off by ssh
20
+ # * 'site_base_port' - Base port for site servers. For example, if a pack of mongrels or thin
21
+ # servers provide the rails end of the web site, they listen on 'site_base_port', +1, etd
22
+ # * 'site_num_servers' - number of mongrels or thins to spin up
23
+ #
24
+ # === String variables
25
+ # * 'host_admin' - user name used on remote hosts. Should not be root, but should be in /etc/sudoers
26
+ # * 'local_admin' - user name of on local hosts which can ssh into remote hosts via public key authentication
27
+ # * 'local_admin_email' - email of local admin who should receive monitoring emails
28
+ # * 'site' - name of site This should satisfy /[a-z][a-z0-9_]*.
29
+ # * 'site_user' - name of site user. TddDeploy assumes that each site will have a unique user on the remote host.
30
+ # this makes it easy to tell nginx and monit where to find configuration files for each site [both
31
+ # know how to included globbed paths]
32
+ #
33
+ # === List Variables
34
+ # * 'hosts' - list of all hosts - defaults to balance_hosts + db_hosts + web_hosts
35
+ # * 'balance_hosts' - load balancing servers [may be empty, in which case 'hosts' is used]
36
+ # * 'db_hosts' - hosts which run the database server [may be empty, in which case 'hosts' is used]
37
+ # * 'web_hosts' - hosts which run the web server [may be empty, in which case 'hosts' is used]
38
+
39
+ module Environ
40
+
41
+ # == DataCache
42
+ #
43
+ # DataCache is a hack to provide a single shared data store for all classes which
44
+ # include TddDeploy::Environ
45
+ class DataCache
46
+ class << self
47
+ attr_accessor :env_hash, :env_types, :env_defaults
48
+ end
49
+ end
50
+
51
+ ENV_FNAME = 'site_host_setup.env'
52
+
53
+ # set up all the standard accessors
54
+ # lazy initialize DataCache.env_hash
55
+ def env_hash
56
+ read_env || reset_env unless defined?(DataCache.env_hash)
57
+ DataCache.env_hash
58
+ end
59
+
60
+ def env_hash=(hash)
61
+ raise ArgumentError.new("env_hash=(): arg must be a hash") unless hash.is_a? Hash
62
+ if !(tmp = hash.keys - DataCache.env_types.keys).empty?
63
+ raise ArgumentError.new("env_hash=(): Illegal Keys in value: #{tmp.join(',')}")
64
+ elsif !(tmp = DataCache.env_types.keys - hash.keys).empty?
65
+ raise ArgumentError.new("env_hash=(): Missing Keys in value: #{tmp.join(',')}")
66
+ else
67
+ DataCache.env_hash = hash
68
+ end
69
+ end
70
+
71
+ DataCache.env_types = {
72
+ 'ssh_timeout' => :int,
73
+ 'site_base_port' => :int,
74
+ 'site_num_servers' => :int,
75
+
76
+ 'host_admin' => :string,
77
+ 'local_admin' => :string,
78
+ 'local_admin_email' => :string,
79
+
80
+ 'site' => :string,
81
+ 'site_user' => :string,
82
+
83
+ # 'hosts' => :list,
84
+ 'balance_hosts' => :list,
85
+ 'db_hosts' => :list,
86
+ 'web_hosts' => :list,
87
+ }
88
+
89
+ DataCache.env_defaults ||= {
90
+ 'ssh_timeout' => 5,
91
+ 'site_base_port' => 8000,
92
+ 'site_num_servers' => 3,
93
+
94
+ 'host_admin' => "host_admin",
95
+ 'local_admin' => "local_admin",
96
+ 'local_admin_email' => "local_admin@bogus.tld",
97
+
98
+ 'site' => "site",
99
+ 'site_user' => "site_user",
100
+
101
+ # 'hosts' => "bar,foo",
102
+ 'balance_hosts' => '',
103
+ 'db_hosts' => 'bar,foo',
104
+ 'web_hosts' => 'bar,foo',
105
+ }
106
+
107
+ def env_types
108
+ DataCache.env_types
109
+ end
110
+
111
+ def env_defaults
112
+ DataCache.env_defaults
113
+ end
114
+
115
+ # set_env(value_hash {}) - convenience method which sets values of the environment
116
+ # hash using a hash rather than one-at-a-time
117
+ def set_env(value_hash = {})
118
+ DataCache.env_hash ||= {}
119
+ value_hash.each do |k, v|
120
+ k = k.to_s
121
+ case self.env_types[k]
122
+ when :int then DataCache.env_hash[k] = v.to_i
123
+ when :string then DataCache.env_hash[k] = v.to_s
124
+ when :list then DataCache.env_hash[k] = self.str_to_list(v)
125
+ else
126
+ if k == 'hosts'
127
+ if DataCache.env_hash['web_hosts'] == DataCache.env_hash['db_hosts']
128
+ DataCache.env_hash['web_hosts'] =
129
+ DataCache.env_hash['db_hosts'] = self.str_to_list(v)
130
+ else
131
+ raise RuntimeError.new("#{self}#reset_env(): Cannot assign value to 'hosts' if web_hosts &/or db_hosts already set.\n web_hosts: #{DataCache.env_hash['web_hosts']}\n db_hosts: #{DataCache.env_hash['db_hosts']}")
132
+ # raise RuntimeError.new("Cannot change hosts key if web_hosts != db_hosts")
133
+ end
134
+ else
135
+ raise ArgumentError.new("#{self}#reset_env(): Illegal environment key: #{k}")
136
+ end
137
+ end
138
+ end
139
+ end
140
+
141
+ def clear_env
142
+ DataCache.env_hash = {}
143
+ end
144
+
145
+ # reset_env resets env_hash to env_defaults
146
+ def reset_env
147
+ clear_env
148
+ set_env self.env_defaults
149
+ end
150
+
151
+ # reads the environment from TddDeploy::Environ::ENV_FNAME (site_host_setup.env) if the file exists
152
+ # someplace between the current directory and the root of the filesystem
153
+ def read_env
154
+ dir_path = Dir.pwd
155
+ DataCache.env_hash ||= {}
156
+ loop do
157
+ path = File.join dir_path, TddDeploy::Environ::ENV_FNAME
158
+ if File.exists? TddDeploy::Environ::ENV_FNAME
159
+ line_no = 0
160
+ if f = File.new(path, 'r')
161
+ begin
162
+ f.each do |line|
163
+ line_no += 1
164
+ if line =~ /^\s*(\w+)\s*=\s*(.*?)\s*$/
165
+ key = $1.downcase
166
+ if self.env_types.keys.include? key
167
+ self.send "#{key}=".to_sym, $2
168
+ # self.env_hash[key] = self.env_types[key] == :list ? self.str_to_list($2) : $2.to_s
169
+ else
170
+ raise ArugmentError.new("TddDeploy::Environ#read_env: Error in #{TddDeploy::Error::ENV_FNAME}: #{line_no}: Illegal Key: #{key}")
171
+ end
172
+ else
173
+ raise ArugmentError.new("TddDeploy::Environ#read_env: Error in #{TddDeploy::Error::ENV_FNAME}: #{line_no}: Unmatched Line: #{line}}")
174
+ end
175
+ end
176
+ ensure
177
+ f.close
178
+ end
179
+ return self.env_hash
180
+ else
181
+ raise RuntimeError.new("Unable to open #{path} for reading")
182
+ end
183
+ elsif dir_path.length <= 1
184
+ # reached root level, so initialize to defaults and exit
185
+ return nil
186
+ else
187
+ # move to parent directory
188
+ dir_path = File.expand_path('..', dir_path)
189
+ end
190
+ end
191
+ nil
192
+ end
193
+
194
+ def str_to_list str
195
+ case
196
+ when str.is_a?(String) then str.split(/[\s,]+/).uniq.sort
197
+ when str.is_a?(Array) then str.uniq.sort
198
+ else
199
+ raise ArgumentError.new("str_to_list: #{str}")
200
+ end
201
+ end
202
+
203
+ def list_to_str key
204
+ tmp = self.env_hash[key]
205
+ tmp.is_a?(Array) ? tmp.join(',') : tmp.to_s
206
+ end
207
+
208
+ # saves the current environment in the current working directory in the file
209
+ # 'site_host_setup.env' [aka TddDeploy::Environ::ENV_FNAME]
210
+ def save_env
211
+ f = File.new(TddDeploy::Environ::ENV_FNAME, "w")
212
+ self.env_types.keys.each do |k|
213
+ v = self.env_hash[k] || ''
214
+ case self.env_types[k]
215
+ when :int then f.write "#{k}=#{v}\n"
216
+ when :string then f.write "#{k}=#{v}\n"
217
+ when :list then
218
+ f.write "#{k}=#{self.list_to_str(k)}\n" unless k == 'hosts'
219
+ else
220
+ raise RuntimeError("unknown key: #{k}")
221
+ end
222
+ end
223
+ f.close
224
+ end
225
+
226
+ # accessors for all defined env variables
227
+ def hosts
228
+ (self.web_hosts.to_a + self.db_hosts.to_a + self.balance_hosts.to_a).uniq.sort
229
+ end
230
+
231
+ def hosts=(list)
232
+ if (self.web_hosts.nil? && self.db_hosts.nil?) || self.web_hosts == self.db_hosts
233
+ self.web_hosts =
234
+ self.db_hosts = self.str_to_list(list)
235
+ else
236
+ raise RuntimeError.new("Cannot assign value to 'hosts' if web_hosts &/or db_hosts already set.\n web_hosts: #{self.web_hosts}\n db_hosts: #{self.db_hosts}")
237
+ end
238
+ end
239
+
240
+ # create accessors for all keys in env_types
241
+ tmp = ''
242
+ DataCache.env_types.each do |k, t|
243
+ tmp +=<<-EOF
244
+ def #{k}
245
+ self.env_hash['#{k}']
246
+ end
247
+ EOF
248
+ case DataCache.env_types[k]
249
+ when :int
250
+ tmp +=<<-EOF
251
+ def #{k}=(v)
252
+ self.env_hash['#{k}'] = v.to_i
253
+ end
254
+ EOF
255
+ when :string
256
+ tmp +=<<-EOF
257
+ def #{k}=(v)
258
+ self.env_hash['#{k}'] = v.to_s
259
+ end
260
+ EOF
261
+ when :list
262
+ tmp +=<<-EOF
263
+ def #{k}=(v)
264
+ self.env_hash['#{k}'] = self.str_to_list(v)
265
+ end
266
+ EOF
267
+ end
268
+ end
269
+
270
+ class_eval tmp
271
+ end
272
+ end
@@ -0,0 +1,29 @@
1
+ require 'tdd_deploy/base'
2
+
3
+ module TddDeploy
4
+ class HostConnection < TddDeploy::Base
5
+ # ping - pings all hosts
6
+ def ping
7
+ require 'net/ping'
8
+ result = true
9
+ self.hosts.each do |host|
10
+ result &= assert Net::Ping::External.new(host).ping?, "Host #{host} should respond to ping"
11
+ end
12
+ result
13
+ end
14
+
15
+ # ssh_login - attempts to log in as *host_admin* on all hosts from current user
16
+ def ssh_login
17
+ deploy_test_on_all_hosts "/home/#{self.host_admin}\n", "unable to connect via ssh" do
18
+ 'pwd'
19
+ end
20
+ end
21
+
22
+ # ssh_login_as_root - attempts to log in as *root* on all hosts from current user
23
+ def ssh_login_as_root
24
+ deploy_test_on_all_hosts_as 'root', '/root', "unable to connect as root via ssh" do
25
+ 'pwd'
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,34 @@
1
+ require 'tdd_deploy/base'
2
+
3
+ module TddDeploy
4
+ # = TddDeploy::RemoteIpTables
5
+ #
6
+ # checks to see if iptables is working by attempting to connect to each host on a collection
7
+ # of 'interesting' ports. the ports probed are: 20, 23, 25, 53, 5432, 2812
8
+ #
9
+ class RemoteIpTables < TddDeploy::Base
10
+ # tcp_some_blocked_ports - checks TCP ports
11
+ def tcp_some_blocked_ports
12
+ self.hosts.each do |host|
13
+ # Linode seems to refuse to block 21 - FTP control
14
+ # [20, 21, 23, 25, 53, 5432, 2812].each do |port|
15
+ [20, 23, 25, 53, 5432, 2812].each do |port|
16
+ tcp_socket = TCPSocket.new(host, port) rescue 'failed'
17
+ assert_equal 'failed', tcp_socket, "Host: #{host}: Should not be able to connect via tcp to port #{port}"
18
+ end
19
+ end
20
+ end
21
+
22
+ # udp_some_blocked_ports - checks UDP ports
23
+ def udp_some_blocked_ports
24
+ self.hosts.each do |host|
25
+ # Linode seems to refuse to block 21 - FTP control
26
+ # [20, 21, 23, 25, 53, 5432, 2812].each do |port|
27
+ [20, 23, 25, 53, 5432, 2812].each do |port|
28
+ udp_socket = UDPSocket.new(host, port) rescue 'failed'
29
+ assert_equal 'failed', udp_socket, "Host: #{host}: Should not be able to connect via udp to port #{port}"
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,17 @@
1
+ require 'tdd_deploy/base'
2
+
3
+ module TddDeploy
4
+ class RemoteMonit < TddDeploy::Base
5
+ def test_monit_installed
6
+ deploy_test_on_all_hosts '/usr/bin/monit', "monit is not installed" do
7
+ 'ls /usr/bin/monit'
8
+ end
9
+ end
10
+
11
+ def test_monit_running
12
+ deploy_test_on_all_hosts /\smonit\s/, "monit is not running" do
13
+ 'ps -p `cat /var/run/monit.pid`'
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ require 'tdd_deploy/base'
2
+
3
+ module TddDeploy
4
+ class RemoteNginx < TddDeploy::Base
5
+ def test_nginx_installed
6
+ deploy_test_on_all_hosts '/usr/sbin/nginx', "nginx is not installed" do
7
+ 'ls /usr/sbin/nginx'
8
+ end
9
+ end
10
+
11
+ def test_nginx_running
12
+ deploy_test_on_all_hosts /\snginx\s/, "nginx is not running" do
13
+ 'ps -p `cat /var/run/nginx.pid`'
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,23 @@
1
+ require 'tdd_deploy/base'
2
+
3
+ module TddDeploy
4
+ class RemotePostfix < TddDeploy::Base
5
+ def test_postfix_installed
6
+ deploy_test_on_all_hosts '/usr/sbin/postfix', "postfix is not installed" do
7
+ 'ls /usr/sbin/postfix'
8
+ end
9
+ end
10
+
11
+ def test_postfix_running
12
+ deploy_test_on_all_hosts_as 'root', /\smaster\s/, "postfix is not running" do
13
+ 'ps -p `cat /var/spool/postfix/pid/master.pid`'
14
+ end
15
+ end
16
+
17
+ # def test_postfix_accepts_mail
18
+ # deploy_test_on_all_hosts "mail works\n", 'postfix accepts mail' do |host, login, userid|
19
+ # "echo \"test mail\" | mail -s 'test mail' -r #{userid}@#{host} #{self.local_admin_email} && echo 'mail works'"
20
+ # end
21
+ # end
22
+ end
23
+ end
@@ -0,0 +1,17 @@
1
+ require 'tdd_deploy/base'
2
+
3
+ module TddDeploy
4
+ class RemotePostgresql < TddDeploy::Base
5
+ def test_postgresql_installed
6
+ deploy_test_on_all_hosts "/usr/bin/postgres\n", 'postgres not installed' do |host, login, admin|
7
+ "ls /usr/bin/postgres"
8
+ end
9
+ end
10
+
11
+ def test_postgresql_running
12
+ deploy_test_on_all_hosts /postgres\s*\|\s*postgres/, "postgresql server not running" do
13
+ "psql --command='\\l' postgres postgres"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ module TddDeploy
2
+ class Engine < Rails::Engine
3
+ # engine_name "tdd_deploy"
4
+ rake_tasks do
5
+ load "tdd_deploy/../tasks/tdd_deploy.rake"
6
+ end
7
+ initializer 'active_support.add_tdd_deploy' do
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,126 @@
1
+ module TddDeploy
2
+ module RunMethods
3
+ require 'net/ssh'
4
+
5
+ # runs the output of the block on all hosts defined in self.hosts as user self.host_admin.
6
+ # Returns a hash of two element arrays containing output [stdout, stderr] returned from the command.
7
+ # Hash keys are host names as strings.
8
+ def run_on_all_hosts(&block)
9
+ run_on_all_hosts_as self.host_admin, &block
10
+ end
11
+
12
+ # Runs the output of the block on all hosts defined in self.hosts as user 'userid'.
13
+ # Returns a hash of two element arrays containing output [stdout, stderr] returned from the command.
14
+ # Hash keys are host names as strings.
15
+ def run_on_all_hosts_as(userid, &block)
16
+ results = {}
17
+ self.hosts.each do |host|
18
+ results[host] = run_in_ssh_session_as(userid, host, &block)
19
+ end
20
+ results
21
+ end
22
+
23
+ # Runs the command secified in &block on 'host' as user 'self.host_admin'.
24
+ # Returns an array [stdout, stderr] returned from the command.
25
+ def run_in_ssh_session(host, &block)
26
+ run_in_ssh_session_as(self.host_admin, host, &block)
27
+ end
28
+
29
+ # Runs the command secified in &block on 'host' as user 'userid'.
30
+ # Returns an array [stdout, stderr] returned from the command.
31
+ def run_in_ssh_session_as(userid, host, &block)
32
+ login = "#{userid}@#{host}"
33
+ match = Regexp.new(match) if match.is_a? String
34
+ raise ArgumentError, 'match expression cannot be empty' if match =~ ''
35
+
36
+ rsp = nil
37
+ err_rsp = nil
38
+ cmd = block.call(host, login, userid)
39
+
40
+ begin
41
+ ssh_session = Net::SSH.start(host, userid, :timeout => self.ssh_timeout)
42
+ raise "Unable to establish connecton to #{host} as #{userid}" if ssh_session.nil?
43
+
44
+ ssh_session.open_channel do |channel|
45
+ channel.exec(cmd) do |ch, success|
46
+ ch.on_data do |chn, data|
47
+ rsp ||= ''
48
+ rsp += data.to_s
49
+ end
50
+
51
+ ch.on_extended_data do |chn, data|
52
+ err_rsp ||= ''
53
+ err_rsp += data.to_s
54
+ end
55
+
56
+ end
57
+ end
58
+
59
+ # must do this or the channel only runs once
60
+ ssh_session.loop(5)
61
+
62
+ ssh_session.close
63
+ rescue Exception => e
64
+ err_rsp = "error talking to #{host} as #{userid}: #{e.message}"
65
+ ssh_session.shutdown! unless ssh_session.nil?
66
+ end
67
+ [rsp, err_rsp, cmd]
68
+ end
69
+
70
+ # run locally runs a comman locally and returns the output of stdout, stderr, and the command
71
+ # run in a 3 element array
72
+ #
73
+ # to send input to the subprocess, include the optional 'stdin_text' parameter. Don't
74
+ # forget to add newlines, if you need them.
75
+ def run_locally(stdin_text = nil, &block)
76
+ raise ArgumentError.new('block required') unless block_given?
77
+
78
+ cmd = block.call
79
+ # cmd = "echo '#{stdin_text}' | #{cmd}" if stdin_text
80
+
81
+ raise "Unable to run_locally - fork method not useable" unless Process.respond_to? :fork
82
+
83
+ # preload stdin if there is input to avoid a race condition
84
+ if stdin_text
85
+ stdin_pipe, child_stdin = IO.pipe if stdin_text
86
+ count = child_stdin.write(stdin_text.to_s)
87
+ child_stdin.close
88
+ end
89
+
90
+ child_stdout, stdout_pipe = IO.pipe
91
+ child_stderr, stderr_pipe = IO.pipe
92
+ unless (pid = Process.fork)
93
+ if stdin_text
94
+ STDIN.reopen(stdin_pipe)
95
+ else
96
+ STDIN.close
97
+ end
98
+ STDOUT.reopen(stdout_pipe)
99
+ STDERR.reopen(stderr_pipe)
100
+ begin
101
+ Process.exec ENV, cmd
102
+ rescue SystemCallError => e
103
+ STDERR.write("Unable to execute command '#{cmd}'\n #{e}")
104
+ ensure
105
+ exit
106
+ end
107
+ end
108
+
109
+ Process.wait
110
+
111
+ stdout = ''
112
+ stderr = ''
113
+ loop do
114
+ reads, writes, obands = IO.select([child_stdout, child_stderr], [], [], 1)
115
+ break if reads.nil?
116
+ stdout += child_stdout.read_nonblock(1024) if reads.include? child_stdout
117
+ stderr += child_stderr.read_nonblock(1024) if reads.include? child_stderr
118
+ end
119
+ stdout = nil if stdout.empty?
120
+ stderr = nil if stderr.empty?
121
+
122
+ [stdout, stderr, cmd]
123
+ end
124
+
125
+ end
126
+ end
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift File.expand_path('../lib', __FILE__)
3
+
4
+ require 'tdd_deploy'
5
+
6
+ module TddDeploy
7
+ class Server < TddDeploy::Base
8
+ LIB_DIR = File.expand_path('../..', __FILE__)
9
+ HOST_TESTS_DIR = File.join(Dir.pwd, 'lib', 'tdd_deploy', 'host_tests')
10
+ SITE_TESTS_DIR = File.join(Dir.pwd, 'lib', 'tdd_deploy', 'site_tests')
11
+ LOCAL_TESTS_DIR = File.join(Dir.pwd, 'lib', 'tdd_deploy', 'local_tests')
12
+
13
+ attr_accessor :test_classes
14
+
15
+ def initialize *args
16
+ @already_defined = TddDeploy.constants
17
+ super
18
+ end
19
+
20
+ def load_all_tests
21
+ if TddDeploy::Base.children == [self.class]
22
+ [TddDeploy::Server::HOST_TESTS_DIR, TddDeploy::Server::SITE_TESTS_DIR,
23
+ TddDeploy::Server::LOCAL_TESTS_DIR].each do |dir|
24
+ if File.exists?(dir)
25
+ puts "gathering tests from #{dir}"
26
+ Dir.new(dir).each do |fname|
27
+ require File.join(dir, fname) unless fname[0] == '.'
28
+ end
29
+ else
30
+ puts "skipping #{dir} - no such directory"
31
+ end
32
+ end
33
+ end
34
+ self.test_classes = TddDeploy::Base.children - [self.class]
35
+ end
36
+
37
+ def run_all_tests
38
+ load_all_tests
39
+
40
+ ret = true
41
+ self.test_classes.each do |klass|
42
+ obj = klass.new
43
+ # puts "#{klass}.instance_methods: #{klass.instance_methods(false)}"
44
+ klass.instance_methods(false).each do |func|
45
+ ret &= obj.send func.to_sym
46
+ end
47
+ end
48
+ ret
49
+ end
50
+
51
+ def call(env)
52
+ run_all_tests
53
+ body = ["<h1>TDD Test Results:</h1>", self.test_results]
54
+ return [200, {'Content-Length' => body.join('').length.to_s, 'Content-Type' => 'text/html'}, body]
55
+ end
56
+ end
57
+ end
58
+
@@ -0,0 +1,46 @@
1
+ require 'tdd_deploy/base'
2
+
3
+ module TddDeploy
4
+ # == TddDeploy::SiteLayout
5
+ #
6
+ # tests for the existence of several directories on all hosts as *site_user* in
7
+ # the *site_user* home directory.
8
+ #
9
+ # The sub directories tested for are:
10
+ #
11
+ # *site* - a directory named for the name of the site.
12
+ # *site*/releases - a standard directory used by Capistrano
13
+ # *site*/nginx.conf - an nginx configuratino fragment which tells nginx to
14
+ # proxy the site's *thin* servers
15
+ # *site*/monitrc - a monit configuration fragment which tells monit how to monitor
16
+ # the site's *thin* servers.
17
+ class SiteLayout < TddDeploy::Base
18
+ def test_site_subdir
19
+ deploy_test_on_all_hosts_as self.site_user, "#{self.site}/", \
20
+ "directory /home/#{self.site_user}/#{self.site} should exist" do
21
+ 'ls -F'
22
+ end
23
+ end
24
+
25
+ def test_releases_subdir
26
+ deploy_test_on_all_hosts_as self.site_user, "releases", \
27
+ "directory /home/#{self.site_user}/#{self.site}/releases should exist" do
28
+ "ls -F #{self.site}"
29
+ end
30
+ end
31
+
32
+ def test_monitrc
33
+ deploy_test_on_all_hosts_as self.site_user, 'monitrc', \
34
+ "file /home/#{self.site_user}/monitrc should exist" do
35
+ 'ls'
36
+ end
37
+ end
38
+
39
+ def test_nginx_conf
40
+ deploy_test_on_all_hosts_as self.site_user, 'nginx.conf', \
41
+ "file /home/#{self.site_user}/nginx.conf should exist" do
42
+ 'ls'
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,14 @@
1
+ require 'tdd_deploy/base'
2
+
3
+ module TddDeploy
4
+ # = TddDeploy::SiteUser
5
+ #
6
+ # tests all hosts to make sure that the local user can log on as *site_user*
7
+ class SiteUser < TddDeploy::Base
8
+ def test_login_as_site_user
9
+ deploy_test_on_all_hosts_as self.site_user, "/home/#{self.site_user}", "unable to log into host as #{self.site_user}" do
10
+ 'pwd'
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ module TddDeploy
2
+ VERSION = '0.0.1'
3
+ end