chef-handler-datadog 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +9 -9
  2. data/.gitignore +4 -3
  3. data/.rspec +1 -0
  4. data/.rubocop.yml +36 -0
  5. data/.travis.yml +7 -2
  6. data/Appraisals +24 -0
  7. data/CHANGELOG.md +23 -0
  8. data/Gemfile +7 -1
  9. data/README.md +16 -3
  10. data/Rakefile +7 -7
  11. data/chef-handler-datadog.gemspec +17 -12
  12. data/gemfiles/chef_10.14.0.gemfile +12 -0
  13. data/gemfiles/chef_10.26.0.gemfile +13 -0
  14. data/gemfiles/chef_10.30.2.gemfile +12 -0
  15. data/gemfiles/chef_11.8.2.gemfile +13 -0
  16. data/lib/chef-handler-datadog.rb +4 -1
  17. data/lib/chef/handler/datadog.rb +112 -89
  18. data/spec/datadog_spec.rb +279 -0
  19. data/spec/spec_helper.rb +48 -0
  20. data/spec/support/cassettes/Chef_Handler_Datadog/failed_Chef_run/sets_event_title_correctly.yml +218 -0
  21. data/spec/support/cassettes/Chef_Handler_Datadog/failed_Chef_run/sets_priority_correctly.yml +218 -0
  22. data/spec/support/cassettes/Chef_Handler_Datadog/handles_no_application_key/fails_when_no_application_key_is_provided.yml +142 -0
  23. data/spec/support/cassettes/Chef_Handler_Datadog/handles_tags_correctly/sets_the_role_and_env_and_tags.yml +215 -0
  24. 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 +214 -0
  25. data/spec/support/cassettes/Chef_Handler_Datadog/reports_correct_hostname_on_an_ec2_node/uses_the_instance_id_when_config_is_specified.yml +214 -0
  26. data/spec/support/cassettes/Chef_Handler_Datadog/reports_correct_hostname_on_an_ec2_node/uses_the_instance_id_when_no_config_specified.yml +214 -0
  27. data/spec/support/cassettes/Chef_Handler_Datadog/reports_metrics_event_and_sets_tags/emits_events/posts_an_event.yml +214 -0
  28. data/spec/support/cassettes/Chef_Handler_Datadog/reports_metrics_event_and_sets_tags/emits_events/sets_priority_correctly.yml +214 -0
  29. data/spec/support/cassettes/Chef_Handler_Datadog/reports_metrics_event_and_sets_tags/emits_metrics/reports_metrics.yml +214 -0
  30. data/spec/support/cassettes/Chef_Handler_Datadog/reports_metrics_event_and_sets_tags/get_and_set_tags/gets_the_tags_for_the_current_node.yml +214 -0
  31. data/spec/support/cassettes/Chef_Handler_Datadog/reports_metrics_event_and_sets_tags/get_and_set_tags/puts_the_tags_for_the_current_node.yml +214 -0
  32. data/spec/support/cassettes/Chef_Handler_Datadog/updated_resources/posts_an_event.yml +217 -0
  33. metadata +102 -15
  34. data/gemfiles/Gemfile.chef-10 +0 -5
  35. data/gemfiles/Gemfile.chef-11 +0 -5
  36. data/test/helper.rb +0 -18
  37. data/test/test_chef-handler-datadog.rb +0 -7
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YzlmYzNiOWYxODgxODhmZjNmY2Y3OTE1MGQ1MTlhMjMxNTA4M2NiZA==
4
+ NzQzODBiZTM2NjkxZDlhZTQ5OWVhNDAwZWQyODk5ZjY5NjMzMWRkNw==
5
5
  data.tar.gz: !binary |-
6
- ODljYmFlYjg5Y2UyMDQ2OWQ2NjlkNzM4OGFkN2FkYmM5ZmEwMDQ1Yg==
7
- !binary "U0hBNTEy":
6
+ YmI2ZTljMTFjMDQ1YjEzOWJjNzBiNTFkMzMzMzBmY2QyODhjODMxMQ==
7
+ SHA512:
8
8
  metadata.gz: !binary |-
