Skip to content

Commit

Permalink
Merge branch 'master' of github.com:heroku/heroku
Browse files Browse the repository at this point in the history
  • Loading branch information
will committed May 9, 2011
2 parents 7a3445d + fc026bf commit 6d0cd5b
Show file tree
Hide file tree
Showing 29 changed files with 832 additions and 309 deletions.
4 changes: 1 addition & 3 deletions .gitignore
@@ -1,6 +1,4 @@
.bundle
coverage
pkg
rdoc
.DS_Store
*.swp
*.rbc
2 changes: 2 additions & 0 deletions Gemfile
Expand Up @@ -6,7 +6,9 @@ group :development do
gem "fakefs"
gem "parka", ">= 0.6.2"
gem "rake", ">= 0.8.7"
gem "rr", "~> 1.0.2"
gem "rspec", ">= 2.0"
gem "simplecov", "~> 0.4.2"
gem "taps", ">= 0.3.23"
gem "webmock"
end
Expand Down
10 changes: 8 additions & 2 deletions Gemfile.lock
@@ -1,9 +1,9 @@
PATH
remote: .
specs:
heroku (2.0.4)
heroku (2.1.0)
launchy (>= 0.3.2)
rest-client (>= 1.4.0, < 1.7.0)
rest-client (~> 1.6.1)
term-ansicolor (~> 1.0.5)

GEM
Expand All @@ -26,6 +26,7 @@ GEM
rake (0.8.7)
rest-client (1.6.1)
mime-types (>= 1.16)
rr (1.0.2)
rspec (2.5.0)
rspec-core (~> 2.5.0)
rspec-expectations (~> 2.5.0)
Expand All @@ -35,6 +36,9 @@ GEM
diff-lcs (~> 1.1.2)
rspec-mocks (2.5.0)
sequel (3.20.0)
simplecov (0.4.2)
simplecov-html (~> 0.4.4)
simplecov-html (0.4.4)
sinatra (1.0)
rack (>= 1.0)
sqlite3 (1.3.3)
Expand All @@ -60,6 +64,8 @@ DEPENDENCIES
heroku!
parka (>= 0.6.2)
rake (>= 0.8.7)
rr (~> 1.0.2)
rspec (>= 2.0)
simplecov (~> 0.4.2)
taps (>= 0.3.23)
webmock
2 changes: 1 addition & 1 deletion heroku.gemspec
Expand Up @@ -15,6 +15,6 @@ Gem::Specification.new do |gem|
gem.files = %x{ git ls-files }.split("\n").select { |d| d =~ %r{^(README|bin/|data/|ext/|lib/|spec/|test/)} }

gem.add_dependency "term-ansicolor", "~> 1.0.5"
gem.add_dependency "rest-client", ">= 1.4.0", "< 1.7.0"
gem.add_dependency "rest-client", "~> 1.6.1"
gem.add_dependency "launchy", ">= 0.3.2"
end
134 changes: 60 additions & 74 deletions lib/heroku/client.rb
Expand Up @@ -430,40 +430,6 @@ def set_workers(app_name, qty)
put("/apps/#{app_name}/workers", :workers => qty).to_s
end

# Capture a bundle from the given app, as a backup or for download.
def bundle_capture(app_name, bundle_name=nil)
xml(post("/apps/#{app_name}/bundles", :bundle => { :name => bundle_name }).to_s).elements["//bundle/name"].text
end

def bundle_destroy(app_name, bundle_name)
delete("/apps/#{app_name}/bundles/#{bundle_name}").to_s
end

# Get a temporary URL where the bundle can be downloaded.
# If bundle_name is nil it will use the most recently captured bundle for the app
def bundle_url(app_name, bundle_name=nil)
bundle = json_decode(get("/apps/#{app_name}/bundles/#{bundle_name || 'latest'}", { :accept => 'application/json' }).to_s)
bundle['temporary_url']
end

def bundle_download(app_name, fname, bundle_name=nil)
warn "[DEPRECATION] `bundle_download` is deprecated. Please use `bundle_url` instead"
data = RestClient.get(bundle_url(app_name, bundle_name)).to_s
File.open(fname, "wb") { |f| f.write data }
end

# Get a list of bundles of the app.
def bundles(app_name)
doc = xml(get("/apps/#{app_name}/bundles").to_s)
doc.elements.to_a("//bundles/bundle").map do |a|
{
:name => a.elements['name'].text,
:state => a.elements['state'].text,
:created_at => Time.parse(a.elements['created-at'].text),
}
end
end

