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,92 +1,298 @@
1
- require "digest"
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
+ ################################################################################
2
21
 
3
22
  module Cucumber
4
23
  module Chef
5
- class ProvisionerError < Error ; end
24
+
25
+ class ProvisionerError < Error; end
6
26
 
7
27
  class Provisioner
8
- def initialize
9
- @cookbook_path = File.join(File.dirname(__FILE__), "../../../cookbooks/cucumber-chef")
28
+ attr_accessor :stdout, :stderr, :stdin
29
+
30
+ HOSTNAME = "cucumber-chef.test-lab"
31
+ PASSWORD = "p@ssw0rd1"
32
+
33
+ ################################################################################
34
+
35
+ def initialize(server, stdout=STDOUT, stderr=STDERR, stdin=STDIN)
36
+ @server = server
37
+ @stdout, @stderr, @stdin = stdout, stderr, stdin
38
+ @stdout.sync = true if @stdout.respond_to?(:sync=)
39
+
40
+ @ssh = Cucumber::Chef::SSH.new(@stdout, @stderr, @stdin)
41
+ @ssh.config[:host] = @server.public_ip_address
42
+ @ssh.config[:ssh_user] = "ubuntu"
43
+ @ssh.config[:identity_file] = Cucumber::Chef::Config[:aws][:identity_file]
44
+
45
+ @command = Cucumber::Chef::Command.new(@stdout, @stderr, @stdin)
46
+
47
+ @cookbooks_path = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "chef_repo", "cookbooks"))
48
+ @roles_path = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "chef_repo", "roles"))
10
49
  end
11
50
 
12
- def bootstrap_node(dns_name, config)
13
- template_file = File.join(File.dirname(__FILE__), "templates/ubuntu10.04-gems.erb")
14
- bootstrap = ::Chef::Knife::Bootstrap.new
15
- @stdout, @stderr, @stdout = StringIO.new, StringIO.new, StringIO.new
16
- ui = ::Chef::Knife::UI.new(@stdout, @stderr, @stdout, bootstrap.config)
17
- bootstrap.ui = ui
18
- nodename = chef_node_name(config)
19
- bootstrap.name_args = [dns_name]
20
- bootstrap.config[:run_list] = "role[test_lab_test]"
21
- bootstrap.config[:ssh_user] = "ubuntu"
22
- bootstrap.config[:identity_file] = config[:knife][:identity_file]
23
- bootstrap.config[:chef_node_name] = nodename
24
- bootstrap.config[:use_sudo] = true
25
- bootstrap.config[:template_file] = template_file
26
- bootstrap.config[:validation_client_name] = config["validation_client_name"]
27
- bootstrap.config[:validation_key] = config["validation_key"]
28
- bootstrap.config[:chef_server_url] = config["chef_server_url"]
29
- bootstrap.run
30
- tag_node(config)
51
+ ################################################################################
52
+
53
+ def build
54
+ template_file = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "lib", "cucumber", "chef", "templates", "bootstrap", "ubuntu-precise-test-lab.erb"))
55
+
56
+ bootstrap(template_file)
57
+ wait_for_chef_server
58
+
59
+ download_chef_credentials
60
+ render_knife_rb
61
+
62
+ upload_cookbook
63
+ upload_role
64
+ tag_node
65
+ add_node_role
66
+
67
+ chef_first_run
68
+
69
+ download_proxy_ssh_credentials
70
+
71
+ reboot_test_lab
31
72
  end
32
73
 
