Class: Heroku::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/heroku/client.rb

Overview

A Ruby class to call the Heroku REST API. You might use this if you want to manage your Heroku apps from within a Ruby program, such as Capistrano.

Example:

require 'heroku'
heroku = Heroku::Client.new('[email protected]', 'mypass')
heroku.create('myapp')

Defined Under Namespace

Classes: AppCrashed, ConsoleSession, Service

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(user, password, host = 'heroku.com') ⇒ Client

Returns a new instance of Client.



27
28
29
30
31
# File 'lib/heroku/client.rb', line 27

def initialize(user, password, host='heroku.com')
	@user = user
	@password = password
	@host = host
end

Instance Attribute Details

#hostObject (readonly)

Returns the value of attribute host.



25
26
27
# File 'lib/heroku/client.rb', line 25

def host
  @host
end

#passwordObject (readonly)

Returns the value of attribute password.



25
26
27
# File 'lib/heroku/client.rb', line 25

def password
  @password
end

#userObject (readonly)

Returns the value of attribute user.



25
26
27
# File 'lib/heroku/client.rb', line 25

def user
  @user
end

Class Method Details

.gem_version_stringObject



21
22
23
# File 'lib/heroku/client.rb', line 21

def self.gem_version_string
	"heroku-gem/#{version}"
end

.versionObject



17
18
19
# File 'lib/heroku/client.rb', line 17

def self.version
	'1.6.3'
end

Instance Method Details

#add_collaborator(app_name, email) ⇒ Object

Invite a person by email address to collaborate on the app.



80
81
82
83
84
85
# File 'lib/heroku/client.rb', line 80

def add_collaborator(app_name, email)
	xml(post("/apps/#{app_name}/collaborators", { 'collaborator[email]' => email }))
rescue RestClient::RequestFailed => e
	raise e unless e.http_code == 422
	e.response.body
end

#add_config_vars(app_name, new_vars) ⇒ Object



375
376
377
# File 'lib/heroku/client.rb', line 375

def add_config_vars(app_name, new_vars)
	put("/apps/#{app_name}/config_vars", new_vars.to_json)
end

#add_domain(app_name, domain) ⇒ Object



107
108
109
# File 'lib/heroku/client.rb', line 107

def add_domain(app_name, domain)
	post("/apps/#{app_name}/domains", domain)
end

#add_key(key) ⇒ Object

Add an ssh public key to the current user.



136
137
138
# File 'lib/heroku/client.rb', line 136

def add_key(key)
	post("/user/keys", key, { 'Content-Type' => 'text/ssh-authkey' })
end

#add_ssl(app_name, pem, key) ⇒ Object



119
120
121
# File 'lib/heroku/client.rb', line 119

def add_ssl(app_name, pem, key)
	JSON.parse(post("/apps/#{app_name}/ssl", :pem => pem, :key => key))
end

#addonsObject



387
388
389
# File 'lib/heroku/client.rb', line 387

def addons
	JSON.parse get("/addons", :accept => 'application/json')
end

#bounce(app_name, upid) ⇒ Object

Bounce a service.



307
308
309
# File 'lib/heroku/client.rb', line 307

def bounce(app_name, upid)
	service(app_name, upid).bounce
end

#bundle_capture(app_name, bundle_name = nil) ⇒ Object

Capture a bundle from the given app, as a backup or for download.



338
339
340
# File 'lib/heroku/client.rb', line 338

def bundle_capture(app_name, bundle_name=nil)
	xml(post("/apps/#{app_name}/bundles", :bundle => { :name => bundle_name })).elements["//bundle/name"].text
end

#bundle_destroy(app_name, bundle_name) ⇒ Object



342
343
344
# File 'lib/heroku/client.rb', line 342

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

#bundle_download(app_name, fname, bundle_name = nil) ⇒ Object



353
354
355
356
357
# File 'lib/heroku/client.rb', line 353

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))
	File.open(fname, "wb") { |f| f.write data }
end

#bundle_url(app_name, bundle_name = nil) ⇒ Object

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



348
349
350
351
# File 'lib/heroku/client.rb', line 348

def bundle_url(app_name, bundle_name=nil)
	bundle = JSON.parse(get("/apps/#{app_name}/bundles/#{bundle_name || 'latest'}", { :accept => 'application/json' }))
	bundle['temporary_url']
end

#bundles(app_name) ⇒ Object

Get a list of bundles of the app.



360
361
362
363
364
365
366
367
368
369
# File 'lib/heroku/client.rb', line 360

