portertech-sensu-settings 10.18.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.
- checksums.yaml +7 -0
- data/LICENSE.txt +20 -0
- data/README.md +28 -0
- data/lib/sensu/settings/constants.rb +5 -0
- data/lib/sensu/settings/loader.rb +502 -0
- data/lib/sensu/settings/rules.rb +166 -0
- data/lib/sensu/settings/validator.rb +88 -0
- data/lib/sensu/settings/validators/api.rb +65 -0
- data/lib/sensu/settings/validators/check.rb +253 -0
- data/lib/sensu/settings/validators/client.rb +238 -0
- data/lib/sensu/settings/validators/extension.rb +18 -0
- data/lib/sensu/settings/validators/filter.rb +27 -0
- data/lib/sensu/settings/validators/handler.rb +132 -0
- data/lib/sensu/settings/validators/mutator.rb +18 -0
- data/lib/sensu/settings/validators/sensu.rb +102 -0
- data/lib/sensu/settings/validators/tessen.rb +22 -0
- data/lib/sensu/settings/validators/time_window.rb +74 -0
- data/lib/sensu/settings/validators/transport.rb +22 -0
- data/lib/sensu/settings/validators.rb +29 -0
- data/lib/sensu/settings.rb +52 -0
- data/sensu-settings.gemspec +24 -0
- metadata +133 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4153f6c86a09ed8235ac34d06f7d869ddab639ed09db776dff89f764fe99377b
|
4
|
+
data.tar.gz: 32db3e0c760c611f630895d2eadc7e90665d6190e050c84ca56560286b6cd055
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e512898250001641dcda1b26bdcb740939c6e900804b134e0713fba0ef47cda55f85b3e21cfdd6173edeafa472e16bf2dffacf68bbce65483be5074959c38086
|
7
|
+
data.tar.gz: b23dc848b0b8884192f45c13d08e692eb49182c0034a12f0d59263426bf1258cf02d09d994e1c79f01692a578d69587c04f1a9275d1d3d03a2b806b3c68856f5
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2014 Heavy Water Operations, LLC.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# Sensu::Settings
|
2
|
+
|
3
|
+
[](https://travis-ci.org/sensu/sensu-settings)
|
4
|
+

|
5
|
+

|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'sensu-settings'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
Documentation can be found [here](http://rubydoc.info/github/sensu/sensu-settings/Sensu/Settings).
|
20
|
+
|
21
|
+
## Contributing
|
22
|
+
|
23
|
+
0. By contributing to this project you agree to abide by the [code of conduct](https://sensuapp.org/conduct).
|
24
|
+
1. [Fork it](https://github.com/sensu/sensu-settings/fork)
|
25
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
26
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
27
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
28
|
+
5. Create a new Pull Request
|
@@ -0,0 +1,502 @@
|
|
1
|
+
require "sensu/settings/validator"
|
2
|
+
require "sensu/json"
|
3
|
+
require "tmpdir"
|
4
|
+
require "socket"
|
5
|
+
require "digest"
|
6
|
+
|
7
|
+
module Sensu
|
8
|
+
module Settings
|
9
|
+
class Loader
|
10
|
+
class Error < RuntimeError; end
|
11
|
+
|
12
|
+
# @!attribute [r] warnings
|
13
|
+
# @return [Array] loader warnings.
|
14
|
+
attr_reader :warnings
|
15
|
+
|
16
|
+
# @!attribute [r] errors
|
17
|
+
# @return [Array] loader errors.
|
18
|
+
attr_reader :errors
|
19
|
+
|
20
|
+
# @!attribute [r] loaded_files
|
21
|
+
# @return [Array] loaded config files.
|
22
|
+
attr_reader :loaded_files
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
@warnings = []
|
26
|
+
@errors = []
|
27
|
+
@settings = default_settings
|
28
|
+
@indifferent_access = false
|
29
|
+
@loaded_files = []
|
30
|
+
self.class.create_category_methods
|
31
|
+
end
|
32
|
+
|
33
|
+
# Auto-detected defaults for client definition
|
34
|
+
#
|
35
|
+
# Client name defaults to system hostname.
|
36
|
+
# Client address defaults to first detected non-loopback ipv4 address.
|
37
|
+
#
|
38
|
+
# Client subscriptions are intentionally omitted here as sensu-client
|
39
|
+
# will provide defaults using client name after final settings are
|
40
|
+
# loaded.
|
41
|
+
#
|
42
|
+
# @return [Hash] default client settings
|
43
|
+
def client_defaults
|
44
|
+
{
|
45
|
+
:name => system_hostname,
|
46
|
+
:address => system_address
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
# Default settings.
|
51
|
+
#
|
52
|
+
# @return [Hash] settings.
|
53
|
+
def default_settings
|
54
|
+
default = {
|
55
|
+
:client => {},
|
56
|
+
:sensu => {
|
57
|
+
:spawn => {
|
58
|
+
:limit => 12
|
59
|
+
},
|
60
|
+
:keepalives => {
|
61
|
+
:thresholds => {
|
62
|
+
:warning => 120,
|
63
|
+
:critical => 180
|
64
|
+
}
|
65
|
+
}
|
66
|
+
},
|
67
|
+
:transport => {
|
68
|
+
:name => "rabbitmq",
|
69
|
+
:reconnect_on_error => true
|
70
|
+
}
|
71
|
+
}
|
72
|
+
CATEGORIES.each do |category|
|
73
|
+
default[category] = {}
|
74
|
+
end
|
75
|
+
if ["client", "rspec"].include?(sensu_service_name)
|
76
|
+
default[:client] = client_defaults
|
77
|
+
end
|
78
|
+
default
|
79
|
+
end
|
80
|
+
|
81
|
+
# Create setting category accessors and methods to test the
|
82
|
+
# existence of definitions. Called in initialize().
|
83
|
+
def self.create_category_methods
|
84
|
+
CATEGORIES.each do |category|
|
85
|
+
define_method(category) do
|
86
|
+
setting_category(category)
|
87
|
+
end
|
88
|
+
method_name = category.to_s.chop + "_exists?"
|
89
|
+
define_method(method_name.to_sym) do |name|
|
90
|
+
definition_exists?(category, name)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Access settings as an indifferent hash.
|
96
|
+
#
|
97
|
+
# @return [Hash] settings.
|
98
|
+
def to_hash
|
99
|
+
unless @indifferent_access
|
100
|
+
indifferent_access!
|
101
|
+
@hexdigest = nil
|
102
|
+
end
|
103
|
+
@settings
|
104
|
+
end
|
105
|
+
|
106
|
+
# Retrieve the setting object corresponding to a key, acting
|
107
|
+
# like a Hash object.
|
108
|
+
#
|
109
|
+
# @param key [String, Symbol]
|
110
|
+
# @return [Object] value for key.
|
111
|
+
def [](key)
|
112
|
+
to_hash[key]
|
113
|
+
end
|
114
|
+
|
115
|
+
# Create a SHA256 hex digest for the settings Hash object. The
|
116
|
+
# client definition scope is ignored when the current process is
|
117
|
+
# not a Sensu client, as it is essentially ignored and it will
|
118
|
+
# likely cause a sum mismatch between two Sensu service systems.
|
119
|
+
# This method will not recalculate the hex digest, unless the
|
120
|
+
# settings have been altered, determine by the values of
|
121
|
+
# `@hexdigest` and `@indifferent_access`.
|
122
|
+
#
|
123
|
+
# @return [String] SHA256 hex digest.
|
124
|
+
def hexdigest
|
125
|
+
if @hexdigest && @indifferent_access
|
126
|
+
@hexdigest
|
127
|
+
else
|
128
|
+
hash = case sensu_service_name
|
129
|
+
when "client", "rspec"
|
130
|
+
to_hash
|
131
|
+
else
|
132
|
+
to_hash.reject do |key, value|
|
133
|
+
key.to_s == "client"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
@hexdigest = Digest::SHA256.hexdigest(hash.to_s)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Load settings from the environment.
|
141
|
+
#
|
142
|
+
# Loads: SENSU_TRANSPORT_NAME, RABBITMQ_URL, REDIS_URL,
|
143
|
+
# SENSU_CLIENT_NAME, SENSU_CLIENT_ADDRESS
|
144
|
+
# SENSU_CLIENT_SUBSCRIPTIONS, SENSU_API_PORT
|
145
|
+
def load_env
|
146
|
+
load_transport_env
|
147
|
+
load_rabbitmq_env
|
148
|
+
load_redis_env
|
149
|
+
load_client_env
|
150
|
+
load_api_env
|
151
|
+
end
|
152
|
+
|
153
|
+
# Load settings from a JSON file.
|
154
|
+
#
|
155
|
+
# @param [String] file path.
|
156
|
+
# @param must_exist [TrueClass, FalseClass] if the file must
|
157
|
+
# exist and is readable.
|
158
|
+
def load_file(file, must_exist=true)
|
159
|
+
if File.file?(file) && File.readable?(file)
|
160
|
+
begin
|
161
|
+
warning("loading config file", :file => file)
|
162
|
+
contents = read_config_file(file)
|
163
|
+
config = contents.empty? ? {} : Sensu::JSON.load(contents)
|
164
|
+
merged = deep_merge(@settings, config)
|
165
|
+
unless @loaded_files.empty?
|
166
|
+
changes = deep_diff(@settings, merged)
|
167
|
+
warning("config file applied changes", {
|
168
|
+
:file => file,
|
169
|
+
:changes => changes
|
170
|
+
})
|
171
|
+
end
|
172
|
+
@settings = merged
|
173
|
+
@indifferent_access = false
|
174
|
+
@loaded_files << file
|
175
|
+
rescue Sensu::JSON::ParseError => error
|
176
|
+
load_error("config file must be valid json", {
|
177
|
+
:file => file,
|
178
|
+
:error => error.to_s
|
179
|
+
})
|
180
|
+
end
|
181
|
+
elsif must_exist
|
182
|
+
load_error("config file does not exist or is not readable", :file => file)
|
183
|
+
else
|
184
|
+
warning("config file does not exist or is not readable", :file => file)
|
185
|
+
warning("ignoring config file", :file => file)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# Load settings from files in a directory. Files may be in
|
190
|
+
# nested directories.
|
191
|
+
#
|
192
|
+
# @param [String] directory path.
|
193
|
+
def load_directory(directory)
|
194
|
+
warning("loading config files from directory", :directory => directory)
|
195
|
+
path = directory.gsub(/\\(?=\S)/, "/")
|
196
|
+
if File.readable?(path) && File.executable?(path)
|
197
|
+
Dir.glob(File.join(path, "**{,/*/**}/*.json")).uniq.each do |file|
|
198
|
+
load_file(file)
|
199
|
+
end
|
200
|
+
else
|
201
|
+
load_error("insufficient permissions for loading", :directory => directory)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# Load Sensu client settings overrides. This method adds any overrides to
|
206
|
+
# the client definition. Overrides include:
|
207
|
+
#
|
208
|
+
# * Ensuring client subscriptions include a single subscription based on the
|
209
|
+
# client name, e.g "client:i-424242".
|
210
|
+
def load_client_overrides
|
211
|
+
@settings[:client][:subscriptions] ||= []
|
212
|
+
if @settings[:client][:subscriptions].is_a?(Array)
|
213
|
+
@settings[:client][:subscriptions] << "client:#{@settings[:client][:name]}"
|
214
|
+
@settings[:client][:subscriptions].uniq!
|
215
|
+
warning("applied sensu client overrides", :client => @settings[:client])
|
216
|
+
@indifferent_access = false
|
217
|
+
else
|
218
|
+
warning("unable to apply sensu client overrides", {
|
219
|
+
:reason => "client subscriptions is not an array",
|
220
|
+
:client => @settings[:client]
|
221
|
+
})
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# Load overrides, i.e. settings which should always be present.
|
226
|
+
# Examples include client settings overrides which ensure a per-client subscription.
|
227
|
+
def load_overrides!
|
228
|
+
load_client_overrides if ["client", "rspec"].include?(sensu_service_name)
|
229
|
+
end
|
230
|
+
|
231
|
+
# Set Sensu settings related environment variables. This method
|
232
|
+
# sets `SENSU_LOADED_TEMPFILE` to a new temporary file path,
|
233
|
+
# a file containing the colon delimited list of loaded
|
234
|
+
# configuration files (using `create_loaded_tempfile!()`. The
|
235
|
+
# environment variable `SENSU_CONFIG_FILES` has been removed,
|
236
|
+
# due to the exec ARG_MAX (E2BIG) error when spawning processes
|
237
|
+
# after loading many configuration files (e.g. > 2000).
|
238
|
+
def set_env!
|
239
|
+
ENV["SENSU_LOADED_TEMPFILE"] = create_loaded_tempfile!
|
240
|
+
end
|
241
|
+
|
242
|
+
# Validate the loaded settings.
|
243
|
+
#
|
244
|
+
# @return [Array] validation failures.
|
245
|
+
def validate
|
246
|
+
validator = Validator.new
|
247
|
+
@errors += validator.run(@settings, sensu_service_name)
|
248
|
+
end
|
249
|
+
|
250
|
+
private
|
251
|
+
|
252
|
+
# Retrieve setting category definitions.
|
253
|
+
#
|
254
|
+
# @param [Symbol] category to retrive.
|
255
|
+
# @return [Array<Hash>] category definitions.
|
256
|
+
def setting_category(category)
|
257
|
+
@settings[category].map do |name, details|
|
258
|
+
details.merge(:name => name.to_s)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
# Check to see if a definition exists in a category.
|
263
|
+
#
|
264
|
+
# @param [Symbol] category to inspect for the definition.
|
265
|
+
# @param [String] name of definition.
|
266
|
+
# @return [TrueClass, FalseClass]
|
267
|
+
def definition_exists?(category, name)
|
268
|
+
@settings[category].has_key?(name.to_sym)
|
269
|
+
end
|
270
|
+
|
271
|
+
# Creates an indifferent hash.
|
272
|
+
#
|
273
|
+
# @return [Hash] indifferent hash.
|
274
|
+
def indifferent_hash
|
275
|
+
Hash.new do |hash, key|
|
276
|
+
if key.is_a?(String)
|
277
|
+
hash[key.to_sym]
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
# Create a copy of a hash with indifferent access.
|
283
|
+
#
|
284
|
+
# @param hash [Hash] hash to make indifferent.
|
285
|
+
# @return [Hash] indifferent version of hash.
|
286
|
+
def with_indifferent_access(hash)
|
287
|
+
hash = indifferent_hash.merge(hash)
|
288
|
+
hash.each do |key, value|
|
289
|
+
if value.is_a?(Hash)
|
290
|
+
hash[key] = with_indifferent_access(value)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
# Update settings to have indifferent access.
|
296
|
+
def indifferent_access!
|
297
|
+
@settings = with_indifferent_access(@settings)
|
298
|
+
@indifferent_access = true
|
299
|
+
end
|
300
|
+
|
301
|
+
# Load Sensu transport settings from the environment. This
|
302
|
+
# method sets the Sensu transport name to `SENSU_TRANSPORT_NAME`
|
303
|
+
# if set.
|
304
|
+
def load_transport_env
|
305
|
+
if ENV["SENSU_TRANSPORT_NAME"]
|
306
|
+
@settings[:transport][:name] = ENV["SENSU_TRANSPORT_NAME"]
|
307
|
+
warning("using sensu transport name environment variable", :transport => @settings[:transport])
|
308
|
+
@indifferent_access = false
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
# Load Sensu RabbitMQ settings from the environment. This method
|
313
|
+
# sets the RabbitMQ settings to `RABBITMQ_URL` if set. The Sensu
|
314
|
+
# RabbitMQ transport accepts a URL string for options.
|
315
|
+
def load_rabbitmq_env
|
316
|
+
if ENV["RABBITMQ_URL"]
|
317
|
+
@settings[:rabbitmq] = ENV["RABBITMQ_URL"]
|
318
|
+
warning("using rabbitmq url environment variable", :rabbitmq => @settings[:rabbitmq])
|
319
|
+
@indifferent_access = false
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
# Load Sensu Redis settings from the environment.
|
324
|
+
#
|
325
|
+
# This method evaluates the REDIS_SENTINEL_URLS and REDIS_URL environment variables
|
326
|
+
# and configures the Redis settings accordingly.
|
327
|
+
#
|
328
|
+
# When REDIS_SENTINEL_URLS is provided as a list of one or more
|
329
|
+
# comma-separated URLs, e.g.
|
330
|
+
# "redis://10.0.0.1:26379,redis://10.0.0.2:26379" these URLs will take
|
331
|
+
# precedence over the value provided by REDIS_URL, if any.
|
332
|
+
#
|
333
|
+
# As the redis library accepts a URL string for options. This
|
334
|
+
# configuration applies to data storage and the redis transport, if used.
|
335
|
+
def load_redis_env
|
336
|
+
if ENV["REDIS_SENTINEL_URLS"]
|
337
|
+
@settings[:redis] = {:sentinels => ENV["REDIS_SENTINEL_URLS"]}
|
338
|
+
warning("using redis sentinel url environment variable", :sentinels => @settings[:redis][:sentinels])
|
339
|
+
@indifferent_access = false
|
340
|
+
elsif ENV["REDIS_URL"]
|
341
|
+
@settings[:redis] = ENV["REDIS_URL"]
|
342
|
+
warning("using redis url environment variable", :redis => @settings[:redis])
|
343
|
+
@indifferent_access = false
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
# Load Sensu client settings from the environment. This method
|
348
|
+
# loads client settings from several variables:
|
349
|
+
# `SENSU_CLIENT_NAME`, `SENSU_CLIENT_ADDRESS`, and
|
350
|
+
# `SENSU_CLIENT_SUBSCRIPTIONS`.
|
351
|
+
def load_client_env
|
352
|
+
@settings[:client][:name] = ENV["SENSU_CLIENT_NAME"] if ENV["SENSU_CLIENT_NAME"]
|
353
|
+
@settings[:client][:address] = ENV["SENSU_CLIENT_ADDRESS"] if ENV["SENSU_CLIENT_ADDRESS"]
|
354
|
+
@settings[:client][:subscriptions] = ENV["SENSU_CLIENT_SUBSCRIPTIONS"].split(",") if ENV["SENSU_CLIENT_SUBSCRIPTIONS"]
|
355
|
+
if ENV.keys.any? {|k| k =~ /^SENSU_CLIENT/}
|
356
|
+
warning("using sensu client environment variables", :client => @settings[:client])
|
357
|
+
end
|
358
|
+
@indifferent_access = false
|
359
|
+
end
|
360
|
+
|
361
|
+
# Load Sensu API settings from the environment. This method sets
|
362
|
+
# the API port to `SENSU_API_PORT` if set.
|
363
|
+
def load_api_env
|
364
|
+
if ENV["SENSU_API_PORT"]
|
365
|
+
@settings[:api] ||= {}
|
366
|
+
@settings[:api][:port] = ENV["SENSU_API_PORT"].to_i
|
367
|
+
warning("using api port environment variable", :api => @settings[:api])
|
368
|
+
@indifferent_access = false
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
# Read a configuration file and force its encoding to 8-bit
|
373
|
+
# ASCII, ignoring invalid characters. If there is a UTF-8 BOM,
|
374
|
+
# it will be removed. Some JSON parsers force ASCII but do not
|
375
|
+
# remove the UTF-8 BOM if present, causing encoding conversion
|
376
|
+
# errors. This method is for consistency across Sensu::JSON
|
377
|
+
# adapters and system platforms.
|
378
|
+
#
|
379
|
+
# @param [String] file path to read.
|
380
|
+
# @return [String] file contents.
|
381
|
+
def read_config_file(file)
|
382
|
+
contents = IO.read(file)
|
383
|
+
if contents.respond_to?(:force_encoding)
|
384
|
+
encoding = ::Encoding::ASCII_8BIT
|
385
|
+
contents = contents.force_encoding(encoding)
|
386
|
+
contents.sub!("\xEF\xBB\xBF".force_encoding(encoding), "")
|
387
|
+
else
|
388
|
+
contents.sub!(/^\357\273\277/, "")
|
389
|
+
end
|
390
|
+
contents.strip
|
391
|
+
end
|
392
|
+
|
393
|
+
# Deep merge two hashes.
|
394
|
+
#
|
395
|
+
# @param [Hash] hash_one to serve as base.
|
396
|
+
# @param [Hash] hash_two to merge in.
|
397
|
+
# @return [Hash] deep merged hash.
|
398
|
+
def deep_merge(hash_one, hash_two)
|
399
|
+
merged = hash_one.dup
|
400
|
+
hash_two.each do |key, value|
|
401
|
+
merged[key] = case
|
402
|
+
when hash_one[key].is_a?(Hash) && value.is_a?(Hash)
|
403
|
+
deep_merge(hash_one[key], value)
|
404
|
+
when hash_one[key].is_a?(Array) && value.is_a?(Array)
|
405
|
+
hash_one[key].concat(value).uniq
|
406
|
+
else
|
407
|
+
value
|
408
|
+
end
|
409
|
+
end
|
410
|
+
merged
|
411
|
+
end
|
412
|
+
|
413
|
+
# Compare two hashes.
|
414
|
+
#
|
415
|
+
# @param [Hash] hash_one to compare.
|
416
|
+
# @param [Hash] hash_two to compare.
|
417
|
+
# @return [Hash] comparison diff hash.
|
418
|
+
def deep_diff(hash_one, hash_two)
|
419
|
+
keys = hash_one.keys.concat(hash_two.keys).uniq
|
420
|
+
keys.inject(Hash.new) do |diff, key|
|
421
|
+
unless hash_one[key] == hash_two[key]
|
422
|
+
if hash_one[key].is_a?(Hash) && hash_two[key].is_a?(Hash)
|
423
|
+
diff[key] = deep_diff(hash_one[key], hash_two[key])
|
424
|
+
else
|
425
|
+
diff[key] = [hash_one[key], hash_two[key]]
|
426
|
+
end
|
427
|
+
end
|
428
|
+
diff
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
# Create a temporary file containing the colon delimited list of
|
433
|
+
# loaded configuration files. Ruby TempFile is not used to
|
434
|
+
# create the temporary file as it would be removed if the Sensu
|
435
|
+
# service daemonizes (fork/detach). The file is created in the
|
436
|
+
# system temporary file directory for the platform (Linux,
|
437
|
+
# Windows, etc.) and the file name contains the Sensu service
|
438
|
+
# name to reduce the likelihood of one Sensu service affecting
|
439
|
+
# another.
|
440
|
+
#
|
441
|
+
# @return [String] tempfile path.
|
442
|
+
def create_loaded_tempfile!
|
443
|
+
dir = ENV["SENSU_LOADED_TEMPFILE_DIR"] || Dir.tmpdir
|
444
|
+
file_name = "sensu_#{sensu_service_name}_loaded_files"
|
445
|
+
path = File.join(dir, file_name)
|
446
|
+
File.open(path, "w") do |file|
|
447
|
+
file.write(@loaded_files.join(":"))
|
448
|
+
end
|
449
|
+
path
|
450
|
+
end
|
451
|
+
|
452
|
+
# Retrieve Sensu service name.
|
453
|
+
#
|
454
|
+
# @return [String] service name.
|
455
|
+
def sensu_service_name
|
456
|
+
File.basename($0).split("-").last
|
457
|
+
end
|
458
|
+
|
459
|
+
# Retrieve the system hostname. If the hostname cannot be
|
460
|
+
# determined and an error is thrown, return "unknown", the same
|
461
|
+
# value Sensu uses for JIT clients.
|
462
|
+
#
|
463
|
+
# @return [String] system hostname.
|
464
|
+
def system_hostname
|
465
|
+
Socket.gethostname rescue "unknown"
|
466
|
+
end
|
467
|
+
|
468
|
+
# Retrieve the system IP address. If a valid non-loopback
|
469
|
+
# IPv4 address cannot be found and an error is thrown,
|
470
|
+
# "unknown" will be returned.
|
471
|
+
#
|
472
|
+
# @return [String] system ip address
|
473
|
+
def system_address
|
474
|
+
Socket.ip_address_list.find { |address|
|
475
|
+
address.ipv4? && !address.ipv4_loopback?
|
476
|
+
}.ip_address rescue "unknown"
|
477
|
+
end
|
478
|
+
|
479
|
+
# Record a warning.
|
480
|
+
#
|
481
|
+
# @param message [String] warning message.
|
482
|
+
# @param data [Hash] warning context.
|
483
|
+
# @return [Array] current warnings.
|
484
|
+
def warning(message, data={})
|
485
|
+
@warnings << {
|
486
|
+
:message => message
|
487
|
+
}.merge(data)
|
488
|
+
end
|
489
|
+
|
490
|
+
# Record a load error and raise a load error exception.
|
491
|
+
#
|
492
|
+
# @param message [String] load error message.
|
493
|
+
# @param data [Hash] load error context.
|
494
|
+
def load_error(message, data={})
|
495
|
+
@errors << {
|
496
|
+
:message => message
|
497
|
+
}.merge(data)
|
498
|
+
raise(Error, message)
|
499
|
+
end
|
500
|
+
end
|
501
|
+
end
|
502
|
+
end
|