big_brother 0.4.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,15 +5,13 @@ module BigBrother
5
5
  set :raise_errors, false
6
6
 
7
7
  get "/" do
8
- running, stopped = BigBrother.clusters.values.partition(&:monitored?)
9
-
10
8
  [200, <<-CONTENT]
11
9
  Big Brother: #{BigBrother::VERSION}
12
10
 
13
11
  Running:
14
- #{running.map { |cluster| "+ #{cluster}\n" }.join}
12
+ #{BigBrother.clusters.running.map { |cluster| "+ #{cluster}\n" }.join}
15
13
  Stopped:
16
- #{stopped.map { |cluster| "- #{cluster}\n" }.join}
14
+ #{BigBrother.clusters.stopped.map { |cluster| "- #{cluster}\n" }.join}
17
15
  CONTENT
18
16
  end
19
17
 
@@ -1,17 +1,37 @@
1
1
  module BigBrother
2
2
  class Cluster
3
- attr_reader :fwmark, :scheduler, :check_interval, :nodes, :name
3
+ attr_reader :fwmark, :scheduler, :check_interval, :nodes, :name, :persistent, :ramp_up_time, :nagios
4
4
 
5
5
  def initialize(name, attributes = {})
6
6
  @name = name
7
- @fwmark = attributes['fwmark']
8
- @scheduler = attributes['scheduler']
9
- @check_interval = attributes.fetch('check_interval', 1)
7
+ @fwmark = attributes[:fwmark]
8
+ @scheduler = attributes[:scheduler]
9
+ @persistent = attributes.fetch(:persistent, 300)
10
+ @check_interval = attributes.fetch(:check_interval, 1)
10
11
  @monitored = false
11
- @nodes = attributes.fetch('nodes', [])
12
+ @nodes = attributes.fetch(:nodes, []).map { |node_config| _coerce_node(node_config) }
12
13
  @last_check = Time.new(0)
13
14
  @up_file = BigBrother::StatusFile.new('up', @name)
14
15
  @down_file = BigBrother::StatusFile.new('down', @name)
16
+ @ramp_up_time = attributes.fetch(:ramp_up_time, 60)
17
+ @has_downpage = attributes[:has_downpage]
18
+ @nagios = attributes[:nagios]
19
+ end
20
+
21
+ def _coerce_node(node_config)
22
+ node_config.is_a?(Node) ? node_config : Node.new(node_config)
23
+ end
24
+
25
+ def downpage_enabled?
26
+ @downpage_enabled
27
+ end
28
+
29
+ def find_node(address, port)
30
+ nodes.find{|node| node.address == address && node.port == port}
31
+ end
32
+
33
+ def has_downpage?
34
+ @has_downpage
15
35
  end
16
36
 
17
37
  def monitored?
@@ -20,7 +40,7 @@ module BigBrother
20
40
 
21
41
  def start_monitoring!
22
42
  BigBrother.logger.info "starting monitoring on cluster #{to_s}"
23
- BigBrother.ipvs.start_cluster(@fwmark, @scheduler)
43
+ BigBrother.ipvs.start_cluster(@fwmark, @scheduler, @persistent)
24
44
  @nodes.each do |node|
25
45
  BigBrother.ipvs.start_node(@fwmark, node.address, 100)
26
46
  end
@@ -62,12 +82,19 @@ module BigBrother
62
82
  def monitor_nodes
63
83
  @last_check = Time.now
64
84
  @nodes.each { |node| node.monitor(self) }
85
+
86
+ _check_downpage if has_downpage?
87
+ _notify_nagios if nagios
65
88
  end
66
89
 
67
90
  def to_s
68
91
  "#{@name} (#{@fwmark})"
69
92
  end
70
93
 
94
+ def ==(other)
95
+ fwmark == other.fwmark
96
+ end
97
+
71
98
  def up_file_exists?
72
99
  @up_file.exists?
73
100
  end
@@ -76,6 +103,13 @@ module BigBrother
76
103
  @down_file.exists?
77
104
  end
78
105
 
106
+ def incorporate_state(another_cluster)
107
+ nodes.each do |node|
108
+ node.incorporate_state(another_cluster.find_node(node.address, node.port))
109
+ end
110
+ self
111
+ end
112
+
79
113
  def _add_nodes(addresses)
80
114
  addresses.each do |address|
81
115
  BigBrother.logger.info "adding #{address} to cluster #{self}"
@@ -83,6 +117,39 @@ module BigBrother
83
117
  end
84
118
  end
85
119
 
120
+ def _add_maintenance_node
121
+ BigBrother.logger.info "adding 127.0.0.1 to cluster #{self}"
122
+ BigBrother.ipvs.start_node(fwmark, '127.0.0.1', 1)
123
+ end
124
+
125
+ def _check_downpage
126
+ total_health = @nodes.collect{ |n| n.weight || 0 }.reduce(:+)
127
+ if total_health <= 0
128
+ _add_maintenance_node unless downpage_enabled?
129
+ @downpage_enabled = true
130
+ else
131
+ _remove_maintenance_node if downpage_enabled?
132
+ @downpage_enabled = false
133
+ end
134
+ end
135
+
136
+ def _notify_nagios
137
+ nodes_down = @nodes.count{|n| n.weight == 0}
138
+ return if @last_node_count == nodes_down
139
+ if ((nodes_down / @nodes.count.to_f) >= 0.5)
140
+ BigBrother.nagios.send_critical(nagios[:host], nagios[:check], "50% of nodes are down", nagios[:server])
141
+ elsif nodes_down > 0
142
+ BigBrother.nagios.send_warning(nagios[:host], nagios[:check], "a node is down", nagios[:server])
143
+ else
144
+ BigBrother.nagios.send_ok(nagios[:host], nagios[:check], "all nodes up", nagios[:server])
145
+ end
146
+ @last_node_count = nodes_down
147
+ end
148
+
149
+ def _remove_maintenance_node
150
+ BigBrother.ipvs.stop_node(fwmark, '127.0.0.1')
151
+ end
152
+
86
153
  def _remove_nodes(addresses)
87
154
  addresses.each do |address|
88
155
  BigBrother.logger.info "removing #{address} to cluster #{self}"
