├── .gitignore ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── bin ├── console └── setup ├── lib ├── swiftype-monitoring.rb └── swiftype-monitoring │ ├── check.rb │ ├── check_concerns.rb │ ├── check_concerns │ ├── config.rb │ ├── es.rb │ ├── formatting.rb │ ├── kestrel.rb │ ├── mysql.rb │ └── redis.rb │ ├── config.rb │ ├── notification.rb │ ├── notification_concerns.rb │ ├── notification_concerns │ └── env.rb │ └── version.rb └── swiftype-monitoring.gemspec /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in swiftype-monitoring.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Swiftype, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwiftypeMonitoring 2 | 3 | SwiftypeMonitoring is a collection of helpers for monitoring services used at Swiftype. 4 | It can be used to build application-level monitoring using Nagios or your tool of choice. 5 | The type of monitoring checks you could write with it include "is this Resque queue too large?", 6 | "Are records in table X too told?", and "is my Elasticsearch cluster healthy?" 7 | 8 | This library was extracted from the internal Swiftype monitoring framework, 9 | however the code is presented as-is. 10 | 11 | ## Usage 12 | 13 | To use, define a subclass of SwiftypeMonitoring::Check and override 14 | the `run` method: 15 | 16 | ``` 17 | class FoobarCheck < SwiftypeMonitoring::Check 18 | # Check thresholds 19 | warning_option('Warning threshold for Foobar', 3600) 20 | critical_option('Critical threshold for Foobar', 7200) 21 | 22 | def run 23 | foobar = get_some_metric 24 | 25 | if foobar > config[:critical] 26 | critical("Foobar is too large (#{foobar}). " + 27 | "Limit is #{config[:critical]}") 28 | end 29 | 30 | if foobar > config[:warning] 31 | warning("Foobar is too large (#{foobar}). " + 32 | "Limit is #{config[:critical]}") 33 | end 34 | 35 | ok("Foobar (#{foobar}) is within acceptable limits") 36 | end 37 | end 38 | ``` 39 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "swiftype/monitoring" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "irb" 14 | IRB.start 15 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | 5 | bundle install 6 | 7 | # Do any other automated setup that you need to do here 8 | -------------------------------------------------------------------------------- /lib/swiftype-monitoring.rb: -------------------------------------------------------------------------------- 1 | require 'swiftype-monitoring/config' 2 | require 'swiftype-monitoring/check' 3 | require 'swiftype-monitoring/notification' 4 | 5 | module SwiftypeMonitoring 6 | end 7 | -------------------------------------------------------------------------------- /lib/swiftype-monitoring/check.rb: -------------------------------------------------------------------------------- 1 | require 'swiftype-monitoring' 2 | require 'swiftype-monitoring/check_concerns' 3 | 4 | require 'sensu-plugin/check/cli' 5 | 6 | module SwiftypeMonitoring 7 | class Check < Sensu::Plugin::Check::CLI 8 | # Include specific concerns 9 | include SwiftypeMonitoring::CheckConcerns::Config 10 | include SwiftypeMonitoring::CheckConcerns::Formatting 11 | include SwiftypeMonitoring::CheckConcerns::Kestrel 12 | include SwiftypeMonitoring::CheckConcerns::MySQL 13 | include SwiftypeMonitoring::CheckConcerns::Redis 14 | include SwiftypeMonitoring::CheckConcerns::ES 15 | 16 | #----------------------------------------------------------------------------------------------- 17 | # Convenient option method wrappers 18 | def self.warning_option(desc, default) 19 | integer_option(:warning, :short => '-w VALUE', :long => '--warning=VALUE', :description => desc, :default => default) 20 | end 21 | 22 | def self.critical_option(desc, default) 23 | integer_option(:critical, :short => '-c VALUE', :long => '--critical=VALUE', :description => desc, :default => default) 24 | end 25 | 26 | def self.integer_option(name, params) 27 | params[:proc] = proc { |a| a.to_i } 28 | option(name, params) 29 | end 30 | 31 | def self.boolean_option(name, params) 32 | params[:boolean] = true 33 | option(name, params) 34 | end 35 | 36 | #----------------------------------------------------------------------------------------------- 37 | def run 38 | puts "ERROR: Please define run method in your check class!" 39 | exit(1) 40 | end 41 | 42 | # Redefine output method to support additional parameter (message) 43 | def output(status_message, additional_info = nil) 44 | status_message ||= @message 45 | output = "#{@status}" 46 | output << ": #{status_message}" if status_message 47 | output << "\n#{additional_info}" if additional_info 48 | puts output 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /lib/swiftype-monitoring/check_concerns.rb: -------------------------------------------------------------------------------- 1 | require 'swiftype-monitoring/check_concerns/config' 2 | require 'swiftype-monitoring/check_concerns/mysql' 3 | require 'swiftype-monitoring/check_concerns/redis' 4 | require 'swiftype-monitoring/check_concerns/kestrel' 5 | require 'swiftype-monitoring/check_concerns/formatting' 6 | require 'swiftype-monitoring/check_concerns/es' 7 | -------------------------------------------------------------------------------- /lib/swiftype-monitoring/check_concerns/config.rb: -------------------------------------------------------------------------------- 1 | require 'swiftype-monitoring/config' 2 | 3 | module SwiftypeMonitoring 4 | module CheckConcerns 5 | module Config 6 | 7 | def swiftype_config_reader 8 | # FIXME: this assumes the script is locates in scripts/swiftype, need to make it more robust 9 | scripts_dir = File.dirname($PROGRAM_NAME) 10 | root_dir = File.expand_path('../..', scripts_dir) 11 | config_file = File.expand_path("config/config.yml", root_dir) 12 | 13 | SwiftypeMonitoring::Config.new(config_file) 14 | end 15 | 16 | # Returns a hash representing our configuration file 17 | def swiftype_config 18 | swiftype_config_reader.data 19 | end 20 | 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/swiftype-monitoring/check_concerns/es.rb: -------------------------------------------------------------------------------- 1 | require 'elasticsearch' 2 | 3 | module SwiftypeMonitoring 4 | module CheckConcerns 5 | module ES 6 | 7 | # 8 | # Simple wrapper class for ES connection that handles connection errors by exiting properly 9 | # 10 | class ConnectionWrapper 11 | attr_reader :check, :name, :config, :client 12 | 13 | def initialize(check, name, config) 14 | @check = check 15 | @name = name.to_s 16 | # use staging port by default 17 | @config = { 18 | :host => "#{config[:host]}:#{config[:port].to_s}", 19 | } 20 | @client = ::Elasticsearch::Client.new @config 21 | # Make sure the connection is actually alive 22 | ping! 23 | end 24 | 25 | # Performs a blocks of code with proper error handling for ES errors 26 | def handling_es_errors 27 | raise "Needs a block!" unless block_given? 28 | yield 29 | rescue Faraday::ConnectionFailed => e 30 | check.critical("Error while talking to ElasticSearch cluster (#{name}): #{e}") 31 | end 32 | 33 | # Test ES connection 34 | def ping! 35 | handling_es_errors do 36 | client.cluster.health 37 | end 38 | end 39 | 40 | # Returns cluster-level statistics 41 | def cluster_health 42 | handling_es_errors do 43 | stats = client.cluster.health 44 | if stats['cluster_name'] != name 45 | check.critical("Cluster name used for current port doesn't match. 46 | Provided: #{name}, returned by ES: #{stats['cluster_name']}") 47 | end 48 | stats 49 | end 50 | end 51 | 52 | def shards_info 53 | handling_es_errors do 54 | shards = client.count["_shards"] 55 | return { 56 | :shards => { 57 | :total => shards["total"].to_i, 58 | :successful => shards["successful"].to_i, 59 | :failed => shards["failed"].to_i 60 | } 61 | } 62 | end 63 | end 64 | end 65 | 66 | #--------------------------------------------------------------------------------------------- 67 | # Returns ES connection config by name 68 | def es_config_by_name(name) 69 | connections = swiftype_config['elasticsearch_connections'] || {} 70 | config = connections[name] 71 | critical("Unknown ES connection: #{name}") unless config 72 | return config.symbolize_keys 73 | end 74 | 75 | #--------------------------------------------------------------------------------------------- 76 | # Create a ES cluster connection 77 | def es_connection(name) 78 | name = name.to_s 79 | config = es_config_by_name(name) 80 | ConnectionWrapper.new(self, name, config) 81 | end 82 | 83 | end 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /lib/swiftype-monitoring/check_concerns/formatting.rb: -------------------------------------------------------------------------------- 1 | require 'swiftype-monitoring/config' 2 | 3 | module SwiftypeMonitoring 4 | module CheckConcerns 5 | module Formatting 6 | 7 | # Convert elapsed seconds from Time.now to the current Time instance 8 | # into an English human readable text like '4 hours and 1 minute ago' 9 | # It is not well suited for dates older than 1 year 10 | # 11 | # @return [String] the humanized elapsed time in pairs 12 | # Always returns a pair of words like: 13 | # '23 days and 3 hours ago' 14 | # '5 hours and 44 minutes ago' 15 | # ' 1 minute and 11 seconds ago' 16 | # 17 | # Or just 1 when no hours/minutes/seconds available: 18 | # '23 days ago' 19 | # '17 hours ago' 20 | # '1 minute ago' 21 | # '1 second ago' 22 | # 23 | # Ẃhen 0 seconds or Time instance is a future date, returns empty string: 24 | # "" 25 | def time_ago_in_words(time) 26 | return 'a very very long time ago' if time.year < 1800 27 | secs = Time.now - time 28 | return 'just now' if secs > -1 && secs < 1 29 | return '' if secs <= -1 30 | pair = ago_in_words_pair(secs) 31 | ary = ago_in_words_singularize(pair) 32 | ary.size == 0 ? '' : ary.join(' and ') << ' ago' 33 | end 34 | 35 | private 36 | 37 | # @private 38 | def ago_in_words_pair(secs) 39 | [[60, :seconds], [60, :minutes], [24, :hours], [100_000, :days]].map{ |count, name| 40 | if secs > 0 41 | secs, n = secs.divmod(count) 42 | "#{n.to_i} #{name}" 43 | end 44 | }.compact.reverse[0..1] 45 | end 46 | 47 | # @private 48 | def ago_in_words_singularize(pair) 49 | if pair.size == 1 50 | pair.map! {|part| part[0, 2].to_i == 1 ? part.chomp('s') : part } 51 | else 52 | pair.map! {|part| part[0, 2].to_i == 1 ? part.chomp('s') : part[0, 2].to_i == 0 ? nil : part } 53 | end 54 | pair.compact 55 | end 56 | 57 | end 58 | end 59 | end 60 | 61 | 62 | module TimeAgoInWords 63 | end 64 | -------------------------------------------------------------------------------- /lib/swiftype-monitoring/check_concerns/kestrel.rb: -------------------------------------------------------------------------------- 1 | require 'kjess' 2 | require 'active_support/core_ext/hash' 3 | 4 | module SwiftypeMonitoring 5 | module CheckConcerns 6 | module Kestrel 7 | 8 | # 9 | # Simple wrapper class for Kestrel connection that handles connection errors by exiting properly 10 | # 11 | class ConnectionWrapper 12 | attr_reader :check, :name, :config, :client 13 | 14 | def initialize(check, name, config) 15 | @check = check 16 | @name = name.to_s 17 | 18 | @config = { 19 | :connect_timeout => 10, 20 | :write_timeout => 10, 21 | :read_timeout => 10 22 | }.merge(config) 23 | 24 | @client = ::KJess::Client.new(@config) 25 | 26 | # Make sure the connection is actually alive 27 | ping! 28 | end 29 | 30 | # Performs a blocks of code with proper error handling for kestrel errors 31 | def handling_kestrel_errors 32 | raise "Needs a block!" unless block_given? 33 | yield 34 | rescue KJess::Error => e 35 | check.critical("Error while talking to Kestrel(#{name}): #{e}") 36 | end 37 | 38 | # Test Kestrel connection 39 | def ping! 40 | handling_kestrel_errors do 41 | client.ping 42 | end 43 | end 44 | 45 | # Returns broker-level and queue-level statistics 46 | def broker_stats 47 | handling_kestrel_errors do 48 | client.stats! 49 | end 50 | end 51 | 52 | # Returns queue-level statistics for all queues 53 | def queue_stats 54 | broker_stats["queues"] 55 | end 56 | 57 | # Return queue 58 | def queue_size(queue_name) 59 | queue_info = queue_stats[queue_name] 60 | queue_info ? queue_info["items"].to_i : nil 61 | end 62 | end 63 | 64 | #--------------------------------------------------------------------------------------------- 65 | # Returns kestrel connection config by name 66 | def kestrel_config_by_name(name) 67 | connections = swiftype_config['kestrel_brokers'] || {} 68 | config = connections[name] 69 | critical("Unknown Kestrel broker: #{name}") unless config 70 | return config.symbolize_keys 71 | end 72 | 73 | #--------------------------------------------------------------------------------------------- 74 | # Create a Kestrel broker connection 75 | def kestrel_connection(name) 76 | name = name.to_s 77 | config = kestrel_config_by_name(name) 78 | ConnectionWrapper.new(self, name, config) 79 | end 80 | 81 | end 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /lib/swiftype-monitoring/check_concerns/mysql.rb: -------------------------------------------------------------------------------- 1 | require 'mysql2' 2 | require 'ostruct' 3 | 4 | module SwiftypeMonitoring 5 | module CheckConcerns 6 | module MySQL 7 | 8 | # 9 | # Simple wrapper class for MySQL connection that handles connection errors by exiting properly 10 | # 11 | class ConnectionWrapper 12 | attr_reader :check, :name, :config, :client 13 | 14 | def initialize(check, name, config) 15 | @check = check 16 | @name = name.to_s 17 | @config = config 18 | @client = Mysql2::Client.new(config) 19 | 20 | # Make sure the connection is actually alive 21 | ping! 22 | end 23 | 24 | #------------------------------------------------------------------------------------------- 25 | # Test MySQL connection 26 | def ping! 27 | client.ping 28 | rescue => e 29 | check.critical("Error while talking to MySQL(#{name}): #{e}") 30 | end 31 | 32 | #------------------------------------------------------------------------------------------- 33 | # Run a query and return the result 34 | def query(*args) 35 | client.query(*args) 36 | rescue Mysql2::Error => e 37 | check.critical("MySQL(#{name}) Error: #{e}", "Query: #{args.inspect}") 38 | end 39 | 40 | #------------------------------------------------------------------------------------------- 41 | # Run a query and return a single row 42 | def fetch_row(sql) 43 | # Run the query 44 | results = query(sql) 45 | 46 | # Check result counts 47 | if results.count == 0 48 | check.critical("Expected to receive a single row, but result set is empty", "SQL: #{sql}") 49 | end 50 | if results.count > 1 51 | check.critical("Expected to receive a single row, but result has #{results.count} lines", "SQL: #{sql}") 52 | end 53 | 54 | # Get the first and only row 55 | return results.first 56 | end 57 | 58 | #------------------------------------------------------------------------------------------- 59 | # Run a query and return a single value result 60 | def fetch_value(sql) 61 | # Get the row 62 | row = fetch_row(sql) 63 | 64 | # Check field count 65 | if row.count > 1 66 | check.critical("Expected to receive a single value, but result has more than one field", "SQL: #{sql}\nResult: #{row.inspect}") 67 | end 68 | 69 | return row.values.first 70 | end 71 | 72 | # Given a table, id_column, and optional timestamp_column, perform a 73 | # query to find the oldest row. 74 | # 75 | # args - the Hash of arguments 76 | # :table - the String name of the table (required) 77 | # :id_columns - an Array of the String names of the ID 78 | # columns (required) 79 | # :timestamp_column - the String name of the timestamp column to 80 | # diff with NOW() (default: 'created_at') 81 | # 82 | # Returns an lag info that responds `lag` and each of the id_columns 83 | # or nil if there are no matching rows. 84 | def fetch_lag_info(args) 85 | table = args.fetch(:table) 86 | id_columns = args.fetch(:id_columns) 87 | time_column = args[:timestamp_column] || 'created_at' 88 | 89 | raise "id_columns must not be empty" if id_columns.empty? 90 | 91 | sql = "SELECT #{id_columns.join(', ')}, TIMESTAMPDIFF(SECOND, #{time_column}, NOW()) AS lag 92 | FROM #{table} ORDER BY #{time_column} ASC LIMIT 1" 93 | 94 | results = query(sql) 95 | 96 | return nil if results.count == 0 97 | 98 | oldest_row = results.first 99 | lag_info = OpenStruct.new(:lag => oldest_row['lag'].to_i) 100 | id_columns.each do |id_column| 101 | lag_info.send("#{id_column}=".to_sym, oldest_row[id_column]) 102 | end 103 | 104 | lag_info 105 | end 106 | end 107 | 108 | #--------------------------------------------------------------------------------------------- 109 | # Returns mysql connection config by name 110 | def mysql_config_by_name(name) 111 | connections = swiftype_config['mysql_connections'] || {} 112 | critical("Unknown MySQL connection: #{name}") unless connections[name] 113 | return connections[name] 114 | end 115 | 116 | #--------------------------------------------------------------------------------------------- 117 | # Create a mysql connection 118 | def mysql_connection(name) 119 | name = name.to_s 120 | config = mysql_config_by_name(name) 121 | ConnectionWrapper.new(self, name, config) 122 | end 123 | 124 | end 125 | end 126 | end 127 | -------------------------------------------------------------------------------- /lib/swiftype-monitoring/check_concerns/redis.rb: -------------------------------------------------------------------------------- 1 | require 'redis' 2 | require 'active_support/core_ext/hash' 3 | 4 | module SwiftypeMonitoring 5 | module CheckConcerns 6 | module Redis 7 | 8 | # 9 | # Simple wrapper class for Redis connection that handles connection errors by exiting properly 10 | # 11 | class ConnectionWrapper 12 | attr_reader :check, :name, :config, :client 13 | 14 | def initialize(check, name, config) 15 | @check = check 16 | @name = name.to_s 17 | @config = config 18 | @client = ::Redis.new(config) 19 | 20 | # Make sure the connection is actually alive 21 | ping 22 | end 23 | 24 | def method_missing(method, *args, &block) 25 | client.send(method, *args, &block) 26 | rescue ::Redis::BaseError => e 27 | check.critical("Redis[#{name}] Error: #{e}") 28 | end 29 | end 30 | 31 | #--------------------------------------------------------------------------------------------- 32 | # Returns redis connection config by name 33 | def redis_config_by_name(name) 34 | connections = swiftype_config['redis_connections'] || {} 35 | config = connections[name] 36 | critical("Unknown Redis connection: #{name}") unless config 37 | return config.symbolize_keys 38 | end 39 | 40 | #--------------------------------------------------------------------------------------------- 41 | # Create a Redis connection 42 | def redis_connection(name) 43 | name = name.to_s 44 | config = redis_config_by_name(name) 45 | ConnectionWrapper.new(self, name, config) 46 | end 47 | 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /lib/swiftype-monitoring/config.rb: -------------------------------------------------------------------------------- 1 | require 'yaml' 2 | require 'erb' 3 | 4 | module SwiftypeMonitoring 5 | class Config 6 | attr_reader :config_file 7 | 8 | def initialize(config_file) 9 | @config_file = config_file 10 | end 11 | 12 | def data 13 | load_config! unless @config 14 | return @config 15 | end 16 | 17 | def load_config! 18 | # Check the file 19 | unless File.readable?(config_file) 20 | raise ArgumentError, "Could not find config file: #{config_file}" 21 | end 22 | 23 | # Load and parse config file 24 | raw_config = File.read(config_file) 25 | erb_config = ERB.new(raw_config).result 26 | @config = YAML.load(erb_config) || {} 27 | end 28 | 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/swiftype-monitoring/notification.rb: -------------------------------------------------------------------------------- 1 | require 'swiftype-monitoring' 2 | require 'swiftype-monitoring/notification_concerns' 3 | require 'swiftype-monitoring/check_concerns' 4 | 5 | require 'mixlib/cli' 6 | require 'logger' 7 | require 'active_support/core_ext/integer/inflections' 8 | 9 | module SwiftypeMonitoring 10 | class Notification 11 | # Make it a CLI script 12 | include Mixlib::CLI 13 | 14 | # Include specific concerns 15 | include SwiftypeMonitoring::CheckConcerns::Config 16 | include SwiftypeMonitoring::NotificationConcerns::Env 17 | 18 | def run 19 | parse_options 20 | send 21 | end 22 | 23 | def send 24 | raise NotImplementedEror, "Please define the #send method on your notification class!" 25 | end 26 | 27 | def logger 28 | @logger ||= Logger.new(STDOUT) 29 | end 30 | 31 | def render_template(template_name, template_type = nil) 32 | # FIXME: this assumes the script is locates in scripts/swiftype, need to make it more robust 33 | scripts_dir = File.dirname($PROGRAM_NAME) 34 | root_dir = File.expand_path('../..', scripts_dir) 35 | templates_dir = File.join(root_dir, 'config', 'notification_templates') 36 | 37 | # Read template 38 | file_name = [ template_name, template_type, 'erb' ].compact.join('.') 39 | template_file = File.join(templates_dir, file_name) 40 | template = File.read(template_file) 41 | 42 | # Render the thing 43 | ERB.new(template, nil, '-').result(binding) 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/swiftype-monitoring/notification_concerns.rb: -------------------------------------------------------------------------------- 1 | require 'swiftype-monitoring/notification_concerns/env' 2 | -------------------------------------------------------------------------------- /lib/swiftype-monitoring/notification_concerns/env.rb: -------------------------------------------------------------------------------- 1 | require 'erb' 2 | 3 | module SwiftypeMonitoring 4 | module NotificationConcerns 5 | module Env 6 | # Include rendering helpers 7 | include ERB::Util 8 | 9 | def read_env(name) 10 | ENV[name] or raise "Missing environment variable: #{name}" 11 | end 12 | 13 | def contact_email_address 14 | read_env('NAGIOS_CONTACTEMAIL') 15 | end 16 | 17 | def alert_type 18 | read_env('NAGIOS_NOTIFICATIONTYPE') 19 | end 20 | 21 | def acknowledgement? 22 | alert_type == 'ACKNOWLEDGEMENT' 23 | end 24 | 25 | def alert_number 26 | read_env('NAGIOS_SERVICENOTIFICATIONNUMBER').to_i 27 | end 28 | 29 | def service_name 30 | read_env('NAGIOS_SERVICEDESC') 31 | end 32 | 33 | def service_status 34 | read_env('NAGIOS_SERVICESTATE') 35 | end 36 | 37 | def service_duration 38 | read_env('NAGIOS_SERVICEDURATION') 39 | end 40 | 41 | def service_output 42 | read_env('NAGIOS_SERVICEOUTPUT') 43 | end 44 | 45 | def service_long_output 46 | read_env('NAGIOS_LONGSERVICEOUTPUT') 47 | end 48 | 49 | def alert_timestamp 50 | Time.parse(read_env('NAGIOS_LONGDATETIME')) 51 | end 52 | 53 | def alert_timestamp_short 54 | alert_timestamp.strftime('%Y-%m-%d %H:%M:%S') 55 | end 56 | 57 | def ack_comment 58 | read_env('NAGIOS_SERVICEACKCOMMENT') 59 | end 60 | 61 | def alert_action 62 | return 'ACKNOWLEDGED' if alert_type == 'ACKNOWLEDGEMENT' 63 | return 'RECOVERED' if alert_type == 'RECOVERY' 64 | return 'TRIGGERED' 65 | end 66 | 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /lib/swiftype-monitoring/version.rb: -------------------------------------------------------------------------------- 1 | module SwiftypeMonitoring 2 | VERSION = "0.1.0" 3 | end 4 | -------------------------------------------------------------------------------- /swiftype-monitoring.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'swiftype-monitoring/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "swiftype-monitoring" 8 | spec.version = SwiftypeMonitoring::VERSION 9 | spec.authors = ["Swiftype Technical Operations Team"] 10 | spec.email = ["ops@swiftype.com"] 11 | 12 | spec.summary = %q{Swiftype Monitoring APIs} 13 | spec.description = %q{Useful collection for writing monitoring checks/scripts for Swiftype infrastructure.} 14 | spec.homepage = "https://swiftype.com/" 15 | spec.license = "MIT" 16 | 17 | spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 18 | spec.bindir = "exe" 19 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 20 | spec.require_paths = ["lib"] 21 | 22 | spec.add_development_dependency "bundler", "~> 1.10" 23 | spec.add_development_dependency "rake", "~> 10.0" 24 | 25 | # Sensu plugins API 26 | spec.add_dependency 'sensu-plugin' 27 | 28 | # Some service clients we may need 29 | spec.add_dependency 'mysql2' 30 | spec.add_dependency 'elasticsearch' 31 | spec.add_dependency 'redis' 32 | spec.add_dependency 'activesupport' 33 | spec.add_dependency 'kjess' 34 | end 35 | --------------------------------------------------------------------------------