lita-sensu 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4baba6224f27645733a5b860d864041d3a4700f6
4
+ data.tar.gz: 054dc0ba79a352b44c9f562afd9ec74ec547f021
5
+ SHA512:
6
+ metadata.gz: a58e9647abdf7b512832d70091b856d44992fddee454aeb698bfd6dba2ea94046aa62d2064f749b1689428475b63b52bab4c0f40439ab3de63f0aa65992bdbc4
7
+ data.tar.gz: 28a0d79bdae1df530cb1fa348f8b8ffb13513791712dcd58e6d10075996d9c0b0130fd30300841d6d80ffd98168f0c7cee0482798b33cf7697d671e9eaf89865
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ lita_config.rb
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ script: bundle exec rake
5
+ before_install:
6
+ - gem update --system
7
+ services:
8
+ - redis-server
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,14 @@
1
+ Copyright 2013 EvenUp Inc
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
14
+
data/README.md ADDED
@@ -0,0 +1,22 @@
1
+ # lita-sensu
2
+
3
+ [![Build Status](https://travis-ci.org/jlambert121/lita-sensu.png?branch=master)](https://travis-ci.org/jlambert121/lita-sensu)
4
+ [![Coverage Status](https://coveralls.io/repos/jlambert121/lita-sensu/badge.png)](https://coveralls.io/r/jlambert121/lita-sensu)
5
+
6
+ TODO: Add a description of the plugin.
7
+
8
+ ## Installation
9
+
10
+ Add lita-sensu to your Lita instance's Gemfile:
11
+
12
+ ``` ruby
13
+ gem "lita-sensu"
14
+ ```
15
+
16
+ ## Configuration
17
+
18
+ TODO: Describe any configuration attributes the plugin exposes.
19
+
20
+ ## Usage
21
+
22
+ TODO: Describe the plugin's features and how to use them.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
data/lib/lita-sensu.rb ADDED
@@ -0,0 +1,12 @@
1
+ require "lita"
2
+
3
+ Lita.load_locales Dir[File.expand_path(
4
+ File.join("..", "..", "locales", "*.yml"), __FILE__
5
+ )]
6
+
7
+ require "lita/handlers/sensu"
8
+
9
+ Lita::Handlers::Sensu.template_root File.expand_path(
10
+ File.join("..", "..", "templates"),
11
+ __FILE__
12
+ )
@@ -0,0 +1,195 @@
1
+ require 'time'
2
+
3
+ module Lita
4
+ module Handlers
5
+ class Sensu < Handler
6
+ # Handle errors
7
+ # https://github.com/MrTin/lita-chuck_norris/blob/master/lib/lita/handlers/chuck_norris.rb
8
+
9
+ config :api_url, default: '127.0.0.1', type: String
10
+ config :api_port, default: 4567, type: Integer
11
+ config :domain, type: String
12
+ config :api_user, type: String
13
+ config :api_pass, type: String
14
+
15
+ route(/sensu client ([^\s]*$)/, :client, help: {"sensu client <client>" => "Shows information on a specific client"})
16
+ route(/sensu client ([^\s]*) history/, :client_history, help: {"sensu client <client> history" => "Shows history information for a specific client"})
17
+ route(/sensu clients/, :clients, help: {"sensu clients" => "List sensu clients"})
18
+ route(/sensu events(?: for (.*))?/, :events, help: {"sensu events [for <client>]" => "Shows current events, optionally for only a specific client"})
19
+ route(/sensu info/, :info, help: { "sensu info" => "Displays sensu information"})
20
+ route(/sensu remove client (.*)/, :remove_client, help: {"sensu remove client <client>" => "Remove client from sensu"})
21
+ route(/sensu resolve event (.*)(?:\/)(.*)/, :resolve, help: {"sensu resolve event <client>[/service]" => "Resolve event/all events for client"})
22
+ route(/sensu silence ([^\s\/]*)(?:\/)?([^\s]*)?(?: for (\d+)(\w))?/, :silence, help: {"sensu silence <hostname>[/<check>][ for <duration><units>]" => "Silence event"})
23
+ route(/sensu stash(es)?/, :stashes, help: {"sensu stashes" => "Displays current sensu stashes"})
24
+
25
+ def client(response)
26
+ client = add_domain(response.matches[0][0])
27
+ resp = http.get("#{config.api_url}:#{config.api_port}/clients/#{client}")
28
+ if resp.status == 200
29
+ client = MultiJson.load(resp.body, :symbolize_keys => true)
30
+ response.reply(MultiJson.dump(client, :pretty => true))
31
+ elsif resp.status == 404
32
+ response.reply("#{client} was not found")
33
+ else
34
+ log.warn("Sensu returned an internal error fetching #{config.api_url}:#{config.api_port}/clients/#{client}")
35
+ response.reply("An error occurred fetching client #{client}")
36
+ end
37
+ end
38
+
39
+ def client_history(response)
40
+ client = add_domain(response.matches[0][0])
41
+ resp = http.get("#{config.api_url}:#{config.api_port}/clients/#{client}/history")
42
+ if resp.status == 200
43
+ history = MultiJson.load(resp.body, :symbolize_keys => true).sort{|a,b| a[:check]<=>b[:check]}
44
+ response.reply(render_template('client_history', history: history))
45
+ else
46
+ log.warn("Sensu returned an internal error fetching #{config.api_url}:#{config.api_port}/clients/#{client}/history")
47
+ response.reply("An error occurred fetching client #{client} history")
48
+ end
49
+ end
50
+
51
+ def clients(response)
52
+ resp = http.get("#{config.api_url}:#{config.api_port}/clients")
53
+ if resp.status == 200
54
+ clients = MultiJson.load(resp.body, :symbolize_keys => true).sort{|a,b| a[:name]<=>b[:name]}
55
+ response.reply(render_template('clients', clients: clients))
56
+ else
57
+ log.warn("Sensu returned an internal error fetching #{config.api_url}:#{config.api_port}/clients")
58
+ response.reply('An error occurred fetching clients')
59
+ end
60
+ end
61
+
62
+ def events(response)
63
+ if response.matches[0][0]
64
+ client = '/' + add_domain(response.matches[0][0])
65
+ else
66
+ client = ''
67
+ end
68
+
69
+ resp = http.get("#{config.api_url}:#{config.api_port}/events#{client}")
70
+ if resp.status == 200
71
+ events = MultiJson.load(resp.body, :symbolize_keys => true).sort{|a,b| a[:client][:name]<=>b[:client][:name]}
72
+ response.reply(render_template('events', events: events))
73
+ else
74
+ log.warn("Sensu returned an internal error fetching #{config.api_url}:#{config.api_port}/events#{client}")
75
+ response.reply('An error occurred fetching clients')
76
+ end
77
+ end
78
+
79
+ def info(response)
80
+ resp = http.get("#{config.api_url}:#{config.api_port}/info")
81
+ raise RequestError unless resp.status == 200
82
+ info = MultiJson.load(resp.body, :symbolize_keys => true)
83
+ response.reply(MultiJson.dump(info, :pretty => true))
84
+ end
85
+
86
+ def remove_client(response)
87
+ client = add_domain(response.matches[0][0])
88
+ resp = http.delete("#{config.api_url}:#{config.api_port}/clients/#{client}")
89
+ if resp.status == 202
90
+ response.reply("#{client} removed")
91
+ elsif resp.status == 404
92
+ response.reply("#{client} was not found")
93
+ else
94
+ log.warn("Sensu returned an internal error deleting #{config.api_url}:#{config.api_port}/clients/#{client}")
95
+ response.reply("An error occurred removing #{client}")
96
+ end
97
+ end
98
+
99
+ def resolve(response)
100
+ client = add_domain(response.matches[0][0])
101
+ check = response.matches[0][1]
102
+
103
+ data = { :client => client, :check => check }
104
+ resp = http.post("#{config.api_url}:#{config.api_port}/resolve", MultiJson.dump(data))
105
+ if resp.status == 202
106
+ response.reply("#{client}/#{check} resolved")
107
+ elsif resp.status == 400
108
+ response.reply("Resolve message was malformed: #{MultiJson.dump(data)}")
109
+ elsif resp.status == 404
110
+ response.reply("#{client}/#{check} was not found")
111
+ else
112
+ log.warn("Sensu returned an internal error resolving #{config.api_url}:#{config.api_port}/resolve with #{MultiJson.dump(data)}")
113
+ response.reply("There was an error resolving #{client}/#{check}")
114
+ end
115
+ end
116
+
117
+ def silence(response)
118
+ client = add_domain(response.matches[0][0])
119
+ check = response.matches[0][1] || nil
120
+ duration = response.matches[0][2].to_i || nil
121
+ units = response.matches[0][3] || nil
122
+
123
+ if check != nil && check != ''
124
+ path = client + '/' + check
125
+ else
126
+ path = client
127
+ end
128
+
129
+ if units
130
+ case units
131
+ when 's'
132
+ expiration = duration
133
+ when 'm'
134
+ expiration = duration * 60
135
+ when 'h'
136
+ expiration = duration * 3600
137
+ when 'd'
138
+ expiration = duration * 3600 * 24
139
+ else
140
+ response.reply("Unknown unit (#{units}). I know s (seconds), m (minutes), h (hours), and d(days)")
141
+ return
142
+ end
143
+ humanDuration = "#{duration}#{units}"
144
+ else
145
+ expiration = 3600
146
+ humanDuration = "1h"
147
+ end
148
+
149
+ data = {
150
+ :content => {
151
+ :by => response.user.name
152
+ },
153
+ :expire => expiration,
154
+ :path => "silence/#{path}"
155
+ }
156
+
157
+ resp = http.post("#{config.api_url}:#{config.api_port}/stashes", MultiJson.dump(data))
158
+ if resp.status == 201
159
+ response.reply("#{path} silenced for #{humanDuration}")
160
+ else
161
+ log.warn("Sensu returned an internal error posting '#{MultiJson.dump(data)}' to #{config.api_url}:#{config.api_port}/stashes")
162
+ response.reply("An error occurred posting to #{path}")
163
+ end
164
+ end
165
+
166
+ def stashes(response)
167
+ resp = http.get("#{config.api_url}:#{config.api_port}/stashes")
168
+ if resp.status == 200
169
+ stashes = MultiJson.load(resp.body, :symbolize_keys => true).sort{|a,b| a[:name]<=>b[:name]}
170
+ response.reply(render_template('stashes', stashes: stashes))
171
+ else
172
+ log.warn("Sensu returned an internal error resolving #{config.api_url}:#{config.api_port}/stashes")
173
+ response.reply('An error occurred fetching stashes')
174
+ end
175
+ end
176
+
177
+ private
178
+
179
+ def add_domain(client)
180
+ if config.domain && !client.include?(config.domain)
181
+ if config.domain[0,1] == '.'
182
+ return client + config.domain
183
+ else
184
+ return client + '.' + config.domain
185
+ end
186
+ else
187
+ return client
188
+ end
189
+ end
190
+
191
+ end
192
+
193
+ Lita.register_handler(Sensu)
194
+ end
195
+ end
@@ -0,0 +1,10 @@
1
+ class SensuApi
2
+
3
+ attr_reader :http, :config
4
+
5
+ def initalize(http, config)
6
+ @http = http
7
+ @config = config
8
+ end
9
+
10
+ end
@@ -0,0 +1,26 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = "lita-sensu"
3
+ spec.version = "0.1.0"
4
+ spec.authors = ["Justin Lambert"]
5
+ spec.email = ["jlambert@eml.cc"]
6
+ spec.description = "Lita plugin to interact with sensu"
7
+ spec.summary = "Lita plugin to interact with sensu"
8
+ spec.homepage = "https://github.com/jlambert121/lita-sensu"
9
+ spec.license = "Apache-2.0"
10
+ spec.metadata = { "lita_plugin_type" => "handler" }
11
+
12
+ spec.files = `git ls-files`.split($/)
13
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
14
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
15
+ spec.require_paths = ["lib"]
16
+
17
+ spec.add_runtime_dependency "lita", ">= 4.3"
18
+
19
+ spec.add_development_dependency "bundler", "~> 1.3"
20
+ spec.add_development_dependency "pry-byebug"
21
+ spec.add_development_dependency "rake"
22
+ spec.add_development_dependency "rack-test"
23
+ spec.add_development_dependency "rspec", ">= 3.0.0"
24
+ spec.add_development_dependency "simplecov"
25
+ spec.add_development_dependency "coveralls"
26
+ end
data/locales/en.yml ADDED
@@ -0,0 +1,4 @@
1
+ en:
2
+ lita:
3
+ handlers:
4
+ sensu:
@@ -0,0 +1,298 @@
1
+ require "spec_helper"
2
+
3
+ describe Lita::Handlers::Sensu, lita_handler: true do
4
+
5
+ it { is_expected.to route_command('sensu client test1').to(:client) }
6
+ it { is_expected.to route_command('sensu client test1 history').to(:client_history) }
7
+ it { is_expected.to route_command('sensu clients').to(:clients) }
8
+ it { is_expected.to route_command('sensu events').to(:events) }
9
+ it { is_expected.to route_command('sensu events for test1').to(:events) }
10
+ it { is_expected.to route_command('sensu info').to(:info) }
11
+ it { is_expected.to route_command('sensu remove client test1').to(:remove_client) }
12
+ it { is_expected.to route_command('sensu resolve event test1/check2').to(:resolve) }
13
+ it { is_expected.to route_command('sensu silence test1').to(:silence) }
14
+ it { is_expected.to route_command('sensu silence test1 check2').to(:silence) }
15
+ it { is_expected.to route_command('sensu silence test1/check2').to(:silence) }
16
+ it { is_expected.to route_command('sensu stashes').to(:stashes) }
17
+
18
+ let(:response) { double("Faraday::Response") }
19
+
20
+ before do
21
+ registry.config.handlers.sensu.api_url = 'http://sensu.example.com'
22
+ registry.config.handlers.sensu.api_port = 5678
23
+ registry.config.handlers.sensu.domain = 'example.com'
24
+ end
25
+
26
+ describe '#client' do
27
+ client_response = '{"subscriptions":["apache","all"],"name":"test1.example.com","address":"172.31.64.55","bind":"127.0.0.1","safe_mode":false,"keepalive":{},"version":"0.14.0","timestamp":1427859223}'
28
+
29
+ before do
30
+ allow_any_instance_of(Faraday::Connection).to receive(:get).with('http://sensu.example.com:5678/clients/test1.example.com').and_return(response)
31
+ end
32
+
33
+ it 'should fetch client info' do
34
+ allow(response).to receive(:status).and_return(200)
35
+ allow(response).to receive(:body).and_return(client_response)
36
+ send_message('sensu client test1.example.com')
37
+ expect(replies.last).to eq("{\n \"subscriptions\": [\n \"apache\",\n \"all\"\n ],\n \"name\": \"test1.example.com\",\n \"address\": \"172.31.64.55\",\n \"bind\": \"127.0.0.1\",\n \"safe_mode\": false,\n \"keepalive\": {\n },\n \"version\": \"0.14.0\",\n \"timestamp\": 1427859223\n}")
38
+ end
39
+
40
+ it 'should fetch client info appending domain name' do
41
+ allow(response).to receive(:status).and_return(200)
42
+ allow(response).to receive(:body).and_return(client_response)
43
+ send_message('sensu client test1')
44
+ expect(replies.last).to eq("{\n \"subscriptions\": [\n \"apache\",\n \"all\"\n ],\n \"name\": \"test1.example.com\",\n \"address\": \"172.31.64.55\",\n \"bind\": \"127.0.0.1\",\n \"safe_mode\": false,\n \"keepalive\": {\n },\n \"version\": \"0.14.0\",\n \"timestamp\": 1427859223\n}")
45
+ end
46
+
47
+ it 'should handle client not found' do
48
+ allow(response).to receive(:status).and_return(404)
49
+ send_message('sensu client test1')
50
+ expect(replies.last).to eq('test1.example.com was not found')
51
+ end
52
+
53
+ it 'should handle internal sensu errors' do
54
+ allow(response).to receive(:status).and_return(500)
55
+ expect(Lita.logger).to receive(:warn).with(/internal error/)
56
+ send_message('sensu client test1')
57
+ expect(replies.last).to eq('An error occurred fetching client test1.example.com')
58
+ end
59
+ end #client
60
+
61
+ describe '#client_history' do
62
+ history_response = '[{"check":"postfix-running","history":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"last_execution":1427885448,"last_status":0},{"check":"postfix-mailq","history":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"last_execution":1427885448,"last_status":0},{"check":"disk-metrics","history":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"last_execution":1427885481,"last_status":0},{"check":"puppet-last_run","history":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"last_execution":1427884845,"last_status":0},{"check":"keepalive","history":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"last_execution":1427885501,"last_status":0}]'
63
+
64
+ before do
65
+ allow_any_instance_of(Faraday::Connection).to receive(:get).with('http://sensu.example.com:5678/clients/test1.example.com/history').and_return(response)
66
+ end
67
+
68
+ it 'should fetch client info' do
69
+ allow(response).to receive(:status).and_return(200)
70
+ allow(response).to receive(:body).and_return(history_response)
71
+ send_message('sensu client test1.example.com history')
72
+ expect(replies.last).to eq("disk-metrics: status - 0; last checked - 2015-04-01 04:51:21 -0600; history - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\nkeepalive: status - 0; last checked - 2015-04-01 04:51:41 -0600; history - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\npostfix-mailq: status - 0; last checked - 2015-04-01 04:50:48 -0600; history - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\npostfix-running: status - 0; last checked - 2015-04-01 04:50:48 -0600; history - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\npuppet-last_run: status - 0; last checked - 2015-04-01 04:40:45 -0600; history - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\n")
73
+ end
74
+
75
+ it 'should fetch client info appending domain name' do
76
+ allow(response).to receive(:status).and_return(200)
77
+ allow(response).to receive(:body).and_return(history_response)
78
+ send_message('sensu client test1 history')
79
+ expect(replies.last).to eq("disk-metrics: status - 0; last checked - 2015-04-01 04:51:21 -0600; history - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\nkeepalive: status - 0; last checked - 2015-04-01 04:51:41 -0600; history - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\npostfix-mailq: status - 0; last checked - 2015-04-01 04:50:48 -0600; history - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\npostfix-running: status - 0; last checked - 2015-04-01 04:50:48 -0600; history - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\npuppet-last_run: status - 0; last checked - 2015-04-01 04:40:45 -0600; history - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\n")
80
+ end
81
+
82
+ it 'should handle internal sensu errors' do
83
+ allow(response).to receive(:status).and_return(500)
84
+ expect(Lita.logger).to receive(:warn).with(/internal error/)
85
+ send_message('sensu client test1 history')
86
+ expect(replies.last).to eq('An error occurred fetching client test1.example.com history')
87
+ end
88
+ end #client_history
89
+
90
+ describe '#clients' do
91
+ clients_response = '[{"subscriptions":["tomcat","all"],"name":"test2.example.com","safe_mode":false,"address":"172.31.0.2","bind":"127.0.0.1","keepalive":{},"version":"0.14.0","timestamp":1427887539},{"subscriptions":["apache","all"],"keepalive":{},"name":"test1.example.com","safe_mode":false,"address":"172.31.0.1","bind":"127.0.0.1","version":"0.14.0","timestamp":1427887548}]'
92
+
93
+ before do
94
+ allow_any_instance_of(Faraday::Connection).to receive(:get).with('http://sensu.example.com:5678/clients').and_return(response)
95
+ end
96
+
97
+ it 'should list all clients' do
98
+ allow(response).to receive(:status).and_return(200)
99
+ allow(response).to receive(:body).and_return(clients_response)
100
+ send_message('sensu clients')
101
+ expect(replies.last).to eq("test1.example.com (172.31.0.1) subscriptions: all, apache\ntest2.example.com (172.31.0.2) subscriptions: all, tomcat\n")
102
+ end
103
+
104
+ it 'should handle internal sensu errors' do
105
+ allow(response).to receive(:status).and_return(500)
106
+ expect(Lita.logger).to receive(:warn).with(/internal error/)
107
+ send_message('sensu clients')
108
+ expect(replies.last).to eq('An error occurred fetching clients')
109
+ end
110
+ end #clients
111
+
112
+ describe '#events' do
113
+ events_response = '[{"id":"a622267d-88bf-4eea-9455-ec8f001ca916","client":{"bind":"127.0.0.1","safe_mode":false,"name":"test1.example.com","subscriptions":["provisioner","all","apache"],"keepalive":{},"address":"172.31.0.1","version":"0.14.0","timestamp":1416007406},"check":{"command":"/etc/sensu/plugins/check-procs.rb -p /usr/sbin/pdns_server -w 2 -c 2 -W 2 -C 1","subscribers":[""],"handlers":["default"],"standalone":true,"interval":60,"name":"pdns-server-running","issued":1416007420,"executed":1416007420,"duration":2.107,"output":"CheckProcs CRITICAL: Found 0 matching processes; cmd //usr/sbin/pdns_server/\\n","status":2,"history":["0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","2","2","2"]},"occurrences":3,"action":"create"},{"id":"4b107db0-f7be-4dc3-a5ba-76a5b9dba4dd","client":{"bind":"127.0.0.1","safe_mode":false,"name":"test2.example.com","subscriptions":["provisioner","all","apache"],"keepalive":{},"address":"172.31.0.2","version":"0.14.0","timestamp":1416007406},"check":{"subscribers":[""],"handlers":["default"],"command":"/etc/sensu/plugins/check-procs.rb -p /usr/sbin/pdns_puppetdb -w 25 -c 30 -C 1","interval":600,"standalone":true,"name":"pdns_puppetdb_running","issued":1416007418,"executed":1416007418,"duration":1.507,"output":"CheckProcs CRITICAL: Found 0 matching processes; cmd //usr/sbin/pdns_puppetdb/\\n","status":2,"history":["0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","2"]},"occurrences":1,"action":"create"}]'
114
+ test1_events_response = '[{"id":"a622267d-88bf-4eea-9455-ec8f001ca916","client":{"bind":"127.0.0.1","safe_mode":false,"name":"test1.example.com","subscriptions":["provisioner","all","apache"],"keepalive":{},"address":"172.31.0.1","version":"0.14.0","timestamp":1416007406},"check":{"command":"/etc/sensu/plugins/check-procs.rb -p /usr/sbin/pdns_server -w 2 -c 2 -W 2 -C 1","subscribers":[""],"handlers":["default"],"standalone":true,"interval":60,"name":"pdns-server-running","issued":1416007420,"executed":1416007420,"duration":2.107,"output":"CheckProcs CRITICAL: Found 0 matching processes; cmd //usr/sbin/pdns_server/\\n","status":2,"history":["0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","2","2","2"]},"occurrences":3,"action":"create"}]'
115
+
116
+ before do
117
+ allow_any_instance_of(Faraday::Connection).to receive(:get).with('http://sensu.example.com:5678/events').and_return(response)
118
+ allow_any_instance_of(Faraday::Connection).to receive(:get).with('http://sensu.example.com:5678/events/test1.example.com').and_return(response)
119
+ end
120
+
121
+ it 'should list all events' do
122
+ allow(response).to receive(:status).and_return(200)
123
+ allow(response).to receive(:body).and_return(events_response)
124
+ send_message('sensu events')
125
+ expect(replies.last).to eq("test1.example.com (pdns-server-running) - CheckProcs CRITICAL: Found 0 matching processes; cmd //usr/sbin/pdns_server/\ntest2.example.com (pdns_puppetdb_running) - CheckProcs CRITICAL: Found 0 matching processes; cmd //usr/sbin/pdns_puppetdb/\n")
126
+ end
127
+
128
+ it 'should list all events for a specific client' do
129
+ allow(response).to receive(:status).and_return(200)
130
+ allow(response).to receive(:body).and_return(test1_events_response)
131
+ send_message('sensu events for test1')
132
+ expect(replies.last).to eq("test1.example.com (pdns-server-running) - CheckProcs CRITICAL: Found 0 matching processes; cmd //usr/sbin/pdns_server/\n")
133
+ end
134
+
135
+ it 'should handle internal sensu errors' do
136
+ allow(response).to receive(:status).and_return(500)
137
+ expect(Lita.logger).to receive(:warn).with(/internal error/)
138
+ send_message('sensu events for test1')
139
+ expect(replies.last).to eq('An error occurred fetching clients')
140
+ end
141
+ end #events
142
+
143
+ describe '#info' do
144
+ info_response = '{"sensu":{"version":"0.14.0"},"transport":{"keepalives":{"messages":0,"consumers":1},"results":{"messages":0,"consumers":1},"connected":true},"redis":{"connected":true}}'
145
+
146
+ before do
147
+ allow_any_instance_of(Faraday::Connection).to receive(:get).with('http://sensu.example.com:5678/info').and_return(response)
148
+ end
149
+
150
+ it 'should return api info' do
151
+ allow(response).to receive(:status).and_return(200)
152
+ allow(response).to receive(:body).and_return(info_response)
153
+ send_message('sensu info')
154
+ expect(replies.last).to eq("{\n \"sensu\": {\n \"version\": \"0.14.0\"\n },\n \"transport\": {\n \"keepalives\": {\n \"messages\": 0,\n \"consumers\": 1\n },\n \"results\": {\n \"messages\": 0,\n \"consumers\": 1\n },\n \"connected\": true\n },\n \"redis\": {\n \"connected\": true\n }\n}")
155
+ end
156
+ end #info
157
+
158
+ describe '#remove_client' do
159
+ remove_response = '[{"subscriptions":["tomcat","all"],"name":"test2.example.com","safe_mode":false,"address":"172.31.0.2","bind":"127.0.0.1","keepalive":{},"version":"0.14.0","timestamp":1427887539},{"subscriptions":["apache","all"],"keepalive":{},"name":"test1.example.com","safe_mode":false,"address":"172.31.0.1","bind":"127.0.0.1","version":"0.14.0","timestamp":1427887548}]'
160
+
161
+ before do
162
+ allow_any_instance_of(Faraday::Connection).to receive(:delete).with('http://sensu.example.com:5678/clients/test1.example.com').and_return(response)
163
+ end
164
+
165
+ it 'should remove a client' do
166
+ allow(response).to receive(:status).and_return(202)
167
+ allow(response).to receive(:body).and_return(remove_response)
168
+ send_message('sensu remove client test1')
169
+ expect(replies.last).to eq("test1.example.com removed")
170
+ end
171
+
172
+ it 'should handle 404' do
173
+ allow(response).to receive(:status).and_return(404)
174
+ send_message('sensu remove client test1')
175
+ expect(replies.last).to eq("test1.example.com was not found")
176
+ end
177
+
178
+ it 'should handle internal sensu errors' do
179
+ allow(response).to receive(:status).and_return(500)
180
+ expect(Lita.logger).to receive(:warn).with(/internal error/)
181
+ send_message('sensu remove client test1')
182
+ expect(replies.last).to eq('An error occurred removing test1.example.com')
183
+ end
184
+ end #remove_client
185
+
186
+ describe '#resolve' do
187
+ before do
188
+ allow_any_instance_of(Faraday::Connection).to receive(:post).with('http://sensu.example.com:5678/resolve', '{"client":"test1.example.com","check":"pdns-server-running"}').and_return(response)
189
+ end
190
+
191
+ it 'should resolve an event' do
192
+ allow(response).to receive(:status).and_return(202)
193
+ send_message('sensu resolve event test1/pdns-server-running')
194
+ expect(replies.last).to eq("test1.example.com/pdns-server-running resolved")
195
+ end
196
+
197
+ it 'should handle malformed messages' do
198
+ allow(response).to receive(:status).and_return(400)
199
+ send_message('sensu resolve event test1/pdns-server-running')
200
+ expect(replies.last).to eq("Resolve message was malformed: {\"client\":\"test1.example.com\",\"check\":\"pdns-server-running\"}")
201
+ end
202
+
203
+ it 'should handle 404' do
204
+ allow(response).to receive(:status).and_return(404)
205
+ send_message('sensu resolve event test1/pdns-server-running')
206
+ expect(replies.last).to eq("test1.example.com/pdns-server-running was not found")
207
+ end
208
+
209
+ it 'should handle internal sensu errors' do
210
+ allow(response).to receive(:status).and_return(500)
211
+ expect(Lita.logger).to receive(:warn).with(/internal error/)
212
+ send_message('sensu resolve event test1/pdns-server-running')
213
+ expect(replies.last).to eq('There was an error resolving test1.example.com/pdns-server-running')
214
+ end
215
+ end #resolve
216
+
217
+ describe '#silence' do
218
+ it 'should silence an event on a specific client' do
219
+ allow_any_instance_of(Faraday::Connection).to receive(:post).with('http://sensu.example.com:5678/stashes', '{"content":{"by":"Test User"},"expire":3600,"path":"silence/test1.example.com/disk-free"}').and_return(response)
220
+ allow(response).to receive(:status).and_return(201)
221
+ send_message('sensu silence test1/disk-free')
222
+ expect(replies.last).to eq("test1.example.com/disk-free silenced for 1h")
223
+ end
224
+
225
+ it 'should silence for seconds' do
226
+ allow_any_instance_of(Faraday::Connection).to receive(:post).with('http://sensu.example.com:5678/stashes', '{"content":{"by":"Test User"},"expire":1,"path":"silence/test1.example.com/disk-free"}').and_return(response)
227
+ allow(response).to receive(:status).and_return(201)
228
+ send_message('sensu silence test1/disk-free for 1s')
229
+ expect(replies.last).to eq("test1.example.com/disk-free silenced for 1s")
230
+ end
231
+
232
+ it 'should silence a client' do
233
+ allow_any_instance_of(Faraday::Connection).to receive(:post).with('http://sensu.example.com:5678/stashes', '{"content":{"by":"Test User"},"expire":3600,"path":"silence/test1.example.com"}').and_return(response)
234
+ allow(response).to receive(:status).and_return(201)
235
+ send_message('sensu silence test1')
236
+ expect(replies.last).to eq("test1.example.com silenced for 1h")
237
+ end
238
+
239
+ it 'should silence for minutes' do
240
+ allow_any_instance_of(Faraday::Connection).to receive(:post).with('http://sensu.example.com:5678/stashes', '{"content":{"by":"Test User"},"expire":60,"path":"silence/test1.example.com"}').and_return(response)
241
+ allow(response).to receive(:status).and_return(201)
242
+ send_message('sensu silence test1 for 1m')
243
+ expect(replies.last).to eq("test1.example.com silenced for 1m")
244
+ end
245
+
246
+ it 'should silence for hours' do
247
+ allow_any_instance_of(Faraday::Connection).to receive(:post).with('http://sensu.example.com:5678/stashes', '{"content":{"by":"Test User"},"expire":3600,"path":"silence/test1.example.com"}').and_return(response)
248
+ allow(response).to receive(:status).and_return(201)
249
+ send_message('sensu silence test1 for 1h')
250
+ expect(replies.last).to eq("test1.example.com silenced for 1h")
251
+ end
252
+
253
+ it 'should silence for days' do
254
+ allow_any_instance_of(Faraday::Connection).to receive(:post).with('http://sensu.example.com:5678/stashes', '{"content":{"by":"Test User"},"expire":86400,"path":"silence/test1.example.com"}').and_return(response)
255
+ allow(response).to receive(:status).and_return(201)
256
+ send_message('sensu silence test1 for 1d')
257
+ expect(replies.last).to eq("test1.example.com silenced for 1d")
258
+ end
259
+
260
+ it 'should provide feedback for invalid duration' do
261
+ allow_any_instance_of(Faraday::Connection).to receive(:post).with('http://sensu.example.com:5678/stashes', '{"content":{"by":"Test User"},"expire":3600,"path":"silence/test1.example.com"}').and_return(response)
262
+ allow(response).to receive(:status).and_return(201)
263
+ send_message('sensu silence test1 for 1z')
264
+ expect(replies.last).to eq("Unknown unit (z). I know s (seconds), m (minutes), h (hours), and d(days)")
265
+ end
266
+
267
+ it 'should handle internal sensu errors' do
268
+ allow_any_instance_of(Faraday::Connection).to receive(:post).with('http://sensu.example.com:5678/stashes', '{"content":{"by":"Test User"},"expire":3600,"path":"silence/test1.example.com/disk-free"}').and_return(response)
269
+ allow(response).to receive(:status).and_return(500)
270
+ expect(Lita.logger).to receive(:warn).with(/internal error/)
271
+ send_message('sensu silence test1/disk-free')
272
+ expect(replies.last).to eq('An error occurred posting to test1.example.com/disk-free')
273
+ end
274
+ end #silence
275
+
276
+ describe '#stashes' do
277
+ stashes_response = '[{"path":"silence/test1.example.com/disk-free","content":{"timestamp":1383441836},"expire":3600}]'
278
+
279
+ before do
280
+ allow_any_instance_of(Faraday::Connection).to receive(:get).with('http://sensu.example.com:5678/stashes').and_return(response)
281
+ end
282
+
283
+ it 'should list all clients' do
284
+ allow(response).to receive(:status).and_return(200)
285
+ allow(response).to receive(:body).and_return(stashes_response)
286
+ send_message('sensu stashes')
287
+ expect(replies.last).to eq("silence/test1.example.com/disk-free added on 2013-11-02 19:23:56 -0600 expires in 3600 seconds\n")
288
+ end
289
+
290
+ it 'should handle internal sensu errors' do
291
+ allow(response).to receive(:status).and_return(500)
292
+ expect(Lita.logger).to receive(:warn).with(/internal error/)
293
+ send_message('sensu stashes')
294
+ expect(replies.last).to eq('An error occurred fetching stashes')
295
+ end
296
+ end #stashes
297
+
298
+ end
@@ -0,0 +1,17 @@
1
+ require "simplecov"
2
+ require "coveralls"
3
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
4
+ SimpleCov::Formatter::HTMLFormatter,
5
+ Coveralls::SimpleCov::Formatter
6
+ ]
7
+ SimpleCov.start {
8
+ add_filter "/spec/"
9
+ add_filter "/.bundle/"
10
+ }
11
+
12
+ require "lita-sensu"
13
+ require "lita/rspec"
14
+
15
+ # A compatibility mode is provided for older plugins upgrading from Lita 3. Since this plugin
16
+ # was generated with Lita 4, the compatibility mode should be left disabled.
17
+ Lita.version_3_compatibility_mode = false
File without changes
@@ -0,0 +1,4 @@
1
+ <% @history.each do |check| %>
2
+ <%= check[:check] %>: status - <%= check[:last_status] %>; last checked - <%= Time.at(check[:last_execution]) %>; history - <%= check[:history].join(',') %>
3
+
4
+ <% end %>
@@ -0,0 +1,16 @@
1
+ <table>
2
+ <tr>
3
+ <th>Check</th>
4
+ <th>Status</th>
5
+ <th>Last Checked</th>
6
+ <th>History</th>
7
+ </tr>
8
+ <% @history.each do |check| %>
9
+ <tr>
10
+ <td><%= check[:check] %></td>
11
+ <td><%= check[:last_status] %></td>
12
+ <td><%= Time.at(check[:last_execution]) %></td>
13
+ <td><%= check[:history].join(',') %></td>
14
+ </tr>
15
+ <% end %>
16
+ </table>
@@ -0,0 +1,4 @@
1
+ <% @clients.each do |client| %>
2
+ <%= client[:name] %> (<%= client[:address] %>) subscriptions: <%= client[:subscriptions].sort().join(', ') %>
3
+
4
+ <% end %>
@@ -0,0 +1,13 @@
1
+ <table>
2
+ <tr>
3
+ <th>Name</th><th>IP</th><th>Subscriptions</th><th>Version
4
+ </tr>
5
+ <% @clients.each do |client| %>
6
+ <tr>
7
+ <td><%= client[:name] %></td>
8
+ <td><%= client[:address] %></td>
9
+ <td><%= client[:subscriptions].sort().join(', ') %></td>
10
+ <td><%= client[:version]%></td>
11
+ </tr>
12
+ <% end %>
13
+ </table>
@@ -0,0 +1,3 @@
1
+ <% @events.each do |event| %>
2
+ <%= event[:client][:name] %> (<%= event[:check][:name] %>) - <%= event[:check][:output] %>
3
+ <% end %>
@@ -0,0 +1,14 @@
1
+ <table>
2
+ <tr>
3
+ <th>Client</th>
4
+ <th>Check</th>
5
+ <th>Occurrences</th>
6
+ <th>Output</th>
7
+ </tr>
8
+ <% @events.each do |event| %>
9
+ <tr>
10
+ <td><%= event[:client][:name] %></td>
11
+ <td><%= event[:check][:name] %></td>
12
+ <td><%= event[:occurrences] %></td>
13
+ <td><%= event[:check][:output] %></td>
14
+ <% end %>
@@ -0,0 +1,9 @@
1
+ <% @stashes.each do |stash| %>
2
+ <%= stash[:path] %> added on <%= Time.at(stash[:content][:timestamp]) %>
3
+ <% if stash[:content][:by] %>
4
+ by <%= stash[:content][:by] %>
5
+ <% end %>
6
+ <% if stash[:expire] %>
7
+ expires in <%= stash[:expire] %> seconds
8
+ <% end %>
9
+ <% end %>
@@ -0,0 +1,12 @@
1
+ <table>
2
+ <tr>
3
+ <th>Path</th><th>Added On</th><th>By</th><th>Seconds to expiration</th>
4
+ </tr>
5
+ <% @stashes.each do |stash| %>
6
+ <tr>
7
+ <td><%= stash[:path] %></td>
8
+ <td><%= Time.at(stash[:content][:timestamp]) %></td>
9
+ <td><%= stash[:content][:by] || ''%> </td>
10
+ <td><%= stash[:expire] || '' %></td>
11
+ </tr>
12
+ <% end %>
metadata ADDED
@@ -0,0 +1,181 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lita-sensu
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Justin Lambert
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-04-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: lita
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '4.3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '4.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry-byebug
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rack-test
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: 3.0.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: 3.0.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: simplecov
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: coveralls
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description: Lita plugin to interact with sensu
126
+ email:
127
+ - jlambert@eml.cc
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - .gitignore
133
+ - .travis.yml
134
+ - Gemfile
135
+ - LICENSE
136
+ - README.md
137
+ - Rakefile
138
+ - lib/lita-sensu.rb
139
+ - lib/lita/handlers/sensu.rb
140
+ - lib/lita/handlers/sensu_api.rb
141
+ - lita-sensu.gemspec
142
+ - locales/en.yml
143
+ - spec/lita/handlers/sensu_spec.rb
144
+ - spec/spec_helper.rb
145
+ - templates/.gitkeep
146
+ - templates/client_history.erb
147
+ - templates/client_history.hipchat.erb
148
+ - templates/clients.erb
149
+ - templates/clients.hipchat.erb
150
+ - templates/events.erb
151
+ - templates/events.hipchat.erb
152
+ - templates/stashes.erb
153
+ - templates/stashes.hipchat.erb
154
+ homepage: https://github.com/jlambert121/lita-sensu
155
+ licenses:
156
+ - Apache-2.0
157
+ metadata:
158
+ lita_plugin_type: handler
159
+ post_install_message:
160
+ rdoc_options: []
161
+ require_paths:
162
+ - lib
163
+ required_ruby_version: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - '>='
166
+ - !ruby/object:Gem::Version
167
+ version: '0'
168
+ required_rubygems_version: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - '>='
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ requirements: []
174
+ rubyforge_project:
175
+ rubygems_version: 2.0.14
176
+ signing_key:
177
+ specification_version: 4
178
+ summary: Lita plugin to interact with sensu
179
+ test_files:
180
+ - spec/lita/handlers/sensu_spec.rb
181
+ - spec/spec_helper.rb