@@ -0,0 +1,34 @@
1
+ require 'forwardable'
2
+
3
+ module BigBrother
4
+ class ClusterCollection
5
+ extend Forwardable
6
+ def_delegators :@clusters, :[], :[]=, :size, :clear
7
+
8
+ def initialize
9
+ @clusters = {}
10
+ end
11
+
12
+ def config(new_clusters)
13
+ new_clusters.each do |cluster_name, cluster|
14
+ if @clusters.key?(cluster_name)
15
+ @clusters[cluster_name] = cluster.incorporate_state(@clusters[cluster_name])
16
+ else
17
+ @clusters[cluster_name] = cluster
18
+ end
19
+ end
20
+ end
21
+
22
+ def running
23
+ @clusters.values.select(&:monitored?)
24
+ end
25
+
26
+ def stopped
27
+ @clusters.values.reject(&:monitored?)
28
+ end
29
+
30
+ def ready_for_check
31
+ @clusters.values.select(&:needs_check?)
32
+ end
33
+ end
34
+ end
@@ -1,18 +1,34 @@
1
1
  module BigBrother
2
2
  class Configuration
3
- def self.evaluate(file)
4
- yaml = YAML.load(File.read(file))
5
- assoc_array = yaml.map do |name, values|
6
- nodes = _parse_nodes(values.delete('nodes'))
7
- [name, Cluster.new(name, values.merge('nodes' => nodes))]
3
+ GLOBAL_CONFIG_KEY = '_big_brother'
4
+
5
+ def self.from_file(config_file)
6
+ config = YAML.load_file(config_file)
7
+ defaults = config.delete(GLOBAL_CONFIG_KEY)
8
+
9
+ config.inject({}) do |clusters, (cluster_name, cluster_values)|
10
+ cluster_details = _apply_defaults(defaults, cluster_values)
11
+ clusters.merge(cluster_name => Cluster.new(cluster_name, _deeply_symbolize_keys(cluster_details)))
8
12
  end
13
+ end
9
14
 
10
- Hash[assoc_array]
15
+ def self._deeply_symbolize_keys(value)
16
+ if value.is_a?(Hash)
17
+ value.inject({}) do |symbolized_hash, (hash_key, hash_value)|
18
+ symbolized_hash[hash_key.to_sym] = _deeply_symbolize_keys(hash_value)
19
+ symbolized_hash
20
+ end
21
+ elsif value.is_a?(Array)
22
+ value.map { |item| _deeply_symbolize_keys(item) }
23
+ else
24
+ value
25
+ end
11
26
  end
12
27
 
13
- def self._parse_nodes(nodes)
14
- nodes.map do |values|
15
- Node.new(values['address'], values['port'], values['path'])
28
+ def self._apply_defaults(defaults_hash, settings_hash)
29
+ return settings_hash unless defaults_hash
30
+ defaults_hash.merge(settings_hash) do |key, oldval, newval|
31
+ oldval.is_a?(Hash) && newval.is_a?(Hash) ? _apply_defaults(oldval, newval) : newval
16
32
  end
17
33
  end
18
34
  end
@@ -4,8 +4,8 @@ module BigBrother
4
4
  @executor = executor
5
5
  end
6
6
 
7
- def start_cluster(fwmark, scheduler)
8
- @executor.invoke("ipvsadm --add-service --fwmark-service #{fwmark} --scheduler #{scheduler}")
7
+ def start_cluster(fwmark, scheduler, persistent)
8
+ @executor.invoke("ipvsadm --add-service --fwmark-service #{fwmark} --scheduler #{scheduler} --persistent #{persistent}")
9
9
  end
10
10
 
11
11
  def stop_cluster(fwmark)
@@ -0,0 +1,30 @@
1
+ module BigBrother
2
+ class Nagios
3
+ module Code
4
+ Ok = 0
5
+ Warning = 1
6
+ Critical = 2
7
+ Unknown = 3
8
+ end
9
+
10
+ def initialize(executor = ShellExecutor.new)
11
+ @executor = executor
12
+ end
13
+
14
+ def send_critical(host, check, message, server)
15
+ _send_passive(host, check, Code::Critical, "CRITICAL #{message}", server)
16
+ end
17
+
18
+ def send_ok(host, check, message, server)
19
+ _send_passive(host, check, Code::Ok, "OK #{message}", server)
20
+ end
21
+
22
+ def send_warning(host, check, message, server)
23
+ _send_passive(host, check, Code::Warning, "WARNING #{message}", server)
24
+ end
25
+
26
+ def _send_passive(host, check, code, message, server)
27
+ @executor.invoke("echo '#{host},#{check},#{code},#{message}' | send_nsca -H #{server} -d ,")
28
+ end
29
+ end
30
+ end
@@ -2,13 +2,25 @@ require 'net/http'
2
2
 
3
3
  module BigBrother
4
4
  class Node
5
- attr_reader :address, :port, :path
5
+ attr_reader :address, :port, :path, :start_time, :weight
6
6
 
7
- def initialize(address, port, path)
8
- @address = address
9
- @port = port
10
- @path = path
11
- @weight = nil
7
+ def initialize(attributes={})
8
+ @address = attributes[:address]
9
+ @port = attributes[:port]
10
+ @path = attributes[:path]
11
+ @weight = attributes[:weight]
12
+ @start_time = attributes.fetch(:start_time, Time.now.to_i)
13
+ end
14
+
15
+ def age
16
+ Time.now.to_i - @start_time
17
+ end
18
+
19
+ def incorporate_state(another_node)
20
+ if another_node
21
+ @weight = another_node.weight
22
+ @start_time = another_node.start_time
23
+ end
12
24
  end
13
25
 
14
26
  def invalidate_weight!
@@ -24,13 +36,26 @@ module BigBrother
24
36
  end
25
37
  end
26
38
 
39
+ def ==(other)
40
+ address == other.address && port == other.port
41
+ end
42
+
27
43
  def _determine_weight(cluster)
28
44
  if cluster.up_file_exists?
29
45
  100
30
46
  elsif cluster.down_file_exists?
31
47
  0
32
48
  else
33
- BigBrother::HealthFetcher.current_health(@address, @port, @path)
49
+ _weight_health(BigBrother::HealthFetcher.current_health(@address, @port, @path), cluster.ramp_up_time)
50
+ end
51
+ end
52
+
53
+ def _weight_health(health, ramp_up_time)
54
+ current_age = age
55
+ if current_age < ramp_up_time
56
+ (health * (current_age / ramp_up_time.to_f)).to_i
57
+ else
58
+ health
34
59
  end
35
60
  end
36
61
  end
@@ -1,7 +1,7 @@
1
1
  module BigBrother
2
2
  class StatusFile
3
3
  def initialize(*filenames)
4
- @path = File.join(BigBrother.config_dir, *filenames)
4
+ @path = File.join(BigBrother.config_dir, *[filenames.map(&:to_s)])
5
5
  end
