sensu 0.5.1
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.
- data/.gitignore +5 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +58 -0
- data/MIT-LICENSE.txt +20 -0
- data/README.org +58 -0
- data/Rakefile +8 -0
- data/bin/sensu-api +6 -0
- data/bin/sensu-client +6 -0
- data/bin/sensu-server +6 -0
- data/lib/sensu.rb +4 -0
- data/lib/sensu/api.rb +137 -0
- data/lib/sensu/client.rb +69 -0
- data/lib/sensu/config.rb +55 -0
- data/lib/sensu/helpers.rb +18 -0
- data/lib/sensu/server.rb +184 -0
- data/sensu.gemspec +27 -0
- metadata +194 -0
    
        data/Gemfile
    ADDED
    
    
    
        data/Gemfile.lock
    ADDED
    
    | @@ -0,0 +1,58 @@ | |
| 1 | 
            +
            PATH
         | 
| 2 | 
            +
              remote: .
         | 
| 3 | 
            +
              specs:
         | 
| 4 | 
            +
                sensu (0.4.0)
         | 
| 5 | 
            +
                  amqp (= 0.7.4)
         | 
| 6 | 
            +
                  async_sinatra
         | 
| 7 | 
            +
                  em-hiredis
         | 
| 8 | 
            +
                  em-syslog
         | 
| 9 | 
            +
                  json
         | 
| 10 | 
            +
                  thin
         | 
| 11 | 
            +
                  uuidtools
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            GEM
         | 
| 14 | 
            +
              remote: http://rubygems.org/
         | 
| 15 | 
            +
              specs:
         | 
| 16 | 
            +
                amqp (0.7.4)
         | 
| 17 | 
            +
                  eventmachine
         | 
| 18 | 
            +
                async_sinatra (0.5.0)
         | 
| 19 | 
            +
                  rack (>= 1.2.1)
         | 
| 20 | 
            +
                  sinatra (>= 1.0)
         | 
| 21 | 
            +
                callsite (0.0.5)
         | 
| 22 | 
            +
                  bundler (~> 1.0.0)
         | 
| 23 | 
            +
                daemons (1.1.4)
         | 
| 24 | 
            +
                em-hiredis (0.1.0)
         | 
| 25 | 
            +
                  hiredis (~> 0.3.0)
         | 
| 26 | 
            +
                em-syslog (0.0.2)
         | 
| 27 | 
            +
                  eventmachine
         | 
| 28 | 
            +
                em-ventually (0.1.2)
         | 
| 29 | 
            +
                  callsite (~> 0.0.5)
         | 
| 30 | 
            +
                  eventmachine
         | 
| 31 | 
            +
                eventmachine (0.12.10)
         | 
| 32 | 
            +
                hiredis (0.3.2)
         | 
| 33 | 
            +
                json (1.5.4)
         | 
| 34 | 
            +
                mime-types (1.16)
         | 
| 35 | 
            +
                minitest (2.5.1)
         | 
| 36 | 
            +
                rack (1.3.2)
         | 
| 37 | 
            +
                rake (0.9.2)
         | 
| 38 | 
            +
                rest-client (1.6.3)
         | 
| 39 | 
            +
                  mime-types (>= 1.16)
         | 
| 40 | 
            +
                sinatra (1.2.6)
         | 
| 41 | 
            +
                  rack (~> 1.1)
         | 
| 42 | 
            +
                  tilt (< 2.0, >= 1.2.2)
         | 
| 43 | 
            +
                thin (1.2.11)
         | 
| 44 | 
            +
                  daemons (>= 1.0.9)
         | 
| 45 | 
            +
                  eventmachine (>= 0.12.6)
         | 
| 46 | 
            +
                  rack (>= 1.0.0)
         | 
| 47 | 
            +
                tilt (1.3.3)
         | 
| 48 | 
            +
                uuidtools (2.1.2)
         | 
| 49 | 
            +
             | 
| 50 | 
            +
            PLATFORMS
         | 
| 51 | 
            +
              ruby
         | 
| 52 | 
            +
             | 
| 53 | 
            +
            DEPENDENCIES
         | 
| 54 | 
            +
              em-ventually
         | 
| 55 | 
            +
              minitest
         | 
| 56 | 
            +
              rake
         | 
| 57 | 
            +
              rest-client
         | 
| 58 | 
            +
              sensu!
         | 
    
        data/MIT-LICENSE.txt
    ADDED
    
    | @@ -0,0 +1,20 @@ | |
| 1 | 
            +
            Copyright (c) 2011 Sonian Inc.
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            Permission is hereby granted, free of charge, to any person obtaining
         | 
| 4 | 
            +
            a copy of this software and associated documentation files (the
         | 
| 5 | 
            +
            "Software"), to deal in the Software without restriction, including
         | 
| 6 | 
            +
            without limitation the rights to use, copy, modify, merge, publish,
         | 
| 7 | 
            +
            distribute, sublicense, and/or sell copies of the Software, and to
         | 
| 8 | 
            +
            permit persons to whom the Software is furnished to do so, subject to
         | 
| 9 | 
            +
            the following conditions:
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            The above copyright notice and this permission notice shall be
         | 
| 12 | 
            +
            included in all copies or substantial portions of the Software.
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         | 
| 15 | 
            +
            EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         | 
| 16 | 
            +
            MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         | 
| 17 | 
            +
            NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
         | 
| 18 | 
            +
            LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         | 
| 19 | 
            +
            OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         | 
| 20 | 
            +
            WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         | 
    
        data/README.org
    ADDED
    
    | @@ -0,0 +1,58 @@ | |
| 1 | 
            +
            * Welcome to Sensu
         | 
| 2 | 
            +
              Sensu is a monitoring system framework, handling the remote execution of checks.
         | 
| 3 | 
            +
             | 
| 4 | 
            +
              Checks can utilize user created plugins, returning an exit status code and outputting to STDOUT.
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              Check results are handled by user created handlers.
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            * License
         | 
