test-kitchen 1.2.1 → 1.3.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 (114) hide show
  1. checksums.yaml +4 -4
  2. data/.cane +1 -1
  3. data/.rubocop.yml +3 -0
  4. data/.travis.yml +20 -9
  5. data/CHANGELOG.md +219 -108
  6. data/Gemfile +10 -6
  7. data/Guardfile +38 -9
  8. data/README.md +11 -1
  9. data/Rakefile +21 -37
  10. data/bin/kitchen +4 -4
  11. data/features/kitchen_action_commands.feature +161 -0
  12. data/features/kitchen_console_command.feature +34 -0
  13. data/features/kitchen_diagnose_command.feature +64 -0
  14. data/features/kitchen_init_command.feature +29 -17
  15. data/features/kitchen_list_command.feature +2 -2
  16. data/features/kitchen_login_command.feature +56 -0
  17. data/features/{sink_command.feature → kitchen_sink_command.feature} +0 -0
  18. data/features/kitchen_test_command.feature +88 -0
  19. data/features/step_definitions/gem_steps.rb +8 -6
  20. data/features/step_definitions/git_steps.rb +4 -2
  21. data/features/step_definitions/output_steps.rb +5 -0
  22. data/features/support/env.rb +12 -9
  23. data/lib/kitchen.rb +60 -38
  24. data/lib/kitchen/base64_stream.rb +55 -0
  25. data/lib/kitchen/busser.rb +124 -58
  26. data/lib/kitchen/cli.rb +121 -38
  27. data/lib/kitchen/collection.rb +3 -3
  28. data/lib/kitchen/color.rb +4 -4
  29. data/lib/kitchen/command.rb +78 -11
  30. data/lib/kitchen/command/action.rb +3 -2
  31. data/lib/kitchen/command/console.rb +12 -5
  32. data/lib/kitchen/command/diagnose.rb +17 -3
  33. data/lib/kitchen/command/driver_discover.rb +26 -7
  34. data/lib/kitchen/command/exec.rb +41 -0
  35. data/lib/kitchen/command/list.rb +44 -14
  36. data/lib/kitchen/command/login.rb +2 -1
  37. data/lib/kitchen/command/sink.rb +2 -1
  38. data/lib/kitchen/command/test.rb +5 -4
  39. data/lib/kitchen/config.rb +146 -14
  40. data/lib/kitchen/configurable.rb +314 -0
  41. data/lib/kitchen/data_munger.rb +522 -18
  42. data/lib/kitchen/diagnostic.rb +43 -4
  43. data/lib/kitchen/driver.rb +4 -4
  44. data/lib/kitchen/driver/base.rb +80 -115
  45. data/lib/kitchen/driver/dummy.rb +34 -6
  46. data/lib/kitchen/driver/proxy.rb +14 -3
  47. data/lib/kitchen/driver/ssh_base.rb +61 -7
  48. data/lib/kitchen/errors.rb +109 -9
  49. data/lib/kitchen/generator/driver_create.rb +39 -5
  50. data/lib/kitchen/generator/init.rb +130 -45
  51. data/lib/kitchen/instance.rb +162 -28
  52. data/lib/kitchen/lazy_hash.rb +79 -7
  53. data/lib/kitchen/loader/yaml.rb +159 -27
  54. data/lib/kitchen/logger.rb +267 -21
  55. data/lib/kitchen/logging.rb +30 -3
  56. data/lib/kitchen/login_command.rb +11 -2
  57. data/lib/kitchen/metadata_chopper.rb +2 -2
  58. data/lib/kitchen/provisioner.rb +4 -4
  59. data/lib/kitchen/provisioner/base.rb +107 -103
  60. data/lib/kitchen/provisioner/chef/berkshelf.rb +36 -8
  61. data/lib/kitchen/provisioner/chef/librarian.rb +40 -11
  62. data/lib/kitchen/provisioner/chef_base.rb +206 -167
  63. data/lib/kitchen/provisioner/chef_solo.rb +25 -7
  64. data/lib/kitchen/provisioner/chef_zero.rb +105 -29
  65. data/lib/kitchen/provisioner/dummy.rb +1 -1
  66. data/lib/kitchen/provisioner/shell.rb +21 -6
  67. data/lib/kitchen/rake_tasks.rb +8 -3
  68. data/lib/kitchen/shell_out.rb +15 -18
  69. data/lib/kitchen/ssh.rb +122 -27
  70. data/lib/kitchen/state_file.rb +24 -7
  71. data/lib/kitchen/thor_tasks.rb +9 -4
  72. data/lib/kitchen/util.rb +43 -118
  73. data/lib/kitchen/version.rb +1 -1
  74. data/lib/vendor/hash_recursive_merge.rb +10 -2
  75. data/spec/kitchen/base64_stream_spec.rb +77 -0
  76. data/spec/kitchen/busser_spec.rb +490 -0
  77. data/spec/kitchen/collection_spec.rb +10 -10
  78. data/spec/kitchen/color_spec.rb +2 -2
  79. data/spec/kitchen/config_spec.rb +234 -62
  80. data/spec/kitchen/configurable_spec.rb +490 -0
  81. data/spec/kitchen/data_munger_spec.rb +1070 -862
  82. data/spec/kitchen/diagnostic_spec.rb +79 -0
  83. data/spec/kitchen/driver/base_spec.rb +80 -85
  84. data/spec/kitchen/driver/dummy_spec.rb +43 -14
  85. data/spec/kitchen/driver/proxy_spec.rb +134 -0
  86. data/spec/kitchen/driver/ssh_base_spec.rb +644 -0
  87. data/spec/kitchen/driver_spec.rb +15 -15
  88. data/spec/kitchen/errors_spec.rb +309 -0
  89. data/spec/kitchen/instance_spec.rb +143 -46
  90. data/spec/kitchen/lazy_hash_spec.rb +36 -9
  91. data/spec/kitchen/loader/yaml_spec.rb +237 -226
  92. data/spec/kitchen/logger_spec.rb +419 -0
  93. data/spec/kitchen/logging_spec.rb +59 -0
  94. data/spec/kitchen/login_command_spec.rb +49 -0
  95. data/spec/kitchen/metadata_chopper_spec.rb +82 -0
  96. data/spec/kitchen/platform_spec.rb +4 -4
  97. data/spec/kitchen/provisioner/base_spec.rb +65 -125
  98. data/spec/kitchen/provisioner/chef_base_spec.rb +798 -0
  99. data/spec/kitchen/provisioner/chef_solo_spec.rb +316 -0
  100. data/spec/kitchen/provisioner/chef_zero_spec.rb +624 -0
  101. data/spec/kitchen/provisioner/shell_spec.rb +269 -0
  102. data/spec/kitchen/provisioner_spec.rb +6 -6
  103. data/spec/kitchen/shell_out_spec.rb +143 -0
  104. data/spec/kitchen/ssh_spec.rb +683 -0
  105. data/spec/kitchen/state_file_spec.rb +28 -21
  106. data/spec/kitchen/suite_spec.rb +7 -7
  107. data/spec/kitchen/util_spec.rb +68 -10
  108. data/spec/kitchen_spec.rb +107 -0
  109. data/spec/spec_helper.rb +18 -13
  110. data/support/chef-client-zero.rb +10 -9
  111. data/support/chef_helpers.sh +16 -0
  112. data/support/download_helpers.sh +109 -0
  113. data/test-kitchen.gemspec +42 -33
  114. metadata +107 -33