6
6
 
7
7
  def content
@@ -19,7 +19,7 @@ module BigBrother
19
19
 
20
20
  def self.tick
21
21
  @outstanding_ticks += 1
22
- BigBrother.clusters.values.select(&:needs_check?).each do |cluster|
22
+ BigBrother.clusters.ready_for_check.each do |cluster|
23
23
  BigBrother.logger.debug("Monitoring cluster #{cluster.name}")
24
24
  cluster.monitor_nodes
25
25
  end
@@ -1,3 +1,3 @@
1
1
  module BigBrother
2
- VERSION = "0.4.1"
2
+ VERSION = "0.5.0"
3
3
  end
data/lib/big_brother.rb CHANGED
@@ -9,10 +9,12 @@ require 'sinatra/synchrony'
9
9
 
10
10
  require 'big_brother/app'
11
11
  require 'big_brother/cluster'
12
+ require 'big_brother/cluster_collection'
12
13
  require 'big_brother/configuration'
13
14
  require 'big_brother/health_fetcher'
14
15
  require 'big_brother/ipvs'
15
16
  require 'big_brother/logger'
17
+ require 'big_brother/nagios'
16
18
  require 'big_brother/node'
17
19
  require 'big_brother/shell_executor'
18
20
  require 'big_brother/status_file'
@@ -25,16 +27,17 @@ require 'thin/callback_rack_handler'
25
27
 
26
28
  module BigBrother
27
29
  class << self
28
- attr_accessor :ipvs, :clusters, :config_dir, :logger
30
+ attr_accessor :ipvs, :nagios, :clusters, :config_dir, :logger
29
31
  end
30
32
 
31
33
  self.ipvs = IPVS.new
32
- self.clusters = {}
34
+ self.nagios = Nagios.new
35
+ self.clusters = BigBrother::ClusterCollection.new
33
36
  self.logger = BigBrother::Logger.new
34
37
 
35
38
  def self.configure(filename)
36
39
  @config_file = filename
37
- @clusters = BigBrother::Configuration.evaluate(filename)
40
+ @clusters.config(BigBrother::Configuration.from_file(filename))
38
41
  end
39
42
 
40
43
  def self.start_ticker!
@@ -106,7 +106,7 @@ module BigBrother
106
106
  last_response.status.should == 200
107
107
  last_response.body.should == "OK"
108
108
  BigBrother.clusters['test'].should be_monitored
109
- @stub_executor.commands.should include("ipvsadm --add-service --fwmark-service 100 --scheduler wrr")
109
+ @stub_executor.commands.should include("ipvsadm --add-service --fwmark-service 100 --scheduler wrr --persistent 300")
110
110
  @stub_executor.commands.should include("ipvsadm --add-server --fwmark-service 100 --real-server 127.0.0.1 --ipip --weight 100")
111
111
  @stub_executor.commands.should include("ipvsadm --add-server --fwmark-service 100 --real-server 127.0.0.2 --ipip --weight 100")
112
112
  end
@@ -0,0 +1,83 @@
1
+ require 'spec_helper'
2
+
3
+ describe BigBrother::ClusterCollection do
4
+ describe "config" do
5
+ it "adds the provided clusters into its collection" do
6
+ clusters_from_config = {
7
+ 'test1' => Factory.cluster(:name => 'test1', :fwmark => 101),
8
+ 'test2' => Factory.cluster(:name => 'test2', :fwmark => 102),
9
+ 'test3' => Factory.cluster(:name => 'test3', :fwmark => 103)
10
+ }
11
+ collection = BigBrother::ClusterCollection.new
12
+
13
+ collection.config(clusters_from_config)
14
+
15
+ collection['test1'].should == clusters_from_config['test1']
16
+ collection['test2'].should == clusters_from_config['test2']
17
+ collection['test3'].should == clusters_from_config['test3']
18
+ end
19
+
20
+ it "incorporates the state of clusters that exist" do
21
+ collection = BigBrother::ClusterCollection.new
22
+
23
+ collection['existing_cluster'] = Factory.cluster(:name => 'existing_cluster')
24
+
25
+ cluster_from_config = Factory.cluster(:name => 'existing_cluster')
26
+ cluster_from_config.should_receive(:incorporate_state).with(collection['existing_cluster'])
27
+
28
+ collection.config({'existing_cluster' => cluster_from_config})
29
+ end
30
+ end
31
+
32
+ describe "running" do
33
+ it "returns the clusters in the collection that are currently running" do
34
+ clusters_from_config = {
35
+ 'test1' => Factory.cluster(:name => 'test1', :fwmark => 101),
36
+ 'test2' => Factory.cluster(:name => 'test2', :fwmark => 102),
37
+ 'test3' => Factory.cluster(:name => 'test3', :fwmark => 103)
38
+ }
39
+ collection = BigBrother::ClusterCollection.new
40
+
41
+ collection.config(clusters_from_config)
42
+ clusters_from_config['test1'].start_monitoring!
43
+ clusters_from_config['test2'].start_monitoring!
44
+
45
+ collection.running.should == [clusters_from_config['test1'], clusters_from_config['test2']]
46
+ end
47
+ end
48
+
49
+ describe "stopped" do
50
+ it "returns the clusters in the collection that are not running" do
51
+ clusters_from_config = {
52
+ 'test1' => Factory.cluster(:name => 'test1', :fwmark => 101),
53
+ 'test2' => Factory.cluster(:name => 'test2', :fwmark => 102),
54
+ 'test3' => Factory.cluster(:name => 'test3', :fwmark => 103)
55
+ }
56
+ collection = BigBrother::ClusterCollection.new
57
+
58
+ collection.config(clusters_from_config)
59
+ clusters_from_config['test1'].start_monitoring!
60
+ clusters_from_config['test2'].start_monitoring!
61
+
62
+ collection.stopped.should == [clusters_from_config['test3']]
63
+ end
64
+ end
65
+
66
+ describe "ready_for_check" do
67
+ it "returns the clusters in the collection that need checking" do
68
+ clusters_from_config = {
69
+ 'test1' => Factory.cluster(:name => 'test1', :fwmark => 101),
70
+ 'test2' => Factory.cluster(:name => 'test2', :fwmark => 102),
71
+ 'test3' => Factory.cluster(:name => 'test3', :fwmark => 103)
72
+ }
73
+ collection = BigBrother::ClusterCollection.new
74
+
75
+ collection.config(clusters_from_config)
76
+ clusters_from_config['test1'].stub(:needs_check?).and_return(true)
77
+ clusters_from_config['test2'].stub(:needs_check?).and_return(true)
78
+ clusters_from_config['test3'].stub(:needs_check?).and_return(false)
79
+
80
+ collection.ready_for_check.should == [clusters_from_config['test1'], clusters_from_config['test2']]
81
+ end
82
+ end
83
+ end
@@ -13,7 +13,7 @@ describe BigBrother::Cluster do
13
13
  cluster = Factory.cluster(:fwmark => 100, :scheduler => 'wrr')
