marty 1.1.5 → 1.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,28 @@
1
+ class Diagnostic::Request
2
+ def self.request
3
+ raise 'Request object has not been been injected into #{name}' unless
4
+ @@request
5
+
6
+ @@request
7
+ end
8
+
9
+ def self.request= req
10
+ @@request = req
11
+ end
12
+
13
+ def self.params
14
+ request.params
15
+ end
16
+
17
+ def self.scope
18
+ params[:scope]
19
+ end
20
+
21
+ def self.op
22
+ params[:op]
23
+ end
24
+
25
+ def self.ssl?
26
+ request.port == 443
27
+ end
28
+ end
@@ -0,0 +1,17 @@
1
+ class Diagnostic::Version < Diagnostic::Base
2
+ def self.generate
3
+ pack do
4
+ begin
5
+ message = `cd #{Rails.root.to_s}; git describe --tags --always;`.strip
6
+ rescue
7
+ message = error("Failed accessing git")
8
+ end
9
+ {
10
+ 'Marty' => Marty::VERSION,
11
+ 'Delorean' => Delorean::VERSION,
12
+ 'Mcfly' => Mcfly::VERSION,
13
+ 'Git' => message,
14
+ }
15
+ end
16
+ end
17
+ end
@@ -6,14 +6,6 @@ class Marty::Helper
6
6
  Kernel.sleep seconds
7
7
  end
8
8
 
9
- delorean_fn :my_ip, sig:0 do
10
- Socket.ip_address_list.detect{|intf| intf.ipv4_private?}.ip_address
11
- end
12
-
13
- delorean_fn :git, sig:0 do
14
- [my_ip, ENV['DELAYED_VER']]
15
- end
16
-
17
9
  delorean_fn :range_step, sig: 3 do
18
10
  |rstart, rend, step|
19
11
  (rstart..rend).step(step).to_a
@@ -1,34 +1,30 @@
1
- <% inconsistent = diff(data) %>
2
1
  <h3><%=name.demodulize%></h3>
3
- <%='<h3 class="error">Issues Detected</h3>' if
4
- inconsistent%>
2
+ <%='<h3 class="error">Inconsistency Detected</h3>' unless consistent %>
5
3
  <div class="wrapper">
6
4
  <table>
7
- <%# Create node table headers if applicable %>
5
+ <%# create node table headers if applicable %>
8
6
  <tr>
9
- <%='<th></th>' if inconsistent %>
7
+ <%='<th></th>' unless success %>
10
8
  <% data.keys.each do |node| %>
11
- <th <%='colspan="2"' unless inconsistent %> scope="col">
12
- <%= inconsistent ? node :
13
- (type == 'local' ? 'local' : 'consistent') %>
9
+ <th <%='colspan="2"' if success %> scope="col">
10
+ <%= success ? 'consistent' : node %>
14
11
  </th>
15
- <% break unless inconsistent %>
12
+ <% break if success %>
16
13
  <% end %>
17
14
  </tr>
18
- <%# Create row headers and display node results %>
19
- <% data[data.keys.first].each do |key, value| %>
15
+ <%# create row headers and display node results %>
16
+ <% data.values.map{|v| v.keys}.flatten.compact.uniq.each do |test| %>
20
17
  <tr>
21
- <th scope="row"><%= key %></th>
22
- <% targets = get_targets(data) %>
23
- <% data.each do |_node, result| %>
24
- <td class="overflow <%= (is_failure?(value) ||
25
- (result[key].to_s != targets[key].to_s)) ? 'error' :
26
- 'passed' %>">
27
- <%= simple_format(result[key].to_s) %>
18
+ <th class="data" scope="row"><%= test %></th>
19
+ <% data.values.each do |diagnostic| %>
20
+ <td class="overflow <%= display_info_css(diagnostic[test]) %>">
21
+ <%= display_info_description(diagnostic[test]) %>
28
22
  </td>
29
- <% break unless inconsistent %>
23
+ <% break if success %>
30
24
  <% end %>