def config_vars(app_name)
json_decode get("/apps/#{app_name}/config_vars").to_s
end
Expand Down Expand Up @@ -501,6 +467,47 @@ def uninstall_addon(app_name, addon, options={})
configure_addon :uninstall, app_name, addon, options
end

def database_session(app_name)
json_decode(post("/apps/#{app_name}/database/session2", '', :x_taps_version => ::Taps.version).to_s)
end

def database_reset(app_name)
post("/apps/#{app_name}/database/reset", '').to_s
end

def maintenance(app_name, mode)
mode = mode == :on ? '1' : '0'
post("/apps/#{app_name}/server/maintenance", :maintenance_mode => mode).to_s
end

def httpcache_purge(app_name)
delete("/apps/#{app_name}/httpcache").to_s
end

def releases(app)
json_decode get("/apps/#{app}/releases").to_s
end

def release(app, release)
json_decode get("/apps/#{app}/releases/#{release}").to_s
end

def rollback(app, release=nil)
post("/apps/#{app}/releases", :rollback => release)
end

def ps_run(app, opts={})
json_decode post("/apps/#{app}/ps", opts).to_s
end

def ps_scale(app, opts={})
post("/apps/#{app}/ps/scale", opts).to_s.to_i
end

def ps_restart(app, opts={})
post("/apps/#{app}/ps/restart", opts)
end

def confirm_billing
post("/user/#{escape(@user)}/confirm_billing").to_s
end
Expand All @@ -511,14 +518,9 @@ def on_warning(&blk)

##################

def resource(uri)
def resource(uri, options={})
RestClient.proxy = ENV['HTTP_PROXY'] || ENV['http_proxy']
resource = RestClient::Resource.new(realize_full_uri(uri),
:user => user,
:password => password,
:ssl_ca_file => local_ca_file
)
enforce_ssl_verification_on_default_host!(resource)
resource = RestClient::Resource.new(realize_full_uri(uri), options.merge(:user => user, :password => password))
resource
end

Expand All @@ -541,7 +543,15 @@ def delete(uri, extra_headers={}) # :nodoc:
def process(method, uri, extra_headers={}, payload=nil)
headers = heroku_headers.merge(extra_headers)
args = [method, payload, headers].compact
response = resource(uri).send(*args)

resource_options = default_resource_options_for_uri(uri)

begin
response = resource(uri, resource_options).send(*args)
rescue RestClient::SSLCertificateNotVerified => ex
host = URI.parse(realize_full_uri(uri)).host
error "WARNING: Unable to verify SSL certificate for #{host}\nTo disable SSL verification, run with HEROKU_SSL_VERIFY=disable"
end

extract_warning(response)
response
Expand Down Expand Up @@ -577,35 +587,6 @@ def escape(value) # :nodoc:
escaped.gsub('.', '%2E') # not covered by the previous URI.escape
end

def database_session(app_name)
json_decode(post("/apps/#{app_name}/database/session2", '', :x_taps_version => ::Taps.version).to_s)
end

def database_reset(app_name)
post("/apps/#{app_name}/database/reset", '').to_s
end

def maintenance(app_name, mode)
mode = mode == :on ? '1' : '0'
post("/apps/#{app_name}/server/maintenance", :maintenance_mode => mode).to_s
end

def httpcache_purge(app_name)
delete("/apps/#{app_name}/httpcache").to_s
end

def releases(app)
json_decode get("/apps/#{app}/releases").to_s
end

def release(app, release)
json_decode get("/apps/#{app}/releases/#{release}").to_s
end

def rollback(app, release=nil)
post("/apps/#{app}/releases", :rollback => release)
end

module JSON
def self.parse(json)
json_decode(json)
Expand Down Expand Up @@ -652,9 +633,14 @@ def realize_full_uri(given)
uri.to_s
end

def enforce_ssl_verification_on_default_host!(resource)
return unless resource.url =~ %r|^https://api.heroku.com|
resource.options[:verify_ssl] = OpenSSL::SSL::VERIFY_PEER
def default_resource_options_for_uri(uri)
if ENV["HEROKU_SSL_VERIFY"] == "disable"
{}
elsif realize_full_uri(uri) =~ %r|^https://api.heroku.com|
{ :verify_ssl => OpenSSL::SSL::VERIFY_PEER, :ssl_ca_file => local_ca_file }
else
{}
end
end

