marty 1.1.5 → 1.1.6

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: eb07735da359866d827f601b5dd6e88cb6867605
4
- data.tar.gz: b617fa0870da7065a59760ba74c7160c49334135
3
+ metadata.gz: bea1f7001c31f2ac05b9b5694618355e46661702
4
+ data.tar.gz: 6345a3b84a2319b1a25c2408dd134e371ff0eed1
5
5
  SHA512:
6
- metadata.gz: 1ba6ad619ea48c1429f1f1eb7776111008bf4bb4832b4ea2e83334e9963dfb598928b3d47394f2c9f0625adb3d1bac90ad3cd32ca87d1677f610c950c5ae049b
7
- data.tar.gz: b36df842fa6e092a2d698b6c299b42402bf8084b923075b2d691fcb3393c81104364558f57aa8de6b562a7221e5e78ef9dc6ad535aeac06d2c2b9502159f69b9
6
+ metadata.gz: f4ac10c766f9e4532065c95b75abc71b0d9d79fea51a83a3c3a0c5ba1bed72a287441d84e2c30efa1186ac8d1cc5ed109e9489d3aa557529e2091be723182486
7
+ data.tar.gz: dc8397f83c0acfbe180e3d7a7228b473d4e13bef61c5faf094abdb45d5dcac7cbfa419aa8d93944de57807f365130c5c28b676494f95813c3b31b19ffcdd3e10
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- marty (1.1.4)
4
+ marty (1.1.6)
5
5
  axlsx (= 2.1.0pre)
6
6
  coderay
7
7
  delorean_lang (~> 0.3.33)
@@ -1,433 +1,32 @@
1
- include ActionView::Helpers::TextHelper
2
- require 'erb'
3
-
4
1
  module Marty
5
2
  class DiagnosticController < ActionController::Base
6
- layout false
7
3
  def op
8
4
  begin
9
- # inject request object into base class of all diagnostics
10
- Base.request = request
11
-
12
- # determine if request is aggregate and return result
13
- params[:scope] = 'nodal' unless params[:scope]
14
- diag = self.class.get_sub_class(params[:op])
15
- @result = params[:scope] == 'local' ? diag.generate : diag.aggregate
5
+ @result = Diagnostic::Reporter.run(request)
16
6
  rescue NameError
17
7
  render file: 'public/400', formats: [:html], status: 400, layout: false
18
8
  else
19
9
  respond_to do |format|
20
- format.html {@result = diag.display(@result, params[:scope])}
21
- format.json {render json: @result}
10
+ format.html {@result = display_parameters}
11
+ format.json {render json: process_result_for_api}
22
12
  end
23
13
  end
24
14
  end
25
15
 
26
- def self.get_sub_class klass
27
- const_get(klass.downcase.camelize)
16
+ def process_result_for_api
17
+ @result.delete('data') unless request.params['data'] == 'true'
18
+ @result.delete('errors') if @result['errors'] && @result['errors'].empty?
19
+ @result
28
20
  end
29
21
 