14
14
 
15
15
  cluster.start_monitoring!
16
- @stub_executor.commands.should include('ipvsadm --add-service --fwmark-service 100 --scheduler wrr')
16
+ @stub_executor.commands.should include('ipvsadm --add-service --fwmark-service 100 --scheduler wrr --persistent 300')
17
17
  end
18
18
  end
19
19
 
@@ -77,6 +77,124 @@ describe BigBrother::Cluster do
77
77
 
78
78
  cluster.monitor_nodes
79
79
  end
80
+
81
+ it "enables a downpage if none of the nodes have health > 0" do
82
+ node1 = Factory.node
83
+ node2 = Factory.node
84
+ cluster = Factory.cluster(:has_downpage => true, :nodes => [node1, node2])
85
+
86
+ BigBrother::HealthFetcher.stub(:current_health).and_return(0)
87
+
88
+ cluster.start_monitoring!
89
+ cluster.monitor_nodes
90
+ cluster.downpage_enabled?.should be_true
91
+ end
92
+
93
+ it "does not enable a downpage if the cluster does not have a downpage enabled" do
94
+ node1 = Factory.node
95
+ node2 = Factory.node
96
+ cluster = Factory.cluster(:has_downpage => false, :nodes => [node1, node2])
97
+
98
+ BigBrother::HealthFetcher.stub(:current_health).and_return(0)
99
+
100
+ cluster.start_monitoring!
101
+ cluster.monitor_nodes
102
+ cluster.downpage_enabled?.should be_false
103
+ end
104
+
105
+ it "adds a downpage node to IPVS when down" do
106
+ node1 = Factory.node
107
+ node2 = Factory.node
108
+ cluster = Factory.cluster(:has_downpage => true, :nodes => [node1, node2], :fwmark => 1)
109
+
110
+ BigBrother::HealthFetcher.stub(:current_health).and_return(0)
111
+
112
+ cluster.start_monitoring!
113
+ cluster.monitor_nodes
114
+
115
+ @stub_executor.commands.last.should == "ipvsadm --add-server --fwmark-service 1 --real-server 127.0.0.1 --ipip --weight 1"
116
+ end
117
+
118
+ it "removes downpage node from IPVS if it exists and cluster is up" do
119
+ node1 = Factory.node
120
+ node2 = Factory.node
121
+ cluster = Factory.cluster(:has_downpage => true, :nodes => [node1, node2], :fwmark => 1)
122
+
123
+ BigBrother::HealthFetcher.stub(:current_health).and_return(0)
124
+
125
+ cluster.start_monitoring!
126
+ cluster.monitor_nodes
127
+
128
+ BigBrother::HealthFetcher.stub(:current_health).and_return(10)
129
+ cluster.monitor_nodes
130
+
131
+ @stub_executor.commands.last.should == "ipvsadm --delete-server --fwmark-service 1 --real-server 127.0.0.1"
132
+ end
133
+
134
+ context "nagios" do
135
+ it "sends critical if at least half of all nodes are down" do
136
+ node1 = Factory.node(:address => '192.168.0.1')
137
+ node2 = Factory.node(:address => '192.168.0.2')
138
+ cluster = Factory.cluster(:nodes => [node1, node2], :nagios => {:host => "prod.load", :check => "test1_check", :server => "server.foo"})
139
+
140
+ node1.stub(:_determine_weight).and_return(0)
141
+ node2.stub(:_determine_weight).and_return(10)
142
+
143
+ cluster.start_monitoring!
144
+ cluster.monitor_nodes
145
+ @stub_executor.commands.should include("echo 'prod.load,test1_check,2,CRITICAL 50% of nodes are down' | send_nsca -H server.foo -d ,")
146
+ end
147
+
148
+ it "does not resend a Nagios check if the state does not change" do
149
+ node1 = Factory.node(:address => '192.168.0.1')
150
+ node2 = Factory.node(:address => '192.168.0.2')
151
+ cluster = Factory.cluster(:nodes => [node1, node2], :nagios => {:host => "prod.load", :check => "test1_check", :server => "server.foo"})
152
+
153
+ node1.stub(:_determine_weight).and_return(0)
154
+ node2.stub(:_determine_weight).and_return(10)
155
+
156
+ cluster.start_monitoring!
157
+ cluster.monitor_nodes
158
+ @stub_executor.clear_commands!
159
+
160
+ cluster.monitor_nodes
161
+ @stub_executor.commands.should_not include("echo 'prod.load,test1_check,2,CRITICAL 50% of nodes are down' | send_nsca -H server.foo -d ,")
162
+ end
163
+
164
+ it "sends info if at least half of one node is down" do
165
+ node1 = Factory.node(:address => '192.168.0.1')
166
+ node2 = Factory.node(:address => '192.168.0.2')
167
+ node3 = Factory.node(:address => '192.168.0.3')
168
+ cluster = Factory.cluster(:nodes => [node1, node2, node3], :nagios => {:host => "prod.load", :check => "test1_check", :server => "server.foo"})
169
+
170
+ node1.stub(:_determine_weight).and_return(0)
171
+ node2.stub(:_determine_weight).and_return(10)
172
+ node3.stub(:_determine_weight).and_return(10)
173
+
174
+ cluster.start_monitoring!
175
+ cluster.monitor_nodes
176
+ @stub_executor.commands.should include("echo 'prod.load,test1_check,1,WARNING a node is down' | send_nsca -H server.foo -d ,")
177
+ end
178
+
179
+ it "sends ok if all nodes back up" do
180
+ node1 = Factory.node(:address => '192.168.0.1')
181
+ node2 = Factory.node(:address => '192.168.0.2')
182
+ cluster = Factory.cluster(:nodes => [node1, node2], :nagios => {:host => "prod.load", :check => "test1_check", :server => "server.foo"})
183
+ node1.stub(:_determine_weight).and_return(0)
184
+ node2.stub(:_determine_weight).and_return(10)
185
+
186
+ cluster.start_monitoring!
187
+ cluster.monitor_nodes
188
+ @stub_executor.commands.should include("echo 'prod.load,test1_check,2,CRITICAL 50% of nodes are down' | send_nsca -H server.foo -d ,")
189
+ @stub_executor.clear_commands!
190
+
191
+ node1.stub(:_determine_weight).and_return(10)
192
+ node2.stub(:_determine_weight).and_return(10)
193
+ cluster.monitor_nodes
194
+ @stub_executor.commands.should include("echo 'prod.load,test1_check,0,OK all nodes up' | send_nsca -H server.foo -d ,")
195
+ end
196
+ end
197
+
80
198
  end
