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.
@@ -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,6 @@
1
+ # nodoc
2
+ module DogWatch
3
+ VERSION = IO.read(File.expand_path('../../../VERSION', __FILE__)) rescue '0.0.1'
4
+ SUMMARY = 'A DSL to create DataDog Monitors'
5
+ DESCRIPTION = IO.read(File.expand_path('../../../README.md', __FILE__)) rescue ''
6
+ 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