From 0b1f68f374903b27ba17c4bddb10aded7176b6d4 Mon Sep 17 00:00:00 2001 From: CyberLeo Date: Sun, 9 Nov 2014 01:47:22 -0600 Subject: [PATCH] And behold, thus she sprang, fully formed, from the head of her progenitor. --- .rbenv-vars | 2 ++ .ruby-version | 1 + Gemfile | 6 ++++ Gemfile.lock | 34 +++++++++++++++++++ Procfile | 1 + app.rb | 12 +++++++ config.ru | 10 ++++++ etc/checks.yml | 13 ++++++++ lib/check.rb | 59 +++++++++++++++++++++++++++++++++ lib/check_exception.rb | 13 ++++++++ lib/checks.rb | 38 +++++++++++++++++++++ lib/configuration.rb | 59 +++++++++++++++++++++++++++++++++ lib/database_check.rb | 21 ++++++++++++ lib/database_check_exception.rb | 8 +++++ lib/website_check.rb | 12 +++++++ lib/website_check_exception.rb | 2 ++ unicorn.rb | 21 ++++++++++++ views/index.erb | 24 ++++++++++++++ views/result.erb | 9 +++++ 19 files changed, 345 insertions(+) create mode 100644 .rbenv-vars create mode 100644 .ruby-version create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 Procfile create mode 100644 app.rb create mode 100644 config.ru create mode 100644 etc/checks.yml create mode 100644 lib/check.rb create mode 100644 lib/check_exception.rb create mode 100644 lib/checks.rb create mode 100644 lib/configuration.rb create mode 100644 lib/database_check.rb create mode 100644 lib/database_check_exception.rb create mode 100644 lib/website_check.rb create mode 100644 lib/website_check_exception.rb create mode 100644 unicorn.rb create mode 100644 views/index.erb create mode 100644 views/result.erb diff --git a/.rbenv-vars b/.rbenv-vars new file mode 100644 index 0000000..f86c6a4 --- /dev/null +++ b/.rbenv-vars @@ -0,0 +1,2 @@ +PORT=8080 +RACK_ENV=development diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..7d2ed7c --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +2.1.4 diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..92992d1 --- /dev/null +++ b/Gemfile @@ -0,0 +1,6 @@ +source 'https://rubygems.org/' + +gem 'foreman' +gem 'unicorn' +gem 'sinatra' +gem 'ruby-mysql' diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..78ebf4c --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,34 @@ +GEM + remote: https://rubygems.org/ + specs: + dotenv (0.11.1) + dotenv-deployment (~> 0.0.2) + dotenv-deployment (0.0.2) + foreman (0.75.0) + dotenv (~> 0.11.1) + thor (~> 0.19.1) + kgio (2.9.2) + rack (1.5.2) + rack-protection (1.5.3) + rack + raindrops (0.13.0) + ruby-mysql (2.9.12) + sinatra (1.4.5) + rack (~> 1.4) + rack-protection (~> 1.4) + tilt (~> 1.3, >= 1.3.4) + thor (0.19.1) + tilt (1.4.1) + unicorn (4.8.3) + kgio (~> 2.6) + rack + raindrops (~> 0.7) + +PLATFORMS + ruby + +DEPENDENCIES + foreman + ruby-mysql + sinatra + unicorn diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..dc85175 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: bundle exec unicorn -p $PORT -c unicorn.rb diff --git a/app.rb b/app.rb new file mode 100644 index 0000000..c0dd40c --- /dev/null +++ b/app.rb @@ -0,0 +1,12 @@ +require 'sinatra/base' +require 'checks' + +$checks = Checks.new + +class App < Sinatra::Base + get '/' do + $checks.check + status ( $checks.passed? ? 200 : 500 ) + erb :index, :locals => { :results => $checks.results } + end +end diff --git a/config.ru b/config.ru new file mode 100644 index 0000000..13dfd2b --- /dev/null +++ b/config.ru @@ -0,0 +1,10 @@ +use Rack::Reloader, 0 +use Rack::ContentLength +use Rack::Logger + +require 'sinatra' + +$:.push('lib') +require './app.rb' + +run App diff --git a/etc/checks.yml b/etc/checks.yml new file mode 100644 index 0000000..5b72f3d --- /dev/null +++ b/etc/checks.yml @@ -0,0 +1,13 @@ +--- +- class: WebsiteCheck + name: Main + uri: http://10.4.4.4/ +- class: WebsiteCheck + name: Mail + uri: http://10.4.4.5/ +- class: WebsiteCheck + name: Simple + uri: http://10.4.4.7/ +- class: DatabaseCheck + name: MySQL + uri: mysql://sys_ping:sys_ping@db:3306/ diff --git a/lib/check.rb b/lib/check.rb new file mode 100644 index 0000000..4d4be78 --- /dev/null +++ b/lib/check.rb @@ -0,0 +1,59 @@ +require 'check_exception' + +class Check + @name = nil + attr_accessor :name + + @uri = nil + attr_accessor :uri + + @timeout = nil # seconds + attr_accessor :timeout + + @max_age = nil # seconds + attr_accessor :max_age + + @reqeust = nil + @passed = nil + @error = nil + attr_reader :error + + def initialize(params = {}) + self.name = params[:name] + self.uri = params[:uri] + self.timeout = params[:timeout] || 5 + self.max_age = params[:max_age] || 30 + end + + def type + raise "Override me!" + end + + def perform_check + raise "Override me!" + end + + def do_it(force = false) + return unless force || stale? + @request = Time.now + begin + Timeout.timeout(timeout) do + perform_check + @passed = true + end + rescue Exception => err + STDERR.puts "#{err.class}: #{err.message}\n#{err.backtrace.join("\n")}" + @error = "#{err.class}: #{err.message}" + @passed = false + end + end + + def stale? + @request ||= Time.at(0) + @request < Time.now - max_age + end + + def passed? + @passed + end +end diff --git a/lib/check_exception.rb b/lib/check_exception.rb new file mode 100644 index 0000000..c086c4d --- /dev/null +++ b/lib/check_exception.rb @@ -0,0 +1,13 @@ +class CheckException < Exception + attr_accessor :exception_type + attr_accessor :data + + def initialize(exception_type, data) + @exception_type = exception_type + @data = data + end + + def message + "#{@exception_type.to_s.gsub(/_/, ' ')}: #{data.inspect}" + end +end diff --git a/lib/checks.rb b/lib/checks.rb new file mode 100644 index 0000000..45f30e9 --- /dev/null +++ b/lib/checks.rb @@ -0,0 +1,38 @@ +require 'configuration' +require 'check' +require 'database_check' +require 'website_check' + + +class Checks + @@config = "checks.yml" + + def self.config + Configuration.load(@@config) + end + + def initialize + @checks = [] + end + + def reload_config + return unless !@config || @config.stale? + @config ||= self.class.config + @checks = @config.map do |check| + Kernel.const_get(check[:class]).new(check) + end + end + + def check + reload_config + @checks.each(&:do_it) + end + + def passed? + @checks.map(&:passed?).uniq == [ true ] + end + + def results + @checks + end +end diff --git a/lib/configuration.rb b/lib/configuration.rb new file mode 100644 index 0000000..eb09ef9 --- /dev/null +++ b/lib/configuration.rb @@ -0,0 +1,59 @@ +require 'yaml' + +class Configuration + include Enumerable + + @@config_path = File.expand_path(File.join(File.dirname(__FILE__), '..', 'etc')) + + def initialize(name) + @config_name = name + end + + def config_file + File.join(@@config_path, @config_name) + end + + def load_config + STDERR.puts "Reloading configuration" + @config_hash = indifferent_params(YAML::load(File.open(config_file))) + @config_time = File.stat(config_file).mtime + end + + def stale? + File.stat(config_file).mtime != @config_time + end + + def config_hash + load_config if !@config_hash || stale? + @config_hash + end + + def each(*args, &block) + config_hash.each(*args, &block) + end + + def [](key) + config_hash[key] + end + + def indifferent_hash + Hash.new {|hash,key| hash[key.to_s] if Symbol === key } + end + + def indifferent_params(object) + case object + when Hash + new_hash = indifferent_hash + object.each {|key, value| new_hash[key] = indifferent_params(value) } + new_hash + when Array + object.map {|item| indifferent_params(item) } + else + object + end + end + + def self.load(name) + new(name) + end +end diff --git a/lib/database_check.rb b/lib/database_check.rb new file mode 100644 index 0000000..854e97e --- /dev/null +++ b/lib/database_check.rb @@ -0,0 +1,21 @@ +require 'database_check_exception' +require 'mysql' + +class DatabaseCheck < Check + def type + "Database" + end + + def perform_check + dsn = URI(uri) + scheme = dsn.scheme + host = dsn.host + port = dsn.port + user = dsn.user + pass = dsn.password + base = dsn.path.gsub(/^\/*|\/*$/, '') + raise DatabaseCheckException.unhandled_database_type(scheme) unless scheme == 'mysql' + my = Mysql.connect(host, user, pass, base) + my.query('SELECT CURRENT_TIMESTAMP;') + end +end diff --git a/lib/database_check_exception.rb b/lib/database_check_exception.rb new file mode 100644 index 0000000..9499522 --- /dev/null +++ b/lib/database_check_exception.rb @@ -0,0 +1,8 @@ +class DatabaseCheckException < CheckException + def self.unhandled_database_type(type) + self.new(:unhandled_database_type, type) + end + def self.maintenance_exception(data) + self.new(:maintenance_exception, data) + end +end diff --git a/lib/website_check.rb b/lib/website_check.rb new file mode 100644 index 0000000..bd49c45 --- /dev/null +++ b/lib/website_check.rb @@ -0,0 +1,12 @@ +require 'website_check_exception' + +class WebsiteCheck < Check + def type + "Website" + end + + def perform_check + to_fetch = URI(uri) + Net::HTTP.get(to_fetch) + end +end diff --git a/lib/website_check_exception.rb b/lib/website_check_exception.rb new file mode 100644 index 0000000..591d96d --- /dev/null +++ b/lib/website_check_exception.rb @@ -0,0 +1,2 @@ +class WebsiteCheckException < CheckException +end diff --git a/unicorn.rb b/unicorn.rb new file mode 100644 index 0000000..698c9d6 --- /dev/null +++ b/unicorn.rb @@ -0,0 +1,21 @@ +worker_processes 1 +preload_app true +timeout 30 + +before_fork do |server, worker| + + Signal.trap 'TERM' do + puts 'Unicorn master intercepting TERM and sending myself QUIT instead' + Process.kill 'QUIT', Process.pid + end + +end + +after_fork do |server, worker| + + Signal.trap 'TERM' do + puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT' + end + +end + diff --git a/views/index.erb b/views/index.erb new file mode 100644 index 0000000..389802d --- /dev/null +++ b/views/index.erb @@ -0,0 +1,24 @@ + + + + + + + +
+<% results.each do |result| %> + <%= erb :result, :locals => { :result => result } %> +<% end %> +
+ + diff --git a/views/result.erb b/views/result.erb new file mode 100644 index 0000000..a89d8fd --- /dev/null +++ b/views/result.erb @@ -0,0 +1,9 @@ +<% +if result.passed? + sc = 'up' + err = '' +else + sc = 'down' + err = result.error ? " - #{CGI.escapeHTML(result.error)}" : '' +end +%><%= result.type %>: <%= result.name %> <%= sc %><%= err %>
\ No newline at end of file -- 2.42.0