9
- YzNiODIxMTI1NTg1ZjNmYTdmZWEyMDVjYzRhOTY1OGViMjc5MDYyZDczNDBl
10
- ZTVlYzM3YzFkZGI3OWUxMjlmNjcyNzMzNjMzMzQ3YmQyYTJhOWMxNmY3ZjU4
11
- ZjA1MjFlMGZmY2MyMGJmZmUwZGEyYTI1MTIzNWM5MDU5NTIwNzE=
9
+ ZGJiZDVlYTQ3M2RlZjg0ZDNiOTA4OGVmMGEzZDdhOGY4MDkwMmQ4MzAzNWYy
10
+ NGM5MjJhOWI3MjgxMDNmOTczNmMwM2MwM2U1YjA0MTI3NTJjOThmZWFiZGM4
11
+ ZDUzYzA1MWQxMWNjODVhZjNlNGZmMTJkYTY3ZGZmNGFkNmYwNGI=
12
12
  data.tar.gz: !binary |-
13
- NGNjYzFmNWI4OTJlNzZlYzFmMjA2NmFmYTRhZTc1M2YwMTg4MDMxZjBkYmJh
14
- ZmNkMGY0ZTdlMjhlODAzODM4NGE3M2FkNDQxNGQ5NzNiMjAwNzgwNDc4ZGUz
15
- MTE4NzZmYzM1YjQzMGQ2MWE2Yjc5MWEwNDQxZTk1MjAxOGE1MWI=
13
+ ZDE4ODE4NTdjMzAxZTgwZTkzM2M5N2Q0ZjExNGFhZTg1MThhOGMyYjAzNTYw
14
+ NmI1NWI2YWRmM2FmYjNiOGExNzU5MmQzYTU5ODJhMjhlNjI1MDQ5NTYzZDMz
15
+ NGE4OTBmNTMyYzQzZTEzN2ZiMTU3ZDAzMDEzZGIzMzUzNTk0MTA=
data/.gitignore CHANGED
@@ -29,9 +29,6 @@ pkg
29
29
  #
30
30
  .DS_Store
31
31
 
32
- # For RVM
33
- .rvmrc
34
-
35
32
  # For TextMate
36
33
  *.tmproj
37
34
  # tmtags
@@ -53,8 +50,12 @@ pkg
53
50
  json
54
51
  rake
55
52
  Gemfile.lock
