furnish 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,37 +1,8 @@
1
- require 'furnish/provisioners/dummy'
1
+ require 'dummy_classes'
2
2
  require 'furnish/test'
3
3
 
4
- Dummy = Furnish::Provisioner::Dummy unless defined? Dummy
5
-
6
- class StartFailDummy < Dummy
7
- def startup(*args)
8
- super
9
- false
10
- end
11
- end
12
-
13
- class StopFailDummy < Dummy
14
- def shutdown
15
- super
16
- false
17
- end
18
- end
19
-
20
- class StartExceptionDummy < Dummy
21
- def startup(*args)
22
- super
23
- raise "ermagherd startup"
24
- end
25
- end
26
-
27
- class StopExceptionDummy < Dummy
28
- def shutdown(*args)
29
- super
30
- raise "ermagherd shutdown"
31
- end
32
- end
33
-
34
4
  module Furnish
5
+ # These tests are run for both threaded and serial cases.
35
6
  class RestartingSchedulerTestCase < SchedulerTestCase
36
7
  def teardown
37
8
  sched.stop
@@ -49,6 +20,91 @@ module Furnish
49
20
  assert(provisioner.store[ [name, "shutdown"].join("-") ], "dummy provisioner for #{name} recorded the shutdown run")
50
21
  end
51
22
 
23
+ def test_recovery_mode
24
+ [RecoverableDummy, RaisingRecoverableDummy].each do |prov|
25
+ test1 = "#{prov}-test1"
26
+ test2 = "#{prov}-test2"
27
+ test3 = "#{prov}-test3"
28
+
29
+ sched.s(test1, Dummy.new)
30
+ sched.s(test2, prov.new)
31
+ sched.run rescue nil # FIXME oof. maybe move this to indepedent tests for serial mode?
32
+ assert(sched.serial || sched.running?)
33
+ unless sched.serial
34
+ sleep 0.1 while !sched.needs_recovery.has_key?(test2)
35
+ end
36
+ assert(sched.needs_recovery?)
37
+ assert_includes(sched.needs_recovery.keys, test2)
38
+ refute_solved(test2)
39
+ sched.s(test3, Dummy.new, [test2])
40
+ refute_solved(test3)
41
+ assert(sched.serial || sched.running?)
42
+ assert_empty(sched.recover)
43
+ assert(sched.serial || sched.running?)
44
+ if sched.serial
45
+ sched.run rescue nil
46
+ end
47
+ sched.wait_for(test1, test2, test3)
48
+ assert_started(test2)
49
+ assert_started(test3)
50
+ assert_started(test1)
51
+ sched.teardown
52
+ sched.stop
53
+
54
+ test4 = "#{prov}-test4"
55
+ test5 = "#{prov}-test5"
56
+
57
+ [test1, test2, test3, test4, test5].each { |x| x.replace(x + "-permfail") }
58
+
59
+ sched.s(test1, Dummy.new)
60
+ sched.s(test2, prov.new)
61
+ sched.s(test3, Dummy.new, [test2])
62
+ sched.s(test4, FailedRecoverDummy.new)
63
+ sched.s(test5, Dummy.new, [test4])
64
+ sched.run rescue nil
65
+ if sched.serial
66
+ assert(sched.needs_recovery?)
67
+ end
68
+ sched.run rescue nil
69
+ assert(sched.serial || sched.running?)
70
+ unless sched.serial
71
+ sleep 0.1 while !sched.needs_recovery.has_key?(test2)
72
+ sleep 0.1 while !sched.needs_recovery.has_key?(test4)
73
+ end
74
+ assert(sched.needs_recovery?)
75
+ refute_solved(test2)
76
+ refute_solved(test3)
77
+ refute_solved(test4)
78
+ assert(sched.serial || sched.running?)
79
+ assert_equal({ test4 => false }, sched.recover)
80
+ assert(sched.serial || sched.running?)
81
+ if sched.serial
82
+ sched.run rescue nil
83
+ end
84
+ sched.wait_for(test1, test2, test3)
85
+ assert_started(test2)
86
+ assert_started(test3)
87
+ assert_started(test1)
88
+ refute_solved(test4)
89
+ refute_solved(test5)
90
+ sched.force_deprovision = true
91
+ sched.deprovision_group(test4)
92
+ sched.force_deprovision = false
93
+ refute_includes(sched.needs_recovery.keys, test4)
94
+ refute(sched.needs_recovery?)
95
+ sched.s(test4, Dummy.new)
96
+ if sched.serial
97
+ sched.run
98
+ end
99
+ assert(sched.serial || sched.running?)
100
+ sched.wait_for(test4, test5)
101
+ assert_solved(test4) # we tore it down, so assert_started will break here.
102
+ assert_started(test5)
103
+ sched.teardown
104
+ sched.stop
105
+ end
106
+ end
107
+
52
108
  def test_run_tracking
