flapjack 0.6.53 → 0.6.54

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 (53) hide show
  1. data/bin/flapjack +103 -19
  2. data/bin/flapjack-nagios-receiver +166 -52
  3. data/bin/flapper +107 -18
  4. data/etc/flapjack_config.yaml.example +16 -0
  5. data/features/events.feature +63 -0
  6. data/features/steps/events_steps.rb +5 -5
  7. data/features/steps/notifications_steps.rb +8 -6
  8. data/features/steps/time_travel_steps.rb +4 -4
  9. data/features/support/env.rb +1 -2
  10. data/flapjack.gemspec +1 -1
  11. data/lib/flapjack/configuration.rb +11 -13
  12. data/lib/flapjack/coordinator.rb +100 -220
  13. data/lib/flapjack/data/entity_check.rb +2 -2
  14. data/lib/flapjack/data/event.rb +3 -3
  15. data/lib/flapjack/executive.rb +30 -40
  16. data/lib/flapjack/filters/delays.rb +1 -1
  17. data/lib/flapjack/gateways/api.rb +6 -23
  18. data/lib/flapjack/gateways/email.rb +4 -10
  19. data/lib/flapjack/gateways/email/alert.html.haml +0 -5
  20. data/lib/flapjack/gateways/email/alert.text.erb +0 -1
  21. data/lib/flapjack/gateways/jabber.rb +80 -67
  22. data/lib/flapjack/gateways/oobetet.rb +29 -25
  23. data/lib/flapjack/gateways/pagerduty.rb +26 -45
  24. data/lib/flapjack/gateways/sms_messagenet.rb +10 -17
  25. data/lib/flapjack/gateways/web.rb +7 -21
  26. data/lib/flapjack/gateways/web/views/_css.haml +3 -0
  27. data/lib/flapjack/gateways/web/views/check.haml +1 -1
  28. data/lib/flapjack/logger.rb +57 -0
  29. data/lib/flapjack/patches.rb +0 -10
  30. data/lib/flapjack/pikelet.rb +214 -30
  31. data/lib/flapjack/redis_pool.rb +2 -17
  32. data/lib/flapjack/version.rb +1 -1
  33. data/spec/lib/flapjack/coordinator_spec.rb +116 -136
  34. data/spec/lib/flapjack/data/entity_check_spec.rb +3 -3
  35. data/spec/lib/flapjack/executive_spec.rb +33 -34
  36. data/spec/lib/flapjack/gateways/api_spec.rb +4 -2
  37. data/spec/lib/flapjack/gateways/jabber_spec.rb +39 -36
  38. data/spec/lib/flapjack/gateways/oobetet_spec.rb +14 -24
  39. data/spec/lib/flapjack/gateways/pagerduty_spec.rb +43 -45
  40. data/spec/lib/flapjack/gateways/web_spec.rb +42 -35
  41. data/spec/lib/flapjack/logger_spec.rb +32 -0
  42. data/spec/lib/flapjack/pikelet_spec.rb +124 -15
  43. data/spec/lib/flapjack/redis_pool_spec.rb +1 -3
  44. data/spec/spec_helper.rb +34 -1
  45. data/tasks/events.rake +1 -0
  46. data/tmp/create_event_ok.rb +31 -0
  47. data/tmp/create_event_unknown.rb +31 -0
  48. data/tmp/create_events_ok.rb +1 -1
  49. metadata +10 -11
  50. data/bin/flapjack-nagios-receiver-control +0 -15
  51. data/bin/flapper-control +0 -15
  52. data/lib/flapjack/daemonizing.rb +0 -186
  53. data/lib/flapjack/gateways/base.rb +0 -38
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'log4r'
4
+ require 'log4r/formatter/patternformatter'
5
+ require 'log4r/outputter/consoleoutputters'
6
+ require 'log4r/outputter/syslogoutputter'
7
+
8
+ module Flapjack
9
+
10
+ class Logger
11
+
12
+ def initialize(name, config = {})
13
+ config ||= {}
14
+
15
+ # @name = name
16
+
17
+ @log4r_logger = Log4r::Logger.new(name)
18
+
19
+ formatter = Log4r::PatternFormatter.new(:pattern => "%d [%l] :: #{name} :: %m",
20
+ :date_pattern => "%Y-%m-%dT%H:%M:%S%z")
21
+
22
+ [Log4r::StdoutOutputter, Log4r::SyslogOutputter].each do |outp_klass|
23
+ outp = outp_klass.new(name)
24
+ outp.formatter = formatter
25
+ @log4r_logger.add(outp)
26
+ end
27
+
28
+ configure(config)
29
+ end
30
+
31
+ def configure(config)
32
+ level = config['level']
33
+
34
+ # we'll let Log4r spit the dummy on invalid level values -- but will
35
+ # assume ALL if nothing is provided
36
+ if level.nil? || level.empty?
37
+ level = 'ALL'
38
+ end
39
+
40
+ new_level = Log4r.const_get(level.upcase)
41
+ return if @log4r_logger.level.eql?(new_level)
42
+
43
+ # puts "setting log level for '#{@name}' to '#{level.upcase}'"
44
+ @log4r_logger.level = new_level
45
+ end
46
+
47
+ def method_missing(method, *args, &block)
48
+ @log4r_logger.send(method.to_sym, *args, &block)
49
+ end
50
+
51
+ def respond_to?(sym)
52
+ @log4r_logger.respond_to?(sym)
53
+ end
54
+
55
+ end
56
+
57
+ end
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'ostruct'
4
- require 'daemons'
5
4
  require 'thin'
