chef-monitor 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ /pkg
2
+ /config
3
+ /orgs
data/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ # Apache License
2
+ # Version 2.0, January 2004
3
+ # http://www.apache.org/licenses/
4
+ #
5
+ #TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+ #
7
+ #1. Definitions.
8
+
9
+ # "License" shall mean the terms and conditions for use, reproduction,
10
+ # and distribution as defined by Sections 1 through 9 of this document.
11
+ #
12
+ # "Licensor" shall mean the copyright owner or entity authorized by
13
+ # the copyright owner that is granting the License.
14
+ #
15
+ # "Legal Entity" shall mean the union of the acting entity and all
16
+ # other entities that control, are controlled by, or are under common
17
+ # control with that entity. For the purposes of this definition,
18
+ # "control" means (i) the power, direct or indirect, to cause the
19
+ # direction or management of such entity, whether by contract or
20
+ # otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ # outstanding shares, or (iii) beneficial ownership of such entity.
22
+ #
23
+ # "You" (or "Your") shall mean an individual or Legal Entity
24
+ # exercising permissions granted by this License.
25
+ #
26
+ # "Source" form shall mean the preferred form for making modifications,
27
+ # including but not limited to software source code, documentation
28
+ # source, and configuration files.
29
+ #
30
+ # "Object" form shall mean any form resulting from mechanical
31
+ # transformation or translation of a Source form, including but
32
+ # not limited to compiled object code, generated documentation,
33
+ # and conversions to other media types.
34
+ #
35
+ # "Work" shall mean the work of authorship, whether in Source or
36
+ # Object form, made available under the License, as indicated by a
37
+ # copyright notice that is included in or attached to the work
38
+ # (an example is provided in the Appendix below).
39
+ #
40
+ # "Derivative Works" shall mean any work, whether in Source or Object
41
+ # form, that is based on (or derived from) the Work and for which the
42
+ # editorial revisions, annotations, elaborations, or other modifications
43
+ # represent, as a whole, an original work of authorship. For the purposes
44
+ # of this License, Derivative Works shall not include works that remain
45
+ # separable from, or merely link (or bind by name) to the interfaces of,
46
+ # the Work and Derivative Works thereof.
47
+ #
48
+ # "Contribution" shall mean any work of authorship, including
49
+ # the original version of the Work and any modifications or additions
50
+ # to that Work or Derivative Works thereof, that is intentionally
51
+ # submitted to Licensor for inclusion in the Work by the copyright owner
52
+ # or by an individual or Legal Entity authorized to submit on behalf of
53
+ # the copyright owner. For the purposes of this definition, "submitted"
54
+ # means any form of electronic, verbal, or written communication sent
55
+ # to the Licensor or its representatives, including but not limited to
56
+ # communication on electronic mailing lists, source code control systems,
57
+ # and issue tracking systems that are managed by, or on behalf of, the
58
+ # Licensor for the purpose of discussing and improving the Work, but
59
+ # excluding communication that is conspicuously marked or otherwise
60
+ # designated in writing by the copyright owner as "Not a Contribution."
61
+ #
62
+ # "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ # on behalf of whom a Contribution has been received by Licensor and
64
+ # subsequently incorporated within the Work.
65
+ #
66
+ #2. Grant of Copyright License. Subject to the terms and conditions of
67
+ # this License, each Contributor hereby grants to You a perpetual,
68
+ # worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ # copyright license to reproduce, prepare Derivative Works of,
70
+ # publicly display, publicly perform, sublicense, and distribute the
71
+ # Work and such Derivative Works in Source or Object form.
72
+ #
73
+ #3. Grant of Patent License. Subject to the terms and conditions of
74
+ # this License, each Contributor hereby grants to You a perpetual,
75
+ # worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ # (except as stated in this section) patent license to make, have made,
77
+ # use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ # where such license applies only to those patent claims licensable
79
+ # by such Contributor that are necessarily infringed by their
80
+ # Contribution(s) alone or by combination of their Contribution(s)
81
+ # with the Work to which such Contribution(s) was submitted. If You
82
+ # institute patent litigation against any entity (including a
83
+ # cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ # or a Contribution incorporated within the Work constitutes direct
85
+ # or contributory patent infringement, then any patent licenses
86
+ # granted to You under this License for that Work shall terminate
87
+ # as of the date such litigation is filed.
88
+ #
89
+ #4. Redistribution. You may reproduce and distribute copies of the
90
+ # Work or Derivative Works thereof in any medium, with or without
91
+ # modifications, and in Source or Object form, provided that You
92
+ # meet the following conditions:
93
+ #
94
+ # (a) You must give any other recipients of the Work or
95
+ # Derivative Works a copy of this License; and
96
+ #
97
+ # (b) You must cause any modified files to carry prominent notices
98
+ # stating that You changed the files; and
99
+ #
100
+ # (c) You must retain, in the Source form of any Derivative Works
101
+ # that You distribute, all copyright, patent, trademark, and
102
+ # attribution notices from the Source form of the Work,
103
+ # excluding those notices that do not pertain to any part of
104
+ # the Derivative Works; and
105
+ #
106
+ # (d) If the Work includes a "NOTICE" text file as part of its
107
+ # distribution, then any Derivative Works that You distribute must
108
+ # include a readable copy of the attribution notices contained
109
+ # within such NOTICE file, excluding those notices that do not
110
+ # pertain to any part of the Derivative Works, in at least one
111
+ # of the following places: within a NOTICE text file distributed
112
+ # as part of the Derivative Works; within the Source form or
113
+ # documentation, if provided along with the Derivative Works; or,
114
+ # within a display generated by the Derivative Works, if and
115
+ # wherever such third-party notices normally appear. The contents
116
+ # of the NOTICE file are for informational purposes only and
117
+ # do not modify the License. You may add Your own attribution
118
+ # notices within Derivative Works that You distribute, alongside
119
+ # or as an addendum to the NOTICE text from the Work, provided
120
+ # that such additional attribution notices cannot be construed
121
+ # as modifying the License.
122
+ #
123
+ # You may add Your own copyright statement to Your modifications and
124
+ # may provide additional or different license terms and conditions
125
+ # for use, reproduction, or distribution of Your modifications, or
126
+ # for any such Derivative Works as a whole, provided Your use,
127
+ # reproduction, and distribution of the Work otherwise complies with
128
+ # the conditions stated in this License.
129
+
130
+ #5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ # any Contribution intentionally submitted for inclusion in the Work
132
+ # by You to the Licensor shall be under the terms and conditions of
133
+ # this License, without any additional terms or conditions.
134
+ # Notwithstanding the above, nothing herein shall supersede or modify
135
+ # the terms of any separate license agreement you may have executed
136
+ # with Licensor regarding such Contributions.
137
+ #
138
+ #6. Trademarks. This License does not grant permission to use the trade
139
+ # names, trademarks, service marks, or product names of the Licensor,
140
+ # except as required for reasonable and customary use in describing the
141
+ # origin of the Work and reproducing the content of the NOTICE file.
142
+ #
143
+ #7. Disclaimer of Warranty. Unless required by applicable law or
144
+ # agreed to in writing, Licensor provides the Work (and each
145
+ # Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ # implied, including, without limitation, any warranties or conditions
148
+ # of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ # PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ # appropriateness of using or redistributing the Work and assume any
151
+ # risks associated with Your exercise of permissions under this License.
152
+ #
153
+ #8. Limitation of Liability. In no event and under no legal theory,
154
+ # whether in tort (including negligence), contract, or otherwise,
155
+ # unless required by applicable law (such as deliberate and grossly
156
+ # negligent acts) or agreed to in writing, shall any Contributor be
157
+ # liable to You for damages, including any direct, indirect, special,
158
+ # incidental, or consequential damages of any character arising as a
159
+ # result of this License or out of the use or inability to use the
160
+ # Work (including but not limited to damages for loss of goodwill,
161
+ # work stoppage, computer failure or malfunction, or any and all
162
+ # other commercial damages or losses), even if such Contributor
163
+ # has been advised of the possibility of such damages.
164
+ #
165
+ #9. Accepting Warranty or Additional Liability. While redistributing
166
+ # the Work or Derivative Works thereof, You may choose to offer,
167
+ # and charge a fee for, acceptance of support, warranty, indemnity,
168
+ # or other liability obligations and/or rights consistent with this
169
+ # License. However, in accepting such obligations, You may act only
170
+ # on Your own behalf and on Your sole responsibility, not on behalf
171
+ # of any other Contributor, and only if You agree to indemnify,
172
+ # defend, and hold each Contributor harmless for any liability
173
+ # incurred by, or claims asserted against, such Contributor by reason
174
+ # of your accepting any such warranty or additional liability.
175
+ #
176
+ #END OF TERMS AND CONDITIONS
177
+
178
+ #APPENDIX: How to apply the Apache License to your work.
179
+ #
180
+ # To apply the Apache License to your work, attach the following
181
+ # boilerplate notice, with the fields enclosed by brackets "[]"
182
+ # replaced with your own identifying information. (Don't include
183
+ # the brackets!) The text should be enclosed in the appropriate
184
+ # comment syntax for the file format. We also recommend that a
185
+ # file or class name and description of purpose be included on the
186
+ # same "printed page" as the copyright notice for easier
187
+ # identification within third-party archives.
188
+ #
189
+ # Copyright [yyyy] [name of copyright owner]
190
+ #
191
+ #Licensed under the Apache License, Version 2.0 (the "License");
192
+ #you may not use this file except in compliance with the License.
193
+ #You may obtain a copy of the License at
194
+ #
195
+ # {http://www.apache.org/licenses/LICENSE-2.0}
196
+
197
+ #Unless required by applicable law or agreed to in writing, software
198
+ #distributed under the License is distributed on an "AS IS" BASIS,
199
+ #WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ #See the License for the specific language governing permissions and
201
+ #limitations under the License.
data/README ADDED
@@ -0,0 +1,131 @@
1
+ #
2
+ # Author:: Sander Botman (<sander.botman@gmail.com>)
3
+ # Copyright:: Copyright (c) 2014 Sander Botman.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ CHEF-MONITOR
19
+
20
+ Chef monitor has two executables:
21
+ - chef-logmon (this will be activated on all frontend servers)
22
+ - chef-worker (this will be activated on your monitor/backend server)
23
+
24
+
25
+ Chef HA configuration:
26
+
27
+ When you have Chef in HA mode, your environment will look something like this:
28
+
29
+ public zone | dmz zone | db zone
30
+ ---------------|------------------------|-----------------------
31
+ | |
32
+ | frontend-server | backend-server
33
+ | webserver01 | / dbserver01
34
+ | 10.1.1.10/24 | / 10.1.5.110/24
35
+ | | /
36
+ internet | | vip <
37
+ | | ^ \
38
+ | frontend-server | | \ backend-server
39
+ | webserver02 | | \ dbserver02
40
+ | 10.1.1.20/24 | | 10.1.5.120/24
41
+ |
42
+ ----------------
43
+ 10.1.5.90/24 | monitor-server |
44
+ keepalived | monserver01 |
45
+ | 10.1.5.130/24 |
46
+ ----------------
47
+
48
+ When running this environment, I suggest you configure the new monitor server.
49
+ The frondend server and monitor server can also be only one single server.
50
+ If you don't have HA mode, then the environment will look something like this:
51
+
52
+ public zone | cloud server |
53
+ ---------------|------------------------|
54
+ | |
55
+ | chefserver |
56
+ internet | chefserver01 |
57
+ | 10.1.1.10/24 |
58
+ | |
59
+
60
+
61
+ CHEF-LOGMON:
62
+
63
+ The logmon tool will run on every frontend server within your HA environment or on the
64
+ chefserver in a more basic environment and is responsible for the following tasks:
65
+
66
+ - Tail your NGINX log and record all POST/PUTS/DELETES
67
+ - This information is sent to your Rabbit-MQ server (which comes default with chef)
68
+
69
+ Basically every change that's being made to chef is registered within RabbitMQ.
70
+
71
+
72
+ CHEF-WORKER:
73
+
74
+ The worker tool will run on your monitor server within the HA environment or on the
75
+ chefserver in a more basic environment and is responsible for the following tasks:
76
+
77
+ - Get the messages from RabbitMQ
78
+ - Download the objects from chef that are changed
79
+ - Commit the changes within a GIT repository
80
+
81
+ In this way every modified object is registered with a GIT commit and a POST-COMMIT script
82
+ will email the differences to any configured email address. This POST-COMMIT part is not
83
+ within the GEM, but comes with the chef-monitor chef cookbook.
84
+
85
+ CONFIGURATION:
86
+
87
+ In order to execute both tools, you will need the following configuration settings:
88
+
89
+ chef_url "https://10.1.5.90"
90
+ node_name "monitor"
91
+ client_key "/opt/chef-monitor/monitor.pem"
92
+ mq_server "10.1.5.90"
93
+ mq_queue "monitor_tasks"
94
+ download_path "/opt/chef-monitor/orgs"
95
+ log_dir "/var/log/chef-monitor"
96
+ pid_dir "/var/run/chef-monitor"
97
+ mon_file "/var/log/opscode/nginx/access.log"
98
+
99
+ Save these settings into /opt/chef-monitor/config.rb
100
+ Make sure your monitor user is created on your chef server and has enough rights to download
101
+ all objects within your organizaton that you want to monitor.
102
+
103
+ Create an directory within your [download_path] with the same name as your organization.
104
+ Initialize this directory with the following commands:
105
+ git init
106
+ touch dummy
107
+ git add .
108
+ git commit -am "enable git control"
109
+
110
+ Add some git configuration settings for the POST-COMMIT script and chef-monitor tools.
111
+ git config hooks.mailinglist sander.botman@gmail.com
112
+ git config hooks.emailprefix <YOUR_ORGANIZATION>
113
+ git config hooks.emaildomain @your.domain.com
114
+
115
+ Set the project name within the gitrepo, so you can identify your chef environment.
116
+ echo MYCHEF > ./.git/description
117
+
118
+
119
+ EXECUTION:
120
+
121
+ After these settings, you should be able to run the tools:
122
+ On all your frontend servers:
123
+ chef-logmon run -- -C /opt/chef-monitor/config.rb <run interactive>
124
+ chef-logmon start -- -C /opt/chef-monitor/config.rb <run as service>
125
+ chef-logmon stop <stop service>
126
+
127
+ On your monitor server:
128
+ chef-worker run -- -C /opt/chef-monitor/config.rb <run interactive>
129
+ chef-worker start -- -C /opt/chef-monitor/config.rb <run as service>
130
+ chef-worker stop <stop service>
131
+
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/chef-logmon ADDED
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Author:: Sander Botman (<sander.botman@gmail.com>)
4
+ # Copyright:: Copyright (c) 2014 Sander Botman.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require "chef_monitor"
20
+ require 'rubygems'
21
+ require 'file-tail'
22
+ require 'bunny'
23
+ require 'json'
24
+ require 'daemons'
25
+
26
+ config=nil
27
+ opt=0
28
+ ARGV.each do |arg|
29
+ config = ARGV[opt+1] if arg == "-C" || arg == "--config"
30
+ opt+=1
31
+ end
32
+
33
+ if config.nil?
34
+ puts "Please enter the configuration file with the option -C or --config"
35
+ exit 1
36
+ else
37
+ Monitor::Config.from_file(config)
38
+ [ "mq_queue", "mq_server", "mon_file", "chef_url", "pid_dir", "log_dir" ].each do |item|
39
+ if Monitor::Config[item.to_sym].empty?
40
+ puts "Missing item: #{item} within the configuration file!"
41
+ exit 1
42
+ end
43
+ end
44
+ end
45
+
46
+ MQSERVER = Monitor::Config[:mq_server]
47
+ MQQUEUE = Monitor::Config[:mq_queue]
48
+ MON_FILE = Monitor::Config[:mon_file]
49
+ CHEF_URL = Monitor::Config[:chef_url]
50
+
51
+ @options = {
52
+ :log_output => true,
53
+ :dir_mode => :normal,
54
+ :dir => Monitor::Config[:pid_dir],
55
+ :log_dir => Monitor::Config[:log_dir]
56
+ }
57
+
58
+ Daemons.run_proc('chef-logmon', @options) do
59
+ logmon = Monitor::Logmon.new
60
+ logmon.run()
61
+ end
62
+
data/bin/chef-worker ADDED
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Author:: Sander Botman (<sander.botman@gmail.com>)
4
+ # Copyright:: Copyright (c) 2014 Sander Botman.
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require "chef_monitor"
20
+ require 'rubygems'
21
+ require 'bunny'
22
+ require 'json'
23
+ require 'daemons'
24
+
25
+ config=nil
26
+ opt=0
27
+ ARGV.each do |arg|
28
+ config = ARGV[opt+1] if arg == "-C" || arg == "--config"
29
+ opt+=1
30
+ end
31
+
32
+ if config.nil?
33
+ puts "Please enter the configuration file with the option -C or --config"
34
+ exit 1
35
+ else
36
+ Monitor::Config.from_file(config)
37
+ [ "mq_queue", "mq_server", "download_path", "node_name", "client_key", "chef_url", "pid_dir", "log_dir" ].each do |item|
38
+ if Monitor::Config[item.to_sym].empty?
39
+ puts "Missing item: #{item} within the configuration file!"
40
+ exit 1
41
+ end
42
+ end
43
+ end
44
+
45
+ MQSERVER = Monitor::Config[:mq_server]
46
+ MQQUEUE = Monitor::Config[:mq_queue]
47
+ DOWNLOAD_PATH = Monitor::Config[:download_path]
48
+ CHEF_URL = Monitor::Config[:chef_url]
49
+ Chef::Config[:node_name] = Monitor::Config[:node_name]
50
+ Chef::Config[:client_key] = Monitor::Config[:client_key]
51
+
52
+ @options = {
53
+ :log_output => true,
54
+ :dir_mode => :normal,
55
+ :dir => Monitor::Config[:pid_dir],
56
+ :log_dir => Monitor::Config[:log_dir]
57
+ }
58
+
59
+ Daemons.run_proc('chef-worker', @options) do
60
+ worker = Monitor::Worker.new
61
+ worker.run()
62
+ end
63
+
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "chef_monitor/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "chef-monitor"
7
+ s.version = Monitor::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.has_rdoc = false
10
+ s.extra_rdoc_files = ["LICENSE"]
11
+ s.authors = ["Sander Botman"]
12
+ s.email = ["sander.botman@gmail.com"]
13
+ s.homepage = "https://github.com/schubergphilis/chef-monitor"
14
+ s.summary = %q{Chef Monitoring tool to monitor all changes made}
15
+ s.description = s.summary
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+ s.license = 'Apache 2.0'
21
+ s.add_dependency "bunny", ">= 1.1.0"
22
+ s.add_dependency "file-tail", ">= 1.0.12"
23
+ s.add_dependency "daemons", ">= 1.1.9"
24
+ s.add_dependency "chef", ">= 10.0.0"
25
+ end
@@ -0,0 +1,23 @@
1
+ #
2
+ # Author:: Sander Botman (<sander.botman@gmail.com>)
3
+ # Copyright:: Copyright (c) 2014 Sander Botman.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ require "chef_monitor/log"
19
+ require "chef_monitor/item"
20
+ require "chef_monitor/itemlist"
21
+ require "chef_monitor/config"
22
+ require "chef_monitor/worker"
23
+ require "chef_monitor/logmon"
@@ -0,0 +1,33 @@
1
+ #
2
+ # Author:: Sander Botman (<sander.botman@gmail.com>)
3
+ # Copyright:: Copyright (c) 2014 Sander Botman.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ require 'mixlib/config'
19
+
20
+ class Monitor
21
+ class Config
22
+
23
+ extend Mixlib::Config
24
+
25
+ default :chef_url, nil
26
+ default :download_path, nil
27
+ default :mq_server, nil
28
+ default :node_name, nil
29
+ default :client_key, nil
30
+
31
+ end
32
+ end
33
+
@@ -0,0 +1,161 @@
1
+ #
2
+ # Author:: Sander Botman (<sander.botman@gmail.com>)
3
+ # Copyright:: Copyright (c) 2014 Sander Botman.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ require 'chef/client'
19
+ require 'chef/knife/cookbook_download'
20
+ require 'chef_monitor/log'
21
+
22
+ class Monitor
23
+ class Item
24
+ def initialize(data)
25
+ @path = nil
26
+ @name = data['name']
27
+ @time = data['time'].freeze
28
+ @user = data['user'].freeze
29
+ @object = data['object'].freeze
30
+ @server = data['server'].freeze
31
+ @action = data['action'].freeze
32
+ @version = data['version'] unless data['version'].nil?
33
+ @organization = data['org'].freeze
34
+ end
35
+
36
+ attr_accessor :path
37
+ attr_reader :name
38
+ attr_reader :time
39
+ attr_reader :user
40
+ attr_reader :object
41
+ attr_reader :action
42
+ attr_reader :version
43
+ attr_reader :organization
44
+
45
+ def download(path)
46
+ case self.object
47
+ when 'data', 'roles', 'clients', 'environments', 'nodes'
48
+ r = download_object(path)
49
+ when 'cookbooks'
50
+ r = download_cookbook(path)
51
+ end
52
+
53
+
54
+ (@version.nil? || @version.empty?) ? object = [@organization, @object, @name].join('/') : object = [@organization, @object, @name, @version].join('/')
55
+
56
+ if r == true
57
+ Monitor::Log.new("Downloaded: " + object, 'INFO')
58
+ return true
59
+ end
60
+ if r.respond_to?("message")
61
+ Monitor::Log.new(r.message + ' with object: ' + object , 'ERROR')
62
+ else
63
+ Monitor::Log.new('Error while downloading object: ' + object , 'ERROR')
64
+ end
65
+ return false
66
+ end
67
+
68
+ def delete(path)
69
+ file = nil
70
+ items = [ path ]
71
+ items << @organization
72
+ items << @object
73
+ if @name
74
+ filename=@name + ".json" if [ "nodes", "clients", "roles", "environments" ].include?(@object)
75
+
76
+ if @object == "cookbooks"
77
+ (@version.nil? || @version.empty?) ? filename=@name : filename=@name + "-" + @version
78
+ end
79
+
80
+ if @object == "data"
81
+ (@version.nil? || @version.empty?) ? filename=@name : filename=@name + "/" + @version + ".json"
82
+ end
83
+
84
+ items << filename
85
+ end
86
+
87
+ file = items.join("/")
88
+ unless file.nil?
89
+ FileUtils.rm_rf(file)
90
+ (@version.nil? || @version.empty?) ? object = [@organization, @object, @name].join('/') : object = [@organization, @object, @name, @version].join('/')
91
+ Monitor::Log.new("Deleted : " + object, 'INFO')
92
+ return true
93
+ end
94
+ return false
95
+ end
96
+
97
+ def commit(path)
98
+ current_dir = Dir.pwd
99
+ commit_dir = File.join(path, @organization, @object)
100
+ Dir.chdir(commit_dir)
101
+ @version ? object = [@organization, @object, @name, @version].join('/') : object = [@organization, @object, @name].join('/')
102
+ text = "User : " + @user + "\nObject : " + @object + "\nAction : " + @action + "\nLog Time : " + @time
103
+ domain = %x(git config hooks.emaildomain)
104
+ domain = "@" + domain unless domain[0,1] == "@"
105
+ username = @name
106
+ useremail = @name + domain
107
+ %x(git config hooks.username #{username} )
108
+ %x(git config hooks.useremail #{useremail} )
109
+ %x(git add .)
110
+ %x(git commit -am \"#{text}\")
111
+ Dir.chdir(current_dir)
112
+ return true
113
+ end
114
+
115
+ private
116
+
117
+ def download_cookbook(path)
118
+ begin
119
+ args = ['cookbook', 'download', @name ]
120
+ args.push @version if @version
121
+ download = Chef::Knife::CookbookDownload.new(args)
122
+ download_dir = File.join(path, @organization, @object)
123
+ FileUtils.mkdir_p(download_dir) unless File.directory?(download_dir)
124
+ download.config[:download_directory] = download_dir
125
+ download.config[:latest] = true unless @version
126
+ download.config[:force] = true
127
+ result = (download.run())
128
+ self.path = download_dir
129
+ rescue Exception => e
130
+ return e
131
+ end
132
+ return true
133
+ end
134
+
135
+ def download_object(path)
136
+ file = File.join(path, @organization, @object, @name + '.json' )
137
+ begin
138
+ case @object
139
+ when 'roles'
140
+ data = JSON.pretty_generate(Chef::Role.load(@name))
141
+ when 'nodes'
142
+ data = JSON.pretty_generate(Chef::Node.load(@name))
143
+ when 'environments'
144
+ data = JSON.pretty_generate(Chef::Environment.load(@name))
145
+ when 'clients'
146
+ data = JSON.pretty_generate(Chef::ApiClient.load(@name))
147
+ when 'data'
148
+ data = JSON.pretty_generate(Chef::DataBagItem.load(@name, @version))
149
+ file = File.join(path, @organization, @object, @name, @version + '.json' )
150
+ end
151
+ FileUtils.mkdir_p(File.dirname(file)) unless File.directory?(File.dirname(file))
152
+ File.open(file, 'w') {|f| f.write(data)}
153
+ self.path = File.dirname(file)
154
+ rescue Exception => e
155
+ return e
156
+ end
157
+ return true
158
+ end
159
+
160
+ end
161
+ end
@@ -0,0 +1,180 @@
1
+ #
2
+ # Author:: Sander Botman (<sander.botman@gmail.com>)
3
+ # Copyright:: Copyright (c) 2014 Sander Botman.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ require 'chef/client'
19
+ require 'chef_monitor/log'
20
+ require 'chef_monitor/item'
21
+
22
+ class Monitor
23
+ class ItemList < Array
24
+
25
+ def initialize(data)
26
+ @name = data['name']
27
+ @time = data['time'].freeze
28
+ @user = data['user'].freeze
29
+ @object = data['object'].freeze
30
+ @server = data['server'].freeze
31
+ @action = data['action'].freeze
32
+ @version = data['version'] unless data['version'].nil? || data['version'].empty?
33
+ @organization = data['org'].freeze
34
+ end
35
+
36
+ attr_reader :name
37
+ attr_reader :time
38
+ attr_reader :user
39
+ attr_reader :object
40
+ attr_reader :action
41
+ attr_reader :version
42
+ attr_reader :organization
43
+
44
+ def get_items()
45
+ name = @name
46
+ object = @object
47
+ version = @version
48
+
49
+ list = []
50
+ items = []
51
+ if object.nil? || object.empty?
52
+ objects = [ "roles", "nodes", "environments", "clients", "cookbooks", "data" ]
53
+ else
54
+ objects = [ object ]
55
+ end
56
+
57
+ objects.each do |object|
58
+ if object == "data"
59
+ if name.nil? || name.empty?
60
+ names = get_names(object)
61
+ names.each {|itemname| list += get_item(object, itemname)}
62
+ else
63
+ list += get_item(object, name)
64
+ end
65
+ else
66
+ if name.nil? || name.empty?
67
+ list += get_item(object)
68
+ else
69
+ return [ create_item(object, name) ]
70
+ end
71
+ end
72
+ end
73
+
74
+ list.each do |item|
75
+ url = item['url'].split('/')
76
+ items << create_item(url[5], url[6], url[7])
77
+ end
78
+ return items
79
+ end
80
+
81
+ def get_names(object)
82
+ names = []
83
+ result = get_item(object)
84
+ result.each do |item|
85
+ url = item['url'].split('/')
86
+ names << url[6]
87
+ end
88
+ return names.uniq
89
+ end
90
+
91
+ def create_item(object, name, version=nil)
92
+ data = {}
93
+ data['time'] = @time
94
+ data['user'] = @user
95
+ data['action'] = @action
96
+ data['server'] = @server
97
+ data['org'] = @organization
98
+ data['name'] = name
99
+ data['object'] = object
100
+ data['version'] = version unless version.nil?
101
+ Monitor::Item.new(data)
102
+ end
103
+
104
+ def parse_data(object, data)
105
+ data = JSON[data].to_hash unless data.class == Hash
106
+ items = []
107
+ data.each do |key,val|
108
+ item = {}
109
+ item['object'] = object
110
+ item['url'] = val
111
+ items << item
112
+ end
113
+ items
114
+ end
115
+
116
+ def get_item(object=nil, name=nil)
117
+ objects = []
118
+ begin
119
+ case object
120
+ when 'roles'
121
+ json = JSON[Chef::Role.list]
122
+ objects = parse_data("role", json)
123
+ when 'nodes'
124
+ json = JSON[Chef::Node.list]
125
+ objects = parse_data("node", json)
126
+ when 'environments'
127
+ json = JSON[Chef::Environment.list]
128
+ objects = parse_data("environment", json)
129
+ when 'clients'
130
+ json = JSON[Chef::ApiClient.list]
131
+ objects = parse_data("client", json)
132
+ when 'data'
133
+ if name
134
+ json = JSON[Chef::DataBag.load(name)]
135
+ items = JSON[json].to_hash
136
+ items.each do |key,val|
137
+ item = {}
138
+ item['object'] = 'data_bag'
139
+ item['url'] = val
140
+ objects << item
141
+ end
142
+ else
143
+ json = JSON[Chef::DataBag.list]
144
+ items = JSON[json].to_hash
145
+ items.each do |key,val|
146
+ objects += get_item(object, key)
147
+ end
148
+ end
149
+ when 'cookbooks'
150
+ json = JSON[rest.get_rest("/cookbooks?num_versions=all")]
151
+ items = JSON[json].to_hash
152
+ items.each do |key,val|
153
+ val['versions'].each do |version|
154
+ item = {}
155
+ item['object'] = 'cookbook'
156
+ item['url'] = version['url']
157
+ objects << item
158
+ end
159
+ end
160
+ end
161
+ rescue Exception => e
162
+ @version ? object = [@organization, @object, @name, @version].join('/') : object = [@organization, @object, @name].join('/')
163
+ if e.respond_to?("message")
164
+ Monitor::Log.new(e.message + ' with object: ' + object , 'ERROR')
165
+ else
166
+ Monitor::Log.new('Error while downloading object: ' + object , 'ERROR')
167
+ end
168
+ return []
169
+ end
170
+ return objects
171
+ end
172
+
173
+ def rest
174
+ @rest ||= begin
175
+ require 'chef/rest'
176
+ Chef::REST.new(Chef::Config[:chef_server_url])
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,35 @@
1
+ #
2
+ # Author:: Sander Botman (<sander.botman@gmail.com>)
3
+ # Copyright:: Copyright (c) 2014 Sander Botman.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ class Monitor
19
+ class Log
20
+
21
+ def initialize(text, type='INFO')
22
+ case type.downcase
23
+ when "INFO".downcase
24
+ puts "[" + Time.now.iso8601 + "] INFO: " + text
25
+ when "WARN".downcase
26
+ puts "[" + Time.now.iso8601 + "] WARN: " + text
27
+ when "ERROR".downcase
28
+ puts "[" + Time.now.iso8601 + "] ERROR: " + text
29
+ else
30
+ puts "[" + Time.now.iso8601 + "] UNKNOWN: " + text
31
+ end
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,62 @@
1
+ #
2
+ # Author:: Sander Botman (<sander.botman@gmail.com>)
3
+ # Copyright:: Copyright (c) 2014 Sander Botman.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ class Monitor
19
+ class Logmon
20
+
21
+ def run
22
+ conn = Bunny.new(:hostname => MQSERVER)
23
+ conn.start
24
+
25
+ ch = conn.create_channel
26
+ q = ch.queue(MQQUEUE, :durable => true)
27
+
28
+ begin
29
+ File.open(MON_FILE) do |mon|
30
+ mon.extend(File::Tail)
31
+ mon.interval = 5
32
+ mon.backward(1)
33
+ mon.tail { |line|
34
+ data = scan(line)
35
+ unless data.nil? || data['org'].nil?
36
+ Monitor::Log.new(data, "INFO")
37
+ q.publish(data, :persistent => true, :content_type => "application/json")
38
+ end
39
+ }
40
+ end
41
+ end
42
+ end
43
+
44
+ def scan(line)
45
+ @regex = /(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) - (.{0})- \[([^\]]+?)\] "(PUT|DELETE|POST) ([^\s]+?) (HTTP\/1\.1)" (\d+) "(.*)" (\d+) "-" "(.*)" "(.*)" "(.*)" "(.*)" "(.*)" "(.*)" "(.*)" "(.*)" "(.*)"/
46
+ if line =~ @regex
47
+ data = {}
48
+ data['time'] = $3
49
+ data['user'] = $16
50
+ data['server'] = ENV['HOSTNAME']
51
+ data['org'] = $5.split('/')[2] unless $5.split('/')[2].nil?
52
+ data['object'] = $5.split('/')[3] unless $5.split('/')[3].nil?
53
+ data['name'] = $5.split('/')[4] unless $5.split('/')[4].nil?
54
+ data['version'] = $5.split('/')[5] unless $5.split('/')[5].nil?
55
+ data['action'] = $4
56
+ return data.to_json
57
+ end
58
+ return nil
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,21 @@
1
+ #
2
+ # Author:: Sander Botman (<sander.botman@gmail.com>)
3
+ # Copyright:: Copyright (c) 2014 Sander Botman.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ class Monitor
19
+ VERSION = "0.1.0"
20
+ MAJOR, MINOR, TINY = VERSION.split('.')
21
+ end
@@ -0,0 +1,76 @@
1
+ #
2
+ # Author:: Sander Botman (<sander.botman@gmail.com>)
3
+ # Copyright:: Copyright (c) 2014 Sander Botman.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ class Monitor
19
+ class Worker
20
+
21
+ def run
22
+ conn = Bunny.new(:hostname => MQSERVER)
23
+ conn.start
24
+
25
+ ch = conn.create_channel
26
+ q = ch.queue(MQQUEUE, :durable => true)
27
+ ch.prefetch(1)
28
+
29
+ begin
30
+ q.subscribe(:ack => true, :block => true) do |delivery_info, properties, body|
31
+ if properties[:content_type] =~ /.*json/
32
+ data = JSON.parse(body).to_hash
33
+
34
+ if data['object'].nil? || data['object'].empty?
35
+ log_obj = data['org']
36
+ elsif data['name'].nil? || data['name'].empty?
37
+ log_obj = [ data['org'], data['object'] ].join('/')
38
+ elsif data['version'].nil? || data['version'].empty?
39
+ log_obj = [ data['org'], data['object'], data['name'] ].join('/')
40
+ else
41
+ log_obj = [ data['org'], data['object'], data['name'], data['version'] ].join('/')
42
+ end
43
+ Monitor::Log.new("Receiving : #{log_obj} action: #{data['action']}", "INFO")
44
+
45
+ unless data['object'].nil? || !File.directory?((File.join(DOWNLOAD_PATH, data['org'])))
46
+ Chef::Config[:chef_server_url] = CHEF_URL + "/organizations/#{data['org']}"
47
+
48
+ Monitor::Item.new(data).delete(DOWNLOAD_PATH)
49
+
50
+ list_obj = Monitor::ItemList.new(data)
51
+ list_items = list_obj.get_items()
52
+
53
+ if data['action'] == "PUT" || data['action'] == "POST" || data['action'] == "DELETE"
54
+ list_items.each do |item|
55
+ if item.download(DOWNLOAD_PATH)
56
+ item.commit(DOWNLOAD_PATH)
57
+ else
58
+ Monitor::Log.new("Could not download #{log_obj}", "ERROR")
59
+ end
60
+ end
61
+ end
62
+ else
63
+ Monitor::Log.new("Ignoring : #{log_obj}", "INFO")
64
+ end
65
+ else
66
+ Monitor::Log.new("Unknown : #{body}", "ERROR")
67
+ end
68
+ ch.ack(delivery_info.delivery_tag)
69
+ end
70
+ rescue Interrupt => _
71
+ conn.close
72
+ end
73
+ end
74
+
75
+ end
76
+ end
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chef-monitor
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Sander Botman
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-02-04 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bunny
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.1.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.1.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: file-tail
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 1.0.12
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 1.0.12
46
+ - !ruby/object:Gem::Dependency
47
+ name: daemons
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: 1.1.9
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 1.1.9
62
+ - !ruby/object:Gem::Dependency
63
+ name: chef
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: 10.0.0
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: 10.0.0
78
+ description: Chef Monitoring tool to monitor all changes made
79
+ email:
80
+ - sander.botman@gmail.com
81
+ executables:
82
+ - chef-logmon
83
+ - chef-worker
84
+ extensions: []
85
+ extra_rdoc_files:
86
+ - LICENSE
87
+ files:
88
+ - .gitignore
89
+ - LICENSE
90
+ - README
91
+ - Rakefile
92
+ - bin/chef-logmon
93
+ - bin/chef-worker
94
+ - chef-monitor.gemspec
95
+ - lib/chef_monitor.rb
96
+ - lib/chef_monitor/config.rb
97
+ - lib/chef_monitor/item.rb
98
+ - lib/chef_monitor/itemlist.rb
99
+ - lib/chef_monitor/log.rb
100
+ - lib/chef_monitor/logmon.rb
101
+ - lib/chef_monitor/version.rb
102
+ - lib/chef_monitor/worker.rb
103
+ homepage: https://github.com/schubergphilis/chef-monitor
104
+ licenses:
105
+ - Apache 2.0
106
+ post_install_message:
107
+ rdoc_options: []
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
+ version: '0'
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ! '>='
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubyforge_project:
124
+ rubygems_version: 1.8.24
125
+ signing_key:
126
+ specification_version: 3
127
+ summary: Chef Monitoring tool to monitor all changes made
128
+ test_files: []