big_brother 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.
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