@@ -0,0 +1,79 @@
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/diagnostic"
22
+
23
+ describe Kitchen::Diagnostic do
24
+
25
+ let(:loader) do
26
+ stub(:diagnose => { :who => "loader" })
27
+ end
28
+
29
+ let(:instances) do
30
+ [
31
+ stub(:name => "i1", :diagnose => { :stuff => "sup" }),
32
+ stub(:name => "i2", :diagnose => { :stuff => "yo" })
33
+ ]
34
+ end
35
+
36
+ it "#read returns a Hash" do
37
+ Kitchen::Diagnostic.new.read.must_be_instance_of Hash
38
+ end
39
+
40
+ it "#read returns the version of Test Kitchen" do
41
+ Kitchen::Diagnostic.new.read["kitchen_version"].must_equal Kitchen::VERSION
42
+ end
43
+
44
+ it "#read returns a timestamp in UTC" do
45
+ Time.stubs(:now).returns(Time.at(0))
46
+
47
+ Kitchen::Diagnostic.new.read["timestamp"].
48
+ must_equal "1970-01-01 00:00:00 UTC"
49
+ end
50
+
51
+ it "#read doesn't return a loader hash if not given one" do
52
+ Kitchen::Diagnostic.new.read.key?("loader").must_equal false
53
+ end
54
+
55
+ it "#read returns the loader's diganose hash if a loader is present" do
56
+ Kitchen::Diagnostic.new(:loader => loader).
57
+ read["loader"].must_equal("who" => "loader")
58
+ end
59
+
60
+ it "#read returns an error hash for loader if error hash is passed in" do
61
+ Kitchen::Diagnostic.new(:loader => { :error => "damn" }).
62
+ read["loader"].must_equal("error" => "damn")
63
+ end
64
+
65
+ it "#read returns an empty hash if no instances were given" do
66
+ Kitchen::Diagnostic.new.read["instances"].must_equal Hash.new
67
+ end
68
+
69
+ it "#read returns the instances' diganose hashes if instances are present" do
70
+ Kitchen::Diagnostic.new(:instances => instances).
71
+ read["instances"].
72
+ must_equal("i1" => { "stuff" => "sup" }, "i2" => { "stuff" => "yo" })
73
+ end
74
+
75
+ it "#read returns an error hash for instances if error hash is passed in" do
76
+ Kitchen::Diagnostic.new(:instances => { :error => "shoot" }).
77
+ read["instances"].must_equal("error" => "shoot")
78
+ end
79
+ end
@@ -16,41 +16,28 @@
16
16
  # See the License for the specific language governing permissions and