53
109
  #--
54
110
  # This is a tad convoluted. Dummy's startup method sets an ivar which
@@ -66,14 +122,12 @@ module Furnish
66
122
  assert_equal("floop", sched.vm.groups['test1'].first.report.last, 'state was stored after provision success')
67
123
 
68
124
  assert(sched.schedule_provision('test2', [Dummy.new, StartFailDummy.new], []))
69
-
70
- # the next few lines are an unfortunate necessity, sorry.
71
- @monitor.kill rescue nil
72
- assert_raises(RuntimeError) do
73
- sched.run
74
- sleep 0.1 while sched.running?
125
+ sched.run rescue nil # FIXME oof. maybe move this to indepedent tests for serial mode?
126
+ unless sched.serial
127
+ sleep 0.1 while !sched.needs_recovery.has_key?('test2')
75
128
  end
76
-
129
+ assert(sched.serial || sched.running?)
130
+ assert_includes(sched.needs_recovery.keys, 'test2')
77
131
  assert_equal("floop", sched.vm.groups['test2'].first.report.last, "provision failed but state is still stored for the provisions that succeeded")
78
132
  end
79
133
 
@@ -198,23 +252,25 @@ module Furnish
198
252
  tempfiles.push(tf)
199
253
  Furnish.logger = Furnish::Logger.new(tf)
200
254
 
201
- sched.run(false)
255
+ sched.signal_handler = false
256
+ sched.run
202
257
  Process.kill(signal, Process.pid)
203
258
 
204
259
  sched.stop
205
260
  sleep 0.1 # wait for any writes to complete
206
261
 
