rhc 1.3.8 → 1.4.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,3 @@
1
- require 'dnsruby'
2
1
  require 'rhc/rest'
3
2
 
4
3
  module RHCHelper
@@ -9,21 +8,16 @@ module RHCHelper
9
8
  extend Runnable
10
9
  extend Commandify
11
10
  extend API
12
- include Dnsruby
13
11
 
14
12
  class << self
15
13
  attr_reader :domain_output, :domain_show_output, :exitcode
16
14
  end
17
15
 
18
16
  def self.unique_namespace(prefix)
19
- namepace = nil
20
- begin
21
- # Loop until we find a unique namespace
22
- chars = ("1".."9").to_a
23
- namespace = prefix + Array.new(8, '').collect{chars[rand(chars.size)]}.join
24
-
25
- end while reserved?(namespace)
26
- namespace
17
+ # TODO: Due to DNS changes with the model refactor,
18
+ # the namespace is not checked here - see #reserved?
19
+ chars = ("1".."9").to_a
20
+ prefix + Array.new(8, '').collect{chars[rand(chars.size)]}.join
27
21
  end
28
22
 
29
23
  def self.create_if_needed(prefix="test")
@@ -61,14 +55,11 @@ module RHCHelper
61
55
  end
62
56
 
63
57
  def self.reserved?(namespace=$namespace)
64
- # If we get a response, then the namespace is reserved
65
- # An exception means that it is available
66
- begin
67
- Dnsruby::Resolver.new.query("#{namespace}.#{$domain}", Dnsruby::Types::TXT)
68
- return true
69
- rescue
70
- return false
71
- end
58
+ # With the recent model refactoring, we no longer create
59
+ # TXT DNS records.
60
+ # Here, we return 'true', since this is the only code that is
61
+ # used in 'Then' clauses of 2 Cucumber scenarios.
62
+ true
72
63
  end
73
64
  end
74
65
  end
@@ -34,7 +34,7 @@ Feature: Scaled Application Operations
34
34
 
35
35
  Examples:
36
36
  | cart | type | value |
37
- | php-5.3 | min | 3 |
37
+ | php-5.3 | min | 1 |
38
38
  | php-5.3 | max | 5 |
39
39
  | php-5.3 | max | -1 |
40
40
 
@@ -32,7 +32,7 @@ Then /^the (.+) cartridge should be (.*)$/ do |name,status|
32
32
  when :stopped
33
33
  "(.+) stopped"
34
34
  when :removed
35
- "Cartridge '#{name}' cannot be found in application"
35
+ "There are no cartridges that match '#{name}'"
36
36
  else
37
37
  raise "Unrecognized status type #{status}"
38
38
  end
data/lib/rhc.rb CHANGED
@@ -12,12 +12,15 @@ require 'rhc/core_ext'
12
12
 
13
13
  module RHC
14
14
  autoload :Auth, 'rhc/auth'
15
+ autoload :CartridgeHelpers, 'rhc/cartridge_helpers'
15
16
  autoload :CommandRunner, 'rhc/command_runner'
16
17
  autoload :Commands, 'rhc/commands'
17
18
  autoload :Config, 'rhc/config'
19
+ autoload :GitHelpers, 'rhc/git_helpers'
18
20
  autoload :Helpers, 'rhc/helpers'
19
21
  autoload :HelpFormatter, 'rhc/help_formatter'
20
22
  autoload :Rest, 'rhc/rest'
23
+ autoload :SSHHelpers, 'rhc/ssh_helpers'
21
24
  autoload :TarGz, 'rhc/tar_gz'
22
25
  autoload :VERSION, 'rhc/version'
23
26
  end
@@ -1,40 +1,98 @@
1
1
  module RHC
2
2
  module CartridgeHelpers
3
3
 
4
- def find_cartridge(rest_obj, cartridge_name, type="embedded")
5
- carts = find_cartridges(rest_obj, [cartridge_name], type)
4
+ protected
5
+ def check_cartridges(names, opts={}, &block)
6
+ cartridge_names = Array(names).map{ |s| s.strip if s && s.length > 0 }.compact
7
+ from = opts[:from] || all_cartridges
6
8
 