33
- def build_controller(dns_name, config)
34
- template_file = File.join(File.dirname(__FILE__), "templates/controller.erb")
35
- bootstrap = ::Chef::Knife::Bootstrap.new
36
- @stdout, @stderr, @stdout = StringIO.new, StringIO.new, StringIO.new
37
- ui = ::Chef::Knife::UI.new(@stdout, @stderr, @stdout, bootstrap.config)
38
- bootstrap.ui = ui
39
- bootstrap.name_args = [dns_name]
40
- bootstrap.config[:ssh_user] = "ubuntu"
41
- bootstrap.config[:identity_file] = config[:knife][:identity_file]
42
- bootstrap.config[:chef_node_name] = "cucumber-chef-controller"
43
- bootstrap.config[:use_sudo] = true
44
- bootstrap.config[:template_file] = template_file
45
- bootstrap.config[:validation_client_name] = config["validation_client_name"]
46
- bootstrap.config[:validation_key] = config["validation_key"]
47
- bootstrap.config[:chef_server_url] = config["chef_server_url"]
48
- bootstrap.run
49
- bootstrap
74
+
75
+ ################################################################################
76
+ private
77
+ ################################################################################
78
+
79
+ def bootstrap(template_file)
80
+ raise ProvisionerError, "You must have the environment variable 'USER' set." if !Cucumber::Chef::Config[:user]
81
+
82
+ @stdout.print("Bootstrapping AWS EC2 instance...")
83
+ Cucumber::Chef.spinner do
84
+ attributes = {
85
+ "run_list" => "role[test_lab]",
86
+ "cucumber_chef" => {
87
+ "version" => Cucumber::Chef::VERSION,
88
+ "prerelease" => Cucumber::Chef::Config[:prerelease]
89
+ }
90
+ }
91
+
92
+ bootstrap = Cucumber::Chef::Bootstrap.new(@stdout, @stderr, @stdin)
93
+ bootstrap.config[:host] = @server.public_ip_address
94
+ bootstrap.config[:ssh_user] = "ubuntu"
95
+ bootstrap.config[:use_sudo] = true
96
+ bootstrap.config[:identity_file] = Cucumber::Chef::Config[:aws][:identity_file]
97
+ bootstrap.config[:template_file] = template_file
98
+ bootstrap.config[:context][:hostname] = HOSTNAME
99
+ bootstrap.config[:context][:chef_server] = HOSTNAME
100
+ bootstrap.config[:context][:amqp_password] = PASSWORD
101
+ bootstrap.config[:context][:admin_password] = PASSWORD
102
+ bootstrap.config[:context][:user] = Cucumber::Chef::Config[:user]
103
+ bootstrap.config[:context][:attributes] = attributes
104
+ bootstrap.run
105
+ end
106
+ @stdout.print("done.\n")
50
107
  end
51
108
 
52
- def upload_cookbook(config)
53
- version_loader = ::Chef::Cookbook::CookbookVersionLoader.new(@cookbook_path)
54
- version_loader.load_cookbooks
55
- uploader = ::Chef::CookbookUploader.new(version_loader.cookbook_version,
56
- @cookbook_path)
57
- uploader.upload_cookbook
109
+ ################################################################################
110
+
111
+ def download_chef_credentials
112
+ @stdout.print("Downloading chef-server credentials...")
113
+ Cucumber::Chef.spinner do
114
+ local_path = Cucumber::Chef.locate(:directory, ".cucumber-chef")
115
+ remote_path = File.join("/", "home", @ssh.config[:ssh_user], ".chef")
116
+
117
+ files = [ "#{Cucumber::Chef::Config[:user]}.pem", "validation.pem" ]
118
+ files.each do |file|
119
+ @ssh.download(File.join(remote_path, file), File.join(local_path, file))
120
+ end
121
+ end
122
+ @stdout.print("done.\n")
123
+ end
124
+
125
+ ################################################################################
126
+
127
+ def download_proxy_ssh_credentials
128
+ @stdout.print("Downloading container SSH credentials...")
129
+ Cucumber::Chef.spinner do
130
+ local_path = Cucumber::Chef.locate(:directory, ".cucumber-chef")
131
+ remote_path = File.join("/", "home", @ssh.config[:ssh_user], ".ssh")
132
+
133
+ files = { "id_rsa" => "id_rsa-ubuntu" }
134
+ files.each do |remote_file, local_file|
135
+ local = File.join(local_path, local_file)
136
+ @ssh.download(File.join(remote_path, remote_file), local)
137
+ File.chmod(0600, local)
138
+ end
139
+ end
140
+ @stdout.print("done.\n")
58
141
  end
