furnish 0.0.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.
- data/.gitignore +19 -0
- data/Gemfile +6 -0
- data/Guardfile +10 -0
- data/LICENSE.txt +22 -0
- data/README.md +110 -0
- data/Rakefile +31 -0
- data/furnish.gemspec +29 -0
- data/lib/furnish/logger.rb +110 -0
- data/lib/furnish/provisioner.rb +46 -0
- data/lib/furnish/provisioner_group.rb +132 -0
- data/lib/furnish/provisioners/dummy.rb +87 -0
- data/lib/furnish/scheduler.rb +410 -0
- data/lib/furnish/version.rb +4 -0
- data/lib/furnish/vm.rb +39 -0
- data/lib/furnish.rb +51 -0
- data/test/helper.rb +15 -0
- data/test/mt_cases.rb +267 -0
- data/test/test_dummy.rb +95 -0
- data/test/test_logger.rb +68 -0
- data/test/test_provisioner_group.rb +51 -0
- data/test/test_scheduler_basic.rb +33 -0
- data/test/test_scheduler_serial.rb +16 -0
- data/test/test_scheduler_threaded.rb +44 -0
- data/test/test_vm.rb +18 -0
- metadata +212 -0
data/test/mt_cases.rb
ADDED
@@ -0,0 +1,267 @@
|
|
1
|
+
require 'furnish/provisioners/dummy'
|
2
|
+
|
3
|
+
Dummy = Furnish::Provisioner::Dummy unless defined? Dummy
|
4
|
+
|
5
|
+
class StartFailDummy < Dummy
|
6
|
+
def startup(*args)
|
7
|
+
super
|
8
|
+
false
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class StopFailDummy < Dummy
|
13
|
+
def shutdown
|
14
|
+
super
|
15
|
+
false
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class StartExceptionDummy < Dummy
|
20
|
+
def startup(*args)
|
21
|
+
super
|
22
|
+
raise "ermagherd startup"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class StopExceptionDummy < Dummy
|
27
|
+
def shutdown(*args)
|
28
|
+
super
|
29
|
+
raise "ermagherd shutdown"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module Furnish
|
34
|
+
class TestCase < MiniTest::Unit::TestCase
|
35
|
+
def setup
|
36
|
+
@tempfiles ||= []
|
37
|
+
file = Tempfile.new('furnish_db')
|
38
|
+
@tempfiles.push(file)
|
39
|
+
logfile = Tempfile.new('furnish_log')
|
40
|
+
@tempfiles.push(logfile)
|
41
|
+
Furnish.logger = Furnish::Logger.new(logfile, 3)
|
42
|
+
Furnish.init(file.path)
|
43
|
+
return file
|
44
|
+
end
|
45
|
+
|
46
|
+
def teardown
|
47
|
+
Furnish.logger.close
|
48
|
+
Furnish.shutdown
|
49
|
+
@tempfiles.each do |file|
|
50
|
+
file.unlink
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class SchedulerTestCase < TestCase
|
56
|
+
attr_reader :sched
|
57
|
+
|
58
|
+
def setup
|
59
|
+
super
|
60
|
+
@sched = Furnish::Scheduler.new
|
61
|
+
@monitor = Thread.new { loop { @sched.running?; sleep 1 } }
|
62
|
+
@monitor.abort_on_exception = true
|
63
|
+
end
|
64
|
+
|
65
|
+
def teardown
|
66
|
+
@monitor.kill rescue nil
|
67
|
+
super
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class RunningSchedulerTestCase < SchedulerTestCase
|
72
|
+
def teardown
|
73
|
+
sched.stop
|
74
|
+
super
|
75
|
+
end
|
76
|
+
|
77
|
+
def assert_started(name)
|
78
|
+
assert_includes(sched.vm.solved, name, 'scheduler thinks it solved it')
|
79
|
+
assert(sched.vm.groups[name].first.store[ [name, "startup"].join("-") ], "dummy provisioner for #{name} recorded the startup run")
|
80
|
+
refute(sched.vm.groups[name].first.store[ [name, "shutdown"].join("-") ], "dummy provisioner for #{name} has not recorded the shutdown run")
|
81
|
+
end
|
82
|
+
|
83
|
+
def assert_shutdown(name, provisioner)
|
84
|
+
refute_includes(sched.vm.solved, name, 'scheduler thinks it solved it')
|
85
|
+
assert(provisioner.store[ [name, "shutdown"].join("-") ], "dummy provisioner for #{name} recorded the shutdown run")
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_provision_cycle
|
89
|
+
machine_names = %w[blarg blarg2 blarg3]
|
90
|
+
|
91
|
+
machine_names.each do |name|
|
92
|
+
assert(sched.schedule_provision(name, Dummy.new))
|
93
|
+
end
|
94
|
+
|
95
|
+
sched.run
|
96
|
+
sched.wait_for(*machine_names)
|
97
|
+
|
98
|
+
machine_names.each do |name|
|
99
|
+
assert_started(name)
|
100
|
+
end
|
101
|
+
|
102
|
+
machine_provs = machine_names.map { |n| sched.vm.groups[n].first }
|
103
|
+
|
104
|
+
sched.teardown
|
105
|
+
|
106
|
+
machine_names.each_with_index do |name, i|
|
107
|
+
assert_shutdown(name, machine_provs[i])
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_dependent_provision
|
112
|
+
# since we can't reliably predict linear order, we just paritition it by
|
113
|
+
# how the dependency resolver should sort things out. This isn't perfect by
|
114
|
+
# any means, but allows us to check the dependency resolver.
|
115
|
+
machine_order = {
|
116
|
+
"blarg1" => %w[blarg2 blarg3],
|
117
|
+
"blarg2" => %w[blarg4],
|
118
|
+
"blarg3" => %w[blarg4],
|
119
|
+
"blarg4" => [],
|
120
|
+
"blarg5" => []
|
121
|
+
}
|
122
|
+
|
123
|
+
assert(sched.schedule_provision('blarg1', Dummy.new))
|
124
|
+
assert(sched.schedule_provision('blarg2', Dummy.new, %w[blarg1]))
|
125
|
+
assert(sched.schedule_provision('blarg3', Dummy.new, %w[blarg1]))
|
126
|
+
assert(sched.schedule_provision('blarg4', Dummy.new, %w[blarg2 blarg3]))
|
127
|
+
assert(sched.schedule_provision('blarg5', Dummy.new))
|
128
|
+
|
129
|
+
sched.run
|
130
|
+
sched.wait_for(*machine_order.keys)
|
131
|
+
|
132
|
+
1.upto(5) { |x| assert_started("blarg#{x}") }
|
133
|
+
|
134
|
+
order = Dummy.new.order
|
135
|
+
possible_next = Set[*%w[blarg1 blarg5]]
|
136
|
+
|
137
|
+
while machine = order.shift
|
138
|
+
assert_includes(possible_next, machine, "machine was matched in possible nexts")
|
139
|
+
machine_order[machine].each do |nexts|
|
140
|
+
possible_next.add(nexts)
|
141
|
+
end
|
142
|
+
|
143
|
+
possible_next.delete(machine)
|
144
|
+
end
|
145
|
+
|
146
|
+
machine_provs = (1..5).map { |n| sched.vm.groups["blarg#{n}"].first }
|
147
|
+
|
148
|
+
sched.teardown
|
149
|
+
|
150
|
+
1.upto(5) { |x| assert_shutdown("blarg#{x}", machine_provs[x-1]) }
|
151
|
+
end
|
152
|
+
|
153
|
+
def test_multiprovision_order
|
154
|
+
dummies = [Dummy.new, Dummy.new]
|
155
|
+
dummies.each_with_index { |x,i| x.id = i }
|
156
|
+
assert(sched.schedule_provision('blarg', dummies))
|
157
|
+
sched.run
|
158
|
+
sched.wait_for('blarg')
|
159
|
+
assert_equal(dummies.map(&:id), dummies.first.call_order.to_a)
|
160
|
+
dummies.first.call_order.clear
|
161
|
+
assert_empty(dummies.first.call_order.to_a)
|
162
|
+
sched.teardown
|
163
|
+
assert_equal(dummies.reverse.map(&:id), dummies.first.call_order.to_a)
|
164
|
+
end
|
165
|
+
|
166
|
+
def test_single_deprovision
|
167
|
+
assert(sched.schedule_provision('blarg', Dummy.new))
|
168
|
+
assert(sched.schedule_provision('blarg2', Dummy.new))
|
169
|
+
assert(sched.schedule_provision('blarg3', Dummy.new, %w[blarg2]))
|
170
|
+
|
171
|
+
sched.run
|
172
|
+
sched.wait_for('blarg', 'blarg2', 'blarg3')
|
173
|
+
|
174
|
+
%w[blarg blarg2 blarg3].each do |name|
|
175
|
+
assert_includes(sched.vm.solved, name, "#{name} is in the solved list")
|
176
|
+
end
|
177
|
+
|
178
|
+
sched.teardown_group("blarg")
|
179
|
+
|
180
|
+
[sched.vm.solved, sched.vm.groups.keys].each do |coll|
|
181
|
+
assert_includes(coll, "blarg2", "blarg2 is still available")
|
182
|
+
assert_includes(coll, "blarg3", "blarg3 is still available")
|
183
|
+
refute_includes(coll, "blarg", "blarg is not still available")
|
184
|
+
end
|
185
|
+
|
186
|
+
#
|
187
|
+
# vm.dependencies doesn't track empty references, so deprovisions that have
|
188
|
+
# dependencies need some extra checks to ensure their behavior. Basically
|
189
|
+
# this just means they can't be tested generically.
|
190
|
+
#
|
191
|
+
assert_includes(sched.vm.dependencies.keys, "blarg3", "blarg3 still has dependencies")
|
192
|
+
sched.teardown_group("blarg3")
|
193
|
+
refute_includes(sched.vm.dependencies.keys, "blarg3", "blarg3 still has dependencies")
|
194
|
+
end
|
195
|
+
|
196
|
+
def test_run_arguments
|
197
|
+
tempfiles = []
|
198
|
+
|
199
|
+
signals = %w[INFO USR2]
|
200
|
+
|
201
|
+
signals.each do |signal|
|
202
|
+
if Signal.list[signal] # not everyone has INFO
|
203
|
+
Signal.trap(signal) { nil } if Signal.list[signal]
|
204
|
+
|
205
|
+
tf = Tempfile.new('furnish_signal_handlers')
|
206
|
+
tempfiles.push(tf)
|
207
|
+
Furnish.logger = Furnish::Logger.new(tf)
|
208
|
+
|
209
|
+
sched.run(false)
|
210
|
+
Process.kill(signal, Process.pid)
|
211
|
+
|
212
|
+
sched.stop
|
213
|
+
sleep 0.1 # wait for any writes to complete
|
214
|
+
|
215
|
+
%w[solved working waiting].each do |section|
|
216
|
+
refute_match(/#{section}/, File.read(tf.path), "#{signal} yielded no output with the #{section} set")
|
217
|
+
end
|
218
|
+
|
219
|
+
sched.run(true)
|
220
|
+
Process.kill(signal, Process.pid)
|
221
|
+
|
222
|
+
sched.stop
|
223
|
+
sleep 0.1 # wait for any writes to complete
|
224
|
+
|
225
|
+
%w[solved working waiting].each do |section|
|
226
|
+
assert_match(/#{section}/, File.read(tf.path), "#{signal} yielded output with the #{section} set")
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
ensure
|
231
|
+
tempfiles.each { |f| f.unlink }
|
232
|
+
end
|
233
|
+
|
234
|
+
def test_provision_failures
|
235
|
+
dummy = StartFailDummy.new
|
236
|
+
assert(sched.schedule_provision('blarg', dummy))
|
237
|
+
assert_raises(RuntimeError, "Could not provision blarg with provisioner StartFailDummy") do
|
238
|
+
sched.run
|
239
|
+
sched.wait_for('blarg')
|
240
|
+
end
|
241
|
+
sched.stop
|
242
|
+
sched.deprovision_group('blarg')
|
243
|
+
|
244
|
+
# tests scheduler crashes not keeping the scheduler from being restarted
|
245
|
+
assert(sched.schedule_provision('blarg', Dummy.new))
|
246
|
+
sched.run
|
247
|
+
sched.wait_for('blarg')
|
248
|
+
sched.stop
|
249
|
+
assert_includes(sched.vm.solved, "blarg")
|
250
|
+
sched.teardown
|
251
|
+
refute_includes(sched.vm.solved, "blarg")
|
252
|
+
|
253
|
+
dummy = StopFailDummy.new
|
254
|
+
assert(sched.schedule_provision('blarg', StopFailDummy.new))
|
255
|
+
sched.run
|
256
|
+
sched.wait_for('blarg')
|
257
|
+
sched.stop
|
258
|
+
assert_includes(sched.vm.solved, "blarg")
|
259
|
+
assert_raises(RuntimeError) { sched.teardown }
|
260
|
+
assert_includes(sched.vm.solved, "blarg")
|
261
|
+
sched.force_deprovision = true
|
262
|
+
sched.teardown
|
263
|
+
refute_includes(sched.vm.solved, "blarg")
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
data/test/test_dummy.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
#
|
2
|
+
# Just some small tests for the dummy provisioner to ensure it's sane. Since
|
3
|
+
# it's used heavily in the scheduler tests, it's important to ensure it itself
|
4
|
+
# works.
|
5
|
+
#
|
6
|
+
# That said, the dummy provisioner should not be used for anything "real" and
|
7
|
+
# this test is largely an exercise in ensuring the rest of the test suite is
|
8
|
+
# not lying to it. Nothing here gets tested that should be used outside of the
|
9
|
+
# test suite.
|
10
|
+
#
|
11
|
+
|
12
|
+
require 'helper'
|
13
|
+
|
14
|
+
class TestDummy < Furnish::TestCase
|
15
|
+
def test_defaults
|
16
|
+
dummy = Dummy.new
|
17
|
+
dummy.name = 'dummy_test'
|
18
|
+
assert(dummy.startup, 'startup returns true by default')
|
19
|
+
assert(dummy.shutdown, 'shutdown returns true by default')
|
20
|
+
assert_equal(['dummy_test'], dummy.report, 'report returns boxed name by default')
|
21
|
+
|
22
|
+
obj = Palsy::Object.new('dummy')
|
23
|
+
%w[startup shutdown report].each do |meth|
|
24
|
+
assert(obj["dummy_test-#{meth}"], "#{meth} persists a breadcrumb when run")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_order_check
|
29
|
+
machine_names = %w[one two three]
|
30
|
+
machine_names.each do |name|
|
31
|
+
dummy = Dummy.new
|
32
|
+
dummy.name = name
|
33
|
+
assert(dummy.startup)
|
34
|
+
end
|
35
|
+
|
36
|
+
dummy = Dummy.new
|
37
|
+
assert_kind_of(Furnish::Provisioner::Dummy, dummy)
|
38
|
+
assert_equal(machine_names, dummy.order.to_a, 'order was respected')
|
39
|
+
|
40
|
+
assert(dummy.order.clear)
|
41
|
+
assert_empty(dummy.order.to_a)
|
42
|
+
|
43
|
+
machine_names.each do |name|
|
44
|
+
dummy = Dummy.new
|
45
|
+
dummy.name = name
|
46
|
+
assert(dummy.shutdown)
|
47
|
+
end
|
48
|
+
|
49
|
+
dummy = Dummy.new
|
50
|
+
assert_kind_of(Furnish::Provisioner::Dummy, dummy)
|
51
|
+
assert_equal(machine_names, dummy.order.to_a, 'order was respected')
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_call_order
|
55
|
+
dummies = Dummy.new, Dummy.new
|
56
|
+
dummies.each_with_index do |x, i|
|
57
|
+
x.name = "foo"
|
58
|
+
x.id = "foo#{i}"
|
59
|
+
assert(x.startup)
|
60
|
+
end
|
61
|
+
|
62
|
+
assert_equal(dummies.map(&:id), dummies.first.call_order.to_a)
|
63
|
+
|
64
|
+
dummies.first.call_order.clear
|
65
|
+
|
66
|
+
dummies.reverse.each do |x|
|
67
|
+
assert(x.startup)
|
68
|
+
end
|
69
|
+
|
70
|
+
assert_equal(dummies.reverse.map(&:id), dummies.first.call_order.to_a)
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_marshal
|
74
|
+
dummy = Dummy.new
|
75
|
+
dummy.name = "dummy_marshal_test"
|
76
|
+
assert(dummy.startup)
|
77
|
+
assert(dummy.shutdown)
|
78
|
+
assert_equal([dummy.name], dummy.report)
|
79
|
+
|
80
|
+
obj = Palsy::Object.new('dummy')
|
81
|
+
%w[startup shutdown report].each do |meth|
|
82
|
+
assert(obj["dummy_marshal_test-#{meth}"], "#{meth} persists a breadcrumb when run")
|
83
|
+
end
|
84
|
+
|
85
|
+
str = Marshal.dump(dummy)
|
86
|
+
refute_empty(str, "dummy was dumped successfully")
|
87
|
+
|
88
|
+
newobj = Marshal.load(str)
|
89
|
+
assert_kind_of(Furnish::Provisioner::Dummy, newobj)
|
90
|
+
refute_equal(dummy.object_id, newobj.object_id)
|
91
|
+
|
92
|
+
assert_equal(obj, dummy.store, "palsy object persists")
|
93
|
+
assert_equal(obj, newobj.store, "palsy object persists")
|
94
|
+
end
|
95
|
+
end
|
data/test/test_logger.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
class TestLogger < Furnish::TestCase
|
5
|
+
def setup
|
6
|
+
super
|
7
|
+
@logger_file = Tempfile.new('furnish_log')
|
8
|
+
@logger = Furnish::Logger.new(@logger_file)
|
9
|
+
end
|
10
|
+
|
11
|
+
def read_logfile
|
12
|
+
File.read(@logger_file.path)
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_defaults
|
16
|
+
logger = Furnish::Logger.new
|
17
|
+
assert_equal($stderr, logger.io, "logger io obj is stderr by default")
|
18
|
+
assert_equal(0, logger.debug_level, "logger debug level is 0 by default")
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_logger_behaves_like_io
|
22
|
+
@logger.puts "ohai"
|
23
|
+
assert_equal("ohai\n", read_logfile)
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_if_debug
|
27
|
+
assert_equal(0, @logger.debug_level, "debug level is 0")
|
28
|
+
@logger.if_debug do
|
29
|
+
puts "foo"
|
30
|
+
end
|
31
|
+
|
32
|
+
assert_empty(read_logfile, "debug level is zero and if_debug defaults at 1")
|
33
|
+
|
34
|
+
@logger.debug_level = 1
|
35
|
+
@logger.if_debug do
|
36
|
+
puts "foo"
|
37
|
+
end
|
38
|
+
|
39
|
+
assert_equal("foo\n", read_logfile, "debug level is 1")
|
40
|
+
|
41
|
+
@logger.if_debug(2) do
|
42
|
+
puts "bar"
|
43
|
+
end
|
44
|
+
|
45
|
+
assert_equal("foo\n", read_logfile, "debug level is 1 and if_debug is 2")
|
46
|
+
|
47
|
+
else_block = proc { puts "quux" }
|
48
|
+
|
49
|
+
@logger.if_debug(2, else_block) do
|
50
|
+
puts "should_not_get_here"
|
51
|
+
end
|
52
|
+
|
53
|
+
assert_equal("foo\nquux\n", read_logfile, "debug level is 1 and else block triggered")
|
54
|
+
|
55
|
+
@logger.debug_level = 2
|
56
|
+
@logger.if_debug(1) do
|
57
|
+
puts "level2"
|
58
|
+
end
|
59
|
+
|
60
|
+
assert_equal("foo\nquux\nlevel2\n", read_logfile, "debug level is 2 and if_debug checking for 1")
|
61
|
+
end
|
62
|
+
|
63
|
+
def teardown
|
64
|
+
@logger.close
|
65
|
+
@logger_file.unlink
|
66
|
+
super
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestProvisionerGroup < Furnish::TestCase
|
4
|
+
def test_constructor
|
5
|
+
dummy = Dummy.new
|
6
|
+
pg = Furnish::ProvisionerGroup.new(dummy, 'blarg')
|
7
|
+
assert_includes(pg, dummy)
|
8
|
+
assert_equal('blarg', pg.name)
|
9
|
+
assert_kind_of(Set, pg.dependencies)
|
10
|
+
assert_empty(pg.dependencies)
|
11
|
+
assert_equal('blarg', dummy.name)
|
12
|
+
|
13
|
+
dummy = Dummy.new
|
14
|
+
pg = Furnish::ProvisionerGroup.new([dummy], 'blarg2', %w[blarg])
|
15
|
+
assert_includes(pg, dummy)
|
16
|
+
assert_equal('blarg2', pg.name)
|
17
|
+
assert_equal(Set['blarg'], pg.dependencies)
|
18
|
+
assert_equal('blarg2', dummy.name)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_up_down
|
22
|
+
store = Palsy::Object.new('dummy')
|
23
|
+
dummy = Dummy.new
|
24
|
+
pg = Furnish::ProvisionerGroup.new(dummy, 'blarg')
|
25
|
+
|
26
|
+
assert(pg.startup, 'started')
|
27
|
+
assert(store[ [pg.name, 'startup'].join("-") ], 'startup ran')
|
28
|
+
assert(pg.shutdown, 'stopped')
|
29
|
+
assert(store[ [pg.name, 'startup'].join("-") ], 'shutdown ran')
|
30
|
+
|
31
|
+
dummy = StartFailDummy.new
|
32
|
+
pg = Furnish::ProvisionerGroup.new(dummy, 'blarg2')
|
33
|
+
assert_raises(RuntimeError, "Could not provision #{pg.name} with provisioner #{dummy.class.name}") { pg.startup }
|
34
|
+
|
35
|
+
dummy = StopFailDummy.new
|
36
|
+
pg = Furnish::ProvisionerGroup.new(dummy, 'blarg3')
|
37
|
+
assert_raises(RuntimeError, "Could not deprovision #{pg.name}/#{dummy.class.name}") { pg.shutdown }
|
38
|
+
pg.shutdown(true)
|
39
|
+
|
40
|
+
dummy = StartExceptionDummy.new
|
41
|
+
pg = Furnish::ProvisionerGroup.new(dummy, 'blarg4')
|
42
|
+
assert_raises(RuntimeError, "Could not provision #{pg.name} with provisioner #{dummy.class.name}") { pg.startup }
|
43
|
+
|
44
|
+
dummy = StopExceptionDummy.new
|
45
|
+
pg = Furnish::ProvisionerGroup.new(dummy, 'blarg4')
|
46
|
+
assert_raises(RuntimeError, "Could not deprovision #{pg.name}/#{dummy.class.name}") { pg.shutdown }
|
47
|
+
pg.shutdown(true)
|
48
|
+
sleep 0.1 # wait for flush
|
49
|
+
assert_match(%r!Deprovision #{dummy.class.name}/#{pg.name} had errors:!, File.binread(Furnish.logger.path))
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestSchedulerBasic < Furnish::SchedulerTestCase
|
4
|
+
def test_schedule_provision
|
5
|
+
sched = Furnish::Scheduler.new
|
6
|
+
|
7
|
+
assert(sched.schedule_provision('blarg', [Dummy.new]), 'we can schedule')
|
8
|
+
assert_includes(sched.vm.waiters.keys, 'blarg', 'exists in the waiters')
|
9
|
+
assert_includes(sched.vm.groups.keys, 'blarg', 'exists in the vm group set')
|
10
|
+
assert_equal(1, sched.vm.groups['blarg'].count, 'one item array')
|
11
|
+
assert_kind_of(Furnish::Provisioner::Dummy, sched.vm.groups['blarg'].first, 'first object is our dummy object')
|
12
|
+
assert_equal('blarg', sched.vm.groups['blarg'].first.name, 'name is set properly')
|
13
|
+
assert_nil(sched.schedule_provision('blarg', [Dummy.new]), 'does not schedule twice')
|
14
|
+
|
15
|
+
assert(sched.schedule_provision('blarg2', Dummy.new), 'scheduling does not need an array')
|
16
|
+
assert_includes(sched.vm.waiters.keys, 'blarg2', 'exists in the waiters')
|
17
|
+
assert_includes(sched.vm.groups.keys, 'blarg2', 'exists in the vm group set')
|
18
|
+
assert_kind_of(Furnish::ProvisionerGroup, sched.vm.groups['blarg2'], 'boxes our single item')
|
19
|
+
assert_kind_of(Furnish::Provisioner::Dummy, sched.vm.groups['blarg2'].first, 'first object is our dummy object')
|
20
|
+
assert_equal('blarg2', sched.vm.groups['blarg2'].first.name, 'name is set properly')
|
21
|
+
|
22
|
+
assert_raises(
|
23
|
+
RuntimeError,
|
24
|
+
"One of your dependencies for blarg3 has not been pre-declared. Cannot continue"
|
25
|
+
) do
|
26
|
+
sched.schedule_provision('blarg3', Dummy.new, %w[frobnik])
|
27
|
+
end
|
28
|
+
|
29
|
+
assert(sched.schedule_provision('blarg4', Dummy.new, %w[blarg2]), 'scheduled with a dependency')
|
30
|
+
assert_includes(sched.vm.waiters.keys, 'blarg4', 'included in waiters list')
|
31
|
+
assert_includes(sched.vm.dependencies['blarg4'], 'blarg2', 'dependencies are tracked for provision')
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestSchedulerSerial < Furnish::RunningSchedulerTestCase
|
4
|
+
def setup
|
5
|
+
super
|
6
|
+
sched.serial = true
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_threading_constructs
|
10
|
+
assert(sched.schedule_provision('blarg', Dummy.new))
|
11
|
+
sched.run
|
12
|
+
refute(sched.running?)
|
13
|
+
assert_nil(sched.wait_for('blarg'))
|
14
|
+
sched.stop # does not explode
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class SleepyDummy < Dummy
|
4
|
+
def startup(*args)
|
5
|
+
sleep 1
|
6
|
+
super
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class SleepyFailingDummy < SleepyDummy
|
11
|
+
def startup(*args)
|
12
|
+
super
|
13
|
+
return false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class TestSchedulerThreaded < Furnish::RunningSchedulerTestCase
|
18
|
+
def setup
|
19
|
+
super
|
20
|
+
sched.serial = false
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_running
|
24
|
+
assert(sched.schedule_provision('blarg', SleepyDummy.new))
|
25
|
+
sched.run
|
26
|
+
assert(sched.running?, 'running after provision')
|
27
|
+
sched.teardown
|
28
|
+
refute(sched.running?, 'not running after teardown')
|
29
|
+
|
30
|
+
# we have a monitor that's waiting for timeouts in the test suite to abort
|
31
|
+
# it if the scheduler crashes.
|
32
|
+
#
|
33
|
+
# this actually tests that functionality, so kill the monitor prematurely.
|
34
|
+
#
|
35
|
+
@monitor.kill rescue nil
|
36
|
+
assert(sched.schedule_provision('blarg', SleepyFailingDummy.new))
|
37
|
+
sched.run
|
38
|
+
assert(sched.running?, 'running after provision')
|
39
|
+
sleep 3
|
40
|
+
assert_raises(RuntimeError, "Could not provision blarg with provisioner SleepyFailingDummy") { sched.running? }
|
41
|
+
sched.teardown
|
42
|
+
refute(sched.running?, 'not running after teardown')
|
43
|
+
end
|
44
|
+
end
|
data/test/test_vm.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestVM < Furnish::TestCase
|
4
|
+
def test_initialize
|
5
|
+
vm = Furnish::VM.new
|
6
|
+
kinds = {
|
7
|
+
:groups => Palsy::Map,
|
8
|
+
:dependencies => Palsy::Map,
|
9
|
+
:solved => Palsy::Set,
|
10
|
+
:working => Palsy::Set,
|
11
|
+
:waiters => Palsy::Set,
|
12
|
+
}
|
13
|
+
|
14
|
+
kinds.each do |key, klass|
|
15
|
+
assert_kind_of(klass, vm.send(key))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|