sensu-settings 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,11 @@
1
+ {
2
+ "checks": {
3
+ "merger": {
4
+ "command": "echo -n merger",
5
+ "subscribers": [
6
+ "bar"
7
+ ],
8
+ "interval": 60
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "checks": {
3
+ "nested": {
4
+ "command": "true",
5
+ "subscribers": [
6
+ "test"
7
+ ],
8
+ "interval": 30
9
+ }
10
+ }
11
+ }
@@ -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
+ }
@@ -0,0 +1,2 @@
1
+ {
2
+ "invalid": "json"
data/spec/helpers.rb ADDED
@@ -0,0 +1,8 @@
1
+ require "rspec"
2
+
3
+ unless RUBY_VERSION < "1.9" || RUBY_PLATFORM =~ /java/
4
+ require "codeclimate-test-reporter"
5
+ CodeClimate::TestReporter.start
6
+ end
7
+
8
+ module Helpers; end
@@ -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