dboard 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ todo.txt
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm ruby-1.9.3-preview1@dboard --create
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,8 @@
1
+ guard 'rspec', :version => 2, :all_on_start => false, :all_after_pass => true, :bundler => false, :keep_failed => false do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
4
+ end
5
+
6
+ guard 'bundler' do
7
+ watch('Gemfile')
8
+ end
data/LICENCE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2011 Joakim Kolsjö
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'bundler'
2
+ require 'rspec/core/rake_task'
3
+
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ desc "Run specs"
7
+ RSpec::Core::RakeTask.new do |t|
8
+ end
9
+
10
+ task :default => :spec
data/dboard.gemspec ADDED
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "dboard"
7
+ s.version = Dboard::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Joakim Kolsjö"]
10
+ s.email = ["joakim.kolsjo@gmail.com"]
11
+ s.homepage = ""
12
+ s.summary = %q{Dashboard framework}
13
+ s.description = %q{Dashboard framework}
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {spec}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+ s.add_dependency "httparty", "~> 0.7.8"
21
+ s.add_dependency "rake", "~> 0.9.2"
22
+ s.add_dependency "json", "~> 1.4.6"
23
+ s.add_dependency "dalli", "~> 1.0.5"
24
+ s.add_dependency "sinatra", "~> 1.2"
25
+ s.add_development_dependency "rspec", "2.6.0"
26
+ s.add_development_dependency "guard", "0.5.1"
27
+ s.add_development_dependency "guard-rspec", "0.4.3"
28
+ s.add_development_dependency "guard-bundler", "0.1.3"
29
+ s.add_development_dependency "rb-fsevent", "0.4.1"
30
+ end
data/lib/api.rb ADDED
@@ -0,0 +1,34 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'cache'))
2
+ require File.expand_path(File.join(File.dirname(__FILE__), 'collector.rb'))
3
+ require 'digest/md5'
4
+ require 'json'
5
+ require 'httparty'
6
+
7
+ module Dboard
8
+ class Api
9
+ MAX_CACHE_TIME = 3600 # seconds
10
+ @@version = nil
11
+
12
+ class Client
13
+ include HTTParty
14
+ end
15
+
16
+ def self.get(params)
17
+ types = {}
18
+ params[:types].split(',').each do |type|
19
+ raw_data = CACHE.get("dashboard::source::#{type}")
20
+ data = raw_data ? JSON.parse(raw_data) : {}
21
+ types.merge!(type => { :data => data, :checksum => Digest::MD5.hexdigest(data.inspect) })
22
+ end
23
+ { :version => (@@version || ENV["COMMIT_HASH"] || "unversioned"), :sources => types }.to_json
24
+ end
25
+
26
+ def self.version=(version)
27
+ @@version = version
28
+ end
29
+
30
+ def self.update(params)
31
+ CACHE.set "dashboard::source::#{params[:type]}", params[:data], MAX_CACHE_TIME
32
+ end
33
+ end
34
+ end
data/lib/cache.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'dalli'
2
+
3
+ module Dboard
4
+ if Config.config[:memcache]
5
+ CACHE = Dalli::Client.new(Config.config[:memcache])
6
+ else
7
+ CACHE = Dalli::Client.new("127.0.0.1:11211")
8
+ end
9
+ end
10
+
data/lib/collector.rb ADDED
@@ -0,0 +1,56 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'publisher'))
2
+ require 'singleton'
3
+
4
+ module Dboard
5
+ class Collector
6
+ include Singleton
7
+
8
+ attr_reader :sources
9
+
10
+ def self.register_source(key, instance)
11
+ Collector.instance.register_source(key, instance)
12
+ end
13
+
14
+ def self.start
15
+ instance.start
16
+ end
17
+
18
+ def initialize
19
+ @sources = {}
20
+ end
21
+
22
+ def start
23
+ @sources.each do |source, instance|
24
+ Thread.new do
25
+ loop do
26
+ time = Time.now
27
+ puts "#{source} updating..."
28
+ update_source(source, instance)
29
+ elapsed_time = Time.now - time
30
+ time_until_next_update = instance.update_interval - elapsed_time
31
+ time_until_next_update = 0 if time_until_next_update < 0
32
+ puts "#{source} done in #{elapsed_time} seconds, will update again in #{time_until_next_update} seconds (interval: #{instance.update_interval})."
33
+ sleep time_until_next_update
34
+ end
35
+ end
36
+ end
37
+ loop { sleep 1 }
38
+ end
39
+
40
+ def update_source(source, instance)
41
+ data = instance.fetch
42
+ publish_data(source, data)
43
+ rescue Exception => ex
44
+ puts "Failed to update #{source}: #{ex.message}"
45
+ puts ex.backtrace
46
+ end
47
+
48
+ def register_source(key, instance)
49
+ @sources.merge!({ key => instance })
50
+ end
51
+
52
+ def publish_data(source, data)
53
+ Publisher.publish(source, data)
54
+ end
55
+ end
56
+ end
data/lib/config.rb ADDED
@@ -0,0 +1,24 @@
1
+ module Dboard
2
+ class Config
3
+ @@config ||= {}
4
+
5
+ def self.basic_auth(opts = {})
6
+ @@config[:basic_auth] = opts
7
+ Dboard::Api::Client.basic_auth(opts[:user], opts[:password])
8
+ end
9
+
10
+ def self.memcache(opts = {})
11
+ @@config[:memcache] = opts
12
+ end
13
+
14
+ def self.api(opts = {})
15
+ @@config[:api] = opts
16
+ Dboard::Api::Client.base_uri opts[:uri]
17
+ end
18
+
19
+ def self.config
20
+ @@config
21
+ end
22
+ end
23
+ end
24
+
data/lib/dboard.rb ADDED
@@ -0,0 +1,3 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'config.rb'))
2
+ require File.expand_path(File.join(File.dirname(__FILE__), 'api.rb'))
3
+ require File.expand_path(File.join(File.dirname(__FILE__), 'collector.rb'))
data/lib/publisher.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'json'
2
+
3
+ module Dboard
4
+ class Publisher
5
+ def self.publish(source, data)
6
+ Api::Client.post("/sources/#{source}", :body => { :data => data.to_json })
7
+ rescue SocketError => ex
8
+ puts "SocketError: #{ex.message}"
9
+ end
10
+ end
11
+ end
data/lib/version.rb ADDED
@@ -0,0 +1,3 @@
1
+ module Dboard
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,37 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ describe Dboard::Collector, "register_source" do
4
+
5
+ before do
6
+ Dboard::Collector.instance.sources.clear
7
+ end
8
+
9
+ it "can register a source" do
10
+ new_relic = mock
11
+ new_relic.stub!(:update_interval).and_return(5)
12
+ Dboard::Collector.instance.register_source :new_relic, new_relic
13
+ Dboard::Collector.instance.sources.should == { :new_relic => new_relic }
14
+ end
15
+
16
+ end
17
+
18
+ describe Dboard::Collector, "update_source" do
19
+ before do
20
+ Dboard::Collector.instance.sources.clear
21
+ end
22
+
23
+ it "should collect and publish data from sources" do
24
+ new_relic = mock
25
+ new_relic.stub!(:fetch).and_return({ :db => "100%" })
26
+ Dboard::Publisher.should_receive(:publish).with(:new_relic, { :db => "100%" })
27
+ Dboard::Collector.instance.update_source(:new_relic, new_relic)
28
+ end
29
+
30
+ it "should print out debugging info" do
31
+ new_relic = mock
32
+ new_relic.stub!(:fetch).and_raise(Exception.new("some error"))
33
+ Dboard::Collector.instance.should_receive(:puts).twice
34
+ Dboard::Collector.instance.update_source(:new_relic, new_relic)
35
+ end
36
+
37
+ end
@@ -0,0 +1,54 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ # Test app
4
+ require 'sinatra'
5
+
6
+ get "/sources" do
7
+ Dboard::Api.get(params)
8
+ end
9
+
10
+ post "/sources/:type" do
11
+ Dboard::Api.update(params)
12
+ end
13
+
14
+ describe "Dashboard" do
15
+
16
+ def app
17
+ Sinatra::Application
18
+ end
19
+
20
+ def start_app
21
+ app.port = 20843
22
+ app.environment = 'test'
23
+ @app_thread = Thread.new { app.run! }
24
+ sleep 1
25
+ end
26
+
27
+ def stop_app
28
+ @app_thread && @app_thread.kill
29
+ end
30
+
31
+ before do
32
+ ENV['API_URL'] = "http://localhost:20843"
33
+ ENV['API_USER'] = 'test'
34
+ ENV['API_PASSWORD'] = 'test'
35
+ @new_relic = mock
36
+ @new_relic.stub!(:fetch).and_return({ :db => "33.3%", :memory => "33333 MB" })
37
+ Dboard::CACHE.delete "dashboard::source::new_relic"
38
+ end
39
+
40
+ it "should collect stats and post them to the server" do
41
+ start_app
42
+ body = Dboard::Api::Client.get("/sources?types=new_relic")
43
+ JSON.parse(body)["sources"]["new_relic"]["data"].should == {}
44
+ Dboard::Collector.instance.update_source(:new_relic, @new_relic)
45
+ body = Dboard::Api::Client.get("/sources?types=new_relic")
46
+ JSON.parse(body)["sources"]["new_relic"]["data"].should == { "db" => "33.3%", "memory" => "33333 MB" }
47
+ end
48
+
49
+ after do
50
+ stop_app
51
+ end
52
+
53
+ end
54
+
@@ -0,0 +1,16 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ describe "Publisher", "publish" do
4
+
5
+ it "should send data to the dashboard server" do
6
+ Dboard::Api::Client.should_receive(:post).with("/sources/new_relic", :body => { :data => { :db => "80%" }.to_json })
7
+ Dboard::Publisher.publish(:new_relic, { :db => "80%" })
8
+ end
9
+
10
+ it "should handle and log socket errors" do
11
+ Dboard::Api::Client.should_receive(:post).and_raise(SocketError.new("failed to connect"))
12
+ Dboard::Publisher.should_receive(:puts).with("SocketError: failed to connect")
13
+ Dboard::Publisher.publish(:new_relic, {})
14
+ end
15
+
16
+ end
@@ -0,0 +1,11 @@
1
+ require "rspec"
2
+
3
+ ENV["RACK_ENV"] ||= 'test'
4
+ ENV['API_URL'] = "http://localhost:20843"
5
+ ENV['API_USER'] = 'test'
6
+ ENV['API_PASSWORD'] = 'test'
7
+
8
+ require File.expand_path(File.join(File.dirname(__FILE__), '../lib/dboard'))
9
+
10
+ Dboard::Api::Client.basic_auth(ENV['API_USER'], ENV['API_PASSWORD'])
11
+ Dboard::Api::Client.base_uri(ENV['API_URL'])
metadata ADDED
@@ -0,0 +1,174 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dboard
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Joakim Kolsjö
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-09-05 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: httparty
16
+ requirement: &2156106140 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.7.8
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2156106140
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ requirement: &2156105640 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 0.9.2
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *2156105640
36
+ - !ruby/object:Gem::Dependency
37
+ name: json
38
+ requirement: &2156105180 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 1.4.6
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *2156105180
47
+ - !ruby/object:Gem::Dependency
48
+ name: dalli
49
+ requirement: &2156104700 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 1.0.5
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *2156104700
58
+ - !ruby/object:Gem::Dependency
59
+ name: sinatra
60
+ requirement: &2156104160 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: '1.2'
66
+ type: :runtime
67
+ prerelease: false
68
+ version_requirements: *2156104160
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: &2156103640 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - =
75
+ - !ruby/object:Gem::Version
76
+ version: 2.6.0
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *2156103640
80
+ - !ruby/object:Gem::Dependency
81
+ name: guard
82
+ requirement: &2156103160 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - =
86
+ - !ruby/object:Gem::Version
87
+ version: 0.5.1
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *2156103160
91
+ - !ruby/object:Gem::Dependency
92
+ name: guard-rspec
93
+ requirement: &2156102620 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - =
97
+ - !ruby/object:Gem::Version
98
+ version: 0.4.3
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: *2156102620
102
+ - !ruby/object:Gem::Dependency
103
+ name: guard-bundler
104
+ requirement: &2156102100 !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - =
108
+ - !ruby/object:Gem::Version
109
+ version: 0.1.3
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: *2156102100
113
+ - !ruby/object:Gem::Dependency
114
+ name: rb-fsevent
115
+ requirement: &2156101580 !ruby/object:Gem::Requirement
116
+ none: false
117
+ requirements:
118
+ - - =
119
+ - !ruby/object:Gem::Version
120
+ version: 0.4.1
121
+ type: :development
122
+ prerelease: false
123
+ version_requirements: *2156101580
124
+ description: Dashboard framework
125
+ email:
126
+ - joakim.kolsjo@gmail.com
127
+ executables: []
128
+ extensions: []
129
+ extra_rdoc_files: []
130
+ files:
131
+ - .gitignore
132
+ - .rvmrc
133
+ - Gemfile
134
+ - Guardfile
135
+ - LICENCE
136
+ - README
137
+ - Rakefile
138
+ - dboard.gemspec
139
+ - lib/api.rb
140
+ - lib/cache.rb
141
+ - lib/collector.rb
142
+ - lib/config.rb
143
+ - lib/dboard.rb
144
+ - lib/publisher.rb
145
+ - lib/version.rb
146
+ - spec/collector_spec.rb
147
+ - spec/integration_spec.rb
148
+ - spec/publisher_spec.rb
149
+ - spec/spec_helper.rb
150
+ homepage: ''
151
+ licenses: []
152
+ post_install_message:
153
+ rdoc_options: []
154
+ require_paths:
155
+ - lib
156
+ required_ruby_version: !ruby/object:Gem::Requirement
157
+ none: false
158
+ requirements:
159
+ - - ! '>='
160
+ - !ruby/object:Gem::Version
161
+ version: '0'
162
+ required_rubygems_version: !ruby/object:Gem::Requirement
163
+ none: false
164
+ requirements:
165
+ - - ! '>='
166
+ - !ruby/object:Gem::Version
167
+ version: '0'
168
+ requirements: []
169
+ rubyforge_project:
170
+ rubygems_version: 1.8.5
171
+ signing_key:
172
+ specification_version: 3
173
+ summary: Dashboard framework
174
+ test_files: []