big_brother 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/.gitignore +21 -0
  2. data/.rake_commit +1 -0
  3. data/.rvmrc +1 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE +22 -0
  6. data/README.md +29 -0
  7. data/Rakefile +6 -0
  8. data/big_brother.gemspec +33 -0
  9. data/bin/bigbro +6 -0
  10. data/bin/ocf_big_brother +174 -0
  11. data/config.ru +5 -0
  12. data/lib/big_brother.rb +49 -0
  13. data/lib/big_brother/app.rb +30 -0
  14. data/lib/big_brother/cli.rb +82 -0
  15. data/lib/big_brother/cluster.rb +70 -0
  16. data/lib/big_brother/configuration.rb +48 -0
  17. data/lib/big_brother/ipvs.rb +51 -0
  18. data/lib/big_brother/logger.rb +11 -0
  19. data/lib/big_brother/node.rb +30 -0
  20. data/lib/big_brother/shell_executor.rb +18 -0
  21. data/lib/big_brother/status_file.rb +26 -0
  22. data/lib/big_brother/ticker.rb +28 -0
  23. data/lib/big_brother/version.rb +3 -0
  24. data/lib/sinatra/synchrony.rb +40 -0
  25. data/lib/thin/backends/tcp_server_with_callbacks.rb +20 -0
  26. data/lib/thin/callback_rack_handler.rb +14 -0
  27. data/lib/thin/callbacks.rb +19 -0
  28. data/spec/big_brother/app_spec.rb +127 -0
  29. data/spec/big_brother/cluster_spec.rb +102 -0
  30. data/spec/big_brother/configuration_spec.rb +81 -0
  31. data/spec/big_brother/ipvs_spec.rb +26 -0
  32. data/spec/big_brother/node_spec.rb +44 -0
  33. data/spec/big_brother/shell_executor_spec.rb +21 -0
  34. data/spec/big_brother/status_file_spec.rb +39 -0
  35. data/spec/big_brother/ticker_spec.rb +60 -0
  36. data/spec/big_brother/version_spec.rb +7 -0
  37. data/spec/big_brother_spec.rb +119 -0
  38. data/spec/spec_helper.rb +57 -0
  39. data/spec/support/example_config.yml +34 -0
  40. data/spec/support/factories/cluster_factory.rb +13 -0
  41. data/spec/support/factories/node_factory.rb +9 -0
  42. data/spec/support/ipvsadm +3 -0
  43. data/spec/support/mock_session.rb +14 -0
  44. data/spec/support/null_logger.rb +7 -0
  45. data/spec/support/playback_executor.rb +13 -0
  46. data/spec/support/recording_executor.rb +11 -0
  47. data/spec/support/stub_server.rb +22 -0
  48. metadata +271 -0
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ *.rbc
3
+ *.swp
4
+ .bundle
5
+ .config
6
+ .vagrant
7
+ .yardoc
8
+ Gemfile.lock
9
+ InstalledFiles
10
+ Vagrantfile
11
+ _yardoc
12
+ coverage
13
+ doc/
14
+ lib/bundler/man
15
+ pkg
16
+ rdoc
17
+ spec/reports
18
+ tags
19
+ test/tmp
20
+ test/version_tmp
21
+ tmp
data/.rake_commit ADDED
@@ -0,0 +1 @@
1
+ --without-prompt=feature
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm ruby-1.9.3-p0@big_brother --create
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in big_brother.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Braintree Payment Solutions LLC
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # BigBrother
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'big_brother'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install big_brother
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require "rspec/core/rake_task"
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+ task :default => :spec
@@ -0,0 +1,33 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/big_brother/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Braintree"]
6
+ gem.email = ["code@getbraintree.com"]
7
+ gem.description = %q{IPVS backend supervisor}
8
+ gem.summary = %q{Process to monitor and update weights for servers in an IPVS pool}
9
+ gem.homepage = "https://github.com/braintree/big_brother"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "big_brother"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = BigBrother::VERSION
17
+
18
+ gem.add_dependency "thin", "~> 1.3.1"
19
+ gem.add_dependency "async-rack", "~> 0.5.1"
20
+ gem.add_dependency "sinatra", "~> 1.0"
21
+ gem.add_dependency "rack-fiber_pool", "~> 0.9"
22
+ gem.add_dependency "eventmachine", "> 1.0.0.beta.1", "< 1.0.0.beta.100"
23
+ gem.add_dependency "em-http-request", "~> 1.0"
24
+ gem.add_dependency "em-synchrony", "~> 1.0"
25
+ gem.add_dependency "em-resolv-replace", "~> 1.1"
26
+ gem.add_dependency "em-syslog", "~> 0.0.2"
27
+
28
+ gem.add_development_dependency "rspec", "~> 2.9.0"
29
+ gem.add_development_dependency "rack-test", "~> 0.6.1"
30
+ gem.add_development_dependency "rake"
31
+ gem.add_development_dependency "rake_commit", "~> 0.13"
32
+ gem.add_development_dependency "vagrant"
33
+ end
data/bin/bigbro ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'big_brother'
4
+ require 'big_brother/cli'
5
+
6
+ BigBrother::CLI.new.start
@@ -0,0 +1,174 @@
1
+ #!/bin/bash
2
+ #
3
+ # Michael Vallaly (Aug '11) Ver 1.0
4
+ #
5
+ # IPVS Supervisor daemon OCF resource handler script
6
+ #
7
+
8
+ AWK_BIN="/usr/bin/awk"
9
+ CURL_BIN="/usr/bin/curl"
10
+ EGREP_BIN="/bin/egrep"
11
+
12
+ CURL_TIMEOUT_SEC=5
13
+ SUPERVISOR_URL="http://127.0.0.1:9292"
14
+
15
+ #######################################################################
16
+
17
+ # Pull in OCF functions
18
+ . /usr/lib/ocf/resource.d/heartbeat/.ocf-shellfuncs
19
+
20
+ #######################################################################
21
+
22
+ meta_data() {
23
+ cat <<END
24
+ <?xml version="1.0"?>
25
+ <!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
26
+ <resource-agent name="Big Brother" version="0.9">
27
+ <version>1.0</version>
28
+
29
+ <longdesc lang="en">
30
+ This is the Big Brother Resource Agent. It enables the management
31
+ and monitoring of IPVS via the OCF resource API.
32
+ </longdesc>
33
+ <shortdesc lang="en">Big Brother resource agent</shortdesc>
34
+
35
+ <parameters>
36
+ <parameter name="cluster" unique="1" required="1">
37
+ <longdesc lang="en">
38
+ Cluster Name as defined in the Big Brother Configuration
39
+ </longdesc>
40
+ <shortdesc lang="en">Cluster Name</shortdesc>
41
+ <content type="string" default="" />
42
+ </parameter>
43
+ </parameters>
44
+
45
+ <actions>
46
+ <action name="start" timeout="20" />
47
+ <action name="stop" timeout="40" />
48
+ <action name="monitor" timeout="20" interval="10" depth="0" start-delay="0" />
49
+ <action name="reload" timeout="60" />
50
+ <action name="migrate_to" timeout="100" />
51
+ <action name="migrate_from" timeout="90" />
52
+ <action name="meta-data" timeout="5" />
53
+ <action name="validate-all" timeout="30" />
54
+ </actions>
55
+ </resource-agent>
56
+ END
57
+ }
58
+
59
+ #######################################################################
60
+
61
+ # don't exit on TERM, to test that lrmd makes sure that we do exit
62
+ trap sigterm_handler TERM
63
+ sigterm_handler() {
64
+ ocf_log info "Attempted to use TERM to bring us down. No such luck."
65
+ return
66
+ }
67
+
68
+ ipvs_cluster_usage() {
69
+ cat <<END
70
+ usage: $0 {start|stop|monitor|meta-data|validate-all}
71
+
72
+ Expects to have a fully populated OCF RA-compliant environment set.
73
+ END
74
+ }
75
+
76
+ ipvs_cluster_start() {
77
+
78
+ # Check if the protocol is already running
79
+ ipvs_cluster_monitor
80
+ if [ $? -eq ${OCF_SUCCESS} ]; then
81
+ return ${OCF_SUCCESS}
82
+ else
83
+ # Start the requested cluster
84
+ if [ `$CURL_BIN -m ${CURL_TIMEOUT_SEC} -X PUT -w "%{http_code}" -o /dev/null -s "${SUPERVISOR_URL}/cluster/${OCF_RESKEY_cluster}"` == 200 ]; then
85
+ return ${OCF_SUCCESS}
86
+ else
87
+ return ${OCF_ERR_GENERIC}
88
+ fi
89
+ fi
90
+
91
+ }
92
+
93
+ ipvs_cluster_stop() {
94
+
95
+ # Check if the protocol is already running
96
+ ipvs_cluster_monitor
97
+ if [ $? -eq ${OCF_SUCCESS} ]; then
98
+ # Stop the requested cluster
99
+ if [ `$CURL_BIN -m ${CURL_TIMEOUT_SEC} -X DELETE -w "%{http_code}" -o /dev/null -s "${SUPERVISOR_URL}/cluster/${OCF_RESKEY_cluster}"` == 200 ]; then
100
+ return ${OCF_SUCCESS}
101
+ else
102
+ return ${OCF_ERR_GENERIC}
103
+ fi
104
+ else
105
+ return ${OCF_SUCCESS}
106
+ fi
107
+
108
+ }
109
+
110
+ ipvs_cluster_monitor() {
111
+
112
+ local cluster_status
113
+ local http_status
114
+
115
+ # Check if the IPVS supervisor is running for the cluster
116
+ cluster_status=`$CURL_BIN -m ${CURL_TIMEOUT_SEC} -w '\nHTTP_Status: %{http_code}\n' -s "${SUPERVISOR_URL}/cluster/${OCF_RESKEY_cluster}"`
117
+ # If curl can't connect then we got bigger issues
118
+ if [ $? -ne 0 ]; then
119
+ return ${OCF_ERR_PERM};
120
+ fi
121
+
122
+ # Check for remote HTTP response code
123
+ http_status=`echo "${cluster_status}" |${EGREP_BIN} -e "^HTTP_Status: "|$AWK_BIN '{print $2}' |tr -d '[:alpha:][:punct:][:space:]' |head -1`
124
+
125
+ # We aren't running if we never get a status code back
126
+ if [ "${http_status}x" != "x" ]; then
127
+ if [ ${http_status} -eq 200 ]; then
128
+ if [ `echo ${cluster_status} |${EGREP_BIN} -e "^Running: "|$AWK_BIN '{print $2}'` == "true" ]; then
129
+ return ${OCF_SUCCESS}
130
+ fi
131
+ fi
132
+ fi
133
+ return ${OCF_NOT_RUNNING}
134
+
135
+ }
136
+
137
+ ipvs_cluster_validate_all() {
138
+
139
+ # Validate binary dependencies are executable
140
+ for req_bin in $AWK_BIN $CURL_BIN $EGREP_BIN; do
141
+ if [ ! -x "$req_bin" ]; then
142
+ ocf_log debug "Unable to execute (${req_bin})! Aborting.."
143
+ return ${OCF_ERR_INSTALLED}
144
+ fi
145
+ done
146
+
147
+ # Check if the IPVS supervisor knows about the cluster
148
+ if [ `$CURL_BIN -m ${CURL_TIMEOUT_SEC} -w '\nHTTP_Status: %{http_code}\n' -s "${SUPERVISOR_URL}/cluster/${OCF_RESKEY_cluster}" |${EGREP_BIN} -ce '^HTTP_Status: 200'` -eq 0 ]; then
149
+ return ${OCF_ERR_ARGS}
150
+ fi
151
+
152
+ return ${OCF_SUCCESS}
153
+
154
+ }
155
+
156
+ case $__OCF_ACTION in
157
+ meta-data) meta_data
158
+ exit ${OCF_SUCCESS}
159
+ ;;
160
+ start) ipvs_cluster_start;;
161
+ stop) ipvs_cluster_stop;;
162
+ monitor) ipvs_cluster_monitor;;
163
+ validate-all) ipvs_cluster_validate_all;;
164
+ usage|help) ipvs_cluster_usage
165
+ exit ${OCF_SUCCESS}
166
+ ;;
167
+ *) ipvs_cluster_usage
168
+ exit ${OCF_ERR_UNIMPLEMENTED}
169
+ ;;
170
+ esac
171
+
172
+ rc=$?
173
+ ocf_log debug "${OCF_RESOURCE_INSTANCE} $__OCF_ACTION : $rc"
174
+ exit $rc
data/config.ru ADDED
@@ -0,0 +1,5 @@
1
+ $LOAD_PATH.unshift File.expand_path('lib', File.dirname(__FILE__))
2
+ require 'big_brother'
3
+
4
+ use Rack::CommonLogger, BigBrother::Logger.new
5
+ run BigBrother::App
@@ -0,0 +1,49 @@
1
+ require 'async-rack'
2
+ require 'sinatra/base'
3
+ require 'em-synchrony/em-http'
4
+ require 'em/syslog'
5
+ require 'thin'
6
+ require 'yaml'
7
+
8
+ require 'sinatra/synchrony'
9
+
10
+ require 'big_brother/app'
11
+ require 'big_brother/cluster'
12
+ require 'big_brother/configuration'
13
+ require 'big_brother/ipvs'
14
+ require 'big_brother/logger'
15
+ require 'big_brother/node'
16
+ require 'big_brother/shell_executor'
17
+ require 'big_brother/status_file'
18
+ require 'big_brother/ticker'
19
+ require 'big_brother/version'
20
+
21
+ require 'thin/callbacks'
22
+ require 'thin/backends/tcp_server_with_callbacks'
23
+ require 'thin/callback_rack_handler'
24
+
25
+ module BigBrother
26
+ class << self
27
+ attr_accessor :ipvs, :clusters, :config_dir, :logger
28
+ end
29
+
30
+ self.ipvs = IPVS.new
31
+ self.clusters = {}
32
+ self.logger = BigBrother::Logger.new
33
+
34
+ def self.configure(filename)
35
+ @config_file = filename
36
+ @clusters = BigBrother::Configuration.evaluate(filename)
37
+ BigBrother::Configuration.synchronize_with_ipvs(@clusters, BigBrother.ipvs.running_configuration)
38
+ end
39
+
40
+ def self.start_ticker!
41
+ Ticker.schedule!
42
+ end
43
+
44
+ def self.reconfigure
45
+ Ticker.pause do
46
+ configure(@config_file)
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,30 @@
1
+ module BigBrother
2
+ class App < Sinatra::Base
3
+ register Sinatra::Synchrony
4
+
5
+ get "/" do
6
+ BigBrother.clusters.map do |name, cluster|
7
+ "#{cluster}: #{cluster.monitored? ? "running" : "not running"}"
8
+ end.join("\n") + "\n"
9
+ end
10
+
11
+ before "/cluster/:name" do |name|
12
+ @cluster = BigBrother.clusters[name]
13
+ halt 404, "Cluster #{name} not found" if @cluster.nil?
14
+ end
15
+
16
+ get "/cluster/:name" do |name|
17
+ [200, "Running: #{@cluster.monitored?}"]
18
+ end
19
+
20
+ put "/cluster/:name" do |name|
21
+ halt 304 if @cluster.monitored?
22
+ @cluster.start_monitoring!
23
+ end
24
+
25
+ delete "/cluster/:name" do |name|
26
+ halt 304 unless @cluster.monitored?
27
+ @cluster.stop_monitoring!
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,82 @@
1
+ module BigBrother
2
+ class CLI < Rack::Server
3
+ class Options
4
+ def parse!(args)
5
+ args, options = args.dup, {}
6
+
7
+ opt_parser = OptionParser.new do |opts|
8
+ opts.banner = "Usage: bigbro [options]"
9
+ opts.on("-c", "--config=file", String,
10
+ "BigBrother configuration file", "Default: /etc/big_brother.conf") { |v| options[:big_brother_config] = v }
11
+ opts.on("-D", "--data-dir=path", String,
12
+ "BigBrother data directory", "Default: /etc/big_brother") { |v| options[:config_dir] = v }
13
+
14
+ opts.separator ""
15
+
16
+ opts.on("-p", "--port=port", Integer,
17
+ "Runs BigBrother on the specified port.", "Default: 9292") { |v| options[:Port] = v }
18
+ opts.on("-b", "--binding=ip", String,
19
+ "Binds BigBrother to the specified ip.", "Default: 0.0.0.0") { |v| options[:Host] = v }
20
+ opts.on("-d", "--daemon", "Make server run as a Daemon.") { options[:daemonize] = true }
21
+ opts.on("-P","--pid=pid",String,
22
+ "Specifies the PID file.",
23
+ "Default: rack.pid") { |v| options[:pid] = v }
24
+
25
+ opts.separator ""
26
+
27
+ opts.on("-h", "--help", "Show this help message.") { puts opts; exit }
28
+ end
29
+
30
+ opt_parser.parse! args
31
+
32
+ options[:config] = File.expand_path("../../config.ru", File.dirname(__FILE__))
33
+ options[:server] = 'thin-with-callbacks'
34
+ options[:backend] = Thin::Backends::TcpServerWithCallbacks
35
+ options
36
+ end
37
+ end
38
+
39
+ def initialize(options = nil)
40
+ super
41
+ end
42
+
43
+ def opt_parser
44
+ Options.new
45
+ end
46
+
47
+ def start
48
+ if !File.exists?(options[:big_brother_config])
49
+ puts "Could not find #{options[:big_brother_config]}. Specify correct location with -c file"
50
+ exit 1
51
+ end
52
+
53
+ BigBrother.config_dir = options[:config_dir]
54
+
55
+ Thin::Callbacks.after_connect do
56
+ EM.syslog_setup('0.0.0.0', 514)
57
+ BigBrother.logger.info "Starting big brother on port #{options[:Port]}"
58
+
59
+ EM.synchrony do
60
+ BigBrother.configure(options[:big_brother_config])
61
+ BigBrother.start_ticker!
62
+ end
63
+
64
+ Signal.trap("HUP") do
65
+ EM.synchrony do
66
+ BigBrother.logger.info "HUP trapped. Reconfiguring big brother"
67
+ BigBrother.reconfigure
68
+ end
69
+ end
70
+ end
71
+
72
+ super
73
+ end
74
+
75
+ def default_options
76
+ super.merge(
77
+ :big_brother_config => '/etc/big_brother.conf',
78
+ :config_dir => '/etc/big_brother'
79
+ )
80
+ end
81
+ end
82
+ end