31
25
  </tr>
32
26
  <% end %>
33
27
  </table>
34
28
  </div>
29
+
30
+ <%= display_alert_message if fatal? %>
@@ -10,13 +10,12 @@
10
10
  color: #333333;
11
11
  font-family: Arial, Helvetica, Verdana, sans-serif;
12
12
  font-weight: normal;
13
- font-size: 9pt; }
13
+ font-size: 9pt;}
14
14
  table { border: none;
15
15
  border-collapse: collapse;
16
16
  display: inline-block;
17
17
  margin: 0px 5px 0px 5px;
18
- text-align: left;
19
- }
18
+ text-align: left;}
20
19
  th { padding: 9px;
21
20
  border: none;
22
21
  background-color: #d6d6d6 }
@@ -24,26 +23,26 @@
24
23
  padding: 9px;
25
24
  }
26
25
  tr { text-align: center;}
27
- tr.passed { background-color: #d0e9c6 }
26
+ td.desc { font-size: 10pt;}
28
27
  td.passed { background-color: #d0e9c6 }
28
+ td.inconsistent { background-color: #ffa500 }
29
29
  td.error { background-color: #ff5555;
30
- color: #ffffff;}
31
- th.error { background-color: #ff5555;}
32
- td.desc { font-size: 10pt; }
30
+ color: #ffffff;}
31
+ td.overflow { max-width: 350px;
32
+ overflow: auto;}
33
+ th.data { text-align: left;}
33
34
  p { white-space: pre-line;}
34
- h1 { display: block;
35
- margin: 0px auto 40px auto;
36
- padding: 8px;
37
- background-color: #1aaa55;
38
- font-size: 18pt;
39
- color: #ffffff;
40
- line-height: 1.5em; }
35
+ h1.application { display: block;
36
+ margin: 0px auto 40px auto;
37
+ padding: 8px;
38
+ background-color: #1aaa55;
39
+ font-size: 18pt;
40
+ color: #ffffff;
41
+ line-height: 1.5em;}
41
42
  h1.error {background-color: #ff5555;}
42
43
  h2 {text-align: center;}
43
44
  h3.error {color: red;}
44
45
  h3 {text-align: center;}
45
- td.overflow { max-width: 350px;
46
- overflow: auto; }
47
46
  .wrapper {
48
47
  overflow-x: auto;
49
48
  white-space: nowrap;
@@ -52,11 +51,11 @@
52
51
  </head>
53
52
  <body>
54
53
  <div style="text-align:center">
55
- <h1 class="<%= 'error' if @result.to_s.include?('error') %>">
56
- <%=Rails.application.class.parent_name%> Diagnostic
54
+ <h1 class="application <%= 'error' unless @result['errors'].empty? %>">
55
+ <%=ENV['DIAG_TITLE'] || Rails.application.class.parent_name%> Diagnostic
57
56
  </h1>
58
57
  <h3><i><%= DateTime.now %></i></h3>
59
- <%== @result %>
58
+ <%== @result['display'] %>
60
59
  </div>
61
60
  </body>
62
61
  </html>
@@ -3,7 +3,7 @@ Delay:
3
3
  # the delay parameter delays the next created job so that a different
4
4
  # worker is more likely to claim it
5
5
  delay =? 2.6
6
- ver = Marty::Helper.sleep(delay) && Marty::Helper.git
6
+ ver = Marty::Helper.sleep(delay) && Diagnostic::Helper.git
7
7
  res = [(Delay() | "ver") for i in Marty::Helper.range_step(0, count, 1)]
8
8
  result = [r.to_a for r in res]
9
9
 
@@ -0,0 +1,28 @@
1
+ module Diagnostic::Database
2
+ def self.db_name
3
+ ActiveRecord::Base.connection_config[:database]
4
+ end
5
+
6
+ def self.db_server_name
7
+ ActiveRecord::Base.connection_config[:host] || 'undefined'
8
+ end
9
+
10
+ def self.db_adapter_name
11
+ ActiveRecord::Base.connection.adapter_name
12
+ end
13
+
14
+ def self.db_time
15
+ ActiveRecord::Base.connection.execute('SELECT NOW();')[0]['now']
16
+ end
17
+
18
+ def self.db_version
19
+ ActiveRecord::Base.connection.execute('SELECT VERSION();')[0]['version']
20
+ end
21
+
22
+ def self.db_schema
23
+ current = ActiveRecord::Migrator.current_version
24
+ raise "Migration is needed.\nCurrent Version: #{current}" if
25
+ ActiveRecord::Migrator.needs_migration?
26
+ current
27
+ end
28
+ end
@@ -0,0 +1,35 @@
1
+ module Diagnostic::Node
2
+ def self.my_ip
3
+ begin
4
+ Socket.ip_address_list.detect{|intf| intf.ipv4_private?}.ip_address
5
+ rescue => e
6
+ e.message
7
+ end
8
+ end
9
+
10
+ def self.get_postgres_connections
11
+ conn = ActiveRecord::Base.connection.execute('SELECT datname,'\
12
+ 'application_name,'\
13
+ 'state,'\
14
+ 'pid,'\
15
+ 'client_addr '\
16
+ 'FROM pg_stat_activity')
17
+ conn.each_with_object({}) do |conn, h|
18
+ h[conn['datname']] ||= []
19
+ h[conn['datname']] << conn.except('datname')
20
+ end
21
+ end
22
+
23
+ def self.get_target_connections target
24
+ get_postgres_connections[Diagnostic::Database.db_name].select{|conn|
25
+ conn['application_name'].include?(target)
26
+ }.map{|conn|
27
+ conn['client_addr'] == '127.0.0.1' ? my_ip : conn['client_addr']
28
+ }.uniq.compact
29
+ end
30
+
31
+ def self.get_nodes
32
+ nodes = get_target_connections('Passenger')
33
+ nodes.empty? ? [my_ip] : nodes
34
+ end
35
+ end
@@ -0,0 +1,47 @@
1
+ module Diagnostic::Packer
2
+ # expects a block that returns either a String or a Hash value and formats
3
+ # it into a diagnostic info object.
4
+ def pack include_ip=true
5
+ begin
6
+ data = yield
7
+ info = case data
8
+ when Hash
9
+ is_valid_info?(data) ? {name.demodulize => data} :
10
+ data.each_with_object({}) do
11
+ |(key, value), hash|
12
+ case value
13
+ when String
14
+ hash[key] = create_info(value)
15
+ when Hash
16
+ raise "Invalid Diagnostic Info #{value}" unless
17
+ is_valid_info?(value)
18
+
19
+ hash[key] = value
20
+ end
21
+ end
22
+ when String
23
+ {name.demodulize => create_info(data)}
24
+ else
25
+ raise "Invalid Data Type: (#{data}, #{data.class}) "\
26
+ "`package` expects a String or Hash value."
27
+ end
28
+ include_ip ? {Diagnostic::Node.my_ip => info} : info
29
+ end
30
+ end
31
+
32
+ def create_info description, status=true, consistent=nil
33
+ {
34
+ 'description' => description,
35
+ 'status' => status,
36
+ 'consistent' => consistent
37
+ }
38
+ end
39
+
40
+ def is_valid_info? info
41
+ info.keys.to_set == Set['description', 'status', 'consistent']
42
+ end
43
+
44
+ def error description
45
+ create_info(description, false)
46
+ end
47
+ end
@@ -1,3 +1,3 @@
1
1
  module Marty
2
- VERSION = "1.1.5"
2
+ VERSION = "1.1.6"
3
3
  end
@@ -4,190 +4,33 @@ module Marty
4
4
  before(:each) { @routes = Marty::Engine.routes }
5
5
  let(:json_response) { JSON.parse(response.body) }
6
6
 
7
- def git
8
- begin
9
- message = `cd #{Rails.root.to_s}; git describe --tags --always;`.strip
10
- rescue
11
- message = error("Failed accessing git")
12
- end
13
- end
14
-
15
- def version
16
- {
17
- "Marty" => Marty::VERSION,
18
- "Delorean" => Delorean::VERSION,
19
- "Mcfly" => Mcfly::VERSION,
20
- "Git" => git,
21
- }
22
- end
23
-
24
- def environment
25
- rbv = "#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL} (#{RUBY_PLATFORM})"
26
- {'Environment' => Rails.env,
27
- 'Rails' => Rails.version,
28
- 'Netzke Core' => Netzke::Core::VERSION,
29
- 'Netzke Basepack' => Netzke::Basepack::VERSION,
30
- 'Ruby' => rbv,
31
- 'RubyGems' => Gem::VERSION,
32
- 'Database Adapter' => described_class::Database.db_adapter_name,
33
- 'Database Server' => described_class::Database.db_server_name,
34
- 'Database Version' => described_class::Database.db_version,
35
- 'Database Schema Version' => described_class::Database.db_schema}
36
- end
37
-
38
- def version_display
39
- <<-ERB
40
- <h3>Version</h3>
41
- <div class="wrapper">
42
- <table>
43
- <tr>
44
- <th colspan="2" scope="col">consistent</th>
45
- </tr>
46
- <tr>
47
- <th scope="row">Marty</th>
48
- <td class="overflow passed"><p>#{Marty::VERSION}</p>
49
- </td>
50
- </tr>
51
- <tr>
52
- <th scope="row">Delorean</th>
53
- <td class="overflow passed"><p>#{Delorean::VERSION}</p>
54
- </td>
55
- </tr>
56
- <tr>
57
- <th scope="row">Mcfly</th>
58
- <td class="overflow passed"><p>#{Mcfly::VERSION}</p>
59
- </td>
60
- </tr>
61
- <tr>
62
- <th scope="row">Git</th>
63
- <td class="overflow passed"><p>#{git}</p>
64
- </td>
65
- </tr>
66
- </table>
67
- </div>
68
- ERB
69
- end
70
-
71
- def version_display_fail val
72
- <<-ERB
73
- <h3>Version</h3>
74
- <h3 class="error">Issues Detected </h3>
75
- <div class="wrapper">
76
- <table>
77
- <tr>
78
- <th></th>
79
- <th scope="col">node1</th>
80
- <th scope="col">node2</th>
81
- </tr>
82
- <tr>
83
- <th scope="row">Marty</th>
84
- <td class="overflow passed"><p>#{Marty::VERSION}</p></td>
85
- <td class="overflow error"><p>#{val}</p></td>
86
- </tr>
87
- <tr>
88
- <th scope="row">Delorean</th>
89
- <td class="overflow passed"><p>#{Delorean::VERSION}</p></td>
90
- <td class="overflow passed"><p>#{Delorean::VERSION}</p></td>
91
- </tr>
92
- <tr>
93
- <th scope="row">Mcfly</th>
94
- <td class="overflow passed"><p>#{Mcfly::VERSION}</p></td>
95
- <td class="overflow passed"><p>#{Mcfly::VERSION}</p></td>
96
- </tr>
97
- <tr>
98
- <th scope="row">Git</th>
99
- <td class="overflow passed"><p>#{git}</p></td>
100
- <td class="overflow passed"><p>#{git}</p></td>
101
- </tr>
102
- </table>
103
- </div>
104
- ERB
105
- end
106
-
107
- def minimize(str)
108
- str.gsub(/\s+/, "")
109
- end
110
-
111
7
  describe 'GET #op' do
112
- it 'returns http success with local scope' do
113
- get :op, op: 'version', scope: 'local'
8
+ it 'returns http success' do
9
+ get :op, format: :json, op: 'version'
114
10
  expect(response).to have_http_status(:success)
115
11
  end
116
12
 
117
- it 'returns the current version JSON' do
118
- get :op, format: :json, op: 'version', scope: 'local'
119
- expect(assigns('result')).to eq(version)
13
+ it 'a request injects the request object into Diagnostic classes' do
14
+ get :op, format: :json, op: 'version'
15
+ expect(Diagnostic::Reporter.request).not_to eq(nil)
120
16
  end
121
17
 
122
- it 'returns the correct environment JSON' do
123
- get :op, format: :json, op: 'environment', scope: 'local'
124
- expect(assigns('result')).to eq(environment)
125
- end
126
-
127
- it 'produces an html display of the diagnostic (version)' do
128
- test = described_class::Version.display({'stub' => version})
129
- expect(minimize(test)).to eq(minimize(version_display))
130
- end
131
-
132
- it 'masks consistent nodes for display (version)' do
133
- data = {'node1' => version, 'node2' => version}
134
- test = described_class::Version.display(data)
135
- expect(minimize(test)).to eq(minimize(version_display))
136
- end
137
-
138
- it 'displays all nodes when there is an inconsistent node (version)' do
139
- ver = '0.0.0'
140
- data = {'node1' => version, 'node2' => version + {'Marty' => ver}}
141
- expected = version_display_fail(ver)
142
- test = described_class::Version.display(data)
143
- expect(minimize(test)).to eq(minimize(expected))
144
- end
145
- end
146
-
147
- describe 'diagnostic classes and aggregate functions' do
148
- it 'has access to DiagnosticController request' do
149
- get :op, op: 'version', scope: 'local'
150
- expect(described_class::Base.request).not_to eq(nil)
151
- end
152
-
153
- it 'can aggregate diagnostics and return appropriate JSON' do
154
- # simulate open-uri nodal diag request
155
- uri_stub = {:open => nil, :readlines => [version.to_json]}
156
- nodes = ['node1', 'node2', 'node3']
157
- expected = nodes.each_with_object({}){|n, h| h[n] = version}
158
-
159
- # mock nodes and diag request to node
160
- allow(described_class::Base.request).to receive(:port).and_return(3000)
161
- allow(described_class::Base).to receive(:get_nodes).and_return(nodes)
162
- allow(described_class::Base).to receive_message_chain(uri_stub)
18
+ it 'returns the current version JSON' do
19
+ get :op, format: :json, op: 'version', data: 'true'
163
20
 
164
- # perform aggregation using Base class function and Version class
165
- expect(described_class::Base.get_nodal_diags('version')).to eq(expected)
166
- expect(described_class::Version.aggregate).to eq(expected)
167
- end
21
+ # generate version data and declare all values consistent
22
+ versions = Diagnostic::Version.generate.each_with_object({}){
23
+ |(n, v),h|
24
+ h[n] = v.each{|t, r| r['consistent'] = true}
25
+ }
168
26
 
169
- # returns true if there are differences in nodes for Base/Version
170
- it 'determines diff of aggregate diagnostic' do
171
- inconsistent = {'1' => version, '2' => version + {'Git' => '123'}}
172
- consistent = inconsistent + {'2' => version}
173
- aggregate_failures do
174
- expect(described_class::Base.diff(inconsistent)).to eq(true)
175
- expect(described_class::Base.diff(consistent)).to eq(false)
176
- expect(described_class::Version.diff(inconsistent)).to eq(true)
177
- expect(described_class::Version.diff(consistent)).to eq(false)
178
- end
179
- end
27
+ expected = {
28
+ 'data' => {
29
+ 'Diagnostic::Version' => versions
30
+ }
31
+ }
180
32
 
181
- it 'can detects errors in diagnostic' do
182
- error_free = version
183
- error_test = version + {'Git' => described_class::Base.
184
- error('Failed accessing git')}
185
- aggregate_failures do
186
- expect(described_class::Base.errors(error_free)).to eq(0)
187
- expect(described_class::Base.errors(error_test)).to eq(1)
188
- expect(described_class::Version.errors(error_free)).to eq(0)
189
- expect(described_class::Version.errors(error_test)).to eq(1)
190
- end
33
+ expect(assigns('result')).to eq(expected)
191
34
  end
192
35
  end
193
36
  end