test-kitchen 1.6.0 → 1.7.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.
Files changed (180) hide show
  1. checksums.yaml +4 -4
  2. data/.cane +8 -7
  3. data/.github/ISSUE_TEMPLATE.md +56 -0
  4. data/.gitignore +28 -27
  5. data/.kitchen.ci.yml +23 -0
  6. data/.kitchen.proxy.yml +27 -0
  7. data/.rubocop.yml +3 -3
  8. data/.travis.yml +70 -53
  9. data/.yardopts +3 -3
  10. data/Berksfile +3 -0
  11. data/CHANGELOG.md +1083 -1051
  12. data/CONTRIBUTING.md +14 -14
  13. data/Gemfile +19 -14
  14. data/Gemfile.proxy_tests +4 -5
  15. data/Guardfile +42 -42
  16. data/LICENSE +15 -15
  17. data/MAINTAINERS.md +23 -24
  18. data/README.md +135 -135
  19. data/Rakefile +61 -76
  20. data/appveyor.yml +44 -34
  21. data/features/kitchen_action_commands.feature +164 -164
  22. data/features/kitchen_command.feature +16 -16
  23. data/features/kitchen_console_command.feature +34 -34
  24. data/features/kitchen_defaults.feature +38 -38
  25. data/features/kitchen_diagnose_command.feature +96 -96
  26. data/features/kitchen_driver_create_command.feature +64 -64
  27. data/features/kitchen_driver_discover_command.feature +25 -25
  28. data/features/kitchen_help_command.feature +16 -16
  29. data/features/kitchen_init_command.feature +274 -274
  30. data/features/kitchen_list_command.feature +104 -104
  31. data/features/kitchen_login_command.feature +62 -62
  32. data/features/kitchen_sink_command.feature +30 -30
  33. data/features/kitchen_test_command.feature +88 -88
  34. data/features/step_definitions/gem_steps.rb +36 -36
  35. data/features/step_definitions/git_steps.rb +5 -5
  36. data/features/step_definitions/output_steps.rb +5 -5
  37. data/features/support/env.rb +75 -75
  38. data/lib/kitchen.rb +150 -150
  39. data/lib/kitchen/base64_stream.rb +55 -55
  40. data/lib/kitchen/cli.rb +419 -419
  41. data/lib/kitchen/collection.rb +55 -55
  42. data/lib/kitchen/color.rb +65 -65
  43. data/lib/kitchen/command.rb +185 -185
  44. data/lib/kitchen/command/action.rb +45 -45
  45. data/lib/kitchen/command/console.rb +58 -58
  46. data/lib/kitchen/command/diagnose.rb +92 -92
  47. data/lib/kitchen/command/driver_discover.rb +105 -105
  48. data/lib/kitchen/command/exec.rb +41 -41
  49. data/lib/kitchen/command/list.rb +119 -119
  50. data/lib/kitchen/command/login.rb +43 -43
  51. data/lib/kitchen/command/sink.rb +54 -54
  52. data/lib/kitchen/command/test.rb +51 -51
  53. data/lib/kitchen/config.rb +322 -322
  54. data/lib/kitchen/configurable.rb +529 -529
  55. data/lib/kitchen/data_munger.rb +959 -960
  56. data/lib/kitchen/diagnostic.rb +141 -141
  57. data/lib/kitchen/driver.rb +56 -56
  58. data/lib/kitchen/driver/base.rb +134 -134
  59. data/lib/kitchen/driver/dummy.rb +108 -108
  60. data/lib/kitchen/driver/proxy.rb +72 -72
  61. data/lib/kitchen/driver/ssh_base.rb +357 -357
  62. data/lib/kitchen/errors.rb +229 -229
  63. data/lib/kitchen/generator/driver_create.rb +177 -177
  64. data/lib/kitchen/generator/init.rb +296 -296
  65. data/lib/kitchen/instance.rb +662 -662
  66. data/lib/kitchen/lazy_hash.rb +142 -142
  67. data/lib/kitchen/loader/yaml.rb +349 -349
  68. data/lib/kitchen/logger.rb +423 -423
  69. data/lib/kitchen/logging.rb +56 -56
  70. data/lib/kitchen/login_command.rb +52 -52
  71. data/lib/kitchen/metadata_chopper.rb +52 -52
  72. data/lib/kitchen/platform.rb +67 -67
  73. data/lib/kitchen/provisioner.rb +54 -54
  74. data/lib/kitchen/provisioner/base.rb +236 -236
  75. data/lib/kitchen/provisioner/chef/berkshelf.rb +114 -114
  76. data/lib/kitchen/provisioner/chef/common_sandbox.rb +322 -322
  77. data/lib/kitchen/provisioner/chef/librarian.rb +112 -112
  78. data/lib/kitchen/provisioner/chef_apply.rb +124 -125
  79. data/lib/kitchen/provisioner/chef_base.rb +341 -294
  80. data/lib/kitchen/provisioner/chef_solo.rb +88 -89
  81. data/lib/kitchen/provisioner/chef_zero.rb +245 -245
  82. data/lib/kitchen/provisioner/dummy.rb +79 -79
  83. data/lib/kitchen/provisioner/shell.rb +138 -138
  84. data/lib/kitchen/rake_tasks.rb +63 -63
  85. data/lib/kitchen/shell_out.rb +93 -93
  86. data/lib/kitchen/ssh.rb +276 -276
  87. data/lib/kitchen/state_file.rb +120 -120
  88. data/lib/kitchen/suite.rb +51 -51
  89. data/lib/kitchen/thor_tasks.rb +66 -66
  90. data/lib/kitchen/transport.rb +54 -54
  91. data/lib/kitchen/transport/base.rb +176 -176
  92. data/lib/kitchen/transport/dummy.rb +79 -79
  93. data/lib/kitchen/transport/ssh.rb +364 -364
  94. data/lib/kitchen/transport/winrm.rb +486 -486
  95. data/lib/kitchen/util.rb +147 -147
  96. data/lib/kitchen/verifier.rb +55 -55
  97. data/lib/kitchen/verifier/base.rb +235 -235
  98. data/lib/kitchen/verifier/busser.rb +277 -277
  99. data/lib/kitchen/verifier/dummy.rb +79 -79
  100. data/lib/kitchen/verifier/shell.rb +101 -101
  101. data/lib/kitchen/version.rb +21 -21
  102. data/lib/vendor/hash_recursive_merge.rb +82 -82
  103. data/spec/kitchen/base64_stream_spec.rb +77 -77
  104. data/spec/kitchen/cli_spec.rb +56 -56
  105. data/spec/kitchen/collection_spec.rb +80 -80
  106. data/spec/kitchen/color_spec.rb +54 -54
  107. data/spec/kitchen/config_spec.rb +408 -408
  108. data/spec/kitchen/configurable_spec.rb +1095 -1062
  109. data/spec/kitchen/data_munger_spec.rb +2694 -2383
  110. data/spec/kitchen/diagnostic_spec.rb +129 -129
  111. data/spec/kitchen/driver/base_spec.rb +121 -121
  112. data/spec/kitchen/driver/dummy_spec.rb +199 -199
  113. data/spec/kitchen/driver/proxy_spec.rb +138 -138
  114. data/spec/kitchen/driver/ssh_base_spec.rb +1115 -1115
  115. data/spec/kitchen/driver_spec.rb +112 -112
  116. data/spec/kitchen/errors_spec.rb +309 -309
  117. data/spec/kitchen/instance_spec.rb +1419 -1419
  118. data/spec/kitchen/lazy_hash_spec.rb +117 -117
  119. data/spec/kitchen/loader/yaml_spec.rb +774 -774
  120. data/spec/kitchen/logger_spec.rb +429 -429
  121. data/spec/kitchen/logging_spec.rb +59 -59
  122. data/spec/kitchen/login_command_spec.rb +68 -68
  123. data/spec/kitchen/metadata_chopper_spec.rb +82 -82
  124. data/spec/kitchen/platform_spec.rb +89 -89
  125. data/spec/kitchen/provisioner/base_spec.rb +386 -386
  126. data/spec/kitchen/provisioner/chef_apply_spec.rb +136 -136
  127. data/spec/kitchen/provisioner/chef_base_spec.rb +1161 -1067
  128. data/spec/kitchen/provisioner/chef_solo_spec.rb +557 -557
  129. data/spec/kitchen/provisioner/chef_zero_spec.rb +1001 -1001
  130. data/spec/kitchen/provisioner/dummy_spec.rb +99 -99
  131. data/spec/kitchen/provisioner/shell_spec.rb +566 -566
  132. data/spec/kitchen/provisioner_spec.rb +107 -107
  133. data/spec/kitchen/shell_out_spec.rb +150 -150
  134. data/spec/kitchen/ssh_spec.rb +693 -693
  135. data/spec/kitchen/state_file_spec.rb +129 -129
  136. data/spec/kitchen/suite_spec.rb +62 -62
  137. data/spec/kitchen/transport/base_spec.rb +89 -89
  138. data/spec/kitchen/transport/ssh_spec.rb +1255 -1255
  139. data/spec/kitchen/transport/winrm_spec.rb +1143 -1143
  140. data/spec/kitchen/transport_spec.rb +112 -112
  141. data/spec/kitchen/util_spec.rb +165 -165
  142. data/spec/kitchen/verifier/base_spec.rb +362 -362
  143. data/spec/kitchen/verifier/busser_spec.rb +610 -610
  144. data/spec/kitchen/verifier/dummy_spec.rb +99 -99
  145. data/spec/kitchen/verifier/shell_spec.rb +160 -158
  146. data/spec/kitchen/verifier_spec.rb +120 -120
  147. data/spec/kitchen_spec.rb +114 -114
  148. data/spec/spec_helper.rb +85 -85
  149. data/spec/support/powershell_max_size_spec.rb +40 -40
  150. data/support/busser_install_command.ps1 +14 -14
  151. data/support/busser_install_command.sh +14 -14
  152. data/support/chef-client-zero.rb +77 -77
  153. data/support/chef_base_init_command.ps1 +18 -18
  154. data/support/chef_base_init_command.sh +2 -2
  155. data/support/chef_base_install_command.ps1 +85 -85
  156. data/support/chef_base_install_command.sh +229 -229
  157. data/support/chef_zero_prepare_command_legacy.ps1 +9 -9
  158. data/support/chef_zero_prepare_command_legacy.sh +10 -10
  159. data/support/download_helpers.sh +109 -109
  160. data/support/dummy-validation.pem +27 -27
  161. data/templates/driver/CHANGELOG.md.erb +3 -3
  162. data/templates/driver/Gemfile.erb +3 -3
  163. data/templates/driver/README.md.erb +64 -64
  164. data/templates/driver/Rakefile.erb +21 -21
  165. data/templates/driver/driver.rb.erb +23 -23
  166. data/templates/driver/gemspec.erb +29 -29
  167. data/templates/driver/gitignore.erb +17 -17
  168. data/templates/driver/license_apachev2.erb +15 -15
  169. data/templates/driver/license_lgplv3.erb +16 -16
  170. data/templates/driver/license_mit.erb +22 -22
  171. data/templates/driver/license_reserved.erb +5 -5
  172. data/templates/driver/tailor.erb +4 -4
  173. data/templates/driver/travis.yml.erb +11 -11
  174. data/templates/driver/version.rb.erb +12 -12
  175. data/templates/init/chefignore.erb +1 -1
  176. data/templates/init/kitchen.yml.erb +18 -18
  177. data/test-kitchen.gemspec +62 -62
  178. data/test/integration/default/default_spec.rb +3 -0
  179. data/testing_windows.md +37 -37
  180. metadata +23 -11