17
17
  # limitations under the License.
18
18
 
19
- require_relative '../../spec_helper'
20
- require 'logger'
21
- require 'stringio'
19
+ require_relative "../../spec_helper"
20
+ require "logger"
21
+ require "stringio"
22
22
 
23
- require 'kitchen'
23
+ require "kitchen"
24
24
 
25
25
  module Kitchen
26
26
 
27
27
  module Driver
28
28
 
29
- class StaticDefaults < Base
30
-
31
- default_config :beans, "kidney"
32
- default_config :tunables, { 'flimflam' => 'positate' }
33
- default_config :edible, true
29
+ class Speedy < Base
34
30
  end
35
31
 
36
- class SubclassDefaults < StaticDefaults
32
+ class Dodgy < Base
37
33
 
38
- default_config :yea, "ya"
34
+ no_parallel_for :converge
39
35
  end
40
36
 
41
- class ComputedDefaults < Base
42
-
43
- default_config :beans, "kidney"
44
- default_config :fetch_command, "curl"
45
- default_config :beans_url do |driver|
46
- "http://gim.me/#{driver[:beans]}"
47
- end
48
- default_config :command do |driver|
49
- "#{driver[:fetch_command]} #{driver[:beans_url]}"
50
- end
51
- default_config :fetch_url do |driver|
52
- "http://gim.me/beans-for/#{driver.instance.name}"
53
- end
37
+ class Slow < Base
38
+
39
+ no_parallel_for :create, :destroy
40
+ no_parallel_for :verify
54
41
  end
55
42
  end
56
43
  end
@@ -62,96 +49,104 @@ describe Kitchen::Driver::Base do
62
49
  let(:config) { Hash.new }
63
50
  let(:state) { Hash.new }
64
51
 
65
- let(:instance) do
66
- stub(:name => "coolbeans", :logger => logger, :to_str => "instance")
52
+ let(:busser) do
53
+ stub(:setup_cmd => "setup", :sync_cmd => "sync", :run_cmd => "run")
67
54
  end
