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.
@@ -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