furnish 0.0.4 → 0.1.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.
@@ -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