68
55
 
69
- describe "user config" do
56
+ let(:instance) do
57
+ stub(
58
+ :name => "coolbeans",
59
+ :logger => logger,
60
+ :busser => busser,
61
+ :to_str => "instance"
62
+ )
63
+ end
70
64
 
71
- let(:driver) do
72
- d = Kitchen::Driver::Base.new(config)
73
- d.instance = instance
74
- d
75
- end
65
+ let(:driver) do
66
+ Kitchen::Driver::Base.new(config).finalize_config!(instance)
67
+ end
76
68
 
77
- it "injects config into driver" do
78
- config[:fruit] = %w{apples oranges}
79
- config[:cool_enough] = true
69
+ it "#instance returns its instance" do
70
+ driver.instance.must_equal instance
71
+ end
80
72
 
81
- driver[:fruit].must_equal ['apples', 'oranges']
82
- driver[:cool_enough].must_equal true
83
- end
73
+ it "#name returns the name of the driver" do
74
+ driver.name.must_equal "Base"
84
75
  end
85
76
 
86
- describe "static default config" do
77
+ describe "#logger" do
87
78
 
88
- let(:driver) do
89
- d = Kitchen::Driver::StaticDefaults.new(config)
90
- d.instance = instance
91
- d
92
- end
79
+ before { @klog = Kitchen.logger }
80
+ after { Kitchen.logger = @klog }
93
81
 
94
- it "uses default config" do
95
- driver[:beans].must_equal "kidney"
96
- driver[:tunables]['flimflam'].must_equal 'positate'
97
- driver[:edible].must_equal true
82
+ it "returns the instance's logger if defined" do
83
+ driver.send(:logger).must_equal logger
98
84
  end
99
85
 
100
- it "uses user config over default config" do
101
- config[:beans] = "pinto"
102
- config[:edible] = false
86
+ it "returns the default logger if instance's logger is not set" do
87
+ driver = Kitchen::Driver::Base.new(config)
88
+ Kitchen.logger = "yep"
103
89
 
104
- driver[:beans].must_equal "pinto"
105
- driver[:edible].must_equal false
90
+ driver.send(:logger).must_equal Kitchen.logger
106
91
  end
107
92
  end
108
93
 
109
- describe "inherited static default config" do
94
+ it "#puts calls logger.info" do
95
+ driver.send(:puts, "yo")
110
96
 
111
- let(:driver) do
112
- p = Kitchen::Driver::SubclassDefaults.new(config)
113
- p.instance = instance
114
- p
115
- end
97
+ logged_output.string.must_match(/I, /)
98
+ logged_output.string.must_match(/yo\n/)
99
+ end
116
100
 
117
- it "contains defaults from superclass" do
118
- driver[:beans].must_equal "kidney"
119
- driver[:tunables]['flimflam'].must_equal 'positate'
120
- driver[:edible].must_equal true
121
- driver[:yea].must_equal "ya"
122
- end
101
+ it "#print calls logger.info" do
102
+ driver.send(:print, "yo")
103
+
104
+ logged_output.string.must_match(/I, /)
105
+ logged_output.string.must_match(/yo\n/)
106
+ end
123
107
 
124
- it "uses user config over default config" do
125
- config[:beans] = "pinto"
126
- config[:edible] = false
108
+ [:create, :converge, :setup, :verify, :destroy].each do |action|
127
109
 
128
- driver[:beans].must_equal "pinto"
129
- driver[:edible].must_equal false
130
- driver[:yea].must_equal "ya"
110
+ it "has a #{action} method that takes state" do
111
+ state = Hash.new
112
+ driver.public_send(action, state).must_be_nil
131
113
  end
132
114
  end
133
115
 
134
- describe "computed default config" do
116
+ it "has a login command that raises ActionFailed by default" do
117
+ proc { driver.login_command(Hash.new) }.must_raise Kitchen::ActionFailed
118
+ end
135
119
 
