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