cycle_chef_handler 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.autotest ADDED
@@ -0,0 +1,23 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'autotest/restart'
4
+
5
+ # Autotest.add_hook :initialize do |at|
6
+ # at.extra_files << "../some/external/dependency.rb"
7
+ #
8
+ # at.libs << ":../some/external"
9
+ #
10
+ # at.add_exception 'vendor'
11
+ #
12
+ # at.add_mapping(/dependency.rb/) do |f, _|
13
+ # at.files_matching(/test_.*rb$/)
14
+ # end
15
+ #
16
+ # %w(TestA TestB).each do |klass|
17
+ # at.extra_class_map[klass] = "test/test_misc.rb"
18
+ # end
19
+ # end
20
+
21
+ # Autotest.add_hook :run_command do |at|
22
+ # system "rake build"
23
+ # end
data/History.txt ADDED
@@ -0,0 +1,6 @@
1
+ === 1.2.0 / 2011-08-30
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
6
+
data/Manifest.txt ADDED
@@ -0,0 +1,8 @@
1
+ .autotest
2
+ History.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ bin/cycle_chef_handler
7
+ lib/cycle_chef_handler.rb
8
+ test/test_cycle_chef_handler.rb
data/README.txt ADDED
@@ -0,0 +1,86 @@
1
+ = cycle_chef_handler
2
+
3
+ * http://github.com/cyclecomputing/cycle_chef_handler
4
+ * http://www.cyclecomputing.com
5
+
6
+ == DESCRIPTION:
7
+
8
+ This extension of Chef::Handler creates reports in Condor class ad format and posts
9
+ them to an amqp-complient message broker. This report handler was created to produce
10
+ reports for the CycleServer Chef Dashboard available from Cycle Computing LLC.
11
+
12
+ == REQUIREMENTS:
13
+
14
+ * chef
15
+ * classad
16
+ * bunny
17
+
18
+ == INSTALL:
19
+
20
+ * sudo gem install cycle_chef_handler
21
+ * edit /etc/chef/client.rb on client nodes:
22
+
23
+ require 'cycle_chef_handler'
24
+
25
+ handler = CycleChefHandler.new(:amqp_config => {:host => 'my_amqp_hostname'})
26
+
27
+ report_handlers << handler
28
+ exception_handlers << handler
29
+
30
+ == ADVANCED USAGE:
31
+
32
+ The constructor takes a hash with the following keys:
33
+
34
+ :amqp_config
35
+ This key should point to a configuration hash suitable for Bunny.new(), including:
36
+
37
+ * :host Host name of amqp broker
38
+ * :port Port of amqp borker, default value = 5672
39
+ * :vhost Name of virtual host on amqp broker, default value = '/'
40
+ * :user User name on amqp broker, default value = 'guest'
41
+ * :pass Password for :user on amqp broker, default value = 'guest'
42
+ * :queue Queue name to be used to read report messages, default value = 'chef.converges'
43
+ * :exchange Exchange name to be used to post report messages, default value = 'chef.converges'
44
+
45
+ :extras
46
+ This optional key may be used to pass arbitrary key, value pairs to the chef report classad.
47
+ It may be used to tag chef clients to make it easier to slice and dice the report information.
48
+ I use it (along with the ec2_metadata gem) to add Amazon Web Services EC2 instance data to
49
+ the report.
50
+
51
+ :extras => {'InstanceId' => Ec2Metadata[:instance_id],
52
+ 'AvailabilityZone' => Ec2Metadata[:placement][:availability_zone],
53
+ 'PublicHostName' => Ec2Metadata[:public_hostname]}
54
+
55
+ :converge_index_file
56
+ This should point to a file where CycleChefHandler will keep track of the number of
57
+ converges that have been attempted. Its default value is /var/run/chef/converge_index.
58
+
59
+ :failed_converge_count_file
60
+ This should point to a file where CycleChefHandler will keep track of the number of
61
+ consecutive failed converges. Its default value is /var/run/chef/failed_converge_count.
62
+
63
+ == DEVELOPERS:
64
+
65
+ After checking out the source, run:
66
+
67
+ $ rake newb
68
+
69
+ This task will install any missing dependencies, run the tests/specs,
70
+ and generate the RDoc.
71
+
72
+ == LICENSE:
73
+
74
+ Copyright 2011 Cycle Computing LLC
75
+
76
+ Licensed under the Apache License, Version 2.0 (the "License");
77
+ you may not use this file except in compliance with the License.
78
+ You may obtain a copy of the License at
79
+
80
+ http://www.apache.org/licenses/LICENSE-2.0
81
+
82
+ Unless required by applicable law or agreed to in writing, software
83
+ distributed under the License is distributed on an "AS IS" BASIS,
84
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
85
+ See the License for the specific language governing permissions and
86
+ limitations under the License.
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require 'fileutils'
6
+ require File.join(File.dirname(__FILE__), 'lib', 'cycle_chef_handler')
7
+
8
+ # copy README.rdoc to README.txt
9
+ FileUtils.cp File.join(File.dirname(__FILE__), 'README.rdoc'), File.join(File.dirname(__FILE__), 'README.txt')
10
+
11
+ # we don't use rubyforge
12
+ Hoe.plugins.delete :rubyforge
13
+
14
+ Hoe.spec 'cycle_chef_handler' do
15
+ developer('Chris Chalfant', 'chris.chalfant@cyclecomputing.com')
16
+ extra_deps << ['chef']
17
+ extra_deps << ['bunny']
18
+ extra_deps << ['classad']
19
+ end
20
+
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ abort "you need to write me"
@@ -0,0 +1,167 @@
1
+ # cycle_chef_handler.rb
2
+ #
3
+ # Report handler for chef clients to be used with CycleServer Chef Dashboard
4
+ #
5
+ # Author: Chris Chalfant (chris.chalfant@cyclecomputing.com)
6
+ #
7
+ # Copyright 2010 Cycle Computing LLC
8
+ #
9
+ # Licensed under the Apache License, Version 2.0 (the "License");
10
+ # you may not use this file except in compliance with the License.
11
+ # You may obtain a copy of the License at
12
+ #
13
+ # http://www.apache.org/licenses/LICENSE-2.0
14
+ #
15
+ # Unless required by applicable law or agreed to in writing, software
16
+ # distributed under the License is distributed on an "AS IS" BASIS,
17
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ # See the License for the specific language governing permissions and
19
+ # limitations under the License.
20
+
21
+ require 'rubygems'
22
+ require 'chef'
23
+ require 'bunny'
24
+ require 'classad'
25
+ require 'fileutils'
26
+ require 'uri'
27
+
28
+ class CycleChefHandler < Chef::Handler
29
+ VERSION = '1.2.0'
30
+
31
+ def initialize(params)
32
+ defaults = {:queue => 'chef.converges',
33
+ :exchange => 'chef.converges'}
34
+
35
+ @amqp_config = defaults.merge(params[:amqp_config])
36
+ check_amqp_config
37
+
38
+ @extras = params[:extras]
39
+ @converge_index_file = params[:converge_index_file] || '/var/run/chef/converge_index'
40
+ @failed_converge_file = params[:failed_converge_count_file] || '/var/run/chef/failed_converge_count'
41
+ end
42
+
43
+ def check_amqp_config
44
+ [:host, :queue, :exchange].each do |i|
45
+ if not @amqp_config[i]
46
+ raise ArgumentError, ":amqp_config missing value for #{i}"
47
+ end
48
+ end
49
+ end
50
+
51
+ def report
52
+
53
+ ## Create and Post a classad
54
+ ad = create_ad
55
+ payload = "<classads>" + ad.to_xml + "</classads>"
56
+
57
+ begin
58
+
59
+ b = Bunny.new(@amqp_config)
60
+
61
+ b.start
62
+ e = b.exchange(@amqp_config[:exchange],
63
+ :type => :topic,
64
+ :durable => true,
65
+ :auto_delete => false)
66
+
67
+ # declare and bind a non-auto-delete queue here to make sure we don't
68
+ # lose any messages posted to an exchange w/o a bound queue
69
+ # make the queue non-durable so we can drop it with a broker reboot
70
+ q = b.queue(@amqp_config[:queue], :auto_delete => false)
71
+ q.bind(@amqp_config[:exchange], :key => @amqp_config[:queue])
72
+
73
+ e.publish(payload, :key => @amqp_config[:queue])
74
+
75
+ rescue Exception => e
76
+
77
+ # log any exceptions, but don't throw one.
78
+ trace = e.backtrace.join("\n")
79
+ Chef::Log.error("Failed to post converge history report: #{e.message} #{trace}")
80
+ return
81
+
82
+ ensure
83
+
84
+ b.stop
85
+
86
+ end
87
+
88
+ Chef::Log.info("Posted converge history report")
89
+
90
+ end
91
+
92
+ def create_ad
93
+ ad = ClassAd.new
94
+ ad['AdType'] = 'Chef.Host'
95
+ ad['ChefNode'] = Chef::Config[:node_name]
96
+ ad['ConvergeStartTime'] = start_time
97
+ ad['ConvergeEndTime'] = end_time
98
+ ad['ConvergeElapsedTime'] = RelativeTime.new(elapsed_time)
99
+
100
+ updated = []
101
+ if not updated_resources.nil?
102
+ updated = updated_resources.map {|x| x.to_s}
103
+ end
104
+
105
+ ad['UpdatedResources'] = updated
106
+ ad['UpdatedResourcesCount'] = updated.size
107
+ ad['ConvergeIndex'] = increment_count_file(@converge_index_file)
108
+ ad['ChefServerUrl'] = Chef::Config[:chef_server_url]
109
+ ad['ChefServerHostName'] = URI.parse(Chef::Config[:chef_server_url]).host
110
+ ad['ChefClientVersion'] = Chef::VERSION
111
+ ad['CycleChefHandlerVersion'] = CycleChefHandler::VERSION
112
+ ad['Success'] = success?
113
+
114
+ exception = nil
115
+ backtrace = nil
116
+ if failed?
117
+ exception = run_status.formatted_exception
118
+ backtrace = run_status.backtrace
119
+ ad['FailedConvergeCount'] = increment_count_file(@failed_converge_file)
120
+ else
121
+ clear_count_file(@failed_converge_file)
122
+ ad['FailedConvergeCount'] = 0
123
+ end
124
+
125
+ ad['Exception'] = exception
126
+ ad['Backtrace'] = backtrace
127
+
128
+ @extras.each do |k,v|
129
+ ad[k] = v
130
+ end
131
+
132
+ ad
133
+ end
134
+
135
+ def increment_count_file(count_file)
136
+ file_dir = File.dirname(count_file)
137
+ if not File.directory? file_dir
138
+ FileUtils.mkdir_p file_dir
139
+ end
140
+
141
+ count = nil
142
+ if File.exists? count_file
143
+ File.open(count_file) do |file|
144
+ count = file.readline.chomp.to_i
145
+ end
146
+ end
147
+
148
+ if count.nil?
149
+ count = 1
150
+ else
151
+ count += 1
152
+ end
153
+
154
+ File.open(count_file, "w") do |file|
155
+ file.puts(count)
156
+ end
157
+
158
+ count
159
+ end
160
+
161
+ def clear_count_file(count_file)
162
+ if File.exist? count_file
163
+ FileUtils.rm count_file
164
+ end
165
+ end
166
+
167
+ end
@@ -0,0 +1,27 @@
1
+ require "test/unit"
2
+ require "cycle_chef_handler"
3
+
4
+ class TestCycleChefHandler < Test::Unit::TestCase
5
+
6
+ def setup
7
+ @index_file = File.join(File.dirname(__FILE__), 'index')
8
+ @handler = CycleChefHandler.new(:amqp_config => {:host=> 'h'},
9
+ :converge_index_file => @index_file )
10
+ end
11
+
12
+ def teardown
13
+ File.unlink @index_file if File.exist? @index_file
14
+ end
15
+
16
+ def test_increment
17
+ assert_equal(1, @handler.increment_count_file(@index_file))
18
+ assert_equal(2, @handler.increment_count_file(@index_file))
19
+ end
20
+
21
+ def test_reset
22
+ assert_equal(1, @handler.increment_count_file(@index_file))
23
+ assert_equal(2, @handler.increment_count_file(@index_file))
24
+ @handler.clear_count_file(@index_file)
25
+ assert_equal(1, @handler.increment_count_file(@index_file))
26
+ end
27
+ end
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cycle_chef_handler
3
+ version: !ruby/object:Gem::Version
4
+ hash: 31
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 2
9
+ - 0
10
+ version: 1.2.0
11
+ platform: ruby
12
+ authors:
13
+ - Chris Chalfant
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-08-30 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: chef
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: bunny
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :runtime
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: classad
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ type: :runtime
61
+ version_requirements: *id003
62
+ - !ruby/object:Gem::Dependency
63
+ name: hoe
64
+ prerelease: false
65
+ requirement: &id004 !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ hash: 19
71
+ segments:
72
+ - 2
73
+ - 6
74
+ - 2
75
+ version: 2.6.2
76
+ type: :development
77
+ version_requirements: *id004
78
+ description: |-
79
+ This extension of Chef::Handler creates reports in Condor class ad format and posts
80
+ them to an amqp-complient message broker. This report handler was created to produce
81
+ reports for the CycleServer Chef Dashboard available from Cycle Computing LLC.
82
+ email:
83
+ - chris.chalfant@cyclecomputing.com
84
+ executables:
85
+ - cycle_chef_handler
86
+ extensions: []
87
+
88
+ extra_rdoc_files:
89
+ - History.txt
90
+ - Manifest.txt
91
+ - README.txt
92
+ files:
93
+ - .autotest
94
+ - History.txt
95
+ - Manifest.txt
96
+ - README.txt
97
+ - Rakefile
98
+ - bin/cycle_chef_handler
99
+ - lib/cycle_chef_handler.rb
100
+ - test/test_cycle_chef_handler.rb
101
+ homepage: http://github.com/cyclecomputing/cycle_chef_handler
102
+ licenses: []
103
+
104
+ post_install_message:
105
+ rdoc_options:
106
+ - --main
107
+ - README.txt
108
+ require_paths:
109
+ - lib
110
+ required_ruby_version: !ruby/object:Gem::Requirement
111
+ none: false
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ hash: 3
116
+ segments:
117
+ - 0
118
+ version: "0"
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ none: false
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ hash: 3
125
+ segments:
126
+ - 0
127
+ version: "0"
128
+ requirements: []
129
+
130
+ rubyforge_project: cycle_chef_handler
131
+ rubygems_version: 1.8.7
132
+ signing_key:
133
+ specification_version: 3
134
+ summary: This extension of Chef::Handler creates reports in Condor class ad format and posts them to an amqp-complient message broker
135
+ test_files:
136
+ - test/test_cycle_chef_handler.rb