| 9 | 
            +
              Sensu is released under the [[https://github.com/sonian/sensu/blob/master/MIT-LICENSE.txt][MIT license]].
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            * Contributing
         | 
| 12 | 
            +
              - [[http://help.github.com/fork-a-repo/][Fork]] [[https://github.com/sonian/sensu][Sensu]]
         | 
| 13 | 
            +
              - Use a [[https://github.com/dchelimsky/rspec/wiki/Topic-Branches][topic branch]]
         | 
| 14 | 
            +
              - Create a [[http://help.github.com/send-pull-requests/][pull request]]
         | 
| 15 | 
            +
             | 
| 16 | 
            +
              Keep it simple.
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            ** Readme Driven Development
         | 
| 19 | 
            +
            *** A Client Will
         | 
| 20 | 
            +
              - Have a set of attributes to describe it, including its responsibilities
         | 
| 21 | 
            +
              - Send keep-alives to a server
         | 
| 22 | 
            +
              - Subscribe to a set of queues bound to a set of fanout exchanges, determined by its responsibilities
         | 
| 23 | 
            +
              - Substitute tokens in check commands with their matching client attribute
         | 
| 24 | 
            +
              - Receive checks from subscriptions, execute them, and publish the results to a queue with its client name
         | 
| 25 | 
            +
              - Not allow overlapping check executions of the same name
         | 
| 26 | 
            +
              - Report when it is unaware of a check it received from a subscription
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            *** A Server Will
         | 
| 29 | 
            +
              - Subscribe to a queue for check scheduling, another for check results
         | 
| 30 | 
            +
              - Populate the check scheduling queue using a JSON config file
         | 
| 31 | 
            +
              - Pull a check from the check scheduling queue and push it onto its associated fanout exchanges
         | 
| 32 | 
            +
              - Pull check results, storing the latest events for clients, a good result will flush a previous event for that client
         | 
| 33 | 
            +
              - Create an event when it stops receiving keep-alives from a client, a new keep-alive for the client will clear the event
         | 
| 34 | 
            +
              - Trigger a event handler, determined by the type of the check that produced the event, providing it with a JSON event file
         | 
| 35 | 
            +
             | 
| 36 | 
            +
            *** An API Will
         | 
| 37 | 
            +
              - List all current events
         | 
| 38 | 
            +
              - List all clients and their attributes
         | 
| 39 | 
            +
              - Show a client and its attributes
         | 
| 40 | 
            +
              - Remove a client and resolve associated events
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            *** A Plugin Will
         | 
| 43 | 
            +
              - Output to STDOUT
         | 
| 44 | 
            +
              - Return a valid exit status code
         | 
| 45 | 
            +
             | 
| 46 | 
            +
            *** A Handler Will
         | 
| 47 | 
            +
              - Accept a command line argument "-f", for an event file path
         | 
| 48 | 
            +
              - Parse the JSON event file
         | 
| 49 | 
            +
              - Handle the event as it wishes
         | 
| 50 | 
            +
             | 
| 51 | 
            +
            ** Testing
         | 
| 52 | 
            +
            *** Requirements
         | 
| 53 | 
            +
              - RabbitMQ (default configuration)
         | 
| 54 | 
            +
              - Redis (default configuration)
         | 
| 55 | 
            +
             | 
| 56 | 
            +
            *** MiniTest
         | 
| 57 | 
            +
             | 
| 58 | 
            +
              : rake test
         | 
    
        data/Rakefile
    ADDED
    
    
    
        data/bin/sensu-api
    ADDED
    
    
    
        data/bin/sensu-client
    ADDED
    
    
    
        data/bin/sensu-server
    ADDED
    
    
    
        data/lib/sensu.rb
    ADDED
    
    
    
        data/lib/sensu/api.rb
    ADDED
    
    | @@ -0,0 +1,137 @@ | |
| 1 | 
            +
            require File.join(File.dirname(__FILE__), 'config')
         | 
| 2 | 
            +
            require 'sinatra/async'
         | 
| 3 | 
            +
            require 'em-hiredis'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Sensu
         | 
| 6 | 
            +
              class API < Sinatra::Base
         | 
| 7 | 
            +
                register Sinatra::Async
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def self.run(options={})
         | 
| 10 | 
            +
                  EM.run do
         | 
| 11 | 
            +
                    self.setup(options)
         | 
| 12 | 
            +
                    self.run!(:port => @settings['api']['port'])
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    Signal.trap('INT') do
         | 
| 15 | 
            +
                      EM.stop
         | 
| 16 | 
            +
                    end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    Signal.trap('TERM') do
         | 
| 19 | 
            +
                      EM.stop
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                def self.setup(options={})
         | 
| 25 | 
            +
                  config = Sensu::Config.new(options)
         | 
| 26 | 
            +
                  @settings = config.settings
         | 
| 27 | 
            +
                  set :redis, EM::Hiredis.connect('redis://' + @settings['redis']['host'] + ':' + @settings['redis']['port'].to_s)
         | 
| 28 | 
            +
                  connection = AMQP.connect(symbolize_keys(@settings['rabbitmq']))
         | 
| 29 | 
            +
                  set :amq, AMQP::Channel.new(connection)
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                helpers do
         | 
| 33 | 
            +
                  include Rack::Utils
         | 
| 34 | 
            +
                  alias_method :conn, :settings
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                before do
         | 
| 38 | 
            +
                  content_type 'application/json'
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                aget '/clients' do
         | 
| 42 | 
            +
                  current_clients = Array.new
         | 
| 43 | 
            +
                  conn.redis.smembers('clients').callback do |clients|
         | 
| 44 | 
            +
                    unless clients.empty?
         | 
| 45 | 
            +
                      clients.each_with_index do |client, index|
         | 
| 46 | 
            +
                        conn.redis.get('client:' + client).callback do |client_json|
         | 
| 47 | 
            +
                          current_clients.push(JSON.parse(client_json))
         | 
| 48 | 
            +
                          body current_clients.to_json if index == clients.size-1
         | 
| 49 | 
            +
                        end
         | 
| 50 | 
            +
                      end
         | 
| 51 | 
            +
                    else
         | 
| 52 | 
            +
                      body current_clients.to_json
         | 
| 53 | 
            +
                    end
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                aget '/client/:id' do |client|
         | 
| 58 | 
            +
                  conn.redis.get('client:' + client).callback do |client_json|
         | 
| 59 | 
            +
                    status 404 if client_json.nil?
         | 
| 60 | 
            +
                    body client_json
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                aget '/events' do
         | 
| 65 | 
            +
                  current_events = Hash.new
         | 
| 66 | 
            +
                  conn.redis.smembers('clients').callback do |clients|
         | 
| 67 | 
            +
                    unless clients.empty?
         | 
| 68 | 
            +
                      clients.each_with_index do |client, index|
         | 
| 69 | 
            +
                        conn.redis.hgetall('events:' + client).callback do |events|
         | 
| 70 | 
            +
                          client_events = Hash[*events]
         | 
| 71 | 
            +
                          client_events.each do |key, value|
         | 
| 72 | 
            +
                            client_events[key] = JSON.parse(value)
         | 
| 73 | 
            +
                          end
         | 
| 74 | 
            +
                          current_events.store(client, client_events) unless client_events.empty?
         | 
| 75 | 
            +
                          body current_events.to_json if index == clients.size-1
         | 
| 76 | 
            +
                        end
         | 
| 77 | 
            +
                      end
         | 
| 78 | 
            +
                    else
         | 
| 79 | 
            +
                      body current_events.to_json
         | 
| 80 | 
            +
                    end
         | 
| 81 | 
            +
                  end
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                adelete '/client/:id' do |client|
         | 
| 85 | 
            +
                  conn.redis.sismember('clients', client).callback do |client_exists|
         | 
| 86 | 
            +
                    unless client_exists == 0
         | 
| 87 | 
            +
                      conn.redis.exists('events:' + client).callback do |events_exist|
         | 
| 88 | 
            +
                        unless events_exist == 0
         | 
| 89 | 
            +
                          conn.redis.hgetall('events:' + client).callback do |events|
         | 
| 90 | 
            +
                            Hash[*events].keys.each do |check|
         | 
| 91 | 
            +
                              conn.amq.queue('results').publish({'check' => check, 'client' => client, 'status' => 0, 'output' => 'client is being removed...'}.to_json)
         | 
| 92 | 
            +
                            end
         | 
| 93 | 
            +
                            EM.add_timer(10) do
         | 
| 94 | 
            +
                              conn.redis.srem('clients', client)
         | 
| 95 | 
            +
                              conn.redis.del('events:' + client)
         | 
| 96 | 
            +
                              conn.redis.del('client:' + client)
         | 
| 97 | 
            +
                            end
         | 
| 98 | 
            +
                          end
         | 
| 99 | 
            +
                        else
         | 
| 100 | 
            +
                          conn.redis.srem('clients', client)
         | 
| 101 | 
            +
                          conn.redis.del('events:' + client)
         | 
| 102 | 
            +
                          conn.redis.del('client:' + client)
         | 
| 103 | 
            +
                        end
         | 
| 104 | 
            +
                        status 204
         | 
| 105 | 
            +
                        body ''
         | 
| 106 | 
            +
                      end
         | 
| 107 | 
            +
                    else
         | 
| 108 | 
            +
                      status 404
         | 
| 109 | 
            +
                      body ''
         | 
| 110 | 
            +
                    end
         | 
| 111 | 
            +
                  end
         | 
| 112 | 
            +
                end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                apost '/test/client' do
         | 
| 115 | 
            +
                  client = '{
         | 
| 116 | 
            +
                    "name": "test",
         | 
| 117 | 
            +
                    "address": "127.0.0.1",
         | 
| 118 | 
            +
                    "subscriptions": [
         | 
| 119 | 
            +
                      "foo",
         | 
| 120 | 
            +
                      "bar"
         | 
| 121 | 
            +
                    ]
         | 
| 122 | 
            +
                  }'
         | 
| 123 | 
            +
                  conn.redis.set('client:test', client).callback do
         | 
| 124 | 
            +
                    conn.redis.sadd('clients', 'test')
         | 
| 125 | 
            +
                    status 201
         | 
| 126 | 
            +
                    body ''
         | 
| 127 | 
            +
                  end
         | 
| 128 | 
            +
                end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                apost '/test/event' do
         | 
| 131 | 
            +
                  conn.redis.hset('events:test', 'test', {'status' => 2, 'output' => 'CRITICAL :: test'}.to_json).callback do
         | 
| 132 | 
            +
                    status 201
         | 
| 133 | 
            +
                    body ''
         | 
| 134 | 
            +
                  end
         | 
| 135 | 
            +
                end
         | 
| 136 | 
            +
              end
         | 
| 137 | 
            +
            end
         | 
    
        data/lib/sensu/client.rb
    ADDED
    
    | @@ -0,0 +1,69 @@ | |
| 1 | 
            +
            require File.join(File.dirname(__FILE__), 'config')
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Sensu
         | 
| 4 | 
            +
              class Client
         | 
| 5 | 
            +
                def self.run(options={})
         | 
| 6 | 
            +
                  EM.run do
         | 
| 7 | 
            +
                    client = self.new(options)
         | 
| 8 | 
            +
                    client.setup_amqp
         | 
| 9 | 
            +
                    client.setup_keep_alives
         | 
| 10 | 
            +
                    client.setup_subscriptions
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                    Signal.trap('INT') do
         | 
| 13 | 
            +
                      EM.stop
         | 
| 14 | 
            +
                    end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    Signal.trap('TERM') do
         | 
| 17 | 
            +
                      EM.stop
         | 
| 18 | 
            +
                    end
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                def initialize(options={})
         | 
| 23 | 
            +
                  config = Sensu::Config.new(:config_file => options[:config_file])
         | 
| 24 | 
            +
                  @settings = config.settings
         | 
| 25 | 
            +
                  @checks_in_progress = Array.new
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                def setup_amqp
         | 
| 29 | 
            +
                  connection = AMQP.connect(symbolize_keys(@settings['rabbitmq']))
         | 
| 30 | 
            +
                  @amq = AMQP::Channel.new(connection)
         | 
| 31 | 
            +
                  @result_queue = @amq.queue('results')
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def setup_keep_alives
         | 
| 35 | 
            +
                  keepalive_queue = @amq.queue('keepalives')
         | 
| 36 | 
            +
                  keepalive_queue.publish(@settings['client'].merge({'timestamp' => Time.now.to_i}).to_json)
         | 
| 37 | 
            +
                  EM.add_periodic_timer(30) do
         | 
| 38 | 
            +
                    keepalive_queue.publish(@settings['client'].merge({'timestamp' => Time.now.to_i}).to_json)
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                def execute_check(check)
         | 
| 43 | 
            +
                  if @settings['checks'][check['name']]
         | 
| 44 | 
            +
                    unless @checks_in_progress.include?(check['name'])
         | 
| 45 | 
            +
                      @checks_in_progress.push(check['name'])
         | 
| 46 | 
            +
                      command = @settings['checks'][check['name']]['command'].gsub(/:::(.*?):::/) do
         | 
| 47 | 
            +
                        @settings['client'][$1.to_s].to_s
         | 
| 48 | 
            +
                      end
         | 
| 49 | 
            +
                      EM.system('sh', '-c', command + ' 2>&1') do |output, status|
         | 
| 50 | 
            +
                        @result_queue.publish({'check' => check['name'], 'client' => @settings['client']['name'], 'status' => status.exitstatus, 'output' => output}.to_json)
         | 
| 51 | 
            +
                        @checks_in_progress.delete(check['name'])
         | 
| 52 | 
            +
                      end
         | 
| 53 | 
            +
                    end
         | 
| 54 | 
            +
                  else
         | 
| 55 | 
            +
                    @result_queue.publish({'check' => check['name'], 'client' => @settings['client']['name'], 'status' => 3, 'output' => 'Unknown check'}.to_json)
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                def setup_subscriptions
         | 
| 60 | 
            +
                  @settings['client']['subscriptions'].each do |exchange|
         | 
| 61 | 
            +
                    uniq_queue_name = UUIDTools::UUID.random_create.to_s
         | 
| 62 | 
            +
                    @amq.queue(uniq_queue_name, :auto_delete => true).bind(@amq.fanout(exchange)).subscribe do |check_json|
         | 
| 63 | 
            +
                      check = JSON.parse(check_json)
         | 
| 64 | 
            +
                      execute_check(check)
         | 
| 65 | 
            +
                    end
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
              end
         | 
| 69 | 
            +
            end
         | 
    
        data/lib/sensu/config.rb
    ADDED
    
    | @@ -0,0 +1,55 @@ | |
| 1 | 
            +
            require 'rubygems' if RUBY_VERSION < '1.9.0'
         | 
| 2 | 
            +
            require 'json'
         | 
| 3 | 
            +
            require 'uuidtools'
         | 
| 4 | 
            +
            require 'amqp'
         | 
| 5 | 
            +
            require 'em/syslog'
         | 
| 6 | 
            +
            require File.join(File.dirname(__FILE__), 'helpers')
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            module Sensu
         | 
| 9 | 
            +
              class Config
         | 
| 10 | 
            +
                attr_accessor :settings
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def initialize(options={})
         | 
| 13 | 
            +
                  config_file = options[:config_file] || '/etc/sensu/config.json'
         | 
| 14 | 
            +
                  @settings = JSON.parse(File.open(config_file, 'r').read)
         | 
| 15 | 
            +
                  validate_config
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def validate_config
         | 
| 19 | 
            +
                  @settings['checks'].each do |name, info|
         | 
| 20 | 
            +
                    unless info['interval'].is_a?(Integer) && info['interval'] > 0
         | 
| 21 | 
            +
                      raise 'configuration invalid, missing interval for check ' + name
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
                    unless info['command'].is_a?(String)
         | 
| 24 | 
            +
                      raise 'configuration invalid, missing command for check ' + name
         | 
| 25 | 
            +
                    end
         | 
| 26 | 
            +
                    unless info['subscribers'].is_a?(Array) && info['subscribers'].count > 0
         | 
| 27 | 
            +
                      raise 'configuration invalid, missing subscribers for check ' + name
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
                  unless @settings['client']['name'].is_a?(String)
         | 
| 31 | 
            +
                    raise 'configuration invalid, client must have a name'
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                  unless @settings['client']['address'].is_a?(String)
         | 
| 34 | 
            +
                    raise 'configuration invalid, client must have an address (ip or hostname)'
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
                  unless @settings['client']['subscriptions'].is_a?(Array) && @settings['client']['subscriptions'].count > 0
         | 
| 37 | 
            +
                    raise 'configuration invalid, client must have subscriptions'
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                def create_working_directory
         | 
| 42 | 
            +
                  begin
         | 
| 43 | 
            +
                    Dir.mkdir('/tmp/sensu')
         | 
| 44 | 
            +
                  rescue SystemCallError
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
                end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                def purge_working_directory
         | 
| 49 | 
            +
                  Dir.foreach('/tmp/sensu') do |file|
         | 
| 50 | 
            +
                    next if file == '.' || file == '..'
         | 
| 51 | 
            +
                    File.delete('/tmp/sensu/' + file)
         | 
| 52 | 
            +
                  end
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
            end
         | 
| @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            def symbolize_keys(item)
         | 
| 2 | 
            +
              case item
         | 
| 3 | 
            +
              when Array
         | 
| 4 | 
            +
                item.map do |i|
         | 
| 5 | 
            +
                  symbolize_keys(i)
         | 
| 6 | 
            +
                end
         | 
| 7 | 
            +
              when Hash
         | 
| 8 | 
            +
                Hash[
         | 
| 9 | 
            +
                      item.map do |key, value|
         | 
| 10 | 
            +
                        k = key.is_a?(String) ? key.to_sym : key
         | 
| 11 | 
            +
                        v = symbolize_keys(value)
         | 
| 12 | 
            +
                        [k,v]
         | 
| 13 | 
            +
                      end
         | 
| 14 | 
            +
                    ]
         | 
| 15 | 
            +
              else
         | 
| 16 | 
            +
                item
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
            end
         | 
    
        data/lib/sensu/server.rb
    ADDED
    
    | @@ -0,0 +1,184 @@ | |
| 1 | 
            +
            require File.join(File.dirname(__FILE__), 'config')
         | 
| 2 | 
            +
            require 'em-hiredis'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Sensu
         | 
| 5 | 
            +
              class Server
         | 
| 6 | 
            +
                attr_accessor :redis
         | 
| 7 | 
            +
                alias :redis_connection :redis
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                def self.run(options={})
         | 
| 10 | 
            +
                  EM.run do
         | 
| 11 | 
            +
                    server = self.new(options)
         | 
| 12 | 
            +
                    server.setup_logging
         | 
| 13 | 
            +
                    server.setup_redis
         | 
| 14 | 
            +
                    server.setup_amqp
         | 
| 15 | 
            +
                    server.setup_keep_alives
         | 
| 16 | 
            +
                    server.setup_handlers
         | 
| 17 | 
            +
                    server.setup_results
         | 
| 18 | 
            +
                    server.setup_publisher
         | 
| 19 | 
            +
                    server.setup_populator
         | 
| 20 | 
            +
                    server.setup_keep_alive_monitor
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                    Signal.trap('INT') do
         | 
| 23 | 
            +
                      EM.stop
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                    Signal.trap('TERM') do
         | 
| 27 | 
            +
                      EM.stop
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                def initialize(options={})
         | 
| 33 | 
            +
                  config = Sensu::Config.new(:config_file => options[:config_file])
         | 
| 34 | 
            +
                  config.create_working_directory
         | 
| 35 | 
            +
                  @settings = config.settings
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                def setup_logging
         | 
| 39 | 
            +
                  EM.syslog_setup(@settings['syslog']['host'], @settings['syslog']['port'])
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                def setup_redis
         | 
| 43 | 
            +
                  @redis = EM::Hiredis.connect('redis://' + @settings['redis']['host'] + ':' + @settings['redis']['port'].to_s)
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                def setup_amqp
         | 
| 47 | 
            +
                  connection = AMQP.connect(symbolize_keys(@settings['rabbitmq']))
         | 
| 48 | 
            +
                  @amq = AMQP::Channel.new(connection)
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                def setup_keep_alives
         | 
| 52 | 
            +
                  @amq.queue('keepalives').subscribe do |keepalive_json|
         | 
| 53 | 
            +
                    client = JSON.parse(keepalive_json)['name']
         | 
| 54 | 
            +
                    @redis.set('client:' + client, keepalive_json).callback do
         | 
| 55 | 
            +
                      @redis.sadd('clients', client)
         | 
| 56 | 
            +
                    end
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                def setup_handlers
         | 
| 61 | 
            +
                  @handler_queue = EM::Queue.new
         | 
| 62 | 
            +
                  handlers_in_progress = 0
         | 
| 63 | 
            +
                  handle = Proc.new do |event|
         | 
| 64 | 
            +
                    if handlers_in_progress < 15
         | 
| 65 | 
            +
                      event_file = proc do
         | 
| 66 | 
            +
                        handlers_in_progress += 1
         | 
| 67 | 
            +
                        file_name = '/tmp/sensu/event-' + UUIDTools::UUID.random_create.to_s
         | 
| 68 | 
            +
                        File.open(file_name, 'w') do |file|
         | 
| 69 | 
            +
                          file.write(JSON.pretty_generate(event))
         | 
| 70 | 
            +
                        end
         | 
| 71 | 
            +
                        file_name
         | 
| 72 | 
            +
                      end
         | 
| 73 | 
            +
                      handler = proc do |event_file|
         | 
| 74 | 
            +
                        EM.system('sh', '-c', @settings['handlers'][event['check']['handler']] + ' -f ' + event_file  + ' 2>&1') do |output, status|
         | 
| 75 | 
            +
                          EM.debug('handled :: ' + event['check']['handler'] + ' :: ' + status.exitstatus.to_s + ' :: ' + output)
         | 
| 76 | 
            +
                          File.delete(event_file)
         | 
| 77 | 
            +
                          handlers_in_progress -= 1
         | 
| 78 | 
            +
                        end
         | 
| 79 | 
            +
                      end
         | 
| 80 | 
            +
                      EM.defer(event_file, handler)
         | 
| 81 | 
            +
                    else
         | 
| 82 | 
            +
                      @handler_queue.push(event)
         | 
| 83 | 
            +
                    end
         | 
| 84 | 
            +
                    EM.next_tick do
         | 
| 85 | 
            +
                      @handler_queue.pop(&handle)
         | 
| 86 | 
            +
                    end
         | 
| 87 | 
            +
                  end
         | 
| 88 | 
            +
                  @handler_queue.pop(&handle)
         | 
| 89 | 
            +
                end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                def handle_event(event)
         | 
| 92 | 
            +
                  @handler_queue.push(event)
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                def setup_results
         | 
| 96 | 
            +
                  @amq.queue('results').subscribe do |result_json|
         | 
| 97 | 
            +
                    result = JSON.parse(result_json)
         | 
| 98 | 
            +
                    @redis.get('client:' + result['client']).callback do |client_json|
         | 
| 99 | 
            +
                      unless client_json.nil?
         | 
| 100 | 
            +
                        client = JSON.parse(client_json)
         | 
| 101 | 
            +
                        check = {'name' => result['check']}
         | 
| 102 | 
            +
                        check.merge!(@settings['checks'][result['check']]) if @settings['checks'].has_key?(result['check'])
         | 
| 103 | 
            +
                        check['handler'] = 'default' unless check['handler']
         | 
| 104 | 
            +
                        event = {
         | 
| 105 | 
            +
                          'client' => client,
         | 
| 106 | 
            +
                          'check' => check,
         | 
| 107 | 
            +
                          'status' => result['status'],
         | 
| 108 | 
            +
                          'output' => result['output']
         | 
| 109 | 
            +
                        }
         | 
| 110 | 
            +
                        if check['handler'] == 'metric'
         | 
| 111 | 
            +
                          handle_event(event)
         | 
| 112 | 
            +
                        else
         | 
| 113 | 
            +
                          if result['status'] == 0
         | 
| 114 | 
            +
                            @redis.hexists('events:' + client['name'], result['check']).callback do |exists|
         | 
| 115 | 
            +
                              if exists == 1
         | 
| 116 | 
            +
                                @redis.hdel('events:' + client['name'], result['check'])
         | 
| 117 | 
            +
                                event['action'] = 'resolve'
         | 
| 118 | 
            +
                                handle_event(event)
         | 
| 119 | 
            +
                              end
         | 
| 120 | 
            +
                            end
         | 
| 121 | 
            +
                          else
         | 
| 122 | 
            +
                            @redis.hset('events:' + client['name'], result['check'], {'status' => result['status'], 'output' => result['output']}.to_json).callback do
         | 
| 123 | 
            +
                              event['action'] = 'create'
         | 
| 124 | 
            +
                              handle_event(event)
         | 
| 125 | 
            +
                            end
         | 
| 126 | 
            +
                          end
         | 
| 127 | 
            +
                        end
         | 
| 128 | 
            +
                      end
         | 
| 129 | 
            +
                    end
         | 
| 130 | 
            +
                  end
         | 
| 131 | 
            +
                end
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                def setup_publisher
         | 
| 134 | 
            +
                  exchanges = Hash.new
         | 
| 135 | 
            +
                  @amq.queue('checks').subscribe do |check_json|
         | 
| 136 | 
            +
                    check = JSON.parse(check_json)
         | 
| 137 | 
            +
                    check['subscribers'].each do |exchange|
         | 
| 138 | 
            +
                      if exchanges[exchange].nil?
         | 
| 139 | 
            +
                        exchanges[exchange] = @amq.fanout(exchange)
         | 
| 140 | 
            +
                      end
         | 
| 141 | 
            +
                      exchanges[exchange].publish({'name' => check['name']}.to_json)
         | 
| 142 | 
            +
                      EM.debug('published :: ' + exchange + ' :: ' + check['name'])
         | 
| 143 | 
            +
                    end
         | 
| 144 | 
            +
                  end
         | 
| 145 | 
            +
                end
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                def setup_populator
         | 
| 148 | 
            +
                  check_queue = @amq.queue('checks')
         | 
| 149 | 
            +
                  @settings['checks'].each_with_index do |(name, info), index|
         | 
| 150 | 
            +
                    EM.add_timer(7*index) do
         | 
| 151 | 
            +
                      EM.add_periodic_timer(info['interval']) do
         | 
| 152 | 
            +
                        check_queue.publish({'name' => name, 'subscribers' => info['subscribers']}.to_json)
         | 
| 153 | 
            +
                      end
         | 
| 154 | 
            +
                    end
         | 
| 155 | 
            +
                  end
         | 
| 156 | 
            +
                end
         | 
| 157 | 
            +
             | 
| 158 | 
            +
                def setup_keep_alive_monitor
         | 
| 159 | 
            +
                  result_queue = @amq.queue('results')
         | 
| 160 | 
            +
                  EM.add_periodic_timer(30) do
         | 
| 161 | 
            +
                    @redis.smembers('clients').callback do |clients|
         | 
| 162 | 
            +
                      clients.each do |client_id|
         | 
| 163 | 
            +
                        @redis.get('client:' + client_id).callback do |client_json|
         | 
| 164 | 
            +
                          client = JSON.parse(client_json)
         | 
| 165 | 
            +
                          time_since_last_check = Time.now.to_i - client['timestamp']
         | 
| 166 | 
            +
                          case
         | 
| 167 | 
            +
                          when time_since_last_check >= 180
         | 
| 168 | 
            +
                            result_queue.publish({'check' => 'keepalive', 'client' => client['name'], 'status' => 2, 'output' => 'No keep-alive sent from host in over 180 seconds'}.to_json)
         | 
| 169 | 
            +
                          when time_since_last_check >= 120
         | 
| 170 | 
            +
                            result_queue.publish({'check' => 'keepalive', 'client' => client['name'], 'status' => 1, 'output' => 'No keep-alive sent from host in over 120 seconds'}.to_json)
         | 
| 171 | 
            +
                          else
         | 
| 172 | 
            +
                            @redis.hexists('events:' + client_id, 'keepalive').callback do |exists|
         | 
| 173 | 
            +
                              if exists == 1
         | 
| 174 | 
            +
                                result_queue.publish({'check' => 'keepalive', 'client' => client['name'], 'status' => 0, 'output' => 'Keep-alive sent from host'}.to_json)
         | 
| 175 | 
            +
                              end
         | 
| 176 | 
            +
                            end
         | 
| 177 | 
            +
                          end
         | 
| 178 | 
            +
                        end
         | 
| 179 | 
            +
                      end
         | 
| 180 | 
            +
                    end
         | 
| 181 | 
            +
                  end
         | 
| 182 | 
            +
                end
         | 
| 183 | 
            +
              end
         | 
| 184 | 
            +
            end
         | 
    
        data/sensu.gemspec
    ADDED
    
    | @@ -0,0 +1,27 @@ | |
| 1 | 
            +
            Gem::Specification.new do |s|
         | 
| 2 | 
            +
              s.name        = "sensu"
         | 
| 3 | 
            +
              s.version     = "0.5.1"
         | 
| 4 | 
            +
              s.authors     = ["Sean Porter", "Justin Kolberg"]
         | 
| 5 | 
            +
              s.email       = ["sean.porter@sonian.net", "justin.kolberg@sonian.net"]
         | 
| 6 | 
            +
              s.homepage    = "https://github.com/sonian/sensu"
         | 
| 7 | 
            +
              s.summary     = %q{A server monitoring framework}
         | 
| 8 | 
            +
              s.description = %q{A server monitoring framework using the publish-subscribe model}
         | 
| 9 | 
            +
              s.license     = "MIT"
         | 
| 10 | 
            +
              s.has_rdoc    = false
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              s.add_dependency("amqp", "0.7.4")
         | 
| 13 | 
            +
              s.add_dependency("json")
         | 
| 14 | 
            +
              s.add_dependency("uuidtools")
         | 
| 15 | 
            +
              s.add_dependency("em-hiredis")
         | 
| 16 | 
            +
              s.add_dependency("em-syslog")
         | 
| 17 | 
            +
              s.add_dependency("async_sinatra")
         | 
| 18 | 
            +
              s.add_dependency("thin")
         | 
| 19 | 
            +
              s.add_development_dependency('rake')
         | 
| 20 | 
            +
              s.add_development_dependency('minitest')
         | 
| 21 | 
            +
              s.add_development_dependency('em-ventually')
         | 
| 22 | 
            +
              s.add_development_dependency('rest-client')
         | 
| 23 | 
            +
             | 
| 24 | 
            +
              s.files         = `git ls-files`.split("\n").reject {|f| f =~ /(dist|test)/}
         | 
| 25 | 
            +
              s.executables   = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
         | 
| 26 | 
            +
              s.require_paths = ["lib"]
         | 
| 27 | 
            +
            end
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,194 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification 
         | 
| 2 | 
            +
            name: sensu
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version 
         | 
| 4 | 
            +
              prerelease: 
         | 
| 5 | 
            +
              version: 0.5.1
         | 
| 6 | 
            +
            platform: ruby
         | 
| 7 | 
            +
            authors: 
         | 
| 8 | 
            +
            - Sean Porter
         | 
| 9 | 
            +
            - Justin Kolberg
         | 
| 10 | 
            +
            autorequire: 
         | 
| 11 | 
            +
            bindir: bin
         | 
| 12 | 
            +
            cert_chain: []
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            date: 2011-09-09 00:00:00 Z
         | 
| 15 | 
            +
            dependencies: 
         | 
| 16 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 17 | 
            +
              name: amqp
         | 
| 18 | 
            +
              prerelease: false
         | 
| 19 | 
            +
              requirement: &id001 !ruby/object:Gem::Requirement 
         | 
| 20 | 
            +
                none: false
         | 
| 21 | 
            +
                requirements: 
         | 
| 22 | 
            +
                - - "="
         | 
| 23 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 24 | 
            +
                    version: 0.7.4
         | 
| 25 | 
            +
              type: :runtime
         | 
| 26 | 
            +
              version_requirements: *id001
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 28 | 
            +
              name: json
         | 
| 29 | 
            +
              prerelease: false
         | 
| 30 | 
            +
              requirement: &id002 !ruby/object:Gem::Requirement 
         | 
| 31 | 
            +
                none: false
         | 
| 32 | 
            +
                requirements: 
         | 
| 33 | 
            +
                - - ">="
         | 
| 34 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 35 | 
            +
                    version: "0"
         | 
| 36 | 
            +
              type: :runtime
         | 
| 37 | 
            +
              version_requirements: *id002
         | 
| 38 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 39 | 
            +
              name: uuidtools
         | 
| 40 | 
            +
              prerelease: false
         | 
| 41 | 
            +
              requirement: &id003 !ruby/object:Gem::Requirement 
         | 
| 42 | 
            +
                none: false
         | 
| 43 | 
            +
                requirements: 
         | 
| 44 | 
            +
                - - ">="
         | 
| 45 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 46 | 
            +
                    version: "0"
         | 
| 47 | 
            +
              type: :runtime
         | 
| 48 | 
            +
              version_requirements: *id003
         | 
| 49 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 50 | 
            +
              name: em-hiredis
         | 
| 51 | 
            +
              prerelease: false
         | 
| 52 | 
            +
              requirement: &id004 !ruby/object:Gem::Requirement 
         | 
| 53 | 
            +
                none: false
         | 
| 54 | 
            +
                requirements: 
         | 
| 55 | 
            +
                - - ">="
         | 
| 56 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 57 | 
            +
                    version: "0"
         | 
| 58 | 
            +
              type: :runtime
         | 
| 59 | 
            +
              version_requirements: *id004
         | 
| 60 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 61 | 
            +
              name: em-syslog
         | 
| 62 | 
            +
              prerelease: false
         | 
| 63 | 
            +
              requirement: &id005 !ruby/object:Gem::Requirement 
         | 
| 64 | 
            +
                none: false
         | 
| 65 | 
            +
                requirements: 
         | 
| 66 | 
            +
                - - ">="
         | 
| 67 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 68 | 
            +
                    version: "0"
         | 
| 69 | 
            +
              type: :runtime
         | 
| 70 | 
            +
              version_requirements: *id005
         | 
| 71 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 72 | 
            +
              name: async_sinatra
         | 
| 73 | 
            +
              prerelease: false
         | 
| 74 | 
            +
              requirement: &id006 !ruby/object:Gem::Requirement 
         | 
| 75 | 
            +
                none: false
         | 
| 76 | 
            +
                requirements: 
         | 
| 77 | 
            +
                - - ">="
         | 
| 78 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 79 | 
            +
                    version: "0"
         | 
| 80 | 
            +
              type: :runtime
         | 
| 81 | 
            +
              version_requirements: *id006
         | 
| 82 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 83 | 
            +
              name: thin
         | 
| 84 | 
            +
              prerelease: false
         | 
| 85 | 
            +
              requirement: &id007 !ruby/object:Gem::Requirement 
         | 
| 86 | 
            +
                none: false
         | 
| 87 | 
            +
                requirements: 
         | 
| 88 | 
            +
                - - ">="
         | 
| 89 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 90 | 
            +
                    version: "0"
         | 
| 91 | 
            +
              type: :runtime
         | 
| 92 | 
            +
              version_requirements: *id007
         | 
| 93 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 94 | 
            +
              name: rake
         | 
| 95 | 
            +
              prerelease: false
         | 
| 96 | 
            +
              requirement: &id008 !ruby/object:Gem::Requirement 
         | 
| 97 | 
            +
                none: false
         | 
| 98 | 
            +
                requirements: 
         | 
| 99 | 
            +
                - - ">="
         | 
| 100 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 101 | 
            +
                    version: "0"
         | 
| 102 | 
            +
              type: :development
         | 
| 103 | 
            +
              version_requirements: *id008
         | 
| 104 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 105 | 
            +
              name: minitest
         | 
| 106 | 
            +
              prerelease: false
         | 
| 107 | 
            +
              requirement: &id009 !ruby/object:Gem::Requirement 
         | 
| 108 | 
            +
                none: false
         | 
| 109 | 
            +
                requirements: 
         | 
| 110 | 
            +
                - - ">="
         | 
| 111 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 112 | 
            +
                    version: "0"
         | 
| 113 | 
            +
              type: :development
         | 
| 114 | 
            +
              version_requirements: *id009
         | 
| 115 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 116 | 
            +
              name: em-ventually
         | 
| 117 | 
            +
              prerelease: false
         | 
| 118 | 
            +
              requirement: &id010 !ruby/object:Gem::Requirement 
         | 
| 119 | 
            +
                none: false
         | 
| 120 | 
            +
                requirements: 
         | 
| 121 | 
            +
                - - ">="
         | 
| 122 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 123 | 
            +
                    version: "0"
         | 
| 124 | 
            +
              type: :development
         | 
| 125 | 
            +
              version_requirements: *id010
         | 
| 126 | 
            +
            - !ruby/object:Gem::Dependency 
         | 
| 127 | 
            +
              name: rest-client
         | 
| 128 | 
            +
              prerelease: false
         | 
| 129 | 
            +
              requirement: &id011 !ruby/object:Gem::Requirement 
         | 
| 130 | 
            +
                none: false
         | 
| 131 | 
            +
                requirements: 
         | 
| 132 | 
            +
                - - ">="
         | 
| 133 | 
            +
                  - !ruby/object:Gem::Version 
         | 
| 134 | 
            +
                    version: "0"
         | 
| 135 | 
            +
              type: :development
         | 
| 136 | 
            +
              version_requirements: *id011
         | 
| 137 | 
            +
            description: A server monitoring framework using the publish-subscribe model
         | 
| 138 | 
            +
            email: 
         | 
| 139 | 
            +
            - sean.porter@sonian.net
         | 
| 140 | 
            +
            - justin.kolberg@sonian.net
         | 
| 141 | 
            +
            executables: 
         | 
| 142 | 
            +
            - sensu-api
         | 
| 143 | 
            +
            - sensu-client
         | 
| 144 | 
            +
            - sensu-server
         | 
| 145 | 
            +
            extensions: []
         | 
| 146 | 
            +
             | 
| 147 | 
            +
            extra_rdoc_files: []
         | 
| 148 | 
            +
             | 
| 149 | 
            +
            files: 
         | 
| 150 | 
            +
            - .gitignore
         | 
| 151 | 
            +
            - Gemfile
         | 
| 152 | 
            +
            - Gemfile.lock
         | 
| 153 | 
            +
            - MIT-LICENSE.txt
         | 
| 154 | 
            +
            - README.org
         | 
| 155 | 
            +
            - Rakefile
         | 
| 156 | 
            +
            - bin/sensu-api
         | 
| 157 | 
            +
            - bin/sensu-client
         | 
| 158 | 
            +
            - bin/sensu-server
         | 
| 159 | 
            +
            - lib/sensu.rb
         | 
| 160 | 
            +
            - lib/sensu/api.rb
         | 
| 161 | 
            +
            - lib/sensu/client.rb
         | 
| 162 | 
            +
            - lib/sensu/config.rb
         | 
| 163 | 
            +
            - lib/sensu/helpers.rb
         | 
| 164 | 
            +
            - lib/sensu/server.rb
         | 
| 165 | 
            +
            - sensu.gemspec
         | 
| 166 | 
            +
            homepage: https://github.com/sonian/sensu
         | 
| 167 | 
            +
            licenses: 
         | 
| 168 | 
            +
            - MIT
         | 
| 169 | 
            +
            post_install_message: 
         | 
| 170 | 
            +
            rdoc_options: []
         | 
| 171 | 
            +
             | 
| 172 | 
            +
            require_paths: 
         | 
| 173 | 
            +
            - lib
         | 
| 174 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement 
         | 
| 175 | 
            +
              none: false
         | 
| 176 | 
            +
              requirements: 
         | 
| 177 | 
            +
              - - ">="
         | 
| 178 | 
            +
                - !ruby/object:Gem::Version 
         | 
| 179 | 
            +
                  version: "0"
         | 
| 180 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement 
         | 
| 181 | 
            +
              none: false
         | 
| 182 | 
            +
              requirements: 
         | 
| 183 | 
            +
              - - ">="
         | 
| 184 | 
            +
                - !ruby/object:Gem::Version 
         | 
| 185 | 
            +
                  version: "0"
         | 
| 186 | 
            +
            requirements: []
         | 
| 187 | 
            +
             | 
| 188 | 
            +
            rubyforge_project: 
         | 
| 189 | 
            +
            rubygems_version: 1.8.8
         | 
| 190 | 
            +
            signing_key: 
         | 
| 191 | 
            +
            specification_version: 3
         | 
| 192 | 
            +
            summary: A server monitoring framework
         | 
| 193 | 
            +
            test_files: []
         | 
| 194 | 
            +
             |