6
5
  require 'resque'
7
6
  # require 'log4r'
@@ -12,15 +11,6 @@ class OpenStruct
12
11
  end
13
12
  end
14
13
 
15
- module Daemons
16
- class PidFile
17
- # we override this method so creating pid files is fork-safe
18
- def filename
19
- File.join(@dir, "#{@progname}#{Process.pid}.pid")
20
- end
21
- end
22
- end
23
-
24
14
  #module Log4r
25
15
  # class Logger
26
16
  # def error(args)
@@ -8,55 +8,239 @@
8
8
  # with butter, at afternoon tea, but can also be served at morning tea."
9
9
  # from http://en.wikipedia.org/wiki/Pancake
10
10
 
11
- require 'log4r'
12
- require 'log4r/outputter/consoleoutputters'
13
- require 'log4r/outputter/syslogoutputter'
11
+ # the redis/synchrony gems need to be required in this particular order, see
12
+ # the redis-rb README for details
13
+ require 'hiredis'
14
+ require 'redis/connection/synchrony'
15
+ require 'redis'
16
+ require 'em-resque'
17
+ require 'em-resque/worker'
18
+ require 'thin'
19
+
20
+ require 'flapjack/executive'
21
+ require 'flapjack/gateways/api'
22
+ require 'flapjack/gateways/jabber'
23
+ require 'flapjack/gateways/oobetet'
24
+ require 'flapjack/gateways/pagerduty'
25
+ require 'flapjack/gateways/email'
26
+ require 'flapjack/gateways/sms_messagenet'
27
+ require 'flapjack/gateways/web'
28
+ require 'flapjack/logger'
14
29
 
15
30
  module Flapjack
31
+
16
32
  module Pikelet
17
- attr_accessor :logger, :config
18
33
 
19
- # Classes including a pikelet subclass and wanting to extend #bootstrap
20
- # should alias the original method and make sure to call them
21
- # as part of their interstitial method.
22
- def bootstrap(opts = {})
23
- return if @bootstrapped
34
+ # TODO find a better way of expressing these two methods
35
+ def self.is_pikelet?(type)
36
+ type_klass = [Flapjack::Pikelet::Generic, Flapjack::Pikelet::Resque,
37
+ Flapjack::Pikelet::Thin].detect do |kl|
24
38
 
25
- unless @logger = opts[:logger]
26
- @logger = Log4r::Logger.new("#{self.class.to_s.downcase.gsub('::', '-')}")
27
- @logger.add(Log4r::StdoutOutputter.new("flapjack"))
28
- @logger.add(Log4r::SyslogOutputter.new("flapjack"))
29
- end
39
+ kl::PIKELET_TYPES[type]
30
40
 
31
- @config = opts[:config] || {}
41
+ end
42
+ !type_klass.nil?
43
+ end
32
44
 