def bundles(app_name)
	doc = xml(get("/apps/#{app_name}/bundles"))
	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

#clear_config_vars(app_name) ⇒ Object



383
384
385
# File 'lib/heroku/client.rb', line 383

def clear_config_vars(app_name)
	delete("/apps/#{app_name}/config_vars")
end

#config_vars(app_name) ⇒ Object



371
372
373
# File 'lib/heroku/client.rb', line 371

def config_vars(app_name)
	JSON.parse get("/apps/#{app_name}/config_vars")
end

#confirm_billingObject



403
404
405
# File 'lib/heroku/client.rb', line 403

def confirm_billing
	post("/user/#{escape(@user)}/confirm_billing")
end

#console(app_name, cmd = nil) ⇒ Object

Execute a one-off console command, or start a new console tty session if cmd is nil.



170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/heroku/client.rb', line 170

def console(app_name, cmd=nil)
	if block_given?
		id = post("/apps/#{app_name}/consoles")
		yield ConsoleSession.new(id, app_name, self)
		delete("/apps/#{app_name}/consoles/#{id}")
	else
		run_console_command("/apps/#{app_name}/console", cmd)
	end
rescue RestClient::RequestFailed => e
	raise(AppCrashed, e.response.body) if e.response.code.to_i == 502
	raise e
end

#create(name = nil, options = {}) ⇒ Object

Create a new app, with an optional name.



55
56
57
58
# File 'lib/heroku/client.rb', line 55

def create(name=nil, options={})
	options[:name] = name if name
	xml(post('/apps', :app => options)).elements["//app/name"].text
end

#cron_logs(app_name) ⇒ Object

Fetch recent cron logs from the app server.



323
324
325
# File 'lib/heroku/client.rb', line 323

def cron_logs(app_name)
	get("/apps/#{app_name}/cron_logs")
end

#database_reset(app_name) ⇒ Object



479
480
481
# File 'lib/heroku/client.rb', line 479

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

#database_session(app_name) ⇒ Object



475
476
477
# File 'lib/heroku/client.rb', line 475

def database_session(app_name)
	post("/apps/#{app_name}/database/session", '')
end

#delete(uri, extra_headers = {}) ⇒ Object

:nodoc:



434
435
436
# File 'lib/heroku/client.rb', line 434

def delete(uri, extra_headers={})    # :nodoc:
	process(:delete, uri, extra_headers)
end

#destroy(name) ⇒ Object

Destroy the app permanently.



67
68
69
# File 'lib/heroku/client.rb', line 67

def destroy(name)
	delete("/apps/#{name}")
end

#down(app_name, upid) ⇒ Object

Bring a service down.



302
303
304
# File 'lib/heroku/client.rb', line 302

def down(app_name, upid)
	service(app_name, upid).down
end

#escape(value) ⇒ Object

:nodoc:



470
471
472
473
# File 'lib/heroku/client.rb', line 470

