clear_skies 0.2.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/Gemfile.lock +149 -0
- data/clear_skies.gemspec +2 -0
- data/lib/clear_skies/aws/cloud_watch/billing.rb +16 -0
- data/lib/clear_skies/aws/cloud_watch/elastic_beanstalk_gauge.rb +47 -0
- data/lib/clear_skies/aws/cloud_watch/elb_gauge.rb +47 -0
- data/lib/clear_skies/aws/cloud_watch/gauge.rb +73 -0
- data/lib/clear_skies/aws/cloud_watch/rds_gauge.rb +42 -0
- data/lib/clear_skies/aws/cloud_watch/request_counter.rb +40 -0
- data/lib/clear_skies/aws/reservation_utilization.rb +129 -0
- data/lib/clear_skies/elasticsearch/report.rb +76 -0
- data/lib/clear_skies/redis/report.rb +104 -39
- data/lib/clear_skies/version.rb +1 -1
- data/lib/clear_skies.rb +9 -6
- metadata +25 -7
- data/lib/clear_skies/cloud_watch/elastic_beanstalk_gauge.rb +0 -45
- data/lib/clear_skies/cloud_watch/elb_gauge.rb +0 -44
- data/lib/clear_skies/cloud_watch/gauge.rb +0 -70
- data/lib/clear_skies/cloud_watch/rds_gauge.rb +0 -37
- data/lib/clear_skies/cloud_watch/request_counter.rb +0 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 82eb78bcb0b8d0e46d77a658b8a46390441f6a61
|
4
|
+
data.tar.gz: 96bd95ac7fe050aab5c42705751651d465cc00fc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ea88cec568d392ecdcaa6e9907c43d6d06558348234d83e5cfc4c6258377b986c0f4e0f38a778bbc9d9e3585f4543cff62c638207061debd3b887b741df4a1cd
|
7
|
+
data.tar.gz: adac7ec7203afb00fd939cabf39ea7b3e22d146946667372475506f3238727bb030e86345ae45e8ae7adaec1a9c8321c06b84c927016992183890ec9fd24b8e6
|
data/.gitignore
CHANGED
data/Gemfile.lock
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
clear_skies (0.4.0)
|
5
|
+
awesome_print
|
6
|
+
aws-sdk
|
7
|
+
elasticsearch
|
8
|
+
greek_fire (>= 0.3.0)
|
9
|
+
puma
|
10
|
+
redis
|
11
|
+
|
12
|
+
GEM
|
13
|
+
remote: https://rubygems.org/
|
14
|
+
specs:
|
15
|
+
actioncable (5.1.2)
|
16
|
+
actionpack (= 5.1.2)
|
17
|
+
nio4r (~> 2.0)
|
18
|
+
websocket-driver (~> 0.6.1)
|
19
|
+
actionmailer (5.1.2)
|
20
|
+
actionpack (= 5.1.2)
|
21
|
+
actionview (= 5.1.2)
|
22
|
+
activejob (= 5.1.2)
|
23
|
+
mail (~> 2.5, >= 2.5.4)
|
24
|
+
rails-dom-testing (~> 2.0)
|
25
|
+
actionpack (5.1.2)
|
26
|
+
actionview (= 5.1.2)
|
27
|
+
activesupport (= 5.1.2)
|
28
|
+
rack (~> 2.0)
|
29
|
+
rack-test (~> 0.6.3)
|
30
|
+
rails-dom-testing (~> 2.0)
|
31
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
32
|
+
actionview (5.1.2)
|
33
|
+
activesupport (= 5.1.2)
|
34
|
+
builder (~> 3.1)
|
35
|
+
erubi (~> 1.4)
|
36
|
+
rails-dom-testing (~> 2.0)
|
37
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
38
|
+
activejob (5.1.2)
|
39
|
+
activesupport (= 5.1.2)
|
40
|
+
globalid (>= 0.3.6)
|
41
|
+
activemodel (5.1.2)
|
42
|
+
activesupport (= 5.1.2)
|
43
|
+
activerecord (5.1.2)
|
44
|
+
activemodel (= 5.1.2)
|
45
|
+
activesupport (= 5.1.2)
|
46
|
+
arel (~> 8.0)
|
47
|
+
activesupport (5.1.2)
|
48
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
49
|
+
i18n (~> 0.7)
|
50
|
+
minitest (~> 5.1)
|
51
|
+
tzinfo (~> 1.1)
|
52
|
+
arel (8.0.0)
|
53
|
+
awesome_print (1.8.0)
|
54
|
+
aws-sdk (2.10.10)
|
55
|
+
aws-sdk-resources (= 2.10.10)
|
56
|
+
aws-sdk-core (2.10.10)
|
57
|
+
aws-sigv4 (~> 1.0)
|
58
|
+
jmespath (~> 1.0)
|
59
|
+
aws-sdk-resources (2.10.10)
|
60
|
+
aws-sdk-core (= 2.10.10)
|
61
|
+
aws-sigv4 (1.0.0)
|
62
|
+
builder (3.2.3)
|
63
|
+
concurrent-ruby (1.0.5)
|
64
|
+
elasticsearch (5.0.4)
|
65
|
+
elasticsearch-api (= 5.0.4)
|
66
|
+
elasticsearch-transport (= 5.0.4)
|
67
|
+
elasticsearch-api (5.0.4)
|
68
|
+
multi_json
|
69
|
+
elasticsearch-transport (5.0.4)
|
70
|
+
faraday
|
71
|
+
multi_json
|
72
|
+
erubi (1.6.1)
|
73
|
+
faraday (0.12.1)
|
74
|
+
multipart-post (>= 1.2, < 3)
|
75
|
+
globalid (0.4.0)
|
76
|
+
activesupport (>= 4.2.0)
|
77
|
+
greek_fire (0.4.1)
|
78
|
+
rails (>= 4.2.0)
|
79
|
+
i18n (0.8.6)
|
80
|
+
jmespath (1.3.1)
|
81
|
+
loofah (2.0.3)
|
82
|
+
nokogiri (>= 1.5.9)
|
83
|
+
mail (2.6.6)
|
84
|
+
mime-types (>= 1.16, < 4)
|
85
|
+
method_source (0.8.2)
|
86
|
+
mime-types (3.1)
|
87
|
+
mime-types-data (~> 3.2015)
|
88
|
+
mime-types-data (3.2016.0521)
|
89
|
+
mini_portile2 (2.2.0)
|
90
|
+
minitest (5.10.2)
|
91
|
+
multi_json (1.12.1)
|
92
|
+
multipart-post (2.0.0)
|
93
|
+
nio4r (2.1.0)
|
94
|
+
nokogiri (1.8.0)
|
95
|
+
mini_portile2 (~> 2.2.0)
|
96
|
+
puma (3.9.1)
|
97
|
+
rack (2.0.3)
|
98
|
+
rack-test (0.6.3)
|
99
|
+
rack (>= 1.0)
|
100
|
+
rails (5.1.2)
|
101
|
+
actioncable (= 5.1.2)
|
102
|
+
actionmailer (= 5.1.2)
|
103
|
+
actionpack (= 5.1.2)
|
104
|
+
actionview (= 5.1.2)
|
105
|
+
activejob (= 5.1.2)
|
106
|
+
activemodel (= 5.1.2)
|
107
|
+
activerecord (= 5.1.2)
|
108
|
+
activesupport (= 5.1.2)
|
109
|
+
bundler (>= 1.3.0, < 2.0)
|
110
|
+
railties (= 5.1.2)
|
111
|
+
sprockets-rails (>= 2.0.0)
|
112
|
+
rails-dom-testing (2.0.3)
|
113
|
+
activesupport (>= 4.2.0)
|
114
|
+
nokogiri (>= 1.6)
|
115
|
+
rails-html-sanitizer (1.0.3)
|
116
|
+
loofah (~> 2.0)
|
117
|
+
railties (5.1.2)
|
118
|
+
actionpack (= 5.1.2)
|
119
|
+
activesupport (= 5.1.2)
|
120
|
+
method_source
|
121
|
+
rake (>= 0.8.7)
|
122
|
+
thor (>= 0.18.1, < 2.0)
|
123
|
+
rake (10.5.0)
|
124
|
+
redis (3.3.3)
|
125
|
+
sprockets (3.7.1)
|
126
|
+
concurrent-ruby (~> 1.0)
|
127
|
+
rack (> 1, < 3)
|
128
|
+
sprockets-rails (3.2.0)
|
129
|
+
actionpack (>= 4.0)
|
130
|
+
activesupport (>= 4.0)
|
131
|
+
sprockets (>= 3.0.0)
|
132
|
+
thor (0.19.4)
|
133
|
+
thread_safe (0.3.6)
|
134
|
+
tzinfo (1.2.3)
|
135
|
+
thread_safe (~> 0.1)
|
136
|
+
websocket-driver (0.6.5)
|
137
|
+
websocket-extensions (>= 0.1.0)
|
138
|
+
websocket-extensions (0.1.2)
|
139
|
+
|
140
|
+
PLATFORMS
|
141
|
+
ruby
|
142
|
+
|
143
|
+
DEPENDENCIES
|
144
|
+
bundler (~> 1.14)
|
145
|
+
clear_skies!
|
146
|
+
rake (~> 10.0)
|
147
|
+
|
148
|
+
BUNDLED WITH
|
149
|
+
1.15.1
|
data/clear_skies.gemspec
CHANGED
@@ -24,7 +24,9 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_dependency "puma"
|
25
25
|
spec.add_dependency "aws-sdk"
|
26
26
|
spec.add_dependency "redis"
|
27
|
+
spec.add_dependency "elasticsearch"
|
27
28
|
spec.add_dependency "greek_fire", ">= 0.3.0"
|
29
|
+
|
28
30
|
spec.add_dependency "awesome_print"
|
29
31
|
|
30
32
|
spec.add_development_dependency "bundler", "~> 1.14"
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module ClearSkies
|
2
|
+
module AWS
|
3
|
+
module CloudWatch
|
4
|
+
class Billing < ClearSkies::AWS::CloudWatch::Gauge
|
5
|
+
def initialize(dimension, statistics, description: nil, &block)
|
6
|
+
super("AWS/Billing", "EstimatedCharges", dimension, statistics, description: description, aws_parameters: {start_time: {days: -1}, end_time: {minutes: -5}, period: 60*60*24}, &block)
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
def self.cloudwatch_client
|
11
|
+
@client ||= Aws::CloudWatch::Client.new(region: "us-east-1")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module ClearSkies
|
2
|
+
module AWS
|
3
|
+
module CloudWatch
|
4
|
+
class ElasticBeanstalkGauge < ClearSkies::AWS::CloudWatch::Gauge
|
5
|
+
def self.beanstalk_client
|
6
|
+
@beanstalk_client ||= ::Aws::ElasticBeanstalk::Client.new
|
7
|
+
end
|
8
|
+
def initialize(metric_name, dimension, statistics, description: nil, &block)
|
9
|
+
super("AWS/ElasticBeanstalk", metric_name, dimension, statistics, description: description, &block)
|
10
|
+
end
|
11
|
+
|
12
|
+
def application_name(environment_name)
|
13
|
+
ElasticBeanstalkGauge.beanstalk_client.describe_environments({environment_names: [environment_name] }).environments.first&.application_name
|
14
|
+
end
|
15
|
+
|
16
|
+
def vpc_id(application_name, environment_name)
|
17
|
+
config = ElasticBeanstalkGauge.beanstalk_client.describe_configuration_settings({ application_name: application_name, environment_name: environment_name }).
|
18
|
+
configuration_settings.find { |config| config.application_name == application_name && config.environment_name == environment_name}
|
19
|
+
option = config.option_settings.find { |option| option.namespace == "aws:ec2:vpc" && option.option_name == "VPCId"}
|
20
|
+
option.value if option
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def labels_from_metric(metric)
|
25
|
+
labels = super(metric)
|
26
|
+
|
27
|
+
if labels.has_key?( "environment_name") && !(Rails.cache.fetch("#{labels["environment_name"]}_skip"))
|
28
|
+
application_name = Rails.cache.fetch("#{labels["environment_name"]}_application_name", expires_in: 1.hour) do
|
29
|
+
application_name(labels["environment_name"])
|
30
|
+
end
|
31
|
+
if application_name
|
32
|
+
labels["application_name"] = application_name
|
33
|
+
|
34
|
+
labels["vpc_id"] = Rails.cache.fetch("#{labels["environment_name"]}_vpc_id_") do
|
35
|
+
vpc_id(labels["application_name"], labels["environment_name"])
|
36
|
+
end
|
37
|
+
end
|
38
|
+
else
|
39
|
+
Rails.cache.write("#{labels["environment_name"]}_skip", true, expires_in: 1.hour, race_condition_ttl: 60.seconds)
|
40
|
+
end
|
41
|
+
|
42
|
+
return labels
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module ClearSkies
|
2
|
+
module AWS
|
3
|
+
|
4
|
+
module CloudWatch
|
5
|
+
class ELBGauge < ClearSkies::AWS::CloudWatch::Gauge
|
6
|
+
def self.elb_client
|
7
|
+
@elb_client ||= Aws::ElasticLoadBalancing::Client.new
|
8
|
+
end
|
9
|
+
def initialize(metric_name, dimension, statistics, description: nil, &block)
|
10
|
+
super("AWS/ELB", metric_name, dimension, statistics, description: description, &block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def tags(load_balancer_name)
|
14
|
+
labels = {}
|
15
|
+
ELBGauge.elb_client.
|
16
|
+
describe_tags({load_balancer_names: [load_balancer_name]}).
|
17
|
+
tag_descriptions.
|
18
|
+
select {|doc| doc.load_balancer_name == load_balancer_name}.each do |tag_description|
|
19
|
+
tag_description.tags.each do |tag|
|
20
|
+
labels[tag.key.downcase] = tag.value
|
21
|
+
end
|
22
|
+
end
|
23
|
+
labels
|
24
|
+
end
|
25
|
+
|
26
|
+
def labels_from_metric(metric)
|
27
|
+
labels = super(metric)
|
28
|
+
|
29
|
+
if labels.has_key?( "load_balancer_name") && !(Rails.cache.fetch("#{labels["load_balancer_name"]}_skip"))
|
30
|
+
|
31
|
+
labels["vpc_id"] = Rails.cache.fetch("#{labels["load_balancer_name"]}_vpc_id_") do
|
32
|
+
ELBGauge.elb_client.describe_load_balancers(load_balancer_names: [labels["load_balancer_name"]]).load_balancer_descriptions.first.vpc_id
|
33
|
+
end
|
34
|
+
|
35
|
+
labels.merge!(Rails.cache.fetch("#{labels["load_balancer_name"]}_tags_", expires_in: 1.hour, race_condition_ttl: 60.seconds) do
|
36
|
+
tags(labels["load_balancer_name"])
|
37
|
+
end)
|
38
|
+
end
|
39
|
+
labels
|
40
|
+
rescue Aws::ElasticLoadBalancing::Errors::LoadBalancerNotFound
|
41
|
+
Rails.cache.write("#{labels["load_balancer_name"]}_skip", true, expires_in: 1.hour, race_condition_ttl: 60.seconds)
|
42
|
+
return labels
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
|
2
|
+
module ClearSkies
|
3
|
+
module AWS
|
4
|
+
module CloudWatch
|
5
|
+
class Gauge < GreekFire::Gauge
|
6
|
+
include ActiveModel::Conversion
|
7
|
+
|
8
|
+
def self.register(*args, &block)
|
9
|
+
GreekFire::Metric.register(self.new(*args, &block))
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(namespace, metric_name, dimensions, statistics, description:nil, aws_parameters:nil, &block)
|
13
|
+
super("#{namespace.underscore.gsub("/", "_")}_#{metric_name.underscore}", description: description)
|
14
|
+
@namespace = namespace
|
15
|
+
@metric_name = metric_name
|
16
|
+
@dimensions = dimensions
|
17
|
+
@statistics = statistics.select { |stat| ["SampleCount", "Average", "Sum", "Minimum", "Maximum"].include?(stat.to_s) }
|
18
|
+
@extended_statistics = statistics - @statistics
|
19
|
+
@aws_parameters = aws_parameters || { }
|
20
|
+
|
21
|
+
@block = block
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.cloudwatch_client
|
25
|
+
@client ||= Aws::CloudWatch::Client.new
|
26
|
+
end
|
27
|
+
|
28
|
+
def aws_metrics
|
29
|
+
Aws::CloudWatch::Resource.new(client: self.class.cloudwatch_client).metrics(
|
30
|
+
namespace: @namespace,
|
31
|
+
metric_name: @metric_name,
|
32
|
+
dimensions: @dimensions.map {|dimension| {name: dimension} }
|
33
|
+
).select { |metrics| metrics.dimensions.count == @dimensions.count }
|
34
|
+
end
|
35
|
+
|
36
|
+
def labels_from_metric(metric)
|
37
|
+
metric.dimensions.inject(ActiveSupport::HashWithIndifferentAccess.new) do |labels, dimension|
|
38
|
+
labels[dimension.name.underscore] = dimension.value
|
39
|
+
labels
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def metrics
|
44
|
+
aws_metrics.map do |metric|
|
45
|
+
labels = labels_from_metric(metric)
|
46
|
+
|
47
|
+
next unless @block.call(labels) if @block
|
48
|
+
|
49
|
+
stats = metric.get_statistics(
|
50
|
+
start_time: Time.now.advance(@aws_parameters[:start_time] || {minutes: -6}),
|
51
|
+
end_time: Time.now.advance(@aws_parameters[:end_time] || {minutes: -5}),
|
52
|
+
period: @aws_parameters[:period] || 1,
|
53
|
+
statistics: @statistics,
|
54
|
+
extended_statistics: @extended_statistics,
|
55
|
+
dimensions: metric.dimensions
|
56
|
+
)
|
57
|
+
|
58
|
+
stats.datapoints.map do |datapoint|
|
59
|
+
datapoint.to_h.select {|k, v| ![:unit, :timestamp].include?(k) }.map do |key, value|
|
60
|
+
if (key == :extended_statistics)
|
61
|
+
value.map {|e_key, e_value| GreekFire::Metric.new(name, labels.merge({statistic: e_key}), e_value)}
|
62
|
+
else
|
63
|
+
GreekFire::Metric.new(name, labels.merge({statistic: key}), value)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end.flatten.compact
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module ClearSkies
|
2
|
+
module AWS
|
3
|
+
|
4
|
+
module CloudWatch
|
5
|
+
class RDSGauge < ClearSkies::AWS::CloudWatch::Gauge
|
6
|
+
def initialize(metric_name, dimension, statistics, description: nil, &block)
|
7
|
+
super("AWS/RDS", metric_name, dimension, statistics, description: description, &block)
|
8
|
+
end
|
9
|
+
|
10
|
+
def tags(db)
|
11
|
+
labels = {}
|
12
|
+
db.client.list_tags_for_resource(resource_name: db.db_instance_arn).tag_list.each do |tag|
|
13
|
+
labels[tag.key.downcase] = tag.value
|
14
|
+
end
|
15
|
+
labels
|
16
|
+
end
|
17
|
+
|
18
|
+
def labels_from_metric(metric)
|
19
|
+
labels = super(metric)
|
20
|
+
|
21
|
+
if labels.has_key?( "db_instance_identifier") && !(Rails.cache.fetch("#{labels["db_instance_identifier"]}_skip"))
|
22
|
+
db = Aws::RDS::DBInstance.new(labels["db_instance_identifier"])
|
23
|
+
|
24
|
+
vpc_id = Rails.cache.fetch("#{labels["db_instance_identifier"]}_vpc_id_") do
|
25
|
+
db.db_subnet_group&.vpc_id
|
26
|
+
end
|
27
|
+
|
28
|
+
labels["vpc_id"] = vpc_id if vpc_id.present?
|
29
|
+
|
30
|
+
labels.merge!(Rails.cache.fetch("#{labels["db_instance_identifier"]}_tags_", expires_in: 1.hour, race_condition_ttl: 60.seconds) do
|
31
|
+
tags(db)
|
32
|
+
end)
|
33
|
+
end
|
34
|
+
labels
|
35
|
+
rescue Aws::RDS::Errors::DBInstanceNotFound
|
36
|
+
Rails.cache.write("#{labels["db_instance_identifier"]}_skip", true, expires_in: 1.hour, race_condition_ttl: 60.seconds)
|
37
|
+
return labels
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module ClearSkies
|
2
|
+
module AWS
|
3
|
+
|
4
|
+
module CloudWatch
|
5
|
+
class RequestCounter < Seahorse::Client::Handler
|
6
|
+
@mutex = Mutex.new
|
7
|
+
def self.increment
|
8
|
+
@mutex.synchronize do
|
9
|
+
@count ||= 0
|
10
|
+
@count += 1
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.count
|
15
|
+
@count ||= 0
|
16
|
+
@count
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(handler)
|
20
|
+
@handler = handler
|
21
|
+
end
|
22
|
+
|
23
|
+
def call(context)
|
24
|
+
RequestCounter.increment
|
25
|
+
@handler.call(context)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class RequestCounterPlugin < Seahorse::Client::Plugin
|
30
|
+
|
31
|
+
def add_handlers(handlers, config)
|
32
|
+
handlers.add(RequestCounter, step: :validate)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
Aws::CloudWatch::Client.add_plugin(ClearSkies::AWS::CloudWatch::RequestCounterPlugin)
|
@@ -0,0 +1,129 @@
|
|
1
|
+
module ClearSkies
|
2
|
+
module AWS
|
3
|
+
class ReservationUtilization < GreekFire::MeasureSet
|
4
|
+
def items
|
5
|
+
client = Aws::EC2::Client.new()
|
6
|
+
reservations = client.describe_reserved_instances(filters: [{name: "state", values: ["active"]}]).reserved_instances.map { |x| ReservationMatcher.new(x) }
|
7
|
+
|
8
|
+
instance_counts = Hash.new { 0 }
|
9
|
+
|
10
|
+
|
11
|
+
instance_spawns = client.describe_instances(filters: [{name: "instance-state-name", values: ["running"]}])
|
12
|
+
|
13
|
+
instance_spawns.reservations.each do |spawn|
|
14
|
+
spawn.instances.each do |instance|
|
15
|
+
reservations.find { |reservation| reservation.match(instance) }
|
16
|
+
|
17
|
+
instance_counts[{instance_type: instance.instance_type, availability_zone: instance.placement.availability_zone, tenancy: instance.placement.tenancy}] += 1
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
[
|
22
|
+
ReservationExpirationGauge.new(reservations),
|
23
|
+
ReservationPurchasedGauge.new(reservations),
|
24
|
+
ReservationUsageGauge.new(reservations),
|
25
|
+
InstancesGauge.new(instance_counts)
|
26
|
+
]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class ReservationMatcher
|
31
|
+
attr_reader :reservation, :match_count, :instance_count
|
32
|
+
|
33
|
+
def initialize(reservation)
|
34
|
+
@reservation = reservation
|
35
|
+
@instance_count = reservation.instance_count
|
36
|
+
|
37
|
+
@match_count = 0
|
38
|
+
end
|
39
|
+
|
40
|
+
def expires_in
|
41
|
+
@reservation.end - Time.now
|
42
|
+
end
|
43
|
+
|
44
|
+
def match(instance)
|
45
|
+
return false if match_count >= instance_count
|
46
|
+
return false unless @reservation.instance_type == instance.instance_type
|
47
|
+
return false unless @reservation.scope == "Region" || @reservation.availability_zone == instance.placement.availability_zone
|
48
|
+
return false unless @reservation.instance_tenancy == instance.placement.tenancy
|
49
|
+
|
50
|
+
@match_count += 1
|
51
|
+
return true
|
52
|
+
end
|
53
|
+
|
54
|
+
def labels
|
55
|
+
{
|
56
|
+
reserved_instances_id: @reservation.reserved_instances_id,
|
57
|
+
instance_type: @reservation.instance_type,
|
58
|
+
availability_zone: @reservation.availability_zone || "Region",
|
59
|
+
tenancy: @reservation.instance_tenancy,
|
60
|
+
}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class InstancesGauge < GreekFire::Gauge
|
65
|
+
def initialize(instance_counts)
|
66
|
+
super("aws_ec2_instances", description: "Number of instances running by type") do |labels|
|
67
|
+
labels.delete(:count)
|
68
|
+
end
|
69
|
+
|
70
|
+
@instance_counts = instance_counts
|
71
|
+
end
|
72
|
+
|
73
|
+
def labels
|
74
|
+
@instance_counts.map do |labels, count|
|
75
|
+
labels.merge(count: count)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class ReservationPurchasedGauge < GreekFire::Gauge
|
81
|
+
def initialize(reservations)
|
82
|
+
super("aws_ec2_reservation_purchases", description: "Number of instance reservations purchased") do |labels|
|
83
|
+
labels.delete(:reservation).instance_count
|
84
|
+
end
|
85
|
+
|
86
|
+
@reservations = reservations
|
87
|
+
end
|
88
|
+
|
89
|
+
def labels
|
90
|
+
@reservations.map do |reservation|
|
91
|
+
reservation.labels.merge(reservation: reservation)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class ReservationUsageGauge < GreekFire::Gauge
|
97
|
+
def initialize(reservations)
|
98
|
+
super("aws_ec2_reservation_usage", description: "Number of instance reservations in use") do |labels|
|
99
|
+
labels.delete(:reservation).match_count
|
100
|
+
end
|
101
|
+
|
102
|
+
@reservations = reservations
|
103
|
+
end
|
104
|
+
|
105
|
+
def labels
|
106
|
+
@reservations.map do |reservation|
|
107
|
+
reservation.labels.merge(reservation: reservation)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
class ReservationExpirationGauge < GreekFire::Gauge
|
113
|
+
def initialize(reservations)
|
114
|
+
super("aws_ec2_reservation_expire_in", description: "seconds until instance reservation expires") do |labels|
|
115
|
+
labels.delete(:reservation).expires_in
|
116
|
+
end
|
117
|
+
|
118
|
+
@reservations = reservations
|
119
|
+
end
|
120
|
+
|
121
|
+
def labels
|
122
|
+
@reservations.map do |reservation|
|
123
|
+
reservation.labels.merge(reservation: reservation)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'elasticsearch'
|
2
|
+
|
3
|
+
module ClearSkies
|
4
|
+
module Elasticsearch
|
5
|
+
class Report
|
6
|
+
|
7
|
+
def self.register(url, extra_labels=nil)
|
8
|
+
reports << ClearSkies::Elasticsearch::Report.new(url, extra_labels || Hash.new)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.reports
|
12
|
+
@reports ||= []
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :url, :extra_labels, :metrics
|
16
|
+
def initialize(url, extra_labels)
|
17
|
+
@url = url
|
18
|
+
@extra_labels = extra_labels
|
19
|
+
GreekFire::Measure.before_metrics { refresh }
|
20
|
+
end
|
21
|
+
|
22
|
+
def elasticsearch_metrics(client, index)
|
23
|
+
metrics = OpenStruct.new
|
24
|
+
metrics.index = index
|
25
|
+
stats = client.indices.stats["indices"][index]["total"]
|
26
|
+
metrics.docs_count = stats["docs"]["count"]
|
27
|
+
metrics.docs_deleted = stats["docs"]["deleted"]
|
28
|
+
metrics
|
29
|
+
end
|
30
|
+
|
31
|
+
def refresh
|
32
|
+
client = ::Elasticsearch::Client.new(hosts: @url)
|
33
|
+
aliases = client.indices.get_aliases
|
34
|
+
|
35
|
+
@metrics = aliases.map do |index, _aliases|
|
36
|
+
elasticsearch_metrics(client, index)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
class Measure < GreekFire::Measure
|
43
|
+
def initialize(name, &block)
|
44
|
+
super(name) do |label|
|
45
|
+
block.call(label.delete(:metric))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
def labels
|
51
|
+
ClearSkies::Elasticsearch::Report.reports.map do |report|
|
52
|
+
report.metrics.map do |metric|
|
53
|
+
report.extra_labels.merge({index: metric.index, metric: metric })
|
54
|
+
end
|
55
|
+
end.flatten
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class Gauge < ClearSkies::Elasticsearch::Measure
|
60
|
+
def to_partial_path
|
61
|
+
GreekFire::Gauge._to_partial_path
|
62
|
+
end
|
63
|
+
end
|
64
|
+
class Counter < ClearSkies::Elasticsearch::Measure
|
65
|
+
def to_partial_path
|
66
|
+
GreekFire::Counter._to_partial_path
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# GreekFire::Metric.register(ClearSkies::Elasticsearch::Gauge.new("elasticsearch_docs_count") { |metrics| metrics.docs_count })
|
73
|
+
# GreekFire::Metric.register(ClearSkies::Elasticsearch::Gauge.new("elasticsearch_docs_deleted") { |metrics| metrics.docs_deleted })
|
74
|
+
|
75
|
+
|
76
|
+
#For a <DIM>, grab N metrics.
|
@@ -1,52 +1,117 @@
|
|
1
1
|
require 'redis'
|
2
|
+
|
2
3
|
module ClearSkies
|
3
4
|
module Redis
|
5
|
+
|
4
6
|
class Report
|
7
|
+
|
5
8
|
def self.register(host, port, extra_labels=nil)
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
GreekFire::Gauge.register("redis_keys", labels: {"database" => databases} ) { | labels| labels.merge!(extra_labels);r.redis_metrics(labels).keys }
|
12
|
-
GreekFire::Gauge.register("redis_last_save", labels: {"database" => databases} ) { | labels| labels.merge!(extra_labels);r.redis_metrics(labels).last_save }
|
13
|
-
GreekFire::Gauge.register("redis_uptime", labels: {"database" => databases} ) { | labels| labels.merge!(extra_labels);r.redis_metrics(labels).uptime }
|
14
|
-
GreekFire::Gauge.register("redis_connected_clients", labels: {"database" => databases} ) { | labels| labels.merge!(extra_labels);r.redis_metrics(labels).connected_clients }
|
15
|
-
GreekFire::Gauge.register("redis_blocked_clients", labels: {"database" => databases} ) { | labels| labels.merge!(extra_labels);r.redis_metrics(labels).blocked_clients }
|
16
|
-
GreekFire::Gauge.register("redis_used_memory", labels: {"database" => databases} ) { | labels| labels.merge!(extra_labels);r.redis_metrics(labels).used_memory }
|
17
|
-
GreekFire::Gauge.register("redis_mem_fragmentation_ratio", labels: {"database" => databases} ) { | labels| labels.merge!(extra_labels);r.redis_metrics(labels).mem_fragmentation_ratio }
|
18
|
-
GreekFire::Gauge.register("redis_rdb_changes_since_last_save", labels: {"database" => databases} ) { | labels| labels.merge!(extra_labels);r.redis_metrics(labels).rdb_changes_since_last_save }
|
19
|
-
GreekFire::Gauge.register("redis_rdb_last_bgsave_time_sec", labels: {"database" => databases} ) { | labels| labels.merge!(extra_labels);r.redis_metrics(labels).rdb_last_bgsave_time_sec }
|
20
|
-
GreekFire::Counter.register("redis_total_commands_processed", labels: {"database" => databases} ) { | labels| labels.merge!(extra_labels);r.redis_metrics(labels).total_commands_processed }
|
21
|
-
end
|
22
|
-
|
23
|
-
|
24
|
-
def redis_metrics(labels)
|
25
|
-
cache_key = "redis_stats_#{@host}_#{@port}_#{labels["database"]}"
|
26
|
-
Rails.cache.fetch(cache_key, expires_in: 1.second) do
|
27
|
-
redis = ::Redis.new(host: @host, port: @port, db: labels["database"])
|
28
|
-
redis_info = redis.info
|
29
|
-
metrics = OpenStruct.new
|
30
|
-
|
31
|
-
metrics.keys = redis.dbsize
|
32
|
-
metrics.last_save = Time.now.to_i - redis.lastsave
|
33
|
-
metrics.uptime = redis_info["uptime_in_seconds"].to_f
|
34
|
-
metrics.connected_clients = redis_info["connected_clients"].to_i
|
35
|
-
metrics.blocked_clients = redis_info["blocked_clients"].to_i
|
36
|
-
metrics.used_memory = redis_info["used_memory"].to_f
|
37
|
-
metrics.mem_fragmentation_ratio = redis_info["mem_fragmentation_ratio"].to_f
|
38
|
-
metrics.rdb_changes_since_last_save = redis_info["rdb_changes_since_last_save"].to_f
|
39
|
-
metrics.rdb_last_bgsave_time_sec = redis_info["rdb_last_bgsave_time_sec"].to_f
|
40
|
-
metrics.total_commands_processed = redis_info["total_commands_processed"].to_f
|
41
|
-
metrics
|
42
|
-
end
|
9
|
+
reports << ClearSkies::Redis::Report.new(host, port, extra_labels || Hash.new)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.reports
|
13
|
+
@reports ||= []
|
43
14
|
end
|
44
15
|
|
45
|
-
|
46
|
-
def initialize(host, port)
|
16
|
+
attr_reader :host, :port, :extra_labels
|
17
|
+
def initialize(host, port, extra_labels)
|
47
18
|
@host = host
|
48
19
|
@port = port
|
20
|
+
@extra_labels = {host: host, port: port}.merge extra_labels
|
21
|
+
end
|
22
|
+
|
23
|
+
def dimensions
|
24
|
+
::Redis.new(:host => @host, :port => @port).info.keys.map {|k| k =~ /^db/ && k.sub("db", "")}.compact
|
25
|
+
end
|
26
|
+
|
27
|
+
def report_dimensions
|
28
|
+
dimensions.map do |dimension|
|
29
|
+
ReportDimension.new(self, dimension)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def items
|
34
|
+
report_dimensions.map
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class MeasureSet < GreekFire::MeasureSet
|
39
|
+
def items
|
40
|
+
report_dimensions = ClearSkies::Redis::Report.reports.map(&:report_dimensions).flatten
|
41
|
+
return [] unless report_dimensions.length > 0
|
42
|
+
|
43
|
+
[
|
44
|
+
"keys",
|
45
|
+
"last_save",
|
46
|
+
"uptime",
|
47
|
+
"connected_clients",
|
48
|
+
"blocked_clients",
|
49
|
+
"used_memory",
|
50
|
+
"mem_fragmentation_ratio",
|
51
|
+
"rdb_changes_since_last_save",
|
52
|
+
"rdb_last_bgsave_time_sec",
|
53
|
+
"total_commands_processed"
|
54
|
+
].map do |metric_name|
|
55
|
+
ClearSkies::Redis::Gauge.new(report_dimensions, "redis", metric_name)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class ReportDimension
|
61
|
+
attr_reader :report, :dimension
|
62
|
+
|
63
|
+
def initialize(report, dimension)
|
64
|
+
@report = report
|
65
|
+
@dimension = dimension
|
66
|
+
end
|
67
|
+
|
68
|
+
def metrics
|
69
|
+
redis = ::Redis.new(host: report.host, port: report.port, db: @dimension)
|
70
|
+
redis_info = redis.info
|
71
|
+
|
72
|
+
metrics = OpenStruct.new
|
73
|
+
metrics.keys = redis.dbsize
|
74
|
+
metrics.last_save = Time.now.to_i - redis.lastsave
|
75
|
+
metrics.uptime = redis_info["uptime_in_seconds"].to_f
|
76
|
+
metrics.connected_clients = redis_info["connected_clients"].to_i
|
77
|
+
metrics.blocked_clients = redis_info["blocked_clients"].to_i
|
78
|
+
metrics.used_memory = redis_info["used_memory"].to_f
|
79
|
+
metrics.mem_fragmentation_ratio = redis_info["mem_fragmentation_ratio"].to_f
|
80
|
+
metrics.rdb_changes_since_last_save = redis_info["rdb_changes_since_last_save"].to_f
|
81
|
+
metrics.rdb_last_bgsave_time_szec = redis_info["rdb_last_bgsave_time_sec"].to_f
|
82
|
+
metrics.total_commands_processed = redis_info["total_commands_processed"].to_f
|
83
|
+
metrics
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class Measure < GreekFire::Measure
|
88
|
+
def initialize(report_dimensions, prefix, name)
|
89
|
+
@report_dimensions = report_dimensions
|
90
|
+
super("#{prefix}_#{name}") do |label|
|
91
|
+
label.delete(:metric).metrics.to_h[name.to_sym]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
def labels
|
97
|
+
@report_dimensions.map do |report_dimension|
|
98
|
+
report_dimension.report.extra_labels.merge({metric: report_dimension })
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class Gauge < ClearSkies::Redis::Measure
|
104
|
+
def to_partial_path
|
105
|
+
GreekFire::Gauge._to_partial_path
|
106
|
+
end
|
107
|
+
end
|
108
|
+
class Counter < ClearSkies::Redis::Measure
|
109
|
+
def to_partial_path
|
110
|
+
GreekFire::Counter._to_partial_path
|
49
111
|
end
|
50
112
|
end
|
51
113
|
end
|
52
114
|
end
|
115
|
+
|
116
|
+
GreekFire::Metric.register ClearSkies::Redis::MeasureSet.new
|
117
|
+
|
data/lib/clear_skies/version.rb
CHANGED
data/lib/clear_skies.rb
CHANGED
@@ -7,9 +7,12 @@ require "greek_fire"
|
|
7
7
|
require "aws-sdk"
|
8
8
|
|
9
9
|
require "clear_skies/app"
|
10
|
-
require "clear_skies/
|
11
|
-
require "clear_skies/cloud_watch/
|
12
|
-
require "clear_skies/cloud_watch/
|
13
|
-
require "clear_skies/cloud_watch/
|
14
|
-
require "clear_skies/cloud_watch/
|
15
|
-
require "clear_skies/
|
10
|
+
require "clear_skies/aws/reservation_utilization"
|
11
|
+
require "clear_skies/aws/cloud_watch/gauge"
|
12
|
+
require "clear_skies/aws/cloud_watch/billing"
|
13
|
+
require "clear_skies/aws/cloud_watch/rds_gauge"
|
14
|
+
require "clear_skies/aws/cloud_watch/elb_gauge"
|
15
|
+
require "clear_skies/aws/cloud_watch/elastic_beanstalk_gauge"
|
16
|
+
require "clear_skies/aws/cloud_watch/request_counter"
|
17
|
+
require "clear_skies/redis/report"
|
18
|
+
require "clear_skies/elasticsearch/report"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: clear_skies
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Constantine
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-07-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: puma
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: elasticsearch
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: greek_fire
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -121,6 +135,7 @@ files:
|
|
121
135
|
- ".ruby-version"
|
122
136
|
- Dockerfile
|
123
137
|
- Gemfile
|
138
|
+
- Gemfile.lock
|
124
139
|
- LICENSE.txt
|
125
140
|
- README.md
|
126
141
|
- Rakefile
|
@@ -130,11 +145,14 @@ files:
|
|
130
145
|
- exe/clear_skies
|
131
146
|
- lib/clear_skies.rb
|
132
147
|
- lib/clear_skies/app.rb
|
133
|
-
- lib/clear_skies/cloud_watch/
|
134
|
-
- lib/clear_skies/cloud_watch/
|
135
|
-
- lib/clear_skies/cloud_watch/
|
136
|
-
- lib/clear_skies/cloud_watch/
|
137
|
-
- lib/clear_skies/cloud_watch/
|
148
|
+
- lib/clear_skies/aws/cloud_watch/billing.rb
|
149
|
+
- lib/clear_skies/aws/cloud_watch/elastic_beanstalk_gauge.rb
|
150
|
+
- lib/clear_skies/aws/cloud_watch/elb_gauge.rb
|
151
|
+
- lib/clear_skies/aws/cloud_watch/gauge.rb
|
152
|
+
- lib/clear_skies/aws/cloud_watch/rds_gauge.rb
|
153
|
+
- lib/clear_skies/aws/cloud_watch/request_counter.rb
|
154
|
+
- lib/clear_skies/aws/reservation_utilization.rb
|
155
|
+
- lib/clear_skies/elasticsearch/report.rb
|
138
156
|
- lib/clear_skies/redis/report.rb
|
139
157
|
- lib/clear_skies/version.rb
|
140
158
|
homepage: https://github.com/omadahealth/clear_skies
|
@@ -1,45 +0,0 @@
|
|
1
|
-
module ClearSkies
|
2
|
-
module CloudWatch
|
3
|
-
class ElasticBeanstalkGauge < ClearSkies::CloudWatch::Gauge
|
4
|
-
def self.client
|
5
|
-
@client ||= Aws::ElasticBeanstalk::Client.new
|
6
|
-
end
|
7
|
-
def initialize(metric_name, dimension, statistics, description: nil, &block)
|
8
|
-
super("AWS/ElasticBeanstalk", metric_name, dimension, statistics, description: description, &block)
|
9
|
-
end
|
10
|
-
|
11
|
-
def application_name(environment_name)
|
12
|
-
ElasticBeanstalkGauge.client.describe_environments({environment_names: [environment_name] }).environments.first&.application_name
|
13
|
-
end
|
14
|
-
|
15
|
-
def vpc_id(application_name, environment_name)
|
16
|
-
config = ElasticBeanstalkGauge.client.describe_configuration_settings({ application_name: application_name, environment_name: environment_name }).
|
17
|
-
configuration_settings.find { |config| config.application_name == application_name && config.environment_name == environment_name}
|
18
|
-
option = config.option_settings.find { |option| option.namespace == "aws:ec2:vpc" && option.option_name == "VPCId"}
|
19
|
-
option.value if option
|
20
|
-
end
|
21
|
-
|
22
|
-
|
23
|
-
def labels_from_metric(metric)
|
24
|
-
labels = super(metric)
|
25
|
-
|
26
|
-
if labels.has_key?( "environment_name") && !(Rails.cache.fetch("#{labels["environment_name"]}_skip"))
|
27
|
-
application_name = Rails.cache.fetch("#{labels["environment_name"]}_application_name", expires_in: 1.hour) do
|
28
|
-
application_name(labels["environment_name"])
|
29
|
-
end
|
30
|
-
if application_name
|
31
|
-
labels["application_name"] = application_name
|
32
|
-
|
33
|
-
labels["vpc_id"] = Rails.cache.fetch("#{labels["environment_name"]}_vpc_id_") do
|
34
|
-
vpc_id(labels["application_name"], labels["environment_name"])
|
35
|
-
end
|
36
|
-
end
|
37
|
-
else
|
38
|
-
Rails.cache.write("#{labels["environment_name"]}_skip", true, expires_in: 1.hour, race_condition_ttl: 60.seconds)
|
39
|
-
end
|
40
|
-
|
41
|
-
return labels
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
@@ -1,44 +0,0 @@
|
|
1
|
-
module ClearSkies
|
2
|
-
module CloudWatch
|
3
|
-
class ELBGauge < ClearSkies::CloudWatch::Gauge
|
4
|
-
def self.client
|
5
|
-
@client ||= Aws::ElasticLoadBalancing::Client.new
|
6
|
-
end
|
7
|
-
def initialize(metric_name, dimension, statistics, description: nil, &block)
|
8
|
-
super("AWS/ELB", metric_name, dimension, statistics, description: description, &block)
|
9
|
-
end
|
10
|
-
|
11
|
-
def tags(load_balancer_name)
|
12
|
-
labels = {}
|
13
|
-
ELBGauge.client.
|
14
|
-
describe_tags({load_balancer_names: [load_balancer_name]}).
|
15
|
-
tag_descriptions.
|
16
|
-
select {|doc| doc.load_balancer_name == load_balancer_name}.each do |tag_description|
|
17
|
-
tag_description.tags.each do |tag|
|
18
|
-
labels[tag.key.downcase] = tag.value
|
19
|
-
end
|
20
|
-
end
|
21
|
-
labels
|
22
|
-
end
|
23
|
-
|
24
|
-
def labels_from_metric(metric)
|
25
|
-
labels = super(metric)
|
26
|
-
|
27
|
-
if labels.has_key?( "load_balancer_name") && !(Rails.cache.fetch("#{labels["load_balancer_name"]}_skip"))
|
28
|
-
|
29
|
-
labels["vpc_id"] = Rails.cache.fetch("#{labels["load_balancer_name"]}_vpc_id_") do
|
30
|
-
ELBGauge.client.describe_load_balancers(load_balancer_names: [labels["load_balancer_name"]]).load_balancer_descriptions.first.vpc_id
|
31
|
-
end
|
32
|
-
|
33
|
-
labels.merge!(Rails.cache.fetch("#{labels["load_balancer_name"]}_tags_", expires_in: 1.hour, race_condition_ttl: 60.seconds) do
|
34
|
-
tags(labels["load_balancer_name"])
|
35
|
-
end)
|
36
|
-
end
|
37
|
-
labels
|
38
|
-
rescue Aws::ElasticLoadBalancing::Errors::LoadBalancerNotFound
|
39
|
-
Rails.cache.write("#{labels["load_balancer_name"]}_skip", true, expires_in: 1.hour, race_condition_ttl: 60.seconds)
|
40
|
-
return labels
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
@@ -1,70 +0,0 @@
|
|
1
|
-
|
2
|
-
module ClearSkies
|
3
|
-
module CloudWatch
|
4
|
-
class Gauge < GreekFire::Gauge
|
5
|
-
include ActiveModel::Conversion
|
6
|
-
|
7
|
-
def self.register(*args, &block)
|
8
|
-
GreekFire::Metric.register(self.new(*args, &block))
|
9
|
-
end
|
10
|
-
|
11
|
-
def initialize(namespace, metric_name, dimensions, statistics, description:nil, &block)
|
12
|
-
super("#{namespace.underscore.gsub("/", "_")}_#{metric_name.underscore}", description: description)
|
13
|
-
@namespace = namespace
|
14
|
-
@metric_name = metric_name
|
15
|
-
@dimensions = dimensions
|
16
|
-
@statistics = statistics.select { |stat| ["SampleCount", "Average", "Sum", "Minimum", "Maximum"].include?(stat.to_s) }
|
17
|
-
@extended_statistics = statistics - @statistics
|
18
|
-
|
19
|
-
@block = block
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.cloudwatch_client
|
23
|
-
@client ||= Aws::CloudWatch::Client.new
|
24
|
-
end
|
25
|
-
|
26
|
-
def aws_metrics
|
27
|
-
Aws::CloudWatch::Resource.new(client: Gauge.cloudwatch_client).metrics(
|
28
|
-
namespace: @namespace,
|
29
|
-
metric_name: @metric_name,
|
30
|
-
dimensions: @dimensions.map {|dimension| {name: dimension} }
|
31
|
-
).select { |metrics| metrics.dimensions.count == @dimensions.count }
|
32
|
-
end
|
33
|
-
|
34
|
-
def labels_from_metric(metric)
|
35
|
-
metric.dimensions.inject(ActiveSupport::HashWithIndifferentAccess.new) do |labels, dimension|
|
36
|
-
labels[dimension.name.underscore] = dimension.value
|
37
|
-
labels
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def metrics
|
42
|
-
aws_metrics.map do |metric|
|
43
|
-
labels = labels_from_metric(metric)
|
44
|
-
|
45
|
-
next unless @block.call(labels) if @block
|
46
|
-
|
47
|
-
stats = metric.get_statistics(
|
48
|
-
start_time: Time.now.advance(minutes: -6),
|
49
|
-
end_time: Time.now.advance(minutes: -5),
|
50
|
-
period: 1,
|
51
|
-
statistics: @statistics,
|
52
|
-
extended_statistics: @extended_statistics,
|
53
|
-
dimensions: metric.dimensions
|
54
|
-
)
|
55
|
-
|
56
|
-
stats.datapoints.map do |datapoint|
|
57
|
-
datapoint.to_h.select {|k, v| ![:unit, :timestamp].include?(k) }.map do |key, value|
|
58
|
-
if (key == :extended_statistics)
|
59
|
-
value.map {|e_key, e_value| GreekFire::Metric.new(name, labels.merge({statistic: e_key}), e_value)}
|
60
|
-
else
|
61
|
-
GreekFire::Metric.new(name, labels.merge({statistic: key}), value)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
end.flatten.compact
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
module ClearSkies
|
2
|
-
module CloudWatch
|
3
|
-
class RDSGauge < ClearSkies::CloudWatch::Gauge
|
4
|
-
def initialize(metric_name, dimension, statistics, description: nil, &block)
|
5
|
-
super("AWS/RDS", metric_name, dimension, statistics, description: description, &block)
|
6
|
-
end
|
7
|
-
|
8
|
-
def tags(db)
|
9
|
-
labels = {}
|
10
|
-
db.client.list_tags_for_resource(resource_name: db.db_instance_arn).tag_list.each do |tag|
|
11
|
-
labels[tag.key.downcase] = tag.value
|
12
|
-
end
|
13
|
-
labels
|
14
|
-
end
|
15
|
-
|
16
|
-
def labels_from_metric(metric)
|
17
|
-
labels = super(metric)
|
18
|
-
|
19
|
-
if labels.has_key?( "db_instance_identifier") && !(Rails.cache.fetch("#{labels["db_instance_identifier"]}_skip"))
|
20
|
-
db = Aws::RDS::DBInstance.new(labels["db_instance_identifier"])
|
21
|
-
|
22
|
-
labels["vpc_id"] = Rails.cache.fetch("#{labels["db_instance_identifier"]}_vpc_id_") do
|
23
|
-
db.db_subnet_group.vpc_id
|
24
|
-
end
|
25
|
-
|
26
|
-
labels.merge!(Rails.cache.fetch("#{labels["db_instance_identifier"]}_tags_", expires_in: 1.hour, race_condition_ttl: 60.seconds) do
|
27
|
-
tags(db)
|
28
|
-
end)
|
29
|
-
end
|
30
|
-
labels
|
31
|
-
rescue Aws::RDS::Errors::DBInstanceNotFound
|
32
|
-
Rails.cache.write("#{labels["db_instance_identifier"]}_skip", true, expires_in: 1.hour, race_condition_ttl: 60.seconds)
|
33
|
-
return labels
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
module ClearSkies
|
2
|
-
module CloudWatch
|
3
|
-
class RequestCounter < Seahorse::Client::Handler
|
4
|
-
@mutex = Mutex.new
|
5
|
-
def self.increment
|
6
|
-
@mutex.synchronize do
|
7
|
-
@count ||= 0
|
8
|
-
@count += 1
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.count
|
13
|
-
@count ||= 0
|
14
|
-
@count
|
15
|
-
end
|
16
|
-
|
17
|
-
def initialize(handler)
|
18
|
-
@handler = handler
|
19
|
-
end
|
20
|
-
|
21
|
-
def call(context)
|
22
|
-
RequestCounter.increment
|
23
|
-
@handler.call(context)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
class RequestCounterPlugin < Seahorse::Client::Plugin
|
28
|
-
|
29
|
-
def add_handlers(handlers, config)
|
30
|
-
handlers.add(RequestCounter, step: :validate)
|
31
|
-
end
|
32
|
-
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
Aws::CloudWatch::Client.add_plugin(ClearSkies::CloudWatch::RequestCounterPlugin)
|