sensu-settings 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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