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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9d79c2c09e6613e01aebac074584db2d77fb0cec
4
+ data.tar.gz: a45e5c711c68224dae11e425b249e60ba2e56862
5
+ SHA512:
6
+ metadata.gz: e61f782beb109454fc226473554a4da0c00b0e809c598228215be23bd00f7c3d85e3623dea6bb27a17e917c5772c72b4036a42c995da1db51badcf8f23ce2a35
7
+ data.tar.gz: e6ed10925ee458aec58a752fcae1680e113cc628e5914d5c1c087b0881012a2a403da9f7eb0c45de430d4bafa9bcb8f59857ade2f296b2297a0b9600e5b0b8fc
@@ -0,0 +1,19 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ .DS_Store
15
+ mkmf.log
16
+
17
+ keys
18
+ VERSION
19
+ attic
@@ -0,0 +1,24 @@
1
+ inherit_from: .rubocop_todo.yml
2
+
3
+ AllCops:
4
+ Include:
5
+ - lib/**/*
6
+ - bin/**/*
7
+ - test/**/*
8
+ - Gemfile
9
+ - Rakefile
10
+ - Thorfile
11
+ Exclude:
12
+ - example/**/*
13
+ - test/data/*
14
+
15
+ Encoding:
16
+ Enabled: false
17
+ RescueModifier:
18
+ Enabled: false
19
+ HashSyntax:
20
+ Enabled: false
21
+ SpaceInsideStringInterpolation:
22
+ Enabled: false
23
+ DoubleNegation:
24
+ Enabled: false
@@ -0,0 +1,25 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2015-12-02 13:53:58 -0500 using RuboCop version 0.34.2.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ # Offense count: 2
10
+ # Configuration parameters: AllowURI, URISchemes.
11
+ Metrics/LineLength:
12
+ Max: 83
13
+
14
+ # Offense count: 2
15
+ # Cop supports --auto-correct.
16
+ # Configuration parameters: EnforcedStyle, SupportedStyles, AllowInnerSlashes.
17
+ Style/RegexpLiteral:
18
+ Exclude:
19
+ - 'dogwatch.gemspec'
20
+
21
+ # Offense count: 2
22
+ # Cop supports --auto-correct.
23
+ Style/RescueModifier:
24
+ Exclude:
25
+ - 'lib/dogwatch/version.rb'
@@ -0,0 +1 @@
1
+ 2.2.2
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in convection.gemspec
4
+ gemspec
5
+
6
+ group :development do
7
+ gem 'bundler', '~> 1.7'
8
+ gem 'minitest'
9
+ gem 'minitest-reporters', '>= 0.5.0'
10
+ gem 'rake', '~> 10.0'
11
+ gem 'rubocop', '~> 0.34'
12
+ gem 'simplecov'
13
+ gem 'thor-scmversion', '= 1.7.0'
14
+ end
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2015 David Greene, Rapid7 LLC.
2
+
3
+ MIT License
4
+ ===========
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining
7
+ a copy of this software and associated documentation files (the
8
+ "Software"), to deal in the Software without restriction, including
9
+ without limitation the rights to use, copy, modify, merge, publish,
10
+ distribute, sublicense, and/or sell copies of the Software, and to
11
+ permit persons to whom the Software is furnished to do so, subject to
12
+ the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be
15
+ included in all copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,66 @@
1
+ # DogWatch
2
+
3
+ _A DSL to create DataDog monitors_
4
+
5
+ This gem is designed to provide a simple method for creating DataDog monitors in Ruby.
6
+
7
+ ## Installation
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'dogwatch'
12
+ ```
13
+
14
+ And then execute:
15
+ ```shell
16
+ $ bundle exec dogwatch create [--dogfile=DOGFILE] [--api_key=API KEY] [--app_key=APP KEY]
17
+ ```
18
+ Or install it yourself as:
19
+ ```shell
20
+ $ gem install dogwatch
21
+ ```
22
+
23
+ ## Credentials
24
+ [DataDog API credentials](https://app.datadoghq.com/account/settings#api) are required.
25
+
26
+ Generate both an api key and an app key and either place them in a file named `~/.dogwatch/credentials` or pass them via the command line.
27
+
28
+ A sample credentials file is provided in the `example` directory.
29
+ ## Usage
30
+
31
+ DogWatch is a thin DSL over the [DataDog ruby client](https://github.com/DataDog/dogapi-rb) that handles generating DataDog monitors.
32
+
33
+ The following is an example of a `Dogfile`:
34
+ ```ruby
35
+ require 'dogwatch'
36
+
37
+ DogWatch.monitor do
38
+ ## Create a new monitor - monitor name is REQUIRED
39
+ monitor 'MONITOR NAME' do
40
+ type :metric_alert # REQUIRED: One of [:metric_alert | :service_check | :event_alert]
41
+ query 'QUERY' # REQUIRED
42
+ message 'MESSAGE'
43
+ tags %w(A list of tags to associate with your monitor)
44
+
45
+ options do
46
+ silenced '*': nil
47
+ notify_no_data false
48
+ no_data_timeframe 3
49
+ timeout_h 99
50
+ renotify_interval 60
51
+ escalation_message 'oh snap'
52
+ include_tags true
53
+ end
54
+ end
55
+ end
56
+ ```
57
+
58
+ Monitors that already exist are matched by name and updated accordingly. If the name isn't matched exactly, DogWatch assumes you want a new monitor.
59
+
60
+ A sample `Dogfile` is provided in the `example` directory.
61
+
62
+ For a full list of options and a description of each parameter, see [DataDog's API documentation](http://docs.datadoghq.com/api/#monitors).
63
+
64
+ ## TO DO
65
+ * More descriptive errors
66
+ * Better error handling if a monitor fails local validation
@@ -0,0 +1,12 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rubocop/rake_task'
3
+ require 'rake/testtask'
4
+
5
+ RuboCop::RakeTask.new
6
+
7
+ Rake::TestTask.new do |t|
8
+ t.libs << 'test'
9
+ t.pattern = 'test/**/test_*.rb'
10
+ end
11
+
12
+ task :default => [:test, :rubocop]
@@ -0,0 +1,5 @@
1
+ # encoding: utf-8
2
+
3
+ require 'bundler'
4
+ require 'bundler/setup'
5
+ require 'thor/scmversion'
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+ require 'thor'
3
+ require 'dogwatch/dogfile'
4
+
5
+ module DogWatch
6
+ ##
7
+ # DogWatch CLI
8
+ ##
9
+ class CLI < Thor
10
+ class_option :dogfile, :type => :string, :default => 'Dogfile'
11
+ class_option :api_key, :type => :string, :default => nil
12
+ class_option :app_key, :type => :string, :default => nil
13
+ def initialize(*args)
14
+ super
15
+ @cwd = Dir.getwd
16
+ @dogfile = DogWatch::DogFile.new
17
+ end
18
+
19
+ desc 'create', 'Create a monitor from a Dogfile'
20
+ def create
21
+ @dogfile.configure(File.absolute_path(options['dogfile'], @cwd),
22
+ options['api_key'], options['app_key'])
23
+ @dogfile.create { |c| say_status(*c.to_thor) }
24
+ end
25
+ end
26
+ end
27
+
28
+ DogWatch::CLI.start(ARGV)
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'dogwatch/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'dogwatch'
8
+ spec.version = DogWatch::VERSION
9
+ spec.authors = ['David Greene']
10
+ spec.email = ['David_Greene@rapid7.com']
11
+ spec.summary = DogWatch::SUMMARY
12
+ spec.description = DogWatch::DESCRIPTION
13
+ spec.homepage = 'https://github.com/rapid7/dogwatch'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(/^(test|spec|features)\//)
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_runtime_dependency 'dogapi', '~> 1.21'
22
+ spec.add_runtime_dependency 'thor', '~> 0.19'
23
+ end
@@ -0,0 +1,23 @@
1
+ require_relative '../lib/dogwatch'
2
+
3
+ DogWatch.monitor do
4
+ monitor 'test_alert' do
5
+ type :metric_alert
6
+ query 'avg(last_1m):avg:system.cpu.user{region:us-east-1} > 20'
7
+ message 'A message to include with notifications for this monitor.'\
8
+ 'Email notifications can be sent to specific users by '\
9
+ 'using the same \'@username\' notation as events.'
10
+
11
+ tags %w(A list of tags to associate with your monitor)
12
+
13
+ options do
14
+ silenced '*': nil
15
+ notify_no_data false
16
+ no_data_timeframe 3
17
+ timeout_h 99
18
+ renotify_interval 60
19
+ escalation_message 'oh snap'
20
+ include_tags true
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,2 @@
1
+ api_key: YOUR API KEY
2
+ app_key: YOUR APP KEY
@@ -0,0 +1,13 @@
1
+ ##
2
+ # Root module
3
+ ##
4
+ module DogWatch
5
+ class << self
6
+ def monitor(*args, &block)
7
+ DogWatch::Monitor.new(*args, &block)
8
+ end
9
+ end
10
+ end
11
+
12
+ require_relative 'dogwatch/version'
13
+ require_relative 'dogwatch/monitor'
@@ -0,0 +1,27 @@
1
+ require_relative 'model/config'
2
+ require_relative 'model/client'
3
+
4
+ module DogWatch
5
+ ##
6
+ # Manage the execution of the Dogfile
7
+ ##
8
+ class DogFile
9
+ # @param [String] dogfile
10
+ # @param [String|Object] api_key
11
+ # @param [String|Object] app_key
12
+ def configure(dogfile, api_key = nil, app_key = nil)
13
+ @dogfile = dogfile
14
+ @config = DogWatch::Model::Config.new(api_key, app_key)
15
+ end
16
+
17
+ # @param [Proc] block
18
+ def create(&block)
19
+ monitor = instance_eval(IO.read(@dogfile), @dogfile, 1)
20
+ monitor.config = @config
21
+ monitor.client
22
+
23
+ monitor.get
24
+ monitor.responses.each { |r| block.call(r) }
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,92 @@
1
+ require 'dogapi'
2
+ require_relative 'config'
3
+ require_relative 'response'
4
+
5
+ module DogWatch
6
+ module Model
7
+ ##
8
+ # Client interface for the DataDog API
9
+ ##
10
+ class Client
11
+ attr_accessor :client
12
+ attr_accessor :config
13
+ attr_reader :response
14
+
15
+ # @param [DogWatch::Model::Config] config
16
+ def initialize(config)
17
+ @config = config
18
+ @client = Dogapi::Client.new(@config.api_key, @config.app_key)
19
+ @all_monitors = all_monitors
20
+ end
21
+
22
+ # @param [DogWatch::Model::Monitor] monitor
23
+ def execute(monitor)
24
+ if monitor_exists?(monitor.name)
25
+ update_monitor(monitor)
26
+ else
27
+ new_monitor(monitor)
28
+ end
29
+ end
30
+
31
+ # @param [DogWatch::Model::Monitor] monitor
32
+ # @return [DogWatch::Model::Response]
33
+ def update_monitor(monitor)
34
+ options = options(monitor)
35
+ existing_monitor = get_monitor(monitor.name)
36
+ response = @client.update_monitor(existing_monitor['id'],
37
+ monitor.attributes.query,
38
+ options)
39
+ updated = %w(200 202).include?(response[0])
40
+ @response = DogWatch::Model::Response.new(response, updated)
41
+ end
42
+
43
+ # @param [DogWatch::Model::Monitor] monitor
44
+ # @return [DogWatch::Model::Response]
45
+ def new_monitor(monitor)
46
+ options = options(monitor)
47
+ response = @client.monitor(monitor.attributes.type,
48
+ monitor.attributes.query,
49
+ options)
50
+ @response = DogWatch::Model::Response.new(response)
51
+ end
52
+
53
+ private
54
+
55
+ # @param [DogWatch::Model::Monitor] monitor
56
+ # @return [Hash]
57
+ def options(monitor)
58
+ {
59
+ name: monitor.name,
60
+ message: monitor.attributes.message,
61
+ tags: monitor.attributes.tags,
62
+ options: monitor.attributes.options
63
+ }
64
+ end
65
+
66
+ # @param [String] name
67
+ # @return [TrueClass|FalseClass]
68
+ def monitor_exists?(name)
69
+ @all_monitors.count { |m| m['name'] == name } > 0
70
+ end
71
+
72
+ # @param [String] name
73
+ # @return [Hash]
74
+ def get_monitor(name)
75
+ @all_monitors.find { |m| m['name'] == name }
76
+ end
77
+
78
+ # @return [Array]
79
+ def all_monitors
80
+ response = @client.get_all_monitors if @all_monitors.nil?
81
+ @all_monitors = response[1] if response[0] == '200'
82
+ @all_monitors
83
+ end
84
+
85
+ # @return [DogWatch::Model::Config]
86
+ def config
87
+ @config = DogWatch::Model::Config.new if @config.nil?
88
+ @config
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,36 @@
1
+ require 'yaml'
2
+
3
+ module DogWatch
4
+ module Model
5
+ ##
6
+ # Manages API configuration. Currently handles
7
+ # credential only.
8
+ ##
9
+ class Config
10
+ attr_accessor :api_key
11
+ attr_accessor :app_key
12
+
13
+ # @param [String] api_key
14
+ # @param [String] app_key
15
+ def initialize(api_key = nil, app_key = nil)
16
+ @api_key = api_key unless api_key.nil?
17
+ @app_key = app_key unless app_key.nil?
18
+ return unless app_key.nil? || api_key.nil?
19
+
20
+ from_file
21
+ end
22
+
23
+ def from_file
24
+ begin
25
+ config_file = IO.read("#{Dir.home}/.dogwatch/credentials")
26
+ rescue
27
+ raise('No credentials supplied')
28
+ end
29
+
30
+ credentials = YAML.load(config_file)
31
+ @api_key = credentials['api_key']
32
+ @app_key = credentials['app_key']
33
+ end
34
+ end
35
+ end
36
+ end