30
- private
31
- ############################################################################
32
- #
33
- # Diagnostics
34
- #
35
- ############################################################################
36
- class Base
37
- @@request = nil
38
- @@read_only = Marty::Util.db_in_recovery?
39
- @@template = ActionController::Base.new.lookup_context.
40
- find_template("marty/diagnostic/diag").identifier
41
-
42
- def self.request
43
- @@request
44
- end
45
-
46
- def self.request= req
47
- @@request = req
48
- end
49
-
50
- def self.aggregate op_name=name.demodulize
51
- get_nodal_diags(op_name)
52
- end
53
-
54
- def self.get_nodal_diags op_name, scope='local'
55
- self.get_nodes.map do |n|
56
- ssl = ENV['HTTPS'] == 'on'
57
- uri = Addressable::URI.new(host: n, port: ssl ? 443 : @@request.port)
58
- uri.query_values = {op: op_name.underscore,
59
- scope: scope}
60
- uri.scheme = ssl ? 'https' : 'http'
61
- uri.path = '/marty/diag.json'
62
- opts = {ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE}
63
- {n => JSON.parse(open(uri, opts).readlines[0])}
64
- end.sum
65
- end
66
-
67
- def self.find_failures data
68
- data.each_with_object({}){
69
- |(k,v), h| h[k] = v.include?('Failure') ? 'F' : 'P'}
70
- end
71
-
72
- def self.errors data
73
- data.keys.count{|n| is_failure?(data[n])}
74
- end
75
-
76
- def self.diff data
77
- data.keys.map{|n| data[n]}.uniq.length != 1
78
- end
79
-
80
- def self.package message
81
- {name.demodulize => message}
82
- end
83
-
84
- def self.is_failure? message
85
- message.to_s.include?('Failure')
86
- end
87
-
88
- def self.error message
89
- "Failure: #{message}"
90
- end
91
-
92
- # determine "target" (highest) value for tests
93
- def self.get_targets data
94
- data.each_with_object({}) do |(_, v), h|
95
- v.each do |k, r|
96
- r = r.to_s
97
- h[k] ||= r
98
- h[k] = r if h[k] < r
99
- end
100
- end
101
- end
102
-
103
- def self.display data, type='nodal'
104
- data = {'local' => data} if type == 'local'
105
- ERB.new(File.open(@@template).read).result(binding)
106
- end
107
-
108
- def self.get_pg_connections
109
- info = ActiveRecord::Base.connection.execute("SELECT datname,"\
110
- "application_name,"\
111
- "state,"\
112
- "pid,"\
113
- "client_addr "\
114
- "FROM pg_stat_activity")
115
- info.each_with_object({}) do |x, h|
116
- h[x["datname"]] ||= []
117
- h[x["datname"]] << {"name" => x["application_name"],
118
- "address"=> x["client_addr"],
119
- "state" => x["state"],
120
- "pid" => x["pid"]}
121
- end
122
- end
123
-
124
- def self.resolve_target_nodes target
125
- db = ActiveRecord::Base.connection_config[:database]
126
- db_conns = get_pg_connections
127
- target_conns = db_conns[db].select{|x|
128
- x['name'].include? target}
129
- target_conns.map{|x| x['address']}.uniq.compact
130
- end
131
-
132
- def self.get_nodes
133
- nodes = resolve_target_nodes("Passenger")
134
- nodes.empty? ? ['127.0.0.1'] : nodes
135
- end
136
- end
137
- ############################################################################
138
- #
139
- # Diagnostic Definitions
140
- # Default: pulls from all nodes; force local with '&scope=local'
141
- #
142
- ############################################################################
143
- class Version < Base
144
- def self.generate
145
- begin
146
- message = `cd #{Rails.root.to_s}; git describe --tags --always;`.strip
147
- rescue
148
- message = error("Failed accessing git")
149
- end
150
- {
151
- 'Marty' => Marty::VERSION,
152
- 'Delorean' => Delorean::VERSION,
153
- 'Mcfly' => Mcfly::VERSION,
154
- 'Git' => message,
155
- }
156
- end
157
- end
158
-
159
- class Database < Base
160
- def self.db_server_name
161
- ActiveRecord::Base.connection_config[:host] || error('undefined')
162
- end
163
-
164
- def self.db_adapter_name
165
- ActiveRecord::Base.connection.adapter_name
166
- end
167
-
168
- def self.db_time
169
- ActiveRecord::Base.connection.execute('SELECT NOW();')
170
- end
171
-
172
- def self.db_version
173
- begin
174
- message = ActiveRecord::Base.connection.
175
- execute('SELECT VERSION();')[0]['version']
176
- rescue => e
177
- return error(message)
178
- end
179
- message
180
- end
181
-
182
- def self.db_schema
183
- begin
184
- current = ActiveRecord::Migrator.current_version
185
- needs_migration = ActiveRecord::Migrator.needs_migration?
186
- rescue => e
187
- return error(e.message)
188
- end
189
- needs_migration ? error("Migration is needed.\n"\
190
- "Current Version: #{current}") : current
191
- end
192
- end
193
-
194
- class Environment < Database
195
- def self.generate
196
- rbv = "#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL} (#{RUBY_PLATFORM})"
197
- {
198
- 'Environment' => Rails.env,
199
- 'Rails' => Rails.version,
200
- 'Netzke Core' => Netzke::Core::VERSION,
201
- 'Netzke Basepack' => Netzke::Basepack::VERSION,
202
- 'Ruby' => rbv,
203
- 'RubyGems' => Gem::VERSION,
204
- 'Database Adapter' => db_adapter_name,
205
- 'Database Server' => db_server_name,
206
- 'Database Version' => db_version,
207
- 'Database Schema Version' => db_schema
208
- }
209
- end
210
- end
211
-
212
- class Nodes < Base
213
- def self.generate
214
- begin
215
- a_nodes = AwsInstanceInfo.new.nodes.sort if AwsInstanceInfo.is_aws?
216
- rescue => e
217
- a_nodes = [e.message]
218
- end
219
- pg_nodes = get_nodes.sort
220
- message = a_nodes.nil? || pg_nodes == a_nodes ? pg_nodes.join("\n") :
221
- error("There is a discrepancy between nodes connected to "\
222
- "Postgres and those discovered through AWS EC2.\n"\
223
- "Postgres: \n#{pg_nodes.join("\n")}\n"\
224
- "AWS: \n#{a_nodes.join("\n")}")
225
- {"PG/AWS" => message}
226
- end
227
- end
228
-
229
- class Env < Base
230
- def self.filter_env filter=''
231
- env = ENV.clone
232
-
233
- # obfuscate SECRET_KEY_BASE for comparison
234
- env['SECRET_KEY_BASE'] = env['SECRET_KEY_BASE'][0,4] if
235
- env['SECRET_KEY_BASE']
236
-
237
- # remove SCRIPT_URI, SCRIPT_URL as calling node differs
238
- ['SCRIPT_URI', 'SCRIPT_URL'].each{|k| env.delete(k)}
239
-
240
- to_block = ['PASSWORD', 'DEBUG']
241
- env.sort.each_with_object({}){|(k,v),h|
242
- h[k] = v if to_block.all?{|b| !k.include?(b)} && k.include?(filter)}
243
- end
244
-
245
- def self.generate
246
- filter_env
247
- end
248
-
249
- def self.aggregate
250
- envs = get_nodal_diags(name.demodulize)
251
- diff(envs) ? envs : package({})
252
- end
253
- end
254
-
255
- class DelayedJob < Base
256
- def self.delayed_job_count
257
- db = ActiveRecord::Base.connection_config[:database]
258
- get_pg_connections[db].count{|c| c['pid'] if
259
- c['name'].include?('delayed_job')}
260
- end
261
-
262
- def self.validate data
263
- data.each_with_object({}) do
264
- |(k,v), h|
265
- h[k] = v.count > 1 ? error("\n" + v.join("\n")) :
266
- v[0] != ENV['DELAYED_VER'] ? error(v[0]) : v[0]
267
- end
268
- end
269
-
270
- def self.generate
271
- count = delayed_job_count
272
- return {'Issue' => ['No delayed jobs are running.']} if count.zero?
273
-
274
- # we will only iterate by half of the total delayed workers to avoid
275
- # excess use of delayed job time
276
- count = (count/2).zero? ? 1 : count/2
277
-
278
- d_engine = Marty::ScriptSet.new.get_engine("Diagnostics")
279
- res = d_engine.evaluate('VersionDelay', 'result', {'count' => count-1})
280
-
281
- # merge results and remove duplicates
282
- res.each_with_object({}){
283
- |r, h|
284
- h[r[0]] ||= []
285
- h[r[0]] << r[1]
286
- }.each_with_object({}){|(k,v), h| h[k] = v.uniq}
287
- end
288
-
289
- def self.aggregate
290
- package(validate(generate))
291
- end
292
-
293
- def self.diff data
294
- data = data[name.demodulize] if data[name.demodulize]
295
- data.keys.map{|k| data[k]}.flatten.uniq.count != 1 ||
296
- data[data.keys[0]] != ENV['DELAYED_VER']
297
- end
298
- end
299
-
300
- ############################################################################
301
- #
302
- # Reports
303
- #
304
- ############################################################################
305
- class Report < Base
306
- class << self
307
- attr_accessor :diags
308
- end
309
-
310
- def diags
311
- self.class.diags
312
- end
313
-
314
- self.diags = ['nodes', 'version', 'environment']
315
-
316
- def self.get_diag_klass diag
317
- controller = name.split(name.demodulize)[0].constantize
318
- controller.const_get(diag.capitalize)
319
- end
320
-
321
- def self.generate
322
- diags.each_with_object({}){|d, h| h[d] = get_diag_klass(d).generate}
323
- end
324
-
325
- def self.aggregate
326
- diags.each_with_object({}){|d, h| h[d] = get_diag_klass(d).aggregate}
327
- end
328
-
329
- def self.display data, type
330
- report = '<h3>' +
331
- name.demodulize +
332
- " #{'(' + type + ')' if type == 'local'}" +
333
- '</h3>'
334
- displays = diags.map{|d| get_diag_klass(d).display(data[d], type)}
335
- ([report] + displays).sum
336
- end
337
- end
338
-
339
- ############################################################################
340
- #
341
- # AWS Helper Class
342
- #
343
- ############################################################################
344
- class AwsInstanceInfo
345
- attr_accessor :id, :doc, :role, :creds, :version, :host, :tag, :nodes
346
-
347
- # aws reserved host used to get instance meta-data
348
- META_DATA_HOST = '169.254.169.254'
349
-
350
- def self.is_aws?
351
- uri = URI.parse("http://#{META_DATA_HOST}")
352
- !(Net::HTTP.get(uri) rescue nil).nil?
353
- end
354
-
355
- def initialize
356
- @id = get_instance_id
357
- @doc = get_document
358
- @role = get_role
359
- @creds = get_credentials
360
- @host = "ec2.#{@doc['region']}.amazonaws.com"
361
- @version = '2016-11-15'
362
- @tag = get_tag
363
- @nodes = get_private_ips
364
- end
365
-
366
- private
367
- def query_meta_data query
368
- uri = URI.parse("http://#{META_DATA_HOST}/latest/meta-data/#{query}/")
369
- Net::HTTP.get(uri)
370
- end
371
-
372
- def query_dynamic query
373
- uri = URI.parse("http://#{META_DATA_HOST}/latest/dynamic/#{query}/")
374
- Net::HTTP.get(uri)
375
- end
376
-
377
- def get_instance_id
378
- query_meta_data('instance-id').to_s
379
- end
380
-
381
- def get_role
382
- query_meta_data('iam/security-credentials').to_s
383
- end
384
-
385
- def get_credentials
386
- JSON.parse(query_meta_data("iam/security-credentials/#{@role}"))
387
- end
388
-
389
- def get_document
390
- JSON.parse(query_dynamic('instance-identity/document'))
391
- end
392
-
393
- def ec2_req action, params = {}
394
- url = "https://#{@host}/?Action=#{action}&Version=#{@version}"
395
- params.each{|a, v| url += "&#{a}=#{v}"}
396
-
397
- sig = Aws::Sigv4::Signer.new(service: 'ec2',
398
- region: @doc['region'],
399
- access_key_id: @creds['AccessKeyId'],
400
- secret_access_key: @creds['SecretAccessKey'],
401
- session_token: @creds['Token'])
402
- signed_url = sig.presign_url(http_method:'GET', url: url)
403
-
404
- http = Net::HTTP.new(@host, 443)
405
- http.use_ssl = true
406
- Hash.from_xml(Net::HTTP.get(signed_url))["#{action}Response"]
407
- end
408
-
409
- def get_tag
410
- params = {'Filter.1.Name' => 'resource-id',
411
- 'Filter.1.Value.1' => get_instance_id,
412
- 'Filter.2.Name' => 'key',
413
- 'Filter.2.Value.1' => 'Name'}
414
- ec2_req('DescribeTags', params)['tagSet']['item']['value']
415
- end
416
-
417
- def get_instances
418
- params = {'Filter.1.Name' => 'tag-value',
419
- 'Filter.1.Value.1' => @tag}
420
- ec2_req('DescribeInstances', params)
421
- end
422
-
423
- def get_private_ips
424
- get_instances['reservationSet']['item'].map{
425
- |i|
426
- item = i['instancesSet']['item']
427
- item.is_a?(Array) ? item.map{|i| i['privateIpAddress']} :
428
- item['privateIpAddress']
429
- }.flatten
430
- end
22
+ def display_parameters
23
+ local = params[:scope] == 'local'
24
+ data = local ? @result : @result['data']
25
+ errors = local ? Diagnostic::Reporter.errors(data) : @result['errors']
26
+ {
27
+ 'display' => Diagnostic::Reporter.displays(data),
28
+ 'errors' => errors
29
+ }
431
30
  end
432
31
  end
433
32
  end