Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adds pg:diagnose #84

Merged
merged 2 commits into from
May 30, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,25 @@ A heroku plugin for awesome pg:* commands that are also great and fun and super.

### Usage


``` bash
~ ➤ heroku pg:diagnose -a will
Report abc123 for will::HEROKU_POSTGRESQL_MAROON_URL
available for one month after creation on 2014-05-30 20:52:09.00093+00

RED: Hit Rate
Name Ratio
-------------- --------------------
index hit rate 0.920046133853151396

GREEN: Connection Count
GREEN: Unused Indexes
GREEN: Bloat
GREEN: Load
GREEN: Long Queries
GREEN: Idle in Transaction
GREEN: Blocking Queries

~ ➤ heroku pg:cache-hit -adashboard
name | ratio
----------------+------------------------
Expand Down
12 changes: 7 additions & 5 deletions init.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
require "heroku/command/base"

if Heroku::VERSION.split(/\./).first.to_i < 3
major, minor, _ = Heroku::VERSION.split(/\./).map(&:to_i)
if (major > 3) || (major == 3 && minor >= 4)
require File.expand_path('lib/heroku/client/heroku_postgresql', File.dirname(__FILE__))
require File.expand_path('lib/heroku/command/pg', File.dirname(__FILE__))
require File.expand_path('lib/heroku/command/pgdiagnose', File.dirname(__FILE__))
else
$stderr.puts(Heroku::Helpers.format_with_bang(<<-EOM))
The heroku-pg-extras plugin was not loaded.
It requires Heroku CLI version >= 3.0.0. You are using #{Heroku::VERSION}.
It requires Heroku CLI version >= 3.4.0. You are using #{Heroku::VERSION}.
EOM
else
require File.expand_path('lib/heroku/client/heroku_postgresql', File.dirname(__FILE__))
require File.expand_path('lib/heroku/command/pg', File.dirname(__FILE__))
end
4 changes: 4 additions & 0 deletions lib/heroku/client/heroku_postgresql.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
class Heroku::Client::HerokuPostgresql
def metrics
http_get "#{resource_name}/metrics"
end

def upgrade
http_post "#{resource_name}/upgrade"
end
Expand Down
104 changes: 104 additions & 0 deletions lib/heroku/command/pgdiagnose.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
class Heroku::Command::Pg < Heroku::Command::Base
DIAGNOSE_URL = ENV.fetch('PGDIAGNOSE_URL', "https://pgdiagnose.herokuapp.com")
# pg:diagnose [DATABASE|REPORT_ID]
#
# run diagnostics report on DATABASE
#
# defaults to DATABASE_URL databases if no DATABASE is specified
# if REPORT_ID is specified instead, a previous report id displayed
def diagnose
db_id = shift_argument

track_extra('diagnose') if can_track?
report = find_or_generate_report(db_id)

puts "Report #{report["id"]} for #{report["app"]}::#{report["database"]}"
puts "available for one month after creation on #{report["created_at"]}"
puts

c = report['checks']
process_checks 'red', c.select{|f| f['status'] == 'red'}
process_checks 'yellow', c.select{|f| f['status'] == 'yellow'}
process_checks 'green', c.select{|f| f['status'] == 'green'}
process_checks 'unknown', c.reject{|f| %w(red yellow green).include?(f['status'])}
end

private

def find_or_generate_report(db_id)
if db_id =~ /\A[a-z0-9\-]{36}\z/
response = get_report(db_id)
else
response = generate_report(db_id)
end

JSON.parse(response.body)
rescue Excon::Errors::Error
error("Unable to connect to PGDiagnose API, please try again later")
end

def get_report(report_id)
Excon.get("#{DIAGNOSE_URL}/reports/#{report_id}", :headers => {"Content-Type" => "application/json"})
end

def generate_report(db_id)
attachment = generate_resolver.resolve(db_id, "DATABASE_URL")
validate_arguments!


@uri = URI.parse(attachment.url) # for nine_two?
if !nine_two?
warn "WARNING: pg:diagnose is only fully suppoted on Postgres version >= 9.2. Some checks will be skipped.\n\n"
end

if attachment.starter_plan?
metrics = nil
else
metrics = hpg_client(attachment).metrics
end

params = {
'url' => attachment.url,
'plan' => attachment.plan,
'metrics' => metrics,
'app' => attachment.app,
'database' => attachment.config_var
}

return Excon.post("#{DIAGNOSE_URL}/reports", :body => params.to_json, :headers => {"Content-Type" => "application/json"})
end

def color(message, status)
if $stdout.tty?
color_code = { "red" => 31, "green" => 32, "yellow" => 33 }.fetch(status, 35)
return "\e[#{color_code}m#{message}\e[0m"
else
return message
end
end

def process_checks(status, checks)
return unless checks.size > 0

checks.each do |check|
status = check['status']
puts color("#{status.upcase}: #{check['name']}", status)
next if "green" == status

results = check['results']
return unless results && results.size > 0

if results.first.kind_of? Array
puts " " + results.first.map(&:capitalize).join(" ")
else
display_table(
results,
results.first.keys,
results.first.keys.map{ |field| field.split(/_/).map(&:capitalize).join(' ') }
)
end
puts
end
end
end