33
- @bootstrapped = true
45
+ def self.create(type, config = {})
46
+ pikelet = nil
47
+ [Flapjack::Pikelet::Generic,
48
+ Flapjack::Pikelet::Resque,
49
+ Flapjack::Pikelet::Thin].each do |kl|
50
+ break if pikelet = kl.create(type, config)
51
+ end
52
+ pikelet
34
53
  end
35
54
 
36
- # Aliasing isn't currently necessary for #cleanup, as it's empty anyway.
37
- # It's probably best practice to do so, in case that changes.
38
- def cleanup
55
+ class Base
56
+ attr_reader :type, :status
57
+
58
+ def initialize(type, pikelet_class, opts = {})
59
+ @type = type
60
+ @klass = pikelet_class
61
+
62
+ @config = opts[:config] || {}
63
+ @redis_config = opts[:redis_config] || {}
64
+
65
+ @logger = Flapjack::Logger.new("flapjack-#{type}", @config['logger'])
66
+
67
+ @status = 'initialized'
68
+ end
69
+
70
+ def start
71
+ @status = 'started'
72
+ end
73
+
74
+ def reload(cfg)
75
+ @logger.configure(cfg['logger'])
76
+ true
77
+ end
78
+
79
+ def stop
80
+ @status = 'stopping'
81
+ end
39
82
  end
40
83
 
41
- end
84
+ class Generic < Flapjack::Pikelet::Base
85
+
86
+ PIKELET_TYPES = {'executive' => Flapjack::Executive,
87
+ 'jabber' => Flapjack::Gateways::Jabber,
88
+ 'pagerduty' => Flapjack::Gateways::Pagerduty,
89
+ 'oobetet' => Flapjack::Gateways::Oobetet}
90
+
91
+ def self.create(type, config = {})
92
+ return unless pikelet_klass = PIKELET_TYPES[type]
93
+ self.new(type, pikelet_klass, config)
94
+ end
95
+
96
+ def initialize(type, pikelet_klass, opts = {})
97
+ super(type, pikelet_klass, opts)
98
+ @pikelet = @klass.new(opts.merge(:logger => @logger))
99
+ end
42
100
 
43
- module GenericPikelet
44
- include Flapjack::Pikelet
101
+ def start
102
+ @fiber = Fiber.new {
103
+ begin
104
+ @pikelet.start
105
+ rescue Exception => e
106
+ trace = e.backtrace.join("\n")
107
+ @logger.fatal "#{e.message}\n#{trace}"
108
+ stop
109
+ end
110
+ }
111
+ super
112
+ @fiber.resume
113
+ end
114
+
115
+ # this should only reload if all changes can be applied -- will
116
+ # return false to log warning otherwise
117
+ def reload(cfg)
118
+ @pikelet.respond_to?(:reload) ?
119
+ (@pikelet.reload(cfg) && super(cfg)) : super(cfg)
120
+ end
45
121
 
46
- def should_quit?
47
- @should_quit
122
+ def stop
123
+ @pikelet.stop
124
+ super
125
+ end
126
+
127
+ def update_status
128
+ return @status unless 'stopping'.eql?(@status)
129
+ @status = 'stopped' if @fiber && !@fiber.alive?
130
+ end
48
131
  end
49
132
 
