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
@@ -3,7 +3,7 @@ include ::Interferon::Logging
|
|
3
3
|
module Interferon::GroupSources
|
4
4
|
class Filesystem
|
5
5
|
def initialize(options)
|
6
|
-
raise ArgumentError,
|
6
|
+
raise ArgumentError, 'missing paths for loading groups from filesystem' \
|
7
7
|
unless options['paths']
|
8
8
|
|
9
9
|
@paths = options['paths']
|
@@ -15,14 +15,14 @@ module Interferon::GroupSources
|
|
15
15
|
|
16
16
|
@paths.each do |path|
|
17
17
|
path = File.expand_path(path)
|
18
|
-
unless Dir.
|
18
|
+
unless Dir.exist?(path)
|
19
19
|
log.warn "no such directory #{path} for reading group files"
|
20
20
|
next
|
21
21
|
end
|
22
22
|
|
23
23
|
Dir.glob(File.join(path, '*.{json,yml,yaml}')).each do |group_file|
|
24
24
|
begin
|
25
|
-
group = YAML
|
25
|
+
group = YAML.parse(File.read(group_file))
|
26
26
|
rescue YAML::SyntaxError => e
|
27
27
|
log.error "syntax error in group file #{group_file}: #{e}"
|
28
28
|
rescue StandardError => e
|
@@ -32,7 +32,7 @@ module Interferon::GroupSources
|
|
32
32
|
if group['people']
|
33
33
|
groups[group['name']] = group['people'] || []
|
34
34
|
elsif group['alias_for']
|
35
|
-
aliases[group['name']] = {:
|
35
|
+
aliases[group['name']] = { group: group['alias_for'], group_file: group_file }
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
@@ -48,7 +48,7 @@ module Interferon::GroupSources
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
-
|
51
|
+
groups
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
@@ -3,46 +3,44 @@ require 'aws'
|
|
3
3
|
module Interferon::HostSources
|
4
4
|
class AwsDynamo
|
5
5
|
def initialize(options)
|
6
|
-
missing = %w
|
6
|
+
missing = %w(access_key_id secret_access_key).reject { |r| options.key?(r) }
|
7
7
|
|
8
|
-
AWS.config(
|
9
|
-
|
10
|
-
:secret_access_key => options['secret_access_key']
|
11
|
-
}) if missing.empty?
|
8
|
+
AWS.config(access_key_id: options['access_key_id'],
|
9
|
+
secret_access_key: options['secret_access_key']) if missing.empty?
|
12
10
|
|
13
11
|
# initialize a list of regions to check
|
14
|
-
if options['regions'] && !options['regions'].empty?
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
12
|
+
@regions = if options['regions'] && !options['regions'].empty?
|
13
|
+
options['regions']
|
14
|
+
else
|
15
|
+
AWS.regions.map(&:name)
|
16
|
+
end
|
19
17
|
end
|
20
18
|
|
21
19
|
def list_hosts
|
22
20
|
hosts = []
|
23
21
|
|
24
22
|
@regions.each do |region|
|
25
|
-
client = AWS::DynamoDB.new(:
|
23
|
+
client = AWS::DynamoDB.new(region: region)
|
26
24
|
|
27
25
|
AWS.memoize do
|
28
26
|
client.tables.each do |table|
|
29
27
|
hosts << {
|
30
|
-
:
|
31
|
-
:
|
32
|
-
:
|
28
|
+
source: 'aws_dynamo',
|
29
|
+
region: region,
|
30
|
+
table_name: table.name,
|
33
31
|
|
34
|
-
:
|
35
|
-
:
|
32
|
+
read_capacity: table.read_capacity_units,
|
33
|
+
write_capacity: table.write_capacity_units,
|
36
34
|
|
37
35
|
# dynamodb does not support tagging
|
38
|
-
:
|
39
|
-
:
|
36
|
+
owners: [],
|
37
|
+
owner_groups: [],
|
40
38
|
}
|
41
39
|
end
|
42
40
|
end
|
43
41
|
end
|
44
42
|
|
45
|
-
|
43
|
+
hosts
|
46
44
|
end
|
47
45
|
end
|
48
46
|
end
|
@@ -3,19 +3,17 @@ require 'aws'
|
|
3
3
|
module Interferon::HostSources
|
4
4
|
class AwsElasticache
|
5
5
|
def initialize(options)
|
6
|
-
missing = %w
|
6
|
+
missing = %w(access_key_id secret_access_key).reject { |r| options.key?(r) }
|
7
7
|
|
8
|
-
AWS.config(
|
9
|
-
|
10
|
-
:secret_access_key => options['secret_access_key']
|
11
|
-
}) if missing.empty?
|
8
|
+
AWS.config(access_key_id: options['access_key_id'],
|
9
|
+
secret_access_key: options['secret_access_key']) if missing.empty?
|
12
10
|
|
13
11
|
# initialize a list of regions to check
|
14
|
-
if options['regions'] && !options['regions'].empty?
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
12
|
+
@regions = if options['regions'] && !options['regions'].empty?
|
13
|
+
options['regions']
|
14
|
+
else
|
15
|
+
AWS.regions.map(&:name)
|
16
|
+
end
|
19
17
|
end
|
20
18
|
|
21
19
|
def list_hosts
|
@@ -23,12 +21,12 @@ module Interferon::HostSources
|
|
23
21
|
|
24
22
|
@regions.each do |region|
|
25
23
|
clusters = []
|
26
|
-
client = AWS::ElastiCache.new(:
|
24
|
+
client = AWS::ElastiCache.new(region: region).client
|
27
25
|
|
28
26
|
AWS.memoize do
|
29
27
|
# read the list of cache clusters; we have to do our own pagination
|
30
28
|
clusters = []
|
31
|
-
options = {:
|
29
|
+
options = { show_cache_node_info: true }
|
32
30
|
loop do
|
33
31
|
r = client.describe_cache_clusters(options)
|
34
32
|
clusters += r.data[:cache_clusters]
|
@@ -41,26 +39,26 @@ module Interferon::HostSources
|
|
41
39
|
clusters.each do |cluster|
|
42
40
|
cluster[:cache_nodes].each do |node|
|
43
41
|
hosts << {
|
44
|
-
:
|
45
|
-
:
|
42
|
+
source: 'aws_elasticache',
|
43
|
+
region: region,
|
46
44
|
|
47
|
-
:
|
48
|
-
:
|
49
|
-
:
|
50
|
-
:
|
45
|
+
cluster_id: cluster[:cache_cluster_id],
|
46
|
+
cluster_status: cluster[:cache_cluster_status],
|
47
|
+
node_type: cluster[:cache_node_type],
|
48
|
+
peer_nodes: cluster[:num_cache_nodes],
|
51
49
|
|
52
|
-
:
|
50
|
+
node_status: node[:cache_node_status],
|
53
51
|
|
54
52
|
# elasticache does not support tagging
|
55
|
-
:
|
56
|
-
:
|
53
|
+
owners: [],
|
54
|
+
owner_groups: [],
|
57
55
|
}
|
58
56
|
end
|
59
57
|
end
|
60
58
|
end
|
61
59
|
end
|
62
60
|
|
63
|
-
|
61
|
+
hosts
|
64
62
|
end
|
65
63
|
end
|
66
64
|
end
|
@@ -3,72 +3,71 @@ require 'aws'
|
|
3
3
|
module Interferon::HostSources
|
4
4
|
class AwsRds
|
5
5
|
def initialize(options)
|
6
|
-
missing = %w
|
6
|
+
missing = %w(access_key_id secret_access_key).reject { |r| options.key?(r) }
|
7
7
|
|
8
|
-
AWS.config(
|
9
|
-
|
10
|
-
:secret_access_key => options['secret_access_key']
|
11
|
-
}) if missing.empty?
|
8
|
+
AWS.config(access_key_id: options['access_key_id'],
|
9
|
+
secret_access_key: options['secret_access_key']) if missing.empty?
|
12
10
|
|
13
11
|
# initialize a list of regions to check
|
14
|
-
if options['regions'] && !options['regions'].empty?
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
12
|
+
@regions = if options['regions'] && !options['regions'].empty?
|
13
|
+
options['regions']
|
14
|
+
else
|
15
|
+
AWS.regions.map(&:name)
|
16
|
+
end
|
19
17
|
end
|
20
18
|
|
21
19
|
def list_hosts
|
22
20
|
hosts = []
|
23
21
|
|
24
22
|
@regions.each do |region|
|
25
|
-
rds = AWS::RDS.new(:
|
23
|
+
rds = AWS::RDS.new(region: region)
|
26
24
|
|
27
25
|
AWS.memoize do
|
28
26
|
rds.instances.each do |instance|
|
29
27
|
# get the tags for the instance
|
30
28
|
arn = arn(region, instance.id)
|
31
|
-
tag_list = rds.client.list_tags_for_resource(:
|
32
|
-
tags = Hash[
|
29
|
+
tag_list = rds.client.list_tags_for_resource(resource_name: arn)[:tag_list]
|
30
|
+
tags = Hash[tag_list.map { |h| [h[:key], h[:value]] }]
|
33
31
|
|
34
32
|
tags['owners'] ||= ''
|
35
33
|
tags['owner_groups'] ||= ''
|
36
34
|
|
37
35
|
# build the host data for this instance
|
38
36
|
hosts << {
|
39
|
-
:
|
40
|
-
:
|
41
|
-
:
|
42
|
-
:
|
43
|
-
:
|
44
|
-
:
|
37
|
+
source: 'aws_rds',
|
38
|
+
region: region,
|
39
|
+
instance_id: instance.id,
|
40
|
+
db_name: instance.db_name,
|
41
|
+
engine: instance.engine,
|
42
|
+
engine_version: instance.engine_version,
|
45
43
|
|
46
44
|
# metrics
|
47
|
-
:
|
48
|
-
:
|
45
|
+
allocated_storage: instance.allocated_storage,
|
46
|
+
iops: instance.iops,
|
49
47
|
|
50
48
|
# replication info
|
51
|
-
:
|
52
|
-
:
|
53
|
-
:
|
54
|
-
:
|
49
|
+
is_replica: !instance.read_replica_source_db_instance_identifier.nil?,
|
50
|
+
replica_source_name: instance.read_replica_source_db_instance_identifier,
|
51
|
+
replica_names: instance.read_replica_db_instance_identifiers.join(','),
|
52
|
+
replicas: instance.read_replica_db_instance_identifiers.count,
|
55
53
|
|
56
|
-
:
|
57
|
-
:
|
54
|
+
owners: tags['owners'].split(','),
|
55
|
+
owner_groups: tags['owner_groups'].split(','),
|
58
56
|
|
59
|
-
:
|
60
|
-
:
|
57
|
+
db_env: tags['db_env'],
|
58
|
+
db_role: tags['db_role'],
|
61
59
|
}
|
62
60
|
end
|
63
61
|
end
|
64
62
|
end
|
65
63
|
|
66
|
-
|
64
|
+
hosts
|
67
65
|
end
|
68
66
|
|
69
67
|
private
|
68
|
+
|
70
69
|
def arn(region, instance_id)
|
71
|
-
|
70
|
+
"arn:aws:rds:#{region}:#{account_number}:db:#{instance_id}"
|
72
71
|
end
|
73
72
|
|
74
73
|
# unfortunately, this appears to be the only way to get your account number
|
@@ -77,8 +76,9 @@ module Interferon::HostSources
|
|
77
76
|
|
78
77
|
begin
|
79
78
|
my_arn = AWS::IAM.new(
|
80
|
-
:
|
81
|
-
:
|
79
|
+
access_key_id: @access_key_id,
|
80
|
+
secret_access_key: @secret_access_key
|
81
|
+
).client.get_user[:user][:arn]
|
82
82
|
rescue AWS::IAM::Errors::AccessDenied => e
|
83
83
|
my_arn = e.message.split[1]
|
84
84
|
end
|
@@ -6,7 +6,7 @@ module Interferon::HostSources
|
|
6
6
|
include ::Interferon::Logging
|
7
7
|
|
8
8
|
def initialize(options)
|
9
|
-
raise ArgumentError,
|
9
|
+
raise ArgumentError, 'missing host for optica source' \
|
10
10
|
unless options['host']
|
11
11
|
|
12
12
|
@host = options['host']
|
@@ -14,15 +14,17 @@ module Interferon::HostSources
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def list_hosts
|
17
|
-
|
18
|
-
|
19
|
-
:
|
20
|
-
:
|
21
|
-
:
|
17
|
+
optica_data['nodes'].map do |_ip, host|
|
18
|
+
{
|
19
|
+
source: 'optica',
|
20
|
+
hostname: host['hostname'],
|
21
|
+
role: host['role'],
|
22
|
+
environment: host['environment'],
|
22
23
|
|
23
|
-
:
|
24
|
-
:
|
25
|
-
}
|
24
|
+
owners: host['ownership'] && host['ownership']['people'] || [],
|
25
|
+
owner_groups: host['ownership'] && host['ownership']['groups'] || [],
|
26
|
+
}
|
27
|
+
end
|
26
28
|
end
|
27
29
|
|
28
30
|
def optica_data
|
@@ -32,7 +34,7 @@ module Interferon::HostSources
|
|
32
34
|
con.open_timeout = 60
|
33
35
|
|
34
36
|
response = con.get('/')
|
35
|
-
JSON
|
37
|
+
JSON.parse(response.body)
|
36
38
|
end
|
37
39
|
end
|
38
40
|
end
|
@@ -7,7 +7,7 @@ module Interferon::HostSources
|
|
7
7
|
include ::Interferon::Logging
|
8
8
|
|
9
9
|
def initialize(options)
|
10
|
-
raise ArgumentError,
|
10
|
+
raise ArgumentError, 'missing host for optica source' \
|
11
11
|
unless options['host']
|
12
12
|
|
13
13
|
@host = options['host']
|
@@ -22,24 +22,26 @@ module Interferon::HostSources
|
|
22
22
|
con.open_timeout = 60
|
23
23
|
|
24
24
|
response = con.get('/')
|
25
|
-
JSON
|
25
|
+
JSON.parse(response.body)
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
29
|
def list_hosts
|
30
|
-
services = Hash.new
|
31
|
-
|
32
|
-
:
|
30
|
+
services = Hash.new do |h, service|
|
31
|
+
h[service] = {
|
32
|
+
source: 'optica_services',
|
33
|
+
service: service,
|
33
34
|
|
34
|
-
:
|
35
|
-
:
|
36
|
-
:
|
37
|
-
:
|
38
|
-
:
|
39
|
-
}
|
35
|
+
owners: Set.new,
|
36
|
+
owner_groups: Set.new,
|
37
|
+
consumer_roles: Set.new,
|
38
|
+
consumer_machine_count: 0,
|
39
|
+
provider_machine_count: 0,
|
40
|
+
}
|
41
|
+
end
|
40
42
|
|
41
|
-
optica_data['nodes'].each do |
|
42
|
-
next unless @envs.empty?
|
43
|
+
optica_data['nodes'].each do |_ip, host|
|
44
|
+
next unless @envs.empty? || @envs.include?(host['environment'])
|
43
45
|
|
44
46
|
# make it easier by initializing possibly-missing data to sane defaults
|
45
47
|
host['ownership'] ||= {}
|
@@ -62,13 +64,13 @@ module Interferon::HostSources
|
|
62
64
|
end
|
63
65
|
|
64
66
|
# convert all sets to arrays
|
65
|
-
services.each do |k,v|
|
67
|
+
services.each do |k, v|
|
66
68
|
services[k][:owners] = v[:owners].to_a
|
67
69
|
services[k][:owner_groups] = v[:owner_groups].to_a
|
68
70
|
services[k][:consumer_roles] = v[:consumer_roles].to_a
|
69
71
|
end
|
70
72
|
|
71
|
-
|
73
|
+
services.values
|
72
74
|
end
|
73
75
|
end
|
74
76
|
end
|
data/lib/interferon/loaders.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
|
2
2
|
module Interferon
|
3
|
-
|
4
3
|
# lets create namespaces for things we'll be loading
|
5
4
|
module Destinations; end
|
6
5
|
module HostSources; end
|
@@ -40,7 +39,7 @@ module Interferon
|
|
40
39
|
next
|
41
40
|
end
|
42
41
|
|
43
|
-
|
42
|
+
unless enabled
|
44
43
|
log.info "skipping #{@loader_for} #{type} because it's not enabled"
|
45
44
|
next
|
46
45
|
end
|
@@ -86,14 +85,14 @@ module Interferon
|
|
86
85
|
klass = @module.const_get(class_name)
|
87
86
|
rescue LoadError => e
|
88
87
|
raise ArgumentError,\
|
89
|
-
|
88
|
+
"Loading Error; interferon does not define #{@loader_for} #{type}: #{e}"
|
90
89
|
rescue NameError => e
|
91
90
|
raise ArgumentError,\
|
92
|
-
|
91
|
+
"Name Error; class #{class_name} is not defined in #{relative_filename}: #{e}"
|
93
92
|
end
|
94
93
|
end
|
95
94
|
|
96
|
-
|
95
|
+
klass
|
97
96
|
end
|
98
97
|
end
|
99
98
|
|
data/lib/interferon/logging.rb
CHANGED
@@ -3,12 +3,11 @@ require 'statsd'
|
|
3
3
|
|
4
4
|
module Interferon
|
5
5
|
module Logging
|
6
|
-
|
7
6
|
def statsd
|
8
7
|
@statsd ||= Statsd.new(
|
9
8
|
Statsd::DEFAULT_HOST,
|
10
9
|
Statsd::DEFAULT_PORT,
|
11
|
-
:
|
10
|
+
namespace: 'alerts_framework'
|
12
11
|
)
|
13
12
|
end
|
14
13
|
|
@@ -20,7 +19,7 @@ module Interferon
|
|
20
19
|
logger = Logger.new(STDERR)
|
21
20
|
logger.level = Logger::INFO unless ENV['DEBUG']
|
22
21
|
logger.progname = classname
|
23
|
-
|
22
|
+
logger
|
24
23
|
end
|
25
24
|
end
|
26
25
|
end
|
data/lib/interferon/version.rb
CHANGED
@@ -4,18 +4,18 @@ module Interferon
|
|
4
4
|
class WorkHoursHelper
|
5
5
|
DEFAULT_WORK_DAYS = (1..5)
|
6
6
|
DEFAULT_WORK_HOURS = (9..16)
|
7
|
-
DEFAULT_WORK_TIMEZONE = 'America/Los_Angeles'
|
7
|
+
DEFAULT_WORK_TIMEZONE = 'America/Los_Angeles'.freeze
|
8
8
|
DEFAULT_WORK_ARGS = {
|
9
|
-
:
|
10
|
-
:
|
11
|
-
:
|
9
|
+
hours: DEFAULT_WORK_HOURS,
|
10
|
+
days: DEFAULT_WORK_DAYS,
|
11
|
+
timezone: DEFAULT_WORK_TIMEZONE,
|
12
12
|
}.freeze
|
13
13
|
|
14
14
|
def self.is_work_hour?(time, args = {})
|
15
15
|
args = args.merge(DEFAULT_WORK_ARGS)
|
16
16
|
tz = TZInfo::Timezone.get args[:timezone]
|
17
17
|
time_in_tz = time + tz.period_for_utc(time).utc_offset
|
18
|
-
|
18
|
+
args[:days].include?(time_in_tz.wday) && args[:hours].include?(time_in_tz.hour)
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|