136
- let(:driver) do
137
- d = Kitchen::Driver::ComputedDefaults.new(config)
138
- d.instance = instance
139
- d
140
- end
120
+ it "has a default verify dependencies method" do
121
+ driver.verify_dependencies.must_be_nil
122
+ end
141
123
 
142
- it "uses computed config" do
143
- driver[:beans_url].must_equal "http://gim.me/kidney"
144
- driver[:command].must_equal "curl http://gim.me/kidney"
124
+ it "#busser returns the instance's busser" do
125
+ driver.send(:busser).must_equal busser
126
+ end
127
+
128
+ describe ".no_parallel_for" do
129
+
130
+ it "registers no serial actions when none are declared" do
131
+ Kitchen::Driver::Speedy.serial_actions.must_equal nil
145
132
  end
146
133
 
147
- it "has access to instance object" do
148
- driver[:fetch_url].must_equal "http://gim.me/beans-for/coolbeans"
134
+ it "registers a single serial action method" do
135
+ Kitchen::Driver::Dodgy.serial_actions.must_equal [:converge]
149
136
  end
150
137
 
151
- it "uses user config over default config" do
152
- config[:command] = "echo listentome"
138
+ it "registers multiple serial action methods" do
139
+ actions = Kitchen::Driver::Slow.serial_actions
140
+
141
+ actions.must_include :create
142
+ actions.must_include :verify
143
+ actions.must_include :destroy
144
+ end
153
145
 
154
- driver[:command].must_equal "echo listentome"
146
+ it "raises a ClientError if value is not an action method" do
147
+ proc {
148
+ Class.new(Kitchen::Driver::Base) { no_parallel_for :telling_stories }
149
+ }.must_raise Kitchen::ClientError
155
150
  end
156
151
  end
157
152
  end
@@ -16,11 +16,11 @@
16
16
  # See the License for the specific language governing permissions and
17
17
  # limitations under the License.
18
18
 
19
- require_relative '../../spec_helper'
20
- require 'logger'
21
- require 'stringio'
19
+ require_relative "../../spec_helper"
20
+ require "logger"
21
+ require "stringio"
22
22
 
23
- require 'kitchen/driver/dummy'
23
+ require "kitchen/driver/dummy"
24
24
 
25
25
  describe Kitchen::Driver::Dummy do
26
26
 
@@ -34,9 +34,7 @@ describe Kitchen::Driver::Dummy do
34
34
  end
35
35
 
36
36
  let(:driver) do
37
- d = Kitchen::Driver::Dummy.new(config)
38
- d.instance = instance
39
- d
37
+ Kitchen::Driver::Dummy.new(config).finalize_config!(instance)
40
38
  end
41
39
 
42
40
  describe "default_config" do
@@ -55,7 +53,7 @@ describe Kitchen::Driver::Dummy do
55
53
  it "sets :my_id to a unique value as an example" do
56
54
  driver.create(state)
57
55
 
58
- state[:my_id].must_match /^coolbeans-/
56
+ state[:my_id].must_match(/^coolbeans-/)
59
57
  end
60
58
 
61
59
  it "calls sleep if :sleep value is greater than 0" do
@@ -65,6 +63,12 @@ describe Kitchen::Driver::Dummy do
65
63
  driver.create(state)
66
64
  end
67
65
 
66
+ it "raises ActionFailed if :fail_create is set" do
67
+ config[:fail_create] = true
68
+
69
+ proc { driver.create(state) }.must_raise Kitchen::ActionFailed
70
+ end
71
+
68
72
  it "randomly raises ActionFailed if :random_failure is set" do
69
73
  config[:random_failure] = true
70
74
  driver.stubs(:randomly_fail?).returns(true)
@@ -77,16 +81,17 @@ describe Kitchen::Driver::Dummy do
77
81
 
78
82
  begin
79
83
  driver.create(state)
80
- rescue Kitchen::ActionFailed => ex
84
+ rescue Kitchen::ActionFailed
81
85
  # If exception is anything other than Kitchen::ActionFailed, this spec
82
86
  # will fail
