check 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ ruby "1.9.3"
2
+ source 'https://rubygems.org'
3
+
4
+ gemspec
5
+
6
+ group :development do
7
+ gem 'awesome_print', '~> 1.1.0'
8
+ gem 'benchmark-ips', '~> 1.2.0'
9
+ gem 'pry', '~> 0.9.10'
10
+ gem 'rake', '~> 0.9.2.2'
11
+ end
12
+
13
+ group :test do
14
+ gem 'guard-minitest', '~> 0.5.0'
15
+ gem 'minitest', '~> 4.2.0'
16
+ gem 'rack-test', '~> 0.6.2'
17
+ gem 'redis_gun', '~> 0.1.0'
18
+ gem 'turn', '~> 0.9.6'
19
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) Gerhard Lazu
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # Check
2
+
3
+ [![travis][1]][2]
4
+
5
+ Redis backed service for monitoring metric data streams against
6
+ pre-defined thresholds.
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'check'
13
+
14
+ And then run:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install check
21
+
22
+ ## Usage
23
+
24
+ Check the examples directory. To run a specific example:
25
+
26
+ $ ruby examples/metric_check.rb
27
+
28
+ NB: you will need to have all gems in the `development` group installed.
29
+
30
+ ## Benchmarks
31
+
32
+ Check the benchmarks directory. To run a specific benchmark:
33
+
34
+ $ ruby benchmarks/metric_check.rb
35
+
36
+ NB: you will need to have all gems in `development` group installed.
37
+
38
+ ## API
39
+
40
+ The gem comes with a self-contained API. It's powered by grape and
41
+ it includes a config.ru and unicorn.conf (check the examples folder).
42
+
43
+ Unicorn is **not** declared as a dependency, feel free to choose
44
+ whichever ruby web server you prefer in the service implementing this
45
+ gem.
46
+
47
+ If you have the gem repository cloned and want to test out the API:
48
+
49
+ unicorn -c examples/unicorn.conf.rb examples/config.ru
50
+
51
+ ## Notifications
52
+
53
+ Check uses redis pub/sub to notify of new positives. There is a
54
+ `Check::Notifications` class which should make it easy to set up and
55
+ configure your own subscriber. It the simplest form, it looks like this:
56
+
57
+ ```ruby
58
+ require 'check/notifications'
59
+
60
+ Check::Notifications.new
61
+ ```
62
+
63
+ The default notifications subscriber is highly customizable, but if this
64
+ isn't enough, feel free to use it as an example for rolling your own.
65
+
66
+ ## Contributing
67
+
68
+ 1. Fork it
69
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
70
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
71
+ 4. Push to the branch (`git push origin my-new-feature`)
72
+ 5. Create new Pull Request
73
+
74
+ [1]: https://secure.travis-ci.org/gosquared/check.png
75
+ [2]: http://travis-ci.org/gosquared/check
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require 'bundler/setup'
4
+ require 'bundler/gem_tasks'
5
+ require 'rake/testtask'
6
+
7
+ Rake::TestTask.new do |t|
8
+ t.pattern = 'test/**/*_test.rb'
9
+ end
10
+
11
+ task :default => [:test]
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'benchmark/ips'
5
+ require 'pry'
6
+
7
+ require_relative '../test/support/redis'
8
+
9
+ at_exit { stop_test_redis_server }
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative 'benchmark'
4
+ require_relative '../lib/check/metric'
5
+
6
+ metric1 = Check::Metric.new(
7
+ name: "metric1",
8
+ lower: 5,
9
+ matches_for_positive: 1000,
10
+ suspend_after_positives: 10
11
+ ).save
12
+
13
+ metric2 = Check::Metric.new(
14
+ name: "metric2",
15
+ lower: 5,
16
+ matches_for_positive: 10,
17
+ suspend_after_positives: 2
18
+ ).save
19
+
20
+ metric3 = Check::Metric.new(
21
+ name: "metric3"
22
+ ).save
23
+ metric3.disable!
24
+
25
+ below_lower = (0..4).to_a
26
+
27
+ Benchmark.ips(1) do |x|
28
+ x.report("Check always") do
29
+ Check::Metric.find(name: "metric1").check(
30
+ value: below_lower.sample
31
+ )
32
+ end
33
+
34
+ x.report("Check suspended") do
35
+ Check::Metric.find(name: "metric2").check(
36
+ value: below_lower.sample
37
+ )
38
+ end
39
+
40
+ x.report("Check disabled") do
41
+ Check::Metric.find(name: "metric3").check(
42
+ value: below_lower.sample
43
+ )
44
+ end
45
+
46
+ x.report("Check inexistent") do
47
+ Check::Metric.find(name: "metric4").check(
48
+ value: below_lower.sample
49
+ )
50
+ end
51
+ end
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative 'benchmark'
4
+ require_relative '../lib/check/metric'
5
+
6
+ range = (1..10000).to_a
7
+ 10000.times do |i|
8
+ Check::Metric.new(name: "metric-#{i}", lower: i).save
9
+ Check::Metric.new(name: "metric-delete", lower: range.sample).save
10
+ end
11
+
12
+ Benchmark.ips(1) do |x|
13
+ x.report("Create unique") do
14
+ i = 0
15
+ Check::Metric.new(
16
+ name: "metric#{i}",
17
+ lower: 10,
18
+ upper: 100,
19
+ over_seconds: 120
20
+ ).save
21
+ i += 1
22
+ end
23
+
24
+ x.report("Create similar") do
25
+ i = 0
26
+ Check::Metric.new(
27
+ name: "metric",
28
+ lower: 10,
29
+ upper: 100,
30
+ over_seconds: 120
31
+ ).save
32
+ i += 1
33
+ end
34
+
35
+ x.report("Delete unique") do
36
+ Check::Metric.new(name: "metric-delete", lower: range.sample).delete
37
+ end
38
+
39
+ x.report("Delete similar") do
40
+ sample = range.sample
41
+ Check::Metric.new(name: "metric-#{sample}", lower: sample).delete
42
+ end
43
+ end
data/check.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env gem build
2
+ # -*- encoding: utf-8 -*-
3
+ lib = File.expand_path('../lib/', __FILE__)
4
+ $:.unshift lib unless $:.include?(lib)
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = 'check'
8
+ gem.version = '0.2.0'
9
+ gem.authors = ['Gerhard Lazu']
10
+ gem.email = ['gerhard@lazu.co.uk']
11
+ gem.description = 'Redis backed service for monitoring metric data streams against pre-defined thresholds'
12
+ gem.summary = 'Data stream monitor'
13
+ gem.homepage = 'https://github.com/gosquared/osprey'
14
+
15
+ gem.files = Dir['lib/**/*', 'examples/**/*', 'benchmarks/**/*', 'Gemfile', 'check.gemspec', 'Rakefile', 'README.md', 'LICENSE']
16
+ gem.executables = Dir['bin/*']
17
+ gem.test_files = Dir['test/**/*']
18
+ gem.require_paths = ['lib']
19
+
20
+ gem.add_runtime_dependency 'hashr', '~> 0.0.21'
21
+ gem.add_runtime_dependency 'hiredis', '~> 0.4.5'
22
+ gem.add_runtime_dependency 'msgpack', '~> 0.4.7'
23
+ gem.add_runtime_dependency 'redis', '~> 3.0.2'
24
+ gem.add_runtime_dependency 'redis-objects', '~> 0.6.1'
25
+
26
+ ## API
27
+ gem.add_runtime_dependency 'grape', '~> 0.2.2'
28
+ gem.add_runtime_dependency 'grape-swagger', '~> 0.3.0'
29
+ end
@@ -0,0 +1,4 @@
1
+ require'bundler/setup'
2
+ require 'check/api'
3
+
4
+ run Check::API
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'awesome_print'
5
+ require 'pry'
6
+
7
+ require_relative '../test/support/redis'
8
+
9
+ def exemplify(description, object)
10
+ puts "\n::: #{description} ".ljust(70, ":::")
11
+ ap(object, indent: -2)
12
+ end
13
+
14
+ at_exit { stop_test_redis_server }
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative 'example'
4
+ require_relative '../lib/check/metric'
5
+
6
+ metric = Check::Metric.new(
7
+ name: "metric1",
8
+ lower: 5,
9
+ upper: 10,
10
+ matches_for_positive: 2,
11
+ suspend_after_positives: 1,
12
+ suspend_for_seconds: 60
13
+ ).save
14
+
15
+ exemplify("Default metric check matches", metric.matches.values)
16
+
17
+ metric.check(value: 4)
18
+ exemplify("Metric check matches after first match", metric.matches.values)
19
+ exemplify("Is this metric checking suspended?", metric.suspended?)
20
+
21
+ metric.check(value: 3)
22
+ exemplify("Metric check matches after second match", metric.matches.values)
23
+ exemplify("Metric check positives after second match", metric.positives.values)
24
+ exemplify("Is this metric checking suspended?", metric.suspended?)
25
+
26
+ metric.check(value: 2)
27
+ exemplify("As the metric check is suspended, new matches will be ignored", metric.matches.values)
28
+ exemplify("Deleting all positives will unsuspend it", metric.delete_positives)
29
+
30
+ exemplify("Metric checking manually disabled", metric.disable!)
31
+ metric.check(value: 1)
32
+ exemplify("As the metric check is disabled, new matches will be ignored", metric.matches.values)
33
+
34
+ exemplify("Metric checking manually enabled", metric.enable!)
35
+ metric.check(value: 0)
36
+ exemplify("New matches no longer ignored", metric.matches.values)
@@ -0,0 +1,21 @@
1
+ require_relative 'example'
2
+ require_relative '../lib/check/metric'
3
+
4
+ metric_check = Check::Metric.new(name: "metric1", lower: 10, upper: 100, over_seconds: 120).save
5
+ exemplify("Check::Metric from hash", metric_check)
6
+
7
+ metric_check = Check::Metric.new(name: metric_check[:name])
8
+ metric_check.lower = 100
9
+ metric_check.upper = 1000
10
+ metric_check.over_seconds = 3600
11
+ metric_check.matches_for_positive = 1
12
+ metric_check.save
13
+ exemplify("Check::Metric from attributes", metric_check)
14
+
15
+ exemplify("Creating a new metric with the same name as the existing one will group them together", metric_check.similar)
16
+
17
+ metric_check.delete
18
+ exemplify("Similar metrics after metric.delete", metric_check.similar)
19
+
20
+ Check::Metric.delete_all(metric_check.name)
21
+ exemplify("Similar metrics after Metric.delete_all(name)", metric_check.similar)
@@ -0,0 +1,41 @@
1
+ # See http://unicorn.bogomips.org/Unicorn/Configurator.html for complete
2
+ # documentation.
3
+
4
+ # Use at least one worker per core if you're on a dedicated server,
5
+ # more will usually help for _short_ waits on databases/caches.
6
+ worker_processes ENV.fetch('API_INSTANCES') { 1 }
7
+
8
+ # listen on both a Unix domain socket and a TCP port,
9
+ #
10
+ # The backlog is for the listen() syscall.
11
+ # Some operating systems allow negative values here to specify the
12
+ # maximum allowable value. In most cases, this number is only
13
+ # recommendation and there are other OS-specific tunables and variables
14
+ # that can affect this number. See the listen(2) syscall documentation
15
+ # of your OS for the exact semantics of this.
16
+ # The shorter backlog ensures quicker failover when busy, and helps the
17
+ # load balancer spread requests evenly.
18
+ listen ENV.fetch('PORT') { 9000 }, backlog: ENV.fetch('TCP_BACKLOG') { 128 }.to_i
19
+ listen ENV.fetch('SOCKET') { '/tmp/check_api.sock' }, backlog: ENV.fetch('UNIX_BACKLOG') { 128 }.to_i
20
+
21
+ # Sets the timeout of worker processes to seconds. Workers handling the
22
+ # request/app.call/response cycle taking longer than this time period
23
+ # will be forcibly killed (via SIGKILL). This timeout is enforced by the
24
+ # master process itself and not subject to the scheduling limitations by
25
+ # the worker process. Due the low-complexity, low-overhead
26
+ # implementation, timeouts of less than 3.0 seconds can be considered
27
+ # inaccurate and unsafe.
28
+ # For running Unicorn behind nginx, it is recommended to set
29
+ # "fail_timeout=0" for in your nginx configuration like this to have
30
+ # nginx always retry backends that may have had workers SIGKILL-ed due
31
+ # to timeouts.
32
+ timeout ENV.fetch('API_TIMEOUT') { 30 }
33
+
34
+ # PID of the unicorn master process
35
+ pid ENV.fetch('API_PID') { '/tmp/check_api.pid' }
36
+
37
+ # By default, the Unicorn logger will write to stderr.
38
+ # Additionally, some applications/frameworks log to stderr or stdout,
39
+ # so prevent them from going to /dev/null when daemonized here:
40
+ stderr_path ENV.fetch('API_STDERR') { $STDERR }
41
+ stdout_path ENV.fetch('API_STDOUT') { $STDOUT }
data/lib/check.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'bundler/setup'
2
+ require 'redis'
3
+
4
+ module Check
5
+ extend self
6
+
7
+ # Can be either redis:// or unix://
8
+ REDIS_URI = ENV.fetch('REDIS_URI', "redis://localhost:6379")
9
+ REDIS_DB = ENV.fetch('REDIS_DB', 0).to_i
10
+ REDIS_NOTIFICATIONS = ENV.fetch('REDIS_NOTIFICATIONS', "check_notifications")
11
+
12
+ Redis.current = Redis.new(
13
+ url: Check::REDIS_URI,
14
+ db: Check::REDIS_DB,
15
+ driver: :hiredis
16
+ )
17
+ end
data/lib/check/api.rb ADDED
@@ -0,0 +1,50 @@
1
+ require 'grape'
2
+ #require 'grape-swagger'
3
+
4
+ require_relative 'api/metrics'
5
+
6
+ module Check
7
+ class API < Grape::API
8
+ default_format :json
9
+ error_format :json
10
+ format :json
11
+
12
+ KEY = ENV.fetch('API_KEY') { false }
13
+
14
+ helpers do
15
+ def authorize!
16
+ if API::KEY
17
+ unless params[:key] == API::KEY
18
+ throw :error,
19
+ :message => { :errors => { :key => ['invalid'] } },
20
+ :status => 401
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ before { authorize! }
27
+
28
+ mount Metrics
29
+ # add_swagger_documentation(mount_path: '/swagger')
30
+ #
31
+ # TODO: would have been very nice to get this working, but grape-swagger
32
+ # looks broken and I can't look into a fix for the initial release.
33
+ #
34
+ # 1. All REST methods get .json format hardcoded
35
+ #
36
+ # 2. When testing, all requests get sent with the OPTIONS method, even
37
+ # though they are clearly defined as POST, DELETE etc.
38
+ #
39
+ # In the meantime, let's expose all routes
40
+ resources :routes do
41
+ # This should have been the default route, but grape requires a fix for this:
42
+ # https://github.com/intridea/grape/issues/86
43
+ desc 'The current page, showing details about all available routes'
44
+ get do
45
+ Metrics.routes.map { |route| route.instance_variable_get(:@options) }
46
+ end
47
+ end
48
+
49
+ end
50
+ end