59
142
 
60
- def upload_role(config)
61
- role_path = File.join(@cookbook_path, "roles")
62
- ::Chef::Config[:role_path] = role_path
63
- role = ::Chef::Role.from_disk("test_lab_test")
64
- role.save
65
- role = ::Chef::Role.from_disk("controller")
66
- role.save
143
+ ################################################################################
144
+
145
+ def render_knife_rb
146
+ @stdout.print("Building 'cc-knife' configuration...")
147
+ Cucumber::Chef.spinner do
148
+ template_file = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "lib", "cucumber", "chef", "templates", "cucumber-chef", "knife-rb.erb"))
149
+ knife_rb = File.expand_path(File.join(Cucumber::Chef.locate(:directory, ".cucumber-chef"), "knife.rb"))
150
+
151
+ context = {
152
+ :chef_server => @server.public_ip_address,
153
+ :librarian_chef => Cucumber::Chef::Config[:librarian_chef],
154
+ :user => Cucumber::Chef::Config[:user]
155
+ }
156
+
157
+ File.open(knife_rb, 'w') do |f|
158
+ f.puts(Cucumber::Chef::Template.render(template_file, context))
159
+ end
160
+ end
161
+ @stdout.print("done.\n")
162
+ end
163
+
164
+ ################################################################################
165
+
166
+ def upload_cookbook
167
+ $logger.debug { "Uploading cucumber-chef cookbooks..." }
168
+ @stdout.print("Uploading cucumber-chef cookbooks...")
169
+
170
+ Cucumber::Chef.spinner do
171
+ Cucumber::Chef.load_knife_config
172
+ cookbook_repo = ::Chef::CookbookLoader.new(@cookbooks_path)
173
+ cookbook_repo.each do |name, cookbook|
174
+ $logger.debug { "::Chef::CookbookUploader(#{name}) ATTEMPT" }
175
+ ::Chef::CookbookUploader.new(cookbook, @cookbooks_path, :force => true).upload_cookbook
176
+ $logger.debug { "::Chef::CookbookUploader(#{name}) UPLOADED" }
177
+ end
178
+ #@command.knife([ "cookbook upload cucumber-chef", "-o", @cookbooks_path ], :silence => true)
179
+ end
180
+
181
+ @stdout.print("done.\n")
182
+ $logger.debug { "Successfully uploaded cucumber-chef test lab cookbooks." }
67
183
  end
68
-
69
- def build_test_lab(config, output)
70
- TestLab.new(config).build(output)
184
+
185
+ ################################################################################
186
+
187
+ def upload_role
188
+ $logger.debug { "Uploading cucumber-chef test lab role..." }
189
+ @stdout.print("Uploading cucumber-chef test lab role...")
190
+
191
+ Cucumber::Chef.spinner do
192
+ Cucumber::Chef.load_knife_config
193
+ ::Chef::Config[:role_path] = @roles_path
194
+ [ "test_lab" ].each do |name|
195
+ role = ::Chef::Role.from_disk(name)
196
+ role.save
197
+ end
198
+ #@command.knife([ "role from file", File.join(@roles_path, "test_lab.rb") ], :silence => true)
199
+ end
200
+
201
+ @stdout.print("done.\n")
202
+ $logger.debug { "Successfully uploaded cucumber-chef test lab roles."}
71
203
  end
72
204
 