207
- %w[solved working waiting].each do |section|
262
+ %w[solved working waiting provisioning].each do |section|
208
263
  refute_match(/#{section}/, File.read(tf.path), "#{signal} yielded no output with the #{section} set")
209
264
  end
210
265
 
211
- sched.run(true)
266
+ sched.signal_handler = true
267
+ sched.run
212
268
  Process.kill(signal, Process.pid)
213
269
 
214
270
  sched.stop
215
271
  sleep 0.1 # wait for any writes to complete
216
272
 
217
- %w[solved working waiting].each do |section|
273
+ %w[solved working waiting provisioning].each do |section|
218
274
  assert_match(/#{section}/, File.read(tf.path), "#{signal} yielded output with the #{section} set")
219
275
  end
220
276
  end
@@ -226,10 +282,12 @@ module Furnish
226
282
  def test_provision_failures
227
283
  dummy = StartFailDummy.new
228
284
  assert(sched.schedule_provision('blarg', dummy))
229
- assert_raises(RuntimeError, "Could not provision blarg with provisioner StartFailDummy") do
230
- sched.run
231
- sched.wait_for('blarg')
285
+ sched.run rescue nil # FIXME oof. maybe move this to indepedent tests for serial mode?
286
+ if !sched.serial
287
+ sleep 0.1 while !sched.needs_recovery.has_key?('blarg')
232
288
  end
289
+ assert(sched.serial || sched.running?, 'still running after failure')
290
+ assert_includes(sched.needs_recovery, 'blarg')
233
291
  sched.stop
234
292
  sched.deprovision_group('blarg')
235
293
 
@@ -0,0 +1,68 @@
1
+ require 'helper'
2
+
3
+ class TestAPI < Furnish::TestCase
4
+ def setup
5
+ super
6
+ # NOTE this class is defined in test/dummy_classes.rb
7
+ @klass = APIDummy
8
+ end
9
+
10
+ def test_constructor
11
+ assert_raises(ArgumentError, "Arguments must be a kind of hash") { @klass.new(nil) }
12
+ obj = @klass.new(:foo => 1)
13
+ assert_equal(1, obj.foo, "attrs are set based on contents of constructor")
14
+ assert_raises(ArgumentError) { @klass.new(:quux => 2) }
15
+ assert_raises(ArgumentError) { @klass.new(:bar => 2) }
16
+ assert_raises(ArgumentError) { @klass.new(:foo => "string") }
17
+ end
18
+
19
+ def test_interface
20
+ assert_respond_to(@klass, :furnish_properties)
21
+ assert_respond_to(@klass, :furnish_property)
22
+
23
+ assert_kind_of(Hash, @klass.furnish_properties)
24
+ assert_includes(@klass.furnish_properties, :foo)
25
+ assert_includes(@klass.furnish_properties, :a_string)
26
+ refute_includes(@klass.furnish_properties, :bar)
27
+ assert_kind_of(Hash, @klass.furnish_properties[:foo])
28
+ assert_equal("does things with foo", @klass.furnish_properties[:foo][:description])
29
+ assert_equal(Integer, @klass.furnish_properties[:foo][:type])
30
+
31
+ obj = @klass.new(:foo => 1)
32
+
33
+ assert_respond_to(obj, :furnish_group_name)
34
+ assert_respond_to(obj, :furnish_group_name=)
35
+
36
+ %w[startup shutdown].each do |meth|
37
+ assert_raises(NotImplementedError, "#{meth} method not implemented for #{@klass.name}") { obj.send(meth) }
38
+ end
39
+ end
40
+
41
+ def test_report
42
+ obj = @klass.new(:foo => 1)
43
+ assert_equal(["unknown"], obj.report, "default report uses 'unknown' as the group name")
44
+ obj.furnish_group_name = "frobnik"
45
+ assert_equal(["frobnik"], obj.report, "when furnish_group_name is set, uses that in the report")
46
+ end
47
+
48
+ def test_to_s
49
+ obj = @klass.new(:foo => 1)
50
+ assert_equal("unknown[#{@klass.name}]", obj.to_s, "formatted properly without a furnish group name")
51
+ obj.furnish_group_name = "frobnik"
52
+ assert_equal("frobnik[#{@klass.name}]", obj.to_s, "formatted properly without a furnish group name")
53
+ end
54
+
55
+ def test_equality
56
+ assert_equal(Dummy.new, Dummy.new)
57
+ assert_equal(APIDummy.new(:foo => 1), APIDummy.new(:foo => 1))
58
+ refute_equal(APIDummy.new(:foo => 2), APIDummy.new(:foo => 1))
59
+ refute_equal(Dummy.new, StopFailDummy.new)
60
+ end
61
+
62
+ def test_recovery
63
+ prov = APIDummy.new
64
+ refute(prov.recover(:startup, nil))
65
+ prov = BrokenRecoverAPIDummy.new
66
+ assert_raises(NotImplementedError) { prov.recover(:startup, nil) }
67
+ end
68
+ end
@@ -14,7 +14,7 @@ require 'helper'
14
14
  class TestDummy < Furnish::TestCase
15
15
  def test_defaults
16
16
  dummy = Dummy.new
17
- dummy.name = 'dummy_test'
17
+ dummy.furnish_group_name = 'dummy_test'
18
18
  assert(dummy.startup, 'startup returns true by default')
19
19
  assert(dummy.shutdown, 'shutdown returns true by default')
20
20
  assert_equal(['dummy_test', 'floop'], dummy.report, 'report returns boxed name and ivar by default')
@@ -29,7 +29,7 @@ class TestDummy < Furnish::TestCase
29
29
  machine_names = %w[one two three]
30
30
  machine_names.each do |name|
31
31
  dummy = Dummy.new
32
- dummy.name = name
32
+ dummy.furnish_group_name = name
33
33
  assert(dummy.startup)
34
34
  end
35
35
 
@@ -42,7 +42,7 @@ class TestDummy < Furnish::TestCase
42
42
 
43
43
  machine_names.each do |name|
44
44
  dummy = Dummy.new
45
- dummy.name = name
45
+ dummy.furnish_group_name = name
46
46
  assert(dummy.shutdown)
47
47
  end
48
48
 
@@ -54,7 +54,7 @@ class TestDummy < Furnish::TestCase
54
54
  def test_call_order
55
55
  dummies = Dummy.new, Dummy.new
56
56
  dummies.each_with_index do |x, i|
57
- x.name = "foo"
57
+ x.furnish_group_name = "foo"
58
58
  x.id = "foo#{i}"
59
59
  assert(x.startup)
60
60
  end
@@ -72,10 +72,10 @@ class TestDummy < Furnish::TestCase
72
72
 
73
73
  def test_marshal
74
74
  dummy = Dummy.new
75
- dummy.name = "dummy_marshal_test"
75
+ dummy.furnish_group_name = "dummy_marshal_test"
76
76
  assert(dummy.startup)
77
77
  assert(dummy.shutdown)
78
- assert_equal([dummy.name, 'floop'], dummy.report)
78
+ assert_equal([dummy.furnish_group_name, 'floop'], dummy.report)
79
79
 
80
80
  obj = Palsy::Object.new('dummy')
81
81
  %w[startup shutdown report].each do |meth|
@@ -0,0 +1,211 @@
1
+ require 'helper'
2
+
3
+ class TestProtocol < Furnish::TestCase
4
+ #--
5
+ # see the Furnish::Protocol docs for truth tables and whatnot
6
+ #++
7
+
8
+ def test_setters
9
+ proto = Furnish::Protocol.new
10
+ assert_equal([:requires, :accepts, :yields], Furnish::Protocol::VALIDATOR_NAMES)
11
+
12
+ Furnish::Protocol::VALIDATOR_NAMES.each do |vname|
13
+ proto.send(vname, :foo, "a description", Integer)
14
+ assert_includes(proto[vname], :foo)
15
+ assert_equal({ :description => "a description", :type => Integer }, proto[vname][:foo])
16
+ proto.send(vname, "bar")
17
+ assert_includes(proto[vname], :bar)
18
+ assert_equal({ :description => "", :type => Object }, proto[vname][:bar])
19
+ end
20
+
21
+ refute(proto[:accepts_from_any])
22
+ proto.accepts_from_any(true)
23
+ assert(proto[:accepts_from_any])
24
+ end
25
+
26
+ def test_configure
27
+ proto = Furnish::Protocol.new
28
+ proto.configure do
29
+ requires :foo, "a description", Integer
30
+ accepts "bar"
31
+ yields :quux, "another description", Hash
32
+ end
33
+
34
+ assert_includes(proto[:requires], :foo)
35
+ assert_includes(proto[:accepts], :bar)
36
+ assert_includes(proto[:yields], :quux)
37
+
38
+ assert_equal(
39
+ { :description => "a description", :type => Integer },
40
+ proto[:requires][:foo]
41
+ )
42
+
43
+ assert_equal(
44
+ { :description => "", :type => Object },
45
+ proto[:accepts][:bar]
46
+ )
47
+
48
+ assert_equal(
49
+ { :description => "another description", :type => Hash },
50
+ proto[:yields][:quux]
51
+ )
52
+
53
+ proto = Furnish::Protocol.new
54
+
55
+ assert_raises(RuntimeError) do
56
+ proto.configure do
57
+ accepts_from(Furnish::Protocol.new)
58
+ end
59
+ end
60
+
61
+ proto = Furnish::Protocol.new
62
+
63
+ assert_raises(RuntimeError) do
64
+ proto.configure do
65
+ requires_from(Furnish::Protocol.new)
66
+ end
67
+ end
68
+ end
69
+
70
+ def test_requires_from
71
+ proto1 = Furnish::Protocol.new
72
+ proto2 = Furnish::Protocol.new
73
+
74
+ assert(proto2.requires_from(nil))
75
+
76
+ proto1.yields(:bar)
77
+ proto2.requires(:bar)
78
+
79
+ assert(proto2.requires_from(proto1))
80
+
81
+ proto1 = Furnish::Protocol.new
82
+ refute(proto2.requires_from(proto1))
83
+
84
+ proto1.yields(:bar)
85
+ proto2 = Furnish::Protocol.new
86
+
87
+ assert(proto2.requires_from(proto1))
88
+
89
+ proto2.requires(:bar)
90
+ proto2.requires(:quux)
91
+
92
+ refute(proto2.requires_from(proto1))
93
+
94
+ proto1 = Furnish::Protocol.new
95
+ proto2 = Furnish::Protocol.new
96
+
97
+ proto1.yields(:bar, "bar", Integer)
98
+ proto2.requires(:bar, "bar", Integer)
99
+
100
+ assert(proto2.requires_from(proto1), 'types match')
101
+
102
+ proto1 = Furnish::Protocol.new
103
+ proto2 = Furnish::Protocol.new
104
+
105
+ proto1.yields(:bar, "bar", Integer)
106
+ proto2.requires(:bar, "bar", Fixnum)
107
+
108
+ assert(proto2.requires_from(proto1), 'Integer is an ancestor of Fixnum')
109
+
110
+ proto1 = Furnish::Protocol.new
111
+ proto2 = Furnish::Protocol.new
112
+
113
+ proto1.yields(:bar, "bar", Fixnum)
114
+ proto2.requires(:bar, "bar", Integer)
115
+
116
+ refute(proto2.requires_from(proto1), 'Fixnum is not an ancestor of Integer')
117
+
118
+ proto1 = Furnish::Protocol.new
119
+ proto2 = Furnish::Protocol.new
120
+
121
+ proto1.yields(:bar, "bar", String)
122
+ proto2.requires(:bar, "bar", Integer)
123
+
124
+ refute(proto2.requires_from(proto1), 'completely different types (String/Integer)')
125
+
126
+ proto1 = Furnish::Protocol.new
127
+ proto2 = Furnish::Protocol.new
128
+
129
+ proto1.yields(:bar, "bar")
130
+ proto2.requires(:bar, "bar", Integer)
131
+
132
+ assert(proto2.requires_from(proto1), 'default type is Object, succeeds with other types')
133
+ end
134
+
135
+ def test_accepts_from
136
+ proto1 = Furnish::Protocol.new
137
+ proto2 = Furnish::Protocol.new
138
+
139
+ assert(proto2.accepts_from(nil))
140
+
141
+ proto1.yields(:bar)
142
+ proto2.accepts(:bar)
143
+
144
+ assert(proto2.accepts_from(proto1))
145
+
146
+ proto1 = Furnish::Protocol.new
147
+ refute(proto2.accepts_from(proto1))
148
+
149
+ proto1.yields(:foo)
150
+ refute(proto2.accepts_from(proto1))
151
+
152
+ proto2.accepts_from_any(true)
153
+ assert(proto2.accepts_from(proto1))
154
+
155
+ proto1 = Furnish::Protocol.new
156
+ proto2 = Furnish::Protocol.new
157
+ proto2.accepts(:quux)
158
+ proto2.accepts(:bar)
159
+ proto1.yields(:quux)
160
+
161
+ assert(proto2.accepts_from(proto1))
162
+
163
+ proto1 = Furnish::Protocol.new
164
+ proto2 = Furnish::Protocol.new
165
+
166
+ proto1.yields(:bar, "bar", Integer)
167
+ proto2.accepts(:bar, "bar", Integer)
168
+
169
+ assert(proto2.accepts_from(proto1), 'types match')
170
+
171
+ proto1 = Furnish::Protocol.new
172
+ proto2 = Furnish::Protocol.new
173
+
174
+ proto1.yields(:bar, "bar", Integer)
175
+ proto2.accepts(:bar, "bar", Fixnum)
176
+
177
+ assert(proto2.accepts_from(proto1), 'Integer is an ancestor of Fixnum')
178
+
179
+ proto1 = Furnish::Protocol.new
180
+ proto2 = Furnish::Protocol.new
181
+
182
+ proto1.yields(:bar, "bar", Fixnum)
183
+ proto2.accepts(:bar, "bar", Integer)
184
+
185
+ refute(proto2.accepts_from(proto1), 'Fixnum is not an ancestor of Integer')
186
+
187
+ proto1 = Furnish::Protocol.new
188
+ proto2 = Furnish::Protocol.new
189
+
190
+ proto1.yields(:bar, "bar", String)
191
+ proto2.accepts(:bar, "bar", Integer)
192
+
193
+ refute(proto2.accepts_from(proto1), 'completely different types (String/Integer)')
194
+
195
+ proto1 = Furnish::Protocol.new
196
+ proto2 = Furnish::Protocol.new
197
+
198
+ proto1.yields(:bar, "bar")
199
+ proto2.accepts(:bar, "bar", Integer)
200
+
201
+ assert(proto2.accepts_from(proto1), 'default type is Object, succeeds with other types')
202
+
203
+ proto1 = Furnish::Protocol.new
204
+ proto2 = Furnish::Protocol.new
205
+
206
+ proto1.yields(:foo, "foo")
207
+ proto2.accepts(:bar, "bar", Integer)
208
+
209
+ refute(proto2.accepts_from(proto1), "nothing accepted is yielded")
210
+ end
211
+ end