cucumber-chef 1.0.3 → 2.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. data/.gitignore +8 -35
  2. data/.rspec +1 -0
  3. data/.rvmrc.template +2 -0
  4. data/.travis.yml +22 -0
  5. data/Gemfile +2 -15
  6. data/LICENSE +202 -201
  7. data/NOTICE +9 -0
  8. data/README.md +696 -92
  9. data/Rakefile +39 -25
  10. data/TODO.md +28 -0
  11. data/bin/cc-knife +32 -0
  12. data/bin/cucumber-chef +409 -79
  13. data/chef_repo/cookbooks/cucumber-chef/LICENSE +202 -0
  14. data/chef_repo/cookbooks/cucumber-chef/README.md +69 -0
  15. data/chef_repo/cookbooks/cucumber-chef/attributes/default.rb +27 -0
  16. data/chef_repo/cookbooks/cucumber-chef/metadata.rb +13 -0
  17. data/chef_repo/cookbooks/cucumber-chef/recipes/default.rb +23 -0
  18. data/chef_repo/cookbooks/cucumber-chef/recipes/lxc.rb +315 -0
  19. data/chef_repo/cookbooks/cucumber-chef/recipes/test_lab.rb +146 -0
  20. data/chef_repo/cookbooks/cucumber-chef/templates/default/db-168-192.erb +15 -0
  21. data/chef_repo/cookbooks/cucumber-chef/templates/default/db-test-lab.erb +15 -0
  22. data/chef_repo/cookbooks/cucumber-chef/templates/default/dhcpd-conf.erb +44 -0
  23. data/chef_repo/cookbooks/cucumber-chef/templates/default/gemrc.erb +10 -0
  24. data/{cookbooks/cucumber-chef/files/default/cucumber-net → chef_repo/cookbooks/cucumber-chef/templates/default/lxc-initializer-config.erb} +1 -1
  25. data/chef_repo/cookbooks/cucumber-chef/templates/default/lxc-install-chef.erb +3 -0
  26. data/chef_repo/cookbooks/cucumber-chef/templates/default/motd.erb +10 -0
  27. data/chef_repo/cookbooks/cucumber-chef/templates/default/named-conf-local.erb +25 -0
  28. data/{cookbooks/cucumber-chef/files/default/permissive-ssh-config → chef_repo/cookbooks/cucumber-chef/templates/default/ssh-config.erb} +3 -1
  29. data/chef_repo/roles/test_lab.rb +24 -0
  30. data/cucumber-chef.gemspec +50 -123
  31. data/examples/README.md +7 -0
  32. data/examples/users_add.feature +51 -0
  33. data/examples/users_auto_remove.feature +50 -0
  34. data/features/support/env.rb +3 -2
  35. data/lib/cucumber/chef/bootstrap.rb +94 -0
  36. data/lib/cucumber/chef/command.rb +78 -0
  37. data/lib/cucumber/chef/config.rb +143 -93
  38. data/lib/cucumber/chef/helpers/chef_client.rb +87 -0
  39. data/lib/cucumber/chef/helpers/chef_server.rb +90 -0
  40. data/lib/cucumber/chef/helpers/command.rb +57 -0
  41. data/lib/cucumber/chef/helpers/container.rb +154 -0
  42. data/lib/cucumber/chef/helpers/minitest.rb +35 -0
  43. data/lib/cucumber/chef/helpers/server.rb +81 -0
  44. data/lib/cucumber/chef/helpers/test_lab.rb +46 -0
  45. data/lib/cucumber/chef/helpers/utility.rb +73 -0
  46. data/lib/cucumber/chef/helpers.rb +56 -0
  47. data/lib/cucumber/chef/logger.rb +90 -0
  48. data/lib/cucumber/chef/provisioner.rb +275 -69
  49. data/lib/cucumber/chef/ssh.rb +190 -0
  50. data/lib/cucumber/chef/steps/chef_steps.rb +32 -0
  51. data/lib/cucumber/chef/steps/minitest_steps.rb +29 -0
  52. data/lib/cucumber/chef/steps/provision_steps.rb +60 -0
  53. data/lib/cucumber/chef/steps/ssh_steps.rb +95 -0
  54. data/lib/cucumber/chef/steps.rb +27 -0
  55. data/lib/cucumber/chef/tcp_socket.rb +83 -0
  56. data/lib/cucumber/chef/template.rb +57 -0
  57. data/lib/cucumber/chef/templates/bootstrap/ubuntu-precise-test-lab.erb +99 -0
  58. data/lib/cucumber/chef/templates/cucumber/env.rb +56 -0
  59. data/lib/cucumber/chef/templates/cucumber/example_feature.erb +49 -0
  60. data/lib/cucumber/chef/templates/cucumber/example_steps.erb +11 -0
  61. data/lib/cucumber/chef/templates/cucumber/readme-data_bags.erb +1 -0
  62. data/lib/cucumber/chef/templates/cucumber/readme-keys.erb +1 -0
  63. data/lib/cucumber/chef/templates/cucumber/readme-roles.erb +1 -0
  64. data/lib/cucumber/chef/templates/cucumber/readme.erb +18 -0
  65. data/lib/cucumber/chef/templates/cucumber-chef/config-rb.erb +33 -0
  66. data/lib/cucumber/chef/templates/cucumber-chef/cucumber-yml.erb +2 -0
  67. data/lib/cucumber/chef/templates/cucumber-chef/knife-rb.erb +18 -0
  68. data/lib/cucumber/chef/test_lab.rb +308 -52
  69. data/lib/cucumber/chef/test_runner.rb +86 -15
  70. data/lib/cucumber/chef/utility.rb +128 -0
  71. data/lib/cucumber/chef/version.rb +30 -1
  72. data/lib/cucumber/chef.rb +53 -20
  73. data/lib/cucumber-chef.rb +24 -1
  74. data/spec/cucumber/chef/config_spec.rb +144 -78
  75. data/spec/cucumber/chef/provisioner_spec.rb +60 -16
  76. data/spec/cucumber/chef/test_lab_spec.rb +62 -19
  77. data/spec/spec_helper.rb +30 -26
  78. data/todo.org +17 -0
  79. metadata +267 -163
  80. data/.document +0 -5
  81. data/VERSION +0 -1
  82. data/cookbooks/cucumber-chef/README.rdoc +0 -8
  83. data/cookbooks/cucumber-chef/files/default/add-git-identity +0 -2
  84. data/cookbooks/cucumber-chef/files/default/controller-first-boot +0 -1
  85. data/cookbooks/cucumber-chef/files/default/cucumber-private-key +0 -27
  86. data/cookbooks/cucumber-chef/files/default/cucumber-run_list +0 -1
  87. data/cookbooks/cucumber-chef/files/default/git-private-key +0 -27
  88. data/cookbooks/cucumber-chef/files/default/install-chef +0 -1
  89. data/cookbooks/cucumber-chef/files/default/lxc-controller-network-config +0 -5
  90. data/cookbooks/cucumber-chef/files/default/lxc-lucid-chef +0 -378
  91. data/cookbooks/cucumber-chef/metadata.rb +0 -6
  92. data/cookbooks/cucumber-chef/recipes/controller.rb +0 -51
  93. data/cookbooks/cucumber-chef/recipes/lxc.rb +0 -35
  94. data/cookbooks/cucumber-chef/recipes/test_lab.rb +0 -23
  95. data/cookbooks/cucumber-chef/recipes/testrunner.rb +0 -46
  96. data/cookbooks/cucumber-chef/roles/controller.rb +0 -7
  97. data/cookbooks/cucumber-chef/roles/test_lab_test.rb +0 -9
  98. data/cookbooks/cucumber-chef/templates/default/controller-client.erb +0 -5
  99. data/cookbooks/cucumber-chef/templates/default/lxc-lucid-chef +0 -385
  100. data/lib/cucumber/chef/handy.rb +0 -90
  101. data/lib/cucumber/chef/templates/controller.erb +0 -35
  102. data/lib/cucumber/chef/templates/env.rb +0 -16
  103. data/lib/cucumber/chef/templates/example_feature.erb +0 -11
  104. data/lib/cucumber/chef/templates/example_step.erb +0 -19
  105. data/lib/cucumber/chef/templates/readme.erb +0 -14
  106. data/lib/cucumber/chef/templates/ubuntu10.04-gems.erb +0 -43
  107. data/lib/cucumber/ec2_server_create.rb +0 -99
  108. data/website/website.html +0 -385