53
+ gemfiles/*.lock
56
54
 
57
55
  # For Ruby Managers:
58
56
  .ruby-gemset
59
57
  .ruby-version
60
58
  .rvmrc
59
+
60
+ # Any environment-specific files
61
+ .env
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,36 @@
1
+ AllCops:
2
+ Includes:
3
+ - chef-handler-datadog.gemspec
4
+ - Appraisals
5
+ - Gemfile
6
+ - Rakefile
7
+
8
+ # We want to continue to support Ruby 1.8.x for now
9
+ HashSyntax:
10
+ EnforcedStyle: hash_rockets
11
+
12
+ # 80 characters is a nice goal, but not worth currently changing in existing
13
+ # code for the sake of changing it to conform to a length set in 1928 (IBM).
14
+ LineLength:
15
+ Max: 150
16
+
17
+ #####
18
+ # These exceptions are good goals to attain, and probably will over time,
19
+ # so periodic disabling and re-running to inspect values is suggested.
20
+
21
+ # TODO: Main class is currently over 100 lines of code, making comprehending
22
+ # it harder. With refactors and simplifications, we can bring this down,
23
+ # but for now, let's not make it too much worse.
24
+ ClassLength:
25
+ Max: 160
26
+
27
+ # TODO: this is currently down to 12 from 22, and should be a constant
28
+ # goal to reduce method complexity. The accepted goal is 6.
29
+ CyclomaticComplexity:
30
+ Max: 12
31
+
32
+ # TODO: The main method `report` is now down from 85 lines. As refactors
33
+ # continue, this should drop. However, the goal of 10 lines in a method may
34
+ # be a little lofty.
35
+ MethodLength:
36
+ Max: 78
data/.travis.yml CHANGED
@@ -1,6 +1,11 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
+ bundler_args: --without=localdev
4
5
  gemfile:
5
- - gemfiles/Gemfile.chef-11
6
- - gemfiles/Gemfile.chef-10
6
+ - gemfiles/chef_10.14.0.gemfile
7
+ - gemfiles/chef_10.26.0.gemfile
8
+ - gemfiles/chef_10.30.2.gemfile
9
+ - gemfiles/chef_11.8.2.gemfile
10
+ env:
11
+ - API_KEY=somefakeapikey APPLICATION_KEY=somefakeapplicationkey
data/Appraisals ADDED
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+
3
+ # Describe any version dependencies here.
4
+
5
+ %w(
6
+ 10.14.0
7
+ 10.30.2
8
+ ).each do |tv|
9
+ appraise "chef-#{tv}" do
10
+ gem 'chef', tv
11
+ end
12
+ end
13
+
14
+ # Due to some oddity in json gem version pinning for versions that have
15
+ # conflicts, specify best version here.
16
+ %w(
17
+ 10.26.0
18
+ 11.8.2
19
+ ).each do |tv|
20
+ appraise "chef-#{tv}" do
21
+ gem 'chef', tv
22
+ gem 'json', '1.7.7'
23
+ end
24
+ end
data/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  Changes
2
2
  =======
3
+
4
+ # 0.3.0 / 2014-01-23
5
+
6
+ * [MISC] **Breaking Change**: Chef 0.9.x is no longer supported. Extra code implemented for 0.9 versions has been removed. [@miketheman][]
7
+ * [BUGFIX] Report correct response when failing to submit tags, [#39][] [@miketheman][]
8
+ * [OPTIMIZE] Refactor hostname resolution to its own method, simplify `report` method. [@miketheman][]
9
+ * [OPTIMIZE] Refactor metrics reporting to its own method, simplify `report` method. [@miketheman][]
10
+ * [OPTIMIZE] Refactor tagging get/set to own methods, simplify `report` method. [@miketheman][]
11
+ * [MISC] Converted `opts` to an instance variable of `config` to reduce the amount of instance variables passing around. [@miketheman][]
12
+ * [OPTIMIZE] Don't try to tag node when Application Key isn't provided, [#31][] [@miketheman][]
13
+ * [OPTIMIZE] Only load in the parts of the Chef gem that are used. [@miketheman][]
14
+ * [OPTIMIZE] Change order in which event_data is constructed, to not lose the full backtrace, [#37][] [@miketheman][]
15
+
16
+ Testing suite: [#18][], [@miketheman][]
17
+ * Removed Tailor testing in favor of Rubocop
18
+ * Added Appraisals with specific Chef versions to be tested
19
+ * Added RSpec testing via VCR and Webmock
20
+
3
21
  # 0.2.0 / 2013-10-31
4
22
 
5
23
  * [BUGFIX] moved Chef gem dependency to development, [#34][] [@miketheman][]
@@ -10,8 +28,13 @@ Changes
10
28
  # 0.1.2 / 2013-04-03
11
29
 
12
30
  And all other versions were prior to this. See git history for more.
31
+
13
32
  <!--- The following link definition list is generated by PimpMyChangelog --->
33
+ [#18]: https://github.com/DataDog/chef-handler-datadog/issues/18
34
+ [#31]: https://github.com/DataDog/chef-handler-datadog/issues/31
14
35
  [#34]: https://github.com/DataDog/chef-handler-datadog/issues/34
36
+ [#37]: https://github.com/DataDog/chef-handler-datadog/issues/37
37
+ [#39]: https://github.com/DataDog/chef-handler-datadog/issues/39
15
38
  [@alq]: https://github.com/alq
16
39
  [@miketheman]: https://github.com/miketheman
17
40
  [@remh]: https://github.com/remh
data/Gemfile CHANGED
@@ -1,4 +1,10 @@
1
- source "http://rubygems.org"
1
+ # encoding: utf-8
2
+ source 'http://rubygems.org'
2
3
 
3
4
  # Specify your gem's dependencies in chef-handler-datadog.gemspec
4
5
  gemspec
6
+
7
+ group :localdev do
8
+ gem 'pry'
9
+ gem 'travis-lint'
10
+ end
data/README.md CHANGED
@@ -2,7 +2,10 @@
2
2
 
3
3
  An Exception and Report Handler for Chef.
4
4
 
5
+ [![Gem Version](https://badge.fury.io/rb/chef-handler-datadog.png)](http://badge.fury.io/rb/chef-handler-datadog)
5
6
  [![Build Status](https://secure.travis-ci.org/DataDog/chef-handler-datadog.png?branch=master)](http://travis-ci.org/DataDog/chef-handler-datadog)
7
+ [![Code Climate](https://codeclimate.com/github/DataDog/chef-handler-datadog.png)](https://codeclimate.com/github/DataDog/chef-handler-datadog)
8
+ [![Dependency Status](https://gemnasium.com/DataDog/chef-handler-datadog.png)](https://gemnasium.com/DataDog/chef-handler-datadog)
6
9
 
7
10
  ## Using chef-handler-datadog
8
11
 
@@ -10,14 +13,24 @@ The Datadog Docs on [Chef](http://docs.datadoghq.com/guides/chef/#deployhandler)
10
13
 
11
14
  ## Contributing to chef-handler-datadog
12
15
 
13
- * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
16
+ * Check out the latest `master` to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
14
17
  * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
15
18
  * Fork the project
16
19
  * Start a feature/bugfix branch
20
+ * Place a `.env` file in the root of the project with `API_KEY` and `APPLICATION_KEY`:
21
+
22
+ API_KEY: myapikey
23
+ APPLICATION_KEY: chefhandlerspecificapplicationkey
24
+
25
+ This file is intentionally .gitignored to prevent security exposure.
26
+
27
+ * Run `rake` to execute tests, ensure they pass
17
28
  * Commit and push until you are happy with your contribution
18
29
  * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
19
- * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
30
+ * Please try not to mess with the `Rakefile`, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
20
31
 
21
32
  ## Copyright
22
33
 
23
- Copyright (c) 2012-2013 Datadog, Inc. See LICENSE.txt for further details.
34
+ Copyright (c) 2012-2014 Datadog, Inc. See LICENSE.txt for further details.
35
+
36
+ [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/DataDog/chef-handler-datadog/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
data/Rakefile CHANGED
@@ -3,15 +3,15 @@
3
3
  require 'rubygems'
4
4
  require 'bundler/gem_tasks'
5
5
 
6
+ require 'appraisal'
7
+ require 'rake/clean'
6
8
  require 'rspec/core/rake_task'
7
- require 'tailor/rake_task'
9
+ require 'rubocop/rake_task'
8
10
 
9
- task :default => :test
11
+ task :default => [:cops, :spec]
12
+
13
+ CLEAN.include(['coverage/', 'doc/', 'pkg/'])
10
14
 
11
15
  RSpec::Core::RakeTask.new(:spec)
12
16
 
13
- Tailor::RakeTask.new do |task|
14
- task.file_set('lib/**/*.rb', "code") do |style|
15
- style.max_line_length 160, :level => :warn
16
- end
17
- end
17
+ Rubocop::RakeTask.new(:cops)
@@ -1,28 +1,33 @@
1
+ # encoding: utf-8
1
2
  require File.expand_path('../lib/chef-handler-datadog', __FILE__)