73
- def tag_node(config)
74
- node = ::Chef::Node.load(chef_node_name)
75
- node.tags << (config.test_mode? ? 'test' : 'user')
76
- node.save
205
+ ################################################################################
206
+
207
+ def tag_node
208
+ $logger.debug { "Tagging cucumber-chef test lab node..." }
209
+ @stdout.print("Tagging cucumber-chef test lab node...")
210
+
211
+ Cucumber::Chef.spinner do
212
+ Cucumber::Chef.load_knife_config
213
+ node = ::Chef::Node.load(HOSTNAME)
214
+ [ Cucumber::Chef::Config[:mode].to_s, Cucumber::Chef::Config[:user].to_s ].each do |tag|
215
+ node.tags << tag
216
+ node.save
217
+ end
218
+ #@command.knife([ "tag create", HOSTNAME, Cucumber::Chef::Config[:mode] ], :silence => true)
219
+ end
220
+
221
+ @stdout.print("done.\n")
222
+ $logger.debug { "Successfully tagged cucumber-chef test lab node."}
77
223
  end
78
-
79
- private
80
-
81
- def chef_node_name(config=nil)
82
- @node_name ||= begin
83
- if config.test_mode?
84
- "cucumber-chef-#{Digest::SHA1.hexdigest(Time.now.to_s)[0..7]}"
85
- else
86
- "cucumber-chef-test-lab"
224
+
225
+ ################################################################################
226
+
227
+ def add_node_role
228
+ $logger.debug { "Setting up cucumber-chef test lab run list..." }
229
+ @stdout.print("Setting up cucumber-chef test lab run list...")
230
+
231
+ Cucumber::Chef.spinner do
232
+ Cucumber::Chef.load_knife_config
233
+ node = ::Chef::Node.load(HOSTNAME)
234
+ [ "role[test_lab]" ].each do |entry|
235
+ node.run_list << entry
87
236
  end
237
+ node.save
238
+ #@command.knife([ "node run_list add", HOSTNAME, "\"role[test_lab]\"" ], :silence => true)
88
239
  end
240
+
241
+ $logger.debug { "Successfully added roles to cucumber-chef test lab."}
242
+ @stdout.print("done.\n")
89
243
  end
244
+
245
+ ################################################################################
246
+
247
+ def chef_first_run
248
+ @stdout.print("Performing chef-client run to setup and configure the cucumber-chef test lab...")
249
+ Cucumber::Chef.spinner do
250
+ command = "/usr/bin/chef-client -j /etc/chef/first-boot.json -l debug"
251
+ command = "sudo #{command}"
252
+ @ssh.exec(command, :silence => true)
253
+ end
254
+ @stdout.print("done.\n")
255
+ end
256
+
257
+ ################################################################################
258
+
259
+ def wait_for_chef_server
260
+ @stdout.print("Waiting for Chef-Server...")
261
+ Cucumber::Chef.spinner do
262
+ Cucumber::Chef::TCPSocket.new(@server.public_ip_address, 4000, "GET").wait
263
+ end
264
+ @stdout.puts("done.\n")
265
+
266
+ @stdout.print("Waiting for Chef-WebUI...")
267
+ Cucumber::Chef.spinner do
268
+ Cucumber::Chef::TCPSocket.new(@server.public_ip_address, 4040, "GET").wait
269
+ end
270
+ @stdout.puts("done.\n")
271
+ end
272
+
273
+ ################################################################################
274
+
275
+ def reboot_test_lab
276
+ @stdout.print("Rebooting test lab; please wait...")
277
+ Cucumber::Chef.spinner do
278
+ command = "sudo reboot"
279
+ @ssh.exec(command, :silence => true)
280
+ sleep(10)
281
+ end
282
+ @stdout.print("done.\n")
283
+
284
+ @stdout.print("Waiting for SSHD...")
285
+ Cucumber::Chef.spinner do
286
+ Cucumber::Chef::TCPSocket.new(@server.public_ip_address, 22).wait
287
+ end
288
+ @stdout.puts("done.\n")
289
+
290
+ wait_for_chef_server
291
+ end
292
+
90
293
  end
294
+
91
295
  end