@@ -1,86 +1,342 @@
1
+ ################################################################################
2
+ #
3
+ # Author: Stephen Nelson-Smith <stephen@atalanta-systems.com>
4
+ # Author: Zachary Patten <zachary@jovelabs.com>
5
+ # Copyright: Copyright (c) 2011-2012 Atalanta Systems Ltd
6
+ # License: Apache License, Version 2.0
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+ ################################################################################
21
+
1
22
  module Cucumber
2
23
  module Chef
3
- class TestLabError < Error ; end
24
+
25
+ class TestLabError < Error; end
4
26
 
5
27
  class TestLab
6
- def initialize(config)
7
- @config = config
8
- @connection =
9
- Fog::Compute.new(:provider => 'AWS',
10
- :aws_access_key_id => @config[:knife][:aws_access_key_id],
11
- :aws_secret_access_key => @config[:knife][:aws_secret_access_key],
12
- :region => @config[:knife][:region])
28
+ attr_reader :connection, :server
29
+ attr_accessor :stdout, :stderr, :stdin
30
+
31
+ INVALID_STATES = %w( terminated pending )
32
+ RUNNING_STATES = %w( running starting-up )
33
+ SHUTDOWN_STATES = %w( shutdown stopping stopped shutting-down )
34
+ VALID_STATES = RUNNING_STATES+SHUTDOWN_STATES
35
+
36
+ ################################################################################
37
+
38
+ def initialize(stdout=STDOUT, stderr=STDERR, stdin=STDIN)
39
+ @stdout, @stderr, @stdin = stdout, stderr, stdin
40
+ @stdout.sync = true if @stdout.respond_to?(:sync=)
41
+
42
+ @connection = Fog::Compute.new(
43
+ :provider => 'AWS',
44
+ :aws_access_key_id => Cucumber::Chef::Config[:aws][:aws_access_key_id],
45
+ :aws_secret_access_key => Cucumber::Chef::Config[:aws][:aws_secret_access_key],
46
+ :region => Cucumber::Chef::Config[:aws][:region]
47
+ )
48
+ ensure_security_group
13
49
  end
