chef-handler-sns 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
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