92
296
  end
297
+
298
+ ################################################################################
@@ -0,0 +1,190 @@
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 SSHError < Error; end
26
+
27
+ class SSH
28
+ attr_accessor :stdout, :stderr, :stdin, :config
29
+
30
+ ################################################################################
31
+
32
+ def initialize(stdout=STDOUT, stderr=STDERR, stdin=STDIN)
33
+ @stdout, @stderr, @stdin = stdout, stderr, stdin
34
+ @stdout.sync = true if @stdout.respond_to?(:sync=)
35
+
36
+ @config = Hash.new(nil)
37
+ end
38
+
39
+ ################################################################################
40
+
41
+ def console
42
+ $logger.debug { "config(#{@config.inspect})" }
43
+
44
+ command = [ "ssh" ]
45
+ command << [ "-q" ]
46
+ command << [ "-o", "UserKnownHostsFile=/dev/null" ]
47
+ command << [ "-o", "StrictHostKeyChecking=no" ]
48
+ command << [ "-o", "KeepAlive=yes" ]
49
+ command << [ "-o", "ServerAliveInterval=60" ]
50
+ command << [ "-i", @config[:identity_file] ] if @config[:identity_file]
51
+ command << [ "-o", "ProxyCommand=\"#{proxy_command}\"" ] if @config[:proxy]
52
+ command << "#{@config[:ssh_user]}@#{@config[:host]}"
53
+ command = command.flatten.compact.join(" ")
54
+ $logger.info { "command(#{command})" }
55
+ Kernel.exec(command)
56
+ end
57
+
58
+ ################################################################################
59
+
60
+ def exec(command, options={})
61
+ @ssh ||= Net::SSH.start(@config[:host], @config[:ssh_user], ssh_options)
62
+
63
+ options = { :silence => false }.merge(options)
64
+ silence = options[:silence]
65
+ output = ""
66
+
67
+ $logger.debug { "config(#{@config.inspect})" }
68
+ $logger.debug { "options(#{options.inspect})" }
69
+ $logger.info { "command(#{command})" }
70
+ channel = @ssh.open_channel do |chan|
71
+ $logger.debug { "channel opened" }
72
+ chan.exec(command) do |ch, success|
73
+ raise SSHError, "Could not execute '#{command}'." unless success
74
+
75
+ ch.on_data do |c, data|
76
+ output += data
77
+ $logger.debug { data.chomp.strip }
78
+ @stdout.print(data) if !silence
79
+ end
80
+
81
+ ch.on_extended_data do |c, type, data|
82
+ output += data
83
+ $logger.debug { data.chomp.strip }
84
+ @stderr.print(data) if !silence
85
+ end
86
+
87
+ end
88
+ end
89
+ channel.wait
90
+ $logger.debug { "channel closed" }
91
+
92
+ output
93
+ end
94
+
95
+ ################################################################################
96
+
97
+ def upload(local, remote)
98
+ @sftp ||= Net::SFTP.start(@config[:host], @config[:ssh_user], ssh_options)
99
+
100
+ $logger.debug { "config(#{@config.inspect})" }
101
+ $logger.info { "parameters(#{local},#{remote})" }
102
+ @sftp.upload!(local.to_s, remote.to_s) do |event, uploader, *args|
103
+ case event
104
+ when :open
105
+ $logger.info { "upload(#{args[0].local} -> #{args[0].remote})" }
106
+ when :close
107
+ $logger.debug { "close(#{args[0].remote})" }
108
+ when :mkdir
109
+ $logger.debug { "mkdir(#{args[0]})" }
110
+ when :put
111
+ $logger.debug { "put(#{args[0].remote}, size #{args[2].size} bytes, offset #{args[1]})" }
112
+ when :finish
113
+ $logger.info { "finish" }
114
+ end
115
+ end
116
+ end
117
+
118
+ ################################################################################
119
+
120
+ def download(remote, local)
121
+ @sftp ||= Net::SFTP.start(@config[:host], @config[:ssh_user], ssh_options)
122
+
123
+ $logger.debug { "config(#{@config.inspect})" }
124
+ $logger.info { "parameters(#{remote},#{local})" }
125
+ @sftp.download!(remote.to_s, local.to_s) do |event, downloader, *args|
126
+ case event
127
+ when :open
128
+ $logger.info { "download(#{args[0].remote} -> #{args[0].local})" }
129
+ when :close
130
+ $logger.debug { "close(#{args[0].local})" }
131
+ when :mkdir
132
+ $logger.debug { "mkdir(#{args[0]})" }
133
+ when :get
134
+ $logger.debug { "get(#{args[0].remote}, size #{args[2].size} bytes, offset #{args[1]})" }
135
+ when :finish
136
+ $logger.info { "finish" }
137
+ end
138
+ end
139
+ end
140
+
141
+
142
+ ################################################################################
143
+ private
144
+ ################################################################################
145
+
146
+ def proxy_command
147
+ $logger.debug { "config(#{@config.inspect})" }
148
+
149
+ if !@config[:identity_file]
150
+ message = "You must specify an identity file in order to SSH proxy."
151
+ $logger.fatal { message }
152
+ raise SSHError, message
153
+ end
154
+
155
+ command = ["ssh"]
156
+ command << [ "-q" ]
157
+ command << [ "-o", "UserKnownHostsFile=/dev/null" ]
158
+ command << [ "-o", "StrictHostKeyChecking=no" ]
159
+ command << [ "-o", "KeepAlive=yes" ]
160
+ command << [ "-o", "ServerAliveInterval=60" ]
161
+ command << [ "-i", @config[:proxy_identity_file] ] if @config[:proxy_identity_file]
162
+ command << "#{@config[:proxy_ssh_user]}@#{@config[:proxy_host]}"
163
+ command << "nc %h %p"
164
+ command = command.flatten.compact.join(" ")
165
+ $logger.debug { "command(#{command})" }
166
+ command
167
+ end
168
+
169
+ ################################################################################
170
+
171
+ def ssh_options
172
+ $logger.debug { "config(#{@config.inspect})" }
173
+ options = {}
174
+ options.merge!(:password => @config[:ssh_password]) if @config[:ssh_password]
175
+ options.merge!(:keys => @config[:identity_file]) if @config[:identity_file]
176
+ options.merge!(:timeout => @config[:timeout]) if @config[:timeout]
177
+ options.merge!(:user_known_hosts_file => '/dev/null') if !@config[:host_key_verify]
178
+ options.merge!(:proxy => Net::SSH::Proxy::Command.new(proxy_command)) if @config[:proxy]
179
+ $logger.debug { "options(#{options.inspect})" }
180
+ options
181
+ end
182
+
183
+ ################################################################################
184
+
185
+ end
186
+
187
+ end
188
+ end
189
+
190
+ ################################################################################
@@ -0,0 +1,32 @@
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
+ And /^the following databags have been updated:$/ do |table|
23
+ table.hashes.each do |entry|
24
+ load_databag(entry['databag'], entry['databag_path'])
25
+ end
26
+ end
27
+
28
+ And /^the following roles have been updated:$/ do |table|
29
+ table.hashes.each do |entry|
30
+ load_role(entry['role'], entry['role_path'])
31
+ end
32
+ end
@@ -0,0 +1,29 @@
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
+ When /^I enable the running of MiniTest suites for "(.*?)"$/ do |name|
23
+ enable_minitest(name)
24
+ end
25
+
26
+ Then /^the tests should run and pass on "(.*?)"$/ do |name|
27
+ results = run_minitests(name)
28
+ results.last.scan(/assertions.*(\d).*failures.*(\d).*errors.*(\d).*skips/).flatten.map { |v| v.to_i }.should == [0,0,0]
29
+ end