14
50
 
15
- def build(output)
16
- if exists?
17
- raise TestLabError.new("A test lab already exists using the AWS credentials you supplied")
51
+ ################################################################################
52
+
53
+ def create
54
+ if (lab_exists? && (@server = labs_running.first))
55
+ @stdout.puts("A test lab already exists using the AWS credentials you have supplied; attempting to reprovision it.")
56
+ else
57
+ server_definition = {
58
+ :image_id => Cucumber::Chef::Config.aws_image_id,
59
+ :groups => Cucumber::Chef::Config[:aws][:aws_security_group],
60
+ :flavor_id => Cucumber::Chef::Config[:aws][:aws_instance_type],
61
+ :key_name => Cucumber::Chef::Config[:aws][:aws_ssh_key_id],
62
+ :availability_zone => Cucumber::Chef::Config[:aws][:availability_zone],
63
+ :tags => { "purpose" => "cucumber-chef", "cucumber-chef-mode" => Cucumber::Chef::Config[:mode] },
64
+ :identity_file => Cucumber::Chef::Config[:aws][:identity_file]
65
+ }
66
+ if (@server = @connection.servers.create(server_definition))
67
+ @stdout.puts("Provisioning cucumber-chef test lab platform.")
68
+
69
+ @stdout.print("Waiting for instance...")
70
+ Cucumber::Chef.spinner do
71
+ @server.wait_for { ready? }
72
+ end
73
+ @stdout.puts("done.\n")
74
+
75
+ tag_server
76
+
77
+ @stdout.print("Waiting for 20 seconds...")
78
+ Cucumber::Chef.spinner do
79
+ sleep(20)
80
+ end
81
+ @stdout.print("done.\n")
82
+ end
83
+ end
84
+
85
+ if @server
86
+ @stdout.print("Waiting for SSHD...")
87
+ Cucumber::Chef.spinner do
88
+ Cucumber::Chef::TCPSocket.new(@server.public_ip_address, 22).wait
89
+ end
90
+ @stdout.puts("done.\n")
18
91
  end
