sensu-settings 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b95af032c4b3a94e59a491db3c04ba24eb8379b0
4
+ data.tar.gz: 2e86d492aa3f2f2e8cfb55e078e351a4812a3df6
5
+ SHA512:
6
+ metadata.gz: 54a52ab2912061d6eca52dc9f4dbfc84c0532cf9b4593275e653272fdaeb62e2989db94783a120eeb1b08c2630cd8567f261b6dec506c55636a4cffb932a5c72
7
+ data.tar.gz: b4410e0f99ce6cba6d820ef90e7503147a794001c20fcd476f8ef94cc04169b38bf9e19aa3af504d8dbc0a527d14bcdf70310e191c590cfd552e9303f26ff5b2
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/.travis.yml ADDED
@@ -0,0 +1,14 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - 2.0.0
7
+ - 2.1.0
8
+ - jruby
9
+ notifications:
10
+ irc:
11
+ - "irc.freenode.net#sensu"
12
+ addons:
13
+ code_climate:
14
+ repo_token: 824f4a75343a9300704f23e93eaaecb25d998e408f2c9b96cd44f55983d95483
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sensu-settings.gemspec
4
+ gemspec
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
+ [![Build Status](https://travis-ci.org/sensu/sensu-settings.svg?branch=master)](https://travis-ci.org/sensu/sensu-settings)
4
+
5
+ [![Code Climate](https://codeclimate.com/github/sensu/sensu-settings.png)](https://codeclimate.com/github/sensu/sensu-settings)
6
+ [![Code Climate Coverage](https://codeclimate.com/github/sensu/sensu-settings/coverage.png)](https://codeclimate.com/github/sensu/sensu-settings)
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'sensu-settings'
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ ## Usage
19
+
20
+ Documentation can be found [here](http://rubydoc.info/github/sensu/sensu-settings/Sensu/Settings).
21
+
22
+ ## Contributing
23
+
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
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,15 @@
1
+ require "sensu/settings/loader"
2
+
3
+ module Sensu
4
+ module Settings
5
+ # Load Sensu settings.
6
+ #
7
+ # @param options [Hash]
8
+ # @return [Loader] a loaded instance of Loader.
9
+ def self.load(options={})
10
+ loader = Loader.new
11
+ loader.load(options)
12
+ loader
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,5 @@
1
+ module Sensu
2
+ module Settings
3
+ CATEGORIES = [:checks, :filters, :mutators, :handlers]
4
+ end
5
+ end
@@ -0,0 +1,255 @@
1
+ gem "multi_json", "1.10.0"
2
+
3
+ require "sensu/settings/validator"
4
+ require "multi_json"
5
+
6
+ module Sensu
7
+ module Settings
8
+ class Loader
9
+ # @!attribute [r] warnings
10
+ # @return [Array] loader warnings.
11
+ attr_reader :warnings
12
+
13
+ # @!attribute [r] loaded_files
14
+ # @return [Array] loaded config files.
15
+ attr_reader :loaded_files
16
+
17
+ def initialize
18
+ @warnings = []
19
+ @settings = Hash[CATEGORIES.map {|category| [category, {}]}]
20
+ @indifferent_access = false
21
+ @loaded_files = []
22
+ self.class.create_category_methods
23
+ end
24
+
25
+ # Create setting category accessors and methods to test the
26
+ # existence of definitions. Called in initialize().
27
+ def self.create_category_methods
28
+ CATEGORIES.each do |category|
29
+ define_method(category) do
30
+ setting_category(category)
31
+ end
32
+ method_name = category.to_s.chop + "_exists?"
33
+ define_method(method_name.to_sym) do |name|
34
+ definition_exists?(category, name)
35
+ end
36
+ end
37
+ end
38
+
39
+ # Access settings as an indifferent hash.
40
+ #
41
+ # @return [Hash] settings.
42
+ def to_hash
43
+ unless @indifferent_access
44
+ indifferent_access!
45
+ end
46
+ @settings
47
+ end
48
+
49
+ # Retrieve the value object corresponding to a key, acting like
50
+ # a Hash object.
51
+ #
52
+ # @param key [String, Symbol]
53
+ # @return [Object] value for key.
54
+ def [](key)
55
+ to_hash[key]
56
+ end
57
+
58
+ # Load settings from the environment.
59
+ # Loads: RABBITMQ_URL, REDIS_URL, REDISTOGO_URL, API_PORT, PORT
60
+ def load_env
61
+ if ENV["RABBITMQ_URL"]
62
+ @settings[:rabbitmq] = ENV["RABBITMQ_URL"]
63
+ warning(@settings[:rabbitmq], "using rabbitmq url environment variable")
64
+ end
65
+ ENV["REDIS_URL"] ||= ENV["REDISTOGO_URL"]
66
+ if ENV["REDIS_URL"]
67
+ @settings[:redis] = ENV["REDIS_URL"]
68
+ warning(@settings[:redis], "using redis url environment variable")
69
+ end
70
+ ENV["API_PORT"] ||= ENV["PORT"]
71
+ if ENV["API_PORT"]
72
+ @settings[:api] ||= {}
73
+ @settings[:api][:port] = ENV["API_PORT"].to_i
74
+ warning(@settings[:api], "using api port environment variable")
75
+ end
76
+ @indifferent_access = false
77
+ end
78
+
79
+ # Load settings from a JSON file.
80
+ #
81
+ # @param [String] file path.
82
+ def load_file(file)
83
+ if File.file?(file) && File.readable?(file)
84
+ begin
85
+ warning(file, "loading config file")
86
+ contents = IO.read(file)
87
+ config = MultiJson.load(contents, :symbolize_keys => true)
88
+ merged = deep_merge(@settings, config)
89
+ unless @loaded_files.empty?
90
+ changes = deep_diff(@settings, merged)
91
+ warning(changes, "config file applied changes")
92
+ end
93
+ @settings = merged
94
+ @indifferent_access = false
95
+ @loaded_files << file
96
+ rescue MultiJson::ParseError => error
97
+ warning(file, "config file must be valid json")
98
+ warning(file, "ignoring config file")
99
+ end
100
+ else
101
+ warning(file, "config file does not exist or is not readable")
102
+ warning(file, "ignoring config file")
103
+ end
104
+ end
105
+
106
+ # Load settings from files in a directory. Files may be in
107
+ # nested directories.
108
+ #
109
+ # @param [String] directory path.
110
+ def load_directory(directory)
111
+ warning(directory, "loading config files from directory")
112
+ path = directory.gsub(/\\(?=\S)/, "/")
113
+ Dir.glob(File.join(path, "**/*.json")).each do |file|
114
+ load_file(file)
115
+ end
116
+ end
117
+
118
+ # Set Sensu settings related environment variables. This method
119
+ # currently sets SENSU_CONFIG_FILES, a colon delimited list of
120
+ # loaded config files.
121
+ def set_env
122
+ ENV["SENSU_CONFIG_FILES"] = @loaded_files.join(":")
123
+ end
124
+
125
+ # Load settings from the environment and the paths provided, set
126
+ # appropriate environment variables.
127
+ #
128
+ # @param [Hash] options
129
+ # @option options [String] :config_file to load.
130
+ # @option options [String] :config_dir to load.
131
+ # @return [Hash] loaded settings.
132
+ def load(options={})
133
+ load_env
134
+ if options[:config_file]
135
+ load_file(options[:config_file])
136
+ end
137
+ if options[:config_dir]
138
+ load_directory(options[:config_dir])
139
+ end
140
+ set_env
141
+ to_hash
142
+ end
143
+
144
+ # Validate the loaded settings.
145
+ #
146
+ # @return [Array] validation failures.
147
+ def validate!
148
+ service = ::File.basename($0).split("-").last
149
+ validator = Validator.new
150
+ validator.run(@settings, service)
151
+ end
152
+
153
+ private
154
+
155
+ # Retrieve setting category definitions.
156
+ #
157
+ # @param [Symbol] category to retrive.
158
+ # @return [Array<Hash>] category definitions.
159
+ def setting_category(category)
160
+ @settings[category].map do |name, details|
161
+ details.merge(:name => name.to_s)
162
+ end
163
+ end
164
+
165
+ # Check to see if a definition exists in a category.
166
+ #
167
+ # @param [Symbol] category to inspect for the definition.
168
+ # @param [String] name of definition.
169
+ # @return [TrueClass, FalseClass]
170
+ def definition_exists?(category, name)
171
+ @settings[category].has_key?(name.to_sym)
172
+ end
173
+
174
+ # Creates an indifferent hash.
175
+ #
176
+ # @return [Hash] indifferent hash.
177
+ def indifferent_hash
178
+ Hash.new do |hash, key|
179
+ if key.is_a?(String)
180
+ hash[key.to_sym]
181
+ end
182
+ end
183
+ end
184
+
185
+ # Create a copy of a hash with indifferent access.
186
+ #
187
+ # @param hash [Hash] hash to make indifferent.
188
+ # @return [Hash] indifferent version of hash.
189
+ def with_indifferent_access(hash)
190
+ hash = indifferent_hash.merge(hash)
191
+ hash.each do |key, value|
192
+ if value.is_a?(Hash)
193
+ hash[key] = with_indifferent_access(value)
194
+ end
195
+ end
196
+ end
197
+
198
+ # Update settings to have indifferent access.
199
+ def indifferent_access!
200
+ @settings = with_indifferent_access(@settings)
201
+ @indifferent_access = true
202
+ end
203
+
204
+ # Deep merge two hashes.
205
+ #
206
+ # @param [Hash] hash_one to serve as base.
207
+ # @param [Hash] hash_two to merge in.
208
+ def deep_merge(hash_one, hash_two)
209
+ merged = hash_one.dup
210
+ hash_two.each do |key, value|
211
+ merged[key] = case
212
+ when hash_one[key].is_a?(Hash) && value.is_a?(Hash)
213
+ deep_merge(hash_one[key], value)
214
+ when hash_one[key].is_a?(Array) && value.is_a?(Array)
215
+ hash_one[key].concat(value).uniq
216
+ else
217
+ value
218
+ end
219
+ end
220
+ merged
221
+ end
222
+
223
+ # Compare two hashes.
224
+ #
225
+ # @param [Hash] hash_one to compare.
226
+ # @param [Hash] hash_two to compare.
227
+ # @return [Hash] comparison diff hash.
228
+ def deep_diff(hash_one, hash_two)
229
+ keys = hash_one.keys.concat(hash_two.keys).uniq
230
+ keys.inject(Hash.new) do |diff, key|
231
+ unless hash_one[key] == hash_two[key]
232
+ if hash_one[key].is_a?(Hash) && hash_two[key].is_a?(Hash)
233
+ diff[key] = deep_diff(hash_one[key], hash_two[key])
234
+ else
235
+ diff[key] = [hash_one[key], hash_two[key]]
236
+ end
237
+ end
238
+ diff
239
+ end
240
+ end
241
+
242
+ # Record a warning for an object.
243
+ #
244
+ # @param object [Object] under suspicion.
245
+ # @param message [String] warning message.
246
+ # @return [Array] current warnings.
247
+ def warning(object, message)
248
+ @warnings << {
249
+ :object => object,
250
+ :message => message
251
+ }
252
+ end
253
+ end
254
+ end
255
+ end
@@ -0,0 +1,154 @@
1
+ module Sensu
2
+ module Settings
3
+ module Rules
4
+ # Check that a value is a hash.
5
+ #
6
+ # @param value [Object] to check.
7
+ # @return [TrueClass, FalseClass]
8
+ def must_be_a_hash(value)
9
+ value.is_a?(Hash)
10
+ end
11
+ alias_method :is_a_hash?, :must_be_a_hash
12
+
13
+ # Check that a value is a hash, if set (not nil).
14
+ #
15
+ # @param value [Object] to check.
16
+ # @return [TrueClass, FalseClass]
17
+ def must_be_a_hash_if_set(value)
18
+ value.nil? ? true : must_be_a_hash(value)
19
+ end
20
+
21
+ # Check that a value is an array.
22
+ #
23
+ # @param value [Object] to check.
24
+ # @return [TrueClass, FalseClass]
25
+ def must_be_an_array(value)
26
+ value.is_a?(Array)
27
+ end
28
+ alias_method :is_an_array?, :must_be_an_array
29
+
30
+ # Check that a value is an array, if set (not nil).
31
+ #
32
+ # @param value [Object] to check.
33
+ # @return [TrueClass, FalseClass]
34
+ def must_be_an_array_if_set(value)
35
+ value.nil? ? true : must_be_an_array(value)
36
+ end
37
+
38
+ # Check that a value is a string.
39
+ #
40
+ # @param value [Object] to check.
41
+ # @return [TrueClass, FalseClass]
42
+ def must_be_a_string(value)
43
+ value.is_a?(String)
44
+ end
45
+
46
+ # Check that a value is a string, if set (not nil).
47
+ #
48
+ # @param value [Object] to check.
49
+ # @return [TrueClass, FalseClass]
50
+ def must_be_a_string_if_set(value)
51
+ value.nil? ? true : must_be_a_string(value)
52
+ end
53
+
54
+ # Check that a value is an integer.
55
+ #
56
+ # @param value [Object] to check.
57
+ # @return [TrueClass, FalseClass]
58
+ def must_be_an_integer(value)
59
+ value.is_a?(Integer)
60
+ end
61
+
62
+ # Check that a value is an integer, if set (not nil).
63
+ #
64
+ # @param value [Object] to check.
65
+ # @return [TrueClass, FalseClass]
66
+ def must_be_an_integer_if_set(value)
67
+ value.nil? ? true : must_be_an_integer(value)
68
+ end
69
+
70
+ # Check that a value is numeric.
71
+ #
72
+ # @param value [Object] to check.
73
+ # @return [TrueClass, FalseClass]
74
+ def must_be_a_numeric(value)
75
+ value.is_a?(Numeric)
76
+ end
77
+
78
+ # Check that a value is numeric, if set (not nil).
79
+ #
80
+ # @param value [Object] to check.
81
+ # @return [TrueClass, FalseClass]
82
+ def must_be_a_numeric_if_set(value)
83
+ value.nil? ? true : must_be_a_numeric(value)
84
+ end
85
+
86
+ # Check that a value matches a regular expression.
87
+ #
88
+ # @param regex [Regexp] pattern to compare with value.
89
+ # @param value [Object] to check if matches pattern.
90
+ # @return [TrueClass, FalseClass]
91
+ def must_match_regex(regex, value)
92
+ value =~ regex
93
+ end
94
+
95
+ # Check if a value is boolean, if set (no nil).
96
+ #
97
+ # @param value [Object] to check.
98
+ # @return [TrueClass, FalseClass]
99
+ def must_be_boolean_if_set(value)
100
+ value.nil? ? true : (!!value == value)
101
+ end
102
+
103
+ # Check that value items are all strings and not empty.
104
+ #
105
+ # @param value [Array] with items to check.
106
+ # @return [TrueClass, FalseClass]
107
+ def items_must_be_strings(value)
108
+ value.all? do |item|
109
+ item.is_a?(String) && !item.empty?
110
+ end
111
+ end
112
+
113
+ # Check if either of the values are set (not nil).
114
+ #
115
+ # @param values [Array<Object>] to check if not nil.
116
+ # @return [TrueClass, FalseClass]
117
+ def either_are_set?(*values)
118
+ values.any? do |value|
119
+ !value.nil?
120
+ end
121
+ end
122
+
123
+ # Check if values are valid times (can be parsed).
124
+ #
125
+ # @param values [Array<Object>] to check if valid time.
126
+ # @return [TrueClass, FalseClass]
127
+ def must_be_time(*values)
128
+ values.all? do |value|
129
+ Time.parse(value) rescue false
130
+ end
131
+ end
132
+
133
+ # Check if values are allowed.
134
+ #
135
+ # @param allowed [Array<Object>] allowed values.
136
+ # @param values [Array<Object>] to check if allowed.
137
+ # @return [TrueClass, FalseClass]
138
+ def must_be_either(allowed, *values)
139
+ values.flatten.all? do |value|
140
+ allowed.include?(value)
141
+ end
142
+ end
143
+
144
+ # Check if values are allowed, if set (not nil).
145
+ #
146
+ # @param allowed [Array<Object>] allowed values.
147
+ # @param values [Array<Object>] to check if allowed.
148
+ # @return [TrueClass, FalseClass]
149
+ def must_be_either_if_set(allowed, *values)
150
+ values[0].nil? ? true : must_be_either(allowed, values)
151
+ end
152
+ end
153
+ end
154
+ end