50
- def stop
51
- @should_quit = true
133
+ class Resque < Flapjack::Pikelet::Base
134
+
135
+ PIKELET_TYPES = {'email' => Flapjack::Gateways::Email,
136
+ 'sms' => Flapjack::Gateways::SmsMessagenet}
137
+
138
+ def self.create(type, opts = {})
139
+ return unless pikelet_klass = PIKELET_TYPES[type]
140
+ self.new(type, pikelet_klass, opts)
141
+ end
142
+
143
+ def initialize(type, pikelet_klass, opts = {})
144
+ super(type, pikelet_klass, opts)
145
+
146
+ pikelet_klass.instance_variable_set('@config', @config)
147
+ pikelet_klass.instance_variable_set('@redis_config', @redis_config)
148
+ pikelet_klass.instance_variable_set('@logger', @logger)
149
+
150
+ # TODO error if config['queue'].nil?
151
+
152
+ @worker = EM::Resque::Worker.new(@config['queue'])
153
+ # # Use these to debug the resque workers
154
+ # worker.verbose = true
155
+ # worker.very_verbose = true
156
+ end
157
+
158
+ def start
159
+ @fiber = Fiber.new {
160
+ begin
161
+ @worker.work(0.1)
162
+ rescue Exception => e
163
+ trace = e.backtrace.join("\n")
164
+ @logger.fatal "#{e.message}\n#{trace}"
165
+ stop
166
+ end
167
+ }
168
+ super
169
+ @klass.start if @klass.respond_to?(:start)
170
+ @fiber.resume
171
+ end
172
+
173
+ # this should only reload if all changes can be applied -- will
174
+ # return false to log warning otherwise
175
+ def reload(cfg)
176
+ @klass.respond_to?(:reload) ?
177
+ (@klass.reload(cfg) && super(cfg)) : super(cfg)
178
+ end
179
+
180
+ def stop
181
+ @worker.shutdown if @worker && @fiber && @fiber.alive?
182
+ @klass.stop if @klass.respond_to?(:stop)
183
+ super
184
+ end
185
+
186
+ def update_status
187
+ return @status unless 'stopping'.eql?(@status)
188
+ @status = 'stopped' if @fiber && !@fiber.alive?
189
+ end
52
190
  end
53
191
 
54
- alias_method :orig_bootstrap, :bootstrap
192
+ class Thin < Flapjack::Pikelet::Base
55
193
 
56
- def bootstrap(opts = {})
57
- @should_quit = false
194
+ PIKELET_TYPES = {'web' => Flapjack::Gateways::Web,
195
+ 'api' => Flapjack::Gateways::API}
58
196
 
59
- orig_bootstrap(opts)
197
+ def self.create(type, opts = {})
198
+ return unless pikelet_klass = PIKELET_TYPES[type]
199
+ ::Thin::Logging.silent = true
200
+ self.new(type, pikelet_klass, :config => opts[:config], :redis_config => opts[:redis_config])
201
+ end
202
+
203
+ def initialize(type, pikelet_klass, opts = {})
204
+ super(type, pikelet_klass, opts)
205
+
206
+ pikelet_klass.instance_variable_set('@config', @config)
207
+ pikelet_klass.instance_variable_set('@redis_config', @redis_config)
208
+ pikelet_klass.instance_variable_set('@logger', @logger)
209
+
210
+ if @config
211
+ @port = @config['port']
212
+ @port = @port.nil? ? nil : @port.to_i
213
+ end
214
+ @port = 3001 if (@port.nil? || @port <= 0 || @port > 65535)
215
+
216
+ @server = ::Thin::Server.new('0.0.0.0', @port,
217
+ @klass, :signals => false)
218
+ end
219
+
220
+ def start
221
+ super
222
+ @klass.start if @klass.respond_to?(:start)
223
+ @server.start
224
+ end
225
+
226
+ # this should only reload if all changes can be applied -- will
227
+ # return false to log warning otherwise
228
+ def reload(cfg)
229
+ # TODO fail if port changes
230
+ @klass.respond_to?(:reload) ?
231
+ (@klass.reload(cfg) && super(cfg)) : super(cfg)
232
+ end
233
+
234
+ def stop
235
+ @server.stop!
236
+ @klass.stop if @klass.respond_to?(:stop)
237
+ super
238
+ end
239
+
240
+ def update_status
241
+ return @status unless 'stopping'.eql?(@status)
242
+ @status = 'stopped' if (@server.backend.size <= 0)
243
+ end
60
244
  end
61
245
 
62
246
  end
@@ -15,26 +15,11 @@ module Flapjack
15
15
 
16
16
  def initialize(opts = {})
17
17
  config = opts.delete(:config)
