doggy 0.1.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 +9 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +61 -0
- data/Rakefile +1 -0
- data/bin/doggy +9 -0
- data/doggy.gemspec +28 -0
- data/lib/doggy.rb +118 -0
- data/lib/doggy/cli.rb +97 -0
- data/lib/doggy/cli/create.rb +25 -0
- data/lib/doggy/cli/delete.rb +21 -0
- data/lib/doggy/cli/mute.rb +19 -0
- data/lib/doggy/cli/pull.rb +27 -0
- data/lib/doggy/cli/push.rb +28 -0
- data/lib/doggy/cli/unmute.rb +19 -0
- data/lib/doggy/cli/version.rb +12 -0
- data/lib/doggy/client.rb +46 -0
- data/lib/doggy/model/dash.rb +91 -0
- data/lib/doggy/model/monitor.rb +129 -0
- data/lib/doggy/model/screen.rb +76 -0
- data/lib/doggy/serializer/json.rb +15 -0
- data/lib/doggy/serializer/yaml.rb +15 -0
- data/lib/doggy/version.rb +3 -0
- data/lib/doggy/worker.rb +31 -0
- metadata +153 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: d45de1eb568f088f9c702bbb55e2961691337c45
|
|
4
|
+
data.tar.gz: 733512dea50824d0800967443ab6827e79d155b7
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 60049f997c3552d938fbf8d70e709f525716939658e3f08a2d4101b679fd12b82285d5266355f6349bc8ba6d998d803f56a2c6dbbaed8987fe90bf64f0a6ba21
|
|
7
|
+
data.tar.gz: ab72d81d8e5d83561e67c126ad52e0a5320f28ccc4905943e60d529e5d2c7617b862e8ffc677a7e38f42e093e9a2983df385914468a39273ba4e789fa75bb0f6
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2015 Vlad Gorodetsky
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Doggy
|
|
2
|
+
|
|
3
|
+
Doggy manages your DataDog dashboards, alerts, monitors, and screenboards.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add this line to your Gemfile:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem 'doggy'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
And then execute:
|
|
14
|
+
|
|
15
|
+
$ bundle
|
|
16
|
+
|
|
17
|
+
Or install it yourself as:
|
|
18
|
+
|
|
19
|
+
$ gem install doggy
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
# Export your DataDog credentials or use ejson
|
|
25
|
+
$ export DATADOG_API_KEY=api_key_goes_here
|
|
26
|
+
$ export DATADOG_APP_KEY=app_key_goes_here
|
|
27
|
+
|
|
28
|
+
# Download selected items from DataDog
|
|
29
|
+
$ doggy pull ID ID
|
|
30
|
+
|
|
31
|
+
# Download all items
|
|
32
|
+
$ doggy pull
|
|
33
|
+
|
|
34
|
+
# Upload selected items to DataDog
|
|
35
|
+
$ doggy push ID ID ID
|
|
36
|
+
|
|
37
|
+
# Upload all items to DataDog
|
|
38
|
+
$ doggy push
|
|
39
|
+
|
|
40
|
+
# Create a new dashboard
|
|
41
|
+
$ doggy create dash 'My New Dash'
|
|
42
|
+
|
|
43
|
+
# Delete selected items from both DataDog and local storage
|
|
44
|
+
$ doggy delete ID ID ID
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Note that we currently don't support global upload due to high risk of overwriting things. We'll turn this feature on after initial testing period.
|
|
48
|
+
|
|
49
|
+
## Development
|
|
50
|
+
|
|
51
|
+
After checking out the repo, run `bundle install` to install dependencies.
|
|
52
|
+
|
|
53
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
54
|
+
|
|
55
|
+
## Contributing
|
|
56
|
+
|
|
57
|
+
1. Fork it ( https://github.com/bai/doggy/fork )
|
|
58
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
59
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
60
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
61
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/doggy
ADDED
data/doggy.gemspec
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'doggy/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "doggy"
|
|
8
|
+
spec.version = Doggy::VERSION
|
|
9
|
+
spec.authors = ["Vlad Gorodetsky"]
|
|
10
|
+
spec.email = ["v@gor.io"]
|
|
11
|
+
|
|
12
|
+
spec.summary = %q{Syncs DataDog dashboards, alerts, screenboards, and monitors.}
|
|
13
|
+
spec.description = %q{Syncs DataDog dashboards, alerts, screenboards, and monitors.}
|
|
14
|
+
spec.homepage = "http://github.com/bai/doggy"
|
|
15
|
+
spec.license = "MIT"
|
|
16
|
+
|
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
18
|
+
spec.bindir = "bin"
|
|
19
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
20
|
+
spec.require_paths = ["lib"]
|
|
21
|
+
|
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.9"
|
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
|
24
|
+
spec.add_dependency "thor", "~> 0.19"
|
|
25
|
+
spec.add_dependency "dogapi", "~> 1.17"
|
|
26
|
+
spec.add_dependency "thread", "~> 0.2"
|
|
27
|
+
spec.add_dependency "ejson", "~> 1.0"
|
|
28
|
+
end
|
data/lib/doggy.rb
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
require 'pathname'
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'yaml'
|
|
5
|
+
require 'dogapi'
|
|
6
|
+
|
|
7
|
+
require 'doggy/version'
|
|
8
|
+
require 'doggy/client'
|
|
9
|
+
require 'doggy/worker'
|
|
10
|
+
require 'doggy/serializer/json'
|
|
11
|
+
require 'doggy/serializer/yaml'
|
|
12
|
+
require 'doggy/model/dash'
|
|
13
|
+
require 'doggy/model/monitor'
|
|
14
|
+
require 'doggy/model/screen'
|
|
15
|
+
|
|
16
|
+
module Doggy
|
|
17
|
+
DOG_SKIP_REGEX = /\[dog\s+skip\]/i.freeze
|
|
18
|
+
DEFAULT_SERIALIZER_CLASS = Doggy::Serializer::Json
|
|
19
|
+
|
|
20
|
+
class DoggyError < StandardError
|
|
21
|
+
def self.status_code(code)
|
|
22
|
+
define_method(:status_code) { code }
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class InvalidOption < DoggyError; status_code(15); end
|
|
27
|
+
class InvalidItemType < DoggyError; status_code(10); end
|
|
28
|
+
|
|
29
|
+
class << self
|
|
30
|
+
# @option arguments [Constant] :serializer A specific serializer class to use, will be initialized by doggy and passed the object instance
|
|
31
|
+
def serializer(options = {})
|
|
32
|
+
@serializer ||= options[:serializer] ? options[:serializer] : DEFAULT_SERIALIZER_CLASS
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def client
|
|
36
|
+
Doggy::Client.new
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Absolute path of where alerts are stored on the filesystem.
|
|
40
|
+
#
|
|
41
|
+
# @return [Pathname]
|
|
42
|
+
def alerts_path
|
|
43
|
+
@alerts_path ||= Pathname.new('alerts').expand_path(Dir.pwd).expand_path.tap { |path| FileUtils.mkdir_p(path) }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Absolute path of where dashes are stored on the filesystem.
|
|
47
|
+
#
|
|
48
|
+
# @return [Pathname]
|
|
49
|
+
def dashes_path
|
|
50
|
+
@dashes_path ||= Pathname.new('dashes').expand_path(Dir.pwd).expand_path.tap { |path| FileUtils.mkdir_p(path) }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Absolute path of where screens are stored on the filesystem.
|
|
54
|
+
#
|
|
55
|
+
# @return [Pathname]
|
|
56
|
+
def screens_path
|
|
57
|
+
@screens_path ||= Pathname.new('screens').expand_path(Dir.pwd).expand_path.tap { |path| FileUtils.mkdir_p(path) }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Cleans up directory
|
|
61
|
+
def clean_dir(dir)
|
|
62
|
+
Dir.foreach(dir) { |f| fn = File.join(dir, f); File.delete(fn) if f != '.' && f != '..'}
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def all_local_items
|
|
66
|
+
@all_local_items ||= Dir[Doggy.dashes_path.join('**/*'), Doggy.alerts_path.join('**/*'), Doggy.screens_path.join('**/*')].inject({}) { |memo, file| memo.merge load_item(f) }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def load_item(f)
|
|
70
|
+
filetype = File.extname(f)
|
|
71
|
+
|
|
72
|
+
item = case filetype
|
|
73
|
+
when '.yaml', '.yml' then Doggy::Serializer::Yaml.load(File.read(f))
|
|
74
|
+
when '.json' then Doggy::Serializer::Json.load(File.read(f))
|
|
75
|
+
else raise InvalidItemType
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
{ [ determine_type(item), item['id'] ] => item }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def determine_type(item)
|
|
82
|
+
return 'dash' if item['graphs']
|
|
83
|
+
return 'monitor' if item['message']
|
|
84
|
+
return 'screen' if item['board_title']
|
|
85
|
+
raise InvalidItemType
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def emit_shipit_deployment
|
|
89
|
+
Doggy.client.dog.emit_event(
|
|
90
|
+
Dogapi::Event.new(ENV['REVISION'], msg_title: "ShipIt Deployment by #{ENV['USER']}", tags: %w(audit shipit), source_type_name: 'shipit')
|
|
91
|
+
)
|
|
92
|
+
rescue => e
|
|
93
|
+
puts "Exception: #{e.message}"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def current_version
|
|
97
|
+
now = Time.now.to_i
|
|
98
|
+
month_ago = now - 3600 * 24 * 30
|
|
99
|
+
events = Doggy.client.dog.stream(month_ago, now, tags: %w(audit shipit))[1]['events']
|
|
100
|
+
|
|
101
|
+
events[0]['text'] # most recetly deployed SHA
|
|
102
|
+
rescue => e
|
|
103
|
+
puts "Exception: #{e.message}"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def all_remote_dashes
|
|
107
|
+
@all_remote_dashes ||= Doggy.client.dog.get_dashboards[1]['dashes'].inject({}) do |memo, dash|
|
|
108
|
+
memo.merge([ 'dash', dash['id'] ] => dash)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def all_remote_monitors
|
|
113
|
+
@all_remote_monitors ||= Doggy.client.dog.get_all_monitors[1].inject({}) do |memo, monitor|
|
|
114
|
+
memo.merge([ 'monitor', monitor['id'] ] => monitor)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
data/lib/doggy/cli.rb
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
require 'thor'
|
|
2
|
+
require 'doggy'
|
|
3
|
+
|
|
4
|
+
module Doggy
|
|
5
|
+
class CLI < Thor
|
|
6
|
+
include Thor::Actions
|
|
7
|
+
|
|
8
|
+
def self.start(*)
|
|
9
|
+
super
|
|
10
|
+
rescue Exception => e
|
|
11
|
+
raise e
|
|
12
|
+
ensure
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def initialize(*args)
|
|
16
|
+
super
|
|
17
|
+
rescue UnknownArgumentError => e
|
|
18
|
+
raise Doggy::InvalidOption, e.message
|
|
19
|
+
ensure
|
|
20
|
+
self.options ||= {}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
check_unknown_options!(:except => [:config, :exec])
|
|
24
|
+
stop_on_unknown_option! :exec
|
|
25
|
+
|
|
26
|
+
desc "pull [SPACE SEPARATED IDs]", "Pulls objects from DataDog"
|
|
27
|
+
long_desc <<-D
|
|
28
|
+
Pull objects from DataDog. If pull is successful, Doggy exits with a status of 0.
|
|
29
|
+
If not, the error is displayed and Doggy exits status 1.
|
|
30
|
+
D
|
|
31
|
+
def pull(*ids)
|
|
32
|
+
require 'doggy/cli/pull'
|
|
33
|
+
Pull.new(options.dup, ids).run
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
desc "push [SPACE SEPARATED IDs]", "Pushes objects to DataDog"
|
|
37
|
+
long_desc <<-D
|
|
38
|
+
Pushes objects to DataDog. If push is successful, Doggy exits with a status of 0.
|
|
39
|
+
If not, the error is displayed and Doggy exits status 1.
|
|
40
|
+
D
|
|
41
|
+
def push(*ids)
|
|
42
|
+
require 'doggy/cli/push'
|
|
43
|
+
Push.new(options.dup, ids).run
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
desc "create OBJECT_TYPE OBJECT_NAME", "Creates a new object on DataDog"
|
|
47
|
+
long_desc <<-D
|
|
48
|
+
Creates a new object on DataDog. If create is successful, Doggy exits with a status of 0.
|
|
49
|
+
If not, the error is displayed and Doggy exits status 1.
|
|
50
|
+
D
|
|
51
|
+
def create(kind, name)
|
|
52
|
+
require 'doggy/cli/create'
|
|
53
|
+
Create.new(options.dup, kind, name).run
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
desc "delete SPACE SEPARATED IDs", "Deletes objects from DataDog"
|
|
57
|
+
long_desc <<-D
|
|
58
|
+
Deletes objects from DataDog. If delete is successful, Doggy exits with a status of 0.
|
|
59
|
+
If not, the error is displayed and Doggy exits status 1.
|
|
60
|
+
D
|
|
61
|
+
def delete(*ids)
|
|
62
|
+
require 'doggy/cli/delete'
|
|
63
|
+
Delete.new(options.dup, ids).run
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
desc "mute [SPACE SEPARATED IDs]", "Mutes monitor on DataDog"
|
|
67
|
+
long_desc <<-D
|
|
68
|
+
Mutes monitor on DataDog. If mute is successful, Doggy exits with a status of 0.
|
|
69
|
+
If not, the error is displayed and Doggy exits status 1.
|
|
70
|
+
D
|
|
71
|
+
def mute(*ids)
|
|
72
|
+
require 'doggy/cli/mute'
|
|
73
|
+
Mute.new(options.dup, ids).run
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
desc "unmute [SPACE SEPARATED IDs]", "Unmutes monitor on DataDog"
|
|
77
|
+
long_desc <<-D
|
|
78
|
+
Deletes objects from DataDog. If delete is successful, Doggy exits with a status of 0.
|
|
79
|
+
If not, the error is displayed and Doggy exits status 1.
|
|
80
|
+
D
|
|
81
|
+
def unmute(*ids)
|
|
82
|
+
require 'doggy/cli/unmute'
|
|
83
|
+
Unmute.new(options.dup, ids).run
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
desc "version", "Detects the most recent SHA deployed by ShipIt"
|
|
87
|
+
long_desc <<-D
|
|
88
|
+
Scans DataDog event stream for shipit events what contain most recently deployed version
|
|
89
|
+
of DataDog properties.
|
|
90
|
+
If not, the error is displayed and Doggy exits status 1.
|
|
91
|
+
D
|
|
92
|
+
def version
|
|
93
|
+
require 'doggy/cli/version'
|
|
94
|
+
Version.new.run
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Doggy
|
|
2
|
+
class CLI::Create
|
|
3
|
+
attr_reader :options, :kind, :name
|
|
4
|
+
|
|
5
|
+
def initialize(options, kind, name)
|
|
6
|
+
@options = options
|
|
7
|
+
@kind = kind
|
|
8
|
+
@name = name
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def run
|
|
12
|
+
begin
|
|
13
|
+
case kind
|
|
14
|
+
when 'dash', 'dashboard' then Doggy::Dash.create(name)
|
|
15
|
+
when 'alert', 'monitor' then Doggy::Monitor.create(name)
|
|
16
|
+
when 'screen', 'screenboard' then Doggy::Screen.create(name)
|
|
17
|
+
else puts 'Unknown item type'
|
|
18
|
+
end
|
|
19
|
+
rescue DoggyError
|
|
20
|
+
puts "Create failed."
|
|
21
|
+
exit 1
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Doggy
|
|
2
|
+
class CLI::Delete
|
|
3
|
+
attr_reader :options, :ids
|
|
4
|
+
|
|
5
|
+
def initialize(options, ids)
|
|
6
|
+
@options = options
|
|
7
|
+
@ids = ids
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def run
|
|
11
|
+
begin
|
|
12
|
+
Doggy::Dash.delete(ids)
|
|
13
|
+
Doggy::Monitor.delete(ids)
|
|
14
|
+
Doggy::Screen.delete(ids)
|
|
15
|
+
rescue DoggyError
|
|
16
|
+
puts "Create failed."
|
|
17
|
+
exit 1
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Doggy
|
|
2
|
+
class CLI::Mute
|
|
3
|
+
attr_reader :options, :ids
|
|
4
|
+
|
|
5
|
+
def initialize(options, ids)
|
|
6
|
+
@options = options
|
|
7
|
+
@ids = ids
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def run
|
|
11
|
+
begin
|
|
12
|
+
Doggy::Monitor.mute(ids)
|
|
13
|
+
rescue DoggyError
|
|
14
|
+
puts "Mute failed."
|
|
15
|
+
exit 1
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Doggy
|
|
2
|
+
class CLI::Pull
|
|
3
|
+
attr_reader :options, :ids
|
|
4
|
+
|
|
5
|
+
def initialize(options, ids)
|
|
6
|
+
@options = options
|
|
7
|
+
@ids = ids
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def run
|
|
11
|
+
begin
|
|
12
|
+
if ids.any?
|
|
13
|
+
Doggy::Dash.download(ids)
|
|
14
|
+
Doggy::Monitor.download(ids)
|
|
15
|
+
Doggy::Screen.download(ids)
|
|
16
|
+
else
|
|
17
|
+
Doggy::Dash.download_all
|
|
18
|
+
Doggy::Monitor.download_all
|
|
19
|
+
Doggy::Screen.download_all
|
|
20
|
+
end
|
|
21
|
+
rescue DoggyError
|
|
22
|
+
puts "Pull failed."
|
|
23
|
+
exit 1
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Doggy
|
|
2
|
+
class CLI::Push
|
|
3
|
+
attr_reader :options, :ids
|
|
4
|
+
|
|
5
|
+
def initialize(options, ids)
|
|
6
|
+
@options = options
|
|
7
|
+
@ids = ids
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def run
|
|
11
|
+
begin
|
|
12
|
+
if ids.any?
|
|
13
|
+
Doggy::Dash.upload(ids)
|
|
14
|
+
Doggy::Monitor.upload(ids)
|
|
15
|
+
Doggy::Screen.upload(ids)
|
|
16
|
+
else
|
|
17
|
+
Doggy::Dash.upload_all
|
|
18
|
+
Doggy::Monitor.upload_all
|
|
19
|
+
Doggy::Screen.upload_all
|
|
20
|
+
Doggy.emit_shipit_deployment if ENV['SHIPIT']
|
|
21
|
+
end
|
|
22
|
+
rescue DoggyError
|
|
23
|
+
puts "Push failed."
|
|
24
|
+
exit 1
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Doggy
|
|
2
|
+
class CLI::Unmute
|
|
3
|
+
attr_reader :options, :ids
|
|
4
|
+
|
|
5
|
+
def initialize(options, ids)
|
|
6
|
+
@options = options
|
|
7
|
+
@ids = ids
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def run
|
|
11
|
+
begin
|
|
12
|
+
Doggy::Monitor.unmute(ids)
|
|
13
|
+
rescue DoggyError
|
|
14
|
+
puts "Unmute failed."
|
|
15
|
+
exit 1
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
data/lib/doggy/client.rb
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
class Dogapi::APIService
|
|
2
|
+
attr_reader :api_key, :application_key # as they are useless in the parent class
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
module Doggy
|
|
6
|
+
class Client
|
|
7
|
+
def api_key
|
|
8
|
+
@api_key ||= ENV.fetch('DATADOG_API_KEY', ejson_config[:datadog_api_key])
|
|
9
|
+
rescue => e
|
|
10
|
+
puts "[DogSync#api_key] Exception: #{e.message}"
|
|
11
|
+
raise
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def app_key
|
|
15
|
+
@app_key ||= ENV.fetch('DATADOG_APP_KEY', ejson_config[:datadog_app_key])
|
|
16
|
+
rescue => e
|
|
17
|
+
puts "[DogSync#app_key] Exception: #{e.message}"
|
|
18
|
+
raise
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def dog
|
|
22
|
+
@dog ||= Dogapi::Client.new(api_key, app_key)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def api_service
|
|
26
|
+
@api_service ||= Dogapi::APIService.new(api_key, app_key)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def api_service_params
|
|
30
|
+
@api_service_params ||= { api_key: Doggy.client.api_service.api_key, application_key: Doggy.client.api_service.application_key }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def ejson_config
|
|
36
|
+
@ejson_config ||= begin
|
|
37
|
+
if File.exists?('secrets.json')
|
|
38
|
+
secrets = JSON.parse(File.read('secrets.json'))
|
|
39
|
+
{ datadog_api_key: secrets['datadog_api_key'], datadog_app_key: secrets['datadog_app_key'] }
|
|
40
|
+
else
|
|
41
|
+
{}
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
module Doggy
|
|
2
|
+
class Dash
|
|
3
|
+
def initialize(**options)
|
|
4
|
+
@id = options[:id]
|
|
5
|
+
@title = options[:title] || raw_local['title']
|
|
6
|
+
@description = options[:description] || raw_local['description']
|
|
7
|
+
@graphs = options[:graphs] || raw_local['graphs']
|
|
8
|
+
@template_variables = options[:template_variables] || raw_local['template_variables']
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.download_all
|
|
12
|
+
ids = Doggy.client.dog.get_dashboards[1]['dashes'].map { |d| d['id'] }
|
|
13
|
+
puts "Downloading #{ids.size} dashboards..."
|
|
14
|
+
Doggy.clean_dir(Doggy.dashes_path)
|
|
15
|
+
download(ids)
|
|
16
|
+
rescue => e
|
|
17
|
+
puts "Exception: #{e.message}"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.upload_all
|
|
21
|
+
ids = Dir[Doggy.dashes_path.join('*')].map { |f| File.basename(f, '.*') }
|
|
22
|
+
puts "Uploading #{ids.size} dashboards from #{Doggy.dashes_path}: #{ids.join(', ')}"
|
|
23
|
+
upload(ids)
|
|
24
|
+
rescue => e
|
|
25
|
+
puts "Exception: #{e.message}"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.download(ids)
|
|
29
|
+
Doggy::Worker.new(threads: Doggy::Worker::CONCURRENT_STREAMS) { |id| new(id: id).save }.call([*ids])
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.upload(ids)
|
|
33
|
+
Doggy::Worker.new(threads: Doggy::Worker::CONCURRENT_STREAMS) { |id| new(id: id).push }.call([*ids])
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.create(name)
|
|
37
|
+
# This graphs placeholder is required as you cannot create an empty dashboard via API
|
|
38
|
+
dash = new(title: name, description: '', graphs: [{
|
|
39
|
+
"definition" => {
|
|
40
|
+
"events" => [],
|
|
41
|
+
"requests" => [
|
|
42
|
+
{"q" => "avg:system.mem.free{*}"}
|
|
43
|
+
],
|
|
44
|
+
"viz" => "timeseries"
|
|
45
|
+
},
|
|
46
|
+
"title" => "Average Memory Free"
|
|
47
|
+
}])
|
|
48
|
+
dash.push
|
|
49
|
+
dash.save
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def raw
|
|
53
|
+
@raw ||= Doggy.client.dog.get_dashboard(@id)[1]['dash'].sort.to_h
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def raw_local
|
|
57
|
+
return {} unless File.exists?(path)
|
|
58
|
+
@raw_local ||= Doggy.serializer.load(File.read(path))
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def save
|
|
62
|
+
puts raw['errors'] and return if raw['errors'] # do now download an item if it doesn't exist
|
|
63
|
+
return if raw['title'] =~ Doggy::DOG_SKIP_REGEX
|
|
64
|
+
File.write(path, Doggy.serializer.dump(raw))
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def push
|
|
68
|
+
return unless File.exists?(path)
|
|
69
|
+
return if @title =~ Doggy::DOG_SKIP_REGEX
|
|
70
|
+
if @id
|
|
71
|
+
Doggy.client.dog.update_dashboard(@id, @title, @description, @graphs, @template_variables)
|
|
72
|
+
else
|
|
73
|
+
dash = Doggy.client.dog.create_dashboard(@title, @description, @graphs)
|
|
74
|
+
# FIXME: Remove duplication
|
|
75
|
+
@id = dash[1]['id']
|
|
76
|
+
@graphs = dash[1]['graphs']
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def delete
|
|
81
|
+
Doggy.client.dog.delete_dashboard(@id)
|
|
82
|
+
File.unlink(path)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
def path
|
|
88
|
+
"#{Doggy.dashes_path}/#{@id}.json"
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
module Doggy
|
|
2
|
+
class Monitor
|
|
3
|
+
def initialize(**options)
|
|
4
|
+
@id = options[:id]
|
|
5
|
+
@query = options[:query]
|
|
6
|
+
@silenced = options[:silenced]
|
|
7
|
+
@name = options[:name]
|
|
8
|
+
@timeout_h = options[:timeout_h]
|
|
9
|
+
@message = options[:message]
|
|
10
|
+
@notify_audit = options[:notify_audit]
|
|
11
|
+
@notify_no_data = options[:notify_no_data]
|
|
12
|
+
@renotify_interval = options[:renotify_interval]
|
|
13
|
+
@escalation_message = options[:escalation_message]
|
|
14
|
+
@no_data_timeframe = options[:no_data_timeframe]
|
|
15
|
+
@silenced_timeout_ts = options[:silenced_timeout_ts]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.download_all
|
|
19
|
+
ids = Doggy.client.dog.get_all_alerts[1]['alerts'].map { |d| d['id'] }
|
|
20
|
+
puts "Downloading #{ids.size} alerts..."
|
|
21
|
+
Doggy.clean_dir(Doggy.alerts_path)
|
|
22
|
+
download(ids)
|
|
23
|
+
rescue => e
|
|
24
|
+
puts "Exception: #{e.message}"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.upload_all
|
|
28
|
+
ids = Dir[Doggy.alerts_path.join('*')].map { |f| File.basename(f, '.*') }
|
|
29
|
+
puts "Uploading #{ids.size} alerts from #{Doggy.alerts_path}: #{ids.join(', ')}"
|
|
30
|
+
upload(ids)
|
|
31
|
+
rescue => e
|
|
32
|
+
puts "Exception: #{e.message}"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.download(ids)
|
|
36
|
+
Doggy::Worker.new(threads: Doggy::Worker::CONCURRENT_STREAMS) { |id| new(id: id).save }.call([*ids])
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.upload(ids)
|
|
40
|
+
Doggy::Worker.new(threads: Doggy::Worker::CONCURRENT_STREAMS) { |id| new(id: id).push }.call([*ids])
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.mute(ids)
|
|
44
|
+
Doggy::Worker.new(threads: Doggy::Worker::CONCURRENT_STREAMS) { |id| new(id: id).mute }.call([*ids])
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def self.unmute(ids)
|
|
48
|
+
Doggy::Worker.new(threads: Doggy::Worker::CONCURRENT_STREAMS) { |id| new(id: id).unmute }.call([*ids])
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def self.create(name)
|
|
52
|
+
# Adding a placeholder query as it's a mandatory parameter
|
|
53
|
+
item = new(name: name, query: 'avg(last_1m):avg:system.load.1{*} > 100')
|
|
54
|
+
item.push
|
|
55
|
+
item.save
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def raw
|
|
59
|
+
@raw ||= begin
|
|
60
|
+
alert = Doggy.client.dog.get_monitor(@id)[1]
|
|
61
|
+
alert.delete('state')
|
|
62
|
+
alert.delete('overall_state')
|
|
63
|
+
alert['options'].delete('silenced')
|
|
64
|
+
alert.sort.to_h
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def raw_local
|
|
69
|
+
return unless File.exists?(path)
|
|
70
|
+
@raw_local ||= Doggy.serializer.load(File.read(path))
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def save
|
|
74
|
+
puts raw['errors'] and return if raw['errors'] # do now download an item if it doesn't exist
|
|
75
|
+
return if raw['name'] =~ Doggy::DOG_SKIP_REGEX
|
|
76
|
+
File.write(path, Doggy.serializer.dump(raw))
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def mute
|
|
80
|
+
Doggy.client.dog.mute_monitor(@id)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def unmute
|
|
84
|
+
Doggy.client.dog.unmute_monitor(@id)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def push
|
|
88
|
+
return if @name =~ Doggy::DOG_SKIP_REGEX
|
|
89
|
+
if @id
|
|
90
|
+
return unless File.exists?(path)
|
|
91
|
+
|
|
92
|
+
Doggy.client.dog.update_monitor(@id, @query || raw_local['query'], {
|
|
93
|
+
name: @name || raw_local['name'],
|
|
94
|
+
timeout_h: @timeout_h || raw_local['timeout_h'],
|
|
95
|
+
message: @message || raw_local['message'],
|
|
96
|
+
notify_audit: @notify_audit || raw_local['notify_audit'],
|
|
97
|
+
notify_no_data: @notify_no_data || raw_local['notify_no_data'],
|
|
98
|
+
renotify_interval: @renotify_interval || raw_local['renotify_interval'],
|
|
99
|
+
escalation_message: @escalation_message || raw_local['escalation_message'],
|
|
100
|
+
no_data_timeframe: @no_data_timeframe || raw_local['no_data_timeframe'],
|
|
101
|
+
silenced_timeout_ts: @silenced_timeout_ts || raw_local['silenced_timeout_ts'],
|
|
102
|
+
options: {
|
|
103
|
+
silenced: mute_state_for(@id),
|
|
104
|
+
},
|
|
105
|
+
})
|
|
106
|
+
else
|
|
107
|
+
result = Doggy.client.dog.monitor('metric alert', @query, name: @name)
|
|
108
|
+
@id = result[1]['id']
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def delete
|
|
113
|
+
Doggy.client.dog.delete_alert(@id)
|
|
114
|
+
File.unlink(path)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
private
|
|
118
|
+
|
|
119
|
+
def path
|
|
120
|
+
"#{Doggy.alerts_path}/#{@id}.json"
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def mute_state_for(id)
|
|
124
|
+
if remote_state = Doggy.all_remote_monitors.detect { |key, value| key == [ 'monitor', id.to_i ] }
|
|
125
|
+
remote_state[1]['options']['silenced'] if remote_state[1]['options']
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
module Doggy
|
|
2
|
+
class Screen
|
|
3
|
+
def initialize(**options)
|
|
4
|
+
@id = options[:id]
|
|
5
|
+
@description = options[:description] || raw_local
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def self.download_all
|
|
9
|
+
ids = Doggy.client.dog.get_all_screenboards[1]['screenboards'].map { |d| d['id'] }
|
|
10
|
+
puts "Downloading #{ids.size} screenboards..."
|
|
11
|
+
Doggy.clean_dir(Doggy.screens_path)
|
|
12
|
+
download(ids)
|
|
13
|
+
rescue => e
|
|
14
|
+
puts "Exception: #{e.message}"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.upload_all
|
|
18
|
+
ids = Dir[Doggy.screens_path.join('*')].map { |f| File.basename(f, '.*') }
|
|
19
|
+
puts "Uploading #{ids.size} screenboards from #{Doggy.screens_path}: #{ids.join(', ')}"
|
|
20
|
+
upload(ids)
|
|
21
|
+
rescue => e
|
|
22
|
+
puts "Exception: #{e.message}"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.download(ids)
|
|
26
|
+
Doggy::Worker.new(threads: Doggy::Worker::CONCURRENT_STREAMS) { |id| new(id: id).save }.call([*ids])
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.upload(ids)
|
|
30
|
+
Doggy::Worker.new(threads: Doggy::Worker::CONCURRENT_STREAMS) { |id| new(id: id).push }.call([*ids])
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.create(name)
|
|
34
|
+
item = new(description: { 'board_title' => name, 'widgets' => [] })
|
|
35
|
+
item.push
|
|
36
|
+
item.save
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def raw
|
|
40
|
+
@raw ||= Doggy.client.dog.get_screenboard(@id)[1].sort.to_h
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def raw_local
|
|
44
|
+
return {} unless File.exists?(path)
|
|
45
|
+
@raw_local ||= Doggy.serializer.load(File.read(path))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def save
|
|
49
|
+
puts raw['errors'] and return if raw['errors'] # do now download an item if it doesn't exist
|
|
50
|
+
return if raw['board_title'] =~ Doggy::DOG_SKIP_REGEX
|
|
51
|
+
File.write(path, Doggy.serializer.dump(raw))
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def push
|
|
55
|
+
return if @description =~ Doggy::DOG_SKIP_REGEX
|
|
56
|
+
if @id
|
|
57
|
+
Doggy.client.dog.update_screenboard(@id, @description)
|
|
58
|
+
else
|
|
59
|
+
result = Doggy.client.dog.create_screenboard(@description)
|
|
60
|
+
@id = result[1]['id']
|
|
61
|
+
@description = result[1]
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def delete
|
|
66
|
+
Doggy.client.dog.delete_screenboard(@id)
|
|
67
|
+
File.unlink(path)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def path
|
|
73
|
+
"#{Doggy.screens_path}/#{@id}.json"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Doggy
|
|
2
|
+
module Serializer
|
|
3
|
+
class Json
|
|
4
|
+
# De-serialize a Hash from JSON string
|
|
5
|
+
def self.load(string)
|
|
6
|
+
::JSON.load(string)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Serialize a Hash to JSON string
|
|
10
|
+
def self.dump(object, options = {})
|
|
11
|
+
::JSON.pretty_generate(object, options) + "\n"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Doggy
|
|
2
|
+
module Serializer
|
|
3
|
+
class Yaml
|
|
4
|
+
# De-serialize a Hash from YAML string
|
|
5
|
+
def self.load(string)
|
|
6
|
+
::YAML.load(string)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Serialize a Hash to YAML string
|
|
10
|
+
def self.dump(object, options = {})
|
|
11
|
+
::YAML.dump(object, options)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/doggy/worker.rb
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require 'thread'
|
|
2
|
+
require 'thread/pool'
|
|
3
|
+
|
|
4
|
+
Thread.abort_on_exception = true
|
|
5
|
+
|
|
6
|
+
module Doggy
|
|
7
|
+
class Worker
|
|
8
|
+
# Spawn 10 threads for HTTP requests.
|
|
9
|
+
CONCURRENT_STREAMS = 10
|
|
10
|
+
|
|
11
|
+
def initialize(options = {}, &runner)
|
|
12
|
+
@runner = runner
|
|
13
|
+
@threads = options.fetch(:threads)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def call(jobs)
|
|
17
|
+
results = []
|
|
18
|
+
pool = Thread::Pool.new(@threads)
|
|
19
|
+
tasks = jobs.map { |job|
|
|
20
|
+
pool.process {
|
|
21
|
+
results << [ job, @runner.call(job) ]
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
pool.shutdown
|
|
25
|
+
if task_with_errors = tasks.detect { |task| task.exception }
|
|
26
|
+
raise task_with_errors.exception
|
|
27
|
+
end
|
|
28
|
+
results
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: doggy
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Vlad Gorodetsky
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2015-09-09 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bundler
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.9'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.9'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rake
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '10.0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '10.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: thor
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0.19'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0.19'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: dogapi
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '1.17'
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '1.17'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: thread
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0.2'
|
|
76
|
+
type: :runtime
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0.2'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: ejson
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '1.0'
|
|
90
|
+
type: :runtime
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '1.0'
|
|
97
|
+
description: Syncs DataDog dashboards, alerts, screenboards, and monitors.
|
|
98
|
+
email:
|
|
99
|
+
- v@gor.io
|
|
100
|
+
executables:
|
|
101
|
+
- doggy
|
|
102
|
+
extensions: []
|
|
103
|
+
extra_rdoc_files: []
|
|
104
|
+
files:
|
|
105
|
+
- ".gitignore"
|
|
106
|
+
- Gemfile
|
|
107
|
+
- LICENSE.txt
|
|
108
|
+
- README.md
|
|
109
|
+
- Rakefile
|
|
110
|
+
- bin/doggy
|
|
111
|
+
- doggy.gemspec
|
|
112
|
+
- lib/doggy.rb
|
|
113
|
+
- lib/doggy/cli.rb
|
|
114
|
+
- lib/doggy/cli/create.rb
|
|
115
|
+
- lib/doggy/cli/delete.rb
|
|
116
|
+
- lib/doggy/cli/mute.rb
|
|
117
|
+
- lib/doggy/cli/pull.rb
|
|
118
|
+
- lib/doggy/cli/push.rb
|
|
119
|
+
- lib/doggy/cli/unmute.rb
|
|
120
|
+
- lib/doggy/cli/version.rb
|
|
121
|
+
- lib/doggy/client.rb
|
|
122
|
+
- lib/doggy/model/dash.rb
|
|
123
|
+
- lib/doggy/model/monitor.rb
|
|
124
|
+
- lib/doggy/model/screen.rb
|
|
125
|
+
- lib/doggy/serializer/json.rb
|
|
126
|
+
- lib/doggy/serializer/yaml.rb
|
|
127
|
+
- lib/doggy/version.rb
|
|
128
|
+
- lib/doggy/worker.rb
|
|
129
|
+
homepage: http://github.com/bai/doggy
|
|
130
|
+
licenses:
|
|
131
|
+
- MIT
|
|
132
|
+
metadata: {}
|
|
133
|
+
post_install_message:
|
|
134
|
+
rdoc_options: []
|
|
135
|
+
require_paths:
|
|
136
|
+
- lib
|
|
137
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
138
|
+
requirements:
|
|
139
|
+
- - ">="
|
|
140
|
+
- !ruby/object:Gem::Version
|
|
141
|
+
version: '0'
|
|
142
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
143
|
+
requirements:
|
|
144
|
+
- - ">="
|
|
145
|
+
- !ruby/object:Gem::Version
|
|
146
|
+
version: '0'
|
|
147
|
+
requirements: []
|
|
148
|
+
rubyforge_project:
|
|
149
|
+
rubygems_version: 2.4.8
|
|
150
|
+
signing_key:
|
|
151
|
+
specification_version: 4
|
|
152
|
+
summary: Syncs DataDog dashboards, alerts, screenboards, and monitors.
|
|
153
|
+
test_files: []
|