pogo 2.32.14 → 2.39.2
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.
- data/README.md +0 -2
- data/lib/heroku/auth.rb +3 -1
- data/lib/heroku/client.rb +8 -5
- data/lib/heroku/client/cisaurus.rb +25 -0
- data/lib/heroku/client/heroku_postgresql.rb +0 -3
- data/lib/heroku/client/rendezvous.rb +2 -1
- data/lib/heroku/command.rb +1 -1
- data/lib/heroku/command/addons.rb +11 -2
- data/lib/heroku/command/apps.rb +105 -20
- data/lib/heroku/command/base.rb +1 -1
- data/lib/heroku/command/certs.rb +95 -34
- data/lib/heroku/command/config.rb +5 -5
- data/lib/heroku/command/domains.rb +4 -4
- data/lib/heroku/command/fork.rb +160 -0
- data/lib/heroku/command/git.rb +19 -20
- data/lib/heroku/command/help.rb +18 -2
- data/lib/heroku/command/keys.rb +1 -1
- data/lib/heroku/command/labs.rb +1 -1
- data/lib/heroku/command/logs.rb +3 -56
- data/lib/heroku/command/maintenance.rb +2 -2
- data/lib/heroku/command/pg.rb +7 -16
- data/lib/heroku/command/pgbackups.rb +37 -17
- data/lib/heroku/command/ps.rb +82 -35
- data/lib/heroku/command/regions.rb +23 -0
- data/lib/heroku/command/releases.rb +3 -3
- data/lib/heroku/command/run.rb +40 -27
- data/lib/heroku/command/sharing.rb +4 -4
- data/lib/heroku/command/ssl.rb +7 -25
- data/lib/heroku/command/stack.rb +1 -1
- data/lib/heroku/helpers/heroku_postgresql.rb +13 -17
- data/lib/heroku/helpers/log_displayer.rb +70 -0
- data/lib/heroku/plugin.rb +3 -0
- data/lib/heroku/updater.rb +11 -2
- data/lib/heroku/version.rb +1 -1
- data/spec/heroku/auth_spec.rb +10 -0
- data/spec/heroku/client/ssl_endpoint_spec.rb +12 -12
- data/spec/heroku/client_spec.rb +100 -100
- data/spec/heroku/command/addons_spec.rb +63 -59
- data/spec/heroku/command/apps_spec.rb +68 -65
- data/spec/heroku/command/base_spec.rb +21 -21
- data/spec/heroku/command/certs_spec.rb +31 -31
- data/spec/heroku/command/config_spec.rb +18 -18
- data/spec/heroku/command/db_spec.rb +3 -3
- data/spec/heroku/command/domains_spec.rb +13 -13
- data/spec/heroku/command/drains_spec.rb +3 -3
- data/spec/heroku/command/fork_spec.rb +56 -0
- data/spec/heroku/command/git_spec.rb +57 -29
- data/spec/heroku/command/labs_spec.rb +8 -8
- data/spec/heroku/command/logs_spec.rb +3 -3
- data/spec/heroku/command/maintenance_spec.rb +5 -5
- data/spec/heroku/command/pg_spec.rb +11 -25
- data/spec/heroku/command/pgbackups_spec.rb +13 -8
- data/spec/heroku/command/ps_spec.rb +23 -23
- data/spec/heroku/command/releases_spec.rb +22 -24
- data/spec/heroku/command/run_spec.rb +12 -15
- data/spec/heroku/command/sharing_spec.rb +9 -9
- data/spec/heroku/command/stack_spec.rb +4 -4
- data/spec/heroku/command_spec.rb +12 -12
- data/spec/heroku/helpers/heroku_postgresql_spec.rb +35 -14
- data/spec/spec_helper.rb +17 -2
- data/spec/support/openssl_mock_helper.rb +1 -1
- metadata +11 -6
- data/spec/heroku/command/ssl_spec.rb +0 -32
data/README.md
CHANGED
data/lib/heroku/auth.rb
CHANGED
|
@@ -305,7 +305,9 @@ class Heroku::Auth
|
|
|
305
305
|
end
|
|
306
306
|
|
|
307
307
|
def base_host(host)
|
|
308
|
-
URI.parse(full_host(host)).host.split(".")
|
|
308
|
+
parts = URI.parse(full_host(host)).host.split(".")
|
|
309
|
+
return parts.first if parts.size == 1
|
|
310
|
+
parts[-2..-1].join(".")
|
|
309
311
|
end
|
|
310
312
|
|
|
311
313
|
def full_host(host)
|
data/lib/heroku/client.rb
CHANGED
|
@@ -14,7 +14,7 @@ require 'heroku/client/ssl_endpoint'
|
|
|
14
14
|
#
|
|
15
15
|
# require 'heroku'
|
|
16
16
|
# heroku = Heroku::Client.new('me@example.com', 'mypass')
|
|
17
|
-
# heroku.create(
|
|
17
|
+
# heroku.create()
|
|
18
18
|
#
|
|
19
19
|
class Heroku::Client
|
|
20
20
|
|
|
@@ -249,13 +249,13 @@ class Heroku::Client
|
|
|
249
249
|
doc.elements["//app/workers"].text.to_i
|
|
250
250
|
end
|
|
251
251
|
|
|
252
|
-
# Scales the web
|
|
252
|
+
# Scales the web dynos.
|
|
253
253
|
def set_dynos(app_name, qty)
|
|
254
254
|
deprecate # 07/31/2012
|
|
255
255
|
put("/apps/#{app_name}/dynos", :dynos => qty).to_s
|
|
256
256
|
end
|
|
257
257
|
|
|
258
|
-
# Scales the background
|
|
258
|
+
# Scales the background dynos.
|
|
259
259
|
def set_workers(app_name, qty)
|
|
260
260
|
deprecate # 07/31/2012
|
|
261
261
|
put("/apps/#{app_name}/workers", :workers => qty).to_s
|
|
@@ -546,8 +546,11 @@ Check the output of "heroku ps" and "heroku logs" for more information.
|
|
|
546
546
|
delete("/apps/#{app_name}/logs/drains?url=#{URI.escape(url)}").to_s
|
|
547
547
|
end
|
|
548
548
|
|
|
549
|
-
def addons
|
|
550
|
-
|
|
549
|
+
def addons(filters = {})
|
|
550
|
+
url = "/addons"
|
|
551
|
+
params = filters.map{|k,v| "#{k}=#{v}"}.join("&")
|
|
552
|
+
params = nil if params.empty?
|
|
553
|
+
json_decode get([url,params].compact.join("?"), :accept => 'application/json').to_s
|
|
551
554
|
end
|
|
552
555
|
|
|
553
556
|
def installed_addons(app_name)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require "heroku/client"
|
|
2
|
+
|
|
3
|
+
class Heroku::Client::Cisaurus
|
|
4
|
+
|
|
5
|
+
include Heroku::Helpers
|
|
6
|
+
|
|
7
|
+
def initialize(uri)
|
|
8
|
+
require 'rest_client'
|
|
9
|
+
@uri = URI.parse(uri)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def authenticated_resource(path)
|
|
13
|
+
host = "#{@uri.scheme}://#{@uri.host}"
|
|
14
|
+
host += ":#{@uri.port}" if @uri.port
|
|
15
|
+
RestClient::Resource.new("#{host}#{path}", "", Heroku::Auth.api_key)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def copy_slug(from, to)
|
|
19
|
+
authenticated_resource("/v1/apps/#{from}/copy/#{to}").post(json_encode("description" => "Forked from #{from}"), :content_type => :json).headers[:location]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def job_done?(job_location)
|
|
23
|
+
202 != authenticated_resource(job_location).get.code
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -18,9 +18,6 @@ class Heroku::Client::HerokuPostgresql
|
|
|
18
18
|
attr_reader :attachment
|
|
19
19
|
def initialize(attachment)
|
|
20
20
|
@attachment = attachment
|
|
21
|
-
if attachment.resource_name == 'SHARED_DATABASE'
|
|
22
|
-
error('This command is not available for shared database')
|
|
23
|
-
end
|
|
24
21
|
require 'rest_client'
|
|
25
22
|
end
|
|
26
23
|
|
|
@@ -30,7 +30,8 @@ class Heroku::Client::Rendezvous
|
|
|
30
30
|
|
|
31
31
|
ssl_socket = Timeout.timeout(connect_timeout) do
|
|
32
32
|
ssl_context = OpenSSL::SSL::SSLContext.new
|
|
33
|
-
|
|
33
|
+
ssl_context.ssl_version = :TLSv1
|
|
34
|
+
|
|
34
35
|
if Heroku::Auth.verify_host?(host)
|
|
35
36
|
ssl_context.ca_file = File.expand_path("../../../../data/cacert.pem", __FILE__)
|
|
36
37
|
ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
data/lib/heroku/command.rb
CHANGED
|
@@ -107,7 +107,7 @@ module Heroku
|
|
|
107
107
|
|
|
108
108
|
global_option :confirm, "--confirm APP"
|
|
109
109
|
global_option :help, "-h", "--help"
|
|
110
|
-
global_option :remote, "--remote REMOTE"
|
|
110
|
+
global_option :remote, "-r", "--remote REMOTE"
|
|
111
111
|
|
|
112
112
|
def self.prepare_run(cmd, args=[])
|
|
113
113
|
command = parse(cmd)
|
|
@@ -42,8 +42,17 @@ module Heroku::Command
|
|
|
42
42
|
#
|
|
43
43
|
# list all available addons
|
|
44
44
|
#
|
|
45
|
+
# --region REGION # specify a region for addon availability
|
|
46
|
+
#
|
|
47
|
+
#Example:
|
|
48
|
+
#
|
|
49
|
+
# $ heroku addons:list --region eu
|
|
50
|
+
# === available
|
|
51
|
+
# adept-scale:battleship, corvette...
|
|
52
|
+
# adminium:enterprise, petproject...
|
|
53
|
+
#
|
|
45
54
|
def list
|
|
46
|
-
addons = heroku.addons
|
|
55
|
+
addons = heroku.addons(options)
|
|
47
56
|
if addons.empty?
|
|
48
57
|
display "No addons available currently"
|
|
49
58
|
else
|
|
@@ -177,7 +186,7 @@ module Heroku::Command
|
|
|
177
186
|
end
|
|
178
187
|
|
|
179
188
|
def app_addon_url(addon)
|
|
180
|
-
"https://api.#{heroku.host}/
|
|
189
|
+
"https://api.#{heroku.host}/apps/#{app}/addons/#{addon}"
|
|
181
190
|
end
|
|
182
191
|
|
|
183
192
|
def partition_addons(addons)
|
data/lib/heroku/command/apps.rb
CHANGED
|
@@ -12,11 +12,11 @@ class Heroku::Command::Apps < Heroku::Command::Base
|
|
|
12
12
|
#
|
|
13
13
|
# $ heroku apps
|
|
14
14
|
# === My Apps
|
|
15
|
-
#
|
|
16
|
-
#
|
|
15
|
+
# example
|
|
16
|
+
# example2
|
|
17
17
|
#
|
|
18
18
|
# === Collaborated Apps
|
|
19
|
-
#
|
|
19
|
+
# theirapp other@owner.name
|
|
20
20
|
#
|
|
21
21
|
def index
|
|
22
22
|
validate_arguments!
|
|
@@ -27,13 +27,33 @@ class Heroku::Command::Apps < Heroku::Command::Base
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
unless my_apps.empty?
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
non_legacy_apps = my_apps.select do |app|
|
|
31
|
+
app["tier"] != "legacy"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
unless non_legacy_apps.empty?
|
|
35
|
+
production_basic_apps, dev_legacy_apps = my_apps.partition do |app|
|
|
36
|
+
["production", "basic"].include?(app["tier"])
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
unless production_basic_apps.empty?
|
|
40
|
+
styled_header("Basic & Production Apps")
|
|
41
|
+
styled_array(production_basic_apps.map { |app| regionized_app_name(app) })
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
unless dev_legacy_apps.empty?
|
|
45
|
+
styled_header("Dev & Legacy Apps")
|
|
46
|
+
styled_array(dev_legacy_apps.map { |app| regionized_app_name(app) })
|
|
47
|
+
end
|
|
48
|
+
else
|
|
49
|
+
styled_header("My Apps")
|
|
50
|
+
styled_array(my_apps.map { |app| regionized_app_name(app) })
|
|
51
|
+
end
|
|
32
52
|
end
|
|
33
53
|
|
|
34
54
|
unless collaborated_apps.empty?
|
|
35
55
|
styled_header("Collaborated Apps")
|
|
36
|
-
styled_array(collaborated_apps.map { |app| [app
|
|
56
|
+
styled_array(collaborated_apps.map { |app| [regionized_app_name(app), app["owner_email"]] })
|
|
37
57
|
end
|
|
38
58
|
else
|
|
39
59
|
display("You have no apps.")
|
|
@@ -51,13 +71,13 @@ class Heroku::Command::Apps < Heroku::Command::Base
|
|
|
51
71
|
#Examples:
|
|
52
72
|
#
|
|
53
73
|
# $ heroku apps:info
|
|
54
|
-
# ===
|
|
55
|
-
# Git URL: git@heroku.com:
|
|
74
|
+
# === example
|
|
75
|
+
# Git URL: git@heroku.com:example.git
|
|
56
76
|
# Repo Size: 5M
|
|
57
77
|
# ...
|
|
58
78
|
#
|
|
59
79
|
# $ heroku apps:info --shell
|
|
60
|
-
# git_url=git@heroku.com:
|
|
80
|
+
# git_url=git@heroku.com:example.git
|
|
61
81
|
# repo_size=5000000
|
|
62
82
|
# ...
|
|
63
83
|
#
|
|
@@ -125,6 +145,10 @@ class Heroku::Command::Apps < Heroku::Command::Base
|
|
|
125
145
|
|
|
126
146
|
data["Owner Email"] = app_data["owner_email"]
|
|
127
147
|
|
|
148
|
+
if app_data["region"]
|
|
149
|
+
data["Region"] = app_data["region"]
|
|
150
|
+
end
|
|
151
|
+
|
|
128
152
|
if app_data["repo_size"]
|
|
129
153
|
data["Repo Size"] = format_bytes(app_data["repo_size"])
|
|
130
154
|
end
|
|
@@ -140,6 +164,10 @@ class Heroku::Command::Apps < Heroku::Command::Base
|
|
|
140
164
|
|
|
141
165
|
data["Web URL"] = app_data["web_url"]
|
|
142
166
|
|
|
167
|
+
if app_data["tier"]
|
|
168
|
+
data["Tier"] = app_data["tier"].capitalize
|
|
169
|
+
end
|
|
170
|
+
|
|
143
171
|
styled_hash(data)
|
|
144
172
|
end
|
|
145
173
|
end
|
|
@@ -155,6 +183,8 @@ class Heroku::Command::Apps < Heroku::Command::Base
|
|
|
155
183
|
# -n, --no-remote # don't create a git remote
|
|
156
184
|
# -r, --remote REMOTE # the git remote to create, default "heroku"
|
|
157
185
|
# -s, --stack STACK # the stack on which to create the app
|
|
186
|
+
# --region REGION # specify region for this app to run in
|
|
187
|
+
# -t, --tier TIER # HIDDEN: the tier for this app
|
|
158
188
|
#
|
|
159
189
|
#Examples:
|
|
160
190
|
#
|
|
@@ -167,18 +197,26 @@ class Heroku::Command::Apps < Heroku::Command::Base
|
|
|
167
197
|
# http://floating-dragon-42.herokuapp.com/ | git@heroku.com:floating-dragon-42.git
|
|
168
198
|
#
|
|
169
199
|
# # specify a name
|
|
170
|
-
# $ heroku apps:create
|
|
171
|
-
# Creating
|
|
172
|
-
# http://
|
|
200
|
+
# $ heroku apps:create example
|
|
201
|
+
# Creating example... done, stack is cedar
|
|
202
|
+
# http://example.heroku.com/ | git@heroku.com:example.git
|
|
173
203
|
#
|
|
174
204
|
# # create a staging app
|
|
175
|
-
# $ heroku apps:create
|
|
205
|
+
# $ heroku apps:create example-staging --remote staging
|
|
206
|
+
#
|
|
207
|
+
# # create an app in the eu region
|
|
208
|
+
# $ heroku apps:create --region eu
|
|
176
209
|
#
|
|
177
210
|
def create
|
|
178
211
|
name = shift_argument || options[:app] || ENV['HEROKU_APP']
|
|
179
212
|
validate_arguments!
|
|
180
213
|
|
|
181
|
-
info = api.post_app({
|
|
214
|
+
info = api.post_app({
|
|
215
|
+
"name" => name,
|
|
216
|
+
"region" => options[:region],
|
|
217
|
+
"stack" => options[:stack],
|
|
218
|
+
"tier" => options[:tier]
|
|
219
|
+
}).body
|
|
182
220
|
begin
|
|
183
221
|
action("Creating #{info['name']}") do
|
|
184
222
|
if info['create_status'] == 'creating'
|
|
@@ -189,7 +227,11 @@ class Heroku::Command::Apps < Heroku::Command::Base
|
|
|
189
227
|
end
|
|
190
228
|
end
|
|
191
229
|
end
|
|
192
|
-
|
|
230
|
+
if info['region']
|
|
231
|
+
status("region is #{info['region']}")
|
|
232
|
+
else
|
|
233
|
+
status("stack is #{info['stack']}")
|
|
234
|
+
end
|
|
193
235
|
end
|
|
194
236
|
|
|
195
237
|
(options[:addons] || "").split(",").each do |addon|
|
|
@@ -222,8 +264,8 @@ class Heroku::Command::Apps < Heroku::Command::Base
|
|
|
222
264
|
#
|
|
223
265
|
#Example:
|
|
224
266
|
#
|
|
225
|
-
# $ heroku apps:rename
|
|
226
|
-
# http://
|
|
267
|
+
# $ heroku apps:rename example-newname
|
|
268
|
+
# http://example-newname.herokuapp.com/ | git@heroku.com:example-newname.git
|
|
227
269
|
# Git remote heroku updated
|
|
228
270
|
#
|
|
229
271
|
def rename
|
|
@@ -261,7 +303,7 @@ class Heroku::Command::Apps < Heroku::Command::Base
|
|
|
261
303
|
#Example:
|
|
262
304
|
#
|
|
263
305
|
# $ heroku apps:open
|
|
264
|
-
# Opening
|
|
306
|
+
# Opening example... done
|
|
265
307
|
#
|
|
266
308
|
def open
|
|
267
309
|
validate_arguments!
|
|
@@ -278,8 +320,8 @@ class Heroku::Command::Apps < Heroku::Command::Base
|
|
|
278
320
|
#
|
|
279
321
|
#Example:
|
|
280
322
|
#
|
|
281
|
-
# $ heroku apps:destroy -a
|
|
282
|
-
# Destroying
|
|
323
|
+
# $ heroku apps:destroy -a example --confirm example
|
|
324
|
+
# Destroying example (including all add-ons)... done
|
|
283
325
|
#
|
|
284
326
|
def destroy
|
|
285
327
|
@app = shift_argument || options[:app] || options[:confirm]
|
|
@@ -308,4 +350,47 @@ class Heroku::Command::Apps < Heroku::Command::Base
|
|
|
308
350
|
alias_command "destroy", "apps:destroy"
|
|
309
351
|
alias_command "apps:delete", "apps:destroy"
|
|
310
352
|
|
|
353
|
+
# apps:upgrade TIER
|
|
354
|
+
#
|
|
355
|
+
# HIDDEN: upgrade an app's pricing tier
|
|
356
|
+
#
|
|
357
|
+
def upgrade
|
|
358
|
+
tier = shift_argument
|
|
359
|
+
error("Usage: heroku apps:upgrade TIER\nMust specify TIER to upgrade.") if tier.nil? || tier.empty?
|
|
360
|
+
validate_arguments!
|
|
361
|
+
|
|
362
|
+
action("Upgrading #{app} to #{tier}") do
|
|
363
|
+
api.put_app(app, "tier" => tier)
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
alias_command "upgrade", "apps:upgrade"
|
|
368
|
+
|
|
369
|
+
# apps:downgrade TIER
|
|
370
|
+
#
|
|
371
|
+
# HIDDEN: downgrade an app's pricing tier
|
|
372
|
+
#
|
|
373
|
+
def downgrade
|
|
374
|
+
tier = shift_argument
|
|
375
|
+
error("Usage: heroku apps:downgrade TIER\nMust specify TIER to downgrade.") if tier.nil? || tier.empty?
|
|
376
|
+
validate_arguments!
|
|
377
|
+
|
|
378
|
+
action("Upgrading #{app} to #{tier}") do
|
|
379
|
+
api.put_app(app, "tier" => tier)
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
alias_command "downgrade", "apps:downgrade"
|
|
384
|
+
|
|
385
|
+
private
|
|
386
|
+
|
|
387
|
+
def regionized_app_name(app)
|
|
388
|
+
# temporary, show region for non-us apps
|
|
389
|
+
if app["region"] && app["region"] != 'us'
|
|
390
|
+
"#{app["name"]} (#{app["region"]})"
|
|
391
|
+
else
|
|
392
|
+
app["name"]
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
|
|
311
396
|
end
|
data/lib/heroku/command/base.rb
CHANGED
|
@@ -205,7 +205,7 @@ protected
|
|
|
205
205
|
return unless File.exists?(".git")
|
|
206
206
|
git("remote -v").split("\n").each do |remote|
|
|
207
207
|
name, url, method = remote.split(/\s/)
|
|
208
|
-
if url =~ /^git@#{Heroku::Auth.git_host}:([\w\d-]+)\.git$/
|
|
208
|
+
if url =~ /^git@#{Heroku::Auth.git_host}(?:[\.\w]*):([\w\d-]+)\.git$/
|
|
209
209
|
remotes[name] = $1
|
|
210
210
|
end
|
|
211
211
|
end
|
data/lib/heroku/command/certs.rb
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
require "heroku/command/base"
|
|
2
|
+
require "excon"
|
|
2
3
|
|
|
3
4
|
# manage ssl endpoints for an app
|
|
4
5
|
#
|
|
5
6
|
class Heroku::Command::Certs < Heroku::Command::Base
|
|
7
|
+
SSL_DOCTOR = Excon.new(ENV["SSL_DOCTOR_URL"] || "https://ssl-doctor.herokuapp.com/")
|
|
8
|
+
|
|
9
|
+
class UsageError < StandardError; end
|
|
6
10
|
|
|
7
11
|
# certs
|
|
8
12
|
#
|
|
9
|
-
#
|
|
13
|
+
# List ssl endpoints for an app.
|
|
10
14
|
#
|
|
11
15
|
def index
|
|
12
16
|
endpoints = heroku.ssl_endpoint_list(app)
|
|
13
17
|
|
|
14
18
|
if endpoints.empty?
|
|
15
19
|
display "#{app} has no SSL Endpoints."
|
|
16
|
-
display "Use `heroku certs:add
|
|
20
|
+
display "Use `heroku certs:add CRT KEY` to add one."
|
|
17
21
|
else
|
|
18
22
|
endpoints.map! do |endpoint|
|
|
19
23
|
{
|
|
@@ -31,28 +35,70 @@ class Heroku::Command::Certs < Heroku::Command::Base
|
|
|
31
35
|
end
|
|
32
36
|
end
|
|
33
37
|
|
|
34
|
-
# certs:
|
|
38
|
+
# certs:chain CRT [CRT ...]
|
|
35
39
|
#
|
|
36
|
-
#
|
|
40
|
+
# Print the ordered and complete chain for the given certificate.
|
|
37
41
|
#
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
# Optional intermediate certificates may be given too, and will
|
|
43
|
+
# be used during chain resolution.
|
|
44
|
+
#
|
|
45
|
+
def chain
|
|
46
|
+
puts read_crt_through_ssl_doctor
|
|
47
|
+
rescue UsageError
|
|
48
|
+
fail("Usage: heroku certs:chain CRT [CRT ...]\nMust specify at least one certificate file.")
|
|
49
|
+
end
|
|
42
50
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
51
|
+
# certs:key CRT KEY [KEY ...]
|
|
52
|
+
#
|
|
53
|
+
# Print the correct key for the given certificate.
|
|
54
|
+
#
|
|
55
|
+
# You must pass one single certificate, and one or more keys.
|
|
56
|
+
# The first key that signs the certificate will be printed back.
|
|
57
|
+
#
|
|
58
|
+
def key
|
|
59
|
+
crt, key = read_crt_and_key_through_ssl_doctor("Testing for signing key")
|
|
60
|
+
puts key
|
|
61
|
+
rescue UsageError
|
|
62
|
+
fail("Usage: heroku certs:key CRT KEY [KEY ...]\nMust specify one certificate file and at least one key file.")
|
|
63
|
+
end
|
|
46
64
|
|
|
65
|
+
# certs:add CRT KEY
|
|
66
|
+
#
|
|
67
|
+
# Add an ssl endpoint to an app.
|
|
68
|
+
#
|
|
69
|
+
# --bypass # bypass the trust chain completion step
|
|
70
|
+
#
|
|
71
|
+
def add
|
|
72
|
+
crt, key = read_crt_and_key
|
|
73
|
+
endpoint = action("Adding SSL Endpoint to #{app}") { heroku.ssl_endpoint_add(app, crt, key) }
|
|
47
74
|
display_warnings(endpoint)
|
|
48
75
|
display "#{app} now served by #{endpoint['cname']}"
|
|
49
76
|
display "Certificate details:"
|
|
50
77
|
display_certificate_info(endpoint)
|
|
78
|
+
rescue UsageError
|
|
79
|
+
fail("Usage: heroku certs:add CRT KEY\nMust specify CRT and KEY to add cert.")
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# certs:update CRT KEY
|
|
83
|
+
#
|
|
84
|
+
# Update an SSL Endpoint on an app.
|
|
85
|
+
#
|
|
86
|
+
# --bypass # bypass the trust chain completion step
|
|
87
|
+
#
|
|
88
|
+
def update
|
|
89
|
+
crt, key = read_crt_and_key
|
|
90
|
+
cname = options[:endpoint] || current_endpoint
|
|
91
|
+
endpoint = action("Updating SSL Endpoint #{cname} for #{app}") { heroku.ssl_endpoint_update(app, cname, crt, key) }
|
|
92
|
+
display_warnings(endpoint)
|
|
93
|
+
display "Updated certificate details:"
|
|
94
|
+
display_certificate_info(endpoint)
|
|
95
|
+
rescue UsageError
|
|
96
|
+
fail("Usage: heroku certs:update CRT KEY\nMust specify CRT and KEY to update cert.")
|
|
51
97
|
end
|
|
52
98
|
|
|
53
99
|
# certs:info
|
|
54
100
|
#
|
|
55
|
-
#
|
|
101
|
+
# Show certificate information for an ssl endpoint.
|
|
56
102
|
#
|
|
57
103
|
def info
|
|
58
104
|
cname = options[:endpoint] || current_endpoint
|
|
@@ -66,7 +112,7 @@ class Heroku::Command::Certs < Heroku::Command::Base
|
|
|
66
112
|
|
|
67
113
|
# certs:remove
|
|
68
114
|
#
|
|
69
|
-
#
|
|
115
|
+
# Remove an SSL Endpoint from an app.
|
|
70
116
|
#
|
|
71
117
|
def remove
|
|
72
118
|
cname = options[:endpoint] || current_endpoint
|
|
@@ -76,29 +122,9 @@ class Heroku::Command::Certs < Heroku::Command::Base
|
|
|
76
122
|
display "NOTE: Billing is still active. Remove SSL Endpoint add-on to stop billing."
|
|
77
123
|
end
|
|
78
124
|
|
|
79
|
-
# certs:update PEM KEY
|
|
80
|
-
#
|
|
81
|
-
# update an SSL Endpoint on an app
|
|
82
|
-
#
|
|
83
|
-
def update
|
|
84
|
-
fail("Usage: heroku certs:update PEM KEY\nMust specify PEM and KEY to update cert.") if args.size < 2
|
|
85
|
-
pem = File.read(args[0]) rescue error("Unable to read #{args[0]} PEM")
|
|
86
|
-
key = File.read(args[1]) rescue error("Unable to read #{args[1]} KEY")
|
|
87
|
-
app = self.app
|
|
88
|
-
cname = options[:endpoint] || current_endpoint
|
|
89
|
-
|
|
90
|
-
endpoint = action("Updating SSL Endpoint #{cname} for #{app}") do
|
|
91
|
-
heroku.ssl_endpoint_update(app, cname, pem, key)
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
display_warnings(endpoint)
|
|
95
|
-
display "Updated certificate details:"
|
|
96
|
-
display_certificate_info(endpoint)
|
|
97
|
-
end
|
|
98
|
-
|
|
99
125
|
# certs:rollback
|
|
100
126
|
#
|
|
101
|
-
#
|
|
127
|
+
# Rollback an SSL Endpoint for an app.
|
|
102
128
|
#
|
|
103
129
|
def rollback
|
|
104
130
|
cname = options[:endpoint] || current_endpoint
|
|
@@ -145,4 +171,39 @@ class Heroku::Command::Certs < Heroku::Command::Base
|
|
|
145
171
|
end
|
|
146
172
|
end
|
|
147
173
|
|
|
174
|
+
def display(msg = "", new_line = true)
|
|
175
|
+
super if $stdout.tty?
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def post_to_ssl_doctor(path, action_text = nil)
|
|
179
|
+
raise UsageError if args.size < 1
|
|
180
|
+
action_text ||= "Resolving trust chain"
|
|
181
|
+
action(action_text) do
|
|
182
|
+
input = args.map { |arg| File.read(arg) rescue error("Unable to read #{args[0]} file") }.join("\n")
|
|
183
|
+
SSL_DOCTOR.post(:path => path, :body => input, :headers => {'Content-Type' => 'application/octet-stream'}, :expects => 200).body
|
|
184
|
+
end
|
|
185
|
+
rescue Excon::Errors::BadRequest, Excon::Errors::UnprocessableEntity => e
|
|
186
|
+
error(e.response.body)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def read_crt_and_key_through_ssl_doctor(action_text = nil)
|
|
190
|
+
crt_and_key = post_to_ssl_doctor("resolve-chain-and-key", action_text)
|
|
191
|
+
Heroku::OkJson.decode(crt_and_key).values_at("pem", "key")
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def read_crt_through_ssl_doctor(action_text = nil)
|
|
195
|
+
post_to_ssl_doctor("resolve-chain", action_text)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def read_crt_and_key_bypassing_ssl_doctor
|
|
199
|
+
raise UsageError if args.size != 2
|
|
200
|
+
crt = File.read(args[0]) rescue error("Unable to read #{args[0]} CRT")
|
|
201
|
+
key = File.read(args[1]) rescue error("Unable to read #{args[1]} KEY")
|
|
202
|
+
[crt, key]
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def read_crt_and_key
|
|
206
|
+
options[:bypass] ? read_crt_and_key_bypassing_ssl_doctor : read_crt_and_key_through_ssl_doctor
|
|
207
|
+
end
|
|
208
|
+
|
|
148
209
|
end
|