overwatch-collection 0.1.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,4 @@
1
+ log/*.log
2
+ pkg
3
+ .yardoc
4
+ doc
data/.watchr ADDED
@@ -0,0 +1,18 @@
1
+ def run_spec(file)
2
+ unless File.exist?(file)
3
+ puts "#{file} does not exist"
4
+ return
5
+ end
6
+
7
+ puts "Running #{file}"
8
+ system "bundle exec rspec #{file}"
9
+ puts
10
+ end
11
+
12
+ watch("spec/.*/*_spec\.rb") do |match|
13
+ run_spec match[0]
14
+ end
15
+
16
+ watch("lib/(.*/.*)\.rb") do |match|
17
+ run_spec %{spec/#{match[1]}_spec.rb}
18
+ end
data/Gemfile ADDED
@@ -0,0 +1,33 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'dm-core', '>= 1.1.0'
4
+ gem 'dm-active_model', '>= 1.1.0'
5
+ gem 'dm-redis-adapter', '>= 0.4.0'
6
+ gem 'dm-serializer', '>= 1.1.0'
7
+ gem 'dm-timestamps', '>= 1.1.0'
8
+ gem 'dm-validations', '>= 1.1.0'
9
+ gem 'dm-types', '>= 1.1.0'
10
+ gem 'yajl-ruby', '>= 0.8.2', :require => 'yajl'
11
+ gem 'hashie', '>= 1.0.0'
12
+ gem 'rest-client', '>= 1.6.3'
13
+ gem 'sinatra', '>= 1.2.6'
14
+ gem 'sinatra-logger', '>= 0.1.1', :require => 'sinatra/logger'
15
+ gem 'activesupport', '>= 3.0.9', :require => 'active_support/all'
16
+ gem 'gli', '>= 1.3.2'
17
+
18
+ group :development, :test do
19
+ gem 'rspec', '>= 2.6.0'
20
+ gem 'rack-test', '>= 0.6.0', :require => 'rack/test'
21
+ gem 'spork', '>= 0.9.0.rc8'
22
+ gem 'watchr', '>= 0.7'
23
+ gem 'factory_girl', '>= 1.3.3'
24
+ gem 'json_spec', '>= 0.5.0'
25
+ gem "timecop", ">= 0.3.5"
26
+ end
27
+
28
+
29
+ group :doc do
30
+ gem 'yard'
31
+ # gem 'rdiscount'
32
+ gem 'yard-dm', '>= 0'
33
+ end
@@ -0,0 +1,104 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activemodel (3.0.9)
5
+ activesupport (= 3.0.9)
6
+ builder (~> 2.1.2)
7
+ i18n (~> 0.5.0)
8
+ activesupport (3.0.9)
9
+ addressable (2.2.6)
10
+ bcrypt-ruby (2.1.4)
11
+ builder (2.1.2)
12
+ diff-lcs (1.1.2)
13
+ dm-active_model (1.1.0)
14
+ activemodel (~> 3.0.4)
15
+ dm-core (~> 1.1.0)
16
+ dm-core (1.1.0)
17
+ addressable (~> 2.2.4)
18
+ dm-redis-adapter (0.4.0)
19
+ dm-core (>= 1.1.0)
20
+ dm-types (>= 1.1.0)
21
+ hiredis (~> 0.3.0)
22
+ redis (~> 2.2)
23
+ dm-serializer (1.1.0)
24
+ dm-core (~> 1.1.0)
25
+ fastercsv (~> 1.5.4)
26
+ json (~> 1.4.6)
27
+ dm-timestamps (1.1.0)
28
+ dm-core (~> 1.1.0)
29
+ dm-types (1.1.0)
30
+ bcrypt-ruby (~> 2.1.4)
31
+ dm-core (~> 1.1.0)
32
+ fastercsv (~> 1.5.4)
33
+ json (~> 1.4.6)
34
+ stringex (~> 1.2.0)
35
+ uuidtools (~> 2.1.2)
36
+ dm-validations (1.1.0)
37
+ dm-core (~> 1.1.0)
38
+ factory_girl (1.3.3)
39
+ fastercsv (1.5.4)
40
+ gli (1.3.2)
41
+ hashie (1.0.0)
42
+ hiredis (0.3.2)
43
+ i18n (0.5.0)
44
+ json (1.4.6)
45
+ json_spec (0.5.0)
46
+ json (~> 1.0)
47
+ rspec (~> 2.0)
48
+ mime-types (1.16)
49
+ rack (1.3.1)
50
+ rack-test (0.6.0)
51
+ rack (>= 1.0)
52
+ redis (2.2.1)
53
+ rest-client (1.6.3)
54
+ mime-types (>= 1.16)
55
+ rspec (2.6.0)
56
+ rspec-core (~> 2.6.0)
57
+ rspec-expectations (~> 2.6.0)
58
+ rspec-mocks (~> 2.6.0)
59
+ rspec-core (2.6.4)
60
+ rspec-expectations (2.6.0)
61
+ diff-lcs (~> 1.1.2)
62
+ rspec-mocks (2.6.0)
63
+ sinatra (1.2.6)
64
+ rack (~> 1.1)
65
+ tilt (< 2.0, >= 1.2.2)
66
+ sinatra-logger (0.1.1)
67
+ sinatra (>= 1.0)
68
+ spork (0.9.0.rc9)
69
+ stringex (1.2.2)
70
+ tilt (1.3.2)
71
+ timecop (0.3.5)
72
+ uuidtools (2.1.2)
73
+ watchr (0.7)
74
+ yajl-ruby (0.8.2)
75
+ yard (0.7.2)
76
+ yard-dm (0.1.1)
77
+
78
+ PLATFORMS
79
+ ruby
80
+
81
+ DEPENDENCIES
82
+ activesupport (>= 3.0.9)
83
+ dm-active_model (>= 1.1.0)
84
+ dm-core (>= 1.1.0)
85
+ dm-redis-adapter (>= 0.4.0)
86
+ dm-serializer (>= 1.1.0)
87
+ dm-timestamps (>= 1.1.0)
88
+ dm-types (>= 1.1.0)
89
+ dm-validations (>= 1.1.0)
90
+ factory_girl (>= 1.3.3)
91
+ gli (>= 1.3.2)
92
+ hashie (>= 1.0.0)
93
+ json_spec (>= 0.5.0)
94
+ rack-test (>= 0.6.0)
95
+ rest-client (>= 1.6.3)
96
+ rspec (>= 2.6.0)
97
+ sinatra (>= 1.2.6)
98
+ sinatra-logger (>= 0.1.1)
99
+ spork (>= 0.9.0.rc8)
100
+ timecop (>= 0.3.5)
101
+ watchr (>= 0.7)
102
+ yajl-ruby (>= 0.8.2)
103
+ yard
104
+ yard-dm
@@ -0,0 +1,26 @@
1
+
2
+
3
+
4
+ ## Features
5
+
6
+ ### Resources
7
+
8
+ One of the biggest differences
9
+ ### Snapshots
10
+
11
+ When a snapshot is recorded, it's socked away in its raw form so you can come back at a later time and review the exact state of a given resource without having to piece individual metrics together.
12
+
13
+ ### Metrics
14
+
15
+ Snapshots are also broken up and saved as individual attribute/value pairs, which enables you to track a particular attribute over a given period of time.
16
+
17
+
18
+ ## Roadmap
19
+
20
+ * Documentation. Like, seriously.
21
+ * Log to STDOUT like a proper service.
22
+ * Callbacks! Decide what happens after data gets collected.
23
+ * Taggable resources and snapshots
24
+ ## TODO
25
+
26
+ * Let config file be, er, configurable
@@ -0,0 +1,57 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require 'rake'
5
+
6
+ # require 'resque/tasks'
7
+ # require 'resque_scheduler/tasks'
8
+
9
+ require 'bundler/gem_tasks'
10
+
11
+ require 'rspec/core/rake_task'
12
+
13
+ desc "Run specs"
14
+ RSpec::Core::RakeTask.new do |task|
15
+ task.pattern = "spec/**/*_spec.rb"
16
+ end
17
+
18
+ desc "Run watchr"
19
+ task :watchr do
20
+ sh %{bundle exec watchr .watchr}
21
+ end
22
+
23
+ desc "Run spork"
24
+ task :spork do
25
+ sh %{bundle exec spork}
26
+ end
27
+
28
+
29
+ Bundler.require(:doc)
30
+ desc "Generate documentation"
31
+ YARD::Rake::YardocTask.new do |t|
32
+ t.files = [ 'lib/**/*.rb' ]
33
+ end
34
+
35
+ # namespace :resque do
36
+ # task :setup do
37
+ # require 'resque'
38
+ # require 'resque_scheduler'
39
+ # require 'resque/scheduler'
40
+
41
+ # Resque.redis = 'localhost:6379'
42
+
43
+ # Resque.schedule = YAML.load_file(
44
+ # File.join(File.expand_path(File.dirname(__FILE__)), 'config/schedule.yml')
45
+ # )
46
+
47
+ # end
48
+ # end
49
+
50
+ namespace :overwatch do
51
+ namespace :test do
52
+ task :snapshot => :environment do
53
+ a = Asset.first
54
+ a.snapshots.create(:raw_data => {:one => rand(10), :two => { :three => rand(10) }})
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'gli'
4
+
5
+ $: << File.expand_path(File.join(File.dirname(__FILE__), "../lib"))
6
+
7
+ require 'overwatch/collection'
8
+
9
+ include GLI
10
+
11
+ version Overwatch::Collection::VERSION
12
+
13
+ desc 'Start overwatch-collection server'
14
+ command :start do |c|
15
+ c.desc 'Port to which to bind'
16
+ c.arg_name 'PORT'
17
+ c.default_value '9001'
18
+ c.flag [:p, :port]
19
+
20
+ c.desc 'Host on which to run'
21
+ c.arg_name 'HOST'
22
+ c.default_value 'localhost'
23
+ c.flag [:h, :host]
24
+
25
+ c.desc 'Config file'
26
+ c.arg_name 'CONFIG'
27
+ c.default_value File.expand_path(File.join(File.dirname(__FILE__), "/../config/overwatch.yml"))
28
+ c.flag [:c, :config]
29
+
30
+ c.action do |global_options, options, args|
31
+ Overwatch.config_path = options[:c]
32
+ Overwatch::Collection::Application.run! :host => options[:h], :port => options[:p].to_i
33
+ end
34
+
35
+ end
36
+
37
+
38
+ exit run(ARGV)
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.require(:default)
4
+
5
+ $: << File.join(File.dirname(__FILE__), "lib")
6
+
7
+ require 'overwatch/collection'
8
+
9
+ run Overwatch::Collection::Application
@@ -0,0 +1,6 @@
1
+ collection:
2
+ storage:
3
+ adapter: 'redis'
4
+ db: '10'
5
+ host: 'localhost'
6
+ port: '6379'
@@ -0,0 +1,5 @@
1
+ require "./version"
2
+
3
+ module .
4
+ # Your code goes here...
5
+ end
@@ -0,0 +1,42 @@
1
+ require 'bundler'
2
+ Bundler.require(:default)
3
+
4
+ require File.expand_path(File.join(File.dirname(__FILE__), "collection/version"))
5
+
6
+ require File.expand_path(File.join(File.dirname(__FILE__), "collection/attributes"))
7
+ require File.expand_path(File.join(File.dirname(__FILE__), "collection/models/resource"))
8
+ require File.expand_path(File.join(File.dirname(__FILE__), "collection/models/snapshot"))
9
+ require File.expand_path(File.join(File.dirname(__FILE__), "collection/application"))
10
+
11
+ module Overwatch
12
+ module Collection
13
+ end
14
+ class << self
15
+ def config_path=(path)
16
+ @config_path = path
17
+ end
18
+
19
+ def config_path
20
+ @config_path ||= File.expand_path(File.dirname(__FILE__)) + "/../../config/overwatch.yml"
21
+ end
22
+
23
+ def config
24
+ @config ||= {}
25
+ @config.merge!(YAML.load_file(config_path))
26
+ end
27
+ end
28
+
29
+ end
30
+
31
+ $redis = Redis.new(
32
+ :host => Overwatch.config['collection']['storage']['host'],
33
+ :port => Overwatch.config['collection']['storage']['port'],
34
+ :db => Overwatch.config['collection']['storage']['db']
35
+ )
36
+
37
+ DataMapper.setup(:default, {
38
+ :adapter => "redis",
39
+ :host => Overwatch.config['collection']['storage']['host'],
40
+ :port => Overwatch.config['collection']['storage']['port'],
41
+ :db => Overwatch.config['collection']['storage']['db']
42
+ })
@@ -0,0 +1,46 @@
1
+ require 'sinatra/base'
2
+ require 'sinatra/logger'
3
+ # require 'sinatra/reloader' if development?
4
+
5
+ module Overwatch
6
+ module Collection
7
+ class Application < Sinatra::Base
8
+ register Sinatra::Logger
9
+ configure do
10
+ set :app_file, __FILE__
11
+ set :root, File.expand_path(File.join(File.dirname(__FILE__), "../../../"))
12
+ set :logging, true
13
+ set :run, false
14
+ set :show_exceptions, false
15
+ set :server, %w[ thin mongrel webrick ]
16
+ set :raise_errors, false
17
+ set :logger_level, :info
18
+ end
19
+
20
+ [ :development, :test].each do |env|
21
+ configure(env) do
22
+ set :raise_errors, true
23
+ set :show_exceptions, true
24
+
25
+ end
26
+ end
27
+
28
+ error DataMapper::ObjectNotFoundError do
29
+ halt 404
30
+ end
31
+
32
+ before do
33
+ content_type "application/json"
34
+ end
35
+
36
+ configure(:production) do
37
+ # TODO: allow this variable to be configured
38
+ set :redis_url, ENV['REDIS_URL'] || 'redis://localhost:6379/0'
39
+ end
40
+
41
+ end
42
+ end
43
+ end
44
+
45
+ require File.expand_path(File.join(File.dirname(__FILE__), "routes/resource"))
46
+ require File.expand_path(File.join(File.dirname(__FILE__), "routes/snapshot"))
@@ -0,0 +1,136 @@
1
+ module Overwatch
2
+ module Collection
3
+ module Attributes
4
+
5
+ def attribute_keys
6
+ $redis.smembers("overwatch::resource:#{self.id}:attribute_keys").sort
7
+ end
8
+
9
+ def average(attr, options={})
10
+ function(:average, attr, options)
11
+ end
12
+
13
+ def min(attr, options={})
14
+ function(:min, attr, options)
15
+ end
16
+
17
+ def max(attr, options={})
18
+ function(:max, attr, options)
19
+ end
20
+
21
+ def median(attr, options={})
22
+ function(:median, attr, options)
23
+ end
24
+
25
+ def first(attr, options={})
26
+ function(:first, attr, options)
27
+ end
28
+
29
+ def last(attr, options={})
30
+ function(:last, attr, options)
31
+ end
32
+
33
+ def function(func, attr, options={})
34
+ case func
35
+ when :max
36
+ values_for(attr, options)[:data].max
37
+ when :min
38
+ values_for(attr, options)[:data].min
39
+ when :average
40
+ values = values_for(attr, options)[:data]
41
+ if is_a_number?(values.first)
42
+ values.map!(&:to_f)
43
+ values.inject(:+) / values.size
44
+ else
45
+ values.first
46
+ end
47
+ when :median
48
+ values = values_for(attr, options)[:data].sort
49
+ mid = values.size / 2
50
+ values[mid]
51
+ when :first
52
+ value = $redis.zrangebyscore("resource:#{self.id}:#{attr}", options[:start_at], options[:end_at])[0]
53
+ value.split(":")[1] rescue nil
54
+ when :last
55
+ value = $redis.zrevrangebyscore("resource:#{self.id}:#{attr}", options[:end_at], options[:start_at])[0]
56
+ value.split(":")[1] rescue nil
57
+ end
58
+ end
59
+
60
+ def values_for(attr, options={})
61
+ raise ArgumentError, "attribute does not exist" unless attribute_keys.include?(attr)
62
+ start_at = options[:start_at] || "-inf" #(Time.now - 1.day).to_i.to_s
63
+ end_at = options[:end_at] || "+inf"
64
+ interval = options[:interval]
65
+ values = $redis.zrangebyscore("overwatch::resource:#{self.id}:#{attr}", start_at, end_at)
66
+ values.map! do |v|
67
+ val = v.split(":")[1]
68
+ is_a_number?(val) ? val.to_f : val
69
+ end
70
+ values.compact!
71
+ values = case interval
72
+ when 'hour'
73
+ values
74
+ when 'day'
75
+ values.each_slice(60).map { |s| s[0] }
76
+ when 'week'
77
+ values.each_slice(100).map { |s| s[0] }
78
+ when 'month'
79
+ values.each_slice(432).map { |s| s[0] }
80
+ else
81
+ values
82
+ end
83
+ { :name => attr, :data => values }#, :start_at => start_at, :end_at => end_at }
84
+ end
85
+
86
+ def is_a_number?(str)
87
+ str.to_s.match(/\A[+-]?\d+?(\.\d+)?\Z/) == nil ? false : true
88
+ end
89
+
90
+ def values_with_dates_for(attr, options={})
91
+ raise ArgumentError, "attribute does not exist" unless attribute_keys.include?(attr)
92
+ start_at = options[:start_at] || "-inf"
93
+ end_at = options[:end_at] || "+inf"
94
+ interval = options[:interval]
95
+ values = $redis.zrangebyscore("overwatch::resource:#{self.id}:#{attr}", start_at, end_at)
96
+ values.map! do |v|
97
+ val = v.split(":")
98
+ [ val[0].to_i * 1000, is_a_number?(val[1]) ? val[1].to_f : val[1] ]
99
+ end
100
+ values.compact!
101
+ values = case interval
102
+ when 'hour'
103
+ values
104
+ when 'day'
105
+ values.each_slice(60).map { |s| s[0] }
106
+ when 'week'
107
+ values.each_slice(100).map { |s| s[0] }
108
+ when 'month'
109
+ values.each_slice(432).map { |s| s[0] }
110
+ else
111
+ values
112
+ end
113
+ { :name => attr, :data => values } #, :start_at => start_at, :end_at => end_at }
114
+ end
115
+
116
+ def from_dotted_hash(source=self.attribute_keys)
117
+ source.map do |main_value|
118
+ main_value.to_s.split(".").reverse.inject(main_value) do |value, key|
119
+ {key.to_sym => value}
120
+ end
121
+ end
122
+ end
123
+
124
+
125
+ def top_level_attributes
126
+ self.attribute_keys.map do |key|
127
+ key.split(".")[0]
128
+ end.uniq
129
+ end
130
+
131
+ def sub_attributes(sub_attr)
132
+ self.attribute_keys.select {|k| k =~ /^#{sub_attr}/ }
133
+ end
134
+ end
135
+ end
136
+ end