87
+ true
83
88
  end
84
89
  end
85
90
 
86
91
  it "logs a create event to INFO" do
87
92
  driver.create(state)
88
93
 
89
- logged_output.string.must_match /^.+ INFO .+ \[Dummy\] Create on .+$/
94
+ logged_output.string.must_match(/^.+ INFO .+ \[Dummy\] Create on .+$/)
90
95
  end
91
96
  end
92
97
 
@@ -99,6 +104,12 @@ describe Kitchen::Driver::Dummy do
99
104
  driver.create(state)
100
105
  end
101
106
 
107
+ it "raises ActionFailed if :fail_converge is set" do
108
+ config[:fail_converge] = true
109
+
110
+ proc { driver.converge(state) }.must_raise Kitchen::ActionFailed
111
+ end
112
+
102
113
  it "randomly raises ActionFailed if :random_failure is set" do
103
114
  config[:random_failure] = true
104
115
  driver.stubs(:randomly_fail?).returns(true)
@@ -109,7 +120,7 @@ describe Kitchen::Driver::Dummy do
109
120
  it "logs a converge event to INFO" do
110
121
  driver.converge(state)
111
122
 
112
- logged_output.string.must_match /^.+ INFO .+ \[Dummy\] Converge on .+$/
123
+ logged_output.string.must_match(/^.+ INFO .+ \[Dummy\] Converge on .+$/)
113
124
  end
114
125
  end
115
126
 
@@ -122,6 +133,12 @@ describe Kitchen::Driver::Dummy do
122
133
  driver.setup(state)
123
134
  end
124
135
 
136
+ it "raises ActionFailed if :fail_setup is set" do
137
+ config[:fail_setup] = true
138
+
139
+ proc { driver.setup(state) }.must_raise Kitchen::ActionFailed
140
+ end
141
+
125
142
  it "randomly raises ActionFailed if :random_failure is set" do
126
143
  config[:random_failure] = true
127
144
  driver.stubs(:randomly_fail?).returns(true)
@@ -132,7 +149,7 @@ describe Kitchen::Driver::Dummy do
132
149
  it "logs a setup event to INFO" do
133
150
  driver.setup(state)
134
151
 
135
- logged_output.string.must_match /^.+ INFO .+ \[Dummy\] Setup on .+$/
152
+ logged_output.string.must_match(/^.+ INFO .+ \[Dummy\] Setup on .+$/)
136
153
  end
137
154
  end
138
155
 
@@ -145,6 +162,12 @@ describe Kitchen::Driver::Dummy do
145
162
  driver.verify(state)
146
163
  end
147
164
 
165
+ it "raises ActionFailed if :fail_verify is set" do
166
+ config[:fail_verify] = true
167
+
168
+ proc { driver.verify(state) }.must_raise Kitchen::ActionFailed
169
+ end
170
+
148
171
  it "randomly raises ActionFailed if :random_failure is set" do
149
172
  config[:random_failure] = true
150
173
  driver.stubs(:randomly_fail?).returns(true)
@@ -155,7 +178,7 @@ describe Kitchen::Driver::Dummy do
155
178
  it "logs a verify event to INFO" do
156
179
  driver.verify(state)
157
180
 
158
- logged_output.string.must_match /^.+ INFO .+ \[Dummy\] Verify on .+$/
181
+ logged_output.string.must_match(/^.+ INFO .+ \[Dummy\] Verify on .+$/)
159
182
  end
160
183
  end
161
184
 
@@ -175,6 +198,12 @@ describe Kitchen::Driver::Dummy do
175
198
  driver.verify(state)
176
199
  end
177
200
 
201
+ it "raises ActionFailed if :fail_destroy is set" do
202
+ config[:fail_destroy] = true
203
+
204
+ proc { driver.destroy(state) }.must_raise Kitchen::ActionFailed
205
+ end
206
+
178
207
  it "randomly raises ActionFailed if :random_failure is set" do
179
208
  config[:random_failure] = true
180
209
  driver.stubs(:randomly_fail?).returns(true)