19
- server_definition = {
20
- :image_id => @config[:knife][:aws_image_id],
21
- :groups => "default",
22
- :flavor_id => "m1.small",
23
- :key_name => @config[:knife][:aws_ssh_key_id],
24
- :availability_zone => @config[:knife][:availability_zone],
25
- :tags => {"purpose" => "cucumber-chef"},
26
- :identity_file => @config[:knife][:identity_file]
27
- }
28
- @server = @connection.servers.create(server_definition)
29
- output.puts "Provisioning cucumber-chef test lab platform."
30
- output.print "Waiting for server"
31
- @server.wait_for { output.print "."; ready? }
32
- output.puts("\n")
33
- tag_server
34
- output.puts "Instance ID: #{@server.id} ; IP Address #{@server.public_ip_address}"
35
- output.puts "Platform provisioned. Run cucumber-chef project to get started."
92
+
36
93
  @server
94
+
95
+ rescue Exception => e
96
+ $logger.fatal { e.message }
97
+ $logger.fatal { "Backtrace:\n#{e.backtrace.join("\n")}" }
98
+ raise TestLabError, e.message
37
99
  end
38
100
 
101
+ ################################################################################
102
+
39
103
  def destroy
40
- running_labs.each do |server|
41
- puts "Destroying Server: #{server.public_ip_address}"
42
- server.destroy
104
+ if ((l = labs).count > 0)
105
+ @stdout.puts("Destroying Servers:")
106
+ l.each do |server|
107
+ @stdout.puts(" * #{server.public_ip_address}")
108
+ server.destroy
109
+ end
110
+ else
111
+ @stdout.puts("There are no cucumber-chef test labs to destroy!")
43
112
  end
44
- nodes.each do |node|
45
- puts "Destroying Node: #{node[:cloud][:public_ipv4]}"
46
- node.destroy
113
+
114
+ rescue Exception => e
115
+ $logger.fatal { e.message }
116
+ $logger.fatal { e.backtrace.join("\n") }
117
+ raise TestLabError, e.message
118
+ end
119
+
120
+ ################################################################################
121
+
122
+ def start
123
+ if (lab_exists? && (@server = labs_shutdown.first))
124
+ if @server.start
125
+
126
+ @stdout.print("Waiting for instance...")
127
+ Cucumber::Chef.spinner do
128
+ @server.wait_for { ready? }
129
+ end
130
+ @stdout.puts("done.\n")
131
+
132
+ @stdout.print("Waiting for SSHD...")
133
+ Cucumber::Chef.spinner do
134
+ Cucumber::Chef::TCPSocket.new(@server.public_ip_address, 22).wait
135
+ end
136
+ @stdout.puts("done.\n")
137
+
138
+ @stdout.puts("Successfully started up cucumber-chef test lab!")
139
+
140
+ info
141
+ else
142
+ @stdout.puts("Failed to start up cucumber-chef test lab!")
143
+ end
144
+ else
145
+ @stdout.puts("There are no available cucumber-chef test labs to start up!")
47
146
  end
147
+
148
+ rescue Exception => e
149
+ $logger.fatal { e.message }
150
+ $logger.fatal { e.backtrace.join("\n") }
151
+ raise TestLabError, e.message
48
152
  end
49
153
 
50
- def exists?
51
- running_labs.size > 0
154
+
155
+ def stop
156
+ if (lab_exists? && (@server = labs_running.first))
157
+ if @server.stop
158
+ @stdout.puts("Successfully shutdown cucumber-chef test lab!")
159
+ else
160
+ @stdout.puts("Failed to shutdown cucumber-chef test lab!")
161
+ end
162
+ else
163
+ @stdout.puts("There are no available cucumber-chef test labs top shutdown!")
164
+ end
165
+
166
+ rescue Exception => e
167
+ $logger.fatal { e.message }
168
+ $logger.fatal { e.backtrace.join("\n") }
169
+ raise TestLabError, e.message
52
170
  end
53
171
 
172
+ ################################################################################
173
+
54
174
  def info
