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