@@ -1,1115 +1,1115 @@
1
- # -*- encoding: utf-8 -*-
2
- #
3
- # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
- #
5
- # Copyright (C) 2014, Fletcher Nichol
6
- #
7
- # Licensed under the Apache License, Version 2.0 (the "License");
8
- # you may not use this file except in compliance with the License.
9
- # You may obtain a copy of the License at
10
- #
11
- # http://www.apache.org/licenses/LICENSE-2.0
12
- #
13
- # Unless required by applicable law or agreed to in writing, software
14
- # distributed under the License is distributed on an "AS IS" BASIS,
15
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
- # See the License for the specific language governing permissions and
17
- # limitations under the License.
18
-
19
- require_relative "../../spec_helper"
20
-
21
- require "kitchen"
22
- require "kitchen/transport/ssh"
23
- require "kitchen/verifier/busser"
24
-
25
- module Kitchen
26
-
27
- module Driver
28
-
29
- class BackCompat < Kitchen::Driver::SSHBase
30
-
31
- def use_run_remote(state, command)
32
- connection = Kitchen::SSH.new(*build_ssh_args(state))
33
- run_remote(command, connection)
34
- end
35
-
36
- def use_transfer_path(state, locals, remote)
37
- connection = Kitchen::SSH.new(*build_ssh_args(state))
38
- transfer_path(locals, remote, connection)
39
- end
40
- end
41
-
42
- class SpeedyCompat < Kitchen::Driver::SSHBase
43
- end
44
-
45
- class DodgyCompat < Kitchen::Driver::SSHBase
46
-
47
- no_parallel_for :converge
48
- end
49
-
50
- class SlowCompat < Kitchen::Driver::SSHBase
51
-
52
- no_parallel_for :create, :destroy
53
- no_parallel_for :verify
54
- end
55
- end
56
- end
57
-
58
- describe Kitchen::Driver::SSHBase do
59
-
60
- let(:logged_output) { StringIO.new }
61
- let(:logger) { Logger.new(logged_output) }
62
- let(:config) { Hash.new }
63
- let(:state) { Hash.new }
64
-
65
- let(:verifier) do
66
- v = mock("busser")
67
- v.responds_like_instance_of(Kitchen::Verifier::Busser)
68
- v.stubs(:install_command).returns("install")
69
- v.stubs(:init_command).returns("init")
70
- v.stubs(:prepare_command).returns("prepare")
71
- v.stubs(:run_command).returns("run")
72
- v.stubs(:create_sandbox).returns(true)
73
- v.stubs(:cleanup_sandbox).returns(true)
74
- v.stubs(:sandbox_path).returns("/tmp/sandbox")
75
- v.stubs(:[]).with(:root_path).returns("/tmp/verifier")
76
- v
77
- end
78
-
79
- let(:provisioner) do
80
- stub(
81
- :install_command => "install",
82
- :init_command => "init",
83
- :prepare_command => "prepare",
84
- :run_command => "run",
85
- :create_sandbox => true,
86
- :cleanup_sandbox => true,
87
- :sandbox_path => "/tmp/sandbox"
88
- )
89
- end
90
-
91
- let(:transport) do
92
- t = mock("transport")
93
- t.responds_like_instance_of(Kitchen::Transport::Base)
94
- t
95
- end
96
-
97
- let(:instance) do
98
- stub(
99
- :name => "coolbeans",
100
- :logger => logger,
101
- :verifier => verifier,
102
- :provisioner => provisioner,
103
- :transport => transport,
104
- :to_str => "instance"
105
- )
106
- end
107
-
108
- let(:driver) do
109
- Kitchen::Driver::SSHBase.new(config).finalize_config!(instance)
110
- end
111
-
112
- it "plugin_version is not set" do
113
- driver.diagnose_plugin[:version].must_equal nil
114
- end
115
-
116
- describe "configuration" do
117
-
118
- it ":sudo defaults to true" do
119
- driver[:sudo].must_equal true
120
- end
121
-
122
- it ":port defaults to 22" do
123
- driver[:port].must_equal 22
124
- end
125
- end
126
-
127
- it "#create raises a ClientError" do
128
- proc { driver.create(state) }.must_raise Kitchen::ClientError
129
- end
130
-
131
- it "#destroy raises a ClientError" do
132
- proc { driver.destroy(state) }.must_raise Kitchen::ClientError
133
- end
134
-
135
- # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
136
- def self.constructs_an_ssh_connection
137
- describe "constructs an SSH connection" do
138
-
139
- it "with hostname set from state" do
140
- transport.expects(:connection).with { |state|
141
- state[:hostname].must_equal "fizzy"
142
- }.returns(stub(:login_command => stub))
143
-
144
- cmd
145
- end
146
-
147
- it "with username set from state" do
148
- transport.expects(:connection).with { |state|
149
- state[:username].must_equal "bork"
150
- }.returns(stub(:login_command => stub))
151
-
152
- cmd
153
- end
154
-
155
- it "with :ssh_key option set from config" do
156
- config[:ssh_key] = "wicked"
157
-
158
- transport.expects(:connection).with { |state|
159
- state[:ssh_key].must_equal "wicked"
160
- }.returns(stub(:login_command => stub))
161
-
162
- cmd
163
- end
164
-
165
- it "with :ssh_key option set from state" do
166
- state[:ssh_key] = "wicked"
167
-
168
- transport.expects(:connection).with { |state|
169
- state[:ssh_key].must_equal "wicked"
170
- }.returns(stub(:login_command => stub))
171
-
172
- cmd
173
- end
174
-
175
- it "with :password option set to falsey by default" do
176
- transport.expects(:connection).with { |state|
177
- state[:password].nil?
178
- }.returns(stub(:login_command => stub))
179
-
180
- cmd
181
- end
182
-
183
- it "with :password option set if given in config" do
184
- config[:password] = "psst"
185
-
186
- transport.expects(:connection).with { |state|
187
- state[:password].must_equal "psst"
188
- }.returns(stub(:login_command => stub))
189
-
190
- cmd
191
- end
192
-
193
- it "with :password option set if given in state" do
194
- state[:password] = "psst"
195
-
196
- transport.expects(:connection).with { |state|
197
- state[:password].must_equal "psst"
198
- }.returns(stub(:login_command => stub))
199
-
200
- cmd
201
- end
202
-
203
- it "with :forward_agent option set to falsey by default" do
204
- transport.expects(:connection).with { |state|
205
- state[:forward_agent].nil?
206
- }.returns(stub(:login_command => stub))
207
-
208
- cmd
209
- end
210
-
211
- it "with :forward_agent option set if given in config" do
212
- config[:forward_agent] = "yeah?"
213
-
214
- transport.expects(:connection).with { |state|
215
- state[:forward_agent].must_equal "yeah?"
216
- }.returns(stub(:login_command => stub))
217
-
218
- cmd
219
- end
220
-
221
- it "with :forward_agent option set if given in state" do
222
- state[:forward_agent] = "yeah?"
223
-
224
- transport.expects(:connection).with { |state|
225
- state[:forward_agent].must_equal "yeah?"
226
- }.returns(stub(:login_command => stub))
227
-
228
- cmd
229
- end
230
-
231
- it "with :port option set to 22 by default" do
232
- transport.expects(:connection).with { |state|
233
- state[:port].must_equal 22
234
- }.returns(stub(:login_command => stub))
235
-
236
- cmd
237
- end
238
-
239
- it "with :port option set if customized in config" do
240
- config[:port] = 1234
241
-
242
- transport.expects(:connection).with { |state|
243
- state[:port].must_equal 1234
244
- }.returns(stub(:login_command => stub))
245
-
246
- cmd
247
- end
248
-
249
- it "with :port option set if customized in state" do
250
- state[:port] = 9999
251
-
252
- transport.expects(:connection).with { |state|
253
- state[:port].must_equal 9999
254
- }.returns(stub(:login_command => stub))
255
-
256
- cmd
257
- end
258
- end
259
- end
260
- # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
261
-
262
- describe "#login_command" do
263
-
264
- let(:cmd) { driver.login_command(state) }
265
-
266
- before do
267
- state[:hostname] = "fizzy"
268
- state[:username] = "bork"
269
- end
270
-
271
- it "returns a LoginCommand" do
272
- transport.stubs(:connection).returns(stub(:login_command => "command"))
273
-
274
- cmd.must_equal "command"
275
- end
276
-
277
- constructs_an_ssh_connection
278
- end
279
-
280
- describe "#converge" do
281
-
282
- let(:cmd) { driver.converge(state) }
283
- let(:connection) { stub(:execute => true, :upload => true) }
284
-
285
- before do
286
- state[:hostname] = "fizzy"
287
- state[:username] = "bork"
288
- provisioner.stubs(:[]).with(:root_path).returns("/rooty")
289
- FakeFS.activate!
290
- FileUtils.mkdir_p("/tmp")
291
- @original_env = ENV.to_hash
292
- ENV.replace("http_proxy" => nil, "HTTP_PROXY" => nil,
293
- "https_proxy" => nil, "HTTPS_PROXY" => nil,
294
- "ftp_proxy" => nil, "FTP_PROXY" => nil,
295
- "no_proxy" => nil, "NO_PROXY" => nil)
296
- end
297
-
298
- after do
299
- FakeFS.deactivate!
300
- FakeFS::FileSystem.clear
301
- ENV.clear
302
- ENV.replace(@original_env)
303
- end
304
-
305
- constructs_an_ssh_connection
306
-
307
- it "creates the sandbox" do
308
- transport.stubs(:connection).yields(connection)
309
- provisioner.expects(:create_sandbox)
310
-
311
- cmd
312
- end
313
-
314
- it "ensures that the sandbox is cleaned up" do
315
- transport.stubs(:connection).raises
316
- provisioner.expects(:cleanup_sandbox)
317
-
318
- begin
319
- cmd
320
- rescue # rubocop:disable Lint/HandleExceptions
321
- end
322
- end
323
-
324
- it "invokes the provisioner commands over ssh" do
325
- transport.stubs(:connection).yields(connection)
326
- order = sequence("order")
327
- connection.expects(:execute).with("install").in_sequence(order)
328
- connection.expects(:execute).with("init").in_sequence(order)
329
- connection.expects(:execute).with("prepare").in_sequence(order)
330
- connection.expects(:execute).with("run").in_sequence(order)
331
-
332
- cmd
333
- end
334
-
335
- it "invokes the #install_command with :http_proxy set in config" do
336
- config[:http_proxy] = "http://proxy"
337
- transport.stubs(:connection).yields(connection)
338
- connection.expects(:execute).with("env http_proxy=http://proxy install")
339
-
340
- cmd
341
- end
342
-
343
- it "invokes the #install_command with ENV[\"http_proxy\"] set" do
344
- ENV["http_proxy"] = "http://proxy"
345
- transport.stubs(:connection).yields(connection)
346
- if running_tests_on_windows?
347
- connection.expects(:execute).
348
- with("env http_proxy=http://proxy HTTP_PROXY=http://proxy install")
349
- else
350
- connection.expects(:execute).with("env http_proxy=http://proxy install")
351
- end
352
- cmd
353
- end
354
-
355
- it "invokes the #install_command with ENV[\"http_proxy\"] and ENV[\"no_proxy\"] set" do
356
- ENV["http_proxy"] = "http://proxy"
357
- ENV["no_proxy"] = "http://no"
358
- transport.stubs(:connection).yields(connection)
359
- if running_tests_on_windows?
360
- connection.expects(:execute).
361
- with("env http_proxy=http://proxy HTTP_PROXY=http://proxy " \
362
- "no_proxy=http://no NO_PROXY=http://no install")
363
- else
364
- connection.expects(:execute).with("env http_proxy=http://proxy " \
365
- "no_proxy=http://no install")
366
- end
367
- cmd
368
- end
369
-
370
- it "invokes the #install_command with :https_proxy set in config" do
371
- config[:https_proxy] = "https://proxy"
372
- transport.stubs(:connection).yields(connection)
373
- connection.expects(:execute).with("env https_proxy=https://proxy install")
374
-
375
- cmd
376
- end
377
-
378
- it "invokes the #install_command with ENV[\"https_proxy\"] set" do
379
- ENV["https_proxy"] = "https://proxy"
380
- transport.stubs(:connection).yields(connection)
381
- if running_tests_on_windows?
382
- connection.expects(:execute).
383
- with("env https_proxy=https://proxy HTTPS_PROXY=https://proxy install")
384
- else
385
- connection.expects(:execute).with("env https_proxy=https://proxy install")
386
- end
387
- cmd
388
- end
389
-
390
- it "invokes the #install_command with ENV[\"https_proxy\"] and ENV[\"no_proxy\"] set" do
391
- ENV["https_proxy"] = "https://proxy"
392
- ENV["no_proxy"] = "https://no"
393
- transport.stubs(:connection).yields(connection)
394
- if running_tests_on_windows?
395
- connection.expects(:execute).
396
- with("env https_proxy=https://proxy HTTPS_PROXY=https://proxy " \
397
- "no_proxy=https://no NO_PROXY=https://no install")
398
- else
399
- connection.expects(:execute).with("env https_proxy=https://proxy " \
400
- "no_proxy=https://no install")
401
- end
402
- cmd
403
- end
404
-
405
- it "invokes the #install_command with :ftp_proxy set in config" do
406
- config[:ftp_proxy] = "ftp://proxy"
407
- transport.stubs(:connection).yields(connection)
408
- connection.expects(:execute).with("env ftp_proxy=ftp://proxy install")
409
-
410
- cmd
411
- end
412
-
413
- it "invokes the #install_command with ENV[\"ftp_proxy\"] set" do
414
- ENV["ftp_proxy"] = "ftp://proxy"
415
- transport.stubs(:connection).yields(connection)
416
- if running_tests_on_windows?
417
- connection.expects(:execute).
418
- with("env ftp_proxy=ftp://proxy FTP_PROXY=ftp://proxy install")
419
- else
420
- connection.expects(:execute).with("env ftp_proxy=ftp://proxy install")
421
- end
422
- cmd
423
- end
424
-
425
- it "invokes the #install_command with ENV[\"ftp_proxy\"] and ENV[\"no_proxy\"] set" do
426
- ENV["ftp_proxy"] = "ftp://proxy"
427
- ENV["no_proxy"] = "http://no"
428
- transport.stubs(:connection).yields(connection)
429
- if running_tests_on_windows?
430
- connection.expects(:execute).
431
- with("env ftp_proxy=ftp://proxy FTP_PROXY=http://proxy " \
432
- "no_proxy=http://no NO_PROXY=http://no install")
433
- else
434
- connection.expects(:execute).with("env ftp_proxy=ftp://proxy " \
435
- "no_proxy=http://no install")
436
- end
437
- cmd
438
- end
439
-
440
- it "invokes the #install_command with :http_proxy & :https_proxy & :ftp_proxy set" do
441
- config[:http_proxy] = "http://proxy"
442
- config[:https_proxy] = "https://proxy"
443
- config[:ftp_proxy] = "ftp://proxy"
444
- transport.stubs(:connection).yields(connection)
445
- connection.expects(:execute).with(
446
- "env http_proxy=http://proxy https_proxy=https://proxy ftp_proxy=ftp://proxy install")
447
-
448
- cmd
449
- end
450
-
451
- describe "transferring files" do
452
-
453
- before do
454
- transport.stubs(:connection).yields(connection)
455
- connection.stubs(:upload)
456
- FileUtils.mkdir_p "/tmp/sandbox/stuff"
457
- end
458
-
459
- it "uploads files" do
460
- connection.expects(:upload).with(["/tmp/sandbox/stuff"], "/rooty")
461
-
462
- cmd
463
- end
464
-
465
- it "logs to info" do
466
- cmd
467
-
468
- logged_output.string.
469
- must_match(/INFO -- : Transferring files to instance$/)
470
- end
471
-
472
- it "logs to debug" do
473
- cmd
474
-
475
- logged_output.string.must_match(/DEBUG -- : Transfer complete$/)
476
- end
477
-
478
- it "raises an ActionFailed on transfer when SshFailed is raised" do
479
- connection.stubs(:upload).raises(Kitchen::Transport::SshFailed.new("dang"))
480
-
481
- proc { cmd }.must_raise Kitchen::ActionFailed
482
- end
483
- end
484
-
485
- it "raises an ActionFailed on execute when SshFailed is raised" do
486
- transport.stubs(:connection).yields(connection)
487
- connection.stubs(:execute).raises(Kitchen::Transport::SshFailed.new("dang"))
488
-
489
- proc { cmd }.must_raise Kitchen::ActionFailed
490
- end
491
- end
492
-
493
- describe "#setup" do
494
-
495
- let(:cmd) { driver.setup(state) }
496
- let(:connection) { mock }
497
-
498
- before do
499
- state[:hostname] = "fizzy"
500
- state[:username] = "bork"
501
- end
502
-
503
- constructs_an_ssh_connection
504
-
505
- it "invokes the Verifier#install_command over ssh" do
506
- transport.stubs(:connection).yields(connection)
507
- connection.expects(:execute).with("install")
508
-
509
- cmd
510
- end
511
-
512
- it "invokes the Verifier#install_command with :http_proxy set in config" do
513
- config[:http_proxy] = "http://proxy"
514
- transport.stubs(:connection).yields(connection)
515
- connection.expects(:execute).with("env http_proxy=http://proxy install")
516
-
517
- cmd
518
- end
519
-
520
- it "invokes the Verifier#install_command with :https_proxy set in config" do
521
- config[:https_proxy] = "https://proxy"
522
- transport.stubs(:connection).yields(connection)
523
- connection.expects(:execute).with("env https_proxy=https://proxy install")
524
-
525
- cmd
526
- end
527
-
528
- it "invokes the Verifier#install_command with :ftp_proxy set in config" do
529
- config[:ftp_proxy] = "ftp://proxy"
530
- transport.stubs(:connection).yields(connection)
531
- connection.expects(:execute).with("env ftp_proxy=ftp://proxy install")
532
-
533
- cmd
534
- end
535
-
536
- it "invokes the Verifier#install_command with :http_proxy & :https_proxy & :ftp_proxy set" do
537
- config[:http_proxy] = "http://proxy"
538
- config[:https_proxy] = "https://proxy"
539
- config[:ftp_proxy] = "ftp://proxy"
540
- transport.stubs(:connection).yields(connection)
541
- connection.expects(:execute).with(
542
- "env http_proxy=http://proxy https_proxy=https://proxy ftp_proxy=ftp://proxy install")
543
-
544
- cmd
545
- end
546
-
547
- it "raises an ActionFailed when SshFailed is raised" do
548
- transport.stubs(:connection).yields(connection)
549
- connection.stubs(:execute).raises(Kitchen::Transport::SshFailed.new("dang"))
550
-
551
- proc { cmd }.must_raise Kitchen::ActionFailed
552
- end
553
- end
554
-
555
- describe "#verify" do
556
-
557
- let(:cmd) { driver.verify(state) }
558
- let(:connection) { stub(:execute => true, :upload => true) }
559
-
560
- before do
561
- state[:hostname] = "fizzy"
562
- state[:username] = "bork"
563
- transport.stubs(:connection).yields(connection)
564
- end
565
-
566
- constructs_an_ssh_connection
567
-
568
- it "creates the sandbox" do
569
- verifier.expects(:create_sandbox)
570
-
571
- cmd
572
- end
573
-
574
- it "ensures that the sandbox is cleanup up" do
575
- transport.stubs(:connection).raises
576
- verifier.expects(:cleanup_sandbox)
577
-
578
- begin
579
- cmd
580
- rescue # rubocop:disable Lint/HandleExceptions
581
- end
582
- end
583
-
584
- it "invokes the verifier commands over the transport" do
585
- order = sequence("order")
586
- connection.expects(:execute).with("init").in_sequence(order)
587
- connection.expects(:execute).with("prepare").in_sequence(order)
588
- connection.expects(:execute).with("run").in_sequence(order)
589
-
590
- cmd
591
- end
592
-
593
- %W[init prepare run].each do |phase|
594
- it "invokes Verifier##{phase}_command over ssh" do
595
- connection.expects(:execute).with(phase)
596
-
597
- cmd
598
- end
599
-
600
- it "invokes Verifier##{phase}_command with :http_proxy set in config" do
601
- config[:http_proxy] = "http://proxy"
602
- connection.expects(:execute).with("env http_proxy=http://proxy #{phase}")
603
-
604
- cmd
605
- end
606
-
607
- it "invokes Verifier##{phase}_command with :https_proxy set in config" do
608
- config[:https_proxy] = "https://proxy"
609
- connection.expects(:execute).with("env https_proxy=https://proxy #{phase}")
610
-
611
- cmd
612
- end
613
-
614
- it "invokes Verifier##{phase}_command with :ftp_proxy set in config" do
615
- config[:ftp_proxy] = "ftp://proxy"
616
- connection.expects(:execute).with("env ftp_proxy=ftp://proxy #{phase}")
617
-
618
- cmd
619
- end
620
-
621
- it "invokes Verifier##{phase}_command with :http_proxy & :https_proxy & :ftp_proxy set" do
622
- config[:http_proxy] = "http://proxy"
623
- config[:https_proxy] = "https://proxy"
624
- config[:ftp_proxy] = "ftp://proxy"
625
- connection.expects(:execute).with(
626
- "env http_proxy=http://proxy https_proxy=https://proxy ftp_proxy=ftp://proxy #{phase}")
627
-
628
- cmd
629
- end
630
- end
631
-
632
- it "logs to info" do
633
- cmd
634
-
635
- logged_output.string.
636
- must_match(/INFO -- : Transferring files to instance$/)
637
- end
638
-
639
- it "uploads sandbox files" do
640
- connection.expects(:upload).with([], "/tmp/verifier")
641
-
642
- cmd
643
- end
644
-
645
- it "logs to debug" do
646
- cmd
647
-
648
- logged_output.string.must_match(/DEBUG -- : Transfer complete$/)
649
- end
650
-
651
- it "raises an ActionFailed on transfer when TransportFailed is raised" do
652
- connection.stubs(:upload).
653
- raises(Kitchen::Transport::TransportFailed.new("dang"))
654
-
655
- proc { cmd }.must_raise Kitchen::ActionFailed
656
- end
657
-
658
- it "raises an ActionFailed when SSHFailed is raised" do
659
- connection.stubs(:execute).raises(Kitchen::Transport::SshFailed.new("dang"))
660
-
661
- proc { cmd }.must_raise Kitchen::ActionFailed
662
- end
663
- end
664
-
665
- describe "#ssh" do
666
-
667
- let(:cmd) { driver.ssh(["host", "user", { :one => "two" }], "go") }
668
- let(:connection) { mock }
669
-
670
- it "creates an SSH connection" do
671
- connection.stubs(:execute)
672
- transport.expects(:connection).with(
673
- :hostname => "host",
674
- :username => "user",
675
- :port => 22,
676
- :one => "two"
677
- ).yields(connection)
678
-
679
- cmd
680
- end
681
-
682
- it "invokes the command over ssh" do
683
- transport.expects(:connection).yields(connection)
684
- connection.expects(:execute).with("go")
685
-
686
- cmd
687
- end
688
- end
689
-
690
- describe "#remote_command" do
691
-
692
- let(:cmd) { driver.remote_command(state, "shipit") }
693
- let(:connection) { mock }
694
-
695
- before do
696
- state[:hostname] = "fizzy"
697
- state[:username] = "bork"
698
- end
699
-
700
- it "creates an SSH connection" do
701
- transport.expects(:connection).with(
702
- :hostname => "fizzy",
703
- :username => "bork",
704
- :port => 22
705
- )
706
-
707
- cmd
708
- end
709
-
710
- it "invokes the command over ssh" do
711
- transport.expects(:connection).yields(connection)
712
- connection.expects(:execute).with("shipit")
713
-
714
- cmd
715
- end
716
- end
717
-
718
- describe "#wait_for_sshd" do
719
-
720
- let(:cmd) do
721
- driver.send(:wait_for_sshd, "host", "user", :one => "two")
722
- end
723
-
724
- it "creates an SSH connection with merged options" do
725
- transport.expects(:connection).with(
726
- :hostname => "host",
727
- :username => "user",
728
- :port => 22,
729
- :one => "two"
730
- ).returns(stub(:wait_until_ready => true))
731
-
732
- cmd
733
- end
734
-
735
- it "calls wait on the SSH connection" do
736
- connection = mock
737
- transport.expects(:connection).returns(connection)
738
- connection.expects(:wait_until_ready)
739
-
740
- cmd
741
- end
742
- end
743
-
744
- describe "to maintain backwards compatibility" do
745
-
746
- let(:driver) do
747
- Kitchen::Driver::BackCompat.new(config).finalize_config!(instance)
748
- end
749
-
750
- it "#instance returns its instance" do
751
- driver.instance.must_equal instance
752
- end
753
-
754
- it "#name returns the name of the driver" do
755
- driver.name.must_equal "BackCompat"
756
- end
757
-
758
- describe "#logger" do
759
-
760
- before { @klog = Kitchen.logger }
761
- after { Kitchen.logger = @klog }
762
-
763
- it "returns the instance's logger if defined" do
764
- driver.send(:logger).must_equal logger
765
- end
766
-
767
- it "returns the default logger if instance's logger is not set" do
768
- driver = Kitchen::Driver::BackCompat.new(config)
769
- Kitchen.logger = "yep"
770
-
771
- driver.send(:logger).must_equal Kitchen.logger
772
- end
773
- end
774
-
775
- it "#puts calls logger.info" do
776
- driver.send(:puts, "yo")
777
-
778
- logged_output.string.must_match(/I, /)
779
- logged_output.string.must_match(/yo\n/)
780
- end
781
-
782
- it "#print calls logger.info" do
783
- driver.send(:print, "yo")
784
-
785
- logged_output.string.must_match(/I, /)
786
- logged_output.string.must_match(/yo\n/)
787
- end
788
-
789
- it "has a default verify dependencies method" do
790
- driver.verify_dependencies.must_be_nil
791
- end
792
-
793
- it "#busser returns the instance's verifier" do
794
- driver.send(:busser).must_equal verifier
795
- end
796
-
797
- describe ".no_parallel_for" do
798
-
799
- it "registers no serial actions when none are declared" do
800
- Kitchen::Driver::SpeedyCompat.serial_actions.must_equal nil
801
- end
802
-
803
- it "registers a single serial action method" do
804
- Kitchen::Driver::DodgyCompat.serial_actions.must_equal [:converge]
805
- end
806
-
807
- it "registers multiple serial action methods" do
808
- actions = Kitchen::Driver::SlowCompat.serial_actions
809
-
810
- actions.must_include :create
811
- actions.must_include :verify
812
- actions.must_include :destroy
813
- end
814
-
815
- it "raises a ClientError if value is not an action method" do
816
- proc {
817
- Class.new(Kitchen::Driver::BackCompat) {
818
- no_parallel_for :telling_stories
819
- }
820
- }.must_raise Kitchen::ClientError
821
- end
822
- end
823
-
824
- # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
825
- def self.constructs_an_ssh_object
826
- it "with hostname set from state" do
827
- Kitchen::SSH.expects(:new).with { |hostname, _username, _opts|
828
- hostname.must_equal "fizzy"
829
- }.returns(connection)
830
-
831
- cmd
832
- end
833
-
834
- it "with username set from state" do
835
- Kitchen::SSH.expects(:new).with { |_hostname, username, _opts|
836
- username.must_equal "bork"
837
- }.returns(connection)
838
-
839
- cmd
840
- end
841
-
842
- it "with :user_known_hosts_file option set to /dev/null" do
843
- Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
844
- opts[:user_known_hosts_file].must_equal "/dev/null"
845
- }.returns(connection)
846
-
847
- cmd
848
- end
849
-
850
- it "with :paranoid option set to false" do
851
- Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
852
- opts[:paranoid].must_equal false
853
- }.returns(connection)
854
-
855
- cmd
856
- end
857
-
858
- it "with :keys_only option set to falsey by default" do
859
- Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
860
- opts[:keys_only].nil?
861
- }.returns(connection)
862
-
863
- cmd
864
- end
865
-
866
- it "with :keys_only option set to true if :ssh_key is set in config" do
867
- config[:ssh_key] = "wicked"
868
-
869
- Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
870
- opts[:keys_only].must_equal true
871
- }.returns(connection)
872
-
873
- cmd
874
- end
875
-
876
- it "with :keys_only option set to true if :ssh_key is set in state" do
877
- state[:ssh_key] = "wicked"
878
-
879
- Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
880
- opts[:keys_only].must_equal true
881
- }.returns(connection)
882
-
883
- cmd
884
- end
885
-
886
- it "with :keys option set to falsey by default" do
887
- Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
888
- opts[:keys].nil?
889
- }.returns(connection)
890
-
891
- cmd
892
- end
893
-
894
- it "with :keys option set to an array if :ssh_key is set in config" do
895
- config[:ssh_key] = "wicked"
896
-
897
- Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
898
- opts[:keys].must_equal ["wicked"]
899
- }.returns(connection)
900
-
901
- cmd
902
- end
903
-
904
- it "with :keys option set to an array if :ssh_key is set in state" do
905
- state[:ssh_key] = "wicked"
906
-
907
- Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
908
- opts[:keys].must_equal ["wicked"]
909
- }.returns(connection)
910
-
911
- cmd
912
- end
913
-
914
- it "with :password option set to falsey by default" do
915
- Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
916
- opts[:password].nil?
917
- }.returns(connection)
918
-
919
- cmd
920
- end
921
-
922
- it "with :password option set if given in config" do
923
- config[:password] = "psst"
924
-
925
- Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
926
- opts[:password].must_equal "psst"
927
- }.returns(connection)
928
-
929
- cmd
930
- end
931
-
932
- it "with :password option set if given in state" do
933
- state[:password] = "psst"
934
-
935
- Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
936
- opts[:password].must_equal "psst"
937
- }.returns(connection)
938
-
939
- cmd
940
- end
941
-
942
- it "with :forward_agent option set to falsey by default" do
943
- Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
944
- opts[:forward_agent].nil?
945
- }.returns(connection)
946
-
947
- cmd
948
- end
949
-
950
- it "with :forward_agent option set if given in config" do
951
- config[:forward_agent] = "yeah?"
952
-
953
- Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
954
- opts[:forward_agent].must_equal "yeah?"
955
- }.returns(connection)
956
-
957
- cmd
958
- end
959
-
960
- it "with :forward_agent option set if given in state" do
961
- state[:forward_agent] = "yeah?"
962
-
963
- Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
964
- opts[:forward_agent].must_equal "yeah?"
965
- }.returns(connection)
966
-
967
- cmd
968
- end
969
-
970
- it "with :port option set to 22 by default" do
971
- Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
972
- opts[:port].must_equal 22
973
- }.returns(connection)
974
-
975
- cmd
976
- end
977
-
978
- it "with :port option set if customized in config" do
979
- config[:port] = 1234
980
-
981
- Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
982
- opts[:port].must_equal 1234
983
- }.returns(connection)
984
-
985
- cmd
986
- end
987
-
988
- it "with :port option set if customized in state" do
989
- state[:port] = 9999
990
-
991
- Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
992
- opts[:port].must_equal 9999
993
- }.returns(connection)
994
-
995
- cmd
996
- end
997
-
998
- it "with :logger option set to driver's logger" do
999
- Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
1000
- opts[:logger].must_equal logger
1001
- }.returns(connection)
1002
-
1003
- cmd
1004
- end
1005
- end
1006
- # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
1007
-
1008
- describe "#run_remote" do
1009
-
1010
- let(:cmd) { driver.use_run_remote(state, "huh") }
1011
- let(:connection) { stub(:exec => true) }
1012
-
1013
- before do
1014
- state[:hostname] = "fizzy"
1015
- state[:username] = "bork"
1016
- end
1017
-
1018
- constructs_an_ssh_object
1019
-
1020
- it "invokes the #install_command with :http_proxy set in config" do
1021
- config[:http_proxy] = "http://proxy"
1022
- Kitchen::SSH.stubs(:new).returns(connection)
1023
- connection.expects(:exec).with("env http_proxy=http://proxy huh")
1024
-
1025
- cmd
1026
- end
1027
-
1028
- it "invokes the #install_command with :https_proxy set in config" do
1029
- config[:https_proxy] = "https://proxy"
1030
- Kitchen::SSH.stubs(:new).returns(connection)
1031
- connection.expects(:exec).with("env https_proxy=https://proxy huh")
1032
-
1033
- cmd
1034
- end
1035
-
1036
- it "invokes the #install_command with :ftp_proxy set in config" do
1037
- config[:ftp_proxy] = "ftp://proxy"
1038
- Kitchen::SSH.stubs(:new).returns(connection)
1039
- connection.expects(:exec).with("env ftp_proxy=ftp://proxy huh")
1040
-
1041
- cmd
1042
- end
1043
-
1044
- it "invokes the #install_command with :http_proxy & :https_proxy & :ftp_proxy set" do
1045
- config[:http_proxy] = "http://proxy"
1046
- config[:https_proxy] = "https://proxy"
1047
- config[:ftp_proxy] = "ftp://proxy"
1048
- Kitchen::SSH.stubs(:new).returns(connection)
1049
- connection.expects(:exec).with(
1050
- "env http_proxy=http://proxy https_proxy=https://proxy ftp_proxy=ftp://proxy huh")
1051
-
1052
- cmd
1053
- end
1054
-
1055
- it "doesn't invoke an ssh command if command is nil" do
1056
- Kitchen::SSH.stubs(:new).returns(mock)
1057
-
1058
- driver.use_run_remote(state, nil)
1059
- end
1060
-
1061
- it "raises an ActionFailed on transfer when SSHFailed is raised" do
1062
- Kitchen::SSH.stubs(:new).returns(connection)
1063
- connection.stubs(:exec).raises(Kitchen::SSHFailed.new("dang"))
1064
-
1065
- proc { cmd }.must_raise Kitchen::ActionFailed
1066
- end
1067
-
1068
- it "raises an ActionFailed on exec when Net::SSH:Exception is raised" do
1069
- Kitchen::SSH.stubs(:new).returns(connection)
1070
- connection.stubs(:exec).raises(Net::SSH::Exception.new("dang"))
1071
-
1072
- proc { cmd }.must_raise Kitchen::ActionFailed
1073
- end
1074
- end
1075
-
1076
- describe "#transfer_path" do
1077
-
1078
- let(:cmd) { driver.use_transfer_path(state, ["nope"], "nadda") }
1079
- let(:connection) { stub(:upload_path! => true) }
1080
-
1081
- before do
1082
- state[:hostname] = "fizzy"
1083
- state[:username] = "bork"
1084
- end
1085
-
1086
- constructs_an_ssh_object
1087
-
1088
- it "doesn't invoke an scp command if locals is nil" do
1089
- Kitchen::SSH.stubs(:new).returns(mock)
1090
-
1091
- driver.use_transfer_path(state, nil, "nope")
1092
- end
1093
-
1094
- it "doesn't invoke an scp command if locals is an empty array" do
1095
- Kitchen::SSH.stubs(:new).returns(mock)
1096
-
1097
- driver.use_transfer_path(state, [], "nope")
1098
- end
1099
-
1100
- it "raises an ActionFailed on transfer when SSHFailed is raised" do
1101
- Kitchen::SSH.stubs(:new).returns(connection)
1102
- connection.stubs(:upload_path!).raises(Kitchen::SSHFailed.new("dang"))
1103
-
1104
- proc { cmd }.must_raise Kitchen::ActionFailed
1105
- end
1106
-
1107
- it "raises an ActionFailed on exec when Net::SSH:Exception is raised" do
1108
- Kitchen::SSH.stubs(:new).returns(connection)
1109
- connection.stubs(:upload_path!).raises(Net::SSH::Exception.new("dang"))
1110
-
1111
- proc { cmd }.must_raise Kitchen::ActionFailed
1112
- end
1113
- end
1114
- end
1115
- end
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
+ #
5
+ # Copyright (C) 2014, Fletcher Nichol
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require_relative "../../spec_helper"
20
+
21
+ require "kitchen"
22
+ require "kitchen/transport/ssh"
23
+ require "kitchen/verifier/busser"
24
+
25
+ module Kitchen
26
+
27
+ module Driver
28
+
29
+ class BackCompat < Kitchen::Driver::SSHBase
30
+
31
+ def use_run_remote(state, command)
32
+ connection = Kitchen::SSH.new(*build_ssh_args(state))
33
+ run_remote(command, connection)
34
+ end
35
+
36
+ def use_transfer_path(state, locals, remote)
37
+ connection = Kitchen::SSH.new(*build_ssh_args(state))
38
+ transfer_path(locals, remote, connection)
39
+ end
40
+ end
41
+
42
+ class SpeedyCompat < Kitchen::Driver::SSHBase
43
+ end
44
+
45
+ class DodgyCompat < Kitchen::Driver::SSHBase
46
+
47
+ no_parallel_for :converge
48
+ end
49
+
50
+ class SlowCompat < Kitchen::Driver::SSHBase
51
+
52
+ no_parallel_for :create, :destroy
53
+ no_parallel_for :verify
54
+ end
55
+ end
56
+ end
57
+
58
+ describe Kitchen::Driver::SSHBase do
59
+
60
+ let(:logged_output) { StringIO.new }
61
+ let(:logger) { Logger.new(logged_output) }
62
+ let(:config) { Hash.new }
63
+ let(:state) { Hash.new }
64
+
65
+ let(:verifier) do
66
+ v = mock("busser")
67
+ v.responds_like_instance_of(Kitchen::Verifier::Busser)
68
+ v.stubs(:install_command).returns("install")
69
+ v.stubs(:init_command).returns("init")
70
+ v.stubs(:prepare_command).returns("prepare")
71
+ v.stubs(:run_command).returns("run")
72
+ v.stubs(:create_sandbox).returns(true)
73
+ v.stubs(:cleanup_sandbox).returns(true)
74
+ v.stubs(:sandbox_path).returns("/tmp/sandbox")
75
+ v.stubs(:[]).with(:root_path).returns("/tmp/verifier")
76
+ v
77
+ end
78
+
79
+ let(:provisioner) do
80
+ stub(
81
+ :install_command => "install",
82
+ :init_command => "init",
83
+ :prepare_command => "prepare",
84
+ :run_command => "run",
85
+ :create_sandbox => true,
86
+ :cleanup_sandbox => true,
87
+ :sandbox_path => "/tmp/sandbox"
88
+ )
89
+ end
90
+
91
+ let(:transport) do
92
+ t = mock("transport")
93
+ t.responds_like_instance_of(Kitchen::Transport::Base)
94
+ t
95
+ end
96
+
97
+ let(:instance) do
98
+ stub(
99
+ :name => "coolbeans",
100
+ :logger => logger,
101
+ :verifier => verifier,
102
+ :provisioner => provisioner,
103
+ :transport => transport,
104
+ :to_str => "instance"
105
+ )
106
+ end
107
+
108
+ let(:driver) do
109
+ Kitchen::Driver::SSHBase.new(config).finalize_config!(instance)
110
+ end
111
+
112
+ it "plugin_version is not set" do
113
+ driver.diagnose_plugin[:version].must_equal nil
114
+ end
115
+
116
+ describe "configuration" do
117
+
118
+ it ":sudo defaults to true" do
119
+ driver[:sudo].must_equal true
120
+ end
121
+
122
+ it ":port defaults to 22" do
123
+ driver[:port].must_equal 22
124
+ end
125
+ end
126
+
127
+ it "#create raises a ClientError" do
128
+ proc { driver.create(state) }.must_raise Kitchen::ClientError
129
+ end
130
+
131
+ it "#destroy raises a ClientError" do
132
+ proc { driver.destroy(state) }.must_raise Kitchen::ClientError
133
+ end
134
+
135
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
136
+ def self.constructs_an_ssh_connection
137
+ describe "constructs an SSH connection" do
138
+
139
+ it "with hostname set from state" do
140
+ transport.expects(:connection).with { |state|
141
+ state[:hostname].must_equal "fizzy"
142
+ }.returns(stub(:login_command => stub))
143
+
144
+ cmd
145
+ end
146
+
147
+ it "with username set from state" do
148
+ transport.expects(:connection).with { |state|
149
+ state[:username].must_equal "bork"
150
+ }.returns(stub(:login_command => stub))
151
+
152
+ cmd
153
+ end
154
+
155
+ it "with :ssh_key option set from config" do
156
+ config[:ssh_key] = "wicked"
157
+
158
+ transport.expects(:connection).with { |state|
159
+ state[:ssh_key].must_equal "wicked"
160
+ }.returns(stub(:login_command => stub))
161
+
162
+ cmd
163
+ end
164
+
165
+ it "with :ssh_key option set from state" do
166
+ state[:ssh_key] = "wicked"
167
+
168
+ transport.expects(:connection).with { |state|
169
+ state[:ssh_key].must_equal "wicked"
170
+ }.returns(stub(:login_command => stub))
171
+
172
+ cmd
173
+ end
174
+
175
+ it "with :password option set to falsey by default" do
176
+ transport.expects(:connection).with { |state|
177
+ state[:password].nil?
178
+ }.returns(stub(:login_command => stub))
179
+
180
+ cmd
181
+ end
182
+
183
+ it "with :password option set if given in config" do
184
+ config[:password] = "psst"
185
+
186
+ transport.expects(:connection).with { |state|
187
+ state[:password].must_equal "psst"
188
+ }.returns(stub(:login_command => stub))
189
+
190
+ cmd
191
+ end
192
+
193
+ it "with :password option set if given in state" do
194
+ state[:password] = "psst"
195
+
196
+ transport.expects(:connection).with { |state|
197
+ state[:password].must_equal "psst"
198
+ }.returns(stub(:login_command => stub))
199
+
200
+ cmd
201
+ end
202
+
203
+ it "with :forward_agent option set to falsey by default" do
204
+ transport.expects(:connection).with { |state|
205
+ state[:forward_agent].nil?
206
+ }.returns(stub(:login_command => stub))
207
+
208
+ cmd
209
+ end
210
+
211
+ it "with :forward_agent option set if given in config" do
212
+ config[:forward_agent] = "yeah?"
213
+
214
+ transport.expects(:connection).with { |state|
215
+ state[:forward_agent].must_equal "yeah?"
216
+ }.returns(stub(:login_command => stub))
217
+
218
+ cmd
219
+ end
220
+
221
+ it "with :forward_agent option set if given in state" do
222
+ state[:forward_agent] = "yeah?"
223
+
224
+ transport.expects(:connection).with { |state|
225
+ state[:forward_agent].must_equal "yeah?"
226
+ }.returns(stub(:login_command => stub))
227
+
228
+ cmd
229
+ end
230
+
231
+ it "with :port option set to 22 by default" do
232
+ transport.expects(:connection).with { |state|
233
+ state[:port].must_equal 22
234
+ }.returns(stub(:login_command => stub))
235
+
236
+ cmd
237
+ end
238
+
239
+ it "with :port option set if customized in config" do
240
+ config[:port] = 1234
241
+
242
+ transport.expects(:connection).with { |state|
243
+ state[:port].must_equal 1234
244
+ }.returns(stub(:login_command => stub))
245
+
246
+ cmd
247
+ end
248
+
249
+ it "with :port option set if customized in state" do
250
+ state[:port] = 9999
251
+
252
+ transport.expects(:connection).with { |state|
253
+ state[:port].must_equal 9999
254
+ }.returns(stub(:login_command => stub))
255
+
256
+ cmd
257
+ end
258
+ end
259
+ end
260
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
261
+
262
+ describe "#login_command" do
263
+
264
+ let(:cmd) { driver.login_command(state) }
265
+
266
+ before do
267
+ state[:hostname] = "fizzy"
268
+ state[:username] = "bork"
269
+ end
270
+
271
+ it "returns a LoginCommand" do
272
+ transport.stubs(:connection).returns(stub(:login_command => "command"))
273
+
274
+ cmd.must_equal "command"
275
+ end
276
+
277
+ constructs_an_ssh_connection
278
+ end
279
+
280
+ describe "#converge" do
281
+
282
+ let(:cmd) { driver.converge(state) }
283
+ let(:connection) { stub(:execute => true, :upload => true) }
284
+
285
+ before do
286
+ state[:hostname] = "fizzy"
287
+ state[:username] = "bork"
288
+ provisioner.stubs(:[]).with(:root_path).returns("/rooty")
289
+ FakeFS.activate!
290
+ FileUtils.mkdir_p("/tmp")
291
+ @original_env = ENV.to_hash
292
+ ENV.replace("http_proxy" => nil, "HTTP_PROXY" => nil,
293
+ "https_proxy" => nil, "HTTPS_PROXY" => nil,
294
+ "ftp_proxy" => nil, "FTP_PROXY" => nil,
295
+ "no_proxy" => nil, "NO_PROXY" => nil)
296
+ end
297
+
298
+ after do
299
+ FakeFS.deactivate!
300
+ FakeFS::FileSystem.clear
301
+ ENV.clear
302
+ ENV.replace(@original_env)
303
+ end
304
+
305
+ constructs_an_ssh_connection
306
+
307
+ it "creates the sandbox" do
308
+ transport.stubs(:connection).yields(connection)
309
+ provisioner.expects(:create_sandbox)
310
+
311
+ cmd
312
+ end
313
+
314
+ it "ensures that the sandbox is cleaned up" do
315
+ transport.stubs(:connection).raises
316
+ provisioner.expects(:cleanup_sandbox)
317
+
318
+ begin
319
+ cmd
320
+ rescue # rubocop:disable Lint/HandleExceptions
321
+ end
322
+ end
323
+
324
+ it "invokes the provisioner commands over ssh" do
325
+ transport.stubs(:connection).yields(connection)
326
+ order = sequence("order")
327
+ connection.expects(:execute).with("install").in_sequence(order)
328
+ connection.expects(:execute).with("init").in_sequence(order)
329
+ connection.expects(:execute).with("prepare").in_sequence(order)
330
+ connection.expects(:execute).with("run").in_sequence(order)
331
+
332
+ cmd
333
+ end
334
+
335
+ it "invokes the #install_command with :http_proxy set in config" do
336
+ config[:http_proxy] = "http://proxy"
337
+ transport.stubs(:connection).yields(connection)
338
+ connection.expects(:execute).with("env http_proxy=http://proxy install")
339
+
340
+ cmd
341
+ end
342
+
343
+ it "invokes the #install_command with ENV[\"http_proxy\"] set" do
344
+ ENV["http_proxy"] = "http://proxy"
345
+ transport.stubs(:connection).yields(connection)
346
+ if running_tests_on_windows?
347
+ connection.expects(:execute).
348
+ with("env http_proxy=http://proxy HTTP_PROXY=http://proxy install")
349
+ else
350
+ connection.expects(:execute).with("env http_proxy=http://proxy install")
351
+ end
352
+ cmd
353
+ end
354
+
355
+ it "invokes the #install_command with ENV[\"http_proxy\"] and ENV[\"no_proxy\"] set" do
356
+ ENV["http_proxy"] = "http://proxy"
357
+ ENV["no_proxy"] = "http://no"
358
+ transport.stubs(:connection).yields(connection)
359
+ if running_tests_on_windows?
360
+ connection.expects(:execute).
361
+ with("env http_proxy=http://proxy HTTP_PROXY=http://proxy " \
362
+ "no_proxy=http://no NO_PROXY=http://no install")
363
+ else
364
+ connection.expects(:execute).with("env http_proxy=http://proxy " \
365
+ "no_proxy=http://no install")
366
+ end
367
+ cmd
368
+ end
369
+
370
+ it "invokes the #install_command with :https_proxy set in config" do
371
+ config[:https_proxy] = "https://proxy"
372
+ transport.stubs(:connection).yields(connection)
373
+ connection.expects(:execute).with("env https_proxy=https://proxy install")
374
+
375
+ cmd
376
+ end
377
+
378
+ it "invokes the #install_command with ENV[\"https_proxy\"] set" do
379
+ ENV["https_proxy"] = "https://proxy"
380
+ transport.stubs(:connection).yields(connection)
381
+ if running_tests_on_windows?
382
+ connection.expects(:execute).
383
+ with("env https_proxy=https://proxy HTTPS_PROXY=https://proxy install")
384
+ else
385
+ connection.expects(:execute).with("env https_proxy=https://proxy install")
386
+ end
387
+ cmd
388
+ end
389
+
390
+ it "invokes the #install_command with ENV[\"https_proxy\"] and ENV[\"no_proxy\"] set" do
391
+ ENV["https_proxy"] = "https://proxy"
392
+ ENV["no_proxy"] = "https://no"
393
+ transport.stubs(:connection).yields(connection)
394
+ if running_tests_on_windows?
395
+ connection.expects(:execute).
396
+ with("env https_proxy=https://proxy HTTPS_PROXY=https://proxy " \
397
+ "no_proxy=https://no NO_PROXY=https://no install")
398
+ else
399
+ connection.expects(:execute).with("env https_proxy=https://proxy " \
400
+ "no_proxy=https://no install")
401
+ end
402
+ cmd
403
+ end
404
+
405
+ it "invokes the #install_command with :ftp_proxy set in config" do
406
+ config[:ftp_proxy] = "ftp://proxy"
407
+ transport.stubs(:connection).yields(connection)
408
+ connection.expects(:execute).with("env ftp_proxy=ftp://proxy install")
409
+
410
+ cmd
411
+ end
412
+
413
+ it "invokes the #install_command with ENV[\"ftp_proxy\"] set" do
414
+ ENV["ftp_proxy"] = "ftp://proxy"
415
+ transport.stubs(:connection).yields(connection)
416
+ if running_tests_on_windows?
417
+ connection.expects(:execute).
418
+ with("env ftp_proxy=ftp://proxy FTP_PROXY=ftp://proxy install")
419
+ else
420
+ connection.expects(:execute).with("env ftp_proxy=ftp://proxy install")
421
+ end
422
+ cmd
423
+ end
424
+
425
+ it "invokes the #install_command with ENV[\"ftp_proxy\"] and ENV[\"no_proxy\"] set" do
426
+ ENV["ftp_proxy"] = "ftp://proxy"
427
+ ENV["no_proxy"] = "http://no"
428
+ transport.stubs(:connection).yields(connection)
429
+ if running_tests_on_windows?
430
+ connection.expects(:execute).
431
+ with("env ftp_proxy=ftp://proxy FTP_PROXY=http://proxy " \
432
+ "no_proxy=http://no NO_PROXY=http://no install")
433
+ else
434
+ connection.expects(:execute).with("env ftp_proxy=ftp://proxy " \
435
+ "no_proxy=http://no install")
436
+ end
437
+ cmd
438
+ end
439
+
440
+ it "invokes the #install_command with :http_proxy & :https_proxy & :ftp_proxy set" do
441
+ config[:http_proxy] = "http://proxy"
442
+ config[:https_proxy] = "https://proxy"
443
+ config[:ftp_proxy] = "ftp://proxy"
444
+ transport.stubs(:connection).yields(connection)
445
+ connection.expects(:execute).with(
446
+ "env http_proxy=http://proxy https_proxy=https://proxy ftp_proxy=ftp://proxy install")
447
+
448
+ cmd
449
+ end
450
+
451
+ describe "transferring files" do
452
+
453
+ before do
454
+ transport.stubs(:connection).yields(connection)
455
+ connection.stubs(:upload)
456
+ FileUtils.mkdir_p "/tmp/sandbox/stuff"
457
+ end
458
+
459
+ it "uploads files" do
460
+ connection.expects(:upload).with(["/tmp/sandbox/stuff"], "/rooty")
461
+
462
+ cmd
463
+ end
464
+
465
+ it "logs to info" do
466
+ cmd
467
+
468
+ logged_output.string.
469
+ must_match(/INFO -- : Transferring files to instance$/)
470
+ end
471
+
472
+ it "logs to debug" do
473
+ cmd
474
+
475
+ logged_output.string.must_match(/DEBUG -- : Transfer complete$/)
476
+ end
477
+
478
+ it "raises an ActionFailed on transfer when SshFailed is raised" do
479
+ connection.stubs(:upload).raises(Kitchen::Transport::SshFailed.new("dang"))
480
+
481
+ proc { cmd }.must_raise Kitchen::ActionFailed
482
+ end
483
+ end
484
+
485
+ it "raises an ActionFailed on execute when SshFailed is raised" do
486
+ transport.stubs(:connection).yields(connection)
487
+ connection.stubs(:execute).raises(Kitchen::Transport::SshFailed.new("dang"))
488
+
489
+ proc { cmd }.must_raise Kitchen::ActionFailed
490
+ end
491
+ end
492
+
493
+ describe "#setup" do
494
+
495
+ let(:cmd) { driver.setup(state) }
496
+ let(:connection) { mock }
497
+
498
+ before do
499
+ state[:hostname] = "fizzy"
500
+ state[:username] = "bork"
501
+ end
502
+
503
+ constructs_an_ssh_connection
504
+
505
+ it "invokes the Verifier#install_command over ssh" do
506
+ transport.stubs(:connection).yields(connection)
507
+ connection.expects(:execute).with("install")
508
+
509
+ cmd
510
+ end
511
+
512
+ it "invokes the Verifier#install_command with :http_proxy set in config" do
513
+ config[:http_proxy] = "http://proxy"
514
+ transport.stubs(:connection).yields(connection)
515
+ connection.expects(:execute).with("env http_proxy=http://proxy install")
516
+
517
+ cmd
518
+ end
519
+
520
+ it "invokes the Verifier#install_command with :https_proxy set in config" do
521
+ config[:https_proxy] = "https://proxy"
522
+ transport.stubs(:connection).yields(connection)
523
+ connection.expects(:execute).with("env https_proxy=https://proxy install")
524
+
525
+ cmd
526
+ end
527
+
528
+ it "invokes the Verifier#install_command with :ftp_proxy set in config" do
529
+ config[:ftp_proxy] = "ftp://proxy"
530
+ transport.stubs(:connection).yields(connection)
531
+ connection.expects(:execute).with("env ftp_proxy=ftp://proxy install")
532
+
533
+ cmd
534
+ end
535
+
536
+ it "invokes the Verifier#install_command with :http_proxy & :https_proxy & :ftp_proxy set" do
537
+ config[:http_proxy] = "http://proxy"
538
+ config[:https_proxy] = "https://proxy"
539
+ config[:ftp_proxy] = "ftp://proxy"
540
+ transport.stubs(:connection).yields(connection)
541
+ connection.expects(:execute).with(
542
+ "env http_proxy=http://proxy https_proxy=https://proxy ftp_proxy=ftp://proxy install")
543
+
544
+ cmd
545
+ end
546
+
547
+ it "raises an ActionFailed when SshFailed is raised" do
548
+ transport.stubs(:connection).yields(connection)
549
+ connection.stubs(:execute).raises(Kitchen::Transport::SshFailed.new("dang"))
550
+
551
+ proc { cmd }.must_raise Kitchen::ActionFailed
552
+ end
553
+ end
554
+
555
+ describe "#verify" do
556
+
557
+ let(:cmd) { driver.verify(state) }
558
+ let(:connection) { stub(:execute => true, :upload => true) }
559
+
560
+ before do
561
+ state[:hostname] = "fizzy"
562
+ state[:username] = "bork"
563
+ transport.stubs(:connection).yields(connection)
564
+ end
565
+
566
+ constructs_an_ssh_connection
567
+
568
+ it "creates the sandbox" do
569
+ verifier.expects(:create_sandbox)
570
+
571
+ cmd
572
+ end
573
+
574
+ it "ensures that the sandbox is cleanup up" do
575
+ transport.stubs(:connection).raises
576
+ verifier.expects(:cleanup_sandbox)
577
+
578
+ begin
579
+ cmd
580
+ rescue # rubocop:disable Lint/HandleExceptions
581
+ end
582
+ end
583
+
584
+ it "invokes the verifier commands over the transport" do
585
+ order = sequence("order")
586
+ connection.expects(:execute).with("init").in_sequence(order)
587
+ connection.expects(:execute).with("prepare").in_sequence(order)
588
+ connection.expects(:execute).with("run").in_sequence(order)
589
+
590
+ cmd
591
+ end
592
+
593
+ %W[init prepare run].each do |phase|
594
+ it "invokes Verifier##{phase}_command over ssh" do
595
+ connection.expects(:execute).with(phase)
596
+
597
+ cmd
598
+ end
599
+
600
+ it "invokes Verifier##{phase}_command with :http_proxy set in config" do
601
+ config[:http_proxy] = "http://proxy"
602
+ connection.expects(:execute).with("env http_proxy=http://proxy #{phase}")
603
+
604
+ cmd
605
+ end
606
+
607
+ it "invokes Verifier##{phase}_command with :https_proxy set in config" do
608
+ config[:https_proxy] = "https://proxy"
609
+ connection.expects(:execute).with("env https_proxy=https://proxy #{phase}")
610
+
611
+ cmd
612
+ end
613
+
614
+ it "invokes Verifier##{phase}_command with :ftp_proxy set in config" do
615
+ config[:ftp_proxy] = "ftp://proxy"
616
+ connection.expects(:execute).with("env ftp_proxy=ftp://proxy #{phase}")
617
+
618
+ cmd
619
+ end
620
+
621
+ it "invokes Verifier##{phase}_command with :http_proxy & :https_proxy & :ftp_proxy set" do
622
+ config[:http_proxy] = "http://proxy"
623
+ config[:https_proxy] = "https://proxy"
624
+ config[:ftp_proxy] = "ftp://proxy"
625
+ connection.expects(:execute).with(
626
+ "env http_proxy=http://proxy https_proxy=https://proxy ftp_proxy=ftp://proxy #{phase}")
627
+
628
+ cmd
629
+ end
630
+ end
631
+
632
+ it "logs to info" do
633
+ cmd
634
+
635
+ logged_output.string.
636
+ must_match(/INFO -- : Transferring files to instance$/)
637
+ end
638
+
639
+ it "uploads sandbox files" do
640
+ connection.expects(:upload).with([], "/tmp/verifier")
641
+
642
+ cmd
643
+ end
644
+
645
+ it "logs to debug" do
646
+ cmd
647
+
648
+ logged_output.string.must_match(/DEBUG -- : Transfer complete$/)
649
+ end
650
+
651
+ it "raises an ActionFailed on transfer when TransportFailed is raised" do
652
+ connection.stubs(:upload).
653
+ raises(Kitchen::Transport::TransportFailed.new("dang"))
654
+
655
+ proc { cmd }.must_raise Kitchen::ActionFailed
656
+ end
657
+
658
+ it "raises an ActionFailed when SSHFailed is raised" do
659
+ connection.stubs(:execute).raises(Kitchen::Transport::SshFailed.new("dang"))
660
+
661
+ proc { cmd }.must_raise Kitchen::ActionFailed
662
+ end
663
+ end
664
+
665
+ describe "#ssh" do
666
+
667
+ let(:cmd) { driver.ssh(["host", "user", { :one => "two" }], "go") }
668
+ let(:connection) { mock }
669
+
670
+ it "creates an SSH connection" do
671
+ connection.stubs(:execute)
672
+ transport.expects(:connection).with(
673
+ :hostname => "host",
674
+ :username => "user",
675
+ :port => 22,
676
+ :one => "two"
677
+ ).yields(connection)
678
+
679
+ cmd
680
+ end
681
+
682
+ it "invokes the command over ssh" do
683
+ transport.expects(:connection).yields(connection)
684
+ connection.expects(:execute).with("go")
685
+
686
+ cmd
687
+ end
688
+ end
689
+
690
+ describe "#remote_command" do
691
+
692
+ let(:cmd) { driver.remote_command(state, "shipit") }
693
+ let(:connection) { mock }
694
+
695
+ before do
696
+ state[:hostname] = "fizzy"
697
+ state[:username] = "bork"
698
+ end
699
+
700
+ it "creates an SSH connection" do
701
+ transport.expects(:connection).with(
702
+ :hostname => "fizzy",
703
+ :username => "bork",
704
+ :port => 22
705
+ )
706
+
707
+ cmd
708
+ end
709
+
710
+ it "invokes the command over ssh" do
711
+ transport.expects(:connection).yields(connection)
712
+ connection.expects(:execute).with("shipit")
713
+
714
+ cmd
715
+ end
716
+ end
717
+
718
+ describe "#wait_for_sshd" do
719
+
720
+ let(:cmd) do
721
+ driver.send(:wait_for_sshd, "host", "user", :one => "two")
722
+ end
723
+
724
+ it "creates an SSH connection with merged options" do
725
+ transport.expects(:connection).with(
726
+ :hostname => "host",
727
+ :username => "user",
728
+ :port => 22,
729
+ :one => "two"
730
+ ).returns(stub(:wait_until_ready => true))
731
+
732
+ cmd
733
+ end
734
+
735
+ it "calls wait on the SSH connection" do
736
+ connection = mock
737
+ transport.expects(:connection).returns(connection)
738
+ connection.expects(:wait_until_ready)
739
+
740
+ cmd
741
+ end
742
+ end
743
+
744
+ describe "to maintain backwards compatibility" do
745
+
746
+ let(:driver) do
747
+ Kitchen::Driver::BackCompat.new(config).finalize_config!(instance)
748
+ end
749
+
750
+ it "#instance returns its instance" do
751
+ driver.instance.must_equal instance
752
+ end
753
+
754
+ it "#name returns the name of the driver" do
755
+ driver.name.must_equal "BackCompat"
756
+ end
757
+
758
+ describe "#logger" do
759
+
760
+ before { @klog = Kitchen.logger }
761
+ after { Kitchen.logger = @klog }
762
+
763
+ it "returns the instance's logger if defined" do
764
+ driver.send(:logger).must_equal logger
765
+ end
766
+
767
+ it "returns the default logger if instance's logger is not set" do
768
+ driver = Kitchen::Driver::BackCompat.new(config)
769
+ Kitchen.logger = "yep"
770
+
771
+ driver.send(:logger).must_equal Kitchen.logger
772
+ end
773
+ end
774
+
775
+ it "#puts calls logger.info" do
776
+ driver.send(:puts, "yo")
777
+
778
+ logged_output.string.must_match(/I, /)
779
+ logged_output.string.must_match(/yo\n/)
780
+ end
781
+
782
+ it "#print calls logger.info" do
783
+ driver.send(:print, "yo")
784
+
785
+ logged_output.string.must_match(/I, /)
786
+ logged_output.string.must_match(/yo\n/)
787
+ end
788
+
789
+ it "has a default verify dependencies method" do
790
+ driver.verify_dependencies.must_be_nil
791
+ end
792
+
793
+ it "#busser returns the instance's verifier" do
794
+ driver.send(:busser).must_equal verifier
795
+ end
796
+
797
+ describe ".no_parallel_for" do
798
+
799
+ it "registers no serial actions when none are declared" do
800
+ Kitchen::Driver::SpeedyCompat.serial_actions.must_equal nil
801
+ end
802
+
803
+ it "registers a single serial action method" do
804
+ Kitchen::Driver::DodgyCompat.serial_actions.must_equal [:converge]
805
+ end
806
+
807
+ it "registers multiple serial action methods" do
808
+ actions = Kitchen::Driver::SlowCompat.serial_actions
809
+
810
+ actions.must_include :create
811
+ actions.must_include :verify
812
+ actions.must_include :destroy
813
+ end
814
+
815
+ it "raises a ClientError if value is not an action method" do
816
+ proc {
817
+ Class.new(Kitchen::Driver::BackCompat) {
818
+ no_parallel_for :telling_stories
819
+ }
820
+ }.must_raise Kitchen::ClientError
821
+ end
822
+ end
823
+
824
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
825
+ def self.constructs_an_ssh_object
826
+ it "with hostname set from state" do
827
+ Kitchen::SSH.expects(:new).with { |hostname, _username, _opts|
828
+ hostname.must_equal "fizzy"
829
+ }.returns(connection)
830
+
831
+ cmd
832
+ end
833
+
834
+ it "with username set from state" do
835
+ Kitchen::SSH.expects(:new).with { |_hostname, username, _opts|
836
+ username.must_equal "bork"
837
+ }.returns(connection)
838
+
839
+ cmd
840
+ end
841
+
842
+ it "with :user_known_hosts_file option set to /dev/null" do
843
+ Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
844
+ opts[:user_known_hosts_file].must_equal "/dev/null"
845
+ }.returns(connection)
846
+
847
+ cmd
848
+ end
849
+
850
+ it "with :paranoid option set to false" do
851
+ Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
852
+ opts[:paranoid].must_equal false
853
+ }.returns(connection)
854
+
855
+ cmd
856
+ end
857
+
858
+ it "with :keys_only option set to falsey by default" do
859
+ Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
860
+ opts[:keys_only].nil?
861
+ }.returns(connection)
862
+
863
+ cmd
864
+ end
865
+
866
+ it "with :keys_only option set to true if :ssh_key is set in config" do
867
+ config[:ssh_key] = "wicked"
868
+
869
+ Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
870
+ opts[:keys_only].must_equal true
871
+ }.returns(connection)
872
+
873
+ cmd
874
+ end
875
+
876
+ it "with :keys_only option set to true if :ssh_key is set in state" do
877
+ state[:ssh_key] = "wicked"
878
+
879
+ Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
880
+ opts[:keys_only].must_equal true
881
+ }.returns(connection)
882
+
883
+ cmd
884
+ end
885
+
886
+ it "with :keys option set to falsey by default" do
887
+ Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
888
+ opts[:keys].nil?
889
+ }.returns(connection)
890
+
891
+ cmd
892
+ end
893
+
894
+ it "with :keys option set to an array if :ssh_key is set in config" do
895
+ config[:ssh_key] = "wicked"
896
+
897
+ Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
898
+ opts[:keys].must_equal ["wicked"]
899
+ }.returns(connection)
900
+
901
+ cmd
902
+ end
903
+
904
+ it "with :keys option set to an array if :ssh_key is set in state" do
905
+ state[:ssh_key] = "wicked"
906
+
907
+ Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
908
+ opts[:keys].must_equal ["wicked"]
909
+ }.returns(connection)
910
+
911
+ cmd
912
+ end
913
+
914
+ it "with :password option set to falsey by default" do
915
+ Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
916
+ opts[:password].nil?
917
+ }.returns(connection)
918
+
919
+ cmd
920
+ end
921
+
922
+ it "with :password option set if given in config" do
923
+ config[:password] = "psst"
924
+
925
+ Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
926
+ opts[:password].must_equal "psst"
927
+ }.returns(connection)
928
+
929
+ cmd
930
+ end
931
+
932
+ it "with :password option set if given in state" do
933
+ state[:password] = "psst"
934
+
935
+ Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
936
+ opts[:password].must_equal "psst"
937
+ }.returns(connection)
938
+
939
+ cmd
940
+ end
941
+
942
+ it "with :forward_agent option set to falsey by default" do
943
+ Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
944
+ opts[:forward_agent].nil?
945
+ }.returns(connection)
946
+
947
+ cmd
948
+ end
949
+
950
+ it "with :forward_agent option set if given in config" do
951
+ config[:forward_agent] = "yeah?"
952
+
953
+ Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
954
+ opts[:forward_agent].must_equal "yeah?"
955
+ }.returns(connection)
956
+
957
+ cmd
958
+ end
959
+
960
+ it "with :forward_agent option set if given in state" do
961
+ state[:forward_agent] = "yeah?"
962
+
963
+ Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
964
+ opts[:forward_agent].must_equal "yeah?"
965
+ }.returns(connection)
966
+
967
+ cmd
968
+ end
969
+
970
+ it "with :port option set to 22 by default" do
971
+ Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
972
+ opts[:port].must_equal 22
973
+ }.returns(connection)
974
+
975
+ cmd
976
+ end
977
+
978
+ it "with :port option set if customized in config" do
979
+ config[:port] = 1234
980
+
981
+ Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
982
+ opts[:port].must_equal 1234
983
+ }.returns(connection)
984
+
985
+ cmd
986
+ end
987
+
988
+ it "with :port option set if customized in state" do
989
+ state[:port] = 9999
990
+
991
+ Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
992
+ opts[:port].must_equal 9999
993
+ }.returns(connection)
994
+
995
+ cmd
996
+ end
997
+
998
+ it "with :logger option set to driver's logger" do
999
+ Kitchen::SSH.expects(:new).with { |_hostname, _username, opts|
1000
+ opts[:logger].must_equal logger
1001
+ }.returns(connection)
1002
+
1003
+ cmd
1004
+ end
1005
+ end
1006
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
1007
+
1008
+ describe "#run_remote" do
1009
+
1010
+ let(:cmd) { driver.use_run_remote(state, "huh") }
1011
+ let(:connection) { stub(:exec => true) }
1012
+
1013
+ before do
1014
+ state[:hostname] = "fizzy"
1015
+ state[:username] = "bork"
1016
+ end
1017
+
1018
+ constructs_an_ssh_object
1019
+
1020
+ it "invokes the #install_command with :http_proxy set in config" do
1021
+ config[:http_proxy] = "http://proxy"
1022
+ Kitchen::SSH.stubs(:new).returns(connection)
1023
+ connection.expects(:exec).with("env http_proxy=http://proxy huh")
1024
+
1025
+ cmd
1026
+ end
1027
+
1028
+ it "invokes the #install_command with :https_proxy set in config" do
1029
+ config[:https_proxy] = "https://proxy"
1030
+ Kitchen::SSH.stubs(:new).returns(connection)
1031
+ connection.expects(:exec).with("env https_proxy=https://proxy huh")
1032
+
1033
+ cmd
1034
+ end
1035
+
1036
+ it "invokes the #install_command with :ftp_proxy set in config" do
1037
+ config[:ftp_proxy] = "ftp://proxy"
1038
+ Kitchen::SSH.stubs(:new).returns(connection)
1039
+ connection.expects(:exec).with("env ftp_proxy=ftp://proxy huh")
1040
+
1041
+ cmd
1042
+ end
1043
+
1044
+ it "invokes the #install_command with :http_proxy & :https_proxy & :ftp_proxy set" do
1045
+ config[:http_proxy] = "http://proxy"
1046
+ config[:https_proxy] = "https://proxy"
1047
+ config[:ftp_proxy] = "ftp://proxy"
1048
+ Kitchen::SSH.stubs(:new).returns(connection)
1049
+ connection.expects(:exec).with(
1050
+ "env http_proxy=http://proxy https_proxy=https://proxy ftp_proxy=ftp://proxy huh")
1051
+
1052
+ cmd
1053
+ end
1054
+
1055
+ it "doesn't invoke an ssh command if command is nil" do
1056
+ Kitchen::SSH.stubs(:new).returns(mock)
1057
+
1058
+ driver.use_run_remote(state, nil)
1059
+ end
1060
+
1061
+ it "raises an ActionFailed on transfer when SSHFailed is raised" do
1062
+ Kitchen::SSH.stubs(:new).returns(connection)
1063
+ connection.stubs(:exec).raises(Kitchen::SSHFailed.new("dang"))
1064
+
1065
+ proc { cmd }.must_raise Kitchen::ActionFailed
1066
+ end
1067
+
1068
+ it "raises an ActionFailed on exec when Net::SSH:Exception is raised" do
1069
+ Kitchen::SSH.stubs(:new).returns(connection)
1070
+ connection.stubs(:exec).raises(Net::SSH::Exception.new("dang"))
1071
+
1072
+ proc { cmd }.must_raise Kitchen::ActionFailed
1073
+ end
1074
+ end
1075
+
1076
+ describe "#transfer_path" do
1077
+
1078
+ let(:cmd) { driver.use_transfer_path(state, ["nope"], "nadda") }
1079
+ let(:connection) { stub(:upload_path! => true) }
1080
+
1081
+ before do
1082
+ state[:hostname] = "fizzy"
1083
+ state[:username] = "bork"
1084
+ end
1085
+
1086
+ constructs_an_ssh_object
1087
+
1088
+ it "doesn't invoke an scp command if locals is nil" do
1089
+ Kitchen::SSH.stubs(:new).returns(mock)
1090
+
1091
+ driver.use_transfer_path(state, nil, "nope")
1092
+ end
1093
+
1094
+ it "doesn't invoke an scp command if locals is an empty array" do
1095
+ Kitchen::SSH.stubs(:new).returns(mock)
1096
+
1097
+ driver.use_transfer_path(state, [], "nope")
1098
+ end
1099
+
1100
+ it "raises an ActionFailed on transfer when SSHFailed is raised" do
1101
+ Kitchen::SSH.stubs(:new).returns(connection)
1102
+ connection.stubs(:upload_path!).raises(Kitchen::SSHFailed.new("dang"))
1103
+
1104
+ proc { cmd }.must_raise Kitchen::ActionFailed
1105
+ end
1106
+
1107
+ it "raises an ActionFailed on exec when Net::SSH:Exception is raised" do
1108
+ Kitchen::SSH.stubs(:new).returns(connection)
1109
+ connection.stubs(:upload_path!).raises(Net::SSH::Exception.new("dang"))
1110
+
1111
+ proc { cmd }.must_raise Kitchen::ActionFailed
1112
+ end
1113
+ end
1114
+ end
1115
+ end