55
- (exists? && running_labs.first.public_ip_address) || ""
175
+ if lab_exists?
176
+ labs.each do |lab|
177
+ @stdout.puts("Instance ID: #{lab.id}")
178
+ @stdout.puts("State: #{lab.state}")
179
+ @stdout.puts("Username: #{lab.username}") if lab.username
180
+ if (lab.public_ip_address || lab.private_ip_address)
181
+ @stdout.puts
182
+ @stdout.puts("IP Address:")
183
+ @stdout.puts(" Public...: #{lab.public_ip_address}") if lab.public_ip_address
184
+ @stdout.puts(" Private..: #{lab.private_ip_address}") if lab.private_ip_address
185
+ end
186
+ if (lab.dns_name || lab.private_dns_name)
187
+ @stdout.puts
188
+ @stdout.puts("DNS:")
189
+ @stdout.puts(" Public...: #{lab.dns_name}") if lab.dns_name
190
+ @stdout.puts(" Private..: #{lab.private_dns_name}") if lab.private_dns_name
191
+ end
192
+ if (lab.tags.count > 0)
193
+ @stdout.puts
194
+ @stdout.puts("Tags:")
195
+ lab.tags.to_hash.each do |k,v|
196
+ @stdout.puts(" #{k}: #{v}")
197
+ end
198
+ end
199
+ if lab.public_ip_address
200
+ @stdout.puts
201
+ @stdout.puts("Chef-Server WebUI:")
202
+ @stdout.puts(" http://#{lab.public_ip_address}:4040/")
203
+ end
204
+ @stdout.puts
205
+ if (labs_running.include?(lab) && (n = nodes))
206
+ @stdout.puts
207
+ @stdout.puts("Nodes:")
208
+ n.each do |node|
209
+ @stdout.puts(" * #{node.name} (#{node.cloud.public_ipv4})")
210
+ end
211
+ end
212
+ if (labs_running.include?(lab) && (c = clients))
213
+ @stdout.puts
214
+ @stdout.puts("Clients:")
215
+ c.each do |client|
216
+ @stdout.puts(" * #{client.name}")
217
+ end
218
+ end
219
+ end
220
+ else
221
+ @stdout.puts("There are no cucumber-chef test labs to display information for!")
222
+ end
223
+
224
+ rescue Exception => e
225
+ $logger.fatal { e.message }
226
+ $logger.fatal { e.backtrace.join("\n") }
227
+ raise TestLabError, e.message
56
228
  end
57
229
 
58
- def public_hostname
59
- nodes.first.cloud.public_hostname
230
+ ################################################################################
231
+
232
+ def lab_exists?
233
+ (labs.size > 0)
234
+ end
235
+
236
+ ################################################################################
237
+
238
+ def labs
239
+ results = @connection.servers.select do |server|
240
+ $logger.debug("candidate") { "ID=#{server.id}, state='#{server.state}'" }
241
+ ( server.tags['cucumber-chef-mode'] == Cucumber::Chef::Config[:mode].to_s &&
242
+ server.tags['cucumber-chef-user'] == Cucumber::Chef::Config[:user].to_s &&
243
+ VALID_STATES.any?{ |state| state == server.state } )
244
+ end
245
+ results.each do |server|
246
+ $logger.debug("results") { "ID=#{server.id}, state='#{server.state}'" }
247
+ end
248
+ results
60
249
  end
61
250
 
251
+ ################################################################################
252
+
253
+ def labs_running
254
+ results = @connection.servers.select do |server|
255
+ $logger.debug("candidate") { "ID=#{server.id}, state='#{server.state}'" }
256
+ ( server.tags['cucumber-chef-mode'] == Cucumber::Chef::Config[:mode].to_s &&
257
+ server.tags['cucumber-chef-user'] == Cucumber::Chef::Config[:user].to_s &&
258
+ RUNNING_STATES.any?{ |state| state == server.state } )
259
+ end
260
+ results.each do |server|
261
+ $logger.debug("results") { "ID=#{server.id}, state='#{server.state}'" }
262
+ end
263
+ results
264
+ end
265
+
266
+ ################################################################################
267
+
268
+ def labs_shutdown
269
+ results = @connection.servers.select do |server|
270
+ $logger.debug("candidate") { "ID=#{server.id}, state='#{server.state}'" }
271
+ ( server.tags['cucumber-chef-mode'] == Cucumber::Chef::Config[:mode].to_s &&
272
+ server.tags['cucumber-chef-user'] == Cucumber::Chef::Config[:user].to_s &&
273
+ SHUTDOWN_STATES.any?{ |state| state == server.state } )
274
+ end
275
+ results.each do |server|
276
+ $logger.debug("results") { "ID=#{server.id}, state='#{server.state}'" }
277
+ end
278
+ results
279
+ end
280
+
281
+ ################################################################################
282
+
62
283
  def nodes
