overwatch-collection 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,50 @@
1
+ module Overwatch
2
+ class Resource
3
+ include DataMapper::Resource
4
+ include Overwatch::Collection::Attributes
5
+
6
+ property :id, Serial, :index => true
7
+ property :name, String, :index => true
8
+ property :api_key, String, :index => true
9
+ property :created_at, DateTime
10
+ property :updated_at, DateTime
11
+
12
+ has n, :snapshots
13
+
14
+ before :create, :generate_api_key
15
+
16
+ validates_presence_of :name
17
+ validates_uniqueness_of :name
18
+ validates_uniqueness_of :api_key
19
+
20
+ # after :create, :schedule_no_data_check
21
+
22
+ def previous_update
23
+ snapshots[-2]
24
+ end
25
+
26
+ def last_update
27
+ snapshots[-1]
28
+ end
29
+ #
30
+ # def run_checks
31
+ # Resque.enqueue(CheckRun, self.id.to_s)
32
+ # end
33
+ #
34
+ # def schedule_no_data_check
35
+ # Resque.enqueue_in(5.minutes, NoDataCheck, self)
36
+ # end
37
+
38
+ def regenerate_api_key
39
+ generate_api_key
40
+ self.save
41
+ end
42
+
43
+ private
44
+
45
+ def generate_api_key
46
+ api_key = Digest::SHA1.hexdigest(Time.now.to_s + rand(12341234).to_s)[1..30]
47
+ self.api_key = api_key
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,88 @@
1
+ module Overwatch
2
+ class Snapshot
3
+ include DataMapper::Resource
4
+
5
+ property :id, Serial, :index => true
6
+ property :data, Json
7
+ property :created_at, DateTime
8
+ property :updated_at, DateTime
9
+ property :created_on, Date
10
+ property :updated_on, Date
11
+ property :created_at_hour, Integer
12
+ property :created_at_min, Integer
13
+ property :created_at_int, Integer
14
+
15
+ belongs_to :resource
16
+
17
+ # attr_accessor :data
18
+ attr_accessor :raw_data
19
+
20
+ after :create, :parse_data
21
+ # after :create, :run_checks
22
+ after :create, :update_attribute_keys
23
+ after :create, :generate_timestamps
24
+ # after :create, :schedule_reaper
25
+
26
+ def data
27
+ begin
28
+ Hashie::Mash.new(
29
+ Yajl.load($redis.hget("overwatch::snapshot:#{self.id}", "data"))
30
+ )
31
+ rescue
32
+ end
33
+ end
34
+
35
+ def run_checks
36
+ self.resource.run_checks
37
+ end
38
+
39
+ def schedule_reaper
40
+ if self.created_at.min % 5 != 0
41
+ Resque.enqueue_in(60.minutes, SnapshotReaper, self)
42
+ # Resque.enqueue_in(60.minutes, AttributeReaper, "resource:#{self.resource_id}:#{key}")
43
+ elsif self.created_at.hour != 0 && self.created_at.min != 0
44
+ Resque.enqueue_in(1.day, SnapshotReaper, self)
45
+ else
46
+ Resque.enqueue_in(30.days, SnapshotReaper, self)
47
+ end
48
+ end
49
+
50
+ def to_dotted_hash(source=self.data,target = {}, namespace = nil)
51
+ prefix = "#{namespace}." if namespace
52
+ case source
53
+ when Hash
54
+ source.each do |key, value|
55
+ to_dotted_hash(value, target, "#{prefix}#{key}")
56
+ end
57
+ else
58
+ target[namespace] = source
59
+ end
60
+ target
61
+ end
62
+
63
+ private
64
+
65
+ def parse_data
66
+ self.to_dotted_hash.each_pair do |key, value|
67
+ timestamp = self.created_at.to_i * 1000
68
+ $redis.zadd "overwatch::resource:#{resource.id}:#{key}", timestamp, "#{timestamp}:#{value}"
69
+ end
70
+ end
71
+
72
+ def update_attribute_keys
73
+ self.to_dotted_hash.keys.each do |key|
74
+ $redis.sadd "overwatch::resource:#{self.resource.id}:attribute_keys", key
75
+ end
76
+ end
77
+
78
+ def generate_timestamps
79
+ self.created_at_int = self.created_at.to_i
80
+ self.save
81
+ end
82
+
83
+ end
84
+
85
+ class AttributeError < Exception; end
86
+ end
87
+
88
+
@@ -0,0 +1,107 @@
1
+ module Overwatch
2
+ module Collection
3
+ class Application < Sinatra::Base
4
+
5
+ get '/resources/?' do
6
+ resources = Resource.all
7
+ if resources.size < 1
8
+ [].to_json
9
+ else
10
+ resources.to_json
11
+ end
12
+ end
13
+
14
+ get '/resources/:id/?' do |id|
15
+ resource = Resource.get(id)
16
+ if resource
17
+ status 200
18
+ resource.to_json
19
+ else
20
+ halt 404
21
+ end
22
+
23
+ end
24
+
25
+ post '/resources/?' do
26
+ data = Yajl.load(request.body.read)
27
+ resource = Resource.new(:name => data['name'])
28
+ if resource.save
29
+ status 201
30
+ resource.to_json
31
+ else
32
+ status 422
33
+ resource.errors.to_json
34
+ end
35
+ end
36
+
37
+ put '/resources/:id/?' do |id|
38
+ data = Yajl.load(request.body.read)
39
+ resource = Resource.get(id)
40
+ if resource
41
+ if resource.update(data)
42
+ status 200
43
+ resource.to_json
44
+ else
45
+ status 409
46
+ resource.errors.to_json
47
+ end
48
+ else
49
+ halt 404
50
+ end
51
+
52
+ end
53
+
54
+ delete '/resources/:id/?' do |id|
55
+ resource = Resource.get(id)
56
+ if resource
57
+ if resource.destroy
58
+ status 200
59
+ else
60
+ status 409
61
+ resource.errors.to_json
62
+ end
63
+ else
64
+ halt 404
65
+ end
66
+
67
+ end
68
+
69
+ get '/resources/:id/attributes/?' do |id|
70
+ resource = Resource.get(id)
71
+ attributes = resource.attribute_keys
72
+ if attributes.length < 1
73
+ [].to_json
74
+ else
75
+ attributes.sort.to_json
76
+ end
77
+ end
78
+
79
+ get '/resources/:id/attributes/:attribute/?' do |id, attribute|
80
+ resource = Resource.get(id)
81
+
82
+ options = {
83
+ :start_at => (params[:start_at].to_i || nil),
84
+ :end_at => (params[:end_at].to_i || nil),
85
+ :interval => (params[:interval].to_s || 'hour')
86
+ }
87
+
88
+ values = resource.values_with_dates_for(attribute, options)
89
+ values.to_json
90
+ end
91
+
92
+ get '/resources/:id/attributes/:attribute/:function/?' do |id, attribute, function|
93
+ resource = Resource.get(id)
94
+
95
+ options = {
96
+ :start_at => (params[:start_at].to_i || nil),
97
+ :end_at => (params[:end_at].to_i || nil),
98
+ :interval => (params[:interval].to_s || 'hour')
99
+ }
100
+
101
+ attribute = resource.function(params[:function].to_sym, attribute, options)
102
+ attribute.to_json
103
+ end
104
+
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,66 @@
1
+ module Overwatch
2
+ module Collection
3
+ class Application < Sinatra::Base
4
+
5
+ get '/resources/:resource_id/snapshots/?' do |resource_id|
6
+ resource = Resource.get(resource_id)
7
+ snapshots = resource.snapshots
8
+
9
+ if snapshots.size < 1
10
+ [].to_json
11
+ else
12
+ snapshots.to_json
13
+ end
14
+ end
15
+
16
+ get '/resources/:resource_id/snapshots/:id/?' do |resource_id, id|
17
+ resource = Resource.get(resource_id)
18
+ snapshot = resource.snapshots.first(:id => id)
19
+
20
+ if resource
21
+ if snapshot
22
+ status 200
23
+ snapshot.to_json
24
+ else
25
+ halt 404
26
+ end
27
+ end
28
+ end
29
+
30
+ post '/snapshots/?' do
31
+ resource = Resource.first(:api_key => params['key'])
32
+ data = Yajl.load(request.body.read)
33
+ snapshot = resource.snapshots.create(:data => data)
34
+
35
+ if resource
36
+ if snapshot
37
+ status 201
38
+ snapshot.to_json
39
+ else
40
+ status 409
41
+ snapshot.errors.to_json
42
+ end
43
+ else
44
+ halt 404
45
+ end
46
+ end
47
+
48
+ get '/resources/:resource_id/snapshots/:id/data/?' do |resource_id, id|
49
+ resource = Resource.get(resource_id)
50
+ snapshot = resource.snapshots.first(:id => id)
51
+
52
+ if resource
53
+ if snapshot
54
+ status 200
55
+ snapshot.data.to_json
56
+ else
57
+ halt 404
58
+ end
59
+ else
60
+ halt 404
61
+ end
62
+
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,5 @@
1
+ module Overwatch
2
+ module Collection
3
+ VERSION = "0.1.1"
4
+ end
5
+ end
File without changes
@@ -0,0 +1,43 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ require 'overwatch/collection/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'overwatch-collection'
8
+ s.version = Overwatch::Collection::VERSION
9
+ s.platform = Gem::Platform::RUBY
10
+ s.authors = [ "Dan Ryan" ]
11
+ s.email = [ "dan@appliedawesome.com" ]
12
+ s.homepage = "https://github.com/danryan/overwatch-collection"
13
+ s.summary = "Overwatch statistical time series collection app"
14
+ s.description = "overwatch-collection is a Redis-backed statistical time series collection application designed to be fast, extensible, and easy to use."
15
+
16
+ s.rubyforge_project = "overwatch-collection"
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {spec}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+
22
+ s.add_dependency 'dm-core', '>= 1.1.0'
23
+ s.add_dependency 'dm-active_model', '>= 1.1.0'
24
+ s.add_dependency 'dm-redis-adapter', '>= 0.4.0'
25
+ s.add_dependency 'dm-serializer', '>= 1.1.0'
26
+ s.add_dependency 'dm-timestamps', '>= 1.1.0'
27
+ s.add_dependency 'dm-validations', '>= 1.1.0'
28
+ s.add_dependency 'dm-types', '>= 1.1.0'
29
+ s.add_dependency 'yajl-ruby', '>= 0.8.2'
30
+ s.add_dependency 'hashie', '>= 1.0.0'
31
+ s.add_dependency 'rest-client', '>= 1.6.3'
32
+ s.add_dependency 'sinatra', '>= 1.2.6'
33
+ s.add_dependency 'sinatra-logger', '>= 0.1.1'
34
+ s.add_dependency 'activesupport', '>= 3.0.9'
35
+
36
+ s.add_development_dependency 'rspec', '>= 2.6.0'
37
+ s.add_development_dependency 'rack-test', '>= 0.6.0'
38
+ s.add_development_dependency 'spork', '>= 0.9.0.rc8'
39
+ s.add_development_dependency 'watchr', '>= 0.7'
40
+ s.add_development_dependency 'factory_girl', '>= 1.3.3'
41
+ s.add_development_dependency 'json_spec', '>= 0.5.0'
42
+ s.add_development_dependency 'timecop', '>= 0.3.5'
43
+ end
@@ -0,0 +1,64 @@
1
+ require 'spec_helper'
2
+
3
+ module Overwatch
4
+ module Collection
5
+ describe Attributes do
6
+ let(:resource) { Overwatch::Resource.create(:name => 'foo') }
7
+ let(:start_at) { (Time.now - 1.hour).to_i * 1000 }
8
+ let(:end_at) { Time.now.to_i * 1000 }
9
+
10
+ before do
11
+ resource.snapshots.create(:data => snapshot_data)
12
+ time_travel!
13
+ resource.snapshots.create(:data => snapshot_data)
14
+ end
15
+
16
+ describe "#attribute_keys" do
17
+ it "should return all available attributes" do
18
+ resource.attribute_keys.should have(2).items
19
+ resource.attribute_keys.should include("load_average.one_minute")
20
+ end
21
+
22
+ end
23
+
24
+ describe "#values_for" do
25
+ before do
26
+ time_travel!(Time.now + 1.hour)
27
+ resource.snapshots.create(:data => snapshot_data)
28
+ end
29
+
30
+ it "should return all values by default" do
31
+ values = resource.values_for("load_average.one_minute")
32
+ values[:data].should have(3).items
33
+ end
34
+
35
+ it "should return values within a given date range" do
36
+ values = resource.values_for("load_average.one_minute",
37
+ :start_at => start_at, :end_at => end_at
38
+ )
39
+ values[:data].should have(2).items
40
+ end
41
+ end
42
+
43
+ describe "#values_with_dates_for" do
44
+ before do
45
+ time_travel!(Time.now + 1.hour)
46
+ resource.snapshots.create(:data => snapshot_data)
47
+ end
48
+
49
+ it "should return all values by default" do
50
+ values = resource.values_with_dates_for("load_average.one_minute")
51
+ values[:data].should have(3).items
52
+ end
53
+
54
+ it "should return values within a given date range" do
55
+ values = resource.values_for("load_average.one_minute",
56
+ :start_at => start_at, :end_at => end_at
57
+ )
58
+ values[:data].should have(2).items
59
+ end
60
+ end
61
+
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ module Overwatch
4
+ describe Resource do
5
+
6
+ describe "new record" do
7
+
8
+ it "generates an api key" do
9
+ resource = Overwatch::Resource.create(:name => "foo")
10
+ resource.api_key.should_not be_nil
11
+ end
12
+
13
+ it "creates a new resource" do
14
+ resource = Overwatch::Resource.create(:name => "foo")
15
+ resource.should be_valid
16
+ resource.name.should == "foo"
17
+ end
18
+
19
+ it "disallows duplicate names" do
20
+ resource = Overwatch::Resource.create(:name => "foo")
21
+ resource2 = Overwatch::Resource.create(:name => "foo")
22
+ resource2.should_not be_valid
23
+ resource2.errors[:name].should == ["Name is already taken"]
24
+ end
25
+
26
+ it "requires a name" do
27
+ resource = Overwatch::Resource.create
28
+ resource.should_not be_valid
29
+ resource.errors[:name].should == ["Name must not be blank"]
30
+ end
31
+
32
+ end
33
+
34
+ describe '#generate_api_key' do
35
+ it "generates an api key" do
36
+ resource = Overwatch::Resource.create(:name => "foo")
37
+ resource.api_key.should_not be_nil
38
+ end
39
+
40
+ it 'disallows duplicate api keys' do
41
+ resource = Overwatch::Resource.create(:name => "foo")
42
+ resource2 = Overwatch::Resource.create(:name => "bar")
43
+ resource.api_key = '1234asdf' && resource2.api_key = '1234asdf'
44
+ resource.save && resource2.save
45
+ resource2.should_not be_valid
46
+ resource2.errors[:api_key].should == ["Api key is already taken"]
47
+ end
48
+ end
49
+
50
+ describe '#regenerate_api_key' do
51
+ it "regenerates an api key" do
52
+ resource = Overwatch::Resource.create(:name => 'foo')
53
+ old_api_key = resource.api_key
54
+ resource.regenerate_api_key
55
+ resource.api_key.should_not == old_api_key
56
+ end
57
+
58
+ end
59
+ end
60
+ end