dogwatch 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.rubocop.yml +24 -0
- data/.rubocop_todo.yml +25 -0
- data/.ruby-version +1 -0
- data/Gemfile +14 -0
- data/LICENSE +23 -0
- data/README.md +66 -0
- data/Rakefile +12 -0
- data/Thorfile +5 -0
- data/bin/dogwatch +28 -0
- data/dogwatch.gemspec +23 -0
- data/example/Dogfile +23 -0
- data/example/credentials.example +2 -0
- data/lib/dogwatch.rb +13 -0
- data/lib/dogwatch/dogfile.rb +27 -0
- data/lib/dogwatch/model/client.rb +92 -0
- data/lib/dogwatch/model/config.rb +36 -0
- data/lib/dogwatch/model/mixin/colorize.rb +23 -0
- data/lib/dogwatch/model/monitor.rb +68 -0
- data/lib/dogwatch/model/options.rb +60 -0
- data/lib/dogwatch/model/response.rb +62 -0
- data/lib/dogwatch/monitor.rb +51 -0
- data/lib/dogwatch/version.rb +6 -0
- data/test/data/monitors.json +58 -0
- data/test/data/responses.rb +22 -0
- data/test/dogwatch/test_client.rb +66 -0
- data/test/dogwatch/test_colorize.rb +21 -0
- data/test/dogwatch/test_config.rb +31 -0
- data/test/dogwatch/test_dogwatch.rb +15 -0
- data/test/dogwatch/test_monitor_model.rb +53 -0
- data/test/dogwatch/test_options.rb +81 -0
- data/test/dogwatch/test_response.rb +32 -0
- data/test/test_helper.rb +19 -0
- metadata +182 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
module DogWatch
|
2
|
+
module Model
|
3
|
+
module Mixin
|
4
|
+
##
|
5
|
+
# Provides a colorize() mixin that handles shell output coloring
|
6
|
+
##
|
7
|
+
module Colorize
|
8
|
+
def colorize(param, options = {})
|
9
|
+
define_method(:color) do
|
10
|
+
case instance_variable_get("@#{ param }")
|
11
|
+
when *options.fetch(:white, [:status]) then :white
|
12
|
+
when *options.fetch(:cyan, [:debug, :trace]) then :cyan
|
13
|
+
when *options.fetch(:green, [:info, :success, :create]) then :green
|
14
|
+
when *options.fetch(:yellow, [:warn, :update]) then :yellow
|
15
|
+
when *options.fetch(:red, [:error, :fail, :delete]) then :red
|
16
|
+
else options.fetch(:default, :green)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require_relative 'options'
|
3
|
+
|
4
|
+
module DogWatch
|
5
|
+
module Model
|
6
|
+
##
|
7
|
+
# Handles monitor DSL methods and object validation
|
8
|
+
##
|
9
|
+
class Monitor
|
10
|
+
TYPE_MAP = {
|
11
|
+
metric_alert: 'metric alert',
|
12
|
+
service_check: 'service check',
|
13
|
+
event_alert: 'event alert'
|
14
|
+
}
|
15
|
+
|
16
|
+
attr_reader :name
|
17
|
+
attr_reader :attributes
|
18
|
+
|
19
|
+
# @param [String] name
|
20
|
+
# @return [String]
|
21
|
+
def initialize(name)
|
22
|
+
@attributes = OpenStruct.new
|
23
|
+
@name = name
|
24
|
+
end
|
25
|
+
|
26
|
+
# @param [Symbol] type
|
27
|
+
# @return [String]
|
28
|
+
def type(type)
|
29
|
+
@attributes.type = TYPE_MAP[type]
|
30
|
+
end
|
31
|
+
|
32
|
+
# @param [String] query
|
33
|
+
# @return [String]
|
34
|
+
def query(query)
|
35
|
+
@attributes.query = query.to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
# @param [String] message
|
39
|
+
# @return [String]
|
40
|
+
def message(message)
|
41
|
+
@attributes.message = message.to_s
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param [Array] tags
|
45
|
+
# @return [Array]
|
46
|
+
def tags(tags)
|
47
|
+
@attributes.tags = tags.to_a
|
48
|
+
end
|
49
|
+
|
50
|
+
# @param [Proc] block
|
51
|
+
# @return [Hash]
|
52
|
+
def options(&block)
|
53
|
+
opts = DogWatch::Model::Options.new
|
54
|
+
opts.instance_eval(&block)
|
55
|
+
@attributes.options = opts.render
|
56
|
+
end
|
57
|
+
|
58
|
+
# @return [TrueClass|FalseClass]
|
59
|
+
def validate
|
60
|
+
return false unless TYPE_MAP.value?(@attributes.type)
|
61
|
+
return true unless @attributes.type.nil? || @attributes.type.empty? ||
|
62
|
+
@attributes.query.nil? || @attributes.query.empty?
|
63
|
+
|
64
|
+
false
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module DogWatch
|
4
|
+
module Model
|
5
|
+
##
|
6
|
+
# Handles the options block methods
|
7
|
+
##
|
8
|
+
class Options
|
9
|
+
attr_reader :attributes
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@attributes = OpenStruct.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def render
|
16
|
+
@attributes.each_pair.to_h
|
17
|
+
end
|
18
|
+
|
19
|
+
# @param [Hash] args
|
20
|
+
def silenced(args)
|
21
|
+
@attributes.silenced = args.to_h
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param [Boolean] notify
|
25
|
+
def notify_no_data(notify = false)
|
26
|
+
@attributes.notify_no_data = !!notify
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param [Integer] minutes
|
30
|
+
def no_data_timeframe(minutes)
|
31
|
+
@attributes.no_data_timeframe = minutes.to_i
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param [Integer] hours
|
35
|
+
def timeout_h(hours)
|
36
|
+
@attributes.timeout_h = hours.to_i
|
37
|
+
end
|
38
|
+
|
39
|
+
# @param [Integer] minutes
|
40
|
+
def renotify_interval(minutes)
|
41
|
+
@attributes.renotify_interval = minutes.to_i
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param [String] message
|
45
|
+
def escalation_message(message)
|
46
|
+
@attributes.escalation_message = message.to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
# @param [Boolean] notify
|
50
|
+
def notify_audit(notify = false)
|
51
|
+
@attributes.notify_audit = !!notify
|
52
|
+
end
|
53
|
+
|
54
|
+
# @param [Boolean] include
|
55
|
+
def include_tags(include = true)
|
56
|
+
@attributes.include_tags = !!include
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require_relative 'mixin/colorize'
|
2
|
+
|
3
|
+
module DogWatch
|
4
|
+
module Model
|
5
|
+
##
|
6
|
+
# Takes DataDog client responses and formats them nicely
|
7
|
+
##
|
8
|
+
class Response
|
9
|
+
extend Mixin::Colorize
|
10
|
+
|
11
|
+
ERROR = '400'
|
12
|
+
CREATED = '200'
|
13
|
+
ACCEPTED = '202'
|
14
|
+
colorize(:action,
|
15
|
+
:green => [:created, :accepted, :updated],
|
16
|
+
:yellow => [],
|
17
|
+
:red => [:error]
|
18
|
+
)
|
19
|
+
|
20
|
+
attr_accessor :response
|
21
|
+
|
22
|
+
def initialize(response, updated = false)
|
23
|
+
@response = response
|
24
|
+
@updated = updated
|
25
|
+
end
|
26
|
+
|
27
|
+
def status
|
28
|
+
return :updated if @updated == true
|
29
|
+
return :created if created?
|
30
|
+
return :error if failed?
|
31
|
+
return :accepted if accepted?
|
32
|
+
end
|
33
|
+
|
34
|
+
def message
|
35
|
+
attrs = @response[1]
|
36
|
+
return attrs['errors'] if attrs.key?('errors')
|
37
|
+
"#{status.to_s.capitalize} monitor #{attrs['name']}"\
|
38
|
+
" with message #{attrs['message']}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_thor
|
42
|
+
action = status
|
43
|
+
text = message
|
44
|
+
[action, text, color]
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def accepted?
|
50
|
+
@response[0] == ACCEPTED ? true : false
|
51
|
+
end
|
52
|
+
|
53
|
+
def created?
|
54
|
+
@response[0] == CREATED ? true : false
|
55
|
+
end
|
56
|
+
|
57
|
+
def failed?
|
58
|
+
@response[0] == ERROR ? true : false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require_relative 'model/client'
|
2
|
+
require_relative 'model/monitor'
|
3
|
+
|
4
|
+
module DogWatch
|
5
|
+
##
|
6
|
+
# Provides a container around each monitor block
|
7
|
+
##
|
8
|
+
class Monitor
|
9
|
+
attr_reader :client
|
10
|
+
attr_reader :responses
|
11
|
+
attr_accessor :config
|
12
|
+
|
13
|
+
# @param [String] name
|
14
|
+
# @param [Proc] block
|
15
|
+
# @return [DogWatch::Model::Monitor]
|
16
|
+
def initialize(name = nil, &block)
|
17
|
+
@name = name
|
18
|
+
@monitors = []
|
19
|
+
@config = nil
|
20
|
+
instance_exec(&block)
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param [String] name
|
24
|
+
# @param [Proc] block
|
25
|
+
# @return [Array]
|
26
|
+
def monitor(name, &block)
|
27
|
+
monitor = DogWatch::Model::Monitor.new(name)
|
28
|
+
monitor.instance_eval(&block)
|
29
|
+
@monitors << monitor
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [Array]
|
33
|
+
def get
|
34
|
+
@responses = @monitors.map do |m|
|
35
|
+
if m.validate
|
36
|
+
@client.execute(m)
|
37
|
+
else
|
38
|
+
# Need somewhere to inject local errors such as if the request
|
39
|
+
# was never sent because the type or query wasn't supplied.
|
40
|
+
res = ['400', { 'errors' => ['The DogWatch monitor is invalid.'] }]
|
41
|
+
DogWatch::Model::Response.new(res)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [DogWatch::Model::Client]
|
47
|
+
def client(client = nil)
|
48
|
+
@client = client.nil? ? DogWatch::Model::Client.new(@config) : client
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
[
|
2
|
+
"200",
|
3
|
+
[
|
4
|
+
{
|
5
|
+
"created_at": 1440168271000,
|
6
|
+
"creator": {
|
7
|
+
"email": "sperson@rapid7.com",
|
8
|
+
"handle": "sperson@rapid7.com",
|
9
|
+
"id": 10000,
|
10
|
+
"name": "Some Person"
|
11
|
+
},
|
12
|
+
"id": 123456,
|
13
|
+
"message": "Some message",
|
14
|
+
"multi": false,
|
15
|
+
"name": "Monitor name",
|
16
|
+
"options": {
|
17
|
+
"no_data_timeframe": 2,
|
18
|
+
"notify_audit": false,
|
19
|
+
"notify_no_data": false,
|
20
|
+
"renotify_interval": 0,
|
21
|
+
"silenced": {},
|
22
|
+
"timeout_h": 0
|
23
|
+
},
|
24
|
+
"org_id": 12345,
|
25
|
+
"overall_state": "OK",
|
26
|
+
"query": "Some query",
|
27
|
+
"tags": [],
|
28
|
+
"type": "event alert"
|
29
|
+
},
|
30
|
+
{
|
31
|
+
"created_at": 1436300628000,
|
32
|
+
"creator": {
|
33
|
+
"email": "sperson@rapid7.com",
|
34
|
+
"handle": "sperson@rapid7.com",
|
35
|
+
"id": 10000,
|
36
|
+
"name": "Some Person"
|
37
|
+
},
|
38
|
+
"id": 234567,
|
39
|
+
"message": "Some message",
|
40
|
+
"multi": false,
|
41
|
+
"name": "Second monitor name",
|
42
|
+
"options": {
|
43
|
+
"escalation_message": "Oh noes!",
|
44
|
+
"no_data_timeframe": 2,
|
45
|
+
"notify_audit": true,
|
46
|
+
"notify_no_data": true,
|
47
|
+
"renotify_interval": 10,
|
48
|
+
"silenced": {},
|
49
|
+
"timeout_h": 1
|
50
|
+
},
|
51
|
+
"org_id": 12345,
|
52
|
+
"overall_state": "OK",
|
53
|
+
"query": "avg(last_1m):avg:system.cpu.user{region:us-east-1} > 20",
|
54
|
+
"tags": [],
|
55
|
+
"type": "metric alert"
|
56
|
+
}
|
57
|
+
]
|
58
|
+
]
|
@@ -0,0 +1,22 @@
|
|
1
|
+
ERROR_RES = [
|
2
|
+
'400',
|
3
|
+
{ 'errors' => ["The value provided for parameter 'query' is invalid"] }
|
4
|
+
]
|
5
|
+
VALID_RES = [
|
6
|
+
'200',
|
7
|
+
{
|
8
|
+
'name' => 'foobar',
|
9
|
+
'query' => 'some query',
|
10
|
+
'message' => 'foobar',
|
11
|
+
'id' => 1_234_567
|
12
|
+
}
|
13
|
+
]
|
14
|
+
ACCEPTED_RES = [
|
15
|
+
'202',
|
16
|
+
{
|
17
|
+
'name' => 'foobar',
|
18
|
+
'query' => 'some query',
|
19
|
+
'message' => 'foobar',
|
20
|
+
'id' => 1_234_567
|
21
|
+
}
|
22
|
+
]
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
require_relative '../../lib/dogwatch/model/client'
|
3
|
+
require_relative '../../lib/dogwatch/model/config'
|
4
|
+
require_relative '../../lib/dogwatch/model/monitor'
|
5
|
+
require 'dogapi'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
class TestClient < Minitest::Test
|
9
|
+
TEST_RESPONSE = [
|
10
|
+
'200',
|
11
|
+
[
|
12
|
+
name: 'test monitor',
|
13
|
+
type: 'metric alert',
|
14
|
+
query: 'test query'
|
15
|
+
]
|
16
|
+
]
|
17
|
+
|
18
|
+
UPDATED_RESPONSE = [
|
19
|
+
'200',
|
20
|
+
[
|
21
|
+
name: 'Monitor name',
|
22
|
+
type: :metric_alert,
|
23
|
+
query: 'scheduled maintenance query'
|
24
|
+
]
|
25
|
+
]
|
26
|
+
|
27
|
+
def setup
|
28
|
+
config = DogWatch::Model::Config.new('foo', 'bar')
|
29
|
+
|
30
|
+
m = monitors
|
31
|
+
k = Class.new(DogWatch::Model::Client) do
|
32
|
+
define_method(:all_monitors) do
|
33
|
+
m[1]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
@client = k.new(config)
|
37
|
+
end
|
38
|
+
|
39
|
+
def monitors
|
40
|
+
monitor_file = File.expand_path('../../data/monitors.json', __FILE__)
|
41
|
+
monitors = JSON.parse(IO.read(monitor_file))
|
42
|
+
monitors
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_create_monitor
|
46
|
+
new_monitor = DogWatch::Model::Monitor.new('test monitor')
|
47
|
+
new_monitor.type(:metric_alert)
|
48
|
+
new_monitor.query('test query')
|
49
|
+
|
50
|
+
@client.client.stub :monitor, TEST_RESPONSE do
|
51
|
+
@client.execute(new_monitor)
|
52
|
+
assert_equal @client.response.status, :created
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_update_monitor
|
57
|
+
update_monitor = DogWatch::Model::Monitor.new('Monitor name')
|
58
|
+
update_monitor.type(:metric_alert)
|
59
|
+
update_monitor.query('scheduled maintenance query')
|
60
|
+
|
61
|
+
@client.client.stub :update_monitor, UPDATED_RESPONSE do
|
62
|
+
@client.execute(update_monitor)
|
63
|
+
assert_equal @client.response.status, :updated
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative '../test_helper'
|
2
|
+
require_relative '../../lib/dogwatch/model/mixin/colorize'
|
3
|
+
|
4
|
+
class ToColor
|
5
|
+
extend DogWatch::Model::Mixin::Colorize
|
6
|
+
|
7
|
+
colorize(:action,
|
8
|
+
:green => [:created, :accepted, :updated],
|
9
|
+
:yellow => [],
|
10
|
+
:red => [:error])
|
11
|
+
end
|
12
|
+
|
13
|
+
class TestColorize < Minitest::Test
|
14
|
+
def setup
|
15
|
+
@tocolor = ToColor.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_colorize_default
|
19
|
+
assert_equal :green, @tocolor.color
|
20
|
+
end
|
21
|
+
end
|