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 +7 -0
- data/.gitignore +22 -0
- data/.travis.yml +14 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +20 -0
- data/README.md +28 -0
- data/Rakefile +6 -0
- data/lib/sensu/settings.rb +15 -0
- data/lib/sensu/settings/constants.rb +5 -0
- data/lib/sensu/settings/loader.rb +255 -0
- data/lib/sensu/settings/rules.rb +154 -0
- data/lib/sensu/settings/validator.rb +76 -0
- data/lib/sensu/settings/validators.rb +23 -0
- data/lib/sensu/settings/validators/api.rb +36 -0
- data/lib/sensu/settings/validators/check.rb +78 -0
- data/lib/sensu/settings/validators/client.rb +113 -0
- data/lib/sensu/settings/validators/filter.rb +18 -0
- data/lib/sensu/settings/validators/handler.rb +133 -0
- data/lib/sensu/settings/validators/mutator.rb +18 -0
- data/lib/sensu/settings/validators/subdue.rb +68 -0
- data/lib/sensu/settings/validators/transport.rb +20 -0
- data/sensu-settings.gemspec +24 -0
- data/spec/assets/conf.d/merger.json +11 -0
- data/spec/assets/conf.d/nested/file.json +11 -0
- data/spec/assets/config.json +126 -0
- data/spec/assets/invalid.json +2 -0
- data/spec/helpers.rb +8 -0
- data/spec/loader_spec.rb +171 -0
- data/spec/rules_spec.rb +65 -0
- data/spec/settings_spec.rb +13 -0
- data/spec/validator_spec.rb +619 -0
- metadata +153 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
module Sensu
|
2
|
+
module Settings
|
3
|
+
module Validators
|
4
|
+
module Mutator
|
5
|
+
# Validate a Sensu mutator definition.
|
6
|
+
# Validates: command, timeout
|
7
|
+
#
|
8
|
+
# @param mutator [Hash] sensu mutator definition.
|
9
|
+
def validate_mutator(mutator)
|
10
|
+
must_be_a_string(mutator[:command]) ||
|
11
|
+
invalid(mutator, "mutator command must be a string")
|
12
|
+
must_be_a_numeric_if_set(mutator[:timeout]) ||
|
13
|
+
invalid(mutator, "mutator timeout must be numeric")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Sensu
|
2
|
+
module Settings
|
3
|
+
module Validators
|
4
|
+
module Subdue
|
5
|
+
# Validate subdue time.
|
6
|
+
# Validates: begin, end
|
7
|
+
#
|
8
|
+
# @param scope [String] definition scope to report under.
|
9
|
+
# @param definition [Hash] sensu definition.
|
10
|
+
# @param object [Hash] to have begin and end validated.
|
11
|
+
def validate_subdue_time(scope, definition, object)
|
12
|
+
if is_a_hash?(object)
|
13
|
+
if either_are_set?(object[:begin], object[:end])
|
14
|
+
must_be_time(object[:begin], object[:end]) ||
|
15
|
+
invalid(definition, "#{scope} begin and end times must be valid")
|
16
|
+
end
|
17
|
+
else
|
18
|
+
invalid(definition, "#{scope} must be a hash")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Validate subdue days.
|
23
|
+
# Validates: days
|
24
|
+
#
|
25
|
+
# @param definition [Hash] sensu definition.
|
26
|
+
def validate_subdue_days(definition)
|
27
|
+
subdue = definition[:subdue]
|
28
|
+
must_be_an_array_if_set(subdue[:days]) ||
|
29
|
+
invalid(definition, "subdue days must be an array")
|
30
|
+
if is_an_array?(subdue[:days])
|
31
|
+
days = %w[sunday monday tuesday wednesday thursday friday saturday]
|
32
|
+
must_be_either(days, subdue[:days]) ||
|
33
|
+
invalid(definition, "subdue days must be valid days of the week")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Validate subdue exceptions.
|
38
|
+
# Validates: exceptions (begin, end)
|
39
|
+
#
|
40
|
+
# @param definition [Hash] sensu definition.
|
41
|
+
def validate_subdue_exceptions(definition)
|
42
|
+
subdue = definition[:subdue]
|
43
|
+
must_be_an_array_if_set(subdue[:exceptions]) ||
|
44
|
+
invalid(definition, "subdue exceptions must be an array")
|
45
|
+
if is_an_array?(subdue[:exceptions])
|
46
|
+
subdue[:exceptions].each do |exception|
|
47
|
+
validate_subdue_time("subdue exceptions", definition, exception)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Validate Sensu subdue, for either a check or handler definition.
|
53
|
+
#
|
54
|
+
# @param definition [Hash] sensu definition.
|
55
|
+
def validate_subdue(definition)
|
56
|
+
subdue = definition[:subdue]
|
57
|
+
validate_subdue_time("subdue", definition, subdue)
|
58
|
+
if is_a_hash?(subdue)
|
59
|
+
must_be_either_if_set(%w[handler publisher], subdue[:at]) ||
|
60
|
+
invalid(definition, "subdue at must be either handler or publisher")
|
61
|
+
validate_subdue_days(definition)
|
62
|
+
validate_subdue_exceptions(definition)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Sensu
|
2
|
+
module Settings
|
3
|
+
module Validators
|
4
|
+
module Transport
|
5
|
+
# Validate a Sensu transport definition.
|
6
|
+
# Validates: name
|
7
|
+
#
|
8
|
+
# @param transport [Hash] sensu transport definition.
|
9
|
+
def validate_transport(transport)
|
10
|
+
must_be_a_hash_if_set(transport) ||
|
11
|
+
invalid(transport, "transport must be a hash")
|
12
|
+
if is_a_hash?(transport)
|
13
|
+
must_be_a_string_if_set(transport[:name]) ||
|
14
|
+
invalid(transport, "transport name must be a string")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "sensu-settings"
|
5
|
+
spec.version = "0.0.1"
|
6
|
+
spec.authors = ["Sean Porter"]
|
7
|
+
spec.email = ["portertech@gmail.com"]
|
8
|
+
spec.summary = "The Sensu settings library, loader and validator"
|
9
|
+
spec.description = "The Sensu settings library, loader and validator"
|
10
|
+
spec.homepage = "https://github.com/sensu/sensu-settings"
|
11
|
+
spec.license = "MIT"
|
12
|
+
|
13
|
+
spec.files = `git ls-files -z`.split("\x0")
|
14
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
15
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
16
|
+
spec.require_paths = ["lib"]
|
17
|
+
|
18
|
+
spec.add_dependency "multi_json", "1.10.0"
|
19
|
+
|
20
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
21
|
+
spec.add_development_dependency "rake"
|
22
|
+
spec.add_development_dependency "rspec"
|
23
|
+
spec.add_development_dependency "codeclimate-test-reporter"
|
24
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
{
|
2
|
+
"api": {
|
3
|
+
"host": "localhost",
|
4
|
+
"port": 4567,
|
5
|
+
"user": "foo",
|
6
|
+
"password": "bar"
|
7
|
+
},
|
8
|
+
"filters": {
|
9
|
+
"production": {
|
10
|
+
"attributes": {
|
11
|
+
"client": {
|
12
|
+
"environment": "production"
|
13
|
+
}
|
14
|
+
}
|
15
|
+
},
|
16
|
+
"development": {
|
17
|
+
"negate": true,
|
18
|
+
"attributes": {
|
19
|
+
"client": {
|
20
|
+
"environment": "production"
|
21
|
+
}
|
22
|
+
}
|
23
|
+
}
|
24
|
+
},
|
25
|
+
"mutators": {
|
26
|
+
"noop": {
|
27
|
+
"command": "cat"
|
28
|
+
}
|
29
|
+
},
|
30
|
+
"handlers": {
|
31
|
+
"default": {
|
32
|
+
"type": "set",
|
33
|
+
"handlers": [
|
34
|
+
"debug"
|
35
|
+
]
|
36
|
+
},
|
37
|
+
"file": {
|
38
|
+
"type": "pipe",
|
39
|
+
"command": "cat > /tmp/sensu_event"
|
40
|
+
},
|
41
|
+
"filtered": {
|
42
|
+
"type": "pipe",
|
43
|
+
"command": "cat > /tmp/sensu_event",
|
44
|
+
"filter": "development"
|
45
|
+
},
|
46
|
+
"severities": {
|
47
|
+
"type": "pipe",
|
48
|
+
"command": "cat > /tmp/sensu_event",
|
49
|
+
"severities": [
|
50
|
+
"critical",
|
51
|
+
"unknown"
|
52
|
+
]
|
53
|
+
},
|
54
|
+
"flapping": {
|
55
|
+
"type": "pipe",
|
56
|
+
"command": "cat > /tmp/sensu_event",
|
57
|
+
"handle_flapping": true
|
58
|
+
},
|
59
|
+
"tcp": {
|
60
|
+
"type": "tcp",
|
61
|
+
"socket": {
|
62
|
+
"host": "localhost",
|
63
|
+
"port": 1234
|
64
|
+
}
|
65
|
+
},
|
66
|
+
"udp": {
|
67
|
+
"type": "udp",
|
68
|
+
"socket": {
|
69
|
+
"host": "localhost",
|
70
|
+
"port": 1234
|
71
|
+
}
|
72
|
+
},
|
73
|
+
"transport": {
|
74
|
+
"type": "transport",
|
75
|
+
"pipe": {
|
76
|
+
"type": "direct",
|
77
|
+
"name": "events"
|
78
|
+
}
|
79
|
+
}
|
80
|
+
},
|
81
|
+
"checks": {
|
82
|
+
"tokens": {
|
83
|
+
"command": "echo -n :::name::: :::nested.attribute::: && exit 2",
|
84
|
+
"subscribers": [
|
85
|
+
"test"
|
86
|
+
],
|
87
|
+
"interval": 1
|
88
|
+
},
|
89
|
+
"standalone": {
|
90
|
+
"command": "echo -n foobar && exit 1",
|
91
|
+
"standalone": true,
|
92
|
+
"interval": 1
|
93
|
+
},
|
94
|
+
"merger": {
|
95
|
+
"command": "this will be overwritten",
|
96
|
+
"subscribers": [
|
97
|
+
"foo"
|
98
|
+
]
|
99
|
+
},
|
100
|
+
"unpublished": {
|
101
|
+
"command": "/bin/true",
|
102
|
+
"publish": false,
|
103
|
+
"subscribers":[
|
104
|
+
"test"
|
105
|
+
]
|
106
|
+
}
|
107
|
+
},
|
108
|
+
"client": {
|
109
|
+
"name": "i-424242",
|
110
|
+
"address": "127.0.0.1",
|
111
|
+
"subscriptions": [
|
112
|
+
"test"
|
113
|
+
],
|
114
|
+
"nested": {
|
115
|
+
"attribute": true
|
116
|
+
},
|
117
|
+
"service": {
|
118
|
+
"password": "secret"
|
119
|
+
},
|
120
|
+
"keepalive": {
|
121
|
+
"thresholds": {
|
122
|
+
"warning": 10
|
123
|
+
}
|
124
|
+
}
|
125
|
+
}
|
126
|
+
}
|
data/spec/helpers.rb
ADDED
data/spec/loader_spec.rb
ADDED
@@ -0,0 +1,171 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "helpers")
|
2
|
+
require "sensu/settings/loader"
|
3
|
+
|
4
|
+
describe "Sensu::Settings::Loader" do
|
5
|
+
include Helpers
|
6
|
+
|
7
|
+
before do
|
8
|
+
@loader = Sensu::Settings::Loader.new
|
9
|
+
@assets_dir = File.join(File.dirname(__FILE__), "assets")
|
10
|
+
@config_file = File.join(@assets_dir, "config.json")
|
11
|
+
@config_dir = File.join(@assets_dir, "conf.d")
|
12
|
+
end
|
13
|
+
|
14
|
+
it "can provide a loader API" do
|
15
|
+
@loader.should respond_to(:load, :validate!)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "can provide indifferent access to settings" do
|
19
|
+
@loader[:checks].should be_kind_of(Hash)
|
20
|
+
@loader["checks"].should be_kind_of(Hash)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "can validate loaded settings" do
|
24
|
+
failures = @loader.validate!
|
25
|
+
failures.size.should eq(0)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "can load RabbitMQ settings from the environment" do
|
29
|
+
ENV["RABBITMQ_URL"] = "amqp://guest:guest@localhost:5672/"
|
30
|
+
@loader.load_env
|
31
|
+
@loader.warnings.size.should eq(1)
|
32
|
+
ENV["RABBITMQ_URL"] = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
it "can load Redis settings from the environment" do
|
36
|
+
ENV["REDIS_URL"] = "redis://username:password@localhost:6789"
|
37
|
+
@loader.load_env
|
38
|
+
@loader.warnings.size.should eq(1)
|
39
|
+
ENV["REDIS_URL"] = nil
|
40
|
+
end
|
41
|
+
|
42
|
+
it "can load Sensu API settings from the environment" do
|
43
|
+
ENV["API_PORT"] = "4567"
|
44
|
+
@loader.load_env
|
45
|
+
@loader.warnings.size.should eq(1)
|
46
|
+
ENV["API_PORT"] = nil
|
47
|
+
end
|
48
|
+
|
49
|
+
it "can load Redis and Sensu API settings from the environment using alternative variables" do
|
50
|
+
ENV["REDISTOGO_URL"] = "redis://username:password@localhost:6789"
|
51
|
+
ENV["PORT"] = "4567"
|
52
|
+
@loader.load_env
|
53
|
+
@loader.warnings.size.should eq(2)
|
54
|
+
ENV["REDISTOGO_URL"] = nil
|
55
|
+
ENV["PORT"] = nil
|
56
|
+
end
|
57
|
+
|
58
|
+
it "can load settings from a file" do
|
59
|
+
@loader.load_file(@config_file)
|
60
|
+
@loader.warnings.size.should eq(1)
|
61
|
+
warning = @loader.warnings.first
|
62
|
+
warning[:object].should eq(File.expand_path(@config_file))
|
63
|
+
warning[:message].should eq("loading config file")
|
64
|
+
@loader[:api][:port].should eq(4567)
|
65
|
+
@loader["api"]["port"].should eq(4567)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "can load settings from a file and validate them" do
|
69
|
+
@loader.load_file(@config_file)
|
70
|
+
failures = @loader.validate!
|
71
|
+
reasons = failures.map do |failure|
|
72
|
+
failure[:message]
|
73
|
+
end
|
74
|
+
reasons.should include("check interval must be an integer")
|
75
|
+
end
|
76
|
+
|
77
|
+
it "can attempt to load settings from a nonexistent file" do
|
78
|
+
@loader.load_file("/tmp/bananaphone")
|
79
|
+
warnings = @loader.warnings
|
80
|
+
warnings.size.should eq(2)
|
81
|
+
messages = warnings.map do |warning|
|
82
|
+
warning[:message]
|
83
|
+
end
|
84
|
+
messages.should include("config file does not exist or is not readable")
|
85
|
+
messages.should include("ignoring config file")
|
86
|
+
end
|
87
|
+
|
88
|
+
it "can attempt to load settings from a file with invalid JSON" do
|
89
|
+
@loader.load_file(File.join(@assets_dir, "invalid.json"))
|
90
|
+
warnings = @loader.warnings
|
91
|
+
warnings.size.should eq(3)
|
92
|
+
messages = warnings.map do |warning|
|
93
|
+
warning[:message]
|
94
|
+
end
|
95
|
+
messages.should include("loading config file")
|
96
|
+
messages.should include("config file must be valid json")
|
97
|
+
messages.should include("ignoring config file")
|
98
|
+
end
|
99
|
+
|
100
|
+
it "can load settings from files in a directory" do
|
101
|
+
@loader.load_directory(@config_dir)
|
102
|
+
warnings = @loader.warnings
|
103
|
+
warnings.size.should eq(4)
|
104
|
+
messages = warnings.map do |warning|
|
105
|
+
warning[:message]
|
106
|
+
end
|
107
|
+
messages.should include("loading config files from directory")
|
108
|
+
messages.should include("loading config file")
|
109
|
+
messages.should include("config file applied changes")
|
110
|
+
@loader[:checks][:nested][:command].should eq("true")
|
111
|
+
end
|
112
|
+
|
113
|
+
it "can attempt to load settings from files in a nonexistent directory" do
|
114
|
+
@loader.load_directory("/tmp/rottentomatos")
|
115
|
+
@loader.warnings.size.should eq(1)
|
116
|
+
warning = @loader.warnings.first
|
117
|
+
warning[:message].should eq("loading config files from directory")
|
118
|
+
end
|
119
|
+
|
120
|
+
it "can load settings from the environment, a file, and a directory" do
|
121
|
+
ENV["RABBITMQ_URL"] = "amqp://guest:guest@localhost:5672/"
|
122
|
+
settings = @loader.load(:config_file => @config_file, :config_dir => @config_dir)
|
123
|
+
settings[:rabbitmq].should eq(ENV["RABBITMQ_URL"])
|
124
|
+
settings[:api][:port].should eq(4567)
|
125
|
+
settings[:checks][:merger][:command].should eq("echo -n merger")
|
126
|
+
settings[:checks][:merger][:subscribers].should eq(["foo", "bar"])
|
127
|
+
settings[:checks][:nested][:command].should eq("true")
|
128
|
+
ENV["SENSU_CONFIG_FILES"].split(":").should eq(@loader.loaded_files)
|
129
|
+
ENV["RABBITMQ_URL"] = nil
|
130
|
+
end
|
131
|
+
|
132
|
+
it "can load settings and determine if certain definitions exist" do
|
133
|
+
@loader.load(:config_file => @config_file, :config_dir => @config_dir)
|
134
|
+
@loader.check_exists?("nonexistent").should be_false
|
135
|
+
@loader.check_exists?("tokens").should be_true
|
136
|
+
@loader.filter_exists?("nonexistent").should be_false
|
137
|
+
@loader.filter_exists?("development").should be_true
|
138
|
+
@loader.mutator_exists?("nonexistent").should be_false
|
139
|
+
@loader.mutator_exists?("noop").should be_true
|
140
|
+
@loader.handler_exists?("nonexistent").should be_false
|
141
|
+
@loader.handler_exists?("default").should be_true
|
142
|
+
end
|
143
|
+
|
144
|
+
it "can load settings and provide setting category accessors" do
|
145
|
+
@loader.load(:config_file => @config_file, :config_dir => @config_dir)
|
146
|
+
@loader.checks.should be_kind_of(Array)
|
147
|
+
@loader.checks.should_not be_empty
|
148
|
+
check = @loader.checks.detect do |check|
|
149
|
+
check[:name] == "tokens"
|
150
|
+
end
|
151
|
+
check[:interval].should eq(1)
|
152
|
+
@loader.filters.should be_kind_of(Array)
|
153
|
+
@loader.filters.should_not be_empty
|
154
|
+
filter = @loader.filters.detect do |filter|
|
155
|
+
filter[:name] == "development"
|
156
|
+
end
|
157
|
+
filter[:negate].should be_true
|
158
|
+
@loader.mutators.should be_kind_of(Array)
|
159
|
+
@loader.mutators.should_not be_empty
|
160
|
+
mutator = @loader.mutators.detect do |mutator|
|
161
|
+
mutator[:name] == "noop"
|
162
|
+
end
|
163
|
+
mutator[:command].should eq("cat")
|
164
|
+
@loader.handlers.should be_kind_of(Array)
|
165
|
+
@loader.handlers.should_not be_empty
|
166
|
+
handler = @loader.handlers.detect do |handler|
|
167
|
+
handler[:name] == "default"
|
168
|
+
end
|
169
|
+
handler[:type].should eq("set")
|
170
|
+
end
|
171
|
+
end
|