63
- search = ::Chef::Search::Query.new
64
- mode = @config[:mode]
65
- query = "roles:test_lab_test AND tags:#{mode}"
66
- nodes, offset, total = search.search("node", URI.escape(query))
284
+ Cucumber::Chef.load_knife_config
285
+ query = "tags:#{Cucumber::Chef::Config[:mode]} AND tags:#{Cucumber::Chef::Config[:user]}"
286
+ $logger.debug { "query(#{query})" }
287
+ nodes, offset, total = ::Chef::Search::Query.new.search("node", URI.escape(query))
67
288
  nodes.compact
68
289
  end
69
290
 
70
- def running_labs
71
- @connection.servers.select do |s|
72
- s.tags['cucumber-chef'] == @config[:mode] && s.state == 'running'
73
- end
291
+ def clients
292
+ Cucumber::Chef.load_knife_config
293
+ query = "tags:#{Cucumber::Chef::Config[:mode]} AND tags:#{Cucumber::Chef::Config[:user]}"
294
+ $logger.debug { "query(#{query})" }
295
+ clients, offset, total = ::Chef::Search::Query.new.search("client", URI.escape(query))
296
+ clients.compact
74
297
  end
75
298
 
299
+
300
+ ################################################################################
76
301
  private
302
+ ################################################################################
303
+
77
304
  def tag_server
78
- tag = @connection.tags.new
79
- tag.resource_id = @server.id
80
- tag.key = "cucumber-chef"
81
- tag.value = @config[:mode]
82
- tag.save
305
+ {
306
+ "cucumber-chef-mode" => Cucumber::Chef::Config[:mode],
307
+ "cucumber-chef-user" => Cucumber::Chef::Config[:user],
308
+ "purpose" => "cucumber-chef"
309
+ }.each do |k, v|
310
+ tag = @connection.tags.new
311
+ tag.resource_id = @server.id
312
+ tag.key, tag.value = k, v
313
+ tag.save
314
+ end
315
+ end
316
+
317
+ ################################################################################
318
+
319
+ def ensure_security_group
320
+ security_group_name = Cucumber::Chef::Config[:aws][:aws_security_group]
321
+ if (security_group = @connection.security_groups.get(security_group_name))
322
+ port_ranges = security_group.ip_permissions.collect{ |entry| entry["fromPort"]..entry["toPort"] }
323
+ security_group.authorize_port_range(22..22) if port_ranges.none?{ |port_range| port_range === 22 }
324
+ security_group.authorize_port_range(4000..4000) if port_ranges.none?{ |port_range| port_range === 4000 }
325
+ security_group.authorize_port_range(4040..4040) if port_ranges.none?{ |port_range| port_range === 4040 }
326
+ elsif (security_group = @connection.security_groups.new(:name => security_group_name, :description => "cucumber-chef test lab")).save
327
+ security_group.authorize_port_range(22..22)
328
+ security_group.authorize_port_range(4000..4000)
329
+ security_group.authorize_port_range(4040..4040)
330
+ else
331
+ raise TestLabError, "Could not find an existing or create a new AWS security group."
332
+ end
83
333
  end
334
+
335
+ ################################################################################
336
+
84
337
  end
338
+
85
339
  end
86
340
  end
341
+
342
+ ################################################################################
@@ -1,32 +1,103 @@
1
+ ################################################################################
2
+ #
3
+ # Author: Stephen Nelson-Smith <stephen@atalanta-systems.com>
4
+ # Author: Zachary Patten <zachary@jovelabs.com>
5
+ # Copyright: Copyright (c) 2011-2012 Atalanta Systems Ltd
6
+ # License: Apache License, Version 2.0
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+ ################################################################################
21
+
1
22
  module Cucumber
2
23
  module Chef
3
- class TestRunnerError < Error ; end
24
+
25
+ class TestRunnerError < Error; end
4
26
 
5
27
  class TestRunner
6
28
 
7
- require 'cucumber/chef/test_lab'
29
+ ################################################################################
30
+
31
+ def initialize(features_path, stdout=STDOUT, stderr=STDERR, stdin=STDIN)
32
+ @features_path = features_path
33
+ @stdout, @stderr, @stdin = stdout, stderr, stdin
34
+ @stdout.sync = true if @stdout.respond_to?(:sync=)
35
+
36
+ @test_lab = Cucumber::Chef::TestLab.new(@stdout, @stderr, @stdin)
37
+
38
+ @ssh = Cucumber::Chef::SSH.new(@stdout, @stderr, @stdin)
39
+ @ssh.config[:host] = @test_lab.labs_running.first.public_ip_address
40
+ @ssh.config[:ssh_user] = "ubuntu"
41
+ @ssh.config[:identity_file] = Cucumber::Chef.locate(:file, ".cucumber-chef", "id_rsa-#{@ssh.config[:ssh_user]}")
8
42
 