@@ -185,7 +214,7 @@ describe Kitchen::Driver::Dummy do
185
214
  it "logs a destroy event to INFO" do
186
215
  driver.destroy(state)
187
216
 
188
- logged_output.string.must_match /^.+ INFO .+ \[Dummy\] Destroy on .+$/
217
+ logged_output.string.must_match(/^.+ INFO .+ \[Dummy\] Destroy on .+$/)
189
218
  end
190
219
  end
191
220
  end
@@ -0,0 +1,134 @@
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/driver/proxy"
22
+
23
+ describe Kitchen::Driver::Proxy do
24
+
25
+ let(:logged_output) { StringIO.new }
26
+ let(:logger) { Logger.new(logged_output) }
27
+ let(:state) { Hash.new }
28
+
29
+ let(:config) do
30
+ { :host => "foobnoobs.com", :reset_command => "mulligan" }
31
+ end
32
+
33
+ let(:instance) do
34
+ stub(:name => "coolbeans", :logger => logger, :to_str => "instance")
35
+ end
36
+
37
+ let(:driver) do
38
+ Kitchen::Driver::Proxy.new(config).finalize_config!(instance)
39
+ end
40
+
41
+ describe "non-parallel action" do
42
+
43
+ it "create must be serially executed" do
44
+ Kitchen::Driver::Proxy.serial_actions.must_include :create
45
+ end
46
+
47
+ it "destroy must be serially executed" do
48
+ Kitchen::Driver::Proxy.serial_actions.must_include :destroy
49
+ end
50
+ end
51
+
52
+ describe "required_config" do
53
+
54
+ [:host, :reset_command].each do |attr|
55
+ it "requires :#{attr}" do
56
+ config.delete(attr)
57
+
58
+ begin
59
+ driver
60
+ flunk "UserError must be raised for missing :#{attr}"
61
+ rescue Kitchen::UserError => e
62
+ e.message.must_match regexify("config[:#{attr}] cannot be blank")
63
+ end
64
+ end
65
+ end
66
+
67
+ def regexify(str)
68
+ Regexp.new(Regexp.escape(str))
69
+ end
70
+ end
71
+
72
+ describe "#create" do
73
+
74
+ it "sets :hostname in state config" do
75
+ driver.stubs(:ssh)
76
+ driver.create(state)
77
+
78
+ state[:hostname].must_equal "foobnoobs.com"
79
+ end
80
+
81
+ it "calls the reset command over ssh" do
82
+ driver.expects(:ssh).with do |ssh_args, cmd|
83
+ ssh_args[0].must_equal "foobnoobs.com"
84
+ cmd.must_equal "mulligan"
85
+ end
86
+
87
+ driver.create(state)
88
+ end
89
+
90
+ it "skips ssh call if :reset_command is falsey" do
91
+ config[:reset_command] = false
92
+ driver.expects(:ssh).never
93
+
94
+ driver.create(state)
95
+ end
96
+ end
97
+
98
+ describe "#destroy" do
99
+
100
+ before do
101
+ state[:hostname] = "beep"
102
+ end
103
+
104
+ it "deletes :hostname in state config" do
105
+ driver.stubs(:ssh)
106
+ driver.destroy(state)
107
+
108
+ state[:hostname].must_equal nil
109
+ end
110
+
111
+ it "calls the reset command over ssh" do
112
+ driver.expects(:ssh).with do |ssh_args, cmd|
113
+ ssh_args[0].must_equal "beep"
114
+ cmd.must_equal "mulligan"
115
+ end
116
+
117
+ driver.destroy(state)
118
+ end
119
+
120
+ it "skips ssh call if :hostname is not in state config" do
121
+ state.delete(:hostname)
122
+ driver.expects(:ssh).never
123
+
124
+ driver.destroy(state)
125
+ end
126
+
127
+ it "skips ssh call if :reset_command is falsey" do
128
+ config[:reset_command] = false
129
+ driver.expects(:ssh).never
130
+
131
+ driver.destroy(state)
132
+ end
133
+ end
134
+ end