18
- super(:size => opts[:size] || 5) {
18
+ @size = opts[:size] || 5
19
+ super(:size => @size) {
19
20
  ::Redis.new(config)
20
21
  }
21
22
  end
22
23
 
23
- def empty!
24
- f = Fiber.current
25
-
26
- until @available.empty? && @pending.empty?
27
- begin
28
- conn = acquire(f)
29
- conn.quit
30
- @available.delete(conn)
31
- ensure
32
- if pending = @pending.shift
33
- pending.resume
34
- end
35
- end
36
- end
37
- end
38
-
39
24
  end
40
25
  end
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  module Flapjack
4
- VERSION = "0.6.53"
4
+ VERSION = "0.6.54"
5
5
  end
@@ -1,12 +1,5 @@
1
1
  require 'spec_helper'
2
2
 
3
- require 'flapjack/configuration'
4
- require 'flapjack/executive'
5
-
6
- require 'flapjack/gateways/web'
7
- require 'flapjack/gateways/jabber'
8
- require 'flapjack/gateways/email'
9
-
10
3
  require 'flapjack/coordinator'
11
4
 
12
5
  describe Flapjack::Coordinator do
@@ -14,57 +7,80 @@ describe Flapjack::Coordinator do
14
7
  let(:fiber) { mock(Fiber) }
15
8
  let(:config) { mock(Flapjack::Configuration) }
16
9
 
17
- # leaving actual testing of daemonisation to that class's tests
18
- it "daemonizes properly" do
19
- config.should_receive(:for_redis).and_return({})
20
- fc = Flapjack::Coordinator.new(config)
10
+ let(:logger) { mock(Logger) }
11
+ let(:stdout_out) { mock('stdout_out') }
12
+ let(:syslog_out) { mock('syslog_out') }
13
+
14
+ def setup_logger
15
+ formatter = mock('Formatter')
16
+ Log4r::PatternFormatter.should_receive(:new).with(
17
+ :pattern => "%d [%l] :: flapjack-coordinator :: %m",
18
+ :date_pattern => "%Y-%m-%dT%H:%M:%S%z").and_return(formatter)
19
+
20
+ stdout_out.should_receive(:formatter=).with(formatter)
21
+ syslog_out.should_receive(:formatter=).with(formatter)
21
22
 
22
- fc.should_receive(:daemonize)
23
- fc.should_not_receive(:build_pikelet)
24
- fc.start(:daemonize => true, :signals => false)
23
+ Log4r::StdoutOutputter.should_receive(:new).and_return(stdout_out)
24
+ Log4r::SyslogOutputter.should_receive(:new).and_return(syslog_out)
25
+ logger.should_receive(:add).with(stdout_out)
26
+ logger.should_receive(:add).with(syslog_out)
27
+ Log4r::Logger.should_receive(:new).and_return(logger)
25
28
  end
26
29
 
27
- it "runs undaemonized" do
30
+ it "starts and stops a pikelet" do
31
+ setup_logger
32
+
33
+ cfg = {'executive' => {'enabled' => 'yes'}}
28
34
  EM.should_receive(:synchrony).and_yield
29
35
  config.should_receive(:for_redis).and_return({})
30
- config.should_receive(:all).and_return('executive' => {'enabled' => 'yes'})
36
+ config.should_receive(:all).and_return(cfg)
37
+
38
+ executive = mock('executive')
39
+ executive.should_receive(:start)
40
+ executive.should_receive(:stop)
41
+ executive.should_receive(:update_status)
42
+ executive.should_receive(:status).exactly(3).times.and_return('stopped')
31
43
 
32
44
  fc = Flapjack::Coordinator.new(config)
33
- fc.should_receive(:build_pikelet)
34
- fc.start(:daemonize => false, :signals => false)
35
- end
45
+ Flapjack::Pikelet.should_receive(:create).with('executive',
46
+ :config => cfg['executive'], :redis_config => {}).and_return(executive)
36
47
 
37
- it "starts after daemonizing" do
38
- EM.should_receive(:synchrony).and_yield
48
+ fiber.should_receive(:resume)
49
+ Fiber.should_receive(:new).and_yield.and_return(fiber)
39
50
 
40
- config.should_receive(:for_redis).and_return({})
41
- config.should_receive(:all).and_return('executive' => {'enabled' => 'yes'})
51
+ EM.should_receive(:stop)
42
52
 
43
- fc = Flapjack::Coordinator.new(config)
44
- fc.should_receive(:build_pikelet)
45
- fc.after_daemonize
53
+ fc.start(:signals => false)
54
+ fc.stop
46
55
  end
47
56
 
48
57
  it "traps system signals and shuts down" do
58
+ setup_logger
59
+
49
60
  RbConfig::CONFIG.should_receive(:[]).with('host_os').and_return('darwin12.0.0')
50
61
 
51
62
  Kernel.should_receive(:trap).with('INT').and_yield
52
63
  Kernel.should_receive(:trap).with('TERM').and_yield
53
64
  Kernel.should_receive(:trap).with('QUIT').and_yield
65
+ Kernel.should_receive(:trap).with('HUP').and_yield
54
66
 
55
67
  config.should_receive(:for_redis).and_return({})
56
68
  fc = Flapjack::Coordinator.new(config)
57
69
  fc.should_receive(:stop).exactly(3).times
70
+ fc.should_receive(:reload)
58
71
 
59
72
  fc.send(:setup_signals)
60
73
  end
61
74
 
62
75
  it "only traps two system signals on Windows" do
76
+ setup_logger
77
+
63
78
  RbConfig::CONFIG.should_receive(:[]).with('host_os').and_return('mswin')
64
79
 
65
80
  Kernel.should_receive(:trap).with('INT').and_yield
66
81
  Kernel.should_receive(:trap).with('TERM').and_yield
67
82
  Kernel.should_not_receive(:trap).with('QUIT')
83
+ Kernel.should_not_receive(:trap).with('HUP')
68
84
 
69
85
  config.should_receive(:for_redis).and_return({})
70
86
  fc = Flapjack::Coordinator.new(config)
@@ -73,145 +89,109 @@ describe Flapjack::Coordinator do
73
89
  fc.send(:setup_signals)
74
90
  end
75
91
 
76
- it "stops its services when closing" do
77
- fiber_exec = mock('fiber_exec')
78
- fiber_rsq = mock('fiber_rsq')
79
-
80
- exec = mock('executive')
81
- exec.should_receive(:is_a?).with(Flapjack::GenericPikelet).twice.and_return(true)
82
- exec.should_receive(:stop)
83
- exec.should_receive(:add_shutdown_event)
84
- exec.should_receive(:cleanup)
85
-
86
- email = mock('worker')
87
- email.should_receive(:is_a?).with(Flapjack::GenericPikelet).twice.and_return(false)
88
- email.should_receive(:shutdown)
89
- Flapjack::Gateways::Email.should_receive(:cleanup)
90
-
91
- backend = mock('backend')
92
- backend.should_receive(:size).twice.and_return(1, 0)
93
- web = mock('web')
94
- web.should_receive(:is_a?).with(Flapjack::GenericPikelet).twice.and_return(false)
95
- web.should_receive(:backend).twice.and_return(backend)
96
- web.should_receive(:stop!)
97
- Flapjack::Gateways::Web.should_receive(:cleanup)
98
-
99
- redis = mock('redis')
100
- redis.should_receive(:quit)
101
- Redis.should_receive(:new).and_return(redis)
92
+ it "stops one pikelet and starts another on reload" do
93
+ setup_logger
102
94
 
103
- fiber.should_receive(:resume)
104
- fiber_stop = mock('fiber_stop')
105
- fiber_stop.should_receive(:resume)
106
- Fiber.should_receive(:new).twice.and_yield.and_return(fiber, fiber_stop)
95
+ old_cfg = {'executive' => {'enabled' => 'yes'}}
96
+ new_cfg = {'gateways' => {'jabber' => {'enabled' => 'yes'}}}
107
97
 
108
- fiber_exec.should_receive(:alive?).exactly(3).times.and_return(true, true, false)
109
- fiber_rsq.should_receive(:alive?).exactly(3).and_return(true, false, false)
98
+ new_config = mock('new_config')
99
+ filename = mock('filename')
110
100
 
111
- EM::Synchrony.should_receive(:sleep)
112
- EM.should_receive(:stop)
101
+ config.should_receive(:all).and_return(old_cfg)
102
+ config.should_receive(:filename).and_return(filename)
113
103
 
114
- pikelets = [{:fiber => fiber_exec, :instance => exec, :class => Flapjack::Executive},
115
- {:fiber => fiber_rsq, :instance => email, :class => Flapjack::Gateways::Email},
116
- {:instance => web, :class => Flapjack::Gateways::Web}]
104
+ Flapjack::Configuration.should_receive(:new).and_return(new_config)
105
+ new_config.should_receive(:load).with(filename)
106
+ new_config.should_receive(:all).and_return(new_cfg)
117
107
 
118
- config.should_receive(:for_redis).and_return({})
108
+ executive = mock('executive')
109
+ executive.should_receive(:type).twice.and_return('executive')
110
+ executive.should_receive(:stop)
111
+ executive.should_receive(:update_status)
112
+ executive.should_receive(:status).exactly(3).times.and_return('stopped')
119
113
 
120
- fc = Flapjack::Coordinator.new(config)
121
- fc.instance_variable_set('@redis_options', {})
122
- fc.instance_variable_set('@pikelets', pikelets)
123
- fc.stop
124
- end
125
-
126
- it "creates an executive pikelet" do
127
- exec = mock('executive')
128
- exec.should_receive(:bootstrap)
129
- Flapjack::Executive.should_receive(:new).and_return(exec)
130
- exec.should_receive(:is_a?).with(Flapjack::GenericPikelet).and_return(true)
131
- exec.should_receive(:main)
114
+ jabber = mock('jabber')
115
+ Flapjack::Pikelet.should_receive(:create).with('jabber',
116
+ :config => {"enabled" => "yes"}, :redis_config => {}).and_return(jabber)
117
+ jabber.should_receive(:start)
132
118
 
133
119
  fiber.should_receive(:resume)
134
120
  Fiber.should_receive(:new).and_yield.and_return(fiber)
135
121
 
136
122
  config.should_receive(:for_redis).and_return({})
137
-
138
123
  fc = Flapjack::Coordinator.new(config)
139
- fc.send(:build_pikelet, Flapjack::Executive, {'enabled' => 'yes'})
140
- pikelets = fc.instance_variable_get('@pikelets')
141
- pikelets.should_not be_nil
142
- pikelets.should be_an(Array)
143
- pikelets.should have(1).pikelet
144
- pikelets.first.should == {:fiber => fiber, :class => Flapjack::Executive, :instance => exec}
124
+ fc.instance_variable_set('@pikelets', [executive])
125
+ fc.reload
126
+ fc.instance_variable_get('@pikelets').should == [jabber]
145
127
  end
146
128
 
147
- it "handles an exception raised by a fiber pikelet" do
148
- jabber = mock('jabber')
149
- jabber.should_receive(:bootstrap)
150
- Flapjack::Gateways::Jabber.should_receive(:new).and_return(jabber)
151
- jabber.should_receive(:is_a?).with(Flapjack::GenericPikelet).and_return(true)
152
- jabber.should_receive(:main).and_raise(RuntimeError)
129
+ it "reloads a pikelet config without restarting it" do
130
+ setup_logger
153
131
 
154
- fiber.should_receive(:resume)
155
- Fiber.should_receive(:new).and_yield.and_return(fiber)
132
+ old_cfg = {'executive' => {'enabled' => 'yes', 'foo' => 'bar'}}
133
+ new_cfg = {'executive' => {'enabled' => 'yes', 'foo' => 'baz'}}
156
134
 
157
- config.should_receive(:for_redis).and_return({})
135
+ new_config = mock('new_config')
136
+ filename = mock('filename')
137
+
138
+ config.should_receive(:all).and_return(old_cfg)
139
+ config.should_receive(:filename).and_return(filename)
140
+
141
+ Flapjack::Configuration.should_receive(:new).and_return(new_config)
142
+ new_config.should_receive(:load).with(filename)
143
+ new_config.should_receive(:all).and_return(new_cfg)
144
+
145
+ executive = mock('executive')
146
+ executive.should_receive(:type).exactly(3).times.and_return('executive')
147
+ executive.should_receive(:reload).with(new_cfg['executive']).and_return(true)
148
+ executive.should_not_receive(:stop)
158
149
 
150
+ config.should_receive(:for_redis).and_return({})
159
151
  fc = Flapjack::Coordinator.new(config)
160
- fc.should_receive(:stop)
161
- fc.send(:build_pikelet, Flapjack::Gateways::Jabber, {'enabled' => 'yes'})
162
- pikelets = fc.instance_variable_get('@pikelets')
163
- pikelets.should_not be_nil
164
- pikelets.should be_an(Array)
165
- pikelets.should have(1).pikelet
166
- pikelets.first.should == {:fiber => fiber, :class => Flapjack::Gateways::Jabber, :instance => jabber}
152
+ fc.instance_variable_set('@pikelets', [executive])
153
+ fc.reload
154
+ fc.instance_variable_get('@pikelets').should == [executive]
167
155
  end
168
156
 
169
- it "creates a resque worker pikelet" do
170
- redis = mock('redis')
171
- Flapjack::RedisPool.should_receive(:new).and_return(redis)
172
- Resque.should_receive(:redis=).with(redis)
157
+ it "reloads a pikelet config while restarting it" do
158
+ setup_logger
173
159
 
174
- worker = mock('worker')
175
- EM::Resque::Worker.should_receive(:new).and_return(worker)
176
- worker.should_receive(:work)
160
+ old_cfg = {'executive' => {'enabled' => 'yes', 'foo' => 'bar'}}
161
+ new_cfg = {'executive' => {'enabled' => 'yes', 'baz' => 'qux'}}
177
162
 
178
- fiber.should_receive(:resume)
179
- Fiber.should_receive(:new).and_yield.and_return(fiber)
163
+ new_config = mock('new_config')
164
+ filename = mock('filename')
180
165
 
181
- config.should_receive(:for_redis).and_return({})
166
+ config.should_receive(:all).and_return(old_cfg)
167
+ config.should_receive(:filename).and_return(filename)
182
168
 
183
- fc = Flapjack::Coordinator.new(config)
184
- fc.send(:build_pikelet, Flapjack::Gateways::Email, {'enabled' => 'yes'})
185
- pikelets = fc.instance_variable_get('@pikelets')
186
- pikelets.should_not be_nil
187
- pikelets.should be_an(Array)
188
- pikelets.should have(1).pikelet
189
- pikelets.first.should == {:fiber => fiber, :class => Flapjack::Gateways::Email,
190
- :instance => worker}
191
- end
169
+ Flapjack::Configuration.should_receive(:new).and_return(new_config)
170
+ new_config.should_receive(:load).with(filename)
171
+ new_config.should_receive(:all).and_return(new_cfg)
192
172
 
193
- it "creates a thin server pikelet" do
194
- redis = mock('redis')
173
+ executive = mock('executive')
174
+ executive.should_receive(:type).exactly(5).times.and_return('executive')
175
+ executive.should_receive(:reload).with(new_cfg['executive']).and_return(false)
176
+ executive.should_receive(:stop)
177
+ executive.should_receive(:update_status)
178
+ executive.should_receive(:status).exactly(3).times.and_return('stopped')
195
179
 
196
- server = mock('server')
197
- server.should_receive(:start)
198
- Thin::Server.should_receive(:new).
199
- with(/^(?:\d{1,3}\.){3}\d{1,3}$/, an_instance_of(Fixnum), Flapjack::Gateways::Web, :signals => false).
200
- and_return(server)
180
+ fiber.should_receive(:resume)
181
+ Fiber.should_receive(:new).and_yield.and_return(fiber)
201
182
 
202
- Flapjack::RedisPool.should_receive(:new)
183
+ new_exec = mock('new_executive')
184
+ new_exec.should_receive(:start)
203
185
 
204
- config.should_receive(:for_redis).and_return({})
186
+ Flapjack::Pikelet.should_receive(:create).
187
+ with('executive', :config => new_cfg['executive'], :redis_config => {}).
188
+ and_return(new_exec)
205
189
 
190
+ config.should_receive(:for_redis).and_return({})
206
191
  fc = Flapjack::Coordinator.new(config)
207
- fc.send(:build_pikelet, Flapjack::Gateways::Web, {'enabled' => 'yes'})
208
- pikelets = fc.instance_variable_get('@pikelets')
209
- pikelets.should_not be_nil
210
- pikelets.should be_an(Array)
211
- pikelets.should have(1).pikelet
212
- pikelets.first.should == {:class => Flapjack::Gateways::Web, :instance => server}
192
+ fc.instance_variable_set('@pikelets', [executive])
193
+ fc.reload
194
+ fc.instance_variable_get('@pikelets').should == [new_exec]
213
195
  end
214
196
 
215
- # NB: exceptions are handled directly by the Thin pikelets
216
-
217
- end
197
+ end