9
- def initialize(project_dir, config)
10
- @project_dir = project_dir
11
- @config = config
43
+ @stdout.puts("Cucumber-Chef Test Runner Initalized!")
12
44
  end
13
45
 
14
- def run
46
+ ################################################################################
47
+
48
+ def run(destroy, *args)
49
+ reset_project
15
50
  upload_project
16
- @project_path = File.join('/home/ubuntu', File.basename(@project_dir), 'features')
17
- connection = Net::SSH.start(@hostname, 'ubuntu', :keys => @key) do |ssh|
18
- @output = ssh.exec!("sudo cucumber #{@project_path}")
51
+
52
+ @stdout.puts("Executing Cucumber-Chef Test Runner")
53
+ remote_path = File.join("/", "home", "ubuntu", "features")
54
+ cucumber_options = args.flatten.compact.uniq.join(" ")
55
+ env = ( destroy ? "DESTROY=1" : nil )
56
+ command = [ "cd #{remote_path}", "&&", "sudo", env, "cucumber", cucumber_options, "--exclude support/roles", "--exclude support/data_bags", "--exclude support/keys", "." ].flatten.compact.join(" ")
57
+
58
+ @ssh.exec(command)
59
+ end
60
+
61
+
62
+ ################################################################################
63
+ private
64
+ ################################################################################
65
+
66
+ def reset_project
67
+ @stdout.print("Cleaning up any previous test runs...")
68
+ Cucumber::Chef.spinner do
69
+ remote_path = File.join("/", "home", "ubuntu", "features")
70
+
71
+ command = "rm -rf #{remote_path}"
72
+ @ssh.exec(command, :silence => true)
19
73
  end
20
- puts @output
74
+ @stdout.print("done.\n")
21
75
  end
22
76
 
77
+ ################################################################################
78
+
23
79
  def upload_project