81
199
 
82
200
  describe "#resume_monitoring!" do
@@ -143,6 +261,18 @@ describe BigBrother::Cluster do
143
261
  end
144
262
  end
145
263
 
264
+ describe "#==" do
265
+ it "is true if two clusters have the same fwmark" do
266
+ cluster1 = Factory.cluster(:fwmark => '100')
267
+ cluster2 = Factory.cluster(:fwmark => '200')
268
+
269
+ cluster1.should_not == cluster2
270
+
271
+ cluster2 = Factory.cluster(:fwmark => '100')
272
+ cluster1.should == cluster2
273
+ end
274
+ end
275
+
146
276
  describe "#up_file_exists?" do
147
277
  it "returns true when an up file exists" do
148
278
  cluster = Factory.cluster(:name => 'name')
@@ -164,4 +294,23 @@ describe BigBrother::Cluster do
164
294
  cluster.down_file_exists?.should be_true
165
295
  end
166
296
  end
297
+
298
+ describe "incorporate_state" do
299
+ it "finds any equivalent nodes from the provided cluster, incorporates their state, and returns self" do
300
+ original_node1 = Factory.node(:address => '127.0.0.1')
301
+ original_node2 = Factory.node(:address => '127.0.1.1')
302
+ original_cluster = Factory.cluster(:nodes => [original_node1, original_node2])
303
+
304
+ config_node1 = Factory.node(:address => '127.0.0.1')
305
+ config_node2 = Factory.node(:address => '127.0.1.1')
306
+ config_cluster = Factory.cluster(:nodes => [config_node1, config_node2])
307
+
308
+ config_node1.should_receive(:incorporate_state).with(original_node1)
309
+ config_node2.should_receive(:incorporate_state).with(original_node2)
310
+
311
+ retval = config_cluster.incorporate_state(original_cluster)
312
+
313
+ retval.should == config_cluster
314
+ end
315
+ end
167
316
  end
@@ -1,17 +1,24 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe BigBrother::Configuration do
4
- describe '.evaluate' do
5
- it 'returns a hash of clusters' do
6
- clusters = BigBrother::Configuration.evaluate(TEST_CONFIG)
4
+ describe 'self.from_file' do
5
+ it 'maintain a collection of clusters' do
6
+ clusters = BigBrother::Configuration.from_file(TEST_CONFIG)
7
7
 
8
8
  clusters['test1'].check_interval.should == 1
9
9
  clusters['test1'].scheduler.should == 'wrr'
10
10
  clusters['test1'].fwmark.should == 1
11
+ clusters['test1'].persistent.should == 20
12
+ clusters['test1'].ramp_up_time.should == 120
13
+ clusters['test1'].has_downpage?.should == true
14
+ clusters['test1'].nagios[:check].should == 'test1_status'
15
+ clusters['test1'].nagios[:host].should == 'prod-load'
16
+ clusters['test1'].nagios[:server].should == 'nsca.host'
11
17
 
12
- clusters['test2'].check_interval.should == 1
18
+ clusters['test2'].check_interval.should == 2
13
19
  clusters['test2'].scheduler.should == 'wrr'
14
20
  clusters['test2'].fwmark.should == 2
21
+ clusters['test2'].ramp_up_time.should == 60
15
22
 
16
23
  clusters['test3'].check_interval.should == 1
17
24
  clusters['test3'].scheduler.should == 'wrr'
@@ -19,7 +26,7 @@ describe BigBrother::Configuration do
19
26
  end
20
27
 
21
28
  it 'populates a clusters nodes' do
22
- clusters = BigBrother::Configuration.evaluate(TEST_CONFIG)
29
+ clusters = BigBrother::Configuration.from_file(TEST_CONFIG)
23
30
 
24
31
  clusters['test1'].nodes.length.should == 2
25
32
 
@@ -31,5 +38,64 @@ describe BigBrother::Configuration do
31
38
  clusters['test1'].nodes[1].port == '9002'
32
39
  clusters['test1'].nodes[1].path == '/test/valid'
33
40
  end
41
+
42
+ it 'allows a default cluster configuration under the global config key' do
43
+ config_file = Tempfile.new('config.yml')
44
+ File.open(config_file, 'w') do |f|
45
+ f.puts(<<-EOF.gsub(/^ {10}/,''))
46
+ ---
47
+ _big_brother:
48
+ check_interval: 2
49
+ scheduler: wrr
50
+ nagios:
51
+ server: 127.0.0.2
52
+ host: ha-services
53
+ test_without_overrides:
54
+ fwmark: 2
55
+ nagios:
56
+ check: test_check
57
+ nodes:
58
+ - address: 127.0.0.1
59
+ port: 9001
60
+ path: /test/invalid
61
+ test_with_overrides:
62
+ fwmark: 3
63
+ scheduler: wlc
64
+ nagios:
65
+ host: override-host
66
+ check: test_overrides_check
67
+ nodes:
68
+ - address: 127.0.0.1
69
+ port: 9001
70
+ path: /test/invalid
71
+ EOF
72
+ end
73
+
74
+ clusters = BigBrother::Configuration.from_file(config_file)
75
+
76
+ clusters['test_without_overrides'].check_interval.should == 2
77
+ clusters['test_without_overrides'].scheduler.should == 'wrr'
78
+ clusters['test_without_overrides'].fwmark.should == 2
79
+ clusters['test_without_overrides'].nagios[:server].should == '127.0.0.2'
80
+ clusters['test_without_overrides'].nagios[:host].should == 'ha-services'
81
+ clusters['test_without_overrides'].nagios[:check].should == 'test_check'
82
+
83
+
84
+ clusters['test_with_overrides'].check_interval.should == 2
85
+ clusters['test_with_overrides'].scheduler.should == 'wlc'
86
+ clusters['test_with_overrides'].fwmark.should == 3
87
+ clusters['test_with_overrides'].nagios[:server].should == '127.0.0.2'
88
+ clusters['test_with_overrides'].nagios[:host].should == 'override-host'
89
+ clusters['test_with_overrides'].nagios[:check].should == 'test_overrides_check'
90
+ end
91
+ end
92
+
93
+ describe '_apply_defaults' do
94
+ it 'returns a new hash with the defaults hash and the settings hash merged recursively' do
95
+ defaults = {:foo => {:bar => 1}}
96
+ settings = {:foo => {:baz => 2}}
97
+ h = BigBrother::Configuration._apply_defaults(defaults, settings)
98
+ h.should == {:foo => {:bar => 1, :baz => 2}}
99
+ end
34
100
  end