7
- if carts.length == 0
8
- valid_carts = rest_obj.cartridges.collect { |c| c.name if c.type == type }.compact
9
-
10
- msg = if RHC::Rest::Application === rest_obj
11
- "Cartridge '#{cartridge_name}' cannot be found in application '#{rest_obj.name}'."
12
- else
13
- "Cartridge '#{cartridge_name}' is not a valid cartridge name."
14
- end
15
-
16
- unless valid_carts.empty?
17
- msg += " Valid cartridges are (#{valid_carts.join(', ')})."
9
+ cartridge_names.map do |name|
10
+ name = name.downcase
11
+ from.find{ |c| c.name.downcase == name } ||
12
+ begin
13
+ carts = from.select{ |c| match_cart(c, name) }
14
+ if carts.empty?
15
+ paragraph { list_cartridges(from) }
16
+ raise RHC::CartridgeNotFoundException, "There are no cartridges that match '#{name}'."
17
+ elsif carts.length == 1
18
+ use_cart(carts.first, name)
19
+ else
20
+ carts.sort!.instance_variable_set(:@for, name)
21
+ carts
22
+ end
23
+ end
24
+ end.tap do |carts|
25
+ yield carts if block_given?
26
+ end.each do |carts|
27
+ if carts.is_a? Array
28
+ name = carts.instance_variable_get(:@for)
29
+ paragraph { list_cartridges(carts) }
30
+ raise RHC::MultipleCartridgesException, "There are multiple cartridges matching '#{name}'. Please provide the short name of the correct cart."
31
+ end
18
32
  end
33
+ end
19
34
 
