test-kitchen 1.0.0.beta.4 → 1.0.0.rc.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +50 -1
- data/Gemfile +1 -1
- data/README.md +18 -7
- data/Rakefile +8 -1
- data/features/kitchen_init_command.feature +90 -11
- data/features/step_definitions/git_steps.rb +3 -0
- data/lib/kitchen/busser.rb +79 -45
- data/lib/kitchen/cli.rb +14 -13
- data/lib/kitchen/config.rb +79 -138
- data/lib/kitchen/data_munger.rb +224 -0
- data/lib/kitchen/driver/base.rb +4 -31
- data/lib/kitchen/driver/ssh_base.rb +6 -16
- data/lib/kitchen/driver.rb +4 -0
- data/lib/kitchen/generator/init.rb +20 -9
- data/lib/kitchen/instance.rb +53 -58
- data/lib/kitchen/lazy_hash.rb +50 -0
- data/lib/kitchen/platform.rb +2 -31
- data/lib/kitchen/provisioner/base.rb +55 -9
- data/lib/kitchen/provisioner/chef/berkshelf.rb +76 -0
- data/lib/kitchen/provisioner/chef/librarian.rb +72 -0
- data/lib/kitchen/provisioner/chef_base.rb +159 -78
- data/lib/kitchen/provisioner/chef_solo.rb +6 -36
- data/lib/kitchen/provisioner/chef_zero.rb +70 -59
- data/lib/kitchen/provisioner/dummy.rb +28 -0
- data/lib/kitchen/provisioner.rb +6 -4
- data/lib/kitchen/shell_out.rb +2 -5
- data/lib/kitchen/ssh.rb +1 -1
- data/lib/kitchen/suite.rb +10 -79
- data/lib/kitchen/util.rb +2 -2
- data/lib/kitchen/version.rb +2 -2
- data/lib/kitchen.rb +5 -0
- data/spec/kitchen/config_spec.rb +84 -123
- data/spec/kitchen/data_munger_spec.rb +1412 -0
- data/spec/kitchen/driver/base_spec.rb +30 -0
- data/spec/kitchen/instance_spec.rb +868 -86
- data/spec/kitchen/lazy_hash_spec.rb +63 -0
- data/spec/kitchen/platform_spec.rb +0 -22
- data/spec/kitchen/provisioner/base_spec.rb +210 -0
- data/spec/kitchen/provisioner_spec.rb +70 -0
- data/spec/kitchen/suite_spec.rb +25 -38
- data/spec/spec_helper.rb +1 -0
- data/support/chef-client-zero.rb +51 -35
- data/support/dummy-validation.pem +27 -0
- data/templates/init/kitchen.yml.erb +10 -22
- data/test-kitchen.gemspec +1 -2
- metadata +20 -18
@@ -33,6 +33,11 @@ module Kitchen
|
|
33
33
|
default_config :edible, true
|
34
34
|
end
|
35
35
|
|
36
|
+
class SubclassDefaults < StaticDefaults
|
37
|
+
|
38
|
+
default_config :yea, "ya"
|
39
|
+
end
|
40
|
+
|
36
41
|
class ComputedDefaults < Base
|
37
42
|
|
38
43
|
default_config :beans, "kidney"
|
@@ -101,6 +106,31 @@ describe Kitchen::Driver::Base do
|
|
101
106
|
end
|
102
107
|
end
|
103
108
|
|
109
|
+
describe "inherited static default config" do
|
110
|
+
|
111
|
+
let(:driver) do
|
112
|
+
p = Kitchen::Driver::SubclassDefaults.new(config)
|
113
|
+
p.instance = instance
|
114
|
+
p
|
115
|
+
end
|
116
|
+
|
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
|
123
|
+
|
124
|
+
it "uses user config over default config" do
|
125
|
+
config[:beans] = "pinto"
|
126
|
+
config[:edible] = false
|
127
|
+
|
128
|
+
driver[:beans].must_equal "pinto"
|
129
|
+
driver[:edible].must_equal false
|
130
|
+
driver[:yea].must_equal "ya"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
104
134
|
describe "computed default config" do
|
105
135
|
|
106
136
|
let(:driver) do
|
@@ -17,7 +17,6 @@
|
|
17
17
|
# limitations under the License.
|
18
18
|
|
19
19
|
require_relative '../spec_helper'
|
20
|
-
require 'logger'
|
21
20
|
require 'stringio'
|
22
21
|
|
23
22
|
require 'kitchen/logging'
|
@@ -25,141 +24,924 @@ require 'kitchen/instance'
|
|
25
24
|
require 'kitchen/driver'
|
26
25
|
require 'kitchen/driver/dummy'
|
27
26
|
require 'kitchen/platform'
|
27
|
+
require 'kitchen/provisioner'
|
28
|
+
require 'kitchen/provisioner/dummy'
|
28
29
|
require 'kitchen/suite'
|
29
30
|
|
30
|
-
|
31
|
+
class DummyStateFile
|
32
|
+
|
33
|
+
def initialize(*args) ; end
|
34
|
+
|
35
|
+
def read
|
36
|
+
@_state = Hash.new unless @_state
|
37
|
+
@_state.dup
|
38
|
+
end
|
39
|
+
|
40
|
+
def write(state)
|
41
|
+
@_state = state.dup
|
42
|
+
end
|
43
|
+
|
44
|
+
def destroy
|
45
|
+
@_state = nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class SerialDummyDriver < Kitchen::Driver::Dummy
|
31
50
|
|
32
|
-
|
33
|
-
|
34
|
-
|
51
|
+
no_parallel_for :create, :verify, :destroy
|
52
|
+
|
53
|
+
attr_reader :action_in_mutex
|
54
|
+
|
55
|
+
def track_locked(action)
|
56
|
+
@action_in_mutex = Hash.new unless @action_in_mutex
|
57
|
+
@action_in_mutex[action] = Kitchen::Instance.mutexes[self.class].locked?
|
58
|
+
end
|
59
|
+
|
60
|
+
def create(state)
|
61
|
+
track_locked(:create)
|
62
|
+
super
|
63
|
+
end
|
64
|
+
|
65
|
+
def converge(state)
|
66
|
+
track_locked(:converge)
|
67
|
+
super
|
68
|
+
end
|
69
|
+
|
70
|
+
def setup(state)
|
71
|
+
track_locked(:setup)
|
72
|
+
super
|
35
73
|
end
|
36
74
|
|
37
|
-
|
38
|
-
|
39
|
-
|
75
|
+
def verify(state)
|
76
|
+
track_locked(:verify)
|
77
|
+
super
|
40
78
|
end
|
41
79
|
|
42
|
-
|
80
|
+
def destroy(state)
|
81
|
+
track_locked(:destroy)
|
82
|
+
super
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe Kitchen::Instance do
|
87
|
+
|
88
|
+
let(:driver) { Kitchen::Driver::Dummy.new({}) }
|
89
|
+
let(:logger_io) { StringIO.new }
|
90
|
+
let(:logger) { Kitchen::Logger.new(:logdev => logger_io) }
|
91
|
+
let(:instance) { Kitchen::Instance.new(opts) }
|
92
|
+
let(:provisioner) { Kitchen::Provisioner::Dummy.new({}) }
|
93
|
+
let(:state_file) { DummyStateFile.new }
|
94
|
+
let(:busser) { Kitchen::Busser.new(suite.name, {}) }
|
43
95
|
|
44
96
|
let(:opts) do
|
45
|
-
{ :suite => suite, :platform => platform, :driver => driver
|
97
|
+
{ :suite => suite, :platform => platform, :driver => driver,
|
98
|
+
:provisioner => provisioner, :busser => busser,
|
99
|
+
:logger => logger, :state_file => state_file }
|
100
|
+
end
|
101
|
+
|
102
|
+
def suite(name = "suite")
|
103
|
+
@suite ||= Kitchen::Suite.new({ :name => name })
|
104
|
+
end
|
105
|
+
|
106
|
+
def platform(name = "platform")
|
107
|
+
@platform ||= Kitchen::Platform.new({ :name => name })
|
108
|
+
end
|
109
|
+
|
110
|
+
describe ".name_for" do
|
111
|
+
|
112
|
+
it "combines the suite and platform names with a dash" do
|
113
|
+
Kitchen::Instance.name_for(suite("suite"), platform("platform")).
|
114
|
+
must_equal "suite-platform"
|
115
|
+
end
|
116
|
+
|
117
|
+
it "squashes periods in suite name" do
|
118
|
+
Kitchen::Instance.name_for(suite("suite.ness"), platform("platform")).
|
119
|
+
must_equal "suiteness-platform"
|
120
|
+
end
|
121
|
+
|
122
|
+
it "squashes periods in platform name" do
|
123
|
+
Kitchen::Instance.name_for(suite("suite"), platform("platform.s")).
|
124
|
+
must_equal "suite-platforms"
|
125
|
+
end
|
126
|
+
|
127
|
+
it "squashes periods in suite and platform names" do
|
128
|
+
Kitchen::Instance.name_for(suite("s.s"), platform("p.p")).
|
129
|
+
must_equal "ss-pp"
|
130
|
+
end
|
131
|
+
|
132
|
+
it "transforms underscores to dashes in suite name" do
|
133
|
+
Kitchen::Instance.name_for(suite("suite_ness"), platform("platform")).
|
134
|
+
must_equal "suite-ness-platform"
|
135
|
+
end
|
136
|
+
|
137
|
+
it "transforms underscores to dashes in platform name" do
|
138
|
+
Kitchen::Instance.name_for(suite("suite"), platform("platform_s")).
|
139
|
+
must_equal "suite-platform-s"
|
140
|
+
end
|
141
|
+
|
142
|
+
it "transforms underscores to dashes in suite and platform names" do
|
143
|
+
Kitchen::Instance.name_for(suite("_s__s_"), platform("pp_")).
|
144
|
+
must_equal "-s--s--pp-"
|
145
|
+
end
|
46
146
|
end
|
47
147
|
|
48
|
-
|
148
|
+
describe "#suite" do
|
49
149
|
|
50
|
-
|
51
|
-
|
52
|
-
|
150
|
+
it "returns its suite" do
|
151
|
+
instance.suite.must_equal suite
|
152
|
+
end
|
153
|
+
|
154
|
+
it "raises an ArgumentError if missing" do
|
155
|
+
opts.delete(:suite)
|
156
|
+
proc { Kitchen::Instance.new(opts) }.must_raise Kitchen::ClientError
|
157
|
+
end
|
53
158
|
end
|
54
159
|
|
55
|
-
|
56
|
-
|
57
|
-
|
160
|
+
describe "#platform" do
|
161
|
+
|
162
|
+
it "returns its platform" do
|
163
|
+
instance.platform.must_equal platform
|
164
|
+
end
|
165
|
+
|
166
|
+
it "raises an ArgumentError if missing" do
|
167
|
+
opts.delete(:platform)
|
168
|
+
proc { Kitchen::Instance.new(opts) }.must_raise Kitchen::ClientError
|
169
|
+
end
|
58
170
|
end
|
59
171
|
|
60
|
-
|
61
|
-
|
172
|
+
describe "#driver" do
|
173
|
+
|
174
|
+
it "returns its driver" do
|
175
|
+
instance.driver.must_equal driver
|
176
|
+
end
|
177
|
+
|
178
|
+
it "raises an ArgumentError if missing" do
|
179
|
+
opts.delete(:driver)
|
180
|
+
proc { Kitchen::Instance.new(opts) }.must_raise Kitchen::ClientError
|
181
|
+
end
|
182
|
+
|
183
|
+
it "sets Driver#instance to itself" do
|
184
|
+
# it's mind-bottling
|
185
|
+
instance.driver.instance.must_equal instance
|
186
|
+
end
|
62
187
|
end
|
63
188
|
|
64
|
-
|
65
|
-
|
189
|
+
describe "#logger" do
|
190
|
+
|
191
|
+
it "returns its logger" do
|
192
|
+
instance.logger.must_equal logger
|
193
|
+
end
|
194
|
+
|
195
|
+
it "uses Kitchen.logger by default" do
|
196
|
+
opts.delete(:logger)
|
197
|
+
instance.logger.must_equal Kitchen.logger
|
198
|
+
end
|
66
199
|
end
|
67
200
|
|
68
|
-
describe "#
|
201
|
+
describe "#provisioner" do
|
69
202
|
|
70
|
-
|
71
|
-
|
72
|
-
:name => suite_name, :run_list => []
|
73
|
-
)
|
74
|
-
opts[:platform] = Kitchen::Platform.new(
|
75
|
-
:name => platform_name
|
76
|
-
)
|
77
|
-
Kitchen::Instance.new(opts)
|
203
|
+
it "returns its provisioner" do
|
204
|
+
instance.provisioner.must_equal provisioner
|
78
205
|
end
|
79
206
|
|
80
|
-
it "
|
81
|
-
|
207
|
+
it "raises an ArgumentError if missing" do
|
208
|
+
opts.delete(:provisioner)
|
209
|
+
proc { Kitchen::Instance.new(opts) }.must_raise Kitchen::ClientError
|
210
|
+
end
|
211
|
+
|
212
|
+
it "sets Provisioner#instance to itself" do
|
213
|
+
# it's mind-bottling
|
214
|
+
instance.provisioner.instance.must_equal instance
|
82
215
|
end
|
216
|
+
end
|
217
|
+
|
218
|
+
describe "#busser" do
|
83
219
|
|
84
|
-
it "
|
85
|
-
|
86
|
-
combo('suite', 'platform.s').name.must_equal "suite-platforms"
|
87
|
-
combo('s.s.', '.p.p').name.must_equal "ss-pp"
|
220
|
+
it "returns its busser" do
|
221
|
+
instance.busser.must_equal busser
|
88
222
|
end
|
89
223
|
|
90
|
-
it "
|
91
|
-
|
92
|
-
|
93
|
-
combo('_s__s_', 'pp_').name.must_equal "-s--s--pp-"
|
224
|
+
it "raises and ArgumentError if missing" do
|
225
|
+
opts.delete(:busser)
|
226
|
+
proc { Kitchen::Instance.new(opts) }.must_raise Kitchen::ClientError
|
94
227
|
end
|
95
228
|
end
|
96
229
|
|
97
|
-
describe
|
230
|
+
describe "#state_file" do
|
231
|
+
|
232
|
+
it "raises an ArgumentError if missing" do
|
233
|
+
opts.delete(:state_file)
|
234
|
+
proc { Kitchen::Instance.new(opts) }.must_raise Kitchen::ClientError
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
it "#name returns it name" do
|
239
|
+
instance.name.must_equal "suite-platform"
|
240
|
+
end
|
241
|
+
|
242
|
+
it "#to_str returns a string representation with its name" do
|
243
|
+
instance.to_str.must_equal "<suite-platform>"
|
244
|
+
end
|
245
|
+
|
246
|
+
it "#login executes the driver's login_command" do
|
247
|
+
driver.stubs(:login_command).with(Hash.new).
|
248
|
+
returns(Kitchen::LoginCommand.new(["echo", "hello"], {:purple => true}))
|
249
|
+
Kernel.expects(:exec).with("echo", "hello", {:purple => true})
|
250
|
+
|
251
|
+
instance.login
|
252
|
+
end
|
253
|
+
|
254
|
+
describe "performing actions" do
|
255
|
+
|
256
|
+
describe "#create" do
|
98
257
|
|
99
|
-
|
258
|
+
describe "with no state" do
|
259
|
+
|
260
|
+
it "calls Driver#create with empty state hash" do
|
261
|
+
driver.expects(:create).with(Hash.new)
|
262
|
+
|
263
|
+
instance.create
|
264
|
+
end
|
265
|
+
|
266
|
+
it "writes the state file with last_action" do
|
267
|
+
instance.create
|
268
|
+
|
269
|
+
state_file.read[:last_action].must_equal "create"
|
270
|
+
end
|
271
|
+
|
272
|
+
it "logs the action start" do
|
273
|
+
instance.create
|
274
|
+
|
275
|
+
logger_io.string.must_match regex_for("Creating #{instance.to_str}")
|
276
|
+
end
|
277
|
+
|
278
|
+
it "logs the action finish" do
|
279
|
+
instance.create
|
280
|
+
|
281
|
+
logger_io.string.
|
282
|
+
must_match regex_for("Finished creating #{instance.to_str}")
|
283
|
+
end
|
100
284
|
|
101
|
-
def combo(suite_list, platform_list)
|
102
|
-
opts[:suite] = Kitchen::Suite.new(
|
103
|
-
:name => 'suite', :run_list => suite_list
|
104
|
-
).extend(Kitchen::Suite::Cheflike)
|
105
|
-
opts[:platform] = Kitchen::Platform.new(
|
106
|
-
:name => 'platform', :run_list => platform_list
|
107
|
-
).extend(Kitchen::Platform::Cheflike)
|
108
|
-
Kitchen::Instance.new(opts).extend(Kitchen::Instance::Cheflike)
|
109
285
|
end
|
110
286
|
|
111
|
-
|
112
|
-
|
287
|
+
describe "with last_action of create" do
|
288
|
+
|
289
|
+
before { state_file.write({ :last_action => "create"}) }
|
290
|
+
|
291
|
+
it "calls Driver#create with state hash" do
|
292
|
+
driver.expects(:create).
|
293
|
+
with { |state| state[:last_action] == "create" }
|
294
|
+
|
295
|
+
instance.create
|
296
|
+
end
|
297
|
+
|
298
|
+
it "writes the state file with last_action" do
|
299
|
+
instance.create
|
300
|
+
|
301
|
+
state_file.read[:last_action].must_equal "create"
|
302
|
+
end
|
113
303
|
end
|
304
|
+
end
|
305
|
+
|
306
|
+
describe "#converge" do
|
307
|
+
|
308
|
+
describe "with no state" do
|
309
|
+
|
310
|
+
it "calls Driver#create and converge with empty state hash" do
|
311
|
+
driver.expects(:create).with(Hash.new)
|
312
|
+
driver.expects(:converge).
|
313
|
+
with { |state| state[:last_action] == "create" }
|
314
|
+
|
315
|
+
instance.converge
|
316
|
+
end
|
317
|
+
|
318
|
+
it "writes the state file with last_action" do
|
319
|
+
instance.converge
|
320
|
+
|
321
|
+
state_file.read[:last_action].must_equal "converge"
|
322
|
+
end
|
114
323
|
|
115
|
-
|
116
|
-
|
324
|
+
it "logs the action start" do
|
325
|
+
instance.converge
|
326
|
+
|
327
|
+
logger_io.string.must_match regex_for("Converging #{instance.to_str}")
|
328
|
+
end
|
329
|
+
|
330
|
+
it "logs the action finish" do
|
331
|
+
instance.converge
|
332
|
+
|
333
|
+
logger_io.string.
|
334
|
+
must_match regex_for("Finished converging #{instance.to_str}")
|
335
|
+
end
|
117
336
|
end
|
118
337
|
|
119
|
-
|
120
|
-
|
338
|
+
describe "with last action of create" do
|
339
|
+
|
340
|
+
before { state_file.write({ :last_action => "create"}) }
|
341
|
+
|
342
|
+
it "calls Driver#converge with state hash" do
|
343
|
+
driver.expects(:converge).
|
344
|
+
with { |state| state[:last_action] == "create" }
|
345
|
+
|
346
|
+
instance.converge
|
347
|
+
end
|
348
|
+
|
349
|
+
it "writes the state file with last_action" do
|
350
|
+
instance.converge
|
351
|
+
|
352
|
+
state_file.read[:last_action].must_equal "converge"
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
describe "with last action of converge" do
|
357
|
+
|
358
|
+
before { state_file.write({ :last_action => "converge"}) }
|
359
|
+
|
360
|
+
it "calls Driver#converge with state hash" do
|
361
|
+
driver.expects(:converge).
|
362
|
+
with { |state| state[:last_action] == "converge" }
|
363
|
+
|
364
|
+
instance.converge
|
365
|
+
end
|
366
|
+
|
367
|
+
it "writes the state file with last_action" do
|
368
|
+
instance.converge
|
369
|
+
|
370
|
+
state_file.read[:last_action].must_equal "converge"
|
371
|
+
end
|
121
372
|
end
|
122
373
|
end
|
123
374
|
|
124
|
-
describe "#
|
375
|
+
describe "#setup" do
|
376
|
+
|
377
|
+
describe "with no state" do
|
378
|
+
|
379
|
+
it "calls Driver#create, converge, and setup with empty state hash" do
|
380
|
+
driver.expects(:create).with(Hash.new)
|
381
|
+
driver.expects(:converge).
|
382
|
+
with { |state| state[:last_action] == "create" }
|
383
|
+
driver.expects(:setup).
|
384
|
+
with { |state| state[:last_action] == "converge" }
|
385
|
+
|
386
|
+
instance.setup
|
387
|
+
end
|
388
|
+
|
389
|
+
it "writes the state file with last_action" do
|
390
|
+
instance.setup
|
125
391
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
392
|
+
state_file.read[:last_action].must_equal "setup"
|
393
|
+
end
|
394
|
+
|
395
|
+
it "logs the action start" do
|
396
|
+
instance.setup
|
397
|
+
|
398
|
+
logger_io.string.must_match regex_for("Setting up #{instance.to_str}")
|
399
|
+
end
|
400
|
+
|
401
|
+
it "logs the action finish" do
|
402
|
+
instance.setup
|
403
|
+
|
404
|
+
logger_io.string.
|
405
|
+
must_match regex_for("Finished setting up #{instance.to_str}")
|
406
|
+
end
|
134
407
|
end
|
135
408
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
:
|
143
|
-
|
409
|
+
describe "with last action of create" do
|
410
|
+
|
411
|
+
before { state_file.write({ :last_action => "create"}) }
|
412
|
+
|
413
|
+
it "calls Driver#converge and setup with state hash" do
|
414
|
+
driver.expects(:converge).
|
415
|
+
with { |state| state[:last_action] == "create" }
|
416
|
+
driver.expects(:setup).
|
417
|
+
with { |state| state[:last_action] == "converge" }
|
418
|
+
|
419
|
+
instance.setup
|
420
|
+
end
|
421
|
+
|
422
|
+
it "writes the state file with last_action" do
|
423
|
+
instance.setup
|
424
|
+
|
425
|
+
state_file.read[:last_action].must_equal "setup"
|
426
|
+
end
|
144
427
|
end
|
145
428
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
429
|
+
describe "with last action of converge" do
|
430
|
+
|
431
|
+
before { state_file.write({ :last_action => "converge"}) }
|
432
|
+
|
433
|
+
it "calls Driver#setup with state hash" do
|
434
|
+
driver.expects(:setup).
|
435
|
+
with { |state| state[:last_action] == "converge" }
|
436
|
+
|
437
|
+
instance.setup
|
438
|
+
end
|
439
|
+
|
440
|
+
it "writes the state file with last_action" do
|
441
|
+
instance.setup
|
442
|
+
|
443
|
+
state_file.read[:last_action].must_equal "setup"
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
447
|
+
describe "with last action of setup" do
|
448
|
+
|
449
|
+
before { state_file.write({ :last_action => "setup"}) }
|
450
|
+
|
451
|
+
it "calls Driver#setup with state hash" do
|
452
|
+
driver.expects(:setup).
|
453
|
+
with { |state| state[:last_action] == "setup" }
|
454
|
+
|
455
|
+
instance.setup
|
456
|
+
end
|
457
|
+
|
458
|
+
it "writes the state file with last_action" do
|
459
|
+
instance.setup
|
460
|
+
|
461
|
+
state_file.read[:last_action].must_equal "setup"
|
462
|
+
end
|
153
463
|
end
|
154
464
|
end
|
155
465
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
466
|
+
describe "#verify" do
|
467
|
+
|
468
|
+
describe "with no state" do
|
469
|
+
|
470
|
+
it "calls Driver#create, converge, setup, and verify with empty state hash" do
|
471
|
+
driver.expects(:create).with(Hash.new)
|
472
|
+
driver.expects(:converge).
|
473
|
+
with { |state| state[:last_action] == "create" }
|
474
|
+
driver.expects(:setup).
|
475
|
+
with { |state| state[:last_action] == "converge" }
|
476
|
+
driver.expects(:verify).
|
477
|
+
with { |state| state[:last_action] == "setup" }
|
478
|
+
|
479
|
+
instance.verify
|
480
|
+
end
|
481
|
+
|
482
|
+
it "writes the state file with last_action" do
|
483
|
+
instance.verify
|
484
|
+
|
485
|
+
state_file.read[:last_action].must_equal "verify"
|
486
|
+
end
|
487
|
+
|
488
|
+
it "logs the action start" do
|
489
|
+
instance.verify
|
490
|
+
|
491
|
+
logger_io.string.must_match regex_for("Verifying #{instance.to_str}")
|
492
|
+
end
|
493
|
+
|
494
|
+
it "logs the action finish" do
|
495
|
+
instance.verify
|
496
|
+
|
497
|
+
logger_io.string.
|
498
|
+
must_match regex_for("Finished verifying #{instance.to_str}")
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
502
|
+
describe "with last of create" do
|
503
|
+
|
504
|
+
before { state_file.write({ :last_action => "create"}) }
|
505
|
+
|
506
|
+
it "calls Driver#converge, setup, and verify with state hash" do
|
507
|
+
driver.expects(:converge).
|
508
|
+
with { |state| state[:last_action] == "create" }
|
509
|
+
driver.expects(:setup).
|
510
|
+
with { |state| state[:last_action] == "converge" }
|
511
|
+
driver.expects(:verify).
|
512
|
+
with { |state| state[:last_action] == "setup" }
|
513
|
+
|
514
|
+
instance.verify
|
515
|
+
end
|
516
|
+
|
517
|
+
it "writes the state file with last_action" do
|
518
|
+
instance.verify
|
519
|
+
|
520
|
+
state_file.read[:last_action].must_equal "verify"
|
521
|
+
end
|
522
|
+
end
|
160
523
|
|
161
|
-
|
162
|
-
|
524
|
+
describe "with last of converge" do
|
525
|
+
|
526
|
+
before { state_file.write({ :last_action => "converge"}) }
|
527
|
+
|
528
|
+
it "calls Driver#setup, and verify with state hash" do
|
529
|
+
driver.expects(:setup).
|
530
|
+
with { |state| state[:last_action] == "converge" }
|
531
|
+
driver.expects(:verify).
|
532
|
+
with { |state| state[:last_action] == "setup" }
|
533
|
+
|
534
|
+
instance.verify
|
535
|
+
end
|
536
|
+
|
537
|
+
it "writes the state file with last_action" do
|
538
|
+
instance.verify
|
539
|
+
|
540
|
+
state_file.read[:last_action].must_equal "verify"
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
describe "with last of setup" do
|
545
|
+
|
546
|
+
before { state_file.write({ :last_action => "setup"}) }
|
547
|
+
|
548
|
+
it "calls Driver#verify with state hash" do
|
549
|
+
driver.expects(:verify).
|
550
|
+
with { |state| state[:last_action] == "setup" }
|
551
|
+
|
552
|
+
instance.verify
|
553
|
+
end
|
554
|
+
|
555
|
+
it "writes the state file with last_action" do
|
556
|
+
instance.verify
|
557
|
+
|
558
|
+
state_file.read[:last_action].must_equal "verify"
|
559
|
+
end
|
560
|
+
end
|
561
|
+
|
562
|
+
describe "with last of verify" do
|
563
|
+
|
564
|
+
before { state_file.write({ :last_action => "verify"}) }
|
565
|
+
|
566
|
+
it "calls Driver#verify with state hash" do
|
567
|
+
driver.expects(:verify).
|
568
|
+
with { |state| state[:last_action] == "verify" }
|
569
|
+
|
570
|
+
instance.verify
|
571
|
+
end
|
572
|
+
|
573
|
+
it "writes the state file with last_action" do
|
574
|
+
instance.verify
|
575
|
+
|
576
|
+
state_file.read[:last_action].must_equal "verify"
|
577
|
+
end
|
578
|
+
end
|
579
|
+
end
|
580
|
+
|
581
|
+
describe "#destroy" do
|
582
|
+
|
583
|
+
describe "with no state" do
|
584
|
+
|
585
|
+
it "calls Driver#destroy with empty state hash" do
|
586
|
+
driver.expects(:destroy).with(Hash.new)
|
587
|
+
|
588
|
+
instance.destroy
|
589
|
+
end
|
590
|
+
|
591
|
+
it "destroys the state file" do
|
592
|
+
state_file.expects(:destroy)
|
593
|
+
|
594
|
+
instance.destroy
|
595
|
+
end
|
596
|
+
|
597
|
+
it "logs the action start" do
|
598
|
+
instance.destroy
|
599
|
+
|
600
|
+
logger_io.string.
|
601
|
+
must_match regex_for("Destroying #{instance.to_str}")
|
602
|
+
end
|
603
|
+
|
604
|
+
it "logs the create finish" do
|
605
|
+
instance.destroy
|
606
|
+
|
607
|
+
logger_io.string.
|
608
|
+
must_match regex_for("Finished destroying #{instance.to_str}")
|
609
|
+
end
|
610
|
+
end
|
611
|
+
|
612
|
+
[:create, :converge, :setup, :verify].each do |action|
|
613
|
+
|
614
|
+
describe "with last_action of #{action}" do
|
615
|
+
|
616
|
+
before { state_file.write({ :last_action => action}) }
|
617
|
+
|
618
|
+
it "calls Driver#create with state hash" do
|
619
|
+
driver.expects(:destroy).
|
620
|
+
with { |state| state[:last_action] == action }
|
621
|
+
|
622
|
+
instance.destroy
|
623
|
+
end
|
624
|
+
|
625
|
+
it "destroys the state file" do
|
626
|
+
state_file.expects(:destroy)
|
627
|
+
|
628
|
+
instance.destroy
|
629
|
+
end
|
630
|
+
end
|
631
|
+
end
|
632
|
+
end
|
633
|
+
|
634
|
+
describe "#test" do
|
635
|
+
|
636
|
+
describe "with no state" do
|
637
|
+
|
638
|
+
it "calls Driver#destroy, create, converge, setup, verify, destroy" do
|
639
|
+
driver.expects(:destroy)
|
640
|
+
driver.expects(:create)
|
641
|
+
driver.expects(:converge)
|
642
|
+
driver.expects(:setup)
|
643
|
+
driver.expects(:verify)
|
644
|
+
driver.expects(:destroy)
|
645
|
+
|
646
|
+
instance.test
|
647
|
+
end
|
648
|
+
|
649
|
+
it "logs the action start" do
|
650
|
+
instance.test
|
651
|
+
|
652
|
+
logger_io.string.must_match regex_for("Testing #{instance.to_str}")
|
653
|
+
end
|
654
|
+
|
655
|
+
it "logs the action finish" do
|
656
|
+
instance.test
|
657
|
+
|
658
|
+
logger_io.string.
|
659
|
+
must_match regex_for("Finished testing #{instance.to_str}")
|
660
|
+
end
|
661
|
+
end
|
662
|
+
|
663
|
+
[:create, :converge, :setup, :verify].each do |action|
|
664
|
+
|
665
|
+
describe "with last action of #{action}" do
|
666
|
+
|
667
|
+
before { state_file.write({ :last_action => action}) }
|
668
|
+
|
669
|
+
it "calls Driver#destroy, create, converge, setup, verify, destroy" do
|
670
|
+
driver.expects(:destroy)
|
671
|
+
driver.expects(:create)
|
672
|
+
driver.expects(:converge)
|
673
|
+
driver.expects(:setup)
|
674
|
+
driver.expects(:verify)
|
675
|
+
driver.expects(:destroy)
|
676
|
+
|
677
|
+
instance.test
|
678
|
+
end
|
679
|
+
end
|
680
|
+
end
|
681
|
+
|
682
|
+
describe "with destroy mode of never" do
|
683
|
+
|
684
|
+
it "calls Driver#destroy, create, converge, setup, verify" do
|
685
|
+
driver.expects(:destroy).once
|
686
|
+
driver.expects(:create)
|
687
|
+
driver.expects(:converge)
|
688
|
+
driver.expects(:setup)
|
689
|
+
driver.expects(:verify)
|
690
|
+
|
691
|
+
instance.test(:never)
|
692
|
+
end
|
693
|
+
end
|
694
|
+
|
695
|
+
describe "with destroy mode of always" do
|
696
|
+
|
697
|
+
it "calls Driver#destroy at even when action fails" do
|
698
|
+
driver.stubs(:converge).raises(Kitchen::ActionFailed)
|
699
|
+
|
700
|
+
driver.expects(:destroy)
|
701
|
+
driver.expects(:create)
|
702
|
+
driver.expects(:converge)
|
703
|
+
driver.expects(:destroy)
|
704
|
+
|
705
|
+
instance.test(:always)
|
706
|
+
end
|
707
|
+
end
|
708
|
+
|
709
|
+
describe "with destroy mode of passing" do
|
710
|
+
|
711
|
+
it "doesn't call Driver#destroy at when action fails" do
|
712
|
+
skip "figure this one out"
|
713
|
+
driver.stubs(:create).raises(Kitchen::ActionFailed, "death")
|
714
|
+
|
715
|
+
driver.expects(:destroy)
|
716
|
+
driver.expects(:create)
|
717
|
+
|
718
|
+
instance.test(:passing)
|
719
|
+
end
|
720
|
+
end
|
721
|
+
end
|
722
|
+
|
723
|
+
[:create, :converge, :setup, :verify, :test].each do |action|
|
724
|
+
|
725
|
+
describe "#{action} on driver crash with ActionFailed" do
|
726
|
+
|
727
|
+
before do
|
728
|
+
driver.stubs(:create).raises(Kitchen::ActionFailed, "death")
|
729
|
+
end
|
730
|
+
|
731
|
+
it "write the state file with last action" do
|
732
|
+
begin
|
733
|
+
instance.public_send(action)
|
734
|
+
rescue Kitchen::Error => e
|
735
|
+
end
|
736
|
+
|
737
|
+
state_file.read[:last_action].must_be_nil
|
738
|
+
end
|
739
|
+
|
740
|
+
it "raises an InstanceFailure" do
|
741
|
+
proc { instance.public_send(action) }.
|
742
|
+
must_raise Kitchen::InstanceFailure
|
743
|
+
end
|
744
|
+
|
745
|
+
it "populates the InstanceFailure message" do
|
746
|
+
begin
|
747
|
+
instance.public_send(action)
|
748
|
+
rescue Kitchen::Error => e
|
749
|
+
e.message.must_match regex_for(
|
750
|
+
"Create failed on instance #{instance.to_str}")
|
751
|
+
end
|
752
|
+
end
|
753
|
+
|
754
|
+
it "logs the failure" do
|
755
|
+
begin
|
756
|
+
instance.public_send(action)
|
757
|
+
rescue Kitchen::Error => e
|
758
|
+
end
|
759
|
+
|
760
|
+
logger_io.string.must_match regex_for(
|
761
|
+
"Create failed on instance #{instance.to_str}")
|
762
|
+
end
|
763
|
+
end
|
764
|
+
|
765
|
+
describe "on driver crash with unexpected exception class" do
|
766
|
+
|
767
|
+
before do
|
768
|
+
driver.stubs(:create).raises(RuntimeError, "watwat")
|
769
|
+
end
|
770
|
+
|
771
|
+
it "write the state file with last action" do
|
772
|
+
begin
|
773
|
+
instance.public_send(action)
|
774
|
+
rescue Kitchen::Error => e
|
775
|
+
end
|
776
|
+
|
777
|
+
state_file.read[:last_action].must_be_nil
|
778
|
+
end
|
779
|
+
|
780
|
+
it "raises an ActionFailed" do
|
781
|
+
proc { instance.public_send(action) }.
|
782
|
+
must_raise Kitchen::ActionFailed
|
783
|
+
end
|
784
|
+
|
785
|
+
it "populates the ActionFailed message" do
|
786
|
+
begin
|
787
|
+
instance.public_send(action)
|
788
|
+
rescue Kitchen::Error => e
|
789
|
+
e.message.must_match regex_for(
|
790
|
+
"Failed to complete #create action: [watwat]")
|
791
|
+
end
|
792
|
+
end
|
793
|
+
|
794
|
+
it "logs the failure" do
|
795
|
+
begin
|
796
|
+
instance.public_send(action)
|
797
|
+
rescue Kitchen::Error => e
|
798
|
+
end
|
799
|
+
|
800
|
+
logger_io.string.must_match regex_for(
|
801
|
+
"Create failed on instance #{instance.to_str}")
|
802
|
+
end
|
803
|
+
end
|
163
804
|
end
|
805
|
+
|
806
|
+
describe "crashes preserve last action for desired verify action" do
|
807
|
+
|
808
|
+
before do
|
809
|
+
driver.stubs(:verify).raises(Kitchen::ActionFailed, "death")
|
810
|
+
end
|
811
|
+
|
812
|
+
[:create, :converge, :setup].each do |action|
|
813
|
+
|
814
|
+
it "for last state #{action}" do
|
815
|
+
state_file.write({ :last_action => action.to_s })
|
816
|
+
begin
|
817
|
+
instance.verify
|
818
|
+
rescue Kitchen::Error => e
|
819
|
+
end
|
820
|
+
|
821
|
+
state_file.read[:last_action].must_equal "setup"
|
822
|
+
end
|
823
|
+
end
|
824
|
+
|
825
|
+
it "for last state verify" do
|
826
|
+
state_file.write({ :last_action => "verify" })
|
827
|
+
begin
|
828
|
+
instance.verify
|
829
|
+
rescue Kitchen::Error => e
|
830
|
+
end
|
831
|
+
|
832
|
+
state_file.read[:last_action].must_equal "verify"
|
833
|
+
end
|
834
|
+
end
|
835
|
+
|
836
|
+
describe "on drivers with serial actions" do
|
837
|
+
|
838
|
+
let(:driver) { SerialDummyDriver.new({}) }
|
839
|
+
|
840
|
+
it "runs in a synchronized block for serial actions" do
|
841
|
+
instance.test
|
842
|
+
|
843
|
+
driver.action_in_mutex[:create].must_equal true
|
844
|
+
driver.action_in_mutex[:converge].must_equal false
|
845
|
+
driver.action_in_mutex[:setup].must_equal false
|
846
|
+
driver.action_in_mutex[:verify].must_equal true
|
847
|
+
driver.action_in_mutex[:destroy].must_equal true
|
848
|
+
end
|
849
|
+
end
|
850
|
+
end
|
851
|
+
|
852
|
+
describe Kitchen::Instance::FSM do
|
853
|
+
|
854
|
+
let(:fsm) { Kitchen::Instance::FSM }
|
855
|
+
|
856
|
+
describe ".actions" do
|
857
|
+
|
858
|
+
it "passing nils returns destroy" do
|
859
|
+
fsm.actions(nil, nil).must_equal [:destroy]
|
860
|
+
end
|
861
|
+
|
862
|
+
it "accepts a string for desired argument" do
|
863
|
+
fsm.actions(nil, "create").must_equal [:create]
|
864
|
+
end
|
865
|
+
|
866
|
+
it "accepts a symbol for desired argument" do
|
867
|
+
fsm.actions(nil, :create).must_equal [:create]
|
868
|
+
end
|
869
|
+
|
870
|
+
it "starting from no state to create returns create" do
|
871
|
+
fsm.actions(nil, :create).must_equal [:create]
|
872
|
+
end
|
873
|
+
|
874
|
+
it "starting from :create to create returns create" do
|
875
|
+
fsm.actions(:create, :create).must_equal [:create]
|
876
|
+
end
|
877
|
+
|
878
|
+
it "starting from no state to converge returns create, converge" do
|
879
|
+
fsm.actions(nil, :converge).must_equal [:create, :converge]
|
880
|
+
end
|
881
|
+
|
882
|
+
it "starting from create to converge returns converge" do
|
883
|
+
fsm.actions(:create, :converge).must_equal [:converge]
|
884
|
+
end
|
885
|
+
|
886
|
+
it "starting from converge to converge returns converge" do
|
887
|
+
fsm.actions(:converge, :converge).must_equal [:converge]
|
888
|
+
end
|
889
|
+
|
890
|
+
it "starting from no state to setup returns create, converge, setup" do
|
891
|
+
fsm.actions(nil, :setup).must_equal [:create, :converge, :setup]
|
892
|
+
end
|
893
|
+
|
894
|
+
it "starting from create to setup returns converge, setup" do
|
895
|
+
fsm.actions(:create, :setup).must_equal [:converge, :setup]
|
896
|
+
end
|
897
|
+
|
898
|
+
it "starting from converge to setup returns setup" do
|
899
|
+
fsm.actions(:converge, :setup).must_equal [:setup]
|
900
|
+
end
|
901
|
+
|
902
|
+
it "starting from setup to setup return setup" do
|
903
|
+
fsm.actions(:setup, :setup).must_equal [:setup]
|
904
|
+
end
|
905
|
+
|
906
|
+
it "starting from no state to verify returns create, converge, setup, verify" do
|
907
|
+
fsm.actions(nil, :verify).must_equal [:create, :converge, :setup, :verify]
|
908
|
+
end
|
909
|
+
|
910
|
+
it "starting from create to verify returns converge, setup, verify" do
|
911
|
+
fsm.actions(:create, :verify).must_equal [:converge, :setup, :verify]
|
912
|
+
end
|
913
|
+
|
914
|
+
it "starting from converge to verify returns setup, verify" do
|
915
|
+
fsm.actions(:converge, :verify).must_equal [:setup, :verify]
|
916
|
+
end
|
917
|
+
|
918
|
+
it "starting from setup to verify returns verify" do
|
919
|
+
fsm.actions(:setup, :verify).must_equal [:verify]
|
920
|
+
end
|
921
|
+
|
922
|
+
it "starting from verify to verify returns verify" do
|
923
|
+
fsm.actions(:verify, :verify).must_equal [:verify]
|
924
|
+
end
|
925
|
+
|
926
|
+
[:verify, :setup, :converge].each do |s|
|
927
|
+
it "starting from #{s} to create returns create" do
|
928
|
+
fsm.actions(s, :create).must_equal [:create]
|
929
|
+
end
|
930
|
+
end
|
931
|
+
|
932
|
+
[:verify, :setup].each do |s|
|
933
|
+
it "starting from #{s} to converge returns converge" do
|
934
|
+
fsm.actions(s, :converge).must_equal [:converge]
|
935
|
+
end
|
936
|
+
end
|
937
|
+
|
938
|
+
it "starting from verify to setup returns setup" do
|
939
|
+
fsm.actions(:verify, :setup).must_equal [:setup]
|
940
|
+
end
|
941
|
+
end
|
942
|
+
end
|
943
|
+
|
944
|
+
def regex_for(string)
|
945
|
+
Regexp.new(Regexp.escape(string))
|
164
946
|
end
|
165
947
|
end
|