2
3
 
3
4
  Gem::Specification.new do |gem|
4
- gem.name = "chef-handler-datadog"
5
+ gem.name = 'chef-handler-datadog'
5
6
  gem.summary = %q{Chef Handler for DataDog events and metrics}
6
7
  gem.description = %q{This Handler will report the events and metrics for a chef-client run to DataDog.}
7
- gem.license = "BSD"
8
+ gem.license = 'BSD'
8
9
  gem.version = ChefHandlerDatadog::VERSION
9
10
 
10
- gem.files = `git ls-files`.split($\)
11
- gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
12
- gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
11
+ gem.files = `git ls-files`.split($\) # rubocop:disable SpecialGlobalVars
12
+ gem.executables = gem.files.grep(/^bin\//).map { |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(/^(test|spec|features)\//)
13
14
  gem.require_paths = ['lib']
14
15
  gem.extra_rdoc_files = ['README.md', 'LICENSE.txt']
15
16
 
16
- gem.add_dependency 'dogapi', ">= 1.2"
17
+ gem.add_dependency 'dogapi', '>= 1.2'
17
18
 
19
+ gem.add_development_dependency 'appraisal', '~> 1.0.0.beta2'
18
20
  gem.add_development_dependency 'bundler'
19
- gem.add_development_dependency 'chef', ">= 10", "<= 12"
21
+ gem.add_development_dependency 'chef', '>= 10', '<= 12'
22
+ gem.add_development_dependency 'dotenv'
20
23
  gem.add_development_dependency 'rake'
21
24
  gem.add_development_dependency 'rspec'
22
- gem.add_development_dependency 'tailor', '~> 1.3'
23
- gem.add_development_dependency 'travis-lint'
25
+ gem.add_development_dependency 'rubocop'
26
+ gem.add_development_dependency 'simplecov'
27
+ gem.add_development_dependency 'vcr'
28
+ gem.add_development_dependency 'webmock'
24
29
 
25
- gem.authors = ["Mike Fiedler", "Adam Jacob", "Alexis Le-Quoc"]
26
- gem.email = ["package@datadoghq.com"]
27
- gem.homepage = "http://www.datadoghq.com/"
30
+ gem.authors = ['Mike Fiedler', 'Adam Jacob', 'Alexis Le-Quoc']
31
+ gem.email = ['package@datadoghq.com']
32
+ gem.homepage = 'http://www.datadoghq.com/'
28
33
  end
@@ -0,0 +1,12 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "chef", "10.14.0"
6
+
7
+ group :localdev do
8
+ gem "pry"
9
+ gem "travis-lint"
10
+ end
11
+
12
+ gemspec :path=>".././"
@@ -0,0 +1,13 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "chef", "10.26.0"
6
+ gem "json", "1.7.7"
7
+
8
+ group :localdev do
9
+ gem "pry"
10
+ gem "travis-lint"
11
+ end
12
+
13
+ gemspec :path=>".././"
@@ -0,0 +1,12 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "chef", "10.30.2"
6
+
7
+ group :localdev do
8
+ gem "pry"
9
+ gem "travis-lint"
10
+ end
11
+
12
+ gemspec :path=>".././"
@@ -0,0 +1,13 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "http://rubygems.org"
4
+
5
+ gem "chef", "11.8.2"
6
+ gem "json", "1.7.7"
7
+
8
+ group :localdev do
9
+ gem "pry"
10
+ gem "travis-lint"
11
+ end
12
+
13
+ gemspec :path=>".././"
@@ -1,3 +1,6 @@
1
+ # encoding: utf-8
2
+ # Helper module for version number only.
3
+ # Real deal in 'chef/handler/datadog.rb'
1
4
  module ChefHandlerDatadog
2
- VERSION = "0.2.0"
5
+ VERSION = '0.3.0'
3
6
  end
@@ -1,82 +1,72 @@
1
+ # encoding: utf-8
1
2
  require 'rubygems'
2
- require 'chef'
3
3
  require 'chef/handler'
4
4
  require 'dogapi'
5
5
 
6
6
  class Chef
7
7
  class Handler
8
+ # Datadog handler to send Chef run details to Datadog
8
9
  class Datadog < Chef::Handler
10
+ attr_reader :config
9
11
 
10
12
  # For the tags to work, the client must have created an Application Key on the
11
13
  # "Account Settings" page here: https://app.datadoghq.com/account/settings
12
14
  # It should be passed along from the node/role/environemnt attributes, as the default is nil.
13
- def initialize(opts = nil)
14
- opts = opts || {}
15
- @api_key = opts[:api_key]
16
- @application_key = opts[:application_key]
17
- # If we're on ec2, use the instance by default, unless instructed otherwise
18
- @use_ec2_instance_id = !opts.has_key?(:use_ec2_instance_id) || opts.has_key?(:use_ec2_instance_id) && opts[:use_ec2_instance_id]
19
- @dog = Dogapi::Client.new(@api_key, application_key = @application_key)
15
+ def initialize(config = {})
16
+ @config = config
17
+ # If *any* api_key is not provided, this will fail immediately.
18
+ @dog = Dogapi::Client.new(config[:api_key], config[:application_key])
20
19
  end
21
20
 
22
21
  def report
23
22
  # resolve correct hostname
24
- hostname = run_status.node.name
25
- if @use_ec2_instance_id && run_status.node.attribute?("ec2") && run_status.node.ec2.attribute?("instance_id")
26
- hostname = run_status.node.ec2.instance_id
27
- end
23
+ hostname = select_hostname(run_status.node)
28
24
 
29
25
  # Send the metrics
30
- begin
31
- @dog.emit_point("chef.resources.total", run_status.all_resources.length, :host => hostname)
32
- @dog.emit_point("chef.resources.updated", run_status.updated_resources.length, :host => hostname)
33
- @dog.emit_point("chef.resources.elapsed_time", run_status.elapsed_time, :host => hostname)
34
- Chef::Log.debug("Submitted chef metrics back to Datadog")
35
- rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT => e
36
- Chef::Log.error("Could not send metrics to Datadog. Connection error:\n" + e)
37
- end
26
+ emit_metrics_to_datadog(hostname, run_status)
38
27
 
39
28
  # Build the correct event
40
- event_title = ""
41
- run_time = pluralize(run_status.elapsed_time, "second")
29
+ event_title = ''
30
+ run_time = pluralize(run_status.elapsed_time, 'second')
42
31
  if run_status.success?
43
- alert_type = "success"
44
- event_priority = "low"
32
+ alert_type = 'success'
33
+ event_priority = 'low'
45
34
  event_title << "Chef completed in #{run_time} on #{hostname} "
46
35
  else
47
36
  event_title << "Chef failed in #{run_time} on #{hostname} "
48
37
  end
49
38
 
50
39
  event_data = "Chef updated #{run_status.updated_resources.length} resources out of #{run_status.all_resources.length} resources total."
40
+
41
+ if run_status.failed?
42
+ alert_type = 'error'
43
+ event_priority = 'normal'
44
+ event_data << "\n@@@\n#{run_status.formatted_exception}\n@@@\n"
45
+ event_data << "\n@@@\n#{run_status.backtrace.join("\n")}\n@@@\n"
46
+ end
47
+
51
48
  if run_status.updated_resources.length.to_i > 0
52
49
  event_data << "\n@@@\n"
53
50
  run_status.updated_resources.each do |r|
54
- event_data << "- #{r.to_s} (#{defined_at(r)})\n"
51
+ event_data << "- #{r.to_s} (#{r.defined_at})\n"
55
52
  end
56
53
  event_data << "\n@@@\n"
57
54
  end
58
55
 
59
- if run_status.failed?
60
- alert_type = "error"
61
- event_priority = "normal"
62
- event_data << "\n@@@\n#{run_status.formatted_exception}\n@@@\n"
63
- event_data << "\n@@@\n#{run_status.backtrace.join("\n")}\n@@@\n"
64
- end
65
-
66
56
  # Submit the details back to Datadog
67
57
  begin
68
58
  # Send the Event data
69
59
  evt = @dog.emit_event(Dogapi::Event.new(event_data,
70
- :msg_title => event_title,
71
- :event_type => 'config_management.run',
72
- :event_object => hostname,
73
- :alert_type => alert_type,
74
- :priority => event_priority,
75
- :source_type_name => 'chef'
60
+ :msg_title => event_title,
61
+ :event_type => 'config_management.run',
62
+ :event_object => hostname,
63
+ :alert_type => alert_type,
64
+ :priority => event_priority,
65
+ :source_type_name => 'chef'
76
66
  ), :host => hostname)
77
67
 
78
68
  begin
79
- # FIXME nice-to-have: abstract format of return value away a bit
69
+ # FIXME: nice-to-have: abstract format of return value away a bit
80
70
  # in dogapi directly. See https://github.com/DataDog/dogapi-rb/issues/18
81
71
  if evt.length < 2
82
72
  Chef::Log.warn("Unexpected response from Datadog Event API: #{evt}")
@@ -93,38 +83,25 @@ class Chef
93
83
  Chef::Log.warn("Could not determine whether chef run was successfully submitted to Datadog: #{evt}")
94
84
  end
95
85
 
96
- # Get the current list of tags, remove any "role:" entries
97
- host_tags = @dog.host_tags(hostname)[1]["tags"] || []
98
- host_tags.delete_if { |tag| tag.start_with?('role:') }
99
-
100
- # Get list of chef roles, rename them to tag format
101
- chef_roles = node.run_list.roles
102
- chef_roles.collect! { |role| "role:" + role }
103
-
104
- # Get the chef environment (as long as it's not '_default')
105
- if node.respond_to?('chef_environment') && node.chef_environment != '_default'
106
- host_tags.delete_if { |tag| tag.start_with?('env:') }
107
- host_tags << "env:" + node.chef_environment
108
- end
109
-
110
- # Combine (union) both arrays. Removes dupes, preserves non-chef tags.
111
- new_host_tags = host_tags | chef_roles
112
-
113
- if @application_key.nil?
86
+ # Update tags
87
+ if config[:application_key].nil?
114
88
  Chef::Log.warn("You need an application key to let Chef tag your nodes " \
115
89
  "in Datadog. Visit https://app.datadoghq.com/account/settings#api to " \
116
90
  "create one and update your datadog attributes in the datadog cookbook."
117
91
  )
92
+ fail ArgumentError, 'Missing Datadog Application Key'
118
93
  else
94
+ new_host_tags = get_combined_tags(hostname, node)
95
+
119
96
  # Replace all tags with the new tags
120
97
  rc = @dog.update_tags(hostname, new_host_tags)
121
98
  begin
122
99
  # See FIXME above about why I feel dirty repeating this code here
123
100
  if rc.length < 2
124
- Chef::Log.warn("Unexpected response from Datadog Event API: #{evt}")
101
+ Chef::Log.warn("Unexpected response from Datadog Event API: #{rc}")
125
102
  else
126
103
  if rc[0].to_i / 100 != 2
127
- Chef::Log.warn("Could not submit #{new_host_tags} tags for #{hostname} to Datadog: #{evt}")
104
+ Chef::Log.warn("Could not submit #{new_host_tags} tags for #{hostname} to Datadog: #{rc}")
128
105
  else
129
106
  Chef::Log.debug("Successfully updated #{hostname}'s tags to #{new_host_tags.join(', ')}")
130
107
  end
@@ -135,48 +112,94 @@ class Chef
135
112
  end
136
113
  rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT => e
137
114
  Chef::Log.error("Could not connect to Datadog. Connection error:\n" + e)
138
- Chef::Log.error("Data to be submitted was:")
115
+ Chef::Log.error('Data to be submitted was:')
139
116
  Chef::Log.error(event_title)
140
117
  Chef::Log.error(event_data)
141
- Chef::Log.error("Tags to be set for this run:")
118
+ Chef::Log.error('Tags to be set for this run:')
142
119
  Chef::Log.error(new_host_tags)
143
120
  end
144
121
  end
145
122
 
146
123
  private
147
124
 
125
+ # Emit Chef metrics to Datadog
126
+ #
127
+ # @param hostname [String] resolved hostname to attach to series
128
+ # @param run_status [Chef::RunStatus] current run status
129
+ def emit_metrics_to_datadog(hostname, run_status)
130
+ @dog.emit_point('chef.resources.total', run_status.all_resources.length, :host => hostname)
131
+ @dog.emit_point('chef.resources.updated', run_status.updated_resources.length, :host => hostname)
132
+ @dog.emit_point('chef.resources.elapsed_time', run_status.elapsed_time, :host => hostname)
133
+ Chef::Log.debug('Submitted Chef metrics back to Datadog')
134
+ rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT => e
135
+ Chef::Log.error("Could not send metrics to Datadog. Connection error:\n" + e)
136
+ end
137
+
138
+ # Call Datadog API for a given hostname and retrieve the current list
139
+ # of Datadog tags - not the same as Chef 'tags' - rather all tags in
140
+ # are `key:value` e.g. `role:database-master`.
141
+ # Build up an array of Datadog tags to send back
142
+ #
143
+ # @param hostname [String]
144
+ # @return [Array] an array of current Datadog tags, roles, env
145
+ def get_combined_tags(hostname, node)
146
+ host_tags = get_host_tags(hostname)
147
+ host_tags << get_node_env(node)
148
+
149
+ chef_roles = get_node_roles(node)
150
+ chef_tags = get_node_tags(node)
151
+
152
+ # Combine (union) all arrays. Removes dupes, preserves non-Chef tags.
153
+ host_tags | chef_roles | chef_tags
154
+ end
155
+
156
+ # Get current tags, drop any that will be replaced
157
+ def get_host_tags(hostname)
158
+ tags = @dog.host_tags(hostname)[1]['tags'] || []
159
+ tags.delete_if { |tag| tag.start_with?('role:', 'env:', 'tag:') }
160
+ end
161
+
162
+ def get_node_roles(node)
163
+ node.run_list.roles.map! { |role| 'role:' + role }
164
+ end
165
+
166
+ def get_node_env(node)
167
+ 'env:' + node.chef_environment if node.respond_to?('chef_environment')
168
+ end
169
+
170
+ def get_node_tags(node)
171
+ node.tags.map! { |tag| 'tag:' + tag }
172
+ end
173
+
148
174
  def pluralize(number, noun)
149
- begin
150
- case number
151
- when 0..1
152
- "less than 1 #{noun}"
153
- else
154
- "#{number.round} #{noun}s"
155
- end
156
- rescue
157
- Chef::Log.warn("Cannot make #{number} more legible")
158
- "#{number} #{noun}s"
175
+ case number
176
+ when 0..1
177
+ "less than 1 #{noun}"
178
+ else
179
+ "#{number.round} #{noun}s"
159
180
  end
181
+ rescue
182
+ Chef::Log.warn("Cannot make #{number} more legible")
183
+ "#{number} #{noun}s"
160
184
  end
161
185
 
162
- # TODO: Remove this once confirmed that Chef 0.9.x is no longer used.
163
- ## This function is to mimic behavior built into a later version of chef than 0.9.x
164
- ## Source is here: https://github.com/opscode/chef/blob/19bc40b148f3862ce88d044e600d01a9838c60bc/chef/lib/chef/resource.rb#L375-L384
165
- ## Including this based on help from schisamo
166
- def defined_at(resource)
167
- cookbook_name = resource.cookbook_name
168
- recipe_name = resource.recipe_name
169
- source_line = resource.source_line
170
- if cookbook_name && recipe_name && source_line
171
- "#{cookbook_name}::#{recipe_name} line #{source_line.split(':')[1]}"
172
- elsif source_line
173
- file, line_no = source_line.split(':')
174
- "#{file} line #{line_no}"
186
+ # Select which hostname to report back to Datadog.
187
+ # Makes decision based on inputs from `config` and when absent, use the
188
+ # node's `ec2` attribute existence to make the decision.
189
+ #
190
+ # @param node [Chef::Node] from `run_status`, can feasibly any `node`
191
+ # @return [String] the hostname decided upon
192
+ def select_hostname(node)
193
+ use_ec2_instance_id = !config.key?(:use_ec2_instance_id) ||
194
+ (config.key?(:use_ec2_instance_id) &&
195
+ config[:use_ec2_instance_id])
196
+
197
+ if use_ec2_instance_id && node.attribute?('ec2') && node.ec2.attribute?('instance_id')
198
+ node.ec2.instance_id
175
199
  else
176
- "dynamically defined"
200
+ node.name
177
201
  end
178
202
  end
179
-
180
- end #end class Datadog
181
- end #end class Handler
182
- end #end class Chef
203
+ end # end class Datadog
204
+ end # end class Handler
205
+ end # end class Chef