def local_ca_file
Expand Down
57 changes: 57 additions & 0 deletions lib/heroku/client/rendezvous.rb
@@ -0,0 +1,57 @@
require "timeout"
require "socket"
require "uri"
require "heroku/client"
require "heroku/helpers"

class Heroku::Client::Rendezvous
include Heroku::Helpers

attr_reader :input, :output

def initialize(input, output)
@input = input
@output = output
end

def on_connect(&blk)
@on_connect = blk if block_given?
@on_connect
end

def connect(rendezvous_url)
uri = URI.parse(rendezvous_url)
host, port, secret = uri.host, uri.port, uri.path[1..-1]

socket = Timeout.timeout(30) do
s = TCPSocket.open(host, port)
s.puts(secret)
s.readline; s.readline
s
end

set_buffer(false)
output.sync = true
on_connect.call if on_connect

loop do
if o = IO.select([input, socket], nil, nil, nil)
if o.first.first == input
data = input.read_nonblock(1000)
socket.write(data)
socket.flush()
elsif o.first.first == socket
data = socket.read_nonblock(1000)
output.write(data)
end
end
end
rescue Timeout::Error
error "\nTimeout awaiting process"
rescue Errno::ECONNREFUSED
error "\nError connecting to process"
rescue Interrupt, EOFError, Errno::EIO
ensure
set_buffer(true)
end
end
54 changes: 28 additions & 26 deletions lib/heroku/command.rb
Expand Up @@ -64,7 +64,7 @@ def self.global_option(name, *args)
global_option :help, "--help", "-h"
global_option :remote, "--remote REMOTE"

def self.run(cmd, args=[])
def self.prepare_run(cmd, args=[])
command = parse(cmd)

unless command
Expand Down Expand Up @@ -109,33 +109,35 @@ def self.run(cmd, args=[])
@current_args = args
@current_options = opts

begin
object = command[:klass].new(args.dup, opts.dup)
object.send(command[:method])
rescue InvalidCommand
error "Unknown command. Run 'heroku help' for usage information."
rescue RestClient::Unauthorized
puts "Authentication failure"
run "login"
[ command[:klass].new(args.dup, opts.dup), command[:method] ]
end

def self.run(cmd, arguments=[])
object, method = prepare_run(cmd, arguments)
object.send(method)
rescue InvalidCommand
error "Unknown command. Run 'heroku help' for usage information."
rescue RestClient::Unauthorized
puts "Authentication failure"
run "login"
retry
rescue RestClient::PaymentRequired => e
retry if run('account:confirm_billing', args.dup)
rescue RestClient::ResourceNotFound => e
error extract_not_found(e.http_body)
rescue RestClient::Locked => e
app = e.response.headers[:x_confirmation_required]
message = extract_error(e.response.body)
if confirmation_required(app, message)
opts[:confirm] = app
retry
rescue RestClient::PaymentRequired => e
retry if run('account:confirm_billing', args.dup)
rescue RestClient::ResourceNotFound => e
error extract_not_found(e.http_body)
rescue RestClient::Locked => e
app = e.response.headers[:x_confirmation_required]
message = extract_error(e.response.body)
if confirmation_required(app, message)
opts[:confirm] = app
retry
end
rescue RestClient::RequestFailed => e
error extract_error(e.http_body)
rescue RestClient::RequestTimeout
error "API request timed out. Please try again, or contact support@heroku.com if this issue persists."
rescue CommandFailed => e
error e.message
end
rescue RestClient::RequestFailed => e
error extract_error(e.http_body)
rescue RestClient::RequestTimeout
error "API request timed out. Please try again, or contact support@heroku.com if this issue persists."
rescue CommandFailed => e
error e.message
rescue OptionParser::ParseError => ex
commands[cmd] ? run("help", [cmd]) : run("help")
rescue Interrupt => e
Expand Down
19 changes: 17 additions & 2 deletions lib/heroku/command/addons.rb
Expand Up @@ -147,11 +147,26 @@ def addon_run
response = yield

if response
done = "done"
price = "(#{ response['price'] })" if response['price']
message = response['message']

if response['message'] =~ /Attached as ([A-Z0-9_]+)\n(.*)/
attachment = $1
message = $2
else
attachment = nil
message = response['message']
end

begin
release = heroku.releases(app).last['name']
done = "done,"
rescue RestClient::RequestFailed => e
release = nil
end
end

out = [ 'done', price ].compact.join(' ')
out = [ done, attachment, release, price ].compact.join(' ')
if message
out += "\n"
out += message.split("\n").map do |line|
Expand Down

0 comments on commit 6d0cd5b

Please sign in to comment.