35
101
  end
@@ -15,6 +15,27 @@ describe BigBrother::Node do
15
15
  @stub_executor.commands.should include("ipvsadm --edit-server --fwmark-service 100 --real-server 127.0.0.1 --ipip --weight 56")
16
16
  end
17
17
 
18
+ it "a node's health should increase linearly over the specified ramp up time" do
19
+ BigBrother::HealthFetcher.stub(:current_health).and_return(100)
20
+ Time.stub(:now).and_return(1345043600)
21
+
22
+ node = Factory.node(:address => '127.0.0.1')
23
+ cluster = Factory.cluster(:ramp_up_time => 60, :fwmark => 100, :nodes => [node])
24
+ cluster.start_monitoring!
25
+
26
+ Time.stub(:now).and_return(1345043630)
27
+ node.monitor(cluster)
28
+ @stub_executor.commands.last.should == "ipvsadm --edit-server --fwmark-service 100 --real-server 127.0.0.1 --ipip --weight 50"
29
+
30
+ Time.stub(:now).and_return(1345043645)
31
+ node.monitor(cluster)
32
+ @stub_executor.commands.last.should == "ipvsadm --edit-server --fwmark-service 100 --real-server 127.0.0.1 --ipip --weight 75"
33
+
34
+ Time.stub(:now).and_return(1345043720)
35
+ node.monitor(cluster)
36
+ @stub_executor.commands.last.should == "ipvsadm --edit-server --fwmark-service 100 --real-server 127.0.0.1 --ipip --weight 100"
37
+ end
38
+
18
39
  it "sets the weight to 100 for each node if an up file exists" do
19
40
  BigBrother::HealthFetcher.stub(:current_health).and_return(56)
20
41
  node = Factory.node(:address => '127.0.0.1')
@@ -86,4 +107,41 @@ describe BigBrother::Node do
86
107
  @stub_executor.commands.should == []
87
108
  end
88
109
  end
110
+
111
+ describe "#==" do
112
+ it "is true when two nodes have the same address and port" do
113
+ node1 = Factory.node(:address => "127.0.0.1", :port => "8000")
114
+ node2 = Factory.node(:address => "127.0.0.1", :port => "8001")
115
+ node1.should_not == node2
116
+
117
+ node2 = Factory.node(:address => "127.0.0.2", :port => "8000")
118
+ node1.should_not == node2
119
+
120
+ node2 = Factory.node(:address => "127.0.0.1", :port => "8000")
121
+ node1.should == node2
122
+ end
123
+ end
124
+
125
+ describe "age" do
126
+ it "is the time in seconds since the node started" do
127
+ Time.stub(:now).and_return(1345043612)
128
+ node = Factory.node(:address => "127.0.0.1")
129
+
130
+ node.age.should == 0
131
+ end
132
+ end
133
+
134
+ describe "incorporate_state" do
135
+ it "takes the weight and the start time from the other node, but leaves rest of config" do
136
+ original_start_time = Time.now
137
+ node_with_state = Factory.node(:path => '/old/path', :start_time => original_start_time, :weight => 65)
138
+ node_from_config = Factory.node(:path => '/new/path', :start_time => Time.now, :weight => 100)
139
+
140
+ node_from_config.incorporate_state(node_with_state)
141
+
142
+ node_from_config.path.should == '/new/path'
143
+ node_from_config.start_time.should == original_start_time
144
+ node_from_config.weight.should == 65
145
+ end
146
+ end
89
147
  end
@@ -1,6 +1,15 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe BigBrother::StatusFile do
4
+ describe "initialize" do
5
+ it "accepts symbols as arguments, since that's how they will come from configuration" do
6
+ status_file = BigBrother::StatusFile.new(:foo)
7
+ status_file.create("for testing")
8
+
9
+ status_file.exists?.should == true
10
+ end
11
+ end
12
+
4
13
  describe "create" do
5
14
  it "creates a nested file" do
6
15
  status_file = BigBrother::StatusFile.new("foo", "bar")
@@ -21,13 +21,14 @@ HTTP
21
21
  spec.run
22
22
  server.stop
23
23
  end
24
+
24
25
  it "reconfigures the clusters" do
25
26
  config_file = Tempfile.new('config.yml')
26
27
  File.open(config_file, 'w') do |f|
27
28
  f.puts(<<-EOF)
28
29
  ---
29
30
  test1:
30
- checkInterval: 1
31
+ check_interval: 1
31
32
  scheduler: wrr
32
33
  fwmark: 1
33
34
  nodes:
@@ -44,7 +45,7 @@ EOF
44
45
  f.puts(<<-EOF)
45
46
  ---
46
47
  test1:
47
- checkInterval: 1
48
+ check_interval: 1
48
49
  scheduler: wrr
49
50
  fwmark: 1
50
51
  nodes:
@@ -57,15 +58,62 @@ EOF
57
58
  BigBrother.clusters['test1'].nodes.first.path.should == "/test/another/path"
58
59
  end
59
60
 
61
+ it "maintains the start_time and weight of existing nodes after reconfiguring" do
62
+ Time.stub(:now).and_return(Time.at(1345043600))
63
+ config_file = Tempfile.new('config.yml')
64
+ File.open(config_file, 'w') do |f|
65
+ f.puts(<<-EOF)
66
+ ---
67
+ test1:
68
+ check_interval: 1
69
+ scheduler: wrr
70
+ fwmark: 1
71
+ nodes:
72
+ - address: 127.0.0.1
73
+ port: 9001
74
+ path: /test/valid
75
+ EOF
76
+ end
77
+ BigBrother.configure(config_file)
78
+ BigBrother.start_ticker!
79
+
80
+ Time.stub(:now).and_return(Time.at(1345043700))
81
+ start_time = BigBrother.clusters['test1'].nodes[0].start_time
82
+ weight = BigBrother.clusters['test1'].nodes[0].weight
83
+
84
+ File.open(config_file, 'w') do |f|
85
+ f.puts(<<-EOF)
86
+ ---
87
+ test1:
88
+ check_interval: 1
89
+ scheduler: wrr
90
+ fwmark: 1
91
+ nodes:
92
+ - address: 127.0.0.1
93
+ port: 9001
94
+ path: /test/valid
95
+ - address: 127.0.0.2
96
+ port: 9001
97
+ path: /test/valid
98
+ EOF
99
+ end
100
+ BigBrother.reconfigure
101
+ BigBrother.clusters['test1'].nodes[0].start_time.should == start_time
102
+ BigBrother.clusters['test1'].nodes[0].weight.should == weight
103
+ BigBrother.clusters['test1'].nodes[1].start_time.should == 1345043700
104
+ BigBrother.clusters['test1'].nodes[1].weight.should be_nil
105
+ end
106
+
60
107
  it "stops the ticker and reconfigures after it has finished all its ticks" do
