dogwatch 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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