interferon 0.1.0 → 0.1.3
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/.rubocop.yml +4 -0
- data/.rubocop_todo.yml +83 -0
- data/.travis.yml +4 -1
- data/bin/interferon +10 -9
- data/interferon.gemspec +18 -17
- data/lib/interferon/alert.rb +4 -10
- data/lib/interferon/alert_dsl.rb +12 -7
- data/lib/interferon/destinations/datadog.rb +103 -103
- data/lib/interferon/group_sources/filesystem.rb +5 -5
- data/lib/interferon/host_sources/aws_dynamo.rb +17 -19
- data/lib/interferon/host_sources/aws_elasticache.rb +20 -22
- data/lib/interferon/host_sources/aws_rds.rb +33 -33
- data/lib/interferon/host_sources/optica.rb +12 -10
- data/lib/interferon/host_sources/optica_services.rb +17 -15
- data/lib/interferon/host_sources/test_host_source.rb +1 -1
- data/lib/interferon/loaders.rb +4 -5
- data/lib/interferon/logging.rb +2 -3
- data/lib/interferon/version.rb +1 -1
- data/lib/interferon/work_hours_helper.rb +5 -5
- data/lib/interferon.rb +79 -80
- data/script/pre-commit +15 -20
- data/spec/fixtures/loaders/host_sources/test_host_source.rb +1 -1
- data/spec/fixtures/loaders/test_sources/order_test_source.rb +1 -1
- data/spec/fixtures/loaders/test_sources/test_source.rb +1 -1
- data/spec/fixtures/loaders2/test_sources/order_test_source.rb +1 -1
- data/spec/fixtures/loaders2/test_sources/secondary_source.rb +1 -1
- data/spec/fixtures/loaders2/test_sources/test_source.rb +1 -2
- data/spec/helpers/logging_helper.rb +2 -2
- data/spec/helpers/mock_alert.rb +1 -1
- data/spec/helpers/optica_helper.rb +70 -70
- data/spec/lib/interferon/destinations/datadog_spec.rb +58 -59
- data/spec/lib/interferon/group_sources/filesystem_spec.rb +29 -24
- data/spec/lib/interferon/host_sources/optica_services_spec.rb +11 -9
- data/spec/lib/interferon/host_sources/optica_spec.rb +6 -3
- data/spec/lib/interferon/loaders_spec.rb +19 -15
- data/spec/lib/interferon_spec.rb +61 -59
- data/spec/lib/work_hours_helper_spec.rb +15 -15
- data/spec/spec_helper.rb +1 -1
- metadata +61 -65
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a9fbb5fdb83e9978e96990983e1c815fd18e7cdc
|
4
|
+
data.tar.gz: 0d5b080a2ad8dbdd8a16edd1bcae27aadfb6c366
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 542ec78306622d95e55f698f2c0b35b7f0a8365a8eb0f2f85d0faff851fd22f5ddaef5d5b5c2f8adf585f0a297d23f0684cf5293122073729bd326cd23dab530
|
7
|
+
data.tar.gz: 271d43056cc9be797fb8e4ae3f98d4370518481f71bcf4b1f0d749b09e1433e36666b9ca701439d4bc6167a9857a2c53977836131d7f5a55355e44fa6d140c9d
|
data/.rubocop.yml
ADDED
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config --exclude-limit 50000`
|
3
|
+
# on 2017-06-05 13:58:15 -0700 using RuboCop version 0.41.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
|
+
Metrics/AbcSize:
|
10
|
+
Enabled: false
|
11
|
+
|
12
|
+
Metrics/ClassLength:
|
13
|
+
Enabled: false
|
14
|
+
|
15
|
+
Metrics/CyclomaticComplexity:
|
16
|
+
Enabled: false
|
17
|
+
Max: 6
|
18
|
+
|
19
|
+
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes.
|
20
|
+
# URISchemes: http, https
|
21
|
+
Metrics/LineLength:
|
22
|
+
Max: 100
|
23
|
+
AllowURI: true
|
24
|
+
|
25
|
+
# Configuration parameters: CountComments.
|
26
|
+
Metrics/MethodLength:
|
27
|
+
Enabled: false
|
28
|
+
|
29
|
+
# Configuration parameters: CountKeywordArgs.
|
30
|
+
Metrics/ParameterLists:
|
31
|
+
Enabled: false
|
32
|
+
Max: 5
|
33
|
+
|
34
|
+
Metrics/PerceivedComplexity:
|
35
|
+
Enabled: false
|
36
|
+
Max: 7
|
37
|
+
|
38
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
39
|
+
# SupportedStyles: nested, compact
|
40
|
+
Style/ClassAndModuleChildren:
|
41
|
+
Enabled: false
|
42
|
+
EnforcedStyle: nested
|
43
|
+
SupportedStyles:
|
44
|
+
- nested
|
45
|
+
- compact
|
46
|
+
|
47
|
+
Style/Documentation:
|
48
|
+
Enabled: false
|
49
|
+
|
50
|
+
Style/DoubleNegation:
|
51
|
+
Enabled: false
|
52
|
+
|
53
|
+
# Offense count: 3
|
54
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
55
|
+
# SupportedStyles: format, sprintf, percent
|
56
|
+
Style/FormatString:
|
57
|
+
Enabled: false
|
58
|
+
EnforcedStyle: format
|
59
|
+
SupportedStyles:
|
60
|
+
- format
|
61
|
+
- sprintf
|
62
|
+
- percent
|
63
|
+
|
64
|
+
# Offense count: 3
|
65
|
+
# Configuration parameters: NamePrefix, NamePrefixBlacklist, NameWhitelist.
|
66
|
+
# NamePrefix: is_, has_, have_
|
67
|
+
# NamePrefixBlacklist: is_, has_, have_
|
68
|
+
# NameWhitelist: is_a?
|
69
|
+
Style/PredicateName:
|
70
|
+
Enabled: false
|
71
|
+
NamePrefix:
|
72
|
+
- is_
|
73
|
+
- has_
|
74
|
+
- have_
|
75
|
+
NamePrefixBlacklist:
|
76
|
+
- is_
|
77
|
+
- has_
|
78
|
+
- have_
|
79
|
+
|
80
|
+
|
81
|
+
Style/TrailingCommaInLiteral:
|
82
|
+
Enabled: true
|
83
|
+
EnforcedStyleForMultiline: consistent_comma
|
data/.travis.yml
CHANGED
data/bin/interferon
CHANGED
@@ -4,13 +4,13 @@ require 'yaml'
|
|
4
4
|
require 'optparse'
|
5
5
|
require 'interferon'
|
6
6
|
|
7
|
-
options={}
|
7
|
+
options = {}
|
8
8
|
|
9
9
|
# set command line options
|
10
10
|
optparse = OptionParser.new do |opts|
|
11
|
-
opts.banner = %
|
11
|
+
opts.banner = %(Usage: interferon --config /path/to/interferon/config)
|
12
12
|
|
13
|
-
opts.on('-c config','--config config', String, 'Path to interferon config') do |key,
|
13
|
+
opts.on('-c config', '--config config', String, 'Path to interferon config') do |key, _value|
|
14
14
|
options[:config] = key
|
15
15
|
end
|
16
16
|
|
@@ -26,7 +26,7 @@ end
|
|
26
26
|
|
27
27
|
def parseconfig(filename)
|
28
28
|
begin
|
29
|
-
c = YAML
|
29
|
+
c = YAML.parse(File.read(filename))
|
30
30
|
rescue Errno::ENOENT => e
|
31
31
|
raise ArgumentError, "config file does not exist:\n#{e.inspect}"
|
32
32
|
rescue Errno::EACCES => e
|
@@ -34,7 +34,7 @@ def parseconfig(filename)
|
|
34
34
|
rescue YAML::SyntaxError => e
|
35
35
|
raise "config file #{filename} contains invalid YAML:\n#{e.inspect}"
|
36
36
|
end
|
37
|
-
|
37
|
+
c.to_ruby
|
38
38
|
end
|
39
39
|
|
40
40
|
# parse command line arguments
|
@@ -46,8 +46,8 @@ end
|
|
46
46
|
|
47
47
|
# validate that required options are present
|
48
48
|
config = parseconfig(options[:config])
|
49
|
-
%w
|
50
|
-
unless config.include?(req) && !
|
49
|
+
%w(alerts_repo_path host_sources destinations).each do |req|
|
50
|
+
unless config.include?(req) && !config[req].empty?
|
51
51
|
puts "config file has no #{req} defined; exiting"
|
52
52
|
exit 2
|
53
53
|
end
|
@@ -59,8 +59,9 @@ a = Interferon::Interferon.new(
|
|
59
59
|
config['alerts_repo_path'],
|
60
60
|
config['group_sources'] || {},
|
61
61
|
config['host_sources'],
|
62
|
-
config['destinations']
|
62
|
+
config['destinations']
|
63
|
+
)
|
63
64
|
|
64
65
|
a.run(options[:dry_run])
|
65
66
|
|
66
|
-
puts
|
67
|
+
puts 'interferon signaling complete!'
|
data/interferon.gemspec
CHANGED
@@ -4,27 +4,28 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'interferon/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |gem|
|
7
|
-
gem.name =
|
7
|
+
gem.name = 'interferon'
|
8
8
|
gem.version = Interferon::VERSION
|
9
|
-
gem.authors = [
|
10
|
-
gem.email = [
|
11
|
-
gem.description = %
|
12
|
-
gem.summary = %
|
13
|
-
gem.homepage =
|
9
|
+
gem.authors = ['Igor Serebryany', 'Jimmy Ngo']
|
10
|
+
gem.email = ['igor.serebryany@airbnb.com', 'jimmy.ngo@airbnb.com']
|
11
|
+
gem.description = %(: Store metrics alerts in code!)
|
12
|
+
gem.summary = %(: Store metrics alerts in code!)
|
13
|
+
gem.homepage = 'https://www.github.com/airbnb/interferon'
|
14
14
|
gem.licenses = ['MIT']
|
15
15
|
|
16
|
-
gem.files = `git ls-files`.split(
|
17
|
-
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
16
|
+
gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
17
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
18
18
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
19
|
|
20
|
-
gem.add_runtime_dependency
|
21
|
-
gem.add_runtime_dependency
|
22
|
-
gem.add_runtime_dependency
|
23
|
-
gem.add_runtime_dependency
|
24
|
-
gem.add_runtime_dependency
|
25
|
-
gem.add_runtime_dependency
|
26
|
-
gem.add_runtime_dependency
|
20
|
+
gem.add_runtime_dependency 'dogapi', '~> 1.27', '>= 1.27.0'
|
21
|
+
gem.add_runtime_dependency 'aws-sdk', '~> 1.35', '>= 1.35.1'
|
22
|
+
gem.add_runtime_dependency 'dogstatsd-ruby', '~> 1.4', '>= 1.4.1'
|
23
|
+
gem.add_runtime_dependency 'diffy', '~> 3.1.0', '>= 3.1.0'
|
24
|
+
gem.add_runtime_dependency 'parallel', '~> 1.9', '>= 1.9.0'
|
25
|
+
gem.add_runtime_dependency 'nokogiri', '< 1.7.0'
|
26
|
+
gem.add_runtime_dependency 'tzinfo', '~> 1.2.2', '>= 1.2.2'
|
27
27
|
|
28
|
-
gem.add_development_dependency
|
29
|
-
gem.add_development_dependency
|
28
|
+
gem.add_development_dependency 'rspec', '~> 3.2'
|
29
|
+
gem.add_development_dependency 'pry', '~> 0.10'
|
30
|
+
gem.add_development_dependency 'rubocop', '0.41.2'
|
30
31
|
end
|
data/lib/interferon/alert.rb
CHANGED
@@ -23,27 +23,21 @@ module Interferon
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def change_name(name)
|
26
|
-
unless @dsl
|
27
|
-
raise "This alert has not yet been evaluated"
|
28
|
-
end
|
26
|
+
raise 'This alert has not yet been evaluated' unless @dsl
|
29
27
|
|
30
28
|
@dsl.name(name)
|
31
29
|
end
|
32
30
|
|
33
31
|
def silence
|
34
|
-
unless @dsl
|
35
|
-
raise "This alert has not yet been evaluated"
|
36
|
-
end
|
32
|
+
raise 'This alert has not yet been evaluated' unless @dsl
|
37
33
|
|
38
34
|
@dsl.silenced(true)
|
39
35
|
end
|
40
36
|
|
41
37
|
def [](attr)
|
42
|
-
unless @dsl
|
43
|
-
raise "This alert has not yet been evaluated"
|
44
|
-
end
|
38
|
+
raise 'This alert has not yet been evaluated' unless @dsl
|
45
39
|
|
46
|
-
|
40
|
+
@dsl.send(attr)
|
47
41
|
end
|
48
42
|
end
|
49
43
|
end
|
data/lib/interferon/alert_dsl.rb
CHANGED
@@ -6,15 +6,16 @@ module Interferon
|
|
6
6
|
@hostinfo = hostinfo
|
7
7
|
end
|
8
8
|
|
9
|
-
def method_missing(meth, *
|
9
|
+
def method_missing(meth, *_args)
|
10
10
|
raise ArgumentError, "No such alerts field '#{meth}'"
|
11
11
|
end
|
12
12
|
|
13
13
|
def [](arg)
|
14
|
-
|
14
|
+
send(arg)
|
15
15
|
end
|
16
16
|
|
17
17
|
private
|
18
|
+
|
18
19
|
def get_or_set(field, val, block, default)
|
19
20
|
if val.nil? && block.nil?
|
20
21
|
f = instance_variable_get(field)
|
@@ -33,7 +34,7 @@ module Interferon
|
|
33
34
|
include DSLMixin
|
34
35
|
|
35
36
|
def name(v = nil, &block)
|
36
|
-
get_or_set(:@name, v, block, ''
|
37
|
+
get_or_set(:@name, v, block, '', &:strip)
|
37
38
|
end
|
38
39
|
|
39
40
|
def message(v = nil, &block)
|
@@ -53,7 +54,7 @@ module Interferon
|
|
53
54
|
if val.is_a? Hash
|
54
55
|
val
|
55
56
|
elsif val == true
|
56
|
-
{
|
57
|
+
{ '*' => nil }
|
57
58
|
else
|
58
59
|
{}
|
59
60
|
end
|
@@ -90,15 +91,19 @@ module Interferon
|
|
90
91
|
get_or_set(:@thresholds, v, block, nil)
|
91
92
|
end
|
92
93
|
|
94
|
+
def evaluation_delay(v = nil, &block)
|
95
|
+
get_or_set(:@evaluation_delay, v, block, nil)
|
96
|
+
end
|
97
|
+
|
93
98
|
def require_full_window(v = nil, &block)
|
94
99
|
get_or_set(:@require_full_window, v, block, nil)
|
95
100
|
end
|
96
101
|
|
97
|
-
def notify(
|
102
|
+
def notify(_v = nil)
|
98
103
|
@notify ||= NotifyDSL.new(@hostinfo)
|
99
104
|
end
|
100
105
|
|
101
|
-
def metric(
|
106
|
+
def metric(_v = nil)
|
102
107
|
@metric ||= MetricDSL.new(@hostinfo)
|
103
108
|
end
|
104
109
|
end
|
@@ -123,7 +128,7 @@ module Interferon
|
|
123
128
|
include DSLMixin
|
124
129
|
|
125
130
|
def datadog_query(v = nil, &block)
|
126
|
-
get_or_set(:@datadog_query, v, block, ''
|
131
|
+
get_or_set(:@datadog_query, v, block, '', &:strip)
|
127
132
|
end
|
128
133
|
end
|
129
134
|
end
|
@@ -11,10 +11,10 @@ module Interferon::Destinations
|
|
11
11
|
include ::Interferon::Logging
|
12
12
|
|
13
13
|
attr_accessor :concurrency
|
14
|
-
ALERT_KEY = 'This alert was created via the alerts framework'
|
14
|
+
ALERT_KEY = 'This alert was created via the alerts framework'.freeze
|
15
15
|
|
16
16
|
def initialize(options)
|
17
|
-
%w
|
17
|
+
%w(app_key api_key).each do |req|
|
18
18
|
unless options[req]
|
19
19
|
raise ArgumentError, "missing required argument #{req}"
|
20
20
|
end
|
@@ -48,17 +48,17 @@ module Interferon::Destinations
|
|
48
48
|
@retries = options['retries'] || 3
|
49
49
|
|
50
50
|
@stats = {
|
51
|
-
:
|
52
|
-
:
|
53
|
-
:
|
54
|
-
:
|
55
|
-
:
|
56
|
-
:
|
57
|
-
:
|
58
|
-
:
|
59
|
-
:
|
60
|
-
:
|
61
|
-
:
|
51
|
+
alerts_created: 0,
|
52
|
+
alerts_to_be_created: 0,
|
53
|
+
alerts_updated: 0,
|
54
|
+
alerts_to_be_updated: 0,
|
55
|
+
alerts_deleted: 0,
|
56
|
+
alerts_to_be_deleted: 0,
|
57
|
+
alerts_silenced: 0,
|
58
|
+
api_successes: 0,
|
59
|
+
api_client_errors: 0,
|
60
|
+
api_unknown_errors: 0,
|
61
|
+
manually_created_alerts: 0,
|
62
62
|
}
|
63
63
|
end
|
64
64
|
|
@@ -67,10 +67,10 @@ module Interferon::Destinations
|
|
67
67
|
end
|
68
68
|
|
69
69
|
def generate_message(message, people)
|
70
|
-
[message, ALERT_KEY, people.map{ |p| "@#{p}" }].flatten.join("\n")
|
70
|
+
[message, ALERT_KEY, people.map { |p| "@#{p}" }].flatten.join("\n")
|
71
71
|
end
|
72
72
|
|
73
|
-
def
|
73
|
+
def fetch_existing_alerts
|
74
74
|
alerts = Queue.new
|
75
75
|
has_more = true
|
76
76
|
|
@@ -84,26 +84,24 @@ module Interferon::Destinations
|
|
84
84
|
log.info("Failed to retrieve existing alerts from datadog. #{code}: #{resp[1].inspect}")
|
85
85
|
else
|
86
86
|
alerts_page = resp[1]
|
87
|
-
if alerts_page.length < @page_size
|
88
|
-
has_more = false
|
89
|
-
end
|
87
|
+
has_more = false if alerts_page.length < @page_size
|
90
88
|
alerts_page.map { |alert| alerts.push(alert) }
|
91
89
|
successful = true
|
92
90
|
break
|
93
91
|
end
|
94
92
|
end
|
95
93
|
|
96
|
-
|
94
|
+
unless successful
|
97
95
|
# Out of retries
|
98
|
-
raise
|
96
|
+
raise 'Retries exceeded for fetching data from datadog.'
|
99
97
|
end
|
100
98
|
end
|
101
|
-
alerts.size
|
99
|
+
Array.new(alerts.size) { alerts.pop }
|
102
100
|
end
|
103
101
|
|
104
102
|
def existing_alerts
|
105
103
|
unless @existing_alerts
|
106
|
-
alerts =
|
104
|
+
alerts = fetch_existing_alerts
|
107
105
|
|
108
106
|
# key alerts by name
|
109
107
|
@existing_alerts = {}
|
@@ -119,9 +117,9 @@ module Interferon::Destinations
|
|
119
117
|
|
120
118
|
# count how many are manually created
|
121
119
|
@stats[:manually_created_alerts] = \
|
122
|
-
@existing_alerts.reject{|
|
120
|
+
@existing_alerts.reject { |_n, a| a['message'].include?(ALERT_KEY) }.length
|
123
121
|
|
124
|
-
log.info
|
122
|
+
log.info 'datadog: found %d existing alerts; %d were manually created' % [
|
125
123
|
@existing_alerts.length,
|
126
124
|
@stats[:manually_created_alerts],
|
127
125
|
]
|
@@ -138,18 +136,22 @@ module Interferon::Destinations
|
|
138
136
|
|
139
137
|
# create the hash of options to send to datadog
|
140
138
|
alert_options = {
|
141
|
-
:
|
142
|
-
:
|
143
|
-
:
|
144
|
-
:
|
145
|
-
:
|
139
|
+
notify_audit: alert['notify']['audit'],
|
140
|
+
notify_no_data: alert['notify_no_data'],
|
141
|
+
no_data_timeframe: alert['no_data_timeframe'],
|
142
|
+
silenced: alert['silenced'],
|
143
|
+
timeout_h: alert['timeout_h'],
|
146
144
|
}
|
147
145
|
|
148
|
-
|
146
|
+
unless alert['evaluation_delay'].nil?
|
147
|
+
alert_options[:evaluation_delay] = alert['evaluation_delay']
|
148
|
+
end
|
149
|
+
|
150
|
+
unless alert['require_full_window'].nil?
|
149
151
|
alert_options[:require_full_window] = alert['require_full_window']
|
150
152
|
end
|
151
153
|
|
152
|
-
|
154
|
+
unless alert['thresholds'].nil?
|
153
155
|
alert_options[:thresholds] = alert['thresholds']
|
154
156
|
end
|
155
157
|
|
@@ -171,11 +173,11 @@ module Interferon::Destinations
|
|
171
173
|
log_datadog_response_code(resp, code, action, alert)
|
172
174
|
|
173
175
|
# assume this was a success
|
174
|
-
|
176
|
+
unless code >= 400 || code == -1
|
175
177
|
# assume this was a success
|
176
178
|
@stats[:alerts_created] += 1 if action == :creating
|
177
179
|
@stats[:alerts_updated] += 1 if action == :updating
|
178
|
-
@stats[:alerts_silenced] += 1
|
180
|
+
@stats[:alerts_silenced] += 1 unless alert_options[:silenced].empty?
|
179
181
|
end
|
180
182
|
|
181
183
|
id = resp[1].nil? ? nil : [resp[1]['id']]
|
@@ -198,17 +200,17 @@ EOM
|
|
198
200
|
@dog.monitor(
|
199
201
|
alert['monitor_type'],
|
200
202
|
datadog_query,
|
201
|
-
:
|
202
|
-
:
|
203
|
-
:
|
203
|
+
name: alert['name'],
|
204
|
+
message: @dry_run ? generate_message(alert, []) : message,
|
205
|
+
options: alert_options
|
204
206
|
)
|
205
207
|
end
|
206
208
|
|
207
209
|
def update_datadog_alert(alert, datadog_query, message, alert_options, existing_alert)
|
208
|
-
|
209
|
-
|
210
|
+
@stats[:alerts_to_be_updated] += 1
|
211
|
+
id = existing_alert['id'][0]
|
210
212
|
|
211
|
-
|
213
|
+
new_alert_text = <<-EOM.strip
|
212
214
|
Query:
|
213
215
|
#{datadog_query.strip}
|
214
216
|
Message:
|
@@ -216,7 +218,7 @@ Message:
|
|
216
218
|
Options:
|
217
219
|
#{alert_options}
|
218
220
|
EOM
|
219
|
-
|
221
|
+
existing_alert_text = <<-EOM.strip
|
220
222
|
Query:
|
221
223
|
#{existing_alert['query'].strip}
|
222
224
|
Message:
|
@@ -224,63 +226,60 @@ Message:
|
|
224
226
|
Options:
|
225
227
|
#{alert_options}
|
226
228
|
EOM
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
229
|
+
diff = Diffy::Diff.new(existing_alert_text, new_alert_text, context: 1)
|
230
|
+
log.info("updating existing alert #{id} (#{alert['name']}):\n#{diff}")
|
231
|
+
|
232
|
+
if @dry_run
|
233
|
+
resp = @dog.monitor(
|
234
|
+
alert['monitor_type'],
|
235
|
+
datadog_query,
|
236
|
+
name: alert['name'],
|
237
|
+
message: generate_message(alert, []),
|
238
|
+
options: alert_options
|
239
|
+
)
|
240
|
+
elsif alert['monitor_type'] == existing_alert['type']
|
241
|
+
resp = @dog.update_monitor(
|
242
|
+
id,
|
243
|
+
datadog_query,
|
244
|
+
name: alert['name'],
|
245
|
+
message: message,
|
246
|
+
options: alert_options
|
247
|
+
)
|
248
|
+
|
249
|
+
# Unmute existing alerts that have been unsilenced.
|
250
|
+
# Datadog does not allow updates to silencing via the update_alert API call.
|
251
|
+
if !existing_alert['options']['silenced'].empty? && alert_options[:silenced].empty?
|
252
|
+
@dog.unmute_monitor(id)
|
253
|
+
end
|
254
|
+
else
|
255
|
+
# Need to recreate alert with new monitor type
|
256
|
+
resp = @dog.delete_monitor(id)
|
257
|
+
code = resp[0].to_i
|
258
|
+
unless code >= 300 || code == -1
|
231
259
|
resp = @dog.monitor(
|
232
260
|
alert['monitor_type'],
|
233
261
|
datadog_query,
|
234
|
-
:
|
235
|
-
:message
|
236
|
-
:
|
262
|
+
name: alert['name'],
|
263
|
+
message: message,
|
264
|
+
options: alert_options
|
237
265
|
)
|
238
|
-
else
|
239
|
-
if alert['monitor_type'] == existing_alert['type']
|
240
|
-
resp = @dog.update_monitor(
|
241
|
-
id,
|
242
|
-
datadog_query,
|
243
|
-
:name => alert['name'],
|
244
|
-
:message => message,
|
245
|
-
:options => alert_options,
|
246
|
-
)
|
247
|
-
|
248
|
-
# Unmute existing alerts that have been unsilenced.
|
249
|
-
# Datadog does not allow updates to silencing via the update_alert API call.
|
250
|
-
if !existing_alert['options']['silenced'].empty? && alert_options[:silenced].empty?
|
251
|
-
@dog.unmute_monitor(id)
|
252
|
-
end
|
253
|
-
else
|
254
|
-
# Need to recreate alert with new monitor type
|
255
|
-
resp = @dog.delete_monitor(id)
|
256
|
-
code = resp[0].to_i
|
257
|
-
if !(code >= 300 || code == -1)
|
258
|
-
resp = @dog.monitor(
|
259
|
-
alert['monitor_type'],
|
260
|
-
datadog_query,
|
261
|
-
:name => alert['name'],
|
262
|
-
:message => message,
|
263
|
-
:options => alert_options,
|
264
|
-
)
|
265
|
-
end
|
266
|
-
end
|
267
266
|
end
|
268
|
-
|
267
|
+
end
|
268
|
+
resp
|
269
269
|
end
|
270
270
|
|
271
|
-
|
272
271
|
def remove_alert(alert)
|
273
272
|
if alert['message'].include?(ALERT_KEY)
|
274
273
|
@stats[:alerts_to_be_deleted] += 1
|
275
274
|
log.info("deleting alert: #{alert['name']}")
|
276
275
|
|
277
|
-
|
276
|
+
unless @dry_run
|
278
277
|
alert['id'].each do |alert_id|
|
279
278
|
resp = @dog.delete_monitor(alert_id)
|
280
279
|
code = resp[0].to_i
|
281
280
|
log_datadog_response_code(resp, code, :deleting)
|
282
281
|
|
283
|
-
|
282
|
+
unless code >= 300 || code == -1
|
284
283
|
# assume this was a success
|
285
284
|
@stats[:alerts_deleted] += 1
|
286
285
|
end
|
@@ -292,18 +291,20 @@ EOM
|
|
292
291
|
end
|
293
292
|
|
294
293
|
def report_stats
|
295
|
-
@stats.each do |k,v|
|
294
|
+
@stats.each do |k, v|
|
296
295
|
statsd.gauge("datadog.#{k}", v)
|
297
296
|
end
|
298
297
|
|
299
|
-
log.info
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
298
|
+
log.info(
|
299
|
+
'datadog: successfully created (%d/%d), updated (%d/%d), and deleted (%d/%d) alerts' % [
|
300
|
+
@stats[:alerts_created],
|
301
|
+
@stats[:alerts_to_be_created],
|
302
|
+
@stats[:alerts_updated],
|
303
|
+
@stats[:alerts_to_be_updated],
|
304
|
+
@stats[:alerts_deleted],
|
305
|
+
@stats[:alerts_to_be_deleted],
|
306
|
+
]
|
307
|
+
)
|
307
308
|
end
|
308
309
|
|
309
310
|
def remove_alert_by_id(alert_id)
|
@@ -314,7 +315,7 @@ EOM
|
|
314
315
|
log_datadog_response_code(resp, code, :deleting)
|
315
316
|
end
|
316
317
|
|
317
|
-
def log_datadog_response_code(resp, code, action, alert=nil)
|
318
|
+
def log_datadog_response_code(resp, code, action, alert = nil)
|
318
319
|
# log whenever we've encountered errors
|
319
320
|
if code != 200 && !alert.nil?
|
320
321
|
api_errors << "#{code} on alert #{alert['name']}"
|
@@ -323,10 +324,10 @@ EOM
|
|
323
324
|
# client error
|
324
325
|
if code == 400
|
325
326
|
@stats[:api_client_errors] += 1
|
326
|
-
|
327
|
-
statsd.gauge('datadog.api.unknown_error', 0, :
|
328
|
-
statsd.gauge('datadog.api.client_error', 1, :
|
329
|
-
statsd.gauge('datadog.api.success', 0, :
|
327
|
+
unless alert.nil?
|
328
|
+
statsd.gauge('datadog.api.unknown_error', 0, tags: ["alert:#{alert}"])
|
329
|
+
statsd.gauge('datadog.api.client_error', 1, tags: ["alert:#{alert}"])
|
330
|
+
statsd.gauge('datadog.api.success', 0, tags: ["alert:#{alert}"])
|
330
331
|
log.error("client error while #{action} alert '#{alert['name']}';" \
|
331
332
|
" query was '#{alert['metric']['datadog_query']}'" \
|
332
333
|
" response was #{resp[0]}:'#{resp[1].inspect}'")
|
@@ -335,23 +336,22 @@ EOM
|
|
335
336
|
# unknown (prob. datadog) error:
|
336
337
|
elsif code > 400 || code == -1
|
337
338
|
@stats[:api_unknown_errors] += 1
|
338
|
-
|
339
|
-
statsd.gauge('datadog.api.unknown_error', 1, :
|
340
|
-
statsd.gauge('datadog.api.client_error', 0, :
|
341
|
-
statsd.gauge('datadog.api.success', 0, :
|
339
|
+
unless alert.nil?
|
340
|
+
statsd.gauge('datadog.api.unknown_error', 1, tags: ["alert:#{alert}"])
|
341
|
+
statsd.gauge('datadog.api.client_error', 0, tags: ["alert:#{alert}"])
|
342
|
+
statsd.gauge('datadog.api.success', 0, tags: ["alert:#{alert}"])
|
342
343
|
log.error("unknown error while #{action} alert '#{alert['name']}':" \
|
343
344
|
" query was '#{alert['metric']['datadog_query']}'" \
|
344
345
|
" response was #{resp[0]}:'#{resp[1].inspect}'")
|
345
346
|
end
|
346
347
|
else
|
347
348
|
@stats[:api_successes] += 1
|
348
|
-
|
349
|
-
statsd.gauge('datadog.api.unknown_error', 0, :
|
350
|
-
statsd.gauge('datadog.api.client_error', 0, :
|
351
|
-
statsd.gauge('datadog.api.success', 1, :
|
349
|
+
unless alert.nil?
|
350
|
+
statsd.gauge('datadog.api.unknown_error', 0, tags: ["alert:#{alert}"])
|
351
|
+
statsd.gauge('datadog.api.client_error', 0, tags: ["alert:#{alert}"])
|
352
|
+
statsd.gauge('datadog.api.success', 1, tags: ["alert:#{alert}"])
|
352
353
|
end
|
353
354
|
end
|
354
355
|
end
|
355
|
-
|
356
356
|
end
|
357
357
|
end
|