chef-handler-datadog 0.9.0 → 0.10.0.rc1
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 +4 -4
- data/.travis.yml +6 -0
- data/Appraisals +9 -1
- data/CHANGELOG.md +15 -0
- data/chef-handler-datadog.gemspec +1 -1
- data/gemfiles/chef_11.gemfile +2 -0
- data/lib/chef/handler/datadog.rb +70 -12
- data/lib/chef/handler/datadog_chef_events.rb +12 -20
- data/lib/chef/handler/datadog_chef_metrics.rb +8 -15
- data/lib/chef/handler/datadog_chef_tags.rb +33 -34
- data/lib/chef_handler_datadog.rb +1 -1
- data/spec/datadog_spec.rb +266 -7
- data/spec/support/cassettes/Chef_Handler_Datadog/failed_Chef_run/sets_alert_handles_when_specified.yml +90 -12
- data/spec/support/cassettes/Chef_Handler_Datadog/failed_Chef_run/sets_event_title_correctly.yml +45 -6
- data/spec/support/cassettes/Chef_Handler_Datadog/failed_Chef_run/sets_priority_correctly.yml +45 -6
- data/spec/support/cassettes/Chef_Handler_Datadog/hostname/uses_the_node_name_when_no_config_specified.yml +45 -6
- data/spec/support/cassettes/Chef_Handler_Datadog/hostname/uses_the_specified_hostname_when_provided.yml +45 -6
- data/spec/support/cassettes/Chef_Handler_Datadog/reports_correct_hostname_on_an_ec2_node/does_not_use_the_instance_id_when_config_specified_to_false.yml +45 -6
- data/spec/support/cassettes/Chef_Handler_Datadog/reports_correct_hostname_on_an_ec2_node/uses_the_instance_id_when_config_is_specified.yml +45 -6
- data/spec/support/cassettes/Chef_Handler_Datadog/reports_correct_hostname_on_an_ec2_node/uses_the_instance_id_when_no_config_specified.yml +45 -6
- data/spec/support/cassettes/Chef_Handler_Datadog/reports_metrics_event_and_sets_tags/emits_events/posts_an_event.yml +45 -6
- data/spec/support/cassettes/Chef_Handler_Datadog/reports_metrics_event_and_sets_tags/emits_events/sets_priority_correctly.yml +45 -6
- data/spec/support/cassettes/Chef_Handler_Datadog/reports_metrics_event_and_sets_tags/emits_metrics/reports_metrics.yml +45 -6
- data/spec/support/cassettes/Chef_Handler_Datadog/reports_metrics_event_and_sets_tags/sets_tags/puts_the_tags_for_the_current_node.yml +45 -6
- data/spec/support/cassettes/Chef_Handler_Datadog/resources/failure_during_compile_phase/{only_emits_a_failure_metric.yml → only_emits_the_run_status_metrics.yml} +43 -4
- data/spec/support/cassettes/Chef_Handler_Datadog/resources/failure_during_compile_phase/posts_an_event.yml +43 -4
- data/spec/support/cassettes/Chef_Handler_Datadog/tags/when_specified/allows_for_empty_scope_prefix.yml +290 -0
- data/spec/support/cassettes/Chef_Handler_Datadog/tags/when_specified/allows_for_empty_tag_prefix.yml +84 -6
- data/spec/support/cassettes/Chef_Handler_Datadog/tags/when_specified/allows_for_user-specified_scope_prefix.yml +290 -0
- data/spec/support/cassettes/Chef_Handler_Datadog/tags/when_specified/allows_for_user-specified_tag_prefix.yml +45 -6
- data/spec/support/cassettes/Chef_Handler_Datadog/tags/when_specified/sets_the_role_and_env_and_tags.yml +45 -6
- data/spec/support/cassettes/Chef_Handler_Datadog/tags/when_tag_blacklist_is_specified/does_not_include_the_tag_s_specified.yml +289 -0
- data/spec/support/cassettes/Chef_Handler_Datadog/tags/when_tag_blacklist_is_unspecified/should_include_all_of_the_tag_s_.yml +289 -0
- data/spec/support/cassettes/Chef_Handler_Datadog/tags/when_unspecified/sets_role_env_and_nothing_else.yml +45 -6
- data/spec/support/cassettes/Chef_Handler_Datadog/tags_submission_retries/when_not_specified/does_not_retry_after_a_failed_submission.yml +37 -0
- data/spec/support/cassettes/Chef_Handler_Datadog/tags_submission_retries/when_specified_as_2_retries/retries_no_more_than_twice.yml +37 -0
- data/spec/support/cassettes/Chef_Handler_Datadog/tags_submission_retries/when_specified_as_2_retries/stops_retrying_once_submission_is_successful.yml +37 -0
- data/spec/support/cassettes/Chef_Handler_Datadog/updated_resources/posts_an_event.yml +45 -6
- data/spec/support/cassettes/Chef_Handler_Datadog/when_reporting_to_multiple_endpoints/emits_events/posts_an_event.yml +575 -0
- data/spec/support/cassettes/Chef_Handler_Datadog/when_reporting_to_multiple_endpoints/emits_metrics/reports_metrics.yml +575 -0
- data/spec/support/cassettes/Chef_Handler_Datadog/when_reporting_to_multiple_endpoints/sets_tags/puts_the_tags_for_the_current_node.yml +575 -0
- metadata +24 -11
- data/spec/support/cassettes/Chef_Handler_Datadog/resources/failure_during_compile_phase/does_not_emit_metrics.yml +0 -82
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f22fc0a3aee09aa96893c748ed07af486d6d20b1
|
|
4
|
+
data.tar.gz: a0cc1fb8210e79b63b4426cb3698403e7b4c4e72
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c1f2f15ac30d0c56271703fe698f3a03b09b2654df96edc87bc744ec6b4702d59590a84455f28398663059cff19419d3372b5b09520dcea7d4ddb17c0dce2be7
|
|
7
|
+
data.tar.gz: bbb290f4667294fb9c9e03fbaa4c652877a5b44f6904083aee69d8aca7b8a7e3b9c9da29ab8336d833242ac7fda4bc1589fd8ec4312f83ae7c3beef0f8f16515
|
data/.travis.yml
CHANGED
data/Appraisals
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
|
|
3
3
|
# Latest release of mainline Chef versions here.
|
|
4
|
-
%w(10
|
|
4
|
+
%w(10 12).each do |tv|
|
|
5
5
|
appraise "chef-#{tv}" do
|
|
6
6
|
gem 'chef', "~> #{tv}.0"
|
|
7
7
|
end
|
|
@@ -14,3 +14,11 @@ appraise 'chef-10.14.4' do
|
|
|
14
14
|
# See: http://git.io/vecAn
|
|
15
15
|
gem 'ohai', '< 8.0'
|
|
16
16
|
end
|
|
17
|
+
|
|
18
|
+
appraise 'chef-11' do
|
|
19
|
+
gem 'chef', '~> 11.0'
|
|
20
|
+
# for some reason bundler installs json 2.x and rack 2.x even when they don't support the version of
|
|
21
|
+
# ruby installed, so let's force compatible versions here
|
|
22
|
+
gem 'json', '< 2.0'
|
|
23
|
+
gem 'rack', '< 2.0'
|
|
24
|
+
end
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
Changes
|
|
2
2
|
=======
|
|
3
3
|
|
|
4
|
+
# 0.10.0 / Unreleased
|
|
5
|
+
|
|
6
|
+
* [FEATURE] Allow passing `scope_prefix` param that prefixes env and role tags [#82][] [@DanielMuller][]
|
|
7
|
+
* [FEATURE] Support regex tag blacklist [#86][] [@ABrehm264][]
|
|
8
|
+
* [FEATURE] Support multiple endpoints [#87][] [@degemer][]
|
|
9
|
+
* [OPTIMIZE] Always emit success and failure metrics [#89][] [@devonbleak][]
|
|
10
|
+
|
|
4
11
|
# 0.9.0 / 2016-03-22
|
|
5
12
|
|
|
6
13
|
* [FEATURE] Enable users to select tag prefix [#81][] [@mstepniowski][]
|
|
@@ -89,9 +96,17 @@ And all other versions were prior to this. See git history for more.
|
|
|
89
96
|
[#78]: https://github.com/DataDog/chef-handler-datadog/issues/78
|
|
90
97
|
[#80]: https://github.com/DataDog/chef-handler-datadog/issues/80
|
|
91
98
|
[#81]: https://github.com/DataDog/chef-handler-datadog/issues/81
|
|
99
|
+
[#82]: https://github.com/DataDog/chef-handler-datadog/issues/82
|
|
100
|
+
[#86]: https://github.com/DataDog/chef-handler-datadog/issues/86
|
|
101
|
+
[#87]: https://github.com/DataDog/chef-handler-datadog/issues/87
|
|
102
|
+
[#89]: https://github.com/DataDog/chef-handler-datadog/issues/89
|
|
103
|
+
[@ABrehm264]: https://github.com/ABrehm264
|
|
104
|
+
[@DanielMuller]: https://github.com/DanielMuller
|
|
92
105
|
[@alq]: https://github.com/alq
|
|
93
106
|
[@bigbam505]: https://github.com/bigbam505
|
|
94
107
|
[@datwiz]: https://github.com/datwiz
|
|
108
|
+
[@degemer]: https://github.com/degemer
|
|
109
|
+
[@devonbleak]: https://github.com/devonbleak
|
|
95
110
|
[@dwradcliffe]: https://github.com/dwradcliffe
|
|
96
111
|
[@jhulten]: https://github.com/jhulten
|
|
97
112
|
[@miketheman]: https://github.com/miketheman
|
|
@@ -14,7 +14,7 @@ Gem::Specification.new do |gem|
|
|
|
14
14
|
gem.require_paths = ['lib']
|
|
15
15
|
gem.extra_rdoc_files = ['README.md', 'LICENSE.txt']
|
|
16
16
|
|
|
17
|
-
gem.add_dependency 'dogapi', '>= 1.
|
|
17
|
+
gem.add_dependency 'dogapi', '>= 1.23'
|
|
18
18
|
|
|
19
19
|
gem.add_development_dependency 'appraisal', '~> 2.0.1'
|
|
20
20
|
gem.add_development_dependency 'bundler'
|
data/gemfiles/chef_11.gemfile
CHANGED
data/lib/chef/handler/datadog.rb
CHANGED
|
@@ -18,8 +18,8 @@ class Chef
|
|
|
18
18
|
# It should be passed along from the node/role/environemnt attributes, as the default is nil.
|
|
19
19
|
def initialize(config = {})
|
|
20
20
|
@config = Mash.new(config)
|
|
21
|
-
|
|
22
|
-
@
|
|
21
|
+
|
|
22
|
+
@dogs = prepare_the_pack
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def report
|
|
@@ -28,8 +28,11 @@ class Chef
|
|
|
28
28
|
|
|
29
29
|
# prepare the metrics, event, and tags information to be reported
|
|
30
30
|
prepare_report_for_datadog
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
|
|
32
|
+
@dogs.each do |dog|
|
|
33
|
+
# post the report information to the datadog service
|
|
34
|
+
send_report_to_datadog dog
|
|
35
|
+
end
|
|
33
36
|
ensure
|
|
34
37
|
# restore the env proxy settings before leaving to avoid downstream side-effects
|
|
35
38
|
restore_env_proxies unless ENV['DATADOG_PROXY'].nil?
|
|
@@ -44,24 +47,22 @@ class Chef
|
|
|
44
47
|
# prepare chef run metrics
|
|
45
48
|
@metrics =
|
|
46
49
|
DatadogChefMetrics.new
|
|
47
|
-
.with_dogapi_client(@dog)
|
|
48
50
|
.with_hostname(hostname)
|
|
49
51
|
.with_run_status(run_status)
|
|
50
52
|
|
|
51
53
|
# Collect and prepare tags
|
|
52
54
|
@tags =
|
|
53
55
|
DatadogChefTags.new
|
|
54
|
-
.with_dogapi_client(@dog)
|
|
55
56
|
.with_hostname(hostname)
|
|
56
57
|
.with_run_status(run_status)
|
|
57
|
-
.with_application_key(config[:application_key])
|
|
58
58
|
.with_tag_prefix(config[:tag_prefix])
|
|
59
59
|
.with_retries(config[:tags_submission_retries])
|
|
60
|
+
.with_tag_blacklist(config[:tags_blacklist_regex])
|
|
61
|
+
.with_scope_prefix(config[:scope_prefix])
|
|
60
62
|
|
|
61
63
|
# Build the chef event information
|
|
62
64
|
@event =
|
|
63
65
|
DatadogChefEvents.new
|
|
64
|
-
.with_dogapi_client(@dog)
|
|
65
66
|
.with_hostname(hostname)
|
|
66
67
|
.with_run_status(run_status)
|
|
67
68
|
.with_failure_notifications(@config['notify_on_failure'])
|
|
@@ -69,10 +70,12 @@ class Chef
|
|
|
69
70
|
end
|
|
70
71
|
|
|
71
72
|
# Submit metrics, event, and tags information to datadog
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
@
|
|
73
|
+
#
|
|
74
|
+
# @param dog [Dogapi::Client] Dogapi Client to be used
|
|
75
|
+
def send_report_to_datadog(dog)
|
|
76
|
+
@metrics.emit_to_datadog dog
|
|
77
|
+
@event.emit_to_datadog dog
|
|
78
|
+
@tags.send_update_to_datadog dog
|
|
76
79
|
rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT => e
|
|
77
80
|
Chef::Log.error("Could not connect to Datadog. Connection error:\n" + e)
|
|
78
81
|
Chef::Log.error('Data to be submitted was:')
|
|
@@ -118,6 +121,61 @@ class Chef
|
|
|
118
121
|
ENV['http_proxy'] = @env_http_proxy
|
|
119
122
|
ENV['https_proxy'] = @env_https_proxy
|
|
120
123
|
end
|
|
124
|
+
|
|
125
|
+
# create and configure all the Dogapi Clients to be used
|
|
126
|
+
#
|
|
127
|
+
# @return [Array] all Dogapi::Client to be used
|
|
128
|
+
def prepare_the_pack
|
|
129
|
+
dogs = []
|
|
130
|
+
endpoints.each do |url, api_key, app_key|
|
|
131
|
+
dogs.push(Dogapi::Client.new(
|
|
132
|
+
api_key,
|
|
133
|
+
app_key,
|
|
134
|
+
nil, # host
|
|
135
|
+
nil, # device
|
|
136
|
+
true, # silent
|
|
137
|
+
nil, # timeout
|
|
138
|
+
url
|
|
139
|
+
))
|
|
140
|
+
end
|
|
141
|
+
dogs
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# return all endpoints as a list of triplets [url, api_key, application_key]
|
|
145
|
+
def endpoints
|
|
146
|
+
validate_keys(@config[:api_key], @config[:application_key], true)
|
|
147
|
+
|
|
148
|
+
endpoints = [[@config[:url], @config[:api_key], @config[:application_key]]]
|
|
149
|
+
|
|
150
|
+
extra_endpoints = @config[:extra_endpoints] || []
|
|
151
|
+
|
|
152
|
+
extra_endpoints.each do |endpoint|
|
|
153
|
+
url = endpoint[:url] || @config[:url]
|
|
154
|
+
api_key = endpoint[:api_key]
|
|
155
|
+
app_key = endpoint[:application_key]
|
|
156
|
+
endpoints << [url, api_key, app_key] if validate_keys(api_key, app_key, false)
|
|
157
|
+
end
|
|
158
|
+
endpoints
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Validate endpoints config (api_key and application key)
|
|
162
|
+
# fails if incorrect and should_fail is true (needed for the default)
|
|
163
|
+
# Doesn't fail for the other endpoints but logs a warning
|
|
164
|
+
def validate_keys(api_key, app_key, should_fail)
|
|
165
|
+
if api_key.nil?
|
|
166
|
+
Chef::Log.warn('You need an API key to communicate with Datadog')
|
|
167
|
+
fail ArgumentError, 'Missing Datadog Api Key' if should_fail
|
|
168
|
+
return false
|
|
169
|
+
end
|
|
170
|
+
if app_key.nil?
|
|
171
|
+
Chef::Log.warn('You need an application key to let Chef tag your nodes ' \
|
|
172
|
+
'in Datadog. Visit https://app.datadoghq.com/account/settings#api to ' \
|
|
173
|
+
'create one and update your datadog attributes in the datadog cookbook.')
|
|
174
|
+
fail ArgumentError, 'Missing Datadog Application Key' if should_fail
|
|
175
|
+
return false
|
|
176
|
+
end
|
|
177
|
+
true
|
|
178
|
+
end
|
|
121
179
|
end # end class Datadog
|
|
122
180
|
end # end class Handler
|
|
123
181
|
end # end class Chef
|
|
@@ -7,7 +7,6 @@ require 'dogapi'
|
|
|
7
7
|
# helper class for sending events about chef runs
|
|
8
8
|
class DatadogChefEvents
|
|
9
9
|
def initialize
|
|
10
|
-
@dog = nil
|
|
11
10
|
@hostname = nil
|
|
12
11
|
@run_status = nil
|
|
13
12
|
@failure_notfications = nil
|
|
@@ -21,15 +20,6 @@ class DatadogChefEvents
|
|
|
21
20
|
@event_body = ''
|
|
22
21
|
end
|
|
23
22
|
|
|
24
|
-
# set the dogapi client handle
|
|
25
|
-
#
|
|
26
|
-
# @param dogapi_client [Dogapi::Client] datadog api client handle
|
|
27
|
-
# @return [DatadogChefTags] instance reference to self enabling method chaining
|
|
28
|
-
def with_dogapi_client(dogapi_client)
|
|
29
|
-
@dog = dogapi_client
|
|
30
|
-
self
|
|
31
|
-
end
|
|
32
|
-
|
|
33
23
|
# set the target hostname (chef node name)
|
|
34
24
|
#
|
|
35
25
|
# @param hostname [String] hostname to use for the handler report
|
|
@@ -67,18 +57,20 @@ class DatadogChefEvents
|
|
|
67
57
|
end
|
|
68
58
|
|
|
69
59
|
# Emit Chef event to Datadog
|
|
70
|
-
|
|
60
|
+
#
|
|
61
|
+
# @param dog [Dogapi::Client] Dogapi Client to be used
|
|
62
|
+
def emit_to_datadog(dog)
|
|
71
63
|
@event_body = ''
|
|
72
64
|
build_event_data
|
|
73
|
-
evt =
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
65
|
+
evt = dog.emit_event(Dogapi::Event.new(@event_body,
|
|
66
|
+
msg_title: @event_title,
|
|
67
|
+
event_type: 'config_management.run',
|
|
68
|
+
event_object: @hostname,
|
|
69
|
+
alert_type: @alert_type,
|
|
70
|
+
priority: @event_priority,
|
|
71
|
+
source_type_name: 'chef',
|
|
72
|
+
tags: @tags
|
|
73
|
+
), host: @hostname)
|
|
82
74
|
|
|
83
75
|
begin
|
|
84
76
|
# FIXME: nice-to-have: abstract format of return value away a bit
|
|
@@ -4,20 +4,10 @@ require 'dogapi'
|
|
|
4
4
|
# helper class for sending datadog metrics from a chef run
|
|
5
5
|
class DatadogChefMetrics
|
|
6
6
|
def initialize
|
|
7
|
-
@dog = nil
|
|
8
7
|
@hostname = ''
|
|
9
8
|
@run_status = nil
|
|
10
9
|
end
|
|
11
10
|
|
|
12
|
-
# set the dogapi client handle
|
|
13
|
-
#
|
|
14
|
-
# @param dogapi_client [Dogapi::Client] datadog api client
|
|
15
|
-
# @return [DatadogChefMetrics] instance reference to self enabling method chaining
|
|
16
|
-
def with_dogapi_client(dogapi_client)
|
|
17
|
-
@dog = dogapi_client
|
|
18
|
-
self
|
|
19
|
-
end
|
|
20
|
-
|
|
21
11
|
# set the target hostname (chef node name)
|
|
22
12
|
#
|
|
23
13
|
# @param hostname [String] hostname used for reporting metrics
|
|
@@ -37,18 +27,21 @@ class DatadogChefMetrics
|
|
|
37
27
|
end
|
|
38
28
|
|
|
39
29
|
# Emit Chef metrics to Datadog
|
|
40
|
-
|
|
30
|
+
#
|
|
31
|
+
# @param dog [Dogapi::Client] Dogapi Client to be used
|
|
32
|
+
def emit_to_datadog(dog)
|
|
41
33
|
# Send base success/failure metric
|
|
42
|
-
|
|
34
|
+
dog.emit_point('chef.run.success', @run_status.success? ? 1 : 0, host: @hostname, type: 'counter')
|
|
35
|
+
dog.emit_point('chef.run.failure', @run_status.success? ? 0 : 1, host: @hostname, type: 'counter')
|
|
43
36
|
|
|
44
37
|
# If there is a failure during compile phase, a large portion of
|
|
45
38
|
# run_status may be unavailable. Bail out here
|
|
46
39
|
warn_msg = 'Error during compile phase, no Datadog metrics available.'
|
|
47
40
|
return Chef::Log.warn(warn_msg) if compile_error?
|
|
48
41
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
42
|
+
dog.emit_point('chef.resources.total', @run_status.all_resources.length, host: @hostname)
|
|
43
|
+
dog.emit_point('chef.resources.updated', @run_status.updated_resources.length, host: @hostname)
|
|
44
|
+
dog.emit_point('chef.resources.elapsed_time', @run_status.elapsed_time, host: @hostname)
|
|
52
45
|
Chef::Log.debug('Submitted Chef metrics back to Datadog')
|
|
53
46
|
rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT => e
|
|
54
47
|
Chef::Log.error("Could not send metrics to Datadog. Connection error:\n" + e)
|
|
@@ -11,17 +11,10 @@ class DatadogChefTags
|
|
|
11
11
|
@run_status = nil
|
|
12
12
|
@application_key = nil
|
|
13
13
|
@tag_prefix = 'tag:'
|
|
14
|
+
@scope_prefix = nil
|
|
14
15
|
@retries = 0
|
|
15
16
|
@combined_host_tags = nil
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
# set the dogapi client handle
|
|
19
|
-
#
|
|
20
|
-
# @param dogapi_client [Dogapi::Client] datadog api client handle
|
|
21
|
-
# @return [DatadogChefTags] instance reference to self enabling method chaining
|
|
22
|
-
def with_dogapi_client(dogapi_client)
|
|
23
|
-
@dog = dogapi_client
|
|
24
|
-
self
|
|
17
|
+
@regex_black_list = nil
|
|
25
18
|
end
|
|
26
19
|
|
|
27
20
|
# set the chef run status used for the report
|
|
@@ -46,26 +39,6 @@ class DatadogChefTags
|
|
|
46
39
|
self
|
|
47
40
|
end
|
|
48
41
|
|
|
49
|
-
# set the datadog application key
|
|
50
|
-
#
|
|
51
|
-
# TODO: the application key is only needed for error checking, e.g. an app key exists
|
|
52
|
-
# would be cleaner to push this check up to the data prep method in the
|
|
53
|
-
# calling handler class
|
|
54
|
-
#
|
|
55
|
-
# @param application_key [String] datadog application key used for chef reports
|
|
56
|
-
# @return [DatadogChefTags] instance reference to self enabling method chaining
|
|
57
|
-
def with_application_key(application_key)
|
|
58
|
-
@application_key = application_key
|
|
59
|
-
if @application_key.nil?
|
|
60
|
-
Chef::Log.warn('You need an application key to let Chef tag your nodes ' \
|
|
61
|
-
'in Datadog. Visit https://app.datadoghq.com/account/settings#api to ' \
|
|
62
|
-
'create one and update your datadog attributes in the datadog cookbook.'
|
|
63
|
-
)
|
|
64
|
-
fail ArgumentError, 'Missing Datadog Application Key'
|
|
65
|
-
end
|
|
66
|
-
self
|
|
67
|
-
end
|
|
68
|
-
|
|
69
42
|
# set the prefix to be added to all Chef tags
|
|
70
43
|
#
|
|
71
44
|
# @param tag_prefix [String] prefix to be added to all Chef tags
|
|
@@ -85,14 +58,34 @@ class DatadogChefTags
|
|
|
85
58
|
self
|
|
86
59
|
end
|
|
87
60
|
|
|
61
|
+
# set the blacklist regexp, node tags matching this regex won't be sent
|
|
62
|
+
#
|
|
63
|
+
# @param tags_blacklist_regex [String] regexp-formatted string
|
|
64
|
+
# @return [DatadogChefTags] instance reference to self enabling method chaining
|
|
65
|
+
def with_tag_blacklist(tags_blacklist_regex)
|
|
66
|
+
@regex_black_list = Regexp.new(tags_blacklist_regex, Regexp::IGNORECASE) unless tags_blacklist_regex.nil? || tags_blacklist_regex.empty?
|
|
67
|
+
self
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# set the prefix to be added to Datadog tags (Role, Env)
|
|
71
|
+
#
|
|
72
|
+
# @param scope_prefix [String] prefix to be added to Datadog tags
|
|
73
|
+
# @return [DatadogChefTags] instance reference to self enabling method chaining
|
|
74
|
+
def with_scope_prefix(scope_prefix)
|
|
75
|
+
@scope_prefix = scope_prefix unless scope_prefix.nil?
|
|
76
|
+
self
|
|
77
|
+
end
|
|
78
|
+
|
|
88
79
|
# send updated chef run generated tags to Datadog
|
|
89
|
-
|
|
80
|
+
#
|
|
81
|
+
# @param dog [Dogapi::Client] Dogapi Client to be used
|
|
82
|
+
def send_update_to_datadog(dog)
|
|
90
83
|
tags = combined_host_tags
|
|
91
84
|
retries = @retries
|
|
92
85
|
begin
|
|
93
86
|
loop do
|
|
94
87
|
should_retry = false
|
|
95
|
-
rc =
|
|
88
|
+
rc = dog.update_tags(@hostname, tags, 'chef')
|
|
96
89
|
# See FIXME in DatadogChefEvents::emit_to_datadog about why I feel dirty repeating this code here
|
|
97
90
|
if rc.length < 2
|
|
98
91
|
Chef::Log.warn("Unexpected response from Datadog Tags API: #{rc}")
|
|
@@ -126,15 +119,21 @@ class DatadogChefTags
|
|
|
126
119
|
private
|
|
127
120
|
|
|
128
121
|
def node_roles
|
|
129
|
-
@node.run_list.roles.map! { |role|
|
|
122
|
+
@node.run_list.roles.map! { |role| "#{@scope_prefix}role:#{role}" }
|
|
130
123
|
end
|
|
131
124
|
|
|
132
125
|
def node_env
|
|
133
|
-
|
|
126
|
+
"#{@scope_prefix}env:#{@node.chef_environment}" if @node.respond_to?('chef_environment')
|
|
134
127
|
end
|
|
135
128
|
|
|
136
129
|
def node_tags
|
|
137
130
|
return [] unless @node.tags
|
|
138
|
-
@node.tags.map { |tag| "#{@tag_prefix}#{tag}" }
|
|
131
|
+
output = @node.tags.map { |tag| "#{@tag_prefix}#{tag}" }
|
|
132
|
+
|
|
133
|
+
# No blacklist, return all results
|
|
134
|
+
return output if @regex_black_list.nil?
|
|
135
|
+
|
|
136
|
+
# The blacklist is set, so return the items which are not filtered by it.
|
|
137
|
+
output.select { |t| !@regex_black_list.match(t) }
|
|
139
138
|
end
|
|
140
139
|
end # end class DatadogChefTags
|
data/lib/chef_handler_datadog.rb
CHANGED
data/spec/datadog_spec.rb
CHANGED
|
@@ -29,6 +29,7 @@ describe Chef::Handler::Datadog, :vcr => :new_episodes do
|
|
|
29
29
|
'api_key' => API_KEY,
|
|
30
30
|
'application_key' => APPLICATION_KEY,
|
|
31
31
|
'tag_prefix' => 'tag',
|
|
32
|
+
'scope_prefix' => nil
|
|
32
33
|
)
|
|
33
34
|
end
|
|
34
35
|
end
|
|
@@ -57,7 +58,7 @@ describe Chef::Handler::Datadog, :vcr => :new_episodes do
|
|
|
57
58
|
it 'reports metrics' do
|
|
58
59
|
expect(a_request(:post, METRICS_ENDPOINT).with(
|
|
59
60
|
:query => { 'api_key' => @handler.config[:api_key] }
|
|
60
|
-
)).to have_been_made.times(
|
|
61
|
+
)).to have_been_made.times(5)
|
|
61
62
|
end
|
|
62
63
|
end
|
|
63
64
|
|
|
@@ -246,6 +247,34 @@ describe Chef::Handler::Datadog, :vcr => :new_episodes do
|
|
|
246
247
|
]),
|
|
247
248
|
)).to have_been_made.times(1)
|
|
248
249
|
end
|
|
250
|
+
|
|
251
|
+
it 'allows for user-specified scope prefix' do
|
|
252
|
+
@handler.config[:scope_prefix] = 'custom-prefix-'
|
|
253
|
+
@handler.run_report_unsafe(@run_status)
|
|
254
|
+
|
|
255
|
+
expect(a_request(:put, HOST_TAG_ENDPOINT + @node.name).with(
|
|
256
|
+
:query => { 'api_key' => @handler.config[:api_key],
|
|
257
|
+
'application_key' => @handler.config[:application_key],
|
|
258
|
+
'source' => 'chef' },
|
|
259
|
+
:body => hash_including(:tags => [
|
|
260
|
+
'custom-prefix-env:hostile', 'custom-prefix-role:highlander'
|
|
261
|
+
]),
|
|
262
|
+
)).to have_been_made.times(1)
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
it 'allows for empty scope prefix' do
|
|
266
|
+
@handler.config[:scope_prefix] = ''
|
|
267
|
+
@handler.run_report_unsafe(@run_status)
|
|
268
|
+
|
|
269
|
+
expect(a_request(:put, HOST_TAG_ENDPOINT + @node.name).with(
|
|
270
|
+
:query => { 'api_key' => @handler.config[:api_key],
|
|
271
|
+
'application_key' => @handler.config[:application_key],
|
|
272
|
+
'source' => 'chef' },
|
|
273
|
+
:body => hash_including(:tags => [
|
|
274
|
+
'env:hostile', 'role:highlander'
|
|
275
|
+
]),
|
|
276
|
+
)).to have_been_made.times(1)
|
|
277
|
+
end
|
|
249
278
|
end
|
|
250
279
|
|
|
251
280
|
describe 'when unspecified' do
|
|
@@ -262,6 +291,39 @@ describe Chef::Handler::Datadog, :vcr => :new_episodes do
|
|
|
262
291
|
)).to have_been_made.times(1)
|
|
263
292
|
end
|
|
264
293
|
end
|
|
294
|
+
|
|
295
|
+
describe 'when tag blacklist is specified' do
|
|
296
|
+
it 'does not include the tag(s) specified' do
|
|
297
|
+
@node.normal.tags = ['allowed_tag', 'not_allowed_tag']
|
|
298
|
+
@handler.config[:tags_blacklist_regex] = 'not_allowed.*'
|
|
299
|
+
@handler.run_report_unsafe(@run_status)
|
|
300
|
+
|
|
301
|
+
expect(a_request(:put, HOST_TAG_ENDPOINT + @node.name).with(
|
|
302
|
+
:query => { 'api_key' => @handler.config[:api_key],
|
|
303
|
+
'application_key' => @handler.config[:application_key],
|
|
304
|
+
'source' => 'chef' },
|
|
305
|
+
:body => hash_including(:tags => [
|
|
306
|
+
'env:hostile', 'role:highlander', 'tag:allowed_tag'
|
|
307
|
+
]),
|
|
308
|
+
)).to have_been_made.times(1)
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
describe 'when tag blacklist is unspecified' do
|
|
313
|
+
it 'should include all of the tag(s)' do
|
|
314
|
+
@node.normal.tags = ['allowed_tag', 'not_allowed_tag']
|
|
315
|
+
@handler.run_report_unsafe(@run_status)
|
|
316
|
+
|
|
317
|
+
expect(a_request(:put, HOST_TAG_ENDPOINT + @node.name).with(
|
|
318
|
+
:query => { 'api_key' => @handler.config[:api_key],
|
|
319
|
+
'application_key' => @handler.config[:application_key],
|
|
320
|
+
'source' => 'chef' },
|
|
321
|
+
:body => hash_including(:tags => [
|
|
322
|
+
'env:hostile', 'role:highlander', 'tag:allowed_tag', 'tag:not_allowed_tag'
|
|
323
|
+
]),
|
|
324
|
+
)).to have_been_made.times(1)
|
|
325
|
+
end
|
|
326
|
+
end
|
|
265
327
|
end
|
|
266
328
|
|
|
267
329
|
context 'tags submission retries' do
|
|
@@ -355,11 +417,9 @@ describe Chef::Handler::Datadog, :vcr => :new_episodes do
|
|
|
355
417
|
end
|
|
356
418
|
|
|
357
419
|
it 'fails when no application key is provided' do
|
|
358
|
-
@handler.config[:application_key] = nil
|
|
359
|
-
|
|
360
420
|
# TODO: figure out how to capture output of Chef::Log
|
|
361
421
|
# Run the report, catch the error
|
|
362
|
-
expect {
|
|
422
|
+
expect { Chef::Handler::Datadog.new(api_key: API_KEY, application_key: nil) }.to raise_error
|
|
363
423
|
end
|
|
364
424
|
end
|
|
365
425
|
|
|
@@ -382,7 +442,7 @@ describe Chef::Handler::Datadog, :vcr => :new_episodes do
|
|
|
382
442
|
Chef::Resource.new('nose'),
|
|
383
443
|
Chef::Resource.new('tail'),
|
|
384
444
|
Chef::Resource.new('fur')
|
|
385
|
-
|
|
445
|
+
]
|
|
386
446
|
all_resources.map { |r| r.updated_by_last_action(true) }
|
|
387
447
|
@run_context.resource_collection.all_resources.replace(all_resources)
|
|
388
448
|
|
|
@@ -474,10 +534,10 @@ describe Chef::Handler::Datadog, :vcr => :new_episodes do
|
|
|
474
534
|
@handler.run_report_unsafe(@run_status)
|
|
475
535
|
end
|
|
476
536
|
|
|
477
|
-
it 'only emits
|
|
537
|
+
it 'only emits the run status metrics' do
|
|
478
538
|
expect(a_request(:post, METRICS_ENDPOINT).with(
|
|
479
539
|
:query => { 'api_key' => @handler.config[:api_key] }
|
|
480
|
-
)).to have_been_made.times(
|
|
540
|
+
)).to have_been_made.times(2)
|
|
481
541
|
end
|
|
482
542
|
|
|
483
543
|
it 'posts an event' do
|
|
@@ -490,6 +550,205 @@ describe Chef::Handler::Datadog, :vcr => :new_episodes do
|
|
|
490
550
|
end
|
|
491
551
|
end
|
|
492
552
|
|
|
553
|
+
describe '#endpoints' do
|
|
554
|
+
context 'with a basic config' do
|
|
555
|
+
it 'returns the correct triplet' do
|
|
556
|
+
handler = Chef::Handler::Datadog.new api_key: API_KEY, application_key: APPLICATION_KEY
|
|
557
|
+
expect(handler.send(:endpoints)).to eq([[nil, API_KEY, APPLICATION_KEY]])
|
|
558
|
+
end
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
context 'with no url and two pairs of keys' do
|
|
562
|
+
it 'returns the correct triplets' do
|
|
563
|
+
triplets = [
|
|
564
|
+
[nil, API_KEY, APPLICATION_KEY],
|
|
565
|
+
[nil, 'api_key_2', 'app_key_2'],
|
|
566
|
+
[nil, 'api_key_3', 'app_key_3']
|
|
567
|
+
]
|
|
568
|
+
handler = Chef::Handler::Datadog.new api_key: triplets[0][1],
|
|
569
|
+
application_key: triplets[0][2],
|
|
570
|
+
extra_endpoints: [{
|
|
571
|
+
api_key: triplets[1][1],
|
|
572
|
+
application_key: triplets[1][2]
|
|
573
|
+
}, {
|
|
574
|
+
api_key: triplets[2][1],
|
|
575
|
+
application_key: triplets[2][2]
|
|
576
|
+
}]
|
|
577
|
+
expect(handler.send(:endpoints)).to eq(triplets)
|
|
578
|
+
end
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
context 'with one url and two pairs of keys' do
|
|
582
|
+
it 'returns the correct triplets' do
|
|
583
|
+
triplets = [
|
|
584
|
+
['https://app.example.com', API_KEY, APPLICATION_KEY],
|
|
585
|
+
['https://app.example.com', 'api_key_2', 'app_key_2'],
|
|
586
|
+
['https://app.example.com', 'api_key_3', 'app_key_3']
|
|
587
|
+
]
|
|
588
|
+
handler = Chef::Handler::Datadog.new api_key: triplets[0][1],
|
|
589
|
+
application_key: triplets[0][2],
|
|
590
|
+
url: triplets[0][0],
|
|
591
|
+
extra_endpoints: [{
|
|
592
|
+
api_key: triplets[1][1],
|
|
593
|
+
application_key: triplets[1][2]
|
|
594
|
+
}, {
|
|
595
|
+
api_key: triplets[2][1],
|
|
596
|
+
application_key: triplets[2][2]
|
|
597
|
+
}]
|
|
598
|
+
expect(handler.send(:endpoints)).to eq(triplets)
|
|
599
|
+
end
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
context 'with multiple urls' do
|
|
603
|
+
it 'returns the correct triplets' do
|
|
604
|
+
triplets = [
|
|
605
|
+
['https://app.datadoghq.com', 'api_key_2', 'app_key_2'],
|
|
606
|
+
['https://app.example.com', 'api_key_3', 'app_key_3'],
|
|
607
|
+
['https://app.example.com', 'api_key_4', 'app_key_4']
|
|
608
|
+
]
|
|
609
|
+
handler = Chef::Handler::Datadog.new api_key: triplets[0][1],
|
|
610
|
+
application_key: triplets[0][2],
|
|
611
|
+
url: triplets[0][0],
|
|
612
|
+
extra_endpoints: [{
|
|
613
|
+
url: triplets[1][0],
|
|
614
|
+
api_key: triplets[1][1],
|
|
615
|
+
application_key: triplets[1][2]
|
|
616
|
+
}, {
|
|
617
|
+
url: triplets[2][0],
|
|
618
|
+
api_key: triplets[2][1],
|
|
619
|
+
application_key: triplets[2][2]
|
|
620
|
+
}]
|
|
621
|
+
expect(handler.send(:endpoints)).to eq(triplets)
|
|
622
|
+
end
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
context 'when missing application keys' do
|
|
626
|
+
it 'returns available triplets' do
|
|
627
|
+
triplets = [
|
|
628
|
+
[nil, API_KEY, APPLICATION_KEY],
|
|
629
|
+
[nil, 'api_key_2', 'app_key_2'],
|
|
630
|
+
[nil, 'api_key_3', 'app_key_3']
|
|
631
|
+
]
|
|
632
|
+
handler = Chef::Handler::Datadog.new api_key: triplets[0][1],
|
|
633
|
+
application_key: triplets[0][2],
|
|
634
|
+
extra_endpoints: [{
|
|
635
|
+
api_key: triplets[1][1],
|
|
636
|
+
application_key: triplets[1][2]
|
|
637
|
+
}, {
|
|
638
|
+
api_key: triplets[2][1]
|
|
639
|
+
}]
|
|
640
|
+
expect(handler.send(:endpoints)).to eq(triplets[0..1])
|
|
641
|
+
end
|
|
642
|
+
end
|
|
643
|
+
|
|
644
|
+
context 'when missing api keys' do
|
|
645
|
+
it 'returns available triplets' do
|
|
646
|
+
triplets = [
|
|
647
|
+
['https://app.datadoghq.com', 'api_key_2', 'app_key_2'],
|
|
648
|
+
['https://app.example.com', 'api_key_3', 'app_key_3'],
|
|
649
|
+
['https://app.example.com', 'api_key_4', 'app_key_4']
|
|
650
|
+
]
|
|
651
|
+
handler = Chef::Handler::Datadog.new api_key: triplets[0][1],
|
|
652
|
+
application_key: triplets[0][2],
|
|
653
|
+
url: triplets[0][0],
|
|
654
|
+
extra_endpoints: [{
|
|
655
|
+
url: triplets[1][0],
|
|
656
|
+
api_key: triplets[1][1],
|
|
657
|
+
application_key: triplets[1][2]
|
|
658
|
+
}, {
|
|
659
|
+
url: triplets[2][0],
|
|
660
|
+
application_key: triplets[2][2]
|
|
661
|
+
}]
|
|
662
|
+
expect(handler.send(:endpoints)).to eq(triplets[0..1])
|
|
663
|
+
end
|
|
664
|
+
end
|
|
665
|
+
end
|
|
666
|
+
|
|
667
|
+
context 'when reporting to multiple endpoints' do
|
|
668
|
+
let(:api_key2) { 'api_key_example' }
|
|
669
|
+
let(:application_key2) { 'application_key_example' }
|
|
670
|
+
let(:base_url2) { 'https://app.example.com' }
|
|
671
|
+
let(:events_endpoint2) { base_url2 + '/api/v1/events' }
|
|
672
|
+
let(:host_tag_endpoint2) { base_url2 + '/api/v1/tags/hosts/' }
|
|
673
|
+
let(:metrics_endpoint2) { base_url2 + '/api/v1/series' }
|
|
674
|
+
let(:handler) do
|
|
675
|
+
Chef::Handler::Datadog.new api_key: API_KEY,
|
|
676
|
+
application_key: APPLICATION_KEY,
|
|
677
|
+
url: BASE_URL,
|
|
678
|
+
extra_endpoints: [{
|
|
679
|
+
api_key: api_key2,
|
|
680
|
+
application_key: application_key2,
|
|
681
|
+
url: base_url2
|
|
682
|
+
}]
|
|
683
|
+
end
|
|
684
|
+
# Construct a good run_status
|
|
685
|
+
before(:each) do
|
|
686
|
+
@node = Chef::Node.build('chef.handler.datadog.test')
|
|
687
|
+
@node.send(:chef_environment, 'testing')
|
|
688
|
+
@events = Chef::EventDispatch::Dispatcher.new
|
|
689
|
+
@run_context = Chef::RunContext.new(@node, {}, @events)
|
|
690
|
+
@run_status = Chef::RunStatus.new(@node, @events)
|
|
691
|
+
|
|
692
|
+
@expected_time = Time.now
|
|
693
|
+
allow(Time).to receive(:now).and_return(@expected_time, @expected_time + 5)
|
|
694
|
+
@run_status.start_clock
|
|
695
|
+
@run_status.stop_clock
|
|
696
|
+
|
|
697
|
+
@run_status.run_context = @run_context
|
|
698
|
+
|
|
699
|
+
# Run the report
|
|
700
|
+
handler.run_report_unsafe(@run_status)
|
|
701
|
+
end
|
|
702
|
+
|
|
703
|
+
context 'emits metrics' do
|
|
704
|
+
it 'reports metrics' do
|
|
705
|
+
expect(a_request(:post, METRICS_ENDPOINT).with(
|
|
706
|
+
:query => { 'api_key' => API_KEY }
|
|
707
|
+
)).to have_been_made.times(5)
|
|
708
|
+
|
|
709
|
+
expect(a_request(:post, metrics_endpoint2).with(
|
|
710
|
+
:query => { 'api_key' => api_key2 }
|
|
711
|
+
)).to have_been_made.times(5)
|
|
712
|
+
end
|
|
713
|
+
end
|
|
714
|
+
|
|
715
|
+
context 'emits events' do
|
|
716
|
+
it 'posts an event' do
|
|
717
|
+
expect(a_request(:post, EVENTS_ENDPOINT).with(
|
|
718
|
+
:query => { 'api_key' => API_KEY },
|
|
719
|
+
:body => hash_including(:msg_text => 'Chef updated 0 resources out of 0 resources total.'),
|
|
720
|
+
:body => hash_including(:msg_title => "Chef completed in 5 seconds on #{@node.name} "),
|
|
721
|
+
:body => hash_including(:tags => ['env:testing']),
|
|
722
|
+
)).to have_been_made.times(1)
|
|
723
|
+
|
|
724
|
+
expect(a_request(:post, events_endpoint2).with(
|
|
725
|
+
:query => { 'api_key' => api_key2 },
|
|
726
|
+
:body => hash_including(:msg_text => 'Chef updated 0 resources out of 0 resources total.'),
|
|
727
|
+
:body => hash_including(:msg_title => "Chef completed in 5 seconds on #{@node.name} "),
|
|
728
|
+
:body => hash_including(:tags => ['env:testing']),
|
|
729
|
+
)).to have_been_made.times(1)
|
|
730
|
+
end
|
|
731
|
+
end
|
|
732
|
+
|
|
733
|
+
context 'sets tags' do
|
|
734
|
+
it 'puts the tags for the current node' do
|
|
735
|
+
expect(a_request(:put, HOST_TAG_ENDPOINT + @node.name).with(
|
|
736
|
+
:query => { 'api_key' => API_KEY,
|
|
737
|
+
'application_key' => APPLICATION_KEY,
|
|
738
|
+
'source' => 'chef' },
|
|
739
|
+
:body => { 'tags' => ['env:testing'] },
|
|
740
|
+
)).to have_been_made.times(1)
|
|
741
|
+
|
|
742
|
+
expect(a_request(:put, host_tag_endpoint2 + @node.name).with(
|
|
743
|
+
:query => { 'api_key' => api_key2,
|
|
744
|
+
'application_key' => application_key2,
|
|
745
|
+
'source' => 'chef' },
|
|
746
|
+
:body => { 'tags' => ['env:testing'] },
|
|
747
|
+
)).to have_been_made.times(1)
|
|
748
|
+
end
|
|
749
|
+
end
|
|
750
|
+
end
|
|
751
|
+
|
|
493
752
|
# TODO: test failures:
|
|
494
753
|
# @run_status.exception = Exception.new('Boy howdy!')
|
|
495
754
|
end
|