riemann-dash 0.2.8 → 0.2.9

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 333646d7d876e408d47965ae5a0f27234e6b8821
4
- data.tar.gz: 286df1ba3416046249397560a6572cff9b87974d
3
+ metadata.gz: 1dcb96d79006cd63317f46f52072596a7e8d8191
4
+ data.tar.gz: 57dbb5238edab2d60bdb09613be4ccb58cbd977f
5
5
  SHA512:
6
- metadata.gz: 71467857647cd99ea04b87956f644897b61405e29e0b950a9dc67501e31128cc249113a27842544c61b385cd32f710da8b8ecf59f77815e91de775fd623a8af6
7
- data.tar.gz: c903a2a28580057a35bb83c7dc5e2126012704b5995c7a19b2a10db0ba521634bb5198e6ce754161851668d26656cb84c178f7b4ec3f07cd01b77310f0c6135c
6
+ metadata.gz: d6cf10ac7232174d526ae3666265e374246e89f7bb8a8e2a0a39b1199ef7c9e4f0db1f2b02ec6199c7e283a19902cda08639d81909f62cba726f07c53c90622e
7
+ data.tar.gz: 0aab886e0f0531fe1e5c075b2a54664b8e00da4d3f78028b21276c8b903c9c0cc34906fed09988d3180779e00866066917609946a9e43c274db388c5224edebe
data/Gemfile.lock CHANGED
@@ -1,8 +1,9 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- riemann-dash (0.2.5)
4
+ riemann-dash (0.2.8)
5
5
  erubis (>= 2.7.0)
6
+ fog
6
7
  multi_json (= 1.3.6)
7
8
  riemann-client (>= 0.0.7)
8
9
  sass (>= 3.1.14)
@@ -12,34 +13,55 @@ PATH
12
13
  GEM
13
14
  remote: https://rubygems.org/
14
15
  specs:
15
- beefcake (0.3.7)
16
+ beefcake (0.5.0)
17
+ builder (3.2.2)
16
18
  coderay (1.0.9)
17
19
  daemons (1.1.9)
18
20
  erubis (2.7.0)
19
21
  eventmachine (1.0.3)
22
+ excon (0.31.0)
23
+ fog (1.19.0)
24
+ builder
25
+ excon (~> 0.31.0)
26
+ formatador (~> 0.2.0)
27
+ mime-types
28
+ multi_json (~> 1.0)
29
+ net-scp (~> 1.1)
30
+ net-ssh (>= 2.1.3)
31
+ nokogiri (~> 1.5)
32
+ ruby-hmac
33
+ formatador (0.2.4)
20
34
  method_source (0.8.1)
35
+ mime-types (2.0)
36
+ mini_portile (0.5.2)
21
37
  mtrc (0.0.4)
22
38
  multi_json (1.3.6)
39
+ net-scp (1.1.2)
40
+ net-ssh (>= 2.6.5)
41
+ net-ssh (2.7.0)
42
+ nokogiri (1.6.1)
43
+ mini_portile (~> 0.5.0)
23
44
  pry (0.9.12)
24
45
  coderay (~> 1.0.5)
25
46
  method_source (~> 0.8)
26
47
  slop (~> 3.4)
27
48
  rack (1.5.2)
28
- rack-protection (1.5.0)
49
+ rack-protection (1.5.2)
29
50
  rack
30
51
  riemann-client (0.2.2)
31
52
  beefcake (>= 0.3.5)
32
53
  mtrc (>= 0.0.4)
33
54
  trollop (>= 1.16.2)
34
- sass (3.2.7)
35
- sinatra (1.3.6)
55
+ ruby-hmac (0.4.0)
56
+ sass (3.3.2)
57
+ sinatra (1.4.4)
36
58
  rack (~> 1.4)
37
- rack-protection (~> 1.3)
38
- tilt (~> 1.3, >= 1.3.3)
59
+ rack-protection (~> 1.4)
60
+ tilt (~> 1.3, >= 1.3.4)
39
61
  slop (3.4.3)
40
- thin (1.5.1)
62
+ thin (1.6.2)
41
63
  daemons (>= 1.0.9)
42
- eventmachine (>= 0.12.6)
64
+ eventmachine (>= 1.0.0)
43
65
  rack (>= 1.0.0)
44
66
  tilt (1.4.1)
45
67
  trollop (2.0)
data/README.markdown CHANGED
@@ -11,7 +11,10 @@ Get started
11
11
  $ riemann-dash
