rhc 1.15.6 → 1.16.9
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/autocomplete/rhc_bash +168 -24
- data/features/assets/deploy.tar.gz +0 -0
- data/features/core_feature.rb +6 -0
- data/features/deployments_feature.rb +127 -0
- data/features/keys_feature.rb +37 -0
- data/features/members_feature.rb +36 -1
- data/lib/rhc/commands/app.rb +79 -2
- data/lib/rhc/commands/deployment.rb +82 -0
- data/lib/rhc/commands/snapshot.rb +6 -1
- data/lib/rhc/commands/sshkey.rb +29 -14
- data/lib/rhc/commands/tail.rb +1 -1
- data/lib/rhc/deployment_helpers.rb +88 -0
- data/lib/rhc/exceptions.rb +24 -0
- data/lib/rhc/git_helpers.rb +1 -1
- data/lib/rhc/helpers.rb +17 -14
- data/lib/rhc/highline_extensions.rb +15 -15
- data/lib/rhc/output_helpers.rb +51 -9
- data/lib/rhc/rest.rb +1 -0
- data/lib/rhc/rest/activation.rb +11 -0
- data/lib/rhc/rest/application.rb +53 -1
- data/lib/rhc/rest/client.rb +16 -12
- data/lib/rhc/rest/deployment.rb +18 -0
- data/lib/rhc/rest/key.rb +14 -2
- data/lib/rhc/rest/mock.rb +90 -5
- data/lib/rhc/ssh_helpers.rb +82 -21
- data/lib/rhc/wizard.rb +2 -2
- data/spec/direct_execution_helper.rb +93 -26
- data/spec/rhc/commands/deployment_spec.rb +286 -0
- data/spec/rhc/commands/snapshot_spec.rb +12 -0
- data/spec/rhc/commands/sshkey_spec.rb +37 -0
- data/spec/rhc/commands/tail_spec.rb +2 -2
- data/spec/rhc/rest_client_spec.rb +6 -2
- data/spec/spec_helper.rb +1 -1
- metadata +17 -5
data/lib/rhc/exceptions.rb
CHANGED
@@ -120,6 +120,18 @@ module RHC
|
|
120
120
|
end
|
121
121
|
end
|
122
122
|
|
123
|
+
class DeploymentNotFoundException < Exception
|
124
|
+
def initialize(message="Deployment not found")
|
125
|
+
super message, 131
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
class DeploymentsNotSupportedException < Exception
|
130
|
+
def initialize(message="The server does not support deployments")
|
131
|
+
super message, 132
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
123
135
|
class MissingScalingValueException < Exception
|
124
136
|
def initialize(message="Must provide either a min or max value for scaling")
|
125
137
|
super message
|
@@ -135,12 +147,24 @@ module RHC
|
|
135
147
|
class ConnectionFailed < Exception
|
136
148
|
end
|
137
149
|
|
150
|
+
class SSHAuthenticationFailed < Exception
|
151
|
+
def initialize(host, user)
|
152
|
+
super "Authentication to server #{host} with user #{user} failed"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
138
156
|
class SSHConnectionRefused < ConnectionFailed
|
139
157
|
def initialize(host, user)
|
140
158
|
super "The server #{host} refused a connection with user #{user}. The application may be unavailable.", 1
|
141
159
|
end
|
142
160
|
end
|
143
161
|
|
162
|
+
class SSHCommandFailed < Exception
|
163
|
+
def initialize(exit_status, message=nil)
|
164
|
+
super message || "SSH command finished with exit status = #{exit_status}", 133
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
144
168
|
class AdditionalStorageArgumentsException < Exception
|
145
169
|
def initialize(message="Only one storage action can be performed at a time.")
|
146
170
|
super message, 1
|
data/lib/rhc/git_helpers.rb
CHANGED
data/lib/rhc/helpers.rb
CHANGED
@@ -330,24 +330,27 @@ module RHC
|
|
330
330
|
headings.merge!({
|
331
331
|
:creation_time => "Created",
|
332
332
|
:expires_in_seconds => "Expires In",
|
333
|
-
:uuid
|
334
|
-
:id
|
335
|
-
:current_scale
|
336
|
-
:scales_from
|
337
|
-
:scales_to
|
338
|
-
:gear_sizes
|
339
|
-
:consumed_gears
|
340
|
-
:max_gears
|
341
|
-
:max_domains
|
333
|
+
:uuid => "ID",
|
334
|
+
:id => 'ID',
|
335
|
+
:current_scale => "Current",
|
336
|
+
:scales_from => "Minimum",
|
337
|
+
:scales_to => "Maximum",
|
338
|
+
:gear_sizes => "Allowed Gear Sizes",
|
339
|
+
:consumed_gears => "Gears Used",
|
340
|
+
:max_gears => "Gears Allowed",
|
341
|
+
:max_domains => "Domains Allowed",
|
342
342
|
:compact_members => "Members",
|
343
|
-
:gear_info
|
344
|
-
:plan_id
|
345
|
-
:url
|
346
|
-
:ssh_string
|
343
|
+
:gear_info => "Gears",
|
344
|
+
:plan_id => "Plan",
|
345
|
+
:url => "URL",
|
346
|
+
:ssh_string => "SSH",
|
347
347
|
:connection_info => "Connection URL",
|
348
|
-
:gear_profile
|
348
|
+
:gear_profile => "Gear Size",
|
349
349
|
:visible_to_ssh? => 'Available',
|
350
350
|
:downloaded_cartridge_url => 'From',
|
351
|
+
:auto_deploy => 'Deployment',
|
352
|
+
:sha1 => 'SHA1',
|
353
|
+
:ref => 'Git Reference'
|
351
354
|
})
|
352
355
|
|
353
356
|
headings[value]
|
@@ -53,13 +53,13 @@ class HighLineExtension < HighLine
|
|
53
53
|
@last_line_open = false
|
54
54
|
@output.puts
|
55
55
|
end
|
56
|
-
statement = statement.join("#{indentation}\n")
|
56
|
+
statement = statement.join("#{indentation}\n")
|
57
57
|
end
|
58
58
|
statement = send(:page_print, statement) unless @page_at.nil?
|
59
59
|
|
60
60
|
@output.print(indentation) unless @last_line_open
|
61
61
|
|
62
|
-
@last_line_open =
|
62
|
+
@last_line_open =
|
63
63
|
if statement[-1, 1] == " " or statement[-1, 1] == "\t"
|
64
64
|
@output.print(statement)
|
65
65
|
@output.flush
|
@@ -147,7 +147,7 @@ class HighLineExtension < HighLine
|
|
147
147
|
@multi_indent = multi
|
148
148
|
@indent_level -= increase
|
149
149
|
end
|
150
|
-
end
|
150
|
+
end
|
151
151
|
#:nocov:
|
152
152
|
|
153
153
|
##
|
@@ -170,7 +170,7 @@ class HighLineExtension < HighLine
|
|
170
170
|
#
|
171
171
|
# > Hello
|
172
172
|
# >
|
173
|
-
# > World
|
173
|
+
# > World
|
174
174
|
#
|
175
175
|
# with only one newline between the two. Biggest margin wins.
|
176
176
|
#
|
@@ -298,7 +298,7 @@ end
|
|
298
298
|
|
299
299
|
#
|
300
300
|
# Represent a columnar layout of items with wrapping and flexible layout.
|
301
|
-
#
|
301
|
+
#
|
302
302
|
class HighLine::Table
|
303
303
|
include RowBased
|
304
304
|
|
@@ -310,7 +310,7 @@ class HighLine::Table
|
|
310
310
|
opts[:color]
|
311
311
|
end
|
312
312
|
|
313
|
-
protected
|
313
|
+
protected
|
314
314
|
attr_reader :items
|
315
315
|
|
316
316
|
def opts
|
@@ -333,7 +333,7 @@ class HighLine::Table
|
|
333
333
|
def source_rows
|
334
334
|
@source_rows ||= begin
|
335
335
|
(@mapper ? (items.map &@mapper) : items).each do |row|
|
336
|
-
row.map! do |col|
|
336
|
+
row.map! do |col|
|
337
337
|
case col
|
338
338
|
when Array then col.join("\n")
|
339
339
|
when String then col
|
@@ -388,17 +388,17 @@ class HighLine::Table
|
|
388
388
|
end
|
389
389
|
end
|
390
390
|
|
391
|
-
remaining = column_widths.inject(0) do |sum, w|
|
391
|
+
remaining = column_widths.inject(0) do |sum, w|
|
392
392
|
if w.set == 0
|
393
393
|
sum += w.max
|
394
|
-
available -= w.min
|
394
|
+
available -= w.min
|
395
395
|
end
|
396
396
|
sum
|
397
397
|
end
|
398
398
|
fair = available.to_f / remaining.to_f
|
399
399
|
|
400
400
|
column_widths.
|
401
|
-
each do |w|
|
401
|
+
each do |w|
|
402
402
|
if w.set == 0
|
403
403
|
alloc = (w.max * fair).to_i
|
404
404
|
overflow = alloc + w.min - w.max
|
@@ -424,7 +424,7 @@ class HighLine::Table
|
|
424
424
|
@widths ||= begin
|
425
425
|
case w = opts[:width]
|
426
426
|
when Array
|
427
|
-
column_widths.zip(w[1..-1]).each do |width, col|
|
427
|
+
column_widths.zip(w[1..-1]).each do |width, col|
|
428
428
|
width.set = col || 0
|
429
429
|
width.max = width.set if width.set > width.max
|
430
430
|
end
|
@@ -450,15 +450,15 @@ class HighLine::Table
|
|
450
450
|
|
451
451
|
def rows
|
452
452
|
@rows ||= begin
|
453
|
-
body = (header_rows + source_rows).inject([]) do |a,row|
|
453
|
+
body = (header_rows + source_rows).inject([]) do |a,row|
|
454
454
|
row = row.zip(widths).map{ |column,w| w && w > 0 ? column.textwrap_ansi(w, false) : [column] }
|
455
455
|
(row.map(&:length).max || 0).times do |i|
|
456
456
|
s = []
|
457
457
|
row.each_with_index do |lines, j|
|
458
458
|
cell = lines[i]
|
459
459
|
l = cell ? cell.strip_ansi.length : 0
|
460
|
-
s <<
|
461
|
-
if align[j] == :right
|
460
|
+
s <<
|
461
|
+
if align[j] == :right
|
462
462
|
"#{' '*(widths[j]-l) if l < widths[j]}#{cell}"
|
463
463
|
else
|
464
464
|
"#{cell}#{' '*(widths[j]-l) if l < widths[j]}"
|
@@ -468,7 +468,7 @@ class HighLine::Table
|
|
468
468
|
end
|
469
469
|
a
|
470
470
|
end
|
471
|
-
|
471
|
+
|
472
472
|
body = heading.to_a.concat(body) if heading
|
473
473
|
body
|
474
474
|
end
|
data/lib/rhc/output_helpers.rb
CHANGED
@@ -26,22 +26,21 @@ module RHC
|
|
26
26
|
#---------------------------
|
27
27
|
# Application information
|
28
28
|
#---------------------------
|
29
|
-
def display_app(app,cartridges =
|
29
|
+
def display_app(app, cartridges=nil, properties=nil)
|
30
30
|
paragraph do
|
31
31
|
header [app.name, "@ #{app.app_url}", "(uuid: #{app.uuid})"] do
|
32
32
|
section(:bottom => 1) do
|
33
33
|
say format_table \
|
34
34
|
nil,
|
35
|
-
get_properties(
|
36
|
-
|
37
|
-
:domain,
|
35
|
+
get_properties(app, properties ||
|
36
|
+
[:domain,
|
38
37
|
:creation_time,
|
39
38
|
:gear_info,
|
40
39
|
:git_url,
|
41
40
|
:initial_git_url,
|
42
41
|
:ssh_string,
|
43
|
-
:
|
44
|
-
|
42
|
+
:auto_deploy,
|
43
|
+
:aliases]),
|
45
44
|
:delete => true
|
46
45
|
end
|
47
46
|
cartridges.each{ |c| section(:bottom => 1){ display_cart(c) } } if cartridges
|
@@ -49,6 +48,10 @@ module RHC
|
|
49
48
|
end
|
50
49
|
end
|
51
50
|
|
51
|
+
def display_app_configurations(rest_app)
|
52
|
+
display_app(rest_app, nil, [:auto_deploy, :keep_deployments, :deployment_type, :deployment_branch])
|
53
|
+
end
|
54
|
+
|
52
55
|
def format_cart_header(cart)
|
53
56
|
[
|
54
57
|
cart.name,
|
@@ -94,7 +97,7 @@ module RHC
|
|
94
97
|
end
|
95
98
|
|
96
99
|
def display_key(key, *properties)
|
97
|
-
properties = [:fingerprint, :visible_to_ssh?] if properties.empty?
|
100
|
+
properties = [:fingerprint, :principal, :visible_to_ssh?] if properties.empty?
|
98
101
|
say format_table(
|
99
102
|
properties.include?(:name) ? nil : format_key_header(key),
|
100
103
|
get_properties(key, *properties),
|
@@ -153,6 +156,41 @@ module RHC
|
|
153
156
|
end
|
154
157
|
end
|
155
158
|
|
159
|
+
def display_deployment(item, highlight_active=true)
|
160
|
+
deployment = item[:deployment]
|
161
|
+
active = item[:active]
|
162
|
+
paragraph do
|
163
|
+
say format_table(
|
164
|
+
"Deployment ID #{deployment.id} #{active ? '(active)' : '(inactive)'}",
|
165
|
+
get_properties(deployment, :ref, :sha1, :created_at, :artifact_url, :hot_deploy, :force_clean_build, :activations),
|
166
|
+
{
|
167
|
+
:delete => true,
|
168
|
+
:color => (:green if active && highlight_active)
|
169
|
+
}
|
170
|
+
)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def display_deployment_list(deployment_activations, highlight_active=true)
|
175
|
+
if deployment_activations.present?
|
176
|
+
paragraph do
|
177
|
+
deployment_activations.each do |item|
|
178
|
+
activation = item[:activation]
|
179
|
+
deployment = item[:deployment]
|
180
|
+
rollback = item[:rollback]
|
181
|
+
rollback_to = item[:rollback_to]
|
182
|
+
rolled_back = item[:rolled_back]
|
183
|
+
active = item[:active]
|
184
|
+
say color(
|
185
|
+
date(activation.created_at.to_s) +
|
186
|
+
', deployment ' + deployment.id +
|
187
|
+
(rollback ? " (rollback to #{date(rollback_to.to_s)}#{rolled_back ? ', rolled back' : ''})" : rolled_back ? ' (rolled back)' : ''),
|
188
|
+
active ? :green : rolled_back ? :yellow : nil)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
156
194
|
private
|
157
195
|
def format_table(heading,values,opts = {})
|
158
196
|
values = values.to_a if values.is_a? Hash
|
@@ -171,7 +209,7 @@ module RHC
|
|
171
209
|
|
172
210
|
# This uses the array of properties to retrieve them from an object
|
173
211
|
def get_properties(object,*properties)
|
174
|
-
properties.map do |prop|
|
212
|
+
properties.flatten.map do |prop|
|
175
213
|
# Either send the property to the object or yield it
|
176
214
|
next if prop.nil?
|
177
215
|
value = begin
|
@@ -195,7 +233,7 @@ module RHC
|
|
195
233
|
end
|
196
234
|
when :visible_to_ssh?
|
197
235
|
value || nil
|
198
|
-
when :creation_time
|
236
|
+
when :creation_time, :created_at
|
199
237
|
date(value)
|
200
238
|
when :scales_from,:scales_to
|
201
239
|
(value == -1 ? "available" : value)
|
@@ -207,6 +245,10 @@ module RHC
|
|
207
245
|
value.kind_of?(Array) ? value.join(', ') : value
|
208
246
|
when :expires_in_seconds
|
209
247
|
distance_of_time_in_words(value)
|
248
|
+
when :activations
|
249
|
+
value.collect{|item| date(item.created_at.to_s)}.join("\n")
|
250
|
+
when :auto_deploy
|
251
|
+
value ? 'auto (on git push)' : "manual (use 'rhc deploy')"
|
210
252
|
else
|
211
253
|
case value
|
212
254
|
when Array then value.empty? ? '<none>' : value.join(', ')
|
data/lib/rhc/rest.rb
CHANGED
@@ -12,6 +12,7 @@ module RHC
|
|
12
12
|
autoload :Authorization, 'rhc/rest/authorization'
|
13
13
|
autoload :Cartridge, 'rhc/rest/cartridge'
|
14
14
|
autoload :Client, 'rhc/rest/client'
|
15
|
+
autoload :Deployment, 'rhc/rest/deployment'
|
15
16
|
autoload :Domain, 'rhc/rest/domain'
|
16
17
|
autoload :EnvironmentVariable, 'rhc/rest/environment_variable'
|
17
18
|
autoload :GearGroup, 'rhc/rest/gear_group'
|
data/lib/rhc/rest/application.rb
CHANGED
@@ -8,7 +8,8 @@ module RHC
|
|
8
8
|
define_attr :domain_id, :name, :creation_time, :uuid,
|
9
9
|
:git_url, :app_url, :gear_profile, :framework,
|
10
10
|
:scalable, :health_check_path, :embedded, :gear_count,
|
11
|
-
:ssh_url, :building_app, :cartridges, :initial_git_url
|
11
|
+
:ssh_url, :building_app, :cartridges, :initial_git_url,
|
12
|
+
:auto_deploy, :deployment_branch, :deployment_type, :keep_deployments, :deployments
|
12
13
|
alias_method :domain_name, :domain_id
|
13
14
|
|
14
15
|
# Query helper to say consistent with cartridge
|
@@ -177,6 +178,57 @@ module RHC
|
|
177
178
|
has_param?('ADD_CARTRIDGE', 'environment_variables')
|
178
179
|
end
|
179
180
|
|
181
|
+
def deployments
|
182
|
+
debug "Listing deployments for application #{name}"
|
183
|
+
raise RHC::DeploymentsNotSupportedException if !supports? "LIST_DEPLOYMENTS"
|
184
|
+
rest_method("LIST_DEPLOYMENTS").sort
|
185
|
+
end
|
186
|
+
|
187
|
+
def deployment_activations
|
188
|
+
items = []
|
189
|
+
|
190
|
+
# building an array of activations with their deployments
|
191
|
+
deployments.each do |deployment|
|
192
|
+
deployment.activations.each do |activation|
|
193
|
+
items << {:activation => activation, :deployment => deployment}
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
items.sort! {|a,b| a[:activation].created_at <=> b[:activation].created_at }
|
198
|
+
|
199
|
+
first_activation = {}
|
200
|
+
|
201
|
+
items.each do |item|
|
202
|
+
deployment = item[:deployment]
|
203
|
+
activation = item[:activation]
|
204
|
+
|
205
|
+
# set the currently active (last activation by date)
|
206
|
+
item[:active] = item == items.last
|
207
|
+
|
208
|
+
# mark rollbacks (activations whose deployment had previous activations)
|
209
|
+
if rollback_to = first_activation[deployment.id]
|
210
|
+
item[:rollback] = true
|
211
|
+
item[:rollback_to] = rollback_to
|
212
|
+
# mark rolled back (all in between a rollback and its original deployment)
|
213
|
+
items.each {|i| i[:rolled_back] = true if i[:activation].created_at > rollback_to && i[:activation].created_at < activation.created_at }
|
214
|
+
else
|
215
|
+
first_activation[deployment.id] = activation.created_at
|
216
|
+
end
|
217
|
+
|
218
|
+
end
|
219
|
+
|
220
|
+
items
|
221
|
+
end
|
222
|
+
|
223
|
+
def configure(options={})
|
224
|
+
debug "Running update for #{name} with options #{options.inspect}"
|
225
|
+
if supports? "UPDATE"
|
226
|
+
rest_method "UPDATE", options
|
227
|
+
else
|
228
|
+
raise RHC::DeploymentsNotSupportedException
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
180
232
|
def add_alias(app_alias)
|
181
233
|
debug "Running add_alias for #{name}"
|
182
234
|
rest_method "ADD_ALIAS", :event => "add-alias", :alias => app_alias
|
data/lib/rhc/rest/client.rb
CHANGED
@@ -518,24 +518,33 @@ module RHC
|
|
518
518
|
data.map{ |json| Alias.new(json, self) }
|
519
519
|
when 'environment-variables'
|
520
520
|
data.map{ |json| EnvironmentVariable.new(json, self) }
|
521
|
+
when 'deployments'
|
522
|
+
data.map{ |json| Deployment.new(json, self) }
|
521
523
|
else
|
522
524
|
data
|
523
525
|
end
|
524
526
|
end
|
525
527
|
|
526
528
|
def parse_messages(result, data)
|
527
|
-
|
529
|
+
raw = (result || {})['messages'] || []
|
530
|
+
raw.delete_if do |m|
|
531
|
+
m.delete_if{ |k,v| k.nil? || v.blank? } if m.is_a? Hash
|
532
|
+
m.blank?
|
533
|
+
end
|
534
|
+
warnings, messages, raw = Array(raw).inject([[],[],[]]) do |a, m|
|
528
535
|
severity, field, text = m.values_at('severity', 'field', 'text')
|
529
|
-
text.gsub
|
530
|
-
text.rstrip!
|
536
|
+
text = (text || "").gsub(/\A\n+/m, "").rstrip
|
531
537
|
case severity
|
532
538
|
when 'warning'
|
533
539
|
a[0] << text
|
534
540
|
when 'debug'
|
541
|
+
a[2] << m
|
535
542
|
a[1] << text if debug?
|
536
543
|
when 'info'
|
544
|
+
a[2] << m
|
537
545
|
a[1] << text if debug? || field == 'result'
|
538
546
|
else
|
547
|
+
a[2] << m
|
539
548
|
a[1] << text
|
540
549
|
end
|
541
550
|
a
|
@@ -548,17 +557,16 @@ module RHC
|
|
548
557
|
end
|
549
558
|
elsif data.is_a?(Hash)
|
550
559
|
data['messages'] = messages
|
551
|
-
data['warnings'] = warnings
|
560
|
+
data['warnings'] = warnings
|
552
561
|
end
|
553
562
|
|
554
563
|
warnings.each do |warning|
|
555
|
-
|
556
|
-
if !defined?(@warning_map) || !@warning_map.include?(warning)
|
557
|
-
@warning_map ||= Set.new
|
564
|
+
unless (@warning_map ||= Set.new).include?(warning)
|
558
565
|
@warning_map << warning
|
559
566
|
warn warning
|
560
567
|
end
|
561
568
|
end if respond_to? :warn
|
569
|
+
raw
|
562
570
|
end
|
563
571
|
|
564
572
|
def raise_generic_error(url, client)
|
@@ -576,11 +584,7 @@ module RHC
|
|
576
584
|
parse_error = nil
|
577
585
|
begin
|
578
586
|
result = RHC::Json.decode(response.content)
|
579
|
-
messages =
|
580
|
-
messages.delete_if do |m|
|
581
|
-
m.delete_if{ |k,v| k.nil? || v.blank? } if m.is_a? Hash
|
582
|
-
m.blank?
|
583
|
-
end
|
587
|
+
messages = parse_messages(result, {})
|
584
588
|
rescue => e
|
585
589
|
debug "Response did not include a message from server: #{e.message}"
|
586
590
|
end
|