chef-handler-sns 0.2.1 → 0.2.2

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.
data/README.md CHANGED
@@ -9,6 +9,7 @@ This Chef Handler is heavily based on [Joshua Timberman](https://github.com/jtim
9
9
  [![Gem Version](https://badge.fury.io/rb/chef-handler-sns.png)](http://badge.fury.io/rb/chef-handler-sns)
10
10
  [![Dependency Status](https://gemnasium.com/onddo/chef-handler-sns.png)](https://gemnasium.com/onddo/chef-handler-sns)
11
11
  [![Code Climate](https://codeclimate.com/github/onddo/chef-handler-sns.png)](https://codeclimate.com/github/onddo/chef-handler-sns)
12
+ [![Build Status](https://travis-ci.org/onddo/chef-handler-sns.png)](https://travis-ci.org/onddo/chef-handler-sns)
12
13
 
13
14
  ## Requirements
14
15
 
@@ -110,17 +111,113 @@ The following options are available to configure the handler:
110
111
  * `secret_key` - AWS secret key (required).
111
112
  * `topic_arn` - AWS topic ARN name (required).
112
113
  * `region` - AWS region (optional).
113
- * `subject` - Message subject string in erubis format (optional). Example:
114
- ```
114
+ * `subject` - Message subject string in erubis format (optional).
115
+ * `body_template` - Full path of an erubis template file to use for the message body (optional).
116
+
117
+ ### subject
118
+
119
+ Here is an example of the `subject` configuration option using the ruby configuration file (`solo.rb` or `client.rb`):
120
+
121
+ ```ruby
115
122
  sns_handler.subject: "Chef-run: <%= node.name %> - <%= run_status.success? ? 'ok' : 'error' %>"
116
123
  ```
117
- * `body_template` - Full path of an erubis template file to use for the message body (optional).
118
124
 
119
- ## Roadmap
125
+ Using the [chef_handler LWRP](http://community.opscode.com/cookbooks/chef_handler):
126
+ ```ruby
127
+ argument_array = [
128
+ :access_key => "***AMAZON-KEY***",
129
+ :secret_key => "***AMAZON-SECRET***",
130
+ :topic_arn => "arn:aws:sns:***",
131
+ :subject => "Chef-run: <%= node.name %> - <%= run_status.success? ? 'ok' : 'error' %>",
132
+ # [...]
133
+ ]
134
+ chef_handler "Chef::Handler::Sns" do
135
+ # [...]
136
+ arguments argument_array
137
+ end
138
+ ```
139
+
140
+ The following variables are accesible inside the template:
141
+
142
+ * `start_time` - The time the chef run started.
143
+ * `end_time` - The time the chef run ended.
144
+ * `elapsed_time` - The time elapsed between the start and finish of the chef run.
145
+ * `run_context` - The Chef::RunContext object used by the chef run.
146
+ * `exception` - The uncaught Exception that terminated the chef run, or nil if the run completed successfully.
147
+ * `backtrace` - The backtrace captured by the uncaught exception that terminated the chef run, or nil if the run completed successfully.
148
+ * `node` - The Chef::Node for this client run.
149
+ * `all_resources` - An Array containing all resources in the chef run's resource_collection.
150
+ * `updated_resources` - An Array containing all resources that were updated during the chef run.
151
+ * `success?` - Was the chef run successful? True if the chef run did not raise an uncaught exception.
152
+ * `failed?` - Did the chef run fail? True if the chef run raised an uncaught exception.
153
+
154
+ ### body_template
155
+
156
+ This configuration option needs to contain the full path of an erubis template. For example:
157
+
158
+ ```ruby
159
+ # recipe "myapp::sns_handler"
160
+
161
+ template "/tmp/chef_handler_sns_body.erb" do
162
+ source "chef_handler_sns_body_erb.erb"
163
+ # [...]
164
+ end
165
+
166
+ argument_array = [
167
+ :access_key => "***AMAZON-KEY***",
168
+ :secret_key => "***AMAZON-SECRET***",
169
+ :topic_arn => "arn:aws:sns:***",
170
+ :body_template => "/tmp/chef_handler_sns_body.erb",
171
+ # [...]
172
+ ]
173
+ chef_handler "Chef::Handler::Sns" do
174
+ # [...]
175
+ arguments argument_array
176
+ end
177
+ ```
178
+
179
+ ```erb
180
+ <%# template "myapp/templates/default/chef_handler_sns_body_erb.erb" %>
181
+
182
+ Node Name: <%= node.name %>
183
+ <% if node.attribute?("fqdn") -%>
184
+ Hostname: <%= node.fqdn %>
185
+ <% end -%>
186
+
187
+ Chef Run List: <%= node.run_list.to_s %>
188
+ Chef Environment: <%= node.chef_environment %>
189
+
190
+ <% if node.attribute?("ec2") -%>
191
+ Instance Id: <%= node.ec2.instance_id %>
192
+ Instance Public Hostname: <%= node.ec2.public_hostname %>
193
+ Instance Hostname: <%= node.ec2.hostname %>
194
+ Instance Public IPv4: <%= node.ec2.public_ipv4 %>
195
+ Instance Local IPv4: <%= node.ec2.local_ipv4 %>
196
+ <% end -%>
197
+
198
+ Chef Client Elapsed Time: <%= elapsed_time.to_s %>
199
+ Chef Client Start Time: <%= start_time.to_s %>
200
+ Chef Client Start Time: <%= end_time.to_s %>
201
+
202
+ <% if exception -%>
203
+ Exception: <%= run_status.formatted_exception %>
204
+ Stacktrace:
205
+ <%= Array(backtrace).join("\n") %>
206
+
207
+ <% end -%>
208
+ ```
209
+
210
+ See the [subject](#subject) documentation for more details on the variables accesible inside the template.
211
+
212
+ ## Running the tests
213
+
214
+ Minitest tests can be run as usual:
215
+
216
+ rake test
120
217
 
121
- * rspec tests.
218
+ ## Contributing
122
219
 
123
- Pull requests are welcome.
220
+ [Pull Requests](http://github.com/onddo/chef-handler-sns/pulls) are welcome.
124
221
 
125
222
  ## License and Author
126
223
 
@@ -17,13 +17,13 @@
17
17
  #
18
18
 
19
19
  require 'chef/handler'
20
+ require 'chef/handler/sns/config'
20
21
  require 'right_aws'
21
22
  require 'erubis'
22
23
 
23
24
  class Chef
24
25
  class Handler
25
26
  class Sns < ::Chef::Handler
26
- require 'chef/handler/sns/config'
27
27
  include ::Chef::Handler::Sns::Config
28
28
 
29
29
  def initialize(config={})
@@ -35,15 +35,23 @@ class Chef
35
35
  config_check
36
36
  sns.publish(topic_arn, sns_body, sns_subject)
37
37
  end
38
-
38
+
39
+ def server
40
+ @sns.params[:server]
41
+ end
42
+
39
43
  protected
40
44
 
41
45
  def sns
42
46
  @sns ||= begin
43
47
  params = {
44
- :logger => Chef::Log,
45
- :region => region || node.ec2.placement_availability_zone.chop
48
+ :logger => Chef::Log
46
49
  }
50
+ if (region)
51
+ params[:region] = region
52
+ elsif node.attribute?('ec2') and node.ec2.attribute?('placement_availability_zone')
53
+ params[:region] = node.ec2.placement_availability_zone.chop
54
+ end
47
55
  params[:token] = token if token
48
56
  RightAws::SnsInterface.new(access_key, secret_key, params)
49
57
  end
@@ -21,29 +21,30 @@ require 'chef/exceptions'
21
21
 
22
22
  class Chef
23
23
  class Handler
24
- class Sns
24
+ class Sns < ::Chef::Handler
25
25
  module Config
26
+ Config.extend Config # let Config use the methods it contains as instance methods
26
27
  include ::Chef::Mixin::ParamsValidate
28
+
29
+ REQUIRED = [ 'access_key', 'secret_key', 'topic_arn' ]
27
30
 
28
31
  def config_init(config={})
29
32
  config.each do |key, value|
30
- if self.respond_to?(key)
33
+ if Config.respond_to?(key) and not /^config_/ =~ key.to_s
31
34
  self.send(key, value)
32
35
  else
33
- Chef::Log.warn("#{self.class.to_s}: cnofiguration method not found: #{key}.")
36
+ Chef::Log.warn("#{self.class.to_s}: configuration method not found: #{key}.")
34
37
  end
35
38
  end
36
39
  end
37
40
 
38
41
  def config_check
39
- required = [ 'access_key', 'secret_key', 'topic_arn' ]
40
- opts = {}
41
- map = {}
42
- required.each do |key|
43
- opts[key] = self.send(key)
44
- map[key] = { :required => true }
42
+ REQUIRED.each do |key|
43
+ if self.send(key).nil?
44
+ raise Exceptions::ValidationFailed,
45
+ "Required argument #{key.to_s} is missing!"
46
+ end
45
47
  end
46
- validate(opts, map)
47
48
 
48
49
  if body_template and not ::File.exists?(body_template)
49
50
  raise Exceptions::ValidationFailed,
@@ -1,7 +1,8 @@
1
+
1
2
  class Chef
2
3
  class Handler
3
- class Sns
4
- VERSION = '0.2.1'
4
+ class Sns < ::Chef::Handler
5
+ VERSION = '0.2.2'
5
6
  end
6
7
  end
7
8
  end
data/test/helper.rb ADDED
@@ -0,0 +1,8 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+
4
+ require 'rubygems'
5
+ gem 'minitest' # ensures you're using the gem, and not the built in MT
6
+ require 'minitest/autorun'
7
+ require 'mocha/setup'
8
+ require 'chef/handler/sns'
@@ -0,0 +1,151 @@
1
+ require 'helper'
2
+ require 'chef/node'
3
+ require 'chef/run_status'
4
+
5
+ class RightAws::FakeSnsInterface < RightAws::SnsInterface
6
+ attr_reader :sns_interface_new, :topic_arn, :message, :subject
7
+
8
+ def fake_new
9
+ @sns_interface_new = true
10
+ return self
11
+ end
12
+
13
+ end
14
+
15
+ class Chef::Handler::FakeSns < Chef::Handler::Sns
16
+
17
+ def get_sns_subject
18
+ sns_subject
19
+ end
20
+
21
+ def get_sns_body
22
+ sns_body
23
+ end
24
+
25
+ end
26
+
27
+ describe Chef::Handler::Sns do
28
+ before do
29
+ RightAws::SnsInterface.any_instance.stubs(:publish).returns(true)
30
+
31
+ @node = Chef::Node.new
32
+ @node.name('test')
33
+ Chef::Handler::Sns.any_instance.stubs(:node).returns(@node)
34
+
35
+ @run_status = if Gem.loaded_specs['chef'].version > Gem::Version.new('0.12.0')
36
+ Chef::RunStatus.new(@node, {})
37
+ else
38
+ Chef::RunStatus.new(@node)
39
+ end
40
+ @run_status.start_clock
41
+ @run_status.stop_clock
42
+
43
+ @config = {
44
+ :access_key => '***AMAZON-KEY***',
45
+ :secret_key => '***AMAZON-SECRET***',
46
+ :topic_arn => 'arn:aws:sns:***',
47
+ }
48
+ end
49
+
50
+ it 'should read the configuration options on initialization' do
51
+ @sns_handler = Chef::Handler::Sns.new(@config)
52
+ assert_equal @sns_handler.access_key, @config[:access_key]
53
+ assert_equal @sns_handler.secret_key, @config[:secret_key]
54
+ end
55
+
56
+ it 'should be able to change configuration options using method calls' do
57
+ @sns_handler = Chef::Handler::Sns.new
58
+ @sns_handler.access_key(@config[:access_key])
59
+ @sns_handler.secret_key(@config[:secret_key])
60
+ assert_equal @sns_handler.access_key, @config[:access_key]
61
+ assert_equal @sns_handler.secret_key, @config[:secret_key]
62
+ end
63
+
64
+ it 'should try to send a SNS message when properly configured' do
65
+ @sns_handler = Chef::Handler::Sns.new(@config)
66
+ RightAws::SnsInterface.any_instance.expects(:publish).once
67
+
68
+ @sns_handler.run_report_safely(@run_status)
69
+ end
70
+
71
+ it 'should create a RightAws::SnsInterface object' do
72
+ @sns_handler = Chef::Handler::Sns.new(@config)
73
+ fake_sns = RightAws::FakeSnsInterface.new(@config[:access_key], @config[:secret_key], {:logger => Chef::Log})
74
+ RightAws::SnsInterface.any_instance.stubs(:new).returns(fake_sns.fake_new)
75
+ @sns_handler.run_report_safely(@run_status)
76
+
77
+ assert_equal fake_sns.sns_interface_new, true
78
+ end
79
+
80
+ it 'should detect the AWS region automatically' do
81
+ @node.set['ec2']['placement_availability_zone'] = 'eu-west-1a'
82
+ @sns_handler = Chef::Handler::Sns.new(@config)
83
+ @sns_handler.run_report_safely(@run_status)
84
+
85
+ @sns_handler.server.must_match Regexp.new('eu-west-1')
86
+ end
87
+
88
+ it 'should not detect AWS region automatically whan manually set' do
89
+ @node.set['ec2']['placement_availability_zone'] = 'eu-west-1a'
90
+ @config[:region] = 'us-east-1'
91
+ @sns_handler = Chef::Handler::Sns.new(@config)
92
+ @sns_handler.run_report_safely(@run_status)
93
+
94
+ @sns_handler.server.must_match Regexp.new('us-east-1')
95
+ end
96
+
97
+ it 'should be able to generate the default subject in chef-client' do
98
+ Chef::Config[:solo] = false
99
+ @fake_sns_handler = Chef::Handler::FakeSns.new(@config)
100
+ Chef::Handler::FakeSns.any_instance.stubs(:node).returns(@node)
101
+ @fake_sns_handler.run_report_unsafe(@run_status)
102
+
103
+ assert_equal @fake_sns_handler.get_sns_subject, 'Chef Client success in test'
104
+ end
105
+
106
+ it 'should be able to generate the default subject in chef-solo' do
107
+ Chef::Config[:solo] = true
108
+ @fake_sns_handler = Chef::Handler::FakeSns.new(@config)
109
+ Chef::Handler::FakeSns.any_instance.stubs(:node).returns(@node)
110
+ @fake_sns_handler.run_report_unsafe(@run_status)
111
+
112
+ assert_equal @fake_sns_handler.get_sns_subject, 'Chef Solo success in test'
113
+ end
114
+
115
+ it 'should use the configured subject when set' do
116
+ @config[:subject] = 'My Subject'
117
+ @fake_sns_handler = Chef::Handler::FakeSns.new(@config)
118
+ Chef::Handler::FakeSns.any_instance.stubs(:node).returns(@node)
119
+ @fake_sns_handler.run_report_unsafe(@run_status)
120
+
121
+ assert_equal @fake_sns_handler.get_sns_subject, 'My Subject'
122
+ end
123
+
124
+ it 'should be able to generate the default message body' do
125
+ @fake_sns_handler = Chef::Handler::FakeSns.new(@config)
126
+ Chef::Handler::FakeSns.any_instance.stubs(:node).returns(@node)
127
+ @fake_sns_handler.run_report_unsafe(@run_status)
128
+
129
+ @fake_sns_handler.get_sns_body.must_match Regexp.new('Node Name: test')
130
+ end
131
+
132
+ it 'should throw an exception when the body template file does not exist' do
133
+ @config[:body_template] = '/tmp/nonexistent-template.erb'
134
+ @sns_handler = Chef::Handler::Sns.new(@config)
135
+
136
+ assert_raises(Chef::Exceptions::ValidationFailed) { @sns_handler.run_report_unsafe(@run_status) }
137
+ end
138
+
139
+ it 'should be able to generate the body template when configured as an option' do
140
+ body_msg = 'My Template'
141
+ @config[:body_template] = '/tmp/existing-template.erb'
142
+ ::File.stubs(:exists?).with(@config[:body_template]).returns(true)
143
+ IO.stubs(:read).with(@config[:body_template]).returns(body_msg)
144
+ @fake_sns_handler = Chef::Handler::FakeSns.new(@config)
145
+ Chef::Handler::FakeSns.any_instance.stubs(:node).returns(@node)
146
+ @fake_sns_handler.run_report_unsafe(@run_status)
147
+
148
+ assert_equal @fake_sns_handler.get_sns_body, body_msg
149
+ end
150
+
151
+ end
@@ -0,0 +1,63 @@
1
+ require 'helper'
2
+ require 'chef/exceptions'
3
+
4
+ class SnsConfig
5
+ include Chef::Handler::Sns::Config
6
+ end
7
+
8
+ describe Chef::Handler::Sns::Config do
9
+ before do
10
+ @config_params = {
11
+ :access_key => '***AMAZON-KEY***',
12
+ :secret_key => '***AMAZON-SECRET***',
13
+ :topic_arn => 'arn:aws:sns:***',
14
+ }
15
+ @sns_config = SnsConfig.new
16
+ end
17
+
18
+ it 'should read the configuration options on config initialization' do
19
+ @sns_config.config_init(@config_params)
20
+
21
+ assert_equal @sns_config.access_key, @config_params[:access_key]
22
+ assert_equal @sns_config.secret_key, @config_params[:secret_key]
23
+ end
24
+
25
+ it 'should be able to change configuration options using method calls' do
26
+ @sns_config.access_key(@config_params[:access_key])
27
+ @sns_config.secret_key(@config_params[:secret_key])
28
+
29
+ assert_equal @sns_config.access_key, @config_params[:access_key]
30
+ assert_equal @sns_config.secret_key, @config_params[:secret_key]
31
+ end
32
+
33
+ [ :access_key, :secret_key, :topic_arn ].each do |required|
34
+ it "should throw an exception when '#{required}' required field is not set" do
35
+ @config_params.delete(required)
36
+ @config_params.each { |key, value| @sns_config.send(key, value) }
37
+
38
+ assert_raises(Chef::Exceptions::ValidationFailed) { @sns_config.config_check }
39
+ end
40
+ end
41
+
42
+ [ :access_key, :secret_key, :region, :token, :topic_arn ].each do |option|
43
+
44
+ it "should accept string values in '#{option}' option" do
45
+ @sns_config.send(option, "test")
46
+ end
47
+
48
+ [ true, false, 25, Object.new ].each do |bad_value|
49
+ it "should throw and exception wen '#{option}' option is set to #{bad_value.to_s}" do
50
+ assert_raises(Chef::Exceptions::ValidationFailed) { @sns_config.send(option, bad_value) }
51
+ end
52
+ end
53
+ end
54
+
55
+ it 'should throw an exception when the body template file does not exist' do
56
+ @sns_config.body_template('/tmp/nonexistent-template.erb')
57
+ ::File.stubs(:exists?).with(@sns_config.body_template).returns(false)
58
+
59
+ assert_raises(Chef::Exceptions::ValidationFailed) { @sns_config.config_check }
60
+ end
61
+
62
+
63
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chef-handler-sns
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-03-02 00:00:00.000000000 Z
12
+ date: 2013-03-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: right_aws
@@ -43,6 +43,70 @@ dependencies:
43
43
  - - ! '>='
44
44
  - !ruby/object:Gem::Version
45
45
  version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: chef
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: 0.9.0
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 0.9.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: minitest
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: mocha
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
46
110
  description: Chef report handler to send SNS notifications on failures or changes
47
111
  email: team@onddo.com
48
112
  executables: []
@@ -55,6 +119,9 @@ files:
55
119
  - lib/chef/handler/sns/templates/body.erb
56
120
  - lib/chef/handler/sns/version.rb
57
121
  - lib/chef/handler/sns.rb
122
+ - test/test_chef_handler_sns_config.rb
123
+ - test/test_chef_handler_sns.rb
124
+ - test/helper.rb
58
125
  homepage: http://github.com/onddo/chef-handler-sns
59
126
  licenses: []
60
127
  post_install_message:
@@ -79,4 +146,7 @@ rubygems_version: 1.8.24
79
146
  signing_key:
80
147
  specification_version: 3
81
148
  summary: Chef SNS reports
82
- test_files: []
149
+ test_files:
150
+ - test/test_chef_handler_sns_config.rb
151
+ - test/test_chef_handler_sns.rb
152
+ - test/helper.rb