12
12
  ```
13
13
 
14
- Then open http://localhost:4567 in a browser. Riemann-dash will connect to the local host (relative to your browser) by default, and show you a small manual.
14
+ Then open http://localhost:4567 in a browser. Riemann-dash will connect to the
15
+ local host (relative to your browser) by default, and show you a small manual.
16
+ Change the IP address in the top right field to point to your Riemann server's
17
+ websocket port.
15
18
 
16
19
  Configuring
17
20
  ===========
@@ -19,7 +22,8 @@ Configuring
19
22
  Riemann-dash takes an optional config file, which you can specify as the first
20
23
  command-line argument. If none is given, it looks for a file in the local
21
24
  directory: config.rb. That file can override any configuration options on the
22
- Dash class, and hence, all Sinatra configuration.
25
+ Dash class, and hence, all Sinatra configuration. You'll find a few usage
26
+ examples in "example/config.rb".
23
27
 
24
28
  ``` ruby
25
29
  set :port, 6000 # HTTP server on port 6000
@@ -27,7 +31,19 @@ set :bind, "1.2.3.4" # Bind to a different interface
27
31
  config[:ws_config] = 'custom/config.json' # Specify custom workspace config
28
32
  ```
29
33
 
34
+ Putting in production
35
+ =====================
30
36
 
37
+ If you expect more than a couple of simultaneous users, you should consider
38
+ running Riemann-dash in a proper application server. The easiest way is to
39
+ install thin or puma. Riemann-dash will automatically use one of them if they
40
+ are present. You'll need the C/C++ compiler, as well as the ruby and openssl
41
+ libraries and headers installed.
42
+
43
+ ``` bash
44
+ $ gem install riemann-dash thin
45
+ $ riemann-dash
46
+ ```
31
47
 
32
48
  Development
33
49
  ===========
data/example/config.rb CHANGED
@@ -15,3 +15,8 @@ config.store[:ws_config] = "#{riemann_base}/config/config.json"
15
15
 
16
16
  # Serve static files from this directory
17
17
  config.store[:public] = "#{riemann_src}/public"
18
+
19
+ # Save workspace configuration to Amazon S3 (you'll need to have the "fog"
20
+ # gem installed)
21
+ # config.store[:ws_config] = 's3://my-bucket/config.json'
22
+ # config.store[:s3_config] = {:aws_access_key_id => "123ABC", :aws_secret_access_key => "789XYZ"}
data/lib/riemann/dash.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'rubygems'
2
2
  require 'sinatra/base'
3
3
  require 'riemann/dash/version'
4
+ require 'riemann/dash/browser_config'
4
5
  require 'riemann/dash/config'
5
6
  require 'riemann/dash/app'
@@ -26,6 +26,7 @@ module Riemann
26
26
  config.load_controllers
27
27
  config.setup_views
28
28
  config.setup_public_dir
29
+ config.setup_storage_backend
29
30
  end
30
31
  end
31
32
  end
@@ -0,0 +1,72 @@
1
+ module Riemann::Dash::BrowserConfig
2
+ require 'multi_json'
3
+ require 'fileutils'
4
+ require 'pp'
5
+
6
+ def self.backend
7
+ @backend
8
+ end
9
+
10
+ def self.backend=(backend)
11
+ @backend = backend
12
+ end
13
+
14
+ # TODO: this is gonna take significant restructuring of the dashboard itself,
15
+ # but we should move to http://arxiv.org/abs/1201.1784 or equivalent CRDTs.
16
+
17
+ # Given a function to extract a key from an element, and a list of elements,
18
+ # returns a map of keys to elements. Keys are assumed unique.
19
+ def self.index_by(keyfn, list)
20
+ list.reduce({}) do |index, element|
21
+ index[keyfn.call(element)] = element
22
+ index
23
+ end
24
+ end
25
+
26
+ # Merges two lists, given a key function which determines equivalent
27
+ # elements, and a merge function to combine equivalent elements.
28
+ def self.merge_lists(keyfn, mergefn, as, bs)
29
+ asi = index_by keyfn, as
30
+ bsi = index_by keyfn, bs
31
+ ids = (as + bs).map(&keyfn).uniq.map do |key|
32
+ mergefn.call asi[key], bsi[key]
33
+ end
34
+ end
35
+
36
+ # Merge two workspaces together
37
+ def self.merge_workspace(a, b)
38
+ # TODO: workspace versions
39
+ return a unless b
40
+ return b unless a
41
+ if (a['view']['version'] || 0) < (b['view']['version'] || 0)
42
+ b
43
+ else
44
+ a
45
+ end
46
+ end
47
+
48
+ # Merge a list of workspaces together
49
+ def self.merge_workspaces(as, bs)
50
+ return as unless bs
51
+ return bs unless as
52
+ merge_lists(lambda { |x| x['name'] },
53
+ method(:merge_workspace),
54
+ as,
55
+ bs)
56
+ end
57
+
58
+ # Merge two configs together
59
+ def self.merge_configs(a, b)
60
+ a.merge 'server' => (a['server'] or b['server']),
61
+ 'server_type' => (a['server_type'] or b['server_type']),
62
+ 'workspaces' => merge_workspaces(a['workspaces'], b['workspaces'])
63
+ end
64
+
65
+ def self.read
66
+ backend.read
67
+ end
68
+
69
+ def self.update(update)
70
+ backend.update(update)
71
+ end
72
+ end
@@ -0,0 +1,39 @@
1
+ class Riemann::Dash::BrowserConfig::File
2
+ require 'multi_json'
3
+ require 'fileutils'
4
+
5
+ def initialize(path)
6
+ @path = path
7
+ end
8
+
9
+ def read
10
+ if ::File.exists? @path
11
+ ::File.open(@path, 'r') do |f|
12
+ f.flock ::File::LOCK_SH
13
+ f.read
14
+ end
15
+ else
16
+ MultiJson.encode({})
17
+ end
18
+ end
19
+
20
+ def update(update)
21
+ update = MultiJson.decode update
22
+
23
+ # Read old config
24
+ old = MultiJson.decode read
25
+
26
+ new = Riemann::Dash::BrowserConfig.merge_configs update, old
27
+
28
+ # Save new config
29
+ FileUtils.mkdir_p ::File.dirname(@path)
30
+ begin
31
+ ::File.open(@path, ::File::RDWR|::File::CREAT, 0644) do |f|
32
+ f.flock ::File::LOCK_EX
33
+ f.write(MultiJson.encode(new, :pretty => true))
34
+ f.flush
35
+ f.truncate f.pos
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,30 @@
1
+ class Riemann::Dash::BrowserConfig::S3
2
+ require 'multi_json'
3
+ require 'fog'
4
+
5
+ def initialize(bucket, path, config = {})
6
+ @bucket = bucket
7
+ @path = path
8
+ @config = config
9
+
10
+ @storage = Fog::Storage::AWS.new(config)
11
+ end
12
+
13
+ def read
14
+ begin
15
+ @storage.get_object(@bucket, @path).body
16
+ rescue Excon::Errors::NotFound
17
+ MultiJson.encode({})
18
+ end
19
+ end
20
+
21
+ def update(update)
22
+ update = MultiJson.decode update
23
+
24
+ # Read old config
25
+ old = MultiJson.decode read
26
+
27
+ new = Riemann::Dash::BrowserConfig.merge_configs update, old
28
+ @storage.put_object @bucket, @path, MultiJson.encode(new, :pretty => true)
29
+ end
30
+ end
@@ -1,4 +1,6 @@
1
1
  class Riemann::Dash::Config
2
+ require 'uri'
3
+
2
4
  attr_accessor :config_path
3
5
  attr_accessor :store
4
6
 
@@ -62,6 +64,25 @@ class Riemann::Dash::Config
62
64
  Riemann::Dash::App.use Riemann::Dash::Static, :root => store[:public]
63
65
  end
64
66
 
67
+ def setup_storage_backend
68
+ uri = URI.parse(ws_config_file)
69
+ backend = case uri.scheme
70
+ when "s3"
71
+ begin
72
+ require 'riemann/dash/browser_config/s3'
73
+ Riemann::Dash::BrowserConfig::S3.new(uri.host, uri.path.sub(/^\//, ''), store[:s3_config])
74
+ rescue LoadError
75
+ raise Exception.new 'Fog library required to save to S3. Run: "gem install fog"'
76
+ end
77
+ when nil, "file"
78
+ require 'riemann/dash/browser_config/file'
79
+ Riemann::Dash::BrowserConfig::File.new(uri.path)
80
+ else
81
+ raise Exception.new "Unknown backend for #{ws_config_file}"
82
+ end
83
+ Riemann::Dash::BrowserConfig.backend = backend
84
+ end
85
+
65
86
  # Load controllers.
66
87
  def load_controllers_from(dir)
67
88
  sorted_controller_list(dir).each do |r|
@@ -113,45 +134,4 @@ class Riemann::Dash::Config
113
134
  end
114
135
  end
115
136
  end
116
-
117
-
118
- require 'multi_json'
119
- require 'fileutils'
120
-
121
-
122
- def read_ws_config
123
- if File.exists? ws_config_file
124
- File.read(ws_config_file)
125
- else
126
- MultiJson.encode({})
127
- end
128
- end
129
-
130
- def update_ws_config(update)
131
- update = MultiJson.decode(update)
132
-
133
- # Read old config
134
- if File.exists? ws_config_file
135
- old = MultiJson.decode File.read(ws_config_file)
136
- else
137
- old = {}
138
- end
139
-
140
- new_config = {}
141
-
142
- # Server
143
- new_config['server'] = update['server'] or old['server']
144
-
145
- # Server Type
146
- new_config['server_type'] = update['server_type'] or old['server_type']
147
-
148
- #p update['workspaces']
149
- new_config['workspaces'] = update['workspaces'] or old['workspaces']
150
-
151
- # Save new config
152
- FileUtils.mkdir_p File.dirname(ws_config_file)
153
- File.open(ws_config_file, 'w') do |f|
154
- f.write(MultiJson.encode(new_config, :pretty => true))
155
- end
156
- end
157
137
  end
@@ -5,16 +5,16 @@ class Riemann::Dash::App
5
5
 
6
6
  get '/config', :provides => 'json' do
7
7
  content_type "application/json"
8
- config.read_ws_config
8
+ Riemann::Dash::BrowserConfig.read
9
9
  end
10
10
 
11
11
  post '/config' do
12
12
  # Read update
13
13
  request.body.rewind
14
- config.update_ws_config(request.body.read)
14
+ Riemann::Dash::BrowserConfig.update request.body.read
15
15
 
16
16
  # Return current config
17
17
  content_type "application/json"
18
- config.read_ws_config
18
+ Riemann::Dash::BrowserConfig.read
19
19
  end
20
- end
20
+ end
@@ -193,17 +193,20 @@ dash = (function() {
193
193
  '<li><b>e</b>: edit the view</li>' +
194
194
  '<li><b>?</b>: display this help box</li>' +
195
195
  '<li><b>s</b>: save the dashboard</li>' +
196
+ '<li><b>r</b>: reload the dashboard from last saved config</li>' +
196
197
  '<li><b>+</b>: increase the size of the view</li>' +
197
198
  '<li><b>-</b>: decrease the size of the view</li>' +
198
199
  '<li><b>v</b>: split the view vertically</li>' +
199
200
  '<li><b>h</b>: split the view horizontally</li>' +
200
201
  '<li><b>&#8592;</b>: left arrow move the view to the left</li>' +
201
202
  '<li><b>&#8594;</b>: right arrow move the view to the right</li>' +
202
- '<li><b>&#8593;</b>: up arrow move the view up>' +
203
+ '<li><b>&#8593;</b>: up arrow move the view up</li>' +
203
204
  '<li><b>&#8595;</b>: down arrow move the view down</li>' +
205
+ '<li><b>pageup</b>: select the parent of the current view</li>' +
204
206
  '<li><b>d</b>: delete a view</li>' +
205
207
  '<li><b>delete</b>: delete a view</li>' +
206
208
  '<li><b>alt-1, alt-2, etc</b>: switch to a different workplace</li>' +
209
+ '<li><b>p</b>: pause/unpause the event stream(s)</li>' +
207
210
  '</ul></div>'
208
211
  );
209
212
 
@@ -0,0 +1,63 @@
1
+ // Pane at the bottom of the dashboard for displaying events in context.
2
+
3
+ var eventPane = (function() {
4
+ var el = $('#event-pane');
5
+ var fixedFields = ['host', 'service', 'time', 'state', 'metric', 'ttl', 'description', 'tags'];
6
+ var fixedTemplate =
7
+ _.template(
8
+ '<div class="host">{{-host}}</div>' +
9
+ '<div class="service">{{-service}}</div>' +
10
+ '<div class="state {{-state}}">{{-state}}</div>' +
11
+ '<div class="metric">{{-metric}}</div>' +
12
+ '<time class="absolute">{{-time}}</time>' +
13
+ '<div class="ttl">{{-ttl}}</div>' +
14
+ '<div class="tags">{{-tags}}</div>' +
15
+ '<div class="description">{{-description}}</description>');
16
+
17
+ var rowTemplate =
18
+ _.template("<tr><td>{{-field}}</td><td>{{-value}}</td></tr>");
19
+
20
+ // Hide the pane
21
+ var hide = function() {
22
+ if (el.hasClass("active")) {
23
+ el.empty();
24
+ el.removeClass("active");
25
+ }
26
+ };
27
+
28
+ // Show an event in the pane
29
+ var show = function(event) {
30
+ hide();
31
+
32
+ if (! el.hasClass("active")) {
33
+ el.addClass("active");
34
+ }
35
+
36
+ el.append(
37
+ fixedTemplate(
38
+ _.defaults(
39
+ util.merge(event, {time: new Date(event.time)}),
40
+ {host: "nil",
41
+ service: "nil",
42
+ state: "nil",
43
+ metric: "nil",
44
+ ttl: "nil",
45
+ tags: "nil",
46
+ description: "nil"})));
47
+
48
+ var table = el.append('<table></table>');
49
+
50
+ // Remaining fields
51
+ _.each(event, function(value, field) {
52
+ if (! _.contains(fixedFields, field)) {
53
+ table.append(rowTemplate({field: field, value: value}));
54
+ }
55
+ });
56
+ };
57
+
58
+ // Hide on escape.
59
+ keys.bind(27, hide);
60
+
61
+ return {show: show,
62
+ hide: hide};
63
+ })();