def escape(value)  # :nodoc:
	escaped = URI.escape(value.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
	escaped.gsub('.', '%2E') # not covered by the previous URI.escape
end

#extract_warning(response) ⇒ Object



447
448
449
450
451
452
453
454
455
456
457
# File 'lib/heroku/client.rb', line 447

def extract_warning(response)
	return unless response
	if response.headers[:x_heroku_warning] && @warning_callback
		warning = response.headers[:x_heroku_warning]
		@displayed_warnings ||= {}
		unless @displayed_warnings[warning]
			@warning_callback.call(warning)
			@displayed_warnings[warning] = true
		end
	end
end

#get(uri, extra_headers = {}) ⇒ Object

:nodoc:



422
423
424
# File 'lib/heroku/client.rb', line 422

def get(uri, extra_headers={})    # :nodoc:
	process(:get, uri, extra_headers)
end

#heroku_headersObject

:nodoc:



459
460
461
462
463
464
# File 'lib/heroku/client.rb', line 459

def heroku_headers   # :nodoc:
	{
		'X-Heroku-API-Version' => '2',
		'User-Agent'           => self.class.gem_version_string,
	}
end

#info(name_or_domain) ⇒ Object

Show info such as mode, custom domain, and collaborators on an app.



44
45
46
47
48
49
50
51
52
# File 'lib/heroku/client.rb', line 44

def info(name_or_domain)
	name_or_domain = name_or_domain.gsub(/^(http:\/\/)?(www\.)?/, '')
	doc = xml(get("/apps/#{name_or_domain}"))
	attrs = doc.elements.to_a('//app/*').inject({}) do |hash, element|
		hash[element.name.gsub(/-/, '_').to_sym] = element.text; hash
	end
	attrs.merge!(:collaborators => list_collaborators(attrs[:name]))
	attrs.merge!(:addons        => installed_addons(attrs[:name]))
end

#install_addon(app_name, addon, config = {}) ⇒ Object



395
396
397
# File 'lib/heroku/client.rb', line 395

def install_addon(app_name, addon, config={})
	post("/apps/#{app_name}/addons/#{escape(addon)}", { :config => config }, :accept => 'application/json')
end

#installed_addons(app_name) ⇒ Object



391
392
393
# File 'lib/heroku/client.rb', line 391

def installed_addons(app_name)
	JSON.parse get("/apps/#{app_name}/addons", :accept => 'application/json')
end

#keysObject

Get the list of ssh public keys for the current user.



128
129
130
131
132
133
# File 'lib/heroku/client.rb', line 128

def keys
	doc = xml get('/user/keys')
	doc.elements.to_a('//keys/key').map do |key|
		key.elements['contents'].text
	end
end

#listObject

Show a list of apps which you are a collaborator on.



34
35
36
37
38
39
40
41
# File 'lib/heroku/client.rb', line 34

def list
	doc = xml(get('/apps'))
	doc.elements.to_a("//apps/app").map do |a|
		name = a.elements.to_a("name").first
		owner = a.elements.to_a("owner").first
		[name.text, owner.text]
	end
end

#list_collaborators(app_name) ⇒ Object

Get a list of collaborators on the app, returns an array of hashes each with :email



72
73
74
75
76
77
# File 'lib/heroku/client.rb', line 72

def list_collaborators(app_name)
	doc = xml(get("/apps/#{app_name}/collaborators"))
	doc.elements.to_a("//collaborators/collaborator").map do |a|
		{ :email => a.elements['email'].text }
	end
end

#list_domains(app_name) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/heroku/client.rb', line 92

def list_domains(app_name)
	doc = xml(get("/apps/#{app_name}/domains"))
	doc.elements.to_a("//domain-names/*").map do |d|
		attrs = { :domain => d.elements['domain'].text }
		if cert = d.elements['cert']
			attrs[:cert] = {
				:expires_at => Time.parse(cert.elements['expires-at'].text),
				:subject    => cert.elements['subject'].text,
				:issuer     => cert.elements['issuer'].text,
			}
		end
		attrs
	end
end

#logs(app_name) ⇒ Object

Fetch recent logs from the app server.



318
319
320
# File 'lib/heroku/client.rb', line 318

def logs(app_name)
	get("/apps/#{app_name}/logs")
end

#maintenance(app_name, mode) ⇒ Object



483
484
485
486
# File 'lib/heroku/client.rb', line 483

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

#on_warning(&blk) ⇒ Object



407
408
409
# File 'lib/heroku/client.rb', line 407

def on_warning(&blk)
	@warning_callback = blk
end

#post(uri, payload = "", extra_headers = {}) ⇒ Object

:nodoc:



426
427
428
# File 'lib/heroku/client.rb', line 426

def post(uri, payload="", extra_headers={})    # :nodoc:
	process(:post, uri, extra_headers, payload)
end

#process(method, uri, extra_headers = {}, payload = nil) ⇒ Object



438
439
440
441
442
443
444
445
# File 'lib/heroku/client.rb', line 438

def process(method, uri, extra_headers={}, payload=nil)
	headers  = heroku_headers.merge(extra_headers)
	args     = [method, payload, headers].compact
	response = resource(uri).send(*args)

	extract_warning(response)
	response
end

#ps(app_name) ⇒ Object

Retreive ps list for the given app name.



281
282
283
# File 'lib/heroku/client.rb', line 281

def ps(app_name)
	JSON.parse resource("/apps/#{app_name}/ps").get(:accept => 'application/json')
end

#put(uri, payload, extra_headers = {}) ⇒ Object

:nodoc:



430
431
432
# File 'lib/heroku/client.rb', line 430

def put(uri, payload, extra_headers={})    # :nodoc:
	process(:put, uri, extra_headers, payload)
end

#rake(app_name, cmd) ⇒ Object

Run a rake command on the Heroku app and return all output as a string.



154
155
156
# File 'lib/heroku/client.rb', line 154

def rake(app_name, cmd)
	start(app_name, "rake #{cmd}", attached=true).to_s
end

#remove_all_keysObject

Clear all keys on the current user.



146
147
148
# File 'lib/heroku/client.rb', line 146

def remove_all_keys
	delete("/user/keys")
end

#remove_collaborator(app_name, email) ⇒ Object

Remove a collaborator.



88
89
90
# File 'lib/heroku/client.rb', line 88

def remove_collaborator(app_name, email)
	delete("/apps/#{app_name}/collaborators/#{escape(email)}")
end

#remove_config_var(app_name, key) ⇒ Object



379
380
381
# File 'lib/heroku/client.rb', line 379

def remove_config_var(app_name, key)
	delete("/apps/#{app_name}/config_vars/#{key}")
end

#remove_domain(app_name, domain) ⇒ Object



111
112
113
# File 'lib/heroku/client.rb', line 111

def remove_domain(app_name, domain)
	delete("/apps/#{app_name}/domains/#{domain}")
end

#remove_domains(app_name) ⇒ Object



115
116
117
# File 'lib/heroku/client.rb', line 115

def remove_domains(app_name)
	delete("/apps/#{app_name}/domains")
end

#remove_key(key) ⇒ Object

Remove an existing ssh public key from the current user.



141
142
143
# File 'lib/heroku/client.rb', line 141

def remove_key(key)
	delete("/user/keys/#{escape(key)}")
end

#remove_ssl(app_name, domain) ⇒ Object



123
124
125
# File 'lib/heroku/client.rb', line 123

def remove_ssl(app_name, domain)
	delete("/apps/#{app_name}/domains/#{domain}/ssl")
end

#resource(uri) ⇒ Object



413
414
415
416
417
418
419
420
# File 'lib/heroku/client.rb', line 413

def resource(uri)
	RestClient.proxy = ENV['HTTP_PROXY']
	if uri =~ /^https?/
		RestClient::Resource.new(uri, user, password)
	else
		RestClient::Resource.new("https://api.#{host}", user, password)[uri]
	end
end

#restart(app_name) ⇒ Object

Restart the app servers.



313
314
315
# File 'lib/heroku/client.rb', line 313

def restart(app_name)
	delete("/apps/#{app_name}/server")
end

#run_console_command(url, command, prefix = nil) ⇒ Object

internal method to run console commands formatting the output



184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/heroku/client.rb', line 184

def run_console_command(url, command, prefix=nil)
	output = post(url, command)
	return output unless prefix
	if output.include?("\n")
		lines  = output.split("\n")
		(lines[0..-2] << "#{prefix}#{lines.last}").join("\n")
	else
		prefix + output
	end
rescue RestClient::RequestFailed => e
	raise e unless e.http_code == 422
	e.http_body
end

#service(app_name, upid) ⇒ Object

Get a Service instance to execute commands against.



292
293
294
# File 'lib/heroku/client.rb', line 292

def service(app_name, upid)
	Service.new(self, app_name, upid)
end

#set_dynos(app_name, qty) ⇒ Object

Scales the web processes.



328
329
330
# File 'lib/heroku/client.rb', line 328

def set_dynos(app_name, qty)
	put("/apps/#{app_name}/dynos", :dynos => qty).to_i
end

#set_workers(app_name, qty) ⇒ Object

Scales the background processes.



333
334
335
# File 'lib/heroku/client.rb', line 333

def set_workers(app_name, qty)
	put("/apps/#{app_name}/workers", :workers => qty).to_i
end

#start(app_name, command, attached = false) ⇒ Object

Run a service. If Responds to #each and yields output as it’s received.



286
287
288
289
# File 'lib/heroku/client.rb', line 286

def start(app_name, command, attached=false)
	service = Service.new(self, app_name)
	service.start(command, attached)
end

#uninstall_addon(app_name, addon) ⇒ Object



399
400
401
# File 'lib/heroku/client.rb', line 399

def uninstall_addon(app_name, addon)
	delete("/apps/#{app_name}/addons/#{escape(addon)}", :accept => 'application/json')
end

#up(app_name, upid) ⇒ Object

Bring a service up.



297
298
299
# File 'lib/heroku/client.rb', line 297

def up(app_name, upid)
	service(app_name, upid).up
end

#update(name, attributes) ⇒ Object

Update an app. Available attributes:

:name => rename the app (changes http and git urls)


62
63
64
# File 'lib/heroku/client.rb', line 62

def update(name, attributes)
	put("/apps/#{name}", :app => attributes)
end

#xml(raw) ⇒ Object

:nodoc:



466
467
468
# File 'lib/heroku/client.rb', line 466

def xml(raw)   # :nodoc:
	REXML::Document.new(raw)
end