20
- raise RHC::CartridgeNotFoundException, msg
21
- elsif carts.length > 1
22
- msg = "Multiple cartridge versions match your criteria. Please specify one."
23
- carts.each { |cart| msg += "\n #{cart.name}" }
24
- raise RHC::MultipleCartridgesException, msg
35
+ def use_cart(cart, for_cartridge_name)
36
+ info "Using #{cart.name}#{cart.display_name ? " (#{cart.display_name})" : ''} for '#{for_cartridge_name}'"
37
+ cart
25
38
  end
26
39
 
27
- carts[0]
28
- end
40
+ def match_cart(cart, search)
41
+ search = search.to_s.downcase.gsub(/[_\-\s]/,' ')
42
+ [
43
+ cart.name,
44
+ cart.description,
45
+ (cart.tags || []).join(' '),
46
+ ].compact.any?{ |s| s.present? && s.downcase.gsub(/[_\-\s]/,' ').include?(search) }
47
+ end
29
48
 
30
- def find_cartridges(rest_obj, cartridge_list, type='embedded')
31
- rest_obj.find_cartridges :regex => cartridge_list.collect { |c| cart_regex c }.join('|'), :type => type
32
- end
49
+ def web_carts_only
50
+ lambda{ |cart|
51
+ next cart unless cart.is_a? Array
52
+ name = cart.instance_variable_get(:@for)
53
+ matching = cart.select(&:only_in_new?)
54
+ if matching.empty?
55
+ raise RHC::MultipleCartridgesException, "You must select only a single web cartridge. '#{name}' matches web cartridges."
56
+ elsif matching.size == 1
57
+ use_cart(matching.first, name)
58
+ else
59
+ matching.instance_variable_set(:@for, name)
60
+ matching
61
+ end
62
+ }
63
+ end
33
64
 
34
- private
65
+ def other_carts_only
66
+ lambda{ |cart|
67
+ next cart unless cart.is_a? Array
68
+ name = cart.instance_variable_get(:@for)
69
+ matching = cart.select{ |c| not c.only_in_new? }
70
+ if matching.empty?
71
+ raise RHC::MultipleCartridgesException, "You must select only a single web cartridge. '#{name}' matches web cartridges."
72
+ elsif matching.size == 1
73
+ use_cart(matching.first, name)
74
+ else
75
+ matching.instance_variable_set(:@for, name)
76
+ matching
77
+ end
78
+ }
79
+ end
35
80
 
36
- def cart_regex(cart)
37
- "^#{cart.rstrip}(-[0-9\.]+){0,1}$"
38
- end
81
+ def standalone_cartridges
82
+ @standalone_cartridges ||= all_cartridges.select{ |c| c.type == 'standalone' }
83
+ end
84
+ def not_standalone_cartridges
85
+ @not_standalone_cartridges ||= all_cartridges.select{ |c| c.type != 'standalone' }
86
+ end
87
+ def all_cartridges
88
+ @all_cartridges = rest_client.cartridges
89
+ end
90
+
91
+ def list_cartridges(cartridges)
92
+ carts = cartridges.map{ |c| [c.name, c.display_name || ''] }.sort{ |a,b| a[1].downcase <=> b[1].downcase }
93
+ carts.unshift ['==========', '=========']
94
+ carts.unshift ['Short Name', 'Full name']
95
+ say table(carts).join("\n")
96
+ end
39
97
  end
40
98
  end
@@ -89,7 +89,7 @@ module RHC
89
89
  1
90
90
  rescue RHC::Exception, RHC::Rest::Exception => e
91
91
  RHC::Helpers.error e.message
92
- e.code.nil? ? 128 : e.code
92
+ e.code.nil? ? 128 : [1, (e.code || 1).to_i].max
93
93
  end
94
94
  else
95
95
  run_active_command
@@ -51,7 +51,7 @@ module RHC::Commands
51
51
  argument :cartridges, "The web framework this application should use", ["-t", "--type cartridge"], :arg_type => :list
52
52
  #argument :additional_cartridges, "A list of other cartridges such as databases you wish to add. Cartridges can also be added later using 'rhc cartridge add'", [], :arg_type => :list
53
53
  def create(name, cartridges)
54
- cartridges = check_cartridges(cartridges)
54
+ cartridges = check_cartridges(cartridges, &require_one_web_cart)
55
55
 
56
56
  options.default \
57
57
  :dns => true,
@@ -278,6 +278,29 @@ module RHC::Commands
278
278
  0
279
279
  end
280
280
 
281
+ summary "SSH into the specified application"
282
+ syntax "<app> [--ssh path_to_ssh_executable]"
283
+ argument :app, "The name of the application you want to SSH into", ["-a", "--app app"], :context => :app_context
284
+ option ["--ssh PATH"], "Path to your SSH executable"
285
+ option ["-n", "--namespace namespace"], "Namespace of the application the cartridge belongs to", :context => :namespace_context, :required => true
286
+ alias_action 'ssh', :root_command => true
287
+ def ssh(app_name)
288
+ raise ArgumentError, "No application specified" unless app_name.present?
289
+ raise OptionParser::InvalidOption, "No system SSH available. Please use the --ssh option to specify the path to your SSH executable, or install SSH." unless options.ssh or has_ssh?
290
+
291
+ domain = rest_client.find_domain(options.namespace)
292
+ app = domain.find_application(app_name)
293
+
294
+ say "Connecting to #{app.ssh_string.to_s} ..."
295
+ if options.ssh
296
+ debug "Using user specified SSH: #{options.ssh}"
297
+ Kernel.send(:system, "#{options.ssh} #{app.ssh_string.to_s}")
298
+ else
299
+ debug "Using system ssh"
300
+ Kernel.send(:system, "ssh #{app.ssh_string.to_s}")
301
+ end
302
+ end
303
+
281
304
  summary "DEPRECATED use 'show <app> --state' instead"
282
305
  syntax "<app> [--namespace namespace] [--app app]"
283
306
  argument :app, "The name of the application you are getting information on", ["-a", "--app app"], :context => :app_context
@@ -292,54 +315,26 @@ module RHC::Commands
292
315
  private
293
316
  include RHC::GitHelpers
294
317
  include RHC::CartridgeHelpers
295
-
296
- def check_sshkeys!
297
- RHC::SSHWizard.new(rest_client, config, options).run
298
- end
299
-
300
- def standalone_cartridges
301
- @standalone_cartridges ||= all_cartridges.select{ |c| c.type == 'standalone' }
302
- end
303
- def all_cartridges
304
- @all_cartridges = rest_client.cartridges
305
- end
306
-
307
- def check_cartridges(cartridge_names)
308
- cartridge_names = Array(cartridge_names).map{ |s| s.strip if s && s.length > 0 }.compact
309
-
310
- cartridge_names.map do |name|
311
- all_cartridges.find{ |c| c.name == name } ||
312
- begin
313
- matching_cartridges = all_cartridges.select{ |c| c.name.include?(name) }
314
- unless matching_cartridges.length == 1
315
- if matching_cartridges.present?
316
- paragraph { list_cartridges(matching_cartridges) }
317
- raise RHC::MultipleCartridgesException, "There are multiple web cartridges named '#{name}'. Please provide the short name of your desired cart." if matching_cartridges.present?
318
- else
319
- paragraph { list_cartridges(all_cartridges) }
320
- raise RHC::CartridgeNotFoundException, "There are no cartridges that match '#{name}'."
321
- end
322
- end
323
- use_cart(matching_cartridges.first, name)
324
- end
325
- end.tap do |carts|
326
- if carts.none?(&:only_in_new?)
327
- section(:bottom => 1){ list_cartridges }
318
+
319
+ def require_one_web_cart
320
+ lambda{ |carts|
321
+ match, ambiguous = carts.partition{ |c| not c.is_a?(Array) }
322
+ selected_web = match.any?(&:only_in_new?)
323
+ possible_web = ambiguous.flatten.any?(&:only_in_new?)
324
+ if not (selected_web or possible_web)
325
+ section(:bottom => 1){ list_cartridges(standalone_cartridges) }
328
326
  raise RHC::CartridgeNotFoundException, "Every application needs a web cartridge to handle incoming web requests. Please provide the short name of one of the carts listed above."
329
327
  end
330
- end
331
- end
332
-
333
- def use_cart(cart, for_cartridge_name)
334
- info "Using #{cart.name}#{cart.display_name ? " (#{cart.display_name})" : ''} for '#{for_cartridge_name}'"
335
- cart
328
+ if selected_web
329
+ carts.map! &other_carts_only
330
+ elsif possible_web
331
+ carts.map! &web_carts_only
332
+ end
333
+ }
336
334
  end
337
335
 
338
- def list_cartridges(cartridges=standalone_cartridges)
339
- carts = cartridges.map{ |c| [c.name, c.display_name || ''] }.sort{ |a,b| a[1].downcase <=> b[1].downcase }
340
- carts.unshift ['==========', '=========']
341
- carts.unshift ['Short Name', 'Full name']
342
- say table(carts).join("\n")
336
+ def check_sshkeys!
337
+ RHC::SSHWizard.new(rest_client, config, options).run
343
338
  end
344
339
 
345
340
  def app_action(app, action, *args)
@@ -363,7 +358,7 @@ module RHC::Commands
363
358
  rescue RHC::Rest::Exception => e
364
359
  if e.code == 109
365
360
  paragraph{ say "Valid cartridge types:" }
366
- paragraph{ list_cartridges }
361
+ paragraph{ list_cartridges(standalone_cartridges) }
367
362
  end
368
363
  raise
369
364
  end
@@ -422,18 +417,19 @@ module RHC::Commands
422
417
  Kernel.sleep 5
423
418
 
424
419
  # Now start checking for DNS
425
- for i in 0..MAX_RETRIES-1
426
- found = host_exists?(host) || hosts_file_contains?(host)
427
- break if found
420
+ host_found = hosts_file_contains?(host) or
421
+ 1.upto(MAX_RETRIES) { |i|
422
+ host_found = host_exists?(host)
423
+ break found if host_found
428
424
 
429
- say " retry # #{i+1} - Waiting for DNS: #{host}"
425
+ say " retry # #{i} - Waiting for DNS: #{host}"
430
426
  Kernel.sleep sleep_time.to_i
431
427
  sleep_time *= DEFAULT_DELAY_THROTTLE
432
- end
428
+ }
433
429
 
434
430
  debug "End checking for application dns @ '#{host} - found=#{found}'"
435
431
 
436
- found
432
+ host_found
437
433
  end
438
434
 
439
435
  def enable_jenkins?
@@ -461,6 +457,22 @@ module RHC::Commands
461
457
  # :nocov:
462
458
  end
463
459
 
460
+ # check the version of SSH that is installed
461
+ def ssh_version
462
+ @ssh_version ||= `ssh -V 2>&1`.strip
463
+ end
464
+
465
+ # return whether or not SSH is installed
466
+ def has_ssh?
467
+ @has_ssh ||= begin
468
+ @ssh_version = nil
469
+ ssh_version
470
+ $?.success?
471
+ rescue
472
+ false
473
+ end
474
+ end
475
+
464
476
  def windows_nslookup_bug?(rest_app)
465
477
  windows_nslookup = run_nslookup(rest_app.host)
466
478
  windows_ping = run_ping(rest_app.host)
@@ -15,7 +15,7 @@ module RHC::Commands
15
15
  carts = rest_client.cartridges.sort_by{ |c| "#{c.type == 'standalone' && 1}_#{c.tags.include?('experimental') ? 1 : 0}_#{(c.display_name || c.name).downcase}" }
16
16
 
17
17
  list = if options.verbose
18
- carts.map do |c|
18
+ carts.map do |c|
19
19
  name = c.display_name != c.name && "#{color(c.display_name, :cyan)} [#{c.name}]" || c.name
20
20
  tags = c.tags - RHC::Rest::Cartridge::HIDDEN_TAGS
21
21
  [
@@ -42,7 +42,7 @@ module RHC::Commands
42
42
  argument :cart_type, "The type of the cartridge you are adding (run 'rhc cartridge list' to obtain a list of available cartridges)", ["-c", "--cartridge cart_type"]
43
43
  alias_action :"app cartridge add", :root_command => true, :deprecated => true
44
44
  def add(cart_type)
45
- cart = find_cartridge rest_client, cart_type
45
+ cart = check_cartridges(cart_type, :from => not_standalone_cartridges).first
46
46
 
47
47
  say "Adding #{cart.name} to application '#{options.app}' ... "
48
48
 
@@ -67,7 +67,7 @@ module RHC::Commands
67
67
  def show(cartridge)
68
68
  rest_domain = rest_client.find_domain(options.namespace)
69
69
  rest_app = rest_domain.find_application(options.app)
70
- rest_cartridge = find_cartridge rest_app, cartridge, nil
70
+ rest_cartridge = check_cartridges(cartridge, :from => rest_app.cartridges).first
71
71
 
72
72
  display_cart(rest_cartridge)
73
73
 
@@ -85,7 +85,7 @@ module RHC::Commands
85
85
 
86
86
  rest_domain = rest_client.find_domain(options.namespace)
87
87
  rest_app = rest_domain.find_application(options.app)
88
- rest_cartridge = rest_app.find_cartridge cartridge, :type => "embedded"
88
+ rest_cartridge = check_cartridges(cartridge, :from => rest_app.cartridges).first
89
89
 
90
90
  confirm_action "Removing a cartridge is a destructive operation that may result in loss of data associated with the cartridge.\n\nAre you sure you wish to remove #{rest_cartridge.name} from '#{rest_app.name}'?"
91
91
 
@@ -103,9 +103,7 @@ module RHC::Commands
103
103
  option ["-a", "--app app"], "Application the cartridge", :context => :app_context, :required => true
104
104
  alias_action :"app cartridge start", :root_command => true, :deprecated => true
105
105
  def start(cartridge)
106
- cartridge_action cartridge, :start
107
-
108
- results { say "#{cartridge} started!" }
106
+ cartridge_action(cartridge, :start){ |_, c| results{ say "#{c.name} started" } }
109
107
  0
110
108
  end
111
109
 
@@ -116,9 +114,7 @@ module RHC::Commands
116
114
  option ["-a", "--app app"], "Application you the cartridge belongs to", :context => :app_context, :required => true
117
115
  alias_action :"app cartridge stop", :root_command => true, :deprecated => true
118
116
  def stop(cartridge)
119
- cartridge_action cartridge, :stop
120
-
121
- results { say "#{cartridge} stopped!" }
117
+ cartridge_action(cartridge, :stop){ |_, c| results{ say "#{c.name} stopped" } }
122
118
  0
123
119
  end
124
120
 
@@ -129,9 +125,7 @@ module RHC::Commands
129
125
  option ["-a", "--app app"], "Application the cartridge belongs to", :context => :app_context, :required => true
130
126
  alias_action :"app cartridge restart", :root_command => true, :deprecated => true
131
127
  def restart(cartridge)
132
- cartridge_action cartridge, :restart
133
-
134
- results { say "#{cartridge} restarted!" }
128
+ cartridge_action(cartridge, :restart){ |_, c| results{ say "#{c.name} restarted" } }
135
129
  0
136
130
  end
137
131
 
@@ -144,7 +138,7 @@ module RHC::Commands
144
138
  def status(cartridge)
145
139
  rest_domain = rest_client.find_domain(options.namespace)
146
140
  rest_app = rest_domain.find_application(options.app)
147
- rest_cartridge = find_cartridge(rest_app, cartridge)
141
+ rest_cartridge = check_cartridges(cartridge, :from => rest_app.cartridges).first
148
142
  results { rest_cartridge.status.each{ |msg| say msg['message'] } }
149
143
  0
150
144
  end
@@ -156,9 +150,7 @@ module RHC::Commands
156
150
  option ["-a", "--app app"], "Application the cartridge belongs to", :context => :app_context, :required => true
157
151
  alias_action :"app cartridge reload", :root_command => true, :deprecated => true
158
152
  def reload(cartridge)
159
- cartridge_action cartridge, :reload
160
-
161
- results { say "#{cartridge} config reloaded!" }
153
+ cartridge_action(cartridge, :reload){ |_, c| results{ say "#{c.name} reloaded" } }
162
154
  0
163
155
  end
164
156
 
@@ -174,7 +166,7 @@ module RHC::Commands
174
166
 
175
167
  rest_domain = rest_client.find_domain(options.namespace)
176
168
  rest_app = rest_domain.find_application(options.app)
177
- rest_cartridge = find_cartridge rest_app, cartridge, nil
169
+ rest_cartridge = check_cartridges(cartridge, :from => rest_app.cartridges).first
178
170
 
179
171
  raise RHC::CartridgeNotScalableException unless rest_cartridge.scalable?
180
172
 
@@ -191,8 +183,6 @@ module RHC::Commands
191
183
  0
192
184
  end
193
185
 
194
- =begin
195
- # Commenting this out for US2438
196
186
  summary 'View/manipulate storage on a cartridge'
197
187
  syntax '<cartridge> -a app [--show] [--add|--remove|--set amount] [--namespace namespace]'
198
188
  argument :cart_type, "The name of the cartridge", ["-c", "--cartridge cart_type"], :arg_type => :list
@@ -203,11 +193,8 @@ module RHC::Commands
203
193
  option ["--remove amount"], "Remove the indicated amount from the additional storage capacity"
204
194
  option ["--set amount"], "Set the specified amount of additional storage capacity"
205
195
  option ["-f", "--force"], "Force the action"
206
- def storage(cartridges)
207
- # Make sure that we are dealing with an array (-c param will only pass in a string)
208
- # BZ 883658
209
- cartridges = [cartridges].flatten
210
-
196
+ def storage(cartridge)
197
+ cartridges = Array(cartridge)
211
198
  rest_domain = rest_client.find_domain(options.namespace)
212
199
  rest_app = rest_domain.find_application(options.app)
213
200
 
@@ -227,8 +214,7 @@ module RHC::Commands
227
214
  if cartridges.length == 0
228
215
  display_cart_storage_list rest_app.cartridges
229
216
  else
230
- cartridges.each do |cartridge_name|
231
- cart = rest_app.find_cartridge(cartridge_name)
217
+ check_cartridges(cartridge, :from => rest_app.cartridges).each do |cart|
232
218
  display_cart_storage_info cart, cart.display_name
233
219
  end
234
220
  end
@@ -237,7 +223,7 @@ module RHC::Commands
237
223
  raise RHC::MultipleCartridgesException,
238
224
  'Exactly one cartridge must be specified for this operation' if cartridges.length != 1
239
225
 
240
- rest_cartridge = find_cartridge rest_app, cartridges.first, nil
226
+ rest_cartridge = check_cartridges(cartridge, :from => rest_app.cartridges).first
241
227
  amount = amount.match(/^(\d+)(GB)?$/i)
242
228
  raise RHC::AdditionalStorageValueException if amount.nil?
243
229
 
@@ -266,17 +252,18 @@ module RHC::Commands
266
252
 
267
253
  0
268
254
  end
269
- =end
270
255
 
271
256
  private
272
257
  include RHC::CartridgeHelpers
273
258
 
274
- def cartridge_action(cartridge, action)
259
+ def cartridge_action(cartridge, action, &block)
275
260
  rest_domain = rest_client.find_domain(options.namespace)
276
261
  rest_app = rest_domain.find_application(options.app)
277
- rest_cartridge = find_cartridge rest_app, cartridge
262
+ rest_cartridge = check_cartridges(cartridge, :from => rest_app.cartridges).first
278
263
  result = rest_cartridge.send action
279
- [result, rest_cartridge, rest_app, rest_domain]
264
+ resp = [result, rest_cartridge, rest_app, rest_domain]
265
+ yield resp if block_given?
266
+ resp
280
267
  end
281
268
  end
282
269
  end