61
108
  config_file = Tempfile.new('config.yml')
62
109
  File.open(config_file, 'w') do |f|
63
110
  f.puts(<<-EOF)
64
111
  ---
65
112
  test1:
66
- checkInterval: 1
113
+ check_interval: 1
67
114
  scheduler: wrr
68
115
  fwmark: 1
116
+ ramp_up_time: 0
69
117
  nodes:
70
118
  - address: 127.0.0.1
71
119
  port: 9001
@@ -84,7 +132,7 @@ EOF
84
132
  f.puts(<<-EOF)
85
133
  ---
86
134
  test1:
87
- checkInterval: 1
135
+ check_interval: 1
88
136
  scheduler: wrr
89
137
  fwmark: 1
90
138
  nodes:
data/spec/spec_helper.rb CHANGED
@@ -15,12 +15,13 @@ RSpec.configure do |config|
15
15
  ipvs = BigBrother.ipvs
16
16
  @stub_executor = StubExecutor.new
17
17
  BigBrother.ipvs = BigBrother::IPVS.new(@stub_executor)
18
+ BigBrother.nagios = BigBrother::Nagios.new(@stub_executor)
18
19
  spec.run
19
20
  BigBrother.ipvs = ipvs
20
21
  end
21
22
 
22
23
  config.before(:each) do
23
- BigBrother.clusters.replace({})
24
+ BigBrother.clusters.clear
24
25
  FileUtils.rm_rf(BigBrother.config_dir)
25
26
  BigBrother.logger = NullLogger.new
26
27
  end
@@ -1,8 +1,15 @@
1
1
  ---
2
2
  test1:
3
- checkInterval: 1
3
+ check_interval: 1
4
4
  scheduler: wrr
5
5
  fwmark: 1
6
+ persistent: 20
7
+ ramp_up_time: 120
8
+ has_downpage: true
9
+ nagios:
10
+ server: nsca.host
11
+ check: test1_status
12
+ host: prod-load
6
13
  nodes:
7
14
  - address: 127.0.0.1
8
15
  port: 9001
@@ -11,7 +18,7 @@ test1:
11
18
  port: 9002
12
19
  path: /test/valid
13
20
  test2:
14
- checkInterval: 1
21
+ check_interval: 2
15
22
  scheduler: wrr
16
23
  fwmark: 2
17
24
  nodes:
@@ -22,7 +29,7 @@ test2:
22
29
  port: 9002
23
30
  path: /test/invalid
24
31
  test3:
25
- checkInterval: 1
32
+ check_interval: 1
26
33
  scheduler: wrr
27
34
  fwmark: 3
28
35
  nodes:
@@ -3,11 +3,12 @@ class Factory
3
3
  BigBrother::Cluster.new(
4
4
  overrides.fetch(:name, 'test'),
5
5
  {
6
- 'fwmark' => overrides.fetch(:fwmark, 100),
7
- 'scheduler' => overrides.fetch(:scheduler, 'wrr'),
8
- 'check_interval' => overrides.fetch(:check_interval, 1),
9
- 'nodes' => overrides.fetch(:nodes, [Factory.node])
10
- }
6
+ :fwmark => 100,
7
+ :scheduler => 'wrr',
8
+ :check_interval => 1,
9
+ :nodes => [Factory.node],
10
+ :ramp_up_time => 0
11
+ }.merge(overrides)
11
12
  )
12
13
  end
13
14
  end
@@ -1,9 +1,11 @@
1
1
  class Factory
2
2
  def self.node(overrides = {})
3
3
  BigBrother::Node.new(
4
- overrides.fetch(:address, 'localhost'),
5
- overrides.fetch(:port, 8081),
6
- overrides.fetch(:path, '/test/status')
4
+ {
5
+ :address => 'localhost',
6
+ :port => 8081,
7
+ :path => '/test/status'
8
+ }.merge(overrides)
7
9
  )
8
10
  end
9
11
  end
@@ -15,4 +15,8 @@ class StubExecutor
15
15
  @responses[command] ||= []
16
16
  @responses[command].push [output, status]
17
17
  end
18
+
19
+ def clear_commands!
20
+ @commands = []
21
+ end
18
22
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: big_brother
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.5.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-03 00:00:00.000000000 Z
12
+ date: 2012-08-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: thin
16
- requirement: &70295616819500 !ruby/object:Gem::Requirement
16
+ requirement: &70176230223860 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 1.3.1
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70295616819500
24
+ version_requirements: *70176230223860
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: async-rack
27
- requirement: &70295616818500 !ruby/object:Gem::Requirement
27
+ requirement: &70176230223060 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 0.5.1
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70295616818500
35
+ version_requirements: *70176230223060
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: sinatra
38
- requirement: &70295616817840 !ruby/object:Gem::Requirement
38
+ requirement: &70176230222360 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '1.0'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70295616817840
46
+ version_requirements: *70176230222360
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rack-fiber_pool
49
- requirement: &70295616817080 !ruby/object:Gem::Requirement
49
+ requirement: &70176230221420 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0.9'
55
55
  type: :runtime
56
56
  prerelease: false
57
- version_requirements: *70295616817080
57
+ version_requirements: *70176230221420
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: eventmachine
60
- requirement: &70295616816140 !ruby/object:Gem::Requirement
60
+ requirement: &70176230220360 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>'
@@ -68,10 +68,10 @@ dependencies:
68
68
  version: 1.0.0.beta.100
69
69
  type: :runtime
70
70
  prerelease: false
71
- version_requirements: *70295616816140
71
+ version_requirements: *70176230220360
72
72
  - !ruby/object:Gem::Dependency
73
73
  name: em-http-request
74
- requirement: &70295616814920 !ruby/object:Gem::Requirement
74
+ requirement: &70176230219060 !ruby/object:Gem::Requirement
75
75
  none: false
