dboard 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.
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: []