dogwatch 1.0.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 +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
|