cycle_chef_handler 1.2.0
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/.autotest +23 -0
- data/History.txt +6 -0
- data/Manifest.txt +8 -0
- data/README.txt +86 -0
- data/Rakefile +20 -0
- data/bin/cycle_chef_handler +3 -0
- data/lib/cycle_chef_handler.rb +167 -0
- data/test/test_cycle_chef_handler.rb +27 -0
- metadata +136 -0
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
data/Manifest.txt
ADDED
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,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
|