24
- lab = Cucumber::Chef::TestLab.new(@config)
25
- @hostname = lab.public_hostname
26
- @key = File.expand_path(@config[:knife][:identity_file])
27
- %x[scp -r -i #{@key} #{@project_dir} ubuntu@#{@hostname}: 2>/dev/null]
28
- puts "Cucumber-chef project: #{File.basename(@project_dir)} sucessfully uploaded to the test lab."
80
+ @stdout.print("Uploading files required for this test run...")
81
+ Cucumber::Chef.spinner do
82
+ local_path = File.join(@features_path)
83
+ remote_path = File.join("/", "home", "ubuntu", "features")
84
+ @ssh.upload(local_path, remote_path)
85
+
86
+ root_path = Cucumber::Chef.locate_parent(".chef")
87
+ cucumber_config_file = File.expand_path(File.join(root_path, "cucumber.yml"))
88
+ if File.exists?(cucumber_config_file)
89
+ remote_file = File.join(remote_path, File.basename(cucumber_config_file))
90
+ @ssh.upload(cucumber_config_file, remote_file)
91
+ end
92
+ end
93
+ @stdout.print("done.\n")
29
94
  end
95
+
96
+ ################################################################################
97
+
30
98
  end
99
+
31
100
  end
32
101
  end
102
+
103
+ ################################################################################
@@ -0,0 +1,128 @@
1
+ ################################################################################
2
+ #
3
+ # Author: Stephen Nelson-Smith <stephen@atalanta-systems.com>
4
+ # Author: Zachary Patten <zachary@jovelabs.com>
5
+ # Copyright: Copyright (c) 2011-2012 Atalanta Systems Ltd
6
+ # License: Apache License, Version 2.0
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+ ################################################################################
21
+
22
+ module Cucumber
23
+ module Chef
24
+
25
+ class UtilityError < Error; end
26
+
27
+ module Utility
28
+
29
+ ################################################################################
30
+
31
+ def is_rc?
32
+ ((Cucumber::Chef::VERSION =~ /rc/) || (Cucumber::Chef::VERSION =~ /pre/))
33
+ end
34
+
35
+ ################################################################################
36
+
37
+ def load_knife_config
38
+ $logger.debug { "attempting to load cucumber-chef test lab 'knife.rb'" }
39
+
40
+ knife_rb = Cucumber::Chef.locate(:file, ".cucumber-chef", "knife.rb")
41
+ ::Chef::Config.from_file(knife_rb)
42
+
43
+ $logger.debug { "load_knife_config(#{knife_rb})" }
44
+ end
45
+
46
+ ################################################################################
47
+
48
+ def locate(type, *args)
49
+ pwd = Dir.pwd.split(File::SEPARATOR)
50
+ $logger.debug { "pwd='#{Dir.pwd}'" } if $logger
51
+ (pwd.length - 1).downto(0) do |i|
52
+ candidate = File.join(pwd[0..i], args)
53
+ $logger.debug { "candidate='#{candidate}'" } if $logger
54
+ case type
55
+ when :file
56
+ if (File.exists?(candidate) && !File.directory?(candidate))
57
+ result = File.expand_path(candidate)
58
+ $logger.debug { "result='#{result}'" } if $logger
59
+ return result
60
+ end
61
+ when :directory
62
+ if (File.exists?(candidate) && File.directory?(candidate))
63
+ result = File.expand_path(candidate)
64
+ $logger.debug { "result='#{result}'" } if $logger
65
+ return result
66
+ end
67
+ when :any
68
+ if File.exists?(candidate)
69
+ result = File.expand_path(candidate)
70
+ $logger.debug { "result='#{result}'" } if $logger
71
+ return result
72
+ end
73
+ end
74
+ end
75
+
76
+ message = "Could not locate #{type} '#{File.join(args)}'."
77
+ $logger.fatal { message } if $logger
78
+ raise UtilityError, message
79
+ end
80
+
81
+ ################################################################################
82
+
83
+ def locate_parent(child)
84
+ parent = (locate(:any, child).split(File::SEPARATOR) rescue nil)
85
+ raise UtilityError, "Could not locate parent of '#{child}'." unless parent
86
+ File.expand_path(File.join(parent[0..(parent.length - 2)]))
87
+ end
88
+
89
+ ################################################################################
90
+
91
+ def spinner(stdout=STDOUT, stderr=STDERR, stdin=STDIN)
92
+ spinning_chars = %w[| / - \\]
93
+ count = 0
94
+ spinner = Thread.new do
95
+ while count do
96
+ stdout.print spinning_chars[(count+=1) % spinning_chars.length]
97
+ stdout.flush if stdout.respond_to?(:flush)
98
+ sleep(0.25)
99
+ stdout.print "\b"
100
+ stdout.flush if stdout.respond_to?(:flush)
101
+ end
102
+ end
103
+ yield.tap do
104
+ count = false
105
+ spinner.join
106
+ end
107
+ end
108
+
109
+ ################################################################################
110
+
111
+ def generate_do_not_edit_warning(message=nil)
112
+ warning = []
113
+ warning << "#"
114
+ warning << "# WARNING: Automatically generated file; DO NOT EDIT!"
115
+ warning << [ "# Cucumber-Chef v#{Cucumber::Chef::VERSION}", message ].compact.join(" ")
116
+ warning << "# Generated on #{Time.now.utc.to_s}"
117
+ warning << "#"
118
+ warning.join("\n")
119
+ end
120
+
121
+ ################################################################################
122
+
123
+ end
124
+
125
+ end
126
+ end
127
+
128
+ ################################################################################
@@ -1,5 +1,34 @@
1
+ ################################################################################
2
+ #
3
+ # Author: Stephen Nelson-Smith <stephen@atalanta-systems.com>
4
+ # Author: Zachary Patten <zachary@jovelabs.com>
5
+ # Copyright: Copyright (c) 2011-2012 Atalanta Systems Ltd
6
+ # License: Apache License, Version 2.0
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+ ################################################################################
21
+
1
22
  module Cucumber
2
23
  module Chef
3
- VERSION = '0.0.2'
24
+
25
+ ################################################################################
26
+
27
+ VERSION = "2.0.0.pre" unless const_defined?(:VERSION)
28
+
29
+ ################################################################################
30
+
4
31
  end
5
32
  end
33
+
34
+ ################################################################################