76
76
  requirements:
77
77
  - - ~>
@@ -79,10 +79,10 @@ dependencies:
79
79
  version: '1.0'
80
80
  type: :runtime
81
81
  prerelease: false
82
- version_requirements: *70295616814920
82
+ version_requirements: *70176230219060
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: em-synchrony
85
- requirement: &70295616814160 !ruby/object:Gem::Requirement
85
+ requirement: &70176230303940 !ruby/object:Gem::Requirement
86
86
  none: false
87
87
  requirements:
88
88
  - - ~>
@@ -90,10 +90,10 @@ dependencies:
90
90
  version: '1.0'
91
91
  type: :runtime
92
92
  prerelease: false
93
- version_requirements: *70295616814160
93
+ version_requirements: *70176230303940
94
94
  - !ruby/object:Gem::Dependency
95
95
  name: em-resolv-replace
96
- requirement: &70295616813240 !ruby/object:Gem::Requirement
96
+ requirement: &70176230302240 !ruby/object:Gem::Requirement
97
97
  none: false
98
98
  requirements:
99
99
  - - ~>
@@ -101,10 +101,10 @@ dependencies:
101
101
  version: '1.1'
102
102
  type: :runtime
103
103
  prerelease: false
104
- version_requirements: *70295616813240
104
+ version_requirements: *70176230302240
105
105
  - !ruby/object:Gem::Dependency
106
106
  name: em-syslog
107
- requirement: &70295616811960 !ruby/object:Gem::Requirement
107
+ requirement: &70176230301680 !ruby/object:Gem::Requirement
108
108
  none: false
109
109
  requirements:
110
110
  - - ~>
@@ -112,10 +112,10 @@ dependencies:
112
112
  version: 0.0.2
113
113
  type: :runtime
114
114
  prerelease: false
115
- version_requirements: *70295616811960
115
+ version_requirements: *70176230301680
116
116
  - !ruby/object:Gem::Dependency
117
117
  name: rspec
118
- requirement: &70295616839480 !ruby/object:Gem::Requirement
118
+ requirement: &70176230300780 !ruby/object:Gem::Requirement
119
119
  none: false
120
120
  requirements:
121
121
  - - ~>
@@ -123,10 +123,10 @@ dependencies:
123
123
  version: 2.9.0
124
124
  type: :development
125
125
  prerelease: false
126
- version_requirements: *70295616839480
126
+ version_requirements: *70176230300780
127
127
  - !ruby/object:Gem::Dependency
128
128
  name: rack-test
129
- requirement: &70295616838880 !ruby/object:Gem::Requirement
129
+ requirement: &70176230300280 !ruby/object:Gem::Requirement
130
130
  none: false
131
131
  requirements:
132
132
  - - ~>
@@ -134,10 +134,10 @@ dependencies:
134
134
  version: 0.6.1
135
135
  type: :development
136
136
  prerelease: false
137
- version_requirements: *70295616838880
137
+ version_requirements: *70176230300280
138
138
  - !ruby/object:Gem::Dependency
139
139
  name: rake
140
- requirement: &70295616838460 !ruby/object:Gem::Requirement
140
+ requirement: &70176230299880 !ruby/object:Gem::Requirement
141
141
  none: false
142
142
  requirements:
143
143
  - - ! '>='
@@ -145,10 +145,10 @@ dependencies:
145
145
  version: '0'
146
146
  type: :development
147
147
  prerelease: false
148
- version_requirements: *70295616838460
148
+ version_requirements: *70176230299880
149
149
  - !ruby/object:Gem::Dependency
150
150
  name: rake_commit
151
- requirement: &70295616837740 !ruby/object:Gem::Requirement
151
+ requirement: &70176230299300 !ruby/object:Gem::Requirement
152
152
  none: false
153
153
  requirements:
154
154
  - - ~>
@@ -156,10 +156,10 @@ dependencies:
156
156
  version: '0.13'
157
157
  type: :development
158
158
  prerelease: false
159
- version_requirements: *70295616837740
159
+ version_requirements: *70176230299300
160
160
  - !ruby/object:Gem::Dependency
161
161
  name: vagrant
162
- requirement: &70295616837060 !ruby/object:Gem::Requirement
162
+ requirement: &70176230298860 !ruby/object:Gem::Requirement
163
163
  none: false
164
164
  requirements:
165
165
  - - ! '>='
@@ -167,7 +167,7 @@ dependencies:
167
167
  version: '0'
168
168
  type: :development
169
169
  prerelease: false
170
- version_requirements: *70295616837060
170
+ version_requirements: *70176230298860
171
171
  description: IPVS backend supervisor
172
172
  email:
173
173
  - code@getbraintree.com
@@ -191,10 +191,12 @@ files:
191
191
  - lib/big_brother/app.rb
192
192
  - lib/big_brother/cli.rb
193
193
  - lib/big_brother/cluster.rb
194
+ - lib/big_brother/cluster_collection.rb
194
195
  - lib/big_brother/configuration.rb
195
196
  - lib/big_brother/health_fetcher.rb
196
197
  - lib/big_brother/ipvs.rb
197
198
  - lib/big_brother/logger.rb
199
+ - lib/big_brother/nagios.rb
198
200
  - lib/big_brother/node.rb
199
201
  - lib/big_brother/shell_executor.rb
200
202
  - lib/big_brother/status_file.rb
@@ -205,6 +207,7 @@ files:
205
207
  - lib/thin/callback_rack_handler.rb
206
208
  - lib/thin/callbacks.rb
207
209
  - spec/big_brother/app_spec.rb
210
+ - spec/big_brother/cluster_collection_spec.rb
208
211
  - spec/big_brother/cluster_spec.rb
209
212
  - spec/big_brother/configuration_spec.rb
210
213
  - spec/big_brother/health_fetcher_spec.rb
@@ -245,12 +248,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
245
248
  version: '0'
246
249
  requirements: []
247
250
  rubyforge_project:
248
- rubygems_version: 1.8.10
251
+ rubygems_version: 1.8.12
249
252
  signing_key:
250
253
  specification_version: 3
251
254
  summary: Process to monitor and update weights for servers in an IPVS pool
252
255
  test_files:
253
256
  - spec/big_brother/app_spec.rb
257
+ - spec/big_brother/cluster_collection_spec.rb
254
258
  - spec/big_brother/cluster_spec.rb
255
259
  - spec/big_brother/configuration_spec.rb
256
260
  - spec/big_brother/health_fetcher_spec.rb